1use std::collections::HashMap;
2
3use editor::EditorSettings;
4use gpui::AppContext;
5use schemars::JsonSchema;
6use serde::{Deserialize, Serialize};
7use settings::{Settings, SettingsSources};
8use ui::Pixels;
9
10#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
11#[serde(rename_all = "snake_case")]
12pub enum JupyterDockPosition {
13 Left,
14 #[default]
15 Right,
16 Bottom,
17}
18
19#[derive(Debug, Default)]
20pub struct JupyterSettings {
21 pub dock: JupyterDockPosition,
22 pub default_width: Pixels,
23 pub kernel_selections: HashMap<String, String>,
24}
25
26impl JupyterSettings {
27 pub fn enabled(cx: &AppContext) -> bool {
28 // In order to avoid a circular dependency between `editor` and `repl` crates,
29 // we put the `enable` flag on its settings.
30 // This allows the editor to set up context for key bindings/actions.
31 EditorSettings::get_global(cx).jupyter.enabled
32 }
33}
34
35#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)]
36pub struct JupyterSettingsContent {
37 /// Where to dock the Jupyter panel.
38 ///
39 /// Default: `right`
40 dock: Option<JupyterDockPosition>,
41 /// Default width in pixels when the jupyter panel is docked to the left or right.
42 ///
43 /// Default: 640
44 pub default_width: Option<f32>,
45 /// Default kernels to select for each language.
46 ///
47 /// Default: `{}`
48 pub kernel_selections: Option<HashMap<String, String>>,
49}
50
51impl JupyterSettingsContent {
52 pub fn set_dock(&mut self, dock: JupyterDockPosition) {
53 self.dock = Some(dock);
54 }
55}
56
57impl Default for JupyterSettingsContent {
58 fn default() -> Self {
59 JupyterSettingsContent {
60 dock: Some(JupyterDockPosition::Right),
61 default_width: Some(640.0),
62 kernel_selections: Some(HashMap::new()),
63 }
64 }
65}
66
67impl Settings for JupyterSettings {
68 const KEY: Option<&'static str> = Some("jupyter");
69
70 type FileContent = JupyterSettingsContent;
71
72 fn load(
73 sources: SettingsSources<Self::FileContent>,
74 _cx: &mut gpui::AppContext,
75 ) -> anyhow::Result<Self>
76 where
77 Self: Sized,
78 {
79 let mut settings = JupyterSettings::default();
80
81 for value in sources.defaults_and_customizations() {
82 if let Some(dock) = value.dock {
83 settings.dock = dock;
84 }
85
86 if let Some(default_width) = value.default_width {
87 settings.default_width = Pixels::from(default_width);
88 }
89
90 if let Some(source) = &value.kernel_selections {
91 for (k, v) in source {
92 settings.kernel_selections.insert(k.clone(), v.clone());
93 }
94 }
95 }
96
97 Ok(settings)
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use gpui::{AppContext, UpdateGlobal};
104 use settings::SettingsStore;
105
106 use super::*;
107
108 #[gpui::test]
109 fn test_deserialize_jupyter_settings(cx: &mut AppContext) {
110 let store = settings::SettingsStore::test(cx);
111 cx.set_global(store);
112
113 EditorSettings::register(cx);
114 JupyterSettings::register(cx);
115
116 assert_eq!(JupyterSettings::enabled(cx), false);
117 assert_eq!(
118 JupyterSettings::get_global(cx).dock,
119 JupyterDockPosition::Right
120 );
121 assert_eq!(
122 JupyterSettings::get_global(cx).default_width,
123 Pixels::from(640.0)
124 );
125
126 // Setting a custom setting through user settings
127 SettingsStore::update_global(cx, |store, cx| {
128 store
129 .set_user_settings(
130 r#"{
131 "jupyter": {
132 "enabled": true,
133 "dock": "left",
134 "default_width": 800.0
135 }
136 }"#,
137 cx,
138 )
139 .unwrap();
140 });
141
142 assert_eq!(JupyterSettings::enabled(cx), true);
143 assert_eq!(
144 JupyterSettings::get_global(cx).dock,
145 JupyterDockPosition::Left
146 );
147 assert_eq!(
148 JupyterSettings::get_global(cx).default_width,
149 Pixels::from(800.0)
150 );
151 }
152}