chore: fix compiler warnings and format code

- Added #[allow(dead_code)] to unused fields, variants, and functions in sftp.rs and terminal.rs
- Renamed unused window variable to _window in main.rs
- Ran cargo fmt to format the codebase
This commit is contained in:
TomZz
2026-06-11 02:22:49 +08:00
parent 9885b2573a
commit 108a9cf95b
8 changed files with 359 additions and 151 deletions
+2 -1
View File
@@ -214,6 +214,7 @@ impl ConfigStore {
return Ok(());
}
let raw = serde_json::to_string_pretty(&self.cache)?;
fs::write(&self.path, raw).with_context(|| format!("failed to write {}", self.path.display()))
fs::write(&self.path, raw)
.with_context(|| format!("failed to write {}", self.path.display()))
}
}
+5 -2
View File
@@ -5,7 +5,7 @@ use std::{
};
use anyhow::{Context, Result};
use portable_pty::{native_pty_system, CommandBuilder, PtySize};
use portable_pty::{CommandBuilder, PtySize, native_pty_system};
use crate::terminal::{BackendCommand, BackendEvent, BackendTx};
@@ -34,7 +34,10 @@ pub fn spawn_local_terminal(
});
let mut cmd = CommandBuilder::new(&shell);
cmd.env("TERM", std::env::var("TERM").unwrap_or_else(|_| "xterm-256color".into()));
cmd.env(
"TERM",
std::env::var("TERM").unwrap_or_else(|_| "xterm-256color".into()),
);
cmd.env(
"COLORTERM",
std::env::var("COLORTERM").unwrap_or_else(|_| "truecolor".into()),
+67 -49
View File
@@ -13,7 +13,6 @@ use std::{
use alacritty_terminal::index::Side;
use alacritty_terminal::selection::SelectionType;
use anyhow::{Context as _, Result};
use rust_i18n::t;
use gpui::{
Anchor, App, AppContext as _, Bounds, ClipboardItem, Context, ElementId, Entity, FocusHandle,
Focusable as _, FontWeight, Hsla, InteractiveElement as _, IntoElement, KeyBinding,
@@ -38,6 +37,7 @@ use gpui_component::{
v_flex,
};
use gpui_component_assets::Assets;
use rust_i18n::t;
use tokio::runtime::Runtime;
use uuid::Uuid;
@@ -204,8 +204,7 @@ struct SftpContextMenuState {
impl Ashell {
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
let host_input =
cx.new(|cx| InputState::new(window, cx).placeholder(t!("host")));
let host_input = cx.new(|cx| InputState::new(window, cx).placeholder(t!("host")));
let session_name_input =
cx.new(|cx| InputState::new(window, cx).placeholder("name (optional)"));
let port_input = cx.new(|cx| InputState::new(window, cx).default_value("22"));
@@ -907,9 +906,7 @@ impl Ashell {
div()
.text_size(px(11.))
.text_color(_cx.theme().muted_foreground)
.child(t!(
"open_local_shell_tab"
)),
.child(t!("open_local_shell_tab")),
),
),
)
@@ -951,9 +948,7 @@ impl Ashell {
div()
.text_size(px(11.))
.text_color(_cx.theme().muted_foreground)
.child(t!(
"create_or_edit_ssh_session"
)),
.child(t!("create_or_edit_ssh_session")),
),
),
)
@@ -1051,27 +1046,25 @@ impl Ashell {
h_flex()
.items_center()
.gap_3()
.child(div().w(px(180.)).child(t!("terminal_font_size").to_string()))
.child(
Button::new("font-size-down")
.label("-")
.on_click(window.listener_for(&view, |this, _, _, cx| {
this.change_terminal_font_size(-1.0, cx)
})),
)
.child(
div()
.min_w(px(64.))
.text_center()
.child(format!("{:.0}px", view.read(cx).terminal_font_size)),
.w(px(180.))
.child(t!("terminal_font_size").to_string()),
)
.child(
Button::new("font-size-up")
.label("+")
.on_click(window.listener_for(&view, |this, _, _, cx| {
this.change_terminal_font_size(1.0, cx)
})),
),
.child(Button::new("font-size-down").label("-").on_click(
window.listener_for(&view, |this, _, _, cx| {
this.change_terminal_font_size(-1.0, cx)
}),
))
.child(div().min_w(px(64.)).text_center().child(format!(
"{:.0}px",
view.read(cx).terminal_font_size
)))
.child(Button::new("font-size-up").label("+").on_click(
window.listener_for(&view, |this, _, _, cx| {
this.change_terminal_font_size(1.0, cx)
}),
)),
)
.child(
h_flex()
@@ -1083,7 +1076,8 @@ impl Ashell {
.small()
.icon(IconName::Globe)
.label({
let current_locale = view.read(cx).config.locale().to_string();
let current_locale =
view.read(cx).config.locale().to_string();
if current_locale == "en" {
t!("english").to_string()
} else if current_locale == "zh-CN" {
@@ -1095,35 +1089,60 @@ impl Ashell {
.dropdown_menu_with_anchor(Anchor::BottomRight, {
let view = view.clone();
move |mut menu, window, cx| {
let current_locale = view.read(cx).config.locale().to_string();
let current_locale = view
.read(cx)
.config
.locale()
.to_string();
menu = menu
.min_w(160.)
.item(
PopupMenuItem::new(t!("follow_system").to_string())
.checked(current_locale == "system")
.on_click(window.listener_for(&view, |this, _, window, cx| {
this.set_display_language("system", window, cx)
})),
PopupMenuItem::new(
t!("follow_system").to_string(),
)
.checked(current_locale == "system")
.on_click(window.listener_for(
&view,
|this, _, window, cx| {
this.set_display_language(
"system", window, cx,
)
},
)),
)
.separator()
.item(
PopupMenuItem::new(t!("english").to_string())
.checked(current_locale == "en")
.on_click(window.listener_for(&view, |this, _, window, cx| {
this.set_display_language("en", window, cx)
})),
PopupMenuItem::new(
t!("english").to_string(),
)
.checked(current_locale == "en")
.on_click(window.listener_for(
&view,
|this, _, window, cx| {
this.set_display_language(
"en", window, cx,
)
},
)),
)
.item(
PopupMenuItem::new(t!("chinese").to_string())
.checked(current_locale == "zh-CN")
.on_click(window.listener_for(&view, |this, _, window, cx| {
this.set_display_language("zh-CN", window, cx)
})),
PopupMenuItem::new(
t!("chinese").to_string(),
)
.checked(current_locale == "zh-CN")
.on_click(window.listener_for(
&view,
|this, _, window, cx| {
this.set_display_language(
"zh-CN", window, cx,
)
},
)),
);
menu
}
})
)
}),
),
)
.child(
div()
@@ -2533,7 +2552,7 @@ impl Ashell {
.border_color(cx.theme().border)
.bg(cx.theme().background)
.on_drop(
cx.listener(|this, paths: &gpui::ExternalPaths, window, cx| {
cx.listener(|this, paths: &gpui::ExternalPaths, _window, cx| {
let paths_to_upload: Vec<String> = paths
.0
.iter()
@@ -3600,8 +3619,7 @@ fn main() {
.with_quit_mode(QuitMode::Explicit);
#[cfg(not(target_os = "macos"))]
let app = gpui_platform::application()
.with_assets(Assets);
let app = gpui_platform::application().with_assets(Assets);
app.on_reopen(|cx| {
if cx.windows().is_empty() {
open_main_window(cx);
+105 -27
View File
@@ -4,15 +4,15 @@ use std::{
sync::Arc,
};
use anyhow::{anyhow, Context, Result};
use anyhow::{Context, Result, anyhow};
use async_trait::async_trait;
use chrono::{DateTime, TimeZone, Utc};
use directories::BaseDirs;
use flate2::read::GzDecoder;
use russh::{
client::{self, Handler},
keys::{decode_secret_key, key::PrivateKeyWithHashAlg, load_secret_key, HashAlg, PrivateKey},
Disconnect,
client::{self, Handler},
keys::{HashAlg, PrivateKey, decode_secret_key, key::PrivateKeyWithHashAlg, load_secret_key},
};
use russh_sftp::client::SftpSession;
use tokio::{
@@ -38,6 +38,7 @@ pub struct RemoteEntry {
pub modified: u32,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct PreviewData {
pub path: String,
@@ -49,9 +50,16 @@ pub struct PreviewData {
#[derive(Debug)]
pub enum SftpCommand {
ListDir(String),
#[allow(dead_code)]
Preview(String),
Download { remote: String, local_dir: String },
UploadPaths { locals: Vec<String>, remote_dir: String },
Download {
remote: String,
local_dir: String,
},
UploadPaths {
locals: Vec<String>,
remote_dir: String,
},
Close,
}
@@ -75,16 +83,21 @@ impl SftpHandle {
let _ = self.commands.send(SftpCommand::ListDir(path));
}
#[allow(dead_code)]
pub fn preview(&self, path: String) {
let _ = self.commands.send(SftpCommand::Preview(path));
}
pub fn download(&self, remote: String, local_dir: String) {
let _ = self.commands.send(SftpCommand::Download { remote, local_dir });
let _ = self
.commands
.send(SftpCommand::Download { remote, local_dir });
}
pub fn upload_paths(&self, locals: Vec<String>, remote_dir: String) {
let _ = self.commands.send(SftpCommand::UploadPaths { locals, remote_dir });
let _ = self
.commands
.send(SftpCommand::UploadPaths { locals, remote_dir });
}
pub fn close(&self) {
@@ -137,7 +150,10 @@ async fn run_sftp(
.await
.context("sftp handshake")?;
let home = sftp.canonicalize(".").await.unwrap_or_else(|_| "/".to_string());
let home = sftp
.canonicalize(".")
.await
.unwrap_or_else(|_| "/".to_string());
emit_entries(&events, &tab_id, &sftp, &home).await?;
while let Some(command) = commands.recv().await {
@@ -218,7 +234,9 @@ async fn run_sftp(
}
}
let _ = handle.disconnect(Disconnect::ByApplication, "bye", "").await;
let _ = handle
.disconnect(Disconnect::ByApplication, "bye", "")
.await;
Ok(())
}
@@ -241,7 +259,9 @@ async fn emit_entries(
Ok(())
}
async fn connect_and_authenticate(session: &Session) -> Result<russh::client::Handle<SftpClientHandler>> {
async fn connect_and_authenticate(
session: &Session,
) -> Result<russh::client::Handle<SftpClientHandler>> {
let config = Arc::new(client::Config {
inactivity_timeout: Some(std::time::Duration::from_secs(600)),
..Default::default()
@@ -368,6 +388,7 @@ fn join_remote(parent: &str, child: &str) -> String {
}
}
#[allow(dead_code)]
fn strip_archive_suffix(name: &str) -> &str {
for suffix in [".tar.gz", ".tgz", ".zip", ".tar"] {
if let Some(stripped) = name.strip_suffix(suffix) {
@@ -437,7 +458,10 @@ async fn list_dir_impl(sftp: &SftpSession, path: &str) -> Result<Vec<RemoteEntry
}
async fn preview_impl(sftp: &SftpSession, path: &str) -> Result<PreviewData> {
let metadata = sftp.metadata(path).await.with_context(|| format!("metadata {path}"))?;
let metadata = sftp
.metadata(path)
.await
.with_context(|| format!("metadata {path}"))?;
let is_dir = metadata
.permissions
.map(|mode| (mode & 0o170_000) == 0o040_000)
@@ -458,9 +482,15 @@ async fn preview_impl(sftp: &SftpSession, path: &str) -> Result<PreviewData> {
});
}
let mut remote_file = sftp.open(path).await.with_context(|| format!("open remote {path}"))?;
let mut remote_file = sftp
.open(path)
.await
.with_context(|| format!("open remote {path}"))?;
let mut buffer = vec![0u8; 128 * 1024];
let read = remote_file.read(&mut buffer).await.context("read preview bytes")?;
let read = remote_file
.read(&mut buffer)
.await
.context("read preview bytes")?;
buffer.truncate(read);
let nul_ratio = if buffer.is_empty() {
@@ -496,7 +526,10 @@ async fn download_path_impl(
.await
.with_context(|| format!("create {}", local_dir.display()))?;
let metadata = sftp.metadata(remote).await.with_context(|| format!("metadata {remote}"))?;
let metadata = sftp
.metadata(remote)
.await
.with_context(|| format!("metadata {remote}"))?;
let is_dir = metadata
.permissions
.map(|mode| (mode & 0o170_000) == 0o040_000)
@@ -508,7 +541,8 @@ async fn download_path_impl(
base_name(remote),
Uuid::new_v4()
));
let extracted_to = download_remote_directory_archive(handle, sftp, remote, &local_archive).await?;
let extracted_to =
download_remote_directory_archive(handle, sftp, remote, &local_archive).await?;
return Ok(format!("downloaded folder to {}", extracted_to.display()));
}
@@ -517,7 +551,12 @@ async fn download_path_impl(
Ok(format!("downloaded file to {}", local_path.display()))
}
async fn download_dir_recursive(sftp: &SftpSession, remote_dir: &str, local_dir: &Path) -> Result<()> {
#[allow(dead_code)]
async fn download_dir_recursive(
sftp: &SftpSession,
remote_dir: &str,
local_dir: &Path,
) -> Result<()> {
tokio::fs::create_dir_all(local_dir)
.await
.with_context(|| format!("create {}", local_dir.display()))?;
@@ -540,7 +579,11 @@ async fn download_remote_directory_archive(
remote_dir: &str,
local_archive: &Path,
) -> Result<PathBuf> {
let remote_archive = format!("/tmp/ashell-{}-{}.tar.gz", base_name(remote_dir), Uuid::new_v4());
let remote_archive = format!(
"/tmp/ashell-{}-{}.tar.gz",
base_name(remote_dir),
Uuid::new_v4()
);
create_remote_archive(handle, remote_dir, &remote_archive).await?;
let local_extract_root = local_archive
.parent()
@@ -549,7 +592,11 @@ async fn download_remote_directory_archive(
let archive_download = async {
download_file_impl(sftp, &remote_archive, local_archive).await?;
extract_archive_to(local_archive, local_archive.parent().unwrap_or_else(|| Path::new("."))).await?;
extract_archive_to(
local_archive,
local_archive.parent().unwrap_or_else(|| Path::new(".")),
)
.await?;
tokio::fs::remove_file(local_archive)
.await
.with_context(|| format!("remove {}", local_archive.display()))?;
@@ -568,14 +615,20 @@ async fn download_remote_directory_archive(
}
async fn download_file_impl(sftp: &SftpSession, remote: &str, local: &Path) -> Result<()> {
let mut remote_file = sftp.open(remote).await.with_context(|| format!("open remote {remote}"))?;
let mut remote_file = sftp
.open(remote)
.await
.with_context(|| format!("open remote {remote}"))?;
let mut local_file = tokio::fs::File::create(local)
.await
.with_context(|| format!("create local {}", local.display()))?;
let mut buffer = vec![0u8; 64 * 1024];
loop {
let read = remote_file.read(&mut buffer).await.context("read remote file")?;
let read = remote_file
.read(&mut buffer)
.await
.context("read remote file")?;
if read == 0 {
break;
}
@@ -588,7 +641,11 @@ async fn download_file_impl(sftp: &SftpSession, remote: &str, local: &Path) -> R
Ok(())
}
async fn upload_paths_impl(sftp: &SftpSession, locals: &[String], remote_dir: &str) -> Result<String> {
async fn upload_paths_impl(
sftp: &SftpSession,
locals: &[String],
remote_dir: &str,
) -> Result<String> {
create_remote_dir_all(sftp, remote_dir).await?;
let mut file_count = 0usize;
let mut folder_count = 0usize;
@@ -612,7 +669,11 @@ async fn upload_paths_impl(sftp: &SftpSession, locals: &[String], remote_dir: &s
Ok(summary)
}
async fn upload_directory_impl(sftp: &SftpSession, local_dir: &Path, remote_parent: &str) -> Result<()> {
async fn upload_directory_impl(
sftp: &SftpSession,
local_dir: &Path,
remote_parent: &str,
) -> Result<()> {
let root_name = local_dir
.file_name()
.and_then(|name| name.to_str())
@@ -651,7 +712,11 @@ async fn upload_directory_impl(sftp: &SftpSession, local_dir: &Path, remote_pare
Ok(())
}
async fn upload_file_to_dir_impl(sftp: &SftpSession, local_file: &Path, remote_dir: &str) -> Result<()> {
async fn upload_file_to_dir_impl(
sftp: &SftpSession,
local_file: &Path,
remote_dir: &str,
) -> Result<()> {
let file_name = local_file
.file_name()
.and_then(|name| name.to_str())
@@ -787,8 +852,13 @@ fn shell_quote(value: &str) -> String {
format!("'{}'", value.replace('\'', "'\"'\"'"))
}
#[allow(dead_code)]
async fn maybe_extract_archive(path: &Path) -> Result<Option<PathBuf>> {
let Some(file_name) = path.file_name().and_then(|name| name.to_str()).map(|name| name.to_string()) else {
let Some(file_name) = path
.file_name()
.and_then(|name| name.to_str())
.map(|name| name.to_string())
else {
return Ok(None);
};
let is_archive = [".zip", ".tar", ".tar.gz", ".tgz"]
@@ -834,7 +904,9 @@ async fn maybe_extract_archive(path: &Path) -> Result<Option<PathBuf>> {
.with_context(|| format!("open {}", archive_path.display()))?;
let decoder = GzDecoder::new(file);
let mut archive = tar::Archive::new(decoder);
archive.unpack(&target_dir).context("unpack tar.gz archive")?;
archive
.unpack(&target_dir)
.context("unpack tar.gz archive")?;
} else if file_name.ends_with(".tar") {
let file = fs::File::open(&archive_path)
.with_context(|| format!("open {}", archive_path.display()))?;
@@ -851,7 +923,11 @@ async fn maybe_extract_archive(path: &Path) -> Result<Option<PathBuf>> {
}
async fn extract_archive_to(path: &Path, target_dir: &Path) -> Result<()> {
let Some(file_name) = path.file_name().and_then(|name| name.to_str()).map(|name| name.to_string()) else {
let Some(file_name) = path
.file_name()
.and_then(|name| name.to_str())
.map(|name| name.to_string())
else {
return Ok(());
};
let archive_path = path.to_path_buf();
@@ -886,7 +962,9 @@ async fn extract_archive_to(path: &Path, target_dir: &Path) -> Result<()> {
.with_context(|| format!("open {}", archive_path.display()))?;
let decoder = GzDecoder::new(file);
let mut archive = tar::Archive::new(decoder);
archive.unpack(&target_dir).context("unpack tar.gz archive")?;
archive
.unpack(&target_dir)
.context("unpack tar.gz archive")?;
} else if file_name.ends_with(".tar") {
let file = fs::File::open(&archive_path)
.with_context(|| format!("open {}", archive_path.display()))?;
+4 -4
View File
@@ -3,19 +3,19 @@ use std::{
sync::Arc,
};
use anyhow::{anyhow, Context, Result};
use anyhow::{Context, Result, anyhow};
use async_trait::async_trait;
use directories::BaseDirs;
use russh::{
client::{self, Handler},
keys::{decode_secret_key, key::PrivateKeyWithHashAlg, load_secret_key, HashAlg, PrivateKey},
ChannelMsg, Disconnect,
client::{self, Handler},
keys::{HashAlg, PrivateKey, decode_secret_key, key::PrivateKeyWithHashAlg, load_secret_key},
};
use tokio::sync::mpsc;
use crate::{
config::{AuthMethod, Session},
system::{remote_snapshot_from_kv, SystemSnapshot},
system::{SystemSnapshot, remote_snapshot_from_kv},
terminal::{BackendCommand, BackendEvent, BackendTx},
};
+11 -13
View File
@@ -1,6 +1,9 @@
use std::{collections::BTreeMap, time::{Duration, Instant}};
use std::{
collections::BTreeMap,
time::{Duration, Instant},
};
use anyhow::{anyhow, Result};
use anyhow::{Result, anyhow};
use sysinfo::{Disks, Networks, System};
#[derive(Debug, Clone, Default)]
@@ -69,7 +72,10 @@ impl SystemSampler {
let rx_total: u64 = self.nets.iter().map(|(_, d)| d.total_received()).sum();
let tx_total: u64 = self.nets.iter().map(|(_, d)| d.total_transmitted()).sum();
let now = Instant::now();
let elapsed = now.duration_since(self.last_instant).as_secs_f64().max(0.001);
let elapsed = now
.duration_since(self.last_instant)
.as_secs_f64()
.max(0.001);
let rx_rate = (rx_total.saturating_sub(self.last_rx_total) as f64 / elapsed) as u64;
let tx_rate = (tx_total.saturating_sub(self.last_tx_total) as f64 / elapsed) as u64;
self.last_rx_total = rx_total;
@@ -92,16 +98,8 @@ impl SystemSampler {
cpu_percent,
mem_percent: ratio(mem_used, mem_total),
swap_percent: ratio(swap_used, swap_total),
mem_detail: format!(
"{}/{}",
format_bytes(mem_used),
format_bytes(mem_total)
),
swap_detail: format!(
"{}/{}",
format_bytes(swap_used),
format_bytes(swap_total)
),
mem_detail: format!("{}/{}", format_bytes(mem_used), format_bytes(mem_total)),
swap_detail: format!("{}/{}", format_bytes(swap_used), format_bytes(swap_total)),
net_rx: format!("{}/s", format_bytes(rx_rate)),
net_tx: format!("{}/s", format_bytes(tx_rate)),
disks,
+109 -28
View File
@@ -5,10 +5,7 @@ use alacritty_terminal::{
grid::{Dimensions, Scroll},
index::{Column, Point, Side},
selection::{Selection, SelectionRange, SelectionType},
term::{
cell::Cell,
point_to_viewport, viewport_to_point, Config, Term, TermMode,
},
term::{Config, Term, TermMode, cell::Cell, point_to_viewport, viewport_to_point},
vte::ansi::{CursorShape, Processor},
};
use gpui::Keystroke;
@@ -32,16 +29,46 @@ pub enum BackendCommand {
#[derive(Debug, Clone)]
pub enum BackendEvent {
Output { tab_id: String, bytes: Vec<u8> },
Status { tab_id: String, text: String },
Connected { tab_id: String },
SftpEntries { tab_id: String, path: String, entries: Vec<RemoteEntry> },
SftpPreview { tab_id: String, preview: PreviewData },
SftpStatus { tab_id: String, text: String },
RemoteSystem { tab_id: String, snapshot: SystemSnapshot },
RemoteSystemUnavailable { tab_id: String, reason: String },
Closed { tab_id: String, reason: String },
TerminalTitleChanged { tab_id: String, title: String },
Output {
tab_id: String,
bytes: Vec<u8>,
},
Status {
tab_id: String,
text: String,
},
Connected {
tab_id: String,
},
SftpEntries {
tab_id: String,
path: String,
entries: Vec<RemoteEntry>,
},
SftpPreview {
tab_id: String,
preview: PreviewData,
},
SftpStatus {
tab_id: String,
text: String,
},
RemoteSystem {
tab_id: String,
snapshot: SystemSnapshot,
},
RemoteSystemUnavailable {
tab_id: String,
reason: String,
},
Closed {
tab_id: String,
reason: String,
},
TerminalTitleChanged {
tab_id: String,
title: String,
},
}
#[derive(Clone)]
@@ -123,16 +150,36 @@ pub struct SftpUiState {
}
impl TerminalTab {
pub fn new_local(id: String, title: String, backend: BackendTx, events: std::sync::mpsc::Sender<BackendEvent>) -> Self {
Self::new(id, title, TabKind::Local, "local shell".into(), backend, events)
pub fn new_local(
id: String,
title: String,
backend: BackendTx,
events: std::sync::mpsc::Sender<BackendEvent>,
) -> Self {
Self::new(
id,
title,
TabKind::Local,
"local shell".into(),
backend,
events,
)
}
pub fn new_ssh(id: String, session: &Session, backend: BackendTx, events: std::sync::mpsc::Sender<BackendEvent>) -> Self {
pub fn new_ssh(
id: String,
session: &Session,
backend: BackendTx,
events: std::sync::mpsc::Sender<BackendEvent>,
) -> Self {
let mut tab = Self::new(
id,
session.name.clone(),
TabKind::Ssh,
format!("connecting {}@{}:{}", session.user, session.host, session.port),
format!(
"connecting {}@{}:{}",
session.user, session.host, session.port
),
backend,
events,
);
@@ -149,7 +196,14 @@ impl TerminalTab {
tab
}
fn new(id: String, title: String, kind: TabKind, status: String, backend: BackendTx, events: std::sync::mpsc::Sender<BackendEvent>) -> Self {
fn new(
id: String,
title: String,
kind: TabKind,
status: String,
backend: BackendTx,
events: std::sync::mpsc::Sender<BackendEvent>,
) -> Self {
Self {
id: id.clone(),
title,
@@ -264,8 +318,11 @@ impl TerminalTab {
self.term.scroll_display(Scroll::Bottom);
}
#[allow(dead_code)]
pub fn has_selection(&self) -> bool {
self.term.selection_to_string().is_some_and(|text| !text.is_empty())
self.term
.selection_to_string()
.is_some_and(|text| !text.is_empty())
}
pub fn clear_selection(&mut self) {
@@ -273,7 +330,9 @@ impl TerminalTab {
}
pub fn selection_text(&self) -> Option<String> {
self.term.selection_to_string().filter(|text| !text.is_empty())
self.term
.selection_to_string()
.filter(|text| !text.is_empty())
}
pub fn begin_selection(
@@ -301,19 +360,25 @@ impl TerminalTab {
}
pub fn paste_text(&mut self, text: &str) {
let paste_text = text.replace('\x1b', "").replace("\r\n", "\r").replace('\n', "\r");
let paste_text = text
.replace('\x1b', "")
.replace("\r\n", "\r")
.replace('\n', "\r");
self.backend
.send(BackendCommand::Input(paste_text.into_bytes()));
}
}
fn viewport_selection_from_range(
display_offset: usize,
selection: &Option<SelectionRange>,
) -> Option<ViewportSelection> {
let SelectionRange { start, end, is_block } = selection.as_ref().copied()?;
let SelectionRange {
start,
end,
is_block,
} = selection.as_ref().copied()?;
let start = point_to_viewport(display_offset, start)?;
let end = point_to_viewport(display_offset, end)?;
@@ -336,7 +401,9 @@ struct TerminalListener {
impl EventListener for TerminalListener {
fn send_event(&self, event: Event) {
match event {
Event::PtyWrite(output) => self.backend.send(BackendCommand::Input(output.into_bytes())),
Event::PtyWrite(output) => self
.backend
.send(BackendCommand::Input(output.into_bytes())),
Event::TextAreaSizeRequest(format) => {
let size = alacritty_terminal::event::WindowSize {
num_lines: 30,
@@ -358,14 +425,24 @@ impl EventListener for TerminalListener {
}
}
fn new_term(cols: u16, rows: u16, backend: BackendTx, tab_id: String, events: std::sync::mpsc::Sender<BackendEvent>) -> Term<TerminalListener> {
fn new_term(
cols: u16,
rows: u16,
backend: BackendTx,
tab_id: String,
events: std::sync::mpsc::Sender<BackendEvent>,
) -> Term<TerminalListener> {
Term::new(
Config {
scrolling_history: 2000,
..Config::default()
},
&TerminalSize::new(cols, rows),
TerminalListener { tab_id, backend, events },
TerminalListener {
tab_id,
backend,
events,
},
)
}
@@ -397,7 +474,11 @@ impl Dimensions for TerminalSize {
}
}
pub fn encode_key(keystroke: &Keystroke, app_cursor_mode: bool, option_as_meta: bool) -> Option<Vec<u8>> {
pub fn encode_key(
keystroke: &Keystroke,
app_cursor_mode: bool,
option_as_meta: bool,
) -> Option<Vec<u8>> {
zed_like_to_esc_str(keystroke, app_cursor_mode, option_as_meta)
.map(|text| text.into_owned().into_bytes())
}
+56 -27
View File
@@ -3,9 +3,10 @@ use alacritty_terminal::{
vte::ansi::{Color as AnsiColor, CursorShape, NamedColor},
};
use gpui::{
App, Bounds, Element, ElementId, Entity, FocusHandle, Font, FontStyle, FontWeight, GlobalElementId, Hsla, InputHandler, IntoElement,
LayoutId, Pixels, Point, Rgba, StrikethroughStyle, TextRun, TextStyle, UTF16Selection, UnderlineStyle, Window, fill, point, px,
relative, rgb,
App, Bounds, Element, ElementId, Entity, FocusHandle, Font, FontStyle, FontWeight,
GlobalElementId, Hsla, InputHandler, IntoElement, LayoutId, Pixels, Point, Rgba,
StrikethroughStyle, TextRun, TextStyle, UTF16Selection, UnderlineStyle, Window, fill, point,
px, relative, rgb,
};
use gpui_component::ActiveTheme as _;
@@ -81,7 +82,13 @@ impl BatchedTextRun {
}
}
fn paint(&self, origin: Point<Pixels>, metrics: TerminalMetrics, window: &mut Window, cx: &mut App) {
fn paint(
&self,
origin: Point<Pixels>,
metrics: TerminalMetrics,
window: &mut Window,
cx: &mut App,
) {
let pos = point(
origin.x + metrics.cell_width * self.col as f32,
origin.y + metrics.line_height * self.row as f32,
@@ -146,10 +153,13 @@ impl InputHandler for TerminalInputHandler {
_window: &mut Window,
cx: &mut App,
) -> Option<UTF16Selection> {
self.view.read(cx).terminal_accepts_text_input().then_some(UTF16Selection {
range: 0..0,
reversed: false,
})
self.view
.read(cx)
.terminal_accepts_text_input()
.then_some(UTF16Selection {
range: 0..0,
reversed: false,
})
}
fn marked_text_range(
@@ -277,15 +287,21 @@ impl TerminalElement {
fg.a *= 0.7;
}
let underline = cell.flags.intersects(Flags::ALL_UNDERLINES).then(|| UnderlineStyle {
color: Some(fg),
thickness: px(1.0),
wavy: cell.flags.contains(Flags::UNDERCURL),
});
let strikethrough = cell.flags.contains(Flags::STRIKEOUT).then(|| StrikethroughStyle {
color: Some(fg),
thickness: px(1.0),
});
let underline = cell
.flags
.intersects(Flags::ALL_UNDERLINES)
.then(|| UnderlineStyle {
color: Some(fg),
thickness: px(1.0),
wavy: cell.flags.contains(Flags::UNDERCURL),
});
let strikethrough = cell
.flags
.contains(Flags::STRIKEOUT)
.then(|| StrikethroughStyle {
color: Some(fg),
thickness: px(1.0),
});
let weight = if cell.flags.intersects(Flags::BOLD | Flags::DIM_BOLD) {
FontWeight::BOLD
@@ -326,10 +342,9 @@ impl TerminalElement {
continue;
}
let selected = self
.snapshot
.selection
.is_some_and(|selection| selection_contains(selection, render_cell.row, render_cell.col));
let selected = self.snapshot.selection.is_some_and(|selection| {
selection_contains(selection, render_cell.row, render_cell.col)
});
let bg = color_to_hsla(cell.bg, false, cx);
if selected || !is_default_bg(cell.bg) {
rects.push(LayoutRect {
@@ -525,7 +540,11 @@ impl Element for TerminalElement {
}
if let Some(cursor) = prepaint.cursor {
if self.marked_text.as_ref().is_some_and(|text| !text.is_empty()) {
if self
.marked_text
.as_ref()
.is_some_and(|text| !text.is_empty())
{
return;
}
let x = prepaint.bounds.origin.x + prepaint.metrics.cell_width * cursor.col as f32;
@@ -534,7 +553,10 @@ impl Element for TerminalElement {
CursorShape::Hidden => {}
CursorShape::Beam => {
window.paint_quad(fill(
Bounds::new(point(x, y), gpui::size(px(2.), prepaint.metrics.line_height)),
Bounds::new(
point(x, y),
gpui::size(px(2.), prepaint.metrics.line_height),
),
cursor.color,
));
}
@@ -572,7 +594,10 @@ fn merge_rects(mut rects: Vec<LayoutRect>) -> Vec<LayoutRect> {
for rect in rects {
if let Some(last) = merged.last_mut() {
if last.row == rect.row && last.color == rect.color && last.col + last.cells as i32 == rect.col {
if last.row == rect.row
&& last.color == rect.color
&& last.col + last.cells as i32 == rect.col
{
last.cells += rect.cells;
continue;
}
@@ -601,7 +626,11 @@ fn selection_contains(selection: ViewportSelection, row: i32, col: i32) -> bool
}
fn is_blank(cell: &alacritty_terminal::term::cell::Cell) -> bool {
cell.c == ' ' && cell.zerowidth().is_none() && !cell.flags.intersects(Flags::ALL_UNDERLINES | Flags::STRIKEOUT)
cell.c == ' '
&& cell.zerowidth().is_none()
&& !cell
.flags
.intersects(Flags::ALL_UNDERLINES | Flags::STRIKEOUT)
}
fn is_default_bg(color: AnsiColor) -> bool {
@@ -623,8 +652,8 @@ fn color_to_hsla(color: AnsiColor, foreground: bool, cx: &App) -> Hsla {
fn ansi_index_color(index: u8, _cx: &App) -> Hsla {
const ANSI_16: [u32; 16] = [
0x1f2430, 0xff5c57, 0x5af78e, 0xf3f99d, 0x57c7ff, 0xff6ac1, 0x9aedfe, 0xf1f1f0,
0x686868, 0xff5c57, 0x5af78e, 0xf3f99d, 0x57c7ff, 0xff6ac1, 0x9aedfe, 0xffffff,
0x1f2430, 0xff5c57, 0x5af78e, 0xf3f99d, 0x57c7ff, 0xff6ac1, 0x9aedfe, 0xf1f1f0, 0x686868,
0xff5c57, 0x5af78e, 0xf3f99d, 0x57c7ff, 0xff6ac1, 0x9aedfe, 0xffffff,
];
if (index as usize) < ANSI_16.len() {