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.

📦 Turbo Core f3ade8d · on v1.0.0 · k33g · 12h ago
agent_files.go · 71 lines · 2.4 KBGo Blame HistoryRaw
 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
71
// The project's files as an agent window offers them: what "@" lists in the
// box, and what a mention is resolved against when a prompt is sent.

package app

import (
	"io/fs"
	"path/filepath"

	"rickub.com/turbo-editors/turbo-core/acp"
)

// projectFileLimit caps how many files an agent window is offered.
//
// Past this the picker is a list to scroll rather than a list to choose from,
// and a walk of a monorepo on every "@" would make the box stutter. Typing one
// more letter after the "@" is the answer for a project this size, exactly as
// it is for the Open File dialog.
const projectFileLimit = 5000

// projectFiles lists the project for the agent windows.
//
// It walks on each call rather than caching: the picker asks once when "@" is
// typed and keeps the answer while it is open, and a list that never noticed a
// file the agent just created would be wrong at the moment it mattered.
func (a *App) projectFiles() []acp.Mention {
	return listProjectFiles(a.projectRoot(), projectFileLimit)
}

// listProjectFiles walks root and returns its regular files, relative to root
// with forward slashes, in the order the walk finds them. The .git directory
// is skipped for the reason the file tree skips it: nothing in it is meant to
// be read by hand, and it is large enough to bury everything else. The walk
// stops at limit files.
//
//	listProjectFiles("/src/p", 5000) // [{app/menus.go /src/p/app/menus.go} …]
func listProjectFiles(root string, limit int) []acp.Mention {
	var out []acp.Mention
	_ = filepath.WalkDir(root, func(path string, entry fs.DirEntry, err error) error {
		if err != nil || entry.IsDir() {
			return skipGit(entry, err)
		}
		if entry.Type().IsRegular() {
			out = appendFile(out, root, path)
		}
		if len(out) >= limit {
			return filepath.SkipAll
		}
		return nil
	})
	return out
}

// skipGit tells the walk to leave .git alone, and to carry on past a directory
// it could not read rather than stop the whole listing for it.
func skipGit(entry fs.DirEntry, err error) error {
	if err == nil && entry.Name() == ".git" {
		return filepath.SkipDir
	}
	return nil
}

// appendFile adds one file to the list, named relative to the project with
// forward slashes whatever the platform, because that is how it is typed.
func appendFile(files []acp.Mention, root, path string) []acp.Mention {
	relative, err := filepath.Rel(root, path)
	if err != nil {
		return files
	}
	return append(files, acp.Mention{Name: filepath.ToSlash(relative), Path: path})
}