/
githubmirror
/
lapce
Обзор
Документация
Войти
/
githubmirror
/
lapce
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
lapce-app/src/editor_tab.rs
484 строки
17 KB
Dongdong Zhou
update dependency
19 июн 2025, 23:42
19 июн 2025, 23:42
a289ebd
Код
Авторство
О чём код?
use std::{ path::{Path, PathBuf}, rc::Rc, sync::Arc, }; use floem::{ peniko::{ Color, kurbo::{Point, Rect}, }, reactive::{ Memo, ReadSignal, RwSignal, Scope, SignalGet, SignalUpdate, SignalWith, create_memo, create_rw_signal, }, views::editor::id::EditorId, }; use lapce_rpc::plugin::VoltID; use serde::{Deserialize, Serialize}; use crate::{ config::{LapceConfig, color::LapceColor, icon::LapceIcons}, doc::{Doc, DocContent}, editor::{ EditorData, EditorInfo, diff::{DiffEditorData, DiffEditorInfo}, location::EditorLocation, }, id::{ DiffEditorId, EditorTabId, KeymapId, SettingsId, SplitId, ThemeColorSettingsId, VoltViewId, }, main_split::{Editors, MainSplitData}, plugin::PluginData, window_tab::WindowTabData, }; #[derive(Clone, Serialize, Deserialize)] pub enum EditorTabChildInfo { Editor(EditorInfo), DiffEditor(DiffEditorInfo), Settings, ThemeColorSettings, Keymap, Volt(VoltID), } impl EditorTabChildInfo { pub fn to_data( &self, data: MainSplitData, editor_tab_id: EditorTabId, ) -> EditorTabChild { match &self { EditorTabChildInfo::Editor(editor_info) => { let editor_id = editor_info.to_data(data, editor_tab_id); EditorTabChild::Editor(editor_id) } EditorTabChildInfo::DiffEditor(diff_editor_info) => { let diff_editor_data = diff_editor_info.to_data(data, editor_tab_id); EditorTabChild::DiffEditor(diff_editor_data.id) } EditorTabChildInfo::Settings => { EditorTabChild::Settings(SettingsId::next()) } EditorTabChildInfo::ThemeColorSettings => { EditorTabChild::ThemeColorSettings(ThemeColorSettingsId::next()) } EditorTabChildInfo::Keymap => EditorTabChild::Keymap(KeymapId::next()), EditorTabChildInfo::Volt(id) => { EditorTabChild::Volt(VoltViewId::next(), id.to_owned()) } } } } #[derive(Clone, Serialize, Deserialize)] pub struct EditorTabInfo { pub active: usize, pub is_focus: bool, pub children: Vec<EditorTabChildInfo>, } impl EditorTabInfo { pub fn to_data( &self, data: MainSplitData, split: SplitId, ) -> RwSignal<EditorTabData> { let editor_tab_id = EditorTabId::next(); let editor_tab_data = { let cx = data.scope.create_child(); let editor_tab_data = EditorTabData { scope: cx, editor_tab_id, split, active: self.active, children: self .children .iter() .map(|child| { ( cx.create_rw_signal(0), cx.create_rw_signal(Rect::ZERO), child.to_data(data.clone(), editor_tab_id), ) }) .collect(), layout_rect: Rect::ZERO, window_origin: Point::ZERO, locations: cx.create_rw_signal(im::Vector::new()), current_location: cx.create_rw_signal(0), }; cx.create_rw_signal(editor_tab_data) }; if self.is_focus { data.active_editor_tab.set(Some(editor_tab_id)); } data.editor_tabs.update(|editor_tabs| { editor_tabs.insert(editor_tab_id, editor_tab_data); }); editor_tab_data } } pub enum EditorTabChildSource { Editor { path: PathBuf, doc: Rc<Doc> }, DiffEditor { left: Rc<Doc>, right: Rc<Doc> }, NewFileEditor, Settings, ThemeColorSettings, Keymap, Volt(VoltID), } #[derive(Clone, Debug, PartialEq, Eq)] pub enum EditorTabChild { Editor(EditorId), DiffEditor(DiffEditorId), Settings(SettingsId), ThemeColorSettings(ThemeColorSettingsId), Keymap(KeymapId), Volt(VoltViewId, VoltID), } #[derive(PartialEq)] pub struct EditorTabChildViewInfo { pub icon: String, pub color: Option<Color>, pub name: String, pub path: Option<PathBuf>, pub confirmed: Option<RwSignal<bool>>, pub is_pristine: bool, } impl EditorTabChild { pub fn id(&self) -> u64 { match self { EditorTabChild::Editor(id) => id.to_raw(), EditorTabChild::DiffEditor(id) => id.to_raw(), EditorTabChild::Settings(id) => id.to_raw(), EditorTabChild::ThemeColorSettings(id) => id.to_raw(), EditorTabChild::Keymap(id) => id.to_raw(), EditorTabChild::Volt(id, _) => id.to_raw(), } } pub fn is_settings(&self) -> bool { matches!(self, EditorTabChild::Settings(_)) } pub fn child_info(&self, data: &WindowTabData) -> EditorTabChildInfo { match &self { EditorTabChild::Editor(editor_id) => { let editor_data = data .main_split .editors .editor_untracked(*editor_id) .unwrap(); EditorTabChildInfo::Editor(editor_data.editor_info(data)) } EditorTabChild::DiffEditor(diff_editor_id) => { let diff_editor_data = data .main_split .diff_editors .get_untracked() .get(diff_editor_id) .cloned() .unwrap(); EditorTabChildInfo::DiffEditor(diff_editor_data.diff_editor_info()) } EditorTabChild::Settings(_) => EditorTabChildInfo::Settings, EditorTabChild::ThemeColorSettings(_) => { EditorTabChildInfo::ThemeColorSettings } EditorTabChild::Keymap(_) => EditorTabChildInfo::Keymap, EditorTabChild::Volt(_, id) => EditorTabChildInfo::Volt(id.to_owned()), } } pub fn view_info( &self, editors: Editors, diff_editors: RwSignal<im::HashMap<DiffEditorId, DiffEditorData>>, plugin: PluginData, config: ReadSignal<Arc<LapceConfig>>, ) -> Memo<EditorTabChildViewInfo> { match self.clone() { EditorTabChild::Editor(editor_id) => create_memo(move |_| { let config = config.get(); let editor_data = editors.editor(editor_id); let path = if let Some(editor_data) = editor_data { let doc = editor_data.doc_signal().get(); let (content, is_pristine, confirmed) = ( doc.content.get(), doc.buffer.with(|b| b.is_pristine()), editor_data.confirmed, ); match content { DocContent::File { path, .. } => { Some((path, confirmed, is_pristine)) } DocContent::Local => None, DocContent::History(_) => None, DocContent::Scratch { name, .. } => { Some((PathBuf::from(name), confirmed, is_pristine)) } } } else { None }; let (icon, color, name, confirmed, is_pristine) = match path { Some((ref path, confirmed, is_pritine)) => { let (svg, color) = config.file_svg(path); ( svg, color, path.file_name() .unwrap_or_default() .to_string_lossy() .into_owned(), confirmed, is_pritine, ) } None => ( config.ui_svg(LapceIcons::FILE), Some(config.color(LapceColor::LAPCE_ICON_ACTIVE)), "local".to_string(), create_rw_signal(true), true, ), }; EditorTabChildViewInfo { icon, color, name, path: path.map(|opt| opt.0), confirmed: Some(confirmed), is_pristine, } }), EditorTabChild::DiffEditor(diff_editor_id) => create_memo(move |_| { let config = config.get(); let diff_editor_data = diff_editors .with(|diff_editors| diff_editors.get(&diff_editor_id).cloned()); let confirmed = diff_editor_data.as_ref().map(|d| d.confirmed); let info = diff_editor_data .map(|diff_editor_data| { [diff_editor_data.left, diff_editor_data.right].map(|data| { let (content, is_pristine) = data.doc_signal().with(|doc| { ( doc.content.get(), doc.buffer.with(|b| b.is_pristine()), ) }); match content { DocContent::File { path, .. } => { Some((path, is_pristine)) } DocContent::Local => None, DocContent::History(_) => None, DocContent::Scratch { name, .. } => { Some((PathBuf::from(name), is_pristine)) } } }) }) .unwrap_or([None, None]); let (icon, color, path, is_pristine) = match info { [Some((path, is_pristine)), None] | [None, Some((path, is_pristine))] => { let (svg, color) = config.file_svg(&path); ( svg, color, format!( "{} (Diff)", path.file_name() .unwrap_or_default() .to_string_lossy() ), is_pristine, ) } [ Some((left_path, left_is_pristine)), Some((right_path, right_is_pristine)), ] => { let (svg, color) = config.files_svg(&[&left_path, &right_path]); let [left_file_name, right_file_name] = [&left_path, &right_path].map(|path| { path.file_name() .unwrap_or_default() .to_string_lossy() }); ( svg, color, format!("{left_file_name} - {right_file_name} (Diff)"), left_is_pristine && right_is_pristine, ) } [None, None] => ( config.ui_svg(LapceIcons::FILE), Some(config.color(LapceColor::LAPCE_ICON_ACTIVE)), "local".to_string(), true, ), }; EditorTabChildViewInfo { icon, color, name: path, path: None, confirmed, is_pristine, } }), EditorTabChild::Settings(_) => create_memo(move |_| { let config = config.get(); EditorTabChildViewInfo { icon: config.ui_svg(LapceIcons::SETTINGS), color: Some(config.color(LapceColor::LAPCE_ICON_ACTIVE)), name: "Settings".to_string(), path: None, confirmed: None, is_pristine: true, } }), EditorTabChild::ThemeColorSettings(_) => create_memo(move |_| { let config = config.get(); EditorTabChildViewInfo { icon: config.ui_svg(LapceIcons::SYMBOL_COLOR), color: Some(config.color(LapceColor::LAPCE_ICON_ACTIVE)), name: "Theme Colors".to_string(), path: None, confirmed: None, is_pristine: true, } }), EditorTabChild::Keymap(_) => create_memo(move |_| { let config = config.get(); EditorTabChildViewInfo { icon: config.ui_svg(LapceIcons::KEYBOARD), color: Some(config.color(LapceColor::LAPCE_ICON_ACTIVE)), name: "Keyboard Shortcuts".to_string(), path: None, confirmed: None, is_pristine: true, } }), EditorTabChild::Volt(_, id) => create_memo(move |_| { let config = config.get(); let display_name = plugin .installed .with(|volts| volts.get(&id).cloned()) .map(|volt| volt.meta.with(|m| m.display_name.clone())) .or_else(|| { plugin.available.volts.with(|volts| { let volt = volts.get(&id); volt.map(|volt| { volt.info.with(|m| m.display_name.clone()) }) }) }) .unwrap_or_else(|| id.name.clone()); EditorTabChildViewInfo { icon: config.ui_svg(LapceIcons::EXTENSIONS), color: Some(config.color(LapceColor::LAPCE_ICON_ACTIVE)), name: display_name, path: None, confirmed: None, is_pristine: true, } }), } } } #[derive(Clone)] pub struct EditorTabData { pub scope: Scope, pub split: SplitId, pub editor_tab_id: EditorTabId, pub active: usize, pub children: Vec<(RwSignal<usize>, RwSignal<Rect>, EditorTabChild)>, pub window_origin: Point, pub layout_rect: Rect, pub locations: RwSignal<im::Vector<EditorLocation>>, pub current_location: RwSignal<usize>, } impl EditorTabData { pub fn get_editor( &self, editors: Editors, path: &Path, ) -> Option<(usize, EditorData)> { for (i, child) in self.children.iter().enumerate() { if let (_, _, EditorTabChild::Editor(editor_id)) = child { if let Some(editor) = editors.editor_untracked(*editor_id) { let is_path = editor.doc().content.with_untracked(|content| { if let DocContent::File { path: p, .. } = content { p == path } else { false } }); if is_path { return Some((i, editor)); } } } } None } pub fn get_unconfirmed_editor_tab_child( &self, editors: Editors, diff_editors: &im::HashMap<EditorId, DiffEditorData>, ) -> Option<(usize, EditorTabChild)> { for (i, (_, _, child)) in self.children.iter().enumerate() { match child { EditorTabChild::Editor(editor_id) => { if let Some(editor) = editors.editor_untracked(*editor_id) { let confirmed = editor.confirmed.get_untracked(); if !confirmed { return Some((i, child.clone())); } } } EditorTabChild::DiffEditor(diff_editor_id) => { if let Some(diff_editor) = diff_editors.get(diff_editor_id) { let confirmed = diff_editor.confirmed.get_untracked(); if !confirmed { return Some((i, child.clone())); } } } _ => (), } } None } pub fn tab_info(&self, data: &WindowTabData) -> EditorTabInfo { EditorTabInfo { active: self.active, is_focus: data.main_split.active_editor_tab.get_untracked() == Some(self.editor_tab_id), children: self .children .iter() .map(|(_, _, child)| child.child_info(data)) .collect(), } } }