mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-11-06 00:15:06 +00:00
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:
co-authored by
Copilot
l0ng-ai
parent
69536b361a
commit
4d33021cbe
@@ -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
|
||||
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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;"))
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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(())
|
||||
})?;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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),
|
||||
|
||||
@@ -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
@@ -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
@@ -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>,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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]]);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user