chore: enforce warnings across build configurations (#1200)

* Treat Rust warnings as errors

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix deny-warnings all-target checks

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Enforce warnings across build configurations

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix cross-platform deny-warnings CI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Limit foreground argv probing to supported platforms

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix Windows warnings denied by CI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix Windows test warnings under deny-warnings

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix Windows settings test compilation

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Gate Windows-unused diff probe helper

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Migrate updater to async ZIP read1 API

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Handle invalid ZIP entry names in read1 API

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Avoid moving ZIP entry name during extraction

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* ci: deny warnings in CI only, keep local builds lenient

Move -D warnings out of .cargo/config.toml so an in-progress local edit
still compiles. The build & test job passes it with --config, which is
appended to the Windows +crt-static rustflags instead of replacing them
the way RUSTFLAGS would. Nightly and release builds no longer fail on
lints; CI gates them. Restore the backdrop parameter name in theme.rs.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
This commit is contained in:
Konstantin Zadorozhny
2026-10-22 18:25:11 +08:00
committed by GitHub
co-authored by Copilot l0ng-ai
parent 69536b361a
commit 4d33021cbe
33 changed files with 125 additions and 184 deletions
+11 -5
View File
@@ -107,6 +107,12 @@ jobs:
# Line tables keep file:line in backtraces but shrink the cached target dir.
env:
CARGO_PROFILE_DEV_DEBUG: line-tables-only
# Warnings fail CI, but not local builds: a half-finished edit should
# still compile. Passed with `--config` rather than RUSTFLAGS, because
# RUSTFLAGS replaces `.cargo/config.toml`'s Windows `+crt-static` (#902)
# while a `--config` array is appended to it. Every cargo step takes the
# same flags so the tests reuse the build instead of recompiling.
DENY_WARNINGS: target.${{ matrix.target }}.rustflags=['-D','warnings']
# Backstop under the per-step timeouts below. Without any timeout at all a
# hung test runs to GitHub's six-hour job limit, which is how a 90-second
# step turned into a six-hour one three times in a day.
@@ -158,7 +164,7 @@ jobs:
if: needs.changes.outputs.code == 'true'
# Cold-cache builds have been observed at ~10 min; warm ones at ~90s.
timeout-minutes: 30
run: cargo build --locked --target ${{ matrix.target }}
run: cargo build --locked --config "${{ env.DENY_WARNINGS }}" --target ${{ matrix.target }}
# A binary that imports VCRUNTIME140.dll cannot start on a Windows
# machine that has never installed the Visual C++ Redistributable — the
@@ -199,12 +205,12 @@ jobs:
- name: Test
if: needs.changes.outputs.code == 'true'
timeout-minutes: 20
run: cargo test --locked --target ${{ matrix.target }}
run: cargo test --locked --config "${{ env.DENY_WARNINGS }}" --target ${{ matrix.target }}
- name: Test desktop updater
if: needs.changes.outputs.code == 'true'
timeout-minutes: 10
run: cargo test --locked --features updater --bin tty7-updater --target ${{ matrix.target }}
run: cargo test --locked --config "${{ env.DENY_WARNINGS }}" --features updater --bin tty7-updater --target ${{ matrix.target }}
# There is deliberately no post-mortem step here, and that is worth
# recording, because an earlier version of this file had one: on failure it
@@ -251,7 +257,7 @@ jobs:
# rather than in [profile.release] because the release profile is shared with
# the GUI builds, which keep their symbols.
env:
RUSTFLAGS: -C strip=symbols
RUSTFLAGS: -D warnings -C strip=symbols
steps:
- uses: actions/checkout@v4
@@ -344,7 +350,7 @@ jobs:
- target: x86_64-apple-darwin
arch: x86_64
env:
RUSTFLAGS: -C strip=symbols
RUSTFLAGS: -D warnings -C strip=symbols
steps:
- uses: actions/checkout@v4
+1 -1
View File
@@ -851,7 +851,7 @@ fn unquote_path(s: &str) -> String {
/// value is assembled as bytes and read back as UTF-8 at the end.
fn c_unescape(s: &str) -> String {
let mut bytes: Vec<u8> = Vec::with_capacity(s.len());
let mut push_char = |bytes: &mut Vec<u8>, ch: char| {
let push_char = |bytes: &mut Vec<u8>, ch: char| {
let mut buf = [0u8; 4];
bytes.extend_from_slice(ch.encode_utf8(&mut buf).as_bytes());
};
@@ -383,6 +383,7 @@ mod tests {
assert_eq!(unique.len(), c.len());
}
#[cfg(unix)]
const STATUS: &str = r#"{"hosts":{"github.com":[
{"state":"success","active":true,"host":"github.com","login":"me"},
{"state":"success","active":false,"host":"github.com","login":"work"},
+4 -4
View File
@@ -1,5 +1,7 @@
use std::io;
use std::sync::{Arc, Mutex};
use std::sync::Arc;
#[cfg(unix)]
use std::sync::Mutex;
use crate::daemon::control::LinkShutdown;
@@ -170,12 +172,11 @@ fn redirect_to_null(fd: libc::c_int) -> io::Result<()> {
Ok(())
}
#[cfg(test)]
#[cfg(all(test, unix))]
mod tests {
use super::*;
use std::io::{Read as _, Write as _};
#[cfg(unix)]
#[test]
fn a_unix_stream_splits_into_working_halves() {
use std::os::unix::net::UnixStream;
@@ -206,7 +207,6 @@ mod tests {
let _ = reader.join().unwrap();
}
#[cfg(unix)]
#[test]
fn a_shut_stdio_writer_refuses_further_writes() {
let tmp = tempfile::NamedTempFile::new().unwrap();
+2
View File
@@ -3166,6 +3166,7 @@ impl ReplayRing {
/// A ring carried across a handoff, with the pane's modes folded back
/// from it: `head` is the carried [`Self::head_modes`], and the pane's
/// modes are that plus every byte the ring still holds.
#[cfg(any(unix, test))]
fn adopted(
segments: Vec<crate::daemon::scrollback::Segment>,
size: WinSize,
@@ -3180,6 +3181,7 @@ impl ReplayRing {
}
/// The geometry new output would be recorded at.
#[cfg(unix)]
fn tail_size(&self) -> WinSize {
self.segments
.back()
+6 -5
View File
@@ -1,13 +1,17 @@
#[cfg(any(target_os = "macos", target_os = "linux", test))]
use std::path::Path;
#[cfg(any(target_os = "macos", target_os = "linux", test))]
use crate::daemon::protocol::{RemoteContext, RemoteKind};
#[cfg(any(target_os = "macos", target_os = "linux", test))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct SshInvocation {
pub context: RemoteContext,
pub forward_args: Vec<String>,
}
#[cfg(any(target_os = "macos", target_os = "linux", test))]
pub(crate) fn parse_ssh_invocation(argv: &[String]) -> Option<SshInvocation> {
let program = argv.first()?;
let name = Path::new(program).file_name()?.to_string_lossy();
@@ -95,6 +99,7 @@ pub(crate) fn parse_ssh_invocation(argv: &[String]) -> Option<SshInvocation> {
})
}
#[cfg(any(target_os = "macos", target_os = "linux", test))]
fn option_takes_value(flag: char) -> bool {
matches!(
flag,
@@ -121,6 +126,7 @@ fn option_takes_value(flag: char) -> bool {
)
}
#[cfg(any(target_os = "macos", target_os = "linux"))]
pub(crate) fn foreground_argv(pid: i32) -> Option<Vec<String>> {
platform_foreground_argv(pid)
}
@@ -210,11 +216,6 @@ fn parse_macos_procargs(buf: &[u8]) -> Option<Vec<String>> {
(!argv.is_empty()).then_some(argv)
}
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
fn platform_foreground_argv(_pid: i32) -> Option<Vec<String>> {
None
}
#[cfg(test)]
mod tests {
use super::*;
+1
View File
@@ -1018,6 +1018,7 @@ mod tests {
assert!(routed.join().unwrap().is_err());
}
#[cfg(unix)]
fn responder_lock() -> std::sync::MutexGuard<'static, ()> {
static LOCK: Mutex<()> = Mutex::new(());
LOCK.lock().unwrap_or_else(|e| e.into_inner())
+2
View File
@@ -31,6 +31,7 @@ impl Registry {
/// Reusing a number would be worse than skipping one: a client that
/// reconnects after a handoff is still holding the old ids, and an `Attach`
/// for one of them has to find the pane it means or nothing at all.
#[cfg(unix)]
fn claim_ids_past(&self, next: u64) {
self.next_id.fetch_max(next, Ordering::Relaxed);
}
@@ -659,6 +660,7 @@ fn hand_over(_registry: &Registry, _exe: &std::path::Path) -> anyhow::Error {
}
/// What a pane does to the registry when its shell dies.
#[cfg(unix)]
fn reaper(registry: Arc<Registry>, id: u64) -> impl FnOnce() + Send + 'static {
move || {
std::thread::Builder::new()
@@ -2527,6 +2527,7 @@ mod tests {
}
/// The OSC 0 title the shell settled on, as `user@host:dir`.
#[cfg(unix)]
fn reported_title(text: &str) -> String {
text.split("\u{1b}]")
.filter_map(|s| s.strip_prefix("0;"))
+1
View File
@@ -1424,6 +1424,7 @@ fn detect_parent_shell() -> Option<PathBuf> {
None
}
#[cfg(any(unix, test))]
fn is_supported_shell(path: &Path) -> bool {
let Some(name) = path.file_name().and_then(|name| name.to_str()) else {
return false;
+1 -1
View File
@@ -488,7 +488,7 @@ fn drain_git_stream(
loop {
match next(idle) {
Ok(GitStreamMsg::Chunk(bytes)) => {
let _ = queued.fetch_update(Ordering::AcqRel, Ordering::Acquire, |q| {
let _ = queued.try_update(Ordering::AcqRel, Ordering::Acquire, |q| {
Some(q.saturating_sub(bytes.len()))
});
split.push(&bytes, &mut *on_line);
+3 -1
View File
@@ -1,6 +1,8 @@
use std::collections::{HashMap, VecDeque};
use std::io::{self, Read, Write};
use std::path::{Path, PathBuf};
#[cfg(unix)]
use std::path::Path;
use std::path::PathBuf;
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use std::sync::{Arc, Condvar, Mutex};
use std::time::Duration;
+39 -29
View File
@@ -2701,13 +2701,24 @@ mod windows {
}
let bytes =
fs::read(archive).map_err(|error| format!("reading {}: {error}", archive.display()))?;
let archive = smol::block_on(async_zip::base::read::mem::ZipFileReader::new(bytes))
.map_err(|error| format!("opening the portable ZIP: {error}"))?;
let entries = archive.file().entries();
if entries.len() > MAX_PORTABLE_ENTRIES {
let options = async_zip::base::read1::ZipOptions {
max_cd_num_files: (MAX_PORTABLE_ENTRIES + 1) as u64,
max_cd_num_files_load: (MAX_PORTABLE_ENTRIES + 1) as u64,
max_uncompressed_size_per_file: MAX_PORTABLE_EXPANDED_BYTES,
max_compressed_size_per_file: MAX_PORTABLE_EXPANDED_BYTES,
..async_zip::base::read1::ZipOptions::untrusted()
};
let mut archive = smol::block_on(
async_zip::base::read1::seek::ZipArchiveReader::open_with_options(
smol::io::Cursor::new(bytes),
options,
),
)
.map_err(|error| format!("opening the portable ZIP: {error}"))?;
if archive.cdrs().len() > MAX_PORTABLE_ENTRIES {
return Err(format!(
"the portable ZIP has {} entries; the limit is {MAX_PORTABLE_ENTRIES}",
entries.len()
archive.cdrs().len()
));
}
fs::create_dir(payload)
@@ -2715,23 +2726,33 @@ mod windows {
let mut seen = HashSet::new();
let mut expanded_bytes = 0u64;
for (index, entry) in entries.iter().enumerate() {
let name = entry
.filename()
.as_str()
.map_err(|error| format!("reading portable ZIP entry {index} name: {error}"))?;
for index in 0..archive.cdrs().len() {
let (name, unix_mode, expected_size, is_directory) = {
let entry = &archive.cdrs()[index];
let name = entry
.insecure_file_name
.as_str()
.ok_or_else(|| {
format!("reading portable ZIP entry {index} name: name is not UTF-8")
})?
.to_owned();
let unix_mode = (entry.cdrh.v_made_by >> 8 == 3)
.then_some((entry.cdrh.exter_attr >> 16) as u16);
let expected_size = entry
.uncompressed_size()
.map_err(|error| format!("reading portable ZIP entry {name} size: {error}"))?;
let is_directory = name.ends_with('/');
(name, unix_mode, expected_size, is_directory)
};
if name.contains('\\') {
return Err(format!(
"the portable ZIP path uses a non-canonical separator: {name}"
));
}
if entry
.unix_permissions()
.is_some_and(|mode| mode & 0o170000 == 0o120000)
{
if unix_mode.is_some_and(|mode| mode & 0o170000 == 0o120000) {
return Err(format!("the portable ZIP contains a symbolic link: {name}"));
}
let relative = PathBuf::from(name);
let relative = PathBuf::from(name.as_str());
let key = portable_path_key(&relative)?;
if !seen.insert(key) {
return Err(format!(
@@ -2741,7 +2762,7 @@ mod windows {
}
validate_portable_relative_path(&relative)?;
expanded_bytes = expanded_bytes
.checked_add(entry.uncompressed_size())
.checked_add(expected_size)
.ok_or_else(|| "the portable ZIP expanded-size total overflowed".to_string())?;
if expanded_bytes > MAX_PORTABLE_EXPANDED_BYTES {
return Err(format!(
@@ -2751,11 +2772,8 @@ mod windows {
}
let output = payload.join(&relative);
let is_directory = entry
.dir()
.map_err(|error| format!("reading portable ZIP entry {name}: {error}"))?;
if is_directory {
if entry.uncompressed_size() != 0 {
if expected_size != 0 {
return Err(format!(
"the portable ZIP directory entry has file data: {name}"
));
@@ -2773,10 +2791,8 @@ mod windows {
.create_new(true)
.open(&output)
.map_err(|error| format!("creating {}: {error}", output.display()))?;
let mut entry_reader = smol::block_on(archive.reader_with_entry(index))
let mut entry_reader = smol::block_on(archive.file(index))
.map_err(|error| format!("opening portable ZIP entry {name}: {error}"))?;
let expected_size = entry.uncompressed_size();
let expected_crc = entry.crc32();
smol::block_on(async {
let mut buffer = [0u8; 64 * 1024];
let mut written = 0u64;
@@ -2805,12 +2821,6 @@ mod windows {
"the portable ZIP entry size is {written}, expected {expected_size}: {name}"
));
}
let actual_crc = entry_reader.compute_hash();
if actual_crc != expected_crc {
return Err(format!(
"the portable ZIP entry failed CRC32 verification: {name}"
));
}
Ok(())
})?;
}
+7 -3
View File
@@ -47,12 +47,14 @@ pub enum Outcome {
/// Freshly linked (or copied, under AppImage) into a directory on PATH.
Installed(PathBuf),
/// Installed somewhere the user's PATH does not currently cover.
#[cfg(unix)]
InstalledOffPath(PathBuf),
/// Installed, but an earlier PATH entry holds a different `tty7` that keeps
/// winning the lookup.
InstalledShadowed { ours: PathBuf, winner: PathBuf },
/// Every directory we would write to is already taken by someone else's
/// `tty7`, and none of them is ours to move.
#[cfg(unix)]
Occupied(PathBuf),
/// Nowhere to write.
Failed(String),
@@ -81,6 +83,7 @@ pub fn install(enabled: bool) -> Outcome {
}
Outcome::AlreadyInstalled(p) => log::debug!("tty7 CLI already on PATH at {}", p.display()),
Outcome::Installed(p) => log::info!("put the tty7 CLI on PATH at {}", p.display()),
#[cfg(unix)]
Outcome::InstalledOffPath(p) => log::warn!(
"installed the tty7 CLI at {}, which is not on your PATH — add it to use `tty7` \
outside a tty7 pane",
@@ -92,6 +95,7 @@ pub fn install(enabled: bool) -> Outcome {
ours.display(),
winner.display()
),
#[cfg(unix)]
Outcome::Occupied(p) => log::info!(
"leaving the existing `tty7` at {} alone; the bundled CLI was not installed",
p.display()
@@ -140,9 +144,9 @@ fn install_inner(enabled: bool) -> Outcome {
// never has a reason to say `Occupied` — but an existing one earlier on
// PATH still beats it, and "installed" alone would be a lie.
let ours = match &outcome {
Outcome::AlreadyInstalled(p) | Outcome::Installed(p) | Outcome::InstalledOffPath(p) => {
p.clone()
}
Outcome::AlreadyInstalled(p) | Outcome::Installed(p) => p.clone(),
#[cfg(unix)]
Outcome::InstalledOffPath(p) => p.clone(),
_ => return outcome,
};
match first_cli_on(&user_path) {
+20 -4
View File
@@ -12,22 +12,33 @@
//! running GUI through `GuiOpen`, then continues normal startup when no GUI
//! receives it.
#[cfg(any(windows, test))]
use std::ffi::OsString;
use std::path::{Path, PathBuf};
#[cfg(any(windows, test))]
use std::path::Path;
#[cfg(windows)]
use std::path::PathBuf;
use anyhow::{Context as _, Result};
#[cfg(windows)]
use anyhow::Context as _;
use anyhow::Result;
#[cfg(any(windows, test))]
use crate::ui::i18n::{L10nKey, t};
#[cfg(any(windows, test))]
const DIRECTORY_KEY: &str = r"Software\Classes\Directory\shell\tty7";
#[cfg(any(windows, test))]
const BACKGROUND_KEY: &str = r"Software\Classes\Directory\Background\shell\tty7";
#[cfg(any(windows, test))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Location {
Directory,
Background,
}
#[cfg(any(windows, test))]
impl Location {
fn key(self) -> &'static str {
match self {
@@ -61,6 +72,7 @@ impl Location {
}
}
#[cfg(any(windows, test))]
#[derive(Debug)]
struct Registration {
location: Location,
@@ -68,6 +80,7 @@ struct Registration {
command: OsString,
}
#[cfg(any(windows, test))]
fn replace_entry_with(
registration: &Registration,
delete: impl FnOnce(&str) -> Result<()>,
@@ -80,6 +93,7 @@ fn replace_entry_with(
write(registration)
}
#[cfg(any(windows, test))]
impl Registration {
fn new(location: Location, app: &Path) -> Self {
Self {
@@ -124,6 +138,7 @@ pub fn refresh_labels() {
/// Quotes are unconditional: both the executable and the Explorer-substituted
/// folder can contain spaces. Windows file names cannot contain a quote, so the
/// resulting command is unambiguous without an additional escape layer.
#[cfg(any(windows, test))]
fn quoted_open_command(executable: &Path, placeholder: &str) -> OsString {
let mut command = OsString::from("\"");
command.push(executable.as_os_str());
@@ -133,6 +148,7 @@ fn quoted_open_command(executable: &Path, placeholder: &str) -> OsString {
command
}
#[cfg(windows)]
fn application_path() -> Result<PathBuf> {
let app = std::env::current_exe().context("locating the running tty7 application")?;
app.parent()
@@ -140,6 +156,7 @@ fn application_path() -> Result<PathBuf> {
Ok(app)
}
#[cfg(any(windows, test))]
fn registrations(app: &Path) -> [Registration; 2] {
[
Registration::new(Location::Directory, app),
@@ -158,8 +175,7 @@ mod windows {
};
use windows_sys::Win32::System::Registry::{
HKEY, HKEY_CURRENT_USER, KEY_READ, KEY_WRITE, REG_OPTION_NON_VOLATILE, REG_SZ, RegCloseKey,
RegCreateKeyExW, RegDeleteTreeW, RegOpenKeyExW, RegQueryInfoKeyW, RegQueryValueExW,
RegSetValueExW,
RegCreateKeyExW, RegDeleteTreeW, RegOpenKeyExW, RegSetValueExW,
};
use windows_sys::Win32::UI::Shell::{SHCNE_ASSOCCHANGED, SHCNF_IDLIST, SHChangeNotify};
+2 -1
View File
@@ -151,7 +151,7 @@ pub enum UpdateInstallHint {
impl UpdateInstallHint {
/// The reason in plain English, pinned by tests. Anything a user reads
/// goes through `localized_update_install_hint` instead (#602).
#[cfg_attr(not(test), allow(dead_code))]
#[cfg(all(test, target_os = "windows"))]
fn english(&self) -> String {
match self {
#[cfg(target_os = "macos")]
@@ -2123,6 +2123,7 @@ struct PackageOffer {
}
impl PackageOffer {
#[cfg(any(target_os = "macos", target_os = "linux", test))]
fn plain(name: String) -> Self {
Self {
name,
+1 -5
View File
@@ -402,6 +402,7 @@ impl ScmData {
/// there". A directory that is not a repository is a perfectly normal
/// thing for a pane to be sitting in, and without somewhere to record the
/// negative answer the next look asks again — every frame.
#[cfg(test)]
pub fn known_status(
&self,
host: HostId,
@@ -411,11 +412,6 @@ impl ScmData {
Some(self.status_for(host, root))
}
/// Whether this root has been read at all, whatever the answer was.
pub fn probed(&self, host: HostId, root: &Path) -> bool {
self.read_at.get(host, root).is_some()
}
pub fn epoch(&self, host: HostId, root: &Path) -> u64 {
self.epoch.get(host, root).copied().unwrap_or(0)
}
+1
View File
@@ -954,6 +954,7 @@ pub(super) struct LinkRoots {
impl LinkRoots {
/// Roots on the machine tty7 is running on, where `$HOME` means what it
/// says and paths are spelled this OS's way.
#[cfg(test)]
pub fn local(dirs: Vec<PathBuf>) -> Self {
Self {
dirs,
+1 -1
View File
@@ -615,7 +615,7 @@ pub(super) enum LoopbackPlan {
/// reachable once a forward exists, and building that forward is something
/// this app already knows how to do. Answering only "local or not" is what
/// left the Ports list showing a port it then refused to open.
pub(crate) fn port_route_of(plan: &LoopbackPlan, local: bool) -> PortRoute {
fn port_route_of(plan: &LoopbackPlan, local: bool) -> PortRoute {
match plan {
// WSL shares this machine's loopback, so its ports are already here
// under the same number.
+4 -51
View File
@@ -20,7 +20,7 @@ use crate::core::session::{
use crate::core::shells::ShellInventory;
use crate::core::ssh_config;
use crate::core::window_state::{WindowGeometry as _, WindowState};
use crate::daemon::protocol::{RemoteContext, RemoteKind, ShellSpec, ssh_option_takes_value};
use crate::daemon::protocol::{RemoteKind, ShellSpec, ssh_option_takes_value};
use crate::daemon::spawn::DaemonMismatch;
use crate::terminal::view::{ChildExited, TerminalView};
use crate::ui::forwards::{ForwardFields, added_forward, rule_of};
@@ -287,6 +287,7 @@ fn title_strip_rule_and_pad(rule: gpui::Pixels) -> (gpui::Pixels, gpui::Pixels)
/// The centre of the box a row built by [`title_strip_row`] gives its contents:
/// the bar's height, less whatever its bottom edge spends on rule and padding.
#[cfg(test)]
fn title_strip_content_centre(rule: gpui::Pixels) -> gpui::Pixels {
let (border, pad) = title_strip_rule_and_pad(rule);
gpui::px((TITLE_BAR_HEIGHT - border.as_f32() - pad.as_f32()) / 2.)
@@ -362,10 +363,6 @@ pub(crate) fn tile_trailing_inset() -> f32 {
(CONTENT_INSET - TILE_PAD).max(TILE_EDGE_GAP)
}
pub(crate) fn tile_trailing_inset_sm() -> f32 {
(CONTENT_INSET - TILE_PAD_SM).max(TILE_EDGE_GAP)
}
pub(crate) const TITLE_BAR_LEAD: f32 = if cfg!(target_os = "macos") {
crate::ui::theme::TRAFFIC_LIGHTS_LEAD
} else {
@@ -762,18 +759,6 @@ impl Tab {
/// [`Self::leaf_title`] together with what a `~` in it would mean — one
/// leaf lookup, so the title and the home shortening it can never come
/// from different panes (#580).
pub(crate) fn leaf_title_and_home(
&self,
window: Option<&Window>,
cx: &App,
) -> (String, Option<std::path::PathBuf>) {
let Some(leaf) = self.title_leaf(window, cx) else {
return (String::new(), None);
};
let leaf = leaf.read(cx);
(leaf.title.clone(), leaf.display_home(cx))
}
/// This tab as the shared label ladder reads it, together with what a `~`
/// in whatever it ends up named would mean.
///
@@ -1554,6 +1539,7 @@ impl Tty7App {
.detach();
}
#[cfg(test)]
pub(crate) fn with_session(
workspace: Option<WorkspaceId>,
session: Option<Session>,
@@ -3522,7 +3508,7 @@ impl Tty7App {
// typed one number to forward one port has not been asked to care
// which local port it lands on, and the row says where it came out.
let retry_free_port = !fields.advanced && rule.bind_port != 0;
let mut failure = match self.place_forward(&route, rule.clone()) {
let failure = match self.place_forward(&route, rule.clone()) {
PlaceOutcome::Placed => None,
// Nobody answered, so nothing was bound and nothing would be bound
// by asking again — a second round trip would only spend another
@@ -8526,39 +8512,6 @@ impl Tty7App {
self.leaf_close_reason(&leaf, cx)
}
pub(crate) fn active_ssh_pane(
&self,
window: &Window,
cx: &App,
) -> Option<(u64, RemoteContext)> {
let pane = self
.tabs
.get(self.active)?
.pane
.focused_or_first(window, cx)?;
let pane = pane.read(cx);
let remote = pane.remote_context()?;
(remote.kind != crate::daemon::protocol::RemoteKind::Wsl).then_some((pane.pane_id, remote))
}
pub(crate) fn active_connected_native_ssh_pane(
&self,
window: &Window,
cx: &App,
) -> Option<(u64, RemoteContext)> {
use crate::daemon::protocol::{RemoteKind, SshPhase};
let (pane_id, remote) = self.active_ssh_pane(window, cx)?;
if remote.kind != RemoteKind::NativeSsh {
return None;
}
let leaf = self
.tabs
.get(self.active)?
.pane
.focused_or_first(window, cx)?;
matches!(leaf.read(cx).ssh_phase(), Some(SshPhase::Connected)).then_some((pane_id, remote))
}
pub(crate) fn select_settings_section(
&mut self,
target: SettingsSection,
+1 -1
View File
@@ -4127,7 +4127,7 @@ mod close_gpui_tests {
/// holds the focus as it is drawn.
fn settle(vcx: &mut VisualTestContext) {
vcx.run_until_parked();
vcx.update(|window, cx| window.draw(cx));
let _ = vcx.update(|window, cx| window.draw(cx));
vcx.run_until_parked();
}
+1 -1
View File
@@ -405,7 +405,7 @@ mod tests {
app.toggle_editor_problems(cx);
});
vcx.run_until_parked();
vcx.update(|window, cx| window.draw(cx));
let _ = vcx.update(|window, cx| window.draw(cx));
app.update_in(&mut vcx, |app, window, cx| {
assert!(app.editor.problems.open);
assert!(app.render_editor_problems(window, cx).is_some());
+1
View File
@@ -1186,6 +1186,7 @@ pub(crate) mod row_probe {
}
/// The count since the last call, and zero from here.
#[cfg(not(target_os = "windows"))]
pub(crate) fn take() -> u64 {
BUILT.replace(0)
}
-9
View File
@@ -1977,15 +1977,6 @@ pub fn t_plural(key: L10nKey, count: usize, args: &[(&str, &str)]) -> String {
)
}
/// Select a named branch of a translation and fill placeholders.
pub fn t_select(key: L10nKey, branch: &'static str, args: &[(&str, &str)]) -> String {
apply_template(
translate_variant(current_locale_index(), key, branch),
args,
None,
)
}
fn apply_template(template: &'static str, args: &[(&str, &str)], count: Option<usize>) -> String {
let mut text = template.to_string();
if let Some(n) = count {
+1 -4
View File
@@ -1896,10 +1896,7 @@ impl Tty7App {
// could not be asked, which is not the same as an empty
// answer — see `Host::pane_procs`.
let procs = match &host {
Some(host) => {
use crate::ui::host_ops::Host as _;
host.pane_procs(pane_id)
}
Some(host) => host.pane_procs(pane_id),
None => Some(crate::terminal::RemoteTerminal::query_procs(pane_id)),
};
let managed = route.map(|r| r.list()).unwrap_or_default();
+1
View File
@@ -101,6 +101,7 @@ impl SearchTab {
}
/// The neighbouring tab, wrapping at the ends.
#[cfg(test)]
pub(crate) fn step(self, forward: bool) -> SearchTab {
let n = Self::ORDER.len();
let i = Self::ORDER.iter().position(|t| *t == self).unwrap_or(0);
+2 -2
View File
@@ -3381,7 +3381,7 @@ mod tests {
#[test]
fn previously_unsearchable_settings_are_findable() {
use SettingsSection::*;
let mut cases: Vec<(&str, SettingsSection)> = vec![
let cases: Vec<(&str, SettingsSection)> = vec![
("opacity", Appearance),
("blur", Appearance),
("completion", Terminal),
@@ -3413,7 +3413,7 @@ mod tests {
("image opacity", Appearance),
];
#[cfg(target_os = "windows")]
cases.extend([
let cases = cases.into_iter().chain([
("material", Appearance),
("mica", Appearance),
("acrylic", Appearance),
-2
View File
@@ -34,7 +34,6 @@ pub(crate) struct Tk {
pub k04: Hsla,
pub k05: Hsla,
pub k06: Hsla,
pub k07: Hsla,
pub k08: Hsla,
pub k15: Hsla,
pub k3: Hsla,
@@ -89,7 +88,6 @@ impl Tk {
k04: a(0.04, 0.055),
k05: a(0.05, 0.07),
k06: a(0.06, 0.08),
k07: a(0.07, 0.09),
k08: a(0.08, 0.08),
k15: a(0.15, 0.16),
k3: a(0.3, 0.3),
+1 -5
View File
@@ -4457,12 +4457,11 @@ mod gpui_tests {
});
}
/// One of the three places on the card a test wants to put the pointer.
/// One of the places on the card a test wants to put the pointer.
#[derive(Clone, Copy)]
enum Spot {
Workspaces,
Tabs,
Search,
}
/// Where that part of the card lands on screen. The card is centred and
@@ -4479,9 +4478,6 @@ mod gpui_tests {
let left_w = super::LEFT_W.min(card_w * 0.5);
let card_left = (viewport.width.as_f32() - card_w) / 2.;
let (dx, dy) = match spot {
// The search row is the first thing in the card; both columns
// start below it.
Spot::Search => (100., 20.),
Spot::Workspaces => (20., super::SEARCH_H + super::COLUMN_PAD + 16.),
// Past the tab column's own header row, onto its first tab.
Spot::Tabs => (
+2 -47
View File
@@ -55,9 +55,8 @@ fn shell_spec(shell: &DetectedShell) -> ShellSpec {
/// same titles.
pub(crate) use tty7_core::core::tab_view::strip_host_prefix;
/// `home` is the home directory of the machine `path` is on, from
/// [`Tab::leaf_title_and_home`](crate::ui::app::Tab::leaf_title_and_home) or
/// the workspace's host; `None` leaves the path spelled out (#580).
/// `home` is the home directory of the machine `path` is on, from the
/// workspace's host; `None` leaves the path spelled out (#580).
pub(crate) fn abbreviate_home<'a>(
path: &'a str,
home: Option<&std::path::Path>,
@@ -1294,52 +1293,8 @@ pub(crate) fn agent_status_label(
}
pub(crate) const LIVE_DOT: u32 = 0x22C55E;
pub(crate) const UNKNOWN_DOT: u32 = 0x9AA0A6;
pub(crate) fn workspace_avatar(
name: &str,
live: crate::terminal::pane_liveness::Liveness,
size: f32,
cx: &App,
) -> impl IntoElement + use<> {
use crate::terminal::pane_liveness::Liveness;
let dot = match live {
Liveness::Alive => Some(LIVE_DOT),
Liveness::Unknown => Some(UNKNOWN_DOT),
Liveness::Stopped => None,
};
let initial: String = name
.chars()
.next()
.map(|c| c.to_uppercase().to_string())
.unwrap_or_else(|| "~".to_string());
// The disc reads the same on every row, current one included: the rows that
// are the current workspace already say so with a badge, a heavier name and
// a selected background, and dimming the disc on top of that only pushed the
// monogram under the liveness dot beside it, which is never dimmed.
div()
.relative()
.flex_shrink_0()
.size(px(size))
.child(
div()
.size(px(size))
.rounded_full()
.bg(cx.theme().secondary)
.flex()
.items_center()
.justify_center()
.text_size(px((size * 0.46).round()))
.font_weight(FontWeight::MEDIUM)
.text_color(cx.theme().foreground.opacity(0.65))
.child(initial),
)
.children(
dot.map(|rgb| Tty7App::status_dot(rgb, 0, size, cx.theme().popover, false, false)),
)
}
pub(crate) fn select_workspace_action(index: usize) -> Option<Box<dyn gpui::Action>> {
Some(match index {
0 => Box::new(SelectWorkspace1) as Box<dyn gpui::Action>,
+3
View File
@@ -319,6 +319,7 @@ pub(crate) const SYSTEM_MATERIAL_OPACITY: f32 = 0.82;
/// 11 22H2 (build 22621), acrylic needs 1809 (build 17763); below that plain
/// translucency is the best Windows can do. Pure function so the fallback
/// table is unit-testable on every platform.
#[cfg(any(target_os = "windows", test))]
pub(crate) fn windows_background_appearance(
backdrop: WindowBackdrop,
blur: bool,
@@ -379,6 +380,7 @@ pub(crate) fn windows_background_appearance(
/// render identically — hiding them beats misrepresenting them. Below 1809
/// (17763) there is no blur API at all, so only plain translucency is left.
/// Pure function so the table is unit-testable on every platform.
#[cfg(any(target_os = "windows", test))]
pub(crate) fn supported_backdrops_for(build: u32) -> &'static [WindowBackdrop] {
if build >= 22_621 {
&[
@@ -428,6 +430,7 @@ pub(crate) fn windows_build_number() -> u32 {
})
}
#[cfg_attr(not(target_os = "windows"), allow(unused_variables))]
pub(crate) fn resolved_background_appearance(
backdrop: WindowBackdrop,
blur: bool,
+1
View File
@@ -208,6 +208,7 @@ fn badge(pixmap: &mut tiny_skia::Pixmap) {
#[cfg(target_os = "linux")]
pub(super) fn render_argb(attention: bool) -> Option<(Vec<u8>, u32)> {
let img = render(attention)?;
debug_assert_eq!(img.width, img.height);
let mut argb = Vec::with_capacity(img.data.len());
for px in img.data.chunks_exact(4) {
argb.extend_from_slice(&[px[3], px[0], px[1], px[2]]);
+2 -2
View File
@@ -5,7 +5,7 @@
use std::net::{Ipv4Addr, SocketAddr, UdpSocket};
use std::time::Duration;
use iroh::dns::{DnsProtocol, DnsResolver};
use iroh::dns::{DnsResolver, NameserverConfig};
/// Answers one A query for `answer`, with an EDNS OPT record whose TTL is
/// `opt_ttl`: [extended RCODE][version][DO + Z flags], per RFC 6891.
@@ -44,7 +44,7 @@ async fn resolve_with_opt_ttl(opt_ttl: u32) -> Result<Vec<std::net::IpAddr>, Str
// No fallback: a public resolver answering instead would hide the bug.
let resolver = DnsResolver::builder()
.with_nameserver(addr, DnsProtocol::Udp)
.add_nameserver_config(NameserverConfig::udp(addr.ip()).with_port(addr.port()))
.disable_fallback()
.build();
let result = resolver