elio 1.0.0

Terminal-native file manager with rich previews, inline images, and mouse support.
Documentation
use super::*;
use std::path::Path;

impl App {
    pub(crate) fn directory_item_count_label(&self, entry: &Entry) -> Option<String> {
        self.directory_item_count(entry).map(format_item_count)
    }

    pub(super) fn cache_directory_item_count(
        &mut self,
        path: PathBuf,
        modified: Option<SystemTime>,
        show_hidden: bool,
        item_count: Option<usize>,
    ) {
        let key = DirectoryItemCountKey {
            path,
            modified,
            show_hidden,
        };
        self.navigation
            .directory_item_count_cache
            .insert(key.clone(), item_count);
        self.navigation
            .directory_item_count_order
            .retain(|queued| queued != &key);
        self.navigation
            .directory_item_count_order
            .push_back(key.clone());

        while self.navigation.directory_item_count_order.len() > DIRECTORY_ITEM_COUNT_CACHE_LIMIT {
            if let Some(stale_key) = self.navigation.directory_item_count_order.pop_front() {
                self.navigation
                    .directory_item_count_cache
                    .remove(&stale_key);
            }
        }
    }

    pub(super) fn queue_visible_directory_item_counts(&mut self) {
        let viewport = DirectoryCountViewport {
            fingerprint: self.navigation.directory_runtime.fingerprint,
            scroll_row: self.navigation.scroll_row,
            cols: self.input.frame_state.metrics.cols.max(1),
            rows_visible: self.input.frame_state.metrics.rows_visible.max(1),
            show_hidden: self.effective_show_hidden(),
        };
        if self.navigation.directory_count_viewport == Some(viewport) {
            return;
        }
        self.navigation.directory_count_viewport = Some(viewport);

        let requests = self
            .visible_entry_indices()
            .into_iter()
            .filter_map(|index| self.navigation.entries.get(index))
            .filter(|entry| entry.is_dir())
            .filter_map(|entry| self.directory_item_count_request_for(entry))
            .collect::<Vec<_>>();

        for request in requests {
            let _ = self.jobs.scheduler.submit_directory_item_count(request);
        }
    }

    pub(super) fn should_redraw_for_directory_item_count(
        &self,
        path: &Path,
        modified: Option<SystemTime>,
        show_hidden: bool,
    ) -> bool {
        if self.effective_show_hidden() != show_hidden {
            return false;
        }

        self.visible_entry_indices().into_iter().any(|index| {
            self.navigation.entries.get(index).is_some_and(|entry| {
                entry.is_dir() && entry.path == path && entry.modified == modified
            })
        })
    }

    fn directory_item_count(&self, entry: &Entry) -> Option<usize> {
        let key = self.directory_item_count_key_for(entry)?;
        self.navigation
            .directory_item_count_cache
            .get(&key)
            .copied()
            .flatten()
    }

    fn directory_item_count_request_for(
        &self,
        entry: &Entry,
    ) -> Option<jobs::DirectoryItemCountRequest> {
        let key = self.directory_item_count_key_for(entry)?;
        if self
            .navigation
            .directory_item_count_cache
            .contains_key(&key)
        {
            return None;
        }
        Some(jobs::DirectoryItemCountRequest {
            path: key.path,
            modified: key.modified,
            show_hidden: key.show_hidden,
        })
    }

    fn directory_item_count_key_for(&self, entry: &Entry) -> Option<DirectoryItemCountKey> {
        entry.is_dir().then(|| DirectoryItemCountKey {
            path: entry.path.clone(),
            modified: entry.modified,
            show_hidden: self.effective_show_hidden(),
        })
    }

    pub(super) fn visible_entry_indices(&self) -> Vec<usize> {
        if self.navigation.entries.is_empty() {
            return Vec::new();
        }

        let cols = self.input.frame_state.metrics.cols.max(1);
        let rows_visible = self.input.frame_state.metrics.rows_visible.max(1);
        let start = self.navigation.scroll_row.saturating_mul(cols);
        let limit = rows_visible.saturating_mul(cols);
        (start..self.navigation.entries.len()).take(limit).collect()
    }
}