1use std::sync::Arc;
2
3use anyhow::Result;
4use client::telemetry::Telemetry;
5use collections::HashMap;
6use command_palette_hooks::CommandPaletteFilter;
7use gpui::{
8 prelude::*, AppContext, EntityId, Global, Model, ModelContext, Subscription, Task, View,
9};
10use project::Fs;
11use settings::{Settings, SettingsStore};
12
13use crate::kernels::kernel_specifications;
14use crate::{JupyterSettings, KernelSpecification, Session};
15
16struct GlobalReplStore(Model<ReplStore>);
17
18impl Global for GlobalReplStore {}
19
20pub struct ReplStore {
21 fs: Arc<dyn Fs>,
22 enabled: bool,
23 sessions: HashMap<EntityId, View<Session>>,
24 kernel_specifications: Vec<KernelSpecification>,
25 telemetry: Arc<Telemetry>,
26 _subscriptions: Vec<Subscription>,
27}
28
29impl ReplStore {
30 const NAMESPACE: &'static str = "repl";
31
32 pub(crate) fn init(fs: Arc<dyn Fs>, telemetry: Arc<Telemetry>, cx: &mut AppContext) {
33 let store = cx.new_model(move |cx| Self::new(fs, telemetry, cx));
34
35 store
36 .update(cx, |store, cx| store.refresh_kernelspecs(cx))
37 .detach_and_log_err(cx);
38
39 cx.set_global(GlobalReplStore(store))
40 }
41
42 pub fn global(cx: &AppContext) -> Model<Self> {
43 cx.global::<GlobalReplStore>().0.clone()
44 }
45
46 pub fn new(fs: Arc<dyn Fs>, telemetry: Arc<Telemetry>, cx: &mut ModelContext<Self>) -> Self {
47 let subscriptions = vec![cx.observe_global::<SettingsStore>(move |this, cx| {
48 this.set_enabled(JupyterSettings::enabled(cx), cx);
49 })];
50
51 let this = Self {
52 fs,
53 telemetry,
54 enabled: JupyterSettings::enabled(cx),
55 sessions: HashMap::default(),
56 kernel_specifications: Vec::new(),
57 _subscriptions: subscriptions,
58 };
59 this.on_enabled_changed(cx);
60 this
61 }
62
63 pub fn fs(&self) -> &Arc<dyn Fs> {
64 &self.fs
65 }
66
67 pub fn telemetry(&self) -> &Arc<Telemetry> {
68 &self.telemetry
69 }
70
71 pub fn is_enabled(&self) -> bool {
72 self.enabled
73 }
74
75 pub fn kernel_specifications(&self) -> impl Iterator<Item = &KernelSpecification> {
76 self.kernel_specifications.iter()
77 }
78
79 pub fn sessions(&self) -> impl Iterator<Item = &View<Session>> {
80 self.sessions.values()
81 }
82
83 fn set_enabled(&mut self, enabled: bool, cx: &mut ModelContext<Self>) {
84 if self.enabled == enabled {
85 return;
86 }
87
88 self.enabled = enabled;
89 self.on_enabled_changed(cx);
90 }
91
92 fn on_enabled_changed(&self, cx: &mut ModelContext<Self>) {
93 if !self.enabled {
94 CommandPaletteFilter::update_global(cx, |filter, _cx| {
95 filter.hide_namespace(Self::NAMESPACE);
96 });
97
98 return;
99 }
100
101 CommandPaletteFilter::update_global(cx, |filter, _cx| {
102 filter.show_namespace(Self::NAMESPACE);
103 });
104
105 cx.notify();
106 }
107
108 pub fn refresh_kernelspecs(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
109 let kernel_specifications = kernel_specifications(self.fs.clone());
110 cx.spawn(|this, mut cx| async move {
111 let kernel_specifications = kernel_specifications.await?;
112
113 this.update(&mut cx, |this, cx| {
114 this.kernel_specifications = kernel_specifications;
115 cx.notify();
116 })
117 })
118 }
119
120 pub fn kernelspec(&self, language_name: &str, cx: &AppContext) -> Option<KernelSpecification> {
121 let settings = JupyterSettings::get_global(cx);
122 let selected_kernel = settings.kernel_selections.get(language_name);
123
124 let found_by_name = self
125 .kernel_specifications
126 .iter()
127 .find(|runtime_specification| {
128 if let Some(selected) = selected_kernel {
129 // Top priority is the selected kernel
130 return runtime_specification.name.to_lowercase() == selected.to_lowercase();
131 }
132 false
133 })
134 .cloned();
135
136 if let Some(found_by_name) = found_by_name {
137 return Some(found_by_name);
138 }
139
140 self.kernel_specifications
141 .iter()
142 .find(|runtime_specification| {
143 runtime_specification.kernelspec.language.to_lowercase()
144 == language_name.to_lowercase()
145 })
146 .cloned()
147 }
148
149 pub fn get_session(&self, entity_id: EntityId) -> Option<&View<Session>> {
150 self.sessions.get(&entity_id)
151 }
152
153 pub fn insert_session(&mut self, entity_id: EntityId, session: View<Session>) {
154 self.sessions.insert(entity_id, session);
155 }
156
157 pub fn remove_session(&mut self, entity_id: EntityId) {
158 self.sessions.remove(&entity_id);
159 }
160}