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