turbo-editors/turbo-corepublic Fork 0
v1.0.0
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-core.git
git clone ssh://git@rickub.com/turbo-editors/turbo-core.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

🛟 Updated. 28d5985 · on v1.0.0 · k33g · 15h ago
README.md · 66 lines · 3.5 KBmarkdown
Blame HistoryOpen raw

snippets

Reads the reusable pieces of text a project and a user keep in TOML, and groups them for a menu to show.

Imports the standard library and the TOML parser, and nothing else — no tcell, no ui, no app. It is tested by writing files and reading them back.

Two files, and why the project wins

File Holds
<project>/<p.ProjectDir()>/snippets.toml The project's
$<p.SnippetDirEnvVar()>/snippets.toml, else <p.UserDir()>/snippets.toml The user's own

Both are read, the user's first. Your snippets should follow you between projects; a project's should arrive with a checkout — neither alone is the whole answer.

Where a group and name clash, the project's replaces the user's. It is the more specific of the two statements, and the one a team agreed on. A same-name-different-group pair is two different snippets, not a clash.

A missing file is not an error — most projects have none, and a user may have none either. A file that is present but unreadable is an error, so a typo is reported rather than silently dropping every snippet in it.

Groups come out in file order

Groups(language) gathers the snippets that apply into groups, and both the groups and the snippets inside them keep the order they were read. That is what makes the menu match the file: someone reordering the file sees the menu reorder.

A snippet naming no languages applies everywhere — the common case is a licence header or a TODO, and making people list every language for that would be worse than showing a few too many. A group left with nothing after filtering does not appear at all.

The starter file

Create writes a commented file with worked examples and refuses to overwrite one that already exists. The bodies in the template contain the two characters \ and t, not a tab: TOML interprets the escape when it reads the file, and a real tab would leave the template looking like whatever a reader's editor does with tabs. TestTheCreatedFilesTabsSurviveTOML is what holds that.

It writes through a temporary file in the same directory, renamed into place — a rename is only atomic within one filesystem, and an interrupted write must leave the previous file intact.

Public API

Name What it does
FileName "snippets.toml". Both directories come from the profile.Profile every function here takes.
Snippet{Name, Body, Group, Languages} One piece of text
Group{Name, Snippets} One submenu's worth
List Everything read, in order
(List) Len() int How many there are in total
(List) Groups(language string) []Group Those applying to a language, gathered into groups
Load(projectDir) (List, error) Reads both files; a missing one is not an error
Create(projectDir) (string, error) Writes the starter file; ErrExists rather than overwriting
Exists(projectDir) bool Whether the project has a file to read
ProjectPath(p, projectDir) string <projectDir>/<p.ProjectDir()>/snippets.toml
UserDir(p) / UserPath(p) string Where the user's own live, or ""
ErrExists The one condition callers act on rather than report
list, err := snippets.Load(".")
if err != nil {
    return err // a file is there but unreadable, which is worth saying
}
for _, group := range list.Groups("go") {
    addSubmenu(group.Name, group.Snippets)
}

Tests

make test
go test ./snippets/

Every test sets TURBO_GO_SNIPPET_DIR to an empty directory, so a run never reads the snippets of whoever is running it.

 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
# snippets

Reads the reusable pieces of text a project and a user keep in TOML, and groups them for a menu to show.

Imports the standard library and the TOML parser, and nothing else — no tcell, no `ui`, no `app`. It is tested by writing files and reading them back.

## Two files, and why the project wins

| File | Holds |
| --- | --- |
| `<project>/<p.ProjectDir()>/snippets.toml` | The project's |
| `$<p.SnippetDirEnvVar()>/snippets.toml`, else `<p.UserDir()>/snippets.toml` | The user's own |

Both are read, the user's first. Your snippets should follow you between projects; a project's should arrive with a checkout — neither alone is the whole answer.

Where a `group` **and** `name` clash, the project's replaces the user's. It is the more specific of the two statements, and the one a team agreed on. A same-name-different-group pair is two different snippets, not a clash.

A missing file is not an error — most projects have none, and a user may have none either. A file that is **present but unreadable is** an error, so a typo is reported rather than silently dropping every snippet in it.

## Groups come out in file order

`Groups(language)` gathers the snippets that apply into groups, and both the groups and the snippets inside them keep the order they were read. That is what makes the menu match the file: someone reordering the file sees the menu reorder.

A snippet naming no `languages` applies everywhere — the common case is a licence header or a `TODO`, and making people list every language for that would be worse than showing a few too many. A group left with nothing after filtering does not appear at all.

## The starter file

`Create` writes a commented file with worked examples and refuses to overwrite one that already exists. The bodies in the template contain the two characters `\` and `t`, not a tab: TOML interprets the escape when it reads the file, and a real tab would leave the template looking like whatever a reader's editor does with tabs. `TestTheCreatedFilesTabsSurviveTOML` is what holds that.

It writes through a temporary file in the same directory, renamed into place — a rename is only atomic within one filesystem, and an interrupted write must leave the previous file intact.

## Public API

| Name | What it does |
| --- | --- |
| `FileName` | `"snippets.toml"`. Both directories come from the `profile.Profile` every function here takes. |
| `Snippet{Name, Body, Group, Languages}` | One piece of text |
| `Group{Name, Snippets}` | One submenu's worth |
| `List` | Everything read, in order |
| `(List) Len() int` | How many there are in total |
| `(List) Groups(language string) []Group` | Those applying to a language, gathered into groups |
| `Load(projectDir) (List, error)` | Reads both files; a missing one is not an error |
| `Create(projectDir) (string, error)` | Writes the starter file; `ErrExists` rather than overwriting |
| `Exists(projectDir) bool` | Whether the project has a file to read |
| `ProjectPath(p, projectDir) string` | `<projectDir>/<p.ProjectDir()>/snippets.toml` |
| `UserDir(p) / UserPath(p) string` | Where the user's own live, or `""` |
| `ErrExists` | The one condition callers act on rather than report |

```go
list, err := snippets.Load(".")
if err != nil {
    return err // a file is there but unreadable, which is worth saying
}
for _, group := range list.Groups("go") {
    addSubmenu(group.Name, group.Snippets)
}
```

## Tests

```sh
make test
go test ./snippets/
```

Every test sets `TURBO_GO_SNIPPET_DIR` to an empty directory, so a run never reads the snippets of whoever is running it.