1use crate::refactoring_assistant::RefactoringAssistant;
2use collections::HashSet;
3use editor::{
4 display_map::{BlockContext, BlockDisposition, BlockProperties, BlockStyle},
5 scroll::autoscroll::{Autoscroll, AutoscrollStrategy},
6 Editor,
7};
8use gpui::{
9 actions, elements::*, platform::MouseButton, AnyViewHandle, AppContext, Entity, View,
10 ViewContext, ViewHandle, WeakViewHandle,
11};
12use std::sync::Arc;
13use workspace::Workspace;
14
15actions!(assistant, [Refactor]);
16
17pub fn init(cx: &mut AppContext) {
18 cx.add_action(RefactoringModal::deploy);
19 cx.add_action(RefactoringModal::confirm);
20 cx.add_action(RefactoringModal::cancel);
21}
22
23enum Event {
24 Dismissed,
25}
26
27struct RefactoringModal {
28 active_editor: WeakViewHandle<Editor>,
29 prompt_editor: ViewHandle<Editor>,
30 has_focus: bool,
31}
32
33impl Entity for RefactoringModal {
34 type Event = Event;
35}
36
37impl View for RefactoringModal {
38 fn ui_name() -> &'static str {
39 "RefactoringModal"
40 }
41
42 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
43 let theme = theme::current(cx);
44 ChildView::new(&self.prompt_editor, cx)
45 .aligned()
46 .left()
47 .contained()
48 .with_style(theme.assistant.modal.container)
49 .mouse::<Self>(0)
50 .on_click_out(MouseButton::Left, |_, _, cx| cx.emit(Event::Dismissed))
51 .on_click_out(MouseButton::Right, |_, _, cx| cx.emit(Event::Dismissed))
52 .into_any()
53 }
54
55 fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
56 self.has_focus = true;
57 cx.focus(&self.prompt_editor);
58 }
59
60 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
61 if !self.prompt_editor.is_focused(cx) {
62 self.has_focus = false;
63 cx.emit(Event::Dismissed);
64 }
65 }
66}
67
68impl RefactoringModal {
69 fn deploy(workspace: &mut Workspace, _: &Refactor, cx: &mut ViewContext<Workspace>) {
70 if let Some(active_editor) = workspace
71 .active_item(cx)
72 .and_then(|item| item.act_as::<Editor>(cx))
73 {
74 active_editor.update(cx, |editor, cx| {
75 let position = editor.selections.newest_anchor().head();
76 let prompt_editor = cx.add_view(|cx| {
77 Editor::single_line(
78 Some(Arc::new(|theme| theme.assistant.modal.editor.clone())),
79 cx,
80 )
81 });
82 let active_editor = cx.weak_handle();
83 let refactoring = cx.add_view(|_| RefactoringModal {
84 active_editor,
85 prompt_editor,
86 has_focus: false,
87 });
88 cx.focus(&refactoring);
89
90 let block_id = editor.insert_blocks(
91 [BlockProperties {
92 style: BlockStyle::Flex,
93 position,
94 height: 2,
95 render: Arc::new({
96 let refactoring = refactoring.clone();
97 move |cx: &mut BlockContext| {
98 ChildView::new(&refactoring, cx)
99 .contained()
100 .with_padding_left(cx.gutter_width)
101 .into_any()
102 }
103 }),
104 disposition: BlockDisposition::Below,
105 }],
106 Some(Autoscroll::Strategy(AutoscrollStrategy::Newest)),
107 cx,
108 )[0];
109 cx.subscribe(&refactoring, move |_, refactoring, event, cx| {
110 let Event::Dismissed = event;
111 if let Some(active_editor) = refactoring.read(cx).active_editor.upgrade(cx) {
112 cx.window_context().defer(move |cx| {
113 active_editor.update(cx, |editor, cx| {
114 editor.remove_blocks(HashSet::from_iter([block_id]), None, cx);
115 })
116 });
117 }
118 })
119 .detach();
120 });
121 }
122 }
123
124 fn cancel(&mut self, _: &editor::Cancel, cx: &mut ViewContext<Self>) {
125 cx.emit(Event::Dismissed);
126 }
127
128 fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
129 if let Some(editor) = self.active_editor.upgrade(cx) {
130 let prompt = self.prompt_editor.read(cx).text(cx);
131 RefactoringAssistant::update(cx, |assistant, cx| {
132 assistant.refactor(&editor, &prompt, cx);
133 });
134 cx.emit(Event::Dismissed);
135 }
136 }
137}