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