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