1835 lines · 61.8 KB · Rust
RawDownload
use crate::command::command_interceptor;use crate::motion::MotionKind;use crate::normal::repeat::Replayer;use crate::surrounds::SurroundsType;use crate::{ToggleMarksView, ToggleRegistersView, UseSystemClipboard, Vim, VimAddon, VimSettings};use crate::{motion::Motion, object::Object};use anyhow::Result;use collections::HashMap;use command_palette_hooks::{CommandPaletteFilter, GlobalCommandPaletteInterceptor};use db::{    sqlez::{domain::Domain, thread_safe_connection::ThreadSafeConnection},    sqlez_macros::sql,};use editor::display_map::{is_invisible, replacement};use editor::{Anchor, ClipboardSelection, Editor, MultiBuffer, ToPoint as EditorToPoint};use gpui::{    Action, App, AppContext, BorrowAppContext, ClipboardEntry, ClipboardItem, DismissEvent, Entity,    EntityId, Global, HighlightStyle, StyledText, Subscription, Task, TextStyle, WeakEntity,};use language::{Buffer, BufferEvent, BufferId, Chunk, Point};use multi_buffer::MultiBufferRow;use picker::{Picker, PickerDelegate};use project::{Project, ProjectItem, ProjectPath};use serde::{Deserialize, Serialize};use settings::{Settings, SettingsStore};use std::borrow::BorrowMut;use std::collections::HashSet;use std::path::Path;use std::{fmt::Display, ops::Range, sync::Arc};use text::{Bias, ToPoint};use theme::ThemeSettings;use ui::{    ActiveTheme, Context, Div, FluentBuilder, KeyBinding, ParentElement, SharedString, Styled,    StyledTypography, Window, h_flex, rems,};use util::ResultExt;use util::rel_path::RelPath;use workspace::searchable::Direction;use workspace::{Workspace, WorkspaceDb, WorkspaceId}; #[derive(Clone, Copy, Default, Debug, PartialEq, Serialize, Deserialize)]pub enum Mode {    #[default]    Normal,    Insert,    Replace,    Visual,    VisualLine,    VisualBlock,    HelixNormal,    HelixSelect,} impl Display for Mode {    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {        match self {            Mode::Normal => write!(f, "NORMAL"),            Mode::Insert => write!(f, "INSERT"),            Mode::Replace => write!(f, "REPLACE"),            Mode::Visual => write!(f, "VISUAL"),            Mode::VisualLine => write!(f, "VISUAL LINE"),            Mode::VisualBlock => write!(f, "VISUAL BLOCK"),            Mode::HelixNormal => write!(f, "NORMAL"),            Mode::HelixSelect => write!(f, "SELECT"),        }    }} impl Mode {    pub fn is_visual(&self) -> bool {        match self {            Self::Visual | Self::VisualLine | Self::VisualBlock | Self::HelixSelect => true,            Self::Normal | Self::Insert | Self::Replace | Self::HelixNormal => false,        }    }} #[derive(Clone, Debug, PartialEq)]pub enum Operator {    Change,    Delete,    Yank,    Replace,    Object {        around: bool,    },    FindForward {        before: bool,        multiline: bool,    },    FindBackward {        after: bool,        multiline: bool,    },    Sneak {        first_char: Option<char>,    },    SneakBackward {        first_char: Option<char>,    },    AddSurrounds {        // Typically no need to configure this as `SendKeystrokes` can be used - see #23088.        target: Option<SurroundsType>,    },    ChangeSurrounds {        target: Option<Object>,        /// Represents whether the opening bracket was used for the target        /// object.        opening: bool,    },    DeleteSurrounds,    Mark,    Jump {        line: bool,    },    Indent,    Outdent,    AutoIndent,    Rewrap,    ShellCommand,    Lowercase,    Uppercase,    OppositeCase,    Rot13,    Rot47,    Digraph {        first_char: Option<char>,    },    Literal {        prefix: Option<String>,    },    Register,    RecordRegister,    ReplayRegister,    ToggleComments,    ReplaceWithRegister,    Exchange,    HelixMatch,    HelixNext {        around: bool,    },    HelixPrevious {        around: bool,    },} #[derive(Default, Clone, Debug)]pub enum RecordedSelection {    #[default]    None,    Visual {        rows: u32,        cols: u32,    },    SingleLine {        cols: u32,    },    VisualBlock {        rows: u32,        cols: u32,    },    VisualLine {        rows: u32,    },} #[derive(Default, Clone, Debug)]pub struct Register {    pub(crate) text: SharedString,    pub(crate) clipboard_selections: Option<Vec<ClipboardSelection>>,} impl From<Register> for ClipboardItem {    fn from(register: Register) -> Self {        if let Some(clipboard_selections) = register.clipboard_selections {            ClipboardItem::new_string_with_json_metadata(register.text.into(), clipboard_selections)        } else {            ClipboardItem::new_string(register.text.into())        }    }} impl From<ClipboardItem> for Register {    fn from(item: ClipboardItem) -> Self {        // For now, we don't store metadata for multiple entries.        match item.entries().first() {            Some(ClipboardEntry::String(value)) if item.entries().len() == 1 => Register {                text: value.text().to_owned().into(),                clipboard_selections: value.metadata_json::<Vec<ClipboardSelection>>(),            },            // For now, registers can't store images. This could change in the future.            _ => Register::default(),        }    }} impl From<String> for Register {    fn from(text: String) -> Self {        Register {            text: text.into(),            clipboard_selections: None,        }    }} #[derive(Default)]pub struct VimGlobals {    pub last_find: Option<Motion>,     pub dot_recording: bool,    pub dot_replaying: bool,     /// pre_count is the number before an operator is specified (3 in 3d2d)    pub pre_count: Option<usize>,    /// post_count is the number after an operator is specified (2 in 3d2d)    pub post_count: Option<usize>,    pub forced_motion: bool,    pub stop_recording_after_next_action: bool,    pub ignore_current_insertion: bool,    pub recording_count: Option<usize>,    pub recorded_count: Option<usize>,    pub recording_actions: Vec<ReplayableAction>,    pub recorded_actions: Vec<ReplayableAction>,    pub recorded_selection: RecordedSelection,     pub recording_register: Option<char>,    pub last_recorded_register: Option<char>,    pub last_replayed_register: Option<char>,    pub replayer: Option<Replayer>,     pub last_yank: Option<SharedString>,    pub registers: HashMap<char, Register>,    pub recordings: HashMap<char, Vec<ReplayableAction>>,     pub focused_vim: Option<WeakEntity<Vim>>,     pub marks: HashMap<EntityId, Entity<MarksState>>,} pub struct MarksState {    workspace: WeakEntity<Workspace>,     multibuffer_marks: HashMap<EntityId, HashMap<String, Vec<Anchor>>>,    buffer_marks: HashMap<BufferId, HashMap<String, Vec<text::Anchor>>>,    watched_buffers: HashMap<BufferId, (MarkLocation, Subscription, Subscription)>,     serialized_marks: HashMap<Arc<Path>, HashMap<String, Vec<Point>>>,    global_marks: HashMap<String, MarkLocation>,     _subscription: Subscription,} #[derive(Debug, PartialEq, Eq, Clone)]pub enum MarkLocation {    Buffer(EntityId),    Path(Arc<Path>),} pub enum Mark {    Local(Vec<Anchor>),    Buffer(EntityId, Vec<Anchor>),    Path(Arc<Path>, Vec<Point>),} impl MarksState {    pub fn new(workspace: &Workspace, cx: &mut App) -> Entity<MarksState> {        cx.new(|cx| {            let buffer_store = workspace.project().read(cx).buffer_store().clone();            let subscription = cx.subscribe(&buffer_store, move |this: &mut Self, _, event, cx| {                if let project::buffer_store::BufferStoreEvent::BufferAdded(buffer) = event {                    this.on_buffer_loaded(buffer, cx);                }            });             let mut this = Self {                workspace: workspace.weak_handle(),                multibuffer_marks: HashMap::default(),                buffer_marks: HashMap::default(),                watched_buffers: HashMap::default(),                serialized_marks: HashMap::default(),                global_marks: HashMap::default(),                _subscription: subscription,            };             this.load(cx);            this        })    }     fn workspace_id(&self, cx: &App) -> Option<WorkspaceId> {        self.workspace            .read_with(cx, |workspace, _| workspace.database_id())            .ok()            .flatten()    }     fn project(&self, cx: &App) -> Option<Entity<Project>> {        self.workspace            .read_with(cx, |workspace, _| workspace.project().clone())            .ok()    }     fn load(&mut self, cx: &mut Context<Self>) {        cx.spawn(async move |this, cx| {            let Some(workspace_id) = this.update(cx, |this, cx| this.workspace_id(cx)).ok()? else {                return None;            };            let (marks, paths) = cx                .background_spawn(async move {                    let marks = DB.get_marks(workspace_id)?;                    let paths = DB.get_global_marks_paths(workspace_id)?;                    anyhow::Ok((marks, paths))                })                .await                .log_err()?;            this.update(cx, |this, cx| this.loaded(marks, paths, cx))                .ok()        })        .detach();    }     fn loaded(        &mut self,        marks: Vec<SerializedMark>,        global_mark_paths: Vec<(String, Arc<Path>)>,        cx: &mut Context<Self>,    ) {        let Some(project) = self.project(cx) else {            return;        };         for mark in marks {            self.serialized_marks                .entry(mark.path)                .or_default()                .insert(mark.name, mark.points);        }         for (name, path) in global_mark_paths {            self.global_marks                .insert(name, MarkLocation::Path(path.clone()));             let project_path = project                .read(cx)                .worktrees(cx)                .filter_map(|worktree| {                    let relative = path.strip_prefix(worktree.read(cx).abs_path()).ok()?;                    let path = RelPath::new(relative, worktree.read(cx).path_style()).log_err()?;                    Some(ProjectPath {                        worktree_id: worktree.read(cx).id(),                        path: path.into_arc(),                    })                })                .next();            if let Some(buffer) = project_path                .and_then(|project_path| project.read(cx).get_open_buffer(&project_path, cx))            {                self.on_buffer_loaded(&buffer, cx)            }        }    }     pub fn on_buffer_loaded(&mut self, buffer_handle: &Entity<Buffer>, cx: &mut Context<Self>) {        let Some(project) = self.project(cx) else {            return;        };        let Some(project_path) = buffer_handle.read(cx).project_path(cx) else {            return;        };        let Some(abs_path) = project.read(cx).absolute_path(&project_path, cx) else {            return;        };        let abs_path: Arc<Path> = abs_path.into();         let Some(serialized_marks) = self.serialized_marks.get(&abs_path) else {            return;        };         let mut loaded_marks = HashMap::default();        let buffer = buffer_handle.read(cx);        for (name, points) in serialized_marks.iter() {            loaded_marks.insert(                name.clone(),                points                    .iter()                    .map(|point| buffer.anchor_before(buffer.clip_point(*point, Bias::Left)))                    .collect(),            );        }        self.buffer_marks.insert(buffer.remote_id(), loaded_marks);        self.watch_buffer(MarkLocation::Path(abs_path), buffer_handle, cx)    }     fn serialize_buffer_marks(        &mut self,        path: Arc<Path>,        buffer: &Entity<Buffer>,        cx: &mut Context<Self>,    ) {        let new_points: HashMap<String, Vec<Point>> =            if let Some(anchors) = self.buffer_marks.get(&buffer.read(cx).remote_id()) {                anchors                    .iter()                    .map(|(name, anchors)| {                        (                            name.clone(),                            buffer                                .read(cx)                                .summaries_for_anchors::<Point, _>(anchors)                                .collect(),                        )                    })                    .collect()            } else {                HashMap::default()            };        let old_points = self.serialized_marks.get(&path);        if old_points == Some(&new_points) {            return;        }        let mut to_write = HashMap::default();         for (key, value) in &new_points {            if self.is_global_mark(key)                && self.global_marks.get(key) != Some(&MarkLocation::Path(path.clone()))            {                if let Some(workspace_id) = self.workspace_id(cx) {                    let path = path.clone();                    let key = key.clone();                    cx.background_spawn(async move {                        DB.set_global_mark_path(workspace_id, key, path).await                    })                    .detach_and_log_err(cx);                }                 self.global_marks                    .insert(key.clone(), MarkLocation::Path(path.clone()));            }            if old_points.and_then(|o| o.get(key)) != Some(value) {                to_write.insert(key.clone(), value.clone());            }        }         self.serialized_marks.insert(path.clone(), new_points);         if let Some(workspace_id) = self.workspace_id(cx) {            cx.background_spawn(async move {                DB.set_marks(workspace_id, path.clone(), to_write).await?;                anyhow::Ok(())            })            .detach_and_log_err(cx);        }    }     fn is_global_mark(&self, key: &str) -> bool {        key.chars()            .next()            .is_some_and(|c| c.is_uppercase() || c.is_digit(10))    }     fn rename_buffer(        &mut self,        old_path: MarkLocation,        new_path: Arc<Path>,        buffer: &Entity<Buffer>,        cx: &mut Context<Self>,    ) {        if let MarkLocation::Buffer(entity_id) = old_path            && let Some(old_marks) = self.multibuffer_marks.remove(&entity_id)        {            let buffer_marks = old_marks                .into_iter()                .map(|(k, v)| (k, v.into_iter().map(|anchor| anchor.text_anchor).collect()))                .collect();            self.buffer_marks                .insert(buffer.read(cx).remote_id(), buffer_marks);        }        self.watch_buffer(MarkLocation::Path(new_path.clone()), buffer, cx);        self.serialize_buffer_marks(new_path, buffer, cx);    }     fn path_for_buffer(&self, buffer: &Entity<Buffer>, cx: &App) -> Option<Arc<Path>> {        let project_path = buffer.read(cx).project_path(cx)?;        let project = self.project(cx)?;        let abs_path = project.read(cx).absolute_path(&project_path, cx)?;        Some(abs_path.into())    }     fn points_at(        &self,        location: &MarkLocation,        multi_buffer: &Entity<MultiBuffer>,        cx: &App,    ) -> bool {        match location {            MarkLocation::Buffer(entity_id) => entity_id == &multi_buffer.entity_id(),            MarkLocation::Path(path) => {                let Some(singleton) = multi_buffer.read(cx).as_singleton() else {                    return false;                };

Lines 1–500 of 1835.