Merge pull request #1598 from zed-industries/terminal-process-info

Mikayla Maki created

Terminal Active Process Title

Change summary

Cargo.lock                                     | 21 +++++++
crates/terminal/Cargo.toml                     |  3 
crates/terminal/src/terminal.rs                | 59 ++++++++++++-------
crates/terminal/src/terminal_container_view.rs | 20 +++++-
4 files changed, 77 insertions(+), 26 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -3251,6 +3251,15 @@ dependencies = [
  "minimal-lexical",
 ]
 
+[[package]]
+name = "ntapi"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f"
+dependencies = [
+ "winapi 0.3.9",
+]
+
 [[package]]
 name = "num-bigint"
 version = "0.4.3"
@@ -3812,6 +3821,17 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "procinfo"
+version = "0.1.0"
+source = "git+https://github.com/zed-industries/wezterm?rev=40a7dbf93542fbe4178c2e4b4bd438126a6432b9#40a7dbf93542fbe4178c2e4b4bd438126a6432b9"
+dependencies = [
+ "libc",
+ "log",
+ "ntapi",
+ "winapi 0.3.9",
+]
+
 [[package]]
 name = "project"
 version = "0.1.0"
@@ -5358,6 +5378,7 @@ dependencies = [
  "libc",
  "mio-extras",
  "ordered-float",
+ "procinfo",
  "project",
  "settings",
  "shellexpand",

crates/terminal/Cargo.toml 🔗

@@ -8,7 +8,8 @@ path = "src/terminal.rs"
 doctest = false
 
 [dependencies]
-alacritty_terminal = { git = "https://github.com/zed-industries/alacritty", rev = "4e1f0c6177975a040b37f942dfb0e723e46a9971"}
+alacritty_terminal = { git = "https://github.com/zed-industries/alacritty", rev = "4e1f0c6177975a040b37f942dfb0e723e46a9971" }
+procinfo = { git = "https://github.com/zed-industries/wezterm", rev = "40a7dbf93542fbe4178c2e4b4bd438126a6432b9", default-features = false }
 editor = { path = "../editor" }
 util = { path = "../util" }
 gpui = { path = "../gpui" }

crates/terminal/src/terminal.rs 🔗

@@ -34,11 +34,14 @@ use mappings::mouse::{
 };
 use modal::deploy_modal;
 
+use procinfo::LocalProcessInfo;
 use settings::{AlternateScroll, Settings, Shell, TerminalBlink};
+
 use std::{
     collections::{HashMap, VecDeque},
     fmt::Display,
     ops::{Deref, RangeInclusive, Sub},
+    os::unix::prelude::AsRawFd,
     path::PathBuf,
     sync::Arc,
     time::{Duration, Instant},
@@ -347,19 +350,8 @@ impl TerminalBuilder {
             }
         };
 
-        let shell_txt = {
-            match shell {
-                Some(Shell::System) | None => {
-                    let mut buf = [0; 1024];
-                    let pw = alacritty_unix::get_pw_entry(&mut buf).unwrap();
-                    pw.shell.to_string()
-                }
-                Some(Shell::Program(program)) => program,
-                Some(Shell::WithArguments { program, args }) => {
-                    format!("{} {}", program, args.join(" "))
-                }
-            }
-        };
+        let fd = pty.file().as_raw_fd();
+        let shell_pid = pty.child().id();
 
         //And connect them together
         let event_loop = EventLoop::new(
@@ -378,8 +370,6 @@ impl TerminalBuilder {
             pty_tx: Notifier(pty_tx),
             term,
             events: VecDeque::with_capacity(10), //Should never get this high.
-            title: shell_txt.clone(),
-            default_title: shell_txt,
             last_content: Default::default(),
             cur_size: initial_size,
             last_mouse: None,
@@ -387,6 +377,10 @@ impl TerminalBuilder {
             last_synced: Instant::now(),
             sync_task: None,
             selection_head: None,
+            shell_fd: fd as u32,
+            shell_pid,
+            foreground_process_info: None,
+            breadcrumb_text: String::new(),
         };
 
         Ok(TerminalBuilder {
@@ -495,8 +489,6 @@ pub struct Terminal {
     pty_tx: Notifier,
     term: Arc<FairMutex<Term<ZedListener>>>,
     events: VecDeque<InternalEvent>,
-    default_title: String,
-    title: String,
     last_mouse: Option<(Point, AlacDirection)>,
     pub matches: Vec<RangeInclusive<Point>>,
     cur_size: TerminalSize,
@@ -504,18 +496,20 @@ pub struct Terminal {
     last_synced: Instant,
     sync_task: Option<Task<()>>,
     selection_head: Option<Point>,
+    breadcrumb_text: String,
+    shell_pid: u32,
+    shell_fd: u32,
+    foreground_process_info: Option<LocalProcessInfo>,
 }
 
 impl Terminal {
     fn process_event(&mut self, event: &AlacTermEvent, cx: &mut ModelContext<Self>) {
         match event {
             AlacTermEvent::Title(title) => {
-                self.title = title.to_string();
-                cx.emit(Event::TitleChanged);
+                self.breadcrumb_text = title.to_string();
             }
             AlacTermEvent::ResetTitle => {
-                self.title = self.default_title.clone();
-                cx.emit(Event::TitleChanged);
+                self.breadcrumb_text = String::new();
             }
             AlacTermEvent::ClipboardStore(_, data) => {
                 cx.write_to_clipboard(ClipboardItem::new(data.to_string()))
@@ -705,11 +699,24 @@ impl Terminal {
             return;
         };
 
-        //Note that this ordering matters for event processing
+        //Note that the ordering of events matters for event processing
         while let Some(e) = self.events.pop_front() {
             self.process_terminal_event(&e, &mut terminal, cx)
         }
 
+        if let Some(process_info) = self.compute_process_info() {
+            let should_emit_title_changed = self
+                .foreground_process_info
+                .as_ref()
+                .map(|old_info| {
+                    process_info.cwd != old_info.cwd || process_info.name != old_info.name
+                })
+                .unwrap_or(true);
+            if should_emit_title_changed {
+                cx.emit(Event::TitleChanged)
+            }
+            self.foreground_process_info = Some(process_info.clone());
+        }
         self.last_content = Self::make_content(&terminal);
         self.last_synced = Instant::now();
     }
@@ -953,6 +960,14 @@ impl Terminal {
             make_search_matches(&term, &searcher).collect()
         })
     }
+
+    fn compute_process_info(&self) -> Option<LocalProcessInfo> {
+        let mut pid = unsafe { libc::tcgetpgrp(self.shell_fd as i32) };
+        if pid < 0 {
+            pid = self.shell_pid as i32;
+        }
+        LocalProcessInfo::with_root_pid(pid as u32)
+    }
 }
 
 impl Drop for Terminal {

crates/terminal/src/terminal_container_view.rs 🔗

@@ -238,9 +238,23 @@ impl Item for TerminalContainer {
         cx: &gpui::AppContext,
     ) -> ElementBox {
         let title = match &self.content {
-            TerminalContainerContent::Connected(connected) => {
-                connected.read(cx).handle().read(cx).title.to_string()
-            }
+            TerminalContainerContent::Connected(connected) => connected
+                .read(cx)
+                .handle()
+                .read(cx)
+                .foreground_process_info
+                .as_ref()
+                .map(|fpi| {
+                    format!(
+                        "{} - {}",
+                        fpi.cwd
+                            .file_name()
+                            .map(|name| name.to_string_lossy().to_string())
+                            .unwrap_or_default(),
+                        fpi.name,
+                    )
+                })
+                .unwrap_or_else(|| "Terminal".to_string()),
             TerminalContainerContent::Error(_) => "Terminal".to_string(),
         };