@@ -4821,7 +4821,7 @@ async fn test_definition(
);
let definitions_1 = project_b
- .update(cx_b, |p, cx| p.definition(&buffer_b, 23, cx))
+ .update(cx_b, |p, cx| p.definitions(&buffer_b, 23, cx))
.await
.unwrap();
cx_b.read(|cx| {
@@ -4852,7 +4852,7 @@ async fn test_definition(
);
let definitions_2 = project_b
- .update(cx_b, |p, cx| p.definition(&buffer_b, 33, cx))
+ .update(cx_b, |p, cx| p.definitions(&buffer_b, 33, cx))
.await
.unwrap();
cx_b.read(|cx| {
@@ -4889,7 +4889,7 @@ async fn test_definition(
);
let type_definitions = project_b
- .update(cx_b, |p, cx| p.type_definition(&buffer_b, 7, cx))
+ .update(cx_b, |p, cx| p.type_definitions(&buffer_b, 7, cx))
.await
.unwrap();
cx_b.read(|cx| {
@@ -5057,7 +5057,7 @@ async fn test_references(
lsp_response_tx
.unbounded_send(Err(anyhow!("can't find references")))
.unwrap();
- references.await.unwrap_err();
+ assert_eq!(references.await.unwrap(), []);
// User is informed that the request is no longer pending.
executor.run_until_parked();
@@ -5641,7 +5641,7 @@ async fn test_open_buffer_while_getting_definition_pointing_to_it(
let definitions;
let buffer_b2;
if rng.r#gen() {
- definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
+ definitions = project_b.update(cx_b, |p, cx| p.definitions(&buffer_b1, 23, cx));
(buffer_b2, _) = project_b
.update(cx_b, |p, cx| {
p.open_buffer_with_lsp((worktree_id, "b.rs"), cx)
@@ -5655,7 +5655,7 @@ async fn test_open_buffer_while_getting_definition_pointing_to_it(
})
.await
.unwrap();
- definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
+ definitions = project_b.update(cx_b, |p, cx| p.definitions(&buffer_b1, 23, cx));
}
let definitions = definitions.await.unwrap();
@@ -4,8 +4,9 @@ pub mod rust_analyzer_ext;
use crate::{
CodeAction, ColorPresentation, Completion, CompletionResponse, CompletionSource,
- CoreCompletion, DocumentColor, Hover, InlayHint, LspAction, LspPullDiagnostics, ProjectItem,
- ProjectPath, ProjectTransaction, PulledDiagnostics, ResolveState, Symbol, ToolchainStore,
+ CoreCompletion, DocumentColor, Hover, InlayHint, LocationLink, LspAction, LspPullDiagnostics,
+ ProjectItem, ProjectPath, ProjectTransaction, PulledDiagnostics, ResolveState, Symbol,
+ ToolchainStore,
buffer_store::{BufferStore, BufferStoreEvent},
environment::ProjectEnvironment,
lsp_command::{self, *},
@@ -3660,12 +3661,8 @@ impl LspStore {
client.add_entity_request_handler(Self::handle_lsp_command::<GetCodeActions>);
client.add_entity_request_handler(Self::handle_lsp_command::<GetCompletions>);
client.add_entity_request_handler(Self::handle_lsp_command::<GetHover>);
- client.add_entity_request_handler(Self::handle_lsp_command::<GetDefinition>);
- client.add_entity_request_handler(Self::handle_lsp_command::<GetDeclaration>);
- client.add_entity_request_handler(Self::handle_lsp_command::<GetTypeDefinition>);
client.add_entity_request_handler(Self::handle_lsp_command::<GetDocumentHighlights>);
client.add_entity_request_handler(Self::handle_lsp_command::<GetDocumentSymbols>);
- 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>);
client.add_entity_request_handler(Self::handle_lsp_command::<LinkedEditingRange>);
@@ -5257,6 +5254,371 @@ impl LspStore {
})
}
+ pub fn definitions(
+ &mut self,
+ buffer_handle: &Entity<Buffer>,
+ position: PointUtf16,
+ cx: &mut Context<Self>,
+ ) -> Task<Result<Vec<LocationLink>>> {
+ if let Some((upstream_client, project_id)) = self.upstream_client() {
+ let request_task = upstream_client.request(proto::MultiLspQuery {
+ buffer_id: buffer_handle.read(cx).remote_id().into(),
+ version: serialize_version(&buffer_handle.read(cx).version()),
+ project_id,
+ strategy: Some(proto::multi_lsp_query::Strategy::All(
+ proto::AllLanguageServers {},
+ )),
+ request: Some(proto::multi_lsp_query::Request::GetDefinition(
+ GetDefinitions { position }.to_proto(project_id, buffer_handle.read(cx)),
+ )),
+ });
+ let buffer = buffer_handle.clone();
+ cx.spawn(async move |weak_project, cx| {
+ let Some(project) = weak_project.upgrade() else {
+ return Ok(Vec::new());
+ };
+ let responses = request_task.await?.responses;
+ let actions = join_all(
+ responses
+ .into_iter()
+ .filter_map(|lsp_response| match lsp_response.response? {
+ proto::lsp_response::Response::GetDefinitionResponse(response) => {
+ Some(response)
+ }
+ unexpected => {
+ debug_panic!("Unexpected response: {unexpected:?}");
+ None
+ }
+ })
+ .map(|definitions_response| {
+ GetDefinitions { position }.response_from_proto(
+ definitions_response,
+ project.clone(),
+ buffer.clone(),
+ cx.clone(),
+ )
+ }),
+ )
+ .await;
+
+ Ok(actions
+ .into_iter()
+ .collect::<Result<Vec<Vec<_>>>>()?
+ .into_iter()
+ .flatten()
+ .dedup()
+ .collect())
+ })
+ } else {
+ let definitions_task = self.request_multiple_lsp_locally(
+ buffer_handle,
+ Some(position),
+ GetDefinitions { position },
+ cx,
+ );
+ cx.spawn(async move |_, _| {
+ Ok(definitions_task
+ .await
+ .into_iter()
+ .flat_map(|(_, definitions)| definitions)
+ .dedup()
+ .collect())
+ })
+ }
+ }
+
+ pub fn declarations(
+ &mut self,
+ buffer_handle: &Entity<Buffer>,
+ position: PointUtf16,
+ cx: &mut Context<Self>,
+ ) -> Task<Result<Vec<LocationLink>>> {
+ if let Some((upstream_client, project_id)) = self.upstream_client() {
+ let request_task = upstream_client.request(proto::MultiLspQuery {
+ buffer_id: buffer_handle.read(cx).remote_id().into(),
+ version: serialize_version(&buffer_handle.read(cx).version()),
+ project_id,
+ strategy: Some(proto::multi_lsp_query::Strategy::All(
+ proto::AllLanguageServers {},
+ )),
+ request: Some(proto::multi_lsp_query::Request::GetDeclaration(
+ GetDeclarations { position }.to_proto(project_id, buffer_handle.read(cx)),
+ )),
+ });
+ let buffer = buffer_handle.clone();
+ cx.spawn(async move |weak_project, cx| {
+ let Some(project) = weak_project.upgrade() else {
+ return Ok(Vec::new());
+ };
+ let responses = request_task.await?.responses;
+ let actions = join_all(
+ responses
+ .into_iter()
+ .filter_map(|lsp_response| match lsp_response.response? {
+ proto::lsp_response::Response::GetDeclarationResponse(response) => {
+ Some(response)
+ }
+ unexpected => {
+ debug_panic!("Unexpected response: {unexpected:?}");
+ None
+ }
+ })
+ .map(|declarations_response| {
+ GetDeclarations { position }.response_from_proto(
+ declarations_response,
+ project.clone(),
+ buffer.clone(),
+ cx.clone(),
+ )
+ }),
+ )
+ .await;
+
+ Ok(actions
+ .into_iter()
+ .collect::<Result<Vec<Vec<_>>>>()?
+ .into_iter()
+ .flatten()
+ .dedup()
+ .collect())
+ })
+ } else {
+ let declarations_task = self.request_multiple_lsp_locally(
+ buffer_handle,
+ Some(position),
+ GetDeclarations { position },
+ cx,
+ );
+ cx.spawn(async move |_, _| {
+ Ok(declarations_task
+ .await
+ .into_iter()
+ .flat_map(|(_, declarations)| declarations)
+ .dedup()
+ .collect())
+ })
+ }
+ }
+
+ pub fn type_definitions(
+ &mut self,
+ buffer_handle: &Entity<Buffer>,
+ position: PointUtf16,
+ cx: &mut Context<Self>,
+ ) -> Task<Result<Vec<LocationLink>>> {
+ if let Some((upstream_client, project_id)) = self.upstream_client() {
+ let request_task = upstream_client.request(proto::MultiLspQuery {
+ buffer_id: buffer_handle.read(cx).remote_id().into(),
+ version: serialize_version(&buffer_handle.read(cx).version()),
+ project_id,
+ strategy: Some(proto::multi_lsp_query::Strategy::All(
+ proto::AllLanguageServers {},
+ )),
+ request: Some(proto::multi_lsp_query::Request::GetTypeDefinition(
+ GetTypeDefinitions { position }.to_proto(project_id, buffer_handle.read(cx)),
+ )),
+ });
+ let buffer = buffer_handle.clone();
+ cx.spawn(async move |weak_project, cx| {
+ let Some(project) = weak_project.upgrade() else {
+ return Ok(Vec::new());
+ };
+ let responses = request_task.await?.responses;
+ let actions = join_all(
+ responses
+ .into_iter()
+ .filter_map(|lsp_response| match lsp_response.response? {
+ proto::lsp_response::Response::GetTypeDefinitionResponse(response) => {
+ Some(response)
+ }
+ unexpected => {
+ debug_panic!("Unexpected response: {unexpected:?}");
+ None
+ }
+ })
+ .map(|type_definitions_response| {
+ GetTypeDefinitions { position }.response_from_proto(
+ type_definitions_response,
+ project.clone(),
+ buffer.clone(),
+ cx.clone(),
+ )
+ }),
+ )
+ .await;
+
+ Ok(actions
+ .into_iter()
+ .collect::<Result<Vec<Vec<_>>>>()?
+ .into_iter()
+ .flatten()
+ .dedup()
+ .collect())
+ })
+ } else {
+ let type_definitions_task = self.request_multiple_lsp_locally(
+ buffer_handle,
+ Some(position),
+ GetTypeDefinitions { position },
+ cx,
+ );
+ cx.spawn(async move |_, _| {
+ Ok(type_definitions_task
+ .await
+ .into_iter()
+ .flat_map(|(_, type_definitions)| type_definitions)
+ .dedup()
+ .collect())
+ })
+ }
+ }
+
+ pub fn implementations(
+ &mut self,
+ buffer_handle: &Entity<Buffer>,
+ position: PointUtf16,
+ cx: &mut Context<Self>,
+ ) -> Task<Result<Vec<LocationLink>>> {
+ if let Some((upstream_client, project_id)) = self.upstream_client() {
+ let request_task = upstream_client.request(proto::MultiLspQuery {
+ buffer_id: buffer_handle.read(cx).remote_id().into(),
+ version: serialize_version(&buffer_handle.read(cx).version()),
+ project_id,
+ strategy: Some(proto::multi_lsp_query::Strategy::All(
+ proto::AllLanguageServers {},
+ )),
+ request: Some(proto::multi_lsp_query::Request::GetImplementation(
+ GetImplementations { position }.to_proto(project_id, buffer_handle.read(cx)),
+ )),
+ });
+ let buffer = buffer_handle.clone();
+ cx.spawn(async move |weak_project, cx| {
+ let Some(project) = weak_project.upgrade() else {
+ return Ok(Vec::new());
+ };
+ let responses = request_task.await?.responses;
+ let actions = join_all(
+ responses
+ .into_iter()
+ .filter_map(|lsp_response| match lsp_response.response? {
+ proto::lsp_response::Response::GetImplementationResponse(response) => {
+ Some(response)
+ }
+ unexpected => {
+ debug_panic!("Unexpected response: {unexpected:?}");
+ None
+ }
+ })
+ .map(|implementations_response| {
+ GetImplementations { position }.response_from_proto(
+ implementations_response,
+ project.clone(),
+ buffer.clone(),
+ cx.clone(),
+ )
+ }),
+ )
+ .await;
+
+ Ok(actions
+ .into_iter()
+ .collect::<Result<Vec<Vec<_>>>>()?
+ .into_iter()
+ .flatten()
+ .dedup()
+ .collect())
+ })
+ } else {
+ let implementations_task = self.request_multiple_lsp_locally(
+ buffer_handle,
+ Some(position),
+ GetImplementations { position },
+ cx,
+ );
+ cx.spawn(async move |_, _| {
+ Ok(implementations_task
+ .await
+ .into_iter()
+ .flat_map(|(_, implementations)| implementations)
+ .dedup()
+ .collect())
+ })
+ }
+ }
+
+ pub fn references(
+ &mut self,
+ buffer_handle: &Entity<Buffer>,
+ position: PointUtf16,
+ cx: &mut Context<Self>,
+ ) -> Task<Result<Vec<Location>>> {
+ if let Some((upstream_client, project_id)) = self.upstream_client() {
+ let request_task = upstream_client.request(proto::MultiLspQuery {
+ buffer_id: buffer_handle.read(cx).remote_id().into(),
+ version: serialize_version(&buffer_handle.read(cx).version()),
+ project_id,
+ strategy: Some(proto::multi_lsp_query::Strategy::All(
+ proto::AllLanguageServers {},
+ )),
+ request: Some(proto::multi_lsp_query::Request::GetReferences(
+ GetReferences { position }.to_proto(project_id, buffer_handle.read(cx)),
+ )),
+ });
+ let buffer = buffer_handle.clone();
+ cx.spawn(async move |weak_project, cx| {
+ let Some(project) = weak_project.upgrade() else {
+ return Ok(Vec::new());
+ };
+ let responses = request_task.await?.responses;
+ let actions = join_all(
+ responses
+ .into_iter()
+ .filter_map(|lsp_response| match lsp_response.response? {
+ proto::lsp_response::Response::GetReferencesResponse(response) => {
+ Some(response)
+ }
+ unexpected => {
+ debug_panic!("Unexpected response: {unexpected:?}");
+ None
+ }
+ })
+ .map(|references_response| {
+ GetReferences { position }.response_from_proto(
+ references_response,
+ project.clone(),
+ buffer.clone(),
+ cx.clone(),
+ )
+ }),
+ )
+ .await;
+
+ Ok(actions
+ .into_iter()
+ .collect::<Result<Vec<Vec<_>>>>()?
+ .into_iter()
+ .flatten()
+ .dedup()
+ .collect())
+ })
+ } else {
+ let references_task = self.request_multiple_lsp_locally(
+ buffer_handle,
+ Some(position),
+ GetReferences { position },
+ cx,
+ );
+ cx.spawn(async move |_, _| {
+ Ok(references_task
+ .await
+ .into_iter()
+ .flat_map(|(_, references)| references)
+ .dedup()
+ .collect())
+ })
+ }
+ }
+
pub fn code_actions(
&mut self,
buffer_handle: &Entity<Buffer>,
@@ -7886,6 +8248,200 @@ impl LspStore {
.collect(),
})
}
+ Some(proto::multi_lsp_query::Request::GetDefinition(message)) => {
+ let get_definitions = GetDefinitions::from_proto(
+ message,
+ lsp_store.clone(),
+ buffer.clone(),
+ cx.clone(),
+ )
+ .await?;
+
+ let definitions = lsp_store
+ .update(&mut cx, |project, cx| {
+ project.request_multiple_lsp_locally(
+ &buffer,
+ Some(get_definitions.position),
+ get_definitions,
+ cx,
+ )
+ })?
+ .await
+ .into_iter();
+
+ lsp_store.update(&mut cx, |project, cx| proto::MultiLspQueryResponse {
+ responses: definitions
+ .map(|(server_id, definitions)| proto::LspResponse {
+ server_id: server_id.to_proto(),
+ response: Some(proto::lsp_response::Response::GetDefinitionResponse(
+ GetDefinitions::response_to_proto(
+ definitions,
+ project,
+ sender_id,
+ &buffer_version,
+ cx,
+ ),
+ )),
+ })
+ .collect(),
+ })
+ }
+ Some(proto::multi_lsp_query::Request::GetDeclaration(message)) => {
+ let get_declarations = GetDeclarations::from_proto(
+ message,
+ lsp_store.clone(),
+ buffer.clone(),
+ cx.clone(),
+ )
+ .await?;
+
+ let declarations = lsp_store
+ .update(&mut cx, |project, cx| {
+ project.request_multiple_lsp_locally(
+ &buffer,
+ Some(get_declarations.position),
+ get_declarations,
+ cx,
+ )
+ })?
+ .await
+ .into_iter();
+
+ lsp_store.update(&mut cx, |project, cx| proto::MultiLspQueryResponse {
+ responses: declarations
+ .map(|(server_id, declarations)| proto::LspResponse {
+ server_id: server_id.to_proto(),
+ response: Some(proto::lsp_response::Response::GetDeclarationResponse(
+ GetDeclarations::response_to_proto(
+ declarations,
+ project,
+ sender_id,
+ &buffer_version,
+ cx,
+ ),
+ )),
+ })
+ .collect(),
+ })
+ }
+ Some(proto::multi_lsp_query::Request::GetTypeDefinition(message)) => {
+ let get_type_definitions = GetTypeDefinitions::from_proto(
+ message,
+ lsp_store.clone(),
+ buffer.clone(),
+ cx.clone(),
+ )
+ .await?;
+
+ let type_definitions = lsp_store
+ .update(&mut cx, |project, cx| {
+ project.request_multiple_lsp_locally(
+ &buffer,
+ Some(get_type_definitions.position),
+ get_type_definitions,
+ cx,
+ )
+ })?
+ .await
+ .into_iter();
+
+ lsp_store.update(&mut cx, |project, cx| proto::MultiLspQueryResponse {
+ responses: type_definitions
+ .map(|(server_id, type_definitions)| proto::LspResponse {
+ server_id: server_id.to_proto(),
+ response: Some(
+ proto::lsp_response::Response::GetTypeDefinitionResponse(
+ GetTypeDefinitions::response_to_proto(
+ type_definitions,
+ project,
+ sender_id,
+ &buffer_version,
+ cx,
+ ),
+ ),
+ ),
+ })
+ .collect(),
+ })
+ }
+ Some(proto::multi_lsp_query::Request::GetImplementation(message)) => {
+ let get_implementations = GetImplementations::from_proto(
+ message,
+ lsp_store.clone(),
+ buffer.clone(),
+ cx.clone(),
+ )
+ .await?;
+
+ let implementations = lsp_store
+ .update(&mut cx, |project, cx| {
+ project.request_multiple_lsp_locally(
+ &buffer,
+ Some(get_implementations.position),
+ get_implementations,
+ cx,
+ )
+ })?
+ .await
+ .into_iter();
+
+ lsp_store.update(&mut cx, |project, cx| proto::MultiLspQueryResponse {
+ responses: implementations
+ .map(|(server_id, implementations)| proto::LspResponse {
+ server_id: server_id.to_proto(),
+ response: Some(
+ proto::lsp_response::Response::GetImplementationResponse(
+ GetImplementations::response_to_proto(
+ implementations,
+ project,
+ sender_id,
+ &buffer_version,
+ cx,
+ ),
+ ),
+ ),
+ })
+ .collect(),
+ })
+ }
+ Some(proto::multi_lsp_query::Request::GetReferences(message)) => {
+ let get_references = GetReferences::from_proto(
+ message,
+ lsp_store.clone(),
+ buffer.clone(),
+ cx.clone(),
+ )
+ .await?;
+
+ let references = lsp_store
+ .update(&mut cx, |project, cx| {
+ project.request_multiple_lsp_locally(
+ &buffer,
+ Some(get_references.position),
+ get_references,
+ cx,
+ )
+ })?
+ .await
+ .into_iter();
+
+ lsp_store.update(&mut cx, |project, cx| proto::MultiLspQueryResponse {
+ responses: references
+ .map(|(server_id, references)| proto::LspResponse {
+ server_id: server_id.to_proto(),
+ response: Some(proto::lsp_response::Response::GetReferencesResponse(
+ GetReferences::response_to_proto(
+ references,
+ project,
+ sender_id,
+ &buffer_version,
+ cx,
+ ),
+ )),
+ })
+ .collect(),
+ })
+ }
None => anyhow::bail!("empty multi lsp query request"),
}
}
@@ -696,7 +696,7 @@ pub struct MarkupContent {
pub value: String,
}
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq)]
pub struct LocationLink {
pub origin: Option<Location>,
pub target: Location,
@@ -3342,91 +3342,52 @@ impl Project {
})
}
- #[inline(never)]
- fn definition_impl(
- &mut self,
- buffer: &Entity<Buffer>,
- position: PointUtf16,
- cx: &mut Context<Self>,
- ) -> Task<Result<Vec<LocationLink>>> {
- self.request_lsp(
- buffer.clone(),
- LanguageServerToQuery::FirstCapable,
- GetDefinition { position },
- cx,
- )
- }
- pub fn definition<T: ToPointUtf16>(
+ pub fn definitions<T: ToPointUtf16>(
&mut self,
buffer: &Entity<Buffer>,
position: T,
cx: &mut Context<Self>,
) -> Task<Result<Vec<LocationLink>>> {
let position = position.to_point_utf16(buffer.read(cx));
- self.definition_impl(buffer, position, cx)
- }
-
- fn declaration_impl(
- &mut self,
- buffer: &Entity<Buffer>,
- position: PointUtf16,
- cx: &mut Context<Self>,
- ) -> Task<Result<Vec<LocationLink>>> {
- self.request_lsp(
- buffer.clone(),
- LanguageServerToQuery::FirstCapable,
- GetDeclaration { position },
- cx,
- )
+ self.lsp_store.update(cx, |lsp_store, cx| {
+ lsp_store.definitions(buffer, position, cx)
+ })
}
- pub fn declaration<T: ToPointUtf16>(
+ pub fn declarations<T: ToPointUtf16>(
&mut self,
buffer: &Entity<Buffer>,
position: T,
cx: &mut Context<Self>,
) -> Task<Result<Vec<LocationLink>>> {
let position = position.to_point_utf16(buffer.read(cx));
- self.declaration_impl(buffer, position, cx)
- }
-
- fn type_definition_impl(
- &mut self,
- buffer: &Entity<Buffer>,
- position: PointUtf16,
- cx: &mut Context<Self>,
- ) -> Task<Result<Vec<LocationLink>>> {
- self.request_lsp(
- buffer.clone(),
- LanguageServerToQuery::FirstCapable,
- GetTypeDefinition { position },
- cx,
- )
+ self.lsp_store.update(cx, |lsp_store, cx| {
+ lsp_store.declarations(buffer, position, cx)
+ })
}
- pub fn type_definition<T: ToPointUtf16>(
+ pub fn type_definitions<T: ToPointUtf16>(
&mut self,
buffer: &Entity<Buffer>,
position: T,
cx: &mut Context<Self>,
) -> Task<Result<Vec<LocationLink>>> {
let position = position.to_point_utf16(buffer.read(cx));
- self.type_definition_impl(buffer, position, cx)
+ self.lsp_store.update(cx, |lsp_store, cx| {
+ lsp_store.type_definitions(buffer, position, cx)
+ })
}
- pub fn implementation<T: ToPointUtf16>(
+ pub fn implementations<T: ToPointUtf16>(
&mut self,
buffer: &Entity<Buffer>,
position: T,
cx: &mut Context<Self>,
) -> Task<Result<Vec<LocationLink>>> {
let position = position.to_point_utf16(buffer.read(cx));
- self.request_lsp(
- buffer.clone(),
- LanguageServerToQuery::FirstCapable,
- GetImplementation { position },
- cx,
- )
+ self.lsp_store.update(cx, |lsp_store, cx| {
+ lsp_store.implementations(buffer, position, cx)
+ })
}
pub fn references<T: ToPointUtf16>(
@@ -3436,12 +3397,9 @@ impl Project {
cx: &mut Context<Self>,
) -> Task<Result<Vec<Location>>> {
let position = position.to_point_utf16(buffer.read(cx));
- self.request_lsp(
- buffer.clone(),
- LanguageServerToQuery::FirstCapable,
- GetReferences { position },
- cx,
- )
+ self.lsp_store.update(cx, |lsp_store, cx| {
+ lsp_store.references(buffer, position, cx)
+ })
}
fn document_highlights_impl(