inline_completion_button.rs

  1use anyhow::Result;
  2use client::UserStore;
  3use copilot::{Copilot, Status};
  4use editor::{actions::ShowInlineCompletion, scroll::Autoscroll, Editor};
  5use feature_flags::{
  6    FeatureFlagAppExt, PredictEditsFeatureFlag, PredictEditsRateCompletionsFeatureFlag,
  7};
  8use fs::Fs;
  9use gpui::{
 10    actions, div, pulsating_between, Action, Animation, AnimationExt, App, AsyncWindowContext,
 11    Corner, Entity, FocusHandle, Focusable, IntoElement, ParentElement, Render, Subscription,
 12    WeakEntity,
 13};
 14use language::{
 15    language_settings::{
 16        self, all_language_settings, AllLanguageSettings, InlineCompletionProvider,
 17    },
 18    File, Language,
 19};
 20use regex::Regex;
 21use settings::{update_settings_file, Settings, SettingsStore};
 22use std::{
 23    sync::{Arc, LazyLock},
 24    time::Duration,
 25};
 26use supermaven::{AccountStatus, Supermaven};
 27use ui::{
 28    prelude::*, Clickable, ContextMenu, ContextMenuEntry, IconButton, IconButtonShape, PopoverMenu,
 29    PopoverMenuHandle, Tooltip,
 30};
 31use workspace::{
 32    create_and_open_local_file, item::ItemHandle, notifications::NotificationId, StatusItemView,
 33    Toast, Workspace,
 34};
 35use zed_actions::OpenBrowser;
 36use zeta::RateCompletionModal;
 37
 38actions!(zeta, [RateCompletions]);
 39actions!(inline_completion, [ToggleMenu]);
 40
 41const COPILOT_SETTINGS_URL: &str = "https://github.com/settings/copilot";
 42
 43struct CopilotErrorToast;
 44
 45pub struct InlineCompletionButton {
 46    editor_subscription: Option<(Subscription, usize)>,
 47    editor_enabled: Option<bool>,
 48    editor_focus_handle: Option<FocusHandle>,
 49    language: Option<Arc<Language>>,
 50    file: Option<Arc<dyn File>>,
 51    inline_completion_provider: Option<Arc<dyn inline_completion::InlineCompletionProviderHandle>>,
 52    fs: Arc<dyn Fs>,
 53    workspace: WeakEntity<Workspace>,
 54    user_store: Entity<UserStore>,
 55    popover_menu_handle: PopoverMenuHandle<ContextMenu>,
 56}
 57
 58enum SupermavenButtonStatus {
 59    Ready,
 60    Errored(String),
 61    NeedsActivation(String),
 62    Initializing,
 63}
 64
 65impl Render for InlineCompletionButton {
 66    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
 67        let all_language_settings = all_language_settings(None, cx);
 68
 69        match all_language_settings.inline_completions.provider {
 70            InlineCompletionProvider::None => div(),
 71
 72            InlineCompletionProvider::Copilot => {
 73                let Some(copilot) = Copilot::global(cx) else {
 74                    return div();
 75                };
 76                let status = copilot.read(cx).status();
 77
 78                let enabled = self.editor_enabled.unwrap_or(false);
 79
 80                let icon = match status {
 81                    Status::Error(_) => IconName::CopilotError,
 82                    Status::Authorized => {
 83                        if enabled {
 84                            IconName::Copilot
 85                        } else {
 86                            IconName::CopilotDisabled
 87                        }
 88                    }
 89                    _ => IconName::CopilotInit,
 90                };
 91
 92                if let Status::Error(e) = status {
 93                    return div().child(
 94                        IconButton::new("copilot-error", icon)
 95                            .icon_size(IconSize::Small)
 96                            .on_click(cx.listener(move |_, _, window, cx| {
 97                                if let Some(workspace) = window.root::<Workspace>().flatten() {
 98                                    workspace.update(cx, |workspace, cx| {
 99                                        workspace.show_toast(
100                                            Toast::new(
101                                                NotificationId::unique::<CopilotErrorToast>(),
102                                                format!("Copilot can't be started: {}", e),
103                                            )
104                                            .on_click(
105                                                "Reinstall Copilot",
106                                                |_, cx| {
107                                                    if let Some(copilot) = Copilot::global(cx) {
108                                                        copilot
109                                                            .update(cx, |copilot, cx| {
110                                                                copilot.reinstall(cx)
111                                                            })
112                                                            .detach();
113                                                    }
114                                                },
115                                            ),
116                                            cx,
117                                        );
118                                    });
119                                }
120                            }))
121                            .tooltip(|window, cx| {
122                                Tooltip::for_action("GitHub Copilot", &ToggleMenu, window, cx)
123                            }),
124                    );
125                }
126                let this = cx.entity().clone();
127
128                div().child(
129                    PopoverMenu::new("copilot")
130                        .menu(move |window, cx| {
131                            Some(match status {
132                                Status::Authorized => this.update(cx, |this, cx| {
133                                    this.build_copilot_context_menu(window, cx)
134                                }),
135                                _ => this.update(cx, |this, cx| {
136                                    this.build_copilot_start_menu(window, cx)
137                                }),
138                            })
139                        })
140                        .anchor(Corner::BottomRight)
141                        .trigger(IconButton::new("copilot-icon", icon).tooltip(|window, cx| {
142                            Tooltip::for_action("GitHub Copilot", &ToggleMenu, window, cx)
143                        }))
144                        .with_handle(self.popover_menu_handle.clone()),
145                )
146            }
147
148            InlineCompletionProvider::Supermaven => {
149                let Some(supermaven) = Supermaven::global(cx) else {
150                    return div();
151                };
152
153                let supermaven = supermaven.read(cx);
154
155                let status = match supermaven {
156                    Supermaven::Starting => SupermavenButtonStatus::Initializing,
157                    Supermaven::FailedDownload { error } => {
158                        SupermavenButtonStatus::Errored(error.to_string())
159                    }
160                    Supermaven::Spawned(agent) => {
161                        let account_status = agent.account_status.clone();
162                        match account_status {
163                            AccountStatus::NeedsActivation { activate_url } => {
164                                SupermavenButtonStatus::NeedsActivation(activate_url.clone())
165                            }
166                            AccountStatus::Unknown => SupermavenButtonStatus::Initializing,
167                            AccountStatus::Ready => SupermavenButtonStatus::Ready,
168                        }
169                    }
170                    Supermaven::Error { error } => {
171                        SupermavenButtonStatus::Errored(error.to_string())
172                    }
173                };
174
175                let icon = status.to_icon();
176                let tooltip_text = status.to_tooltip();
177                let has_menu = status.has_menu();
178                let this = cx.entity().clone();
179                let fs = self.fs.clone();
180
181                return div().child(
182                    PopoverMenu::new("supermaven")
183                        .menu(move |window, cx| match &status {
184                            SupermavenButtonStatus::NeedsActivation(activate_url) => {
185                                Some(ContextMenu::build(window, cx, |menu, _, _| {
186                                    let fs = fs.clone();
187                                    let activate_url = activate_url.clone();
188                                    menu.entry("Sign In", None, move |_, cx| {
189                                        cx.open_url(activate_url.as_str())
190                                    })
191                                    .entry(
192                                        "Use Copilot",
193                                        None,
194                                        move |_, cx| {
195                                            set_completion_provider(
196                                                fs.clone(),
197                                                cx,
198                                                InlineCompletionProvider::Copilot,
199                                            )
200                                        },
201                                    )
202                                }))
203                            }
204                            SupermavenButtonStatus::Ready => Some(this.update(cx, |this, cx| {
205                                this.build_supermaven_context_menu(window, cx)
206                            })),
207                            _ => None,
208                        })
209                        .anchor(Corner::BottomRight)
210                        .trigger(IconButton::new("supermaven-icon", icon).tooltip(
211                            move |window, cx| {
212                                if has_menu {
213                                    Tooltip::for_action(
214                                        tooltip_text.clone(),
215                                        &ToggleMenu,
216                                        window,
217                                        cx,
218                                    )
219                                } else {
220                                    Tooltip::text(tooltip_text.clone())(window, cx)
221                                }
222                            },
223                        ))
224                        .with_handle(self.popover_menu_handle.clone()),
225                );
226            }
227
228            InlineCompletionProvider::Zed => {
229                if !cx.has_flag::<PredictEditsFeatureFlag>() {
230                    return div();
231                }
232
233                let enabled = self.editor_enabled.unwrap_or(false);
234
235                let zeta_icon = if enabled {
236                    IconName::ZedPredict
237                } else {
238                    IconName::ZedPredictDisabled
239                };
240
241                let current_user_terms_accepted =
242                    self.user_store.read(cx).current_user_has_accepted_terms();
243
244                let icon_button = || {
245                    let base = IconButton::new("zed-predict-pending-button", zeta_icon)
246                        .shape(IconButtonShape::Square);
247
248                    match (
249                        current_user_terms_accepted,
250                        self.popover_menu_handle.is_deployed(),
251                        enabled,
252                    ) {
253                        (Some(false) | None, _, _) => {
254                            let signed_in = current_user_terms_accepted.is_some();
255                            let tooltip_meta = if signed_in {
256                                "Read Terms of Service"
257                            } else {
258                                "Sign in to use"
259                            };
260
261                            base.tooltip(move |window, cx| {
262                                Tooltip::with_meta(
263                                    "Edit Predictions",
264                                    None,
265                                    tooltip_meta,
266                                    window,
267                                    cx,
268                                )
269                            })
270                            .on_click(cx.listener(
271                                move |_, _, window, cx| {
272                                    telemetry::event!(
273                                        "Pending ToS Clicked",
274                                        source = "Edit Prediction Status Button"
275                                    );
276                                    window.dispatch_action(
277                                        zed_actions::OpenZedPredictOnboarding.boxed_clone(),
278                                        cx,
279                                    );
280                                },
281                            ))
282                        }
283                        (Some(true), true, _) => base,
284                        (Some(true), false, true) => base.tooltip(|window, cx| {
285                            Tooltip::for_action("Edit Prediction", &ToggleMenu, window, cx)
286                        }),
287                        (Some(true), false, false) => base.tooltip(|window, cx| {
288                            Tooltip::with_meta(
289                                "Edit Prediction",
290                                Some(&ToggleMenu),
291                                "Disabled For This File",
292                                window,
293                                cx,
294                            )
295                        }),
296                    }
297                };
298
299                let this = cx.entity().clone();
300
301                let mut popover_menu = PopoverMenu::new("zeta")
302                    .menu(move |window, cx| {
303                        Some(this.update(cx, |this, cx| this.build_zeta_context_menu(window, cx)))
304                    })
305                    .anchor(Corner::BottomRight)
306                    .with_handle(self.popover_menu_handle.clone());
307
308                let is_refreshing = self
309                    .inline_completion_provider
310                    .as_ref()
311                    .map_or(false, |provider| provider.is_refreshing(cx));
312
313                if is_refreshing {
314                    popover_menu = popover_menu.trigger(
315                        icon_button().with_animation(
316                            "pulsating-label",
317                            Animation::new(Duration::from_secs(2))
318                                .repeat()
319                                .with_easing(pulsating_between(0.2, 1.0)),
320                            |icon_button, delta| icon_button.alpha(delta),
321                        ),
322                    );
323                } else {
324                    popover_menu = popover_menu.trigger(icon_button());
325                }
326
327                div().child(popover_menu.into_any_element())
328            }
329        }
330    }
331}
332
333impl InlineCompletionButton {
334    pub fn new(
335        workspace: WeakEntity<Workspace>,
336        fs: Arc<dyn Fs>,
337        user_store: Entity<UserStore>,
338        popover_menu_handle: PopoverMenuHandle<ContextMenu>,
339        cx: &mut Context<Self>,
340    ) -> Self {
341        if let Some(copilot) = Copilot::global(cx) {
342            cx.observe(&copilot, |_, _, cx| cx.notify()).detach()
343        }
344
345        cx.observe_global::<SettingsStore>(move |_, cx| cx.notify())
346            .detach();
347
348        Self {
349            editor_subscription: None,
350            editor_enabled: None,
351            editor_focus_handle: None,
352            language: None,
353            file: None,
354            inline_completion_provider: None,
355            popover_menu_handle,
356            workspace,
357            fs,
358            user_store,
359        }
360    }
361
362    pub fn build_copilot_start_menu(
363        &mut self,
364        window: &mut Window,
365        cx: &mut Context<Self>,
366    ) -> Entity<ContextMenu> {
367        let fs = self.fs.clone();
368        ContextMenu::build(window, cx, |menu, _, _| {
369            menu.entry("Sign In", None, copilot::initiate_sign_in)
370                .entry("Disable Copilot", None, {
371                    let fs = fs.clone();
372                    move |_window, cx| hide_copilot(fs.clone(), cx)
373                })
374                .entry("Use Supermaven", None, {
375                    let fs = fs.clone();
376                    move |_window, cx| {
377                        set_completion_provider(
378                            fs.clone(),
379                            cx,
380                            InlineCompletionProvider::Supermaven,
381                        )
382                    }
383                })
384        })
385    }
386
387    pub fn build_language_settings_menu(&self, mut menu: ContextMenu, cx: &mut App) -> ContextMenu {
388        let fs = self.fs.clone();
389
390        menu = menu.header("Show Edit Predictions For");
391
392        if let Some(language) = self.language.clone() {
393            let fs = fs.clone();
394            let language_enabled =
395                language_settings::language_settings(Some(language.name()), None, cx)
396                    .show_inline_completions;
397
398            menu = menu.toggleable_entry(
399                language.name(),
400                language_enabled,
401                IconPosition::End,
402                None,
403                move |_, cx| {
404                    toggle_show_inline_completions_for_language(language.clone(), fs.clone(), cx)
405                },
406            );
407        }
408
409        let settings = AllLanguageSettings::get_global(cx);
410        let globally_enabled = settings.show_inline_completions(None, cx);
411        menu = menu.toggleable_entry(
412            "All Files",
413            globally_enabled,
414            IconPosition::End,
415            None,
416            move |_, cx| toggle_inline_completions_globally(fs.clone(), cx),
417        );
418        menu = menu.separator().header("Privacy Settings");
419
420        if let Some(provider) = &self.inline_completion_provider {
421            let data_collection = provider.data_collection_state(cx);
422            if data_collection.is_supported() {
423                let provider = provider.clone();
424                let enabled = data_collection.is_enabled();
425
426                menu = menu.item(
427                    // TODO: We want to add something later that communicates whether
428                    // the current project is open-source.
429                    ContextMenuEntry::new("Share Training Data")
430                        .toggleable(IconPosition::End, data_collection.is_enabled())
431                        .documentation_aside(|_| {
432                            Label::new("Zed automatically detects if your project is open-source. This setting is only applicable in such cases.").into_any_element()
433                        })
434                        .handler(move |_, cx| {
435                            provider.toggle_data_collection(cx);
436
437                            if !enabled {
438                                telemetry::event!(
439                                    "Data Collection Enabled",
440                                    source = "Edit Prediction Status Menu"
441                                );
442                            } else {
443                                telemetry::event!(
444                                    "Data Collection Disabled",
445                                    source = "Edit Prediction Status Menu"
446                                );
447                            }
448                        })
449                )
450            }
451        }
452
453        menu = menu.item(
454            ContextMenuEntry::new("Configure Excluded Files")
455                .documentation_aside(|_| {
456                    Label::new("This item takes you to the settings where you can specify files that will never be captured by any edit prediction model. List both specific file extensions and individual file names.").into_any_element()
457                })
458                .handler(move |window, cx| {
459                    if let Some(workspace) = window.root().flatten() {
460                        let workspace = workspace.downgrade();
461                        window
462                            .spawn(cx, |cx| {
463                                open_disabled_globs_setting_in_editor(
464                                    workspace,
465                                    cx,
466                                )
467                            })
468                            .detach_and_log_err(cx);
469                    }
470                }),
471        );
472
473        if self.file.as_ref().map_or(false, |file| {
474            !all_language_settings(Some(file), cx).inline_completions_enabled_for_path(file.path())
475        }) {
476            menu = menu.item(
477                ContextMenuEntry::new("This file is excluded.")
478                    .disabled(true)
479                    .icon(IconName::ZedPredictDisabled)
480                    .icon_size(IconSize::Small),
481            );
482        }
483
484        if let Some(editor_focus_handle) = self.editor_focus_handle.clone() {
485            menu = menu
486                .separator()
487                .entry(
488                    "Predict Edit at Cursor",
489                    Some(Box::new(ShowInlineCompletion)),
490                    {
491                        let editor_focus_handle = editor_focus_handle.clone();
492
493                        move |window, cx| {
494                            editor_focus_handle.dispatch_action(&ShowInlineCompletion, window, cx);
495                        }
496                    },
497                )
498                .context(editor_focus_handle);
499        }
500
501        menu
502    }
503
504    fn build_copilot_context_menu(
505        &self,
506        window: &mut Window,
507        cx: &mut Context<Self>,
508    ) -> Entity<ContextMenu> {
509        ContextMenu::build(window, cx, |menu, _, cx| {
510            self.build_language_settings_menu(menu, cx)
511                .separator()
512                .link(
513                    "Go to Copilot Settings",
514                    OpenBrowser {
515                        url: COPILOT_SETTINGS_URL.to_string(),
516                    }
517                    .boxed_clone(),
518                )
519                .action("Sign Out", copilot::SignOut.boxed_clone())
520        })
521    }
522
523    fn build_supermaven_context_menu(
524        &self,
525        window: &mut Window,
526        cx: &mut Context<Self>,
527    ) -> Entity<ContextMenu> {
528        ContextMenu::build(window, cx, |menu, _, cx| {
529            self.build_language_settings_menu(menu, cx)
530                .separator()
531                .action("Sign Out", supermaven::SignOut.boxed_clone())
532        })
533    }
534
535    fn build_zeta_context_menu(
536        &self,
537        window: &mut Window,
538        cx: &mut Context<Self>,
539    ) -> Entity<ContextMenu> {
540        let workspace = self.workspace.clone();
541        ContextMenu::build(window, cx, |menu, _window, cx| {
542            self.build_language_settings_menu(menu, cx).when(
543                cx.has_flag::<PredictEditsRateCompletionsFeatureFlag>(),
544                |this| {
545                    this.entry(
546                        "Rate Completions",
547                        Some(RateCompletions.boxed_clone()),
548                        move |window, cx| {
549                            workspace
550                                .update(cx, |workspace, cx| {
551                                    RateCompletionModal::toggle(workspace, window, cx)
552                                })
553                                .ok();
554                        },
555                    )
556                },
557            )
558        })
559    }
560
561    pub fn update_enabled(&mut self, editor: Entity<Editor>, cx: &mut Context<Self>) {
562        let editor = editor.read(cx);
563        let snapshot = editor.buffer().read(cx).snapshot(cx);
564        let suggestion_anchor = editor.selections.newest_anchor().start;
565        let language = snapshot.language_at(suggestion_anchor);
566        let file = snapshot.file_at(suggestion_anchor).cloned();
567        self.editor_enabled = {
568            let file = file.as_ref();
569            Some(
570                file.map(|file| {
571                    all_language_settings(Some(file), cx)
572                        .inline_completions_enabled_for_path(file.path())
573                })
574                .unwrap_or(true),
575            )
576        };
577        self.inline_completion_provider = editor.inline_completion_provider();
578        self.language = language.cloned();
579        self.file = file;
580        self.editor_focus_handle = Some(editor.focus_handle(cx));
581
582        cx.notify();
583    }
584
585    pub fn toggle_menu(&mut self, window: &mut Window, cx: &mut Context<Self>) {
586        self.popover_menu_handle.toggle(window, cx);
587    }
588}
589
590impl StatusItemView for InlineCompletionButton {
591    fn set_active_pane_item(
592        &mut self,
593        item: Option<&dyn ItemHandle>,
594        _: &mut Window,
595        cx: &mut Context<Self>,
596    ) {
597        if let Some(editor) = item.and_then(|item| item.act_as::<Editor>(cx)) {
598            self.editor_subscription = Some((
599                cx.observe(&editor, Self::update_enabled),
600                editor.entity_id().as_u64() as usize,
601            ));
602            self.update_enabled(editor, cx);
603        } else {
604            self.language = None;
605            self.editor_subscription = None;
606            self.editor_enabled = None;
607        }
608        cx.notify();
609    }
610}
611
612impl SupermavenButtonStatus {
613    fn to_icon(&self) -> IconName {
614        match self {
615            SupermavenButtonStatus::Ready => IconName::Supermaven,
616            SupermavenButtonStatus::Errored(_) => IconName::SupermavenError,
617            SupermavenButtonStatus::NeedsActivation(_) => IconName::SupermavenInit,
618            SupermavenButtonStatus::Initializing => IconName::SupermavenInit,
619        }
620    }
621
622    fn to_tooltip(&self) -> String {
623        match self {
624            SupermavenButtonStatus::Ready => "Supermaven is ready".to_string(),
625            SupermavenButtonStatus::Errored(error) => format!("Supermaven error: {}", error),
626            SupermavenButtonStatus::NeedsActivation(_) => "Supermaven needs activation".to_string(),
627            SupermavenButtonStatus::Initializing => "Supermaven initializing".to_string(),
628        }
629    }
630
631    fn has_menu(&self) -> bool {
632        match self {
633            SupermavenButtonStatus::Ready | SupermavenButtonStatus::NeedsActivation(_) => true,
634            SupermavenButtonStatus::Errored(_) | SupermavenButtonStatus::Initializing => false,
635        }
636    }
637}
638
639async fn open_disabled_globs_setting_in_editor(
640    workspace: WeakEntity<Workspace>,
641    mut cx: AsyncWindowContext,
642) -> Result<()> {
643    let settings_editor = workspace
644        .update_in(&mut cx, |_, window, cx| {
645            create_and_open_local_file(paths::settings_file(), window, cx, || {
646                settings::initial_user_settings_content().as_ref().into()
647            })
648        })?
649        .await?
650        .downcast::<Editor>()
651        .unwrap();
652
653    settings_editor
654        .downgrade()
655        .update_in(&mut cx, |item, window, cx| {
656            let text = item.buffer().read(cx).snapshot(cx).text();
657
658            let settings = cx.global::<SettingsStore>();
659
660            // Ensure that we always have "inline_completions { "disabled_globs": [] }"
661            let edits = settings.edits_for_update::<AllLanguageSettings>(&text, |file| {
662                file.inline_completions
663                    .get_or_insert_with(Default::default)
664                    .disabled_globs
665                    .get_or_insert_with(Vec::new);
666            });
667
668            if !edits.is_empty() {
669                item.edit(edits.iter().cloned(), cx);
670            }
671
672            let text = item.buffer().read(cx).snapshot(cx).text();
673
674            static DISABLED_GLOBS_REGEX: LazyLock<Regex> = LazyLock::new(|| {
675                Regex::new(r#""disabled_globs":\s*\[\s*(?P<content>(?:.|\n)*?)\s*\]"#).unwrap()
676            });
677            // Only capture [...]
678            let range = DISABLED_GLOBS_REGEX.captures(&text).and_then(|captures| {
679                captures
680                    .name("content")
681                    .map(|inner_match| inner_match.start()..inner_match.end())
682            });
683            if let Some(range) = range {
684                item.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
685                    selections.select_ranges(vec![range]);
686                });
687            }
688        })?;
689
690    anyhow::Ok(())
691}
692
693fn toggle_inline_completions_globally(fs: Arc<dyn Fs>, cx: &mut App) {
694    let show_inline_completions = all_language_settings(None, cx).show_inline_completions(None, cx);
695    update_settings_file::<AllLanguageSettings>(fs, cx, move |file, _| {
696        file.defaults.show_inline_completions = Some(!show_inline_completions)
697    });
698}
699
700fn set_completion_provider(fs: Arc<dyn Fs>, cx: &mut App, provider: InlineCompletionProvider) {
701    update_settings_file::<AllLanguageSettings>(fs, cx, move |file, _| {
702        file.features
703            .get_or_insert(Default::default())
704            .inline_completion_provider = Some(provider);
705    });
706}
707
708fn toggle_show_inline_completions_for_language(
709    language: Arc<Language>,
710    fs: Arc<dyn Fs>,
711    cx: &mut App,
712) {
713    let show_inline_completions =
714        all_language_settings(None, cx).show_inline_completions(Some(&language), cx);
715    update_settings_file::<AllLanguageSettings>(fs, cx, move |file, _| {
716        file.languages
717            .entry(language.name())
718            .or_default()
719            .show_inline_completions = Some(!show_inline_completions);
720    });
721}
722
723fn hide_copilot(fs: Arc<dyn Fs>, cx: &mut App) {
724    update_settings_file::<AllLanguageSettings>(fs, cx, move |file, _| {
725        file.features
726            .get_or_insert(Default::default())
727            .inline_completion_provider = Some(InlineCompletionProvider::None);
728    });
729}