# `Minga.Project.FileTree`
[🔗](https://github.com/jsmestad/minga/blob/main/lib/minga/project/file_tree.ex#L1)

Pure data structure for a navigable filesystem tree.

Holds the root path, a set of expanded directories, a cursor position,
and a show_hidden toggle. The flat list of visible entries is cached in
the struct after the first computation and invalidated whenever tree
state changes (expand, collapse, toggle_hidden, refresh, reveal).

No GenServer; the editor owns this struct in its state.

# `entry`

```elixir
@type entry() :: %{
  path: String.t(),
  name: String.t(),
  dir?: boolean(),
  depth: non_neg_integer(),
  last_child?: boolean(),
  guides: [boolean()]
}
```

A single visible entry in the tree.

The `guides` field is a list of booleans, one per ancestor depth level
(index 0 = depth 0, index 1 = depth 1, etc.). `true` means the ancestor
at that depth has more siblings below this entry (draw `│`), `false`
means it was the last child (draw blank). The renderer uses this plus
`last_child?` to pick `├──` vs `└──` at the entry's own depth.

# `t`

```elixir
@type t() :: %Minga.Project.FileTree{
  cursor: non_neg_integer(),
  entries: [entry()] | nil,
  expanded: MapSet.t(String.t()),
  git_status: Minga.Project.FileTree.GitStatus.status_map(),
  root: String.t(),
  show_hidden: boolean(),
  width: pos_integer()
}
```

# `collapse`

```elixir
@spec collapse(t()) :: t()
```

Collapses the directory at cursor, or if on a file/collapsed dir, collapses the parent.

# `collapse_all`

```elixir
@spec collapse_all(t()) :: t()
```

Collapses all directories, keeping only the root expanded. Resets cursor to 0.

# `ensure_entries`

```elixir
@spec ensure_entries(t()) :: t()
```

Returns the tree with entries guaranteed to be populated.

If entries are already cached, returns the tree unchanged.
Otherwise computes entries from the filesystem and caches them.
Use this when you need to read entries multiple times from the
same tree without redundant filesystem walks.

# `expand`

```elixir
@spec expand(t()) :: t()
```

Expands the directory at cursor. No-op on files or already-expanded dirs.

# `move_down`

```elixir
@spec move_down(t()) :: t()
```

Moves the cursor down by one entry, clamped to the last visible entry.

# `move_up`

```elixir
@spec move_up(t()) :: t()
```

Moves the cursor up by one entry.

# `new`

```elixir
@spec new(
  String.t(),
  keyword()
) :: t()
```

Creates a new file tree rooted at the given directory path.

# `refresh`

```elixir
@spec refresh(t()) :: t()
```

Refreshes the tree by rescanning the filesystem (clamps cursor).

# `refresh_git_status`

```elixir
@spec refresh_git_status(t()) :: t()
```

Refreshes git status for the tree root. Returns the updated tree.

# `reveal`

```elixir
@spec reveal(t(), String.t()) :: t()
```

Highlights the given file path in the tree by expanding its parent
directories and moving the cursor to it.

# `selected_entry`

```elixir
@spec selected_entry(t()) :: entry() | nil
```

Returns the entry at the current cursor position, or nil if empty.

This reads from the cached entries if available. For performance-sensitive
callers that will call this repeatedly, call `ensure_entries/1` first to
populate the cache; subsequent calls on the same struct will use it.

# `toggle_expand`

```elixir
@spec toggle_expand(t()) :: t()
```

Toggles expand/collapse for the entry at the cursor.

If the cursor is on a directory, toggles its expanded state.
If on a file, this is a no-op.

# `toggle_hidden`

```elixir
@spec toggle_hidden(t()) :: t()
```

Toggles visibility of hidden files (dotfiles).

# `visible_entries`

```elixir
@spec visible_entries(t()) :: [entry()]
```

Returns the flat list of currently visible entries.

Returns cached entries if available. Otherwise walks the directory tree
starting from root, descending into expanded directories. Results are
sorted: directories first, then files, both alphabetically. Hidden
files are excluded unless `show_hidden` is true.

Note: this returns only the list, not the updated struct. If the cache
was empty, the computed entries are not stored back in the struct.
Callers that need repeated access should call `ensure_entries/1` first
to populate the cache, then read `.entries` or call this function on
the returned struct.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
