1
1
mirror of https://github.com/wez/wezterm.git synced 2024-09-20 03:09:06 +03:00

it worked!

This commit is contained in:
g4c 2021-08-29 16:34:36 +08:00 committed by Wez Furlong
parent e45a268ed1
commit a26f190c7c
4 changed files with 198 additions and 27 deletions

View File

@ -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),

View File

@ -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(())
}
}

View File

@ -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

View File

@ -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]),
)));