pls
2 |
3 |
6 |
--------------------------------------------------------------------------------
/src/output.rs:
--------------------------------------------------------------------------------
1 | mod cell;
2 | mod grid;
3 | mod table;
4 |
5 | pub use cell::Cell;
6 | pub use grid::Grid;
7 | pub use table::Table;
8 |
--------------------------------------------------------------------------------
/examples/src/examples/__init__.py:
--------------------------------------------------------------------------------
1 | from importlib.metadata import version
2 |
3 |
4 | __pkg__ = __package__
5 | __version__ = version(__pkg__)
6 |
--------------------------------------------------------------------------------
/src/.pls.yml:
--------------------------------------------------------------------------------
1 | specs:
2 | - pattern: \.rs$
3 | collapse:
4 | ext: "" # Collapse module index files under their corresponding directories.
5 |
--------------------------------------------------------------------------------
/src/traits.rs:
--------------------------------------------------------------------------------
1 | mod detail;
2 | mod imp;
3 | mod name;
4 | mod sym;
5 |
6 | pub use detail::Detail;
7 | pub use imp::Imp;
8 | pub use name::Name;
9 | pub use sym::Sym;
10 |
--------------------------------------------------------------------------------
/examples/src/examples/confs/importance.yml:
--------------------------------------------------------------------------------
1 | # .pls.yml
2 | app_const:
3 | imp_styles:
4 | - [-2, "red"]
5 | - [-1, "yellow"]
6 | - [1, "green"]
7 | - [2, "bright_magenta"]
8 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*]
2 | insert_final_newline = true
3 | indent_size = 2
4 |
5 | [*.py]
6 | indent_size = 4
7 |
8 | [*.rs]
9 | indent_size = 4
10 |
11 | [justfile]
12 | indent_size = 4
13 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | # Build artifacts
2 | dist/
3 |
4 | # Astro generated types
5 | .astro/
6 |
7 | # Dependencies
8 | node_modules/
9 |
10 | # Generated examples
11 | src/examples/
12 |
--------------------------------------------------------------------------------
/docs/src/styles/layout.css:
--------------------------------------------------------------------------------
1 | .hero {
2 | grid-template-columns: 1fr;
3 | }
4 |
5 | .hero .copy {
6 | align-items: center;
7 | }
8 |
9 | .hero .actions {
10 | justify-content: center;
11 | }
12 |
--------------------------------------------------------------------------------
/examples/.gitignore:
--------------------------------------------------------------------------------
1 | # Python cache
2 | __pycache__/
3 | *.py[cod]
4 |
5 | # Environment files
6 | .env
7 |
8 | # Virtual environment
9 | .venv/
10 |
11 | # PDM
12 | .pdm-python
13 | *.egg-info/
14 |
--------------------------------------------------------------------------------
/docs/src/styles/typography.css:
--------------------------------------------------------------------------------
1 | span.math {
2 | font-family: serif;
3 | font-style: italic;
4 | }
5 |
6 | pre[data-language="bash"] .ec-line .code::before {
7 | content: "$ ";
8 | opacity: 0.5;
9 | }
10 |
--------------------------------------------------------------------------------
/docs/src/styles/color.css:
--------------------------------------------------------------------------------
1 | /* Additional colors */
2 | :root {
3 | --color-background-tint: rgba(255, 255, 255, 0.03);
4 | }
5 |
6 | :root[data-theme="light"] {
7 | --color-background-tint: rgba(0, 0, 0, 0.03);
8 | }
9 |
--------------------------------------------------------------------------------
/src/models.rs:
--------------------------------------------------------------------------------
1 | mod node;
2 | mod owner;
3 | mod perm;
4 | mod pls;
5 | mod spec;
6 | mod window;
7 |
8 | pub use node::Node;
9 | pub use owner::OwnerMan;
10 | pub use perm::Perm;
11 | pub use pls::Pls;
12 | pub use spec::Spec;
13 | pub use window::Window;
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # IDE junk
2 | .vscode
3 | .idea
4 |
5 | # Node.js modules
6 | node_modules
7 |
8 | # Build artifacts
9 | target
10 |
11 | # Profiling artifacts
12 | flamegraph.svg
13 | report.json
14 | report.html
15 |
16 | # pre-commit executable
17 | pre-commit.pyz
18 |
--------------------------------------------------------------------------------
/examples/src/examples/confs/outer.yml:
--------------------------------------------------------------------------------
1 | # .pls.yml
2 | app_const:
3 | table:
4 | header_style: red bold underline
5 | entry_const:
6 | user_styles:
7 | curr: green bold
8 | other: green bold
9 | specs:
10 | - pattern: ^a$
11 | style: red underline
12 |
--------------------------------------------------------------------------------
/docs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "astro/tsconfigs/strict",
3 | "compilerOptions": {
4 | "jsx": "preserve",
5 | "baseUrl": ".",
6 | "paths": {
7 | "@/components/*": ["src/components/*"],
8 | "@/examples/*": ["src/examples/*"]
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/ext.rs:
--------------------------------------------------------------------------------
1 | //! This module contains extensions to the standard library.
2 | //!
3 | //! The public interface of this module consists of the following traits:
4 | //!
5 | //! * [`Abs`]
6 | //! * [`Ctime`]
7 |
8 | mod abs;
9 | mod ctime;
10 |
11 | pub use abs::Abs;
12 | pub use ctime::Ctime;
13 |
--------------------------------------------------------------------------------
/examples/src/examples/hero.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 |
3 | from examples.utils.main import write_out
4 |
5 |
6 | PROJECT_ROOT = Path(__file__).parents[3]
7 |
8 |
9 | def hero():
10 | write_out("--det=all", bench=PROJECT_ROOT, dest_name="hero")
11 |
12 |
13 | if __name__ == "__main__":
14 | hero()
15 |
--------------------------------------------------------------------------------
/examples/src/examples/confs/inner.yml:
--------------------------------------------------------------------------------
1 | # .pls.yml
2 | app_const:
3 | table:
4 | header_style: blue
5 | entry_const:
6 | nlink_styles:
7 | file_sing: blue
8 | timestamp_formats:
9 | mtime: "pls is a prettier and powerful ls(1)
13 | for the pros.
14 | actions:
15 | - text: Get started
16 | link: /guides/get_started/
17 | icon: right-arrow
18 | variant: primary
19 | - text: Learn more
20 | link: /about/intro/
21 | icon: open-book
22 | - text: Get source
23 | link: https://github.com/pls-rs/pls/
24 | icon: github
25 | ---
26 |
27 | import { Content as Hero } from "@/examples/hero/hero.mdx";
28 |
29 | ```bash
30 | pls --det all
31 | ```
32 |
33 | (path: P) -> PathBuf
43 | where
44 | P: AsRef(url: S, text: Option) -> String
20 | where
21 | S: AsRef
pls
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | [`pls`](https://pls.cli.rs/) is a prettier and powerful `ls(1)` for the pros.
10 | The "p" stands for
11 |
12 | - **pretty**: `pls` output is cleaner, friendlier and more colorful.
13 | - **powerful**: `pls` provides more features than the competition.
14 | - **performant**: `pls` is speedy and performant (written in Rust).
15 | - **practical**: `pls` has sensible defaults and an effortless interface.
16 | - **petite**: `pls` is a small, single-file, binary executable.
17 | - **pliable**: `pls` can be extensively tweaked by power users and pros.
18 | - **personable**: `pls` prioritises consumption by humans over scripts.
19 |
20 | Pick whichever adjective helps you remember the command name.
21 |
22 | For more information, see the [documentation](https://pls.cli.rs/).
23 |
--------------------------------------------------------------------------------
/src/traits/sym.rs:
--------------------------------------------------------------------------------
1 | use crate::enums::{SymTarget, Typ};
2 | use crate::exc::Exc;
3 | use crate::models::Node;
4 | use std::fs;
5 |
6 | pub trait Sym {
7 | fn target(&self) -> Option
9 |
13 |
14 | {required && *}
15 | {type}
16 |
24 | Properties of
26 | {subfieldsType || type}
25 |
37 | Examples of
39 | {title}
38 | "]
7 |
8 | license = "GPL-3.0-or-later"
9 |
10 | homepage = "https://pls.cli.rs/"
11 | repository = "https://github.com/pls-rs/pls"
12 | documentation = "https://pls.cli.rs/"
13 | readme = "README.md"
14 | exclude = [".github/", "/readme_assets", "/justfile", "/.gitignore"]
15 |
16 | keywords = ["cli", "terminal", "posix", "ls"]
17 | categories = ["command-line-utilities"]
18 |
19 | edition = "2021"
20 | rust-version = "1.80.0"
21 |
22 | [[bin]]
23 | name = "pls"
24 |
25 | [dependencies]
26 | base64 = "0.22.1"
27 | clap = { version = "4.3.11", features = ["derive", "wrap_help"] }
28 | colored = "2.0.0"
29 | crossterm = { version = "0.28.1", default-features = false }
30 | env_logger = { version = "0.11.5", default-features = false }
31 | figment = { version = "0.10.10", features = ["yaml", "test"] }
32 | git2 = { version = "0.19.0", default-features = false }
33 | home = "0.5.5"
34 | libc = "0.2.158"
35 | log = { version = "0.4.19", features = ["release_max_level_off"] }
36 | number_prefix = "0.4.0"
37 | path-clean = "1.0.1"
38 | regex = { version = "1.8.4", default-features = false, features = ["std", "perf"] }
39 | resvg = { version = "0.43.0", default-features = false }
40 | serde = { version = "1.0.164", features = ["derive"] }
41 | serde_regex = "1.1.0"
42 | shellexpand = { version = "3.1.0", default-features = false, features = ["base-0"] }
43 | time = { version = "0.3.22", default-features = false, features = ["std", "alloc", "local-offset", "formatting"] }
44 | unicode-segmentation = "1.10.1"
45 | uzers = { version = "0.12.1", default-features = false, features = ["cache"] }
46 | xterm-query = "0.5.2"
47 |
48 | [profile.release]
49 | # Reference: https://github.com/johnthagen/min-sized-rust
50 | codegen-units = 1
51 | panic = "abort"
52 | lto = true
53 | strip = true
54 |
55 | [package.metadata.release]
56 | sign-commit = true
57 | sign-tag = true
58 | publish = false
59 | push = false
60 | pre-release-commit-message = "Release {{version}}"
61 | tag-message = "Release {{crate_name}} version {{version}}"
62 |
--------------------------------------------------------------------------------
/src/traits/name.rs:
--------------------------------------------------------------------------------
1 | use crate::models::Node;
2 | use std::path::PathBuf;
3 |
4 | pub trait Name {
5 | fn ext(&self) -> String;
6 | fn stem(&self) -> String;
7 | fn cname(&self) -> String;
8 |
9 | fn aligned_name(&self) -> String;
10 | }
11 |
12 | impl Name for Node<'_> {
13 | // ===========
14 | // Sort fields
15 | // ===========
16 |
17 | /// Get the extension for a node.
18 | ///
19 | /// Returns a blank string if the node does not have an extension.
20 | fn ext(&self) -> String {
21 | self.path
22 | .extension()
23 | .unwrap_or_default()
24 | .to_string_lossy()
25 | .to_string()
26 | }
27 |
28 | /// Get the name for the node, without the extension, if any.
29 | ///
30 | /// Returns the full name if the node does not have an extension.
31 | fn stem(&self) -> String {
32 | self.path
33 | .file_stem()
34 | .unwrap_or_default()
35 | .to_string_lossy()
36 | .to_string()
37 | }
38 |
39 | /// Get the canonical name for the node.
40 | ///
41 | /// The canonical name is the name of the node, stripped of leading symbols
42 | /// and normalised to lowercase.
43 | fn cname(&self) -> String {
44 | self.name
45 | .to_lowercase()
46 | .trim_start_matches(|c: char| !c.is_alphanumeric())
47 | .to_string()
48 | }
49 |
50 | // ===============
51 | // Name components
52 | // ===============
53 |
54 | /// Get the name of the node when aligning for leading dots.
55 | ///
56 | /// If the node name starts with a dot, the dot is dimmed. If not, the name
57 | /// is left-padded with a space to line up the alphabetic characters.
58 | fn aligned_name(&self) -> String {
59 | let path = PathBuf::from(&self.display_name);
60 | if let Some(name) = path.file_name() {
61 | let name = name.to_string_lossy();
62 |
63 | // 'clear' ensures that the dot and padding spaces are not formatted.
64 | let aligned_name = if name.starts_with('.') {
65 | format!(" .>{}", name.strip_prefix('.').unwrap())
66 | } else {
67 | format!(" >{}", name)
68 | };
69 |
70 | if let Some(parent) = path.parent() {
71 | return parent.join(aligned_name).to_string_lossy().to_string();
72 | }
73 | }
74 | self.display_name.clone()
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/models/spec.rs:
--------------------------------------------------------------------------------
1 | use crate::enums::Collapse;
2 | use regex::bytes::{Regex, RegexBuilder};
3 | use serde::{Deserialize, Serialize};
4 |
5 | /// Represents the specification for identifying and styling a node.
6 | ///
7 | /// Specs are the ideological core of `pls` and the key differentiating factor
8 | /// from other tools.
9 | #[derive(Debug, Serialize, Deserialize)]
10 | pub struct Spec {
11 | /// a regex pattern to match against the node's name
12 | #[serde(with = "serde_regex")]
13 | pub pattern: Regex,
14 | /// names of the icon to use for the node
15 | pub icons: Option