Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,12 @@ report.html

# pre-commit executable
pre-commit.pyz
ignored_file
test_mixed/ignored.txt
test_ignored/

# Test scenarios
test_scenarios/fully_ignored/
test_scenarios/partially_ignored/ignored.txt
test_scenarios/nested_ignored/sub1/
*.log
63 changes: 63 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

`pls` is a prettier and powerful `ls(1)` replacement written in Rust. It's designed to be a modern, fast, and feature-rich directory listing tool with extensive customization options.

## Architecture

The codebase is organized into several key modules:

- **Core**: `src/main.rs` contains the application entry point with a global `PLS` instance
- **Models**: `src/models/` contains core data structures including `Pls` (main application state), `Node` (file system entries), and `Window` (terminal info)
- **Configuration**: `src/config/` handles YAML configuration files and command-line argument parsing
- **Arguments**: `src/args/` manages command-line input processing and grouping
- **Output**: `src/output/` handles different display formats (grid, table, etc.)
- **Formatting**: `src/fmt/` provides text rendering and markup processing
- **Graphics**: `src/gfx/` handles terminal graphics protocol support (Kitty, SVG)
- **Enums**: `src/enums/` contains various enumeration types for appearance, sorting, etc.
- **Traits**: `src/traits/` defines common behaviors across different components
- **Utils**: `src/utils/` provides utility functions for paths, URLs, and vectors

## Development Commands

### Rust (Core Application)
- **Build**: `cargo build`
- **Run**: `cargo run -- [args]` or `just run [args]`
- **Test**: `cargo test` or `just test`
- **Debug**: `RUST_LOG=debug cargo run -- [args]` or `just debug [args]`
- **Release**: `cargo build --release` or `just release`

### Frontend/Documentation (JavaScript/TypeScript)
- **Lint**: `pnpm lint` (ESLint)
- **Format**: `pnpm format` (Prettier)
- **Check formatting**: `pnpm format:check`
- **All checks**: `pnpm checks` (lint + format check)
- **Fix linting**: `pnpm lint:fix`

### Project Management
- **Install dependencies**: `just install` (installs for all sub-projects)
- **Pre-commit setup**: `just pre-commit` (installs Git hooks)
- **Lint all**: `just lint` (runs pre-commit on all files)

## Key Configuration

The application uses:
- **Configuration files**: `.pls.yml` files for customization (handled by `ConfMan`)
- **Command-line args**: Parsed via `clap` crate
- **Environment**: `RUST_LOG` for debug logging
- **Package manager**: `pnpm` for JavaScript dependencies

## Multi-Language Setup

This is a polyglot project with:
- **Rust**: Main application (`src/`, `Cargo.toml`)
- **JavaScript/TypeScript**: Documentation site (`docs/`, Astro-based)
- **Python**: Examples and utilities (`examples/`, PDM-managed)
- **Just**: Task runner (`justfile` for automation)

## Terminal Graphics

The application detects and supports Kitty's terminal graphics protocol for enhanced visual output. This is handled in `src/gfx/` with runtime detection.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ env_logger = { version = "0.11.5", default-features = false }
figment = { version = "0.10.10", features = ["yaml", "test"] }
git2 = { version = "0.19.0", default-features = false }
home = "0.5.5"
lazy_static = "1.4.0"
libc = "0.2.158"
log = { version = "0.4.19", features = ["release_max_level_off"] }
number_prefix = "0.4.0"
Expand Down
8 changes: 5 additions & 3 deletions src/args/dir_group.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::args::input::Input;
use crate::enums::DetailField;
use crate::exc::Exc;
use crate::models::{Node, OwnerMan};
use crate::models::{GitMan, Node, OwnerMan};
use crate::traits::Imp;
use crate::PLS;
use log::debug;
Expand Down Expand Up @@ -44,6 +44,7 @@ impl DirGroup {
pub fn entries(
&self,
owner_man: &mut OwnerMan,
git_man: &mut GitMan,
) -> Result<Vec<HashMap<DetailField, String>>, Exc> {
let mut nodes = self.nodes()?;
if PLS.args.collapse {
Expand All @@ -61,6 +62,7 @@ impl DirGroup {
&self.input.conf.entry_const,
&[],
None,
git_man,
)
})
.collect();
Expand All @@ -83,7 +85,7 @@ impl DirGroup {
///
/// If any criteria is not met, the node is not to be rendered and `None` is
/// returned.
fn node(&self, entry: DirEntry) -> Option<Node> {
fn node(&self, entry: DirEntry) -> Option<Node<'_>> {
let name = entry.file_name();
debug!("Checking visibility of name {name:?}.");
let haystack = name.as_bytes();
Expand Down Expand Up @@ -128,7 +130,7 @@ impl DirGroup {
///
/// Unlike [`FilesGroup`](crate::args::files_group::FilesGroup), this
/// function filters out nodes based on visibility.
fn nodes(&self) -> Result<Vec<Node>, Exc> {
fn nodes(&self) -> Result<Vec<Node<'_>>, Exc> {
let entries = self.input.path.read_dir().map_err(Exc::Io)?;

let entries = entries
Expand Down
7 changes: 4 additions & 3 deletions src/args/files_group.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::args::input::Input;
use crate::config::{Conf, ConfMan};
use crate::enums::DetailField;
use crate::models::{Node, OwnerMan};
use crate::models::{GitMan, Node, OwnerMan};
use crate::utils::paths::common_ancestor;
use log::debug;
use std::collections::HashMap;
Expand Down Expand Up @@ -54,7 +54,7 @@ impl FilesGroup {
/// Since individual nodes are not nested, the function uses each node's
/// [`Node::row`] instead of the flattened output of each node's
/// [`Node::entries`].
pub fn entries(&self, owner_man: &mut OwnerMan) -> Vec<HashMap<DetailField, String>> {
pub fn entries(&self, owner_man: &mut OwnerMan, git_man: &mut GitMan) -> Vec<HashMap<DetailField, String>> {
self.nodes()
.iter()
.map(|(node, conf)| {
Expand All @@ -64,6 +64,7 @@ impl FilesGroup {
&self.parent_conf.app_const,
&conf.entry_const,
&[],
git_man,
)
})
.collect()
Expand All @@ -79,7 +80,7 @@ impl FilesGroup {
/// does not filter out nodes based on their visibility. This is because the
/// files in this group have been explicitly provided by the user and should
/// be rendered regardless of their visibility.
fn nodes(&self) -> Vec<(Node, &Conf)> {
fn nodes(&self) -> Vec<(Node<'_>, &Conf)> {
self.inputs
.iter()
.map(|input| {
Expand Down
11 changes: 6 additions & 5 deletions src/args/group.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::config::{Conf, ConfMan};
use crate::enums::{DetailField, Typ};
use crate::exc::Exc;
use crate::fmt::render;
use crate::models::OwnerMan;
use crate::models::{GitMan, OwnerMan};
use crate::output::{Grid, Table};
use crate::PLS;
use std::collections::HashMap;
Expand Down Expand Up @@ -53,7 +53,7 @@ impl Group {
groups
}

pub fn render(&self, show_title: bool, owner_man: &mut OwnerMan) -> Result<(), Exc> {
pub fn render(&self, show_title: bool, owner_man: &mut OwnerMan, git_man: &mut GitMan) -> Result<(), Exc> {
if show_title {
if let Self::Dir(group) = self {
println!(
Expand All @@ -63,7 +63,7 @@ impl Group {
}
}

let entries = self.entries(owner_man)?;
let entries = self.entries(owner_man, git_man)?;

if PLS.args.grid {
let grid = Grid::new(entries);
Expand Down Expand Up @@ -93,10 +93,11 @@ impl Group {
pub fn entries(
&self,
owner_man: &mut OwnerMan,
git_man: &mut GitMan,
) -> Result<Vec<HashMap<DetailField, String>>, Exc> {
match self {
Self::Dir(group) => group.entries(owner_man),
Self::Files(group) => Ok(group.entries(owner_man)),
Self::Dir(group) => group.entries(owner_man, git_man),
Self::Files(group) => Ok(group.entries(owner_man, git_man)),
}
}
}
3 changes: 3 additions & 0 deletions src/config/entry_const.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ pub struct EntryConst {
pub blocks_style: String,
/// mapping of timestamp fields to the human-readable format
pub timestamp_formats: HashMap<DetailField, String>,
/// style for git status
pub git_style: String,
/// mapping of symlink state to more symlink state info (including style)
pub symlink: HashMap<SymState, SymlinkInfo>,
}
Expand Down Expand Up @@ -114,6 +116,7 @@ impl Default for EntryConst {
)
})
.collect(),
git_style: String::from("cyan"),
symlink: [
(SymState::Ok, "󰁔", "magenta", ""), // nf-md-arrow_right
(SymState::Broken, "󱞣", "red", "strikethrough"), // nf-md-arrow_down_right
Expand Down
Loading