logiguard fork: GPUI xdg-activation keyboard-focus serial fix
Some checks failed
Update All Top Ranking Issues / update_top_ranking_issues (push) Has been cancelled
Triage Project Sync (#84) / Sync triage project (push) Has been cancelled
release_nightly / notify_on_failure (push) Has been cancelled
release_nightly / check_style (push) Has been cancelled
release_nightly / run_tests_windows (push) Has been cancelled
release_nightly / clippy_windows (push) Has been cancelled
release_nightly / bundle_linux_aarch64 (push) Has been cancelled
release_nightly / bundle_linux_x86_64 (push) Has been cancelled
release_nightly / bundle_mac_aarch64 (push) Has been cancelled
release_nightly / bundle_mac_x86_64 (push) Has been cancelled
release_nightly / bundle_windows_aarch64 (push) Has been cancelled
release_nightly / bundle_windows_x86_64 (push) Has been cancelled
release_nightly / build_nix_linux_x86_64 (push) Has been cancelled
release_nightly / build_nix_mac_aarch64 (push) Has been cancelled
release_nightly / update_nightly_tag (push) Has been cancelled
Hotfix Review Monitor / check-hotfix-reviews (push) Has been cancelled
Stale PR Review Reminder / check-stale-prs (push) Has been cancelled
Update Weekly Top Ranking Issues / update_top_ranking_issues (push) Has been cancelled
Bump collab-staging Tag / update-collab-staging-tag (push) Has been cancelled
compliance_check / scheduled_compliance_check (push) Has been cancelled
Some checks failed
Update All Top Ranking Issues / update_top_ranking_issues (push) Has been cancelled
Triage Project Sync (#84) / Sync triage project (push) Has been cancelled
release_nightly / notify_on_failure (push) Has been cancelled
release_nightly / check_style (push) Has been cancelled
release_nightly / run_tests_windows (push) Has been cancelled
release_nightly / clippy_windows (push) Has been cancelled
release_nightly / bundle_linux_aarch64 (push) Has been cancelled
release_nightly / bundle_linux_x86_64 (push) Has been cancelled
release_nightly / bundle_mac_aarch64 (push) Has been cancelled
release_nightly / bundle_mac_x86_64 (push) Has been cancelled
release_nightly / bundle_windows_aarch64 (push) Has been cancelled
release_nightly / bundle_windows_x86_64 (push) Has been cancelled
release_nightly / build_nix_linux_x86_64 (push) Has been cancelled
release_nightly / build_nix_mac_aarch64 (push) Has been cancelled
release_nightly / update_nightly_tag (push) Has been cancelled
Hotfix Review Monitor / check-hotfix-reviews (push) Has been cancelled
Stale PR Review Reminder / check-stale-prs (push) Has been cancelled
Update Weekly Top Ranking Issues / update_top_ranking_issues (push) Has been cancelled
Bump collab-staging Tag / update-collab-staging-tag (push) Has been cancelled
compliance_check / scheduled_compliance_check (push) Has been cancelled
Single-commit orphan branch: full zed-industries/zed @ 8c74db0 source tree with a 3-file patch applied (no upstream history). Patch (crates/gpui_linux/src/linux/wayland/): - serial.rs: add SerialKind::KeyboardEnter - client.rs: store wl_keyboard.enter serial; add latest_serial_of() - window.rs: activate() uses keyboard-enter serial (Mutter focus gate) Mutter honors window activation only when the token carries the keyboard- focus serial from wl_keyboard.enter; GPUI used a stale mouse-press serial. See docs/tray-window-focus-wayland.md in logiguard. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
61
crates/edit_prediction_ui/Cargo.toml
Normal file
61
crates/edit_prediction_ui/Cargo.toml
Normal file
@@ -0,0 +1,61 @@
|
||||
[package]
|
||||
name = "edit_prediction_ui"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
publish.workspace = true
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[lib]
|
||||
path = "src/edit_prediction_ui.rs"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
buffer_diff.workspace = true
|
||||
collections.workspace = true
|
||||
time.workspace = true
|
||||
client.workspace = true
|
||||
cloud_llm_client.workspace = true
|
||||
codestral.workspace = true
|
||||
command_palette_hooks.workspace = true
|
||||
copilot.workspace = true
|
||||
copilot_chat.workspace = true
|
||||
copilot_ui.workspace = true
|
||||
edit_prediction_types.workspace = true
|
||||
edit_prediction.workspace = true
|
||||
editor.workspace = true
|
||||
feature_flags.workspace = true
|
||||
fs.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
indoc.workspace = true
|
||||
language.workspace = true
|
||||
markdown.workspace = true
|
||||
menu.workspace = true
|
||||
multi_buffer.workspace = true
|
||||
paths.workspace = true
|
||||
project.workspace = true
|
||||
regex.workspace = true
|
||||
settings.workspace = true
|
||||
telemetry.workspace = true
|
||||
text.workspace = true
|
||||
theme_settings.workspace = true
|
||||
ui.workspace = true
|
||||
util.workspace = true
|
||||
workspace.workspace = true
|
||||
zed_actions.workspace = true
|
||||
zeta_prompt.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
copilot = { workspace = true, features = ["test-support"] }
|
||||
editor = { workspace = true, features = ["test-support"] }
|
||||
futures.workspace = true
|
||||
indoc.workspace = true
|
||||
project = { workspace = true, features = ["test-support"] }
|
||||
theme = { workspace = true, features = ["test-support"] }
|
||||
workspace = { workspace = true, features = ["test-support"] }
|
||||
|
||||
|
||||
1
crates/edit_prediction_ui/LICENSE-GPL
Symbolic link
1
crates/edit_prediction_ui/LICENSE-GPL
Symbolic link
@@ -0,0 +1 @@
|
||||
../../LICENSE-GPL
|
||||
1730
crates/edit_prediction_ui/src/edit_prediction_button.rs
Normal file
1730
crates/edit_prediction_ui/src/edit_prediction_button.rs
Normal file
File diff suppressed because it is too large
Load Diff
417
crates/edit_prediction_ui/src/edit_prediction_context_view.rs
Normal file
417
crates/edit_prediction_ui/src/edit_prediction_context_view.rs
Normal file
@@ -0,0 +1,417 @@
|
||||
use std::{
|
||||
any::TypeId,
|
||||
collections::VecDeque,
|
||||
ops::Add,
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use client::{Client, UserStore};
|
||||
use editor::{
|
||||
Editor, PathKey,
|
||||
display_map::{BlockPlacement, BlockProperties, BlockStyle},
|
||||
};
|
||||
use futures::StreamExt as _;
|
||||
use gpui::{
|
||||
Animation, AnimationExt, App, AppContext as _, Context, Entity, EventEmitter, FocusHandle,
|
||||
Focusable, InteractiveElement as _, IntoElement as _, ParentElement as _, SharedString,
|
||||
Styled as _, Task, TextAlign, Window, actions, div, pulsating_between,
|
||||
};
|
||||
use multi_buffer::{Anchor, MultiBuffer};
|
||||
use project::Project;
|
||||
use text::Point;
|
||||
use ui::{
|
||||
ButtonCommon, Clickable, Disableable, FluentBuilder as _, IconButton, IconName,
|
||||
StyledTypography as _, h_flex, v_flex,
|
||||
};
|
||||
|
||||
use edit_prediction::{
|
||||
ContextRetrievalFinishedDebugEvent, ContextRetrievalStartedDebugEvent, DebugEvent,
|
||||
EditPredictionStore,
|
||||
};
|
||||
use workspace::Item;
|
||||
|
||||
pub struct EditPredictionContextView {
|
||||
empty_focus_handle: FocusHandle,
|
||||
project: Entity<Project>,
|
||||
store: Entity<EditPredictionStore>,
|
||||
runs: VecDeque<RetrievalRun>,
|
||||
current_ix: usize,
|
||||
_update_task: Task<Result<()>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RetrievalRun {
|
||||
editor: Entity<Editor>,
|
||||
started_at: Instant,
|
||||
metadata: Vec<(&'static str, SharedString)>,
|
||||
finished_at: Option<Instant>,
|
||||
}
|
||||
|
||||
actions!(
|
||||
dev,
|
||||
[
|
||||
/// Go to the previous context retrieval run
|
||||
EditPredictionContextGoBack,
|
||||
/// Go to the next context retrieval run
|
||||
EditPredictionContextGoForward
|
||||
]
|
||||
);
|
||||
|
||||
impl EditPredictionContextView {
|
||||
pub fn new(
|
||||
project: Entity<Project>,
|
||||
client: &Arc<Client>,
|
||||
user_store: &Entity<UserStore>,
|
||||
window: &mut gpui::Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let store = EditPredictionStore::global(client, user_store, cx);
|
||||
|
||||
let mut debug_rx = store.update(cx, |store, cx| store.debug_info(&project, cx));
|
||||
let _update_task = cx.spawn_in(window, async move |this, cx| {
|
||||
while let Some(event) = debug_rx.next().await {
|
||||
this.update_in(cx, |this, window, cx| {
|
||||
this.handle_store_event(event, window, cx)
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
|
||||
Self {
|
||||
empty_focus_handle: cx.focus_handle(),
|
||||
project,
|
||||
runs: VecDeque::new(),
|
||||
current_ix: 0,
|
||||
store,
|
||||
_update_task,
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_store_event(
|
||||
&mut self,
|
||||
event: DebugEvent,
|
||||
window: &mut gpui::Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
match event {
|
||||
DebugEvent::ContextRetrievalStarted(info) => {
|
||||
if info.project_entity_id == self.project.entity_id() {
|
||||
self.handle_context_retrieval_started(info, window, cx);
|
||||
}
|
||||
}
|
||||
DebugEvent::ContextRetrievalFinished(info) => {
|
||||
if info.project_entity_id == self.project.entity_id() {
|
||||
self.handle_context_retrieval_finished(info, window, cx);
|
||||
}
|
||||
}
|
||||
DebugEvent::EditPredictionStarted(_) => {}
|
||||
DebugEvent::EditPredictionFinished(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_context_retrieval_started(
|
||||
&mut self,
|
||||
info: ContextRetrievalStartedDebugEvent,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
if self
|
||||
.runs
|
||||
.back()
|
||||
.is_some_and(|run| run.finished_at.is_none())
|
||||
{
|
||||
self.runs.pop_back();
|
||||
}
|
||||
|
||||
let multibuffer = cx.new(|_| MultiBuffer::new(language::Capability::ReadOnly));
|
||||
let editor = cx
|
||||
.new(|cx| Editor::for_multibuffer(multibuffer, Some(self.project.clone()), window, cx));
|
||||
|
||||
if self.runs.len() == 32 {
|
||||
self.runs.pop_front();
|
||||
}
|
||||
|
||||
self.runs.push_back(RetrievalRun {
|
||||
editor,
|
||||
started_at: info.timestamp,
|
||||
finished_at: None,
|
||||
metadata: Vec::new(),
|
||||
});
|
||||
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn handle_context_retrieval_finished(
|
||||
&mut self,
|
||||
info: ContextRetrievalFinishedDebugEvent,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let Some(run) = self.runs.back_mut() else {
|
||||
return;
|
||||
};
|
||||
|
||||
run.finished_at = Some(info.timestamp);
|
||||
run.metadata = info.metadata;
|
||||
|
||||
let related_files = self.store.update(cx, |store, cx| {
|
||||
store.context_for_project_with_buffers(&self.project, cx)
|
||||
});
|
||||
|
||||
let editor = run.editor.clone();
|
||||
let multibuffer = run.editor.read(cx).buffer().clone();
|
||||
|
||||
if self.current_ix + 2 == self.runs.len() {
|
||||
self.current_ix += 1;
|
||||
}
|
||||
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
let mut paths: Vec<(PathKey, _, Vec<_>, Vec<usize>, usize)> = Vec::new();
|
||||
for (related_file, buffer) in related_files {
|
||||
let orders = related_file
|
||||
.excerpts
|
||||
.iter()
|
||||
.map(|excerpt| excerpt.order)
|
||||
.collect::<Vec<_>>();
|
||||
let min_order = orders.iter().copied().min().unwrap_or(usize::MAX);
|
||||
let point_ranges = related_file
|
||||
.excerpts
|
||||
.iter()
|
||||
.map(|excerpt| {
|
||||
Point::new(excerpt.row_range.start, 0)..Point::new(excerpt.row_range.end, 0)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
cx.update(|_, cx| {
|
||||
let path = if let Some(file) = buffer.read(cx).file() {
|
||||
PathKey::with_sort_prefix(min_order as u64, file.path().clone())
|
||||
} else {
|
||||
PathKey::for_buffer(&buffer, cx)
|
||||
};
|
||||
paths.push((path, buffer, point_ranges, orders, min_order));
|
||||
})?;
|
||||
}
|
||||
|
||||
paths.sort_by_key(|(_, _, _, _, min_order)| *min_order);
|
||||
|
||||
let mut excerpt_anchors_with_orders: Vec<(Anchor, usize)> = Vec::new();
|
||||
|
||||
multibuffer.update(cx, |multibuffer, cx| {
|
||||
multibuffer.clear(cx);
|
||||
|
||||
for (path, buffer, ranges, orders, _) in paths {
|
||||
multibuffer.set_excerpts_for_path(path, buffer.clone(), ranges.clone(), 0, cx);
|
||||
let snapshot = multibuffer.snapshot(cx);
|
||||
let buffer_snapshot = buffer.read(cx).snapshot();
|
||||
for (range, order) in ranges.into_iter().zip(orders) {
|
||||
let text_anchor = buffer_snapshot.anchor_range_inside(range);
|
||||
if let Some(start) = snapshot.anchor_in_buffer(text_anchor.start) {
|
||||
excerpt_anchors_with_orders.push((start, order));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
editor.update_in(cx, |editor, window, cx| {
|
||||
let blocks = excerpt_anchors_with_orders
|
||||
.into_iter()
|
||||
.map(|(anchor, order)| {
|
||||
let label = SharedString::from(format!("order: {order}"));
|
||||
BlockProperties {
|
||||
placement: BlockPlacement::Above(anchor),
|
||||
height: Some(1),
|
||||
style: BlockStyle::Sticky,
|
||||
render: Arc::new(move |cx| {
|
||||
div()
|
||||
.pl(cx.anchor_x)
|
||||
.text_ui_xs(cx)
|
||||
.text_color(cx.editor_style.status.info)
|
||||
.child(label.clone())
|
||||
.into_any_element()
|
||||
}),
|
||||
priority: 0,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
editor.insert_blocks(blocks, None, cx);
|
||||
editor.move_to_beginning(&Default::default(), window, cx);
|
||||
})?;
|
||||
|
||||
this.update(cx, |_, cx| cx.notify())
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn handle_go_back(
|
||||
&mut self,
|
||||
_: &EditPredictionContextGoBack,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.current_ix = self.current_ix.saturating_sub(1);
|
||||
cx.focus_self(window);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn handle_go_forward(
|
||||
&mut self,
|
||||
_: &EditPredictionContextGoForward,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.current_ix = self
|
||||
.current_ix
|
||||
.add(1)
|
||||
.min(self.runs.len().saturating_sub(1));
|
||||
cx.focus_self(window);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn render_informational_footer(
|
||||
&self,
|
||||
cx: &mut Context<'_, EditPredictionContextView>,
|
||||
) -> ui::Div {
|
||||
let run = &self.runs[self.current_ix];
|
||||
let new_run_started = self
|
||||
.runs
|
||||
.back()
|
||||
.map_or(false, |latest_run| latest_run.finished_at.is_none());
|
||||
|
||||
h_flex()
|
||||
.p_2()
|
||||
.w_full()
|
||||
.font_buffer(cx)
|
||||
.text_xs()
|
||||
.border_t_1()
|
||||
.gap_2()
|
||||
.child(v_flex().h_full().flex_1().child({
|
||||
let t0 = run.started_at;
|
||||
let mut table = ui::Table::new(2).width(ui::px(300.)).no_ui_font();
|
||||
for (key, value) in &run.metadata {
|
||||
table = table.row(vec![
|
||||
key.into_any_element(),
|
||||
value.clone().into_any_element(),
|
||||
])
|
||||
}
|
||||
table = table.row(vec![
|
||||
"Total Time".into_any_element(),
|
||||
format!("{} ms", (run.finished_at.unwrap_or(t0) - t0).as_millis())
|
||||
.into_any_element(),
|
||||
]);
|
||||
table
|
||||
}))
|
||||
.child(
|
||||
v_flex().h_full().text_align(TextAlign::Right).child(
|
||||
h_flex()
|
||||
.justify_end()
|
||||
.child(
|
||||
IconButton::new("go-back", IconName::ChevronLeft)
|
||||
.disabled(self.current_ix == 0 || self.runs.len() < 2)
|
||||
.tooltip(ui::Tooltip::for_action_title(
|
||||
"Go to previous run",
|
||||
&EditPredictionContextGoBack,
|
||||
))
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
this.handle_go_back(&EditPredictionContextGoBack, window, cx);
|
||||
})),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.child(format!("{}/{}", self.current_ix + 1, self.runs.len()))
|
||||
.map(|this| {
|
||||
if new_run_started {
|
||||
this.with_animation(
|
||||
"pulsating-count",
|
||||
Animation::new(Duration::from_secs(2))
|
||||
.repeat()
|
||||
.with_easing(pulsating_between(0.4, 0.8)),
|
||||
|label, delta| label.opacity(delta),
|
||||
)
|
||||
.into_any_element()
|
||||
} else {
|
||||
this.into_any_element()
|
||||
}
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
IconButton::new("go-forward", IconName::ChevronRight)
|
||||
.disabled(self.current_ix + 1 == self.runs.len())
|
||||
.tooltip(ui::Tooltip::for_action_title(
|
||||
"Go to next run",
|
||||
&EditPredictionContextGoBack,
|
||||
))
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
this.handle_go_forward(
|
||||
&EditPredictionContextGoForward,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
})),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Focusable for EditPredictionContextView {
|
||||
fn focus_handle(&self, cx: &App) -> FocusHandle {
|
||||
self.runs
|
||||
.get(self.current_ix)
|
||||
.map(|run| run.editor.read(cx).focus_handle(cx))
|
||||
.unwrap_or_else(|| self.empty_focus_handle.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<()> for EditPredictionContextView {}
|
||||
|
||||
impl Item for EditPredictionContextView {
|
||||
type Event = ();
|
||||
|
||||
fn tab_content_text(&self, _detail: usize, _cx: &App) -> SharedString {
|
||||
"Edit Prediction Context".into()
|
||||
}
|
||||
|
||||
fn buffer_kind(&self, _cx: &App) -> workspace::item::ItemBufferKind {
|
||||
workspace::item::ItemBufferKind::Multibuffer
|
||||
}
|
||||
|
||||
fn act_as_type<'a>(
|
||||
&'a self,
|
||||
type_id: TypeId,
|
||||
self_handle: &'a Entity<Self>,
|
||||
_: &'a App,
|
||||
) -> Option<gpui::AnyEntity> {
|
||||
if type_id == TypeId::of::<Self>() {
|
||||
Some(self_handle.clone().into())
|
||||
} else if type_id == TypeId::of::<Editor>() {
|
||||
Some(self.runs.get(self.current_ix)?.editor.clone().into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl gpui::Render for EditPredictionContextView {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl ui::IntoElement {
|
||||
v_flex()
|
||||
.key_context("EditPredictionContext")
|
||||
.on_action(cx.listener(Self::handle_go_back))
|
||||
.on_action(cx.listener(Self::handle_go_forward))
|
||||
.size_full()
|
||||
.map(|this| {
|
||||
if self.runs.is_empty() {
|
||||
this.child(
|
||||
v_flex()
|
||||
.size_full()
|
||||
.justify_center()
|
||||
.items_center()
|
||||
.child("No retrieval runs yet"),
|
||||
)
|
||||
} else {
|
||||
this.child(self.runs[self.current_ix].editor.clone())
|
||||
.child(self.render_informational_footer(cx))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
198
crates/edit_prediction_ui/src/edit_prediction_ui.rs
Normal file
198
crates/edit_prediction_ui/src/edit_prediction_ui.rs
Normal file
@@ -0,0 +1,198 @@
|
||||
mod edit_prediction_button;
|
||||
mod edit_prediction_context_view;
|
||||
mod rate_prediction_modal;
|
||||
|
||||
use command_palette_hooks::CommandPaletteFilter;
|
||||
use edit_prediction::{EditPredictionStore, ResetOnboarding, capture_example};
|
||||
use edit_prediction_context_view::EditPredictionContextView;
|
||||
use editor::Editor;
|
||||
use feature_flags::FeatureFlagAppExt as _;
|
||||
use gpui::TaskExt;
|
||||
use gpui::actions;
|
||||
use language::language_settings::AllLanguageSettings;
|
||||
use project::DisableAiSettings;
|
||||
use rate_prediction_modal::RatePredictionsModal;
|
||||
use settings::{Settings as _, SettingsStore};
|
||||
use std::any::{Any as _, TypeId};
|
||||
use ui::{App, prelude::*};
|
||||
use workspace::{SplitDirection, Workspace};
|
||||
|
||||
pub use edit_prediction_button::{
|
||||
EditPredictionButton, ToggleMenu, get_available_providers, set_completion_provider,
|
||||
};
|
||||
|
||||
use crate::rate_prediction_modal::PredictEditsRatePredictionsFeatureFlag;
|
||||
|
||||
actions!(
|
||||
dev,
|
||||
[
|
||||
/// Opens the edit prediction context view.
|
||||
OpenEditPredictionContextView,
|
||||
]
|
||||
);
|
||||
|
||||
actions!(
|
||||
edit_prediction,
|
||||
[
|
||||
/// Opens the rate completions modal.
|
||||
RatePredictions,
|
||||
/// Captures an ExampleSpec from the current editing session and opens it as Markdown.
|
||||
CaptureExample,
|
||||
]
|
||||
);
|
||||
|
||||
pub fn init(cx: &mut App) {
|
||||
feature_gate_predict_edits_actions(cx);
|
||||
|
||||
cx.observe_new(move |workspace: &mut Workspace, _, _cx| {
|
||||
workspace.register_action(|workspace, _: &RatePredictions, window, cx| {
|
||||
if cx.has_flag::<PredictEditsRatePredictionsFeatureFlag>() {
|
||||
RatePredictionsModal::toggle(workspace, window, cx);
|
||||
}
|
||||
});
|
||||
|
||||
workspace.register_action(|workspace, _: &CaptureExample, window, cx| {
|
||||
capture_example_as_markdown(workspace, window, cx);
|
||||
});
|
||||
workspace.register_action_renderer(|div, _, _, cx| {
|
||||
div.on_action(cx.listener(
|
||||
move |workspace, _: &OpenEditPredictionContextView, window, cx| {
|
||||
let project = workspace.project();
|
||||
workspace.split_item(
|
||||
SplitDirection::Right,
|
||||
Box::new(cx.new(|cx| {
|
||||
EditPredictionContextView::new(
|
||||
project.clone(),
|
||||
workspace.client(),
|
||||
workspace.user_store(),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
},
|
||||
))
|
||||
});
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn feature_gate_predict_edits_actions(cx: &mut App) {
|
||||
let rate_completion_action_types = [TypeId::of::<RatePredictions>()];
|
||||
let reset_onboarding_action_types = [TypeId::of::<ResetOnboarding>()];
|
||||
let all_action_types = [
|
||||
TypeId::of::<RatePredictions>(),
|
||||
TypeId::of::<CaptureExample>(),
|
||||
TypeId::of::<edit_prediction::ResetOnboarding>(),
|
||||
zed_actions::OpenZedPredictOnboarding.type_id(),
|
||||
TypeId::of::<edit_prediction::ClearHistory>(),
|
||||
TypeId::of::<rate_prediction_modal::ThumbsUpActivePrediction>(),
|
||||
TypeId::of::<rate_prediction_modal::ThumbsDownActivePrediction>(),
|
||||
TypeId::of::<rate_prediction_modal::NextEdit>(),
|
||||
TypeId::of::<rate_prediction_modal::PreviousEdit>(),
|
||||
];
|
||||
|
||||
CommandPaletteFilter::update_global(cx, |filter, _cx| {
|
||||
filter.hide_action_types(&rate_completion_action_types);
|
||||
filter.hide_action_types(&reset_onboarding_action_types);
|
||||
filter.hide_action_types(&[zed_actions::OpenZedPredictOnboarding.type_id()]);
|
||||
});
|
||||
|
||||
cx.observe_global::<SettingsStore>(move |cx| {
|
||||
let is_ai_disabled = DisableAiSettings::get_global(cx).disable_ai;
|
||||
let has_feature_flag = cx.has_flag::<PredictEditsRatePredictionsFeatureFlag>();
|
||||
|
||||
CommandPaletteFilter::update_global(cx, |filter, _cx| {
|
||||
if is_ai_disabled {
|
||||
filter.hide_action_types(&all_action_types);
|
||||
} else if has_feature_flag {
|
||||
filter.show_action_types(&rate_completion_action_types);
|
||||
} else {
|
||||
filter.hide_action_types(&rate_completion_action_types);
|
||||
}
|
||||
});
|
||||
})
|
||||
.detach();
|
||||
|
||||
cx.observe_flag::<PredictEditsRatePredictionsFeatureFlag, _>(move |value, cx| {
|
||||
if !DisableAiSettings::get_global(cx).disable_ai {
|
||||
if *value {
|
||||
CommandPaletteFilter::update_global(cx, |filter, _cx| {
|
||||
filter.show_action_types(&rate_completion_action_types);
|
||||
});
|
||||
} else {
|
||||
CommandPaletteFilter::update_global(cx, |filter, _cx| {
|
||||
filter.hide_action_types(&rate_completion_action_types);
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn capture_example_as_markdown(
|
||||
workspace: &mut Workspace,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Workspace>,
|
||||
) -> Option<()> {
|
||||
let markdown_language = workspace
|
||||
.app_state()
|
||||
.languages
|
||||
.language_for_name("Markdown");
|
||||
|
||||
let fs = workspace.app_state().fs.clone();
|
||||
let project = workspace.project().clone();
|
||||
let editor = workspace.active_item_as::<Editor>(cx)?;
|
||||
let editor = editor.read(cx);
|
||||
let (buffer, cursor_anchor) = editor
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.text_anchor_for_position(editor.selections.newest_anchor().head(), cx)?;
|
||||
let ep_store = EditPredictionStore::try_global(cx)?;
|
||||
let events = ep_store.update(cx, |store, cx| store.edit_history_for_project(&project, cx));
|
||||
let example = capture_example(project.clone(), buffer, cursor_anchor, events, true, cx)?;
|
||||
|
||||
let examples_dir = AllLanguageSettings::get_global(cx)
|
||||
.edit_predictions
|
||||
.examples_dir
|
||||
.clone();
|
||||
|
||||
cx.spawn_in(window, async move |workspace_entity, cx| {
|
||||
let markdown_language = markdown_language.await?;
|
||||
let example_spec = example.await?;
|
||||
let buffer = if let Some(dir) = examples_dir {
|
||||
fs.create_dir(&dir).await.ok();
|
||||
let mut path = dir.join(&example_spec.name.replace(' ', "--").replace(':', "-"));
|
||||
path.set_extension("md");
|
||||
project
|
||||
.update(cx, |project, cx| project.open_local_buffer(&path, cx))
|
||||
.await?
|
||||
} else {
|
||||
project
|
||||
.update(cx, |project, cx| {
|
||||
project.create_buffer(Some(markdown_language.clone()), false, cx)
|
||||
})
|
||||
.await?
|
||||
};
|
||||
|
||||
buffer.update(cx, |buffer, cx| {
|
||||
buffer.set_text(example_spec.to_markdown(), cx);
|
||||
buffer.set_language(Some(markdown_language), cx);
|
||||
});
|
||||
workspace_entity.update_in(cx, |workspace, window, cx| {
|
||||
workspace.add_item_to_active_pane(
|
||||
Box::new(
|
||||
cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
|
||||
),
|
||||
None,
|
||||
true,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
})
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
None
|
||||
}
|
||||
1065
crates/edit_prediction_ui/src/rate_prediction_modal.rs
Normal file
1065
crates/edit_prediction_ui/src/rate_prediction_modal.rs
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user