summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorewy <ewy0@protonmail.com>2026-04-29 22:34:47 +0200
committerewy <ewy0@protonmail.com>2026-04-29 22:34:47 +0200
commit42fb6efd01e3640ea9d15dc1e0a072c1ea8295b1 (patch)
treeebf351b66f0288c8e9f879c529a962b025bebf15
parent03a31799a872385d821130f46942fc13dae76774 (diff)
fix very embarrassing bug where -a didnt actually work
-rw-r--r--cache/cache.go81
-rw-r--r--cache/cache_test.go2
-rw-r--r--indexers/pikdex/hydrate_test.go4
-rw-r--r--main.go39
-rw-r--r--runner/create.go (renamed from testx/create.go)7
-rw-r--r--runner/create_test.go (renamed from testx/create_test.go)2
-rw-r--r--runner/stub.go7
-rw-r--r--search/search_test.go106
-rw-r--r--spool/spool.go11
-rw-r--r--viewport/viewport.go6
10 files changed, 149 insertions, 116 deletions
diff --git a/cache/cache.go b/cache/cache.go
index 328b1e9..39ee062 100644
--- a/cache/cache.go
+++ b/cache/cache.go
@@ -16,13 +16,38 @@ type Cache struct {
Entries []Entry
}
+type cacheInit struct{}
+
+// Path is the file path to the "contexts" cache file
+var Path string
+
+// FsPath is the Path with the leading slash removed, to be opened from fs.FS
+var FsPath string
+
+var Init model.Initializer = &cacheInit{}
+
+func (i *cacheInit) Init() error {
+ Path = path.Join(paths.Cache, "contexts")
+ FsPath = Path[1:]
+ return nil
+}
+
// Merge combines two caches and filters duplicate keys
-func (c Cache) Merge(other Cache) Cache {
+func (c *Cache) Merge(other *Cache) *Cache {
+ if other == nil && c != nil {
+ return c
+ }
+ if c == nil && other != nil {
+ return other
+ }
+ if c == nil {
+ return nil
+ }
mp := make(map[string]string)
for _, e := range append(c.Entries, other.Entries...) {
mp[e.Path] = e.Label
}
- result := Cache{}
+ result := &Cache{}
for p, l := range mp {
result.Entries = append(result.Entries, Entry{Label: l, Path: p})
}
@@ -36,22 +61,16 @@ type Entry struct {
var Empty = Cache{}
-// Path is the file path to the "contexts" cache file
-var Path = path.Join(paths.Cache, "contexts")
-
-// FsPath is the Path with the leading slash removed, to be opened from fs.FS
-var FsPath = Path[1:]
-
var UnexpectedEntryError = errors.New("unexpected cache entry")
// LoadFile creates a Cache from a file or an empty one if the file does not exist
// this handles opening a reader for Unmarshal
-func LoadFile(root fs.FS, path string) (Cache, error) {
+func LoadFile(root fs.FS, path string) (*Cache, error) {
fd, err := root.Open(path)
if errors.Is(err, fs.ErrNotExist) {
- return Cache{}, nil
+ return nil, nil
} else if err != nil {
- return Cache{}, err
+ return nil, err
}
if fd != nil {
defer fd.Close()
@@ -60,8 +79,8 @@ func LoadFile(root fs.FS, path string) (Cache, error) {
}
// Unmarshal attempts to create a Cache from reader content
-func Unmarshal(r io.Reader) (Cache, error) {
- c := Cache{}
+func Unmarshal(r io.Reader) (*Cache, error) {
+ c := &Cache{}
scanner := bufio.NewScanner(r)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
@@ -86,7 +105,7 @@ func Unmarshal(r io.Reader) (Cache, error) {
}
// Marshal returns the file representation of the Cache
-func (c Cache) Marshal() []byte {
+func (c *Cache) Marshal() []byte {
b := strings.Builder{}
for _, e := range c.Entries {
b.WriteString(e.Path)
@@ -97,12 +116,10 @@ func (c Cache) Marshal() []byte {
return []byte(b.String())
}
-func (c Cache) String() string {
+func (c *Cache) String() string {
return string(c.Marshal())
}
-
-// New creates a new Cache from a model.State
-func New(st *model.State) Cache {
+func New(st *model.State) *Cache {
c := &Cache{}
for _, s := range st.Sources {
c.Entries = append(c.Entries, Entry{
@@ -110,11 +127,22 @@ func New(st *model.State) Cache {
Label: s.Label(),
})
}
- return *c
+ return c
+}
+
+func Insert(in *model.State) error {
+ f := os.DirFS("/")
+ loaded, err := LoadFile(f, FsPath)
+ if err != nil {
+ return err
+ }
+ insert := New(in)
+ result := loaded.Merge(insert)
+ return SaveFile(Path, result)
}
// SaveFile helps you use Save with a file path instead of a reader
-func SaveFile(path string, s *model.State, loaded Cache) error {
+func SaveFile(path string, loaded *Cache) error {
fd, err := os.Create(path)
if err != nil {
return err
@@ -122,19 +150,18 @@ func SaveFile(path string, s *model.State, loaded Cache) error {
if fd != nil {
defer fd.Close()
}
- return Save(s, fd, loaded)
+ return Save(fd, loaded)
}
-// Save writes the cache combined with the "loaded" cache to the writer.
-func Save(s *model.State, w io.Writer, loaded Cache) error {
- result := New(s).Merge(loaded)
- _, err := w.Write([]byte(result.Marshal()))
+// Save writes a cache to the contexts file
+func Save(w io.Writer, loaded *Cache) error {
+ _, err := w.Write(loaded.Marshal())
return err
}
// LoadState creates a state with model.NewState based on cache content
-func LoadState(f fs.FS, cache Cache, indexers []model.Indexer, runners []model.Runner) (*model.State, []error) {
+func LoadState(f fs.FS, cache *Cache, indexers []model.Indexer, runners []model.Runner) (*model.State, []error) {
var locs []string
for _, e := range cache.Entries {
locs = append(locs, e.Path)
@@ -144,7 +171,7 @@ func LoadState(f fs.FS, cache Cache, indexers []model.Indexer, runners []model.R
// Strip removes the needle's entries from the receiver's entries when they have matching paths.
// used to skip already indexed locations when auto-all-ing
-func (c Cache) Strip(needle Cache) Cache {
+func (c *Cache) Strip(needle Cache) Cache {
var result []Entry
outer:
for _, e := range c.Entries {
diff --git a/cache/cache_test.go b/cache/cache_test.go
index 1a9c46d..95ded9d 100644
--- a/cache/cache_test.go
+++ b/cache/cache_test.go
@@ -5,7 +5,7 @@ package cache
import (
"github.com/stretchr/testify/assert"
"path/filepath"
- . "pik/testx"
+ . "pik/runner"
"strings"
"testing"
"testing/fstest"
diff --git a/indexers/pikdex/hydrate_test.go b/indexers/pikdex/hydrate_test.go
index e72954e..72ac8ea 100644
--- a/indexers/pikdex/hydrate_test.go
+++ b/indexers/pikdex/hydrate_test.go
@@ -4,7 +4,7 @@ package pikdex
import (
"github.com/stretchr/testify/assert"
- "pik/testx"
+ "pik/runner"
"testing"
)
@@ -19,7 +19,7 @@ func TestHydrate(t *testing.T) {
},
},
}
- src := testx.TSource("asdf", "target")
+ src := runner.TSource("asdf", "target")
hyd := src.Hydrate(nil)
assert.NotNil(t, hyd)
err := p.Mod(src, hyd)
diff --git a/main.go b/main.go
index 045e5e0..ff7fc9e 100644
--- a/main.go
+++ b/main.go
@@ -24,14 +24,15 @@ import (
"sync"
)
-// preInitializers are ran before the initializers.
+// syncInitializers are ran before the initializers.
// useful for initializing stuff like paths, preparing directories, and reading the environment
-var preInitializers = []model.Initializer{
+var syncInitializers = []model.Initializer{
paths.Paths,
+ cache.Init,
}
// initializers are ran before indexing with the indexers,
-// data from the preInitializers can be accessed at this time.
+// data from the syncInitializers can be accessed at this time.
var initializers = []model.Initializer{
pikdex.Indexer,
python.Python,
@@ -65,7 +66,7 @@ var ForceConfirm = false
// SourcesWithoutResults is a failed cache from the previous iteration
// used for stripping out results to prevent double-index
-var SourcesWithoutResults cache.Cache
+var SourcesWithoutResults *cache.Cache
//go:embed version.txt
var version string
@@ -79,18 +80,14 @@ func main() {
os.Exit(0)
}
- wg := sync.WaitGroup{}
- for _, i := range preInitializers {
- wg.Go(func() {
- err := i.Init()
- if err != nil {
- _, _ = spool.Warn("%v\n", err)
- }
- })
+ for _, i := range syncInitializers {
+ err := i.Init()
+ if err != nil {
+ _, _ = spool.Warn("%v\n", err)
+ }
}
- wg.Wait()
- wg = sync.WaitGroup{}
+ wg := sync.WaitGroup{}
for _, i := range initializers {
wg.Go(func() {
err := i.Init()
@@ -121,12 +118,15 @@ func main() {
var st *model.State
var stateErrors []error
- var c cache.Cache
+ var c *cache.Cache
if !*flags.All {
st, stateErrors = model.NewState(fs, locs, indexers, runners)
+ err = cache.Insert(st)
+ if err != nil {
+ spool.Warn("%v\n", err)
+ }
} else {
c, err = cache.LoadFile(fs, cache.Path[1:])
- c.Strip(SourcesWithoutResults)
if err != nil {
_, _ = spool.Warn("%v\n", err)
os.Exit(1)
@@ -135,11 +135,6 @@ func main() {
}
if stateErrors != nil {
_, _ = spool.Warn("%v\n", stateErrors)
- } else {
- err = cache.SaveFile(cache.Path, st, c)
- if err != nil {
- _, _ = spool.Warn("%v", err)
- }
}
if *flags.List {
@@ -186,7 +181,7 @@ func main() {
}
if result.Target == nil {
- _, _ = spool.Print("target not found.")
+ _, _ = spool.Warn("target not found.")
os.Exit(1)
return
}
diff --git a/testx/create.go b/runner/create.go
index 24faaed..4e9035b 100644
--- a/testx/create.go
+++ b/runner/create.go
@@ -1,13 +1,12 @@
//go:build test
-package testx
+package runner
import (
"github.com/stretchr/testify/assert"
"os/exec"
"pik/identity"
"pik/model"
- "pik/runner"
"testing"
)
@@ -34,7 +33,7 @@ func TState(sources ...*model.Source) *model.State {
}
type TestTarget struct {
- runner.Stub
+ Stub
Id identity.Identity
SubValue []string
MyTags model.Tags
@@ -53,7 +52,7 @@ func (t TestTarget) Visible() bool {
}
func (t TestTarget) Hydrate(src *model.Source) (model.HydratedTarget, error) {
- return runner.HydratedStub{}, nil
+ return HydratedStub{}, nil
}
func (t TestTarget) Sub() []string {
diff --git a/testx/create_test.go b/runner/create_test.go
index d0e3099..fc9e535 100644
--- a/testx/create_test.go
+++ b/runner/create_test.go
@@ -1,6 +1,6 @@
//go:build test
-package testx
+package runner
import (
"github.com/stretchr/testify/assert"
diff --git a/runner/stub.go b/runner/stub.go
index 3282fe8..9899b0d 100644
--- a/runner/stub.go
+++ b/runner/stub.go
@@ -11,6 +11,11 @@ import (
type Stub struct {
}
+func (s Stub) File(src *model.Source) string {
+ //TODO implement me
+ panic("implement me")
+}
+
func (s Stub) Matches(input string) bool {
//TODO implement me
panic("implement me")
@@ -108,7 +113,7 @@ func (h HydratedStub) Icon() string {
panic("implement me")
}
-func (h HydratedStub) Description() string {
+func (h HydratedStub) Description(src *model.HydratedSource) string {
//TODO implement me
panic("implement me")
}
diff --git a/search/search_test.go b/search/search_test.go
index 3671f75..7246ffc 100644
--- a/search/search_test.go
+++ b/search/search_test.go
@@ -4,121 +4,121 @@ package search
import (
"github.com/stretchr/testify/assert"
- "pik/testx"
+ "pik/runner"
"testing"
)
func TestSearch_TargetOnly(t *testing.T) {
- st := testx.TState(testx.TSource("src", "abc", "def"))
+ st := runner.TState(runner.TSource("src", "abc", "def"))
res := Search(st, "def")
- testx.AssertSourceIs(t, "src", res.Source)
- testx.AssertTargetIs(t, "def", res.Target)
+ runner.AssertSourceIs(t, "src", res.Source)
+ runner.AssertTargetIs(t, "def", res.Target)
}
func TestSearch_TargetAndSource(t *testing.T) {
- st := testx.TState(testx.TSource("src", "abc", "def"))
+ st := runner.TState(runner.TSource("src", "abc", "def"))
res := Search(st, "src", "def")
- testx.AssertSourceIs(t, "src", res.Source)
- testx.AssertTargetIs(t, "def", res.Target)
+ runner.AssertSourceIs(t, "src", res.Source)
+ runner.AssertTargetIs(t, "def", res.Target)
}
func TestSearch_TargetAndSource_CaseInsensitive(t *testing.T) {
- st := testx.TState(testx.TSource("src", "abc", "def"))
+ st := runner.TState(runner.TSource("src", "abc", "def"))
res := Search(st, "SRC", "DeF")
- testx.AssertSourceIs(t, "src", res.Source)
- testx.AssertTargetIs(t, "def", res.Target)
+ runner.AssertSourceIs(t, "src", res.Source)
+ runner.AssertTargetIs(t, "def", res.Target)
}
func TestSearch_SourceDefaultTarget(t *testing.T) {
- st := testx.TState(testx.TSource("src", "abc", "src"))
+ st := runner.TState(runner.TSource("src", "abc", "src"))
res := Search(st, "src")
- testx.AssertSourceIs(t, "src", res.Source)
+ runner.AssertSourceIs(t, "src", res.Source)
assert.NotNil(t, res.Target)
}
func TestSearch_SubdirWrong(t *testing.T) {
- st := testx.TState(testx.TSource("src", "abc", "src"))
- st.Sources[0].Targets = append(st.Sources[0].Targets, testx.TTarget("script", "subdir"))
+ st := runner.TState(runner.TSource("src", "abc", "src"))
+ st.Sources[0].Targets = append(st.Sources[0].Targets, runner.TTarget("script", "subdir"))
res := Search(st, "wrong", "script")
- testx.AssertSourceIs(t, "src", res.Source)
- testx.AssertTargetIs(t, "script", res.Target)
+ runner.AssertSourceIs(t, "src", res.Source)
+ runner.AssertTargetIs(t, "script", res.Target)
assert.Equal(t, []string{"wrong"}, res.Sub)
assert.NotNil(t, res.Target)
assert.True(t, res.NeedsConfirmation)
}
func TestSearch_SubdirMissing(t *testing.T) {
- st := testx.TState(testx.TSource("src", "abc", "src"))
- st.Sources[0].Targets = append(st.Sources[0].Targets, testx.TTarget("script", "subdir"))
+ st := runner.TState(runner.TSource("src", "abc", "src"))
+ st.Sources[0].Targets = append(st.Sources[0].Targets, runner.TTarget("script", "subdir"))
res := Search(st, "script")
- testx.AssertSourceIs(t, "src", res.Source)
- testx.AssertTargetIs(t, "script", res.Target)
+ runner.AssertSourceIs(t, "src", res.Source)
+ runner.AssertTargetIs(t, "script", res.Target)
assert.Nil(t, res.Sub)
assert.NotNil(t, res.Target)
assert.False(t, res.NeedsConfirmation)
}
func TestSearch_Args(t *testing.T) {
- st := testx.TState(testx.TSource("src", "abc", "def"))
+ st := runner.TState(runner.TSource("src", "abc", "def"))
res := Search(st, "def", "a1", "a2")
- testx.AssertSourceIs(t, "src", res.Source)
- testx.AssertTargetIs(t, "def", res.Target)
+ runner.AssertSourceIs(t, "src", res.Source)
+ runner.AssertTargetIs(t, "def", res.Target)
assert.Equal(t, []string{"a1", "a2"}, res.Args)
}
func TestSearch_Args_SubdirMissing(t *testing.T) {
- st := testx.TState(testx.TSource("src", "abc", "src"))
- st.Sources[0].Targets = append(st.Sources[0].Targets, testx.TTarget("script", "subdir"))
+ st := runner.TState(runner.TSource("src", "abc", "src"))
+ st.Sources[0].Targets = append(st.Sources[0].Targets, runner.TTarget("script", "subdir"))
res := Search(st, "script", "a1", "a2")
- testx.AssertSourceIs(t, "src", res.Source)
- testx.AssertTargetIs(t, "script", res.Target)
+ runner.AssertSourceIs(t, "src", res.Source)
+ runner.AssertTargetIs(t, "script", res.Target)
assert.Equal(t, []string{"a1", "a2"}, res.Args)
}
func TestSearch_Args_SubdirPresent(t *testing.T) {
- st := testx.TState(testx.TSource("src", "abc", "src"))
- st.Sources[0].Targets = append(st.Sources[0].Targets, testx.TTarget("script", "subdir"))
+ st := runner.TState(runner.TSource("src", "abc", "src"))
+ st.Sources[0].Targets = append(st.Sources[0].Targets, runner.TTarget("script", "subdir"))
res := Search(st, "subdir", "script", "a1", "a2")
- testx.AssertSourceIs(t, "src", res.Source)
- testx.AssertTargetIs(t, "script", res.Target)
+ runner.AssertSourceIs(t, "src", res.Source)
+ runner.AssertTargetIs(t, "script", res.Target)
assert.Equal(t, []string{"a1", "a2"}, res.Args)
}
func TestSearch_SecondarySource(t *testing.T) {
- st := testx.TState(testx.TSource("src", "abc", "def"), testx.TSource("aaa", "hjkl"))
+ st := runner.TState(runner.TSource("src", "abc", "def"), runner.TSource("aaa", "hjkl"))
res := Search(st, "aaa", "hjkl")
- testx.AssertSourceIs(t, "aaa", res.Source)
- testx.AssertTargetIs(t, "hjkl", res.Target)
+ runner.AssertSourceIs(t, "aaa", res.Source)
+ runner.AssertTargetIs(t, "hjkl", res.Target)
}
func TestSearch_SecondarySource_DuplicateTargetName(t *testing.T) {
- st := testx.TState(testx.TSource("src", "abc", "def"), testx.TSource("aaa", "abc"))
+ st := runner.TState(runner.TSource("src", "abc", "def"), runner.TSource("aaa", "abc"))
res := Search(st, "aaa", "def")
- testx.AssertSourceIs(t, "src", res.Source)
- testx.AssertTargetIs(t, "def", res.Target)
+ runner.AssertSourceIs(t, "src", res.Source)
+ runner.AssertTargetIs(t, "def", res.Target)
assert.True(t, res.NeedsConfirmation)
}
func TestSearch_SourceTargetMixup(t *testing.T) {
- st := testx.TState(testx.TSource("src", "abc"), testx.TSource("aaa", "ccc"))
+ st := runner.TState(runner.TSource("src", "abc"), runner.TSource("aaa", "ccc"))
res := Search(st, "src", "ccc")
- testx.AssertSourceIs(t, "aaa", res.Source)
- testx.AssertTargetIs(t, "ccc", res.Target)
+ runner.AssertSourceIs(t, "aaa", res.Source)
+ runner.AssertTargetIs(t, "ccc", res.Target)
assert.True(t, res.NeedsConfirmation)
}
func TestSearch_Override(t *testing.T) {
- st := testx.TState(testx.TSource("src", "abc.override.sh", "abc.sh"))
+ st := runner.TState(runner.TSource("src", "abc.override.sh", "abc.sh"))
res := Search(st, "src", "abc")
- assert.Equal(t, "abc.override.sh", res.Target.(*testx.TestTarget).Id.Full)
+ assert.Equal(t, "abc.override.sh", res.Target.(*runner.TestTarget).Id.Full)
assert.False(t, res.NeedsConfirmation)
}
func TestSearch_SubdirDefault(t *testing.T) {
- tgt := testx.TTarget("subname", "subname")
- src := testx.TSource("src")
+ tgt := runner.TTarget("subname", "subname")
+ src := runner.TSource("src")
src.Targets = append(src.Targets, tgt)
- st := testx.TState(src)
+ st := runner.TState(src)
res := Search(st, "subname")
assert.Nil(t, res.Args)
assert.Equal(t, res.Target, tgt)
@@ -126,8 +126,8 @@ func TestSearch_SubdirDefault(t *testing.T) {
}
func TestSearch_SourceDefault(t *testing.T) {
- src := testx.TSource("sourcename", "sourcename")
- st := testx.TState(src)
+ src := runner.TSource("sourcename", "sourcename")
+ st := runner.TState(src)
res := Search(st, "sourcename")
assert.Nil(t, res.Args)
assert.Equal(t, res.Target, src.Targets[0])
@@ -135,8 +135,8 @@ func TestSearch_SourceDefault(t *testing.T) {
}
func TestSearch_SourceDefault_Other(t *testing.T) {
- src := testx.TSource("src", "src", "other")
- st := testx.TState(src)
+ src := runner.TSource("src", "src", "other")
+ st := runner.TState(src)
res := Search(st, "src", "other")
assert.Nil(t, res.Args)
assert.Equal(t, res.Target, src.Targets[1])
@@ -144,11 +144,11 @@ func TestSearch_SourceDefault_Other(t *testing.T) {
}
func TestSearch_SubdirDefault_Other(t *testing.T) {
- tgt := testx.TTarget("subname", "subname")
- other := testx.TTarget("othername", "subname")
- src := testx.TSource("src")
+ tgt := runner.TTarget("subname", "subname")
+ other := runner.TTarget("othername", "subname")
+ src := runner.TSource("src")
src.Targets = append(src.Targets, tgt, other)
- st := testx.TState(src)
+ st := runner.TState(src)
res := Search(st, "subname", "othername")
assert.Nil(t, res.Args)
assert.Equal(t, other, res.Target)
diff --git a/spool/spool.go b/spool/spool.go
index 4dc7b87..e7b13e9 100644
--- a/spool/spool.go
+++ b/spool/spool.go
@@ -5,8 +5,15 @@ import (
"os"
)
-var Print = fmt.Printf
+var (
+ Stderr = os.Stderr
+ Stdout = os.Stdout
+)
+
+var Print = func(format string, values ...any) (any, error) {
+ return fmt.Fprintf(Stdout, format, values...)
+}
var Warn = func(format string, values ...any) (any, error) {
- return fmt.Fprintf(os.Stderr, format, values...)
+ return fmt.Fprintf(Stderr, format, values...)
}
diff --git a/viewport/viewport.go b/viewport/viewport.go
index ff1ca05..7b897e7 100644
--- a/viewport/viewport.go
+++ b/viewport/viewport.go
@@ -52,9 +52,9 @@ func Crop(input string, lines []string, height int) (output string, scrollStart
}
if end >= size {
- diff := size - 1 - end
- start += diff
- end += diff
+ diff := end - size
+ start -= diff
+ end -= diff
}
scrollStart = float32(start) / float32(size)