Performance tweaks (#54321)

Release Notes:

- N/A or Added/Fixed/Improved ...
This commit is contained in:
Lukas Wirth 2026-04-21 09:25:22 +02:00 committed by GitHub
parent 040b03b34d
commit a62ae579ab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 141 additions and 138 deletions

1
Cargo.lock generated
View file

@ -17998,6 +17998,7 @@ name = "title_bar"
version = "0.1.0"
dependencies = [
"anyhow",
"arrayvec",
"auto_update",
"call",
"channel",

View file

@ -39,13 +39,12 @@ fn create_table_cell(
cx: &Context<'_, CsvPreviewView>,
) -> gpui::Stateful<Div> {
div()
.id(ElementId::NamedInteger(
.id(ElementId::Name(
format!(
"csv-display-cell-{}-{}",
*display_cell_id.row, *display_cell_id.col
)
.into(),
0,
))
.cursor_pointer()
.flex()

View file

@ -66,10 +66,14 @@ struct Identifier {
enum DefinitionTask {
CacheHit(Arc<CacheEntry>),
CacheMiss {
definitions: Task<Result<Option<Vec<LocationLink>>>>,
type_definitions: Task<Result<Option<Vec<LocationLink>>>>,
},
CacheMiss(
Task<
Option<(
Task<Result<Option<Vec<LocationLink>>>>,
Task<Result<Option<Vec<LocationLink>>>>,
)>,
>,
),
}
#[derive(Debug)]
@ -270,39 +274,37 @@ impl RelatedExcerptStore {
let futures = this.update(cx, |this, cx| {
identifiers_with_distance
.into_iter()
.filter_map(|(identifier, _)| {
.map(|(identifier, _)| {
let task = if let Some(entry) = this.cache.get(&identifier) {
DefinitionTask::CacheHit(entry.clone())
} else {
let definitions = this
.project
.update(cx, |project, cx| {
project.definitions(&buffer, identifier.range.start, cx)
})
.ok()?;
let type_definitions = this
.project
.update(cx, |project, cx| {
project.type_definitions(&buffer, identifier.range.start, cx)
})
.ok()?;
DefinitionTask::CacheMiss {
definitions,
type_definitions,
}
let project = this.project.clone();
let buffer = buffer.downgrade();
DefinitionTask::CacheMiss(cx.spawn(async move |_, cx| {
let buffer = buffer.upgrade()?;
let definitions = project
.update(cx, |project, cx| {
project.definitions(&buffer, identifier.range.start, cx)
})
.ok()?;
let type_definitions = project
.update(cx, |project, cx| {
project.type_definitions(&buffer, identifier.range.start, cx)
})
.ok()?;
Some((definitions, type_definitions))
}))
};
let cx = async_cx.clone();
let project = project.clone();
Some(async move {
async move {
match task {
DefinitionTask::CacheHit(cache_entry) => {
Some((identifier, cache_entry, None))
}
DefinitionTask::CacheMiss {
definitions,
type_definitions,
} => {
DefinitionTask::CacheMiss(task) => {
let (definitions, type_definitions) = task.await?;
let (definition_locations, type_definition_locations) =
futures::join!(definitions, type_definitions);
let duration = start_time.elapsed();
@ -349,7 +351,7 @@ impl RelatedExcerptStore {
}))
}
}
})
}
})
.collect::<Vec<_>>()
})?;

View file

@ -15,7 +15,7 @@ use project::{
project_settings::ProjectSettings,
};
use settings::{
SemanticTokenColorOverride, SemanticTokenFontStyle, SemanticTokenFontWeight,
SemanticTokenColorOverride, SemanticTokenFontStyle, SemanticTokenFontWeight, SemanticTokenRule,
SemanticTokenRules, Settings as _,
};
use text::BufferId;
@ -295,13 +295,14 @@ impl Editor {
) else {
continue;
};
let theme = cx.theme().syntax();
token_highlights.reserve(2 * server_tokens.len());
token_highlights.extend(buffer_into_editor_highlights(
&server_tokens,
stylizer,
&multi_buffer_snapshot,
&mut interner,
cx,
theme,
));
}
@ -328,7 +329,7 @@ fn buffer_into_editor_highlights<'a, 'b>(
stylizer: &'a SemanticTokenStylizer,
multi_buffer_snapshot: &'a multi_buffer::MultiBufferSnapshot,
interner: &'b mut HighlightStyleInterner,
cx: &'a App,
theme: &'a SyntaxTheme,
) -> impl Iterator<Item = SemanticTokenHighlight> + use<'a, 'b> {
multi_buffer_snapshot
.text_anchors_to_visible_anchors(
@ -341,12 +342,7 @@ fn buffer_into_editor_highlights<'a, 'b>(
.zip(buffer_tokens)
.filter_map(|((multi_buffer_start, multi_buffer_end), token)| {
let range = multi_buffer_start?..multi_buffer_end?;
let style = convert_token(
stylizer,
cx.theme().syntax(),
token.token_type,
token.token_modifiers,
)?;
let style = convert_token(stylizer, theme, token.token_type, token.token_modifiers)?;
let style = interner.intern(style);
Some(SemanticTokenHighlight {
range,
@ -365,27 +361,19 @@ fn convert_token(
modifiers: u32,
) -> Option<HighlightStyle> {
let rules = stylizer.rules_for_token(token_type)?;
let matching: Vec<_> = rules
.iter()
.filter(|rule| {
rule.token_modifiers
.iter()
.all(|m| stylizer.has_modifier(modifiers, m))
})
.collect();
if let Some(rule) = matching.last() {
if rule.no_style_defined() {
return None;
}
let filter = |rule: &&SemanticTokenRule| {
rule.token_modifiers
.iter()
.all(|m| stylizer.has_modifier(modifiers, m))
};
let last = rules.last()?;
if last.no_style_defined() && filter(&last) {
return None;
}
let mut highlight = HighlightStyle::default();
let mut empty = true;
for rule in matching {
empty = false;
for rule in rules.into_iter().filter(filter) {
let style = rule
.style
.iter()
@ -400,7 +388,7 @@ fn convert_token(
highlight.$highlight_field = rule
.$rule_field
.map($transform)
.or_else(|| style.and_then(|s| s.$highlight_field))
.or_else(|| style.as_ref().and_then(|s| s.$highlight_field))
.or(highlight.$highlight_field)
};
}
@ -460,8 +448,7 @@ fn convert_token(
},
);
}
if empty { None } else { Some(highlight) }
Some(highlight)
}
#[cfg(test)]

View file

@ -3,6 +3,7 @@ use std::{
cmp,
fmt::Debug,
ops::{Add, Sub},
ptr::NonNull,
};
/// Maximum children per internal node (R-tree style branching factor).
@ -30,7 +31,7 @@ where
/// Reusable stack for tree traversal during insertion.
insert_path: Vec<usize>,
/// Reusable stack for search operations.
search_stack: Vec<usize>,
search_stack: Vec<NonNull<Node<U>>>,
}
/// A node in the bounds tree.
@ -150,12 +151,14 @@ where
// Slow path: search the tree
self.search_stack.clear();
self.search_stack.push(root_idx);
self.search_stack.push(NonNull::from(&self.nodes[root_idx]));
let mut max_found = 0u32;
while let Some(node_idx) = self.search_stack.pop() {
let node = &self.nodes[node_idx];
while let Some(node) = self.search_stack.pop() {
// SAFETY: `node` is guaranteed to be valid as the `nodes` stack is unmodified in this function
// and the `search_stack` only contains pointers from this function call.
let node = unsafe { node.as_ref() };
// Pruning: skip if this subtree can't improve our result
if node.max_order <= max_found {
@ -174,11 +177,14 @@ where
NodeKind::Internal { children } => {
// Children are maintained with highest max_order at the end.
// Push in forward order to highest (last) is popped first.
for &child_idx in children.as_slice() {
if self.nodes[child_idx].max_order > max_found {
self.search_stack.push(child_idx);
}
}
self.search_stack.extend(
children
.as_slice()
.iter()
.map(|&child_idx| &self.nodes[child_idx])
.filter(|node| node.max_order > max_found)
.map(NonNull::from),
);
}
}
}

View file

@ -1048,7 +1048,7 @@ impl<T> AtlasTextureList<T> {
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(C)]
#[expect(missing_docs)]
pub struct AtlasTile {

View file

@ -353,8 +353,8 @@ impl PlatformAtlas for TestAtlas {
>,
) -> anyhow::Result<Option<crate::AtlasTile>> {
let mut state = self.0.lock();
if let Some(tile) = state.tiles.get(key) {
return Ok(Some(tile.clone()));
if let Some(&tile) = state.tiles.get(key) {
return Ok(Some(tile));
}
drop(state);
@ -384,7 +384,7 @@ impl PlatformAtlas for TestAtlas {
},
);
Ok(Some(state.tiles[key].clone()))
Ok(Some(state.tiles[key]))
}
fn remove(&self, key: &AtlasKey) {

View file

@ -88,11 +88,11 @@ impl Scene {
match &mut primitive {
Primitive::Shadow(shadow) => {
shadow.order = order;
self.shadows.push(shadow.clone());
self.shadows.push(*shadow);
}
Primitive::Quad(quad) => {
quad.order = order;
self.quads.push(quad.clone());
self.quads.push(*quad);
}
Primitive::Path(path) => {
path.order = order;
@ -101,19 +101,19 @@ impl Scene {
}
Primitive::Underline(underline) => {
underline.order = order;
self.underlines.push(underline.clone());
self.underlines.push(*underline);
}
Primitive::MonochromeSprite(sprite) => {
sprite.order = order;
self.monochrome_sprites.push(sprite.clone());
self.monochrome_sprites.push(*sprite);
}
Primitive::SubpixelSprite(sprite) => {
sprite.order = order;
self.subpixel_sprites.push(sprite.clone());
self.subpixel_sprites.push(*sprite);
}
Primitive::PolychromeSprite(sprite) => {
sprite.order = order;
self.polychrome_sprites.push(sprite.clone());
self.polychrome_sprites.push(*sprite);
}
Primitive::Surface(surface) => {
surface.order = order;
@ -481,7 +481,7 @@ pub enum PrimitiveBatch {
Surfaces(Range<usize>),
}
#[derive(Default, Debug, Clone)]
#[derive(Default, Debug, Copy, Clone)]
#[repr(C)]
#[expect(missing_docs)]
pub struct Quad {
@ -501,7 +501,7 @@ impl From<Quad> for Primitive {
}
}
#[derive(Debug, Clone)]
#[derive(Debug, Copy, Clone)]
#[repr(C)]
#[expect(missing_docs)]
pub struct Underline {
@ -520,7 +520,7 @@ impl From<Underline> for Primitive {
}
}
#[derive(Debug, Clone)]
#[derive(Debug, Copy, Clone)]
#[repr(C)]
#[expect(missing_docs)]
pub struct Shadow {
@ -652,7 +652,7 @@ impl Default for TransformationMatrix {
}
}
#[derive(Clone, Debug)]
#[derive(Copy, Clone, Debug)]
#[repr(C)]
#[expect(missing_docs)]
pub struct MonochromeSprite {
@ -671,7 +671,7 @@ impl From<MonochromeSprite> for Primitive {
}
}
#[derive(Clone, Debug)]
#[derive(Copy, Clone, Debug)]
#[repr(C)]
#[expect(missing_docs)]
pub struct SubpixelSprite {
@ -690,7 +690,7 @@ impl From<SubpixelSprite> for Primitive {
}
}
#[derive(Clone, Debug)]
#[derive(Copy, Clone, Debug)]
#[repr(C)]
#[expect(missing_docs)]
pub struct PolychromeSprite {

View file

@ -186,7 +186,7 @@ impl LineLayout {
if width > wrap_width && boundary > last_boundary {
// When used line_clamp, we should limit the number of lines.
if let Some(max_lines) = max_lines
&& boundaries.len() >= max_lines - 1
&& boundaries.len() >= max_lines.saturating_sub(1)
{
break;
}

View file

@ -1627,7 +1627,7 @@ pub struct DispatchEventResult {
/// Indicates which region of the window is visible. Content falling outside of this mask will not be
/// rendered. Currently, only rectangular content masks are supported, but we give the mask its own type
/// to leave room to support more complex shapes in the future.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
#[repr(C)]
pub struct ContentMask<P: Clone + Debug + Default + PartialEq> {
/// The bounds
@ -2713,7 +2713,7 @@ impl Window {
.set_active_node(deferred_draw.parent_node);
let paint_start = self.paint_index();
let content_mask = deferred_draw.content_mask.clone();
let content_mask = deferred_draw.content_mask;
if let Some(element) = deferred_draw.element.as_mut() {
self.with_rendered_view(deferred_draw.current_view, |window| {
window.with_content_mask(content_mask, |window| {
@ -2790,7 +2790,7 @@ impl Window {
parent_node: reused_subtree.refresh_node_id(deferred_draw.parent_node),
element_id_stack: deferred_draw.element_id_stack.clone(),
text_style_stack: deferred_draw.text_style_stack.clone(),
content_mask: deferred_draw.content_mask.clone(),
content_mask: deferred_draw.content_mask,
rem_size: deferred_draw.rem_size,
priority: deferred_draw.priority,
element: None,

View file

@ -44,7 +44,7 @@ impl PlatformAtlas for MetalAtlas {
) -> Result<Option<AtlasTile>> {
let mut lock = self.0.lock();
if let Some(tile) = lock.tiles_by_key.get(key) {
Ok(Some(tile.clone()))
Ok(Some(*tile))
} else {
let Some((size, bytes)) = build()? else {
return Ok(None);
@ -54,7 +54,7 @@ impl PlatformAtlas for MetalAtlas {
.context("failed to allocate")?;
let texture = lock.texture(tile.texture_id);
texture.upload(tile.bounds, &bytes);
lock.tiles_by_key.insert(key.clone(), tile.clone());
lock.tiles_by_key.insert(key.clone(), tile);
Ok(Some(tile))
}
}

View file

@ -1469,7 +1469,7 @@ impl MetalRenderer {
buffer_contents,
SurfaceBounds {
bounds: surface.bounds,
content_mask: surface.content_mask.clone(),
content_mask: surface.content_mask,
},
);
}

View file

@ -46,7 +46,7 @@ impl JsonSchema for SharedString {
impl Default for SharedString {
fn default() -> Self {
Self(ArcCow::Owned(Arc::default()))
Self(ArcCow::Borrowed(""))
}
}

View file

@ -103,7 +103,7 @@ impl PlatformAtlas for WgpuAtlas {
) -> Result<Option<AtlasTile>> {
let mut lock = self.0.lock();
if let Some(tile) = lock.tiles_by_key.get(key) {
Ok(Some(tile.clone()))
Ok(Some(*tile))
} else {
profiling::scope!("new tile");
let Some((size, bytes)) = build()? else {
@ -113,7 +113,7 @@ impl PlatformAtlas for WgpuAtlas {
.allocate(size, key.texture_kind())
.context("failed to allocate")?;
lock.upload_texture(tile.texture_id, tile.bounds, &bytes);
lock.tiles_by_key.insert(key.clone(), tile.clone());
lock.tiles_by_key.insert(key.clone(), tile);
Ok(Some(tile))
}
}

View file

@ -80,7 +80,7 @@ impl PlatformAtlas for DirectXAtlas {
) -> anyhow::Result<Option<AtlasTile>> {
let mut lock = self.0.lock();
if let Some(tile) = lock.tiles_by_key.get(key) {
Ok(Some(tile.clone()))
Ok(Some(*tile))
} else {
let Some((size, bytes)) = build()? else {
return Ok(None);
@ -90,7 +90,7 @@ impl PlatformAtlas for DirectXAtlas {
.ok_or_else(|| anyhow::anyhow!("failed to allocate"))?;
let texture = lock.texture(tile.texture_id);
texture.upload(&lock.device_context, tile.bounds, &bytes);
lock.tiles_by_key.insert(key.clone(), tile.clone());
lock.tiles_by_key.insert(key.clone(), tile);
Ok(Some(tile))
}
}

View file

@ -174,7 +174,7 @@ impl WindowsWindowState {
}
pub(crate) fn is_maximized(&self) -> bool {
!self.is_fullscreen() && unsafe { IsZoomed(self.hwnd) }.as_bool()
!self.is_fullscreen()
}
fn bounds(&self) -> Bounds<Pixels> {

View file

@ -103,15 +103,16 @@ impl ExcerptAnchor {
}
pub(crate) fn cmp(&self, other: &Self, snapshot: &MultiBufferSnapshot) -> Ordering {
let Some(self_path_key) = snapshot.path_keys_by_index.get(&self.path) else {
let Some(self_path_key) = snapshot.path_keys.get_index(self.path.0 as usize) else {
panic!("anchor's path was never added to multibuffer")
};
let Some(other_path_key) = snapshot.path_keys_by_index.get(&other.path) else {
let Some(other_path_key) = snapshot.path_keys.get_index(other.path.0 as usize) else {
panic!("anchor's path was never added to multibuffer")
};
if self_path_key.cmp(other_path_key) != Ordering::Equal {
return self_path_key.cmp(other_path_key);
match self_path_key.cmp(other_path_key) {
Ordering::Equal => (),
ordering => return ordering,
}
// in the case that you removed the buffer containing self,
@ -122,16 +123,19 @@ impl ExcerptAnchor {
}
// two anchors into the same buffer at the same path
// TODO(cole) buffer_for_path is slow
let Some(buffer) = snapshot
.buffer_for_path(&self_path_key)
.filter(|buffer| buffer.remote_id() == self.text_anchor.buffer_id)
.buffers
.get(&self.text_anchor.buffer_id)
.filter(|buffer_state| buffer_state.path_key == *self_path_key)
else {
// buffer no longer exists at the original path (which may have been reused for a different buffer),
// so no way to compare the anchors
return Ordering::Equal;
};
let text_cmp = self.text_anchor().cmp(&other.text_anchor(), buffer);
// two anchors into the same buffer at the same path that still exists at that path in the multibuffer
let text_cmp = self
.text_anchor()
.cmp(&other.text_anchor(), &buffer.buffer_snapshot);
if text_cmp != Ordering::Equal {
return text_cmp;
}

View file

@ -15,7 +15,7 @@ use buffer_diff::{
DiffHunkStatus, DiffHunkStatusKind,
};
use clock::ReplicaId;
use collections::{BTreeMap, Bound, HashMap, HashSet};
use collections::{BTreeMap, Bound, HashMap, HashSet, IndexSet};
use gpui::{App, Context, Entity, EventEmitter};
use itertools::Itertools;
use language::{
@ -676,7 +676,7 @@ impl DiffState {
#[derive(Clone)]
struct BufferStateSnapshot {
path_key: PathKey,
pub(crate) path_key: PathKey,
path_key_index: PathKeyIndex,
buffer_snapshot: BufferSnapshot,
}
@ -695,8 +695,7 @@ impl fmt::Debug for BufferStateSnapshot {
pub struct MultiBufferSnapshot {
excerpts: SumTree<Excerpt>,
buffers: TreeMap<BufferId, BufferStateSnapshot>,
path_keys_by_index: TreeMap<PathKeyIndex, PathKey>,
indices_by_path_key: TreeMap<PathKey, PathKeyIndex>,
path_keys: Arc<IndexSet<PathKey>>,
diffs: SumTree<DiffStateSnapshot>,
diff_transforms: SumTree<DiffTransform>,
non_text_state_update_count: usize,
@ -1802,8 +1801,7 @@ impl MultiBuffer {
show_deleted_hunks: _,
use_extended_diff_range: _,
show_headers: _,
path_keys_by_index: _,
indices_by_path_key: _,
path_keys: _,
buffers,
} = self.snapshot.get_mut();
let start = ExcerptDimension(MultiBufferOffset::ZERO);
@ -2497,8 +2495,7 @@ impl MultiBuffer {
excerpts,
diffs: buffer_diff,
buffers: buffer_snapshots,
path_keys_by_index: _,
indices_by_path_key: _,
path_keys: _,
diff_transforms: _,
non_text_state_update_count,
edit_count,
@ -3582,7 +3579,7 @@ impl MultiBufferSnapshot {
let Some(excerpt) = cursor.item() else {
break;
};
if &excerpt.path_key != path {
if excerpt.path_key != *path {
break;
}
let buffer_snapshot = excerpt.buffer_snapshot(self);
@ -6356,13 +6353,6 @@ impl MultiBufferSnapshot {
))
}
pub fn buffer_for_path(&self, path: &PathKey) -> Option<&BufferSnapshot> {
let (_, _, excerpt) = self
.excerpts
.find::<ExcerptSummary, _>((), path, Bias::Left);
Some(excerpt?.buffer_snapshot(self))
}
pub fn path_for_buffer(&self, buffer_id: BufferId) -> Option<&PathKey> {
Some(&self.buffers.get(&buffer_id)?.path_key)
}
@ -6378,9 +6368,7 @@ impl MultiBufferSnapshot {
}
fn first_excerpt_for_path(&self, path_key: &PathKey) -> Option<&Excerpt> {
let (_, _, first_excerpt) =
self.excerpts
.find::<ExcerptSummary, _>((), path_key, Bias::Left);
let (_, _, first_excerpt) = self.excerpts.find::<PathKey, _>((), path_key, Bias::Left);
first_excerpt
}
@ -6389,7 +6377,7 @@ impl MultiBufferSnapshot {
}
fn try_path_for_anchor(&self, anchor: ExcerptAnchor) -> Option<&PathKey> {
self.path_keys_by_index.get(&anchor.path)
self.path_keys.get_index(anchor.path.0 as usize)
}
pub fn path_for_anchor(&self, anchor: ExcerptAnchor) -> &PathKey {
@ -6831,7 +6819,7 @@ impl MultiBufferSnapshot {
excerpt.path_key
);
assert_eq!(
self.path_keys_by_index.get(&excerpt.path_key_index),
self.path_keys.get_index(excerpt.path_key_index.0 as usize),
Some(&excerpt.path_key),
"excerpt path key index does not match path key: {:#?}",
excerpt.path_key,
@ -7470,6 +7458,23 @@ impl sum_tree::SeekTarget<'_, ExcerptSummary, ExcerptSummary> for AnchorSeekTarg
}
}
impl sum_tree::ContextLessSummary for PathKey {
fn zero() -> Self {
PathKey::min()
}
fn add_summary(&mut self, summary: &Self) {
debug_assert!(
summary >= self,
"Path keys must be in ascending order: {:?} > {:?}",
summary,
self
);
*self = summary.clone();
}
}
impl sum_tree::SeekTarget<'_, ExcerptSummary, ExcerptSummary> for PathKey {
fn cmp(
&self,

View file

@ -253,8 +253,8 @@ impl MultiBuffer {
for (path_index, excerpt_anchors) in &buffers {
let path = snapshot
.path_keys_by_index
.get(&path_index)
.path_keys
.get_index(path_index.0 as usize)
.expect("anchor from wrong multibuffer");
let mut excerpt_anchors = excerpt_anchors.peekable();
@ -353,18 +353,15 @@ impl MultiBuffer {
pub(crate) fn get_or_create_path_key_index(&mut self, path_key: &PathKey) -> PathKeyIndex {
let mut snapshot = self.snapshot.borrow_mut();
if let Some(&existing) = snapshot.indices_by_path_key.get(path_key) {
return existing;
if let Some(existing) = snapshot.path_keys.get_index_of(path_key) {
return PathKeyIndex(existing as u64);
}
let index = snapshot
.path_keys_by_index
.last()
.map(|(index, _)| PathKeyIndex(index.0 + 1))
.unwrap_or(PathKeyIndex(0));
snapshot.path_keys_by_index.insert(index, path_key.clone());
snapshot.indices_by_path_key.insert(path_key.clone(), index);
index
PathKeyIndex(
Arc::make_mut(&mut snapshot.path_keys)
.insert_full(path_key.clone())
.0 as u64,
)
}
pub fn update_path_excerpts(

View file

@ -59,6 +59,7 @@ ui.workspace = true
util.workspace = true
workspace.workspace = true
zed_actions.workspace = true
arrayvec = "0.7.6"
[target.'cfg(windows)'.dependencies]
windows.workspace = true

View file

@ -7,6 +7,7 @@ mod update_version;
use crate::application_menu::{ApplicationMenu, show_menus};
use crate::plan_chip::PlanChip;
use arrayvec::ArrayVec;
use git_ui::worktree_picker::WorktreePicker;
pub use platform_title_bar::{
self, DraggedWindowTab, MergeAllWindows, MoveTabToNewWindow, PlatformTitleBar,
@ -177,7 +178,7 @@ impl Render for TitleBar {
let show_menus = show_menus(cx);
let mut children = Vec::new();
let mut children = <ArrayVec<_, 4>>::new();
let mut project_name = None;
let mut repository = None;