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