Files
sanspie 5eb1832907
ci-platforms / darwin amd64 smoke (push) Skipped
ci / build (amd64, linux, ubuntu-24.04) (push) Failing after 15m28s
ci / release (push) Skipped
ci-platforms / windows amd64 smoke (push) Skipped
ci-platforms / darwin arm64 smoke (push) Skipped
ci / checks (push) Successful in 7m47s
Complete Stage 12 for v0.12.0
2026-11-05 11:46:53 +03:00

110 lines
2.7 KiB
Go

package preview
import (
"aego/asset"
"aego/asset/pipeline"
"aego/core/ids"
"aego/project"
"crypto/sha256"
"encoding/binary"
"io/fs"
"maps"
"os"
"path"
"slices"
"strings"
)
const SourceKind asset.ArtifactKind = "editor.source"
type sourceFile struct {
info pipeline.AssetInfo
modified, size int64
}
// Editor source IDs are for navigation only. They never enter the import index,
// scene references, export manifests or the project sidecar files.
func (s *Service) RefreshSources() bool {
if s.d.Pipeline == nil || s.d.Docs == nil {
return false
}
r, err := os.OpenRoot(s.d.Pipeline.Abs("."))
if err != nil {
return false
}
defer r.Close()
entries := map[ids.AssetID]sourceFile{}
add := func(name string) {
if h := s.d.Docs.Handlers().Resolve(SourceKind, name, ""); h.ID == "asset.readonly" {
return
}
st, err := r.Lstat(name)
if err != nil || !st.Mode().IsRegular() {
return
}
raw := sha256.Sum256([]byte("aego.editor.source:" + name))
id := ids.AssetID{Hi: binary.BigEndian.Uint64(raw[:8]), Lo: binary.BigEndian.Uint64(raw[8:16])}
entries[id] = sourceFile{pipeline.AssetInfo{ID: id, Path: name, Name: path.Base(name), Kind: SourceKind}, st.ModTime().UnixNano(), st.Size()}
}
add(project.FileName)
if st, e := r.Lstat("native"); e == nil && st.IsDir() {
visited := 0
_ = fs.WalkDir(r.FS(), "native", func(name string, d fs.DirEntry, err error) error {
if err != nil {
return nil
}
visited++
if visited > 4096 || len(entries) > 512 {
return fs.SkipAll
}
base := d.Name()
if d.IsDir() && (strings.HasPrefix(base, ".") || base == "vendor" || base == "node_modules" || base == "generated") {
return fs.SkipDir
}
if d.Type().IsRegular() && strings.HasSuffix(base, ".go") {
add(name)
}
return nil
})
}
if s.sources != nil && maps.Equal(s.sources, entries) {
return false
}
for id, old := range s.sources {
if next, ok := entries[id]; !ok || next != old {
s.Forget(id)
}
}
s.sources = entries
return true
}
func (s *Service) SourceFiles() []pipeline.AssetInfo {
if s.sources == nil {
s.RefreshSources()
}
v := make([]pipeline.AssetInfo, 0, len(s.sources))
for _, a := range s.sources {
v = append(v, a.info)
}
slices.SortFunc(v, func(a, b pipeline.AssetInfo) int { return strings.Compare(a.Path, b.Path) })
return v
}
func (s *Service) SourceFile(id ids.AssetID) (pipeline.AssetInfo, bool) {
if s.sources == nil {
s.RefreshSources()
}
v, ok := s.sources[id]
return v.info, ok
}
func (s *Service) SourceID(rel string) (ids.AssetID, bool) {
if s.sources == nil {
s.RefreshSources()
}
for id, a := range s.sources {
if a.info.Path == rel {
return id, true
}
}
return ids.AssetID{}, false
}