1use std::collections::BTreeMap;
17
18pub const FEATURE_LSP: u32 = 1 << 8;
20
21pub const C2S_LSP_OPEN: u8 = 0x60;
27pub const C2S_LSP_CLOSE: u8 = 0x61;
29pub const C2S_LSP_ACK: u8 = 0x62;
32pub const C2S_LSP_QUERY: u8 = 0x63;
38pub const C2S_LSP_CANCEL: u8 = 0x64;
40pub const C2S_LSP_SERVERS: u8 = 0x65;
42pub const C2S_LSP_STOP: u8 = 0x66;
45pub const C2S_LSP_BUFFER: u8 = 0x67;
52
53pub const S2C_LSP_OPENED: u8 = 0x60;
60pub const S2C_LSP_STATE: u8 = 0x61;
63pub const S2C_LSP_DIAG: u8 = 0x62;
67pub const S2C_LSP_QUERY: u8 = 0x63;
70pub const S2C_LSP_CLOSED: u8 = 0x64;
72pub const S2C_LSP_SERVERS: u8 = 0x65;
75pub const S2C_LSP_STOPPED: u8 = 0x66;
77
78pub const LSP_STATUS_OK: u8 = 0;
82pub const LSP_STATUS_UNKNOWN_ID: u8 = 1;
84pub const LSP_STATUS_NOT_FOUND: u8 = 2;
87pub const LSP_STATUS_WRONG_TYPE: u8 = 3;
89pub const LSP_STATUS_PERMISSION: u8 = 4;
90pub const LSP_STATUS_TOO_LARGE: u8 = 5;
92pub const LSP_STATUS_BUDGET: u8 = 6;
94pub const LSP_STATUS_INVALID: u8 = 7;
96pub const LSP_STATUS_CANCELLED: u8 = 8;
98pub const LSP_STATUS_OTHER: u8 = 9;
100pub const LSP_STATUS_WARMING: u8 = 10;
102
103pub fn lsp_status_text(status: u8) -> &'static str {
105 match status {
106 LSP_STATUS_OK => "ok",
107 LSP_STATUS_UNKNOWN_ID => "unknown attachment",
108 LSP_STATUS_NOT_FOUND => "not found",
109 LSP_STATUS_WRONG_TYPE => "wrong type",
110 LSP_STATUS_PERMISSION => "permission denied",
111 LSP_STATUS_TOO_LARGE => "too large",
112 LSP_STATUS_BUDGET => "budget exhausted",
113 LSP_STATUS_INVALID => "invalid request",
114 LSP_STATUS_CANCELLED => "cancelled",
115 LSP_STATUS_WARMING => "warming up",
116 _ => "error",
117 }
118}
119
120pub const LSP_OPEN_WATCH: u8 = 1 << 0;
124pub const LSP_OPEN_DIAGS: u8 = 1 << 1;
126pub const LSP_OPEN_FROM_PTY: u8 = 1 << 2;
130
131pub const LSP_ID_INVALID: u16 = 0xFFFF;
133
134pub const LSP_STREAM_STATE: u8 = 0;
137pub const LSP_STREAM_DIAG: u8 = 1;
138
139pub const LSP_QUERY_DEFINITION: u8 = 1;
143pub const LSP_QUERY_REFERENCES: u8 = 2;
145pub const LSP_QUERY_HOVER: u8 = 3;
147pub const LSP_QUERY_DOC_SYMBOLS: u8 = 4;
149pub const LSP_QUERY_WS_SYMBOLS: u8 = 5;
151pub const LSP_QUERY_RENAME: u8 = 6;
153pub const LSP_QUERY_COMPLETION: u8 = 7;
156pub const LSP_QUERY_SIGNATURE: u8 = 8;
158
159pub const LSP_REFS_INCLUDE_DECLARATION: u8 = 1 << 0;
163
164pub const LSP_BUFFER_RELEASE: u8 = 1 << 0;
169
170pub const LSP_DIAG_FULL: u8 = 1 << 0;
177
178pub const LSP_RESP_TRUNCATED: u8 = 1 << 0;
182pub const LSP_RESP_INCOMPLETE: u8 = 1 << 1;
186
187pub const LSP_CLOSED_CLIENT_REQUEST: u8 = 0;
190pub const LSP_CLOSED_ROOT_GONE: u8 = 1;
191pub const LSP_CLOSED_PERMISSION_LOST: u8 = 2;
192pub const LSP_CLOSED_BACKEND_FAILED: u8 = 3;
193pub const LSP_CLOSED_RESOURCE_LIMIT: u8 = 4;
194
195pub const LSP_PHASE_SPAWNING: u8 = 0;
198pub const LSP_PHASE_INITIALIZING: u8 = 1;
199pub const LSP_PHASE_INDEXING: u8 = 2;
200pub const LSP_PHASE_READY: u8 = 3;
201pub const LSP_PHASE_FAILED: u8 = 4;
202
203pub const LSP_PROGRESS_UNKNOWN: u8 = 255;
205
206pub const LSP_CAP_DEFINITION: u32 = 1 << 0;
210pub const LSP_CAP_REFERENCES: u32 = 1 << 1;
211pub const LSP_CAP_HOVER: u32 = 1 << 2;
212pub const LSP_CAP_DOC_SYMBOLS: u32 = 1 << 3;
213pub const LSP_CAP_WS_SYMBOLS: u32 = 1 << 4;
214pub const LSP_CAP_RENAME: u32 = 1 << 5;
215pub const LSP_CAP_COMPLETION: u32 = 1 << 6;
216pub const LSP_CAP_SIGNATURE: u32 = 1 << 7;
217
218pub const LSP_COMPLETION_DEPRECATED: u8 = 1 << 0;
221pub const LSP_COMPLETION_SNIPPET: u8 = 1 << 1;
224pub const LSP_COMPLETION_PRESELECT: u8 = 1 << 2;
225
226pub const LSP_SIGNATURE_ACTIVE: u8 = 1 << 0;
230
231pub const LSP_SIGNATURE_NO_PARAM: u16 = 0xFFFF;
233
234pub const LSP_SEVERITY_ERROR: u8 = 1;
237pub const LSP_SEVERITY_WARNING: u8 = 2;
238pub const LSP_SEVERITY_INFO: u8 = 3;
239pub const LSP_SEVERITY_HINT: u8 = 4;
240
241pub const LSP_DIAG_UNNECESSARY: u8 = 1 << 0;
244pub const LSP_DIAG_DEPRECATED: u8 = 1 << 1;
245
246pub const LSP_MARKUP_PLAIN: u8 = 0;
249pub const LSP_MARKUP_MARKDOWN: u8 = 1;
250
251pub const LSP_SYMBOL_DEPRECATED: u8 = 1 << 0;
254
255pub const LSP_STATE_RECORD_SERVER: u8 = 0x01;
258pub const LSP_DIAG_RECORD_FILE: u8 = 0x01;
259pub const LSP_DIAG_RECORD_DIAG: u8 = 0x02;
260pub const LSP_QUERY_RECORD_LOCATION: u8 = 0x01;
261pub const LSP_QUERY_RECORD_MARKUP: u8 = 0x02;
262pub const LSP_QUERY_RECORD_SYMBOL: u8 = 0x03;
263pub const LSP_QUERY_RECORD_EDIT: u8 = 0x04;
264pub const LSP_QUERY_RECORD_COMPLETION: u8 = 0x05;
265pub const LSP_QUERY_RECORD_SIGNATURE: u8 = 0x06;
266
267pub type LspHash = [u8; 16];
270
271pub const LSP_HASH_NONE: LspHash = [0; 16];
273
274fn decompress_guarded(data: &[u8]) -> Option<Vec<u8>> {
278 if data.len() < 4 {
279 return None;
280 }
281 let declared = u32::from_le_bytes(data[0..4].try_into().unwrap()) as usize;
282 if declared > crate::MAX_DECOMPRESSED {
283 return None;
284 }
285 lz4_flex::decompress_size_prepended(data).ok()
286}
287
288fn push_str(buf: &mut Vec<u8>, s: &str) {
293 let b = s.as_bytes();
294 buf.extend_from_slice(&(b.len() as u16).to_le_bytes());
295 buf.extend_from_slice(b);
296}
297
298fn push_bytes(buf: &mut Vec<u8>, b: &[u8]) {
301 buf.extend_from_slice(&(b.len() as u32).to_le_bytes());
302 buf.extend_from_slice(b);
303}
304
305fn take_u8(b: &mut &[u8]) -> Option<u8> {
306 let (&x, rest) = b.split_first()?;
307 *b = rest;
308 Some(x)
309}
310
311fn take_u16(b: &mut &[u8]) -> Option<u16> {
312 if b.len() < 2 {
313 return None;
314 }
315 let v = u16::from_le_bytes([b[0], b[1]]);
316 *b = &b[2..];
317 Some(v)
318}
319
320fn take_u32(b: &mut &[u8]) -> Option<u32> {
321 if b.len() < 4 {
322 return None;
323 }
324 let v = u32::from_le_bytes(b[0..4].try_into().unwrap());
325 *b = &b[4..];
326 Some(v)
327}
328
329fn take_u64(b: &mut &[u8]) -> Option<u64> {
330 if b.len() < 8 {
331 return None;
332 }
333 let v = u64::from_le_bytes(b[0..8].try_into().unwrap());
334 *b = &b[8..];
335 Some(v)
336}
337
338fn take_hash(b: &mut &[u8]) -> Option<LspHash> {
339 if b.len() < 16 {
340 return None;
341 }
342 let hash: LspHash = b[0..16].try_into().unwrap();
343 *b = &b[16..];
344 Some(hash)
345}
346
347fn take_str<'a>(b: &mut &'a [u8]) -> Option<&'a str> {
348 let len = take_u16(b)? as usize;
349 if b.len() < len {
350 return None;
351 }
352 let s = std::str::from_utf8(&b[..len]).ok()?;
353 *b = &b[len..];
354 Some(s)
355}
356
357fn take_text<'a>(b: &mut &'a [u8]) -> Option<&'a str> {
358 let len = take_u32(b)? as usize;
359 if b.len() < len {
360 return None;
361 }
362 let s = std::str::from_utf8(&b[..len]).ok()?;
363 *b = &b[len..];
364 Some(s)
365}
366
367fn body_of(msg: &[u8], opcode: u8) -> Option<&[u8]> {
369 if msg.first() != Some(&opcode) {
370 return None;
371 }
372 Some(&msg[1..])
373}
374
375fn begin_record(buf: &mut Vec<u8>) -> usize {
379 let start = buf.len();
380 buf.extend_from_slice(&0u32.to_le_bytes());
381 start
382}
383
384fn end_record(buf: &mut [u8], start: usize) {
385 let len = (buf.len() - start - 4) as u32;
386 buf[start..start + 4].copy_from_slice(&len.to_le_bytes());
387}
388
389fn next_record<'a>(data: &mut &'a [u8]) -> Option<(u8, &'a [u8])> {
392 let mut b = *data;
393 let len = take_u32(&mut b)? as usize;
394 if b.len() < len || len == 0 {
395 return None;
396 }
397 let record = &b[..len];
398 *data = &b[len..];
399 Some((record[0], &record[1..]))
400}
401
402pub fn msg_lsp_open(nonce: u16, flags: u8, diag_latency_ms: u16, path: &str) -> Vec<u8> {
407 let mut msg = Vec::with_capacity(8 + path.len());
408 msg.push(C2S_LSP_OPEN);
409 msg.extend_from_slice(&nonce.to_le_bytes());
410 msg.push(flags);
411 msg.extend_from_slice(&diag_latency_ms.to_le_bytes());
412 push_str(&mut msg, path);
413 msg
414}
415
416pub fn msg_lsp_open_from_pty(
420 nonce: u16,
421 flags: u8,
422 diag_latency_ms: u16,
423 path: &str,
424 src_pty_id: u16,
425) -> Vec<u8> {
426 let mut msg = msg_lsp_open(nonce, flags | LSP_OPEN_FROM_PTY, diag_latency_ms, path);
427 msg.extend_from_slice(&src_pty_id.to_le_bytes());
428 msg
429}
430
431pub fn lsp_open_src_pty(msg: &[u8]) -> Option<u16> {
433 if msg.first().copied() != Some(C2S_LSP_OPEN) || msg.len() < 8 {
434 return None;
435 }
436 if msg[3] & LSP_OPEN_FROM_PTY == 0 {
437 return None;
438 }
439 let path_len = u16::from_le_bytes([msg[6], msg[7]]) as usize;
440 let off = 8usize.checked_add(path_len)?;
441 let b = msg.get(off..off + 2)?;
442 Some(u16::from_le_bytes([b[0], b[1]]))
443}
444
445pub fn lsp_open_rebase(msg: &[u8], cwd: Option<&str>) -> Option<Vec<u8>> {
449 lsp_open_src_pty(msg)?;
450 let (nonce, flags, diag_ms, path) = parse_lsp_open(msg)?;
451 let joined = cwd.map(|dir| {
452 std::path::Path::new(dir)
453 .join(path)
454 .to_string_lossy()
455 .into_owned()
456 });
457 let eff = joined.as_deref().unwrap_or(path);
458 Some(msg_lsp_open(
459 nonce,
460 flags & !LSP_OPEN_FROM_PTY,
461 diag_ms,
462 eff,
463 ))
464}
465
466pub fn parse_lsp_open(msg: &[u8]) -> Option<(u16, u8, u16, &str)> {
468 let mut b = body_of(msg, C2S_LSP_OPEN)?;
469 let nonce = take_u16(&mut b)?;
470 let flags = take_u8(&mut b)?;
471 let diag_latency_ms = take_u16(&mut b)?;
472 let path = take_str(&mut b)?;
473 Some((nonce, flags, diag_latency_ms, path))
474}
475
476pub fn msg_lsp_close(lsp_id: u16) -> Vec<u8> {
477 let mut msg = Vec::with_capacity(3);
478 msg.push(C2S_LSP_CLOSE);
479 msg.extend_from_slice(&lsp_id.to_le_bytes());
480 msg
481}
482
483pub fn parse_lsp_close(msg: &[u8]) -> Option<u16> {
484 let mut b = body_of(msg, C2S_LSP_CLOSE)?;
485 take_u16(&mut b)
486}
487
488pub fn msg_lsp_ack(lsp_id: u16, stream: u8, update_id: u32) -> Vec<u8> {
489 let mut msg = Vec::with_capacity(8);
490 msg.push(C2S_LSP_ACK);
491 msg.extend_from_slice(&lsp_id.to_le_bytes());
492 msg.push(stream);
493 msg.extend_from_slice(&update_id.to_le_bytes());
494 msg
495}
496
497pub fn parse_lsp_ack(msg: &[u8]) -> Option<(u16, u8, u32)> {
499 let mut b = body_of(msg, C2S_LSP_ACK)?;
500 let lsp_id = take_u16(&mut b)?;
501 let stream = take_u8(&mut b)?;
502 let update_id = take_u32(&mut b)?;
503 Some((lsp_id, stream, update_id))
504}
505
506#[derive(Clone, Debug, PartialEq, Eq)]
508pub struct LspQueryRequest<'a> {
509 pub nonce: u16,
510 pub lsp_id: u16,
511 pub kind: u8,
513 pub flags: u8,
514 pub line: u32,
516 pub col: u32,
518 pub path: &'a str,
520 pub arg: &'a str,
522}
523
524pub fn msg_lsp_query(req: &LspQueryRequest<'_>) -> Vec<u8> {
525 let mut msg = Vec::with_capacity(19 + req.path.len() + req.arg.len());
526 msg.push(C2S_LSP_QUERY);
527 msg.extend_from_slice(&req.nonce.to_le_bytes());
528 msg.extend_from_slice(&req.lsp_id.to_le_bytes());
529 msg.push(req.kind);
530 msg.push(req.flags);
531 msg.extend_from_slice(&req.line.to_le_bytes());
532 msg.extend_from_slice(&req.col.to_le_bytes());
533 push_str(&mut msg, req.path);
534 push_str(&mut msg, req.arg);
535 msg
536}
537
538pub fn parse_lsp_query(msg: &[u8]) -> Option<LspQueryRequest<'_>> {
539 let mut b = body_of(msg, C2S_LSP_QUERY)?;
540 let nonce = take_u16(&mut b)?;
541 let lsp_id = take_u16(&mut b)?;
542 let kind = take_u8(&mut b)?;
543 let flags = take_u8(&mut b)?;
544 let line = take_u32(&mut b)?;
545 let col = take_u32(&mut b)?;
546 let path = take_str(&mut b)?;
547 let arg = take_str(&mut b)?;
548 Some(LspQueryRequest {
549 nonce,
550 lsp_id,
551 kind,
552 flags,
553 line,
554 col,
555 path,
556 arg,
557 })
558}
559
560pub fn msg_lsp_cancel(nonce: u16) -> Vec<u8> {
561 let mut msg = Vec::with_capacity(3);
562 msg.push(C2S_LSP_CANCEL);
563 msg.extend_from_slice(&nonce.to_le_bytes());
564 msg
565}
566
567pub fn parse_lsp_cancel(msg: &[u8]) -> Option<u16> {
568 let mut b = body_of(msg, C2S_LSP_CANCEL)?;
569 take_u16(&mut b)
570}
571
572pub fn msg_lsp_servers(nonce: u16) -> Vec<u8> {
573 let mut msg = Vec::with_capacity(3);
574 msg.push(C2S_LSP_SERVERS);
575 msg.extend_from_slice(&nonce.to_le_bytes());
576 msg
577}
578
579pub fn parse_lsp_servers(msg: &[u8]) -> Option<u16> {
580 let mut b = body_of(msg, C2S_LSP_SERVERS)?;
581 take_u16(&mut b)
582}
583
584pub fn msg_lsp_stop(nonce: u16, server_ref: u16) -> Vec<u8> {
585 let mut msg = Vec::with_capacity(5);
586 msg.push(C2S_LSP_STOP);
587 msg.extend_from_slice(&nonce.to_le_bytes());
588 msg.extend_from_slice(&server_ref.to_le_bytes());
589 msg
590}
591
592pub fn parse_lsp_stop(msg: &[u8]) -> Option<(u16, u16)> {
594 let mut b = body_of(msg, C2S_LSP_STOP)?;
595 let nonce = take_u16(&mut b)?;
596 let server_ref = take_u16(&mut b)?;
597 Some((nonce, server_ref))
598}
599
600pub fn msg_lsp_buffer(lsp_id: u16, flags: u8, path: &str, text: &[u8]) -> Vec<u8> {
604 let compressed = lz4_flex::compress_prepend_size(text);
605 let mut msg = Vec::with_capacity(6 + path.len() + compressed.len());
606 msg.push(C2S_LSP_BUFFER);
607 msg.extend_from_slice(&lsp_id.to_le_bytes());
608 msg.push(flags);
609 push_str(&mut msg, path);
610 msg.extend_from_slice(&compressed);
611 msg
612}
613
614pub fn parse_lsp_buffer(msg: &[u8]) -> Option<(u16, u8, &str, Vec<u8>)> {
617 let mut b = body_of(msg, C2S_LSP_BUFFER)?;
618 let lsp_id = take_u16(&mut b)?;
619 let flags = take_u8(&mut b)?;
620 let path = take_str(&mut b)?;
621 let text = decompress_guarded(b)?;
622 Some((lsp_id, flags, path, text))
623}
624
625pub fn msg_lsp_opened(
630 nonce: u16,
631 lsp_id: u16,
632 status: u8,
633 flags: u8,
634 root: &str,
635 detail: &str,
636) -> Vec<u8> {
637 let mut msg = Vec::with_capacity(11 + root.len() + detail.len());
638 msg.push(S2C_LSP_OPENED);
639 msg.extend_from_slice(&nonce.to_le_bytes());
640 msg.extend_from_slice(&lsp_id.to_le_bytes());
641 msg.push(status);
642 msg.push(flags);
643 push_str(&mut msg, root);
644 push_str(&mut msg, detail);
645 msg
646}
647
648#[derive(Clone, Debug, PartialEq, Eq)]
650pub struct LspOpened<'a> {
651 pub nonce: u16,
652 pub lsp_id: u16,
653 pub status: u8,
654 pub flags: u8,
655 pub root: &'a str,
657 pub detail: &'a str,
659}
660
661pub fn parse_lsp_opened(msg: &[u8]) -> Option<LspOpened<'_>> {
662 let mut b = body_of(msg, S2C_LSP_OPENED)?;
663 let nonce = take_u16(&mut b)?;
664 let lsp_id = take_u16(&mut b)?;
665 let status = take_u8(&mut b)?;
666 let flags = take_u8(&mut b)?;
667 let root = take_str(&mut b)?;
668 let detail = take_str(&mut b)?;
669 Some(LspOpened {
670 nonce,
671 lsp_id,
672 status,
673 flags,
674 root,
675 detail,
676 })
677}
678
679pub fn msg_lsp_state(lsp_id: u16, state_id: u32, flags: u8, records: &[u8]) -> Vec<u8> {
680 let compressed = lz4_flex::compress_prepend_size(records);
681 let mut msg = Vec::with_capacity(8 + compressed.len());
682 msg.push(S2C_LSP_STATE);
683 msg.extend_from_slice(&lsp_id.to_le_bytes());
684 msg.extend_from_slice(&state_id.to_le_bytes());
685 msg.push(flags);
686 msg.extend_from_slice(&compressed);
687 msg
688}
689
690pub fn parse_lsp_state(msg: &[u8]) -> Option<(u16, u32, u8, Vec<u8>)> {
693 let mut b = body_of(msg, S2C_LSP_STATE)?;
694 let lsp_id = take_u16(&mut b)?;
695 let state_id = take_u32(&mut b)?;
696 let flags = take_u8(&mut b)?;
697 let records = decompress_guarded(b)?;
698 Some((lsp_id, state_id, flags, records))
699}
700
701pub fn msg_lsp_diag(lsp_id: u16, update_id: u32, flags: u8, records: &[u8]) -> Vec<u8> {
702 let compressed = lz4_flex::compress_prepend_size(records);
703 let mut msg = Vec::with_capacity(8 + compressed.len());
704 msg.push(S2C_LSP_DIAG);
705 msg.extend_from_slice(&lsp_id.to_le_bytes());
706 msg.extend_from_slice(&update_id.to_le_bytes());
707 msg.push(flags);
708 msg.extend_from_slice(&compressed);
709 msg
710}
711
712pub fn parse_lsp_diag(msg: &[u8]) -> Option<(u16, u32, u8, Vec<u8>)> {
715 let mut b = body_of(msg, S2C_LSP_DIAG)?;
716 let lsp_id = take_u16(&mut b)?;
717 let update_id = take_u32(&mut b)?;
718 let flags = take_u8(&mut b)?;
719 let records = decompress_guarded(b)?;
720 Some((lsp_id, update_id, flags, records))
721}
722
723pub fn msg_lsp_query_resp(
724 nonce: u16,
725 status: u8,
726 flags: u8,
727 detail: &str,
728 records: &[u8],
729) -> Vec<u8> {
730 let compressed = lz4_flex::compress_prepend_size(records);
731 let mut msg = Vec::with_capacity(7 + detail.len() + compressed.len());
732 msg.push(S2C_LSP_QUERY);
733 msg.extend_from_slice(&nonce.to_le_bytes());
734 msg.push(status);
735 msg.push(flags);
736 push_str(&mut msg, detail);
737 msg.extend_from_slice(&compressed);
738 msg
739}
740
741#[derive(Clone, Debug, PartialEq, Eq)]
743pub struct LspQueryResp {
744 pub nonce: u16,
745 pub status: u8,
746 pub flags: u8,
747 pub detail: String,
750 pub records: Vec<u8>,
752}
753
754pub fn parse_lsp_query_resp(msg: &[u8]) -> Option<LspQueryResp> {
756 let mut b = body_of(msg, S2C_LSP_QUERY)?;
757 let nonce = take_u16(&mut b)?;
758 let status = take_u8(&mut b)?;
759 let flags = take_u8(&mut b)?;
760 let detail = take_str(&mut b)?.to_string();
761 let records = decompress_guarded(b)?;
762 Some(LspQueryResp {
763 nonce,
764 status,
765 flags,
766 detail,
767 records,
768 })
769}
770
771pub fn msg_lsp_closed(lsp_id: u16, reason: u8) -> Vec<u8> {
772 let mut msg = Vec::with_capacity(4);
773 msg.push(S2C_LSP_CLOSED);
774 msg.extend_from_slice(&lsp_id.to_le_bytes());
775 msg.push(reason);
776 msg
777}
778
779pub fn parse_lsp_closed(msg: &[u8]) -> Option<(u16, u8)> {
781 let mut b = body_of(msg, S2C_LSP_CLOSED)?;
782 let lsp_id = take_u16(&mut b)?;
783 let reason = take_u8(&mut b)?;
784 Some((lsp_id, reason))
785}
786
787pub fn msg_lsp_servers_resp(nonce: u16, status: u8, flags: u8, records: &[u8]) -> Vec<u8> {
788 let compressed = lz4_flex::compress_prepend_size(records);
789 let mut msg = Vec::with_capacity(5 + compressed.len());
790 msg.push(S2C_LSP_SERVERS);
791 msg.extend_from_slice(&nonce.to_le_bytes());
792 msg.push(status);
793 msg.push(flags);
794 msg.extend_from_slice(&compressed);
795 msg
796}
797
798pub fn parse_lsp_servers_resp(msg: &[u8]) -> Option<(u16, u8, u8, Vec<u8>)> {
801 let mut b = body_of(msg, S2C_LSP_SERVERS)?;
802 let nonce = take_u16(&mut b)?;
803 let status = take_u8(&mut b)?;
804 let flags = take_u8(&mut b)?;
805 let records = decompress_guarded(b)?;
806 Some((nonce, status, flags, records))
807}
808
809pub fn msg_lsp_stopped(nonce: u16, status: u8) -> Vec<u8> {
810 let mut msg = Vec::with_capacity(4);
811 msg.push(S2C_LSP_STOPPED);
812 msg.extend_from_slice(&nonce.to_le_bytes());
813 msg.push(status);
814 msg
815}
816
817pub fn parse_lsp_stopped(msg: &[u8]) -> Option<(u16, u8)> {
819 let mut b = body_of(msg, S2C_LSP_STOPPED)?;
820 let nonce = take_u16(&mut b)?;
821 let status = take_u8(&mut b)?;
822 Some((nonce, status))
823}
824
825#[derive(Clone, Debug, PartialEq, Eq)]
831pub enum LspStateRecord<'a> {
832 Server {
835 server_ref: u16,
837 phase: u8,
839 progress_pct: u8,
841 caps: u32,
843 epoch: u32,
845 refused_edits: u32,
847 rss: u64,
849 id: &'a str,
851 msg: &'a str,
853 },
854}
855
856pub fn append_lsp_state_record(buf: &mut Vec<u8>, record: &LspStateRecord<'_>) {
858 let start = begin_record(buf);
859 match record {
860 LspStateRecord::Server {
861 server_ref,
862 phase,
863 progress_pct,
864 caps,
865 epoch,
866 refused_edits,
867 rss,
868 id,
869 msg,
870 } => {
871 buf.push(LSP_STATE_RECORD_SERVER);
872 buf.extend_from_slice(&server_ref.to_le_bytes());
873 buf.push(*phase);
874 buf.push(*progress_pct);
875 buf.extend_from_slice(&caps.to_le_bytes());
876 buf.extend_from_slice(&epoch.to_le_bytes());
877 buf.extend_from_slice(&refused_edits.to_le_bytes());
878 buf.extend_from_slice(&rss.to_le_bytes());
879 push_str(buf, id);
880 push_str(buf, msg);
881 }
882 }
883 end_record(buf, start);
884}
885
886pub struct LspStateRecordIter<'a> {
887 data: &'a [u8],
888}
889
890pub fn lsp_state_records(data: &[u8]) -> LspStateRecordIter<'_> {
892 LspStateRecordIter { data }
893}
894
895impl<'a> Iterator for LspStateRecordIter<'a> {
896 type Item = LspStateRecord<'a>;
897
898 fn next(&mut self) -> Option<LspStateRecord<'a>> {
899 loop {
900 let (kind, mut b) = next_record(&mut self.data)?;
901 match kind {
902 LSP_STATE_RECORD_SERVER => {
903 let server_ref = take_u16(&mut b)?;
904 let phase = take_u8(&mut b)?;
905 let progress_pct = take_u8(&mut b)?;
906 let caps = take_u32(&mut b)?;
907 let epoch = take_u32(&mut b)?;
908 let refused_edits = take_u32(&mut b)?;
909 let rss = take_u64(&mut b)?;
910 let id = take_str(&mut b)?;
911 let msg = take_str(&mut b)?;
912 return Some(LspStateRecord::Server {
913 server_ref,
914 phase,
915 progress_pct,
916 caps,
917 epoch,
918 refused_edits,
919 rss,
920 id,
921 msg,
922 });
923 }
924 _ => continue, }
926 }
927 }
928}
929
930#[derive(Clone, Debug, PartialEq, Eq)]
937pub enum LspServersRecord<'a> {
938 Server {
940 server_ref: u16,
941 phase: u8,
942 progress_pct: u8,
943 caps: u32,
944 epoch: u32,
945 refused_edits: u32,
946 rss: u64,
947 id: &'a str,
948 msg: &'a str,
949 root: &'a str,
951 },
952}
953
954pub fn append_lsp_servers_record(buf: &mut Vec<u8>, record: &LspServersRecord<'_>) {
956 let start = begin_record(buf);
957 match record {
958 LspServersRecord::Server {
959 server_ref,
960 phase,
961 progress_pct,
962 caps,
963 epoch,
964 refused_edits,
965 rss,
966 id,
967 msg,
968 root,
969 } => {
970 buf.push(LSP_STATE_RECORD_SERVER);
971 buf.extend_from_slice(&server_ref.to_le_bytes());
972 buf.push(*phase);
973 buf.push(*progress_pct);
974 buf.extend_from_slice(&caps.to_le_bytes());
975 buf.extend_from_slice(&epoch.to_le_bytes());
976 buf.extend_from_slice(&refused_edits.to_le_bytes());
977 buf.extend_from_slice(&rss.to_le_bytes());
978 push_str(buf, id);
979 push_str(buf, msg);
980 push_str(buf, root);
981 }
982 }
983 end_record(buf, start);
984}
985
986pub struct LspServersRecordIter<'a> {
987 data: &'a [u8],
988}
989
990pub fn lsp_servers_records(data: &[u8]) -> LspServersRecordIter<'_> {
992 LspServersRecordIter { data }
993}
994
995impl<'a> Iterator for LspServersRecordIter<'a> {
996 type Item = LspServersRecord<'a>;
997
998 fn next(&mut self) -> Option<LspServersRecord<'a>> {
999 loop {
1000 let (kind, mut b) = next_record(&mut self.data)?;
1001 match kind {
1002 LSP_STATE_RECORD_SERVER => {
1003 let server_ref = take_u16(&mut b)?;
1004 let phase = take_u8(&mut b)?;
1005 let progress_pct = take_u8(&mut b)?;
1006 let caps = take_u32(&mut b)?;
1007 let epoch = take_u32(&mut b)?;
1008 let refused_edits = take_u32(&mut b)?;
1009 let rss = take_u64(&mut b)?;
1010 let id = take_str(&mut b)?;
1011 let msg = take_str(&mut b)?;
1012 let root = take_str(&mut b)?;
1013 return Some(LspServersRecord::Server {
1014 server_ref,
1015 phase,
1016 progress_pct,
1017 caps,
1018 epoch,
1019 refused_edits,
1020 rss,
1021 id,
1022 msg,
1023 root,
1024 });
1025 }
1026 _ => continue, }
1028 }
1029 }
1030}
1031
1032#[derive(Clone, Debug, PartialEq, Eq)]
1038pub enum LspDiagRecord<'a> {
1039 File {
1044 hash: LspHash,
1045 n: u16,
1046 path: &'a str,
1047 },
1048 Diag {
1051 severity: u8,
1053 flags: u8,
1055 line: u32,
1056 col: u32,
1057 end_line: u32,
1058 end_col: u32,
1059 code: &'a str,
1060 source: &'a str,
1062 msg: &'a str,
1063 },
1064}
1065
1066pub fn append_lsp_diag_record(buf: &mut Vec<u8>, record: &LspDiagRecord<'_>) {
1068 let start = begin_record(buf);
1069 match record {
1070 LspDiagRecord::File { hash, n, path } => {
1071 buf.push(LSP_DIAG_RECORD_FILE);
1072 buf.extend_from_slice(hash);
1073 buf.extend_from_slice(&n.to_le_bytes());
1074 push_str(buf, path);
1075 }
1076 LspDiagRecord::Diag {
1077 severity,
1078 flags,
1079 line,
1080 col,
1081 end_line,
1082 end_col,
1083 code,
1084 source,
1085 msg,
1086 } => {
1087 buf.push(LSP_DIAG_RECORD_DIAG);
1088 buf.push(*severity);
1089 buf.push(*flags);
1090 buf.extend_from_slice(&line.to_le_bytes());
1091 buf.extend_from_slice(&col.to_le_bytes());
1092 buf.extend_from_slice(&end_line.to_le_bytes());
1093 buf.extend_from_slice(&end_col.to_le_bytes());
1094 push_str(buf, code);
1095 push_str(buf, source);
1096 push_bytes(buf, msg.as_bytes());
1097 }
1098 }
1099 end_record(buf, start);
1100}
1101
1102pub struct LspDiagRecordIter<'a> {
1103 data: &'a [u8],
1104}
1105
1106pub fn lsp_diag_records(data: &[u8]) -> LspDiagRecordIter<'_> {
1108 LspDiagRecordIter { data }
1109}
1110
1111impl<'a> Iterator for LspDiagRecordIter<'a> {
1112 type Item = LspDiagRecord<'a>;
1113
1114 fn next(&mut self) -> Option<LspDiagRecord<'a>> {
1115 loop {
1116 let (kind, mut b) = next_record(&mut self.data)?;
1117 match kind {
1118 LSP_DIAG_RECORD_FILE => {
1119 let hash = take_hash(&mut b)?;
1120 let n = take_u16(&mut b)?;
1121 let path = take_str(&mut b)?;
1122 return Some(LspDiagRecord::File { hash, n, path });
1123 }
1124 LSP_DIAG_RECORD_DIAG => {
1125 let severity = take_u8(&mut b)?;
1126 let flags = take_u8(&mut b)?;
1127 let line = take_u32(&mut b)?;
1128 let col = take_u32(&mut b)?;
1129 let end_line = take_u32(&mut b)?;
1130 let end_col = take_u32(&mut b)?;
1131 let code = take_str(&mut b)?;
1132 let source = take_str(&mut b)?;
1133 let msg = take_text(&mut b)?;
1134 return Some(LspDiagRecord::Diag {
1135 severity,
1136 flags,
1137 line,
1138 col,
1139 end_line,
1140 end_col,
1141 code,
1142 source,
1143 msg,
1144 });
1145 }
1146 _ => continue, }
1148 }
1149 }
1150}
1151
1152#[derive(Clone, Debug, PartialEq, Eq)]
1158pub enum LspQueryRecord<'a> {
1159 Location {
1161 flags: u8,
1162 hash: LspHash,
1164 line: u32,
1165 col: u32,
1166 end_line: u32,
1167 end_col: u32,
1168 path: &'a str,
1169 },
1170 Markup {
1172 format: u8,
1174 text: &'a str,
1175 },
1176 Symbol {
1180 sym_kind: u8,
1182 flags: u8,
1184 depth: u8,
1185 line: u32,
1186 col: u32,
1187 end_line: u32,
1188 end_col: u32,
1189 name: &'a str,
1190 path: &'a str,
1191 },
1192 Edit {
1197 flags: u8,
1198 hash: LspHash,
1199 line: u32,
1200 col: u32,
1201 end_line: u32,
1202 end_col: u32,
1203 new_text: &'a str,
1204 path: &'a str,
1205 },
1206 Completion {
1213 item_kind: u8,
1215 flags: u8,
1217 line: u32,
1218 col: u32,
1219 end_line: u32,
1220 end_col: u32,
1221 label: &'a str,
1222 insert: &'a str,
1223 detail: &'a str,
1224 },
1225 Signature {
1231 flags: u8,
1233 active_param: u16,
1235 param_start: u16,
1236 param_end: u16,
1237 label: &'a str,
1238 doc: &'a str,
1239 },
1240}
1241
1242pub fn append_lsp_query_record(buf: &mut Vec<u8>, record: &LspQueryRecord<'_>) {
1244 let start = begin_record(buf);
1245 match record {
1246 LspQueryRecord::Location {
1247 flags,
1248 hash,
1249 line,
1250 col,
1251 end_line,
1252 end_col,
1253 path,
1254 } => {
1255 buf.push(LSP_QUERY_RECORD_LOCATION);
1256 buf.push(*flags);
1257 buf.extend_from_slice(hash);
1258 buf.extend_from_slice(&line.to_le_bytes());
1259 buf.extend_from_slice(&col.to_le_bytes());
1260 buf.extend_from_slice(&end_line.to_le_bytes());
1261 buf.extend_from_slice(&end_col.to_le_bytes());
1262 push_str(buf, path);
1263 }
1264 LspQueryRecord::Markup { format, text } => {
1265 buf.push(LSP_QUERY_RECORD_MARKUP);
1266 buf.push(*format);
1267 push_bytes(buf, text.as_bytes());
1268 }
1269 LspQueryRecord::Symbol {
1270 sym_kind,
1271 flags,
1272 depth,
1273 line,
1274 col,
1275 end_line,
1276 end_col,
1277 name,
1278 path,
1279 } => {
1280 buf.push(LSP_QUERY_RECORD_SYMBOL);
1281 buf.push(*sym_kind);
1282 buf.push(*flags);
1283 buf.push(*depth);
1284 buf.extend_from_slice(&line.to_le_bytes());
1285 buf.extend_from_slice(&col.to_le_bytes());
1286 buf.extend_from_slice(&end_line.to_le_bytes());
1287 buf.extend_from_slice(&end_col.to_le_bytes());
1288 push_str(buf, name);
1289 push_str(buf, path);
1290 }
1291 LspQueryRecord::Edit {
1292 flags,
1293 hash,
1294 line,
1295 col,
1296 end_line,
1297 end_col,
1298 new_text,
1299 path,
1300 } => {
1301 buf.push(LSP_QUERY_RECORD_EDIT);
1302 buf.push(*flags);
1303 buf.extend_from_slice(hash);
1304 buf.extend_from_slice(&line.to_le_bytes());
1305 buf.extend_from_slice(&col.to_le_bytes());
1306 buf.extend_from_slice(&end_line.to_le_bytes());
1307 buf.extend_from_slice(&end_col.to_le_bytes());
1308 push_bytes(buf, new_text.as_bytes());
1309 push_str(buf, path);
1310 }
1311 LspQueryRecord::Completion {
1312 item_kind,
1313 flags,
1314 line,
1315 col,
1316 end_line,
1317 end_col,
1318 label,
1319 insert,
1320 detail,
1321 } => {
1322 buf.push(LSP_QUERY_RECORD_COMPLETION);
1323 buf.push(*item_kind);
1324 buf.push(*flags);
1325 buf.extend_from_slice(&line.to_le_bytes());
1326 buf.extend_from_slice(&col.to_le_bytes());
1327 buf.extend_from_slice(&end_line.to_le_bytes());
1328 buf.extend_from_slice(&end_col.to_le_bytes());
1329 push_str(buf, label);
1330 push_str(buf, insert);
1331 push_str(buf, detail);
1332 }
1333 LspQueryRecord::Signature {
1334 flags,
1335 active_param,
1336 param_start,
1337 param_end,
1338 label,
1339 doc,
1340 } => {
1341 buf.push(LSP_QUERY_RECORD_SIGNATURE);
1342 buf.push(*flags);
1343 buf.extend_from_slice(&active_param.to_le_bytes());
1344 buf.extend_from_slice(¶m_start.to_le_bytes());
1345 buf.extend_from_slice(¶m_end.to_le_bytes());
1346 push_str(buf, label);
1347 push_bytes(buf, doc.as_bytes());
1348 }
1349 }
1350 end_record(buf, start);
1351}
1352
1353pub struct LspQueryRecordIter<'a> {
1354 data: &'a [u8],
1355}
1356
1357pub fn lsp_query_records(data: &[u8]) -> LspQueryRecordIter<'_> {
1359 LspQueryRecordIter { data }
1360}
1361
1362impl<'a> Iterator for LspQueryRecordIter<'a> {
1363 type Item = LspQueryRecord<'a>;
1364
1365 fn next(&mut self) -> Option<LspQueryRecord<'a>> {
1366 loop {
1367 let (kind, mut b) = next_record(&mut self.data)?;
1368 match kind {
1369 LSP_QUERY_RECORD_LOCATION => {
1370 let flags = take_u8(&mut b)?;
1371 let hash = take_hash(&mut b)?;
1372 let line = take_u32(&mut b)?;
1373 let col = take_u32(&mut b)?;
1374 let end_line = take_u32(&mut b)?;
1375 let end_col = take_u32(&mut b)?;
1376 let path = take_str(&mut b)?;
1377 return Some(LspQueryRecord::Location {
1378 flags,
1379 hash,
1380 line,
1381 col,
1382 end_line,
1383 end_col,
1384 path,
1385 });
1386 }
1387 LSP_QUERY_RECORD_MARKUP => {
1388 let format = take_u8(&mut b)?;
1389 let text = take_text(&mut b)?;
1390 return Some(LspQueryRecord::Markup { format, text });
1391 }
1392 LSP_QUERY_RECORD_SYMBOL => {
1393 let sym_kind = take_u8(&mut b)?;
1394 let flags = take_u8(&mut b)?;
1395 let depth = take_u8(&mut b)?;
1396 let line = take_u32(&mut b)?;
1397 let col = take_u32(&mut b)?;
1398 let end_line = take_u32(&mut b)?;
1399 let end_col = take_u32(&mut b)?;
1400 let name = take_str(&mut b)?;
1401 let path = take_str(&mut b)?;
1402 return Some(LspQueryRecord::Symbol {
1403 sym_kind,
1404 flags,
1405 depth,
1406 line,
1407 col,
1408 end_line,
1409 end_col,
1410 name,
1411 path,
1412 });
1413 }
1414 LSP_QUERY_RECORD_EDIT => {
1415 let flags = take_u8(&mut b)?;
1416 let hash = take_hash(&mut b)?;
1417 let line = take_u32(&mut b)?;
1418 let col = take_u32(&mut b)?;
1419 let end_line = take_u32(&mut b)?;
1420 let end_col = take_u32(&mut b)?;
1421 let new_text = take_text(&mut b)?;
1422 let path = take_str(&mut b)?;
1423 return Some(LspQueryRecord::Edit {
1424 flags,
1425 hash,
1426 line,
1427 col,
1428 end_line,
1429 end_col,
1430 new_text,
1431 path,
1432 });
1433 }
1434 LSP_QUERY_RECORD_COMPLETION => {
1435 let item_kind = take_u8(&mut b)?;
1436 let flags = take_u8(&mut b)?;
1437 let line = take_u32(&mut b)?;
1438 let col = take_u32(&mut b)?;
1439 let end_line = take_u32(&mut b)?;
1440 let end_col = take_u32(&mut b)?;
1441 let label = take_str(&mut b)?;
1442 let insert = take_str(&mut b)?;
1443 let detail = take_str(&mut b)?;
1444 return Some(LspQueryRecord::Completion {
1445 item_kind,
1446 flags,
1447 line,
1448 col,
1449 end_line,
1450 end_col,
1451 label,
1452 insert,
1453 detail,
1454 });
1455 }
1456 LSP_QUERY_RECORD_SIGNATURE => {
1457 let flags = take_u8(&mut b)?;
1458 let active_param = take_u16(&mut b)?;
1459 let param_start = take_u16(&mut b)?;
1460 let param_end = take_u16(&mut b)?;
1461 let label = take_str(&mut b)?;
1462 let doc = take_text(&mut b)?;
1463 return Some(LspQueryRecord::Signature {
1464 flags,
1465 active_param,
1466 param_start,
1467 param_end,
1468 label,
1469 doc,
1470 });
1471 }
1472 _ => continue, }
1474 }
1475 }
1476}
1477
1478#[derive(Clone, Debug, PartialEq, Eq)]
1484pub struct LspServerState {
1485 pub phase: u8,
1486 pub progress_pct: u8,
1487 pub caps: u32,
1488 pub epoch: u32,
1489 pub refused_edits: u32,
1490 pub rss: u64,
1491 pub id: String,
1492 pub msg: String,
1493}
1494
1495#[derive(Clone, Debug, Default, PartialEq, Eq)]
1498pub struct LspStateMirror {
1499 pub servers: BTreeMap<u16, LspServerState>,
1501 pub flags: u8,
1503}
1504
1505impl LspStateMirror {
1506 pub fn new() -> Self {
1507 Self::default()
1508 }
1509
1510 pub fn apply_state(&mut self, msg: &[u8]) -> Option<u32> {
1514 let (_lsp_id, state_id, flags, records) = parse_lsp_state(msg)?;
1515 let mut next = LspStateMirror {
1516 flags,
1517 ..Default::default()
1518 };
1519 for record in lsp_state_records(&records) {
1520 match record {
1521 LspStateRecord::Server {
1522 server_ref,
1523 phase,
1524 progress_pct,
1525 caps,
1526 epoch,
1527 refused_edits,
1528 rss,
1529 id,
1530 msg,
1531 } => {
1532 next.servers.insert(
1533 server_ref,
1534 LspServerState {
1535 phase,
1536 progress_pct,
1537 caps,
1538 epoch,
1539 refused_edits,
1540 rss,
1541 id: id.to_string(),
1542 msg: msg.to_string(),
1543 },
1544 );
1545 }
1546 }
1547 }
1548 *self = next;
1549 Some(state_id)
1550 }
1551}
1552
1553#[derive(Clone, Debug, PartialEq, Eq)]
1555pub struct LspDiagnostic {
1556 pub severity: u8,
1557 pub flags: u8,
1558 pub line: u32,
1559 pub col: u32,
1560 pub end_line: u32,
1561 pub end_col: u32,
1562 pub code: String,
1563 pub source: String,
1564 pub msg: String,
1565}
1566
1567#[derive(Clone, Debug, Default, PartialEq, Eq)]
1569pub struct LspFileDiags {
1570 pub hash: LspHash,
1572 pub diags: Vec<LspDiagnostic>,
1573}
1574
1575#[derive(Clone, Debug, Default, PartialEq, Eq)]
1578pub struct LspDiagMirror {
1579 pub files: BTreeMap<String, LspFileDiags>,
1581}
1582
1583impl LspDiagMirror {
1584 pub fn new() -> Self {
1585 Self::default()
1586 }
1587
1588 pub fn apply_diag(&mut self, msg: &[u8]) -> Option<u32> {
1591 let (_lsp_id, update_id, flags, records) = parse_lsp_diag(msg)?;
1592 if flags & LSP_DIAG_FULL != 0 {
1593 self.files.clear();
1594 }
1595 let mut current: Option<String> = None;
1597 for record in lsp_diag_records(&records) {
1598 match record {
1599 LspDiagRecord::File { hash, n, path } => {
1600 if n == 0 {
1601 self.files.remove(path);
1602 current = None;
1603 } else {
1604 let entry = self.files.entry(path.to_string()).or_default();
1605 entry.hash = hash;
1606 entry.diags.clear();
1607 current = Some(path.to_string());
1608 }
1609 }
1610 LspDiagRecord::Diag {
1611 severity,
1612 flags,
1613 line,
1614 col,
1615 end_line,
1616 end_col,
1617 code,
1618 source,
1619 msg,
1620 } => {
1621 if let Some(file) = current.as_ref().and_then(|p| self.files.get_mut(p)) {
1622 file.diags.push(LspDiagnostic {
1623 severity,
1624 flags,
1625 line,
1626 col,
1627 end_line,
1628 end_col,
1629 code: code.to_string(),
1630 source: source.to_string(),
1631 msg: msg.to_string(),
1632 });
1633 }
1634 }
1635 }
1636 }
1637 Some(update_id)
1638 }
1639}
1640
1641#[cfg(test)]
1642mod tests {
1643 use super::*;
1644
1645 #[test]
1646 fn lsp_open_from_pty_roundtrip_and_rebase() {
1647 let m = msg_lsp_open_from_pty(7, LSP_OPEN_WATCH, 500, "sub", 3);
1648 assert_eq!(
1649 m,
1650 vec![
1651 0x60, 0x07, 0x00, 0x05, 0xf4, 0x01, 0x03, 0x00, 0x73, 0x75, 0x62, 0x03, 0x00
1652 ]
1653 );
1654 assert_eq!(lsp_open_src_pty(&m), Some(3));
1655 assert_eq!(
1656 lsp_open_src_pty(&msg_lsp_open(7, LSP_OPEN_WATCH, 500, "sub")),
1657 None
1658 );
1659 let (_, flags, _, path) = parse_lsp_open(&m).unwrap();
1660 assert_eq!(flags & LSP_OPEN_FROM_PTY, LSP_OPEN_FROM_PTY);
1661 assert_eq!(path, "sub");
1662 let reb = lsp_open_rebase(&m, Some("/w")).unwrap();
1664 let (_, rf, _, rp) = parse_lsp_open(&reb).unwrap();
1665 assert_eq!(rf & LSP_OPEN_FROM_PTY, 0);
1666 assert_eq!(rp, "/w/sub");
1667 }
1668
1669 fn hex(b: &[u8]) -> String {
1670 b.iter().map(|x| format!("{x:02x}")).collect()
1671 }
1672
1673 fn hash(fill: u8) -> LspHash {
1674 [fill; 16]
1675 }
1676
1677 #[test]
1678 fn request_roundtrips() {
1679 let msg = msg_lsp_open(7, LSP_OPEN_WATCH | LSP_OPEN_DIAGS, 500, "/src");
1680 assert_eq!(parse_lsp_open(&msg), Some((7, 3, 500, "/src")));
1681
1682 let msg = msg_lsp_close(9);
1683 assert_eq!(parse_lsp_close(&msg), Some(9));
1684
1685 let msg = msg_lsp_ack(9, LSP_STREAM_DIAG, 0x01020304);
1686 assert_eq!(parse_lsp_ack(&msg), Some((9, LSP_STREAM_DIAG, 0x01020304)));
1687
1688 let req = LspQueryRequest {
1689 nonce: 3,
1690 lsp_id: 9,
1691 kind: LSP_QUERY_REFERENCES,
1692 flags: LSP_REFS_INCLUDE_DECLARATION,
1693 line: 10,
1694 col: 4,
1695 path: "src/lib.rs",
1696 arg: "",
1697 };
1698 assert_eq!(parse_lsp_query(&msg_lsp_query(&req)), Some(req));
1699
1700 let msg = msg_lsp_cancel(3);
1701 assert_eq!(parse_lsp_cancel(&msg), Some(3));
1702
1703 let msg = msg_lsp_servers(4);
1704 assert_eq!(parse_lsp_servers(&msg), Some(4));
1705
1706 let msg = msg_lsp_stop(5, 2);
1707 assert_eq!(parse_lsp_stop(&msg), Some((5, 2)));
1708
1709 let msg = msg_lsp_buffer(9, 0, "src/lib.rs", b"fn main() {}\n");
1712 assert_eq!(
1713 parse_lsp_buffer(&msg),
1714 Some((9, 0, "src/lib.rs", b"fn main() {}\n".to_vec()))
1715 );
1716 let msg = msg_lsp_buffer(9, LSP_BUFFER_RELEASE, "src/lib.rs", b"");
1717 assert_eq!(
1718 parse_lsp_buffer(&msg),
1719 Some((9, LSP_BUFFER_RELEASE, "src/lib.rs", Vec::new()))
1720 );
1721 }
1722
1723 #[test]
1724 fn response_roundtrips() {
1725 let msg = msg_lsp_opened(7, 1, LSP_STATUS_OK, 0, "/src", "");
1726 assert_eq!(
1727 parse_lsp_opened(&msg),
1728 Some(LspOpened {
1729 nonce: 7,
1730 lsp_id: 1,
1731 status: LSP_STATUS_OK,
1732 flags: 0,
1733 root: "/src",
1734 detail: "",
1735 })
1736 );
1737
1738 let msg = msg_lsp_opened(
1739 8,
1740 LSP_ID_INVALID,
1741 LSP_STATUS_NOT_FOUND,
1742 0,
1743 "",
1744 "gopls: not found on PATH",
1745 );
1746 let opened = parse_lsp_opened(&msg).unwrap();
1747 assert_eq!(opened.lsp_id, LSP_ID_INVALID);
1748 assert_eq!(opened.detail, "gopls: not found on PATH");
1749
1750 let mut records = Vec::new();
1751 append_lsp_state_record(
1752 &mut records,
1753 &LspStateRecord::Server {
1754 server_ref: 1,
1755 phase: LSP_PHASE_INDEXING,
1756 progress_pct: 42,
1757 caps: LSP_CAP_DEFINITION | LSP_CAP_HOVER,
1758 epoch: 2,
1759 refused_edits: 1,
1760 rss: 3 << 30,
1761 id: "rust-analyzer",
1762 msg: "indexing 42%",
1763 },
1764 );
1765 let msg = msg_lsp_state(1, 5, 0, &records);
1766 let (lsp_id, state_id, flags, decoded) = parse_lsp_state(&msg).unwrap();
1767 assert_eq!((lsp_id, state_id, flags), (1, 5, 0));
1768 assert_eq!(decoded, records);
1769
1770 let msg = msg_lsp_diag(1, 6, LSP_DIAG_FULL, &[]);
1771 assert_eq!(parse_lsp_diag(&msg), Some((1, 6, LSP_DIAG_FULL, vec![])));
1772
1773 let msg = msg_lsp_query_resp(3, LSP_STATUS_OK, 0, "", &[]);
1774 assert_eq!(
1775 parse_lsp_query_resp(&msg),
1776 Some(LspQueryResp {
1777 nonce: 3,
1778 status: LSP_STATUS_OK,
1779 flags: 0,
1780 detail: String::new(),
1781 records: vec![],
1782 })
1783 );
1784 let msg = msg_lsp_query_resp(4, LSP_STATUS_OTHER, 0, "gopls: boom", &[]);
1786 let resp = parse_lsp_query_resp(&msg).unwrap();
1787 assert_eq!(resp.status, LSP_STATUS_OTHER);
1788 assert_eq!(resp.detail, "gopls: boom");
1789
1790 let msg = msg_lsp_closed(1, LSP_CLOSED_BACKEND_FAILED);
1791 assert_eq!(parse_lsp_closed(&msg), Some((1, LSP_CLOSED_BACKEND_FAILED)));
1792
1793 let msg = msg_lsp_servers_resp(4, LSP_STATUS_OK, 0, &[]);
1794 assert_eq!(
1795 parse_lsp_servers_resp(&msg),
1796 Some((4, LSP_STATUS_OK, 0, vec![]))
1797 );
1798
1799 let msg = msg_lsp_stopped(5, LSP_STATUS_OK);
1800 assert_eq!(parse_lsp_stopped(&msg), Some((5, LSP_STATUS_OK)));
1801 }
1802
1803 #[test]
1804 fn state_record_roundtrip() {
1805 let record = LspStateRecord::Server {
1806 server_ref: 2,
1807 phase: LSP_PHASE_READY,
1808 progress_pct: LSP_PROGRESS_UNKNOWN,
1809 caps: LSP_CAP_RENAME,
1810 epoch: 0,
1811 refused_edits: 0,
1812 rss: 0,
1813 id: "gopls",
1814 msg: "",
1815 };
1816 let mut buf = Vec::new();
1817 append_lsp_state_record(&mut buf, &record);
1818 let decoded: Vec<_> = lsp_state_records(&buf).collect();
1819 assert_eq!(decoded, vec![record]);
1820 }
1821
1822 #[test]
1823 fn servers_record_roundtrip() {
1824 let record = LspServersRecord::Server {
1825 server_ref: 2,
1826 phase: LSP_PHASE_READY,
1827 progress_pct: 100,
1828 caps: 0x3F,
1829 epoch: 1,
1830 refused_edits: 0,
1831 rss: 1 << 20,
1832 id: "gopls",
1833 msg: "ready",
1834 root: "/work/api",
1835 };
1836 let mut buf = Vec::new();
1837 append_lsp_servers_record(&mut buf, &record);
1838 let decoded: Vec<_> = lsp_servers_records(&buf).collect();
1839 assert_eq!(decoded, vec![record]);
1840 }
1841
1842 #[test]
1843 fn diag_record_roundtrip() {
1844 let records = vec![
1845 LspDiagRecord::File {
1846 hash: hash(0xAB),
1847 n: 1,
1848 path: "src/lib.rs",
1849 },
1850 LspDiagRecord::Diag {
1851 severity: LSP_SEVERITY_ERROR,
1852 flags: LSP_DIAG_UNNECESSARY,
1853 line: 10,
1854 col: 4,
1855 end_line: 10,
1856 end_col: 9,
1857 code: "E0308",
1858 source: "rustc",
1859 msg: "mismatched types",
1860 },
1861 LspDiagRecord::File {
1862 hash: LSP_HASH_NONE,
1863 n: 0,
1864 path: "src/old.rs",
1865 },
1866 ];
1867 let mut buf = Vec::new();
1868 for r in &records {
1869 append_lsp_diag_record(&mut buf, r);
1870 }
1871 let decoded: Vec<_> = lsp_diag_records(&buf).collect();
1872 assert_eq!(decoded, records);
1873 }
1874
1875 #[test]
1876 fn query_record_roundtrip() {
1877 let records = vec![
1878 LspQueryRecord::Location {
1879 flags: 0,
1880 hash: hash(0xCD),
1881 line: 5,
1882 col: 0,
1883 end_line: 5,
1884 end_col: 12,
1885 path: "src/main.rs",
1886 },
1887 LspQueryRecord::Markup {
1888 format: LSP_MARKUP_MARKDOWN,
1889 text: "```rust\nfn main()\n```",
1890 },
1891 LspQueryRecord::Symbol {
1892 sym_kind: 12, flags: LSP_SYMBOL_DEPRECATED,
1894 depth: 1,
1895 line: 3,
1896 col: 4,
1897 end_line: 9,
1898 end_col: 1,
1899 name: "main",
1900 path: "src/main.rs",
1901 },
1902 LspQueryRecord::Edit {
1903 flags: 0,
1904 hash: hash(0xEF),
1905 line: 3,
1906 col: 7,
1907 end_line: 3,
1908 end_col: 11,
1909 new_text: "run",
1910 path: "src/main.rs",
1911 },
1912 LspQueryRecord::Completion {
1913 item_kind: 3, flags: LSP_COMPLETION_SNIPPET | LSP_COMPLETION_PRESELECT,
1915 line: 4,
1916 col: 2,
1917 end_line: 4,
1918 end_col: 6,
1919 label: "push",
1920 insert: "push(${1:value})",
1921 detail: "fn(&mut self, value: T)",
1922 },
1923 LspQueryRecord::Signature {
1924 flags: LSP_SIGNATURE_ACTIVE,
1925 active_param: 1,
1926 param_start: 14,
1927 param_end: 22,
1928 label: "fn get(&self, index: usize)",
1929 doc: "Returns a reference to an element.",
1930 },
1931 ];
1932 let mut buf = Vec::new();
1933 for r in &records {
1934 append_lsp_query_record(&mut buf, r);
1935 }
1936 let decoded: Vec<_> = lsp_query_records(&buf).collect();
1937 assert_eq!(decoded, records);
1938 }
1939
1940 #[test]
1941 fn unknown_record_kind_is_skipped() {
1942 let mut buf = Vec::new();
1943 let start = begin_record(&mut buf);
1945 buf.push(0x7F);
1946 buf.extend_from_slice(b"future");
1947 end_record(&mut buf, start);
1948 append_lsp_query_record(
1949 &mut buf,
1950 &LspQueryRecord::Markup {
1951 format: LSP_MARKUP_PLAIN,
1952 text: "hi",
1953 },
1954 );
1955 let decoded: Vec<_> = lsp_query_records(&buf).collect();
1956 assert_eq!(
1957 decoded,
1958 vec![LspQueryRecord::Markup {
1959 format: LSP_MARKUP_PLAIN,
1960 text: "hi",
1961 }]
1962 );
1963 }
1964
1965 #[test]
1966 fn malformed_record_ends_iteration() {
1967 let mut buf = Vec::new();
1968 append_lsp_diag_record(
1969 &mut buf,
1970 &LspDiagRecord::File {
1971 hash: LSP_HASH_NONE,
1972 n: 0,
1973 path: "a",
1974 },
1975 );
1976 buf.extend_from_slice(&99u32.to_le_bytes());
1978 buf.push(LSP_DIAG_RECORD_FILE);
1979 let decoded: Vec<_> = lsp_diag_records(&buf).collect();
1980 assert_eq!(decoded.len(), 1);
1981 }
1982
1983 #[test]
1984 fn oversized_declared_length_is_rejected_before_allocation() {
1985 let mut msg = vec![S2C_LSP_STATE];
1986 msg.extend_from_slice(&1u16.to_le_bytes());
1987 msg.extend_from_slice(&1u32.to_le_bytes());
1988 msg.push(0);
1989 msg.extend_from_slice(&(u32::MAX).to_le_bytes());
1991 msg.extend_from_slice(&[0; 8]);
1992 assert_eq!(parse_lsp_state(&msg), None);
1993 }
1994
1995 #[test]
1996 fn state_mirror_replaces_whole_state() {
1997 let mut records = Vec::new();
1998 append_lsp_state_record(
1999 &mut records,
2000 &LspStateRecord::Server {
2001 server_ref: 1,
2002 phase: LSP_PHASE_INDEXING,
2003 progress_pct: 10,
2004 caps: 0,
2005 epoch: 0,
2006 refused_edits: 0,
2007 rss: 0,
2008 id: "rust-analyzer",
2009 msg: "indexing",
2010 },
2011 );
2012 let mut mirror = LspStateMirror::new();
2013 assert_eq!(
2014 mirror.apply_state(&msg_lsp_state(1, 1, 0, &records)),
2015 Some(1)
2016 );
2017 assert_eq!(mirror.servers.len(), 1);
2018 assert_eq!(mirror.servers[&1].phase, LSP_PHASE_INDEXING);
2019
2020 let mut records = Vec::new();
2022 append_lsp_state_record(
2023 &mut records,
2024 &LspStateRecord::Server {
2025 server_ref: 2,
2026 phase: LSP_PHASE_READY,
2027 progress_pct: 100,
2028 caps: 0x3F,
2029 epoch: 0,
2030 refused_edits: 0,
2031 rss: 0,
2032 id: "gopls",
2033 msg: "",
2034 },
2035 );
2036 assert_eq!(
2037 mirror.apply_state(&msg_lsp_state(1, 2, 0, &records)),
2038 Some(2)
2039 );
2040 assert_eq!(mirror.servers.len(), 1);
2041 assert!(mirror.servers.contains_key(&2));
2042 }
2043
2044 #[test]
2045 fn diag_mirror_applies_replacement_sets() {
2046 let mut mirror = LspDiagMirror::new();
2047
2048 let mut records = Vec::new();
2050 append_lsp_diag_record(
2051 &mut records,
2052 &LspDiagRecord::File {
2053 hash: hash(1),
2054 n: 1,
2055 path: "a.rs",
2056 },
2057 );
2058 append_lsp_diag_record(
2059 &mut records,
2060 &LspDiagRecord::Diag {
2061 severity: LSP_SEVERITY_ERROR,
2062 flags: 0,
2063 line: 1,
2064 col: 0,
2065 end_line: 1,
2066 end_col: 5,
2067 code: "E1",
2068 source: "t",
2069 msg: "boom",
2070 },
2071 );
2072 assert_eq!(
2073 mirror.apply_diag(&msg_lsp_diag(1, 1, LSP_DIAG_FULL, &records)),
2074 Some(1)
2075 );
2076 assert_eq!(mirror.files.len(), 1);
2077 assert_eq!(mirror.files["a.rs"].diags.len(), 1);
2078
2079 let mut records = Vec::new();
2081 append_lsp_diag_record(
2082 &mut records,
2083 &LspDiagRecord::File {
2084 hash: hash(2),
2085 n: 0,
2086 path: "a.rs",
2087 },
2088 );
2089 append_lsp_diag_record(
2090 &mut records,
2091 &LspDiagRecord::File {
2092 hash: hash(3),
2093 n: 1,
2094 path: "b.rs",
2095 },
2096 );
2097 append_lsp_diag_record(
2098 &mut records,
2099 &LspDiagRecord::Diag {
2100 severity: LSP_SEVERITY_WARNING,
2101 flags: 0,
2102 line: 2,
2103 col: 1,
2104 end_line: 2,
2105 end_col: 2,
2106 code: "",
2107 source: "t",
2108 msg: "hm",
2109 },
2110 );
2111 assert_eq!(mirror.apply_diag(&msg_lsp_diag(1, 2, 0, &records)), Some(2));
2112 assert!(!mirror.files.contains_key("a.rs"));
2113 assert_eq!(mirror.files["b.rs"].hash, hash(3));
2114
2115 assert_eq!(
2117 mirror.apply_diag(&msg_lsp_diag(1, 3, LSP_DIAG_FULL, &[])),
2118 Some(3)
2119 );
2120 assert!(mirror.files.is_empty());
2121 }
2122
2123 #[test]
2126 fn wire_fixtures() {
2127 assert_eq!(
2128 hex(&msg_lsp_open(
2129 0x0102,
2130 LSP_OPEN_WATCH | LSP_OPEN_DIAGS,
2131 500,
2132 "/src"
2133 )),
2134 "60020103f40104002f737263"
2135 );
2136 assert_eq!(hex(&msg_lsp_close(7)), "610700");
2137 assert_eq!(
2138 hex(&msg_lsp_ack(7, LSP_STREAM_DIAG, 0x01020304)),
2139 "6207000104030201"
2140 );
2141 assert_eq!(
2142 hex(&msg_lsp_query(&LspQueryRequest {
2143 nonce: 3,
2144 lsp_id: 7,
2145 kind: LSP_QUERY_DEFINITION,
2146 flags: 0,
2147 line: 10,
2148 col: 4,
2149 path: "a.rs",
2150 arg: "",
2151 })),
2152 "630300070001000a000000040000000400612e72730000"
2153 );
2154 assert_eq!(hex(&msg_lsp_cancel(10)), "640a00");
2155 assert_eq!(hex(&msg_lsp_servers(11)), "650b00");
2156 assert_eq!(hex(&msg_lsp_stop(12, 2)), "660c000200");
2157 assert_eq!(
2158 hex(&msg_lsp_opened(5, 1, LSP_STATUS_OK, 0, "/w", "")),
2159 "6005000100000002002f770000"
2160 );
2161 assert_eq!(hex(&msg_lsp_closed(1, LSP_CLOSED_ROOT_GONE)), "64010001");
2162 assert_eq!(hex(&msg_lsp_stopped(6, LSP_STATUS_OK)), "66060000");
2163
2164 let mut buf = Vec::new();
2168 append_lsp_diag_record(
2169 &mut buf,
2170 &LspDiagRecord::File {
2171 hash: hash(0xAB),
2172 n: 1,
2173 path: "a.rs",
2174 },
2175 );
2176 assert_eq!(
2177 hex(&buf),
2178 format!("1900000001{}01000400612e7273", "ab".repeat(16))
2179 );
2180 let mut buf = Vec::new();
2181 append_lsp_query_record(
2182 &mut buf,
2183 &LspQueryRecord::Markup {
2184 format: LSP_MARKUP_MARKDOWN,
2185 text: "hi",
2186 },
2187 );
2188 assert_eq!(hex(&buf), "080000000201020000006869");
2189 let mut buf = Vec::new();
2190 append_lsp_query_record(
2191 &mut buf,
2192 &LspQueryRecord::Completion {
2193 item_kind: 2,
2194 flags: 0,
2195 line: 1,
2196 col: 2,
2197 end_line: 1,
2198 end_col: 4,
2199 label: "ab",
2200 insert: "",
2201 detail: "T",
2202 },
2203 );
2204 assert_eq!(
2205 hex(&buf),
2206 "1c00000005020001000000020000000100000004000000020061620000010054"
2207 );
2208 let mut buf = Vec::new();
2209 append_lsp_query_record(
2210 &mut buf,
2211 &LspQueryRecord::Signature {
2212 flags: LSP_SIGNATURE_ACTIVE,
2213 active_param: 0,
2214 param_start: 2,
2215 param_end: 6,
2216 label: "f(a: T)",
2217 doc: "d",
2218 },
2219 );
2220 assert_eq!(
2221 hex(&buf),
2222 "16000000060100000200060007006628613a2054290100000064"
2223 );
2224 }
2225}