1use std::sync::Arc;
2
3use anyhow::Result;
4use collections::HashMap;
5use gpui::{
6 prelude::*, AppContext, EntityId, Global, Model, ModelContext, Subscription, Task, View,
7};
8use language::Language;
9use project::Fs;
10use settings::{Settings, SettingsStore};
11
12use crate::kernels::kernel_specifications;
13use crate::{JupyterSettings, KernelSpecification, Session};
14
15struct GlobalReplStore(Model<ReplStore>);
16
17impl Global for GlobalReplStore {}
18
19pub struct ReplStore {
20 fs: Arc<dyn Fs>,
21 enabled: bool,
22 sessions: HashMap<EntityId, View<Session>>,
23 kernel_specifications: Vec<KernelSpecification>,
24 _subscriptions: Vec<Subscription>,
25}
26
27impl ReplStore {
28 pub(crate) fn init(fs: Arc<dyn Fs>, cx: &mut AppContext) {
29 let store = cx.new_model(move |cx| Self::new(fs, cx));
30
31 store
32 .update(cx, |store, cx| store.refresh_kernelspecs(cx))
33 .detach_and_log_err(cx);
34
35 cx.set_global(GlobalReplStore(store))
36 }
37
38 pub fn global(cx: &AppContext) -> Model<Self> {
39 cx.global::<GlobalReplStore>().0.clone()
40 }
41
42 pub fn new(fs: Arc<dyn Fs>, cx: &mut ModelContext<Self>) -> Self {
43 let subscriptions = vec![cx.observe_global::<SettingsStore>(move |this, cx| {
44 this.set_enabled(JupyterSettings::enabled(cx), cx);
45 })];
46
47 Self {
48 fs,
49 enabled: JupyterSettings::enabled(cx),
50 sessions: HashMap::default(),
51 kernel_specifications: Vec::new(),
52 _subscriptions: subscriptions,
53 }
54 }
55
56 pub fn fs(&self) -> &Arc<dyn Fs> {
57 &self.fs
58 }
59
60 pub fn is_enabled(&self) -> bool {
61 self.enabled
62 }
63
64 pub fn kernel_specifications(&self) -> impl Iterator<Item = &KernelSpecification> {
65 self.kernel_specifications.iter()
66 }
67
68 pub fn sessions(&self) -> impl Iterator<Item = &View<Session>> {
69 self.sessions.values()
70 }
71
72 fn set_enabled(&mut self, enabled: bool, cx: &mut ModelContext<Self>) {
73 if self.enabled != enabled {
74 self.enabled = enabled;
75 cx.notify();
76 }
77 }
78
79 pub fn refresh_kernelspecs(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
80 let kernel_specifications = kernel_specifications(self.fs.clone());
81 cx.spawn(|this, mut cx| async move {
82 let kernel_specifications = kernel_specifications.await?;
83
84 this.update(&mut cx, |this, cx| {
85 this.kernel_specifications = kernel_specifications;
86 cx.notify();
87 })
88 })
89 }
90
91 pub fn kernelspec(
92 &self,
93 language: &Language,
94 cx: &mut ModelContext<Self>,
95 ) -> Option<KernelSpecification> {
96 let settings = JupyterSettings::get_global(cx);
97 let language_name = language.code_fence_block_name();
98 let selected_kernel = settings.kernel_selections.get(language_name.as_ref());
99
100 self.kernel_specifications
101 .iter()
102 .find(|runtime_specification| {
103 if let Some(selected) = selected_kernel {
104 // Top priority is the selected kernel
105 runtime_specification.name.to_lowercase() == selected.to_lowercase()
106 } else {
107 // Otherwise, we'll try to find a kernel that matches the language
108 runtime_specification.kernelspec.language.to_lowercase()
109 == language_name.to_lowercase()
110 }
111 })
112 .cloned()
113 }
114
115 pub fn get_session(&self, entity_id: EntityId) -> Option<&View<Session>> {
116 self.sessions.get(&entity_id)
117 }
118
119 pub fn insert_session(&mut self, entity_id: EntityId, session: View<Session>) {
120 self.sessions.insert(entity_id, session);
121 }
122
123 pub fn remove_session(&mut self, entity_id: EntityId) {
124 self.sessions.remove(&entity_id);
125 }
126}