Compare commits

...

13 Commits

Author SHA1 Message Date
Conrad Irwin
14b2cd06a7 Fix panic in vim text-objects (#22753)
Caused by messing up offsets between multi-buffers and excerpts :(

Fixes #22739

Release Notes:

- Fixed a panic in vim text objects in multibuffers
2025-01-08 11:10:33 -05:00
Peter Tripp
5d35ba34e8 v0.168.x stable 2025-01-08 11:00:58 -05:00
gcp-cherry-pick-bot[bot]
b1b8fc02b8 Fix panic in request_multiple_lsp_locally (cherry-pick #22806) (#22807)
Cherry-picked Fix panic in request_multiple_lsp_locally (#22806)

Release Notes:

- Fix a panic after disconnecting from a remote project

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-01-07 23:38:38 -07:00
gcp-cherry-pick-bot[bot]
a8ef5aa87c Reduce amount of workspace serialization happening (cherry-pick #22730) (#22763)
Cherry-picked Reduce amount of workspace serialization happening
(#22730)

Part of https://github.com/zed-industries/zed/issues/16472

Reduces amount of workspace serialization happening by:
* fixing the deserialization logic: now it does not set panels that are
hidden to active
* cleaning up `active_panel_index` for docks that are closed, to avoid
emitting extra events for such closed docks
* adjusting outline panel to drop active editor subscriptions on
deactivation — this way, `cx.observe` on the dock with outline panel is
not triggered (used to be triggered on every selection change before)
* adjusting workspace dock drag listener to remember previous
coordinates and only resize the dock if those had changed
* adjusting workspace pane event listener to ignore
`pane::Event::UserSavedItem` and `pane::Event::ChangeItemTitle` that
seem to happen relatively frequently but not influence values that are
serialized for the workspace
* not using `cx.observe` on docks, instead explicitly serializing on
panel zoom and size changes

Release Notes:

- Reduced amount of workspace serialization happening

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2025-01-07 13:40:02 +02:00
Zed Bot
e9f548639c Bump to 0.168.2 for @maxdeviant 2025-01-06 18:54:00 +00:00
gcp-cherry-pick-bot[bot]
37a019ec7a zed: Add timeouts for feature flag resolution in workspace panel initialization (cherry-pick #22715) (#22721)
Cherry-picked zed: Add timeouts for feature flag resolution in workspace
panel initialization (#22715)

This PR adds timeouts when resolving feature flags during workspace
panel initialization so that we don't block indefinitely if Zed is not
connected to the internet.

Right now we wait for 5 seconds, but this value was chosen arbitrarily.

Release Notes:

- N/A

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2025-01-06 13:01:45 -05:00
gcp-cherry-pick-bot[bot]
46f0a6860d Return back Rust completion details (cherry-pick #22648) (#22651)
Cherry-picked Return back Rust completion details (#22648)

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

In Zed, Rust's label generators expected the details to come in ` (use
std.foo.Bar)` form, but recently, r-a started to send these details
without the leading whitespace which broke the code generation.

The PR makes LSP results parsing more lenient to work with both details'
forms.

Release Notes:

- Fixed Rust completion labels not showing the imports

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2025-01-04 13:49:13 +02:00
gcp-cherry-pick-bot[bot]
74c82e1e1a language_model_selector: Refresh the models when the providers change (cherry-pick #22624) (#22626)
Cherry-picked language_model_selector: Refresh the models when the
providers change (#22624)

This PR fixes an issue introduced in #21939 where the list of models in
the language model selector could be outdated.

Since we're no longer recreating the picker each render, we now need to
make sure we are updating the list of models accordingly when there are
changes to the language model providers.

I noticed it specifically in Assistant1.

Release Notes:

- Fixed a staleness issue with the language model selector.

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2025-01-03 15:33:08 -05:00
Peter Tripp
da62407ef0 zed 0.168.1 2025-01-03 11:06:25 -05:00
Agus Zubiaga
7d92b14e85 Fix vertical alignment when jumping from multibuffers (#22613)
Clicking buffer headers and line numbers would sometimes take you to a
disorienting scroll position. This PR improves that so the destination
line is roughly at the same Y position as it appeared in the
multibuffer.



https://github.com/user-attachments/assets/3ad71537-cf26-4136-948f-c5a96df57178


**Note**: The alignment won't always be perfect because the multibuffer
and target buffer might start at a different absolute Y position
(because of open search, breadcrumbs, etc). I wanted to compensate for
that, but that requires a fundamental change that I'd prefer to make
separately.

Release Notes:

- Fix vertical alignment when jumping from multibuffers
2025-01-03 11:01:44 -05:00
Kirill Bulatov
03c261566b Fix tooltips too eager to disappear when there's a gap between the tooltip source and the tooltip itself (#22583)
Follow-up of https://github.com/zed-industries/zed/pull/22548

Release Notes:

- N/A

Co-authored-by: Peter Tripp <peter@zed.dev>
2025-01-03 11:12:17 +02:00
gcp-cherry-pick-bot[bot]
b800ff51c0 Remove stuck tooltips (cherry-pick #22548) (#22550)
Cherry-picked Remove stuck tooltips (#22548)

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

Follow-up of https://github.com/zed-industries/zed/pull/22488
Previous PR broke git blame tooltips, which are expected to be open when
hovered, even if the mouse cursor is moved away from the actual blame
entry that caused the tooltip to appear.

Current version moves the invalidation logic into `prepaint_tooltip`,
where the new data about the tooltip origin is used to ensure we
invalidate only tooltips that have no mouse cursor in either origin
bounds or tooltip bounds (if it's hoverable).


Release Notes:

- Fixed tooltips getting stuck

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2025-01-01 21:07:39 +02:00
Peter Tripp
56f288e635 v0.168.x preview 2025-01-01 12:23:34 -05:00
17 changed files with 276 additions and 140 deletions

2
Cargo.lock generated
View File

@@ -16204,7 +16204,7 @@ dependencies = [
[[package]]
name = "zed"
version = "0.168.0"
version = "0.168.2"
dependencies = [
"activity_indicator",
"anyhow",

View File

@@ -993,7 +993,10 @@ pub(crate) struct FocusedBlock {
#[derive(Clone)]
enum JumpData {
MultiBufferRow(MultiBufferRow),
MultiBufferRow {
row: MultiBufferRow,
line_offset_from_top: u32,
},
MultiBufferPoint {
excerpt_id: ExcerptId,
position: Point,
@@ -12487,7 +12490,10 @@ impl Editor {
);
}
}
Some(JumpData::MultiBufferRow(row)) => {
Some(JumpData::MultiBufferRow {
row,
line_offset_from_top,
}) => {
let point = MultiBufferPoint::new(row.0, 0);
if let Some((buffer, buffer_point, _)) =
self.buffer.read(cx).point_to_buffer_point(point, cx)
@@ -12495,7 +12501,7 @@ impl Editor {
let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
new_selections_by_buffer
.entry(buffer)
.or_insert((Vec::new(), None))
.or_insert((Vec::new(), Some(*line_offset_from_top)))
.0
.push(buffer_offset..buffer_offset)
}

View File

@@ -599,8 +599,15 @@ impl EditorElement {
.row;
if let Some((_, Some(hitbox))) = line_numbers.get(&MultiBufferRow(multi_buffer_row)) {
if hitbox.contains(&event.position) {
let scroll_position_row =
position_map.scroll_pixel_position.y / position_map.line_height;
let line_offset_from_top = display_row - scroll_position_row as u32;
editor.open_excerpts_common(
Some(JumpData::MultiBufferRow(MultiBufferRow(multi_buffer_row))),
Some(JumpData::MultiBufferRow {
row: MultiBufferRow(multi_buffer_row),
line_offset_from_top,
}),
modifiers.alt,
cx,
);
@@ -2959,7 +2966,12 @@ impl EditorElement {
selected_buffer_ids: &Vec<BufferId>,
cx: &mut WindowContext,
) -> AnyElement {
let jump_data = header_jump_data(snapshot, DisplayRow(0), FILE_HEADER_HEIGHT, excerpt);
let jump_data = header_jump_data(
snapshot,
DisplayRow(scroll_position as u32),
FILE_HEADER_HEIGHT + MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
excerpt,
);
let editor_bg_color = cx.theme().colors().editor_background;
@@ -5096,13 +5108,12 @@ fn header_jump_data(
let offset_from_excerpt_start = if jump_anchor == excerpt_start {
0
} else {
let excerpt_start_row = language::ToPoint::to_point(&jump_anchor, buffer).row;
let excerpt_start_row = language::ToPoint::to_point(&excerpt_start, buffer).row;
jump_position.row - excerpt_start_row
};
let line_offset_from_top = block_row_start.0
+ height
+ offset_from_excerpt_start.saturating_sub(
let line_offset_from_top = (block_row_start.0 + height + offset_from_excerpt_start)
.saturating_sub(
snapshot
.scroll_anchor
.scroll_position(&snapshot.display_snapshot)

View File

@@ -33,9 +33,9 @@ use util::ResultExt;
use crate::{
current_platform, hash, init_app_menus, Action, ActionRegistry, Any, AnyView, AnyWindowHandle,
Asset, AssetSource, BackgroundExecutor, ClipboardItem, Context, DispatchPhase, DisplayId,
Entity, EventEmitter, FocusHandle, FocusId, ForegroundExecutor, Global, KeyBinding, Keymap,
Keystroke, LayoutId, Menu, MenuItem, OwnedMenu, PathPromptOptions, Pixels, Platform,
Asset, AssetSource, BackgroundExecutor, Bounds, ClipboardItem, Context, DispatchPhase,
DisplayId, Entity, EventEmitter, FocusHandle, FocusId, ForegroundExecutor, Global, KeyBinding,
Keymap, Keystroke, LayoutId, Menu, MenuItem, OwnedMenu, PathPromptOptions, Pixels, Platform,
PlatformDisplay, Point, PromptBuilder, PromptHandle, PromptLevel, Render,
RenderablePromptHandle, Reservation, ScreenCaptureSource, SharedString, SubscriberSet,
Subscription, SvgRenderer, Task, TextSystem, View, ViewContext, Window, WindowAppearance,
@@ -1612,6 +1612,12 @@ pub struct AnyTooltip {
/// The absolute position of the mouse when the tooltip was deployed.
pub mouse_position: Point<Pixels>,
/// Whether the tooltitp can be hovered or not.
pub hoverable: bool,
/// Bounds of the element that triggered the tooltip appearance.
pub origin_bounds: Bounds<Pixels>,
}
/// A keystroke event, and potentially the associated action

View File

@@ -1923,6 +1923,7 @@ impl Interactivity {
cx.on_mouse_event({
let active_tooltip = active_tooltip.clone();
let hitbox = hitbox.clone();
let source_bounds = hitbox.bounds;
let tooltip_id = self.tooltip_id;
move |_: &MouseMoveEvent, phase, cx| {
let is_hovered =
@@ -1952,6 +1953,8 @@ impl Interactivity {
tooltip: Some(AnyTooltip {
view: build_tooltip(cx),
mouse_position: cx.mouse_position(),
hoverable: tooltip_is_hoverable,
origin_bounds: source_bounds,
}),
_task: None,
});

View File

@@ -675,6 +675,7 @@ impl Element for InteractiveText {
if let Some(tooltip_builder) = self.tooltip_builder.clone() {
let hitbox = hitbox.clone();
let source_bounds = hitbox.bounds;
let active_tooltip = interactive_state.active_tooltip.clone();
let pending_mouse_down = interactive_state.mouse_down_index.clone();
let text_layout = text_layout.clone();
@@ -708,6 +709,8 @@ impl Element for InteractiveText {
tooltip: Some(AnyTooltip {
view: tooltip,
mouse_position: cx.mouse_position(),
hoverable: true,
origin_bounds: source_bounds,
}),
_task: None,
}

View File

@@ -1586,6 +1586,19 @@ impl<'a> WindowContext<'a> {
}
}
// Element's parent can get hidden (e.g. via the `visible_on_hover` method),
// and element's `paint` won't be called (ergo, mouse listeners also won't be active) to detect that the tooltip has to be removed.
// Ensure it's not stuck around in such cases.
let invalidate_tooltip = !tooltip_request
.tooltip
.origin_bounds
.contains(&self.mouse_position())
&& (!tooltip_request.tooltip.hoverable
|| !tooltip_bounds.contains(&self.mouse_position()));
if invalidate_tooltip {
return None;
}
self.with_absolute_element_offset(tooltip_bounds.origin, |cx| element.prepaint(cx));
self.window.tooltip_bounds = Some(TooltipBounds {

View File

@@ -2,8 +2,8 @@ use std::sync::Arc;
use feature_flags::ZedPro;
use gpui::{
Action, AnyElement, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Task,
View, WeakView,
Action, AnyElement, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Model,
Subscription, Task, View, WeakView,
};
use language_model::{LanguageModel, LanguageModelAvailability, LanguageModelRegistry};
use picker::{Picker, PickerDelegate};
@@ -17,6 +17,10 @@ type OnModelChanged = Arc<dyn Fn(Arc<dyn LanguageModel>, &AppContext) + 'static>
pub struct LanguageModelSelector {
picker: View<Picker<LanguageModelPickerDelegate>>,
/// The task used to update the picker's matches when there is a change to
/// the language model registry.
update_matches_task: Option<Task<()>>,
_subscriptions: Vec<Subscription>,
}
impl LanguageModelSelector {
@@ -26,7 +30,51 @@ impl LanguageModelSelector {
) -> Self {
let on_model_changed = Arc::new(on_model_changed);
let all_models = LanguageModelRegistry::global(cx)
let all_models = Self::all_models(cx);
let delegate = LanguageModelPickerDelegate {
language_model_selector: cx.view().downgrade(),
on_model_changed: on_model_changed.clone(),
all_models: all_models.clone(),
filtered_models: all_models,
selected_index: 0,
};
let picker =
cx.new_view(|cx| Picker::uniform_list(delegate, cx).max_height(Some(rems(20.).into())));
LanguageModelSelector {
picker,
update_matches_task: None,
_subscriptions: vec![cx.subscribe(
&LanguageModelRegistry::global(cx),
Self::handle_language_model_registry_event,
)],
}
}
fn handle_language_model_registry_event(
&mut self,
_registry: Model<LanguageModelRegistry>,
event: &language_model::Event,
cx: &mut ViewContext<Self>,
) {
match event {
language_model::Event::ProviderStateChanged
| language_model::Event::AddedProvider(_)
| language_model::Event::RemovedProvider(_) => {
let task = self.picker.update(cx, |this, cx| {
let query = this.query(cx);
this.delegate.all_models = Self::all_models(cx);
this.delegate.update_matches(query, cx)
});
self.update_matches_task = Some(task);
}
_ => {}
}
}
fn all_models(cx: &AppContext) -> Vec<ModelInfo> {
LanguageModelRegistry::global(cx)
.read(cx)
.providers()
.iter()
@@ -44,20 +92,7 @@ impl LanguageModelSelector {
}
})
})
.collect::<Vec<_>>();
let delegate = LanguageModelPickerDelegate {
language_model_selector: cx.view().downgrade(),
on_model_changed: on_model_changed.clone(),
all_models: all_models.clone(),
filtered_models: all_models,
selected_index: 0,
};
let picker =
cx.new_view(|cx| Picker::uniform_list(delegate, cx).max_height(Some(rems(20.).into())));
LanguageModelSelector { picker }
.collect::<Vec<_>>()
}
}
@@ -152,25 +187,25 @@ impl PickerDelegate for LanguageModelPickerDelegate {
let llm_registry = LanguageModelRegistry::global(cx);
let configured_models: Vec<_> = llm_registry
let configured_providers = llm_registry
.read(cx)
.providers()
.iter()
.filter(|provider| provider.is_authenticated(cx))
.map(|provider| provider.id())
.collect();
.collect::<Vec<_>>();
cx.spawn(|this, mut cx| async move {
let filtered_models = cx
.background_executor()
.spawn(async move {
let displayed_models = if configured_models.is_empty() {
let displayed_models = if configured_providers.is_empty() {
all_models
} else {
all_models
.into_iter()
.filter(|model_info| {
configured_models.contains(&model_info.model.provider_id())
configured_providers.contains(&model_info.model.provider_id())
})
.collect::<Vec<_>>()
};

View File

@@ -253,49 +253,51 @@ impl LspAdapter for RustLspAdapter {
.as_ref()
.and_then(|detail| detail.detail.as_ref())
.or(completion.detail.as_ref())
.map(ToOwned::to_owned);
.map(|detail| detail.trim());
let function_signature = completion
.label_details
.as_ref()
.and_then(|detail| detail.description.as_ref())
.or(completion.detail.as_ref())
.map(ToOwned::to_owned);
match completion.kind {
Some(lsp::CompletionItemKind::FIELD) if detail.is_some() => {
.and_then(|detail| detail.description.as_deref())
.or(completion.detail.as_deref());
match (detail, completion.kind) {
(Some(detail), Some(lsp::CompletionItemKind::FIELD)) => {
let name = &completion.label;
let text = format!("{}: {}", name, detail.unwrap());
let source = Rope::from(format!("struct S {{ {} }}", text).as_str());
let runs = language.highlight_text(&source, 11..11 + text.len());
let text = format!("{name}: {detail}");
let prefix = "struct S { ";
let source = Rope::from(format!("{prefix}{text} }}"));
let runs =
language.highlight_text(&source, prefix.len()..prefix.len() + text.len());
return Some(CodeLabel {
text,
runs,
filter_range: 0..name.len(),
});
}
Some(lsp::CompletionItemKind::CONSTANT | lsp::CompletionItemKind::VARIABLE)
if detail.is_some()
&& completion.insert_text_format != Some(lsp::InsertTextFormat::SNIPPET) =>
{
(
Some(detail),
Some(lsp::CompletionItemKind::CONSTANT | lsp::CompletionItemKind::VARIABLE),
) if completion.insert_text_format != Some(lsp::InsertTextFormat::SNIPPET) => {
let name = &completion.label;
let text = format!(
"{}: {}",
name,
completion.detail.as_ref().or(detail.as_ref()).unwrap()
completion.detail.as_deref().unwrap_or(detail)
);
let source = Rope::from(format!("let {} = ();", text).as_str());
let runs = language.highlight_text(&source, 4..4 + text.len());
let prefix = "let ";
let source = Rope::from(format!("{prefix}{text} = ();"));
let runs =
language.highlight_text(&source, prefix.len()..prefix.len() + text.len());
return Some(CodeLabel {
text,
runs,
filter_range: 0..name.len(),
});
}
Some(lsp::CompletionItemKind::FUNCTION | lsp::CompletionItemKind::METHOD)
if detail.is_some() =>
{
(
Some(detail),
Some(lsp::CompletionItemKind::FUNCTION | lsp::CompletionItemKind::METHOD),
) => {
static REGEX: LazyLock<Regex> = LazyLock::new(|| Regex::new("\\(…?\\)").unwrap());
let detail = detail.unwrap();
const FUNCTION_PREFIXES: [&str; 6] = [
"async fn",
"async unsafe fn",
@@ -315,10 +317,11 @@ impl LspAdapter for RustLspAdapter {
// fn keyword should be followed by opening parenthesis.
if let Some((prefix, suffix)) = fn_keyword {
let mut text = REGEX.replace(&completion.label, suffix).to_string();
let source = Rope::from(format!("{prefix} {} {{}}", text).as_str());
let source = Rope::from(format!("{prefix} {text} {{}}"));
let run_start = prefix.len() + 1;
let runs = language.highlight_text(&source, run_start..run_start + text.len());
if detail.starts_with(" (") {
if detail.starts_with("(") {
text.push(' ');
text.push_str(&detail);
}
@@ -342,7 +345,7 @@ impl LspAdapter for RustLspAdapter {
});
}
}
Some(kind) => {
(_, Some(kind)) => {
let highlight_name = match kind {
lsp::CompletionItemKind::STRUCT
| lsp::CompletionItemKind::INTERFACE
@@ -356,9 +359,9 @@ impl LspAdapter for RustLspAdapter {
};
let mut label = completion.label.clone();
if let Some(detail) = detail.filter(|detail| detail.starts_with(" (")) {
use std::fmt::Write;
write!(label, "{detail}").ok()?;
if let Some(detail) = detail.filter(|detail| detail.starts_with("(")) {
label.push(' ');
label.push_str(detail);
}
let mut label = CodeLabel::plain(label, None);
if let Some(highlight_name) = highlight_name {
@@ -883,7 +886,7 @@ mod tests {
kind: Some(lsp::CompletionItemKind::FUNCTION),
label: "hello(…)".to_string(),
label_details: Some(CompletionItemLabelDetails {
detail: Some(" (use crate::foo)".into()),
detail: Some("(use crate::foo)".into()),
description: Some("fn(&mut Option<T>) -> Vec<T>".to_string())
}),
..Default::default()

View File

@@ -4447,18 +4447,27 @@ impl Panel for OutlinePanel {
.update(&mut cx, |outline_panel, cx| {
let old_active = outline_panel.active;
outline_panel.active = active;
if active && old_active != active {
if let Some((active_item, active_editor)) = outline_panel
.workspace
.upgrade()
.and_then(|workspace| workspace_active_editor(workspace.read(cx), cx))
{
if outline_panel.should_replace_active_item(active_item.as_ref()) {
outline_panel.replace_active_editor(active_item, active_editor, cx);
} else {
outline_panel.update_fs_entries(active_editor, None, cx)
if old_active != active {
if active {
if let Some((active_item, active_editor)) =
outline_panel.workspace.upgrade().and_then(|workspace| {
workspace_active_editor(workspace.read(cx), cx)
})
{
if outline_panel.should_replace_active_item(active_item.as_ref()) {
outline_panel.replace_active_editor(
active_item,
active_editor,
cx,
);
} else {
outline_panel.update_fs_entries(active_editor, None, cx)
}
return;
}
} else if !outline_panel.pinned {
}
if !outline_panel.pinned {
outline_panel.clear_previous(cx);
}
}
@@ -4605,9 +4614,11 @@ fn subscribe_for_editor_events(
cx: &mut ViewContext<OutlinePanel>,
) -> Subscription {
let debounce = Some(UPDATE_DEBOUNCE);
cx.subscribe(
editor,
move |outline_panel, editor, e: &EditorEvent, cx| match e {
cx.subscribe(editor, move |outline_panel, editor, e: &EditorEvent, cx| {
if !outline_panel.active {
return;
}
match e {
EditorEvent::SelectionsChanged { local: true } => {
outline_panel.reveal_entry_for_selection(editor, cx);
cx.notify();
@@ -4712,8 +4723,8 @@ fn subscribe_for_editor_events(
outline_panel.update_non_fs_items(cx);
}
_ => {}
},
)
}
})
}
fn empty_icon() -> AnyElement {

View File

@@ -5586,13 +5586,13 @@ impl LspStore {
<R::LspRequest as lsp::request::Request>::Result: Send,
<R::LspRequest as lsp::request::Request>::Params: Send,
{
debug_assert!(self.upstream_client().is_none());
let Some(local) = self.as_local() else {
return Task::ready(Vec::new());
};
let snapshot = buffer.read(cx).snapshot();
let scope = position.and_then(|position| snapshot.language_scope_at(position));
let server_ids = self
.as_local()
.unwrap()
let server_ids = local
.language_servers_for_buffer(buffer.read(cx), cx)
.filter(|(adapter, _)| {
scope

View File

@@ -514,13 +514,15 @@ fn text_object(
let excerpt = snapshot.excerpt_containing(offset..offset)?;
let buffer = excerpt.buffer();
let offset = excerpt.map_offset_to_buffer(offset);
let mut matches: Vec<Range<usize>> = buffer
.text_object_ranges(offset..offset, TreeSitterOptions::default())
.filter_map(|(r, m)| if m == target { Some(r) } else { None })
.collect();
matches.sort_by_key(|r| (r.end - r.start));
if let Some(range) = matches.first() {
if let Some(buffer_range) = matches.first() {
let range = excerpt.map_range_from_buffer(buffer_range.clone());
return Some(range.start.to_display_point(map)..range.end.to_display_point(map));
}
@@ -537,12 +539,14 @@ fn text_object(
.filter_map(|(r, m)| if m == target { Some(r) } else { None })
.collect();
matches.sort_by_key(|r| r.start);
if let Some(range) = matches.first() {
if !range.is_empty() {
if let Some(buffer_range) = matches.first() {
if !buffer_range.is_empty() {
let range = excerpt.map_range_from_buffer(buffer_range.clone());
return Some(range.start.to_display_point(map)..range.end.to_display_point(map));
}
}
return Some(around_range.start.to_display_point(map)..around_range.end.to_display_point(map));
let buffer_range = excerpt.map_range_from_buffer(around_range.clone());
return Some(buffer_range.start.to_display_point(map)..buffer_range.end.to_display_point(map));
}
fn argument(

View File

@@ -170,6 +170,7 @@ impl From<&dyn PanelHandle> for AnyView {
pub struct Dock {
position: DockPosition,
panel_entries: Vec<PanelEntry>,
workspace: WeakView<Workspace>,
is_open: bool,
active_panel_index: Option<usize>,
focus_handle: FocusHandle,
@@ -236,6 +237,7 @@ impl Dock {
});
Self {
position,
workspace: workspace.downgrade(),
panel_entries: Default::default(),
active_panel_index: None,
is_open: false,
@@ -337,6 +339,9 @@ impl Dock {
self.is_open = open;
if let Some(active_panel) = self.active_panel_entry() {
active_panel.panel.set_active(open, cx);
if !open {
self.active_panel_index = None;
}
}
cx.notify();
@@ -354,6 +359,11 @@ impl Dock {
}
}
self.workspace
.update(cx, |workspace, cx| {
workspace.serialize_workspace(cx);
})
.ok();
cx.notify();
}
@@ -484,7 +494,8 @@ impl Dock {
},
);
if !self.restore_state(cx) && panel.read(cx).starts_open(cx) {
self.restore_state(cx);
if panel.read(cx).starts_open(cx) {
self.activate_panel(index, cx);
self.set_open(true, cx);
}
@@ -652,9 +663,14 @@ impl Render for Dock {
)
.on_mouse_up(
MouseButton::Left,
cx.listener(|v, e: &MouseUpEvent, cx| {
cx.listener(|dock, e: &MouseUpEvent, cx| {
if e.click_count == 2 {
v.resize_active_panel(None, cx);
dock.resize_active_panel(None, cx);
dock.workspace
.update(cx, |workspace, cx| {
workspace.serialize_workspace(cx);
})
.ok();
cx.stop_propagation();
}
}),

View File

@@ -743,6 +743,7 @@ pub struct Workspace {
weak_self: WeakView<Self>,
workspace_actions: Vec<Box<dyn Fn(Div, &mut ViewContext<Self>) -> Div>>,
zoomed: Option<AnyWeakView>,
previous_dock_drag_coordinates: Option<Point<Pixels>>,
zoomed_position: Option<DockPosition>,
center: PaneGroup,
left_dock: View<Dock>,
@@ -1020,18 +1021,6 @@ impl Workspace {
ThemeSettings::reload_current_theme(cx);
}),
cx.observe(&left_dock, |this, _, cx| {
this.serialize_workspace(cx);
cx.notify();
}),
cx.observe(&bottom_dock, |this, _, cx| {
this.serialize_workspace(cx);
cx.notify();
}),
cx.observe(&right_dock, |this, _, cx| {
this.serialize_workspace(cx);
cx.notify();
}),
cx.on_release(|this, window, cx| {
this.app_state.workspace_store.update(cx, |store, _| {
let window = window.downcast::<Self>().unwrap();
@@ -1047,6 +1036,7 @@ impl Workspace {
weak_self: weak_handle.clone(),
zoomed: None,
zoomed_position: None,
previous_dock_drag_coordinates: None,
center: PaneGroup::new(center_pane.clone()),
panes: vec![center_pane.clone()],
panes_by_item: Default::default(),
@@ -3068,6 +3058,7 @@ impl Workspace {
event: &pane::Event,
cx: &mut ViewContext<Self>,
) {
let mut serialize_workspace = true;
match event {
pane::Event::AddItem { item } => {
item.added_to_pane(self, pane, cx);
@@ -3078,10 +3069,14 @@ impl Workspace {
pane::Event::Split(direction) => {
self.split_and_clone(pane, *direction, cx);
}
pane::Event::JoinIntoNext => self.join_pane_into_next(pane, cx),
pane::Event::JoinAll => self.join_all_panes(cx),
pane::Event::JoinIntoNext => {
self.join_pane_into_next(pane, cx);
}
pane::Event::JoinAll => {
self.join_all_panes(cx);
}
pane::Event::Remove { focus_on_pane } => {
self.remove_pane(pane, focus_on_pane.clone(), cx)
self.remove_pane(pane, focus_on_pane.clone(), cx);
}
pane::Event::ActivateItem { local } => {
cx.on_next_frame(|_, cx| {
@@ -3099,16 +3094,20 @@ impl Workspace {
self.update_active_view_for_followers(cx);
}
}
pane::Event::UserSavedItem { item, save_intent } => cx.emit(Event::UserSavedItem {
pane: pane.downgrade(),
item: item.boxed_clone(),
save_intent: *save_intent,
}),
pane::Event::UserSavedItem { item, save_intent } => {
cx.emit(Event::UserSavedItem {
pane: pane.downgrade(),
item: item.boxed_clone(),
save_intent: *save_intent,
});
serialize_workspace = false;
}
pane::Event::ChangeItemTitle => {
if pane == self.active_pane {
self.active_item_path_changed(cx);
}
self.update_window_edited(cx);
serialize_workspace = false;
}
pane::Event::RemoveItem { .. } => {}
pane::Event::RemovedItem { item_id } => {
@@ -3147,7 +3146,9 @@ impl Workspace {
}
}
self.serialize_workspace(cx);
if serialize_workspace {
self.serialize_workspace(cx);
}
}
pub fn unfollow_in_pane(
@@ -4908,32 +4909,39 @@ impl Render for Workspace {
})
.when(self.zoomed.is_none(), |this| {
this.on_drag_move(cx.listener(
|workspace, e: &DragMoveEvent<DraggedDock>, cx| {
match e.drag(cx).0 {
DockPosition::Left => {
resize_left_dock(
e.event.position.x
- workspace.bounds.left(),
workspace,
cx,
);
}
DockPosition::Right => {
resize_right_dock(
workspace.bounds.right()
- e.event.position.x,
workspace,
cx,
);
}
DockPosition::Bottom => {
resize_bottom_dock(
workspace.bounds.bottom()
- e.event.position.y,
workspace,
cx,
);
}
move |workspace, e: &DragMoveEvent<DraggedDock>, cx| {
if workspace.previous_dock_drag_coordinates
!= Some(e.event.position)
{
workspace.previous_dock_drag_coordinates =
Some(e.event.position);
match e.drag(cx).0 {
DockPosition::Left => {
resize_left_dock(
e.event.position.x
- workspace.bounds.left(),
workspace,
cx,
);
}
DockPosition::Right => {
resize_right_dock(
workspace.bounds.right()
- e.event.position.x,
workspace,
cx,
);
}
DockPosition::Bottom => {
resize_bottom_dock(
workspace.bounds.bottom()
- e.event.position.y,
workspace,
cx,
);
}
};
workspace.serialize_workspace(cx);
}
},
))

View File

@@ -2,7 +2,7 @@
description = "The fast, collaborative code editor."
edition = "2021"
name = "zed"
version = "0.168.0"
version = "0.168.2"
publish = false
license = "GPL-3.0-or-later"
authors = ["Zed Team <hi@zed.dev>"]

View File

@@ -1 +1 @@
dev
stable

View File

@@ -20,6 +20,7 @@ use command_palette_hooks::CommandPaletteFilter;
use editor::ProposedChangesEditorToolbar;
use editor::{scroll::Autoscroll, Editor, MultiBuffer};
use feature_flags::FeatureFlagAppExt;
use futures::FutureExt;
use futures::{channel::mpsc, select_biased, StreamExt};
use gpui::{
actions, point, px, AppContext, AsyncAppContext, Context, FocusableView, MenuItem,
@@ -348,7 +349,16 @@ fn initialize_panels(prompt_builder: Arc<PromptBuilder>, cx: &mut ViewContext<Wo
workspace.add_panel(assistant_panel, cx)
})?;
let git_ui_enabled = git_ui_feature_flag.await;
let git_ui_enabled = {
let mut git_ui_feature_flag = git_ui_feature_flag.fuse();
let mut timeout =
FutureExt::fuse(smol::Timer::after(std::time::Duration::from_secs(5)));
select_biased! {
is_git_ui_enabled = git_ui_feature_flag => is_git_ui_enabled,
_ = timeout => false,
}
};
let git_panel = if git_ui_enabled {
Some(git_ui::git_panel::GitPanel::load(workspace_handle.clone(), cx.clone()).await?)
} else {
@@ -363,7 +373,14 @@ fn initialize_panels(prompt_builder: Arc<PromptBuilder>, cx: &mut ViewContext<Wo
let is_assistant2_enabled = if cfg!(test) {
false
} else {
assistant2_feature_flag.await
let mut assistant2_feature_flag = assistant2_feature_flag.fuse();
let mut timeout =
FutureExt::fuse(smol::Timer::after(std::time::Duration::from_secs(5)));
select_biased! {
is_assistant2_enabled = assistant2_feature_flag => is_assistant2_enabled,
_ = timeout => false,
}
};
let assistant2_panel = if is_assistant2_enabled {
Some(assistant2::AssistantPanel::load(workspace_handle.clone(), cx.clone()).await?)