mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-07 20:39:04 +03:00
Added settings for common terminal configurations
This commit is contained in:
parent
024011a571
commit
dc120c1e05
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -5365,6 +5365,7 @@ dependencies = [
|
|||||||
"ordered-float",
|
"ordered-float",
|
||||||
"project",
|
"project",
|
||||||
"settings",
|
"settings",
|
||||||
|
"shellexpand",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"theme",
|
"theme",
|
||||||
"util",
|
"util",
|
||||||
|
@ -54,7 +54,7 @@
|
|||||||
// "soft_wrap": "none",
|
// "soft_wrap": "none",
|
||||||
// 2. Soft wrap lines that overflow the editor:
|
// 2. Soft wrap lines that overflow the editor:
|
||||||
// "soft_wrap": "editor_width",
|
// "soft_wrap": "editor_width",
|
||||||
// 2. Soft wrap lines at the preferred line length
|
// 3. Soft wrap lines at the preferred line length
|
||||||
// "soft_wrap": "preferred_line_length",
|
// "soft_wrap": "preferred_line_length",
|
||||||
"soft_wrap": "none",
|
"soft_wrap": "none",
|
||||||
// The column at which to soft-wrap lines, for buffers where soft-wrap
|
// The column at which to soft-wrap lines, for buffers where soft-wrap
|
||||||
@ -65,6 +65,55 @@
|
|||||||
"hard_tabs": false,
|
"hard_tabs": false,
|
||||||
// How many columns a tab should occupy.
|
// How many columns a tab should occupy.
|
||||||
"tab_size": 4,
|
"tab_size": 4,
|
||||||
|
// Settings specific to the terminal
|
||||||
|
"terminal": {
|
||||||
|
// What shell to use when opening a terminal. May take 3 values:
|
||||||
|
// 1. Use the system's terminal configuration (e.g. $TERM). Note this is not
|
||||||
|
// A substitution, only an exact text match
|
||||||
|
// "shell": "system"
|
||||||
|
// 2. A program:
|
||||||
|
// "shell": {
|
||||||
|
// "program": "sh"
|
||||||
|
// }
|
||||||
|
// 3. A program with arguments:
|
||||||
|
// "shell": {
|
||||||
|
// "with_arguments": {
|
||||||
|
// "program": "/bin/bash",
|
||||||
|
// "arguments": ["--login"]
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
"shell": "system",
|
||||||
|
// What working directory to use when launching the terminal.
|
||||||
|
// May take 4 values:
|
||||||
|
// 1. Use the current file's project directory.
|
||||||
|
// "working_directory": "current_project_directory"
|
||||||
|
// 2. Use the first project in this workspace's directory
|
||||||
|
// "working_directory": "first_project_directory"
|
||||||
|
// 3. Always use this platform's home directory (if we can find it)
|
||||||
|
// "working_directory": "always_home"
|
||||||
|
// 4. Always use a specific directory. This value will be shell expanded.
|
||||||
|
// If this path is not a valid directory the terminal will default to
|
||||||
|
// this platform's home directory (if we can find it)
|
||||||
|
// "working_directory": {
|
||||||
|
// "always": {
|
||||||
|
// "directory": "~/zed/projects/"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
"working_directory": "current_project_directory",
|
||||||
|
//Any key-value pairs added to this list will be added to the terminal's
|
||||||
|
//enviroment. Use `:` to seperate multiple values, not multiple list items
|
||||||
|
"env": [
|
||||||
|
//["KEY", "value1:value2"]
|
||||||
|
]
|
||||||
|
//Set the terminal's font size. If this option is not listed,
|
||||||
|
//the terminal will default to matching the buffer's font size.
|
||||||
|
//"font_size": "15"
|
||||||
|
//Set the terminal's font family. If this option is not listed,
|
||||||
|
//the terminal will default to matching the buffer's font family.
|
||||||
|
//"font_family": "Zed Mono"
|
||||||
|
},
|
||||||
// Different settings for specific languages.
|
// Different settings for specific languages.
|
||||||
"languages": {
|
"languages": {
|
||||||
"Plain Text": {
|
"Plain Text": {
|
||||||
|
@ -22,14 +22,16 @@ pub use keymap_file::{keymap_file_json_schema, KeymapFileContent};
|
|||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub projects_online_by_default: bool,
|
pub projects_online_by_default: bool,
|
||||||
pub buffer_font_family: FamilyId,
|
pub buffer_font_family: FamilyId,
|
||||||
pub buffer_font_size: f32,
|
|
||||||
pub default_buffer_font_size: f32,
|
pub default_buffer_font_size: f32,
|
||||||
|
pub buffer_font_size: f32,
|
||||||
pub hover_popover_enabled: bool,
|
pub hover_popover_enabled: bool,
|
||||||
pub show_completions_on_input: bool,
|
pub show_completions_on_input: bool,
|
||||||
pub vim_mode: bool,
|
pub vim_mode: bool,
|
||||||
pub autosave: Autosave,
|
pub autosave: Autosave,
|
||||||
pub editor_defaults: EditorSettings,
|
pub editor_defaults: EditorSettings,
|
||||||
pub editor_overrides: EditorSettings,
|
pub editor_overrides: EditorSettings,
|
||||||
|
pub terminal_defaults: TerminalSettings,
|
||||||
|
pub terminal_overrides: TerminalSettings,
|
||||||
pub language_defaults: HashMap<Arc<str>, EditorSettings>,
|
pub language_defaults: HashMap<Arc<str>, EditorSettings>,
|
||||||
pub language_overrides: HashMap<Arc<str>, EditorSettings>,
|
pub language_overrides: HashMap<Arc<str>, EditorSettings>,
|
||||||
pub theme: Arc<Theme>,
|
pub theme: Arc<Theme>,
|
||||||
@ -73,6 +75,38 @@ pub enum Autosave {
|
|||||||
OnWindowChange,
|
OnWindowChange,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Deserialize, JsonSchema)]
|
||||||
|
pub struct TerminalSettings {
|
||||||
|
pub shell: Option<Shell>,
|
||||||
|
pub working_directory: Option<WorkingDirectory>,
|
||||||
|
pub font_size: Option<f32>,
|
||||||
|
pub font_family: Option<String>,
|
||||||
|
pub env: Option<Vec<(String, String)>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum Shell {
|
||||||
|
System,
|
||||||
|
Program(String),
|
||||||
|
WithArguments { program: String, args: Vec<String> },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Shell {
|
||||||
|
fn default() -> Self {
|
||||||
|
Shell::System
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum WorkingDirectory {
|
||||||
|
CurrentProjectDirectory,
|
||||||
|
FirstProjectDirectory,
|
||||||
|
AlwaysHome,
|
||||||
|
Always { directory: String },
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, Default, Deserialize, JsonSchema)]
|
||||||
pub struct SettingsFileContent {
|
pub struct SettingsFileContent {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@ -92,6 +126,8 @@ pub struct SettingsFileContent {
|
|||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub editor: EditorSettings,
|
pub editor: EditorSettings,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
pub terminal: TerminalSettings,
|
||||||
|
#[serde(default)]
|
||||||
#[serde(alias = "language_overrides")]
|
#[serde(alias = "language_overrides")]
|
||||||
pub languages: HashMap<Arc<str>, EditorSettings>,
|
pub languages: HashMap<Arc<str>, EditorSettings>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@ -133,8 +169,10 @@ impl Settings {
|
|||||||
format_on_save: required(defaults.editor.format_on_save),
|
format_on_save: required(defaults.editor.format_on_save),
|
||||||
enable_language_server: required(defaults.editor.enable_language_server),
|
enable_language_server: required(defaults.editor.enable_language_server),
|
||||||
},
|
},
|
||||||
language_defaults: defaults.languages,
|
|
||||||
editor_overrides: Default::default(),
|
editor_overrides: Default::default(),
|
||||||
|
terminal_defaults: Default::default(),
|
||||||
|
terminal_overrides: Default::default(),
|
||||||
|
language_defaults: defaults.languages,
|
||||||
language_overrides: Default::default(),
|
language_overrides: Default::default(),
|
||||||
theme: themes.get(&defaults.theme.unwrap()).unwrap(),
|
theme: themes.get(&defaults.theme.unwrap()).unwrap(),
|
||||||
}
|
}
|
||||||
@ -171,7 +209,14 @@ impl Settings {
|
|||||||
merge(&mut self.vim_mode, data.vim_mode);
|
merge(&mut self.vim_mode, data.vim_mode);
|
||||||
merge(&mut self.autosave, data.autosave);
|
merge(&mut self.autosave, data.autosave);
|
||||||
|
|
||||||
|
// Ensure terminal font is loaded, so we can request it in terminal_element layout
|
||||||
|
if let Some(terminal_font) = &data.terminal.font_family {
|
||||||
|
font_cache.load_family(&[terminal_font]).log_err();
|
||||||
|
}
|
||||||
|
|
||||||
self.editor_overrides = data.editor;
|
self.editor_overrides = data.editor;
|
||||||
|
self.terminal_defaults.font_size = data.terminal.font_size;
|
||||||
|
self.terminal_overrides = data.terminal;
|
||||||
self.language_overrides = data.languages;
|
self.language_overrides = data.languages;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,6 +284,8 @@ impl Settings {
|
|||||||
enable_language_server: Some(true),
|
enable_language_server: Some(true),
|
||||||
},
|
},
|
||||||
editor_overrides: Default::default(),
|
editor_overrides: Default::default(),
|
||||||
|
terminal_defaults: Default::default(),
|
||||||
|
terminal_overrides: Default::default(),
|
||||||
language_defaults: Default::default(),
|
language_defaults: Default::default(),
|
||||||
language_overrides: Default::default(),
|
language_overrides: Default::default(),
|
||||||
projects_online_by_default: true,
|
projects_online_by_default: true,
|
||||||
|
@ -22,6 +22,7 @@ futures = "0.3"
|
|||||||
ordered-float = "2.1.1"
|
ordered-float = "2.1.1"
|
||||||
itertools = "0.10"
|
itertools = "0.10"
|
||||||
dirs = "4.0.0"
|
dirs = "4.0.0"
|
||||||
|
shellexpand = "2.1.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
gpui = { path = "../gpui", features = ["test-support"] }
|
gpui = { path = "../gpui", features = ["test-support"] }
|
||||||
|
@ -2,7 +2,7 @@ mod keymappings;
|
|||||||
|
|
||||||
use alacritty_terminal::{
|
use alacritty_terminal::{
|
||||||
ansi::{ClearMode, Handler},
|
ansi::{ClearMode, Handler},
|
||||||
config::{Config, PtyConfig},
|
config::{Config, Program, PtyConfig},
|
||||||
event::{Event as AlacTermEvent, Notify},
|
event::{Event as AlacTermEvent, Notify},
|
||||||
event_loop::{EventLoop, Msg, Notifier},
|
event_loop::{EventLoop, Msg, Notifier},
|
||||||
grid::Scroll,
|
grid::Scroll,
|
||||||
@ -12,7 +12,7 @@ use alacritty_terminal::{
|
|||||||
Term,
|
Term,
|
||||||
};
|
};
|
||||||
use futures::{channel::mpsc::unbounded, StreamExt};
|
use futures::{channel::mpsc::unbounded, StreamExt};
|
||||||
use settings::Settings;
|
use settings::{Settings, Shell};
|
||||||
use std::{collections::HashMap, path::PathBuf, sync::Arc};
|
use std::{collections::HashMap, path::PathBuf, sync::Arc};
|
||||||
|
|
||||||
use gpui::{keymap::Keystroke, ClipboardItem, CursorStyle, Entity, ModelContext};
|
use gpui::{keymap::Keystroke, ClipboardItem, CursorStyle, Entity, ModelContext};
|
||||||
@ -46,16 +46,32 @@ pub struct TerminalConnection {
|
|||||||
impl TerminalConnection {
|
impl TerminalConnection {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
working_directory: Option<PathBuf>,
|
working_directory: Option<PathBuf>,
|
||||||
|
shell: Option<Shell>,
|
||||||
|
env_vars: Option<Vec<(String, String)>>,
|
||||||
initial_size: SizeInfo,
|
initial_size: SizeInfo,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> TerminalConnection {
|
) -> TerminalConnection {
|
||||||
let pty_config = PtyConfig {
|
let pty_config = {
|
||||||
shell: None, //Use the users default shell
|
let shell = shell.and_then(|shell| match shell {
|
||||||
working_directory: working_directory.clone(),
|
Shell::System => None,
|
||||||
hold: false,
|
Shell::Program(program) => Some(Program::Just(program)),
|
||||||
|
Shell::WithArguments { program, args } => Some(Program::WithArgs { program, args }),
|
||||||
|
});
|
||||||
|
|
||||||
|
PtyConfig {
|
||||||
|
shell,
|
||||||
|
working_directory: working_directory.clone(),
|
||||||
|
hold: false,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut env: HashMap<String, String> = HashMap::new();
|
let mut env: HashMap<String, String> = HashMap::new();
|
||||||
|
if let Some(envs) = env_vars {
|
||||||
|
for (var, val) in envs {
|
||||||
|
env.insert(var, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//TODO: Properly set the current locale,
|
//TODO: Properly set the current locale,
|
||||||
env.insert("LC_ALL".to_string(), "en_US.UTF-8".to_string());
|
env.insert("LC_ALL".to_string(), "en_US.UTF-8".to_string());
|
||||||
|
|
||||||
|
@ -18,10 +18,10 @@ use gpui::{
|
|||||||
};
|
};
|
||||||
use modal::deploy_modal;
|
use modal::deploy_modal;
|
||||||
|
|
||||||
use project::{Project, ProjectPath};
|
use project::{LocalWorktree, Project, ProjectPath};
|
||||||
use settings::Settings;
|
use settings::{Settings, WorkingDirectory};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::path::PathBuf;
|
use std::path::{Path, PathBuf};
|
||||||
use workspace::{Item, Workspace};
|
use workspace::{Item, Workspace};
|
||||||
|
|
||||||
use crate::terminal_element::TerminalEl;
|
use crate::terminal_element::TerminalEl;
|
||||||
@ -110,8 +110,15 @@ impl Terminal {
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
let connection =
|
let (shell, envs) = {
|
||||||
cx.add_model(|cx| TerminalConnection::new(working_directory, size_info, cx));
|
let settings = cx.global::<Settings>();
|
||||||
|
let shell = settings.terminal_overrides.shell.clone();
|
||||||
|
let envs = settings.terminal_overrides.env.clone(); //Should be short and cheap.
|
||||||
|
(shell, envs)
|
||||||
|
};
|
||||||
|
|
||||||
|
let connection = cx
|
||||||
|
.add_model(|cx| TerminalConnection::new(working_directory, shell, envs, size_info, cx));
|
||||||
|
|
||||||
Terminal::from_connection(connection, modal, cx)
|
Terminal::from_connection(connection, modal, cx)
|
||||||
}
|
}
|
||||||
@ -371,12 +378,41 @@ impl Item for Terminal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///Get's the working directory for the given workspace, respecting the user's settings.
|
||||||
|
fn get_wd_for_workspace(workspace: &Workspace, cx: &AppContext) -> Option<PathBuf> {
|
||||||
|
let wd_setting = cx
|
||||||
|
.global::<Settings>()
|
||||||
|
.terminal_overrides
|
||||||
|
.working_directory
|
||||||
|
.clone()
|
||||||
|
.unwrap_or(WorkingDirectory::CurrentProjectDirectory);
|
||||||
|
let res = match wd_setting {
|
||||||
|
WorkingDirectory::CurrentProjectDirectory => current_project_directory(workspace, cx),
|
||||||
|
WorkingDirectory::FirstProjectDirectory => first_project_directory(workspace, cx),
|
||||||
|
WorkingDirectory::AlwaysHome => None,
|
||||||
|
WorkingDirectory::Always { directory } => shellexpand::full(&directory)
|
||||||
|
.ok()
|
||||||
|
.map(|dir| Path::new(&dir.to_string()).to_path_buf())
|
||||||
|
.filter(|dir| dir.is_dir()),
|
||||||
|
};
|
||||||
|
res.or_else(|| home_dir())
|
||||||
|
}
|
||||||
|
|
||||||
|
///Get's the first project's home directory, or the home directory
|
||||||
|
fn first_project_directory(workspace: &Workspace, cx: &AppContext) -> Option<PathBuf> {
|
||||||
|
workspace
|
||||||
|
.worktrees(cx)
|
||||||
|
.next()
|
||||||
|
.and_then(|worktree_handle| worktree_handle.read(cx).as_local())
|
||||||
|
.and_then(get_path_from_wt)
|
||||||
|
}
|
||||||
|
|
||||||
///Gets the intuitively correct working directory from the given workspace
|
///Gets the intuitively correct working directory from the given workspace
|
||||||
///If there is an active entry for this project, returns that entry's worktree root.
|
///If there is an active entry for this project, returns that entry's worktree root.
|
||||||
///If there's no active entry but there is a worktree, returns that worktrees root.
|
///If there's no active entry but there is a worktree, returns that worktrees root.
|
||||||
///If either of these roots are files, or if there are any other query failures,
|
///If either of these roots are files, or if there are any other query failures,
|
||||||
/// returns the user's home directory
|
/// returns the user's home directory
|
||||||
fn get_wd_for_workspace(workspace: &Workspace, cx: &AppContext) -> Option<PathBuf> {
|
fn current_project_directory(workspace: &Workspace, cx: &AppContext) -> Option<PathBuf> {
|
||||||
let project = workspace.project().read(cx);
|
let project = workspace.project().read(cx);
|
||||||
|
|
||||||
project
|
project
|
||||||
@ -384,12 +420,13 @@ fn get_wd_for_workspace(workspace: &Workspace, cx: &AppContext) -> Option<PathBu
|
|||||||
.and_then(|entry_id| project.worktree_for_entry(entry_id, cx))
|
.and_then(|entry_id| project.worktree_for_entry(entry_id, cx))
|
||||||
.or_else(|| workspace.worktrees(cx).next())
|
.or_else(|| workspace.worktrees(cx).next())
|
||||||
.and_then(|worktree_handle| worktree_handle.read(cx).as_local())
|
.and_then(|worktree_handle| worktree_handle.read(cx).as_local())
|
||||||
.and_then(|wt| {
|
.and_then(get_path_from_wt)
|
||||||
wt.root_entry()
|
}
|
||||||
.filter(|re| re.is_dir())
|
|
||||||
.map(|_| wt.abs_path().to_path_buf())
|
fn get_path_from_wt(wt: &LocalWorktree) -> Option<PathBuf> {
|
||||||
})
|
wt.root_entry()
|
||||||
.or_else(|| home_dir())
|
.filter(|re| re.is_dir())
|
||||||
|
.map(|_| wt.abs_path().to_path_buf())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -398,10 +435,6 @@ mod tests {
|
|||||||
use crate::tests::terminal_test_context::TerminalTestContext;
|
use crate::tests::terminal_test_context::TerminalTestContext;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use alacritty_terminal::{
|
|
||||||
index::{Column, Line, Point, Side},
|
|
||||||
selection::{Selection, SelectionType},
|
|
||||||
};
|
|
||||||
use gpui::TestAppContext;
|
use gpui::TestAppContext;
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
@ -419,37 +452,6 @@ mod tests {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO: I don't think this is actually testing anything anymore.
|
|
||||||
///Integration test for selections, clipboard, and terminal execution
|
|
||||||
#[gpui::test(retries = 5)]
|
|
||||||
async fn test_copy(cx: &mut TestAppContext) {
|
|
||||||
|
|
||||||
let mut cx = TerminalTestContext::new(cx);
|
|
||||||
let grid_content = cx
|
|
||||||
.execute_and_wait("expr 3 + 4", |content, _cx| content.contains("7"))
|
|
||||||
.await;
|
|
||||||
|
|
||||||
//Get the position of the result
|
|
||||||
let idx = grid_content.chars().position(|c| c == '7').unwrap();
|
|
||||||
let result_line = grid_content
|
|
||||||
.chars()
|
|
||||||
.take(idx)
|
|
||||||
.filter(|c| *c == '\n')
|
|
||||||
.count() as i32;
|
|
||||||
|
|
||||||
let copy_res = cx.update_connection(|connection, _cx| {
|
|
||||||
let mut term = connection.term.lock();
|
|
||||||
term.selection = Some(Selection::new(
|
|
||||||
SelectionType::Semantic,
|
|
||||||
Point::new(Line(result_line), Column(0)),
|
|
||||||
Side::Right,
|
|
||||||
));
|
|
||||||
term.selection_to_string()
|
|
||||||
});
|
|
||||||
|
|
||||||
assert_eq!(copy_res.unwrap(), "7");
|
|
||||||
}
|
|
||||||
|
|
||||||
///Working directory calculation tests
|
///Working directory calculation tests
|
||||||
|
|
||||||
///No Worktrees in project -> home_dir()
|
///No Worktrees in project -> home_dir()
|
||||||
@ -469,8 +471,10 @@ mod tests {
|
|||||||
assert!(active_entry.is_none());
|
assert!(active_entry.is_none());
|
||||||
assert!(workspace.worktrees(cx).next().is_none());
|
assert!(workspace.worktrees(cx).next().is_none());
|
||||||
|
|
||||||
let res = get_wd_for_workspace(workspace, cx);
|
let res = current_project_directory(workspace, cx);
|
||||||
assert_eq!(res, home_dir())
|
assert_eq!(res, None);
|
||||||
|
let res = first_project_directory(workspace, cx);
|
||||||
|
assert_eq!(res, None);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -507,8 +511,10 @@ mod tests {
|
|||||||
assert!(active_entry.is_none());
|
assert!(active_entry.is_none());
|
||||||
assert!(workspace.worktrees(cx).next().is_some());
|
assert!(workspace.worktrees(cx).next().is_some());
|
||||||
|
|
||||||
let res = get_wd_for_workspace(workspace, cx);
|
let res = current_project_directory(workspace, cx);
|
||||||
assert_eq!(res, home_dir())
|
assert_eq!(res, None);
|
||||||
|
let res = first_project_directory(workspace, cx);
|
||||||
|
assert_eq!(res, None);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -543,7 +549,9 @@ mod tests {
|
|||||||
assert!(active_entry.is_none());
|
assert!(active_entry.is_none());
|
||||||
assert!(workspace.worktrees(cx).next().is_some());
|
assert!(workspace.worktrees(cx).next().is_some());
|
||||||
|
|
||||||
let res = get_wd_for_workspace(workspace, cx);
|
let res = current_project_directory(workspace, cx);
|
||||||
|
assert_eq!(res, Some((Path::new("/root/")).to_path_buf()));
|
||||||
|
let res = first_project_directory(workspace, cx);
|
||||||
assert_eq!(res, Some((Path::new("/root/")).to_path_buf()));
|
assert_eq!(res, Some((Path::new("/root/")).to_path_buf()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -555,17 +563,32 @@ mod tests {
|
|||||||
let params = cx.update(AppState::test);
|
let params = cx.update(AppState::test);
|
||||||
let project = Project::test(params.fs.clone(), [], cx).await;
|
let project = Project::test(params.fs.clone(), [], cx).await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
|
let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
|
||||||
let (wt, _) = project
|
let (wt1, _) = project
|
||||||
.update(cx, |project, cx| {
|
.update(cx, |project, cx| {
|
||||||
project.find_or_create_local_worktree("/root.txt", true, cx)
|
project.find_or_create_local_worktree("/root1/", true, cx)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (wt2, _) = project
|
||||||
|
.update(cx, |project, cx| {
|
||||||
|
project.find_or_create_local_worktree("/root2.txt", true, cx)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
//Setup root
|
//Setup root
|
||||||
let entry = cx
|
let _ = cx
|
||||||
.update(|cx| {
|
.update(|cx| {
|
||||||
wt.update(cx, |wt, cx| {
|
wt1.update(cx, |wt, cx| {
|
||||||
|
wt.as_local().unwrap().create_entry(Path::new(""), true, cx)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let entry2 = cx
|
||||||
|
.update(|cx| {
|
||||||
|
wt2.update(cx, |wt, cx| {
|
||||||
wt.as_local()
|
wt.as_local()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.create_entry(Path::new(""), false, cx)
|
.create_entry(Path::new(""), false, cx)
|
||||||
@ -576,8 +599,8 @@ mod tests {
|
|||||||
|
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
let p = ProjectPath {
|
let p = ProjectPath {
|
||||||
worktree_id: wt.read(cx).id(),
|
worktree_id: wt2.read(cx).id(),
|
||||||
path: entry.path,
|
path: entry2.path,
|
||||||
};
|
};
|
||||||
project.update(cx, |project, cx| project.set_active_path(Some(p), cx));
|
project.update(cx, |project, cx| project.set_active_path(Some(p), cx));
|
||||||
});
|
});
|
||||||
@ -589,8 +612,10 @@ mod tests {
|
|||||||
|
|
||||||
assert!(active_entry.is_some());
|
assert!(active_entry.is_some());
|
||||||
|
|
||||||
let res = get_wd_for_workspace(workspace, cx);
|
let res = current_project_directory(workspace, cx);
|
||||||
assert_eq!(res, home_dir());
|
assert_eq!(res, None);
|
||||||
|
let res = first_project_directory(workspace, cx);
|
||||||
|
assert_eq!(res, Some((Path::new("/root1/")).to_path_buf()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -601,17 +626,32 @@ mod tests {
|
|||||||
let params = cx.update(AppState::test);
|
let params = cx.update(AppState::test);
|
||||||
let project = Project::test(params.fs.clone(), [], cx).await;
|
let project = Project::test(params.fs.clone(), [], cx).await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
|
let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
|
||||||
let (wt, _) = project
|
let (wt1, _) = project
|
||||||
.update(cx, |project, cx| {
|
.update(cx, |project, cx| {
|
||||||
project.find_or_create_local_worktree("/root/", true, cx)
|
project.find_or_create_local_worktree("/root1/", true, cx)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (wt2, _) = project
|
||||||
|
.update(cx, |project, cx| {
|
||||||
|
project.find_or_create_local_worktree("/root2/", true, cx)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
//Setup root
|
//Setup root
|
||||||
let entry = cx
|
let _ = cx
|
||||||
.update(|cx| {
|
.update(|cx| {
|
||||||
wt.update(cx, |wt, cx| {
|
wt1.update(cx, |wt, cx| {
|
||||||
|
wt.as_local().unwrap().create_entry(Path::new(""), true, cx)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let entry2 = cx
|
||||||
|
.update(|cx| {
|
||||||
|
wt2.update(cx, |wt, cx| {
|
||||||
wt.as_local().unwrap().create_entry(Path::new(""), true, cx)
|
wt.as_local().unwrap().create_entry(Path::new(""), true, cx)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -620,8 +660,8 @@ mod tests {
|
|||||||
|
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
let p = ProjectPath {
|
let p = ProjectPath {
|
||||||
worktree_id: wt.read(cx).id(),
|
worktree_id: wt2.read(cx).id(),
|
||||||
path: entry.path,
|
path: entry2.path,
|
||||||
};
|
};
|
||||||
project.update(cx, |project, cx| project.set_active_path(Some(p), cx));
|
project.update(cx, |project, cx| project.set_active_path(Some(p), cx));
|
||||||
});
|
});
|
||||||
@ -633,8 +673,10 @@ mod tests {
|
|||||||
|
|
||||||
assert!(active_entry.is_some());
|
assert!(active_entry.is_some());
|
||||||
|
|
||||||
let res = get_wd_for_workspace(workspace, cx);
|
let res = current_project_directory(workspace, cx);
|
||||||
assert_eq!(res, Some((Path::new("/root/")).to_path_buf()));
|
assert_eq!(res, Some((Path::new("/root2/")).to_path_buf()));
|
||||||
|
let res = first_project_directory(workspace, cx);
|
||||||
|
assert_eq!(res, Some((Path::new("/root1/")).to_path_buf()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ use itertools::Itertools;
|
|||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use theme::TerminalStyle;
|
use theme::TerminalStyle;
|
||||||
|
use util::ResultExt;
|
||||||
|
|
||||||
use std::{cmp::min, ops::Range, rc::Rc, sync::Arc};
|
use std::{cmp::min, ops::Range, rc::Rc, sync::Arc};
|
||||||
use std::{fmt::Debug, ops::Sub};
|
use std::{fmt::Debug, ops::Sub};
|
||||||
@ -256,10 +257,11 @@ impl Element for TerminalEl {
|
|||||||
for layout_line in &layout.layout_lines {
|
for layout_line in &layout.layout_lines {
|
||||||
for layout_cell in &layout_line.cells {
|
for layout_cell in &layout_line.cells {
|
||||||
let position = vec2f(
|
let position = vec2f(
|
||||||
origin.x() + layout_cell.point.column as f32 * layout.em_width.0,
|
(origin.x() + layout_cell.point.column as f32 * layout.em_width.0)
|
||||||
|
.floor(),
|
||||||
origin.y() + layout_cell.point.line as f32 * layout.line_height.0,
|
origin.y() + layout_cell.point.line as f32 * layout.line_height.0,
|
||||||
);
|
);
|
||||||
let size = vec2f(layout.em_width.0, layout.line_height.0);
|
let size = vec2f(layout.em_width.0.ceil(), layout.line_height.0);
|
||||||
|
|
||||||
cx.scene.push_quad(Quad {
|
cx.scene.push_quad(Quad {
|
||||||
bounds: RectF::new(position, size),
|
bounds: RectF::new(position, size),
|
||||||
@ -318,7 +320,7 @@ impl Element for TerminalEl {
|
|||||||
|
|
||||||
//Don't actually know the start_x for a line, until here:
|
//Don't actually know the start_x for a line, until here:
|
||||||
let cell_origin = vec2f(
|
let cell_origin = vec2f(
|
||||||
origin.x() + point.column as f32 * layout.em_width.0,
|
(origin.x() + point.column as f32 * layout.em_width.0).floor(),
|
||||||
origin.y() + point.line as f32 * layout.line_height.0,
|
origin.y() + point.line as f32 * layout.line_height.0,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -420,14 +422,33 @@ pub fn mouse_to_cell_data(
|
|||||||
|
|
||||||
///Configures a text style from the current settings.
|
///Configures a text style from the current settings.
|
||||||
fn make_text_style(font_cache: &FontCache, settings: &Settings) -> TextStyle {
|
fn make_text_style(font_cache: &FontCache, settings: &Settings) -> TextStyle {
|
||||||
|
// Pull the font family from settings properly overriding
|
||||||
|
let family_id = settings
|
||||||
|
.terminal_overrides
|
||||||
|
.font_family
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|family_name| dbg!(font_cache.load_family(&[family_name]).log_err()))
|
||||||
|
.or_else(|| {
|
||||||
|
settings
|
||||||
|
.terminal_defaults
|
||||||
|
.font_family
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|family_name| font_cache.load_family(&[family_name]).log_err())
|
||||||
|
})
|
||||||
|
.unwrap_or(settings.buffer_font_family);
|
||||||
|
|
||||||
TextStyle {
|
TextStyle {
|
||||||
color: settings.theme.editor.text_color,
|
color: settings.theme.editor.text_color,
|
||||||
font_family_id: settings.buffer_font_family,
|
font_family_id: family_id,
|
||||||
font_family_name: font_cache.family_name(settings.buffer_font_family).unwrap(),
|
font_family_name: font_cache.family_name(family_id).unwrap(),
|
||||||
font_id: font_cache
|
font_id: font_cache
|
||||||
.select_font(settings.buffer_font_family, &Default::default())
|
.select_font(family_id, &Default::default())
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
font_size: settings.buffer_font_size,
|
font_size: settings
|
||||||
|
.terminal_overrides
|
||||||
|
.font_size
|
||||||
|
.or(settings.terminal_defaults.font_size)
|
||||||
|
.unwrap_or(settings.buffer_font_size),
|
||||||
font_properties: Default::default(),
|
font_properties: Default::default(),
|
||||||
underline: Default::default(),
|
underline: Default::default(),
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use alacritty_terminal::term::SizeInfo;
|
use alacritty_terminal::term::SizeInfo;
|
||||||
use gpui::{AppContext, ModelContext, ModelHandle, ReadModelWith, TestAppContext};
|
use gpui::{AppContext, ModelHandle, ReadModelWith, TestAppContext};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -28,7 +28,8 @@ impl<'a> TerminalTestContext<'a> {
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
let connection = cx.add_model(|cx| TerminalConnection::new(None, size_info, cx));
|
let connection =
|
||||||
|
cx.add_model(|cx| TerminalConnection::new(None, None, None, size_info, cx));
|
||||||
|
|
||||||
TerminalTestContext { cx, connection }
|
TerminalTestContext { cx, connection }
|
||||||
}
|
}
|
||||||
@ -56,13 +57,6 @@ impl<'a> TerminalTestContext<'a> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_connection<F, S>(&mut self, f: F) -> S
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut TerminalConnection, &mut ModelContext<TerminalConnection>) -> S,
|
|
||||||
{
|
|
||||||
self.connection.update(self.cx, |conn, cx| f(conn, cx))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn grid_as_str(connection: &TerminalConnection) -> String {
|
fn grid_as_str(connection: &TerminalConnection) -> String {
|
||||||
let term = connection.term.lock();
|
let term = connection.term.lock();
|
||||||
let grid_iterator = term.renderable_content().display_iter;
|
let grid_iterator = term.renderable_content().display_iter;
|
||||||
|
@ -39,15 +39,20 @@ where
|
|||||||
Self(rx)
|
Self(rx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///Loads the given watched JSON file. In the special case that the file is
|
||||||
|
///empty (ignoring whitespace) or is not a file, this will return T::default()
|
||||||
async fn load(fs: Arc<dyn Fs>, path: &Path) -> Option<T> {
|
async fn load(fs: Arc<dyn Fs>, path: &Path) -> Option<T> {
|
||||||
if fs.is_file(&path).await {
|
if !fs.is_file(&path).await {
|
||||||
fs.load(&path)
|
return Some(T::default());
|
||||||
.await
|
|
||||||
.log_err()
|
|
||||||
.and_then(|data| parse_json_with_comments(&data).log_err())
|
|
||||||
} else {
|
|
||||||
Some(T::default())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fs.load(&path).await.log_err().and_then(|data| {
|
||||||
|
if data.trim().is_empty() {
|
||||||
|
Some(T::default())
|
||||||
|
} else {
|
||||||
|
parse_json_with_comments(&data).log_err()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,18 +79,25 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::MutableAppContext) {
|
|||||||
cx.add_global_action(move |_: &IncreaseBufferFontSize, cx| {
|
cx.add_global_action(move |_: &IncreaseBufferFontSize, cx| {
|
||||||
cx.update_global::<Settings, _, _>(|settings, cx| {
|
cx.update_global::<Settings, _, _>(|settings, cx| {
|
||||||
settings.buffer_font_size = (settings.buffer_font_size + 1.0).max(MIN_FONT_SIZE);
|
settings.buffer_font_size = (settings.buffer_font_size + 1.0).max(MIN_FONT_SIZE);
|
||||||
|
if let Some(terminal_font_size) = settings.terminal_overrides.font_size.as_mut() {
|
||||||
|
*terminal_font_size = (*terminal_font_size + 1.0).max(MIN_FONT_SIZE);
|
||||||
|
}
|
||||||
cx.refresh_windows();
|
cx.refresh_windows();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
cx.add_global_action(move |_: &DecreaseBufferFontSize, cx| {
|
cx.add_global_action(move |_: &DecreaseBufferFontSize, cx| {
|
||||||
cx.update_global::<Settings, _, _>(|settings, cx| {
|
cx.update_global::<Settings, _, _>(|settings, cx| {
|
||||||
settings.buffer_font_size = (settings.buffer_font_size - 1.0).max(MIN_FONT_SIZE);
|
settings.buffer_font_size = (settings.buffer_font_size - 1.0).max(MIN_FONT_SIZE);
|
||||||
|
if let Some(terminal_font_size) = settings.terminal_overrides.font_size.as_mut() {
|
||||||
|
*terminal_font_size = (*terminal_font_size - 1.0).max(MIN_FONT_SIZE);
|
||||||
|
}
|
||||||
cx.refresh_windows();
|
cx.refresh_windows();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
cx.add_global_action(move |_: &ResetBufferFontSize, cx| {
|
cx.add_global_action(move |_: &ResetBufferFontSize, cx| {
|
||||||
cx.update_global::<Settings, _, _>(|settings, cx| {
|
cx.update_global::<Settings, _, _>(|settings, cx| {
|
||||||
settings.buffer_font_size = settings.default_buffer_font_size;
|
settings.buffer_font_size = settings.default_buffer_font_size;
|
||||||
|
settings.terminal_overrides.font_size = settings.terminal_defaults.font_size;
|
||||||
cx.refresh_windows();
|
cx.refresh_windows();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user