use crate::app::file_open::SortMode;
use crate::input::keybindings::Action;
use crate::model::event::{BufferId, SplitDirection, SplitId};
use crate::services::async_bridge::LspMessageType;
use ratatui::layout::Rect;
use std::collections::HashMap;
use std::ops::Range;
use std::path::{Path, PathBuf};
pub const DEFAULT_BACKGROUND_FILE: &str = "scripts/landscape-wide.txt";
#[derive(Debug, Clone, Default)]
pub(super) struct EventLineInfo {
pub start_line: usize,
pub end_line: usize,
pub line_delta: i32,
}
#[derive(Debug, Clone)]
pub(super) struct SearchState {
pub query: String,
pub matches: Vec<usize>,
pub current_match_index: Option<usize>,
pub wrap_search: bool,
pub search_range: Option<Range<usize>>,
pub case_sensitive: bool,
pub whole_word: bool,
}
#[derive(Debug, Clone)]
pub(super) struct Bookmark {
pub buffer_id: BufferId,
pub position: usize,
}
#[derive(Debug, Clone)]
pub(super) struct InteractiveReplaceState {
pub search: String,
pub replacement: String,
pub current_match_pos: usize,
pub start_pos: usize,
pub has_wrapped: bool,
pub replacements_made: usize,
}
#[derive(Debug, Clone, PartialEq)]
pub enum BufferKind {
File {
path: PathBuf,
uri: Option<lsp_types::Uri>,
},
Virtual {
mode: String,
},
}
#[derive(Debug, Clone)]
pub struct BufferMetadata {
pub kind: BufferKind,
pub display_name: String,
pub lsp_enabled: bool,
pub lsp_disabled_reason: Option<String>,
pub read_only: bool,
pub binary: bool,
}
impl BufferMetadata {
pub fn file_path(&self) -> Option<&PathBuf> {
match &self.kind {
BufferKind::File { path, .. } => Some(path),
BufferKind::Virtual { .. } => None,
}
}
pub fn file_uri(&self) -> Option<&lsp_types::Uri> {
match &self.kind {
BufferKind::File { uri, .. } => uri.as_ref(),
BufferKind::Virtual { .. } => None,
}
}
pub fn is_virtual(&self) -> bool {
matches!(self.kind, BufferKind::Virtual { .. })
}
pub fn virtual_mode(&self) -> Option<&str> {
match &self.kind {
BufferKind::Virtual { mode } => Some(mode),
BufferKind::File { .. } => None,
}
}
}
impl BufferMetadata {
pub fn new() -> Self {
Self {
kind: BufferKind::File {
path: PathBuf::new(),
uri: None,
},
display_name: "[No Name]".to_string(),
lsp_enabled: true,
lsp_disabled_reason: None,
read_only: false,
binary: false,
}
}
pub fn with_file(path: PathBuf, working_dir: &Path) -> Self {
let file_uri = url::Url::from_file_path(&path)
.ok()
.and_then(|u| u.as_str().parse::<lsp_types::Uri>().ok());
let display_name = Self::display_name_for_path(&path, working_dir);
Self {
kind: BufferKind::File {
path,
uri: file_uri,
},
display_name,
lsp_enabled: true,
lsp_disabled_reason: None,
read_only: false,
binary: false,
}
}
fn display_name_for_path(path: &Path, working_dir: &Path) -> String {
let canonical_working_dir = working_dir
.canonicalize()
.unwrap_or_else(|_| working_dir.to_path_buf());
let absolute_path = if path.is_absolute() {
path.to_path_buf()
} else {
canonical_working_dir.join(path)
};
let canonical_path = absolute_path
.canonicalize()
.unwrap_or_else(|_| absolute_path.clone());
let relative = canonical_path
.strip_prefix(&canonical_working_dir)
.or_else(|_| path.strip_prefix(working_dir))
.ok()
.and_then(|rel| rel.to_str().map(|s| s.to_string()));
relative
.or_else(|| canonical_path.to_str().map(|s| s.to_string()))
.unwrap_or_else(|| "[Unknown]".to_string())
}
pub fn virtual_buffer(name: String, mode: String, read_only: bool) -> Self {
Self {
kind: BufferKind::Virtual { mode },
display_name: name,
lsp_enabled: false, lsp_disabled_reason: Some("Virtual buffer".to_string()),
read_only,
binary: false,
}
}
pub fn disable_lsp(&mut self, reason: String) {
self.lsp_enabled = false;
self.lsp_disabled_reason = Some(reason);
}
}
#[derive(Debug, Clone)]
pub(super) struct MacroRecordingState {
pub key: char,
pub actions: Vec<Action>,
}
#[derive(Debug, Clone)]
pub(super) struct LspProgressInfo {
pub language: String,
pub title: String,
pub message: Option<String>,
pub percentage: Option<u32>,
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub(super) struct LspMessageEntry {
pub language: String,
pub message_type: LspMessageType,
pub message: String,
pub timestamp: std::time::Instant,
}
#[derive(Debug, Clone, PartialEq)]
pub enum HoverTarget {
SplitSeparator(SplitId, SplitDirection),
ScrollbarThumb(SplitId),
ScrollbarTrack(SplitId),
MenuBarItem(usize),
MenuDropdownItem(usize, usize),
SubmenuItem(usize, usize),
PopupListItem(usize, usize),
SuggestionItem(usize),
FileExplorerBorder,
FileBrowserNavShortcut(usize),
FileBrowserEntry(usize),
FileBrowserHeader(SortMode),
FileBrowserScrollbar,
TabName(BufferId, SplitId),
TabCloseButton(BufferId, SplitId),
CloseSplitButton(SplitId),
MaximizeSplitButton(SplitId),
FileExplorerCloseButton,
}
#[derive(Debug, Clone, Default)]
pub(super) struct MouseState {
pub dragging_scrollbar: Option<SplitId>,
pub last_position: Option<(u16, u16)>,
pub lsp_hover_state: Option<(usize, std::time::Instant, u16, u16)>,
pub lsp_hover_request_sent: bool,
pub drag_start_row: Option<u16>,
pub drag_start_top_byte: Option<usize>,
pub dragging_separator: Option<(SplitId, SplitDirection)>,
pub drag_start_position: Option<(u16, u16)>,
pub drag_start_ratio: Option<f32>,
pub dragging_file_explorer: bool,
pub drag_start_explorer_width: Option<f32>,
pub hover_target: Option<HoverTarget>,
pub dragging_text_selection: bool,
pub drag_selection_split: Option<SplitId>,
pub drag_selection_anchor: Option<usize>,
}
#[derive(Debug, Clone, Default)]
pub struct ViewLineMapping {
pub char_mappings: Vec<Option<usize>>,
pub line_end_byte: usize,
}
#[derive(Debug, Clone, Default)]
pub(super) struct CachedLayout {
pub file_explorer_area: Option<Rect>,
pub editor_content_area: Option<Rect>,
pub split_areas: Vec<(SplitId, BufferId, Rect, Rect, usize, usize)>,
pub separator_areas: Vec<(SplitId, SplitDirection, u16, u16, u16)>,
pub popup_areas: Vec<(usize, Rect, Rect, usize, usize)>,
pub suggestions_area: Option<(Rect, usize, usize, usize)>,
pub tab_areas: Vec<(SplitId, BufferId, u16, u16, u16, u16)>,
pub close_split_areas: Vec<(SplitId, u16, u16, u16)>,
pub maximize_split_areas: Vec<(SplitId, u16, u16, u16)>,
pub view_line_mappings: HashMap<SplitId, Vec<ViewLineMapping>>,
}