1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-23 13:21:38 +03:00

mux: allow specifying cache policy for process information

This allows individual call sites to be able to force an immediate
resolution of the process information.

This should help to address https://github.com/wez/wezterm/issues/4811
wherein the problem seems to be that the cwd used for a new spawn
is taken from a stale read.
This commit is contained in:
Wez Furlong 2024-02-02 17:41:41 -07:00
parent a8223a756c
commit aa81a46d58
No known key found for this signature in database
GPG Key ID: 7A7F66A31EC9B387
12 changed files with 71 additions and 48 deletions

View File

@ -42,6 +42,8 @@ As features stabilize some brief notes about them will accumulate here.
* Tab bar wouldn't immediately reflect the result of calling `tab:set_title`.
#4941
* Command Palette: Missing space between keycaps on macOS. #4885
* macOS: stale/invalid cwd used when spawning new panes when shell integration
is NOT in use. #4811
### 20240128-202157-1e552d76

View File

@ -1,6 +1,7 @@
use super::*;
use luahelper::{dynamic_to_lua_value, from_lua, to_lua};
use mlua::Value;
use mux::pane::CachePolicy;
use std::cmp::Ordering;
use std::sync::Arc;
use termwiz::cell::SemanticType;
@ -147,7 +148,9 @@ impl UserData for MuxPane {
methods.add_method("get_current_working_dir", |_, this, _: ()| {
let mux = get_mux()?;
let pane = this.resolve(&mux)?;
Ok(pane.get_current_working_dir().map(|url| Url { url }))
Ok(pane
.get_current_working_dir(CachePolicy::FetchImmediate)
.map(|url| Url { url }))
});
methods.add_method("get_metadata", |lua, this, _: ()| {
@ -160,13 +163,13 @@ impl UserData for MuxPane {
methods.add_method("get_foreground_process_name", |_, this, _: ()| {
let mux = get_mux()?;
let pane = this.resolve(&mux)?;
Ok(pane.get_foreground_process_name())
Ok(pane.get_foreground_process_name(CachePolicy::FetchImmediate))
});
methods.add_method("get_foreground_process_info", |_, this, _: ()| {
let mux = get_mux()?;
let pane = this.resolve(&mux)?;
Ok(pane.get_foreground_process_info())
Ok(pane.get_foreground_process_info(CachePolicy::AllowStale))
});
methods.add_method("get_cursor_position", |_, this, _: ()| {

View File

@ -1,5 +1,5 @@
use crate::client::{ClientId, ClientInfo};
use crate::pane::{Pane, PaneId};
use crate::pane::{CachePolicy, Pane, PaneId};
use crate::tab::{SplitRequest, Tab, TabId};
use crate::window::{Window, WindowId};
use anyhow::{anyhow, Context, Error};
@ -1131,11 +1131,12 @@ impl Mux {
command_dir: Option<String>,
pane: Option<Arc<dyn Pane>>,
target_domain: DomainId,
policy: CachePolicy,
) -> Option<String> {
command_dir.or_else(|| {
match pane {
Some(pane) if pane.domain_id() == target_domain => pane
.get_current_working_dir()
.get_current_working_dir(policy)
.and_then(|url| {
percent_decode_str(url.path())
.decode_utf8()
@ -1193,6 +1194,7 @@ impl Mux {
command_dir,
Some(Arc::clone(&current_pane)),
domain.domain_id(),
CachePolicy::FetchImmediate,
),
},
other => other,
@ -1338,6 +1340,7 @@ impl Mux {
None => None,
},
domain.domain_id(),
CachePolicy::FetchImmediate,
);
let tab = domain

View File

@ -1,7 +1,7 @@
use crate::domain::DomainId;
use crate::pane::{
CloseReason, ForEachPaneLogicalLine, LogicalLine, Pane, PaneId, Pattern, SearchResult,
WithPaneLines,
CachePolicy, CloseReason, ForEachPaneLogicalLine, LogicalLine, Pane, PaneId, Pattern,
SearchResult, WithPaneLines,
};
use crate::renderable::*;
use crate::tmux::{TmuxDomain, TmuxDomainState};
@ -447,7 +447,7 @@ impl Pane for LocalPane {
// If the title is the default pane title, then try to spice
// things up a bit by returning the process basename instead
if title == "wezterm" {
if let Some(proc_name) = self.get_foreground_process_name() {
if let Some(proc_name) = self.get_foreground_process_name(CachePolicy::AllowStale) {
let proc_name = std::path::Path::new(&proc_name);
if let Some(name) = proc_name.file_name() {
return name.to_string_lossy().to_string();
@ -501,12 +501,12 @@ impl Pane for LocalPane {
}
}
fn get_current_working_dir(&self) -> Option<Url> {
fn get_current_working_dir(&self, policy: CachePolicy) -> Option<Url> {
self.terminal
.lock()
.get_current_dir()
.cloned()
.or_else(|| self.divine_current_working_dir())
.or_else(|| self.divine_current_working_dir(policy))
}
fn tty_name(&self) -> Option<String> {
@ -522,19 +522,19 @@ impl Pane for LocalPane {
}
}
fn get_foreground_process_info(&self) -> Option<LocalProcessInfo> {
fn get_foreground_process_info(&self, policy: CachePolicy) -> Option<LocalProcessInfo> {
#[cfg(unix)]
if let Some(pid) = self.pty.lock().process_group_leader() {
return LocalProcessInfo::with_root_pid(pid as u32);
}
self.divine_foreground_process()
self.divine_foreground_process(policy)
}
fn get_foreground_process_name(&self) -> Option<String> {
fn get_foreground_process_name(&self, policy: CachePolicy) -> Option<String> {
#[cfg(unix)]
{
let leader = self.get_leader();
let leader = self.get_leader(policy);
if let Some(path) = &leader.path {
return Some(path.to_string_lossy().to_string());
}
@ -542,7 +542,7 @@ impl Pane for LocalPane {
}
#[cfg(windows)]
if let Some(fg) = self.divine_foreground_process() {
if let Some(fg) = self.divine_foreground_process(policy) {
return Some(fg.executable.to_string_lossy().to_string());
}
@ -551,7 +551,7 @@ impl Pane for LocalPane {
}
fn can_close_without_prompting(&self, _reason: CloseReason) -> bool {
if let Some(info) = self.divine_process_list(true) {
if let Some(info) = self.divine_process_list(CachePolicy::FetchImmediate) {
log::trace!(
"can_close_without_prompting? procs in pane {:#?}",
info.root
@ -1015,10 +1015,12 @@ impl LocalPane {
}
#[cfg(unix)]
fn get_leader(&self) -> CachedLeaderInfo {
fn get_leader(&self, policy: CachePolicy) -> CachedLeaderInfo {
let mut leader = self.leader.lock();
if let Some(info) = leader.as_mut() {
if policy == CachePolicy::FetchImmediate {
leader.replace(CachedLeaderInfo::new(self.pty.lock().as_raw_fd()));
} else if let Some(info) = leader.as_mut() {
// If stale, queue up some work in another thread to update.
// Right now, we'll return the stale data.
if info.expired() && info.can_update() {
@ -1038,10 +1040,10 @@ impl LocalPane {
(*leader).clone().unwrap()
}
fn divine_current_working_dir(&self) -> Option<Url> {
fn divine_current_working_dir(&self, policy: CachePolicy) -> Option<Url> {
#[cfg(unix)]
{
let leader = self.get_leader();
let leader = self.get_leader(policy);
if let Some(path) = &leader.current_working_dir {
return Url::parse(&format!("file://localhost{}", path.display())).ok();
}
@ -1049,7 +1051,7 @@ impl LocalPane {
}
#[cfg(windows)]
if let Some(fg) = self.divine_foreground_process() {
if let Some(fg) = self.divine_foreground_process(policy) {
// Since windows paths typically start with something like C:\,
// we cannot simply stick `localhost` on the front; we have to
// omit the hostname otherwise the url parser is unhappy.
@ -1060,11 +1062,11 @@ impl LocalPane {
None
}
fn divine_process_list(&self, force_refresh: bool) -> Option<MappedMutexGuard<CachedProcInfo>> {
fn divine_process_list(&self, policy: CachePolicy) -> Option<MappedMutexGuard<CachedProcInfo>> {
if let ProcessState::Running { pid: Some(pid), .. } = &*self.process.lock() {
let mut proc_list = self.proc_list.lock();
let expired = force_refresh
let expired = policy == CachePolicy::FetchImmediate
|| proc_list
.as_ref()
.map(|info| info.updated.elapsed() > PROC_INFO_CACHE_TTL)
@ -1115,8 +1117,8 @@ impl LocalPane {
}
#[allow(dead_code)]
fn divine_foreground_process(&self) -> Option<LocalProcessInfo> {
if let Some(info) = self.divine_process_list(false) {
fn divine_foreground_process(&self, policy: CachePolicy) -> Option<LocalProcessInfo> {
if let Some(info) = self.divine_process_list(policy) {
Some(info.foreground.clone())
} else {
None

View File

@ -298,11 +298,14 @@ pub trait Pane: Downcast + Send + Sync {
None
}
fn get_current_working_dir(&self) -> Option<Url>;
fn get_foreground_process_name(&self) -> Option<String> {
fn get_current_working_dir(&self, policy: CachePolicy) -> Option<Url>;
fn get_foreground_process_name(&self, _policy: CachePolicy) -> Option<String> {
None
}
fn get_foreground_process_info(&self) -> Option<procinfo::LocalProcessInfo> {
fn get_foreground_process_info(
&self,
_policy: CachePolicy,
) -> Option<procinfo::LocalProcessInfo> {
None
}
@ -316,6 +319,12 @@ pub trait Pane: Downcast + Send + Sync {
}
impl_downcast!(Pane);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CachePolicy {
FetchImmediate,
AllowStale,
}
/// This trait is used to implement/provide a callback that is used together
/// with the Pane::with_lines_mut method.
/// Ideally we'd simply pass an FnMut with the same signature as the trait
@ -624,7 +633,7 @@ mod test {
fn is_alt_screen_active(&self) -> bool {
false
}
fn get_current_working_dir(&self) -> Option<Url> {
fn get_current_working_dir(&self, _policy: CachePolicy) -> Option<Url> {
None
}
fn key_down(&self, _: KeyCode, _: KeyModifiers) -> anyhow::Result<()> {

View File

@ -254,7 +254,7 @@ fn pane_tree(
}
Tree::Leaf(pane) => {
let dims = pane.get_dimensions();
let working_dir = pane.get_current_working_dir();
let working_dir = pane.get_current_working_dir(CachePolicy::AllowStale);
let cursor_pos = pane.get_cursor_position();
PaneNode::Leaf(PaneEntry {
@ -2299,7 +2299,7 @@ mod test {
fn is_alt_screen_active(&self) -> bool {
false
}
fn get_current_working_dir(&self) -> Option<Url> {
fn get_current_working_dir(&self, _policy: CachePolicy) -> Option<Url> {
None
}
}

View File

@ -5,7 +5,8 @@
use crate::domain::{alloc_domain_id, Domain, DomainId, DomainState};
use crate::pane::{
alloc_pane_id, CloseReason, ForEachPaneLogicalLine, LogicalLine, Pane, PaneId, WithPaneLines,
alloc_pane_id, CachePolicy, CloseReason, ForEachPaneLogicalLine, LogicalLine, Pane, PaneId,
WithPaneLines,
};
use crate::renderable::*;
use crate::tab::Tab;
@ -289,7 +290,7 @@ impl Pane for TermWizTerminalPane {
self.terminal.lock().is_alt_screen_active()
}
fn get_current_working_dir(&self) -> Option<Url> {
fn get_current_working_dir(&self, _policy: CachePolicy) -> Option<Url> {
self.terminal.lock().get_current_dir().cloned()
}

View File

@ -8,8 +8,8 @@ use config::configuration;
use config::keyassignment::ScrollbackEraseMode;
use mux::domain::DomainId;
use mux::pane::{
alloc_pane_id, CloseReason, ForEachPaneLogicalLine, LogicalLine, Pane, PaneId, Pattern,
SearchResult, WithPaneLines,
alloc_pane_id, CachePolicy, CloseReason, ForEachPaneLogicalLine, LogicalLine, Pane, PaneId,
Pattern, SearchResult, WithPaneLines,
};
use mux::renderable::{RenderableDimensions, StableCursorPosition};
use mux::tab::TabId;
@ -537,7 +537,7 @@ impl Pane for ClientPane {
false
}
fn get_current_working_dir(&self) -> Option<Url> {
fn get_current_working_dir(&self, _policy: CachePolicy) -> Option<Url> {
self.renderable.lock().inner.borrow().working_dir.clone()
}

View File

@ -7,8 +7,8 @@ use config::keyassignment::{
};
use mux::domain::DomainId;
use mux::pane::{
ForEachPaneLogicalLine, LogicalLine, Pane, PaneId, Pattern, PerformAssignmentResult,
SearchResult, WithPaneLines,
CachePolicy, ForEachPaneLogicalLine, LogicalLine, Pane, PaneId, Pattern,
PerformAssignmentResult, SearchResult, WithPaneLines,
};
use mux::renderable::*;
use mux::tab::TabId;
@ -1251,8 +1251,8 @@ impl Pane for CopyOverlay {
self.delegate.set_clipboard(clipboard)
}
fn get_current_working_dir(&self) -> Option<Url> {
self.delegate.get_current_working_dir()
fn get_current_working_dir(&self, policy: CachePolicy) -> Option<Url> {
self.delegate.get_current_working_dir(policy)
}
fn get_cursor_position(&self) -> StableCursorPosition {

View File

@ -4,7 +4,8 @@ use config::keyassignment::{ClipboardCopyDestination, QuickSelectArguments, Scro
use config::ConfigHandle;
use mux::domain::DomainId;
use mux::pane::{
ForEachPaneLogicalLine, LogicalLine, Pane, PaneId, Pattern, SearchResult, WithPaneLines,
CachePolicy, ForEachPaneLogicalLine, LogicalLine, Pane, PaneId, Pattern, SearchResult,
WithPaneLines,
};
use mux::renderable::*;
use parking_lot::{MappedMutexGuard, Mutex};
@ -472,8 +473,8 @@ impl Pane for QuickSelectOverlay {
self.delegate.set_clipboard(clipboard)
}
fn get_current_working_dir(&self) -> Option<Url> {
self.delegate.get_current_working_dir()
fn get_current_working_dir(&self, policy: CachePolicy) -> Option<Url> {
self.delegate.get_current_working_dir(policy)
}
fn get_cursor_position(&self) -> StableCursorPosition {

View File

@ -40,7 +40,9 @@ use config::{
};
use lfucache::*;
use mlua::{FromLua, UserData, UserDataFields};
use mux::pane::{CloseReason, Pane, PaneId, Pattern as MuxPattern, PerformAssignmentResult};
use mux::pane::{
CachePolicy, CloseReason, Pane, PaneId, Pattern as MuxPattern, PerformAssignmentResult,
};
use mux::renderable::RenderableDimensions;
use mux::tab::{
PositionedPane, PositionedSplit, SplitDirection, SplitRequest, SplitSize as MuxSplitSize, Tab,
@ -284,7 +286,7 @@ impl UserData for PaneInformation {
let mut name = None;
if let Some(mux) = Mux::try_get() {
if let Some(pane) = mux.get_pane(this.pane_id) {
name = pane.get_foreground_process_name();
name = pane.get_foreground_process_name(CachePolicy::AllowStale);
}
}
match name {
@ -305,7 +307,7 @@ impl UserData for PaneInformation {
if let Some(mux) = Mux::try_get() {
if let Some(pane) = mux.get_pane(this.pane_id) {
return Ok(pane
.get_current_working_dir()
.get_current_working_dir(CachePolicy::AllowStale)
.map(|url| url_funcs::Url { url }));
}
}

View File

@ -4,7 +4,7 @@ use codec::*;
use config::TermConfig;
use mux::client::ClientId;
use mux::domain::SplitSource;
use mux::pane::{Pane, PaneId};
use mux::pane::{CachePolicy, Pane, PaneId};
use mux::renderable::{RenderableDimensions, StableCursorPosition};
use mux::tab::TabId;
use mux::{Mux, MuxNotification};
@ -75,7 +75,7 @@ impl PerPane {
changed = true;
}
let working_dir = pane.get_current_working_dir();
let working_dir = pane.get_current_working_dir(CachePolicy::AllowStale);
if working_dir != self.working_dir {
changed = true;
}