1use std::{cell::RefCell, rc::Rc, sync::Arc};
2
3use client::telemetry::Telemetry;
4use collections::HashMap;
5use copilot::{Copilot, CopilotCompletionProvider};
6use editor::{Editor, EditorMode};
7use gpui::{AnyWindowHandle, AppContext, Context, ViewContext, WeakView};
8use language::language_settings::all_language_settings;
9use settings::SettingsStore;
10use supermaven::{Supermaven, SupermavenCompletionProvider};
11
12pub fn init(telemetry: Arc<Telemetry>, cx: &mut AppContext) {
13 let editors: Rc<RefCell<HashMap<WeakView<Editor>, AnyWindowHandle>>> = Rc::default();
14 cx.observe_new_views({
15 let editors = editors.clone();
16 let telemetry = telemetry.clone();
17 move |editor: &mut Editor, cx: &mut ViewContext<Editor>| {
18 if editor.mode() != EditorMode::Full {
19 return;
20 }
21
22 register_backward_compatible_actions(editor, cx);
23
24 let editor_handle = cx.view().downgrade();
25 cx.on_release({
26 let editor_handle = editor_handle.clone();
27 let editors = editors.clone();
28 move |_, _, _| {
29 editors.borrow_mut().remove(&editor_handle);
30 }
31 })
32 .detach();
33 editors
34 .borrow_mut()
35 .insert(editor_handle, cx.window_handle());
36 let provider = all_language_settings(None, cx).inline_completions.provider;
37 assign_inline_completion_provider(editor, provider, &telemetry, cx);
38 }
39 })
40 .detach();
41
42 let mut provider = all_language_settings(None, cx).inline_completions.provider;
43 for (editor, window) in editors.borrow().iter() {
44 _ = window.update(cx, |_window, cx| {
45 _ = editor.update(cx, |editor, cx| {
46 assign_inline_completion_provider(editor, provider, &telemetry, cx);
47 })
48 });
49 }
50
51 cx.observe_global::<SettingsStore>(move |cx| {
52 let new_provider = all_language_settings(None, cx).inline_completions.provider;
53 if new_provider != provider {
54 provider = new_provider;
55 for (editor, window) in editors.borrow().iter() {
56 _ = window.update(cx, |_window, cx| {
57 _ = editor.update(cx, |editor, cx| {
58 assign_inline_completion_provider(editor, provider, &telemetry, cx);
59 })
60 });
61 }
62 }
63 })
64 .detach();
65}
66
67fn register_backward_compatible_actions(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
68 // We renamed some of these actions to not be copilot-specific, but that
69 // would have not been backwards-compatible. So here we are re-registering
70 // the actions with the old names to not break people's keymaps.
71 editor
72 .register_action(cx.listener(
73 |editor, _: &copilot::Suggest, cx: &mut ViewContext<Editor>| {
74 editor.show_inline_completion(&Default::default(), cx);
75 },
76 ))
77 .detach();
78 editor
79 .register_action(cx.listener(
80 |editor, _: &copilot::NextSuggestion, cx: &mut ViewContext<Editor>| {
81 editor.next_inline_completion(&Default::default(), cx);
82 },
83 ))
84 .detach();
85 editor
86 .register_action(cx.listener(
87 |editor, _: &copilot::PreviousSuggestion, cx: &mut ViewContext<Editor>| {
88 editor.previous_inline_completion(&Default::default(), cx);
89 },
90 ))
91 .detach();
92 editor
93 .register_action(cx.listener(
94 |editor,
95 _: &editor::actions::AcceptPartialCopilotSuggestion,
96 cx: &mut ViewContext<Editor>| {
97 editor.accept_partial_inline_completion(&Default::default(), cx);
98 },
99 ))
100 .detach();
101}
102
103fn assign_inline_completion_provider(
104 editor: &mut Editor,
105 provider: language::language_settings::InlineCompletionProvider,
106 telemetry: &Arc<Telemetry>,
107 cx: &mut ViewContext<Editor>,
108) {
109 match provider {
110 language::language_settings::InlineCompletionProvider::None => {}
111 language::language_settings::InlineCompletionProvider::Copilot => {
112 if let Some(copilot) = Copilot::global(cx) {
113 if let Some(buffer) = editor.buffer().read(cx).as_singleton() {
114 if buffer.read(cx).file().is_some() {
115 copilot.update(cx, |copilot, cx| {
116 copilot.register_buffer(&buffer, cx);
117 });
118 }
119 }
120 let provider = cx.new_model(|_| {
121 CopilotCompletionProvider::new(copilot).with_telemetry(telemetry.clone())
122 });
123 editor.set_inline_completion_provider(Some(provider), cx);
124 }
125 }
126 language::language_settings::InlineCompletionProvider::Supermaven => {
127 if let Some(supermaven) = Supermaven::global(cx) {
128 let provider = cx.new_model(|_| {
129 SupermavenCompletionProvider::new(supermaven).with_telemetry(telemetry.clone())
130 });
131 editor.set_inline_completion_provider(Some(provider), cx);
132 }
133 }
134 }
135}