Checkpoint, this commit does not compile
This commit is contained in:
parent
b493bafb48
commit
9b6df1fb61
6 changed files with 116 additions and 50 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -5368,6 +5368,7 @@ dependencies = [
|
||||||
"shellexpand",
|
"shellexpand",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"theme",
|
"theme",
|
||||||
|
"thiserror",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
|
|
@ -25,6 +25,7 @@ dirs = "4.0.0"
|
||||||
shellexpand = "2.1.0"
|
shellexpand = "2.1.0"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
|
thiserror = "1.0"
|
||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
@ -13,12 +13,14 @@ use alacritty_terminal::{
|
||||||
tty::{self, setup_env},
|
tty::{self, setup_env},
|
||||||
Term,
|
Term,
|
||||||
};
|
};
|
||||||
|
use anyhow::{bail, Result};
|
||||||
use futures::{
|
use futures::{
|
||||||
channel::mpsc::{unbounded, UnboundedSender},
|
channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender},
|
||||||
StreamExt,
|
StreamExt,
|
||||||
};
|
};
|
||||||
use settings::{Settings, Shell};
|
use settings::{Settings, Shell};
|
||||||
use std::{collections::HashMap, path::PathBuf, sync::Arc};
|
use std::{collections::HashMap, fmt::Display, path::PathBuf, sync::Arc};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
use gpui::{keymap::Keystroke, ClipboardItem, CursorStyle, Entity, ModelContext};
|
use gpui::{keymap::Keystroke, ClipboardItem, CursorStyle, Entity, ModelContext};
|
||||||
|
|
||||||
|
@ -52,23 +54,84 @@ impl EventListener for ZedListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum TerminalConnection {
|
#[derive(Error, Debug)]
|
||||||
Connected(Terminal),
|
pub struct TerminalError {
|
||||||
Disconnected {
|
|
||||||
directory: Option<PathBuf>,
|
directory: Option<PathBuf>,
|
||||||
shell: Option<Shell>,
|
shell: Option<Shell>,
|
||||||
error: Option<std::io::Error>,
|
source: std::io::Error,
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TerminalConnection {
|
impl Display for TerminalError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let dir_string: String = self
|
||||||
|
.directory
|
||||||
|
.map(|path| {
|
||||||
|
match path
|
||||||
|
.into_os_string()
|
||||||
|
.into_string()
|
||||||
|
.map_err(|os_str| format!("<non-utf8 path> {}", os_str.to_string_lossy()))
|
||||||
|
{
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(s) => s,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
let default_dir =
|
||||||
|
dirs::home_dir().map(|buf| buf.into_os_string().to_string_lossy());
|
||||||
|
match default_dir {
|
||||||
|
Some(dir) => format!("<none specified, using home> {}", dir),
|
||||||
|
None => "<none specified, could not find home>".to_string(),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let shell = self
|
||||||
|
.shell
|
||||||
|
.map(|shell| match shell {
|
||||||
|
Shell::System => {
|
||||||
|
let mut buf = [0; 1024];
|
||||||
|
let pw = alacritty_unix::get_pw_entry(&mut buf).ok();
|
||||||
|
|
||||||
|
match pw {
|
||||||
|
Some(pw) => format!("<system defined shell> {}", pw.shell),
|
||||||
|
None => "<could not access system defined shell>".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Shell::Program(s) => s,
|
||||||
|
Shell::WithArguments { program, args } => format!("{} {}", program, args.join(" ")),
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
let mut buf = [0; 1024];
|
||||||
|
let pw = alacritty_unix::get_pw_entry(&mut buf).ok();
|
||||||
|
match pw {
|
||||||
|
Some(pw) => {
|
||||||
|
format!("<none specified, using system defined shell> {}", pw.shell)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
"<none specified, could not access system defined shell> {}".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"Working directory: {} Shell command: `{}`, IOError: {}",
|
||||||
|
dir_string, shell, self.source
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DisconnectedPTY {
|
||||||
|
terminal: Terminal,
|
||||||
|
events_rx: UnboundedReceiver<AlacTermEvent>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DisconnectedPTY {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
working_directory: Option<PathBuf>,
|
working_directory: Option<PathBuf>,
|
||||||
shell: Option<Shell>,
|
shell: Option<Shell>,
|
||||||
env: Option<HashMap<String, String>>,
|
env: Option<HashMap<String, String>>,
|
||||||
initial_size: TerminalDimensions,
|
initial_size: TerminalDimensions,
|
||||||
cx: &mut ModelContext<Self>,
|
) -> Result<DisconnectedPTY> {
|
||||||
) -> TerminalConnection {
|
|
||||||
let pty_config = {
|
let pty_config = {
|
||||||
let alac_shell = shell.clone().and_then(|shell| match shell {
|
let alac_shell = shell.clone().and_then(|shell| match shell {
|
||||||
Shell::System => None,
|
Shell::System => None,
|
||||||
|
@ -107,11 +170,11 @@ impl TerminalConnection {
|
||||||
let pty = match tty::new(&pty_config, initial_size.into(), None) {
|
let pty = match tty::new(&pty_config, initial_size.into(), None) {
|
||||||
Ok(pty) => pty,
|
Ok(pty) => pty,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
return TerminalConnection::Disconnected {
|
bail!(TerminalError {
|
||||||
directory: working_directory,
|
directory: working_directory,
|
||||||
shell,
|
shell,
|
||||||
error: Some(error),
|
source: error,
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -149,20 +212,20 @@ impl TerminalConnection {
|
||||||
associated_directory: working_directory,
|
associated_directory: working_directory,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Ok(DisconnectedPTY {
|
||||||
|
terminal,
|
||||||
|
events_rx,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn connect(self, cx: &mut ModelContext<Terminal>) -> Terminal {
|
||||||
cx.spawn_weak(|this, mut cx| async move {
|
cx.spawn_weak(|this, mut cx| async move {
|
||||||
//Listen for terminal events
|
//Listen for terminal events
|
||||||
while let Some(event) = events_rx.next().await {
|
while let Some(event) = self.events_rx.next().await {
|
||||||
match this.upgrade(&cx) {
|
match this.upgrade(&cx) {
|
||||||
Some(this) => {
|
Some(this) => {
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
match this {
|
this.process_terminal_event(event, cx);
|
||||||
TerminalConnection::Connected(conn) => {
|
|
||||||
conn.process_terminal_event(event, cx)
|
|
||||||
}
|
|
||||||
//There should never be a state where the terminal is disconnected
|
|
||||||
//And receiving events from the pty
|
|
||||||
TerminalConnection::Disconnected { .. } => unreachable!(),
|
|
||||||
}
|
|
||||||
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
|
@ -173,14 +236,7 @@ impl TerminalConnection {
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
TerminalConnection::Connected(terminal)
|
self.terminal
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_terminal(&self) -> Option<&Terminal> {
|
|
||||||
match self {
|
|
||||||
TerminalConnection::Connected(conn) => Some(&conn),
|
|
||||||
TerminalConnection::Disconnected { .. } => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,7 +252,7 @@ impl Terminal {
|
||||||
fn process_terminal_event(
|
fn process_terminal_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
event: alacritty_terminal::event::Event,
|
event: alacritty_terminal::event::Event,
|
||||||
cx: &mut ModelContext<TerminalConnection>,
|
cx: &mut ModelContext<Terminal>,
|
||||||
) {
|
) {
|
||||||
match event {
|
match event {
|
||||||
// TODO: Handle is_self_focused in subscription on terminal view
|
// TODO: Handle is_self_focused in subscription on terminal view
|
||||||
|
@ -361,21 +417,23 @@ impl Terminal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for TerminalConnection {
|
impl Drop for DisconnectedPTY {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
match self {
|
self.terminal.pty_tx.0.send(Msg::Shutdown).ok();
|
||||||
TerminalConnection::Connected(conn) => {
|
|
||||||
conn.pty_tx.0.send(Msg::Shutdown).ok();
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Entity for TerminalConnection {
|
impl Drop for Terminal {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.pty_tx.0.send(Msg::Shutdown).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Entity for Terminal {
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO Move this around
|
||||||
mod alacritty_unix {
|
mod alacritty_unix {
|
||||||
use alacritty_terminal::config::Program;
|
use alacritty_terminal::config::Program;
|
||||||
use gpui::anyhow::{bail, Result};
|
use gpui::anyhow::{bail, Result};
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use gpui::{ModelHandle, ViewContext};
|
use gpui::{ModelHandle, ViewContext};
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
use crate::{get_wd_for_workspace, DeployModal, Event, TerminalConnection, TerminalView};
|
use crate::{connection::Terminal, get_wd_for_workspace, DeployModal, Event, TerminalView};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct StoredConnection(ModelHandle<TerminalConnection>);
|
struct StoredConnection(ModelHandle<Terminal>);
|
||||||
|
|
||||||
pub fn deploy_modal(workspace: &mut Workspace, _: &DeployModal, cx: &mut ViewContext<Workspace>) {
|
pub fn deploy_modal(workspace: &mut Workspace, _: &DeployModal, cx: &mut ViewContext<Workspace>) {
|
||||||
// Pull the terminal connection out of the global if it has been stored
|
// Pull the terminal connection out of the global if it has been stored
|
||||||
|
@ -46,7 +46,7 @@ pub fn deploy_modal(workspace: &mut Workspace, _: &DeployModal, cx: &mut ViewCon
|
||||||
|
|
||||||
pub fn on_event(
|
pub fn on_event(
|
||||||
workspace: &mut Workspace,
|
workspace: &mut Workspace,
|
||||||
_: ModelHandle<TerminalConnection>,
|
_: ModelHandle<Terminal>,
|
||||||
event: &Event,
|
event: &Event,
|
||||||
cx: &mut ViewContext<Workspace>,
|
cx: &mut ViewContext<Workspace>,
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -3,7 +3,7 @@ pub mod connection;
|
||||||
mod modal;
|
mod modal;
|
||||||
pub mod terminal_element;
|
pub mod terminal_element;
|
||||||
|
|
||||||
use connection::{Event, TerminalConnection};
|
use connection::{DisconnectedPTY, Event, Terminal, TerminalError};
|
||||||
use dirs::home_dir;
|
use dirs::home_dir;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, elements::*, geometry::vector::vec2f, keymap::Keystroke, AppContext, ClipboardItem,
|
actions, elements::*, geometry::vector::vec2f, keymap::Keystroke, AppContext, ClipboardItem,
|
||||||
|
@ -64,7 +64,7 @@ pub fn init(cx: &mut MutableAppContext) {
|
||||||
|
|
||||||
///A terminal view, maintains the PTY's file handles and communicates with the terminal
|
///A terminal view, maintains the PTY's file handles and communicates with the terminal
|
||||||
pub struct TerminalView {
|
pub struct TerminalView {
|
||||||
connection: ModelHandle<TerminalConnection>,
|
connection: Result<ModelHandle<Terminal>, TerminalError>,
|
||||||
has_new_content: bool,
|
has_new_content: bool,
|
||||||
//Currently using iTerm bell, show bell emoji in tab until input is received
|
//Currently using iTerm bell, show bell emoji in tab until input is received
|
||||||
has_bell: bool,
|
has_bell: bool,
|
||||||
|
@ -94,14 +94,20 @@ impl TerminalView {
|
||||||
(shell, envs)
|
(shell, envs)
|
||||||
};
|
};
|
||||||
|
|
||||||
let connection = cx
|
let connection = DisconnectedPTY::new(working_directory, shell, envs, size_info)
|
||||||
.add_model(|cx| TerminalConnection::new(working_directory, shell, envs, size_info, cx));
|
.map(|pty| cx.add_model(|cx| pty.connect(cx)))
|
||||||
|
.map_err(|err| {
|
||||||
|
match err.downcast::<TerminalError>() {
|
||||||
|
Ok(err) => err,
|
||||||
|
Err(_) => unreachable!(), //This should never happen
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
TerminalView::from_connection(connection, modal, cx)
|
TerminalView::from_connection(connection, modal, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_connection(
|
fn from_connection(
|
||||||
connection: ModelHandle<TerminalConnection>,
|
connection: Result<ModelHandle<Terminal>, TerminalError>,
|
||||||
modal: bool,
|
modal: bool,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> TerminalView {
|
) -> TerminalView {
|
||||||
|
|
|
@ -24,7 +24,7 @@ impl<'a> TerminalTestContext<'a> {
|
||||||
);
|
);
|
||||||
|
|
||||||
let connection =
|
let connection =
|
||||||
cx.add_model(|cx| TerminalConnection::new(None, None, None, size_info, cx));
|
cx.add_model(|cx| TerminalConnection::new_tty(None, None, None, size_info, cx));
|
||||||
|
|
||||||
TerminalTestContext { cx, connection }
|
TerminalTestContext { cx, connection }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue