Compare commits

...

10 Commits

Author SHA1 Message Date
Conrad Irwin
e8fe5de669 Use pkexec without temp file
Updates #22045
2025-01-08 09:50:33 -07:00
Peter Tripp
83889bb235 Bump Zed to v0.170 (#22838) 2025-01-08 11:02:44 -05:00
Peter Tripp
ebc4688c2a Fix script/bump-zed-minor-versions. Revert #22834 Revert #22614 (#22837)
Fixes an incorrect error message.
Turns out it is impossible to set remote tracking to a branch that doesn't exist on the remote, so let's not even try.

Reverts #22834
Reverts #22614
2025-01-08 10:59:30 -05:00
Peter Tripp
7f0e13258c Fix upstream branch tracking error in script/bump-zed-minor-versions (#22834)
Follow-up to: https://github.com/zed-industries/zed/pull/22614
2025-01-08 10:38:56 -05:00
Danilo Leal
8cd2afeacc Improve MessageNotification design (#22829)
Just fine-tuning some bits of the visual design.

| Before | After |
|--------|--------|
| <img width="1426" alt="Screenshot 2025-01-08 at 11 26 32 AM"
src="https://github.com/user-attachments/assets/9312d3e3-9f20-43c3-9e9d-19f557521b95"
/> | <img width="1426" alt="Screenshot 2025-01-08 at 11 27 13 AM"
src="https://github.com/user-attachments/assets/1521f019-c558-441d-b99a-68a7ff8a8d92"
/> |

Release Notes:

- N/A
2025-01-08 14:51:14 +00:00
Danilo Leal
b890a12030 Improve LSP notification design (#22828)
Mostly just fine-tuning the styles and modernizing some of the component
usage. Visually, it doesn't change that _much_, but it still polishes it
up a bit.

| Before | After |
|--------|--------|
| <img width="1426" alt="Screenshot 2025-01-08 at 11 25 01 AM"
src="https://github.com/user-attachments/assets/df074f88-08c0-47c2-bd98-1a8b6dbadc99"
/> | <img width="1426" alt="Screenshot 2025-01-08 at 11 23 24 AM"
src="https://github.com/user-attachments/assets/250e3aee-fd1b-4b32-b305-e58b4fede75a"
/> |

Release Notes:

- N/A
2025-01-08 14:46:40 +00:00
Danilo Leal
115aa43354 Adjust TintColor color token terminology (#22826)
Previously, to use a green and red shade with `TintColor` you'd need to
pass `Positive` and `Negative`, respectively. This terminology always
tripped me up, because, for example, I'd often try to use something
like:

```
Button::new("icon_color", "Negative")
      style(ButtonStyle::Tinted(TintColor::Negative))
      .color(Color::Error)
      .icon_color(Color::Error)
      .icon(IconName::Trash),
)
```

...and due to `icon_color` taking `Color::Error`, I'd always get
`TintColor` wrong at a first try, because I would, out of muscle memory,
write `TintColor::Error`, which wouldn't compile. That's exactly the
change in this PR—`TintColor` now takes `Success` and `Error` instead of
`Positive` and `Negative`, for more consistency.


Release Notes:

- N/A
2025-01-08 14:40:48 +00:00
Cole Miller
bbb473b8df Add a dedicated action to open files (#22625)
Closes #22531
Closes #22250
Closes #15679

Release Notes:

- Add `workspace::OpenFiles` action to enable opening individual files
on Linux and Windows
2025-01-08 14:29:15 +00:00
Agus Zubiaga
36301442dd assistant2: Handle non-text files in context pickers (#22795)
We'll now show an error message if the user tries to add a directory
that contains no text files or when they try to add a single non-text
file.

Release Notes:

- N/A

---------

Co-authored-by: Danilo <danilo@zed.dev>
2025-01-08 14:06:29 +00:00
Richard Feldman
52f29b4a1f Fix conversation selector popover menu offset (#22796)
Before, the conversation popover menu covered up what you were typing
because it wasn't offset properly.

Now it's offset properly, using the UI font size so the amount of offset
scales with the font size:

<img width="435" alt="Screenshot 2025-01-07 at 4 34 27 PM"
src="https://github.com/user-attachments/assets/55e40910-8cd4-4548-b4fb-521eb2845775"
/>
<img width="454" alt="Screenshot 2025-01-07 at 4 33 58 PM"
src="https://github.com/user-attachments/assets/30350489-09f1-4cb8-9f95-ed4ee87bc110"
/>
<img width="488" alt="Screenshot 2025-01-07 at 4 34 18 PM"
src="https://github.com/user-attachments/assets/de60d990-2bd9-418d-a616-56beb3e4aa8a"
/>

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-01-08 13:32:48 +00:00
24 changed files with 267 additions and 245 deletions

2
Cargo.lock generated
View File

@@ -16199,7 +16199,7 @@ dependencies = [
[[package]]
name = "zed"
version = "0.169.0"
version = "0.170.0"
dependencies = [
"activity_indicator",
"anyhow",

View File

@@ -1 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18.4286 9H10.5714C9.70355 9 9 9.70355 9 10.5714V18.4286C9 19.2964 9.70355 20 10.5714 20H18.4286C19.2964 20 20 19.2964 20 18.4286V10.5714C20 9.70355 19.2964 9 18.4286 9Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.57143 15C4.70714 15 4 14.2929 4 13.4286V5.57143C4 4.70714 4.70714 4 5.57143 4H13.4286C14.2929 4 15 4.70714 15 5.57143" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 338 B

After

Width:  |  Height:  |  Size: 576 B

View File

@@ -3654,7 +3654,7 @@ impl ContextEditor {
let (style, tooltip) = match token_state(&self.context, cx) {
Some(TokenState::NoTokensLeft { .. }) => (
ButtonStyle::Tinted(TintColor::Negative),
ButtonStyle::Tinted(TintColor::Error),
Some(Tooltip::text("Token limit reached", cx)),
),
Some(TokenState::HasMoreTokens {
@@ -3711,7 +3711,7 @@ impl ContextEditor {
let (style, tooltip) = match token_state(&self.context, cx) {
Some(TokenState::NoTokensLeft { .. }) => (
ButtonStyle::Tinted(TintColor::Negative),
ButtonStyle::Tinted(TintColor::Error),
Some(Tooltip::text("Token limit reached", cx)),
),
Some(TokenState::HasMoreTokens {

View File

@@ -231,27 +231,36 @@ impl PickerDelegate for DirectoryContextPickerDelegate {
.collect::<Vec<_>>()
})?;
let open_all_buffers_tasks = cx.background_executor().spawn(async move {
let mut buffers = Vec::with_capacity(open_buffer_tasks.len());
for open_buffer_task in open_buffer_tasks {
let buffer = open_buffer_task.await?;
buffers.push(buffer);
}
anyhow::Ok(buffers)
});
let buffers = open_all_buffers_tasks.await?;
let buffers = futures::future::join_all(open_buffer_tasks).await;
this.update(&mut cx, |this, cx| {
let mut text = String::new();
for buffer in buffers {
let mut ok_count = 0;
for buffer in buffers.into_iter().flatten() {
let buffer = buffer.read(cx);
let path = buffer.file().map_or(&path, |file| file.path());
push_fenced_codeblock(&path, buffer.text(), &mut text);
ok_count += 1;
}
if ok_count == 0 {
let Some(workspace) = workspace.upgrade() else {
return anyhow::Ok(());
};
workspace.update(cx, |workspace, cx| {
workspace.show_error(
&anyhow::anyhow!(
"Could not read any text files from {}",
path.display()
),
cx,
);
});
return anyhow::Ok(());
}
this.delegate

View File

@@ -239,21 +239,34 @@ impl PickerDelegate for FileContextPickerDelegate {
return anyhow::Ok(());
};
let buffer = open_buffer_task.await?;
let result = open_buffer_task.await;
this.update(&mut cx, |this, cx| {
this.delegate
.context_store
.update(cx, |context_store, cx| {
context_store.insert_file(buffer.read(cx));
})?;
this.update(&mut cx, |this, cx| match result {
Ok(buffer) => {
this.delegate
.context_store
.update(cx, |context_store, cx| {
context_store.insert_file(buffer.read(cx));
})?;
match confirm_behavior {
ConfirmBehavior::KeepOpen => {}
ConfirmBehavior::Close => this.delegate.dismissed(cx),
match confirm_behavior {
ConfirmBehavior::KeepOpen => {}
ConfirmBehavior::Close => this.delegate.dismissed(cx),
}
anyhow::Ok(())
}
Err(err) => {
let Some(workspace) = workspace.upgrade() else {
return anyhow::Ok(());
};
anyhow::Ok(())
workspace.update(cx, |workspace, cx| {
workspace.show_error(&err, cx);
});
anyhow::Ok(())
}
})??;
anyhow::Ok(())

View File

@@ -10,7 +10,7 @@ use language_model::{LanguageModelRegistry, LanguageModelRequestTool};
use language_model_selector::LanguageModelSelector;
use rope::Point;
use settings::Settings;
use theme::ThemeSettings;
use theme::{get_ui_font_size, ThemeSettings};
use ui::{
prelude::*, ButtonLike, ElevationIndex, KeyBinding, PopoverMenu, PopoverMenuHandle,
SwitchWithLabel,
@@ -276,7 +276,7 @@ impl Render for MessageEditor {
.anchor(gpui::Corner::BottomLeft)
.offset(gpui::Point {
x: px(0.0),
y: px(-16.0),
y: (-get_ui_font_size(cx) * 2) - px(4.0),
})
.with_handle(self.inline_context_picker_menu_handle.clone()),
)

View File

@@ -175,7 +175,7 @@ pub(crate) fn suggest(buffer: Model<Buffer>, cx: &mut ViewContext<Workspace>) {
"Do you want to install the recommended '{}' extension for '{}' files?",
extension_id, file_name_or_extension
))
.with_click_message("Yes")
.with_click_message("Yes, install extension")
.on_click({
let extension_id = extension_id.clone();
move |cx| {
@@ -186,7 +186,7 @@ pub(crate) fn suggest(buffer: Model<Buffer>, cx: &mut ViewContext<Workspace>) {
});
}
})
.with_secondary_click_message("No")
.with_secondary_click_message("No, don't install it")
.on_secondary_click(move |cx| {
let key = language_extension_key(&extension_id);
db::write_and_log(cx, move || {

View File

@@ -9,8 +9,6 @@ use git::GitHostingProviderRegistry;
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
use ashpd::desktop::trash;
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
use smol::process::Command;
#[cfg(unix)]
use std::os::fd::AsFd;
@@ -547,25 +545,36 @@ impl Fs for RealFs {
}
Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => {
if cfg!(any(target_os = "linux", target_os = "freebsd")) {
let target_path = path.to_path_buf();
let temp_file = smol::unblock(move || create_temp_file(&target_path)).await?;
let pkexec_path = smol::unblock(|| which::which("pkexec"))
.await
.map_err(|_| anyhow::anyhow!("pkexec not found in PATH"))?;
let temp_path = temp_file.into_temp_path();
let temp_path_for_write = temp_path.to_path_buf();
let mut child = smol::process::Command::new(&pkexec_path)
.arg("sh")
.arg("-c")
.arg(format!(
"cat > {}",
shlex::try_quote(&path.to_string_lossy())?
))
.stdin(std::process::Stdio::piped())
.spawn()?;
let async_file = smol::fs::OpenOptions::new()
.write(true)
.open(&temp_path)
.await?;
let mut writer = smol::io::BufWriter::with_capacity(buffer_size, async_file);
for chunk in chunks(text, line_ending) {
let Some(stdin) = child.stdin.take() else {
return Err(anyhow::anyhow!("Failed to write to file as root, no stdin"));
};
let mut writer = smol::io::BufWriter::with_capacity(buffer_size, stdin);
for chunk in text.chunks() {
writer.write_all(chunk.as_bytes()).await?;
}
writer.flush().await?;
write_to_file_as_root(temp_path_for_write, path.to_path_buf()).await
let output = child.output().await?;
if !output.status.success() {
return Err(anyhow::anyhow!(
"Failed to write to file as root, exit status {}",
output.status
));
}
Ok(())
} else {
// Todo: Implement for Mac and Windows
Err(e.into())
@@ -2002,61 +2011,6 @@ fn create_temp_file(path: &Path) -> Result<NamedTempFile> {
Ok(temp_file)
}
#[cfg(target_os = "macos")]
async fn write_to_file_as_root(_temp_file_path: PathBuf, _target_file_path: PathBuf) -> Result<()> {
unimplemented!("write_to_file_as_root is not implemented")
}
#[cfg(target_os = "windows")]
async fn write_to_file_as_root(_temp_file_path: PathBuf, _target_file_path: PathBuf) -> Result<()> {
unimplemented!("write_to_file_as_root is not implemented")
}
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
async fn write_to_file_as_root(temp_file_path: PathBuf, target_file_path: PathBuf) -> Result<()> {
use shlex::try_quote;
use std::os::unix::fs::PermissionsExt;
use which::which;
let pkexec_path = smol::unblock(|| which("pkexec"))
.await
.map_err(|_| anyhow::anyhow!("pkexec not found in PATH"))?;
let script_file = smol::unblock(move || {
let script_file = tempfile::Builder::new()
.prefix("write-to-file-as-root-")
.tempfile_in(paths::temp_dir())?;
writeln!(
script_file.as_file(),
"#!/usr/bin/env sh\nset -eu\ncat \"{}\" > \"{}\"",
try_quote(&temp_file_path.to_string_lossy())?,
try_quote(&target_file_path.to_string_lossy())?
)?;
let mut perms = script_file.as_file().metadata()?.permissions();
perms.set_mode(0o700); // rwx------
script_file.as_file().set_permissions(perms)?;
Result::<_>::Ok(script_file)
})
.await?;
let script_path = script_file.into_temp_path();
let output = Command::new(&pkexec_path)
.arg("--disable-internal-agent")
.arg(&script_path)
.output()
.await?;
if !output.status.success() {
return Err(anyhow::anyhow!("Failed to write to file as root"));
}
Ok(())
}
pub fn normalize_path(path: &Path) -> PathBuf {
let mut components = path.components().peekable();
let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {

View File

@@ -1418,6 +1418,11 @@ impl AppContext {
pub fn get_name(&self) -> &'static str {
self.name.as_ref().unwrap()
}
/// Returns `true` if the platform file picker supports selecting a mix of files and directories.
pub fn can_select_mixed_files_and_dirs(&self) -> bool {
self.platform.can_select_mixed_files_and_dirs()
}
}
impl Context for AppContext {

View File

@@ -19,6 +19,7 @@ pub struct Anchored {
fit_mode: AnchoredFitMode,
anchor_position: Option<Point<Pixels>>,
position_mode: AnchoredPositionMode,
offset: Option<Point<Pixels>>,
}
/// anchored gives you an element that will avoid overflowing the window bounds.
@@ -30,6 +31,7 @@ pub fn anchored() -> Anchored {
fit_mode: AnchoredFitMode::SwitchAnchor,
anchor_position: None,
position_mode: AnchoredPositionMode::Window,
offset: None,
}
}
@@ -47,6 +49,13 @@ impl Anchored {
self
}
/// Offset the final position by this amount.
/// Useful when you want to anchor to an element but offset from it, such as in PopoverMenu.
pub fn offset(mut self, offset: Point<Pixels>) -> Self {
self.offset = Some(offset);
self
}
/// Sets the position mode for this anchored element. Local will have this
/// interpret its [`Anchored::position`] as relative to the parent element.
/// While Window will have it interpret the position as relative to the window.
@@ -129,6 +138,7 @@ impl Element for Anchored {
self.anchor_corner,
size,
bounds,
self.offset,
);
let limits = Bounds {
@@ -245,18 +255,22 @@ impl AnchoredPositionMode {
anchor_corner: Corner,
size: Size<Pixels>,
bounds: Bounds<Pixels>,
offset: Option<Point<Pixels>>,
) -> (Point<Pixels>, Bounds<Pixels>) {
let offset = offset.unwrap_or_default();
match self {
AnchoredPositionMode::Window => {
let anchor_position = anchor_position.unwrap_or(bounds.origin);
let bounds = Bounds::from_corner_and_size(anchor_corner, anchor_position, size);
let bounds =
Bounds::from_corner_and_size(anchor_corner, anchor_position + offset, size);
(anchor_position, bounds)
}
AnchoredPositionMode::Local => {
let anchor_position = anchor_position.unwrap_or_default();
let bounds = Bounds::from_corner_and_size(
anchor_corner,
bounds.origin + anchor_position,
bounds.origin + anchor_position + offset,
size,
);
(anchor_position, bounds)

View File

@@ -175,6 +175,7 @@ pub(crate) trait Platform: 'static {
options: PathPromptOptions,
) -> oneshot::Receiver<Result<Option<Vec<PathBuf>>>>;
fn prompt_for_new_path(&self, directory: &Path) -> oneshot::Receiver<Result<Option<PathBuf>>>;
fn can_select_mixed_files_and_dirs(&self) -> bool;
fn reveal_path(&self, path: &Path);
fn open_with_system(&self, path: &Path);

View File

@@ -372,6 +372,11 @@ impl<P: LinuxClient + 'static> Platform for P {
done_rx
}
fn can_select_mixed_files_and_dirs(&self) -> bool {
// org.freedesktop.portal.FileChooser only supports "pick files" and "pick directories".
false
}
fn reveal_path(&self, path: &Path) {
self.reveal_path(path.to_owned());
}

View File

@@ -759,6 +759,10 @@ impl Platform for MacPlatform {
done_rx
}
fn can_select_mixed_files_and_dirs(&self) -> bool {
true
}
fn reveal_path(&self, path: &Path) {
unsafe {
let path = path.to_path_buf();

View File

@@ -299,6 +299,10 @@ impl Platform for TestPlatform {
rx
}
fn can_select_mixed_files_and_dirs(&self) -> bool {
true
}
fn reveal_path(&self, _path: &std::path::Path) {
unimplemented!()
}

View File

@@ -407,6 +407,11 @@ impl Platform for WindowsPlatform {
rx
}
fn can_select_mixed_files_and_dirs(&self) -> bool {
// The FOS_PICKFOLDERS flag toggles between "only files" and "only folders".
false
}
fn reveal_path(&self, path: &Path) {
let Ok(file_full_path) = path.canonicalize() else {
log::error!("unable to parse file path");

View File

@@ -381,7 +381,7 @@ impl TitleBar {
.style(ButtonStyle::Subtle)
.icon_size(IconSize::Small)
.toggle_state(is_muted)
.selected_style(ButtonStyle::Tinted(TintColor::Negative))
.selected_style(ButtonStyle::Tinted(TintColor::Error))
.on_click(move |_, cx| {
toggle_mute(&Default::default(), cx);
})
@@ -398,7 +398,7 @@ impl TitleBar {
},
)
.style(ButtonStyle::Subtle)
.selected_style(ButtonStyle::Tinted(TintColor::Negative))
.selected_style(ButtonStyle::Tinted(TintColor::Error))
.icon_size(IconSize::Small)
.toggle_state(is_deafened)
.tooltip(move |cx| {

View File

@@ -474,9 +474,9 @@ impl ComponentPreview for Button {
.style(ButtonStyle::Tinted(TintColor::Accent)),
),
single_example(
"Negative",
Button::new("tinted_negative", "Negative")
.style(ButtonStyle::Tinted(TintColor::Negative)),
"Error",
Button::new("tinted_negative", "Error")
.style(ButtonStyle::Tinted(TintColor::Error)),
),
single_example(
"Warning",
@@ -484,9 +484,9 @@ impl ComponentPreview for Button {
.style(ButtonStyle::Tinted(TintColor::Warning)),
),
single_example(
"Positive",
Button::new("tinted_positive", "Positive")
.style(ButtonStyle::Tinted(TintColor::Positive)),
"Success",
Button::new("tinted_positive", "Success")
.style(ButtonStyle::Tinted(TintColor::Success)),
),
],
),
@@ -527,8 +527,8 @@ impl ComponentPreview for Button {
),
single_example(
"Tinted Icons",
Button::new("icon_color", "Delete")
.style(ButtonStyle::Tinted(TintColor::Negative))
Button::new("icon_color", "Error")
.style(ButtonStyle::Tinted(TintColor::Error))
.color(Color::Error)
.icon_color(Color::Error)
.icon(IconName::Trash)

View File

@@ -49,9 +49,9 @@ pub enum IconPosition {
pub enum TintColor {
#[default]
Accent,
Negative,
Error,
Warning,
Positive,
Success,
}
impl TintColor {
@@ -63,7 +63,7 @@ impl TintColor {
label_color: cx.theme().colors().text,
icon_color: cx.theme().colors().text,
},
TintColor::Negative => ButtonLikeStyles {
TintColor::Error => ButtonLikeStyles {
background: cx.theme().status().error_background,
border_color: cx.theme().status().error_border,
label_color: cx.theme().colors().text,
@@ -75,7 +75,7 @@ impl TintColor {
label_color: cx.theme().colors().text,
icon_color: cx.theme().colors().text,
},
TintColor::Positive => ButtonLikeStyles {
TintColor::Success => ButtonLikeStyles {
background: cx.theme().status().success_background,
border_color: cx.theme().status().success_border,
label_color: cx.theme().colors().text,
@@ -89,9 +89,9 @@ impl From<TintColor> for Color {
fn from(tint: TintColor) -> Self {
match tint {
TintColor::Accent => Color::Accent,
TintColor::Negative => Color::Error,
TintColor::Error => Color::Error,
TintColor::Warning => Color::Warning,
TintColor::Positive => Color::Success,
TintColor::Success => Color::Success,
}
}
}

View File

@@ -254,13 +254,14 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
let mut menu_layout_id = None;
let menu_element = element_state.menu.borrow_mut().as_mut().map(|menu| {
let offset = self.resolved_offset(cx);
let mut anchored = anchored()
.snap_to_window_with_margin(px(8.))
.anchor(self.anchor);
.anchor(self.anchor)
.offset(offset);
if let Some(child_bounds) = element_state.child_bounds {
anchored = anchored.position(
child_bounds.corner(self.resolved_attach()) + self.resolved_offset(cx),
);
anchored =
anchored.position(child_bounds.corner(self.resolved_attach()) + offset);
}
let mut element = deferred(anchored.child(div().occlude().child(menu.clone())))
.with_priority(1)

View File

@@ -449,6 +449,10 @@ where
);
}
pub fn log_err<E: std::fmt::Debug>(error: &E) {
log_error_with_caller(*Location::caller(), error, log::Level::Warn);
}
pub trait TryFutureExt {
fn log_err(self) -> LogErrorFuture<Self>
where

View File

@@ -5,7 +5,6 @@ use gpui::{
EventEmitter, Global, PromptLevel, Render, ScrollHandle, Task, View, ViewContext,
VisualContext, WindowContext,
};
use language::DiagnosticSeverity;
use std::{any::TypeId, ops::DerefMut, time::Duration};
use ui::{prelude::*, Tooltip};
@@ -266,89 +265,57 @@ impl Render for LanguageServerPrompt {
return div().id("language_server_prompt_notification");
};
h_flex()
let (icon, color) = match request.level {
PromptLevel::Info => (IconName::Info, Color::Accent),
PromptLevel::Warning => (IconName::Warning, Color::Warning),
PromptLevel::Critical => (IconName::XCircle, Color::Error),
};
div()
.id("language_server_prompt_notification")
.group("language_server_prompt_notification")
.occlude()
.elevation_3(cx)
.items_start()
.justify_between()
.p_2()
.gap_2()
.w_full()
.max_h(vh(0.8, cx))
.elevation_3(cx)
.overflow_y_scroll()
.track_scroll(&self.scroll_handle)
.group("")
.child(
v_flex()
.w_full()
.p_3()
.overflow_hidden()
.child(
h_flex()
.w_full()
.justify_between()
.child(
h_flex()
.flex_grow()
.children(
match request.level {
PromptLevel::Info => None,
PromptLevel::Warning => {
Some(DiagnosticSeverity::WARNING)
}
PromptLevel::Critical => {
Some(DiagnosticSeverity::ERROR)
}
}
.map(|severity| {
svg()
.size(cx.text_style().font_size)
.flex_none()
.mr_1()
.mt(px(-2.0))
.map(|icon| {
if severity == DiagnosticSeverity::ERROR {
icon.path(IconName::Warning.path())
.text_color(Color::Error.color(cx))
} else {
icon.path(IconName::Warning.path())
.text_color(Color::Warning.color(cx))
}
})
}),
)
.child(
Label::new(request.lsp_name.clone())
.size(LabelSize::Default),
),
.gap_2()
.child(Icon::new(icon).color(color))
.child(Label::new(request.lsp_name.clone())),
)
.child(
ui::IconButton::new("close", ui::IconName::Close)
.on_click(cx.listener(|_, _, cx| cx.emit(gpui::DismissEvent))),
h_flex()
.child(
IconButton::new("copy", IconName::Copy)
.on_click({
let message = request.message.clone();
move |_, cx| {
cx.write_to_clipboard(
ClipboardItem::new_string(message.clone()),
)
}
})
.tooltip(|cx| Tooltip::text("Copy Description", cx)),
)
.child(IconButton::new("close", IconName::Close).on_click(
cx.listener(|_, _, cx| cx.emit(gpui::DismissEvent)),
)),
),
)
.child(
v_flex()
.child(
h_flex().absolute().right_0().rounded_md().child(
ui::IconButton::new("copy", ui::IconName::Copy)
.on_click({
let message = request.message.clone();
move |_, cx| {
cx.write_to_clipboard(ClipboardItem::new_string(
message.clone(),
))
}
})
.tooltip(|cx| Tooltip::text("Copy", cx))
.visible_on_hover(""),
),
)
.child(Label::new(request.message.to_string()).size(LabelSize::Small)),
)
.child(Label::new(request.message.to_string()).size(LabelSize::Small))
.children(request.actions.iter().enumerate().map(|(ix, action)| {
let this_handle = cx.view().clone();
ui::Button::new(ix, action.title.clone())
Button::new(ix, action.title.clone())
.size(ButtonSize::Large)
.on_click(move |_, cx| {
let this_handle = this_handle.clone();
@@ -444,12 +411,10 @@ impl EventEmitter<DismissEvent> for ErrorMessagePrompt {}
pub mod simple_message_notification {
use gpui::{
div, DismissEvent, EventEmitter, InteractiveElement, ParentElement, Render, SharedString,
StatefulInteractiveElement, Styled, ViewContext,
div, DismissEvent, EventEmitter, ParentElement, Render, SharedString, Styled, ViewContext,
};
use std::sync::Arc;
use ui::prelude::*;
use ui::{h_flex, v_flex, Button, Icon, IconName, Label, StyledExt};
pub struct MessageNotification {
message: SharedString,
@@ -515,36 +480,43 @@ pub mod simple_message_notification {
impl Render for MessageNotification {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
v_flex()
.p_3()
.gap_2()
.elevation_3(cx)
.p_4()
.child(
h_flex()
.gap_4()
.justify_between()
.child(div().max_w_80().child(Label::new(self.message.clone())))
.child(
div()
.id("cancel")
.child(Icon::new(IconName::Close))
.cursor_pointer()
IconButton::new("close", IconName::Close)
.on_click(cx.listener(|this, _, cx| this.dismiss(cx))),
),
)
.child(
h_flex()
.gap_3()
.gap_2()
.children(self.click_message.iter().map(|message| {
Button::new(message.clone(), message.clone()).on_click(cx.listener(
|this, _, cx| {
Button::new(message.clone(), message.clone())
.label_size(LabelSize::Small)
.icon(IconName::Check)
.icon_position(IconPosition::Start)
.icon_size(IconSize::Small)
.icon_color(Color::Success)
.on_click(cx.listener(|this, _, cx| {
if let Some(on_click) = this.on_click.as_ref() {
(on_click)(cx)
};
this.dismiss(cx)
},
))
}))
}))
.children(self.secondary_click_message.iter().map(|message| {
Button::new(message.clone(), message.clone())
.style(ButtonStyle::Filled)
.label_size(LabelSize::Small)
.icon(IconName::Close)
.icon_position(IconPosition::Start)
.icon_size(IconSize::Small)
.icon_color(Color::Error)
.on_click(cx.listener(|this, _, cx| {
if let Some(on_click) = this.secondary_on_click.as_ref() {
(on_click)(cx)

View File

@@ -34,10 +34,10 @@ use gpui::{
action_as, actions, canvas, impl_action_as, impl_actions, point, relative, size,
transparent_black, Action, AnyView, AnyWeakView, AppContext, AsyncAppContext,
AsyncWindowContext, Bounds, CursorStyle, Decorations, DragMoveEvent, Entity as _, EntityId,
EventEmitter, Flatten, FocusHandle, FocusableView, Global, Hsla, KeyContext, Keystroke,
ManagedView, Model, ModelContext, MouseButton, PathPromptOptions, Point, PromptLevel, Render,
ResizeEdge, Size, Stateful, Subscription, Task, Tiling, View, WeakView, WindowBounds,
WindowHandle, WindowId, WindowOptions,
EventEmitter, FocusHandle, FocusableView, Global, Hsla, KeyContext, Keystroke, ManagedView,
Model, ModelContext, MouseButton, PathPromptOptions, Point, PromptLevel, Render, ResizeEdge,
Size, Stateful, Subscription, Task, Tiling, View, WeakView, WindowBounds, WindowHandle,
WindowId, WindowOptions,
};
pub use item::{
FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, PreviewTabsSettings,
@@ -145,6 +145,7 @@ actions!(
NewTerminal,
NewWindow,
Open,
OpenFiles,
OpenInTerminal,
ReloadActiveItem,
SaveAs,
@@ -332,6 +333,42 @@ pub fn init_settings(cx: &mut AppContext) {
TabBarSettings::register(cx);
}
fn prompt_and_open_paths(
app_state: Arc<AppState>,
options: PathPromptOptions,
cx: &mut AppContext,
) {
let paths = cx.prompt_for_paths(options);
cx.spawn(|cx| async move {
match paths.await.anyhow().and_then(|res| res) {
Ok(Some(paths)) => {
cx.update(|cx| {
open_paths(&paths, app_state, OpenOptions::default(), cx).detach_and_log_err(cx)
})
.ok();
}
Ok(None) => {}
Err(err) => {
util::log_err(&err);
cx.update(|cx| {
if let Some(workspace_window) = cx
.active_window()
.and_then(|window| window.downcast::<Workspace>())
{
workspace_window
.update(cx, |workspace, cx| {
workspace.show_portal_error(err.to_string(), cx);
})
.ok();
}
})
.ok();
}
}
})
.detach();
}
pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
init_settings(cx);
notifications::init(cx);
@@ -343,41 +380,33 @@ pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
cx.on_action({
let app_state = Arc::downgrade(&app_state);
move |_: &Open, cx: &mut AppContext| {
let paths = cx.prompt_for_paths(PathPromptOptions {
files: true,
directories: true,
multiple: true,
});
if let Some(app_state) = app_state.upgrade() {
cx.spawn(move |cx| async move {
match Flatten::flatten(paths.await.map_err(|e| e.into())) {
Ok(Some(paths)) => {
cx.update(|cx| {
open_paths(&paths, app_state, OpenOptions::default(), cx)
.detach_and_log_err(cx)
})
.ok();
}
Ok(None) => {}
Err(err) => {
cx.update(|cx| {
if let Some(workspace_window) = cx
.active_window()
.and_then(|window| window.downcast::<Workspace>())
{
workspace_window
.update(cx, |workspace, cx| {
workspace.show_portal_error(err.to_string(), cx);
})
.ok();
}
})
.ok();
}
};
})
.detach();
prompt_and_open_paths(
app_state,
PathPromptOptions {
files: true,
directories: true,
multiple: true,
},
cx,
);
}
}
});
cx.on_action({
let app_state = Arc::downgrade(&app_state);
move |_: &OpenFiles, cx: &mut AppContext| {
let directories = cx.can_select_mixed_files_and_dirs();
if let Some(app_state) = app_state.upgrade() {
prompt_and_open_paths(
app_state,
PathPromptOptions {
files: true,
directories,
multiple: true,
},
cx,
);
}
}
});

View File

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

View File

@@ -66,7 +66,7 @@ git checkout -q ${prev_minor_branch_name}
git clean -q -dff
stable_tag_name="v$(script/get-crate-version zed)"
if git show-ref --quiet refs/tags/${stable_tag_name}; then
echo "tag ${preview_tag_name} already exists"
echo "tag ${stable_tag_name} already exists"
exit 1
fi
old_prev_minor_sha=$(git rev-parse HEAD)
@@ -77,7 +77,6 @@ git tag ${stable_tag_name}
echo "Creating new preview branch ${minor_branch_name}..."
git checkout -q main
git checkout -q -b ${minor_branch_name}
git branch --set-upstream-to=origin/${minor_branch_name} ${minor_branch_name}
echo -n preview > crates/zed/RELEASE_CHANNEL
git commit -q --all --message "${minor_branch_name} preview"
git tag ${preview_tag_name}