Support tasks from rust-analyzer (#28359)

(and any other LSP server in theory, if it exposes any LSP-ext endpoint
for the same)

Closes https://github.com/zed-industries/zed/issues/16160

* adds a way to disable tree-sitter tasks (the ones from the plugins,
enabled by default) with
```json5
"languages": {
  "Rust": "tasks": {
      "enabled": false
    }
  }
}
```
language settings

* adds a way to disable LSP tasks (the ones from the rust-analyzer
language server, enabled by default) with
```json5
"lsp": {
  "rust-analyzer": {
    "enable_lsp_tasks": false,
  }
}
```

* adds rust-analyzer tasks into tasks modal and gutter:

<img width="1728" alt="modal"
src="https://github.com/user-attachments/assets/22b9cee1-4ffb-4c9e-b1f1-d01e80e72508"
/>

<img width="396" alt="gutter"
src="https://github.com/user-attachments/assets/bd818079-e247-4332-bdb5-1b7cb1cce768"
/>


Release Notes:

- Added tasks from rust-analyzer
This commit is contained in:
Kirill Bulatov 2025-04-08 15:07:56 -06:00 committed by GitHub
parent 763cc6dba3
commit 39c98ce882
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 882 additions and 201 deletions

View file

@ -977,62 +977,69 @@ async fn location_links_from_proto(
let mut links = Vec::new();
for link in proto_links {
let origin = match link.origin {
Some(origin) => {
let buffer_id = BufferId::new(origin.buffer_id)?;
let buffer = lsp_store
.update(&mut cx, |lsp_store, cx| {
lsp_store.wait_for_remote_buffer(buffer_id, cx)
})?
.await?;
let start = origin
.start
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("missing origin start"))?;
let end = origin
.end
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("missing origin end"))?;
buffer
.update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
.await?;
Some(Location {
buffer,
range: start..end,
})
}
None => None,
};
let target = link.target.ok_or_else(|| anyhow!("missing target"))?;
let buffer_id = BufferId::new(target.buffer_id)?;
let buffer = lsp_store
.update(&mut cx, |lsp_store, cx| {
lsp_store.wait_for_remote_buffer(buffer_id, cx)
})?
.await?;
let start = target
.start
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("missing target start"))?;
let end = target
.end
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("missing target end"))?;
buffer
.update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
.await?;
let target = Location {
buffer,
range: start..end,
};
links.push(LocationLink { origin, target })
links.push(location_link_from_proto(link, &lsp_store, &mut cx).await?)
}
Ok(links)
}
pub async fn location_link_from_proto(
link: proto::LocationLink,
lsp_store: &Entity<LspStore>,
cx: &mut AsyncApp,
) -> Result<LocationLink> {
let origin = match link.origin {
Some(origin) => {
let buffer_id = BufferId::new(origin.buffer_id)?;
let buffer = lsp_store
.update(cx, |lsp_store, cx| {
lsp_store.wait_for_remote_buffer(buffer_id, cx)
})?
.await?;
let start = origin
.start
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("missing origin start"))?;
let end = origin
.end
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("missing origin end"))?;
buffer
.update(cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
.await?;
Some(Location {
buffer,
range: start..end,
})
}
None => None,
};
let target = link.target.ok_or_else(|| anyhow!("missing target"))?;
let buffer_id = BufferId::new(target.buffer_id)?;
let buffer = lsp_store
.update(cx, |lsp_store, cx| {
lsp_store.wait_for_remote_buffer(buffer_id, cx)
})?
.await?;
let start = target
.start
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("missing target start"))?;
let end = target
.end
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("missing target end"))?;
buffer
.update(cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
.await?;
let target = Location {
buffer,
range: start..end,
};
Ok(LocationLink { origin, target })
}
async fn location_links_from_lsp(
message: Option<lsp::GotoDefinitionResponse>,
lsp_store: Entity<LspStore>,
@ -1115,6 +1122,65 @@ async fn location_links_from_lsp(
Ok(definitions)
}
pub async fn location_link_from_lsp(
link: lsp::LocationLink,
lsp_store: &Entity<LspStore>,
buffer: &Entity<Buffer>,
server_id: LanguageServerId,
cx: &mut AsyncApp,
) -> Result<LocationLink> {
let (lsp_adapter, language_server) =
language_server_for_buffer(&lsp_store, &buffer, server_id, cx)?;
let (origin_range, target_uri, target_range) = (
link.origin_selection_range,
link.target_uri,
link.target_selection_range,
);
let target_buffer_handle = lsp_store
.update(cx, |lsp_store, cx| {
lsp_store.open_local_buffer_via_lsp(
target_uri,
language_server.server_id(),
lsp_adapter.name.clone(),
cx,
)
})?
.await?;
cx.update(|cx| {
let origin_location = origin_range.map(|origin_range| {
let origin_buffer = buffer.read(cx);
let origin_start =
origin_buffer.clip_point_utf16(point_from_lsp(origin_range.start), Bias::Left);
let origin_end =
origin_buffer.clip_point_utf16(point_from_lsp(origin_range.end), Bias::Left);
Location {
buffer: buffer.clone(),
range: origin_buffer.anchor_after(origin_start)
..origin_buffer.anchor_before(origin_end),
}
});
let target_buffer = target_buffer_handle.read(cx);
let target_start =
target_buffer.clip_point_utf16(point_from_lsp(target_range.start), Bias::Left);
let target_end =
target_buffer.clip_point_utf16(point_from_lsp(target_range.end), Bias::Left);
let target_location = Location {
buffer: target_buffer_handle,
range: target_buffer.anchor_after(target_start)
..target_buffer.anchor_before(target_end),
};
LocationLink {
origin: origin_location,
target: target_location,
}
})
}
fn location_links_to_proto(
links: Vec<LocationLink>,
lsp_store: &mut LspStore,
@ -1123,45 +1189,52 @@ fn location_links_to_proto(
) -> Vec<proto::LocationLink> {
links
.into_iter()
.map(|definition| {
let origin = definition.origin.map(|origin| {
lsp_store
.buffer_store()
.update(cx, |buffer_store, cx| {
buffer_store.create_buffer_for_peer(&origin.buffer, peer_id, cx)
})
.detach_and_log_err(cx);
let buffer_id = origin.buffer.read(cx).remote_id().into();
proto::Location {
start: Some(serialize_anchor(&origin.range.start)),
end: Some(serialize_anchor(&origin.range.end)),
buffer_id,
}
});
lsp_store
.buffer_store()
.update(cx, |buffer_store, cx| {
buffer_store.create_buffer_for_peer(&definition.target.buffer, peer_id, cx)
})
.detach_and_log_err(cx);
let buffer_id = definition.target.buffer.read(cx).remote_id().into();
let target = proto::Location {
start: Some(serialize_anchor(&definition.target.range.start)),
end: Some(serialize_anchor(&definition.target.range.end)),
buffer_id,
};
proto::LocationLink {
origin,
target: Some(target),
}
})
.map(|definition| location_link_to_proto(definition, lsp_store, peer_id, cx))
.collect()
}
pub fn location_link_to_proto(
location: LocationLink,
lsp_store: &mut LspStore,
peer_id: PeerId,
cx: &mut App,
) -> proto::LocationLink {
let origin = location.origin.map(|origin| {
lsp_store
.buffer_store()
.update(cx, |buffer_store, cx| {
buffer_store.create_buffer_for_peer(&origin.buffer, peer_id, cx)
})
.detach_and_log_err(cx);
let buffer_id = origin.buffer.read(cx).remote_id().into();
proto::Location {
start: Some(serialize_anchor(&origin.range.start)),
end: Some(serialize_anchor(&origin.range.end)),
buffer_id,
}
});
lsp_store
.buffer_store()
.update(cx, |buffer_store, cx| {
buffer_store.create_buffer_for_peer(&location.target.buffer, peer_id, cx)
})
.detach_and_log_err(cx);
let buffer_id = location.target.buffer.read(cx).remote_id().into();
let target = proto::Location {
start: Some(serialize_anchor(&location.target.range.start)),
end: Some(serialize_anchor(&location.target.range.end)),
buffer_id,
};
proto::LocationLink {
origin,
target: Some(target),
}
}
#[async_trait(?Send)]
impl LspCommand for GetReferences {
type Response = Vec<Location>;