Merge branch 'main' into panels
This commit is contained in:
commit
146809eef0
183 changed files with 10202 additions and 5720 deletions
|
@ -16,7 +16,6 @@ use gpui::{
|
|||
use itertools::Itertools;
|
||||
use language::CursorShape;
|
||||
use ordered_float::OrderedFloat;
|
||||
use settings::Settings;
|
||||
use terminal::{
|
||||
alacritty_terminal::{
|
||||
ansi::{Color as AnsiColor, Color::Named, CursorShape as AlacCursorShape, NamedColor},
|
||||
|
@ -25,9 +24,9 @@ use terminal::{
|
|||
term::{cell::Flags, TermMode},
|
||||
},
|
||||
mappings::colors::convert_color,
|
||||
IndexedCell, Terminal, TerminalContent, TerminalSize,
|
||||
IndexedCell, Terminal, TerminalContent, TerminalSettings, TerminalSize,
|
||||
};
|
||||
use theme::TerminalStyle;
|
||||
use theme::{TerminalStyle, ThemeSettings};
|
||||
use util::ResultExt;
|
||||
|
||||
use std::{fmt::Debug, ops::RangeInclusive};
|
||||
|
@ -510,47 +509,6 @@ impl TerminalElement {
|
|||
|
||||
scene.push_mouse_region(region);
|
||||
}
|
||||
|
||||
///Configures a text style from the current settings.
|
||||
pub fn make_text_style(font_cache: &FontCache, settings: &Settings) -> TextStyle {
|
||||
let font_family_name = settings
|
||||
.terminal_overrides
|
||||
.font_family
|
||||
.as_ref()
|
||||
.or(settings.terminal_defaults.font_family.as_ref())
|
||||
.unwrap_or(&settings.buffer_font_family_name);
|
||||
let font_features = settings
|
||||
.terminal_overrides
|
||||
.font_features
|
||||
.as_ref()
|
||||
.or(settings.terminal_defaults.font_features.as_ref())
|
||||
.unwrap_or(&settings.buffer_font_features);
|
||||
|
||||
let family_id = font_cache
|
||||
.load_family(&[font_family_name], &font_features)
|
||||
.log_err()
|
||||
.unwrap_or(settings.buffer_font_family);
|
||||
|
||||
let font_size = settings
|
||||
.terminal_overrides
|
||||
.font_size
|
||||
.or(settings.terminal_defaults.font_size)
|
||||
.unwrap_or(settings.buffer_font_size);
|
||||
|
||||
let font_id = font_cache
|
||||
.select_font(family_id, &Default::default())
|
||||
.unwrap();
|
||||
|
||||
TextStyle {
|
||||
color: settings.theme.editor.text_color,
|
||||
font_family_id: family_id,
|
||||
font_family_name: font_cache.family_name(family_id).unwrap(),
|
||||
font_id,
|
||||
font_size,
|
||||
font_properties: Default::default(),
|
||||
underline: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Element<TerminalView> for TerminalElement {
|
||||
|
@ -563,20 +521,48 @@ impl Element<TerminalView> for TerminalElement {
|
|||
view: &mut TerminalView,
|
||||
cx: &mut LayoutContext<TerminalView>,
|
||||
) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
|
||||
let settings = cx.global::<Settings>();
|
||||
let font_cache = cx.font_cache();
|
||||
let settings = settings::get::<ThemeSettings>(cx);
|
||||
let terminal_settings = settings::get::<TerminalSettings>(cx);
|
||||
|
||||
//Setup layout information
|
||||
let terminal_theme = settings.theme.terminal.clone(); //TODO: Try to minimize this clone.
|
||||
let link_style = settings.theme.editor.link_definition;
|
||||
let tooltip_style = settings.theme.tooltip.clone();
|
||||
|
||||
let text_style = TerminalElement::make_text_style(font_cache, settings);
|
||||
let font_cache = cx.font_cache();
|
||||
let font_size = terminal_settings
|
||||
.font_size(cx)
|
||||
.unwrap_or(settings.buffer_font_size(cx));
|
||||
let font_family_name = terminal_settings
|
||||
.font_family
|
||||
.as_ref()
|
||||
.unwrap_or(&settings.buffer_font_family_name);
|
||||
let font_features = terminal_settings
|
||||
.font_features
|
||||
.as_ref()
|
||||
.unwrap_or(&settings.buffer_font_features);
|
||||
let family_id = font_cache
|
||||
.load_family(&[font_family_name], &font_features)
|
||||
.log_err()
|
||||
.unwrap_or(settings.buffer_font_family);
|
||||
let font_id = font_cache
|
||||
.select_font(family_id, &Default::default())
|
||||
.unwrap();
|
||||
|
||||
let text_style = TextStyle {
|
||||
color: settings.theme.editor.text_color,
|
||||
font_family_id: family_id,
|
||||
font_family_name: font_cache.family_name(family_id).unwrap(),
|
||||
font_id,
|
||||
font_size,
|
||||
font_properties: Default::default(),
|
||||
underline: Default::default(),
|
||||
};
|
||||
let selection_color = settings.theme.editor.selection.selection;
|
||||
let match_color = settings.theme.search.match_background;
|
||||
let gutter;
|
||||
let dimensions = {
|
||||
let line_height = text_style.font_size * settings.terminal_line_height();
|
||||
let line_height = text_style.font_size * terminal_settings.line_height.value();
|
||||
let cell_width = font_cache.em_advance(text_style.font_id, text_style.font_size);
|
||||
gutter = cell_width;
|
||||
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::TerminalView;
|
||||
use db::kvp::KEY_VALUE_STORE;
|
||||
use gpui::{
|
||||
actions, anyhow::Result, elements::*, serde_json, AppContext, AsyncAppContext, Entity,
|
||||
Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext,
|
||||
};
|
||||
use project::Fs;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::{settings_file::SettingsFile, Settings, TerminalDockPosition, WorkingDirectory};
|
||||
use settings::SettingsStore;
|
||||
use terminal::{TerminalDockPosition, TerminalSettings};
|
||||
use util::{ResultExt, TryFutureExt};
|
||||
use workspace::{
|
||||
dock::{DockPosition, Panel},
|
||||
|
@ -31,6 +35,7 @@ pub enum Event {
|
|||
|
||||
pub struct TerminalPanel {
|
||||
pane: ViewHandle<Pane>,
|
||||
fs: Arc<dyn Fs>,
|
||||
workspace: WeakViewHandle<Workspace>,
|
||||
pending_serialization: Task<Option<()>>,
|
||||
_subscriptions: Vec<Subscription>,
|
||||
|
@ -38,22 +43,13 @@ pub struct TerminalPanel {
|
|||
|
||||
impl TerminalPanel {
|
||||
pub fn new(workspace: &Workspace, cx: &mut ViewContext<Self>) -> Self {
|
||||
let mut old_dock_position = cx.global::<Settings>().terminal_overrides.dock;
|
||||
cx.observe_global::<Settings, _>(move |_, cx| {
|
||||
let new_dock_position = cx.global::<Settings>().terminal_overrides.dock;
|
||||
if new_dock_position != old_dock_position {
|
||||
old_dock_position = new_dock_position;
|
||||
cx.emit(Event::DockPositionChanged);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
let this = cx.weak_handle();
|
||||
let weak_self = cx.weak_handle();
|
||||
let pane = cx.add_view(|cx| {
|
||||
let window_id = cx.window_id();
|
||||
let mut pane = Pane::new(
|
||||
workspace.weak_handle(),
|
||||
workspace.app_state().background_actions,
|
||||
Default::default(),
|
||||
cx,
|
||||
);
|
||||
pane.set_can_split(false, cx);
|
||||
|
@ -65,7 +61,7 @@ impl TerminalPanel {
|
|||
})
|
||||
});
|
||||
pane.set_render_tab_bar_buttons(cx, move |_, cx| {
|
||||
let this = this.clone();
|
||||
let this = weak_self.clone();
|
||||
Pane::render_tab_bar_button(
|
||||
0,
|
||||
"icons/plus_12.svg",
|
||||
|
@ -89,12 +85,23 @@ impl TerminalPanel {
|
|||
cx.observe(&pane, |_, _, cx| cx.notify()),
|
||||
cx.subscribe(&pane, Self::handle_pane_event),
|
||||
];
|
||||
Self {
|
||||
let this = Self {
|
||||
pane,
|
||||
fs: workspace.app_state().fs.clone(),
|
||||
workspace: workspace.weak_handle(),
|
||||
pending_serialization: Task::ready(None),
|
||||
_subscriptions: subscriptions,
|
||||
}
|
||||
};
|
||||
let mut old_dock_position = this.position(cx);
|
||||
cx.observe_global::<SettingsStore, _>(move |this, cx| {
|
||||
let new_dock_position = this.position(cx);
|
||||
if new_dock_position != old_dock_position {
|
||||
old_dock_position = new_dock_position;
|
||||
cx.emit(Event::DockPositionChanged);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
this
|
||||
}
|
||||
|
||||
pub fn load(
|
||||
|
@ -187,12 +194,9 @@ impl TerminalPanel {
|
|||
cx.spawn(|this, mut cx| async move {
|
||||
let pane = this.read_with(&cx, |this, _| this.pane.clone())?;
|
||||
workspace.update(&mut cx, |workspace, cx| {
|
||||
let working_directory_strategy = cx
|
||||
.global::<Settings>()
|
||||
.terminal_overrides
|
||||
let working_directory_strategy = settings::get::<TerminalSettings>(cx)
|
||||
.working_directory
|
||||
.clone()
|
||||
.unwrap_or(WorkingDirectory::CurrentProjectDirectory);
|
||||
.clone();
|
||||
let working_directory =
|
||||
crate::get_working_directory(workspace, cx, working_directory_strategy);
|
||||
let window_id = cx.window_id();
|
||||
|
@ -262,17 +266,10 @@ impl View for TerminalPanel {
|
|||
|
||||
impl Panel for TerminalPanel {
|
||||
fn position(&self, cx: &WindowContext) -> DockPosition {
|
||||
let settings = cx.global::<Settings>();
|
||||
let dock = settings
|
||||
.terminal_overrides
|
||||
.dock
|
||||
.or(settings.terminal_defaults.dock)
|
||||
.unwrap()
|
||||
.into();
|
||||
match dock {
|
||||
settings::TerminalDockPosition::Left => DockPosition::Left,
|
||||
settings::TerminalDockPosition::Bottom => DockPosition::Bottom,
|
||||
settings::TerminalDockPosition::Right => DockPosition::Right,
|
||||
match settings::get::<TerminalSettings>(cx).dock {
|
||||
TerminalDockPosition::Left => DockPosition::Left,
|
||||
TerminalDockPosition::Bottom => DockPosition::Bottom,
|
||||
TerminalDockPosition::Right => DockPosition::Right,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -281,21 +278,21 @@ impl Panel for TerminalPanel {
|
|||
}
|
||||
|
||||
fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>) {
|
||||
SettingsFile::update(cx, move |settings| {
|
||||
settings::update_settings_file::<TerminalSettings>(self.fs.clone(), cx, move |settings| {
|
||||
let dock = match position {
|
||||
DockPosition::Left => TerminalDockPosition::Left,
|
||||
DockPosition::Bottom => TerminalDockPosition::Bottom,
|
||||
DockPosition::Right => TerminalDockPosition::Right,
|
||||
};
|
||||
settings.terminal.dock = Some(dock);
|
||||
settings.dock = Some(dock);
|
||||
});
|
||||
}
|
||||
|
||||
fn default_size(&self, cx: &WindowContext) -> f32 {
|
||||
let settings = &cx.global::<Settings>().terminal_overrides;
|
||||
let settings = settings::get::<TerminalSettings>(cx);
|
||||
match self.position(cx) {
|
||||
DockPosition::Left | DockPosition::Right => settings.default_width.unwrap_or(640.),
|
||||
DockPosition::Bottom => settings.default_height.unwrap_or(320.),
|
||||
DockPosition::Left | DockPosition::Right => settings.default_width,
|
||||
DockPosition::Bottom => settings.default_height,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ mod persistence;
|
|||
pub mod terminal_element;
|
||||
pub mod terminal_panel;
|
||||
|
||||
use crate::{persistence::TERMINAL_DB, terminal_element::TerminalElement};
|
||||
use context_menu::{ContextMenu, ContextMenuItem};
|
||||
use dirs::home_dir;
|
||||
use gpui::{
|
||||
|
@ -16,7 +17,6 @@ use gpui::{
|
|||
};
|
||||
use project::{LocalWorktree, Project};
|
||||
use serde::Deserialize;
|
||||
use settings::{Settings, TerminalBlink, WorkingDirectory};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use smol::Timer;
|
||||
use std::{
|
||||
|
@ -30,7 +30,7 @@ use terminal::{
|
|||
index::Point,
|
||||
term::{search::RegexSearch, TermMode},
|
||||
},
|
||||
Event, Terminal,
|
||||
Event, Terminal, TerminalBlink, WorkingDirectory,
|
||||
};
|
||||
use util::ResultExt;
|
||||
use workspace::{
|
||||
|
@ -41,7 +41,7 @@ use workspace::{
|
|||
Pane, ToolbarItemLocation, Workspace, WorkspaceId,
|
||||
};
|
||||
|
||||
use crate::{persistence::TERMINAL_DB, terminal_element::TerminalElement};
|
||||
pub use terminal::TerminalSettings;
|
||||
|
||||
const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
|
||||
|
||||
|
@ -64,6 +64,8 @@ impl_actions!(terminal, [SendText, SendKeystroke]);
|
|||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
terminal_panel::init(cx);
|
||||
terminal::init(cx);
|
||||
|
||||
cx.add_action(TerminalView::deploy);
|
||||
|
||||
register_deserializable_item::<TerminalView>(cx);
|
||||
|
@ -102,9 +104,9 @@ impl TerminalView {
|
|||
_: &workspace::NewTerminal,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) {
|
||||
let strategy = cx.global::<Settings>().terminal_strategy();
|
||||
|
||||
let working_directory = get_working_directory(workspace, cx, strategy);
|
||||
let strategy = settings::get::<TerminalSettings>(cx);
|
||||
let working_directory =
|
||||
get_working_directory(workspace, cx, strategy.working_directory.clone());
|
||||
|
||||
let window_id = cx.window_id();
|
||||
let terminal = workspace
|
||||
|
@ -216,10 +218,7 @@ impl TerminalView {
|
|||
self.terminal.update(cx, |term, cx| {
|
||||
term.try_keystroke(
|
||||
&Keystroke::parse("ctrl-cmd-space").unwrap(),
|
||||
cx.global::<Settings>()
|
||||
.terminal_overrides
|
||||
.option_as_meta
|
||||
.unwrap_or(false),
|
||||
settings::get::<TerminalSettings>(cx).option_as_meta,
|
||||
)
|
||||
});
|
||||
}
|
||||
|
@ -245,16 +244,7 @@ impl TerminalView {
|
|||
return true;
|
||||
}
|
||||
|
||||
let setting = {
|
||||
let settings = cx.global::<Settings>();
|
||||
settings
|
||||
.terminal_overrides
|
||||
.blinking
|
||||
.clone()
|
||||
.unwrap_or(TerminalBlink::TerminalControlled)
|
||||
};
|
||||
|
||||
match setting {
|
||||
match settings::get::<TerminalSettings>(cx).blinking {
|
||||
//If the user requested to never blink, don't blink it.
|
||||
TerminalBlink::Off => true,
|
||||
//If the terminal is controlling it, check terminal mode
|
||||
|
@ -347,10 +337,7 @@ impl TerminalView {
|
|||
self.terminal.update(cx, |term, cx| {
|
||||
term.try_keystroke(
|
||||
&keystroke,
|
||||
cx.global::<Settings>()
|
||||
.terminal_overrides
|
||||
.option_as_meta
|
||||
.unwrap_or(false),
|
||||
settings::get::<TerminalSettings>(cx).option_as_meta,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -413,10 +400,7 @@ impl View for TerminalView {
|
|||
self.terminal.update(cx, |term, cx| {
|
||||
term.try_keystroke(
|
||||
&event.keystroke,
|
||||
cx.global::<Settings>()
|
||||
.terminal_overrides
|
||||
.option_as_meta
|
||||
.unwrap_or(false),
|
||||
settings::get::<TerminalSettings>(cx).option_as_meta,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -618,7 +602,9 @@ impl Item for TerminalView {
|
|||
.flatten()
|
||||
.or_else(|| {
|
||||
cx.read(|cx| {
|
||||
let strategy = cx.global::<Settings>().terminal_strategy();
|
||||
let strategy = settings::get::<TerminalSettings>(cx)
|
||||
.working_directory
|
||||
.clone();
|
||||
workspace
|
||||
.upgrade(cx)
|
||||
.map(|workspace| {
|
||||
|
@ -802,22 +788,18 @@ fn get_path_from_wt(wt: &LocalWorktree) -> Option<PathBuf> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use gpui::TestAppContext;
|
||||
use project::{Entry, Project, ProjectPath, Worktree};
|
||||
use std::path::Path;
|
||||
use workspace::AppState;
|
||||
|
||||
use std::path::Path;
|
||||
// Working directory calculation tests
|
||||
|
||||
///Working directory calculation tests
|
||||
|
||||
///No Worktrees in project -> home_dir()
|
||||
// No Worktrees in project -> home_dir()
|
||||
#[gpui::test]
|
||||
async fn no_worktree(cx: &mut TestAppContext) {
|
||||
//Setup variables
|
||||
let (project, workspace) = blank_workspace(cx).await;
|
||||
//Test
|
||||
let (project, workspace) = init_test(cx).await;
|
||||
cx.read(|cx| {
|
||||
let workspace = workspace.read(cx);
|
||||
let active_entry = project.read(cx).active_entry();
|
||||
|
@ -833,14 +815,12 @@ mod tests {
|
|||
});
|
||||
}
|
||||
|
||||
///No active entry, but a worktree, worktree is a file -> home_dir()
|
||||
// No active entry, but a worktree, worktree is a file -> home_dir()
|
||||
#[gpui::test]
|
||||
async fn no_active_entry_worktree_is_file(cx: &mut TestAppContext) {
|
||||
//Setup variables
|
||||
let (project, workspace) = init_test(cx).await;
|
||||
|
||||
let (project, workspace) = blank_workspace(cx).await;
|
||||
create_file_wt(project.clone(), "/root.txt", cx).await;
|
||||
|
||||
cx.read(|cx| {
|
||||
let workspace = workspace.read(cx);
|
||||
let active_entry = project.read(cx).active_entry();
|
||||
|
@ -856,14 +836,12 @@ mod tests {
|
|||
});
|
||||
}
|
||||
|
||||
//No active entry, but a worktree, worktree is a folder -> worktree_folder
|
||||
// No active entry, but a worktree, worktree is a folder -> worktree_folder
|
||||
#[gpui::test]
|
||||
async fn no_active_entry_worktree_is_dir(cx: &mut TestAppContext) {
|
||||
//Setup variables
|
||||
let (project, workspace) = blank_workspace(cx).await;
|
||||
let (_wt, _entry) = create_folder_wt(project.clone(), "/root/", cx).await;
|
||||
let (project, workspace) = init_test(cx).await;
|
||||
|
||||
//Test
|
||||
let (_wt, _entry) = create_folder_wt(project.clone(), "/root/", cx).await;
|
||||
cx.update(|cx| {
|
||||
let workspace = workspace.read(cx);
|
||||
let active_entry = project.read(cx).active_entry();
|
||||
|
@ -878,17 +856,15 @@ mod tests {
|
|||
});
|
||||
}
|
||||
|
||||
//Active entry with a work tree, worktree is a file -> home_dir()
|
||||
// Active entry with a work tree, worktree is a file -> home_dir()
|
||||
#[gpui::test]
|
||||
async fn active_entry_worktree_is_file(cx: &mut TestAppContext) {
|
||||
//Setup variables
|
||||
let (project, workspace) = init_test(cx).await;
|
||||
|
||||
let (project, workspace) = blank_workspace(cx).await;
|
||||
let (_wt, _entry) = create_folder_wt(project.clone(), "/root1/", cx).await;
|
||||
let (wt2, entry2) = create_file_wt(project.clone(), "/root2.txt", cx).await;
|
||||
insert_active_entry_for(wt2, entry2, project.clone(), cx);
|
||||
|
||||
//Test
|
||||
cx.update(|cx| {
|
||||
let workspace = workspace.read(cx);
|
||||
let active_entry = project.read(cx).active_entry();
|
||||
|
@ -902,16 +878,15 @@ mod tests {
|
|||
});
|
||||
}
|
||||
|
||||
//Active entry, with a worktree, worktree is a folder -> worktree_folder
|
||||
// Active entry, with a worktree, worktree is a folder -> worktree_folder
|
||||
#[gpui::test]
|
||||
async fn active_entry_worktree_is_dir(cx: &mut TestAppContext) {
|
||||
//Setup variables
|
||||
let (project, workspace) = blank_workspace(cx).await;
|
||||
let (project, workspace) = init_test(cx).await;
|
||||
|
||||
let (_wt, _entry) = create_folder_wt(project.clone(), "/root1/", cx).await;
|
||||
let (wt2, entry2) = create_folder_wt(project.clone(), "/root2/", cx).await;
|
||||
insert_active_entry_for(wt2, entry2, project.clone(), cx);
|
||||
|
||||
//Test
|
||||
cx.update(|cx| {
|
||||
let workspace = workspace.read(cx);
|
||||
let active_entry = project.read(cx).active_entry();
|
||||
|
@ -925,11 +900,12 @@ mod tests {
|
|||
});
|
||||
}
|
||||
|
||||
///Creates a worktree with 1 file: /root.txt
|
||||
pub async fn blank_workspace(
|
||||
/// Creates a worktree with 1 file: /root.txt
|
||||
pub async fn init_test(
|
||||
cx: &mut TestAppContext,
|
||||
) -> (ModelHandle<Project>, ViewHandle<Workspace>) {
|
||||
let params = cx.update(AppState::test);
|
||||
cx.update(|cx| theme::init((), cx));
|
||||
|
||||
let project = Project::test(params.fs.clone(), [], cx).await;
|
||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
|
@ -937,7 +913,7 @@ mod tests {
|
|||
(project, workspace)
|
||||
}
|
||||
|
||||
///Creates a worktree with 1 folder: /root{suffix}/
|
||||
/// Creates a worktree with 1 folder: /root{suffix}/
|
||||
async fn create_folder_wt(
|
||||
project: ModelHandle<Project>,
|
||||
path: impl AsRef<Path>,
|
||||
|
@ -946,7 +922,7 @@ mod tests {
|
|||
create_wt(project, true, path, cx).await
|
||||
}
|
||||
|
||||
///Creates a worktree with 1 file: /root{suffix}.txt
|
||||
/// Creates a worktree with 1 file: /root{suffix}.txt
|
||||
async fn create_file_wt(
|
||||
project: ModelHandle<Project>,
|
||||
path: impl AsRef<Path>,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue