1use crate::{DocumentHighlight, Location, Project, ProjectTransaction};
2use anyhow::{anyhow, Result};
3use async_trait::async_trait;
4use client::{proto, PeerId};
5use gpui::{AppContext, AsyncAppContext, ModelHandle};
6use language::{
7 point_from_lsp,
8 proto::{deserialize_anchor, serialize_anchor},
9 range_from_lsp, Anchor, Bias, Buffer, PointUtf16, ToLspPosition, ToPointUtf16,
10};
11use lsp::{DocumentHighlightKind, ServerCapabilities};
12use std::{cmp::Reverse, ops::Range, path::Path};
13
14#[async_trait(?Send)]
15pub(crate) trait LspCommand: 'static + Sized {
16 type Response: 'static + Default + Send;
17 type LspRequest: 'static + Send + lsp::request::Request;
18 type ProtoRequest: 'static + Send + proto::RequestMessage;
19
20 fn check_capabilities(&self, _: &lsp::ServerCapabilities) -> bool {
21 true
22 }
23
24 fn to_lsp(
25 &self,
26 path: &Path,
27 cx: &AppContext,
28 ) -> <Self::LspRequest as lsp::request::Request>::Params;
29 async fn response_from_lsp(
30 self,
31 message: <Self::LspRequest as lsp::request::Request>::Result,
32 project: ModelHandle<Project>,
33 buffer: ModelHandle<Buffer>,
34 cx: AsyncAppContext,
35 ) -> Result<Self::Response>;
36
37 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest;
38 fn from_proto(
39 message: Self::ProtoRequest,
40 project: &mut Project,
41 buffer: &Buffer,
42 ) -> Result<Self>;
43 fn response_to_proto(
44 response: Self::Response,
45 project: &mut Project,
46 peer_id: PeerId,
47 buffer_version: &clock::Global,
48 cx: &AppContext,
49 ) -> <Self::ProtoRequest as proto::RequestMessage>::Response;
50 async fn response_from_proto(
51 self,
52 message: <Self::ProtoRequest as proto::RequestMessage>::Response,
53 project: ModelHandle<Project>,
54 buffer: ModelHandle<Buffer>,
55 cx: AsyncAppContext,
56 ) -> Result<Self::Response>;
57 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> u64;
58}
59
60pub(crate) struct PrepareRename {
61 pub position: PointUtf16,
62}
63
64pub(crate) struct PerformRename {
65 pub position: PointUtf16,
66 pub new_name: String,
67 pub push_to_history: bool,
68}
69
70pub(crate) struct GetDefinition {
71 pub position: PointUtf16,
72}
73
74pub(crate) struct GetReferences {
75 pub position: PointUtf16,
76}
77
78pub(crate) struct GetDocumentHighlights {
79 pub position: PointUtf16,
80}
81
82#[async_trait(?Send)]
83impl LspCommand for PrepareRename {
84 type Response = Option<Range<Anchor>>;
85 type LspRequest = lsp::request::PrepareRenameRequest;
86 type ProtoRequest = proto::PrepareRename;
87
88 fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::TextDocumentPositionParams {
89 lsp::TextDocumentPositionParams {
90 text_document: lsp::TextDocumentIdentifier {
91 uri: lsp::Url::from_file_path(path).unwrap(),
92 },
93 position: self.position.to_lsp_position(),
94 }
95 }
96
97 async fn response_from_lsp(
98 self,
99 message: Option<lsp::PrepareRenameResponse>,
100 _: ModelHandle<Project>,
101 buffer: ModelHandle<Buffer>,
102 cx: AsyncAppContext,
103 ) -> Result<Option<Range<Anchor>>> {
104 buffer.read_with(&cx, |buffer, _| {
105 if let Some(
106 lsp::PrepareRenameResponse::Range(range)
107 | lsp::PrepareRenameResponse::RangeWithPlaceholder { range, .. },
108 ) = message
109 {
110 let Range { start, end } = range_from_lsp(range);
111 if buffer.clip_point_utf16(start, Bias::Left) == start
112 && buffer.clip_point_utf16(end, Bias::Left) == end
113 {
114 return Ok(Some(buffer.anchor_after(start)..buffer.anchor_before(end)));
115 }
116 }
117 Ok(None)
118 })
119 }
120
121 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PrepareRename {
122 proto::PrepareRename {
123 project_id,
124 buffer_id: buffer.remote_id(),
125 position: Some(language::proto::serialize_anchor(
126 &buffer.anchor_before(self.position),
127 )),
128 }
129 }
130
131 fn from_proto(message: proto::PrepareRename, _: &mut Project, buffer: &Buffer) -> Result<Self> {
132 let position = message
133 .position
134 .and_then(deserialize_anchor)
135 .ok_or_else(|| anyhow!("invalid position"))?;
136 if !buffer.can_resolve(&position) {
137 Err(anyhow!("cannot resolve position"))?;
138 }
139 Ok(Self {
140 position: position.to_point_utf16(buffer),
141 })
142 }
143
144 fn response_to_proto(
145 range: Option<Range<Anchor>>,
146 _: &mut Project,
147 _: PeerId,
148 buffer_version: &clock::Global,
149 _: &AppContext,
150 ) -> proto::PrepareRenameResponse {
151 proto::PrepareRenameResponse {
152 can_rename: range.is_some(),
153 start: range
154 .as_ref()
155 .map(|range| language::proto::serialize_anchor(&range.start)),
156 end: range
157 .as_ref()
158 .map(|range| language::proto::serialize_anchor(&range.end)),
159 version: buffer_version.into(),
160 }
161 }
162
163 async fn response_from_proto(
164 self,
165 message: proto::PrepareRenameResponse,
166 _: ModelHandle<Project>,
167 buffer: ModelHandle<Buffer>,
168 mut cx: AsyncAppContext,
169 ) -> Result<Option<Range<Anchor>>> {
170 if message.can_rename {
171 buffer
172 .update(&mut cx, |buffer, _| {
173 buffer.wait_for_version(message.version.into())
174 })
175 .await;
176 let start = message.start.and_then(deserialize_anchor);
177 let end = message.end.and_then(deserialize_anchor);
178 Ok(start.zip(end).map(|(start, end)| start..end))
179 } else {
180 Ok(None)
181 }
182 }
183
184 fn buffer_id_from_proto(message: &proto::PrepareRename) -> u64 {
185 message.buffer_id
186 }
187}
188
189#[async_trait(?Send)]
190impl LspCommand for PerformRename {
191 type Response = ProjectTransaction;
192 type LspRequest = lsp::request::Rename;
193 type ProtoRequest = proto::PerformRename;
194
195 fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::RenameParams {
196 lsp::RenameParams {
197 text_document_position: lsp::TextDocumentPositionParams {
198 text_document: lsp::TextDocumentIdentifier {
199 uri: lsp::Url::from_file_path(path).unwrap(),
200 },
201 position: self.position.to_lsp_position(),
202 },
203 new_name: self.new_name.clone(),
204 work_done_progress_params: Default::default(),
205 }
206 }
207
208 async fn response_from_lsp(
209 self,
210 message: Option<lsp::WorkspaceEdit>,
211 project: ModelHandle<Project>,
212 buffer: ModelHandle<Buffer>,
213 mut cx: AsyncAppContext,
214 ) -> Result<ProjectTransaction> {
215 if let Some(edit) = message {
216 let (language_name, language_server) = buffer.read_with(&cx, |buffer, _| {
217 let language = buffer
218 .language()
219 .ok_or_else(|| anyhow!("buffer's language was removed"))?;
220 let language_server = buffer
221 .language_server()
222 .cloned()
223 .ok_or_else(|| anyhow!("buffer's language server was removed"))?;
224 Ok::<_, anyhow::Error>((language.name().to_string(), language_server))
225 })?;
226 Project::deserialize_workspace_edit(
227 project,
228 edit,
229 self.push_to_history,
230 language_name,
231 language_server,
232 &mut cx,
233 )
234 .await
235 } else {
236 Ok(ProjectTransaction::default())
237 }
238 }
239
240 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PerformRename {
241 proto::PerformRename {
242 project_id,
243 buffer_id: buffer.remote_id(),
244 position: Some(language::proto::serialize_anchor(
245 &buffer.anchor_before(self.position),
246 )),
247 new_name: self.new_name.clone(),
248 }
249 }
250
251 fn from_proto(message: proto::PerformRename, _: &mut Project, buffer: &Buffer) -> Result<Self> {
252 let position = message
253 .position
254 .and_then(deserialize_anchor)
255 .ok_or_else(|| anyhow!("invalid position"))?;
256 if !buffer.can_resolve(&position) {
257 Err(anyhow!("cannot resolve position"))?;
258 }
259 Ok(Self {
260 position: position.to_point_utf16(buffer),
261 new_name: message.new_name,
262 push_to_history: false,
263 })
264 }
265
266 fn response_to_proto(
267 response: ProjectTransaction,
268 project: &mut Project,
269 peer_id: PeerId,
270 _: &clock::Global,
271 cx: &AppContext,
272 ) -> proto::PerformRenameResponse {
273 let transaction = project.serialize_project_transaction_for_peer(response, peer_id, cx);
274 proto::PerformRenameResponse {
275 transaction: Some(transaction),
276 }
277 }
278
279 async fn response_from_proto(
280 self,
281 message: proto::PerformRenameResponse,
282 project: ModelHandle<Project>,
283 _: ModelHandle<Buffer>,
284 mut cx: AsyncAppContext,
285 ) -> Result<ProjectTransaction> {
286 let message = message
287 .transaction
288 .ok_or_else(|| anyhow!("missing transaction"))?;
289 project
290 .update(&mut cx, |project, cx| {
291 project.deserialize_project_transaction(message, self.push_to_history, cx)
292 })
293 .await
294 }
295
296 fn buffer_id_from_proto(message: &proto::PerformRename) -> u64 {
297 message.buffer_id
298 }
299}
300
301#[async_trait(?Send)]
302impl LspCommand for GetDefinition {
303 type Response = Vec<Location>;
304 type LspRequest = lsp::request::GotoDefinition;
305 type ProtoRequest = proto::GetDefinition;
306
307 fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::GotoDefinitionParams {
308 lsp::GotoDefinitionParams {
309 text_document_position_params: lsp::TextDocumentPositionParams {
310 text_document: lsp::TextDocumentIdentifier {
311 uri: lsp::Url::from_file_path(path).unwrap(),
312 },
313 position: self.position.to_lsp_position(),
314 },
315 work_done_progress_params: Default::default(),
316 partial_result_params: Default::default(),
317 }
318 }
319
320 async fn response_from_lsp(
321 self,
322 message: Option<lsp::GotoDefinitionResponse>,
323 project: ModelHandle<Project>,
324 buffer: ModelHandle<Buffer>,
325 mut cx: AsyncAppContext,
326 ) -> Result<Vec<Location>> {
327 let mut definitions = Vec::new();
328 let (language, language_server) = buffer
329 .read_with(&cx, |buffer, _| {
330 buffer
331 .language()
332 .cloned()
333 .zip(buffer.language_server().cloned())
334 })
335 .ok_or_else(|| anyhow!("buffer no longer has language server"))?;
336
337 if let Some(message) = message {
338 let mut unresolved_locations = Vec::new();
339 match message {
340 lsp::GotoDefinitionResponse::Scalar(loc) => {
341 unresolved_locations.push((loc.uri, loc.range));
342 }
343 lsp::GotoDefinitionResponse::Array(locs) => {
344 unresolved_locations.extend(locs.into_iter().map(|l| (l.uri, l.range)));
345 }
346 lsp::GotoDefinitionResponse::Link(links) => {
347 unresolved_locations.extend(
348 links
349 .into_iter()
350 .map(|l| (l.target_uri, l.target_selection_range)),
351 );
352 }
353 }
354
355 for (target_uri, target_range) in unresolved_locations {
356 let target_buffer_handle = project
357 .update(&mut cx, |this, cx| {
358 this.open_local_buffer_via_lsp(
359 target_uri,
360 language.name().to_string(),
361 language_server.clone(),
362 cx,
363 )
364 })
365 .await?;
366
367 cx.read(|cx| {
368 let target_buffer = target_buffer_handle.read(cx);
369 let target_start = target_buffer
370 .clip_point_utf16(point_from_lsp(target_range.start), Bias::Left);
371 let target_end = target_buffer
372 .clip_point_utf16(point_from_lsp(target_range.end), Bias::Left);
373 definitions.push(Location {
374 buffer: target_buffer_handle,
375 range: target_buffer.anchor_after(target_start)
376 ..target_buffer.anchor_before(target_end),
377 });
378 });
379 }
380 }
381
382 Ok(definitions)
383 }
384
385 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDefinition {
386 proto::GetDefinition {
387 project_id,
388 buffer_id: buffer.remote_id(),
389 position: Some(language::proto::serialize_anchor(
390 &buffer.anchor_before(self.position),
391 )),
392 }
393 }
394
395 fn from_proto(message: proto::GetDefinition, _: &mut Project, buffer: &Buffer) -> Result<Self> {
396 let position = message
397 .position
398 .and_then(deserialize_anchor)
399 .ok_or_else(|| anyhow!("invalid position"))?;
400 if !buffer.can_resolve(&position) {
401 Err(anyhow!("cannot resolve position"))?;
402 }
403 Ok(Self {
404 position: position.to_point_utf16(buffer),
405 })
406 }
407
408 fn response_to_proto(
409 response: Vec<Location>,
410 project: &mut Project,
411 peer_id: PeerId,
412 _: &clock::Global,
413 cx: &AppContext,
414 ) -> proto::GetDefinitionResponse {
415 let locations = response
416 .into_iter()
417 .map(|definition| {
418 let buffer = project.serialize_buffer_for_peer(&definition.buffer, peer_id, cx);
419 proto::Location {
420 start: Some(serialize_anchor(&definition.range.start)),
421 end: Some(serialize_anchor(&definition.range.end)),
422 buffer: Some(buffer),
423 }
424 })
425 .collect();
426 proto::GetDefinitionResponse { locations }
427 }
428
429 async fn response_from_proto(
430 self,
431 message: proto::GetDefinitionResponse,
432 project: ModelHandle<Project>,
433 _: ModelHandle<Buffer>,
434 mut cx: AsyncAppContext,
435 ) -> Result<Vec<Location>> {
436 let mut locations = Vec::new();
437 for location in message.locations {
438 let buffer = location.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
439 let buffer = project
440 .update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
441 .await?;
442 let start = location
443 .start
444 .and_then(deserialize_anchor)
445 .ok_or_else(|| anyhow!("missing target start"))?;
446 let end = location
447 .end
448 .and_then(deserialize_anchor)
449 .ok_or_else(|| anyhow!("missing target end"))?;
450 locations.push(Location {
451 buffer,
452 range: start..end,
453 })
454 }
455 Ok(locations)
456 }
457
458 fn buffer_id_from_proto(message: &proto::GetDefinition) -> u64 {
459 message.buffer_id
460 }
461}
462
463#[async_trait(?Send)]
464impl LspCommand for GetReferences {
465 type Response = Vec<Location>;
466 type LspRequest = lsp::request::References;
467 type ProtoRequest = proto::GetReferences;
468
469 fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::ReferenceParams {
470 lsp::ReferenceParams {
471 text_document_position: lsp::TextDocumentPositionParams {
472 text_document: lsp::TextDocumentIdentifier {
473 uri: lsp::Url::from_file_path(path).unwrap(),
474 },
475 position: self.position.to_lsp_position(),
476 },
477 work_done_progress_params: Default::default(),
478 partial_result_params: Default::default(),
479 context: lsp::ReferenceContext {
480 include_declaration: true,
481 },
482 }
483 }
484
485 async fn response_from_lsp(
486 self,
487 locations: Option<Vec<lsp::Location>>,
488 project: ModelHandle<Project>,
489 buffer: ModelHandle<Buffer>,
490 mut cx: AsyncAppContext,
491 ) -> Result<Vec<Location>> {
492 let mut references = Vec::new();
493 let (language, language_server) = buffer
494 .read_with(&cx, |buffer, _| {
495 buffer
496 .language()
497 .cloned()
498 .zip(buffer.language_server().cloned())
499 })
500 .ok_or_else(|| anyhow!("buffer no longer has language server"))?;
501
502 if let Some(locations) = locations {
503 for lsp_location in locations {
504 let target_buffer_handle = project
505 .update(&mut cx, |this, cx| {
506 this.open_local_buffer_via_lsp(
507 lsp_location.uri,
508 language.name().to_string(),
509 language_server.clone(),
510 cx,
511 )
512 })
513 .await?;
514
515 cx.read(|cx| {
516 let target_buffer = target_buffer_handle.read(cx);
517 let target_start = target_buffer
518 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
519 let target_end = target_buffer
520 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
521 references.push(Location {
522 buffer: target_buffer_handle,
523 range: target_buffer.anchor_after(target_start)
524 ..target_buffer.anchor_before(target_end),
525 });
526 });
527 }
528 }
529
530 Ok(references)
531 }
532
533 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetReferences {
534 proto::GetReferences {
535 project_id,
536 buffer_id: buffer.remote_id(),
537 position: Some(language::proto::serialize_anchor(
538 &buffer.anchor_before(self.position),
539 )),
540 }
541 }
542
543 fn from_proto(message: proto::GetReferences, _: &mut Project, buffer: &Buffer) -> Result<Self> {
544 let position = message
545 .position
546 .and_then(deserialize_anchor)
547 .ok_or_else(|| anyhow!("invalid position"))?;
548 if !buffer.can_resolve(&position) {
549 Err(anyhow!("cannot resolve position"))?;
550 }
551 Ok(Self {
552 position: position.to_point_utf16(buffer),
553 })
554 }
555
556 fn response_to_proto(
557 response: Vec<Location>,
558 project: &mut Project,
559 peer_id: PeerId,
560 _: &clock::Global,
561 cx: &AppContext,
562 ) -> proto::GetReferencesResponse {
563 let locations = response
564 .into_iter()
565 .map(|definition| {
566 let buffer = project.serialize_buffer_for_peer(&definition.buffer, peer_id, cx);
567 proto::Location {
568 start: Some(serialize_anchor(&definition.range.start)),
569 end: Some(serialize_anchor(&definition.range.end)),
570 buffer: Some(buffer),
571 }
572 })
573 .collect();
574 proto::GetReferencesResponse { locations }
575 }
576
577 async fn response_from_proto(
578 self,
579 message: proto::GetReferencesResponse,
580 project: ModelHandle<Project>,
581 _: ModelHandle<Buffer>,
582 mut cx: AsyncAppContext,
583 ) -> Result<Vec<Location>> {
584 let mut locations = Vec::new();
585 for location in message.locations {
586 let buffer = location.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
587 let target_buffer = project
588 .update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
589 .await?;
590 let start = location
591 .start
592 .and_then(deserialize_anchor)
593 .ok_or_else(|| anyhow!("missing target start"))?;
594 let end = location
595 .end
596 .and_then(deserialize_anchor)
597 .ok_or_else(|| anyhow!("missing target end"))?;
598 locations.push(Location {
599 buffer: target_buffer,
600 range: start..end,
601 })
602 }
603 Ok(locations)
604 }
605
606 fn buffer_id_from_proto(message: &proto::GetReferences) -> u64 {
607 message.buffer_id
608 }
609}
610
611#[async_trait(?Send)]
612impl LspCommand for GetDocumentHighlights {
613 type Response = Vec<DocumentHighlight>;
614 type LspRequest = lsp::request::DocumentHighlightRequest;
615 type ProtoRequest = proto::GetDocumentHighlights;
616
617 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
618 capabilities.document_highlight_provider.is_some()
619 }
620
621 fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::DocumentHighlightParams {
622 lsp::DocumentHighlightParams {
623 text_document_position_params: lsp::TextDocumentPositionParams {
624 text_document: lsp::TextDocumentIdentifier {
625 uri: lsp::Url::from_file_path(path).unwrap(),
626 },
627 position: self.position.to_lsp_position(),
628 },
629 work_done_progress_params: Default::default(),
630 partial_result_params: Default::default(),
631 }
632 }
633
634 async fn response_from_lsp(
635 self,
636 lsp_highlights: Option<Vec<lsp::DocumentHighlight>>,
637 _: ModelHandle<Project>,
638 buffer: ModelHandle<Buffer>,
639 cx: AsyncAppContext,
640 ) -> Result<Vec<DocumentHighlight>> {
641 buffer.read_with(&cx, |buffer, _| {
642 let mut lsp_highlights = lsp_highlights.unwrap_or_default();
643 lsp_highlights.sort_unstable_by_key(|h| (h.range.start, Reverse(h.range.end)));
644 Ok(lsp_highlights
645 .into_iter()
646 .map(|lsp_highlight| {
647 let start = buffer
648 .clip_point_utf16(point_from_lsp(lsp_highlight.range.start), Bias::Left);
649 let end = buffer
650 .clip_point_utf16(point_from_lsp(lsp_highlight.range.end), Bias::Left);
651 DocumentHighlight {
652 range: buffer.anchor_after(start)..buffer.anchor_before(end),
653 kind: lsp_highlight
654 .kind
655 .unwrap_or(lsp::DocumentHighlightKind::READ),
656 }
657 })
658 .collect())
659 })
660 }
661
662 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDocumentHighlights {
663 proto::GetDocumentHighlights {
664 project_id,
665 buffer_id: buffer.remote_id(),
666 position: Some(language::proto::serialize_anchor(
667 &buffer.anchor_before(self.position),
668 )),
669 }
670 }
671
672 fn from_proto(
673 message: proto::GetDocumentHighlights,
674 _: &mut Project,
675 buffer: &Buffer,
676 ) -> Result<Self> {
677 let position = message
678 .position
679 .and_then(deserialize_anchor)
680 .ok_or_else(|| anyhow!("invalid position"))?;
681 if !buffer.can_resolve(&position) {
682 Err(anyhow!("cannot resolve position"))?;
683 }
684 Ok(Self {
685 position: position.to_point_utf16(buffer),
686 })
687 }
688
689 fn response_to_proto(
690 response: Vec<DocumentHighlight>,
691 _: &mut Project,
692 _: PeerId,
693 _: &clock::Global,
694 _: &AppContext,
695 ) -> proto::GetDocumentHighlightsResponse {
696 let highlights = response
697 .into_iter()
698 .map(|highlight| proto::DocumentHighlight {
699 start: Some(serialize_anchor(&highlight.range.start)),
700 end: Some(serialize_anchor(&highlight.range.end)),
701 kind: match highlight.kind {
702 DocumentHighlightKind::TEXT => proto::document_highlight::Kind::Text.into(),
703 DocumentHighlightKind::WRITE => proto::document_highlight::Kind::Write.into(),
704 DocumentHighlightKind::READ => proto::document_highlight::Kind::Read.into(),
705 _ => proto::document_highlight::Kind::Text.into(),
706 },
707 })
708 .collect();
709 proto::GetDocumentHighlightsResponse { highlights }
710 }
711
712 async fn response_from_proto(
713 self,
714 message: proto::GetDocumentHighlightsResponse,
715 _: ModelHandle<Project>,
716 _: ModelHandle<Buffer>,
717 _: AsyncAppContext,
718 ) -> Result<Vec<DocumentHighlight>> {
719 Ok(message
720 .highlights
721 .into_iter()
722 .map(|highlight| {
723 let start = highlight
724 .start
725 .and_then(deserialize_anchor)
726 .ok_or_else(|| anyhow!("missing target start"))?;
727 let end = highlight
728 .end
729 .and_then(deserialize_anchor)
730 .ok_or_else(|| anyhow!("missing target end"))?;
731 let kind = match proto::document_highlight::Kind::from_i32(highlight.kind) {
732 Some(proto::document_highlight::Kind::Text) => DocumentHighlightKind::TEXT,
733 Some(proto::document_highlight::Kind::Read) => DocumentHighlightKind::READ,
734 Some(proto::document_highlight::Kind::Write) => DocumentHighlightKind::WRITE,
735 None => DocumentHighlightKind::TEXT,
736 };
737 Ok(DocumentHighlight {
738 range: start..end,
739 kind,
740 })
741 })
742 .collect::<Result<Vec<_>>>()?)
743 }
744
745 fn buffer_id_from_proto(message: &proto::GetDocumentHighlights) -> u64 {
746 message.buffer_id
747 }
748}