Module: Pikuri::Workspace::Listing
- Defined in:
- lib/pikuri/workspace/listing.rb
Overview
Renders a directory as rows a model can orient from — name-sorted,
directories first, sizes ls -h style:
Listing.render(project_root, filesystem: fs, depth: 2) # =>
lib/
pikuri/ [3 dirs, 1 file]
version.rb 48B
spec/ [empty]
README.md 4.2K
broken.md [Errno::ENOENT]
A directory shows [3 dirs, 1 file] exactly where its contents aren't
on screen — the deepest rendered level — so lib/ above, expanded, never
restates children that are already rows. Counts are immediate children,
not recursive.
A row that can't be measured prints its errno class rather than a prose
word or a fake zero: ENOENT is a dangling symlink or an entry that
vanished mid-listing, EACCES a directory you can't open, ELOOP a
symlink cycle. Each implies a different follow-up, and a model reads
EACCES unaided. Only the root failing raises.
A credential path is [denied] and is never descended into or +stat+ed
(+.ssh/ [denied]+). Shown rather than omitted because this listing
answers "what is here", where a silent omission is a lie the model
would burn turns re-probing — and ~/.ssh existing is not the secret;
its filenames and bytes are. That is why Filesystem#denied? is a
required constructor-level input here: a default of "nothing is
denied" would let a new caller leak by forgetting.
Implementation details
Walks with Dir.children, not the rg --files prefix derivation
Search::Glob uses, so symlinked / gitignored-only / empty directories
can't silently vanish from the answer. The price is a caller-visible one:
nothing evaluates .gitignore here, so everything shows except .git/ —
ls behaviour, and the one place the file tools diverge on gitignore.
Full argument in DECISIONS.md D_directory_listing.
Constant Summary collapse
- MAX_BYTES =
Returns hard byte cap on rendered output. Same value as Search::Glob::MAX_BYTES; re-declared rather than cross-referenced because Zeitwerk's eager-load order between siblings isn't guaranteed.
50 * 1024
- MAX_BYTES_LABEL =
Returns human-readable MAX_BYTES for truncation messages.
"#{MAX_BYTES / 1024} KB"- DEFAULT_DEPTH =
Returns default
depth— immediate children only. 1- MAX_DEPTH =
Returns deepest
depthhonoured; larger values clamp down. Past three levels a listing stops being orientation and becomes the whole-tree dump MAX_BYTES would eat anyway. 3- INDENT =
Returns per-level indent for nested rows.
' '- GAP =
Returns separator between an entry's name and its size / summary column.
' '- GIT_DIR =
Returns the one entry never listed, matching Search::Glob's
--glob '!.git/*'. '.git'- SIZE_UNITS =
Returns size suffixes,
ls -hstyle. %w[B K M G T].freeze
- DENIED =
Returns stands in for the size / summary column on a path under Filesystem#denied?.
'[denied]'
Class Method Summary collapse
-
.render(dir, filesystem:, depth: DEFAULT_DEPTH) ⇒ String
Render +dir+'s entries.
Class Method Details
.render(dir, filesystem:, depth: DEFAULT_DEPTH) ⇒ String
Render +dir+'s entries.
87 88 89 90 91 92 93 94 95 96 |
# File 'lib/pikuri/workspace/listing.rb', line 87 def self.render(dir, filesystem:, depth: DEFAULT_DEPTH) rows = child_rows(dir.to_s, filesystem: filesystem, depth: depth.clamp(1, MAX_DEPTH), indent: '') return '' if rows.empty? content, marker = Search::Utils.head_truncate( rows.join("\n"), max_bytes: MAX_BYTES, hint: 'list a subdirectory instead' ) content + marker end |