Add option for code context menu items to have dynamic width (#37404)

Follow up to https://github.com/zed-industries/zed/pull/30598

This PR introduces the `display_options` field in the
`CompletionResponse`, allowing a code context menu width to be
dynamically dictated based on its larger item. This will allow us to
have the @-mentions and slash commands completion menus in the agent
panel not be bigger than it needs to be. It may also be relevant/useful
in the future for other use cases.

For now, we set all instances of code context menus to use a fixed
width, as defined in the PR linked above, which means this PR shouldn't
cause any visual change.

Release Notes:

- N/A

Co-authored-by: Michael Sloan <mgsloan+github@gmail.com>
This commit is contained in:
Danilo Leal
2025-09-02 20:18:15 -03:00
committed by GitHub
co-authored by Michael Sloan
parent 4b96ad3fba
commit 946efb03df
11 changed files with 110 additions and 21 deletions
+39 -2
View File
@@ -11,9 +11,9 @@ use language::{Buffer, LanguageName, LanguageRegistry};
use markdown::{Markdown, MarkdownElement};
use multi_buffer::{Anchor, ExcerptId};
use ordered_float::OrderedFloat;
use project::CompletionSource;
use project::lsp_store::CompletionDocumentation;
use project::{CodeAction, Completion, TaskSourceKind};
use project::{CompletionDisplayOptions, CompletionSource};
use task::DebugScenario;
use task::TaskContext;
@@ -232,6 +232,7 @@ pub struct CompletionsMenu {
markdown_cache: Rc<RefCell<VecDeque<(MarkdownCacheKey, Entity<Markdown>)>>>,
language_registry: Option<Arc<LanguageRegistry>>,
language: Option<LanguageName>,
display_options: CompletionDisplayOptions,
snippet_sort_order: SnippetSortOrder,
}
@@ -271,6 +272,7 @@ impl CompletionsMenu {
is_incomplete: bool,
buffer: Entity<Buffer>,
completions: Box<[Completion]>,
display_options: CompletionDisplayOptions,
snippet_sort_order: SnippetSortOrder,
language_registry: Option<Arc<LanguageRegistry>>,
language: Option<LanguageName>,
@@ -304,6 +306,7 @@ impl CompletionsMenu {
markdown_cache: RefCell::new(VecDeque::new()).into(),
language_registry,
language,
display_options,
snippet_sort_order,
};
@@ -375,6 +378,7 @@ impl CompletionsMenu {
markdown_cache: RefCell::new(VecDeque::new()).into(),
language_registry: None,
language: None,
display_options: CompletionDisplayOptions::default(),
snippet_sort_order,
}
}
@@ -737,6 +741,33 @@ impl CompletionsMenu {
cx: &mut Context<Editor>,
) -> AnyElement {
let show_completion_documentation = self.show_completion_documentation;
let widest_completion_ix = if self.display_options.dynamic_width {
let completions = self.completions.borrow();
let widest_completion_ix = self
.entries
.borrow()
.iter()
.enumerate()
.max_by_key(|(_, mat)| {
let completion = &completions[mat.candidate_id];
let documentation = &completion.documentation;
let mut len = completion.label.text.chars().count();
if let Some(CompletionDocumentation::SingleLine(text)) = documentation {
if show_completion_documentation {
len += text.chars().count();
}
}
len
})
.map(|(ix, _)| ix);
drop(completions);
widest_completion_ix
} else {
None
};
let selected_item = self.selected_item;
let completions = self.completions.clone();
let entries = self.entries.clone();
@@ -863,7 +894,13 @@ impl CompletionsMenu {
.max_h(max_height_in_lines as f32 * window.line_height())
.track_scroll(self.scroll_handle.clone())
.with_sizing_behavior(ListSizingBehavior::Infer)
.w(rems(34.));
.map(|this| {
if self.display_options.dynamic_width {
this.with_width_from_item(widest_completion_ix)
} else {
this.w(rems(34.))
}
});
Popover::new().child(list).into_any_element()
}
+21 -8
View File
@@ -147,21 +147,22 @@ use multi_buffer::{
use parking_lot::Mutex;
use persistence::DB;
use project::{
BreakpointWithPosition, CodeAction, Completion, CompletionIntent, CompletionResponse,
CompletionSource, DisableAiSettings, DocumentHighlight, InlayHint, Location, LocationLink,
PrepareRenameResponse, Project, ProjectItem, ProjectPath, ProjectTransaction, TaskSourceKind,
debugger::breakpoint_store::Breakpoint,
BreakpointWithPosition, CodeAction, Completion, CompletionDisplayOptions, CompletionIntent,
CompletionResponse, CompletionSource, DisableAiSettings, DocumentHighlight, InlayHint,
Location, LocationLink, PrepareRenameResponse, Project, ProjectItem, ProjectPath,
ProjectTransaction, TaskSourceKind,
debugger::{
breakpoint_store::{
BreakpointEditAction, BreakpointSessionState, BreakpointState, BreakpointStore,
BreakpointStoreEvent,
Breakpoint, BreakpointEditAction, BreakpointSessionState, BreakpointState,
BreakpointStore, BreakpointStoreEvent,
},
session::{Session, SessionEvent},
},
git_store::{GitStoreEvent, RepositoryEvent},
lsp_store::{CompletionDocumentation, FormatTrigger, LspFormatTarget, OpenLspBufferHandle},
project_settings::{DiagnosticSeverity, GoToDiagnosticSeverityFilter},
project_settings::{GitGutterSetting, ProjectSettings},
project_settings::{
DiagnosticSeverity, GitGutterSetting, GoToDiagnosticSeverityFilter, ProjectSettings,
},
};
use rand::{seq::SliceRandom, thread_rng};
use rpc::{ErrorCode, ErrorExt, proto::PeerId};
@@ -5635,17 +5636,25 @@ impl Editor {
// that having one source with `is_incomplete: true` doesn't cause all to be re-queried.
let mut completions = Vec::new();
let mut is_incomplete = false;
let mut display_options: Option<CompletionDisplayOptions> = None;
if let Some(provider_responses) = provider_responses.await.log_err()
&& !provider_responses.is_empty()
{
for response in provider_responses {
completions.extend(response.completions);
is_incomplete = is_incomplete || response.is_incomplete;
match display_options.as_mut() {
None => {
display_options = Some(response.display_options);
}
Some(options) => options.merge(&response.display_options),
}
}
if completion_settings.words == WordsCompletionMode::Fallback {
words = Task::ready(BTreeMap::default());
}
}
let display_options = display_options.unwrap_or_default();
let mut words = words.await;
if let Some(word_to_exclude) = &word_to_exclude {
@@ -5687,6 +5696,7 @@ impl Editor {
is_incomplete,
buffer.clone(),
completions.into(),
display_options,
snippet_sort_order,
languages,
language,
@@ -22260,6 +22270,7 @@ fn snippet_completions(
if scopes.is_empty() {
return Task::ready(Ok(CompletionResponse {
completions: vec![],
display_options: CompletionDisplayOptions::default(),
is_incomplete: false,
}));
}
@@ -22284,6 +22295,7 @@ fn snippet_completions(
if last_word.is_empty() {
return Ok(CompletionResponse {
completions: vec![],
display_options: CompletionDisplayOptions::default(),
is_incomplete: true,
});
}
@@ -22405,6 +22417,7 @@ fn snippet_completions(
Ok(CompletionResponse {
completions,
display_options: CompletionDisplayOptions::default(),
is_incomplete,
})
})