1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-22 13:16:39 +03:00

Add get_foreground_process_name, expose it and cwd in PaneInformation

Add `get_foreground_process_name` to both Pane and the lua wrapper.

Add `foreground_process_name` and `current_working_dir` fields to
`PaneInformation`.  In order for those to be dynamically fetched,
switch the lua conversion for `PaneInformation` to be a UserData
with field access methods.  It's a little more verbose but allows
us to lazily compute these two new fields.

refs: https://github.com/wez/wezterm/discussions/1421
refs: https://github.com/wez/wezterm/issues/915
refs: https://github.com/wez/wezterm/issues/876
This commit is contained in:
Wez Furlong 2021-12-23 18:44:12 -07:00
parent 38087755e1
commit 1d064e0913
12 changed files with 170 additions and 15 deletions

13
Cargo.lock generated
View File

@ -2247,20 +2247,21 @@ dependencies = [
[[package]] [[package]]
name = "mlua" name = "mlua"
version = "0.5.4" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd448d3e7018f2ff38dd732a374045f5b037eb4ee477d9241d9bb8c209528c1c" checksum = "871831bd7c53c48b85db47a03e5974915679a91ddd5f897dece6ce030e5191d2"
dependencies = [ dependencies = [
"bstr 0.2.17", "bstr 0.2.17",
"cc", "cc",
"futures-core", "futures-core",
"futures-task", "futures-task",
"futures-util", "futures-util",
"lazy_static",
"lua-src", "lua-src",
"luajit-src", "luajit-src",
"num-traits", "num-traits",
"once_cell",
"pkg-config", "pkg-config",
"rustc-hash",
] ]
[[package]] [[package]]
@ -3430,6 +3431,12 @@ version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]] [[package]]
name = "rustc_version" name = "rustc_version"
version = "0.2.3" version = "0.2.3"

View File

@ -25,7 +25,7 @@ lazy_static = "1.4"
libc = "0.2" libc = "0.2"
log = "0.4" log = "0.4"
luahelper = { path = "../luahelper" } luahelper = { path = "../luahelper" }
mlua = {version="0.5", features=["vendored", "lua54", "async", "send"]} mlua = {version="0.7", features=["vendored", "lua54", "async", "send"]}
# file change notification # file change notification
notify = "4.0" notify = "4.0"
open = "2.0" open = "2.0"

View File

@ -22,6 +22,7 @@ As features stabilize some brief notes about them will accumulate here.
* [ActivateTabRelativeNoWrap](config/lua/keyassignment/ActivateTabRelativeNoWrap.md) key assignment [#1414](https://github.com/wez/wezterm/issues/1414) * [ActivateTabRelativeNoWrap](config/lua/keyassignment/ActivateTabRelativeNoWrap.md) key assignment [#1414](https://github.com/wez/wezterm/issues/1414)
* [QuickSelectArgs](config/lua/keyassignment/QuickSelectArgs.md) key assignment [#846](https://github.com/wez/wezterm/issues/846) [#1362](https://github.com/wez/wezterm/issues/1362) * [QuickSelectArgs](config/lua/keyassignment/QuickSelectArgs.md) key assignment [#846](https://github.com/wez/wezterm/issues/846) [#1362](https://github.com/wez/wezterm/issues/1362)
* [wezterm.open_wth](config/lua/wezterm/open_with.md) function for opening URLs/documents with the default or a specific application [#1362](https://github.com/wez/wezterm/issues/1362) * [wezterm.open_wth](config/lua/wezterm/open_with.md) function for opening URLs/documents with the default or a specific application [#1362](https://github.com/wez/wezterm/issues/1362)
* [pane:get_foreground_process_name()](config/lua/pane/get_foreground_process_name.md) method, [PaneInformation](config/lua/PaneInformation.md) now has `foreground_process_name` and `current_working_dir` fields. [#1421](https://github.com/wez/wezterm/discussions/1421) [#915](https://github.com/wez/wezterm/issues/915) [#876](https://github.com/wez/wezterm/issues/876)
#### Changed #### Changed

View File

@ -1,7 +1,7 @@
# PaneInformation # PaneInformation
The `PaneInformation` struct describes a pane. Unlike [the Pane The `PaneInformation` struct describes a pane. Unlike [the Pane
object](pane/index.md), `PaneInformation` is purely a snapshot of some of object](pane/index.md), `PaneInformation` is a snapshot of some of
the key characteristics of the pane, intended for use in synchronous, fast, the key characteristics of the pane, intended for use in synchronous, fast,
event callbacks that format GUI elements such as the window and tab title bars. event callbacks that format GUI elements such as the window and tab title bars.
@ -20,3 +20,34 @@ The `PaneInformation` struct contains the following fields:
* `title` - the title of the pane, per [pane:get_title()](pane/get_title.md) at the time the pane information was captured * `title` - the title of the pane, per [pane:get_title()](pane/get_title.md) at the time the pane information was captured
* `user_vars` - the user variables defined for the pane, per [pane:get_user_vars()](pane/get_user_vars.md) at the time the pane information was captured. * `user_vars` - the user variables defined for the pane, per [pane:get_user_vars()](pane/get_user_vars.md) at the time the pane information was captured.
*Since: nightly builds only*
Additional fields are available; note that accessing these may not be cheap to
compute and may slow down wezterm. Unlike the fields listed above, these are
not pre-computed snapshots of information, so if you don't use them, you won't
pay the cost of computing them.
* `foreground_process_name` - the path to the executable image per [pane:get_foreground_process_name()](pane/get_foreground_process_name.md), or an empty string if unavailable.
* `current_working_dir` - the current working directory, per [pane:get_current_working_dir()](pane/get_current_working_dir.md).
This example places the executable name in the tab titles:
```lua
local wezterm = require 'wezterm'
wezterm.on("format-tab-title", function(tab, tabs, panes, config, hover, max_width)
local pane = tab.active_pane
local title = pane.foreground_process_name .. " " .. pane.pane_id
local color = "navy"
if tab.is_active then
color = "blue"
end
return {
{Background={Color=color}},
{Text=" " .. title .. " "},
}
end)
return {
}
```

View File

@ -0,0 +1,28 @@
# `pane:get_foreground_process_name()`
*Since: nightly builds only*
Returns the path to the executable image for the pane.
This method has some restrictions and caveats:
* This information is only available for local panes. Multiplexer panes do not report this information. Similarly, if you are using eg: `ssh` to connect to a remote host, you won't be able to access the name of the remote process that is running.
* On unix systems, the *process group leader* (the foreground process) will be queried, but that concept doesn't exist on Windows, so instead, the program that was used to launch the pane will be used
* On Linux, macOS and Windows, the process can be queried to determine this path. Other operating systems (notably, FreeBSD and other unix systems) are not currently supported
* Querying the path may fail for a variety of reasons outside of the control of WezTerm
* Querying process information has some runtime overhead, which may cause wezterm to slow down if over-used.
If the path is not known then this method returns `nil`.
This example sets the right status are to the executable path:
```lua
local wezterm = require 'wezterm'
wezterm.on("update-right-status", function(window, pane)
window:set_right_status(pane:get_foreground_process_name())
end)
return {
}
```

View File

@ -9,7 +9,7 @@ edition = "2018"
[dependencies] [dependencies]
bstr = "0.2" bstr = "0.2"
log = "0.4" log = "0.4"
mlua = "0.5" mlua = "0.7"
serde = {version="1.0", features = ["rc", "derive"]} serde = {version="1.0", features = ["rc", "derive"]}
serde_json = "1.0" serde_json = "1.0"
strsim = "0.10" strsim = "0.10"

View File

@ -20,7 +20,7 @@ libc = "0.2"
log = "0.4" log = "0.4"
luahelper = { path = "../luahelper" } luahelper = { path = "../luahelper" }
metrics = { version="0.17", features=["std"]} metrics = { version="0.17", features=["std"]}
mlua = "0.5" mlua = "0.7"
portable-pty = { path = "../pty", features = ["serde_support"]} portable-pty = { path = "../pty", features = ["serde_support"]}
promise = { path = "../promise" } promise = { path = "../promise" }
rangeset = { path = "../rangeset" } rangeset = { path = "../rangeset" }

View File

@ -320,6 +320,36 @@ impl Pane for LocalPane {
.or_else(|| self.divine_current_working_dir()) .or_else(|| self.divine_current_working_dir())
} }
#[cfg(not(any(windows, target_os = "linux", target_os = "macos")))]
fn get_foreground_process_name(&self) -> Option<String> {
None
}
#[cfg(any(windows, target_os = "linux", target_os = "macos"))]
fn get_foreground_process_name(&self) -> Option<String> {
use sysinfo::{Pid, ProcessExt, ProcessRefreshKind, RefreshKind, System, SystemExt};
let leader;
#[cfg(unix)]
{
leader = self.pty.borrow().process_group_leader()?;
}
#[cfg(windows)]
{
if let ProcessState::Running { pid: Some(pid), .. } = &*self.process.borrow() {
leader = *pid;
} else {
return None;
}
}
let system = System::new_with_specifics(
RefreshKind::new().with_processes(ProcessRefreshKind::new()),
);
let proc = system.process(leader as Pid)?;
Some(proc.exe().to_string_lossy().to_string())
}
fn can_close_without_prompting(&self, _reason: CloseReason) -> bool { fn can_close_without_prompting(&self, _reason: CloseReason) -> bool {
if let Some(proc_list) = self.divine_process_list() { if let Some(proc_list) = self.divine_process_list() {
log::trace!( log::trace!(

View File

@ -370,6 +370,9 @@ pub trait Pane: Downcast {
} }
fn get_current_working_dir(&self) -> Option<Url>; fn get_current_working_dir(&self) -> Option<Url>;
fn get_foreground_process_name(&self) -> Option<String> {
None
}
fn trickle_paste(&self, text: String) -> anyhow::Result<()> { fn trickle_paste(&self, text: String) -> anyhow::Result<()> {
if text.len() <= PASTE_CHUNK_SIZE { if text.len() <= PASTE_CHUNK_SIZE {

View File

@ -42,7 +42,7 @@ log = "0.4"
lru = "0.7" lru = "0.7"
luahelper = { path = "../luahelper" } luahelper = { path = "../luahelper" }
metrics = { version="0.17", features=["std"]} metrics = { version="0.17", features=["std"]}
mlua = "0.5" mlua = "0.7"
mux = { path = "../mux" } mux = { path = "../mux" }
open = "2.0" open = "2.0"
ordered-float = "2.8" ordered-float = "2.8"

View File

@ -38,6 +38,9 @@ impl UserData for PaneObject {
.get_current_working_dir() .get_current_working_dir()
.map(|u| u.to_string())) .map(|u| u.to_string()))
}); });
methods.add_method("get_foreground_process_name", |_, this, _: ()| {
Ok(this.pane()?.get_foreground_process_name())
});
methods.add_method("paste", |_, this, text: String| { methods.add_method("paste", |_, this, text: String| {
this.pane()?.send_paste(&text).map_err(luaerr)?; this.pane()?.send_paste(&text).map_err(luaerr)?;
Ok(()) Ok(())

View File

@ -26,8 +26,7 @@ use config::{
configuration, AudibleBell, ConfigHandle, DimensionContext, GradientOrientation, TermConfig, configuration, AudibleBell, ConfigHandle, DimensionContext, GradientOrientation, TermConfig,
WindowCloseConfirmation, WindowCloseConfirmation,
}; };
use luahelper::impl_lua_conversion; use mlua::{FromLua, UserData, UserDataFields};
use mlua::FromLua;
use mux::domain::{DomainId, DomainState}; use mux::domain::{DomainId, DomainState};
use mux::pane::{CloseReason, Pane, PaneId}; use mux::pane::{CloseReason, Pane, PaneId};
use mux::renderable::RenderableDimensions; use mux::renderable::RenderableDimensions;
@ -35,7 +34,6 @@ use mux::tab::{PositionedPane, PositionedSplit, SplitDirection, Tab, TabId};
use mux::window::WindowId as MuxWindowId; use mux::window::WindowId as MuxWindowId;
use mux::{Mux, MuxNotification}; use mux::{Mux, MuxNotification};
use portable_pty::PtySize; use portable_pty::PtySize;
use serde::*;
use smol::channel::Sender; use smol::channel::Sender;
use smol::Timer; use smol::Timer;
use std::cell::{RefCell, RefMut}; use std::cell::{RefCell, RefMut};
@ -157,17 +155,31 @@ pub struct PaneState {
} }
/// Data used when synchronously formatting pane and window titles /// Data used when synchronously formatting pane and window titles
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Debug, Clone)]
pub struct TabInformation { pub struct TabInformation {
pub tab_id: TabId, pub tab_id: TabId,
pub tab_index: usize, pub tab_index: usize,
pub is_active: bool, pub is_active: bool,
pub active_pane: Option<PaneInformation>, pub active_pane: Option<PaneInformation>,
} }
impl_lua_conversion!(TabInformation);
impl UserData for TabInformation {
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
fields.add_field_method_get("tab_id", |_, this| Ok(this.tab_id));
fields.add_field_method_get("tab_index", |_, this| Ok(this.tab_index));
fields.add_field_method_get("is_active", |_, this| Ok(this.is_active));
fields.add_field_method_get("active_pane", |_, this| {
if let Some(pane) = &this.active_pane {
Ok(Some(pane.clone()))
} else {
Ok(None)
}
});
}
}
/// Data used when synchronously formatting pane and window titles /// Data used when synchronously formatting pane and window titles
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Debug, Clone)]
pub struct PaneInformation { pub struct PaneInformation {
pub pane_id: PaneId, pub pane_id: PaneId,
pub pane_index: usize, pub pane_index: usize,
@ -182,7 +194,47 @@ pub struct PaneInformation {
pub title: String, pub title: String,
pub user_vars: HashMap<String, String>, pub user_vars: HashMap<String, String>,
} }
impl_lua_conversion!(PaneInformation);
impl UserData for PaneInformation {
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
fields.add_field_method_get("pane_id", |_, this| Ok(this.pane_id));
fields.add_field_method_get("pane_index", |_, this| Ok(this.pane_index));
fields.add_field_method_get("is_active", |_, this| Ok(this.is_active));
fields.add_field_method_get("is_zoomed", |_, this| Ok(this.is_zoomed));
fields.add_field_method_get("left", |_, this| Ok(this.left));
fields.add_field_method_get("top", |_, this| Ok(this.top));
fields.add_field_method_get("width", |_, this| Ok(this.width));
fields.add_field_method_get("height", |_, this| Ok(this.height));
fields.add_field_method_get("pixel_width", |_, this| Ok(this.pixel_width));
fields.add_field_method_get("pixel_height", |_, this| Ok(this.pixel_width));
fields.add_field_method_get("title", |_, this| Ok(this.title.clone()));
fields.add_field_method_get("user_vars", |_, this| Ok(this.user_vars.clone()));
fields.add_field_method_get("foreground_process_name", |_, this| {
let mut name = None;
if let Some(mux) = Mux::get() {
if let Some(pane) = mux.get_pane(this.pane_id) {
name = pane.get_foreground_process_name();
}
}
match name {
Some(name) => Ok(name),
None => Ok("".to_string()),
}
});
fields.add_field_method_get("current_working_dir", |_, this| {
let mut name = None;
if let Some(mux) = Mux::get() {
if let Some(pane) = mux.get_pane(this.pane_id) {
name = pane.get_current_working_dir().map(|u| u.to_string());
}
}
match name {
Some(name) => Ok(name),
None => Ok("".to_string()),
}
});
}
}
#[derive(Default, Clone)] #[derive(Default, Clone)]
pub struct TabState { pub struct TabState {