use super::*;
use crate::DocumentEncodingErrorKind;
use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CursorPosition {
line: usize,
column: usize,
}
impl Default for CursorPosition {
fn default() -> Self {
Self::new(1, 1)
}
}
impl CursorPosition {
pub fn new(line: usize, column: usize) -> Self {
Self {
line: line.max(1),
column: column.max(1),
}
}
pub fn line(&self) -> usize {
self.line
}
pub fn column(&self) -> usize {
self.column
}
pub fn to_text_position(self) -> TextPosition {
TextPosition::new(self.line.saturating_sub(1), self.column.saturating_sub(1))
}
pub fn from_text_position(position: TextPosition) -> Self {
Self::new(
position.line0().saturating_add(1),
position.col0().saturating_add(1),
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LoadPhase {
Opening,
InspectingSource,
PreparingIndex,
RecoveringSession,
Ready,
}
impl LoadPhase {
pub(super) fn as_raw(self) -> u8 {
match self {
Self::Opening => 0,
Self::InspectingSource => 1,
Self::PreparingIndex => 2,
Self::RecoveringSession => 3,
Self::Ready => 4,
}
}
pub(super) fn from_raw(raw: u8) -> Self {
match raw {
0 => Self::Opening,
1 => Self::InspectingSource,
2 => Self::PreparingIndex,
3 => Self::RecoveringSession,
4 => Self::Ready,
_ => Self::Opening,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FileProgress {
path: Arc<PathBuf>,
completed_bytes: u64,
total_bytes: u64,
load_phase: Option<LoadPhase>,
}
impl FileProgress {
pub(super) fn new(path: Arc<PathBuf>, completed_bytes: u64, total_bytes: u64) -> Self {
Self {
path,
completed_bytes,
total_bytes,
load_phase: None,
}
}
pub(super) fn loading(
path: Arc<PathBuf>,
completed_bytes: u64,
total_bytes: u64,
load_phase: LoadPhase,
) -> Self {
Self {
path,
completed_bytes,
total_bytes,
load_phase: Some(load_phase),
}
}
pub fn path(&self) -> &Path {
self.path.as_path()
}
pub fn completed_bytes(&self) -> u64 {
self.completed_bytes
}
pub fn total_bytes(&self) -> u64 {
self.total_bytes
}
pub fn load_phase(&self) -> Option<LoadPhase> {
self.load_phase
}
pub fn fraction(&self) -> f32 {
if self.total_bytes == 0 {
1.0
} else {
self.completed_bytes.min(self.total_bytes) as f32 / self.total_bytes as f32
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BackgroundActivity {
Idle,
Loading(FileProgress),
Saving(FileProgress),
}
impl BackgroundActivity {
pub fn is_idle(&self) -> bool {
matches!(self, Self::Idle)
}
pub fn loading_state(&self) -> Option<&FileProgress> {
match self {
Self::Loading(progress) => Some(progress),
Self::Idle | Self::Saving(_) => None,
}
}
pub fn loading_phase(&self) -> Option<LoadPhase> {
self.loading_state().and_then(FileProgress::load_phase)
}
pub fn save_state(&self) -> Option<&FileProgress> {
match self {
Self::Saving(progress) => Some(progress),
Self::Idle | Self::Loading(_) => None,
}
}
pub fn progress(&self) -> Option<&FileProgress> {
match self {
Self::Idle => None,
Self::Loading(progress) | Self::Saving(progress) => Some(progress),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BackgroundIssueKind {
LoadFailed,
SaveFailed,
LoadDiscarded,
SaveDiscarded,
}
impl BackgroundIssueKind {
pub const fn is_load(self) -> bool {
matches!(self, Self::LoadFailed | Self::LoadDiscarded)
}
pub const fn is_save(self) -> bool {
matches!(self, Self::SaveFailed | Self::SaveDiscarded)
}
pub const fn is_discarded(self) -> bool {
matches!(self, Self::LoadDiscarded | Self::SaveDiscarded)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BackgroundIssue {
kind: BackgroundIssueKind,
path: Arc<PathBuf>,
message: Arc<str>,
}
impl BackgroundIssue {
pub(super) fn new(kind: BackgroundIssueKind, path: PathBuf, message: String) -> Self {
Self {
kind,
path: Arc::new(path),
message: Arc::from(message),
}
}
pub fn kind(&self) -> BackgroundIssueKind {
self.kind
}
pub fn path(&self) -> &Path {
self.path.as_path()
}
pub fn message(&self) -> &str {
self.message.as_ref()
}
pub fn is_load(&self) -> bool {
self.kind.is_load()
}
pub fn is_save(&self) -> bool {
self.kind.is_save()
}
pub fn is_discarded(&self) -> bool {
self.kind.is_discarded()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DocumentSessionStatus {
generation: u64,
document: DocumentStatus,
background_activity: BackgroundActivity,
background_issue: Option<BackgroundIssue>,
close_pending: bool,
}
impl DocumentSessionStatus {
pub(super) fn new(
generation: u64,
document: DocumentStatus,
background_activity: BackgroundActivity,
background_issue: Option<BackgroundIssue>,
close_pending: bool,
) -> Self {
Self {
generation,
document,
background_activity,
background_issue,
close_pending,
}
}
pub fn generation(&self) -> u64 {
self.generation
}
pub fn document(&self) -> &DocumentStatus {
&self.document
}
pub fn path(&self) -> Option<&Path> {
self.document.path()
}
pub fn is_dirty(&self) -> bool {
self.document.is_dirty()
}
pub fn line_count(&self) -> LineCount {
self.document.line_count()
}
pub fn display_line_count(&self) -> usize {
self.document.display_line_count()
}
pub fn exact_line_count(&self) -> Option<usize> {
self.document.exact_line_count()
}
pub fn is_line_count_exact(&self) -> bool {
self.document.is_line_count_exact()
}
pub fn file_len(&self) -> usize {
self.document.file_len()
}
pub fn line_ending(&self) -> LineEnding {
self.document.line_ending()
}
pub fn encoding(&self) -> DocumentEncoding {
self.document.encoding()
}
pub fn preserve_save_error(&self) -> Option<DocumentEncodingErrorKind> {
self.document.preserve_save_error()
}
pub fn can_preserve_save(&self) -> bool {
self.document.can_preserve_save()
}
pub fn encoding_origin(&self) -> DocumentEncodingOrigin {
self.document.encoding_origin()
}
pub fn decoding_had_errors(&self) -> bool {
self.document.decoding_had_errors()
}
pub fn backing(&self) -> DocumentBacking {
self.document.backing()
}
pub fn has_edit_buffer(&self) -> bool {
self.document.has_edit_buffer()
}
pub fn has_rope(&self) -> bool {
self.document.has_rope()
}
pub fn has_piece_table(&self) -> bool {
self.document.has_piece_table()
}
pub fn indexing_state(&self) -> Option<ByteProgress> {
self.document.indexing_state()
}
pub fn is_indexing(&self) -> bool {
self.document.is_indexing()
}
pub fn background_activity(&self) -> &BackgroundActivity {
&self.background_activity
}
pub fn background_issue(&self) -> Option<&BackgroundIssue> {
self.background_issue.as_ref()
}
pub fn close_pending(&self) -> bool {
self.close_pending
}
pub fn loading_state(&self) -> Option<&FileProgress> {
self.background_activity.loading_state()
}
pub fn loading_phase(&self) -> Option<LoadPhase> {
self.background_activity.loading_phase()
}
pub fn save_state(&self) -> Option<&FileProgress> {
self.background_activity.save_state()
}
pub fn is_busy(&self) -> bool {
!matches!(self.background_activity, BackgroundActivity::Idle)
}
pub fn is_loading(&self) -> bool {
matches!(self.background_activity, BackgroundActivity::Loading(_))
}
pub fn is_saving(&self) -> bool {
matches!(self.background_activity, BackgroundActivity::Saving(_))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EditorTabStatus {
id: u64,
session: DocumentSessionStatus,
cursor: CursorPosition,
pinned: bool,
}
impl EditorTabStatus {
pub(super) fn new(
id: u64,
session: DocumentSessionStatus,
cursor: CursorPosition,
pinned: bool,
) -> Self {
Self {
id,
session,
cursor,
pinned,
}
}
pub fn id(&self) -> u64 {
self.id
}
pub fn session(&self) -> &DocumentSessionStatus {
&self.session
}
pub fn generation(&self) -> u64 {
self.session.generation()
}
pub fn document(&self) -> &DocumentStatus {
self.session.document()
}
pub fn path(&self) -> Option<&Path> {
self.session.path()
}
pub fn is_dirty(&self) -> bool {
self.session.is_dirty()
}
pub fn line_count(&self) -> LineCount {
self.session.line_count()
}
pub fn display_line_count(&self) -> usize {
self.session.display_line_count()
}
pub fn exact_line_count(&self) -> Option<usize> {
self.session.exact_line_count()
}
pub fn is_line_count_exact(&self) -> bool {
self.session.is_line_count_exact()
}
pub fn file_len(&self) -> usize {
self.session.file_len()
}
pub fn line_ending(&self) -> LineEnding {
self.session.line_ending()
}
pub fn encoding(&self) -> DocumentEncoding {
self.session.encoding()
}
pub fn preserve_save_error(&self) -> Option<DocumentEncodingErrorKind> {
self.session.preserve_save_error()
}
pub fn can_preserve_save(&self) -> bool {
self.session.can_preserve_save()
}
pub fn encoding_origin(&self) -> DocumentEncodingOrigin {
self.session.encoding_origin()
}
pub fn decoding_had_errors(&self) -> bool {
self.session.decoding_had_errors()
}
pub fn backing(&self) -> DocumentBacking {
self.session.backing()
}
pub fn has_edit_buffer(&self) -> bool {
self.session.has_edit_buffer()
}
pub fn has_rope(&self) -> bool {
self.session.has_rope()
}
pub fn has_piece_table(&self) -> bool {
self.session.has_piece_table()
}
pub fn background_activity(&self) -> &BackgroundActivity {
self.session.background_activity()
}
pub fn background_issue(&self) -> Option<&BackgroundIssue> {
self.session.background_issue()
}
pub fn close_pending(&self) -> bool {
self.session.close_pending()
}
pub fn loading_state(&self) -> Option<&FileProgress> {
self.session.loading_state()
}
pub fn loading_phase(&self) -> Option<LoadPhase> {
self.session.loading_phase()
}
pub fn save_state(&self) -> Option<&FileProgress> {
self.session.save_state()
}
pub fn is_busy(&self) -> bool {
self.session.is_busy()
}
pub fn is_loading(&self) -> bool {
self.session.is_loading()
}
pub fn is_saving(&self) -> bool {
self.session.is_saving()
}
pub fn cursor(&self) -> CursorPosition {
self.cursor
}
pub fn is_pinned(&self) -> bool {
self.pinned
}
}
#[derive(Debug)]
pub enum SaveError {
NoPath,
InProgress,
Io(DocumentError),
}
impl std::fmt::Display for SaveError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::NoPath => write!(f, "no path set for current document"),
Self::InProgress => write!(f, "save already in progress"),
Self::Io(err) => write!(f, "{err}"),
}
}
}
impl std::error::Error for SaveError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Io(err) => Some(err),
Self::NoPath | Self::InProgress => None,
}
}
}