1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-22 04:56:12 +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]]
name = "mlua"
version = "0.5.4"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd448d3e7018f2ff38dd732a374045f5b037eb4ee477d9241d9bb8c209528c1c"
checksum = "871831bd7c53c48b85db47a03e5974915679a91ddd5f897dece6ce030e5191d2"
dependencies = [
"bstr 0.2.17",
"cc",
"futures-core",
"futures-task",
"futures-util",
"lazy_static",
"lua-src",
"luajit-src",
"num-traits",
"once_cell",
"pkg-config",
"rustc-hash",
]
[[package]]
@ -3430,6 +3431,12 @@ version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc_version"
version = "0.2.3"

View File

@ -25,7 +25,7 @@ lazy_static = "1.4"
libc = "0.2"
log = "0.4"
luahelper = { path = "../luahelper" }
mlua = {version="0.5", features=["vendored", "lua54", "async", "send"]}
mlua = {version="0.7", features=["vendored", "lua54", "async", "send"]}
# file change notification
notify = "4.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)
* [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)
* [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

View File

@ -1,7 +1,7 @@
# PaneInformation
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,
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
* `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]
bstr = "0.2"
log = "0.4"
mlua = "0.5"
mlua = "0.7"
serde = {version="1.0", features = ["rc", "derive"]}
serde_json = "1.0"
strsim = "0.10"

View File

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

View File

@ -320,6 +320,36 @@ impl Pane for LocalPane {
.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 {
if let Some(proc_list) = self.divine_process_list() {
log::trace!(

View File

@ -370,6 +370,9 @@ pub trait Pane: Downcast {
}
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<()> {
if text.len() <= PASTE_CHUNK_SIZE {

View File

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

View File

@ -38,6 +38,9 @@ impl UserData for PaneObject {
.get_current_working_dir()
.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| {
this.pane()?.send_paste(&text).map_err(luaerr)?;
Ok(())

View File

@ -26,8 +26,7 @@ use config::{
configuration, AudibleBell, ConfigHandle, DimensionContext, GradientOrientation, TermConfig,
WindowCloseConfirmation,
};
use luahelper::impl_lua_conversion;
use mlua::FromLua;
use mlua::{FromLua, UserData, UserDataFields};
use mux::domain::{DomainId, DomainState};
use mux::pane::{CloseReason, Pane, PaneId};
use mux::renderable::RenderableDimensions;
@ -35,7 +34,6 @@ use mux::tab::{PositionedPane, PositionedSplit, SplitDirection, Tab, TabId};
use mux::window::WindowId as MuxWindowId;
use mux::{Mux, MuxNotification};
use portable_pty::PtySize;
use serde::*;
use smol::channel::Sender;
use smol::Timer;
use std::cell::{RefCell, RefMut};
@ -157,17 +155,31 @@ pub struct PaneState {
}
/// Data used when synchronously formatting pane and window titles
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Debug, Clone)]
pub struct TabInformation {
pub tab_id: TabId,
pub tab_index: usize,
pub is_active: bool,
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
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Debug, Clone)]
pub struct PaneInformation {
pub pane_id: PaneId,
pub pane_index: usize,
@ -182,7 +194,47 @@ pub struct PaneInformation {
pub title: 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)]
pub struct TabState {