refactoring_modal.rs

  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}