1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
/* Projects on disk, and the workspaces they open as.
*
* A *project* is a directory somebody works in. A *workspace* is the tab it
* currently occupies in a session. This file is about the first: finding them,
* naming them, and saying where each one's layout lives. The second lives in
* app.c, because a workspace is tabs.
*
* The list is derived when it is asked for rather than watched. One readdir per
* root plus two faccessat per entry is forty checkouts for eighty-one syscalls,
* which is nothing next to the keystroke that asked -- and an answer nobody
* remembered cannot be stale, which no watcher can promise on a bind mount.
*/
#ifndef SLOSH_PROJECT_H
#define SLOSH_PROJECT_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/* The file a project uses to say what it needs. One name, so there is no
* precedence question. `.layout` rather than `.kdl`: zellij speaks the same
* syntax under the same extension with a different schema, and an extension
* that names the wrong owner is worse than one that does not name the
* parser -- the file says what it is, `--check` says how it was read. */
#define PROJECT_LAYOUT_FILE "slosh.layout"
/* The spelling this replaced. Still *found* by the project scan, so nobody's
* checked-in workspace vanishes on upgrade; everything slosh writes is the
* new name, and the new name wins where both exist. */
#define PROJECT_LAYOUT_FILE_OLD "slosh.layout.kdl"
/* How many projects one session will look at. A picker is not a filesystem
* browser: past this many the answer to "which project" is a search, not a
* list, and the list is what this is for. */
#define PROJECTS_MAX 128
typedef struct {
char name[64]; /* the directory's own name, which is what people call it */
char path[512]; /* absolute, resolved */
char slug[64]; /* `project:name.hash8` -- the purpose a workspace carries */
/* The project's own layout file, or "" for one inferred from a `.git`. The
* distinction is the whole of what "declared" means here, and the picker
* shows it: a project with no layout is an invitation to save one. */
char layout[512];
int64_t mtime; /* of `layout`, 0 when there is none. Drift is derivable from
* this and a pane's `since` without anything storing it. */
} project_t;
/* Every project under `roots`, sorted by name. `roots` is a NUL-terminated
* array of directories; `depth` is how many levels below each to look, and a
* directory that *is* a project is never descended into -- which is what keeps
* this off node_modules without a rule about node_modules.
*
* A root that does not exist is not an error. Neither is one with nothing in
* it: the setting is a statement of where you keep things, not a promise that
* you have any. */
size_t project_scan(const char *const *roots, int depth, project_t *out,
size_t max);
/* One project by name, or by path when `name` looks like one (it has a `/`).
* False when no root holds it -- which is also the containment check, since a
* path that no root holds is a path this session will not write to. */
bool project_find(const char *const *roots, int depth, const char *name,
project_t *out);
/* The purpose a workspace for this directory carries: `project:<name>.<hash>`
* of the resolved path. Hashed on the path rather than the name so that two
* worktrees of one repo are two workspaces, which is what they are. */
void project_slug(const char *path, const char *name, char *out, size_t cap);
#endif /* SLOSH_PROJECT_H */
|