Detailed changes
@@ -442,6 +442,7 @@ pub struct Editor {
next_completion_id: CompletionId,
available_code_actions: Option<(ModelHandle<Buffer>, Arc<[CodeAction]>)>,
code_actions_task: Option<Task<()>>,
+ document_highlights_task: Option<Task<()>>,
pending_rename: Option<RenameState>,
}
@@ -903,6 +904,7 @@ impl Editor {
next_completion_id: 0,
available_code_actions: Default::default(),
code_actions_task: Default::default(),
+ document_highlights_task: Default::default(),
pending_rename: Default::default(),
};
this.end_selection(cx);
@@ -2295,6 +2297,76 @@ impl Editor {
None
}
+ fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
+ let project = self.project.as_ref()?;
+ let buffer = self.buffer.read(cx);
+ let newest_selection = self.newest_anchor_selection().clone();
+ let cursor_position = newest_selection.head();
+ let (cursor_buffer, cursor_buffer_position) =
+ buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
+ let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
+ if cursor_buffer != tail_buffer {
+ return None;
+ }
+
+ let highlights = project.update(cx, |project, cx| {
+ project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
+ });
+
+ enum DocumentHighlightRead {}
+ enum DocumentHighlightWrite {}
+
+ self.document_highlights_task = Some(cx.spawn_weak(|this, mut cx| async move {
+ let highlights = highlights.log_err().await;
+ if let Some((this, highlights)) = this.upgrade(&cx).zip(highlights) {
+ this.update(&mut cx, |this, cx| {
+ let buffer_id = cursor_position.buffer_id;
+ let excerpt_id = cursor_position.excerpt_id.clone();
+ let settings = (this.build_settings)(cx);
+ let buffer = this.buffer.read(cx);
+ if !buffer
+ .text_anchor_for_position(cursor_position, cx)
+ .map_or(false, |(buffer, _)| buffer == cursor_buffer)
+ {
+ return;
+ }
+
+ let mut write_ranges = Vec::new();
+ let mut read_ranges = Vec::new();
+ for highlight in highlights {
+ let range = Anchor {
+ buffer_id,
+ excerpt_id: excerpt_id.clone(),
+ text_anchor: highlight.range.start,
+ }..Anchor {
+ buffer_id,
+ excerpt_id: excerpt_id.clone(),
+ text_anchor: highlight.range.end,
+ };
+ if highlight.kind == lsp::DocumentHighlightKind::WRITE {
+ write_ranges.push(range);
+ } else {
+ read_ranges.push(range);
+ }
+ }
+
+ this.highlight_ranges::<DocumentHighlightRead>(
+ read_ranges,
+ settings.style.document_highlight_read_background,
+ cx,
+ );
+ this.highlight_ranges::<DocumentHighlightWrite>(
+ write_ranges,
+ settings.style.document_highlight_write_background,
+ cx,
+ );
+ cx.notify();
+ });
+ }
+ }));
+ None
+ }
+
pub fn render_code_actions_indicator(&self, cx: &mut ViewContext<Self>) -> Option<ElementBox> {
if self.available_code_actions.is_some() {
enum Tag {}
@@ -4840,6 +4912,7 @@ impl Editor {
self.available_code_actions.take();
}
self.refresh_code_actions(cx);
+ self.refresh_document_highlights(cx);
self.pause_cursor_blinking(cx);
cx.emit(Event::SelectionsChanged);
@@ -5294,6 +5367,8 @@ impl EditorSettings {
highlighted_line_background: Default::default(),
diff_background_deleted: Default::default(),
diff_background_inserted: Default::default(),
+ document_highlight_read_background: Default::default(),
+ document_highlight_write_background: Default::default(),
line_number: Default::default(),
line_number_active: Default::default(),
selection: Default::default(),
@@ -1,4 +1,4 @@
-use crate::{Location, Project, ProjectTransaction};
+use crate::{DocumentHighlight, Location, Project, ProjectTransaction};
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use client::{proto, PeerId};
@@ -8,7 +8,8 @@ use language::{
proto::{deserialize_anchor, serialize_anchor},
range_from_lsp, Anchor, Bias, Buffer, PointUtf16, ToLspPosition, ToPointUtf16,
};
-use std::{ops::Range, path::Path};
+use lsp::DocumentHighlightKind;
+use std::{cmp::Reverse, ops::Range, path::Path};
#[async_trait(?Send)]
pub(crate) trait LspCommand: 'static + Sized {
@@ -70,6 +71,10 @@ pub(crate) struct GetReferences {
pub position: PointUtf16,
}
+pub(crate) struct GetDocumentHighlights {
+ pub position: PointUtf16,
+}
+
#[async_trait(?Send)]
impl LspCommand for PrepareRename {
type Response = Option<Range<Anchor>>;
@@ -598,3 +603,138 @@ impl LspCommand for GetReferences {
message.buffer_id
}
}
+
+#[async_trait(?Send)]
+impl LspCommand for GetDocumentHighlights {
+ type Response = Vec<DocumentHighlight>;
+ type LspRequest = lsp::request::DocumentHighlightRequest;
+ type ProtoRequest = proto::GetDocumentHighlights;
+
+ fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::DocumentHighlightParams {
+ lsp::DocumentHighlightParams {
+ text_document_position_params: lsp::TextDocumentPositionParams {
+ text_document: lsp::TextDocumentIdentifier {
+ uri: lsp::Url::from_file_path(path).unwrap(),
+ },
+ position: self.position.to_lsp_position(),
+ },
+ work_done_progress_params: Default::default(),
+ partial_result_params: Default::default(),
+ }
+ }
+
+ async fn response_from_lsp(
+ self,
+ lsp_highlights: Option<Vec<lsp::DocumentHighlight>>,
+ _: ModelHandle<Project>,
+ buffer: ModelHandle<Buffer>,
+ cx: AsyncAppContext,
+ ) -> Result<Vec<DocumentHighlight>> {
+ buffer.read_with(&cx, |buffer, _| {
+ let mut lsp_highlights = lsp_highlights.unwrap_or_default();
+ lsp_highlights.sort_unstable_by_key(|h| (h.range.start, Reverse(h.range.end)));
+ Ok(lsp_highlights
+ .into_iter()
+ .map(|lsp_highlight| {
+ let start = buffer
+ .clip_point_utf16(point_from_lsp(lsp_highlight.range.start), Bias::Left);
+ let end = buffer
+ .clip_point_utf16(point_from_lsp(lsp_highlight.range.end), Bias::Left);
+ DocumentHighlight {
+ range: buffer.anchor_after(start)..buffer.anchor_before(end),
+ kind: lsp_highlight
+ .kind
+ .unwrap_or(lsp::DocumentHighlightKind::READ),
+ }
+ })
+ .collect())
+ })
+ }
+
+ fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDocumentHighlights {
+ proto::GetDocumentHighlights {
+ project_id,
+ buffer_id: buffer.remote_id(),
+ position: Some(language::proto::serialize_anchor(
+ &buffer.anchor_before(self.position),
+ )),
+ }
+ }
+
+ fn from_proto(
+ message: proto::GetDocumentHighlights,
+ _: &mut Project,
+ buffer: &Buffer,
+ ) -> Result<Self> {
+ let position = message
+ .position
+ .and_then(deserialize_anchor)
+ .ok_or_else(|| anyhow!("invalid position"))?;
+ if !buffer.can_resolve(&position) {
+ Err(anyhow!("cannot resolve position"))?;
+ }
+ Ok(Self {
+ position: position.to_point_utf16(buffer),
+ })
+ }
+
+ fn response_to_proto(
+ response: Vec<DocumentHighlight>,
+ _: &mut Project,
+ _: PeerId,
+ _: &clock::Global,
+ _: &AppContext,
+ ) -> proto::GetDocumentHighlightsResponse {
+ let highlights = response
+ .into_iter()
+ .map(|highlight| proto::DocumentHighlight {
+ start: Some(serialize_anchor(&highlight.range.start)),
+ end: Some(serialize_anchor(&highlight.range.end)),
+ kind: match highlight.kind {
+ DocumentHighlightKind::TEXT => proto::document_highlight::Kind::Text.into(),
+ DocumentHighlightKind::WRITE => proto::document_highlight::Kind::Write.into(),
+ DocumentHighlightKind::READ => proto::document_highlight::Kind::Read.into(),
+ _ => proto::document_highlight::Kind::Text.into(),
+ },
+ })
+ .collect();
+ proto::GetDocumentHighlightsResponse { highlights }
+ }
+
+ async fn response_from_proto(
+ self,
+ message: proto::GetDocumentHighlightsResponse,
+ _: ModelHandle<Project>,
+ _: ModelHandle<Buffer>,
+ _: AsyncAppContext,
+ ) -> Result<Vec<DocumentHighlight>> {
+ Ok(message
+ .highlights
+ .into_iter()
+ .map(|highlight| {
+ let start = highlight
+ .start
+ .and_then(deserialize_anchor)
+ .ok_or_else(|| anyhow!("missing target start"))?;
+ let end = highlight
+ .end
+ .and_then(deserialize_anchor)
+ .ok_or_else(|| anyhow!("missing target end"))?;
+ let kind = match proto::document_highlight::Kind::from_i32(highlight.kind) {
+ Some(proto::document_highlight::Kind::Text) => DocumentHighlightKind::TEXT,
+ Some(proto::document_highlight::Kind::Read) => DocumentHighlightKind::READ,
+ Some(proto::document_highlight::Kind::Write) => DocumentHighlightKind::WRITE,
+ None => DocumentHighlightKind::TEXT,
+ };
+ Ok(DocumentHighlight {
+ range: start..end,
+ kind,
+ })
+ })
+ .collect::<Result<Vec<_>>>()?)
+ }
+
+ fn buffer_id_from_proto(message: &proto::GetDocumentHighlights) -> u64 {
+ message.buffer_id
+ }
+}
@@ -18,7 +18,7 @@ use language::{
Diagnostic, DiagnosticEntry, File as _, Language, LanguageRegistry, Operation, PointUtf16,
ToLspPosition, ToOffset, ToPointUtf16, Transaction,
};
-use lsp::{DiagnosticSeverity, LanguageServer};
+use lsp::{DiagnosticSeverity, DocumentHighlightKind, LanguageServer};
use lsp_command::*;
use postage::{broadcast, prelude::Stream, sink::Sink, watch};
use rand::prelude::*;
@@ -123,6 +123,12 @@ pub struct Location {
pub range: Range<language::Anchor>,
}
+#[derive(Debug)]
+pub struct DocumentHighlight {
+ pub range: Range<language::Anchor>,
+ pub kind: DocumentHighlightKind,
+}
+
#[derive(Clone, Debug)]
pub struct Symbol {
pub source_worktree_id: WorktreeId,
@@ -202,6 +208,7 @@ impl Project {
client.add_entity_request_handler(Self::handle_get_code_actions);
client.add_entity_request_handler(Self::handle_get_completions);
client.add_entity_request_handler(Self::handle_lsp_command::<GetDefinition>);
+ client.add_entity_request_handler(Self::handle_lsp_command::<GetDocumentHighlights>);
client.add_entity_request_handler(Self::handle_lsp_command::<GetReferences>);
client.add_entity_request_handler(Self::handle_lsp_command::<PrepareRename>);
client.add_entity_request_handler(Self::handle_lsp_command::<PerformRename>);
@@ -1269,6 +1276,16 @@ impl Project {
self.request_lsp(buffer.clone(), GetReferences { position }, cx)
}
+ pub fn document_highlights<T: ToPointUtf16>(
+ &self,
+ buffer: &ModelHandle<Buffer>,
+ position: T,
+ cx: &mut ModelContext<Self>,
+ ) -> Task<Result<Vec<DocumentHighlight>>> {
+ let position = position.to_point_utf16(buffer.read(cx));
+ self.request_lsp(buffer.clone(), GetDocumentHighlights { position }, cx)
+ }
+
pub fn symbols(&self, query: &str, cx: &mut ModelContext<Self>) -> Task<Result<Vec<Symbol>>> {
if self.is_local() {
let mut language_servers = HashMap::default();
@@ -25,57 +25,59 @@ message Envelope {
GetDefinitionResponse get_definition_response = 19;
GetReferences get_references = 20;
GetReferencesResponse get_references_response = 21;
- GetProjectSymbols get_project_symbols = 22;
- GetProjectSymbolsResponse get_project_symbols_response = 23;
- OpenBufferForSymbol open_buffer_for_symbol = 24;
- OpenBufferForSymbolResponse open_buffer_for_symbol_response = 25;
-
- RegisterWorktree register_worktree = 26;
- UnregisterWorktree unregister_worktree = 27;
- ShareWorktree share_worktree = 28;
- UpdateWorktree update_worktree = 29;
- UpdateDiagnosticSummary update_diagnostic_summary = 30;
- DiskBasedDiagnosticsUpdating disk_based_diagnostics_updating = 31;
- DiskBasedDiagnosticsUpdated disk_based_diagnostics_updated = 32;
-
- OpenBuffer open_buffer = 33;
- OpenBufferResponse open_buffer_response = 34;
- CloseBuffer close_buffer = 35;
- UpdateBuffer update_buffer = 36;
- UpdateBufferFile update_buffer_file = 37;
- SaveBuffer save_buffer = 38;
- BufferSaved buffer_saved = 39;
- BufferReloaded buffer_reloaded = 40;
- FormatBuffers format_buffers = 41;
- FormatBuffersResponse format_buffers_response = 42;
- GetCompletions get_completions = 43;
- GetCompletionsResponse get_completions_response = 44;
- ApplyCompletionAdditionalEdits apply_completion_additional_edits = 45;
- ApplyCompletionAdditionalEditsResponse apply_completion_additional_edits_response = 46;
- GetCodeActions get_code_actions = 47;
- GetCodeActionsResponse get_code_actions_response = 48;
- ApplyCodeAction apply_code_action = 49;
- ApplyCodeActionResponse apply_code_action_response = 50;
- PrepareRename prepare_rename = 51;
- PrepareRenameResponse prepare_rename_response = 52;
- PerformRename perform_rename = 53;
- PerformRenameResponse perform_rename_response = 54;
-
- GetChannels get_channels = 55;
- GetChannelsResponse get_channels_response = 56;
- JoinChannel join_channel = 57;
- JoinChannelResponse join_channel_response = 58;
- LeaveChannel leave_channel = 59;
- SendChannelMessage send_channel_message = 60;
- SendChannelMessageResponse send_channel_message_response = 61;
- ChannelMessageSent channel_message_sent = 62;
- GetChannelMessages get_channel_messages = 63;
- GetChannelMessagesResponse get_channel_messages_response = 64;
-
- UpdateContacts update_contacts = 65;
-
- GetUsers get_users = 66;
- GetUsersResponse get_users_response = 67;
+ GetDocumentHighlights get_document_highlights = 22;
+ GetDocumentHighlightsResponse get_document_highlights_response = 23;
+ GetProjectSymbols get_project_symbols = 24;
+ GetProjectSymbolsResponse get_project_symbols_response = 25;
+ OpenBufferForSymbol open_buffer_for_symbol = 26;
+ OpenBufferForSymbolResponse open_buffer_for_symbol_response = 27;
+
+ RegisterWorktree register_worktree = 28;
+ UnregisterWorktree unregister_worktree = 29;
+ ShareWorktree share_worktree = 30;
+ UpdateWorktree update_worktree = 31;
+ UpdateDiagnosticSummary update_diagnostic_summary = 32;
+ DiskBasedDiagnosticsUpdating disk_based_diagnostics_updating = 33;
+ DiskBasedDiagnosticsUpdated disk_based_diagnostics_updated = 34;
+
+ OpenBuffer open_buffer = 35;
+ OpenBufferResponse open_buffer_response = 36;
+ CloseBuffer close_buffer = 37;
+ UpdateBuffer update_buffer = 38;
+ UpdateBufferFile update_buffer_file = 39;
+ SaveBuffer save_buffer = 40;
+ BufferSaved buffer_saved = 41;
+ BufferReloaded buffer_reloaded = 42;
+ FormatBuffers format_buffers = 43;
+ FormatBuffersResponse format_buffers_response = 44;
+ GetCompletions get_completions = 45;
+ GetCompletionsResponse get_completions_response = 46;
+ ApplyCompletionAdditionalEdits apply_completion_additional_edits = 47;
+ ApplyCompletionAdditionalEditsResponse apply_completion_additional_edits_response = 48;
+ GetCodeActions get_code_actions = 49;
+ GetCodeActionsResponse get_code_actions_response = 50;
+ ApplyCodeAction apply_code_action = 51;
+ ApplyCodeActionResponse apply_code_action_response = 52;
+ PrepareRename prepare_rename = 53;
+ PrepareRenameResponse prepare_rename_response = 54;
+ PerformRename perform_rename = 55;
+ PerformRenameResponse perform_rename_response = 56;
+
+ GetChannels get_channels = 57;
+ GetChannelsResponse get_channels_response = 58;
+ JoinChannel join_channel = 59;
+ JoinChannelResponse join_channel_response = 60;
+ LeaveChannel leave_channel = 61;
+ SendChannelMessage send_channel_message = 62;
+ SendChannelMessageResponse send_channel_message_response = 63;
+ ChannelMessageSent channel_message_sent = 64;
+ GetChannelMessages get_channel_messages = 65;
+ GetChannelMessagesResponse get_channel_messages_response = 66;
+
+ UpdateContacts update_contacts = 67;
+
+ GetUsers get_users = 68;
+ GetUsersResponse get_users_response = 69;
}
}
@@ -181,12 +183,34 @@ message GetReferencesResponse {
repeated Location locations = 1;
}
+message GetDocumentHighlights {
+ uint64 project_id = 1;
+ uint64 buffer_id = 2;
+ Anchor position = 3;
+ }
+
+message GetDocumentHighlightsResponse {
+ repeated DocumentHighlight highlights = 1;
+}
+
message Location {
Buffer buffer = 1;
Anchor start = 2;
Anchor end = 3;
}
+message DocumentHighlight {
+ Kind kind = 1;
+ Anchor start = 2;
+ Anchor end = 3;
+
+ enum Kind {
+ Text = 0;
+ Read = 1;
+ Write = 2;
+ }
+}
+
message GetProjectSymbols {
uint64 project_id = 1;
string query = 2;
@@ -157,6 +157,8 @@ messages!(
(GetCompletionsResponse, Foreground),
(GetDefinition, Foreground),
(GetDefinitionResponse, Foreground),
+ (GetDocumentHighlights, Foreground),
+ (GetDocumentHighlightsResponse, Foreground),
(GetReferences, Foreground),
(GetReferencesResponse, Foreground),
(GetProjectSymbols, Background),
@@ -210,6 +212,7 @@ request_messages!(
(GetCodeActions, GetCodeActionsResponse),
(GetCompletions, GetCompletionsResponse),
(GetDefinition, GetDefinitionResponse),
+ (GetDocumentHighlights, GetDocumentHighlightsResponse),
(GetReferences, GetReferencesResponse),
(GetProjectSymbols, GetProjectSymbolsResponse),
(GetUsers, GetUsersResponse),
@@ -245,6 +248,7 @@ entity_messages!(
GetCodeActions,
GetCompletions,
GetDefinition,
+ GetDocumentHighlights,
GetReferences,
GetProjectSymbols,
JoinProject,
@@ -80,6 +80,7 @@ impl Server {
.add_message_handler(Server::disk_based_diagnostics_updated)
.add_request_handler(Server::get_definition)
.add_request_handler(Server::get_references)
+ .add_request_handler(Server::get_document_highlights)
.add_request_handler(Server::get_project_symbols)
.add_request_handler(Server::open_buffer_for_symbol)
.add_request_handler(Server::open_buffer)
@@ -604,6 +605,20 @@ impl Server {
.await?)
}
+ async fn get_document_highlights(
+ self: Arc<Server>,
+ request: TypedEnvelope<proto::GetDocumentHighlights>,
+ ) -> tide::Result<proto::GetDocumentHighlightsResponse> {
+ let host_connection_id = self
+ .state()
+ .read_project(request.payload.project_id, request.sender_id)?
+ .host_connection_id;
+ Ok(self
+ .peer
+ .forward_request(request.sender_id, host_connection_id, request.payload)
+ .await?)
+ }
+
async fn get_project_symbols(
self: Arc<Server>,
request: TypedEnvelope<proto::GetProjectSymbols>,
@@ -2855,6 +2870,148 @@ mod tests {
});
}
+ #[gpui::test(iterations = 10)]
+ async fn test_document_highlights(mut cx_a: TestAppContext, mut cx_b: TestAppContext) {
+ cx_a.foreground().forbid_parking();
+ let lang_registry = Arc::new(LanguageRegistry::new());
+ let fs = FakeFs::new(cx_a.background());
+ fs.insert_tree(
+ "/root-1",
+ json!({
+ ".zed.toml": r#"collaborators = ["user_b"]"#,
+ "main.rs": "fn double(number: i32) -> i32 { number + number }",
+ }),
+ )
+ .await;
+
+ // Set up a fake language server.
+ let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake();
+ lang_registry.add(Arc::new(Language::new(
+ LanguageConfig {
+ name: "Rust".into(),
+ path_suffixes: vec!["rs".to_string()],
+ language_server: Some(language_server_config),
+ ..Default::default()
+ },
+ Some(tree_sitter_rust::language()),
+ )));
+
+ // Connect to a server as 2 clients.
+ let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
+ let client_a = server.create_client(&mut cx_a, "user_a").await;
+ let client_b = server.create_client(&mut cx_b, "user_b").await;
+
+ // Share a project as client A
+ let project_a = cx_a.update(|cx| {
+ Project::local(
+ client_a.clone(),
+ client_a.user_store.clone(),
+ lang_registry.clone(),
+ fs.clone(),
+ cx,
+ )
+ });
+ let (worktree_a, _) = project_a
+ .update(&mut cx_a, |p, cx| {
+ p.find_or_create_local_worktree("/root-1", false, cx)
+ })
+ .await
+ .unwrap();
+ worktree_a
+ .read_with(&cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
+ .await;
+ let project_id = project_a.update(&mut cx_a, |p, _| p.next_remote_id()).await;
+ let worktree_id = worktree_a.read_with(&cx_a, |tree, _| tree.id());
+ project_a
+ .update(&mut cx_a, |p, cx| p.share(cx))
+ .await
+ .unwrap();
+
+ // Join the worktree as client B.
+ let project_b = Project::remote(
+ project_id,
+ client_b.clone(),
+ client_b.user_store.clone(),
+ lang_registry.clone(),
+ fs.clone(),
+ &mut cx_b.to_async(),
+ )
+ .await
+ .unwrap();
+
+ // Open the file on client B.
+ let buffer_b = cx_b
+ .background()
+ .spawn(project_b.update(&mut cx_b, |p, cx| {
+ p.open_buffer((worktree_id, "main.rs"), cx)
+ }))
+ .await
+ .unwrap();
+
+ // Request document highlights as the guest.
+ let highlights =
+ project_b.update(&mut cx_b, |p, cx| p.document_highlights(&buffer_b, 34, cx));
+
+ let mut fake_language_server = fake_language_servers.next().await.unwrap();
+ fake_language_server.handle_request::<lsp::request::DocumentHighlightRequest, _>(
+ |params| {
+ assert_eq!(
+ params
+ .text_document_position_params
+ .text_document
+ .uri
+ .as_str(),
+ "file:///root-1/main.rs"
+ );
+ assert_eq!(
+ params.text_document_position_params.position,
+ lsp::Position::new(0, 34)
+ );
+ Some(vec![
+ lsp::DocumentHighlight {
+ kind: Some(lsp::DocumentHighlightKind::WRITE),
+ range: lsp::Range::new(
+ lsp::Position::new(0, 10),
+ lsp::Position::new(0, 16),
+ ),
+ },
+ lsp::DocumentHighlight {
+ kind: Some(lsp::DocumentHighlightKind::READ),
+ range: lsp::Range::new(
+ lsp::Position::new(0, 32),
+ lsp::Position::new(0, 38),
+ ),
+ },
+ lsp::DocumentHighlight {
+ kind: Some(lsp::DocumentHighlightKind::READ),
+ range: lsp::Range::new(
+ lsp::Position::new(0, 41),
+ lsp::Position::new(0, 47),
+ ),
+ },
+ ])
+ },
+ );
+
+ let highlights = highlights.await.unwrap();
+ buffer_b.read_with(&cx_b, |buffer, _| {
+ let snapshot = buffer.snapshot();
+
+ let highlights = highlights
+ .into_iter()
+ .map(|highlight| (highlight.kind, highlight.range.to_offset(&snapshot)))
+ .collect::<Vec<_>>();
+ assert_eq!(
+ highlights,
+ &[
+ (lsp::DocumentHighlightKind::WRITE, 10..16),
+ (lsp::DocumentHighlightKind::READ, 32..38),
+ (lsp::DocumentHighlightKind::READ, 41..47)
+ ]
+ )
+ });
+ }
+
#[gpui::test(iterations = 10)]
async fn test_project_symbols(mut cx_a: TestAppContext, mut cx_b: TestAppContext) {
cx_a.foreground().forbid_parking();
@@ -279,6 +279,8 @@ pub struct EditorStyle {
pub gutter_padding_factor: f32,
pub active_line_background: Color,
pub highlighted_line_background: Color,
+ pub document_highlight_read_background: Color,
+ pub document_highlight_write_background: Color,
pub diff_background_deleted: Color,
pub diff_background_inserted: Color,
pub line_number: Color,
@@ -386,6 +388,8 @@ impl InputEditorStyle {
gutter_padding_factor: Default::default(),
active_line_background: Default::default(),
highlighted_line_background: Default::default(),
+ document_highlight_read_background: Default::default(),
+ document_highlight_write_background: Default::default(),
diff_background_deleted: Default::default(),
diff_background_inserted: Default::default(),
line_number: Default::default(),
@@ -249,6 +249,8 @@ gutter_background = "$surface.1"
gutter_padding_factor = 2.5
active_line_background = "$state.active_line"
highlighted_line_background = "$state.highlighted_line"
+document_highlight_read_background = "#99999920"
+document_highlight_write_background = "#99999916"
diff_background_deleted = "$state.deleted_line"
diff_background_inserted = "$state.inserted_line"
line_number = "$text.2.color"