# `Ascii.Pieces.FileTree`
[🔗](https://github.com/houllette/ascii_art/blob/main/lib/ascii/pieces/file_tree.ex#L1)

A tree listing opened folder by folder, then closed again.

A project listed the way `tree -F` prints it. As upstream draws it, a
cursor walks down the list opening one folder at a time, then closes them
from the bottom up. With `walk: false` it is a tree to use instead: your
files, the folders you open, and a cursor that keys and clicks move, in a
terminal UI or a LiveView.

## Options

Give them to `Ascii.new/2` as `options:`, and new ones while it plays
with `Ascii.put_options/2` (or a Phoenix player's `options`): the tree
is laid out anew from them.

  * `root` - the first line, above the listing. Default `"."`.
  * `entries` - the listing as text: one entry a line, two spaces of
    indent a level, folders ending in "/". Default: upstream's small web
    project. Being text, it is cut at 256 characters: for a real tree,
    give `paths`.
  * `paths` - the entries as a list of paths, as `Path.wildcard/1` gives
    them, in place of `entries`: each path's folders are made from it, a
    path ending in "/" is a folder, and every folder's entries come in the
    order first given (sort them for `tree`'s order). Up to 256 paths.
    Default `[]`, for `entries`. `entries/1` gives the text for paths.
  * `walk` - `true` (the default) plays upstream's walk; `false` draws the
    tree still, as `open` and `cursor` say, and takes keys and clicks.
  * `open` - with `walk: false`, the paths of the open folders, as
    `["src/", "src/lib/"]`. An entry's path is its name after its
    folders', `"src/lib/fetch.ts"`, a folder's ending in "/". Default `[]`:
    only the root's entries show.
  * `cursor` - with `walk: false`, the path of the entry under the cursor,
    drawn as the walk draws it, the branch turned arrow. One inside a
    closed folder sits on the nearest folder shown; `""` (the default), or
    a path not in the tree, is no cursor. A listing longer than its rows
    scrolls to keep the cursor in the middle of them.
  * `cols`, `rows` - the frame's size, 12 to 200 by 4 to 100; 36 by 23 by
    default. The listing is centred in the width, between the root and a
    blank row over the count.

## Colours

In one ink, or a gradient, with `:ink` and `:gradient` (see
`Ascii.Ink`); part by part with `:parts` (see `Ascii.Parts`),
`parts: true` taking the colours suggested here:

  * `root` - the first line.
  * `branch` - the tree's lines, grey.
  * `cursor` - the branch turned arrow, yellow.
  * `folder` - a folder's name, blue.
  * `file`, and `file.<extension>` - a file's name, and by its extension
    in lowercase (`file.ts` for `index.ts`; a name without one, or a
    dotfile, is just `file`). Suggested: `file.ex` and `file.exs` purple,
    `file.ts` blue, `file.js` and `file.json` yellow, `file.css` pink,
    `file.md` grey; any other in the ink. `%{"file.rs" => "#dea584"}`
    colours a kind of your own.
  * `marker` - the `+` on a closed folder, yellow.
  * `summary` - the count on the last row, grey.

## Keys and clicks

With `walk: false`, `Ascii.handle_event/2` takes keys and clicks,
changing `cursor` and `open`, from which the app reads the choice:

  * ↑ and ↓ move the cursor, as do home, end, page up and page down;
    with no cursor, any of them puts it on the first entry (end, the
    last).
  * → or enter opens a closed folder, and → on an open one moves into it;
    ← closes an open folder, or moves to the folder an entry is in; enter
    and space open and close a folder.
  * A click on an entry's row puts the cursor on it, and opens or closes
    it if it is a folder.
  * Enter on a file is ignored, as is a key that would change nothing:
    the app has the key, and the file in `cursor`.

Walking, it takes none. In Phoenix, give the player `interactive` (and
`notify` to be told of each key and click); in ExRatatui,
`Ascii.ExRatatui.handle_event/3`; in a terminal,
`mix ascii.play file-tree --option walk=false --interactive`.

## Screen readers

`Ascii.describe/1` tells the tree and where the cursor is, as "file
tree of my_app: 2 folders, 4 files; at lib/my_app.ex". Walking, it gives
the piece's name and note.

## Example

```elixir
paths = ["lib/my_app.ex", "lib/my_app/repo.ex", "mix.exs", "README.md"]
options = %{walk: false, paths: paths, root: "my_app", open: ["lib/"], cursor: "lib/"}
tree = Ascii.new!("file-tree", options: options, parts: true)

{:ok, tree} = Ascii.handle_event(tree, {:key, "down"})
tree.options.cursor
#=> "lib/my_app.ex"

Ascii.describe(tree)
#=> "file tree of my_app: 2 folders, 4 files; at lib/my_app.ex"
```

```heex
<Ascii.Phoenix.live_player id="files" piece="file-tree" options={%{walk: false, paths: @paths}} parts interactive notify />
```

Ported from `src/pieces/file-tree.ts` in bas3line/ascii (MIT).

# `entries`

```elixir
@spec entries(paths :: [String.t()]) :: String.t()
```

`entries` text for a list of paths, as the `paths` option takes them: each
path's folders are made from it, a path ending in "/" (or holding others)
is a folder, and every folder's entries come in the order first given.

    iex> Ascii.Pieces.FileTree.entries(["lib/app.ex", "lib/app/repo.ex", "mix.exs", "priv/"])
    "lib/\n  app.ex\n  app/\n    repo.ex\nmix.exs\npriv/"

---

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