Skip to content

Commit 3752aaf

Browse files
committed
refactor(terminal): drop tmux bridge variants from TerminalIo and BridgeTarget
- Remove TerminalBridgeTarget::Tmux variant, keeping only Pty - Remove TerminalIo::TmuxAttached variant - Delete create_tmux_terminal_runtime function - Simplify create_terminal_runtime to direct Pty dispatch - Remove tmux session kill logic from terminal_close and close_workspace_terminals - Retain #[cfg(test)] Mock variant for test infrastructure
1 parent 420d49b commit 3752aaf

2 files changed

Lines changed: 24 additions & 258 deletions

File tree

apps/server/src/app.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,7 @@ pub(crate) enum TerminalIo {
4444
writer_tx: std::sync::mpsc::Sender<crate::services::terminal::PtyWriteRequest>,
4545
master: Mutex<Box<dyn MasterPty + Send>>,
4646
},
47-
TmuxAttached {
48-
session_name: String,
49-
pane_id: String,
50-
writer: Mutex<Option<Box<dyn Write + Send>>>,
51-
master: Mutex<Box<dyn MasterPty + Send>>,
52-
},
47+
/// Test-only variant for constructing mock terminal runtimes without a real PTY.
5348
#[cfg(test)]
5449
Mock,
5550
}

apps/server/src/services/terminal.rs

Lines changed: 23 additions & 252 deletions
Original file line numberDiff line numberDiff line change
@@ -183,22 +183,12 @@ fn terminate_terminal_runtime(runtime: Arc<TerminalRuntime>) {
183183
}
184184
}
185185
// Then close the writer end so no further input can be sent.
186-
// For PTY, we drop the sender which signals the writer thread to exit.
187-
// For TmuxAttached, we take the writer handle.
186+
// Writer thread will exit when the channel is closed (sender dropped).
187+
// No explicit action needed; dropping the runtime closes the channel.
188188
match &runtime.io {
189-
TerminalIo::Pty { .. } => {
190-
// Writer thread will exit when the channel is closed (sender dropped).
191-
// No explicit action needed; dropping the runtime closes the channel.
192-
}
193-
TerminalIo::TmuxAttached { writer, .. } => {
194-
if let Ok(mut writer) = writer.lock() {
195-
writer.take();
196-
}
197-
}
189+
TerminalIo::Pty { .. } => {}
198190
#[cfg(test)]
199191
TerminalIo::Mock => {}
200-
#[cfg(not(test))]
201-
_ => {}
202192
}
203193
}
204194

@@ -215,12 +205,6 @@ pub(crate) enum TerminalBridgeTarget {
215205
cols: Option<u16>,
216206
rows: Option<u16>,
217207
},
218-
Tmux {
219-
session_name: String,
220-
pane_id: String,
221-
cols: Option<u16>,
222-
rows: Option<u16>,
223-
},
224208
}
225209

226210
pub(crate) struct TerminalCreateOptions {
@@ -545,176 +529,6 @@ fn create_pty_terminal_runtime(
545529
Ok(runtime)
546530
}
547531

548-
fn create_tmux_terminal_runtime(
549-
terminal_id: u64,
550-
workspace_id: &str,
551-
session_name: &str,
552-
pane_id: &str,
553-
cols: Option<u16>,
554-
rows: Option<u16>,
555-
options: TerminalCreateOptions,
556-
app: &AppHandle,
557-
_state: State<'_, AppState>,
558-
) -> Result<Arc<TerminalRuntime>, String> {
559-
let attach_runtime = crate::services::tmux::attach_tmux_session(session_name, cols, rows)?;
560-
let reader = attach_runtime
561-
.pair
562-
.master
563-
.try_clone_reader()
564-
.map_err(|e| e.to_string())?;
565-
let writer = attach_runtime
566-
.pair
567-
.master
568-
.take_writer()
569-
.map_err(|e| e.to_string())?;
570-
571-
let runtime = Arc::new(TerminalRuntime {
572-
io: TerminalIo::TmuxAttached {
573-
session_name: session_name.to_string(),
574-
pane_id: pane_id.to_string(),
575-
writer: Mutex::new(Some(writer)),
576-
master: Mutex::new(attach_runtime.pair.master),
577-
},
578-
output: Mutex::new(String::new()),
579-
size: Mutex::new((80, 24)),
580-
persist_workspace_terminal: options.persist_workspace_terminal,
581-
child: Some(attach_runtime.child),
582-
killer: Some(attach_runtime.killer),
583-
process_id: None,
584-
process_group_leader: None,
585-
});
586-
587-
let app_handle = app.clone();
588-
let state_handle = app.clone();
589-
let runtime_out = runtime.clone();
590-
let workspace_id_out = workspace_id.to_string();
591-
std::thread::spawn(move || {
592-
let mut reader = reader;
593-
let mut buf = [0u8; 4096];
594-
let mut decoder = Utf8StreamDecoder::new();
595-
loop {
596-
match reader.read(&mut buf) {
597-
Ok(0) => {
598-
let text = decoder.finish();
599-
let state: State<AppState> = state_handle.state();
600-
emit_runtime_output(
601-
&runtime_out,
602-
&app_handle,
603-
state,
604-
&workspace_id_out,
605-
terminal_id,
606-
&text,
607-
);
608-
break;
609-
}
610-
Ok(n) => {
611-
let text = decoder.push(&buf[..n]);
612-
if text.is_empty() {
613-
continue;
614-
}
615-
let state: State<AppState> = state_handle.state();
616-
emit_runtime_output(
617-
&runtime_out,
618-
&app_handle,
619-
state,
620-
&workspace_id_out,
621-
terminal_id,
622-
&text,
623-
);
624-
}
625-
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {
626-
// Non-blocking read with no data available: sleep and retry.
627-
std::thread::sleep(std::time::Duration::from_millis(10));
628-
continue;
629-
}
630-
Err(err) => {
631-
let text = decoder.finish();
632-
let state: State<AppState> = state_handle.state();
633-
if !text.is_empty() {
634-
emit_runtime_output(
635-
&runtime_out,
636-
&app_handle,
637-
state,
638-
&workspace_id_out,
639-
terminal_id,
640-
&text,
641-
);
642-
}
643-
let state: State<AppState> = state_handle.state();
644-
let error_msg = format!("\n[terminal error: read failed: {err}]\n");
645-
emit_runtime_output(
646-
&runtime_out,
647-
&app_handle,
648-
state,
649-
&workspace_id_out,
650-
terminal_id,
651-
&error_msg,
652-
);
653-
break;
654-
}
655-
}
656-
}
657-
});
658-
659-
let app_handle = app.clone();
660-
let state_handle = app.clone();
661-
let runtime_out = runtime.clone();
662-
let workspace_id_out = workspace_id.to_string();
663-
let key = terminal_key(workspace_id, terminal_id);
664-
std::thread::spawn(move || {
665-
let exit_text = match &runtime_out.child {
666-
Some(child) => match child.lock() {
667-
Ok(mut child) => format_terminal_exit_message(child.wait()),
668-
Err(error) => {
669-
format!("\n[terminal exited: failed to lock child handle: {error}]\n")
670-
}
671-
},
672-
None => "\n[terminal exited]\n".to_string(),
673-
};
674-
let state: State<AppState> = state_handle.state();
675-
emit_runtime_output(
676-
&runtime_out,
677-
&app_handle,
678-
state,
679-
&workspace_id_out,
680-
terminal_id,
681-
&exit_text,
682-
);
683-
let state: State<AppState> = state_handle.state();
684-
if let Ok(Some((binding_workspace_id, session_id))) =
685-
crate::services::session_runtime::session_runtime_binding_for_terminal(
686-
terminal_id,
687-
state,
688-
)
689-
{
690-
if binding_workspace_id == workspace_id_out {
691-
let _ = crate::services::session_runtime::remove_terminal_runtime_registration(
692-
&workspace_id_out,
693-
&session_id,
694-
state,
695-
);
696-
}
697-
}
698-
if runtime_out.persist_workspace_terminal {
699-
let _ =
700-
set_workspace_terminal_recoverable(state, &workspace_id_out, terminal_id, false);
701-
}
702-
sync_bound_terminal_runtime_state(
703-
&workspace_id_out,
704-
terminal_id,
705-
SessionStatus::Interrupted,
706-
false,
707-
Some(SessionRuntimeLiveness::ProviderExited),
708-
state,
709-
);
710-
if let Ok(mut terms) = state.terminals.lock() {
711-
terms.remove(&key);
712-
}
713-
});
714-
715-
Ok(runtime)
716-
}
717-
718532
pub(crate) fn create_terminal_runtime(
719533
workspace_id: &str,
720534
_cwd: &str,
@@ -727,40 +541,23 @@ pub(crate) fn create_terminal_runtime(
727541
) -> Result<TerminalInfo, String> {
728542
let terminal_id = next_terminal_id(state)?;
729543
let bridge_target = options.bridge_target.clone();
730-
let runtime = match bridge_target {
731-
TerminalBridgeTarget::Pty {
732-
cwd,
733-
target,
734-
cols,
735-
rows,
736-
} => create_pty_terminal_runtime(
737-
terminal_id,
738-
workspace_id,
739-
&cwd,
740-
&target,
741-
cols,
742-
rows,
743-
options,
744-
app,
745-
state,
746-
)?,
747-
TerminalBridgeTarget::Tmux {
748-
session_name,
749-
pane_id,
750-
cols,
751-
rows,
752-
} => create_tmux_terminal_runtime(
753-
terminal_id,
754-
workspace_id,
755-
&session_name,
756-
&pane_id,
757-
cols,
758-
rows,
759-
options,
760-
app,
761-
state,
762-
)?,
763-
};
544+
let TerminalBridgeTarget::Pty {
545+
cwd,
546+
target,
547+
cols,
548+
rows,
549+
} = bridge_target;
550+
let runtime = create_pty_terminal_runtime(
551+
terminal_id,
552+
workspace_id,
553+
&cwd,
554+
&target,
555+
cols,
556+
rows,
557+
options,
558+
app,
559+
state,
560+
)?;
764561

765562
if runtime.persist_workspace_terminal {
766563
if let Err(error) = persist_workspace_terminal(state, workspace_id, terminal_id, "", true) {
@@ -873,21 +670,8 @@ pub(crate) fn terminal_write(
873670
}
874671
}
875672
}
876-
TerminalIo::TmuxAttached { writer, .. } => {
877-
let mut writer = writer.lock().map_err(|e| e.to_string())?;
878-
if let Some(handle) = writer.as_mut() {
879-
handle
880-
.write_all(decorated_input.as_bytes())
881-
.map_err(|e| e.to_string())?;
882-
handle.flush().map_err(|e| e.to_string())?;
883-
} else {
884-
return Err("terminal_stdin_closed".to_string());
885-
}
886-
}
887673
#[cfg(test)]
888674
TerminalIo::Mock => {}
889-
#[cfg(not(test))]
890-
_ => {}
891675
}
892676
#[cfg(test)]
893677
state
@@ -944,7 +728,7 @@ pub(crate) fn terminal_resize(
944728
let terms = state.terminals.lock().map_err(|e| e.to_string())?;
945729
let runtime = terms.get(&key).ok_or("terminal_not_found")?.clone();
946730
match &runtime.io {
947-
TerminalIo::Pty { master, .. } | TerminalIo::TmuxAttached { master, .. } => {
731+
TerminalIo::Pty { master, .. } => {
948732
let master = master.lock().map_err(|e| e.to_string())?;
949733
master
950734
.resize(PtySize {
@@ -957,9 +741,10 @@ pub(crate) fn terminal_resize(
957741
}
958742
#[cfg(test)]
959743
TerminalIo::Mock => Ok(()),
744+
#[cfg(not(test))]
960745
_ => {
961746
eprintln!("warning: terminal_resize: unknown TerminalIo variant, skipping");
962-
return Ok(());
747+
Ok(())
963748
}
964749
}
965750
}
@@ -976,14 +761,7 @@ pub(crate) fn terminal_close(
976761
};
977762

978763
if let Some(runtime) = runtime {
979-
let tmux_session = match &runtime.io {
980-
TerminalIo::TmuxAttached { session_name, .. } => Some(session_name.clone()),
981-
_ => None,
982-
};
983764
terminate_terminal_runtime(runtime);
984-
if let Some(session_name) = tmux_session {
985-
let _ = crate::services::tmux::kill_tmux_session(&session_name);
986-
}
987765
}
988766
let is_bound_session_terminal =
989767
crate::services::session_runtime::session_runtime_binding_for_terminal(terminal_id, state)?
@@ -1026,14 +804,7 @@ pub(crate) fn close_workspace_terminals(workspace_id: &str, state: State<'_, App
1026804
};
1027805

1028806
for (terminal_id, runtime) in runtimes {
1029-
let tmux_session = match &runtime.io {
1030-
TerminalIo::TmuxAttached { session_name, .. } => Some(session_name.clone()),
1031-
_ => None,
1032-
};
1033807
terminate_terminal_runtime(runtime);
1034-
if let Some(session_name) = tmux_session {
1035-
let _ = crate::services::tmux::kill_tmux_session(&session_name);
1036-
}
1037808
sync_bound_terminal_runtime_state(
1038809
workspace_id,
1039810
terminal_id,

0 commit comments

Comments
 (0)