mirror of
https://github.com/wez/wezterm.git
synced 2024-09-20 03:09:06 +03:00
it worked!
This commit is contained in:
parent
e45a268ed1
commit
a26f190c7c
@ -3,12 +3,12 @@ use crate::pane::{Pane, PaneId};
|
||||
use crate::tab::{SplitDirection, Tab, TabId};
|
||||
use crate::tmux_commands::{ListAllPanes, TmuxCommand};
|
||||
use crate::window::WindowId;
|
||||
use crate::Mux;
|
||||
use crate::{Mux, MuxWindowBuilder};
|
||||
use async_trait::async_trait;
|
||||
use flume;
|
||||
use portable_pty::{CommandBuilder, PtySize};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::collections::{HashMap, HashSet, VecDeque};
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tmux_cc::*;
|
||||
@ -20,14 +20,15 @@ enum State {
|
||||
WaitingForResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct TmuxRemotePane {
|
||||
// members for local
|
||||
local_pane_id: PaneId,
|
||||
tx: flume::Sender<String>,
|
||||
pub local_pane_id: PaneId,
|
||||
pub tx: flume::Sender<String>,
|
||||
// members sync with remote
|
||||
session_id: TmuxSessionId,
|
||||
window_id: TmuxWindowId,
|
||||
pane_id: TmuxPaneId,
|
||||
pub session_id: TmuxSessionId,
|
||||
pub window_id: TmuxWindowId,
|
||||
pub pane_id: TmuxPaneId,
|
||||
pub cursor_x: u64,
|
||||
pub cursor_y: u64,
|
||||
pub pane_width: u64,
|
||||
@ -39,9 +40,9 @@ pub(crate) struct TmuxRemotePane {
|
||||
pub(crate) type RefTmuxRemotePane = Arc<Mutex<TmuxRemotePane>>;
|
||||
|
||||
pub(crate) struct TmuxTab {
|
||||
tab_id: TabId,
|
||||
tmux_window_id: TmuxWindowId,
|
||||
panes: Vec<TmuxPaneId>,
|
||||
pub tab_id: TabId, // local tab ID
|
||||
pub tmux_window_id: TmuxWindowId,
|
||||
pub panes: HashSet<TmuxPaneId>, // tmux panes within tmux window
|
||||
}
|
||||
|
||||
pub(crate) struct TmuxDomainState {
|
||||
@ -50,7 +51,7 @@ pub(crate) struct TmuxDomainState {
|
||||
// parser: RefCell<Parser>,
|
||||
state: RefCell<State>,
|
||||
pub cmd_queue: RefCell<VecDeque<Box<dyn TmuxCommand>>>,
|
||||
pub gui_window_id: RefCell<Option<usize>>,
|
||||
pub gui_window: RefCell<Option<MuxWindowBuilder>>,
|
||||
pub gui_tabs: RefCell<Vec<TmuxTab>>,
|
||||
pub remote_panes: RefCell<HashMap<TmuxPaneId, RefTmuxRemotePane>>,
|
||||
pub tmux_session: RefCell<Option<TmuxSessionId>>,
|
||||
@ -64,7 +65,7 @@ impl TmuxDomainState {
|
||||
pub fn advance(&self, events: Box<Vec<Event>>) {
|
||||
for event in events.iter() {
|
||||
let state = *self.state.borrow();
|
||||
log::error!("tmux: {:?} in state {:?}", event, state);
|
||||
log::info!("tmux: {:?} in state {:?}", event, state);
|
||||
match event {
|
||||
Event::Guarded(response) => match state {
|
||||
State::WaitForInitialGuard => {
|
||||
@ -77,7 +78,7 @@ impl TmuxDomainState {
|
||||
let resp = response.clone();
|
||||
promise::spawn::spawn(async move {
|
||||
if let Err(err) = cmd.process_result(domain_id, &resp) {
|
||||
log::error!("error processing result: {}", err);
|
||||
log::error!("Tmux processing command result error: {}", err);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
@ -87,28 +88,30 @@ impl TmuxDomainState {
|
||||
Event::Output { pane, text } => {
|
||||
let pane_map = self.remote_panes.borrow_mut();
|
||||
if let Some(ref_pane) = pane_map.get(pane) {
|
||||
// TODO: handle escape?
|
||||
let tmux_pane = ref_pane.lock().unwrap();
|
||||
tmux_pane
|
||||
.tx
|
||||
.send(text.to_string())
|
||||
.expect("send to tmux pane failed");
|
||||
} else {
|
||||
log::error!("Tmux pane {} havn't been attached", pane);
|
||||
}
|
||||
}
|
||||
Event::WindowAdd { window: _ } => {
|
||||
if self.gui_window_id.borrow().is_none() {
|
||||
if self.gui_window.borrow().is_none() {
|
||||
if let Some(mux) = Mux::get() {
|
||||
let window_builder = mux.new_empty_window();
|
||||
log::info!("Tmux create window id {}", window_builder.window_id);
|
||||
{
|
||||
let mut window_id = self.gui_window_id.borrow_mut();
|
||||
*window_id = Some(window_builder.window_id);
|
||||
let mut window_id = self.gui_window.borrow_mut();
|
||||
*window_id = Some(window_builder); // keep the builder so it won't be purged
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::SessionChanged { session, name: _ } => {
|
||||
*self.tmux_session.borrow_mut() = Some(*session);
|
||||
log::info!("tmux session changed:{}", session);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@ -158,7 +161,7 @@ impl TmuxDomain {
|
||||
// parser,
|
||||
state: RefCell::new(State::WaitForInitialGuard),
|
||||
cmd_queue: RefCell::new(cmd_queue),
|
||||
gui_window_id: RefCell::new(None),
|
||||
gui_window: RefCell::new(None),
|
||||
gui_tabs: RefCell::new(Vec::default()),
|
||||
remote_panes: RefCell::new(HashMap::default()),
|
||||
tmux_session: RefCell::new(None),
|
||||
|
@ -1,10 +1,19 @@
|
||||
use crate::domain::DomainId;
|
||||
use crate::tmux::{TmuxDomain, TmuxDomainState};
|
||||
use crate::localpane::LocalPane;
|
||||
use crate::pane::alloc_pane_id;
|
||||
use crate::tab::{Tab, TabId};
|
||||
use crate::tmux::{TmuxDomain, TmuxDomainState, TmuxRemotePane, TmuxTab};
|
||||
use crate::tmux_pty::TmuxPty;
|
||||
use crate::Mux;
|
||||
use crate::Pane;
|
||||
use anyhow::anyhow;
|
||||
use portable_pty::{MasterPty, PtySize};
|
||||
use std::collections::HashSet;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tmux_cc::*;
|
||||
|
||||
pub(crate) trait TmuxCommand {
|
||||
pub(crate) trait TmuxCommand: Send {
|
||||
fn get_command(&self) -> String;
|
||||
fn process_result(&self, domain_id: DomainId, result: &Guarded) -> anyhow::Result<()>;
|
||||
}
|
||||
@ -24,6 +33,54 @@ pub(crate) struct PaneItem {
|
||||
}
|
||||
|
||||
impl TmuxDomainState {
|
||||
fn check_pane_attached(&self, target: &PaneItem) -> bool {
|
||||
let pane_list = self.gui_tabs.borrow();
|
||||
let local_tab = match pane_list
|
||||
.iter()
|
||||
.find(|&x| x.tmux_window_id == target.window_id)
|
||||
{
|
||||
Some(x) => x,
|
||||
None => {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
match local_tab.panes.get(&target.pane_id) {
|
||||
Some(_) => {
|
||||
return true;
|
||||
}
|
||||
None => {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_attached_pane(&self, target: &PaneItem, tab_id: &TabId) -> anyhow::Result<()> {
|
||||
let mut pane_list = self.gui_tabs.borrow_mut();
|
||||
let local_tab = match pane_list
|
||||
.iter_mut()
|
||||
.find(|x| x.tmux_window_id == target.window_id)
|
||||
{
|
||||
Some(x) => x,
|
||||
None => {
|
||||
pane_list.push(TmuxTab {
|
||||
tab_id: *tab_id,
|
||||
tmux_window_id: target.window_id,
|
||||
panes: HashSet::new(),
|
||||
});
|
||||
pane_list.last_mut().unwrap()
|
||||
}
|
||||
};
|
||||
match local_tab.panes.get(&target.pane_id) {
|
||||
Some(_) => {
|
||||
anyhow::bail!("Tmux pane already attached");
|
||||
}
|
||||
None => {
|
||||
local_tab.panes.insert(target.pane_id);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn sync_pane_state(&self, panes: &[PaneItem]) -> anyhow::Result<()> {
|
||||
// TODO:
|
||||
// 1) iter over current session panes
|
||||
@ -32,9 +89,76 @@ impl TmuxDomainState {
|
||||
// 4) update pane state if exist
|
||||
let current_session = self.tmux_session.borrow().unwrap_or(0);
|
||||
for pane in panes.iter() {
|
||||
if pane.session_id != current_session {
|
||||
if pane.session_id != current_session || self.check_pane_attached(&pane) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let local_pane_id = alloc_pane_id();
|
||||
let channel = flume::unbounded::<String>();
|
||||
let ref_pane = Arc::new(Mutex::new(TmuxRemotePane {
|
||||
local_pane_id,
|
||||
tx: channel.0.clone(),
|
||||
session_id: pane.session_id,
|
||||
window_id: pane.window_id,
|
||||
pane_id: pane.pane_id,
|
||||
cursor_x: pane.cursor_x,
|
||||
cursor_y: pane.cursor_y,
|
||||
pane_width: pane.pane_width,
|
||||
pane_height: pane.pane_height,
|
||||
pane_left: pane.pane_left,
|
||||
pane_top: pane.pane_top,
|
||||
}));
|
||||
|
||||
{
|
||||
let mut pane_map = self.remote_panes.borrow_mut();
|
||||
pane_map.insert(pane.pane_id, ref_pane.clone());
|
||||
}
|
||||
|
||||
let pane_pty = TmuxPty {
|
||||
rx: channel.1.clone(),
|
||||
master_pane: ref_pane,
|
||||
};
|
||||
let writer = pane_pty.try_clone_writer().unwrap();
|
||||
let mux = Mux::get().expect("should be called at main thread");
|
||||
let size = PtySize {
|
||||
rows: pane.pane_height as u16,
|
||||
cols: pane.pane_width as u16,
|
||||
pixel_width: 0,
|
||||
pixel_height: 0,
|
||||
};
|
||||
|
||||
let terminal = wezterm_term::Terminal::new(
|
||||
crate::pty_size_to_terminal_size(size),
|
||||
std::sync::Arc::new(config::TermConfig::new()),
|
||||
"WezTerm",
|
||||
config::wezterm_version(),
|
||||
Box::new(writer),
|
||||
);
|
||||
|
||||
let local_pane: Rc<dyn Pane> = Rc::new(LocalPane::new(
|
||||
local_pane_id,
|
||||
terminal,
|
||||
Box::new(pane_pty.clone()),
|
||||
Box::new(pane_pty.clone()),
|
||||
self.domain_id,
|
||||
));
|
||||
|
||||
let tab = Rc::new(Tab::new(&size));
|
||||
tab.assign_pane(&local_pane);
|
||||
|
||||
let mut gui_window = self.gui_window.borrow_mut();
|
||||
let gui_window_id = match gui_window.as_mut() {
|
||||
Some(x) => &mut *x,
|
||||
None => {
|
||||
anyhow::bail!("None tmux GUI window created")
|
||||
}
|
||||
};
|
||||
|
||||
mux.add_tab_and_active_pane(&tab)?;
|
||||
mux.add_tab_to_window(&tab, **gui_window_id)?;
|
||||
gui_window_id.notify();
|
||||
|
||||
self.add_attached_pane(&pane, &tab.tab_id())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -119,3 +243,30 @@ impl TmuxCommand for ListAllPanes {
|
||||
anyhow::bail!("Tmux domain lost");
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct CapturePane(TmuxPaneId);
|
||||
impl TmuxCommand for CapturePane {
|
||||
fn get_command(&self) -> String {
|
||||
format!("capturep -p -t {} -e -C\n", self.0)
|
||||
}
|
||||
|
||||
fn process_result(&self, domain_id: DomainId, result: &Guarded) -> anyhow::Result<()> {
|
||||
let mux = Mux::get().expect("to be called on main thread");
|
||||
let domain = match mux.get_domain(domain_id) {
|
||||
Some(d) => d,
|
||||
None => anyhow::bail!("Tmux domain lost"),
|
||||
};
|
||||
let tmux_domain = match domain.downcast_ref::<TmuxDomain>() {
|
||||
Some(t) => t,
|
||||
None => anyhow::bail!("Tmux domain lost"),
|
||||
};
|
||||
|
||||
let pane_map = tmux_domain.inner.remote_panes.borrow();
|
||||
if let Some(pane) = pane_map.get(&self.0) {
|
||||
let lock = pane.lock().unwrap();
|
||||
lock.tx.send(result.output.to_owned()).unwrap();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use flume;
|
||||
use portable_pty::MasterPty;
|
||||
use portable_pty::{Child, MasterPty};
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use crate::tmux::RefTmuxRemotePane;
|
||||
@ -10,7 +10,7 @@ pub(crate) struct TmuxReader {
|
||||
|
||||
impl Read for TmuxReader {
|
||||
fn read(&mut self, mut buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
match self.rx.try_recv() {
|
||||
match self.rx.recv() {
|
||||
Ok(str) => {
|
||||
return buf.write(str.as_bytes());
|
||||
}
|
||||
@ -22,9 +22,10 @@ impl Read for TmuxReader {
|
||||
}
|
||||
|
||||
// A local tmux pane(tab) based on a tmux pty
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct TmuxPty {
|
||||
master_pane: RefTmuxRemotePane,
|
||||
rx: flume::Receiver<String>,
|
||||
pub master_pane: RefTmuxRemotePane,
|
||||
pub rx: flume::Receiver<String>,
|
||||
// TODO: wx
|
||||
}
|
||||
|
||||
@ -39,6 +40,24 @@ impl Write for TmuxPty {
|
||||
}
|
||||
}
|
||||
|
||||
impl Child for TmuxPty {
|
||||
fn try_wait(&mut self) -> std::io::Result<Option<portable_pty::ExitStatus>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn kill(&mut self) -> std::io::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn wait(&mut self) -> std::io::Result<portable_pty::ExitStatus> {
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn process_id(&self) -> Option<u32> {
|
||||
Some(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl MasterPty for TmuxPty {
|
||||
fn resize(&self, size: portable_pty::PtySize) -> Result<(), anyhow::Error> {
|
||||
// TODO: perform pane resize
|
||||
|
@ -91,7 +91,6 @@ impl Parser {
|
||||
let mut tmux_parser = tmux_state.borrow_mut();
|
||||
// TODO: wrap events into some Result to capture bytes cannot be parsed
|
||||
let tmux_events = tmux_parser.advance_bytes(bytes);
|
||||
log::info!("parsed tmux events: {:?}", &tmux_events);
|
||||
callback(Action::DeviceControl(DeviceControlMode::TmuxEvents(
|
||||
Box::new(tmux_events),
|
||||
)));
|
||||
@ -257,7 +256,6 @@ impl<'a, F: FnMut(Action)> VTActor for Performer<'a, F> {
|
||||
if let Some(tmux_state) = &self.state.tmux_state {
|
||||
let mut tmux_parser = tmux_state.borrow_mut();
|
||||
if let Some(tmux_event) = tmux_parser.advance_byte(data) {
|
||||
log::info!("parsed tmux event:{:?}", &tmux_event);
|
||||
(self.callback)(Action::DeviceControl(DeviceControlMode::TmuxEvents(
|
||||
Box::new(vec![tmux_event]),
|
||||
)));
|
||||
|
Loading…
Reference in New Issue
Block a user