diff options
| -rw-r--r-- | README.md | 10 | ||||
| -rw-r--r-- | main.go | 15 | ||||
| -rw-r--r-- | menu/menu.go | 11 | ||||
| -rw-r--r-- | model/runner.go | 1 | ||||
| -rw-r--r-- | runner/exc/runner.go | 8 | ||||
| -rw-r--r-- | runner/js/indexer.go | 39 | ||||
| -rw-r--r-- | runner/js/js.go | 106 | ||||
| -rw-r--r-- | runner/js/runner.go | 28 | ||||
| -rw-r--r-- | runner/js/target_npm.go | 42 | ||||
| -rw-r--r-- | runner/js/target_script.go | 55 | ||||
| -rw-r--r-- | runner/python/runner.go | 5 | ||||
| -rw-r--r-- | runner/shell/hydrated.go | 14 | ||||
| -rw-r--r-- | runner/shell/target.go | 4 |
13 files changed, 300 insertions, 38 deletions
@@ -76,11 +76,18 @@ pik reads the first comment line from your targets and informs you in the tui! * external targets will also show up in the tui * search with `/` and `?`, scroll results with `n` and `N` +### supported pik runners +* python +* shell +* executable files +* java- and typescript ### supported external runners * `just` * `make` +* `pyproject.toml` +* * if you want any more let me know and it should be done very fast ## planned features @@ -89,9 +96,6 @@ As this program has already gone through a number of iterations and forms, this time to catch up with all the features it used to have. This list is not exhaustive, but it is ordered by importance I attach to these features. -* runner for executable files - * this will also enable arbitrary shells like node by way of the shebang -* indexers for other target types such as `npm` * whitelists for external runners in `.pik` * adding descriptions to external targets * expand tui: @@ -14,6 +14,7 @@ import ( "github.com/ewy1/pik/run" "github.com/ewy1/pik/runner/exc" "github.com/ewy1/pik/runner/gnumake" + "github.com/ewy1/pik/runner/js" "github.com/ewy1/pik/runner/just" "github.com/ewy1/pik/runner/python" "github.com/ewy1/pik/runner/shell" @@ -36,6 +37,7 @@ var initializers = ComponentList[model.Initializer]{ pikdex.Indexer, python.Python, git.Git, + js.Js, } // indexers are methods which scan a directory and return a number of targets. @@ -43,6 +45,7 @@ var indexers = ComponentList[model.Indexer]{ pikdex.Indexer, just.Indexer, gnumake.Indexer, + js.Js, } // runners are modules which know how to turn a file into an exec.Cmd @@ -51,6 +54,7 @@ var runners = ComponentList[model.Runner]{ shell.Runner, python.Python, exc.Exc, + js.Js, } // hydrators are ran when the menu is required @@ -131,13 +135,16 @@ func main() { args := pflag.Args() var result *search.Result + cancelled := false if len(args) == 0 { - source, target, err := menu.Show(st, hydrators) + md, err := menu.Show(st, hydrators) if err != nil { _, _ = spool.Warn("%v\n", err) os.Exit(1) } + cancelled = md.Cancel + source, target := md.Result() if target != nil { t := target.Target() result = &search.Result{ @@ -166,6 +173,12 @@ func main() { return } + if cancelled { + _, _ = spool.Warn("no target selected\n") + os.Exit(0) + return + } + if result.Target == nil { _, _ = spool.Warn("target not found\n") os.Exit(1) diff --git a/menu/menu.go b/menu/menu.go index 3d379d0..b524d95 100644 --- a/menu/menu.go +++ b/menu/menu.go @@ -10,24 +10,23 @@ import ( var WrongModelTypeError = errors.New("wrong model type") var NoSourcesIndexedError = errors.New("no sources indexed") -func Show(st *model.State, hydrators []model.Modder) (*model.HydratedSource, model.HydratedTarget, error) { +func Show(st *model.State, hydrators []model.Modder) (*Model, error) { if len(st.Sources) == 0 { - return nil, nil, NoSourcesIndexedError + return nil, NoSourcesIndexedError } md := NewModel(st, hydrators) var opts []tea.ProgramOption program := tea.NewProgram(md, opts...) resultModel, err := program.Run() if err != nil { - return nil, nil, err + return nil, err } result, ok := resultModel.(*Model) if !ok { - return nil, nil, WrongModelTypeError + return nil, WrongModelTypeError } - src, t := result.Result() - return src, t, nil + return result, nil } func Hydrate(st *model.State, hydrators []model.Modder) *model.HydratedState { diff --git a/model/runner.go b/model/runner.go index e8cbcc0..eda2505 100644 --- a/model/runner.go +++ b/model/runner.go @@ -8,7 +8,6 @@ import ( // these are mostly used for pikdex but other runners can use them as well // these have to be registered in main.go type Runner interface { - Hydrate(target Target) (HydratedTarget, error) Wants(fs fs.FS, file string, entry fs.DirEntry) (bool, error) CreateTarget(fs fs.FS, source string, file string, entry fs.DirEntry) (Target, error) } diff --git a/runner/exc/runner.go b/runner/exc/runner.go index 092fd80..2eae44c 100644 --- a/runner/exc/runner.go +++ b/runner/exc/runner.go @@ -9,14 +9,6 @@ import ( "path/filepath" ) -func (e *exc) Hydrate(target model.Target) (model.HydratedTarget, error) { - return &Hydrated{ - BaseHydration: &runner.BaseHydration[*Executable]{ - Self: target.(*Executable), - }, - }, nil -} - func (e *exc) Wants(fs fs.FS, file string, entry fs.DirEntry) (bool, error) { if entry.IsDir() { return false, nil diff --git a/runner/js/indexer.go b/runner/js/indexer.go new file mode 100644 index 0000000..8a2a6b2 --- /dev/null +++ b/runner/js/indexer.go @@ -0,0 +1,39 @@ +package js + +import ( + "encoding/json" + "errors" + "github.com/ewy1/pik/model" + "io/fs" + "os" + "path/filepath" +) + +type Package struct { + Scripts map[string]string `json:"scripts"` +} + +func (n *js) Index(path string, f fs.FS, runners []model.Runner) ([]model.Target, error) { + p := &Package{} + // are there any other package.jsons? i hope not, because i don't know them + content, err := os.ReadFile(filepath.Join(path, "package.json")) + if errors.Is(err, fs.ErrNotExist) { + return nil, nil + } else if err != nil { + return nil, err + } + err = json.Unmarshal(content, p) + if err != nil { + return nil, err + } + + var targets []model.Target + if n.Npm == "" { + return nil, NoNpm + } + for k, s := range p.Scripts { + targets = append(targets, n.CreateRun(k, s)) + } + + return targets, nil +} diff --git a/runner/js/js.go b/runner/js/js.go new file mode 100644 index 0000000..7c2af58 --- /dev/null +++ b/runner/js/js.go @@ -0,0 +1,106 @@ +package js + +import ( + "errors" + "github.com/ewy1/pik/identity" + "github.com/ewy1/pik/model" + "github.com/ewy1/pik/runner" + "os/exec" + "path/filepath" + "slices" +) + +var jsExtensions = []string{ + ".js", + ".cjs", +} + +var tsExtensions = []string{ + ".ts", +} + +var extensions = append(jsExtensions, tsExtensions...) + +var managers = []string{ + "pnpm", + "yarn", + "npm", +} + +var jsInterpreters = []string{ + "node", + "bun", +} + +var tsInterpeters = []string{ + "ts", + "ts-node", + "bun", +} + +type js struct { + JsInterpreter string + TsInterpreter string + Npm string +} + +var Js = &js{} + +var UnsupportedFile = errors.New("unsupported file") +var NoJsInterpreter = errors.New("no js interpreter found in $PATH") +var NoTsInterpreter = errors.New("no ts interpreter found in $PATH") +var NoNpm = errors.New("npm not found in $PATH") + +func (n *js) Interpreter(file string) (string, error) { + ext := filepath.Ext(file) + if slices.Contains(jsInterpreters, ext) { + if n.JsInterpreter == "" { + return "", NoJsInterpreter + } + return n.JsInterpreter, nil + } + if slices.Contains(tsInterpeters, ext) { + if n.TsInterpreter == "" { + return "", NoTsInterpreter + } + return n.TsInterpreter, nil + } + return "", UnsupportedFile +} + +func (n *js) Init() error { + for _, p := range jsInterpreters { + if r, err := exec.LookPath(p); err != nil { + n.JsInterpreter = r + } + } + for _, p := range tsInterpeters { + if r, err := exec.LookPath(p); err != nil { + n.TsInterpreter = r + } + } + + for _, m := range managers { + if r, err := exec.LookPath(m); err == nil { + n.Npm = r + } + } + + return nil +} + +var npmSub = []string{ + "npm", +} + +func (n *js) CreateRun(name, cmd string) model.Target { + return &Npm{ + BaseTarget: runner.BaseTarget{ + Identity: identity.New(cmd), + MyTags: model.TagsFromFilename(cmd), + MySub: npmSub, + }, + Name: name, + Cmd: cmd, + } +} diff --git a/runner/js/runner.go b/runner/js/runner.go new file mode 100644 index 0000000..8b5f4f1 --- /dev/null +++ b/runner/js/runner.go @@ -0,0 +1,28 @@ +package js + +import ( + "github.com/ewy1/pik/identity" + "github.com/ewy1/pik/model" + "github.com/ewy1/pik/runner" + "io/fs" + "path/filepath" + "slices" +) + +func (n *js) Wants(fs fs.FS, file string, entry fs.DirEntry) (bool, error) { + ext := filepath.Ext(entry.Name()) + return slices.Contains(extensions, ext), nil +} + +func (n *js) CreateTarget(fs fs.FS, source string, file string, entry fs.DirEntry) (model.Target, error) { + ext := filepath.Ext(entry.Name()) + typed := slices.Contains(tsExtensions, ext) + return &Script{ + BaseTarget: runner.BaseTarget{ + Identity: identity.New(entry.Name()), + MyTags: model.TagsFromFilename(entry.Name()), + MySub: runner.SubFromFile(file), + }, + Typed: typed, + }, nil +} diff --git a/runner/js/target_npm.go b/runner/js/target_npm.go new file mode 100644 index 0000000..8319fad --- /dev/null +++ b/runner/js/target_npm.go @@ -0,0 +1,42 @@ +package js + +import ( + "github.com/ewy1/pik/model" + "github.com/ewy1/pik/runner" + "os/exec" + "path/filepath" +) + +type Npm struct { + runner.BaseTarget + Name string + Cmd string +} + +func (n *Npm) Icon() string { + return "\uE60B" +} + +func (n *Npm) Description(src *model.HydratedSource) string { + return n.Cmd +} + +func (n *Npm) Target() model.Target { + return n +} + +func (n *Npm) Create(s *model.Source) *exec.Cmd { + return exec.Command(Js.Npm, "run", n.Name) +} + +func (n *Npm) Label() string { + return n.Name +} + +func (n *Npm) Hydrate(src *model.Source) (model.HydratedTarget, error) { + return n, nil +} + +func (n *Npm) File(src *model.Source) string { + return filepath.Join(src.Path, "package.json") +} diff --git a/runner/js/target_script.go b/runner/js/target_script.go new file mode 100644 index 0000000..6ad213d --- /dev/null +++ b/runner/js/target_script.go @@ -0,0 +1,55 @@ +package js + +import ( + "github.com/ewy1/pik/describe" + "github.com/ewy1/pik/model" + "github.com/ewy1/pik/runner" + "github.com/ewy1/pik/spool" + "os/exec" + "path/filepath" +) + +type Script struct { + runner.BaseTarget + Typed bool +} + +func (t *Script) Icon() string { + if t.Typed { + return "\uE628" + } else { + return "\uE60C" + } +} + +func (t *Script) Description(src *model.HydratedSource) string { + d, err := describe.Describe(t, t.File(src.Source)) + if err != nil { + _, _ = spool.Warn("%v\n", err) + } + return d +} + +func (t *Script) Target() model.Target { + return t +} + +func (t *Script) Create(s *model.Source) *exec.Cmd { + if t.Typed { + return exec.Command(Js.TsInterpreter, t.File(s)) + } else { + return exec.Command(Js.JsInterpreter, t.File(s)) + } +} + +func (t *Script) Label() string { + return t.Identity.Full +} + +func (t *Script) Hydrate(src *model.Source) (model.HydratedTarget, error) { + return t, nil +} + +func (t *Script) File(src *model.Source) string { + return filepath.Join(src.Path, "package.json") +} diff --git a/runner/python/runner.go b/runner/python/runner.go index 6aa6017..8d03f52 100644 --- a/runner/python/runner.go +++ b/runner/python/runner.go @@ -30,11 +30,6 @@ func (p python) Init() error { return err } -func (p python) Hydrate(target model.Target) (model.HydratedTarget, error) { - //TODO implement me - panic("implement me") -} - func (p python) Wants(fs fs.FS, file string, entry fs.DirEntry) (bool, error) { return !entry.IsDir() && filepath.Ext(entry.Name()) == ".py", nil } diff --git a/runner/shell/hydrated.go b/runner/shell/hydrated.go index fec74f1..440d9cd 100644 --- a/runner/shell/hydrated.go +++ b/runner/shell/hydrated.go @@ -1,7 +1,6 @@ package shell import ( - "errors" "github.com/ewy1/pik/describe" "github.com/ewy1/pik/model" "github.com/ewy1/pik/runner" @@ -19,19 +18,8 @@ func (h *Hydrated) Icon() string { func (h *Hydrated) Description(src *model.HydratedSource) string { desc, err := describe.Describe(h.Target(), h.Target().File(src.Source)) if err != nil { - spool.Warn("%v\n", err) + _, _ = spool.Warn("%v\n", err) return "" } return desc } - -var WrongTargetError = errors.New("wrong target type") - -func (s *shell) Hydrate(target model.Target) (model.HydratedTarget, error) { - cast, ok := target.(*Target) - if !ok { - return nil, WrongTargetError - } - hyd := &Hydrated{BaseHydration: runner.Hydrated(cast)} - return hyd, nil -} diff --git a/runner/shell/target.go b/runner/shell/target.go index 49f7a18..d1262af 100644 --- a/runner/shell/target.go +++ b/runner/shell/target.go @@ -21,7 +21,9 @@ func (s *Target) String() string { } func (s *Target) Hydrate(_ *model.Source) (model.HydratedTarget, error) { - return Runner.Hydrate(s) + return &Hydrated{ + BaseHydration: runner.Hydrated(s), + }, nil } func (s *Target) Sub() []string { |
