lib.rs

  1use buffer::{Bias, Point, Selection};
  2use editor::{Autoscroll, Editor, EditorSettings};
  3use gpui::{
  4    action, elements::*, geometry::vector::Vector2F, keymap::Binding, Axis, Entity,
  5    MutableAppContext, RenderContext, View, ViewContext, ViewHandle,
  6};
  7use postage::watch;
  8use workspace::{Settings, Workspace};
  9
 10action!(Toggle);
 11action!(Confirm);
 12
 13pub fn init(cx: &mut MutableAppContext) {
 14    cx.add_bindings([
 15        Binding::new("ctrl-g", Toggle, Some("Editor")),
 16        Binding::new("escape", Toggle, Some("GoToLine")),
 17        Binding::new("enter", Confirm, Some("GoToLine")),
 18    ]);
 19    cx.add_action(GoToLine::toggle);
 20    cx.add_action(GoToLine::confirm);
 21}
 22
 23pub struct GoToLine {
 24    settings: watch::Receiver<Settings>,
 25    line_editor: ViewHandle<Editor>,
 26    active_editor: ViewHandle<Editor>,
 27    restore_state: Option<RestoreState>,
 28    line_selection: Option<Selection<usize>>,
 29    cursor_point: Point,
 30    max_point: Point,
 31}
 32
 33struct RestoreState {
 34    scroll_position: Vector2F,
 35    selections: Vec<Selection<usize>>,
 36}
 37
 38pub enum Event {
 39    Dismissed,
 40}
 41
 42impl GoToLine {
 43    pub fn new(
 44        active_editor: ViewHandle<Editor>,
 45        settings: watch::Receiver<Settings>,
 46        cx: &mut ViewContext<Self>,
 47    ) -> Self {
 48        let line_editor = cx.add_view(|cx| {
 49            Editor::single_line(
 50                {
 51                    let settings = settings.clone();
 52                    move |_| {
 53                        let settings = settings.borrow();
 54                        EditorSettings {
 55                            tab_size: settings.tab_size,
 56                            style: settings.theme.selector.input_editor.as_editor(),
 57                        }
 58                    }
 59                },
 60                cx,
 61            )
 62        });
 63        cx.subscribe(&line_editor, Self::on_line_editor_event)
 64            .detach();
 65
 66        let (restore_state, cursor_point, max_point) = active_editor.update(cx, |editor, cx| {
 67            let restore_state = Some(RestoreState {
 68                scroll_position: editor.scroll_position(cx),
 69                selections: editor.selections::<usize>(cx).collect(),
 70            });
 71
 72            (
 73                restore_state,
 74                editor.newest_selection(cx).head(),
 75                editor.buffer().read(cx).max_point(),
 76            )
 77        });
 78
 79        Self {
 80            settings: settings.clone(),
 81            line_editor,
 82            active_editor,
 83            restore_state,
 84            line_selection: None,
 85            cursor_point,
 86            max_point,
 87        }
 88    }
 89
 90    fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>) {
 91        workspace.toggle_modal(cx, |cx, workspace| {
 92            let editor = workspace
 93                .active_item(cx)
 94                .unwrap()
 95                .to_any()
 96                .downcast::<Editor>()
 97                .unwrap();
 98            let view = cx.add_view(|cx| GoToLine::new(editor, workspace.settings.clone(), cx));
 99            cx.subscribe(&view, Self::on_event).detach();
100            view
101        });
102    }
103
104    fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
105        self.restore_state.take();
106        cx.emit(Event::Dismissed);
107    }
108
109    fn on_event(
110        workspace: &mut Workspace,
111        _: ViewHandle<Self>,
112        event: &Event,
113        cx: &mut ViewContext<Workspace>,
114    ) {
115        match event {
116            Event::Dismissed => workspace.dismiss_modal(cx),
117        }
118    }
119
120    fn on_line_editor_event(
121        &mut self,
122        _: ViewHandle<Editor>,
123        event: &editor::Event,
124        cx: &mut ViewContext<Self>,
125    ) {
126        match event {
127            editor::Event::Blurred => cx.emit(Event::Dismissed),
128            editor::Event::Edited => {
129                let line_editor = self.line_editor.read(cx).buffer().read(cx).text();
130                let mut components = line_editor.trim().split(&[',', ':'][..]);
131                let row = components.next().and_then(|row| row.parse::<u32>().ok());
132                let column = components.next().and_then(|row| row.parse::<u32>().ok());
133                if let Some(point) = row.map(|row| {
134                    Point::new(
135                        row.saturating_sub(1),
136                        column.map(|column| column.saturating_sub(1)).unwrap_or(0),
137                    )
138                }) {
139                    self.line_selection = self.active_editor.update(cx, |active_editor, cx| {
140                        let buffer = active_editor.buffer().read(cx);
141                        let point = buffer.clip_point(point, Bias::Left);
142                        active_editor.select_ranges([point..point], Some(Autoscroll::Center), cx);
143                        active_editor.set_highlighted_row(Some(point.row));
144                        Some(active_editor.newest_selection(cx))
145                    });
146                    cx.notify();
147                }
148            }
149            _ => {}
150        }
151    }
152}
153
154impl Entity for GoToLine {
155    type Event = Event;
156
157    fn release(&mut self, cx: &mut MutableAppContext) {
158        let line_selection = self.line_selection.take();
159        let restore_state = self.restore_state.take();
160        self.active_editor.update(cx, |editor, cx| {
161            editor.set_highlighted_row(None);
162            if let Some((line_selection, restore_state)) = line_selection.zip(restore_state) {
163                if line_selection.id == editor.newest_selection::<usize>(cx).id {
164                    editor.set_scroll_position(restore_state.scroll_position, cx);
165                    editor.update_selections(restore_state.selections, None, cx);
166                }
167            }
168        })
169    }
170}
171
172impl View for GoToLine {
173    fn ui_name() -> &'static str {
174        "GoToLine"
175    }
176
177    fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox {
178        let theme = &self.settings.borrow().theme.selector;
179
180        let label = format!(
181            "{},{} of {} lines",
182            self.cursor_point.row + 1,
183            self.cursor_point.column + 1,
184            self.max_point.row + 1
185        );
186
187        Align::new(
188            ConstrainedBox::new(
189                Container::new(
190                    Flex::new(Axis::Vertical)
191                        .with_child(
192                            Container::new(ChildView::new(self.line_editor.id()).boxed())
193                                .with_style(theme.input_editor.container)
194                                .boxed(),
195                        )
196                        .with_child(
197                            Container::new(Label::new(label, theme.empty.label.clone()).boxed())
198                                .with_style(theme.empty.container)
199                                .boxed(),
200                        )
201                        .boxed(),
202                )
203                .with_style(theme.container)
204                .boxed(),
205            )
206            .with_max_width(500.0)
207            .boxed(),
208        )
209        .top()
210        .named("go to line")
211    }
212
213    fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
214        cx.focus(&self.line_editor);
215    }
216
217    fn on_blur(&mut self, _: &mut ViewContext<Self>) {}
218}