edit_prediction_ui.rs

  1mod edit_prediction_button;
  2mod edit_prediction_context_view;
  3mod rate_prediction_modal;
  4
  5use command_palette_hooks::CommandPaletteFilter;
  6use edit_prediction::{ResetOnboarding, Zeta2FeatureFlag, capture_example};
  7use edit_prediction_context_view::EditPredictionContextView;
  8use editor::Editor;
  9use feature_flags::FeatureFlagAppExt as _;
 10use gpui::actions;
 11use language::language_settings::AllLanguageSettings;
 12use project::DisableAiSettings;
 13use rate_prediction_modal::RatePredictionsModal;
 14use settings::{Settings as _, SettingsStore};
 15use std::any::{Any as _, TypeId};
 16use ui::{App, prelude::*};
 17use workspace::{SplitDirection, Workspace};
 18
 19pub use edit_prediction_button::{EditPredictionButton, ToggleMenu};
 20
 21use crate::rate_prediction_modal::PredictEditsRatePredictionsFeatureFlag;
 22
 23actions!(
 24    dev,
 25    [
 26        /// Opens the edit prediction context view.
 27        OpenEditPredictionContextView,
 28    ]
 29);
 30
 31actions!(
 32    edit_prediction,
 33    [
 34        /// Opens the rate completions modal.
 35        RatePredictions,
 36        /// Captures an ExampleSpec from the current editing session and opens it as Markdown.
 37        CaptureExample,
 38    ]
 39);
 40
 41pub fn init(cx: &mut App) {
 42    feature_gate_predict_edits_actions(cx);
 43
 44    cx.observe_new(move |workspace: &mut Workspace, _, _cx| {
 45        workspace.register_action(|workspace, _: &RatePredictions, window, cx| {
 46            if cx.has_flag::<PredictEditsRatePredictionsFeatureFlag>() {
 47                RatePredictionsModal::toggle(workspace, window, cx);
 48            }
 49        });
 50
 51        workspace.register_action(|workspace, _: &CaptureExample, window, cx| {
 52            capture_example_as_markdown(workspace, window, cx);
 53        });
 54        workspace.register_action_renderer(|div, _, _, cx| {
 55            let has_flag = cx.has_flag::<Zeta2FeatureFlag>();
 56            div.when(has_flag, |div| {
 57                div.on_action(cx.listener(
 58                    move |workspace, _: &OpenEditPredictionContextView, window, cx| {
 59                        let project = workspace.project();
 60                        workspace.split_item(
 61                            SplitDirection::Right,
 62                            Box::new(cx.new(|cx| {
 63                                EditPredictionContextView::new(
 64                                    project.clone(),
 65                                    workspace.client(),
 66                                    workspace.user_store(),
 67                                    window,
 68                                    cx,
 69                                )
 70                            })),
 71                            window,
 72                            cx,
 73                        );
 74                    },
 75                ))
 76            })
 77        });
 78    })
 79    .detach();
 80}
 81
 82fn feature_gate_predict_edits_actions(cx: &mut App) {
 83    let rate_completion_action_types = [TypeId::of::<RatePredictions>()];
 84    let reset_onboarding_action_types = [TypeId::of::<ResetOnboarding>()];
 85    let all_action_types = [
 86        TypeId::of::<RatePredictions>(),
 87        TypeId::of::<CaptureExample>(),
 88        TypeId::of::<edit_prediction::ResetOnboarding>(),
 89        zed_actions::OpenZedPredictOnboarding.type_id(),
 90        TypeId::of::<edit_prediction::ClearHistory>(),
 91        TypeId::of::<rate_prediction_modal::ThumbsUpActivePrediction>(),
 92        TypeId::of::<rate_prediction_modal::ThumbsDownActivePrediction>(),
 93        TypeId::of::<rate_prediction_modal::NextEdit>(),
 94        TypeId::of::<rate_prediction_modal::PreviousEdit>(),
 95    ];
 96
 97    CommandPaletteFilter::update_global(cx, |filter, _cx| {
 98        filter.hide_action_types(&rate_completion_action_types);
 99        filter.hide_action_types(&reset_onboarding_action_types);
100        filter.hide_action_types(&[zed_actions::OpenZedPredictOnboarding.type_id()]);
101    });
102
103    cx.observe_global::<SettingsStore>(move |cx| {
104        let is_ai_disabled = DisableAiSettings::get_global(cx).disable_ai;
105        let has_feature_flag = cx.has_flag::<PredictEditsRatePredictionsFeatureFlag>();
106
107        CommandPaletteFilter::update_global(cx, |filter, _cx| {
108            if is_ai_disabled {
109                filter.hide_action_types(&all_action_types);
110            } else if has_feature_flag {
111                filter.show_action_types(&rate_completion_action_types);
112            } else {
113                filter.hide_action_types(&rate_completion_action_types);
114            }
115        });
116    })
117    .detach();
118
119    cx.observe_flag::<PredictEditsRatePredictionsFeatureFlag, _>(move |is_enabled, cx| {
120        if !DisableAiSettings::get_global(cx).disable_ai {
121            if is_enabled {
122                CommandPaletteFilter::update_global(cx, |filter, _cx| {
123                    filter.show_action_types(&rate_completion_action_types);
124                });
125            } else {
126                CommandPaletteFilter::update_global(cx, |filter, _cx| {
127                    filter.hide_action_types(&rate_completion_action_types);
128                });
129            }
130        }
131    })
132    .detach();
133}
134
135fn capture_example_as_markdown(
136    workspace: &mut Workspace,
137    window: &mut Window,
138    cx: &mut Context<Workspace>,
139) -> Option<()> {
140    let markdown_language = workspace
141        .app_state()
142        .languages
143        .language_for_name("Markdown");
144
145    let fs = workspace.app_state().fs.clone();
146    let project = workspace.project().clone();
147    let editor = workspace.active_item_as::<Editor>(cx)?;
148    let editor = editor.read(cx);
149    let (buffer, cursor_anchor) = editor
150        .buffer()
151        .read(cx)
152        .text_anchor_for_position(editor.selections.newest_anchor().head(), cx)?;
153    let example = capture_example(project.clone(), buffer, cursor_anchor, true, cx)?;
154
155    let examples_dir = AllLanguageSettings::get_global(cx)
156        .edit_predictions
157        .examples_dir
158        .clone();
159
160    cx.spawn_in(window, async move |workspace_entity, cx| {
161        let markdown_language = markdown_language.await?;
162        let example_spec = example.await?;
163        let buffer = if let Some(dir) = examples_dir {
164            fs.create_dir(&dir).await.ok();
165            let mut path = dir.join(&example_spec.name.replace(' ', "--").replace(':', "-"));
166            path.set_extension("md");
167            project.update(cx, |project, cx| project.open_local_buffer(&path, cx))
168        } else {
169            project.update(cx, |project, cx| project.create_buffer(false, cx))
170        }?
171        .await?;
172
173        buffer.update(cx, |buffer, cx| {
174            buffer.set_text(example_spec.to_markdown(), cx);
175            buffer.set_language(Some(markdown_language), cx);
176        })?;
177        workspace_entity.update_in(cx, |workspace, window, cx| {
178            workspace.add_item_to_active_pane(
179                Box::new(
180                    cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
181                ),
182                None,
183                true,
184                window,
185                cx,
186            );
187        })
188    })
189    .detach_and_log_err(cx);
190    None
191}