Rename BufferView -> Editor

Max Brunsfeld and Nathan Sobo created

* BufferElement -> EditorElement

Co-Authored-By: Nathan Sobo <nathan@zed.dev>

Change summary

zed/src/editor.rs             | 4090 ++++++++++++++++++++++++++++++++++++
zed/src/editor/buffer_view.rs | 4118 -------------------------------------
zed/src/editor/element.rs     |   20 
zed/src/file_finder.rs        |   19 
zed/src/workspace.rs          |   12 
5 files changed, 4,110 insertions(+), 4,149 deletions(-)

Detailed changes

zed/src/editor.rs 🔗

@@ -1,19 +1,4099 @@
 mod buffer;
-mod buffer_element;
-pub mod buffer_view;
 pub mod display_map;
+mod element;
 pub mod movement;
 
+use crate::{
+    settings::{Settings, StyleId},
+    util::post_inc,
+    workspace,
+    worktree::FileHandle,
+};
+use anyhow::Result;
 pub use buffer::*;
-pub use buffer_element::*;
-pub use buffer_view::*;
 pub use display_map::DisplayPoint;
 use display_map::*;
+pub use element::*;
+use gpui::{
+    color::ColorU, fonts::Properties as FontProperties, geometry::vector::Vector2F,
+    keymap::Binding, text_layout, AppContext, ClipboardItem, Element, ElementBox, Entity,
+    FontCache, ModelHandle, MutableAppContext, Task, TextLayoutCache, View, ViewContext,
+    WeakViewHandle,
+};
+use parking_lot::Mutex;
+use postage::watch;
+use serde::{Deserialize, Serialize};
+use smallvec::SmallVec;
+use smol::Timer;
 use std::{
-    cmp,
+    cmp::{self, Ordering},
+    fmt::Write,
+    iter::FromIterator,
+    mem,
     ops::{Range, RangeInclusive},
+    path::Path,
+    sync::Arc,
+    time::Duration,
 };
 
+const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
+
+pub fn init(app: &mut MutableAppContext) {
+    app.add_bindings(vec![
+        Binding::new("escape", "buffer:cancel", Some("BufferView")),
+        Binding::new("backspace", "buffer:backspace", Some("BufferView")),
+        Binding::new("ctrl-h", "buffer:backspace", Some("BufferView")),
+        Binding::new("delete", "buffer:delete", Some("BufferView")),
+        Binding::new("ctrl-d", "buffer:delete", Some("BufferView")),
+        Binding::new("enter", "buffer:newline", Some("BufferView")),
+        Binding::new("tab", "buffer:insert", Some("BufferView")).with_arg("\t".to_string()),
+        Binding::new("ctrl-shift-K", "buffer:delete_line", Some("BufferView")),
+        Binding::new(
+            "alt-backspace",
+            "buffer:delete_to_previous_word_boundary",
+            Some("BufferView"),
+        ),
+        Binding::new(
+            "alt-delete",
+            "buffer:delete_to_next_word_boundary",
+            Some("BufferView"),
+        ),
+        Binding::new(
+            "cmd-backspace",
+            "buffer:delete_to_beginning_of_line",
+            Some("BufferView"),
+        ),
+        Binding::new(
+            "cmd-delete",
+            "buffer:delete_to_end_of_line",
+            Some("BufferView"),
+        ),
+        Binding::new("cmd-shift-D", "buffer:duplicate_line", Some("BufferView")),
+        Binding::new("ctrl-cmd-up", "buffer:move_line_up", Some("BufferView")),
+        Binding::new("ctrl-cmd-down", "buffer:move_line_down", Some("BufferView")),
+        Binding::new("cmd-x", "buffer:cut", Some("BufferView")),
+        Binding::new("cmd-c", "buffer:copy", Some("BufferView")),
+        Binding::new("cmd-v", "buffer:paste", Some("BufferView")),
+        Binding::new("cmd-z", "buffer:undo", Some("BufferView")),
+        Binding::new("cmd-shift-Z", "buffer:redo", Some("BufferView")),
+        Binding::new("up", "buffer:move_up", Some("BufferView")),
+        Binding::new("down", "buffer:move_down", Some("BufferView")),
+        Binding::new("left", "buffer:move_left", Some("BufferView")),
+        Binding::new("right", "buffer:move_right", Some("BufferView")),
+        Binding::new("ctrl-p", "buffer:move_up", Some("BufferView")),
+        Binding::new("ctrl-n", "buffer:move_down", Some("BufferView")),
+        Binding::new("ctrl-b", "buffer:move_left", Some("BufferView")),
+        Binding::new("ctrl-f", "buffer:move_right", Some("BufferView")),
+        Binding::new(
+            "alt-left",
+            "buffer:move_to_previous_word_boundary",
+            Some("BufferView"),
+        ),
+        Binding::new(
+            "alt-right",
+            "buffer:move_to_next_word_boundary",
+            Some("BufferView"),
+        ),
+        Binding::new(
+            "cmd-left",
+            "buffer:move_to_beginning_of_line",
+            Some("BufferView"),
+        ),
+        Binding::new(
+            "ctrl-a",
+            "buffer:move_to_beginning_of_line",
+            Some("BufferView"),
+        ),
+        Binding::new(
+            "cmd-right",
+            "buffer:move_to_end_of_line",
+            Some("BufferView"),
+        ),
+        Binding::new("ctrl-e", "buffer:move_to_end_of_line", Some("BufferView")),
+        Binding::new("cmd-up", "buffer:move_to_beginning", Some("BufferView")),
+        Binding::new("cmd-down", "buffer:move_to_end", Some("BufferView")),
+        Binding::new("shift-up", "buffer:select_up", Some("BufferView")),
+        Binding::new("shift-down", "buffer:select_down", Some("BufferView")),
+        Binding::new("shift-left", "buffer:select_left", Some("BufferView")),
+        Binding::new("shift-right", "buffer:select_right", Some("BufferView")),
+        Binding::new(
+            "alt-shift-left",
+            "buffer:select_to_previous_word_boundary",
+            Some("BufferView"),
+        ),
+        Binding::new(
+            "alt-shift-right",
+            "buffer:select_to_next_word_boundary",
+            Some("BufferView"),
+        ),
+        Binding::new(
+            "cmd-shift-left",
+            "buffer:select_to_beginning_of_line",
+            Some("BufferView"),
+        )
+        .with_arg(true),
+        Binding::new(
+            "ctrl-shift-A",
+            "buffer:select_to_beginning_of_line",
+            Some("BufferView"),
+        )
+        .with_arg(true),
+        Binding::new(
+            "cmd-shift-right",
+            "buffer:select_to_end_of_line",
+            Some("BufferView"),
+        ),
+        Binding::new(
+            "ctrl-shift-E",
+            "buffer:select_to_end_of_line",
+            Some("BufferView"),
+        ),
+        Binding::new(
+            "cmd-shift-up",
+            "buffer:select_to_beginning",
+            Some("BufferView"),
+        ),
+        Binding::new("cmd-shift-down", "buffer:select_to_end", Some("BufferView")),
+        Binding::new("cmd-a", "buffer:select_all", Some("BufferView")),
+        Binding::new("cmd-l", "buffer:select_line", Some("BufferView")),
+        Binding::new(
+            "cmd-shift-L",
+            "buffer:split_selection_into_lines",
+            Some("BufferView"),
+        ),
+        Binding::new(
+            "cmd-alt-up",
+            "buffer:add_selection_above",
+            Some("BufferView"),
+        ),
+        Binding::new(
+            "cmd-alt-down",
+            "buffer:add_selection_below",
+            Some("BufferView"),
+        ),
+        Binding::new(
+            "alt-up",
+            "buffer:select_larger_syntax_node",
+            Some("BufferView"),
+        ),
+        Binding::new(
+            "alt-down",
+            "buffer:select_smaller_syntax_node",
+            Some("BufferView"),
+        ),
+        Binding::new(
+            "ctrl-m",
+            "buffer:move_to_enclosing_bracket",
+            Some("BufferView"),
+        ),
+        Binding::new("pageup", "buffer:page_up", Some("BufferView")),
+        Binding::new("pagedown", "buffer:page_down", Some("BufferView")),
+        Binding::new("alt-cmd-[", "buffer:fold", Some("BufferView")),
+        Binding::new("alt-cmd-]", "buffer:unfold", Some("BufferView")),
+        Binding::new(
+            "alt-cmd-f",
+            "buffer:fold_selected_ranges",
+            Some("BufferView"),
+        ),
+    ]);
+
+    app.add_action("buffer:scroll", Editor::scroll);
+    app.add_action("buffer:select", Editor::select);
+    app.add_action("buffer:cancel", Editor::cancel);
+    app.add_action("buffer:insert", Editor::insert);
+    app.add_action("buffer:newline", Editor::newline);
+    app.add_action("buffer:backspace", Editor::backspace);
+    app.add_action("buffer:delete", Editor::delete);
+    app.add_action("buffer:delete_line", Editor::delete_line);
+    app.add_action(
+        "buffer:delete_to_previous_word_boundary",
+        Editor::delete_to_previous_word_boundary,
+    );
+    app.add_action(
+        "buffer:delete_to_next_word_boundary",
+        Editor::delete_to_next_word_boundary,
+    );
+    app.add_action(
+        "buffer:delete_to_beginning_of_line",
+        Editor::delete_to_beginning_of_line,
+    );
+    app.add_action(
+        "buffer:delete_to_end_of_line",
+        Editor::delete_to_end_of_line,
+    );
+    app.add_action("buffer:duplicate_line", Editor::duplicate_line);
+    app.add_action("buffer:move_line_up", Editor::move_line_up);
+    app.add_action("buffer:move_line_down", Editor::move_line_down);
+    app.add_action("buffer:cut", Editor::cut);
+    app.add_action("buffer:copy", Editor::copy);
+    app.add_action("buffer:paste", Editor::paste);
+    app.add_action("buffer:undo", Editor::undo);
+    app.add_action("buffer:redo", Editor::redo);
+    app.add_action("buffer:move_up", Editor::move_up);
+    app.add_action("buffer:move_down", Editor::move_down);
+    app.add_action("buffer:move_left", Editor::move_left);
+    app.add_action("buffer:move_right", Editor::move_right);
+    app.add_action(
+        "buffer:move_to_previous_word_boundary",
+        Editor::move_to_previous_word_boundary,
+    );
+    app.add_action(
+        "buffer:move_to_next_word_boundary",
+        Editor::move_to_next_word_boundary,
+    );
+    app.add_action(
+        "buffer:move_to_beginning_of_line",
+        Editor::move_to_beginning_of_line,
+    );
+    app.add_action("buffer:move_to_end_of_line", Editor::move_to_end_of_line);
+    app.add_action("buffer:move_to_beginning", Editor::move_to_beginning);
+    app.add_action("buffer:move_to_end", Editor::move_to_end);
+    app.add_action("buffer:select_up", Editor::select_up);
+    app.add_action("buffer:select_down", Editor::select_down);
+    app.add_action("buffer:select_left", Editor::select_left);
+    app.add_action("buffer:select_right", Editor::select_right);
+    app.add_action(
+        "buffer:select_to_previous_word_boundary",
+        Editor::select_to_previous_word_boundary,
+    );
+    app.add_action(
+        "buffer:select_to_next_word_boundary",
+        Editor::select_to_next_word_boundary,
+    );
+    app.add_action(
+        "buffer:select_to_beginning_of_line",
+        Editor::select_to_beginning_of_line,
+    );
+    app.add_action(
+        "buffer:select_to_end_of_line",
+        Editor::select_to_end_of_line,
+    );
+    app.add_action("buffer:select_to_beginning", Editor::select_to_beginning);
+    app.add_action("buffer:select_to_end", Editor::select_to_end);
+    app.add_action("buffer:select_all", Editor::select_all);
+    app.add_action("buffer:select_line", Editor::select_line);
+    app.add_action(
+        "buffer:split_selection_into_lines",
+        Editor::split_selection_into_lines,
+    );
+    app.add_action("buffer:add_selection_above", Editor::add_selection_above);
+    app.add_action("buffer:add_selection_below", Editor::add_selection_below);
+    app.add_action(
+        "buffer:select_larger_syntax_node",
+        Editor::select_larger_syntax_node,
+    );
+    app.add_action(
+        "buffer:select_smaller_syntax_node",
+        Editor::select_smaller_syntax_node,
+    );
+    app.add_action(
+        "buffer:move_to_enclosing_bracket",
+        Editor::move_to_enclosing_bracket,
+    );
+    app.add_action("buffer:page_up", Editor::page_up);
+    app.add_action("buffer:page_down", Editor::page_down);
+    app.add_action("buffer:fold", Editor::fold);
+    app.add_action("buffer:unfold", Editor::unfold);
+    app.add_action("buffer:fold_selected_ranges", Editor::fold_selected_ranges);
+}
+
+pub enum SelectAction {
+    Begin {
+        position: DisplayPoint,
+        add: bool,
+    },
+    Update {
+        position: DisplayPoint,
+        scroll_position: Vector2F,
+    },
+    End,
+}
+
+pub struct Editor {
+    handle: WeakViewHandle<Self>,
+    buffer: ModelHandle<Buffer>,
+    display_map: DisplayMap,
+    selection_set_id: SelectionSetId,
+    pending_selection: Option<Selection>,
+    next_selection_id: usize,
+    add_selections_state: Option<AddSelectionsState>,
+    select_larger_syntax_node_stack: Vec<Vec<Selection>>,
+    scroll_position: Mutex<Vector2F>,
+    autoscroll_requested: Mutex<bool>,
+    settings: watch::Receiver<Settings>,
+    focused: bool,
+    cursors_visible: bool,
+    blink_epoch: usize,
+    blinking_paused: bool,
+    single_line: bool,
+}
+
+struct AddSelectionsState {
+    above: bool,
+    stack: Vec<usize>,
+}
+
+#[derive(Serialize, Deserialize)]
+struct ClipboardSelection {
+    len: usize,
+    is_entire_line: bool,
+}
+
+impl Editor {
+    pub fn single_line(settings: watch::Receiver<Settings>, ctx: &mut ViewContext<Self>) -> Self {
+        let buffer = ctx.add_model(|ctx| Buffer::new(0, String::new(), ctx));
+        let mut view = Self::for_buffer(buffer, settings, ctx);
+        view.single_line = true;
+        view
+    }
+
+    pub fn for_buffer(
+        buffer: ModelHandle<Buffer>,
+        settings: watch::Receiver<Settings>,
+        ctx: &mut ViewContext<Self>,
+    ) -> Self {
+        ctx.observe_model(&buffer, Self::on_buffer_changed);
+        ctx.subscribe_to_model(&buffer, Self::on_buffer_event);
+        let display_map = DisplayMap::new(buffer.clone(), settings.borrow().tab_size, ctx.as_ref());
+
+        let mut next_selection_id = 0;
+        let (selection_set_id, _) = buffer.update(ctx, |buffer, ctx| {
+            buffer.add_selection_set(
+                vec![Selection {
+                    id: post_inc(&mut next_selection_id),
+                    start: buffer.anchor_before(0),
+                    end: buffer.anchor_before(0),
+                    reversed: false,
+                    goal: SelectionGoal::None,
+                }],
+                Some(ctx),
+            )
+        });
+        Self {
+            handle: ctx.handle().downgrade(),
+            buffer,
+            display_map,
+            selection_set_id,
+            pending_selection: None,
+            next_selection_id,
+            add_selections_state: None,
+            select_larger_syntax_node_stack: Vec::new(),
+            scroll_position: Mutex::new(Vector2F::zero()),
+            autoscroll_requested: Mutex::new(false),
+            settings,
+            focused: false,
+            cursors_visible: false,
+            blink_epoch: 0,
+            blinking_paused: false,
+            single_line: false,
+        }
+    }
+
+    pub fn buffer(&self) -> &ModelHandle<Buffer> {
+        &self.buffer
+    }
+
+    pub fn is_gutter_visible(&self) -> bool {
+        !self.single_line
+    }
+
+    fn scroll(&mut self, scroll_position: &Vector2F, ctx: &mut ViewContext<Self>) {
+        *self.scroll_position.lock() = *scroll_position;
+        ctx.notify();
+    }
+
+    pub fn scroll_position(&self) -> Vector2F {
+        *self.scroll_position.lock()
+    }
+
+    pub fn clamp_scroll_left(&self, max: f32) {
+        let mut scroll_position = self.scroll_position.lock();
+        let scroll_left = scroll_position.x();
+        scroll_position.set_x(scroll_left.min(max));
+    }
+
+    pub fn autoscroll_vertically(
+        &self,
+        viewport_height: f32,
+        line_height: f32,
+        app: &AppContext,
+    ) -> bool {
+        let mut scroll_position = self.scroll_position.lock();
+        let scroll_top = scroll_position.y();
+        scroll_position.set_y(scroll_top.min(self.max_point(app).row().saturating_sub(1) as f32));
+
+        let mut autoscroll_requested = self.autoscroll_requested.lock();
+        if *autoscroll_requested {
+            *autoscroll_requested = false;
+        } else {
+            return false;
+        }
+
+        let visible_lines = viewport_height / line_height;
+        let first_cursor_top = self
+            .selections(app)
+            .first()
+            .unwrap()
+            .head()
+            .to_display_point(&self.display_map, app)
+            .row() as f32;
+        let last_cursor_bottom = self
+            .selections(app)
+            .last()
+            .unwrap()
+            .head()
+            .to_display_point(&self.display_map, app)
+            .row() as f32
+            + 1.0;
+
+        let margin = ((visible_lines - (last_cursor_bottom - first_cursor_top)) / 2.0)
+            .floor()
+            .min(3.0);
+        if margin < 0.0 {
+            return false;
+        }
+
+        let target_top = (first_cursor_top - margin).max(0.0);
+        let target_bottom = last_cursor_bottom + margin;
+        let start_row = scroll_position.y();
+        let end_row = start_row + visible_lines;
+
+        if target_top < start_row {
+            scroll_position.set_y(target_top);
+        } else if target_bottom >= end_row {
+            scroll_position.set_y(target_bottom - visible_lines);
+        }
+
+        true
+    }
+
+    pub fn autoscroll_horizontally(
+        &self,
+        start_row: u32,
+        viewport_width: f32,
+        scroll_width: f32,
+        max_glyph_width: f32,
+        layouts: &[text_layout::Line],
+        ctx: &AppContext,
+    ) {
+        let mut target_left = std::f32::INFINITY;
+        let mut target_right = 0.0_f32;
+        for selection in self.selections(ctx) {
+            let head = selection.head().to_display_point(&self.display_map, ctx);
+            let start_column = head.column().saturating_sub(3);
+            let end_column = cmp::min(
+                self.display_map.line_len(head.row(), ctx),
+                head.column() + 3,
+            );
+            target_left = target_left
+                .min(layouts[(head.row() - start_row) as usize].x_for_index(start_column as usize));
+            target_right = target_right.max(
+                layouts[(head.row() - start_row) as usize].x_for_index(end_column as usize)
+                    + max_glyph_width,
+            );
+        }
+        target_right = target_right.min(scroll_width);
+
+        if target_right - target_left > viewport_width {
+            return;
+        }
+
+        let mut scroll_position = self.scroll_position.lock();
+        let scroll_left = scroll_position.x() * max_glyph_width;
+        let scroll_right = scroll_left + viewport_width;
+
+        if target_left < scroll_left {
+            scroll_position.set_x(target_left / max_glyph_width);
+        } else if target_right > scroll_right {
+            scroll_position.set_x((target_right - viewport_width) / max_glyph_width);
+        }
+    }
+
+    fn select(&mut self, arg: &SelectAction, ctx: &mut ViewContext<Self>) {
+        match arg {
+            SelectAction::Begin { position, add } => self.begin_selection(*position, *add, ctx),
+            SelectAction::Update {
+                position,
+                scroll_position,
+            } => self.update_selection(*position, *scroll_position, ctx),
+            SelectAction::End => self.end_selection(ctx),
+        }
+    }
+
+    fn begin_selection(&mut self, position: DisplayPoint, add: bool, ctx: &mut ViewContext<Self>) {
+        if !self.focused {
+            ctx.focus_self();
+            ctx.emit(Event::Activate);
+        }
+
+        let cursor = self
+            .display_map
+            .anchor_before(position, Bias::Left, ctx.as_ref());
+        let selection = Selection {
+            id: post_inc(&mut self.next_selection_id),
+            start: cursor.clone(),
+            end: cursor,
+            reversed: false,
+            goal: SelectionGoal::None,
+        };
+
+        if !add {
+            self.update_selections(Vec::new(), false, ctx);
+        }
+        self.pending_selection = Some(selection);
+
+        ctx.notify();
+    }
+
+    fn update_selection(
+        &mut self,
+        position: DisplayPoint,
+        scroll_position: Vector2F,
+        ctx: &mut ViewContext<Self>,
+    ) {
+        let buffer = self.buffer.read(ctx);
+        let cursor = self
+            .display_map
+            .anchor_before(position, Bias::Left, ctx.as_ref());
+        if let Some(selection) = self.pending_selection.as_mut() {
+            selection.set_head(buffer, cursor);
+        } else {
+            log::error!("update_selection dispatched with no pending selection");
+            return;
+        }
+
+        *self.scroll_position.lock() = scroll_position;
+
+        ctx.notify();
+    }
+
+    fn end_selection(&mut self, ctx: &mut ViewContext<Self>) {
+        if let Some(selection) = self.pending_selection.take() {
+            let ix = self.selection_insertion_index(&selection.start, ctx.as_ref());
+            let mut selections = self.selections(ctx.as_ref()).to_vec();
+            selections.insert(ix, selection);
+            self.update_selections(selections, false, ctx);
+        } else {
+            log::error!("end_selection dispatched with no pending selection");
+        }
+    }
+
+    pub fn is_selecting(&self) -> bool {
+        self.pending_selection.is_some()
+    }
+
+    pub fn cancel(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let selections = self.selections(ctx.as_ref());
+        if let Some(pending_selection) = self.pending_selection.take() {
+            if selections.is_empty() {
+                self.update_selections(vec![pending_selection], true, ctx);
+            }
+        } else {
+            let mut oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
+            if selections.len() == 1 {
+                oldest_selection.start = oldest_selection.head().clone();
+                oldest_selection.end = oldest_selection.head().clone();
+            }
+            self.update_selections(vec![oldest_selection], true, ctx);
+        }
+    }
+
+    fn select_ranges<I, T>(&mut self, ranges: I, autoscroll: bool, ctx: &mut ViewContext<Self>)
+    where
+        I: IntoIterator<Item = Range<T>>,
+        T: ToOffset,
+    {
+        let buffer = self.buffer.read(ctx);
+        let mut selections = Vec::new();
+        for range in ranges {
+            let mut start = range.start.to_offset(buffer);
+            let mut end = range.end.to_offset(buffer);
+            let reversed = if start > end {
+                mem::swap(&mut start, &mut end);
+                true
+            } else {
+                false
+            };
+            selections.push(Selection {
+                id: post_inc(&mut self.next_selection_id),
+                start: buffer.anchor_before(start),
+                end: buffer.anchor_before(end),
+                reversed,
+                goal: SelectionGoal::None,
+            });
+        }
+        self.update_selections(selections, autoscroll, ctx);
+    }
+
+    #[cfg(test)]
+    fn select_display_ranges<'a, T>(&mut self, ranges: T, ctx: &mut ViewContext<Self>) -> Result<()>
+    where
+        T: IntoIterator<Item = &'a Range<DisplayPoint>>,
+    {
+        let mut selections = Vec::new();
+        for range in ranges {
+            let mut start = range.start;
+            let mut end = range.end;
+            let reversed = if start > end {
+                mem::swap(&mut start, &mut end);
+                true
+            } else {
+                false
+            };
+
+            selections.push(Selection {
+                id: post_inc(&mut self.next_selection_id),
+                start: self
+                    .display_map
+                    .anchor_before(start, Bias::Left, ctx.as_ref()),
+                end: self
+                    .display_map
+                    .anchor_before(end, Bias::Left, ctx.as_ref()),
+                reversed,
+                goal: SelectionGoal::None,
+            });
+        }
+        self.update_selections(selections, false, ctx);
+        Ok(())
+    }
+
+    pub fn insert(&mut self, text: &String, ctx: &mut ViewContext<Self>) {
+        let mut old_selections = SmallVec::<[_; 32]>::new();
+        {
+            let buffer = self.buffer.read(ctx);
+            for selection in self.selections(ctx.as_ref()) {
+                let start = selection.start.to_offset(buffer);
+                let end = selection.end.to_offset(buffer);
+                old_selections.push((selection.id, start..end));
+            }
+        }
+
+        self.start_transaction(ctx);
+        let mut new_selections = Vec::new();
+        self.buffer.update(ctx, |buffer, ctx| {
+            let edit_ranges = old_selections.iter().map(|(_, range)| range.clone());
+            if let Err(error) = buffer.edit(edit_ranges, text.as_str(), Some(ctx)) {
+                log::error!("error inserting text: {}", error);
+            };
+            let text_len = text.len() as isize;
+            let mut delta = 0_isize;
+            new_selections = old_selections
+                .into_iter()
+                .map(|(id, range)| {
+                    let start = range.start as isize;
+                    let end = range.end as isize;
+                    let anchor = buffer.anchor_before((start + delta + text_len) as usize);
+                    let deleted_count = end - start;
+                    delta += text_len - deleted_count;
+                    Selection {
+                        id,
+                        start: anchor.clone(),
+                        end: anchor,
+                        reversed: false,
+                        goal: SelectionGoal::None,
+                    }
+                })
+                .collect();
+        });
+
+        self.update_selections(new_selections, true, ctx);
+        self.end_transaction(ctx);
+    }
+
+    fn newline(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        if self.single_line {
+            ctx.propagate_action();
+        } else {
+            self.insert(&"\n".into(), ctx);
+        }
+    }
+
+    pub fn backspace(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        self.start_transaction(ctx);
+        let mut selections = self.selections(ctx.as_ref()).to_vec();
+        {
+            let buffer = self.buffer.read(ctx);
+            for selection in &mut selections {
+                let range = selection.point_range(buffer);
+                if range.start == range.end {
+                    let head = selection
+                        .head()
+                        .to_display_point(&self.display_map, ctx.as_ref());
+                    let cursor = self.display_map.anchor_before(
+                        movement::left(&self.display_map, head, ctx.as_ref()).unwrap(),
+                        Bias::Left,
+                        ctx.as_ref(),
+                    );
+                    selection.set_head(&buffer, cursor);
+                    selection.goal = SelectionGoal::None;
+                }
+            }
+        }
+
+        self.update_selections(selections, true, ctx);
+        self.insert(&String::new(), ctx);
+        self.end_transaction(ctx);
+    }
+
+    pub fn delete(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        self.start_transaction(ctx);
+        let mut selections = self.selections(ctx.as_ref()).to_vec();
+        {
+            let buffer = self.buffer.read(ctx);
+            for selection in &mut selections {
+                let range = selection.point_range(buffer);
+                if range.start == range.end {
+                    let head = selection
+                        .head()
+                        .to_display_point(&self.display_map, ctx.as_ref());
+                    let cursor = self.display_map.anchor_before(
+                        movement::right(&self.display_map, head, ctx.as_ref()).unwrap(),
+                        Bias::Right,
+                        ctx.as_ref(),
+                    );
+                    selection.set_head(&buffer, cursor);
+                    selection.goal = SelectionGoal::None;
+                }
+            }
+        }
+
+        self.update_selections(selections, true, ctx);
+        self.insert(&String::new(), ctx);
+        self.end_transaction(ctx);
+    }
+
+    pub fn delete_line(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        self.start_transaction(ctx);
+
+        let app = ctx.as_ref();
+        let buffer = self.buffer.read(app);
+
+        let mut new_cursors = Vec::new();
+        let mut edit_ranges = Vec::new();
+
+        let mut selections = self.selections(app).iter().peekable();
+        while let Some(selection) = selections.next() {
+            let (mut rows, _) =
+                selection.buffer_rows_for_display_rows(false, &self.display_map, app);
+            let goal_display_column = selection
+                .head()
+                .to_display_point(&self.display_map, app)
+                .column();
+
+            // Accumulate contiguous regions of rows that we want to delete.
+            while let Some(next_selection) = selections.peek() {
+                let (next_rows, _) =
+                    next_selection.buffer_rows_for_display_rows(false, &self.display_map, app);
+                if next_rows.start <= rows.end {
+                    rows.end = next_rows.end;
+                    selections.next().unwrap();
+                } else {
+                    break;
+                }
+            }
+
+            let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
+            let edit_end;
+            let cursor_buffer_row;
+            if buffer.max_point().row >= rows.end {
+                // If there's a line after the range, delete the \n from the end of the row range
+                // and position the cursor on the next line.
+                edit_end = Point::new(rows.end, 0).to_offset(buffer);
+                cursor_buffer_row = rows.end;
+            } else {
+                // If there isn't a line after the range, delete the \n from the line before the
+                // start of the row range and position the cursor there.
+                edit_start = edit_start.saturating_sub(1);
+                edit_end = buffer.len();
+                cursor_buffer_row = rows.start.saturating_sub(1);
+            }
+
+            let mut cursor =
+                Point::new(cursor_buffer_row, 0).to_display_point(&self.display_map, app);
+            *cursor.column_mut() = cmp::min(
+                goal_display_column,
+                self.display_map.line_len(cursor.row(), app),
+            );
+
+            new_cursors.push((
+                selection.id,
+                cursor.to_buffer_point(&self.display_map, Bias::Left, app),
+            ));
+            edit_ranges.push(edit_start..edit_end);
+        }
+
+        new_cursors.sort_unstable_by_key(|(_, range)| range.clone());
+        let new_selections = new_cursors
+            .into_iter()
+            .map(|(id, cursor)| {
+                let anchor = buffer.anchor_before(cursor);
+                Selection {
+                    id,
+                    start: anchor.clone(),
+                    end: anchor,
+                    reversed: false,
+                    goal: SelectionGoal::None,
+                }
+            })
+            .collect();
+        self.buffer
+            .update(ctx, |buffer, ctx| buffer.edit(edit_ranges, "", Some(ctx)))
+            .unwrap();
+        self.update_selections(new_selections, true, ctx);
+        self.end_transaction(ctx);
+    }
+
+    pub fn duplicate_line(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        self.start_transaction(ctx);
+
+        let mut selections = self.selections(ctx.as_ref()).to_vec();
+        {
+            // Temporarily bias selections right to allow newly duplicate lines to push them down
+            // when the selections are at the beginning of a line.
+            let buffer = self.buffer.read(ctx);
+            for selection in &mut selections {
+                selection.start = selection.start.bias_right(buffer);
+                selection.end = selection.end.bias_right(buffer);
+            }
+        }
+        self.update_selections(selections.clone(), false, ctx);
+
+        let app = ctx.as_ref();
+        let buffer = self.buffer.read(ctx);
+
+        let mut edits = Vec::new();
+        let mut selections_iter = selections.iter_mut().peekable();
+        while let Some(selection) = selections_iter.next() {
+            // Avoid duplicating the same lines twice.
+            let (mut rows, _) =
+                selection.buffer_rows_for_display_rows(false, &self.display_map, app);
+            while let Some(next_selection) = selections_iter.peek() {
+                let (next_rows, _) =
+                    next_selection.buffer_rows_for_display_rows(false, &self.display_map, app);
+                if next_rows.start <= rows.end - 1 {
+                    rows.end = next_rows.end;
+                    selections_iter.next().unwrap();
+                } else {
+                    break;
+                }
+            }
+
+            // Copy the text from the selected row region and splice it at the start of the region.
+            let start = Point::new(rows.start, 0);
+            let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
+            let text = buffer
+                .text_for_range(start..end)
+                .chain(Some("\n"))
+                .collect::<String>();
+            edits.push((start, text));
+        }
+
+        self.buffer.update(ctx, |buffer, ctx| {
+            for (offset, text) in edits.into_iter().rev() {
+                buffer.edit(Some(offset..offset), text, Some(ctx)).unwrap();
+            }
+        });
+
+        // Restore bias on selections.
+        let buffer = self.buffer.read(ctx);
+        for selection in &mut selections {
+            selection.start = selection.start.bias_left(buffer);
+            selection.end = selection.end.bias_left(buffer);
+        }
+        self.update_selections(selections, true, ctx);
+
+        self.end_transaction(ctx);
+    }
+
+    pub fn move_line_up(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        self.start_transaction(ctx);
+
+        let app = ctx.as_ref();
+        let buffer = self.buffer.read(ctx);
+
+        let mut edits = Vec::new();
+        let mut new_selection_ranges = Vec::new();
+        let mut old_folds = Vec::new();
+        let mut new_folds = Vec::new();
+
+        let mut selections = self.selections(app).iter().peekable();
+        let mut contiguous_selections = Vec::new();
+        while let Some(selection) = selections.next() {
+            // Accumulate contiguous regions of rows that we want to move.
+            contiguous_selections.push(selection.point_range(buffer));
+            let (mut buffer_rows, mut display_rows) =
+                selection.buffer_rows_for_display_rows(false, &self.display_map, app);
+            while let Some(next_selection) = selections.peek() {
+                let (next_buffer_rows, next_display_rows) =
+                    next_selection.buffer_rows_for_display_rows(false, &self.display_map, app);
+                if next_buffer_rows.start <= buffer_rows.end {
+                    buffer_rows.end = next_buffer_rows.end;
+                    display_rows.end = next_display_rows.end;
+                    contiguous_selections.push(next_selection.point_range(buffer));
+                    selections.next().unwrap();
+                } else {
+                    break;
+                }
+            }
+
+            // Cut the text from the selected rows and paste it at the start of the previous line.
+            if display_rows.start != 0 {
+                let start = Point::new(buffer_rows.start, 0).to_offset(buffer);
+                let end = Point::new(buffer_rows.end - 1, buffer.line_len(buffer_rows.end - 1))
+                    .to_offset(buffer);
+
+                let prev_row_display_start = DisplayPoint::new(display_rows.start - 1, 0);
+                let prev_row_start =
+                    prev_row_display_start.to_buffer_offset(&self.display_map, Bias::Left, app);
+
+                let mut text = String::new();
+                text.extend(buffer.text_for_range(start..end));
+                text.push('\n');
+                edits.push((prev_row_start..prev_row_start, text));
+                edits.push((start - 1..end, String::new()));
+
+                let row_delta = buffer_rows.start
+                    - prev_row_display_start
+                        .to_buffer_point(&self.display_map, Bias::Left, app)
+                        .row;
+
+                // Move selections up.
+                for range in &mut contiguous_selections {
+                    range.start.row -= row_delta;
+                    range.end.row -= row_delta;
+                }
+
+                // Move folds up.
+                old_folds.push(start..end);
+                for fold in self.display_map.folds_in_range(start..end, app) {
+                    let mut start = fold.start.to_point(buffer);
+                    let mut end = fold.end.to_point(buffer);
+                    start.row -= row_delta;
+                    end.row -= row_delta;
+                    new_folds.push(start..end);
+                }
+            }
+
+            new_selection_ranges.extend(contiguous_selections.drain(..));
+        }
+
+        self.unfold_ranges(old_folds, ctx);
+        self.buffer.update(ctx, |buffer, ctx| {
+            for (range, text) in edits.into_iter().rev() {
+                buffer.edit(Some(range), text, Some(ctx)).unwrap();
+            }
+        });
+        self.fold_ranges(new_folds, ctx);
+        self.select_ranges(new_selection_ranges, true, ctx);
+
+        self.end_transaction(ctx);
+    }
+
+    pub fn move_line_down(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        self.start_transaction(ctx);
+
+        let app = ctx.as_ref();
+        let buffer = self.buffer.read(ctx);
+
+        let mut edits = Vec::new();
+        let mut new_selection_ranges = Vec::new();
+        let mut old_folds = Vec::new();
+        let mut new_folds = Vec::new();
+
+        let mut selections = self.selections(app).iter().peekable();
+        let mut contiguous_selections = Vec::new();
+        while let Some(selection) = selections.next() {
+            // Accumulate contiguous regions of rows that we want to move.
+            contiguous_selections.push(selection.point_range(buffer));
+            let (mut buffer_rows, mut display_rows) =
+                selection.buffer_rows_for_display_rows(false, &self.display_map, app);
+            while let Some(next_selection) = selections.peek() {
+                let (next_buffer_rows, next_display_rows) =
+                    next_selection.buffer_rows_for_display_rows(false, &self.display_map, app);
+                if next_buffer_rows.start <= buffer_rows.end {
+                    buffer_rows.end = next_buffer_rows.end;
+                    display_rows.end = next_display_rows.end;
+                    contiguous_selections.push(next_selection.point_range(buffer));
+                    selections.next().unwrap();
+                } else {
+                    break;
+                }
+            }
+
+            // Cut the text from the selected rows and paste it at the end of the next line.
+            if display_rows.end <= self.display_map.max_point(app).row() {
+                let start = Point::new(buffer_rows.start, 0).to_offset(buffer);
+                let end = Point::new(buffer_rows.end - 1, buffer.line_len(buffer_rows.end - 1))
+                    .to_offset(buffer);
+
+                let next_row_display_end = DisplayPoint::new(
+                    display_rows.end,
+                    self.display_map.line_len(display_rows.end, app),
+                );
+                let next_row_end =
+                    next_row_display_end.to_buffer_offset(&self.display_map, Bias::Right, app);
+
+                let mut text = String::new();
+                text.push('\n');
+                text.extend(buffer.text_for_range(start..end));
+                edits.push((start..end + 1, String::new()));
+                edits.push((next_row_end..next_row_end, text));
+
+                let row_delta = next_row_display_end
+                    .to_buffer_point(&self.display_map, Bias::Right, app)
+                    .row
+                    - buffer_rows.end
+                    + 1;
+
+                // Move selections down.
+                for range in &mut contiguous_selections {
+                    range.start.row += row_delta;
+                    range.end.row += row_delta;
+                }
+
+                // Move folds down.
+                old_folds.push(start..end);
+                for fold in self.display_map.folds_in_range(start..end, app) {
+                    let mut start = fold.start.to_point(buffer);
+                    let mut end = fold.end.to_point(buffer);
+                    start.row += row_delta;
+                    end.row += row_delta;
+                    new_folds.push(start..end);
+                }
+            }
+
+            new_selection_ranges.extend(contiguous_selections.drain(..));
+        }
+
+        self.unfold_ranges(old_folds, ctx);
+        self.buffer.update(ctx, |buffer, ctx| {
+            for (range, text) in edits.into_iter().rev() {
+                buffer.edit(Some(range), text, Some(ctx)).unwrap();
+            }
+        });
+        self.fold_ranges(new_folds, ctx);
+        self.select_ranges(new_selection_ranges, true, ctx);
+
+        self.end_transaction(ctx);
+    }
+
+    pub fn cut(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        self.start_transaction(ctx);
+        let mut text = String::new();
+        let mut selections = self.selections(ctx.as_ref()).to_vec();
+        let mut clipboard_selections = Vec::with_capacity(selections.len());
+        {
+            let buffer = self.buffer.read(ctx);
+            let max_point = buffer.max_point();
+            for selection in &mut selections {
+                let mut start = selection.start.to_point(buffer);
+                let mut end = selection.end.to_point(buffer);
+                let is_entire_line = start == end;
+                if is_entire_line {
+                    start = Point::new(start.row, 0);
+                    end = cmp::min(max_point, Point::new(start.row + 1, 0));
+                    selection.start = buffer.anchor_before(start);
+                    selection.end = buffer.anchor_before(end);
+                }
+                let mut len = 0;
+                for chunk in buffer.text_for_range(start..end) {
+                    text.push_str(chunk);
+                    len += chunk.len();
+                }
+                clipboard_selections.push(ClipboardSelection {
+                    len,
+                    is_entire_line,
+                });
+            }
+        }
+        self.update_selections(selections, true, ctx);
+        self.insert(&String::new(), ctx);
+        self.end_transaction(ctx);
+
+        ctx.as_mut()
+            .write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
+    }
+
+    pub fn copy(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let buffer = self.buffer.read(ctx);
+        let max_point = buffer.max_point();
+        let mut text = String::new();
+        let selections = self.selections(ctx.as_ref());
+        let mut clipboard_selections = Vec::with_capacity(selections.len());
+        for selection in selections {
+            let mut start = selection.start.to_point(buffer);
+            let mut end = selection.end.to_point(buffer);
+            let is_entire_line = start == end;
+            if is_entire_line {
+                start = Point::new(start.row, 0);
+                end = cmp::min(max_point, Point::new(start.row + 1, 0));
+            }
+            let mut len = 0;
+            for chunk in buffer.text_for_range(start..end) {
+                text.push_str(chunk);
+                len += chunk.len();
+            }
+            clipboard_selections.push(ClipboardSelection {
+                len,
+                is_entire_line,
+            });
+        }
+
+        ctx.as_mut()
+            .write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
+    }
+
+    pub fn paste(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        if let Some(item) = ctx.as_mut().read_from_clipboard() {
+            let clipboard_text = item.text();
+            if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
+                let selections = self.selections(ctx.as_ref()).to_vec();
+                if clipboard_selections.len() != selections.len() {
+                    let merged_selection = ClipboardSelection {
+                        len: clipboard_selections.iter().map(|s| s.len).sum(),
+                        is_entire_line: clipboard_selections.iter().all(|s| s.is_entire_line),
+                    };
+                    clipboard_selections.clear();
+                    clipboard_selections.push(merged_selection);
+                }
+
+                self.start_transaction(ctx);
+                let mut new_selections = Vec::with_capacity(selections.len());
+                let mut clipboard_chars = clipboard_text.chars().cycle();
+                for (selection, clipboard_selection) in
+                    selections.iter().zip(clipboard_selections.iter().cycle())
+                {
+                    let to_insert =
+                        String::from_iter(clipboard_chars.by_ref().take(clipboard_selection.len));
+
+                    self.buffer.update(ctx, |buffer, ctx| {
+                        let selection_start = selection.start.to_point(buffer);
+                        let selection_end = selection.end.to_point(buffer);
+
+                        // If the corresponding selection was empty when this slice of the
+                        // clipboard text was written, then the entire line containing the
+                        // selection was copied. If this selection is also currently empty,
+                        // then paste the line before the current line of the buffer.
+                        let new_selection_start = selection.end.bias_right(buffer);
+                        if selection_start == selection_end && clipboard_selection.is_entire_line {
+                            let line_start = Point::new(selection_start.row, 0);
+                            buffer
+                                .edit(Some(line_start..line_start), to_insert, Some(ctx))
+                                .unwrap();
+                        } else {
+                            buffer
+                                .edit(Some(&selection.start..&selection.end), to_insert, Some(ctx))
+                                .unwrap();
+                        };
+
+                        let new_selection_start = new_selection_start.bias_left(buffer);
+                        new_selections.push(Selection {
+                            id: selection.id,
+                            start: new_selection_start.clone(),
+                            end: new_selection_start,
+                            reversed: false,
+                            goal: SelectionGoal::None,
+                        });
+                    });
+                }
+                self.update_selections(new_selections, true, ctx);
+                self.end_transaction(ctx);
+            } else {
+                self.insert(clipboard_text, ctx);
+            }
+        }
+    }
+
+    pub fn undo(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        self.buffer
+            .update(ctx, |buffer, ctx| buffer.undo(Some(ctx)));
+    }
+
+    pub fn redo(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        self.buffer
+            .update(ctx, |buffer, ctx| buffer.redo(Some(ctx)));
+    }
+
+    pub fn move_left(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let app = ctx.as_ref();
+        let mut selections = self.selections(app).to_vec();
+        {
+            for selection in &mut selections {
+                let start = selection.start.to_display_point(&self.display_map, app);
+                let end = selection.end.to_display_point(&self.display_map, app);
+
+                if start != end {
+                    selection.end = selection.start.clone();
+                } else {
+                    let cursor = self.display_map.anchor_before(
+                        movement::left(&self.display_map, start, app).unwrap(),
+                        Bias::Left,
+                        app,
+                    );
+                    selection.start = cursor.clone();
+                    selection.end = cursor;
+                }
+                selection.reversed = false;
+                selection.goal = SelectionGoal::None;
+            }
+        }
+        self.update_selections(selections, true, ctx);
+    }
+
+    pub fn select_left(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let mut selections = self.selections(ctx.as_ref()).to_vec();
+        {
+            let buffer = self.buffer.read(ctx);
+            for selection in &mut selections {
+                let head = selection
+                    .head()
+                    .to_display_point(&self.display_map, ctx.as_ref());
+                let cursor = self.display_map.anchor_before(
+                    movement::left(&self.display_map, head, ctx.as_ref()).unwrap(),
+                    Bias::Left,
+                    ctx.as_ref(),
+                );
+                selection.set_head(&buffer, cursor);
+                selection.goal = SelectionGoal::None;
+            }
+        }
+        self.update_selections(selections, true, ctx);
+    }
+
+    pub fn move_right(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let mut selections = self.selections(ctx.as_ref()).to_vec();
+        {
+            let app = ctx.as_ref();
+            for selection in &mut selections {
+                let start = selection.start.to_display_point(&self.display_map, app);
+                let end = selection.end.to_display_point(&self.display_map, app);
+
+                if start != end {
+                    selection.start = selection.end.clone();
+                } else {
+                    let cursor = self.display_map.anchor_before(
+                        movement::right(&self.display_map, end, app).unwrap(),
+                        Bias::Right,
+                        app,
+                    );
+                    selection.start = cursor.clone();
+                    selection.end = cursor;
+                }
+                selection.reversed = false;
+                selection.goal = SelectionGoal::None;
+            }
+        }
+        self.update_selections(selections, true, ctx);
+    }
+
+    pub fn select_right(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let mut selections = self.selections(ctx.as_ref()).to_vec();
+        {
+            let app = ctx.as_ref();
+            let buffer = self.buffer.read(app);
+            for selection in &mut selections {
+                let head = selection
+                    .head()
+                    .to_display_point(&self.display_map, ctx.as_ref());
+                let cursor = self.display_map.anchor_before(
+                    movement::right(&self.display_map, head, app).unwrap(),
+                    Bias::Right,
+                    app,
+                );
+                selection.set_head(&buffer, cursor);
+                selection.goal = SelectionGoal::None;
+            }
+        }
+        self.update_selections(selections, true, ctx);
+    }
+
+    pub fn move_up(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        if self.single_line {
+            ctx.propagate_action();
+        } else {
+            let mut selections = self.selections(ctx.as_ref()).to_vec();
+            {
+                let app = ctx.as_ref();
+                for selection in &mut selections {
+                    let start = selection.start.to_display_point(&self.display_map, app);
+                    let end = selection.end.to_display_point(&self.display_map, app);
+                    if start != end {
+                        selection.goal = SelectionGoal::None;
+                    }
+
+                    let (start, goal) =
+                        movement::up(&self.display_map, start, selection.goal, app).unwrap();
+                    let cursor = self.display_map.anchor_before(start, Bias::Left, app);
+                    selection.start = cursor.clone();
+                    selection.end = cursor;
+                    selection.goal = goal;
+                    selection.reversed = false;
+                }
+            }
+            self.update_selections(selections, true, ctx);
+        }
+    }
+
+    pub fn select_up(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let mut selections = self.selections(ctx.as_ref()).to_vec();
+        {
+            let app = ctx.as_ref();
+            let buffer = self.buffer.read(app);
+            for selection in &mut selections {
+                let head = selection.head().to_display_point(&self.display_map, app);
+                let (head, goal) =
+                    movement::up(&self.display_map, head, selection.goal, app).unwrap();
+                selection.set_head(
+                    &buffer,
+                    self.display_map.anchor_before(head, Bias::Left, app),
+                );
+                selection.goal = goal;
+            }
+        }
+        self.update_selections(selections, true, ctx);
+    }
+
+    pub fn move_down(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        if self.single_line {
+            ctx.propagate_action();
+        } else {
+            let mut selections = self.selections(ctx.as_ref()).to_vec();
+            {
+                let app = ctx.as_ref();
+                for selection in &mut selections {
+                    let start = selection.start.to_display_point(&self.display_map, app);
+                    let end = selection.end.to_display_point(&self.display_map, app);
+                    if start != end {
+                        selection.goal = SelectionGoal::None;
+                    }
+
+                    let (start, goal) =
+                        movement::down(&self.display_map, end, selection.goal, app).unwrap();
+                    let cursor = self.display_map.anchor_before(start, Bias::Right, app);
+                    selection.start = cursor.clone();
+                    selection.end = cursor;
+                    selection.goal = goal;
+                    selection.reversed = false;
+                }
+            }
+            self.update_selections(selections, true, ctx);
+        }
+    }
+
+    pub fn select_down(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let mut selections = self.selections(ctx.as_ref()).to_vec();
+        {
+            let app = ctx.as_ref();
+            let buffer = self.buffer.read(app);
+            for selection in &mut selections {
+                let head = selection.head().to_display_point(&self.display_map, app);
+                let (head, goal) =
+                    movement::down(&self.display_map, head, selection.goal, app).unwrap();
+                selection.set_head(
+                    &buffer,
+                    self.display_map.anchor_before(head, Bias::Right, app),
+                );
+                selection.goal = goal;
+            }
+        }
+        self.update_selections(selections, true, ctx);
+    }
+
+    pub fn move_to_previous_word_boundary(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let app = ctx.as_ref();
+        let mut selections = self.selections(app).to_vec();
+        {
+            for selection in &mut selections {
+                let head = selection.head().to_display_point(&self.display_map, app);
+                let new_head = movement::prev_word_boundary(&self.display_map, head, app).unwrap();
+                let anchor = self.display_map.anchor_before(new_head, Bias::Left, app);
+                selection.start = anchor.clone();
+                selection.end = anchor;
+                selection.reversed = false;
+                selection.goal = SelectionGoal::None;
+            }
+        }
+        self.update_selections(selections, true, ctx);
+    }
+
+    pub fn select_to_previous_word_boundary(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let app = ctx.as_ref();
+        let mut selections = self.selections(app).to_vec();
+        {
+            let buffer = self.buffer.read(ctx);
+            for selection in &mut selections {
+                let head = selection.head().to_display_point(&self.display_map, app);
+                let new_head = movement::prev_word_boundary(&self.display_map, head, app).unwrap();
+                let anchor = self.display_map.anchor_before(new_head, Bias::Left, app);
+                selection.set_head(buffer, anchor);
+                selection.goal = SelectionGoal::None;
+            }
+        }
+        self.update_selections(selections, true, ctx);
+    }
+
+    pub fn delete_to_previous_word_boundary(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        self.start_transaction(ctx);
+        self.select_to_previous_word_boundary(&(), ctx);
+        self.backspace(&(), ctx);
+        self.end_transaction(ctx);
+    }
+
+    pub fn move_to_next_word_boundary(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let app = ctx.as_ref();
+        let mut selections = self.selections(app).to_vec();
+        {
+            for selection in &mut selections {
+                let head = selection.head().to_display_point(&self.display_map, app);
+                let new_head = movement::next_word_boundary(&self.display_map, head, app).unwrap();
+                let anchor = self.display_map.anchor_before(new_head, Bias::Left, app);
+                selection.start = anchor.clone();
+                selection.end = anchor;
+                selection.reversed = false;
+                selection.goal = SelectionGoal::None;
+            }
+        }
+        self.update_selections(selections, true, ctx);
+    }
+
+    pub fn select_to_next_word_boundary(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let app = ctx.as_ref();
+        let mut selections = self.selections(app).to_vec();
+        {
+            let buffer = self.buffer.read(ctx);
+            for selection in &mut selections {
+                let head = selection.head().to_display_point(&self.display_map, app);
+                let new_head = movement::next_word_boundary(&self.display_map, head, app).unwrap();
+                let anchor = self.display_map.anchor_before(new_head, Bias::Left, app);
+                selection.set_head(buffer, anchor);
+                selection.goal = SelectionGoal::None;
+            }
+        }
+        self.update_selections(selections, true, ctx);
+    }
+
+    pub fn delete_to_next_word_boundary(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        self.start_transaction(ctx);
+        self.select_to_next_word_boundary(&(), ctx);
+        self.delete(&(), ctx);
+        self.end_transaction(ctx);
+    }
+
+    pub fn move_to_beginning_of_line(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let app = ctx.as_ref();
+        let mut selections = self.selections(app).to_vec();
+        {
+            for selection in &mut selections {
+                let head = selection.head().to_display_point(&self.display_map, app);
+                let new_head =
+                    movement::line_beginning(&self.display_map, head, true, app).unwrap();
+                let anchor = self.display_map.anchor_before(new_head, Bias::Left, app);
+                selection.start = anchor.clone();
+                selection.end = anchor;
+                selection.reversed = false;
+                selection.goal = SelectionGoal::None;
+            }
+        }
+        self.update_selections(selections, true, ctx);
+    }
+
+    pub fn select_to_beginning_of_line(
+        &mut self,
+        toggle_indent: &bool,
+        ctx: &mut ViewContext<Self>,
+    ) {
+        let app = ctx.as_ref();
+        let mut selections = self.selections(app).to_vec();
+        {
+            let buffer = self.buffer.read(ctx);
+            for selection in &mut selections {
+                let head = selection.head().to_display_point(&self.display_map, app);
+                let new_head =
+                    movement::line_beginning(&self.display_map, head, *toggle_indent, app).unwrap();
+                let anchor = self.display_map.anchor_before(new_head, Bias::Left, app);
+                selection.set_head(buffer, anchor);
+                selection.goal = SelectionGoal::None;
+            }
+        }
+        self.update_selections(selections, true, ctx);
+    }
+
+    pub fn delete_to_beginning_of_line(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        self.start_transaction(ctx);
+        self.select_to_beginning_of_line(&false, ctx);
+        self.backspace(&(), ctx);
+        self.end_transaction(ctx);
+    }
+
+    pub fn move_to_end_of_line(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let app = ctx.as_ref();
+        let mut selections = self.selections(app).to_vec();
+        {
+            for selection in &mut selections {
+                let head = selection.head().to_display_point(&self.display_map, app);
+                let new_head = movement::line_end(&self.display_map, head, app).unwrap();
+                let anchor = self.display_map.anchor_before(new_head, Bias::Left, app);
+                selection.start = anchor.clone();
+                selection.end = anchor;
+                selection.reversed = false;
+                selection.goal = SelectionGoal::None;
+            }
+        }
+        self.update_selections(selections, true, ctx);
+    }
+
+    pub fn select_to_end_of_line(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let app = ctx.as_ref();
+        let mut selections = self.selections(app).to_vec();
+        {
+            let buffer = self.buffer.read(ctx);
+            for selection in &mut selections {
+                let head = selection.head().to_display_point(&self.display_map, app);
+                let new_head = movement::line_end(&self.display_map, head, app).unwrap();
+                let anchor = self.display_map.anchor_before(new_head, Bias::Left, app);
+                selection.set_head(buffer, anchor);
+                selection.goal = SelectionGoal::None;
+            }
+        }
+        self.update_selections(selections, true, ctx);
+    }
+
+    pub fn delete_to_end_of_line(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        self.start_transaction(ctx);
+        self.select_to_end_of_line(&(), ctx);
+        self.delete(&(), ctx);
+        self.end_transaction(ctx);
+    }
+
+    pub fn move_to_beginning(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let buffer = self.buffer.read(ctx);
+        let cursor = buffer.anchor_before(Point::new(0, 0));
+        let selection = Selection {
+            id: post_inc(&mut self.next_selection_id),
+            start: cursor.clone(),
+            end: cursor,
+            reversed: false,
+            goal: SelectionGoal::None,
+        };
+        self.update_selections(vec![selection], true, ctx);
+    }
+
+    pub fn select_to_beginning(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let mut selection = self.selections(ctx.as_ref()).last().unwrap().clone();
+        selection.set_head(self.buffer.read(ctx), Anchor::Start);
+        self.update_selections(vec![selection], true, ctx);
+    }
+
+    pub fn move_to_end(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let buffer = self.buffer.read(ctx);
+        let cursor = buffer.anchor_before(buffer.max_point());
+        let selection = Selection {
+            id: post_inc(&mut self.next_selection_id),
+            start: cursor.clone(),
+            end: cursor,
+            reversed: false,
+            goal: SelectionGoal::None,
+        };
+        self.update_selections(vec![selection], true, ctx);
+    }
+
+    pub fn select_to_end(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let mut selection = self.selections(ctx.as_ref()).last().unwrap().clone();
+        selection.set_head(self.buffer.read(ctx), Anchor::End);
+        self.update_selections(vec![selection], true, ctx);
+    }
+
+    pub fn select_all(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let selection = Selection {
+            id: post_inc(&mut self.next_selection_id),
+            start: Anchor::Start,
+            end: Anchor::End,
+            reversed: false,
+            goal: SelectionGoal::None,
+        };
+        self.update_selections(vec![selection], false, ctx);
+    }
+
+    pub fn select_line(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let app = ctx.as_ref();
+        let buffer = self.buffer.read(app);
+        let mut selections = self.selections(app).to_vec();
+        let max_point = buffer.max_point();
+        for selection in &mut selections {
+            let (rows, _) = selection.buffer_rows_for_display_rows(true, &self.display_map, app);
+            selection.start = buffer.anchor_before(Point::new(rows.start, 0));
+            selection.end = buffer.anchor_before(cmp::min(max_point, Point::new(rows.end, 0)));
+            selection.reversed = false;
+        }
+        self.update_selections(selections, true, ctx);
+    }
+
+    pub fn split_selection_into_lines(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let app = ctx.as_ref();
+        let buffer = self.buffer.read(app);
+
+        let mut to_unfold = Vec::new();
+        let mut new_selections = Vec::new();
+        for selection in self.selections(app) {
+            let range = selection.point_range(buffer).sorted();
+            if range.start.row != range.end.row {
+                new_selections.push(Selection {
+                    id: post_inc(&mut self.next_selection_id),
+                    start: selection.start.clone(),
+                    end: selection.start.clone(),
+                    reversed: false,
+                    goal: SelectionGoal::None,
+                });
+            }
+            for row in range.start.row + 1..range.end.row {
+                let cursor = buffer.anchor_before(Point::new(row, buffer.line_len(row)));
+                new_selections.push(Selection {
+                    id: post_inc(&mut self.next_selection_id),
+                    start: cursor.clone(),
+                    end: cursor,
+                    reversed: false,
+                    goal: SelectionGoal::None,
+                });
+            }
+            new_selections.push(Selection {
+                id: selection.id,
+                start: selection.end.clone(),
+                end: selection.end.clone(),
+                reversed: false,
+                goal: SelectionGoal::None,
+            });
+            to_unfold.push(range);
+        }
+        self.unfold_ranges(to_unfold, ctx);
+        self.update_selections(new_selections, true, ctx);
+    }
+
+    pub fn add_selection_above(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        self.add_selection(true, ctx);
+    }
+
+    pub fn add_selection_below(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        self.add_selection(false, ctx);
+    }
+
+    fn add_selection(&mut self, above: bool, ctx: &mut ViewContext<Self>) {
+        let app = ctx.as_ref();
+
+        let mut selections = self.selections(app).to_vec();
+        let mut state = self.add_selections_state.take().unwrap_or_else(|| {
+            let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
+            let range = oldest_selection
+                .display_range(&self.display_map, app)
+                .sorted();
+            let columns = cmp::min(range.start.column(), range.end.column())
+                ..cmp::max(range.start.column(), range.end.column());
+
+            selections.clear();
+            let mut stack = Vec::new();
+            for row in range.start.row()..=range.end.row() {
+                if let Some(selection) =
+                    self.build_columnar_selection(row, &columns, oldest_selection.reversed, app)
+                {
+                    stack.push(selection.id);
+                    selections.push(selection);
+                }
+            }
+
+            if above {
+                stack.reverse();
+            }
+
+            AddSelectionsState { above, stack }
+        });
+
+        let last_added_selection = *state.stack.last().unwrap();
+        let mut new_selections = Vec::new();
+        if above == state.above {
+            let end_row = if above {
+                0
+            } else {
+                self.display_map.max_point(app).row()
+            };
+
+            'outer: for selection in selections {
+                if selection.id == last_added_selection {
+                    let range = selection.display_range(&self.display_map, app).sorted();
+                    debug_assert_eq!(range.start.row(), range.end.row());
+                    let mut row = range.start.row();
+                    let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
+                    {
+                        start..end
+                    } else {
+                        cmp::min(range.start.column(), range.end.column())
+                            ..cmp::max(range.start.column(), range.end.column())
+                    };
+
+                    while row != end_row {
+                        if above {
+                            row -= 1;
+                        } else {
+                            row += 1;
+                        }
+
+                        if let Some(new_selection) =
+                            self.build_columnar_selection(row, &columns, selection.reversed, app)
+                        {
+                            state.stack.push(new_selection.id);
+                            if above {
+                                new_selections.push(new_selection);
+                                new_selections.push(selection);
+                            } else {
+                                new_selections.push(selection);
+                                new_selections.push(new_selection);
+                            }
+
+                            continue 'outer;
+                        }
+                    }
+                }
+
+                new_selections.push(selection);
+            }
+        } else {
+            new_selections = selections;
+            new_selections.retain(|s| s.id != last_added_selection);
+            state.stack.pop();
+        }
+
+        self.update_selections(new_selections, true, ctx);
+        if state.stack.len() > 1 {
+            self.add_selections_state = Some(state);
+        }
+    }
+
+    pub fn select_larger_syntax_node(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let app = ctx.as_ref();
+        let buffer = self.buffer.read(app);
+
+        let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
+        let mut selected_larger_node = false;
+        let old_selections = self.selections(app).to_vec();
+        let mut new_selections = Vec::new();
+        for selection in &old_selections {
+            let old_range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
+            let mut new_range = old_range.clone();
+            while let Some(containing_range) = buffer.range_for_syntax_ancestor(new_range.clone()) {
+                new_range = containing_range;
+                if !self.display_map.intersects_fold(new_range.start, app)
+                    && !self.display_map.intersects_fold(new_range.end, app)
+                {
+                    break;
+                }
+            }
+
+            selected_larger_node |= new_range != old_range;
+            new_selections.push(Selection {
+                id: selection.id,
+                start: buffer.anchor_before(new_range.start),
+                end: buffer.anchor_before(new_range.end),
+                reversed: selection.reversed,
+                goal: SelectionGoal::None,
+            });
+        }
+
+        if selected_larger_node {
+            stack.push(old_selections);
+            self.update_selections(new_selections, true, ctx);
+        }
+        self.select_larger_syntax_node_stack = stack;
+    }
+
+    pub fn select_smaller_syntax_node(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
+        if let Some(selections) = stack.pop() {
+            self.update_selections(selections, true, ctx);
+        }
+        self.select_larger_syntax_node_stack = stack;
+    }
+
+    pub fn move_to_enclosing_bracket(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let buffer = self.buffer.read(ctx.as_ref());
+        let mut selections = self.selections(ctx.as_ref()).to_vec();
+        for selection in &mut selections {
+            let selection_range = selection.offset_range(buffer);
+            if let Some((open_range, close_range)) =
+                buffer.enclosing_bracket_ranges(selection_range.clone())
+            {
+                let close_range = close_range.to_inclusive();
+                let destination = if close_range.contains(&selection_range.start)
+                    && close_range.contains(&selection_range.end)
+                {
+                    open_range.end
+                } else {
+                    *close_range.start()
+                };
+                selection.start = buffer.anchor_before(destination);
+                selection.end = selection.start.clone();
+            }
+        }
+
+        self.update_selections(selections, true, ctx);
+    }
+
+    fn build_columnar_selection(
+        &mut self,
+        row: u32,
+        columns: &Range<u32>,
+        reversed: bool,
+        ctx: &AppContext,
+    ) -> Option<Selection> {
+        let is_empty = columns.start == columns.end;
+        let line_len = self.display_map.line_len(row, ctx);
+        if columns.start < line_len || (is_empty && columns.start == line_len) {
+            let start = DisplayPoint::new(row, columns.start);
+            let end = DisplayPoint::new(row, cmp::min(columns.end, line_len));
+            Some(Selection {
+                id: post_inc(&mut self.next_selection_id),
+                start: self.display_map.anchor_before(start, Bias::Left, ctx),
+                end: self.display_map.anchor_before(end, Bias::Left, ctx),
+                reversed,
+                goal: SelectionGoal::ColumnRange {
+                    start: columns.start,
+                    end: columns.end,
+                },
+            })
+        } else {
+            None
+        }
+    }
+
+    pub fn selections_in_range<'a>(
+        &'a self,
+        range: Range<DisplayPoint>,
+        app: &'a AppContext,
+    ) -> impl 'a + Iterator<Item = Range<DisplayPoint>> {
+        let start = self.display_map.anchor_before(range.start, Bias::Left, app);
+        let start_index = self.selection_insertion_index(&start, app);
+        let pending_selection = self.pending_selection.as_ref().and_then(|s| {
+            let selection_range = s.display_range(&self.display_map, app);
+            if selection_range.start <= range.end || selection_range.end <= range.end {
+                Some(selection_range)
+            } else {
+                None
+            }
+        });
+        self.selections(app)[start_index..]
+            .iter()
+            .map(move |s| s.display_range(&self.display_map, app))
+            .take_while(move |r| r.start <= range.end || r.end <= range.end)
+            .chain(pending_selection)
+    }
+
+    fn selection_insertion_index(&self, start: &Anchor, app: &AppContext) -> usize {
+        let buffer = self.buffer.read(app);
+        let selections = self.selections(app);
+        match selections.binary_search_by(|probe| probe.start.cmp(&start, buffer).unwrap()) {
+            Ok(index) => index,
+            Err(index) => {
+                if index > 0
+                    && selections[index - 1].end.cmp(&start, buffer).unwrap() == Ordering::Greater
+                {
+                    index - 1
+                } else {
+                    index
+                }
+            }
+        }
+    }
+
+    fn selections<'a>(&self, app: &'a AppContext) -> &'a [Selection] {
+        self.buffer
+            .read(app)
+            .selections(self.selection_set_id)
+            .unwrap()
+    }
+
+    fn update_selections(
+        &mut self,
+        mut selections: Vec<Selection>,
+        autoscroll: bool,
+        ctx: &mut ViewContext<Self>,
+    ) {
+        // Merge overlapping selections.
+        let buffer = self.buffer.read(ctx);
+        let mut i = 1;
+        while i < selections.len() {
+            if selections[i - 1]
+                .end
+                .cmp(&selections[i].start, buffer)
+                .unwrap()
+                >= Ordering::Equal
+            {
+                let removed = selections.remove(i);
+                if removed.start.cmp(&selections[i - 1].start, buffer).unwrap() < Ordering::Equal {
+                    selections[i - 1].start = removed.start;
+                }
+                if removed.end.cmp(&selections[i - 1].end, buffer).unwrap() > Ordering::Equal {
+                    selections[i - 1].end = removed.end;
+                }
+            } else {
+                i += 1;
+            }
+        }
+
+        self.buffer.update(ctx, |buffer, ctx| {
+            buffer
+                .update_selection_set(self.selection_set_id, selections, Some(ctx))
+                .unwrap()
+        });
+        self.pause_cursor_blinking(ctx);
+
+        if autoscroll {
+            *self.autoscroll_requested.lock() = true;
+            ctx.notify();
+        }
+
+        self.add_selections_state = None;
+        self.select_larger_syntax_node_stack.clear();
+    }
+
+    fn start_transaction(&self, ctx: &mut ViewContext<Self>) {
+        self.buffer.update(ctx, |buffer, _| {
+            buffer
+                .start_transaction(Some(self.selection_set_id))
+                .unwrap()
+        });
+    }
+
+    fn end_transaction(&self, ctx: &mut ViewContext<Self>) {
+        self.buffer.update(ctx, |buffer, ctx| {
+            buffer
+                .end_transaction(Some(self.selection_set_id), Some(ctx))
+                .unwrap()
+        });
+    }
+
+    pub fn page_up(&mut self, _: &(), _: &mut ViewContext<Self>) {
+        log::info!("BufferView::page_up");
+    }
+
+    pub fn page_down(&mut self, _: &(), _: &mut ViewContext<Self>) {
+        log::info!("BufferView::page_down");
+    }
+
+    pub fn fold(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let mut fold_ranges = Vec::new();
+
+        let app = ctx.as_ref();
+        for selection in self.selections(app) {
+            let range = selection.display_range(&self.display_map, app).sorted();
+            let buffer_start_row = range
+                .start
+                .to_buffer_point(&self.display_map, Bias::Left, app)
+                .row;
+
+            for row in (0..=range.end.row()).rev() {
+                if self.is_line_foldable(row, app)
+                    && !self.display_map.is_line_folded(row, ctx.as_ref())
+                {
+                    let fold_range = self.foldable_range_for_line(row, app);
+                    if fold_range.end.row >= buffer_start_row {
+                        fold_ranges.push(fold_range);
+                        if row <= range.start.row() {
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        self.fold_ranges(fold_ranges, ctx);
+    }
+
+    pub fn unfold(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let app = ctx.as_ref();
+        let buffer = self.buffer.read(app);
+        let ranges = self
+            .selections(app)
+            .iter()
+            .map(|s| {
+                let range = s.display_range(&self.display_map, app).sorted();
+                let mut start = range
+                    .start
+                    .to_buffer_point(&self.display_map, Bias::Left, app);
+                let mut end = range
+                    .end
+                    .to_buffer_point(&self.display_map, Bias::Left, app);
+                start.column = 0;
+                end.column = buffer.line_len(end.row);
+                start..end
+            })
+            .collect::<Vec<_>>();
+        self.unfold_ranges(ranges, ctx);
+    }
+
+    fn is_line_foldable(&self, display_row: u32, app: &AppContext) -> bool {
+        let max_point = self.max_point(app);
+        if display_row >= max_point.row() {
+            false
+        } else {
+            let (start_indent, is_blank) = self.display_map.line_indent(display_row, app);
+            if is_blank {
+                false
+            } else {
+                for display_row in display_row + 1..=max_point.row() {
+                    let (indent, is_blank) = self.display_map.line_indent(display_row, app);
+                    if !is_blank {
+                        return indent > start_indent;
+                    }
+                }
+                false
+            }
+        }
+    }
+
+    fn foldable_range_for_line(&self, start_row: u32, app: &AppContext) -> Range<Point> {
+        let max_point = self.max_point(app);
+
+        let (start_indent, _) = self.display_map.line_indent(start_row, app);
+        let start = DisplayPoint::new(start_row, self.line_len(start_row, app));
+        let mut end = None;
+        for row in start_row + 1..=max_point.row() {
+            let (indent, is_blank) = self.display_map.line_indent(row, app);
+            if !is_blank && indent <= start_indent {
+                end = Some(DisplayPoint::new(row - 1, self.line_len(row - 1, app)));
+                break;
+            }
+        }
+
+        let end = end.unwrap_or(max_point);
+        return start.to_buffer_point(&self.display_map, Bias::Left, app)
+            ..end.to_buffer_point(&self.display_map, Bias::Left, app);
+    }
+
+    pub fn fold_selected_ranges(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let buffer = self.buffer.read(ctx);
+        let ranges = self
+            .selections(ctx.as_ref())
+            .iter()
+            .map(|s| s.point_range(buffer).sorted())
+            .collect();
+        self.fold_ranges(ranges, ctx);
+    }
+
+    fn fold_ranges<T: ToOffset>(&mut self, ranges: Vec<Range<T>>, ctx: &mut ViewContext<Self>) {
+        if !ranges.is_empty() {
+            self.display_map.fold(ranges, ctx.as_ref());
+            *self.autoscroll_requested.lock() = true;
+            ctx.notify();
+        }
+    }
+
+    fn unfold_ranges<T: ToOffset>(&mut self, ranges: Vec<Range<T>>, ctx: &mut ViewContext<Self>) {
+        if !ranges.is_empty() {
+            self.display_map.unfold(ranges, ctx.as_ref());
+            *self.autoscroll_requested.lock() = true;
+            ctx.notify();
+        }
+    }
+
+    pub fn line(&self, display_row: u32, ctx: &AppContext) -> String {
+        self.display_map.line(display_row, ctx)
+    }
+
+    pub fn line_len(&self, display_row: u32, ctx: &AppContext) -> u32 {
+        self.display_map.line_len(display_row, ctx)
+    }
+
+    pub fn longest_row(&self, ctx: &AppContext) -> u32 {
+        self.display_map.longest_row(ctx)
+    }
+
+    pub fn max_point(&self, ctx: &AppContext) -> DisplayPoint {
+        self.display_map.max_point(ctx)
+    }
+
+    pub fn text(&self, ctx: &AppContext) -> String {
+        self.display_map.text(ctx)
+    }
+
+    pub fn font_size(&self) -> f32 {
+        self.settings.borrow().buffer_font_size
+    }
+
+    pub fn font_ascent(&self, font_cache: &FontCache) -> f32 {
+        let settings = self.settings.borrow();
+        let font_id = font_cache.default_font(settings.buffer_font_family);
+        let ascent = font_cache.metric(font_id, |m| m.ascent);
+        font_cache.scale_metric(ascent, font_id, settings.buffer_font_size)
+    }
+
+    pub fn font_descent(&self, font_cache: &FontCache) -> f32 {
+        let settings = self.settings.borrow();
+        let font_id = font_cache.default_font(settings.buffer_font_family);
+        let ascent = font_cache.metric(font_id, |m| m.descent);
+        font_cache.scale_metric(ascent, font_id, settings.buffer_font_size)
+    }
+
+    pub fn line_height(&self, font_cache: &FontCache) -> f32 {
+        let settings = self.settings.borrow();
+        let font_id = font_cache.default_font(settings.buffer_font_family);
+        font_cache.line_height(font_id, settings.buffer_font_size)
+    }
+
+    pub fn em_width(&self, font_cache: &FontCache) -> f32 {
+        let settings = self.settings.borrow();
+        let font_id = font_cache.default_font(settings.buffer_font_family);
+        font_cache.em_width(font_id, settings.buffer_font_size)
+    }
+
+    // TODO: Can we make this not return a result?
+    pub fn max_line_number_width(
+        &self,
+        font_cache: &FontCache,
+        layout_cache: &TextLayoutCache,
+        app: &AppContext,
+    ) -> Result<f32> {
+        let settings = self.settings.borrow();
+        let font_size = settings.buffer_font_size;
+        let font_id =
+            font_cache.select_font(settings.buffer_font_family, &FontProperties::new())?;
+        let digit_count = (self.buffer.read(app).row_count() as f32).log10().floor() as usize + 1;
+
+        Ok(layout_cache
+            .layout_str(
+                "1".repeat(digit_count).as_str(),
+                font_size,
+                &[(digit_count, font_id, ColorU::black())],
+            )
+            .width())
+    }
+
+    pub fn layout_line_numbers(
+        &self,
+        viewport_height: f32,
+        font_cache: &FontCache,
+        layout_cache: &TextLayoutCache,
+        ctx: &AppContext,
+    ) -> Result<Vec<text_layout::Line>> {
+        let settings = self.settings.borrow();
+        let font_size = settings.buffer_font_size;
+        let font_id =
+            font_cache.select_font(settings.buffer_font_family, &FontProperties::new())?;
+
+        let start_row = self.scroll_position().y() as usize;
+        let end_row = cmp::min(
+            self.max_point(ctx).row() as usize,
+            start_row + (viewport_height / self.line_height(font_cache)).ceil() as usize,
+        );
+        let line_count = end_row - start_row + 1;
+
+        let mut layouts = Vec::with_capacity(line_count);
+        let mut line_number = String::new();
+        for buffer_row in self
+            .display_map
+            .snapshot(ctx)
+            .buffer_rows(start_row as u32)
+            .take(line_count)
+        {
+            line_number.clear();
+            write!(&mut line_number, "{}", buffer_row + 1).unwrap();
+            layouts.push(layout_cache.layout_str(
+                &line_number,
+                font_size,
+                &[(line_number.len(), font_id, ColorU::black())],
+            ));
+        }
+
+        Ok(layouts)
+    }
+
+    pub fn layout_lines(
+        &self,
+        mut rows: Range<u32>,
+        font_cache: &FontCache,
+        layout_cache: &TextLayoutCache,
+        ctx: &AppContext,
+    ) -> Result<Vec<text_layout::Line>> {
+        rows.end = cmp::min(rows.end, self.display_map.max_point(ctx).row() + 1);
+        if rows.start >= rows.end {
+            return Ok(Vec::new());
+        }
+
+        let settings = self.settings.borrow();
+        let font_size = settings.buffer_font_size;
+        let font_family = settings.buffer_font_family;
+        let mut prev_font_properties = FontProperties::new();
+        let mut prev_font_id = font_cache
+            .select_font(font_family, &prev_font_properties)
+            .unwrap();
+
+        let mut layouts = Vec::with_capacity(rows.len());
+        let mut line = String::new();
+        let mut styles = Vec::new();
+        let mut row = rows.start;
+        let mut snapshot = self.display_map.snapshot(ctx);
+        let chunks = snapshot.highlighted_chunks_for_rows(rows.clone());
+        let theme = settings.theme.clone();
+
+        'outer: for (chunk, style_ix) in chunks.chain(Some(("\n", StyleId::default()))) {
+            for (ix, line_chunk) in chunk.split('\n').enumerate() {
+                if ix > 0 {
+                    layouts.push(layout_cache.layout_str(&line, font_size, &styles));
+                    line.clear();
+                    styles.clear();
+                    row += 1;
+                    if row == rows.end {
+                        break 'outer;
+                    }
+                }
+
+                if !line_chunk.is_empty() {
+                    let (color, font_properties) = theme.syntax_style(style_ix);
+                    // Avoid a lookup if the font properties match the previous ones.
+                    let font_id = if font_properties == prev_font_properties {
+                        prev_font_id
+                    } else {
+                        font_cache.select_font(font_family, &font_properties)?
+                    };
+                    line.push_str(line_chunk);
+                    styles.push((line_chunk.len(), font_id, color));
+                    prev_font_id = font_id;
+                    prev_font_properties = font_properties;
+                }
+            }
+        }
+
+        Ok(layouts)
+    }
+
+    pub fn layout_line(
+        &self,
+        row: u32,
+        font_cache: &FontCache,
+        layout_cache: &TextLayoutCache,
+        app: &AppContext,
+    ) -> Result<text_layout::Line> {
+        let settings = self.settings.borrow();
+        let font_id =
+            font_cache.select_font(settings.buffer_font_family, &FontProperties::new())?;
+
+        let line = self.line(row, app);
+
+        Ok(layout_cache.layout_str(
+            &line,
+            settings.buffer_font_size,
+            &[(self.line_len(row, app) as usize, font_id, ColorU::black())],
+        ))
+    }
+
+    fn next_blink_epoch(&mut self) -> usize {
+        self.blink_epoch += 1;
+        self.blink_epoch
+    }
+
+    fn pause_cursor_blinking(&mut self, ctx: &mut ViewContext<Self>) {
+        self.cursors_visible = true;
+        ctx.notify();
+
+        let epoch = self.next_blink_epoch();
+        ctx.spawn(|this, mut ctx| async move {
+            Timer::after(CURSOR_BLINK_INTERVAL).await;
+            this.update(&mut ctx, |this, ctx| {
+                this.resume_cursor_blinking(epoch, ctx);
+            })
+        })
+        .detach();
+    }
+
+    fn resume_cursor_blinking(&mut self, epoch: usize, ctx: &mut ViewContext<Self>) {
+        if epoch == self.blink_epoch {
+            self.blinking_paused = false;
+            self.blink_cursors(epoch, ctx);
+        }
+    }
+
+    fn blink_cursors(&mut self, epoch: usize, ctx: &mut ViewContext<Self>) {
+        if epoch == self.blink_epoch && self.focused && !self.blinking_paused {
+            self.cursors_visible = !self.cursors_visible;
+            ctx.notify();
+
+            let epoch = self.next_blink_epoch();
+            ctx.spawn(|this, mut ctx| async move {
+                Timer::after(CURSOR_BLINK_INTERVAL).await;
+                this.update(&mut ctx, |this, ctx| this.blink_cursors(epoch, ctx));
+            })
+            .detach();
+        }
+    }
+
+    pub fn cursors_visible(&self) -> bool {
+        self.cursors_visible
+    }
+
+    fn on_buffer_changed(&mut self, _: ModelHandle<Buffer>, ctx: &mut ViewContext<Self>) {
+        ctx.notify();
+    }
+
+    fn on_buffer_event(
+        &mut self,
+        _: ModelHandle<Buffer>,
+        event: &buffer::Event,
+        ctx: &mut ViewContext<Self>,
+    ) {
+        match event {
+            buffer::Event::Edited => ctx.emit(Event::Edited),
+            buffer::Event::Dirtied => ctx.emit(Event::Dirtied),
+            buffer::Event::Saved => ctx.emit(Event::Saved),
+            buffer::Event::FileHandleChanged => ctx.emit(Event::FileHandleChanged),
+            buffer::Event::Reloaded => ctx.emit(Event::FileHandleChanged),
+            buffer::Event::Reparsed => {}
+        }
+    }
+}
+
+pub enum Event {
+    Activate,
+    Edited,
+    Blurred,
+    Dirtied,
+    Saved,
+    FileHandleChanged,
+}
+
+impl Entity for Editor {
+    type Event = Event;
+}
+
+impl View for Editor {
+    fn render<'a>(&self, _: &AppContext) -> ElementBox {
+        EditorElement::new(self.handle.clone()).boxed()
+    }
+
+    fn ui_name() -> &'static str {
+        "BufferView"
+    }
+
+    fn on_focus(&mut self, ctx: &mut ViewContext<Self>) {
+        self.focused = true;
+        self.blink_cursors(self.blink_epoch, ctx);
+    }
+
+    fn on_blur(&mut self, ctx: &mut ViewContext<Self>) {
+        self.focused = false;
+        self.cursors_visible = false;
+        ctx.emit(Event::Blurred);
+        ctx.notify();
+    }
+}
+
+impl workspace::Item for Buffer {
+    type View = Editor;
+
+    fn file(&self) -> Option<&FileHandle> {
+        self.file()
+    }
+
+    fn build_view(
+        handle: ModelHandle<Self>,
+        settings: watch::Receiver<Settings>,
+        ctx: &mut ViewContext<Self::View>,
+    ) -> Self::View {
+        Editor::for_buffer(handle, settings, ctx)
+    }
+}
+
+impl workspace::ItemView for Editor {
+    fn should_activate_item_on_event(event: &Self::Event) -> bool {
+        matches!(event, Event::Activate)
+    }
+
+    fn should_update_tab_on_event(event: &Self::Event) -> bool {
+        matches!(
+            event,
+            Event::Saved | Event::Dirtied | Event::FileHandleChanged
+        )
+    }
+
+    fn title(&self, app: &AppContext) -> std::string::String {
+        let filename = self
+            .buffer
+            .read(app)
+            .file()
+            .and_then(|file| file.file_name(app));
+        if let Some(name) = filename {
+            name.to_string_lossy().into()
+        } else {
+            "untitled".into()
+        }
+    }
+
+    fn entry_id(&self, ctx: &AppContext) -> Option<(usize, Arc<Path>)> {
+        self.buffer.read(ctx).file().map(|file| file.entry_id())
+    }
+
+    fn clone_on_split(&self, ctx: &mut ViewContext<Self>) -> Option<Self>
+    where
+        Self: Sized,
+    {
+        let clone = Editor::for_buffer(self.buffer.clone(), self.settings.clone(), ctx);
+        *clone.scroll_position.lock() = *self.scroll_position.lock();
+        Some(clone)
+    }
+
+    fn save(
+        &mut self,
+        new_file: Option<FileHandle>,
+        ctx: &mut ViewContext<Self>,
+    ) -> Task<Result<()>> {
+        self.buffer.update(ctx, |b, ctx| b.save(new_file, ctx))
+    }
+
+    fn is_dirty(&self, ctx: &AppContext) -> bool {
+        self.buffer.read(ctx).is_dirty()
+    }
+
+    fn has_conflict(&self, ctx: &AppContext) -> bool {
+        self.buffer.read(ctx).has_conflict()
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::{
+        editor::Point,
+        settings,
+        test::{build_app_state, sample_text},
+    };
+    use buffer::History;
+    use unindent::Unindent;
+
+    #[gpui::test]
+    fn test_selection_with_mouse(app: &mut gpui::MutableAppContext) {
+        let buffer = app.add_model(|ctx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n", ctx));
+        let settings = settings::channel(&app.font_cache()).unwrap().1;
+        let (_, buffer_view) = app.add_window(|ctx| Editor::for_buffer(buffer, settings, ctx));
+
+        buffer_view.update(app, |view, ctx| {
+            view.begin_selection(DisplayPoint::new(2, 2), false, ctx);
+        });
+
+        let view = buffer_view.read(app);
+        let selections = view
+            .selections_in_range(
+                DisplayPoint::zero()..view.max_point(app.as_ref()),
+                app.as_ref(),
+            )
+            .collect::<Vec<_>>();
+        assert_eq!(
+            selections,
+            [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
+        );
+
+        buffer_view.update(app, |view, ctx| {
+            view.update_selection(DisplayPoint::new(3, 3), Vector2F::zero(), ctx);
+        });
+
+        let view = buffer_view.read(app);
+        let selections = view
+            .selections_in_range(
+                DisplayPoint::zero()..view.max_point(app.as_ref()),
+                app.as_ref(),
+            )
+            .collect::<Vec<_>>();
+        assert_eq!(
+            selections,
+            [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
+        );
+
+        buffer_view.update(app, |view, ctx| {
+            view.update_selection(DisplayPoint::new(1, 1), Vector2F::zero(), ctx);
+        });
+
+        let view = buffer_view.read(app);
+        let selections = view
+            .selections_in_range(
+                DisplayPoint::zero()..view.max_point(app.as_ref()),
+                app.as_ref(),
+            )
+            .collect::<Vec<_>>();
+        assert_eq!(
+            selections,
+            [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
+        );
+
+        buffer_view.update(app, |view, ctx| {
+            view.end_selection(ctx);
+            view.update_selection(DisplayPoint::new(3, 3), Vector2F::zero(), ctx);
+        });
+
+        let view = buffer_view.read(app);
+        let selections = view
+            .selections_in_range(
+                DisplayPoint::zero()..view.max_point(app.as_ref()),
+                app.as_ref(),
+            )
+            .collect::<Vec<_>>();
+        assert_eq!(
+            selections,
+            [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
+        );
+
+        buffer_view.update(app, |view, ctx| {
+            view.begin_selection(DisplayPoint::new(3, 3), true, ctx);
+            view.update_selection(DisplayPoint::new(0, 0), Vector2F::zero(), ctx);
+        });
+
+        let view = buffer_view.read(app);
+        let selections = view
+            .selections_in_range(
+                DisplayPoint::zero()..view.max_point(app.as_ref()),
+                app.as_ref(),
+            )
+            .collect::<Vec<_>>();
+        assert_eq!(
+            selections,
+            [
+                DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
+                DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
+            ]
+        );
+
+        buffer_view.update(app, |view, ctx| {
+            view.end_selection(ctx);
+        });
+
+        let view = buffer_view.read(app);
+        let selections = view
+            .selections_in_range(
+                DisplayPoint::zero()..view.max_point(app.as_ref()),
+                app.as_ref(),
+            )
+            .collect::<Vec<_>>();
+        assert_eq!(
+            selections,
+            [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
+        );
+    }
+
+    #[gpui::test]
+    fn test_canceling_pending_selection(app: &mut gpui::MutableAppContext) {
+        let buffer = app.add_model(|ctx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n", ctx));
+        let settings = settings::channel(&app.font_cache()).unwrap().1;
+        let (_, view) = app.add_window(|ctx| Editor::for_buffer(buffer, settings, ctx));
+
+        view.update(app, |view, ctx| {
+            view.begin_selection(DisplayPoint::new(2, 2), false, ctx);
+        });
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
+        );
+
+        view.update(app, |view, ctx| {
+            view.update_selection(DisplayPoint::new(3, 3), Vector2F::zero(), ctx);
+        });
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
+        );
+
+        view.update(app, |view, ctx| {
+            view.cancel(&(), ctx);
+            view.update_selection(DisplayPoint::new(1, 1), Vector2F::zero(), ctx);
+        });
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
+        );
+    }
+
+    #[gpui::test]
+    fn test_cancel(app: &mut gpui::MutableAppContext) {
+        let buffer = app.add_model(|ctx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n", ctx));
+        let settings = settings::channel(&app.font_cache()).unwrap().1;
+        let (_, view) = app.add_window(|ctx| Editor::for_buffer(buffer, settings, ctx));
+
+        view.update(app, |view, ctx| {
+            view.begin_selection(DisplayPoint::new(3, 4), false, ctx);
+            view.update_selection(DisplayPoint::new(1, 1), Vector2F::zero(), ctx);
+            view.end_selection(ctx);
+
+            view.begin_selection(DisplayPoint::new(0, 1), true, ctx);
+            view.update_selection(DisplayPoint::new(0, 3), Vector2F::zero(), ctx);
+            view.end_selection(ctx);
+        });
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            [
+                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
+                DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
+            ]
+        );
+
+        view.update(app, |view, ctx| view.cancel(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
+        );
+
+        view.update(app, |view, ctx| view.cancel(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
+        );
+    }
+
+    #[gpui::test]
+    fn test_layout_line_numbers(app: &mut gpui::MutableAppContext) {
+        let layout_cache = TextLayoutCache::new(app.platform().fonts());
+        let font_cache = app.font_cache().clone();
+
+        let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(6, 6), ctx));
+
+        let settings = settings::channel(&font_cache).unwrap().1;
+        let (_, view) = app.add_window(|ctx| Editor::for_buffer(buffer.clone(), settings, ctx));
+
+        let layouts = view
+            .read(app)
+            .layout_line_numbers(1000.0, &font_cache, &layout_cache, app.as_ref())
+            .unwrap();
+        assert_eq!(layouts.len(), 6);
+    }
+
+    #[gpui::test]
+    fn test_fold(app: &mut gpui::MutableAppContext) {
+        let buffer = app.add_model(|ctx| {
+            Buffer::new(
+                0,
+                "
+                    impl Foo {
+                        // Hello!
+
+                        fn a() {
+                            1
+                        }
+
+                        fn b() {
+                            2
+                        }
+
+                        fn c() {
+                            3
+                        }
+                    }
+                "
+                .unindent(),
+                ctx,
+            )
+        });
+        let settings = settings::channel(&app.font_cache()).unwrap().1;
+        let (_, view) = app.add_window(|ctx| Editor::for_buffer(buffer.clone(), settings, ctx));
+
+        view.update(app, |view, ctx| {
+            view.select_display_ranges(&[DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)], ctx)
+                .unwrap();
+            view.fold(&(), ctx);
+            assert_eq!(
+                view.text(ctx.as_ref()),
+                "
+                    impl Foo {
+                        // Hello!
+
+                        fn a() {
+                            1
+                        }
+
+                        fn b() {…
+                        }
+
+                        fn c() {…
+                        }
+                    }
+                "
+                .unindent(),
+            );
+
+            view.fold(&(), ctx);
+            assert_eq!(
+                view.text(ctx.as_ref()),
+                "
+                    impl Foo {…
+                    }
+                "
+                .unindent(),
+            );
+
+            view.unfold(&(), ctx);
+            assert_eq!(
+                view.text(ctx.as_ref()),
+                "
+                    impl Foo {
+                        // Hello!
+
+                        fn a() {
+                            1
+                        }
+
+                        fn b() {…
+                        }
+
+                        fn c() {…
+                        }
+                    }
+                "
+                .unindent(),
+            );
+
+            view.unfold(&(), ctx);
+            assert_eq!(view.text(ctx.as_ref()), buffer.read(ctx).text());
+        });
+    }
+
+    #[gpui::test]
+    fn test_move_cursor(app: &mut gpui::MutableAppContext) {
+        let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(6, 6), ctx));
+        let settings = settings::channel(&app.font_cache()).unwrap().1;
+        let (_, view) = app.add_window(|ctx| Editor::for_buffer(buffer.clone(), settings, ctx));
+
+        buffer.update(app, |buffer, ctx| {
+            buffer
+                .edit(
+                    vec![
+                        Point::new(1, 0)..Point::new(1, 0),
+                        Point::new(1, 1)..Point::new(1, 1),
+                    ],
+                    "\t",
+                    Some(ctx),
+                )
+                .unwrap();
+        });
+
+        view.update(app, |view, ctx| {
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
+            );
+
+            view.move_down(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
+            );
+
+            view.move_right(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
+            );
+
+            view.move_left(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
+            );
+
+            view.move_up(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
+            );
+
+            view.move_to_end(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
+            );
+
+            view.move_to_beginning(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
+            );
+
+            view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)], ctx)
+                .unwrap();
+            view.select_to_beginning(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
+            );
+
+            view.select_to_end(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
+            );
+        });
+    }
+
+    #[gpui::test]
+    fn test_move_cursor_multibyte(app: &mut gpui::MutableAppContext) {
+        let buffer = app.add_model(|ctx| Buffer::new(0, "ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", ctx));
+        let settings = settings::channel(&app.font_cache()).unwrap().1;
+        let (_, view) = app.add_window(|ctx| Editor::for_buffer(buffer.clone(), settings, ctx));
+
+        assert_eq!('ⓐ'.len_utf8(), 3);
+        assert_eq!('α'.len_utf8(), 2);
+
+        view.update(app, |view, ctx| {
+            view.fold_ranges(
+                vec![
+                    Point::new(0, 6)..Point::new(0, 12),
+                    Point::new(1, 2)..Point::new(1, 4),
+                    Point::new(2, 4)..Point::new(2, 8),
+                ],
+                ctx,
+            );
+            assert_eq!(view.text(ctx.as_ref()), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\n");
+
+            view.move_right(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[empty_range(0, "ⓐ".len())]
+            );
+            view.move_right(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[empty_range(0, "ⓐⓑ".len())]
+            );
+            view.move_right(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[empty_range(0, "ⓐⓑ…".len())]
+            );
+
+            view.move_down(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[empty_range(1, "ab…".len())]
+            );
+            view.move_left(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[empty_range(1, "ab".len())]
+            );
+            view.move_left(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[empty_range(1, "a".len())]
+            );
+
+            view.move_down(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[empty_range(2, "α".len())]
+            );
+            view.move_right(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[empty_range(2, "αβ".len())]
+            );
+            view.move_right(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[empty_range(2, "αβ…".len())]
+            );
+            view.move_right(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[empty_range(2, "αβ…ε".len())]
+            );
+
+            view.move_up(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[empty_range(1, "ab…e".len())]
+            );
+            view.move_up(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[empty_range(0, "ⓐⓑ…ⓔ".len())]
+            );
+            view.move_left(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[empty_range(0, "ⓐⓑ…".len())]
+            );
+            view.move_left(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[empty_range(0, "ⓐⓑ".len())]
+            );
+            view.move_left(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[empty_range(0, "ⓐ".len())]
+            );
+        });
+    }
+
+    #[gpui::test]
+    fn test_move_cursor_different_line_lengths(app: &mut gpui::MutableAppContext) {
+        let buffer = app.add_model(|ctx| Buffer::new(0, "ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", ctx));
+        let settings = settings::channel(&app.font_cache()).unwrap().1;
+        let (_, view) = app.add_window(|ctx| Editor::for_buffer(buffer.clone(), settings, ctx));
+        view.update(app, |view, ctx| {
+            view.select_display_ranges(&[empty_range(0, "ⓐⓑⓒⓓⓔ".len())], ctx)
+                .unwrap();
+
+            view.move_down(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[empty_range(1, "abcd".len())]
+            );
+
+            view.move_down(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[empty_range(2, "αβγ".len())]
+            );
+
+            view.move_down(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[empty_range(3, "abcd".len())]
+            );
+
+            view.move_down(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
+            );
+
+            view.move_up(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[empty_range(3, "abcd".len())]
+            );
+
+            view.move_up(&(), ctx);
+            assert_eq!(
+                view.selection_ranges(ctx.as_ref()),
+                &[empty_range(2, "αβγ".len())]
+            );
+        });
+    }
+
+    #[gpui::test]
+    fn test_beginning_end_of_line(app: &mut gpui::MutableAppContext) {
+        let buffer = app.add_model(|ctx| Buffer::new(0, "abc\n  def", ctx));
+        let settings = settings::channel(&app.font_cache()).unwrap().1;
+        let (_, view) = app.add_window(|ctx| Editor::for_buffer(buffer, settings, ctx));
+        view.update(app, |view, ctx| {
+            view.select_display_ranges(
+                &[
+                    DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
+                    DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
+                ],
+                ctx,
+            )
+            .unwrap();
+        });
+
+        view.update(app, |view, ctx| view.move_to_beginning_of_line(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
+                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
+            ]
+        );
+
+        view.update(app, |view, ctx| view.move_to_beginning_of_line(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
+                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
+            ]
+        );
+
+        view.update(app, |view, ctx| view.move_to_beginning_of_line(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
+                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
+            ]
+        );
+
+        view.update(app, |view, ctx| view.move_to_end_of_line(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
+                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
+            ]
+        );
+
+        // Moving to the end of line again is a no-op.
+        view.update(app, |view, ctx| view.move_to_end_of_line(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
+                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
+            ]
+        );
+
+        view.update(app, |view, ctx| {
+            view.move_left(&(), ctx);
+            view.select_to_beginning_of_line(&true, ctx);
+        });
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
+                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
+            ]
+        );
+
+        view.update(app, |view, ctx| {
+            view.select_to_beginning_of_line(&true, ctx)
+        });
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
+                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
+            ]
+        );
+
+        view.update(app, |view, ctx| {
+            view.select_to_beginning_of_line(&true, ctx)
+        });
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
+                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
+            ]
+        );
+
+        view.update(app, |view, ctx| view.select_to_end_of_line(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
+                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
+            ]
+        );
+
+        view.update(app, |view, ctx| view.delete_to_end_of_line(&(), ctx));
+        assert_eq!(view.read(app).text(app.as_ref()), "ab\n  de");
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
+                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
+            ]
+        );
+
+        view.update(app, |view, ctx| view.delete_to_beginning_of_line(&(), ctx));
+        assert_eq!(view.read(app).text(app.as_ref()), "\n");
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
+                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
+            ]
+        );
+    }
+
+    #[gpui::test]
+    fn test_prev_next_word_boundary(app: &mut gpui::MutableAppContext) {
+        let buffer =
+            app.add_model(|ctx| Buffer::new(0, "use std::str::{foo, bar}\n\n  {baz.qux()}", ctx));
+        let settings = settings::channel(&app.font_cache()).unwrap().1;
+        let (_, view) = app.add_window(|ctx| Editor::for_buffer(buffer, settings, ctx));
+        view.update(app, |view, ctx| {
+            view.select_display_ranges(
+                &[
+                    DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
+                    DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
+                ],
+                ctx,
+            )
+            .unwrap();
+        });
+
+        view.update(app, |view, ctx| {
+            view.move_to_previous_word_boundary(&(), ctx)
+        });
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 9)..DisplayPoint::new(0, 9),
+                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 3),
+            ]
+        );
+
+        view.update(app, |view, ctx| {
+            view.move_to_previous_word_boundary(&(), ctx)
+        });
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 7)..DisplayPoint::new(0, 7),
+                DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2),
+            ]
+        );
+
+        view.update(app, |view, ctx| {
+            view.move_to_previous_word_boundary(&(), ctx)
+        });
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 4)..DisplayPoint::new(0, 4),
+                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
+            ]
+        );
+
+        view.update(app, |view, ctx| {
+            view.move_to_previous_word_boundary(&(), ctx)
+        });
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
+                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
+            ]
+        );
+
+        view.update(app, |view, ctx| {
+            view.move_to_previous_word_boundary(&(), ctx)
+        });
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
+                DisplayPoint::new(0, 24)..DisplayPoint::new(0, 24),
+            ]
+        );
+
+        view.update(app, |view, ctx| {
+            view.move_to_previous_word_boundary(&(), ctx)
+        });
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
+                DisplayPoint::new(0, 23)..DisplayPoint::new(0, 23),
+            ]
+        );
+
+        view.update(app, |view, ctx| view.move_to_next_word_boundary(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
+                DisplayPoint::new(0, 24)..DisplayPoint::new(0, 24),
+            ]
+        );
+
+        view.update(app, |view, ctx| view.move_to_next_word_boundary(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 4)..DisplayPoint::new(0, 4),
+                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
+            ]
+        );
+
+        view.update(app, |view, ctx| view.move_to_next_word_boundary(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 7)..DisplayPoint::new(0, 7),
+                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
+            ]
+        );
+
+        view.update(app, |view, ctx| view.move_to_next_word_boundary(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 9)..DisplayPoint::new(0, 9),
+                DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2),
+            ]
+        );
+
+        view.update(app, |view, ctx| {
+            view.move_right(&(), ctx);
+            view.select_to_previous_word_boundary(&(), ctx);
+        });
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 10)..DisplayPoint::new(0, 9),
+                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 2),
+            ]
+        );
+
+        view.update(app, |view, ctx| {
+            view.select_to_previous_word_boundary(&(), ctx)
+        });
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 10)..DisplayPoint::new(0, 7),
+                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 0),
+            ]
+        );
+
+        view.update(app, |view, ctx| view.select_to_next_word_boundary(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 10)..DisplayPoint::new(0, 9),
+                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 2),
+            ]
+        );
+
+        view.update(app, |view, ctx| view.delete_to_next_word_boundary(&(), ctx));
+        assert_eq!(
+            view.read(app).text(app.as_ref()),
+            "use std::s::{foo, bar}\n\n  {az.qux()}"
+        );
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 10)..DisplayPoint::new(0, 10),
+                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 3),
+            ]
+        );
+
+        view.update(app, |view, ctx| {
+            view.delete_to_previous_word_boundary(&(), ctx)
+        });
+        assert_eq!(
+            view.read(app).text(app.as_ref()),
+            "use std::::{foo, bar}\n\n  az.qux()}"
+        );
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 9)..DisplayPoint::new(0, 9),
+                DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2),
+            ]
+        );
+    }
+
+    #[gpui::test]
+    fn test_backspace(app: &mut gpui::MutableAppContext) {
+        let buffer = app.add_model(|ctx| {
+            Buffer::new(
+                0,
+                "one two three\nfour five six\nseven eight nine\nten\n",
+                ctx,
+            )
+        });
+        let settings = settings::channel(&app.font_cache()).unwrap().1;
+        let (_, view) = app.add_window(|ctx| Editor::for_buffer(buffer.clone(), settings, ctx));
+
+        view.update(app, |view, ctx| {
+            view.select_display_ranges(
+                &[
+                    // an empty selection - the preceding character is deleted
+                    DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
+                    // one character selected - it is deleted
+                    DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
+                    // a line suffix selected - it is deleted
+                    DisplayPoint::new(2, 6)..DisplayPoint::new(3, 0),
+                ],
+                ctx,
+            )
+            .unwrap();
+            view.backspace(&(), ctx);
+        });
+
+        assert_eq!(
+            buffer.read(app).text(),
+            "oe two three\nfou five six\nseven ten\n"
+        );
+    }
+
+    #[gpui::test]
+    fn test_delete(app: &mut gpui::MutableAppContext) {
+        let buffer = app.add_model(|ctx| {
+            Buffer::new(
+                0,
+                "one two three\nfour five six\nseven eight nine\nten\n",
+                ctx,
+            )
+        });
+        let settings = settings::channel(&app.font_cache()).unwrap().1;
+        let (_, view) = app.add_window(|ctx| Editor::for_buffer(buffer.clone(), settings, ctx));
+
+        view.update(app, |view, ctx| {
+            view.select_display_ranges(
+                &[
+                    // an empty selection - the following character is deleted
+                    DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
+                    // one character selected - it is deleted
+                    DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
+                    // a line suffix selected - it is deleted
+                    DisplayPoint::new(2, 6)..DisplayPoint::new(3, 0),
+                ],
+                ctx,
+            )
+            .unwrap();
+            view.delete(&(), ctx);
+        });
+
+        assert_eq!(
+            buffer.read(app).text(),
+            "on two three\nfou five six\nseven ten\n"
+        );
+    }
+
+    #[gpui::test]
+    fn test_delete_line(app: &mut gpui::MutableAppContext) {
+        let settings = settings::channel(&app.font_cache()).unwrap().1;
+        let buffer = app.add_model(|ctx| Buffer::new(0, "abc\ndef\nghi\n", ctx));
+        let (_, view) = app.add_window(|ctx| Editor::for_buffer(buffer, settings, ctx));
+        view.update(app, |view, ctx| {
+            view.select_display_ranges(
+                &[
+                    DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
+                    DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
+                    DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
+                ],
+                ctx,
+            )
+            .unwrap();
+            view.delete_line(&(), ctx);
+        });
+        assert_eq!(view.read(app).text(app.as_ref()), "ghi");
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            vec![
+                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
+                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
+            ]
+        );
+
+        let settings = settings::channel(&app.font_cache()).unwrap().1;
+        let buffer = app.add_model(|ctx| Buffer::new(0, "abc\ndef\nghi\n", ctx));
+        let (_, view) = app.add_window(|ctx| Editor::for_buffer(buffer, settings, ctx));
+        view.update(app, |view, ctx| {
+            view.select_display_ranges(&[DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)], ctx)
+                .unwrap();
+            view.delete_line(&(), ctx);
+        });
+        assert_eq!(view.read(app).text(app.as_ref()), "ghi\n");
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
+        );
+    }
+
+    #[gpui::test]
+    fn test_duplicate_line(app: &mut gpui::MutableAppContext) {
+        let settings = settings::channel(&app.font_cache()).unwrap().1;
+        let buffer = app.add_model(|ctx| Buffer::new(0, "abc\ndef\nghi\n", ctx));
+        let (_, view) = app.add_window(|ctx| Editor::for_buffer(buffer, settings, ctx));
+        view.update(app, |view, ctx| {
+            view.select_display_ranges(
+                &[
+                    DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
+                    DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
+                    DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
+                    DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
+                ],
+                ctx,
+            )
+            .unwrap();
+            view.duplicate_line(&(), ctx);
+        });
+        assert_eq!(
+            view.read(app).text(app.as_ref()),
+            "abc\nabc\ndef\ndef\nghi\n\n"
+        );
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            vec![
+                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
+                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
+                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
+                DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
+            ]
+        );
+
+        let settings = settings::channel(&app.font_cache()).unwrap().1;
+        let buffer = app.add_model(|ctx| Buffer::new(0, "abc\ndef\nghi\n", ctx));
+        let (_, view) = app.add_window(|ctx| Editor::for_buffer(buffer, settings, ctx));
+        view.update(app, |view, ctx| {
+            view.select_display_ranges(
+                &[
+                    DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
+                    DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
+                ],
+                ctx,
+            )
+            .unwrap();
+            view.duplicate_line(&(), ctx);
+        });
+        assert_eq!(
+            view.read(app).text(app.as_ref()),
+            "abc\ndef\nghi\nabc\ndef\nghi\n"
+        );
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            vec![
+                DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
+                DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
+            ]
+        );
+    }
+
+    #[gpui::test]
+    fn test_move_line_up_down(app: &mut gpui::MutableAppContext) {
+        let settings = settings::channel(&app.font_cache()).unwrap().1;
+        let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(10, 5), ctx));
+        let (_, view) = app.add_window(|ctx| Editor::for_buffer(buffer, settings, ctx));
+        view.update(app, |view, ctx| {
+            view.fold_ranges(
+                vec![
+                    Point::new(0, 2)..Point::new(1, 2),
+                    Point::new(2, 3)..Point::new(4, 1),
+                    Point::new(7, 0)..Point::new(8, 4),
+                ],
+                ctx,
+            );
+            view.select_display_ranges(
+                &[
+                    DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
+                    DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
+                    DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
+                    DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
+                ],
+                ctx,
+            )
+            .unwrap();
+        });
+        assert_eq!(
+            view.read(app).text(app.as_ref()),
+            "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
+        );
+
+        view.update(app, |view, ctx| view.move_line_up(&(), ctx));
+        assert_eq!(
+            view.read(app).text(app.as_ref()),
+            "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
+        );
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            vec![
+                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
+                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
+                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
+                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
+            ]
+        );
+
+        view.update(app, |view, ctx| view.move_line_down(&(), ctx));
+        assert_eq!(
+            view.read(app).text(app.as_ref()),
+            "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
+        );
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            vec![
+                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
+                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
+                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
+                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
+            ]
+        );
+
+        view.update(app, |view, ctx| view.move_line_down(&(), ctx));
+        assert_eq!(
+            view.read(app).text(app.as_ref()),
+            "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
+        );
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            vec![
+                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
+                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
+                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
+                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
+            ]
+        );
+
+        view.update(app, |view, ctx| view.move_line_up(&(), ctx));
+        assert_eq!(
+            view.read(app).text(app.as_ref()),
+            "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
+        );
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            vec![
+                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
+                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
+                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
+                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
+            ]
+        );
+    }
+
+    #[gpui::test]
+    fn test_clipboard(app: &mut gpui::MutableAppContext) {
+        let buffer = app.add_model(|ctx| Buffer::new(0, "one two three four five six ", ctx));
+        let settings = settings::channel(&app.font_cache()).unwrap().1;
+        let view = app
+            .add_window(|ctx| Editor::for_buffer(buffer.clone(), settings, ctx))
+            .1;
+
+        // Cut with three selections. Clipboard text is divided into three slices.
+        view.update(app, |view, ctx| {
+            view.select_ranges(vec![0..4, 8..14, 19..24], false, ctx);
+            view.cut(&(), ctx);
+        });
+        assert_eq!(view.read(app).text(app.as_ref()), "two four six ");
+
+        // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
+        view.update(app, |view, ctx| {
+            view.select_ranges(vec![4..4, 9..9, 13..13], false, ctx);
+            view.paste(&(), ctx);
+        });
+        assert_eq!(
+            view.read(app).text(app.as_ref()),
+            "two one four three six five "
+        );
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 8)..DisplayPoint::new(0, 8),
+                DisplayPoint::new(0, 19)..DisplayPoint::new(0, 19),
+                DisplayPoint::new(0, 28)..DisplayPoint::new(0, 28)
+            ]
+        );
+
+        // Paste again but with only two cursors. Since the number of cursors doesn't
+        // match the number of slices in the clipboard, the entire clipboard text
+        // is pasted at each cursor.
+        view.update(app, |view, ctx| {
+            view.select_ranges(vec![0..0, 28..28], false, ctx);
+            view.insert(&"( ".to_string(), ctx);
+            view.paste(&(), ctx);
+            view.insert(&") ".to_string(), ctx);
+        });
+        assert_eq!(
+            view.read(app).text(app.as_ref()),
+            "( one three five ) two one four three six five ( one three five ) "
+        );
+
+        view.update(app, |view, ctx| {
+            view.select_ranges(vec![0..0], false, ctx);
+            view.insert(&"123\n4567\n89\n".to_string(), ctx);
+        });
+        assert_eq!(
+            view.read(app).text(app.as_ref()),
+            "123\n4567\n89\n( one three five ) two one four three six five ( one three five ) "
+        );
+
+        // Cut with three selections, one of which is full-line.
+        view.update(app, |view, ctx| {
+            view.select_display_ranges(
+                &[
+                    DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2),
+                    DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
+                    DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
+                ],
+                ctx,
+            )
+            .unwrap();
+            view.cut(&(), ctx);
+        });
+        assert_eq!(
+            view.read(app).text(app.as_ref()),
+            "13\n9\n( one three five ) two one four three six five ( one three five ) "
+        );
+
+        // Paste with three selections, noticing how the copied selection that was full-line
+        // gets inserted before the second cursor.
+        view.update(app, |view, ctx| {
+            view.select_display_ranges(
+                &[
+                    DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
+                    DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
+                    DisplayPoint::new(2, 2)..DisplayPoint::new(2, 3),
+                ],
+                ctx,
+            )
+            .unwrap();
+            view.paste(&(), ctx);
+        });
+        assert_eq!(
+            view.read(app).text(app.as_ref()),
+            "123\n4567\n9\n( 8ne three five ) two one four three six five ( one three five ) "
+        );
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
+                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
+                DisplayPoint::new(3, 3)..DisplayPoint::new(3, 3),
+            ]
+        );
+
+        // Copy with a single cursor only, which writes the whole line into the clipboard.
+        view.update(app, |view, ctx| {
+            view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)], ctx)
+                .unwrap();
+            view.copy(&(), ctx);
+        });
+
+        // Paste with three selections, noticing how the copied full-line selection is inserted
+        // before the empty selections but replaces the selection that is non-empty.
+        view.update(app, |view, ctx| {
+            view.select_display_ranges(
+                &[
+                    DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
+                    DisplayPoint::new(1, 0)..DisplayPoint::new(1, 2),
+                    DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
+                ],
+                ctx,
+            )
+            .unwrap();
+            view.paste(&(), ctx);
+        });
+        assert_eq!(
+                view.read(app).text(app.as_ref()),
+                "123\n123\n123\n67\n123\n9\n( 8ne three five ) two one four three six five ( one three five ) "
+            );
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[
+                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
+                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
+                DisplayPoint::new(5, 1)..DisplayPoint::new(5, 1),
+            ]
+        );
+    }
+
+    #[gpui::test]
+    fn test_select_all(app: &mut gpui::MutableAppContext) {
+        let buffer = app.add_model(|ctx| Buffer::new(0, "abc\nde\nfgh", ctx));
+        let settings = settings::channel(&app.font_cache()).unwrap().1;
+        let (_, view) = app.add_window(|ctx| Editor::for_buffer(buffer, settings, ctx));
+        view.update(app, |b, ctx| b.select_all(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
+        );
+    }
+
+    #[gpui::test]
+    fn test_select_line(app: &mut gpui::MutableAppContext) {
+        let settings = settings::channel(&app.font_cache()).unwrap().1;
+        let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(6, 5), ctx));
+        let (_, view) = app.add_window(|ctx| Editor::for_buffer(buffer, settings, ctx));
+        view.update(app, |view, ctx| {
+            view.select_display_ranges(
+                &[
+                    DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
+                    DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
+                    DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
+                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
+                ],
+                ctx,
+            )
+            .unwrap();
+            view.select_line(&(), ctx);
+        });
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            vec![
+                DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
+                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
+            ]
+        );
+
+        view.update(app, |view, ctx| view.select_line(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            vec![
+                DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
+                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
+            ]
+        );
+
+        view.update(app, |view, ctx| view.select_line(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
+        );
+    }
+
+    #[gpui::test]
+    fn test_split_selection_into_lines(app: &mut gpui::MutableAppContext) {
+        let settings = settings::channel(&app.font_cache()).unwrap().1;
+        let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(9, 5), ctx));
+        let (_, view) = app.add_window(|ctx| Editor::for_buffer(buffer, settings, ctx));
+        view.update(app, |view, ctx| {
+            view.fold_ranges(
+                vec![
+                    Point::new(0, 2)..Point::new(1, 2),
+                    Point::new(2, 3)..Point::new(4, 1),
+                    Point::new(7, 0)..Point::new(8, 4),
+                ],
+                ctx,
+            );
+            view.select_display_ranges(
+                &[
+                    DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
+                    DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
+                    DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
+                    DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
+                ],
+                ctx,
+            )
+            .unwrap();
+        });
+        assert_eq!(
+            view.read(app).text(app.as_ref()),
+            "aa…bbb\nccc…eeee\nfffff\nggggg\n…i"
+        );
+
+        view.update(app, |view, ctx| view.split_selection_into_lines(&(), ctx));
+        assert_eq!(
+            view.read(app).text(app.as_ref()),
+            "aa…bbb\nccc…eeee\nfffff\nggggg\n…i"
+        );
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            [
+                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
+                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
+                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
+                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4)
+            ]
+        );
+
+        view.update(app, |view, ctx| {
+            view.select_display_ranges(&[DisplayPoint::new(4, 0)..DisplayPoint::new(0, 1)], ctx)
+                .unwrap();
+            view.split_selection_into_lines(&(), ctx);
+        });
+        assert_eq!(
+            view.read(app).text(app.as_ref()),
+            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\n…i"
+        );
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            [
+                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
+                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
+                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
+                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
+                DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
+                DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
+                DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
+                DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
+            ]
+        );
+    }
+
+    #[gpui::test]
+    fn test_add_selection_above_below(app: &mut gpui::MutableAppContext) {
+        let settings = settings::channel(&app.font_cache()).unwrap().1;
+        let buffer = app.add_model(|ctx| Buffer::new(0, "abc\ndefghi\n\njk\nlmno\n", ctx));
+        let (_, view) = app.add_window(|ctx| Editor::for_buffer(buffer, settings, ctx));
+
+        view.update(app, |view, ctx| {
+            view.select_display_ranges(&[DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)], ctx)
+                .unwrap();
+        });
+        view.update(app, |view, ctx| view.add_selection_above(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            vec![
+                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
+                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
+            ]
+        );
+
+        view.update(app, |view, ctx| view.add_selection_above(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            vec![
+                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
+                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
+            ]
+        );
+
+        view.update(app, |view, ctx| view.add_selection_below(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
+        );
+
+        view.update(app, |view, ctx| view.add_selection_below(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            vec![
+                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
+                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
+            ]
+        );
+
+        view.update(app, |view, ctx| view.add_selection_below(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            vec![
+                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
+                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
+            ]
+        );
+
+        view.update(app, |view, ctx| {
+            view.select_display_ranges(&[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)], ctx)
+                .unwrap();
+        });
+        view.update(app, |view, ctx| view.add_selection_below(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            vec![
+                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
+                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
+            ]
+        );
+
+        view.update(app, |view, ctx| view.add_selection_below(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            vec![
+                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
+                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
+            ]
+        );
+
+        view.update(app, |view, ctx| view.add_selection_above(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
+        );
+
+        view.update(app, |view, ctx| view.add_selection_above(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
+        );
+
+        view.update(app, |view, ctx| {
+            view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)], ctx)
+                .unwrap();
+        });
+        view.update(app, |view, ctx| view.add_selection_below(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            vec![
+                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
+                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
+                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
+            ]
+        );
+
+        view.update(app, |view, ctx| view.add_selection_below(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            vec![
+                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
+                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
+                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
+                DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
+            ]
+        );
+
+        view.update(app, |view, ctx| view.add_selection_above(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            vec![
+                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
+                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
+                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
+            ]
+        );
+
+        view.update(app, |view, ctx| {
+            view.select_display_ranges(&[DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)], ctx)
+                .unwrap();
+        });
+        view.update(app, |view, ctx| view.add_selection_above(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            vec![
+                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
+                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
+                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
+                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
+            ]
+        );
+
+        view.update(app, |view, ctx| view.add_selection_below(&(), ctx));
+        assert_eq!(
+            view.read(app).selection_ranges(app.as_ref()),
+            vec![
+                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
+                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
+                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
+            ]
+        );
+    }
+
+    #[gpui::test]
+    async fn test_select_larger_smaller_syntax_node(mut app: gpui::TestAppContext) {
+        let app_state = app.read(build_app_state);
+        let lang = app_state.language_registry.select_language("z.rs");
+        let text = r#"
+            use mod1::mod2::{mod3, mod4};
+
+            fn fn_1(param1: bool, param2: &str) {
+                let var1 = "text";
+            }
+        "#
+        .unindent();
+        let buffer = app.add_model(|ctx| {
+            let history = History::new(text.into());
+            Buffer::from_history(0, history, None, lang.cloned(), ctx)
+        });
+        let (_, view) = app.add_window(|ctx| Editor::for_buffer(buffer, app_state.settings, ctx));
+        view.condition(&app, |view, ctx| !view.buffer.read(ctx).is_parsing())
+            .await;
+
+        view.update(&mut app, |view, ctx| {
+            view.select_display_ranges(
+                &[
+                    DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
+                    DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
+                    DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
+                ],
+                ctx,
+            )
+            .unwrap();
+            view.select_larger_syntax_node(&(), ctx);
+        });
+        assert_eq!(
+            view.read_with(&app, |view, ctx| view.selection_ranges(ctx)),
+            &[
+                DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
+                DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
+                DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
+            ]
+        );
+
+        view.update(&mut app, |view, ctx| {
+            view.select_larger_syntax_node(&(), ctx);
+        });
+        assert_eq!(
+            view.read_with(&app, |view, ctx| view.selection_ranges(ctx)),
+            &[
+                DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
+                DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
+            ]
+        );
+
+        view.update(&mut app, |view, ctx| {
+            view.select_larger_syntax_node(&(), ctx);
+        });
+        assert_eq!(
+            view.read_with(&app, |view, ctx| view.selection_ranges(ctx)),
+            &[DisplayPoint::new(0, 0)..DisplayPoint::new(5, 0)]
+        );
+
+        // Trying to expand the selected syntax node one more time has no effect.
+        view.update(&mut app, |view, ctx| {
+            view.select_larger_syntax_node(&(), ctx);
+        });
+        assert_eq!(
+            view.read_with(&app, |view, ctx| view.selection_ranges(ctx)),
+            &[DisplayPoint::new(0, 0)..DisplayPoint::new(5, 0)]
+        );
+
+        view.update(&mut app, |view, ctx| {
+            view.select_smaller_syntax_node(&(), ctx);
+        });
+        assert_eq!(
+            view.read_with(&app, |view, ctx| view.selection_ranges(ctx)),
+            &[
+                DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
+                DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
+            ]
+        );
+
+        view.update(&mut app, |view, ctx| {
+            view.select_smaller_syntax_node(&(), ctx);
+        });
+        assert_eq!(
+            view.read_with(&app, |view, ctx| view.selection_ranges(ctx)),
+            &[
+                DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
+                DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
+                DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
+            ]
+        );
+
+        view.update(&mut app, |view, ctx| {
+            view.select_smaller_syntax_node(&(), ctx);
+        });
+        assert_eq!(
+            view.read_with(&app, |view, ctx| view.selection_ranges(ctx)),
+            &[
+                DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
+                DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
+                DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
+            ]
+        );
+
+        // Trying to shrink the selected syntax node one more time has no effect.
+        view.update(&mut app, |view, ctx| {
+            view.select_smaller_syntax_node(&(), ctx);
+        });
+        assert_eq!(
+            view.read_with(&app, |view, ctx| view.selection_ranges(ctx)),
+            &[
+                DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
+                DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
+                DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
+            ]
+        );
+
+        // Ensure that we keep expanding the selection if the larger selection starts or ends within
+        // a fold.
+        view.update(&mut app, |view, ctx| {
+            view.fold_ranges(
+                vec![
+                    Point::new(0, 21)..Point::new(0, 24),
+                    Point::new(3, 20)..Point::new(3, 22),
+                ],
+                ctx,
+            );
+            view.select_larger_syntax_node(&(), ctx);
+        });
+        assert_eq!(
+            view.read_with(&app, |view, ctx| view.selection_ranges(ctx)),
+            &[
+                DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
+                DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
+                DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
+            ]
+        );
+    }
+
+    impl Editor {
+        fn selection_ranges(&self, app: &AppContext) -> Vec<Range<DisplayPoint>> {
+            self.selections_in_range(DisplayPoint::zero()..self.max_point(app), app)
+                .collect::<Vec<_>>()
+        }
+    }
+
+    fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
+        let point = DisplayPoint::new(row as u32, column as u32);
+        point..point
+    }
+}
+
 #[derive(Copy, Clone)]
 pub enum Bias {
     Left,

zed/src/editor/buffer_view.rs 🔗

@@ -1,4118 +0,0 @@
-use super::{
-    buffer, movement, Anchor, Bias, Buffer, BufferElement, DisplayMap, DisplayPoint, Point,
-    Selection, SelectionGoal, SelectionSetId, ToOffset, ToPoint,
-};
-use crate::{
-    settings::{Settings, StyleId},
-    util::post_inc,
-    workspace,
-    worktree::FileHandle,
-};
-use anyhow::Result;
-use gpui::{
-    color::ColorU, fonts::Properties as FontProperties, geometry::vector::Vector2F,
-    keymap::Binding, text_layout, AppContext, ClipboardItem, Element, ElementBox, Entity,
-    FontCache, ModelHandle, MutableAppContext, Task, TextLayoutCache, View, ViewContext,
-    WeakViewHandle,
-};
-use parking_lot::Mutex;
-use postage::watch;
-use serde::{Deserialize, Serialize};
-use smallvec::SmallVec;
-use smol::Timer;
-use std::{
-    cmp::{self, Ordering},
-    fmt::Write,
-    iter::FromIterator,
-    mem,
-    ops::Range,
-    path::Path,
-    sync::Arc,
-    time::Duration,
-};
-
-const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
-
-pub fn init(app: &mut MutableAppContext) {
-    app.add_bindings(vec![
-        Binding::new("escape", "buffer:cancel", Some("BufferView")),
-        Binding::new("backspace", "buffer:backspace", Some("BufferView")),
-        Binding::new("ctrl-h", "buffer:backspace", Some("BufferView")),
-        Binding::new("delete", "buffer:delete", Some("BufferView")),
-        Binding::new("ctrl-d", "buffer:delete", Some("BufferView")),
-        Binding::new("enter", "buffer:newline", Some("BufferView")),
-        Binding::new("tab", "buffer:insert", Some("BufferView")).with_arg("\t".to_string()),
-        Binding::new("ctrl-shift-K", "buffer:delete_line", Some("BufferView")),
-        Binding::new(
-            "alt-backspace",
-            "buffer:delete_to_previous_word_boundary",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "alt-delete",
-            "buffer:delete_to_next_word_boundary",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "cmd-backspace",
-            "buffer:delete_to_beginning_of_line",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "cmd-delete",
-            "buffer:delete_to_end_of_line",
-            Some("BufferView"),
-        ),
-        Binding::new("cmd-shift-D", "buffer:duplicate_line", Some("BufferView")),
-        Binding::new("ctrl-cmd-up", "buffer:move_line_up", Some("BufferView")),
-        Binding::new("ctrl-cmd-down", "buffer:move_line_down", Some("BufferView")),
-        Binding::new("cmd-x", "buffer:cut", Some("BufferView")),
-        Binding::new("cmd-c", "buffer:copy", Some("BufferView")),
-        Binding::new("cmd-v", "buffer:paste", Some("BufferView")),
-        Binding::new("cmd-z", "buffer:undo", Some("BufferView")),
-        Binding::new("cmd-shift-Z", "buffer:redo", Some("BufferView")),
-        Binding::new("up", "buffer:move_up", Some("BufferView")),
-        Binding::new("down", "buffer:move_down", Some("BufferView")),
-        Binding::new("left", "buffer:move_left", Some("BufferView")),
-        Binding::new("right", "buffer:move_right", Some("BufferView")),
-        Binding::new("ctrl-p", "buffer:move_up", Some("BufferView")),
-        Binding::new("ctrl-n", "buffer:move_down", Some("BufferView")),
-        Binding::new("ctrl-b", "buffer:move_left", Some("BufferView")),
-        Binding::new("ctrl-f", "buffer:move_right", Some("BufferView")),
-        Binding::new(
-            "alt-left",
-            "buffer:move_to_previous_word_boundary",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "alt-right",
-            "buffer:move_to_next_word_boundary",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "cmd-left",
-            "buffer:move_to_beginning_of_line",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "ctrl-a",
-            "buffer:move_to_beginning_of_line",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "cmd-right",
-            "buffer:move_to_end_of_line",
-            Some("BufferView"),
-        ),
-        Binding::new("ctrl-e", "buffer:move_to_end_of_line", Some("BufferView")),
-        Binding::new("cmd-up", "buffer:move_to_beginning", Some("BufferView")),
-        Binding::new("cmd-down", "buffer:move_to_end", Some("BufferView")),
-        Binding::new("shift-up", "buffer:select_up", Some("BufferView")),
-        Binding::new("shift-down", "buffer:select_down", Some("BufferView")),
-        Binding::new("shift-left", "buffer:select_left", Some("BufferView")),
-        Binding::new("shift-right", "buffer:select_right", Some("BufferView")),
-        Binding::new(
-            "alt-shift-left",
-            "buffer:select_to_previous_word_boundary",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "alt-shift-right",
-            "buffer:select_to_next_word_boundary",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "cmd-shift-left",
-            "buffer:select_to_beginning_of_line",
-            Some("BufferView"),
-        )
-        .with_arg(true),
-        Binding::new(
-            "ctrl-shift-A",
-            "buffer:select_to_beginning_of_line",
-            Some("BufferView"),
-        )
-        .with_arg(true),
-        Binding::new(
-            "cmd-shift-right",
-            "buffer:select_to_end_of_line",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "ctrl-shift-E",
-            "buffer:select_to_end_of_line",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "cmd-shift-up",
-            "buffer:select_to_beginning",
-            Some("BufferView"),
-        ),
-        Binding::new("cmd-shift-down", "buffer:select_to_end", Some("BufferView")),
-        Binding::new("cmd-a", "buffer:select_all", Some("BufferView")),
-        Binding::new("cmd-l", "buffer:select_line", Some("BufferView")),
-        Binding::new(
-            "cmd-shift-L",
-            "buffer:split_selection_into_lines",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "cmd-alt-up",
-            "buffer:add_selection_above",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "cmd-alt-down",
-            "buffer:add_selection_below",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "alt-up",
-            "buffer:select_larger_syntax_node",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "alt-down",
-            "buffer:select_smaller_syntax_node",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "ctrl-m",
-            "buffer:move_to_enclosing_bracket",
-            Some("BufferView"),
-        ),
-        Binding::new("pageup", "buffer:page_up", Some("BufferView")),
-        Binding::new("pagedown", "buffer:page_down", Some("BufferView")),
-        Binding::new("alt-cmd-[", "buffer:fold", Some("BufferView")),
-        Binding::new("alt-cmd-]", "buffer:unfold", Some("BufferView")),
-        Binding::new(
-            "alt-cmd-f",
-            "buffer:fold_selected_ranges",
-            Some("BufferView"),
-        ),
-    ]);
-
-    app.add_action("buffer:scroll", BufferView::scroll);
-    app.add_action("buffer:select", BufferView::select);
-    app.add_action("buffer:cancel", BufferView::cancel);
-    app.add_action("buffer:insert", BufferView::insert);
-    app.add_action("buffer:newline", BufferView::newline);
-    app.add_action("buffer:backspace", BufferView::backspace);
-    app.add_action("buffer:delete", BufferView::delete);
-    app.add_action("buffer:delete_line", BufferView::delete_line);
-    app.add_action(
-        "buffer:delete_to_previous_word_boundary",
-        BufferView::delete_to_previous_word_boundary,
-    );
-    app.add_action(
-        "buffer:delete_to_next_word_boundary",
-        BufferView::delete_to_next_word_boundary,
-    );
-    app.add_action(
-        "buffer:delete_to_beginning_of_line",
-        BufferView::delete_to_beginning_of_line,
-    );
-    app.add_action(
-        "buffer:delete_to_end_of_line",
-        BufferView::delete_to_end_of_line,
-    );
-    app.add_action("buffer:duplicate_line", BufferView::duplicate_line);
-    app.add_action("buffer:move_line_up", BufferView::move_line_up);
-    app.add_action("buffer:move_line_down", BufferView::move_line_down);
-    app.add_action("buffer:cut", BufferView::cut);
-    app.add_action("buffer:copy", BufferView::copy);
-    app.add_action("buffer:paste", BufferView::paste);
-    app.add_action("buffer:undo", BufferView::undo);
-    app.add_action("buffer:redo", BufferView::redo);
-    app.add_action("buffer:move_up", BufferView::move_up);
-    app.add_action("buffer:move_down", BufferView::move_down);
-    app.add_action("buffer:move_left", BufferView::move_left);
-    app.add_action("buffer:move_right", BufferView::move_right);
-    app.add_action(
-        "buffer:move_to_previous_word_boundary",
-        BufferView::move_to_previous_word_boundary,
-    );
-    app.add_action(
-        "buffer:move_to_next_word_boundary",
-        BufferView::move_to_next_word_boundary,
-    );
-    app.add_action(
-        "buffer:move_to_beginning_of_line",
-        BufferView::move_to_beginning_of_line,
-    );
-    app.add_action(
-        "buffer:move_to_end_of_line",
-        BufferView::move_to_end_of_line,
-    );
-    app.add_action("buffer:move_to_beginning", BufferView::move_to_beginning);
-    app.add_action("buffer:move_to_end", BufferView::move_to_end);
-    app.add_action("buffer:select_up", BufferView::select_up);
-    app.add_action("buffer:select_down", BufferView::select_down);
-    app.add_action("buffer:select_left", BufferView::select_left);
-    app.add_action("buffer:select_right", BufferView::select_right);
-    app.add_action(
-        "buffer:select_to_previous_word_boundary",
-        BufferView::select_to_previous_word_boundary,
-    );
-    app.add_action(
-        "buffer:select_to_next_word_boundary",
-        BufferView::select_to_next_word_boundary,
-    );
-    app.add_action(
-        "buffer:select_to_beginning_of_line",
-        BufferView::select_to_beginning_of_line,
-    );
-    app.add_action(
-        "buffer:select_to_end_of_line",
-        BufferView::select_to_end_of_line,
-    );
-    app.add_action(
-        "buffer:select_to_beginning",
-        BufferView::select_to_beginning,
-    );
-    app.add_action("buffer:select_to_end", BufferView::select_to_end);
-    app.add_action("buffer:select_all", BufferView::select_all);
-    app.add_action("buffer:select_line", BufferView::select_line);
-    app.add_action(
-        "buffer:split_selection_into_lines",
-        BufferView::split_selection_into_lines,
-    );
-    app.add_action(
-        "buffer:add_selection_above",
-        BufferView::add_selection_above,
-    );
-    app.add_action(
-        "buffer:add_selection_below",
-        BufferView::add_selection_below,
-    );
-    app.add_action(
-        "buffer:select_larger_syntax_node",
-        BufferView::select_larger_syntax_node,
-    );
-    app.add_action(
-        "buffer:select_smaller_syntax_node",
-        BufferView::select_smaller_syntax_node,
-    );
-    app.add_action(
-        "buffer:move_to_enclosing_bracket",
-        BufferView::move_to_enclosing_bracket,
-    );
-    app.add_action("buffer:page_up", BufferView::page_up);
-    app.add_action("buffer:page_down", BufferView::page_down);
-    app.add_action("buffer:fold", BufferView::fold);
-    app.add_action("buffer:unfold", BufferView::unfold);
-    app.add_action(
-        "buffer:fold_selected_ranges",
-        BufferView::fold_selected_ranges,
-    );
-}
-
-pub enum SelectAction {
-    Begin {
-        position: DisplayPoint,
-        add: bool,
-    },
-    Update {
-        position: DisplayPoint,
-        scroll_position: Vector2F,
-    },
-    End,
-}
-
-pub struct BufferView {
-    handle: WeakViewHandle<Self>,
-    buffer: ModelHandle<Buffer>,
-    display_map: DisplayMap,
-    selection_set_id: SelectionSetId,
-    pending_selection: Option<Selection>,
-    next_selection_id: usize,
-    add_selections_state: Option<AddSelectionsState>,
-    select_larger_syntax_node_stack: Vec<Vec<Selection>>,
-    scroll_position: Mutex<Vector2F>,
-    autoscroll_requested: Mutex<bool>,
-    settings: watch::Receiver<Settings>,
-    focused: bool,
-    cursors_visible: bool,
-    blink_epoch: usize,
-    blinking_paused: bool,
-    single_line: bool,
-}
-
-struct AddSelectionsState {
-    above: bool,
-    stack: Vec<usize>,
-}
-
-#[derive(Serialize, Deserialize)]
-struct ClipboardSelection {
-    len: usize,
-    is_entire_line: bool,
-}
-
-impl BufferView {
-    pub fn single_line(settings: watch::Receiver<Settings>, ctx: &mut ViewContext<Self>) -> Self {
-        let buffer = ctx.add_model(|ctx| Buffer::new(0, String::new(), ctx));
-        let mut view = Self::for_buffer(buffer, settings, ctx);
-        view.single_line = true;
-        view
-    }
-
-    pub fn for_buffer(
-        buffer: ModelHandle<Buffer>,
-        settings: watch::Receiver<Settings>,
-        ctx: &mut ViewContext<Self>,
-    ) -> Self {
-        ctx.observe_model(&buffer, Self::on_buffer_changed);
-        ctx.subscribe_to_model(&buffer, Self::on_buffer_event);
-        let display_map = DisplayMap::new(buffer.clone(), settings.borrow().tab_size, ctx.as_ref());
-
-        let mut next_selection_id = 0;
-        let (selection_set_id, _) = buffer.update(ctx, |buffer, ctx| {
-            buffer.add_selection_set(
-                vec![Selection {
-                    id: post_inc(&mut next_selection_id),
-                    start: buffer.anchor_before(0),
-                    end: buffer.anchor_before(0),
-                    reversed: false,
-                    goal: SelectionGoal::None,
-                }],
-                Some(ctx),
-            )
-        });
-        Self {
-            handle: ctx.handle().downgrade(),
-            buffer,
-            display_map,
-            selection_set_id,
-            pending_selection: None,
-            next_selection_id,
-            add_selections_state: None,
-            select_larger_syntax_node_stack: Vec::new(),
-            scroll_position: Mutex::new(Vector2F::zero()),
-            autoscroll_requested: Mutex::new(false),
-            settings,
-            focused: false,
-            cursors_visible: false,
-            blink_epoch: 0,
-            blinking_paused: false,
-            single_line: false,
-        }
-    }
-
-    pub fn buffer(&self) -> &ModelHandle<Buffer> {
-        &self.buffer
-    }
-
-    pub fn is_gutter_visible(&self) -> bool {
-        !self.single_line
-    }
-
-    fn scroll(&mut self, scroll_position: &Vector2F, ctx: &mut ViewContext<Self>) {
-        *self.scroll_position.lock() = *scroll_position;
-        ctx.notify();
-    }
-
-    pub fn scroll_position(&self) -> Vector2F {
-        *self.scroll_position.lock()
-    }
-
-    pub fn clamp_scroll_left(&self, max: f32) {
-        let mut scroll_position = self.scroll_position.lock();
-        let scroll_left = scroll_position.x();
-        scroll_position.set_x(scroll_left.min(max));
-    }
-
-    pub fn autoscroll_vertically(
-        &self,
-        viewport_height: f32,
-        line_height: f32,
-        app: &AppContext,
-    ) -> bool {
-        let mut scroll_position = self.scroll_position.lock();
-        let scroll_top = scroll_position.y();
-        scroll_position.set_y(scroll_top.min(self.max_point(app).row().saturating_sub(1) as f32));
-
-        let mut autoscroll_requested = self.autoscroll_requested.lock();
-        if *autoscroll_requested {
-            *autoscroll_requested = false;
-        } else {
-            return false;
-        }
-
-        let visible_lines = viewport_height / line_height;
-        let first_cursor_top = self
-            .selections(app)
-            .first()
-            .unwrap()
-            .head()
-            .to_display_point(&self.display_map, app)
-            .row() as f32;
-        let last_cursor_bottom = self
-            .selections(app)
-            .last()
-            .unwrap()
-            .head()
-            .to_display_point(&self.display_map, app)
-            .row() as f32
-            + 1.0;
-
-        let margin = ((visible_lines - (last_cursor_bottom - first_cursor_top)) / 2.0)
-            .floor()
-            .min(3.0);
-        if margin < 0.0 {
-            return false;
-        }
-
-        let target_top = (first_cursor_top - margin).max(0.0);
-        let target_bottom = last_cursor_bottom + margin;
-        let start_row = scroll_position.y();
-        let end_row = start_row + visible_lines;
-
-        if target_top < start_row {
-            scroll_position.set_y(target_top);
-        } else if target_bottom >= end_row {
-            scroll_position.set_y(target_bottom - visible_lines);
-        }
-
-        true
-    }
-
-    pub fn autoscroll_horizontally(
-        &self,
-        start_row: u32,
-        viewport_width: f32,
-        scroll_width: f32,
-        max_glyph_width: f32,
-        layouts: &[text_layout::Line],
-        ctx: &AppContext,
-    ) {
-        let mut target_left = std::f32::INFINITY;
-        let mut target_right = 0.0_f32;
-        for selection in self.selections(ctx) {
-            let head = selection.head().to_display_point(&self.display_map, ctx);
-            let start_column = head.column().saturating_sub(3);
-            let end_column = cmp::min(
-                self.display_map.line_len(head.row(), ctx),
-                head.column() + 3,
-            );
-            target_left = target_left
-                .min(layouts[(head.row() - start_row) as usize].x_for_index(start_column as usize));
-            target_right = target_right.max(
-                layouts[(head.row() - start_row) as usize].x_for_index(end_column as usize)
-                    + max_glyph_width,
-            );
-        }
-        target_right = target_right.min(scroll_width);
-
-        if target_right - target_left > viewport_width {
-            return;
-        }
-
-        let mut scroll_position = self.scroll_position.lock();
-        let scroll_left = scroll_position.x() * max_glyph_width;
-        let scroll_right = scroll_left + viewport_width;
-
-        if target_left < scroll_left {
-            scroll_position.set_x(target_left / max_glyph_width);
-        } else if target_right > scroll_right {
-            scroll_position.set_x((target_right - viewport_width) / max_glyph_width);
-        }
-    }
-
-    fn select(&mut self, arg: &SelectAction, ctx: &mut ViewContext<Self>) {
-        match arg {
-            SelectAction::Begin { position, add } => self.begin_selection(*position, *add, ctx),
-            SelectAction::Update {
-                position,
-                scroll_position,
-            } => self.update_selection(*position, *scroll_position, ctx),
-            SelectAction::End => self.end_selection(ctx),
-        }
-    }
-
-    fn begin_selection(&mut self, position: DisplayPoint, add: bool, ctx: &mut ViewContext<Self>) {
-        if !self.focused {
-            ctx.focus_self();
-            ctx.emit(Event::Activate);
-        }
-
-        let cursor = self
-            .display_map
-            .anchor_before(position, Bias::Left, ctx.as_ref());
-        let selection = Selection {
-            id: post_inc(&mut self.next_selection_id),
-            start: cursor.clone(),
-            end: cursor,
-            reversed: false,
-            goal: SelectionGoal::None,
-        };
-
-        if !add {
-            self.update_selections(Vec::new(), false, ctx);
-        }
-        self.pending_selection = Some(selection);
-
-        ctx.notify();
-    }
-
-    fn update_selection(
-        &mut self,
-        position: DisplayPoint,
-        scroll_position: Vector2F,
-        ctx: &mut ViewContext<Self>,
-    ) {
-        let buffer = self.buffer.read(ctx);
-        let cursor = self
-            .display_map
-            .anchor_before(position, Bias::Left, ctx.as_ref());
-        if let Some(selection) = self.pending_selection.as_mut() {
-            selection.set_head(buffer, cursor);
-        } else {
-            log::error!("update_selection dispatched with no pending selection");
-            return;
-        }
-
-        *self.scroll_position.lock() = scroll_position;
-
-        ctx.notify();
-    }
-
-    fn end_selection(&mut self, ctx: &mut ViewContext<Self>) {
-        if let Some(selection) = self.pending_selection.take() {
-            let ix = self.selection_insertion_index(&selection.start, ctx.as_ref());
-            let mut selections = self.selections(ctx.as_ref()).to_vec();
-            selections.insert(ix, selection);
-            self.update_selections(selections, false, ctx);
-        } else {
-            log::error!("end_selection dispatched with no pending selection");
-        }
-    }
-
-    pub fn is_selecting(&self) -> bool {
-        self.pending_selection.is_some()
-    }
-
-    pub fn cancel(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        let selections = self.selections(ctx.as_ref());
-        if let Some(pending_selection) = self.pending_selection.take() {
-            if selections.is_empty() {
-                self.update_selections(vec![pending_selection], true, ctx);
-            }
-        } else {
-            let mut oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
-            if selections.len() == 1 {
-                oldest_selection.start = oldest_selection.head().clone();
-                oldest_selection.end = oldest_selection.head().clone();
-            }
-            self.update_selections(vec![oldest_selection], true, ctx);
-        }
-    }
-
-    fn select_ranges<I, T>(&mut self, ranges: I, autoscroll: bool, ctx: &mut ViewContext<Self>)
-    where
-        I: IntoIterator<Item = Range<T>>,
-        T: ToOffset,
-    {
-        let buffer = self.buffer.read(ctx);
-        let mut selections = Vec::new();
-        for range in ranges {
-            let mut start = range.start.to_offset(buffer);
-            let mut end = range.end.to_offset(buffer);
-            let reversed = if start > end {
-                mem::swap(&mut start, &mut end);
-                true
-            } else {
-                false
-            };
-            selections.push(Selection {
-                id: post_inc(&mut self.next_selection_id),
-                start: buffer.anchor_before(start),
-                end: buffer.anchor_before(end),
-                reversed,
-                goal: SelectionGoal::None,
-            });
-        }
-        self.update_selections(selections, autoscroll, ctx);
-    }
-
-    #[cfg(test)]
-    fn select_display_ranges<'a, T>(&mut self, ranges: T, ctx: &mut ViewContext<Self>) -> Result<()>
-    where
-        T: IntoIterator<Item = &'a Range<DisplayPoint>>,
-    {
-        let mut selections = Vec::new();
-        for range in ranges {
-            let mut start = range.start;
-            let mut end = range.end;
-            let reversed = if start > end {
-                mem::swap(&mut start, &mut end);
-                true
-            } else {
-                false
-            };
-
-            selections.push(Selection {
-                id: post_inc(&mut self.next_selection_id),
-                start: self
-                    .display_map
-                    .anchor_before(start, Bias::Left, ctx.as_ref()),
-                end: self
-                    .display_map
-                    .anchor_before(end, Bias::Left, ctx.as_ref()),
-                reversed,
-                goal: SelectionGoal::None,
-            });
-        }
-        self.update_selections(selections, false, ctx);
-        Ok(())
-    }
-
-    pub fn insert(&mut self, text: &String, ctx: &mut ViewContext<Self>) {
-        let mut old_selections = SmallVec::<[_; 32]>::new();
-        {
-            let buffer = self.buffer.read(ctx);
-            for selection in self.selections(ctx.as_ref()) {
-                let start = selection.start.to_offset(buffer);
-                let end = selection.end.to_offset(buffer);
-                old_selections.push((selection.id, start..end));
-            }
-        }
-
-        self.start_transaction(ctx);
-        let mut new_selections = Vec::new();
-        self.buffer.update(ctx, |buffer, ctx| {
-            let edit_ranges = old_selections.iter().map(|(_, range)| range.clone());
-            if let Err(error) = buffer.edit(edit_ranges, text.as_str(), Some(ctx)) {
-                log::error!("error inserting text: {}", error);
-            };
-            let text_len = text.len() as isize;
-            let mut delta = 0_isize;
-            new_selections = old_selections
-                .into_iter()
-                .map(|(id, range)| {
-                    let start = range.start as isize;
-                    let end = range.end as isize;
-                    let anchor = buffer.anchor_before((start + delta + text_len) as usize);
-                    let deleted_count = end - start;
-                    delta += text_len - deleted_count;
-                    Selection {
-                        id,
-                        start: anchor.clone(),
-                        end: anchor,
-                        reversed: false,
-                        goal: SelectionGoal::None,
-                    }
-                })
-                .collect();
-        });
-
-        self.update_selections(new_selections, true, ctx);
-        self.end_transaction(ctx);
-    }
-
-    fn newline(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        if self.single_line {
-            ctx.propagate_action();
-        } else {
-            self.insert(&"\n".into(), ctx);
-        }
-    }
-
-    pub fn backspace(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        self.start_transaction(ctx);
-        let mut selections = self.selections(ctx.as_ref()).to_vec();
-        {
-            let buffer = self.buffer.read(ctx);
-            for selection in &mut selections {
-                let range = selection.point_range(buffer);
-                if range.start == range.end {
-                    let head = selection
-                        .head()
-                        .to_display_point(&self.display_map, ctx.as_ref());
-                    let cursor = self.display_map.anchor_before(
-                        movement::left(&self.display_map, head, ctx.as_ref()).unwrap(),
-                        Bias::Left,
-                        ctx.as_ref(),
-                    );
-                    selection.set_head(&buffer, cursor);
-                    selection.goal = SelectionGoal::None;
-                }
-            }
-        }
-
-        self.update_selections(selections, true, ctx);
-        self.insert(&String::new(), ctx);
-        self.end_transaction(ctx);
-    }
-
-    pub fn delete(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        self.start_transaction(ctx);
-        let mut selections = self.selections(ctx.as_ref()).to_vec();
-        {
-            let buffer = self.buffer.read(ctx);
-            for selection in &mut selections {
-                let range = selection.point_range(buffer);
-                if range.start == range.end {
-                    let head = selection
-                        .head()
-                        .to_display_point(&self.display_map, ctx.as_ref());
-                    let cursor = self.display_map.anchor_before(
-                        movement::right(&self.display_map, head, ctx.as_ref()).unwrap(),
-                        Bias::Right,
-                        ctx.as_ref(),
-                    );
-                    selection.set_head(&buffer, cursor);
-                    selection.goal = SelectionGoal::None;
-                }
-            }
-        }
-
-        self.update_selections(selections, true, ctx);
-        self.insert(&String::new(), ctx);
-        self.end_transaction(ctx);
-    }
-
-    pub fn delete_line(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        self.start_transaction(ctx);
-
-        let app = ctx.as_ref();
-        let buffer = self.buffer.read(app);
-
-        let mut new_cursors = Vec::new();
-        let mut edit_ranges = Vec::new();
-
-        let mut selections = self.selections(app).iter().peekable();
-        while let Some(selection) = selections.next() {
-            let (mut rows, _) =
-                selection.buffer_rows_for_display_rows(false, &self.display_map, app);
-            let goal_display_column = selection
-                .head()
-                .to_display_point(&self.display_map, app)
-                .column();
-
-            // Accumulate contiguous regions of rows that we want to delete.
-            while let Some(next_selection) = selections.peek() {
-                let (next_rows, _) =
-                    next_selection.buffer_rows_for_display_rows(false, &self.display_map, app);
-                if next_rows.start <= rows.end {
-                    rows.end = next_rows.end;
-                    selections.next().unwrap();
-                } else {
-                    break;
-                }
-            }
-
-            let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
-            let edit_end;
-            let cursor_buffer_row;
-            if buffer.max_point().row >= rows.end {
-                // If there's a line after the range, delete the \n from the end of the row range
-                // and position the cursor on the next line.
-                edit_end = Point::new(rows.end, 0).to_offset(buffer);
-                cursor_buffer_row = rows.end;
-            } else {
-                // If there isn't a line after the range, delete the \n from the line before the
-                // start of the row range and position the cursor there.
-                edit_start = edit_start.saturating_sub(1);
-                edit_end = buffer.len();
-                cursor_buffer_row = rows.start.saturating_sub(1);
-            }
-
-            let mut cursor =
-                Point::new(cursor_buffer_row, 0).to_display_point(&self.display_map, app);
-            *cursor.column_mut() = cmp::min(
-                goal_display_column,
-                self.display_map.line_len(cursor.row(), app),
-            );
-
-            new_cursors.push((
-                selection.id,
-                cursor.to_buffer_point(&self.display_map, Bias::Left, app),
-            ));
-            edit_ranges.push(edit_start..edit_end);
-        }
-
-        new_cursors.sort_unstable_by_key(|(_, range)| range.clone());
-        let new_selections = new_cursors
-            .into_iter()
-            .map(|(id, cursor)| {
-                let anchor = buffer.anchor_before(cursor);
-                Selection {
-                    id,
-                    start: anchor.clone(),
-                    end: anchor,
-                    reversed: false,
-                    goal: SelectionGoal::None,
-                }
-            })
-            .collect();
-        self.buffer
-            .update(ctx, |buffer, ctx| buffer.edit(edit_ranges, "", Some(ctx)))
-            .unwrap();
-        self.update_selections(new_selections, true, ctx);
-        self.end_transaction(ctx);
-    }
-
-    pub fn duplicate_line(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        self.start_transaction(ctx);
-
-        let mut selections = self.selections(ctx.as_ref()).to_vec();
-        {
-            // Temporarily bias selections right to allow newly duplicate lines to push them down
-            // when the selections are at the beginning of a line.
-            let buffer = self.buffer.read(ctx);
-            for selection in &mut selections {
-                selection.start = selection.start.bias_right(buffer);
-                selection.end = selection.end.bias_right(buffer);
-            }
-        }
-        self.update_selections(selections.clone(), false, ctx);
-
-        let app = ctx.as_ref();
-        let buffer = self.buffer.read(ctx);
-
-        let mut edits = Vec::new();
-        let mut selections_iter = selections.iter_mut().peekable();
-        while let Some(selection) = selections_iter.next() {
-            // Avoid duplicating the same lines twice.
-            let (mut rows, _) =
-                selection.buffer_rows_for_display_rows(false, &self.display_map, app);
-            while let Some(next_selection) = selections_iter.peek() {
-                let (next_rows, _) =
-                    next_selection.buffer_rows_for_display_rows(false, &self.display_map, app);
-                if next_rows.start <= rows.end - 1 {
-                    rows.end = next_rows.end;
-                    selections_iter.next().unwrap();
-                } else {
-                    break;
-                }
-            }
-
-            // Copy the text from the selected row region and splice it at the start of the region.
-            let start = Point::new(rows.start, 0);
-            let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
-            let text = buffer
-                .text_for_range(start..end)
-                .chain(Some("\n"))
-                .collect::<String>();
-            edits.push((start, text));
-        }
-
-        self.buffer.update(ctx, |buffer, ctx| {
-            for (offset, text) in edits.into_iter().rev() {
-                buffer.edit(Some(offset..offset), text, Some(ctx)).unwrap();
-            }
-        });
-
-        // Restore bias on selections.
-        let buffer = self.buffer.read(ctx);
-        for selection in &mut selections {
-            selection.start = selection.start.bias_left(buffer);
-            selection.end = selection.end.bias_left(buffer);
-        }
-        self.update_selections(selections, true, ctx);
-
-        self.end_transaction(ctx);
-    }
-
-    pub fn move_line_up(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        self.start_transaction(ctx);
-
-        let app = ctx.as_ref();
-        let buffer = self.buffer.read(ctx);
-
-        let mut edits = Vec::new();
-        let mut new_selection_ranges = Vec::new();
-        let mut old_folds = Vec::new();
-        let mut new_folds = Vec::new();
-
-        let mut selections = self.selections(app).iter().peekable();
-        let mut contiguous_selections = Vec::new();
-        while let Some(selection) = selections.next() {
-            // Accumulate contiguous regions of rows that we want to move.
-            contiguous_selections.push(selection.point_range(buffer));
-            let (mut buffer_rows, mut display_rows) =
-                selection.buffer_rows_for_display_rows(false, &self.display_map, app);
-            while let Some(next_selection) = selections.peek() {
-                let (next_buffer_rows, next_display_rows) =
-                    next_selection.buffer_rows_for_display_rows(false, &self.display_map, app);
-                if next_buffer_rows.start <= buffer_rows.end {
-                    buffer_rows.end = next_buffer_rows.end;
-                    display_rows.end = next_display_rows.end;
-                    contiguous_selections.push(next_selection.point_range(buffer));
-                    selections.next().unwrap();
-                } else {
-                    break;
-                }
-            }
-
-            // Cut the text from the selected rows and paste it at the start of the previous line.
-            if display_rows.start != 0 {
-                let start = Point::new(buffer_rows.start, 0).to_offset(buffer);
-                let end = Point::new(buffer_rows.end - 1, buffer.line_len(buffer_rows.end - 1))
-                    .to_offset(buffer);
-
-                let prev_row_display_start = DisplayPoint::new(display_rows.start - 1, 0);
-                let prev_row_start =
-                    prev_row_display_start.to_buffer_offset(&self.display_map, Bias::Left, app);
-
-                let mut text = String::new();
-                text.extend(buffer.text_for_range(start..end));
-                text.push('\n');
-                edits.push((prev_row_start..prev_row_start, text));
-                edits.push((start - 1..end, String::new()));
-
-                let row_delta = buffer_rows.start
-                    - prev_row_display_start
-                        .to_buffer_point(&self.display_map, Bias::Left, app)
-                        .row;
-
-                // Move selections up.
-                for range in &mut contiguous_selections {
-                    range.start.row -= row_delta;
-                    range.end.row -= row_delta;
-                }
-
-                // Move folds up.
-                old_folds.push(start..end);
-                for fold in self.display_map.folds_in_range(start..end, app) {
-                    let mut start = fold.start.to_point(buffer);
-                    let mut end = fold.end.to_point(buffer);
-                    start.row -= row_delta;
-                    end.row -= row_delta;
-                    new_folds.push(start..end);
-                }
-            }
-
-            new_selection_ranges.extend(contiguous_selections.drain(..));
-        }
-
-        self.unfold_ranges(old_folds, ctx);
-        self.buffer.update(ctx, |buffer, ctx| {
-            for (range, text) in edits.into_iter().rev() {
-                buffer.edit(Some(range), text, Some(ctx)).unwrap();
-            }
-        });
-        self.fold_ranges(new_folds, ctx);
-        self.select_ranges(new_selection_ranges, true, ctx);
-
-        self.end_transaction(ctx);
-    }
-
-    pub fn move_line_down(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        self.start_transaction(ctx);
-
-        let app = ctx.as_ref();
-        let buffer = self.buffer.read(ctx);
-
-        let mut edits = Vec::new();
-        let mut new_selection_ranges = Vec::new();
-        let mut old_folds = Vec::new();
-        let mut new_folds = Vec::new();
-
-        let mut selections = self.selections(app).iter().peekable();
-        let mut contiguous_selections = Vec::new();
-        while let Some(selection) = selections.next() {
-            // Accumulate contiguous regions of rows that we want to move.
-            contiguous_selections.push(selection.point_range(buffer));
-            let (mut buffer_rows, mut display_rows) =
-                selection.buffer_rows_for_display_rows(false, &self.display_map, app);
-            while let Some(next_selection) = selections.peek() {
-                let (next_buffer_rows, next_display_rows) =
-                    next_selection.buffer_rows_for_display_rows(false, &self.display_map, app);
-                if next_buffer_rows.start <= buffer_rows.end {
-                    buffer_rows.end = next_buffer_rows.end;
-                    display_rows.end = next_display_rows.end;
-                    contiguous_selections.push(next_selection.point_range(buffer));
-                    selections.next().unwrap();
-                } else {
-                    break;
-                }
-            }
-
-            // Cut the text from the selected rows and paste it at the end of the next line.
-            if display_rows.end <= self.display_map.max_point(app).row() {
-                let start = Point::new(buffer_rows.start, 0).to_offset(buffer);
-                let end = Point::new(buffer_rows.end - 1, buffer.line_len(buffer_rows.end - 1))
-                    .to_offset(buffer);
-
-                let next_row_display_end = DisplayPoint::new(
-                    display_rows.end,
-                    self.display_map.line_len(display_rows.end, app),
-                );
-                let next_row_end =
-                    next_row_display_end.to_buffer_offset(&self.display_map, Bias::Right, app);
-
-                let mut text = String::new();
-                text.push('\n');
-                text.extend(buffer.text_for_range(start..end));
-                edits.push((start..end + 1, String::new()));
-                edits.push((next_row_end..next_row_end, text));
-
-                let row_delta = next_row_display_end
-                    .to_buffer_point(&self.display_map, Bias::Right, app)
-                    .row
-                    - buffer_rows.end
-                    + 1;
-
-                // Move selections down.
-                for range in &mut contiguous_selections {
-                    range.start.row += row_delta;
-                    range.end.row += row_delta;
-                }
-
-                // Move folds down.
-                old_folds.push(start..end);
-                for fold in self.display_map.folds_in_range(start..end, app) {
-                    let mut start = fold.start.to_point(buffer);
-                    let mut end = fold.end.to_point(buffer);
-                    start.row += row_delta;
-                    end.row += row_delta;
-                    new_folds.push(start..end);
-                }
-            }
-
-            new_selection_ranges.extend(contiguous_selections.drain(..));
-        }
-
-        self.unfold_ranges(old_folds, ctx);
-        self.buffer.update(ctx, |buffer, ctx| {
-            for (range, text) in edits.into_iter().rev() {
-                buffer.edit(Some(range), text, Some(ctx)).unwrap();
-            }
-        });
-        self.fold_ranges(new_folds, ctx);
-        self.select_ranges(new_selection_ranges, true, ctx);
-
-        self.end_transaction(ctx);
-    }
-
-    pub fn cut(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        self.start_transaction(ctx);
-        let mut text = String::new();
-        let mut selections = self.selections(ctx.as_ref()).to_vec();
-        let mut clipboard_selections = Vec::with_capacity(selections.len());
-        {
-            let buffer = self.buffer.read(ctx);
-            let max_point = buffer.max_point();
-            for selection in &mut selections {
-                let mut start = selection.start.to_point(buffer);
-                let mut end = selection.end.to_point(buffer);
-                let is_entire_line = start == end;
-                if is_entire_line {
-                    start = Point::new(start.row, 0);
-                    end = cmp::min(max_point, Point::new(start.row + 1, 0));
-                    selection.start = buffer.anchor_before(start);
-                    selection.end = buffer.anchor_before(end);
-                }
-                let mut len = 0;
-                for chunk in buffer.text_for_range(start..end) {
-                    text.push_str(chunk);
-                    len += chunk.len();
-                }
-                clipboard_selections.push(ClipboardSelection {
-                    len,
-                    is_entire_line,
-                });
-            }
-        }
-        self.update_selections(selections, true, ctx);
-        self.insert(&String::new(), ctx);
-        self.end_transaction(ctx);
-
-        ctx.as_mut()
-            .write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
-    }
-
-    pub fn copy(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        let buffer = self.buffer.read(ctx);
-        let max_point = buffer.max_point();
-        let mut text = String::new();
-        let selections = self.selections(ctx.as_ref());
-        let mut clipboard_selections = Vec::with_capacity(selections.len());
-        for selection in selections {
-            let mut start = selection.start.to_point(buffer);
-            let mut end = selection.end.to_point(buffer);
-            let is_entire_line = start == end;
-            if is_entire_line {
-                start = Point::new(start.row, 0);
-                end = cmp::min(max_point, Point::new(start.row + 1, 0));
-            }
-            let mut len = 0;
-            for chunk in buffer.text_for_range(start..end) {
-                text.push_str(chunk);
-                len += chunk.len();
-            }
-            clipboard_selections.push(ClipboardSelection {
-                len,
-                is_entire_line,
-            });
-        }
-
-        ctx.as_mut()
-            .write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
-    }
-
-    pub fn paste(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        if let Some(item) = ctx.as_mut().read_from_clipboard() {
-            let clipboard_text = item.text();
-            if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
-                let selections = self.selections(ctx.as_ref()).to_vec();
-                if clipboard_selections.len() != selections.len() {
-                    let merged_selection = ClipboardSelection {
-                        len: clipboard_selections.iter().map(|s| s.len).sum(),
-                        is_entire_line: clipboard_selections.iter().all(|s| s.is_entire_line),
-                    };
-                    clipboard_selections.clear();
-                    clipboard_selections.push(merged_selection);
-                }
-
-                self.start_transaction(ctx);
-                let mut new_selections = Vec::with_capacity(selections.len());
-                let mut clipboard_chars = clipboard_text.chars().cycle();
-                for (selection, clipboard_selection) in
-                    selections.iter().zip(clipboard_selections.iter().cycle())
-                {
-                    let to_insert =
-                        String::from_iter(clipboard_chars.by_ref().take(clipboard_selection.len));
-
-                    self.buffer.update(ctx, |buffer, ctx| {
-                        let selection_start = selection.start.to_point(buffer);
-                        let selection_end = selection.end.to_point(buffer);
-
-                        // If the corresponding selection was empty when this slice of the
-                        // clipboard text was written, then the entire line containing the
-                        // selection was copied. If this selection is also currently empty,
-                        // then paste the line before the current line of the buffer.
-                        let new_selection_start = selection.end.bias_right(buffer);
-                        if selection_start == selection_end && clipboard_selection.is_entire_line {
-                            let line_start = Point::new(selection_start.row, 0);
-                            buffer
-                                .edit(Some(line_start..line_start), to_insert, Some(ctx))
-                                .unwrap();
-                        } else {
-                            buffer
-                                .edit(Some(&selection.start..&selection.end), to_insert, Some(ctx))
-                                .unwrap();
-                        };
-
-                        let new_selection_start = new_selection_start.bias_left(buffer);
-                        new_selections.push(Selection {
-                            id: selection.id,
-                            start: new_selection_start.clone(),
-                            end: new_selection_start,
-                            reversed: false,
-                            goal: SelectionGoal::None,
-                        });
-                    });
-                }
-                self.update_selections(new_selections, true, ctx);
-                self.end_transaction(ctx);
-            } else {
-                self.insert(clipboard_text, ctx);
-            }
-        }
-    }
-
-    pub fn undo(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        self.buffer
-            .update(ctx, |buffer, ctx| buffer.undo(Some(ctx)));
-    }
-
-    pub fn redo(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        self.buffer
-            .update(ctx, |buffer, ctx| buffer.redo(Some(ctx)));
-    }
-
-    pub fn move_left(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        let app = ctx.as_ref();
-        let mut selections = self.selections(app).to_vec();
-        {
-            for selection in &mut selections {
-                let start = selection.start.to_display_point(&self.display_map, app);
-                let end = selection.end.to_display_point(&self.display_map, app);
-
-                if start != end {
-                    selection.end = selection.start.clone();
-                } else {
-                    let cursor = self.display_map.anchor_before(
-                        movement::left(&self.display_map, start, app).unwrap(),
-                        Bias::Left,
-                        app,
-                    );
-                    selection.start = cursor.clone();
-                    selection.end = cursor;
-                }
-                selection.reversed = false;
-                selection.goal = SelectionGoal::None;
-            }
-        }
-        self.update_selections(selections, true, ctx);
-    }
-
-    pub fn select_left(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        let mut selections = self.selections(ctx.as_ref()).to_vec();
-        {
-            let buffer = self.buffer.read(ctx);
-            for selection in &mut selections {
-                let head = selection
-                    .head()
-                    .to_display_point(&self.display_map, ctx.as_ref());
-                let cursor = self.display_map.anchor_before(
-                    movement::left(&self.display_map, head, ctx.as_ref()).unwrap(),
-                    Bias::Left,
-                    ctx.as_ref(),
-                );
-                selection.set_head(&buffer, cursor);
-                selection.goal = SelectionGoal::None;
-            }
-        }
-        self.update_selections(selections, true, ctx);
-    }
-
-    pub fn move_right(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        let mut selections = self.selections(ctx.as_ref()).to_vec();
-        {
-            let app = ctx.as_ref();
-            for selection in &mut selections {
-                let start = selection.start.to_display_point(&self.display_map, app);
-                let end = selection.end.to_display_point(&self.display_map, app);
-
-                if start != end {
-                    selection.start = selection.end.clone();
-                } else {
-                    let cursor = self.display_map.anchor_before(
-                        movement::right(&self.display_map, end, app).unwrap(),
-                        Bias::Right,
-                        app,
-                    );
-                    selection.start = cursor.clone();
-                    selection.end = cursor;
-                }
-                selection.reversed = false;
-                selection.goal = SelectionGoal::None;
-            }
-        }
-        self.update_selections(selections, true, ctx);
-    }
-
-    pub fn select_right(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        let mut selections = self.selections(ctx.as_ref()).to_vec();
-        {
-            let app = ctx.as_ref();
-            let buffer = self.buffer.read(app);
-            for selection in &mut selections {
-                let head = selection
-                    .head()
-                    .to_display_point(&self.display_map, ctx.as_ref());
-                let cursor = self.display_map.anchor_before(
-                    movement::right(&self.display_map, head, app).unwrap(),
-                    Bias::Right,
-                    app,
-                );
-                selection.set_head(&buffer, cursor);
-                selection.goal = SelectionGoal::None;
-            }
-        }
-        self.update_selections(selections, true, ctx);
-    }
-
-    pub fn move_up(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        if self.single_line {
-            ctx.propagate_action();
-        } else {
-            let mut selections = self.selections(ctx.as_ref()).to_vec();
-            {
-                let app = ctx.as_ref();
-                for selection in &mut selections {
-                    let start = selection.start.to_display_point(&self.display_map, app);
-                    let end = selection.end.to_display_point(&self.display_map, app);
-                    if start != end {
-                        selection.goal = SelectionGoal::None;
-                    }
-
-                    let (start, goal) =
-                        movement::up(&self.display_map, start, selection.goal, app).unwrap();
-                    let cursor = self.display_map.anchor_before(start, Bias::Left, app);
-                    selection.start = cursor.clone();
-                    selection.end = cursor;
-                    selection.goal = goal;
-                    selection.reversed = false;
-                }
-            }
-            self.update_selections(selections, true, ctx);
-        }
-    }
-
-    pub fn select_up(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        let mut selections = self.selections(ctx.as_ref()).to_vec();
-        {
-            let app = ctx.as_ref();
-            let buffer = self.buffer.read(app);
-            for selection in &mut selections {
-                let head = selection.head().to_display_point(&self.display_map, app);
-                let (head, goal) =
-                    movement::up(&self.display_map, head, selection.goal, app).unwrap();
-                selection.set_head(
-                    &buffer,
-                    self.display_map.anchor_before(head, Bias::Left, app),
-                );
-                selection.goal = goal;
-            }
-        }
-        self.update_selections(selections, true, ctx);
-    }
-
-    pub fn move_down(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        if self.single_line {
-            ctx.propagate_action();
-        } else {
-            let mut selections = self.selections(ctx.as_ref()).to_vec();
-            {
-                let app = ctx.as_ref();
-                for selection in &mut selections {
-                    let start = selection.start.to_display_point(&self.display_map, app);
-                    let end = selection.end.to_display_point(&self.display_map, app);
-                    if start != end {
-                        selection.goal = SelectionGoal::None;
-                    }
-
-                    let (start, goal) =
-                        movement::down(&self.display_map, end, selection.goal, app).unwrap();
-                    let cursor = self.display_map.anchor_before(start, Bias::Right, app);
-                    selection.start = cursor.clone();
-                    selection.end = cursor;
-                    selection.goal = goal;
-                    selection.reversed = false;
-                }
-            }
-            self.update_selections(selections, true, ctx);
-        }
-    }
-
-    pub fn select_down(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        let mut selections = self.selections(ctx.as_ref()).to_vec();
-        {
-            let app = ctx.as_ref();
-            let buffer = self.buffer.read(app);
-            for selection in &mut selections {
-                let head = selection.head().to_display_point(&self.display_map, app);
-                let (head, goal) =
-                    movement::down(&self.display_map, head, selection.goal, app).unwrap();
-                selection.set_head(
-                    &buffer,
-                    self.display_map.anchor_before(head, Bias::Right, app),
-                );
-                selection.goal = goal;
-            }
-        }
-        self.update_selections(selections, true, ctx);
-    }
-
-    pub fn move_to_previous_word_boundary(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        let app = ctx.as_ref();
-        let mut selections = self.selections(app).to_vec();
-        {
-            for selection in &mut selections {
-                let head = selection.head().to_display_point(&self.display_map, app);
-                let new_head = movement::prev_word_boundary(&self.display_map, head, app).unwrap();
-                let anchor = self.display_map.anchor_before(new_head, Bias::Left, app);
-                selection.start = anchor.clone();
-                selection.end = anchor;
-                selection.reversed = false;
-                selection.goal = SelectionGoal::None;
-            }
-        }
-        self.update_selections(selections, true, ctx);
-    }
-
-    pub fn select_to_previous_word_boundary(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        let app = ctx.as_ref();
-        let mut selections = self.selections(app).to_vec();
-        {
-            let buffer = self.buffer.read(ctx);
-            for selection in &mut selections {
-                let head = selection.head().to_display_point(&self.display_map, app);
-                let new_head = movement::prev_word_boundary(&self.display_map, head, app).unwrap();
-                let anchor = self.display_map.anchor_before(new_head, Bias::Left, app);
-                selection.set_head(buffer, anchor);
-                selection.goal = SelectionGoal::None;
-            }
-        }
-        self.update_selections(selections, true, ctx);
-    }
-
-    pub fn delete_to_previous_word_boundary(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        self.start_transaction(ctx);
-        self.select_to_previous_word_boundary(&(), ctx);
-        self.backspace(&(), ctx);
-        self.end_transaction(ctx);
-    }
-
-    pub fn move_to_next_word_boundary(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        let app = ctx.as_ref();
-        let mut selections = self.selections(app).to_vec();
-        {
-            for selection in &mut selections {
-                let head = selection.head().to_display_point(&self.display_map, app);
-                let new_head = movement::next_word_boundary(&self.display_map, head, app).unwrap();
-                let anchor = self.display_map.anchor_before(new_head, Bias::Left, app);
-                selection.start = anchor.clone();
-                selection.end = anchor;
-                selection.reversed = false;
-                selection.goal = SelectionGoal::None;
-            }
-        }
-        self.update_selections(selections, true, ctx);
-    }
-
-    pub fn select_to_next_word_boundary(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        let app = ctx.as_ref();
-        let mut selections = self.selections(app).to_vec();
-        {
-            let buffer = self.buffer.read(ctx);
-            for selection in &mut selections {
-                let head = selection.head().to_display_point(&self.display_map, app);
-                let new_head = movement::next_word_boundary(&self.display_map, head, app).unwrap();
-                let anchor = self.display_map.anchor_before(new_head, Bias::Left, app);
-                selection.set_head(buffer, anchor);
-                selection.goal = SelectionGoal::None;
-            }
-        }
-        self.update_selections(selections, true, ctx);
-    }
-
-    pub fn delete_to_next_word_boundary(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        self.start_transaction(ctx);
-        self.select_to_next_word_boundary(&(), ctx);
-        self.delete(&(), ctx);
-        self.end_transaction(ctx);
-    }
-
-    pub fn move_to_beginning_of_line(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        let app = ctx.as_ref();
-        let mut selections = self.selections(app).to_vec();
-        {
-            for selection in &mut selections {
-                let head = selection.head().to_display_point(&self.display_map, app);
-                let new_head =
-                    movement::line_beginning(&self.display_map, head, true, app).unwrap();
-                let anchor = self.display_map.anchor_before(new_head, Bias::Left, app);
-                selection.start = anchor.clone();
-                selection.end = anchor;
-                selection.reversed = false;
-                selection.goal = SelectionGoal::None;
-            }
-        }
-        self.update_selections(selections, true, ctx);
-    }
-
-    pub fn select_to_beginning_of_line(
-        &mut self,
-        toggle_indent: &bool,
-        ctx: &mut ViewContext<Self>,
-    ) {
-        let app = ctx.as_ref();
-        let mut selections = self.selections(app).to_vec();
-        {
-            let buffer = self.buffer.read(ctx);
-            for selection in &mut selections {
-                let head = selection.head().to_display_point(&self.display_map, app);
-                let new_head =
-                    movement::line_beginning(&self.display_map, head, *toggle_indent, app).unwrap();
-                let anchor = self.display_map.anchor_before(new_head, Bias::Left, app);
-                selection.set_head(buffer, anchor);
-                selection.goal = SelectionGoal::None;
-            }
-        }
-        self.update_selections(selections, true, ctx);
-    }
-
-    pub fn delete_to_beginning_of_line(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        self.start_transaction(ctx);
-        self.select_to_beginning_of_line(&false, ctx);
-        self.backspace(&(), ctx);
-        self.end_transaction(ctx);
-    }
-
-    pub fn move_to_end_of_line(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        let app = ctx.as_ref();
-        let mut selections = self.selections(app).to_vec();
-        {
-            for selection in &mut selections {
-                let head = selection.head().to_display_point(&self.display_map, app);
-                let new_head = movement::line_end(&self.display_map, head, app).unwrap();
-                let anchor = self.display_map.anchor_before(new_head, Bias::Left, app);
-                selection.start = anchor.clone();
-                selection.end = anchor;
-                selection.reversed = false;
-                selection.goal = SelectionGoal::None;
-            }
-        }
-        self.update_selections(selections, true, ctx);
-    }
-
-    pub fn select_to_end_of_line(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        let app = ctx.as_ref();
-        let mut selections = self.selections(app).to_vec();
-        {
-            let buffer = self.buffer.read(ctx);
-            for selection in &mut selections {
-                let head = selection.head().to_display_point(&self.display_map, app);
-                let new_head = movement::line_end(&self.display_map, head, app).unwrap();
-                let anchor = self.display_map.anchor_before(new_head, Bias::Left, app);
-                selection.set_head(buffer, anchor);
-                selection.goal = SelectionGoal::None;
-            }
-        }
-        self.update_selections(selections, true, ctx);
-    }
-
-    pub fn delete_to_end_of_line(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        self.start_transaction(ctx);
-        self.select_to_end_of_line(&(), ctx);
-        self.delete(&(), ctx);
-        self.end_transaction(ctx);
-    }
-
-    pub fn move_to_beginning(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        let buffer = self.buffer.read(ctx);
-        let cursor = buffer.anchor_before(Point::new(0, 0));
-        let selection = Selection {
-            id: post_inc(&mut self.next_selection_id),
-            start: cursor.clone(),
-            end: cursor,
-            reversed: false,
-            goal: SelectionGoal::None,
-        };
-        self.update_selections(vec![selection], true, ctx);
-    }
-
-    pub fn select_to_beginning(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        let mut selection = self.selections(ctx.as_ref()).last().unwrap().clone();
-        selection.set_head(self.buffer.read(ctx), Anchor::Start);
-        self.update_selections(vec![selection], true, ctx);
-    }
-
-    pub fn move_to_end(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        let buffer = self.buffer.read(ctx);
-        let cursor = buffer.anchor_before(buffer.max_point());
-        let selection = Selection {
-            id: post_inc(&mut self.next_selection_id),
-            start: cursor.clone(),
-            end: cursor,
-            reversed: false,
-            goal: SelectionGoal::None,
-        };
-        self.update_selections(vec![selection], true, ctx);
-    }
-
-    pub fn select_to_end(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        let mut selection = self.selections(ctx.as_ref()).last().unwrap().clone();
-        selection.set_head(self.buffer.read(ctx), Anchor::End);
-        self.update_selections(vec![selection], true, ctx);
-    }
-
-    pub fn select_all(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        let selection = Selection {
-            id: post_inc(&mut self.next_selection_id),
-            start: Anchor::Start,
-            end: Anchor::End,
-            reversed: false,
-            goal: SelectionGoal::None,
-        };
-        self.update_selections(vec![selection], false, ctx);
-    }
-
-    pub fn select_line(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        let app = ctx.as_ref();
-        let buffer = self.buffer.read(app);
-        let mut selections = self.selections(app).to_vec();
-        let max_point = buffer.max_point();
-        for selection in &mut selections {
-            let (rows, _) = selection.buffer_rows_for_display_rows(true, &self.display_map, app);
-            selection.start = buffer.anchor_before(Point::new(rows.start, 0));
-            selection.end = buffer.anchor_before(cmp::min(max_point, Point::new(rows.end, 0)));
-            selection.reversed = false;
-        }
-        self.update_selections(selections, true, ctx);
-    }
-
-    pub fn split_selection_into_lines(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        use super::RangeExt;
-
-        let app = ctx.as_ref();
-        let buffer = self.buffer.read(app);
-
-        let mut to_unfold = Vec::new();
-        let mut new_selections = Vec::new();
-        for selection in self.selections(app) {
-            let range = selection.point_range(buffer).sorted();
-            if range.start.row != range.end.row {
-                new_selections.push(Selection {
-                    id: post_inc(&mut self.next_selection_id),
-                    start: selection.start.clone(),
-                    end: selection.start.clone(),
-                    reversed: false,
-                    goal: SelectionGoal::None,
-                });
-            }
-            for row in range.start.row + 1..range.end.row {
-                let cursor = buffer.anchor_before(Point::new(row, buffer.line_len(row)));
-                new_selections.push(Selection {
-                    id: post_inc(&mut self.next_selection_id),
-                    start: cursor.clone(),
-                    end: cursor,
-                    reversed: false,
-                    goal: SelectionGoal::None,
-                });
-            }
-            new_selections.push(Selection {
-                id: selection.id,
-                start: selection.end.clone(),
-                end: selection.end.clone(),
-                reversed: false,
-                goal: SelectionGoal::None,
-            });
-            to_unfold.push(range);
-        }
-        self.unfold_ranges(to_unfold, ctx);
-        self.update_selections(new_selections, true, ctx);
-    }
-
-    pub fn add_selection_above(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        self.add_selection(true, ctx);
-    }
-
-    pub fn add_selection_below(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        self.add_selection(false, ctx);
-    }
-
-    fn add_selection(&mut self, above: bool, ctx: &mut ViewContext<Self>) {
-        use super::RangeExt;
-
-        let app = ctx.as_ref();
-
-        let mut selections = self.selections(app).to_vec();
-        let mut state = self.add_selections_state.take().unwrap_or_else(|| {
-            let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
-            let range = oldest_selection
-                .display_range(&self.display_map, app)
-                .sorted();
-            let columns = cmp::min(range.start.column(), range.end.column())
-                ..cmp::max(range.start.column(), range.end.column());
-
-            selections.clear();
-            let mut stack = Vec::new();
-            for row in range.start.row()..=range.end.row() {
-                if let Some(selection) =
-                    self.build_columnar_selection(row, &columns, oldest_selection.reversed, app)
-                {
-                    stack.push(selection.id);
-                    selections.push(selection);
-                }
-            }
-
-            if above {
-                stack.reverse();
-            }
-
-            AddSelectionsState { above, stack }
-        });
-
-        let last_added_selection = *state.stack.last().unwrap();
-        let mut new_selections = Vec::new();
-        if above == state.above {
-            let end_row = if above {
-                0
-            } else {
-                self.display_map.max_point(app).row()
-            };
-
-            'outer: for selection in selections {
-                if selection.id == last_added_selection {
-                    let range = selection.display_range(&self.display_map, app).sorted();
-                    debug_assert_eq!(range.start.row(), range.end.row());
-                    let mut row = range.start.row();
-                    let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
-                    {
-                        start..end
-                    } else {
-                        cmp::min(range.start.column(), range.end.column())
-                            ..cmp::max(range.start.column(), range.end.column())
-                    };
-
-                    while row != end_row {
-                        if above {
-                            row -= 1;
-                        } else {
-                            row += 1;
-                        }
-
-                        if let Some(new_selection) =
-                            self.build_columnar_selection(row, &columns, selection.reversed, app)
-                        {
-                            state.stack.push(new_selection.id);
-                            if above {
-                                new_selections.push(new_selection);
-                                new_selections.push(selection);
-                            } else {
-                                new_selections.push(selection);
-                                new_selections.push(new_selection);
-                            }
-
-                            continue 'outer;
-                        }
-                    }
-                }
-
-                new_selections.push(selection);
-            }
-        } else {
-            new_selections = selections;
-            new_selections.retain(|s| s.id != last_added_selection);
-            state.stack.pop();
-        }
-
-        self.update_selections(new_selections, true, ctx);
-        if state.stack.len() > 1 {
-            self.add_selections_state = Some(state);
-        }
-    }
-
-    pub fn select_larger_syntax_node(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        let app = ctx.as_ref();
-        let buffer = self.buffer.read(app);
-
-        let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
-        let mut selected_larger_node = false;
-        let old_selections = self.selections(app).to_vec();
-        let mut new_selections = Vec::new();
-        for selection in &old_selections {
-            let old_range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
-            let mut new_range = old_range.clone();
-            while let Some(containing_range) = buffer.range_for_syntax_ancestor(new_range.clone()) {
-                new_range = containing_range;
-                if !self.display_map.intersects_fold(new_range.start, app)
-                    && !self.display_map.intersects_fold(new_range.end, app)
-                {
-                    break;
-                }
-            }
-
-            selected_larger_node |= new_range != old_range;
-            new_selections.push(Selection {
-                id: selection.id,
-                start: buffer.anchor_before(new_range.start),
-                end: buffer.anchor_before(new_range.end),
-                reversed: selection.reversed,
-                goal: SelectionGoal::None,
-            });
-        }
-
-        if selected_larger_node {
-            stack.push(old_selections);
-            self.update_selections(new_selections, true, ctx);
-        }
-        self.select_larger_syntax_node_stack = stack;
-    }
-
-    pub fn select_smaller_syntax_node(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
-        if let Some(selections) = stack.pop() {
-            self.update_selections(selections, true, ctx);
-        }
-        self.select_larger_syntax_node_stack = stack;
-    }
-
-    pub fn move_to_enclosing_bracket(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        use super::RangeExt as _;
-
-        let buffer = self.buffer.read(ctx.as_ref());
-        let mut selections = self.selections(ctx.as_ref()).to_vec();
-        for selection in &mut selections {
-            let selection_range = selection.offset_range(buffer);
-            if let Some((open_range, close_range)) =
-                buffer.enclosing_bracket_ranges(selection_range.clone())
-            {
-                let close_range = close_range.to_inclusive();
-                let destination = if close_range.contains(&selection_range.start)
-                    && close_range.contains(&selection_range.end)
-                {
-                    open_range.end
-                } else {
-                    *close_range.start()
-                };
-                selection.start = buffer.anchor_before(destination);
-                selection.end = selection.start.clone();
-            }
-        }
-
-        self.update_selections(selections, true, ctx);
-    }
-
-    fn build_columnar_selection(
-        &mut self,
-        row: u32,
-        columns: &Range<u32>,
-        reversed: bool,
-        ctx: &AppContext,
-    ) -> Option<Selection> {
-        let is_empty = columns.start == columns.end;
-        let line_len = self.display_map.line_len(row, ctx);
-        if columns.start < line_len || (is_empty && columns.start == line_len) {
-            let start = DisplayPoint::new(row, columns.start);
-            let end = DisplayPoint::new(row, cmp::min(columns.end, line_len));
-            Some(Selection {
-                id: post_inc(&mut self.next_selection_id),
-                start: self.display_map.anchor_before(start, Bias::Left, ctx),
-                end: self.display_map.anchor_before(end, Bias::Left, ctx),
-                reversed,
-                goal: SelectionGoal::ColumnRange {
-                    start: columns.start,
-                    end: columns.end,
-                },
-            })
-        } else {
-            None
-        }
-    }
-
-    pub fn selections_in_range<'a>(
-        &'a self,
-        range: Range<DisplayPoint>,
-        app: &'a AppContext,
-    ) -> impl 'a + Iterator<Item = Range<DisplayPoint>> {
-        let start = self.display_map.anchor_before(range.start, Bias::Left, app);
-        let start_index = self.selection_insertion_index(&start, app);
-        let pending_selection = self.pending_selection.as_ref().and_then(|s| {
-            let selection_range = s.display_range(&self.display_map, app);
-            if selection_range.start <= range.end || selection_range.end <= range.end {
-                Some(selection_range)
-            } else {
-                None
-            }
-        });
-        self.selections(app)[start_index..]
-            .iter()
-            .map(move |s| s.display_range(&self.display_map, app))
-            .take_while(move |r| r.start <= range.end || r.end <= range.end)
-            .chain(pending_selection)
-    }
-
-    fn selection_insertion_index(&self, start: &Anchor, app: &AppContext) -> usize {
-        let buffer = self.buffer.read(app);
-        let selections = self.selections(app);
-        match selections.binary_search_by(|probe| probe.start.cmp(&start, buffer).unwrap()) {
-            Ok(index) => index,
-            Err(index) => {
-                if index > 0
-                    && selections[index - 1].end.cmp(&start, buffer).unwrap() == Ordering::Greater
-                {
-                    index - 1
-                } else {
-                    index
-                }
-            }
-        }
-    }
-
-    fn selections<'a>(&self, app: &'a AppContext) -> &'a [Selection] {
-        self.buffer
-            .read(app)
-            .selections(self.selection_set_id)
-            .unwrap()
-    }
-
-    fn update_selections(
-        &mut self,
-        mut selections: Vec<Selection>,
-        autoscroll: bool,
-        ctx: &mut ViewContext<Self>,
-    ) {
-        // Merge overlapping selections.
-        let buffer = self.buffer.read(ctx);
-        let mut i = 1;
-        while i < selections.len() {
-            if selections[i - 1]
-                .end
-                .cmp(&selections[i].start, buffer)
-                .unwrap()
-                >= Ordering::Equal
-            {
-                let removed = selections.remove(i);
-                if removed.start.cmp(&selections[i - 1].start, buffer).unwrap() < Ordering::Equal {
-                    selections[i - 1].start = removed.start;
-                }
-                if removed.end.cmp(&selections[i - 1].end, buffer).unwrap() > Ordering::Equal {
-                    selections[i - 1].end = removed.end;
-                }
-            } else {
-                i += 1;
-            }
-        }
-
-        self.buffer.update(ctx, |buffer, ctx| {
-            buffer
-                .update_selection_set(self.selection_set_id, selections, Some(ctx))
-                .unwrap()
-        });
-        self.pause_cursor_blinking(ctx);
-
-        if autoscroll {
-            *self.autoscroll_requested.lock() = true;
-            ctx.notify();
-        }
-
-        self.add_selections_state = None;
-        self.select_larger_syntax_node_stack.clear();
-    }
-
-    fn start_transaction(&self, ctx: &mut ViewContext<Self>) {
-        self.buffer.update(ctx, |buffer, _| {
-            buffer
-                .start_transaction(Some(self.selection_set_id))
-                .unwrap()
-        });
-    }
-
-    fn end_transaction(&self, ctx: &mut ViewContext<Self>) {
-        self.buffer.update(ctx, |buffer, ctx| {
-            buffer
-                .end_transaction(Some(self.selection_set_id), Some(ctx))
-                .unwrap()
-        });
-    }
-
-    pub fn page_up(&mut self, _: &(), _: &mut ViewContext<Self>) {
-        log::info!("BufferView::page_up");
-    }
-
-    pub fn page_down(&mut self, _: &(), _: &mut ViewContext<Self>) {
-        log::info!("BufferView::page_down");
-    }
-
-    pub fn fold(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        use super::RangeExt;
-
-        let mut fold_ranges = Vec::new();
-
-        let app = ctx.as_ref();
-        for selection in self.selections(app) {
-            let range = selection.display_range(&self.display_map, app).sorted();
-            let buffer_start_row = range
-                .start
-                .to_buffer_point(&self.display_map, Bias::Left, app)
-                .row;
-
-            for row in (0..=range.end.row()).rev() {
-                if self.is_line_foldable(row, app)
-                    && !self.display_map.is_line_folded(row, ctx.as_ref())
-                {
-                    let fold_range = self.foldable_range_for_line(row, app);
-                    if fold_range.end.row >= buffer_start_row {
-                        fold_ranges.push(fold_range);
-                        if row <= range.start.row() {
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-
-        self.fold_ranges(fold_ranges, ctx);
-    }
-
-    pub fn unfold(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        use super::RangeExt;
-
-        let app = ctx.as_ref();
-        let buffer = self.buffer.read(app);
-        let ranges = self
-            .selections(app)
-            .iter()
-            .map(|s| {
-                let range = s.display_range(&self.display_map, app).sorted();
-                let mut start = range
-                    .start
-                    .to_buffer_point(&self.display_map, Bias::Left, app);
-                let mut end = range
-                    .end
-                    .to_buffer_point(&self.display_map, Bias::Left, app);
-                start.column = 0;
-                end.column = buffer.line_len(end.row);
-                start..end
-            })
-            .collect::<Vec<_>>();
-        self.unfold_ranges(ranges, ctx);
-    }
-
-    fn is_line_foldable(&self, display_row: u32, app: &AppContext) -> bool {
-        let max_point = self.max_point(app);
-        if display_row >= max_point.row() {
-            false
-        } else {
-            let (start_indent, is_blank) = self.display_map.line_indent(display_row, app);
-            if is_blank {
-                false
-            } else {
-                for display_row in display_row + 1..=max_point.row() {
-                    let (indent, is_blank) = self.display_map.line_indent(display_row, app);
-                    if !is_blank {
-                        return indent > start_indent;
-                    }
-                }
-                false
-            }
-        }
-    }
-
-    fn foldable_range_for_line(&self, start_row: u32, app: &AppContext) -> Range<Point> {
-        let max_point = self.max_point(app);
-
-        let (start_indent, _) = self.display_map.line_indent(start_row, app);
-        let start = DisplayPoint::new(start_row, self.line_len(start_row, app));
-        let mut end = None;
-        for row in start_row + 1..=max_point.row() {
-            let (indent, is_blank) = self.display_map.line_indent(row, app);
-            if !is_blank && indent <= start_indent {
-                end = Some(DisplayPoint::new(row - 1, self.line_len(row - 1, app)));
-                break;
-            }
-        }
-
-        let end = end.unwrap_or(max_point);
-        return start.to_buffer_point(&self.display_map, Bias::Left, app)
-            ..end.to_buffer_point(&self.display_map, Bias::Left, app);
-    }
-
-    pub fn fold_selected_ranges(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        use super::RangeExt;
-
-        let buffer = self.buffer.read(ctx);
-        let ranges = self
-            .selections(ctx.as_ref())
-            .iter()
-            .map(|s| s.point_range(buffer).sorted())
-            .collect();
-        self.fold_ranges(ranges, ctx);
-    }
-
-    fn fold_ranges<T: ToOffset>(&mut self, ranges: Vec<Range<T>>, ctx: &mut ViewContext<Self>) {
-        if !ranges.is_empty() {
-            self.display_map.fold(ranges, ctx.as_ref());
-            *self.autoscroll_requested.lock() = true;
-            ctx.notify();
-        }
-    }
-
-    fn unfold_ranges<T: ToOffset>(&mut self, ranges: Vec<Range<T>>, ctx: &mut ViewContext<Self>) {
-        if !ranges.is_empty() {
-            self.display_map.unfold(ranges, ctx.as_ref());
-            *self.autoscroll_requested.lock() = true;
-            ctx.notify();
-        }
-    }
-
-    pub fn line(&self, display_row: u32, ctx: &AppContext) -> String {
-        self.display_map.line(display_row, ctx)
-    }
-
-    pub fn line_len(&self, display_row: u32, ctx: &AppContext) -> u32 {
-        self.display_map.line_len(display_row, ctx)
-    }
-
-    pub fn longest_row(&self, ctx: &AppContext) -> u32 {
-        self.display_map.longest_row(ctx)
-    }
-
-    pub fn max_point(&self, ctx: &AppContext) -> DisplayPoint {
-        self.display_map.max_point(ctx)
-    }
-
-    pub fn text(&self, ctx: &AppContext) -> String {
-        self.display_map.text(ctx)
-    }
-
-    pub fn font_size(&self) -> f32 {
-        self.settings.borrow().buffer_font_size
-    }
-
-    pub fn font_ascent(&self, font_cache: &FontCache) -> f32 {
-        let settings = self.settings.borrow();
-        let font_id = font_cache.default_font(settings.buffer_font_family);
-        let ascent = font_cache.metric(font_id, |m| m.ascent);
-        font_cache.scale_metric(ascent, font_id, settings.buffer_font_size)
-    }
-
-    pub fn font_descent(&self, font_cache: &FontCache) -> f32 {
-        let settings = self.settings.borrow();
-        let font_id = font_cache.default_font(settings.buffer_font_family);
-        let ascent = font_cache.metric(font_id, |m| m.descent);
-        font_cache.scale_metric(ascent, font_id, settings.buffer_font_size)
-    }
-
-    pub fn line_height(&self, font_cache: &FontCache) -> f32 {
-        let settings = self.settings.borrow();
-        let font_id = font_cache.default_font(settings.buffer_font_family);
-        font_cache.line_height(font_id, settings.buffer_font_size)
-    }
-
-    pub fn em_width(&self, font_cache: &FontCache) -> f32 {
-        let settings = self.settings.borrow();
-        let font_id = font_cache.default_font(settings.buffer_font_family);
-        font_cache.em_width(font_id, settings.buffer_font_size)
-    }
-
-    // TODO: Can we make this not return a result?
-    pub fn max_line_number_width(
-        &self,
-        font_cache: &FontCache,
-        layout_cache: &TextLayoutCache,
-        app: &AppContext,
-    ) -> Result<f32> {
-        let settings = self.settings.borrow();
-        let font_size = settings.buffer_font_size;
-        let font_id =
-            font_cache.select_font(settings.buffer_font_family, &FontProperties::new())?;
-        let digit_count = (self.buffer.read(app).row_count() as f32).log10().floor() as usize + 1;
-
-        Ok(layout_cache
-            .layout_str(
-                "1".repeat(digit_count).as_str(),
-                font_size,
-                &[(digit_count, font_id, ColorU::black())],
-            )
-            .width())
-    }
-
-    pub fn layout_line_numbers(
-        &self,
-        viewport_height: f32,
-        font_cache: &FontCache,
-        layout_cache: &TextLayoutCache,
-        ctx: &AppContext,
-    ) -> Result<Vec<text_layout::Line>> {
-        let settings = self.settings.borrow();
-        let font_size = settings.buffer_font_size;
-        let font_id =
-            font_cache.select_font(settings.buffer_font_family, &FontProperties::new())?;
-
-        let start_row = self.scroll_position().y() as usize;
-        let end_row = cmp::min(
-            self.max_point(ctx).row() as usize,
-            start_row + (viewport_height / self.line_height(font_cache)).ceil() as usize,
-        );
-        let line_count = end_row - start_row + 1;
-
-        let mut layouts = Vec::with_capacity(line_count);
-        let mut line_number = String::new();
-        for buffer_row in self
-            .display_map
-            .snapshot(ctx)
-            .buffer_rows(start_row as u32)
-            .take(line_count)
-        {
-            line_number.clear();
-            write!(&mut line_number, "{}", buffer_row + 1).unwrap();
-            layouts.push(layout_cache.layout_str(
-                &line_number,
-                font_size,
-                &[(line_number.len(), font_id, ColorU::black())],
-            ));
-        }
-
-        Ok(layouts)
-    }
-
-    pub fn layout_lines(
-        &self,
-        mut rows: Range<u32>,
-        font_cache: &FontCache,
-        layout_cache: &TextLayoutCache,
-        ctx: &AppContext,
-    ) -> Result<Vec<text_layout::Line>> {
-        rows.end = cmp::min(rows.end, self.display_map.max_point(ctx).row() + 1);
-        if rows.start >= rows.end {
-            return Ok(Vec::new());
-        }
-
-        let settings = self.settings.borrow();
-        let font_size = settings.buffer_font_size;
-        let font_family = settings.buffer_font_family;
-        let mut prev_font_properties = FontProperties::new();
-        let mut prev_font_id = font_cache
-            .select_font(font_family, &prev_font_properties)
-            .unwrap();
-
-        let mut layouts = Vec::with_capacity(rows.len());
-        let mut line = String::new();
-        let mut styles = Vec::new();
-        let mut row = rows.start;
-        let mut snapshot = self.display_map.snapshot(ctx);
-        let chunks = snapshot.highlighted_chunks_for_rows(rows.clone());
-        let theme = settings.theme.clone();
-
-        'outer: for (chunk, style_ix) in chunks.chain(Some(("\n", StyleId::default()))) {
-            for (ix, line_chunk) in chunk.split('\n').enumerate() {
-                if ix > 0 {
-                    layouts.push(layout_cache.layout_str(&line, font_size, &styles));
-                    line.clear();
-                    styles.clear();
-                    row += 1;
-                    if row == rows.end {
-                        break 'outer;
-                    }
-                }
-
-                if !line_chunk.is_empty() {
-                    let (color, font_properties) = theme.syntax_style(style_ix);
-                    // Avoid a lookup if the font properties match the previous ones.
-                    let font_id = if font_properties == prev_font_properties {
-                        prev_font_id
-                    } else {
-                        font_cache.select_font(font_family, &font_properties)?
-                    };
-                    line.push_str(line_chunk);
-                    styles.push((line_chunk.len(), font_id, color));
-                    prev_font_id = font_id;
-                    prev_font_properties = font_properties;
-                }
-            }
-        }
-
-        Ok(layouts)
-    }
-
-    pub fn layout_line(
-        &self,
-        row: u32,
-        font_cache: &FontCache,
-        layout_cache: &TextLayoutCache,
-        app: &AppContext,
-    ) -> Result<text_layout::Line> {
-        let settings = self.settings.borrow();
-        let font_id =
-            font_cache.select_font(settings.buffer_font_family, &FontProperties::new())?;
-
-        let line = self.line(row, app);
-
-        Ok(layout_cache.layout_str(
-            &line,
-            settings.buffer_font_size,
-            &[(self.line_len(row, app) as usize, font_id, ColorU::black())],
-        ))
-    }
-
-    fn next_blink_epoch(&mut self) -> usize {
-        self.blink_epoch += 1;
-        self.blink_epoch
-    }
-
-    fn pause_cursor_blinking(&mut self, ctx: &mut ViewContext<Self>) {
-        self.cursors_visible = true;
-        ctx.notify();
-
-        let epoch = self.next_blink_epoch();
-        ctx.spawn(|this, mut ctx| async move {
-            Timer::after(CURSOR_BLINK_INTERVAL).await;
-            this.update(&mut ctx, |this, ctx| {
-                this.resume_cursor_blinking(epoch, ctx);
-            })
-        })
-        .detach();
-    }
-
-    fn resume_cursor_blinking(&mut self, epoch: usize, ctx: &mut ViewContext<Self>) {
-        if epoch == self.blink_epoch {
-            self.blinking_paused = false;
-            self.blink_cursors(epoch, ctx);
-        }
-    }
-
-    fn blink_cursors(&mut self, epoch: usize, ctx: &mut ViewContext<Self>) {
-        if epoch == self.blink_epoch && self.focused && !self.blinking_paused {
-            self.cursors_visible = !self.cursors_visible;
-            ctx.notify();
-
-            let epoch = self.next_blink_epoch();
-            ctx.spawn(|this, mut ctx| async move {
-                Timer::after(CURSOR_BLINK_INTERVAL).await;
-                this.update(&mut ctx, |this, ctx| this.blink_cursors(epoch, ctx));
-            })
-            .detach();
-        }
-    }
-
-    pub fn cursors_visible(&self) -> bool {
-        self.cursors_visible
-    }
-
-    fn on_buffer_changed(&mut self, _: ModelHandle<Buffer>, ctx: &mut ViewContext<Self>) {
-        ctx.notify();
-    }
-
-    fn on_buffer_event(
-        &mut self,
-        _: ModelHandle<Buffer>,
-        event: &buffer::Event,
-        ctx: &mut ViewContext<Self>,
-    ) {
-        match event {
-            buffer::Event::Edited => ctx.emit(Event::Edited),
-            buffer::Event::Dirtied => ctx.emit(Event::Dirtied),
-            buffer::Event::Saved => ctx.emit(Event::Saved),
-            buffer::Event::FileHandleChanged => ctx.emit(Event::FileHandleChanged),
-            buffer::Event::Reloaded => ctx.emit(Event::FileHandleChanged),
-            buffer::Event::Reparsed => {}
-        }
-    }
-}
-
-pub enum Event {
-    Activate,
-    Edited,
-    Blurred,
-    Dirtied,
-    Saved,
-    FileHandleChanged,
-}
-
-impl Entity for BufferView {
-    type Event = Event;
-}
-
-impl View for BufferView {
-    fn render<'a>(&self, _: &AppContext) -> ElementBox {
-        BufferElement::new(self.handle.clone()).boxed()
-    }
-
-    fn ui_name() -> &'static str {
-        "BufferView"
-    }
-
-    fn on_focus(&mut self, ctx: &mut ViewContext<Self>) {
-        self.focused = true;
-        self.blink_cursors(self.blink_epoch, ctx);
-    }
-
-    fn on_blur(&mut self, ctx: &mut ViewContext<Self>) {
-        self.focused = false;
-        self.cursors_visible = false;
-        ctx.emit(Event::Blurred);
-        ctx.notify();
-    }
-}
-
-impl workspace::Item for Buffer {
-    type View = BufferView;
-
-    fn file(&self) -> Option<&FileHandle> {
-        self.file()
-    }
-
-    fn build_view(
-        handle: ModelHandle<Self>,
-        settings: watch::Receiver<Settings>,
-        ctx: &mut ViewContext<Self::View>,
-    ) -> Self::View {
-        BufferView::for_buffer(handle, settings, ctx)
-    }
-}
-
-impl workspace::ItemView for BufferView {
-    fn should_activate_item_on_event(event: &Self::Event) -> bool {
-        matches!(event, Event::Activate)
-    }
-
-    fn should_update_tab_on_event(event: &Self::Event) -> bool {
-        matches!(
-            event,
-            Event::Saved | Event::Dirtied | Event::FileHandleChanged
-        )
-    }
-
-    fn title(&self, app: &AppContext) -> std::string::String {
-        let filename = self
-            .buffer
-            .read(app)
-            .file()
-            .and_then(|file| file.file_name(app));
-        if let Some(name) = filename {
-            name.to_string_lossy().into()
-        } else {
-            "untitled".into()
-        }
-    }
-
-    fn entry_id(&self, ctx: &AppContext) -> Option<(usize, Arc<Path>)> {
-        self.buffer.read(ctx).file().map(|file| file.entry_id())
-    }
-
-    fn clone_on_split(&self, ctx: &mut ViewContext<Self>) -> Option<Self>
-    where
-        Self: Sized,
-    {
-        let clone = BufferView::for_buffer(self.buffer.clone(), self.settings.clone(), ctx);
-        *clone.scroll_position.lock() = *self.scroll_position.lock();
-        Some(clone)
-    }
-
-    fn save(
-        &mut self,
-        new_file: Option<FileHandle>,
-        ctx: &mut ViewContext<Self>,
-    ) -> Task<Result<()>> {
-        self.buffer.update(ctx, |b, ctx| b.save(new_file, ctx))
-    }
-
-    fn is_dirty(&self, ctx: &AppContext) -> bool {
-        self.buffer.read(ctx).is_dirty()
-    }
-
-    fn has_conflict(&self, ctx: &AppContext) -> bool {
-        self.buffer.read(ctx).has_conflict()
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::{
-        editor::Point,
-        settings,
-        test::{build_app_state, sample_text},
-    };
-    use buffer::History;
-    use unindent::Unindent;
-
-    #[gpui::test]
-    fn test_selection_with_mouse(app: &mut gpui::MutableAppContext) {
-        let buffer = app.add_model(|ctx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n", ctx));
-        let settings = settings::channel(&app.font_cache()).unwrap().1;
-        let (_, buffer_view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx));
-
-        buffer_view.update(app, |view, ctx| {
-            view.begin_selection(DisplayPoint::new(2, 2), false, ctx);
-        });
-
-        let view = buffer_view.read(app);
-        let selections = view
-            .selections_in_range(
-                DisplayPoint::zero()..view.max_point(app.as_ref()),
-                app.as_ref(),
-            )
-            .collect::<Vec<_>>();
-        assert_eq!(
-            selections,
-            [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
-        );
-
-        buffer_view.update(app, |view, ctx| {
-            view.update_selection(DisplayPoint::new(3, 3), Vector2F::zero(), ctx);
-        });
-
-        let view = buffer_view.read(app);
-        let selections = view
-            .selections_in_range(
-                DisplayPoint::zero()..view.max_point(app.as_ref()),
-                app.as_ref(),
-            )
-            .collect::<Vec<_>>();
-        assert_eq!(
-            selections,
-            [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
-        );
-
-        buffer_view.update(app, |view, ctx| {
-            view.update_selection(DisplayPoint::new(1, 1), Vector2F::zero(), ctx);
-        });
-
-        let view = buffer_view.read(app);
-        let selections = view
-            .selections_in_range(
-                DisplayPoint::zero()..view.max_point(app.as_ref()),
-                app.as_ref(),
-            )
-            .collect::<Vec<_>>();
-        assert_eq!(
-            selections,
-            [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
-        );
-
-        buffer_view.update(app, |view, ctx| {
-            view.end_selection(ctx);
-            view.update_selection(DisplayPoint::new(3, 3), Vector2F::zero(), ctx);
-        });
-
-        let view = buffer_view.read(app);
-        let selections = view
-            .selections_in_range(
-                DisplayPoint::zero()..view.max_point(app.as_ref()),
-                app.as_ref(),
-            )
-            .collect::<Vec<_>>();
-        assert_eq!(
-            selections,
-            [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
-        );
-
-        buffer_view.update(app, |view, ctx| {
-            view.begin_selection(DisplayPoint::new(3, 3), true, ctx);
-            view.update_selection(DisplayPoint::new(0, 0), Vector2F::zero(), ctx);
-        });
-
-        let view = buffer_view.read(app);
-        let selections = view
-            .selections_in_range(
-                DisplayPoint::zero()..view.max_point(app.as_ref()),
-                app.as_ref(),
-            )
-            .collect::<Vec<_>>();
-        assert_eq!(
-            selections,
-            [
-                DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
-                DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
-            ]
-        );
-
-        buffer_view.update(app, |view, ctx| {
-            view.end_selection(ctx);
-        });
-
-        let view = buffer_view.read(app);
-        let selections = view
-            .selections_in_range(
-                DisplayPoint::zero()..view.max_point(app.as_ref()),
-                app.as_ref(),
-            )
-            .collect::<Vec<_>>();
-        assert_eq!(
-            selections,
-            [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
-        );
-    }
-
-    #[gpui::test]
-    fn test_canceling_pending_selection(app: &mut gpui::MutableAppContext) {
-        let buffer = app.add_model(|ctx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n", ctx));
-        let settings = settings::channel(&app.font_cache()).unwrap().1;
-        let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx));
-
-        view.update(app, |view, ctx| {
-            view.begin_selection(DisplayPoint::new(2, 2), false, ctx);
-        });
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
-        );
-
-        view.update(app, |view, ctx| {
-            view.update_selection(DisplayPoint::new(3, 3), Vector2F::zero(), ctx);
-        });
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
-        );
-
-        view.update(app, |view, ctx| {
-            view.cancel(&(), ctx);
-            view.update_selection(DisplayPoint::new(1, 1), Vector2F::zero(), ctx);
-        });
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
-        );
-    }
-
-    #[gpui::test]
-    fn test_cancel(app: &mut gpui::MutableAppContext) {
-        let buffer = app.add_model(|ctx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n", ctx));
-        let settings = settings::channel(&app.font_cache()).unwrap().1;
-        let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx));
-
-        view.update(app, |view, ctx| {
-            view.begin_selection(DisplayPoint::new(3, 4), false, ctx);
-            view.update_selection(DisplayPoint::new(1, 1), Vector2F::zero(), ctx);
-            view.end_selection(ctx);
-
-            view.begin_selection(DisplayPoint::new(0, 1), true, ctx);
-            view.update_selection(DisplayPoint::new(0, 3), Vector2F::zero(), ctx);
-            view.end_selection(ctx);
-        });
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            [
-                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
-                DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
-            ]
-        );
-
-        view.update(app, |view, ctx| view.cancel(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
-        );
-
-        view.update(app, |view, ctx| view.cancel(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
-        );
-    }
-
-    #[gpui::test]
-    fn test_layout_line_numbers(app: &mut gpui::MutableAppContext) {
-        let layout_cache = TextLayoutCache::new(app.platform().fonts());
-        let font_cache = app.font_cache().clone();
-
-        let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(6, 6), ctx));
-
-        let settings = settings::channel(&font_cache).unwrap().1;
-        let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx));
-
-        let layouts = view
-            .read(app)
-            .layout_line_numbers(1000.0, &font_cache, &layout_cache, app.as_ref())
-            .unwrap();
-        assert_eq!(layouts.len(), 6);
-    }
-
-    #[gpui::test]
-    fn test_fold(app: &mut gpui::MutableAppContext) {
-        let buffer = app.add_model(|ctx| {
-            Buffer::new(
-                0,
-                "
-                    impl Foo {
-                        // Hello!
-
-                        fn a() {
-                            1
-                        }
-
-                        fn b() {
-                            2
-                        }
-
-                        fn c() {
-                            3
-                        }
-                    }
-                "
-                .unindent(),
-                ctx,
-            )
-        });
-        let settings = settings::channel(&app.font_cache()).unwrap().1;
-        let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx));
-
-        view.update(app, |view, ctx| {
-            view.select_display_ranges(&[DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)], ctx)
-                .unwrap();
-            view.fold(&(), ctx);
-            assert_eq!(
-                view.text(ctx.as_ref()),
-                "
-                    impl Foo {
-                        // Hello!
-
-                        fn a() {
-                            1
-                        }
-
-                        fn b() {…
-                        }
-
-                        fn c() {…
-                        }
-                    }
-                "
-                .unindent(),
-            );
-
-            view.fold(&(), ctx);
-            assert_eq!(
-                view.text(ctx.as_ref()),
-                "
-                    impl Foo {…
-                    }
-                "
-                .unindent(),
-            );
-
-            view.unfold(&(), ctx);
-            assert_eq!(
-                view.text(ctx.as_ref()),
-                "
-                    impl Foo {
-                        // Hello!
-
-                        fn a() {
-                            1
-                        }
-
-                        fn b() {…
-                        }
-
-                        fn c() {…
-                        }
-                    }
-                "
-                .unindent(),
-            );
-
-            view.unfold(&(), ctx);
-            assert_eq!(view.text(ctx.as_ref()), buffer.read(ctx).text());
-        });
-    }
-
-    #[gpui::test]
-    fn test_move_cursor(app: &mut gpui::MutableAppContext) {
-        let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(6, 6), ctx));
-        let settings = settings::channel(&app.font_cache()).unwrap().1;
-        let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx));
-
-        buffer.update(app, |buffer, ctx| {
-            buffer
-                .edit(
-                    vec![
-                        Point::new(1, 0)..Point::new(1, 0),
-                        Point::new(1, 1)..Point::new(1, 1),
-                    ],
-                    "\t",
-                    Some(ctx),
-                )
-                .unwrap();
-        });
-
-        view.update(app, |view, ctx| {
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
-            );
-
-            view.move_down(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
-            );
-
-            view.move_right(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
-            );
-
-            view.move_left(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
-            );
-
-            view.move_up(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
-            );
-
-            view.move_to_end(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
-            );
-
-            view.move_to_beginning(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
-            );
-
-            view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)], ctx)
-                .unwrap();
-            view.select_to_beginning(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
-            );
-
-            view.select_to_end(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
-            );
-        });
-    }
-
-    #[gpui::test]
-    fn test_move_cursor_multibyte(app: &mut gpui::MutableAppContext) {
-        let buffer = app.add_model(|ctx| Buffer::new(0, "ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", ctx));
-        let settings = settings::channel(&app.font_cache()).unwrap().1;
-        let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx));
-
-        assert_eq!('ⓐ'.len_utf8(), 3);
-        assert_eq!('α'.len_utf8(), 2);
-
-        view.update(app, |view, ctx| {
-            view.fold_ranges(
-                vec![
-                    Point::new(0, 6)..Point::new(0, 12),
-                    Point::new(1, 2)..Point::new(1, 4),
-                    Point::new(2, 4)..Point::new(2, 8),
-                ],
-                ctx,
-            );
-            assert_eq!(view.text(ctx.as_ref()), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\n");
-
-            view.move_right(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[empty_range(0, "ⓐ".len())]
-            );
-            view.move_right(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[empty_range(0, "ⓐⓑ".len())]
-            );
-            view.move_right(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[empty_range(0, "ⓐⓑ…".len())]
-            );
-
-            view.move_down(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[empty_range(1, "ab…".len())]
-            );
-            view.move_left(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[empty_range(1, "ab".len())]
-            );
-            view.move_left(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[empty_range(1, "a".len())]
-            );
-
-            view.move_down(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[empty_range(2, "α".len())]
-            );
-            view.move_right(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[empty_range(2, "αβ".len())]
-            );
-            view.move_right(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[empty_range(2, "αβ…".len())]
-            );
-            view.move_right(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[empty_range(2, "αβ…ε".len())]
-            );
-
-            view.move_up(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[empty_range(1, "ab…e".len())]
-            );
-            view.move_up(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[empty_range(0, "ⓐⓑ…ⓔ".len())]
-            );
-            view.move_left(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[empty_range(0, "ⓐⓑ…".len())]
-            );
-            view.move_left(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[empty_range(0, "ⓐⓑ".len())]
-            );
-            view.move_left(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[empty_range(0, "ⓐ".len())]
-            );
-        });
-    }
-
-    #[gpui::test]
-    fn test_move_cursor_different_line_lengths(app: &mut gpui::MutableAppContext) {
-        let buffer = app.add_model(|ctx| Buffer::new(0, "ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", ctx));
-        let settings = settings::channel(&app.font_cache()).unwrap().1;
-        let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx));
-        view.update(app, |view, ctx| {
-            view.select_display_ranges(&[empty_range(0, "ⓐⓑⓒⓓⓔ".len())], ctx)
-                .unwrap();
-
-            view.move_down(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[empty_range(1, "abcd".len())]
-            );
-
-            view.move_down(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[empty_range(2, "αβγ".len())]
-            );
-
-            view.move_down(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[empty_range(3, "abcd".len())]
-            );
-
-            view.move_down(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
-            );
-
-            view.move_up(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[empty_range(3, "abcd".len())]
-            );
-
-            view.move_up(&(), ctx);
-            assert_eq!(
-                view.selection_ranges(ctx.as_ref()),
-                &[empty_range(2, "αβγ".len())]
-            );
-        });
-    }
-
-    #[gpui::test]
-    fn test_beginning_end_of_line(app: &mut gpui::MutableAppContext) {
-        let buffer = app.add_model(|ctx| Buffer::new(0, "abc\n  def", ctx));
-        let settings = settings::channel(&app.font_cache()).unwrap().1;
-        let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx));
-        view.update(app, |view, ctx| {
-            view.select_display_ranges(
-                &[
-                    DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
-                    DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
-                ],
-                ctx,
-            )
-            .unwrap();
-        });
-
-        view.update(app, |view, ctx| view.move_to_beginning_of_line(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
-                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
-            ]
-        );
-
-        view.update(app, |view, ctx| view.move_to_beginning_of_line(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
-                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
-            ]
-        );
-
-        view.update(app, |view, ctx| view.move_to_beginning_of_line(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
-                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
-            ]
-        );
-
-        view.update(app, |view, ctx| view.move_to_end_of_line(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
-                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
-            ]
-        );
-
-        // Moving to the end of line again is a no-op.
-        view.update(app, |view, ctx| view.move_to_end_of_line(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
-                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
-            ]
-        );
-
-        view.update(app, |view, ctx| {
-            view.move_left(&(), ctx);
-            view.select_to_beginning_of_line(&true, ctx);
-        });
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
-                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
-            ]
-        );
-
-        view.update(app, |view, ctx| {
-            view.select_to_beginning_of_line(&true, ctx)
-        });
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
-                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
-            ]
-        );
-
-        view.update(app, |view, ctx| {
-            view.select_to_beginning_of_line(&true, ctx)
-        });
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
-                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
-            ]
-        );
-
-        view.update(app, |view, ctx| view.select_to_end_of_line(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
-                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
-            ]
-        );
-
-        view.update(app, |view, ctx| view.delete_to_end_of_line(&(), ctx));
-        assert_eq!(view.read(app).text(app.as_ref()), "ab\n  de");
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
-                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
-            ]
-        );
-
-        view.update(app, |view, ctx| view.delete_to_beginning_of_line(&(), ctx));
-        assert_eq!(view.read(app).text(app.as_ref()), "\n");
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
-                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
-            ]
-        );
-    }
-
-    #[gpui::test]
-    fn test_prev_next_word_boundary(app: &mut gpui::MutableAppContext) {
-        let buffer =
-            app.add_model(|ctx| Buffer::new(0, "use std::str::{foo, bar}\n\n  {baz.qux()}", ctx));
-        let settings = settings::channel(&app.font_cache()).unwrap().1;
-        let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx));
-        view.update(app, |view, ctx| {
-            view.select_display_ranges(
-                &[
-                    DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
-                    DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
-                ],
-                ctx,
-            )
-            .unwrap();
-        });
-
-        view.update(app, |view, ctx| {
-            view.move_to_previous_word_boundary(&(), ctx)
-        });
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 9)..DisplayPoint::new(0, 9),
-                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 3),
-            ]
-        );
-
-        view.update(app, |view, ctx| {
-            view.move_to_previous_word_boundary(&(), ctx)
-        });
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 7)..DisplayPoint::new(0, 7),
-                DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2),
-            ]
-        );
-
-        view.update(app, |view, ctx| {
-            view.move_to_previous_word_boundary(&(), ctx)
-        });
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 4)..DisplayPoint::new(0, 4),
-                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
-            ]
-        );
-
-        view.update(app, |view, ctx| {
-            view.move_to_previous_word_boundary(&(), ctx)
-        });
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
-                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
-            ]
-        );
-
-        view.update(app, |view, ctx| {
-            view.move_to_previous_word_boundary(&(), ctx)
-        });
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
-                DisplayPoint::new(0, 24)..DisplayPoint::new(0, 24),
-            ]
-        );
-
-        view.update(app, |view, ctx| {
-            view.move_to_previous_word_boundary(&(), ctx)
-        });
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
-                DisplayPoint::new(0, 23)..DisplayPoint::new(0, 23),
-            ]
-        );
-
-        view.update(app, |view, ctx| view.move_to_next_word_boundary(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
-                DisplayPoint::new(0, 24)..DisplayPoint::new(0, 24),
-            ]
-        );
-
-        view.update(app, |view, ctx| view.move_to_next_word_boundary(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 4)..DisplayPoint::new(0, 4),
-                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
-            ]
-        );
-
-        view.update(app, |view, ctx| view.move_to_next_word_boundary(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 7)..DisplayPoint::new(0, 7),
-                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
-            ]
-        );
-
-        view.update(app, |view, ctx| view.move_to_next_word_boundary(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 9)..DisplayPoint::new(0, 9),
-                DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2),
-            ]
-        );
-
-        view.update(app, |view, ctx| {
-            view.move_right(&(), ctx);
-            view.select_to_previous_word_boundary(&(), ctx);
-        });
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 10)..DisplayPoint::new(0, 9),
-                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 2),
-            ]
-        );
-
-        view.update(app, |view, ctx| {
-            view.select_to_previous_word_boundary(&(), ctx)
-        });
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 10)..DisplayPoint::new(0, 7),
-                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 0),
-            ]
-        );
-
-        view.update(app, |view, ctx| view.select_to_next_word_boundary(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 10)..DisplayPoint::new(0, 9),
-                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 2),
-            ]
-        );
-
-        view.update(app, |view, ctx| view.delete_to_next_word_boundary(&(), ctx));
-        assert_eq!(
-            view.read(app).text(app.as_ref()),
-            "use std::s::{foo, bar}\n\n  {az.qux()}"
-        );
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 10)..DisplayPoint::new(0, 10),
-                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 3),
-            ]
-        );
-
-        view.update(app, |view, ctx| {
-            view.delete_to_previous_word_boundary(&(), ctx)
-        });
-        assert_eq!(
-            view.read(app).text(app.as_ref()),
-            "use std::::{foo, bar}\n\n  az.qux()}"
-        );
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 9)..DisplayPoint::new(0, 9),
-                DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2),
-            ]
-        );
-    }
-
-    #[gpui::test]
-    fn test_backspace(app: &mut gpui::MutableAppContext) {
-        let buffer = app.add_model(|ctx| {
-            Buffer::new(
-                0,
-                "one two three\nfour five six\nseven eight nine\nten\n",
-                ctx,
-            )
-        });
-        let settings = settings::channel(&app.font_cache()).unwrap().1;
-        let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx));
-
-        view.update(app, |view, ctx| {
-            view.select_display_ranges(
-                &[
-                    // an empty selection - the preceding character is deleted
-                    DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
-                    // one character selected - it is deleted
-                    DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
-                    // a line suffix selected - it is deleted
-                    DisplayPoint::new(2, 6)..DisplayPoint::new(3, 0),
-                ],
-                ctx,
-            )
-            .unwrap();
-            view.backspace(&(), ctx);
-        });
-
-        assert_eq!(
-            buffer.read(app).text(),
-            "oe two three\nfou five six\nseven ten\n"
-        );
-    }
-
-    #[gpui::test]
-    fn test_delete(app: &mut gpui::MutableAppContext) {
-        let buffer = app.add_model(|ctx| {
-            Buffer::new(
-                0,
-                "one two three\nfour five six\nseven eight nine\nten\n",
-                ctx,
-            )
-        });
-        let settings = settings::channel(&app.font_cache()).unwrap().1;
-        let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx));
-
-        view.update(app, |view, ctx| {
-            view.select_display_ranges(
-                &[
-                    // an empty selection - the following character is deleted
-                    DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
-                    // one character selected - it is deleted
-                    DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
-                    // a line suffix selected - it is deleted
-                    DisplayPoint::new(2, 6)..DisplayPoint::new(3, 0),
-                ],
-                ctx,
-            )
-            .unwrap();
-            view.delete(&(), ctx);
-        });
-
-        assert_eq!(
-            buffer.read(app).text(),
-            "on two three\nfou five six\nseven ten\n"
-        );
-    }
-
-    #[gpui::test]
-    fn test_delete_line(app: &mut gpui::MutableAppContext) {
-        let settings = settings::channel(&app.font_cache()).unwrap().1;
-        let buffer = app.add_model(|ctx| Buffer::new(0, "abc\ndef\nghi\n", ctx));
-        let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx));
-        view.update(app, |view, ctx| {
-            view.select_display_ranges(
-                &[
-                    DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
-                    DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
-                    DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
-                ],
-                ctx,
-            )
-            .unwrap();
-            view.delete_line(&(), ctx);
-        });
-        assert_eq!(view.read(app).text(app.as_ref()), "ghi");
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            vec![
-                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
-                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
-            ]
-        );
-
-        let settings = settings::channel(&app.font_cache()).unwrap().1;
-        let buffer = app.add_model(|ctx| Buffer::new(0, "abc\ndef\nghi\n", ctx));
-        let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx));
-        view.update(app, |view, ctx| {
-            view.select_display_ranges(&[DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)], ctx)
-                .unwrap();
-            view.delete_line(&(), ctx);
-        });
-        assert_eq!(view.read(app).text(app.as_ref()), "ghi\n");
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
-        );
-    }
-
-    #[gpui::test]
-    fn test_duplicate_line(app: &mut gpui::MutableAppContext) {
-        let settings = settings::channel(&app.font_cache()).unwrap().1;
-        let buffer = app.add_model(|ctx| Buffer::new(0, "abc\ndef\nghi\n", ctx));
-        let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx));
-        view.update(app, |view, ctx| {
-            view.select_display_ranges(
-                &[
-                    DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
-                    DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
-                    DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
-                    DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
-                ],
-                ctx,
-            )
-            .unwrap();
-            view.duplicate_line(&(), ctx);
-        });
-        assert_eq!(
-            view.read(app).text(app.as_ref()),
-            "abc\nabc\ndef\ndef\nghi\n\n"
-        );
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            vec![
-                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
-                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
-                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
-                DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
-            ]
-        );
-
-        let settings = settings::channel(&app.font_cache()).unwrap().1;
-        let buffer = app.add_model(|ctx| Buffer::new(0, "abc\ndef\nghi\n", ctx));
-        let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx));
-        view.update(app, |view, ctx| {
-            view.select_display_ranges(
-                &[
-                    DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
-                    DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
-                ],
-                ctx,
-            )
-            .unwrap();
-            view.duplicate_line(&(), ctx);
-        });
-        assert_eq!(
-            view.read(app).text(app.as_ref()),
-            "abc\ndef\nghi\nabc\ndef\nghi\n"
-        );
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            vec![
-                DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
-                DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
-            ]
-        );
-    }
-
-    #[gpui::test]
-    fn test_move_line_up_down(app: &mut gpui::MutableAppContext) {
-        let settings = settings::channel(&app.font_cache()).unwrap().1;
-        let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(10, 5), ctx));
-        let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx));
-        view.update(app, |view, ctx| {
-            view.fold_ranges(
-                vec![
-                    Point::new(0, 2)..Point::new(1, 2),
-                    Point::new(2, 3)..Point::new(4, 1),
-                    Point::new(7, 0)..Point::new(8, 4),
-                ],
-                ctx,
-            );
-            view.select_display_ranges(
-                &[
-                    DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
-                    DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
-                    DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
-                    DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
-                ],
-                ctx,
-            )
-            .unwrap();
-        });
-        assert_eq!(
-            view.read(app).text(app.as_ref()),
-            "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
-        );
-
-        view.update(app, |view, ctx| view.move_line_up(&(), ctx));
-        assert_eq!(
-            view.read(app).text(app.as_ref()),
-            "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
-        );
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            vec![
-                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
-                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
-                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
-                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
-            ]
-        );
-
-        view.update(app, |view, ctx| view.move_line_down(&(), ctx));
-        assert_eq!(
-            view.read(app).text(app.as_ref()),
-            "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
-        );
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            vec![
-                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
-                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
-                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
-                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
-            ]
-        );
-
-        view.update(app, |view, ctx| view.move_line_down(&(), ctx));
-        assert_eq!(
-            view.read(app).text(app.as_ref()),
-            "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
-        );
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            vec![
-                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
-                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
-                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
-                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
-            ]
-        );
-
-        view.update(app, |view, ctx| view.move_line_up(&(), ctx));
-        assert_eq!(
-            view.read(app).text(app.as_ref()),
-            "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
-        );
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            vec![
-                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
-                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
-                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
-                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
-            ]
-        );
-    }
-
-    #[gpui::test]
-    fn test_clipboard(app: &mut gpui::MutableAppContext) {
-        let buffer = app.add_model(|ctx| Buffer::new(0, "one two three four five six ", ctx));
-        let settings = settings::channel(&app.font_cache()).unwrap().1;
-        let view = app
-            .add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx))
-            .1;
-
-        // Cut with three selections. Clipboard text is divided into three slices.
-        view.update(app, |view, ctx| {
-            view.select_ranges(vec![0..4, 8..14, 19..24], false, ctx);
-            view.cut(&(), ctx);
-        });
-        assert_eq!(view.read(app).text(app.as_ref()), "two four six ");
-
-        // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
-        view.update(app, |view, ctx| {
-            view.select_ranges(vec![4..4, 9..9, 13..13], false, ctx);
-            view.paste(&(), ctx);
-        });
-        assert_eq!(
-            view.read(app).text(app.as_ref()),
-            "two one four three six five "
-        );
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 8)..DisplayPoint::new(0, 8),
-                DisplayPoint::new(0, 19)..DisplayPoint::new(0, 19),
-                DisplayPoint::new(0, 28)..DisplayPoint::new(0, 28)
-            ]
-        );
-
-        // Paste again but with only two cursors. Since the number of cursors doesn't
-        // match the number of slices in the clipboard, the entire clipboard text
-        // is pasted at each cursor.
-        view.update(app, |view, ctx| {
-            view.select_ranges(vec![0..0, 28..28], false, ctx);
-            view.insert(&"( ".to_string(), ctx);
-            view.paste(&(), ctx);
-            view.insert(&") ".to_string(), ctx);
-        });
-        assert_eq!(
-            view.read(app).text(app.as_ref()),
-            "( one three five ) two one four three six five ( one three five ) "
-        );
-
-        view.update(app, |view, ctx| {
-            view.select_ranges(vec![0..0], false, ctx);
-            view.insert(&"123\n4567\n89\n".to_string(), ctx);
-        });
-        assert_eq!(
-            view.read(app).text(app.as_ref()),
-            "123\n4567\n89\n( one three five ) two one four three six five ( one three five ) "
-        );
-
-        // Cut with three selections, one of which is full-line.
-        view.update(app, |view, ctx| {
-            view.select_display_ranges(
-                &[
-                    DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2),
-                    DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
-                    DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
-                ],
-                ctx,
-            )
-            .unwrap();
-            view.cut(&(), ctx);
-        });
-        assert_eq!(
-            view.read(app).text(app.as_ref()),
-            "13\n9\n( one three five ) two one four three six five ( one three five ) "
-        );
-
-        // Paste with three selections, noticing how the copied selection that was full-line
-        // gets inserted before the second cursor.
-        view.update(app, |view, ctx| {
-            view.select_display_ranges(
-                &[
-                    DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
-                    DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
-                    DisplayPoint::new(2, 2)..DisplayPoint::new(2, 3),
-                ],
-                ctx,
-            )
-            .unwrap();
-            view.paste(&(), ctx);
-        });
-        assert_eq!(
-            view.read(app).text(app.as_ref()),
-            "123\n4567\n9\n( 8ne three five ) two one four three six five ( one three five ) "
-        );
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
-                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
-                DisplayPoint::new(3, 3)..DisplayPoint::new(3, 3),
-            ]
-        );
-
-        // Copy with a single cursor only, which writes the whole line into the clipboard.
-        view.update(app, |view, ctx| {
-            view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)], ctx)
-                .unwrap();
-            view.copy(&(), ctx);
-        });
-
-        // Paste with three selections, noticing how the copied full-line selection is inserted
-        // before the empty selections but replaces the selection that is non-empty.
-        view.update(app, |view, ctx| {
-            view.select_display_ranges(
-                &[
-                    DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
-                    DisplayPoint::new(1, 0)..DisplayPoint::new(1, 2),
-                    DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
-                ],
-                ctx,
-            )
-            .unwrap();
-            view.paste(&(), ctx);
-        });
-        assert_eq!(
-                view.read(app).text(app.as_ref()),
-                "123\n123\n123\n67\n123\n9\n( 8ne three five ) two one four three six five ( one three five ) "
-            );
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[
-                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
-                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
-                DisplayPoint::new(5, 1)..DisplayPoint::new(5, 1),
-            ]
-        );
-    }
-
-    #[gpui::test]
-    fn test_select_all(app: &mut gpui::MutableAppContext) {
-        let buffer = app.add_model(|ctx| Buffer::new(0, "abc\nde\nfgh", ctx));
-        let settings = settings::channel(&app.font_cache()).unwrap().1;
-        let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx));
-        view.update(app, |b, ctx| b.select_all(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
-        );
-    }
-
-    #[gpui::test]
-    fn test_select_line(app: &mut gpui::MutableAppContext) {
-        let settings = settings::channel(&app.font_cache()).unwrap().1;
-        let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(6, 5), ctx));
-        let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx));
-        view.update(app, |view, ctx| {
-            view.select_display_ranges(
-                &[
-                    DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
-                    DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
-                    DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
-                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
-                ],
-                ctx,
-            )
-            .unwrap();
-            view.select_line(&(), ctx);
-        });
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            vec![
-                DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
-                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
-            ]
-        );
-
-        view.update(app, |view, ctx| view.select_line(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            vec![
-                DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
-                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
-            ]
-        );
-
-        view.update(app, |view, ctx| view.select_line(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
-        );
-    }
-
-    #[gpui::test]
-    fn test_split_selection_into_lines(app: &mut gpui::MutableAppContext) {
-        let settings = settings::channel(&app.font_cache()).unwrap().1;
-        let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(9, 5), ctx));
-        let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx));
-        view.update(app, |view, ctx| {
-            view.fold_ranges(
-                vec![
-                    Point::new(0, 2)..Point::new(1, 2),
-                    Point::new(2, 3)..Point::new(4, 1),
-                    Point::new(7, 0)..Point::new(8, 4),
-                ],
-                ctx,
-            );
-            view.select_display_ranges(
-                &[
-                    DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
-                    DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
-                    DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
-                    DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
-                ],
-                ctx,
-            )
-            .unwrap();
-        });
-        assert_eq!(
-            view.read(app).text(app.as_ref()),
-            "aa…bbb\nccc…eeee\nfffff\nggggg\n…i"
-        );
-
-        view.update(app, |view, ctx| view.split_selection_into_lines(&(), ctx));
-        assert_eq!(
-            view.read(app).text(app.as_ref()),
-            "aa…bbb\nccc…eeee\nfffff\nggggg\n…i"
-        );
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            [
-                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
-                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
-                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
-                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4)
-            ]
-        );
-
-        view.update(app, |view, ctx| {
-            view.select_display_ranges(&[DisplayPoint::new(4, 0)..DisplayPoint::new(0, 1)], ctx)
-                .unwrap();
-            view.split_selection_into_lines(&(), ctx);
-        });
-        assert_eq!(
-            view.read(app).text(app.as_ref()),
-            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\n…i"
-        );
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            [
-                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
-                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
-                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
-                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
-                DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
-                DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
-                DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
-                DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
-            ]
-        );
-    }
-
-    #[gpui::test]
-    fn test_add_selection_above_below(app: &mut gpui::MutableAppContext) {
-        let settings = settings::channel(&app.font_cache()).unwrap().1;
-        let buffer = app.add_model(|ctx| Buffer::new(0, "abc\ndefghi\n\njk\nlmno\n", ctx));
-        let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx));
-
-        view.update(app, |view, ctx| {
-            view.select_display_ranges(&[DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)], ctx)
-                .unwrap();
-        });
-        view.update(app, |view, ctx| view.add_selection_above(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            vec![
-                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
-                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
-            ]
-        );
-
-        view.update(app, |view, ctx| view.add_selection_above(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            vec![
-                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
-                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
-            ]
-        );
-
-        view.update(app, |view, ctx| view.add_selection_below(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
-        );
-
-        view.update(app, |view, ctx| view.add_selection_below(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            vec![
-                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
-                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
-            ]
-        );
-
-        view.update(app, |view, ctx| view.add_selection_below(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            vec![
-                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
-                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
-            ]
-        );
-
-        view.update(app, |view, ctx| {
-            view.select_display_ranges(&[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)], ctx)
-                .unwrap();
-        });
-        view.update(app, |view, ctx| view.add_selection_below(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            vec![
-                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
-                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
-            ]
-        );
-
-        view.update(app, |view, ctx| view.add_selection_below(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            vec![
-                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
-                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
-            ]
-        );
-
-        view.update(app, |view, ctx| view.add_selection_above(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
-        );
-
-        view.update(app, |view, ctx| view.add_selection_above(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
-        );
-
-        view.update(app, |view, ctx| {
-            view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)], ctx)
-                .unwrap();
-        });
-        view.update(app, |view, ctx| view.add_selection_below(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            vec![
-                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
-                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
-                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
-            ]
-        );
-
-        view.update(app, |view, ctx| view.add_selection_below(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            vec![
-                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
-                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
-                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
-                DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
-            ]
-        );
-
-        view.update(app, |view, ctx| view.add_selection_above(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            vec![
-                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
-                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
-                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
-            ]
-        );
-
-        view.update(app, |view, ctx| {
-            view.select_display_ranges(&[DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)], ctx)
-                .unwrap();
-        });
-        view.update(app, |view, ctx| view.add_selection_above(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            vec![
-                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
-                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
-                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
-                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
-            ]
-        );
-
-        view.update(app, |view, ctx| view.add_selection_below(&(), ctx));
-        assert_eq!(
-            view.read(app).selection_ranges(app.as_ref()),
-            vec![
-                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
-                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
-                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
-            ]
-        );
-    }
-
-    #[gpui::test]
-    async fn test_select_larger_smaller_syntax_node(mut app: gpui::TestAppContext) {
-        let app_state = app.read(build_app_state);
-        let lang = app_state.language_registry.select_language("z.rs");
-        let text = r#"
-            use mod1::mod2::{mod3, mod4};
-
-            fn fn_1(param1: bool, param2: &str) {
-                let var1 = "text";
-            }
-        "#
-        .unindent();
-        let buffer = app.add_model(|ctx| {
-            let history = History::new(text.into());
-            Buffer::from_history(0, history, None, lang.cloned(), ctx)
-        });
-        let (_, view) =
-            app.add_window(|ctx| BufferView::for_buffer(buffer, app_state.settings, ctx));
-        view.condition(&app, |view, ctx| !view.buffer.read(ctx).is_parsing())
-            .await;
-
-        view.update(&mut app, |view, ctx| {
-            view.select_display_ranges(
-                &[
-                    DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
-                    DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
-                    DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
-                ],
-                ctx,
-            )
-            .unwrap();
-            view.select_larger_syntax_node(&(), ctx);
-        });
-        assert_eq!(
-            view.read_with(&app, |view, ctx| view.selection_ranges(ctx)),
-            &[
-                DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
-                DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
-                DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
-            ]
-        );
-
-        view.update(&mut app, |view, ctx| {
-            view.select_larger_syntax_node(&(), ctx);
-        });
-        assert_eq!(
-            view.read_with(&app, |view, ctx| view.selection_ranges(ctx)),
-            &[
-                DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
-                DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
-            ]
-        );
-
-        view.update(&mut app, |view, ctx| {
-            view.select_larger_syntax_node(&(), ctx);
-        });
-        assert_eq!(
-            view.read_with(&app, |view, ctx| view.selection_ranges(ctx)),
-            &[DisplayPoint::new(0, 0)..DisplayPoint::new(5, 0)]
-        );
-
-        // Trying to expand the selected syntax node one more time has no effect.
-        view.update(&mut app, |view, ctx| {
-            view.select_larger_syntax_node(&(), ctx);
-        });
-        assert_eq!(
-            view.read_with(&app, |view, ctx| view.selection_ranges(ctx)),
-            &[DisplayPoint::new(0, 0)..DisplayPoint::new(5, 0)]
-        );
-
-        view.update(&mut app, |view, ctx| {
-            view.select_smaller_syntax_node(&(), ctx);
-        });
-        assert_eq!(
-            view.read_with(&app, |view, ctx| view.selection_ranges(ctx)),
-            &[
-                DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
-                DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
-            ]
-        );
-
-        view.update(&mut app, |view, ctx| {
-            view.select_smaller_syntax_node(&(), ctx);
-        });
-        assert_eq!(
-            view.read_with(&app, |view, ctx| view.selection_ranges(ctx)),
-            &[
-                DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
-                DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
-                DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
-            ]
-        );
-
-        view.update(&mut app, |view, ctx| {
-            view.select_smaller_syntax_node(&(), ctx);
-        });
-        assert_eq!(
-            view.read_with(&app, |view, ctx| view.selection_ranges(ctx)),
-            &[
-                DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
-                DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
-                DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
-            ]
-        );
-
-        // Trying to shrink the selected syntax node one more time has no effect.
-        view.update(&mut app, |view, ctx| {
-            view.select_smaller_syntax_node(&(), ctx);
-        });
-        assert_eq!(
-            view.read_with(&app, |view, ctx| view.selection_ranges(ctx)),
-            &[
-                DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
-                DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
-                DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
-            ]
-        );
-
-        // Ensure that we keep expanding the selection if the larger selection starts or ends within
-        // a fold.
-        view.update(&mut app, |view, ctx| {
-            view.fold_ranges(
-                vec![
-                    Point::new(0, 21)..Point::new(0, 24),
-                    Point::new(3, 20)..Point::new(3, 22),
-                ],
-                ctx,
-            );
-            view.select_larger_syntax_node(&(), ctx);
-        });
-        assert_eq!(
-            view.read_with(&app, |view, ctx| view.selection_ranges(ctx)),
-            &[
-                DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
-                DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
-                DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
-            ]
-        );
-    }
-
-    impl BufferView {
-        fn selection_ranges(&self, app: &AppContext) -> Vec<Range<DisplayPoint>> {
-            self.selections_in_range(DisplayPoint::zero()..self.max_point(app), app)
-                .collect::<Vec<_>>()
-        }
-    }
-
-    fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
-        let point = DisplayPoint::new(row as u32, column as u32);
-        point..point
-    }
-}

zed/src/editor/buffer_element.rs → zed/src/editor/element.rs 🔗

@@ -1,4 +1,4 @@
-use super::{BufferView, DisplayPoint, SelectAction};
+use super::{DisplayPoint, Editor, SelectAction};
 use gpui::{
     color::{ColorF, ColorU},
     geometry::{
@@ -16,16 +16,16 @@ use smallvec::SmallVec;
 use std::cmp::Ordering;
 use std::cmp::{self};
 
-pub struct BufferElement {
-    view: WeakViewHandle<BufferView>,
+pub struct EditorElement {
+    view: WeakViewHandle<Editor>,
 }
 
-impl BufferElement {
-    pub fn new(view: WeakViewHandle<BufferView>) -> Self {
+impl EditorElement {
+    pub fn new(view: WeakViewHandle<Editor>) -> Self {
         Self { view }
     }
 
-    fn view<'a>(&self, ctx: &'a AppContext) -> &'a BufferView {
+    fn view<'a>(&self, ctx: &'a AppContext) -> &'a Editor {
         self.view.upgrade(ctx).unwrap().read(ctx)
     }
 
@@ -302,7 +302,7 @@ impl BufferElement {
     }
 }
 
-impl Element for BufferElement {
+impl Element for EditorElement {
     type LayoutState = Option<LayoutState>;
     type PaintState = Option<PaintState>;
 
@@ -510,7 +510,7 @@ pub struct LayoutState {
 impl LayoutState {
     fn scroll_width(
         &self,
-        view: &BufferView,
+        view: &Editor,
         font_cache: &FontCache,
         layout_cache: &TextLayoutCache,
         app: &AppContext,
@@ -525,7 +525,7 @@ impl LayoutState {
 
     fn scroll_max(
         &self,
-        view: &BufferView,
+        view: &Editor,
         font_cache: &FontCache,
         layout_cache: &TextLayoutCache,
         app: &AppContext,
@@ -547,7 +547,7 @@ pub struct PaintState {
 impl PaintState {
     fn point_for_position(
         &self,
-        view: &BufferView,
+        view: &Editor,
         layout: &LayoutState,
         position: Vector2F,
         font_cache: &FontCache,

zed/src/file_finder.rs 🔗

@@ -1,5 +1,5 @@
 use crate::{
-    editor::{buffer_view, BufferView},
+    editor::{self, Editor},
     settings::Settings,
     util,
     workspace::Workspace,
@@ -28,7 +28,7 @@ pub struct FileFinder {
     handle: WeakViewHandle<Self>,
     settings: watch::Receiver<Settings>,
     workspace: WeakViewHandle<Workspace>,
-    query_buffer: ViewHandle<BufferView>,
+    query_buffer: ViewHandle<Editor>,
     search_count: usize,
     latest_search_id: usize,
     latest_search_did_cancel: bool,
@@ -290,8 +290,8 @@ impl FileFinder {
     ) -> Self {
         ctx.observe_view(&workspace, Self::workspace_updated);
 
-        let query_buffer = ctx.add_view(|ctx| BufferView::single_line(settings.clone(), ctx));
-        ctx.subscribe_to_view(&query_buffer, Self::on_query_buffer_event);
+        let query_buffer = ctx.add_view(|ctx| Editor::single_line(settings.clone(), ctx));
+        ctx.subscribe_to_view(&query_buffer, Self::on_query_editor_event);
 
         Self {
             handle: ctx.handle().downgrade(),
@@ -315,15 +315,14 @@ impl FileFinder {
         }
     }
 
-    fn on_query_buffer_event(
+    fn on_query_editor_event(
         &mut self,
-        _: ViewHandle<BufferView>,
-        event: &buffer_view::Event,
+        _: ViewHandle<Editor>,
+        event: &editor::Event,
         ctx: &mut ViewContext<Self>,
     ) {
-        use buffer_view::Event::*;
         match event {
-            Edited => {
+            editor::Event::Edited => {
                 let query = self.query_buffer.read(ctx).text(ctx.as_ref());
                 if query.is_empty() {
                     self.latest_search_id = util::post_inc(&mut self.search_count);
@@ -335,7 +334,7 @@ impl FileFinder {
                     }
                 }
             }
-            Blurred => ctx.emit(Event::Dismissed),
+            editor::Event::Blurred => ctx.emit(Event::Dismissed),
             _ => {}
         }
     }

zed/src/workspace.rs 🔗

@@ -1,7 +1,7 @@
 pub mod pane;
 pub mod pane_group;
 use crate::{
-    editor::{Buffer, BufferView},
+    editor::{Buffer, Editor},
     language::LanguageRegistry,
     settings::Settings,
     time::ReplicaId,
@@ -452,7 +452,7 @@ impl Workspace {
     pub fn open_new_file(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
         let buffer = ctx.add_model(|ctx| Buffer::new(self.replica_id, "", ctx));
         let buffer_view =
-            ctx.add_view(|ctx| BufferView::for_buffer(buffer.clone(), self.settings.clone(), ctx));
+            ctx.add_view(|ctx| Editor::for_buffer(buffer.clone(), self.settings.clone(), ctx));
         self.items.push(ItemHandle::downgrade(&buffer));
         self.add_item_view(Box::new(buffer_view), ctx);
     }
@@ -776,7 +776,7 @@ impl WorkspaceHandle for ViewHandle<Workspace> {
 mod tests {
     use super::*;
     use crate::{
-        editor::BufferView,
+        editor::Editor,
         test::{build_app_state, temp_tree},
     };
     use serde_json::json;
@@ -1049,7 +1049,7 @@ mod tests {
         let editor = app.read(|ctx| {
             let pane = workspace.read(ctx).active_pane().read(ctx);
             let item = pane.active_item().unwrap();
-            item.to_any().downcast::<BufferView>().unwrap()
+            item.to_any().downcast::<Editor>().unwrap()
         });
 
         app.update(|ctx| editor.update(ctx, |editor, ctx| editor.insert(&"x".to_string(), ctx)));
@@ -1095,7 +1095,7 @@ mod tests {
                 .active_item(ctx)
                 .unwrap()
                 .to_any()
-                .downcast::<BufferView>()
+                .downcast::<Editor>()
                 .unwrap()
         });
         editor.update(&mut app, |editor, ctx| {
@@ -1155,7 +1155,7 @@ mod tests {
                 .active_item(ctx)
                 .unwrap()
                 .to_any()
-                .downcast::<BufferView>()
+                .downcast::<Editor>()
                 .unwrap()
         });
         app.read(|ctx| {