mirror of
https://github.com/wez/wezterm.git
synced 2024-12-24 22:01:47 +03:00
refactor: split out termwindow spawn functions
This commit is contained in:
parent
6ff349308b
commit
63ca8e88b6
@ -5,7 +5,8 @@
|
||||
//! be rendered as a popup/context menu if the system supports it; at the
|
||||
//! time of writing our window layer doesn't provide an API for context
|
||||
//! menus.
|
||||
use crate::gui::termwindow::{ClipboardHelper, SpawnWhere, TermWindow};
|
||||
use crate::gui::termwindow::spawn::SpawnWhere;
|
||||
use crate::gui::termwindow::{ClipboardHelper, TermWindow};
|
||||
use anyhow::anyhow;
|
||||
use config::configuration;
|
||||
use config::keyassignment::{SpawnCommand, SpawnTabDomain};
|
||||
|
@ -21,7 +21,7 @@ use ::window::glium::Surface;
|
||||
use ::window::MouseButtons as WMB;
|
||||
use ::window::MouseEventKind as WMEK;
|
||||
use ::window::*;
|
||||
use anyhow::{anyhow, bail, ensure};
|
||||
use anyhow::{anyhow, ensure};
|
||||
use config::keyassignment::{
|
||||
ClipboardCopyDestination, ClipboardPasteSource, InputMap, KeyAssignment, MouseEventTrigger,
|
||||
SpawnCommand, SpawnTabDomain,
|
||||
@ -36,7 +36,7 @@ use mux::renderable::{RenderableDimensions, StableCursorPosition};
|
||||
use mux::tab::{PositionedPane, PositionedSplit, SplitDirection, TabId};
|
||||
use mux::window::WindowId as MuxWindowId;
|
||||
use mux::Mux;
|
||||
use portable_pty::{CommandBuilder, PtySize};
|
||||
use portable_pty::PtySize;
|
||||
use std::any::Any;
|
||||
use std::cell::{RefCell, RefMut};
|
||||
use std::collections::HashMap;
|
||||
@ -57,6 +57,8 @@ use wezterm_term::input::LastMouseClick;
|
||||
use wezterm_term::{ClipboardSelection, Line, StableRowIndex, TerminalConfiguration};
|
||||
|
||||
mod render;
|
||||
pub mod spawn;
|
||||
use spawn::SpawnWhere;
|
||||
|
||||
const ATLAS_SIZE: usize = 128;
|
||||
|
||||
@ -70,13 +72,6 @@ pub fn set_window_class(cls: &str) {
|
||||
*WINDOW_CLASS.lock().unwrap() = cls.to_owned();
|
||||
}
|
||||
|
||||
#[derive(Copy, Debug, Clone, Eq, PartialEq)]
|
||||
pub enum SpawnWhere {
|
||||
NewWindow,
|
||||
NewTab,
|
||||
SplitPane(SplitDirection),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct RowsAndCols {
|
||||
rows: usize,
|
||||
@ -1682,179 +1677,6 @@ impl TermWindow {
|
||||
self.move_tab(tab)
|
||||
}
|
||||
|
||||
fn spawn_command(&mut self, spawn: &SpawnCommand, spawn_where: SpawnWhere) {
|
||||
Self::spawn_command_impl(
|
||||
spawn,
|
||||
spawn_where,
|
||||
self.terminal_size,
|
||||
self.mux_window_id,
|
||||
ClipboardHelper {
|
||||
window: self.window.as_ref().unwrap().clone(),
|
||||
clipboard_contents: Arc::clone(&self.clipboard_contents),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn spawn_command_impl(
|
||||
spawn: &SpawnCommand,
|
||||
spawn_where: SpawnWhere,
|
||||
size: PtySize,
|
||||
src_window_id: MuxWindowId,
|
||||
clipboard: ClipboardHelper,
|
||||
) {
|
||||
let spawn = spawn.clone();
|
||||
|
||||
promise::spawn::spawn(async move {
|
||||
let mux = Mux::get().unwrap();
|
||||
let activity = Activity::new();
|
||||
let mux_builder;
|
||||
|
||||
let target_window_id = if spawn_where == SpawnWhere::NewWindow {
|
||||
mux_builder = mux.new_empty_window();
|
||||
*mux_builder
|
||||
} else {
|
||||
src_window_id
|
||||
};
|
||||
|
||||
let (domain, cwd) = match spawn.domain {
|
||||
SpawnTabDomain::DefaultDomain => {
|
||||
let cwd = mux
|
||||
.get_active_tab_for_window(src_window_id)
|
||||
.and_then(|tab| tab.get_active_pane())
|
||||
.and_then(|pane| pane.get_current_working_dir());
|
||||
(mux.default_domain().clone(), cwd)
|
||||
}
|
||||
SpawnTabDomain::CurrentPaneDomain => {
|
||||
if spawn_where == SpawnWhere::NewWindow {
|
||||
// CurrentPaneDomain is the default value for the spawn domain.
|
||||
// It doesn't make sense to use it when spawning a new window,
|
||||
// so we treat it as DefaultDomain instead.
|
||||
let cwd = mux
|
||||
.get_active_tab_for_window(src_window_id)
|
||||
.and_then(|tab| tab.get_active_pane())
|
||||
.and_then(|pane| pane.get_current_working_dir());
|
||||
(mux.default_domain().clone(), cwd)
|
||||
} else {
|
||||
let tab = match mux.get_active_tab_for_window(src_window_id) {
|
||||
Some(tab) => tab,
|
||||
None => bail!("window has no tabs?"),
|
||||
};
|
||||
let pane = tab
|
||||
.get_active_pane()
|
||||
.ok_or_else(|| anyhow!("current tab has no pane!?"))?;
|
||||
(
|
||||
mux.get_domain(pane.domain_id()).ok_or_else(|| {
|
||||
anyhow!("current tab has unresolvable domain id!?")
|
||||
})?,
|
||||
pane.get_current_working_dir(),
|
||||
)
|
||||
}
|
||||
}
|
||||
SpawnTabDomain::DomainName(name) => (
|
||||
mux.get_domain_by_name(&name).ok_or_else(|| {
|
||||
anyhow!("spawn_tab called with unresolvable domain name {}", name)
|
||||
})?,
|
||||
None,
|
||||
),
|
||||
};
|
||||
|
||||
if domain.state() == DomainState::Detached {
|
||||
bail!("Cannot spawn a tab into a Detached domain");
|
||||
}
|
||||
|
||||
let cwd = if let Some(cwd) = spawn.cwd.as_ref() {
|
||||
Some(cwd.to_str().map(|s| s.to_owned()).ok_or_else(|| {
|
||||
anyhow!(
|
||||
"Domain::spawn requires that the cwd be unicode in {:?}",
|
||||
cwd
|
||||
)
|
||||
})?)
|
||||
} else {
|
||||
match cwd {
|
||||
Some(url) if url.scheme() == "file" => {
|
||||
let path = url.path().to_string();
|
||||
// On Windows the file URI can produce a path like:
|
||||
// `/C:\Users` which is valid in a file URI, but the leading slash
|
||||
// is not liked by the windows file APIs, so we strip it off here.
|
||||
let bytes = path.as_bytes();
|
||||
if bytes.len() > 2 && bytes[0] == b'/' && bytes[2] == b':' {
|
||||
Some(path[1..].to_owned())
|
||||
} else {
|
||||
Some(path)
|
||||
}
|
||||
}
|
||||
Some(_) | None => None,
|
||||
}
|
||||
};
|
||||
|
||||
let cmd_builder = if let Some(args) = spawn.args {
|
||||
let mut builder = CommandBuilder::from_argv(args.iter().map(Into::into).collect());
|
||||
for (k, v) in spawn.set_environment_variables.iter() {
|
||||
builder.env(k, v);
|
||||
}
|
||||
if let Some(cwd) = spawn.cwd {
|
||||
builder.cwd(cwd);
|
||||
}
|
||||
Some(builder)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
match spawn_where {
|
||||
SpawnWhere::SplitPane(direction) => {
|
||||
let mux = Mux::get().unwrap();
|
||||
if let Some(tab) = mux.get_active_tab_for_window(target_window_id) {
|
||||
let pane = tab
|
||||
.get_active_pane()
|
||||
.ok_or_else(|| anyhow!("tab to have a pane"))?;
|
||||
|
||||
log::trace!("doing split_pane");
|
||||
domain
|
||||
.split_pane(cmd_builder, cwd, tab.tab_id(), pane.pane_id(), direction)
|
||||
.await?;
|
||||
} else {
|
||||
log::error!("there is no active tab while splitting pane!?");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let tab = domain
|
||||
.spawn(size, cmd_builder, cwd, target_window_id)
|
||||
.await?;
|
||||
let tab_id = tab.tab_id();
|
||||
let pane = tab
|
||||
.get_active_pane()
|
||||
.ok_or_else(|| anyhow!("newly spawned tab to have a pane"))?;
|
||||
|
||||
if spawn_where != SpawnWhere::NewWindow {
|
||||
let clipboard: Arc<dyn wezterm_term::Clipboard> = Arc::new(clipboard);
|
||||
pane.set_clipboard(&clipboard);
|
||||
let mut window = mux
|
||||
.get_window_mut(target_window_id)
|
||||
.ok_or_else(|| anyhow!("no such window!?"))?;
|
||||
if let Some(idx) = window.idx_by_id(tab_id) {
|
||||
window.set_active(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
drop(activity);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn spawn_tab(&mut self, domain: &SpawnTabDomain) {
|
||||
self.spawn_command(
|
||||
&SpawnCommand {
|
||||
domain: domain.clone(),
|
||||
..Default::default()
|
||||
},
|
||||
SpawnWhere::NewTab,
|
||||
);
|
||||
}
|
||||
|
||||
fn selection_text(&self, pane: &Rc<dyn Pane>) -> String {
|
||||
let mut s = String::new();
|
||||
if let Some(sel) = self
|
||||
|
@ -1,10 +1,7 @@
|
||||
use crate::gui::termwindow::rgbcolor_alpha_to_window_color;
|
||||
use crate::gui::termwindow::rgbcolor_to_window_color;
|
||||
use crate::gui::termwindow::BorrowedShapeCacheKey;
|
||||
use crate::gui::termwindow::MappedQuads;
|
||||
use crate::gui::termwindow::RenderState;
|
||||
use crate::gui::termwindow::ScrollHit;
|
||||
use crate::gui::termwindow::ShapedInfo;
|
||||
use crate::gui::termwindow::{
|
||||
rgbcolor_alpha_to_window_color, rgbcolor_to_window_color, BorrowedShapeCacheKey, MappedQuads,
|
||||
RenderState, ScrollHit, ShapedInfo,
|
||||
};
|
||||
use ::window::bitmaps::{Texture2d, TextureCoord, TextureRect, TextureSize};
|
||||
use ::window::glium;
|
||||
use ::window::glium::uniforms::{
|
||||
@ -21,9 +18,7 @@ use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use termwiz::surface::{CursorShape, CursorVisibility};
|
||||
use wezterm_font::units::PixelLength;
|
||||
use wezterm_term::color::ColorAttribute;
|
||||
use wezterm_term::color::ColorPalette;
|
||||
use wezterm_term::color::RgbColor;
|
||||
use wezterm_term::color::{ColorAttribute, ColorPalette, RgbColor};
|
||||
use wezterm_term::{CellAttributes, Line, StableRowIndex};
|
||||
use window::bitmaps::atlas::SpriteSlice;
|
||||
use window::Color;
|
||||
|
191
wezterm-gui/src/gui/termwindow/spawn.rs
Normal file
191
wezterm-gui/src/gui/termwindow/spawn.rs
Normal file
@ -0,0 +1,191 @@
|
||||
use crate::gui::termwindow::{ClipboardHelper, MuxWindowId};
|
||||
use anyhow::{anyhow, bail};
|
||||
use config::keyassignment::{SpawnCommand, SpawnTabDomain};
|
||||
use mux::activity::Activity;
|
||||
use mux::domain::DomainState;
|
||||
use mux::tab::SplitDirection;
|
||||
use mux::Mux;
|
||||
use portable_pty::{CommandBuilder, PtySize};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Copy, Debug, Clone, Eq, PartialEq)]
|
||||
pub enum SpawnWhere {
|
||||
NewWindow,
|
||||
NewTab,
|
||||
SplitPane(SplitDirection),
|
||||
}
|
||||
|
||||
impl super::TermWindow {
|
||||
pub fn spawn_command(&mut self, spawn: &SpawnCommand, spawn_where: SpawnWhere) {
|
||||
Self::spawn_command_impl(
|
||||
spawn,
|
||||
spawn_where,
|
||||
self.terminal_size,
|
||||
self.mux_window_id,
|
||||
ClipboardHelper {
|
||||
window: self.window.as_ref().unwrap().clone(),
|
||||
clipboard_contents: Arc::clone(&self.clipboard_contents),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn spawn_command_impl(
|
||||
spawn: &SpawnCommand,
|
||||
spawn_where: SpawnWhere,
|
||||
size: PtySize,
|
||||
src_window_id: MuxWindowId,
|
||||
clipboard: ClipboardHelper,
|
||||
) {
|
||||
let spawn = spawn.clone();
|
||||
|
||||
promise::spawn::spawn(async move {
|
||||
let mux = Mux::get().unwrap();
|
||||
let activity = Activity::new();
|
||||
let mux_builder;
|
||||
|
||||
let target_window_id = if spawn_where == SpawnWhere::NewWindow {
|
||||
mux_builder = mux.new_empty_window();
|
||||
*mux_builder
|
||||
} else {
|
||||
src_window_id
|
||||
};
|
||||
|
||||
let (domain, cwd) = match spawn.domain {
|
||||
SpawnTabDomain::DefaultDomain => {
|
||||
let cwd = mux
|
||||
.get_active_tab_for_window(src_window_id)
|
||||
.and_then(|tab| tab.get_active_pane())
|
||||
.and_then(|pane| pane.get_current_working_dir());
|
||||
(mux.default_domain().clone(), cwd)
|
||||
}
|
||||
SpawnTabDomain::CurrentPaneDomain => {
|
||||
if spawn_where == SpawnWhere::NewWindow {
|
||||
// CurrentPaneDomain is the default value for the spawn domain.
|
||||
// It doesn't make sense to use it when spawning a new window,
|
||||
// so we treat it as DefaultDomain instead.
|
||||
let cwd = mux
|
||||
.get_active_tab_for_window(src_window_id)
|
||||
.and_then(|tab| tab.get_active_pane())
|
||||
.and_then(|pane| pane.get_current_working_dir());
|
||||
(mux.default_domain().clone(), cwd)
|
||||
} else {
|
||||
let tab = match mux.get_active_tab_for_window(src_window_id) {
|
||||
Some(tab) => tab,
|
||||
None => bail!("window has no tabs?"),
|
||||
};
|
||||
let pane = tab
|
||||
.get_active_pane()
|
||||
.ok_or_else(|| anyhow!("current tab has no pane!?"))?;
|
||||
(
|
||||
mux.get_domain(pane.domain_id()).ok_or_else(|| {
|
||||
anyhow!("current tab has unresolvable domain id!?")
|
||||
})?,
|
||||
pane.get_current_working_dir(),
|
||||
)
|
||||
}
|
||||
}
|
||||
SpawnTabDomain::DomainName(name) => (
|
||||
mux.get_domain_by_name(&name).ok_or_else(|| {
|
||||
anyhow!("spawn_tab called with unresolvable domain name {}", name)
|
||||
})?,
|
||||
None,
|
||||
),
|
||||
};
|
||||
|
||||
if domain.state() == DomainState::Detached {
|
||||
bail!("Cannot spawn a tab into a Detached domain");
|
||||
}
|
||||
|
||||
let cwd = if let Some(cwd) = spawn.cwd.as_ref() {
|
||||
Some(cwd.to_str().map(|s| s.to_owned()).ok_or_else(|| {
|
||||
anyhow!(
|
||||
"Domain::spawn requires that the cwd be unicode in {:?}",
|
||||
cwd
|
||||
)
|
||||
})?)
|
||||
} else {
|
||||
match cwd {
|
||||
Some(url) if url.scheme() == "file" => {
|
||||
let path = url.path().to_string();
|
||||
// On Windows the file URI can produce a path like:
|
||||
// `/C:\Users` which is valid in a file URI, but the leading slash
|
||||
// is not liked by the windows file APIs, so we strip it off here.
|
||||
let bytes = path.as_bytes();
|
||||
if bytes.len() > 2 && bytes[0] == b'/' && bytes[2] == b':' {
|
||||
Some(path[1..].to_owned())
|
||||
} else {
|
||||
Some(path)
|
||||
}
|
||||
}
|
||||
Some(_) | None => None,
|
||||
}
|
||||
};
|
||||
|
||||
let cmd_builder = if let Some(args) = spawn.args {
|
||||
let mut builder = CommandBuilder::from_argv(args.iter().map(Into::into).collect());
|
||||
for (k, v) in spawn.set_environment_variables.iter() {
|
||||
builder.env(k, v);
|
||||
}
|
||||
if let Some(cwd) = spawn.cwd {
|
||||
builder.cwd(cwd);
|
||||
}
|
||||
Some(builder)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
match spawn_where {
|
||||
SpawnWhere::SplitPane(direction) => {
|
||||
let mux = Mux::get().unwrap();
|
||||
if let Some(tab) = mux.get_active_tab_for_window(target_window_id) {
|
||||
let pane = tab
|
||||
.get_active_pane()
|
||||
.ok_or_else(|| anyhow!("tab to have a pane"))?;
|
||||
|
||||
log::trace!("doing split_pane");
|
||||
domain
|
||||
.split_pane(cmd_builder, cwd, tab.tab_id(), pane.pane_id(), direction)
|
||||
.await?;
|
||||
} else {
|
||||
log::error!("there is no active tab while splitting pane!?");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let tab = domain
|
||||
.spawn(size, cmd_builder, cwd, target_window_id)
|
||||
.await?;
|
||||
let tab_id = tab.tab_id();
|
||||
let pane = tab
|
||||
.get_active_pane()
|
||||
.ok_or_else(|| anyhow!("newly spawned tab to have a pane"))?;
|
||||
|
||||
if spawn_where != SpawnWhere::NewWindow {
|
||||
let clipboard: Arc<dyn wezterm_term::Clipboard> = Arc::new(clipboard);
|
||||
pane.set_clipboard(&clipboard);
|
||||
let mut window = mux
|
||||
.get_window_mut(target_window_id)
|
||||
.ok_or_else(|| anyhow!("no such window!?"))?;
|
||||
if let Some(idx) = window.idx_by_id(tab_id) {
|
||||
window.set_active(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
drop(activity);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
pub fn spawn_tab(&mut self, domain: &SpawnTabDomain) {
|
||||
self.spawn_command(
|
||||
&SpawnCommand {
|
||||
domain: domain.clone(),
|
||||
..Default::default()
|
||||
},
|
||||
SpawnWhere::NewTab,
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user