1use alacritty_terminal::vte::ansi::{
2 CursorShape as AlacCursorShape, CursorStyle as AlacCursorStyle,
3};
4use collections::HashMap;
5use gpui::{FontFallbacks, FontFeatures, FontWeight, Pixels, px};
6use schemars::JsonSchema;
7use serde::{Deserialize, Serialize};
8
9pub use settings::AlternateScroll;
10
11use settings::{
12 RegisterSetting, ShowScrollbar, TerminalBlink, TerminalDockPosition, TerminalLineHeight,
13 VenvSettings, WorkingDirectory, merge_from::MergeFrom,
14};
15use task::Shell;
16use theme::FontFamilyName;
17
18#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
19pub struct Toolbar {
20 pub breadcrumbs: bool,
21}
22
23#[derive(Clone, Debug, Deserialize, RegisterSetting)]
24pub struct TerminalSettings {
25 pub shell: Shell,
26 pub working_directory: WorkingDirectory,
27 pub font_size: Option<Pixels>, // todo(settings_refactor) can be non-optional...
28 pub font_family: Option<FontFamilyName>,
29 pub font_fallbacks: Option<FontFallbacks>,
30 pub font_features: Option<FontFeatures>,
31 pub font_weight: Option<FontWeight>,
32 pub line_height: TerminalLineHeight,
33 pub env: HashMap<String, String>,
34 pub cursor_shape: CursorShape,
35 pub blinking: TerminalBlink,
36 pub alternate_scroll: AlternateScroll,
37 pub option_as_meta: bool,
38 pub copy_on_select: bool,
39 pub keep_selection_on_copy: bool,
40 pub button: bool,
41 pub dock: TerminalDockPosition,
42 pub default_width: Pixels,
43 pub default_height: Pixels,
44 pub detect_venv: VenvSettings,
45 pub max_scroll_history_lines: Option<usize>,
46 pub scroll_multiplier: f32,
47 pub toolbar: Toolbar,
48 pub scrollbar: ScrollbarSettings,
49 pub minimum_contrast: f32,
50}
51
52#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
53pub struct ScrollbarSettings {
54 /// When to show the scrollbar in the terminal.
55 ///
56 /// Default: inherits editor scrollbar settings
57 pub show: Option<ShowScrollbar>,
58}
59
60fn settings_shell_to_task_shell(shell: settings::Shell) -> Shell {
61 match shell {
62 settings::Shell::System => Shell::System,
63 settings::Shell::Program(program) => Shell::Program(program),
64 settings::Shell::WithArguments {
65 program,
66 args,
67 title_override,
68 } => Shell::WithArguments {
69 program,
70 args,
71 title_override: title_override.map(Into::into),
72 },
73 }
74}
75
76impl settings::Settings for TerminalSettings {
77 fn from_settings(content: &settings::SettingsContent) -> Self {
78 let user_content = content.terminal.clone().unwrap();
79 // Note: we allow a subset of "terminal" settings in the project files.
80 let mut project_content = user_content.project.clone();
81 project_content.merge_from_option(content.project.terminal.as_ref());
82 TerminalSettings {
83 shell: settings_shell_to_task_shell(project_content.shell.unwrap()),
84 working_directory: project_content.working_directory.unwrap(),
85 font_size: user_content.font_size.map(px),
86 font_family: user_content.font_family,
87 font_fallbacks: user_content.font_fallbacks.map(|fallbacks| {
88 FontFallbacks::from_fonts(
89 fallbacks
90 .into_iter()
91 .map(|family| family.0.to_string())
92 .collect(),
93 )
94 }),
95 font_features: user_content.font_features,
96 font_weight: user_content.font_weight.map(FontWeight),
97 line_height: user_content.line_height.unwrap(),
98 env: project_content.env.unwrap(),
99 cursor_shape: user_content.cursor_shape.unwrap().into(),
100 blinking: user_content.blinking.unwrap(),
101 alternate_scroll: user_content.alternate_scroll.unwrap(),
102 option_as_meta: user_content.option_as_meta.unwrap(),
103 copy_on_select: user_content.copy_on_select.unwrap(),
104 keep_selection_on_copy: user_content.keep_selection_on_copy.unwrap(),
105 button: user_content.button.unwrap(),
106 dock: user_content.dock.unwrap(),
107 default_width: px(user_content.default_width.unwrap()),
108 default_height: px(user_content.default_height.unwrap()),
109 detect_venv: project_content.detect_venv.unwrap(),
110 scroll_multiplier: user_content.scroll_multiplier.unwrap(),
111 max_scroll_history_lines: user_content.max_scroll_history_lines,
112 toolbar: Toolbar {
113 breadcrumbs: user_content.toolbar.unwrap().breadcrumbs.unwrap(),
114 },
115 scrollbar: ScrollbarSettings {
116 show: user_content.scrollbar.unwrap().show,
117 },
118 minimum_contrast: user_content.minimum_contrast.unwrap(),
119 }
120 }
121}
122
123#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
124#[serde(rename_all = "snake_case")]
125pub enum CursorShape {
126 /// Cursor is a block like `█`.
127 #[default]
128 Block,
129 /// Cursor is an underscore like `_`.
130 Underline,
131 /// Cursor is a vertical bar like `⎸`.
132 Bar,
133 /// Cursor is a hollow box like `▯`.
134 Hollow,
135}
136
137impl From<settings::CursorShapeContent> for CursorShape {
138 fn from(value: settings::CursorShapeContent) -> Self {
139 match value {
140 settings::CursorShapeContent::Block => CursorShape::Block,
141 settings::CursorShapeContent::Underline => CursorShape::Underline,
142 settings::CursorShapeContent::Bar => CursorShape::Bar,
143 settings::CursorShapeContent::Hollow => CursorShape::Hollow,
144 }
145 }
146}
147
148impl From<CursorShape> for AlacCursorShape {
149 fn from(value: CursorShape) -> Self {
150 match value {
151 CursorShape::Block => AlacCursorShape::Block,
152 CursorShape::Underline => AlacCursorShape::Underline,
153 CursorShape::Bar => AlacCursorShape::Beam,
154 CursorShape::Hollow => AlacCursorShape::HollowBlock,
155 }
156 }
157}
158
159impl From<CursorShape> for AlacCursorStyle {
160 fn from(value: CursorShape) -> Self {
161 AlacCursorStyle {
162 shape: value.into(),
163 blinking: false,
164 }
165 }
166}