Compare commits

...

8 Commits

Author SHA1 Message Date
Julia Ryan
31f5df5e33 Add test for nix sandbox
Co-authored-by: Rahul Butani <rrbutani@users.noreply.github.com>
2025-10-15 00:49:26 -07:00
Conrad Irwin
0c08bbca05 Avoid gap between titlebar and body on linux (#40228)
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: John Tur <john-tur@outlook.com>

Release Notes:

- N/A

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: John Tur <john-tur@outlook.com>
2025-10-15 04:44:00 +00:00
Cole Miller
ba0b68779d Fix triggers for debugger thread and session lists not rendering (#40227)
Release Notes:

- N/A
2025-10-15 04:36:04 +00:00
Cole Miller
45af5e4239 Fix a couple of bugs in remote browser debugging implementation (#40225)
Follow-up to #39248 

- Correctly forward ports over SSH, including the port from the debug
scenario's `url`
- Give the companion time to start up, instead of bailing if the first
connection attempt fails

Release Notes:

- Fixed not being able to launch a browser debugging session in an SSH
project.
2025-10-14 23:05:19 -04:00
Mikayla Maki
01f9b1e9b4 chore: VSCode -> VS Code (#40224)
Release Notes:

- N/A
2025-10-15 02:21:37 +00:00
Mikayla Maki
635b71c486 chore: Delete main.py (#40221)
Release Notes:

- N/A
2025-10-15 01:32:46 +00:00
Mikayla Maki
c4a7552a04 Bump Zed to v0.210 (#40219)
Release Notes:

- N/A
2025-10-15 01:10:56 +00:00
Mikayla Maki
918aee550c docs: Update releases.md (#40220)
Release Notes:

- N/A
2025-10-15 00:55:40 +00:00
13 changed files with 164 additions and 109 deletions

View File

@@ -59,6 +59,9 @@ jobs:
pushFilter: "${{ inputs.cachix-filter }}"
cachixArgs: "-v"
- run: which protoc || true
- run: mount
- run: nix-build --expr 'derivation { system = "x86_64-linux"; builder = "not-a-builder"; name = "not-a-name"; }' -vvv
- run: nix build .#${{ inputs.flake-output }} -L --accept-flake-config
- name: Limit /nix/store to 50GB on macs

2
Cargo.lock generated
View File

@@ -21203,7 +21203,7 @@ dependencies = [
[[package]]
name = "zed"
version = "0.209.0"
version = "0.210.0"
dependencies = [
"acp_tools",
"activity_indicator",

View File

@@ -1,7 +1,7 @@
use std::rc::Rc;
use collections::HashMap;
use gpui::{Entity, WeakEntity};
use gpui::{Corner, Entity, WeakEntity};
use project::debugger::session::{ThreadId, ThreadStatus};
use ui::{CommonAnimationExt, ContextMenu, DropdownMenu, DropdownStyle, Indicator, prelude::*};
use util::{maybe, truncate_and_trailoff};
@@ -211,6 +211,7 @@ impl DebugPanel {
this
}),
)
.attach(Corner::BottomLeft)
.style(DropdownStyle::Ghost)
.handle(self.session_picker_menu_handle.clone());
@@ -322,6 +323,7 @@ impl DebugPanel {
this
}),
)
.attach(Corner::BottomLeft)
.disabled(session_terminated)
.style(DropdownStyle::Ghost)
.handle(self.thread_picker_menu_handle.clone()),

View File

@@ -14,12 +14,13 @@ use super::dap_command::{
TerminateCommand, TerminateThreadsCommand, ThreadsCommand, VariablesCommand,
};
use super::dap_store::DapStore;
use anyhow::{Context as _, Result, anyhow};
use anyhow::{Context as _, Result, anyhow, bail};
use base64::Engine;
use collections::{HashMap, HashSet, IndexMap};
use dap::adapters::{DebugAdapterBinary, DebugAdapterName};
use dap::messages::Response;
use dap::requests::{Request, RunInTerminal, StartDebugging};
use dap::transport::TcpTransport;
use dap::{
Capabilities, ContinueArguments, EvaluateArgumentsContext, Module, Source, StackFrameId,
SteppingGranularity, StoppedEvent, VariableReference,
@@ -47,12 +48,14 @@ use remote::RemoteClient;
use rpc::ErrorExt;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use smol::net::TcpListener;
use smol::net::{TcpListener, TcpStream};
use std::any::TypeId;
use std::collections::BTreeMap;
use std::net::Ipv4Addr;
use std::ops::RangeInclusive;
use std::path::PathBuf;
use std::process::Stdio;
use std::time::Duration;
use std::u64;
use std::{
any::Any,
@@ -63,6 +66,7 @@ use std::{
};
use task::TaskContext;
use text::{PointUtf16, ToPointUtf16};
use url::Url;
use util::command::new_smol_command;
use util::{ResultExt, debug_panic, maybe};
use worktree::Worktree;
@@ -2768,31 +2772,42 @@ impl Session {
let mut console_output = self.console_output(cx);
let task = cx.spawn(async move |this, cx| {
let (dap_port, _child) =
if remote_client.read_with(cx, |client, _| client.shares_network_interface())? {
(request.server_port, None)
} else {
let port = {
let listener = TcpListener::bind("127.0.0.1:0")
.await
.context("getting port for DAP")?;
listener.local_addr()?.port()
};
let child = remote_client.update(cx, |client, _| {
let command = client.build_forward_port_command(
port,
"localhost".into(),
request.server_port,
)?;
let child = new_smol_command(command.program)
.args(command.args)
.envs(command.env)
.spawn()
.context("spawning port forwarding process")?;
anyhow::Ok(child)
})??;
(port, Some(child))
};
let forward_ports_process = if remote_client
.read_with(cx, |client, _| client.shares_network_interface())?
{
request.other.insert(
"proxyUri".into(),
format!("127.0.0.1:{}", request.server_port).into(),
);
None
} else {
let port = TcpTransport::unused_port(Ipv4Addr::LOCALHOST)
.await
.context("getting port for DAP")?;
request
.other
.insert("proxyUri".into(), format!("127.0.0.1:{port}").into());
let mut port_forwards = vec![(port, "localhost".to_owned(), request.server_port)];
if let Some(value) = request.params.get("url")
&& let Some(url) = value.as_str()
&& let Some(url) = Url::parse(url).ok()
&& let Some(frontend_port) = url.port()
{
port_forwards.push((frontend_port, "localhost".to_owned(), frontend_port));
}
let child = remote_client.update(cx, |client, _| {
let command = client.build_forward_ports_command(port_forwards)?;
let child = new_smol_command(command.program)
.args(command.args)
.envs(command.env)
.spawn()
.context("spawning port forwarding process")?;
anyhow::Ok(child)
})??;
Some(child)
};
let mut companion_process = None;
let companion_port =
@@ -2814,14 +2829,17 @@ impl Session {
}
}
};
this.update(cx, |this, cx| {
this.companion_port = Some(companion_port);
let Some(mut child) = companion_process else {
return;
};
if let Some(stderr) = child.stderr.take() {
let mut background_tasks = Vec::new();
if let Some(mut forward_ports_process) = forward_ports_process {
background_tasks.push(cx.spawn(async move |_| {
forward_ports_process.status().await.log_err();
}));
};
if let Some(mut companion_process) = companion_process {
if let Some(stderr) = companion_process.stderr.take() {
let mut console_output = console_output.clone();
this.background_tasks.push(cx.spawn(async move |_, _| {
background_tasks.push(cx.spawn(async move |_| {
let mut stderr = BufReader::new(stderr);
let mut line = String::new();
while let Ok(n) = stderr.read_line(&mut line).await
@@ -2835,9 +2853,9 @@ impl Session {
}
}));
}
this.background_tasks.push(cx.spawn({
background_tasks.push(cx.spawn({
let mut console_output = console_output.clone();
async move |_, _| match child.status().await {
async move |_| match companion_process.status().await {
Ok(status) => {
if status.success() {
console_output
@@ -2860,17 +2878,33 @@ impl Session {
.ok();
}
}
}))
})?;
}));
}
request
.other
.insert("proxyUri".into(), format!("127.0.0.1:{dap_port}").into());
// TODO pass wslInfo as needed
let companion_address = format!("127.0.0.1:{companion_port}");
let mut companion_started = false;
for _ in 0..10 {
if TcpStream::connect(&companion_address).await.is_ok() {
companion_started = true;
break;
}
cx.background_executor()
.timer(Duration::from_millis(100))
.await;
}
if !companion_started {
console_output
.send("Browser companion failed to start".into())
.await
.ok();
bail!("Browser companion failed to start");
}
let response = http_client
.post_json(
&format!("http://127.0.0.1:{companion_port}/launch-and-attach"),
&format!("http://{companion_address}/launch-and-attach"),
serde_json::to_string(&request)
.context("serializing request")?
.into(),
@@ -2895,6 +2929,11 @@ impl Session {
}
}
this.update(cx, |this, _| {
this.background_tasks.extend(background_tasks);
this.companion_port = Some(companion_port);
})?;
anyhow::Ok(())
});
self.background_tasks.push(cx.spawn(async move |_, _| {
@@ -2926,15 +2965,16 @@ impl Session {
}
}
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct LaunchBrowserInCompanionParams {
server_port: u16,
params: HashMap<String, serde_json::Value>,
#[serde(flatten)]
other: HashMap<String, serde_json::Value>,
}
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct KillCompanionBrowserParams {
launch_id: u64,

View File

@@ -836,16 +836,14 @@ impl RemoteClient {
connection.build_command(program, args, env, working_dir, port_forward)
}
pub fn build_forward_port_command(
pub fn build_forward_ports_command(
&self,
local_port: u16,
host: String,
remote_port: u16,
forwards: Vec<(u16, String, u16)>,
) -> Result<CommandTemplate> {
let Some(connection) = self.remote_connection() else {
return Err(anyhow!("no ssh connection"));
};
connection.build_forward_port_command(local_port, host, remote_port)
connection.build_forward_ports_command(forwards)
}
pub fn upload_directory(
@@ -1116,11 +1114,9 @@ pub(crate) trait RemoteConnection: Send + Sync {
working_dir: Option<String>,
port_forward: Option<(u16, String, u16)>,
) -> Result<CommandTemplate>;
fn build_forward_port_command(
fn build_forward_ports_command(
&self,
local_port: u16,
remote: String,
remote_port: u16,
forwards: Vec<(u16, String, u16)>,
) -> Result<CommandTemplate>;
fn connection_options(&self) -> RemoteConnectionOptions;
fn path_style(&self) -> PathStyle;
@@ -1551,19 +1547,17 @@ mod fake {
})
}
fn build_forward_port_command(
fn build_forward_ports_command(
&self,
local_port: u16,
host: String,
remote_port: u16,
forwards: Vec<(u16, String, u16)>,
) -> anyhow::Result<CommandTemplate> {
Ok(CommandTemplate {
program: "ssh".into(),
args: vec![
"-N".into(),
"-L".into(),
format!("{local_port}:{host}:{remote_port}"),
],
args: std::iter::once("-N".to_owned())
.chain(forwards.into_iter().map(|(local_port, host, remote_port)| {
format!("{local_port}:{host}:{remote_port}")
}))
.collect(),
env: Default::default(),
})
}

View File

@@ -146,19 +146,20 @@ impl RemoteConnection for SshRemoteConnection {
)
}
fn build_forward_port_command(
fn build_forward_ports_command(
&self,
local_port: u16,
host: String,
remote_port: u16,
forwards: Vec<(u16, String, u16)>,
) -> Result<CommandTemplate> {
let Self { socket, .. } = self;
let mut args = socket.ssh_args();
args.push("-N".into());
for (local_port, host, remote_port) in forwards {
args.push("-L".into());
args.push(format!("{local_port}:{host}:{remote_port}"));
}
Ok(CommandTemplate {
program: "ssh".into(),
args: vec![
"-N".into(),
"-L".into(),
format!("{local_port}:{host}:{remote_port}"),
],
args,
env: Default::default(),
})
}

View File

@@ -485,11 +485,9 @@ impl RemoteConnection for WslRemoteConnection {
})
}
fn build_forward_port_command(
fn build_forward_ports_command(
&self,
_: u16,
_: String,
_: u16,
_: Vec<(u16, String, u16)>,
) -> anyhow::Result<CommandTemplate> {
Err(anyhow!("WSL shares a network interface with the host"))
}

View File

@@ -56,7 +56,7 @@ impl Into<BaseKeymapContent> for BaseKeymap {
impl Display for BaseKeymap {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
BaseKeymap::VSCode => write!(f, "VSCode"),
BaseKeymap::VSCode => write!(f, "VS Code"),
BaseKeymap::JetBrains => write!(f, "JetBrains"),
BaseKeymap::SublimeText => write!(f, "Sublime Text"),
BaseKeymap::Atom => write!(f, "Atom"),
@@ -71,7 +71,7 @@ impl Display for BaseKeymap {
impl BaseKeymap {
#[cfg(target_os = "macos")]
pub const OPTIONS: [(&'static str, Self); 7] = [
("VSCode (Default)", Self::VSCode),
("VS Code (Default)", Self::VSCode),
("Atom", Self::Atom),
("JetBrains", Self::JetBrains),
("Sublime Text", Self::SublimeText),
@@ -82,7 +82,7 @@ impl BaseKeymap {
#[cfg(not(target_os = "macos"))]
pub const OPTIONS: [(&'static str, Self); 6] = [
("VSCode (Default)", Self::VSCode),
("VS Code (Default)", Self::VSCode),
("Atom", Self::Atom),
("JetBrains", Self::JetBrains),
("Sublime Text", Self::SublimeText),

View File

@@ -97,6 +97,7 @@ impl Render for PlatformTitleBar {
})
// this border is to avoid a transparent gap in the rounded corners
.mt(px(-1.))
.mb(px(-1.))
.border(px(1.))
.border_color(titlebar_color),
})

View File

@@ -1,6 +1,6 @@
use gpui::{Corner, Entity, Pixels, Point};
use crate::{ContextMenu, PopoverMenu, prelude::*};
use crate::{ButtonLike, ContextMenu, PopoverMenu, prelude::*};
use super::PopoverMenuHandle;
@@ -137,36 +137,52 @@ impl RenderOnce for DropdownMenu {
let full_width = self.full_width;
let trigger_size = self.trigger_size;
let button = match self.label {
LabelKind::Text(text) => Button::new(self.id.clone(), text)
.style(button_style)
.when(self.chevron, |this| {
this.icon(IconName::ChevronUpDown)
.icon_position(IconPosition::End)
.icon_size(IconSize::XSmall)
.icon_color(Color::Muted)
})
.when(full_width, |this| this.full_width())
.size(trigger_size)
.disabled(self.disabled),
LabelKind::Element(_element) => Button::new(self.id.clone(), "")
.style(button_style)
.when(self.chevron, |this| {
this.icon(IconName::ChevronUpDown)
.icon_position(IconPosition::End)
.icon_size(IconSize::XSmall)
.icon_color(Color::Muted)
})
.when(full_width, |this| this.full_width())
.size(trigger_size)
.disabled(self.disabled),
}
.when_some(self.tab_index, |this, tab_index| this.tab_index(tab_index));
let (text_button, element_button) = match self.label {
LabelKind::Text(text) => (
Some(
Button::new(self.id.clone(), text)
.style(button_style)
.when(self.chevron, |this| {
this.icon(IconName::ChevronUpDown)
.icon_position(IconPosition::End)
.icon_size(IconSize::XSmall)
.icon_color(Color::Muted)
})
.when(full_width, |this| this.full_width())
.size(trigger_size)
.disabled(self.disabled)
.when_some(self.tab_index, |this, tab_index| this.tab_index(tab_index)),
),
None,
),
LabelKind::Element(element) => (
None,
Some(
ButtonLike::new(self.id.clone())
.child(element)
.style(button_style)
.when(self.chevron, |this| {
this.child(
Icon::new(IconName::ChevronUpDown)
.size(IconSize::XSmall)
.color(Color::Muted),
)
})
.when(full_width, |this| this.full_width())
.size(trigger_size)
.disabled(self.disabled)
.when_some(self.tab_index, |this, tab_index| this.tab_index(tab_index)),
),
),
};
PopoverMenu::new((self.id.clone(), "popover"))
.full_width(self.full_width)
.menu(move |_window, _cx| Some(self.menu.clone()))
.trigger(button)
.when_some(text_button, |this, text_button| this.trigger(text_button))
.when_some(element_button, |this, element_button| {
this.trigger(element_button)
})
.attach(match self.attach {
Some(attach) => attach,
None => Corner::BottomRight,

View File

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

View File

@@ -40,7 +40,7 @@ This is mostly a formality on Wednesday's minor update releases, but can be bene
1. Check the release assets.
- Ensure the stable and preview release jobs have finished without error.
- Ensure each draft has the proper number of assets—releases currently have 10 assets each.
- Ensure each draft has the proper number of assets—releases currently have 11 assets each.
- Download the artifacts for each release draft and test that you can run them locally.
1. Publish the drafts.

View File