WIP

Mikayla Maki created

Change summary

crates/project/src/project.rs                       | 21 ++++
crates/settings/src/settings.rs                     | 42 ++++++----
crates/terminal/src/terminal.rs                     | 28 +++----
crates/terminal_view/Cargo.toml                     |  2 
crates/terminal_view/src/terminal_container_view.rs | 57 ++++----------
crates/terminal_view/src/terminal_view.rs           |  5 +
6 files changed, 76 insertions(+), 79 deletions(-)

Detailed changes

crates/project/src/project.rs 🔗

@@ -62,7 +62,7 @@ use std::{
     },
     time::Instant,
 };
-use terminal::Terminal;
+use terminal::{Terminal, TerminalBuilder};
 use thiserror::Error;
 use util::{defer, post_inc, ResultExt, TryFutureExt as _};
 
@@ -1196,14 +1196,29 @@ impl Project {
 
     pub fn create_terminal_connection(
         &mut self,
-        _cx: &mut ModelContext<Self>,
+        working_directory: Option<PathBuf>,
+        window_id: usize,
+        cx: &mut ModelContext<Self>,
     ) -> Result<ModelHandle<Terminal>> {
         if self.is_remote() {
             return Err(anyhow!(
                 "creating terminals as a guest is not supported yet"
             ));
         } else {
-            unimplemented!()
+            let settings = cx.global::<Settings>();
+            let shell = settings.terminal_shell();
+            let envs = settings.terminal_env();
+            let scroll = settings.terminal_scroll();
+
+            TerminalBuilder::new(
+                working_directory.clone(),
+                shell,
+                envs,
+                settings.terminal_overrides.blinking.clone(),
+                scroll,
+                window_id,
+            )
+            .map(|builder| cx.add_model(|cx| builder.subscribe(cx)))
         }
     }
 

crates/settings/src/settings.rs 🔗

@@ -221,6 +221,12 @@ pub enum WorkingDirectory {
     Always { directory: String },
 }
 
+impl Default for WorkingDirectory {
+    fn default() -> Self {
+        Self::CurrentProjectDirectory
+    }
+}
+
 #[derive(PartialEq, Eq, Debug, Default, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema)]
 #[serde(rename_all = "snake_case")]
 pub enum DockAnchor {
@@ -473,30 +479,30 @@ impl Settings {
         })
     }
 
+    fn terminal_setting<F, R: Default + Clone>(&self, f: F) -> R
+    where
+        F: Fn(&TerminalSettings) -> Option<&R>,
+    {
+        f(&self.terminal_overrides)
+            .or_else(|| f(&self.terminal_defaults))
+            .cloned()
+            .unwrap_or_else(|| R::default())
+    }
+
     pub fn terminal_scroll(&self) -> AlternateScroll {
-        *self.terminal_overrides.alternate_scroll.as_ref().unwrap_or(
-            self.terminal_defaults
-                .alternate_scroll
-                .as_ref()
-                .unwrap_or_else(|| &AlternateScroll::On),
-        )
+        self.terminal_setting(|terminal_setting| terminal_setting.alternate_scroll.as_ref())
     }
 
-    pub fn terminal_shell(&self) -> Option<Shell> {
-        self.terminal_overrides
-            .shell
-            .as_ref()
-            .or(self.terminal_defaults.shell.as_ref())
-            .cloned()
+    pub fn terminal_shell(&self) -> Shell {
+        self.terminal_setting(|terminal_setting| terminal_setting.shell.as_ref())
     }
 
     pub fn terminal_env(&self) -> HashMap<String, String> {
-        self.terminal_overrides.env.clone().unwrap_or_else(|| {
-            self.terminal_defaults
-                .env
-                .clone()
-                .unwrap_or_else(|| HashMap::default())
-        })
+        self.terminal_setting(|terminal_setting| terminal_setting.env.as_ref())
+    }
+
+    pub fn terminal_strategy(&self) -> WorkingDirectory {
+        self.terminal_setting(|terminal_setting| terminal_setting.working_directory.as_ref())
     }
 
     #[cfg(any(test, feature = "test-support"))]

crates/terminal/src/terminal.rs 🔗

@@ -198,7 +198,7 @@ impl Dimensions for TerminalSize {
 #[derive(Error, Debug)]
 pub struct TerminalError {
     pub directory: Option<PathBuf>,
-    pub shell: Option<Shell>,
+    pub shell: Shell,
     pub source: std::io::Error,
 }
 
@@ -226,24 +226,20 @@ impl TerminalError {
             })
     }
 
-    pub fn shell_to_string(&self) -> Option<String> {
-        self.shell.as_ref().map(|shell| match shell {
+    pub fn shell_to_string(&self) -> String {
+        match &self.shell {
             Shell::System => "<system shell>".to_string(),
             Shell::Program(p) => p.to_string(),
             Shell::WithArguments { program, args } => format!("{} {}", program, args.join(" ")),
-        })
+        }
     }
 
     pub fn fmt_shell(&self) -> String {
-        self.shell
-            .clone()
-            .map(|shell| match shell {
-                Shell::System => "<system defined shell>".to_string(),
-
-                Shell::Program(s) => s,
-                Shell::WithArguments { program, args } => format!("{} {}", program, args.join(" ")),
-            })
-            .unwrap_or_else(|| "<none specified, using system defined shell>".to_string())
+        match &self.shell {
+            Shell::System => "<system defined shell>".to_string(),
+            Shell::Program(s) => s.to_string(),
+            Shell::WithArguments { program, args } => format!("{} {}", program, args.join(" ")),
+        }
     }
 }
 
@@ -268,18 +264,18 @@ pub struct TerminalBuilder {
 impl TerminalBuilder {
     pub fn new(
         working_directory: Option<PathBuf>,
-        shell: Option<Shell>,
+        shell: Shell,
         mut env: HashMap<String, String>,
         blink_settings: Option<TerminalBlink>,
         alternate_scroll: AlternateScroll,
         window_id: usize,
     ) -> Result<TerminalBuilder> {
         let pty_config = {
-            let alac_shell = shell.clone().and_then(|shell| match shell {
+            let alac_shell = match shell.clone() {
                 Shell::System => None,
                 Shell::Program(program) => Some(Program::Just(program)),
                 Shell::WithArguments { program, args } => Some(Program::WithArgs { program, args }),
-            });
+            };
 
             PtyConfig {
                 shell: alac_shell,

crates/terminal_view/Cargo.toml 🔗

@@ -4,7 +4,7 @@ version = "0.1.0"
 edition = "2021"
 
 [lib]
-path = "src/terminal_container_view.rs"
+path = "src/terminal_view.rs"
 doctest = false
 
 [dependencies]

crates/terminal_view/src/terminal_container_view.rs 🔗

@@ -1,18 +1,14 @@
-mod persistence;
-pub mod terminal_element;
-pub mod terminal_view;
-
 use crate::persistence::TERMINAL_DB;
-use crate::terminal_view::TerminalView;
+use crate::TerminalView;
 use terminal::alacritty_terminal::index::Point;
-use terminal::{Event, TerminalBuilder, TerminalError};
+use terminal::{Event, Terminal, TerminalError};
 
+use crate::regex_search_for_query;
 use dirs::home_dir;
 use gpui::{
     actions, elements::*, AnyViewHandle, AppContext, Entity, ModelHandle, MutableAppContext, Task,
     View, ViewContext, ViewHandle, WeakViewHandle,
 };
-use terminal_view::regex_search_for_query;
 use util::{truncate_and_trailoff, ResultExt};
 use workspace::searchable::{SearchEvent, SearchOptions, SearchableItem, SearchableItemHandle};
 use workspace::{
@@ -36,7 +32,7 @@ pub fn init(cx: &mut MutableAppContext) {
 
     register_deserializable_item::<TerminalContainer>(cx);
 
-    terminal_view::init(cx);
+    // terminal_view::init(cx);
 }
 
 //Make terminal view an enum, that can give you views for the error and non-error states
@@ -81,47 +77,31 @@ impl TerminalContainer {
         _: &workspace::NewTerminal,
         cx: &mut ViewContext<Workspace>,
     ) {
-        let strategy = cx
-            .global::<Settings>()
-            .terminal_overrides
-            .working_directory
-            .clone()
-            .unwrap_or(WorkingDirectory::CurrentProjectDirectory);
+        let strategy = cx.global::<Settings>().terminal_strategy();
 
         let working_directory = get_working_directory(workspace, cx, strategy);
-        let view = cx.add_view(|cx| {
-            TerminalContainer::new(working_directory, false, workspace.database_id(), cx)
+
+        let window_id = cx.window_id();
+        let terminal = workspace.project().update(cx, |project, cx| {
+            project.create_terminal_connection(working_directory, window_id, cx)
         });
+
+        let view = cx.add_view(|cx| TerminalContainer::new(terminal, workspace.database_id(), cx));
         workspace.add_item(Box::new(view), cx);
     }
 
     ///Create a new Terminal view. This spawns a task, a thread, and opens the TTY devices    
     pub fn new(
-        working_directory: Option<PathBuf>,
-        modal: bool,
+        model: anyhow::Result<ModelHandle<Terminal>>,
         workspace_id: WorkspaceId,
         cx: &mut ViewContext<Self>,
     ) -> Self {
-        let settings = cx.global::<Settings>();
-        let shell = settings.terminal_shell();
-        let envs = settings.terminal_env();
-        let scroll = settings.terminal_scroll();
-
-        let content = match TerminalBuilder::new(
-            working_directory.clone(),
-            shell,
-            envs,
-            settings.terminal_overrides.blinking.clone(),
-            scroll,
-            cx.window_id(),
-        ) {
+        let content = match model {
             Ok(terminal) => {
-                let terminal = cx.add_model(|cx| terminal.subscribe(cx));
                 let item_id = cx.view_id();
                 let view = cx.add_view(|cx| {
-                    TerminalView::from_terminal(terminal, modal, workspace_id, item_id, cx)
+                    TerminalView::from_terminal(terminal, false, workspace_id, item_id, cx)
                 });
-
                 cx.subscribe(&view, |_this, _content, event, cx| cx.emit(*event))
                     .detach();
                 TerminalContainerContent::Connected(view)
@@ -136,7 +116,7 @@ impl TerminalContainer {
 
         TerminalContainer {
             content,
-            associated_directory: working_directory,
+            associated_directory: None, //working_directory,
         }
     }
 
@@ -183,12 +163,7 @@ impl View for ErrorView {
         //We want to be able to select the text
         //Want to be able to scroll if the error message is massive somehow (resiliency)
 
-        let program_text = {
-            match self.error.shell_to_string() {
-                Some(shell_txt) => format!("Shell Program: `{}`", shell_txt),
-                None => "No program specified".to_string(),
-            }
-        };
+        let program_text = format!("Shell Program: `{}`", self.error.shell_to_string());
 
         let directory_text = {
             match self.error.directory.as_ref() {

crates/terminal_view/src/terminal_view.rs 🔗

@@ -1,3 +1,7 @@
+mod persistence;
+pub mod terminal_container_view;
+pub mod terminal_element;
+
 use std::{ops::RangeInclusive, time::Duration};
 
 use context_menu::{ContextMenu, ContextMenuItem};
@@ -52,6 +56,7 @@ impl_actions!(terminal, [SendText, SendKeystroke]);
 impl_internal_actions!(project_panel, [DeployContextMenu]);
 
 pub fn init(cx: &mut MutableAppContext) {
+    terminal_container_view::init(cx);
     //Useful terminal views
     cx.add_action(TerminalView::send_text);
     cx.add_action(TerminalView::send_keystroke);