go_to_line.rs

  1use editor::{display_map::ToDisplayPoint, Autoscroll, DisplayPoint, Editor};
  2use gpui::{
  3    actions, elements::*, geometry::vector::Vector2F, Axis, Entity, MutableAppContext,
  4    RenderContext, View, ViewContext, ViewHandle,
  5};
  6use settings::Settings;
  7use text::{Bias, Point};
  8use workspace::Workspace;
  9
 10actions!(go_to_line, [Toggle, Confirm]);
 11
 12pub fn init(cx: &mut MutableAppContext) {
 13    cx.add_action(GoToLine::toggle);
 14    cx.add_action(GoToLine::confirm);
 15}
 16
 17pub struct GoToLine {
 18    line_editor: ViewHandle<Editor>,
 19    active_editor: ViewHandle<Editor>,
 20    prev_scroll_position: Option<Vector2F>,
 21    cursor_point: Point,
 22    max_point: Point,
 23}
 24
 25pub enum Event {
 26    Dismissed,
 27}
 28
 29impl GoToLine {
 30    pub fn new(active_editor: ViewHandle<Editor>, cx: &mut ViewContext<Self>) -> Self {
 31        let line_editor = cx.add_view(|cx| {
 32            Editor::single_line(Some(|theme| theme.selector.input_editor.clone()), cx)
 33        });
 34        cx.subscribe(&line_editor, Self::on_line_editor_event)
 35            .detach();
 36
 37        let (scroll_position, cursor_point, max_point) = active_editor.update(cx, |editor, cx| {
 38            let scroll_position = editor.scroll_position(cx);
 39            let buffer = editor.buffer().read(cx).read(cx);
 40            (
 41                Some(scroll_position),
 42                editor.newest_selection_with_snapshot(&buffer).head(),
 43                buffer.max_point(),
 44            )
 45        });
 46
 47        Self {
 48            line_editor,
 49            active_editor,
 50            prev_scroll_position: scroll_position,
 51            cursor_point,
 52            max_point,
 53        }
 54    }
 55
 56    fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>) {
 57        if let Some(editor) = workspace
 58            .active_item(cx)
 59            .and_then(|active_item| active_item.downcast::<Editor>())
 60        {
 61            workspace.toggle_modal(cx, |cx, _| {
 62                let view = cx.add_view(|cx| GoToLine::new(editor, cx));
 63                cx.subscribe(&view, Self::on_event).detach();
 64                view
 65            });
 66        }
 67    }
 68
 69    fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
 70        self.prev_scroll_position.take();
 71        self.active_editor.update(cx, |active_editor, cx| {
 72            if let Some(rows) = active_editor.highlighted_rows() {
 73                let snapshot = active_editor.snapshot(cx).display_snapshot;
 74                let position = DisplayPoint::new(rows.start, 0).to_point(&snapshot);
 75                active_editor.select_ranges([position..position], Some(Autoscroll::Center), cx);
 76            }
 77        });
 78        cx.emit(Event::Dismissed);
 79    }
 80
 81    fn on_event(
 82        workspace: &mut Workspace,
 83        _: ViewHandle<Self>,
 84        event: &Event,
 85        cx: &mut ViewContext<Workspace>,
 86    ) {
 87        match event {
 88            Event::Dismissed => workspace.dismiss_modal(cx),
 89        }
 90    }
 91
 92    fn on_line_editor_event(
 93        &mut self,
 94        _: ViewHandle<Editor>,
 95        event: &editor::Event,
 96        cx: &mut ViewContext<Self>,
 97    ) {
 98        match event {
 99            editor::Event::Blurred => cx.emit(Event::Dismissed),
100            editor::Event::BufferEdited { .. } => {
101                let line_editor = self.line_editor.read(cx).buffer().read(cx).read(cx).text();
102                let mut components = line_editor.trim().split(&[',', ':'][..]);
103                let row = components.next().and_then(|row| row.parse::<u32>().ok());
104                let column = components.next().and_then(|row| row.parse::<u32>().ok());
105                if let Some(point) = row.map(|row| {
106                    Point::new(
107                        row.saturating_sub(1),
108                        column.map(|column| column.saturating_sub(1)).unwrap_or(0),
109                    )
110                }) {
111                    self.active_editor.update(cx, |active_editor, cx| {
112                        let snapshot = active_editor.snapshot(cx).display_snapshot;
113                        let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left);
114                        let display_point = point.to_display_point(&snapshot);
115                        let row = display_point.row();
116                        active_editor.highlight_rows(Some(row..row + 1));
117                        active_editor.request_autoscroll(Autoscroll::Center, cx);
118                    });
119                    cx.notify();
120                }
121            }
122            _ => {}
123        }
124    }
125}
126
127impl Entity for GoToLine {
128    type Event = Event;
129
130    fn release(&mut self, cx: &mut MutableAppContext) {
131        let scroll_position = self.prev_scroll_position.take();
132        self.active_editor.update(cx, |editor, cx| {
133            editor.highlight_rows(None);
134            if let Some(scroll_position) = scroll_position {
135                editor.set_scroll_position(scroll_position, cx);
136            }
137        })
138    }
139}
140
141impl View for GoToLine {
142    fn ui_name() -> &'static str {
143        "GoToLine"
144    }
145
146    fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
147        let theme = &cx.global::<Settings>().theme.selector;
148
149        let label = format!(
150            "{},{} of {} lines",
151            self.cursor_point.row + 1,
152            self.cursor_point.column + 1,
153            self.max_point.row + 1
154        );
155
156        Align::new(
157            ConstrainedBox::new(
158                Container::new(
159                    Flex::new(Axis::Vertical)
160                        .with_child(
161                            Container::new(ChildView::new(&self.line_editor).boxed())
162                                .with_style(theme.input_editor.container)
163                                .boxed(),
164                        )
165                        .with_child(
166                            Container::new(Label::new(label, theme.empty.label.clone()).boxed())
167                                .with_style(theme.empty.container)
168                                .boxed(),
169                        )
170                        .boxed(),
171                )
172                .with_style(theme.container)
173                .boxed(),
174            )
175            .with_max_width(500.0)
176            .boxed(),
177        )
178        .top()
179        .named("go to line")
180    }
181
182    fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
183        cx.focus(&self.line_editor);
184    }
185}