Improve loading times for extension themes (#40015)

This PR primarily does two things:
- replace `serde_json::from_reader` with `serde_json::from_slice`, as
the latter is much much faster, even with loading the file into memory
first.
- runs the initial loading of themes and icon themes coming from
extensions in parallel instead of sequential.

Measuring the `eager_load_active_theme_and_icon_theme` method, this
drastically improves the speed at which this happens (tested this method
primarily with debug builds on my MacBook Pro, but the `Before`
measurement was also confirmed against a `release-fast` build):
- Before: ~260ms on average (in one run, it even took 600ms)
- After: ~20ms on average

Which reduces the time this method takes to load these by around ~92%.

Given that we block on this during the initial app startup, this should
drastically improve Zeds initial startup loading time. Yet, it also
improves responsiveness when installing theme extensions and trying
these.

I also replaced all other `serde_json::from_reader` implementations with
`serde_json::from_slice` and added the former to `disallowed_methods`,
given
https://github.com/serde-rs/json/issues/160#issuecomment-253446892.

Release Notes:

- Improved Zed startup speed when using themes provided by extensions
This commit is contained in:
Finn Evers
2025-10-13 11:53:19 +02:00
committed by GitHub
parent 47a66c938f
commit 81cd435e08
6 changed files with 115 additions and 55 deletions
+16 -9
View File
@@ -965,10 +965,11 @@ async fn heuristic_syntactic_expand(
let row_count = node_end.row - node_start.row + 1;
let mut ancestor_range = None;
let reached_outline_node = cx.background_executor().scoped({
let node_range = node_range.clone();
let outline_range = outline_range.clone();
let ancestor_range = &mut ancestor_range;
|scope| {scope.spawn(async move {
let node_range = node_range.clone();
let outline_range = outline_range.clone();
let ancestor_range = &mut ancestor_range;
|scope| {
scope.spawn(async move {
// Stop if we've exceeded the row count or reached an outline node. Then, find the interval
// of node children which contains the query range. For example, this allows just returning
// the header of a declaration rather than the entire declaration.
@@ -980,8 +981,11 @@ async fn heuristic_syntactic_expand(
if cursor.goto_first_child() {
loop {
let child_node = cursor.node();
let child_range = previous_end..Point::from_ts_point(child_node.end_position());
if included_child_start.is_none() && child_range.contains(&input_range.start) {
let child_range =
previous_end..Point::from_ts_point(child_node.end_position());
if included_child_start.is_none()
&& child_range.contains(&input_range.start)
{
included_child_start = Some(child_range.start);
}
if child_range.contains(&input_range.end) {
@@ -997,19 +1001,22 @@ async fn heuristic_syntactic_expand(
if let Some(start) = included_child_start {
let row_count = end.row - start.row;
if row_count < max_row_count {
*ancestor_range = Some(Some(RangeInclusive::new(start.row, end.row)));
*ancestor_range =
Some(Some(RangeInclusive::new(start.row, end.row)));
return;
}
}
log::info!(
"Expanding to ancestor started on {} node exceeding row limit of {max_row_count}.",
"Expanding to ancestor started on {} node\
exceeding row limit of {max_row_count}.",
node.grammar_name()
);
*ancestor_range = Some(None);
}
})
}});
}
});
reached_outline_node.await;
if let Some(node) = ancestor_range {
return node;