123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- package edit
- import (
- "fmt"
- "os"
- "strings"
- "src.elv.sh/pkg/cli"
- "src.elv.sh/pkg/cli/histutil"
- "src.elv.sh/pkg/diag"
- "src.elv.sh/pkg/eval"
- "src.elv.sh/pkg/eval/vals"
- "src.elv.sh/pkg/eval/vars"
- "src.elv.sh/pkg/store/storedefs"
- )
- func initMaxHeight(appSpec *cli.AppSpec, nb eval.NsBuilder) {
- maxHeight := newIntVar(-1)
- appSpec.MaxHeight = func() int { return maxHeight.GetRaw().(int) }
- nb.AddVar("max-height", maxHeight)
- }
- func initReadlineHooks(appSpec *cli.AppSpec, ev *eval.Evaler, nb eval.NsBuilder) {
- initBeforeReadline(appSpec, ev, nb)
- initAfterReadline(appSpec, ev, nb)
- }
- func initBeforeReadline(appSpec *cli.AppSpec, ev *eval.Evaler, nb eval.NsBuilder) {
- hook := newListVar(vals.EmptyList)
- nb.AddVar("before-readline", hook)
- appSpec.BeforeReadline = append(appSpec.BeforeReadline, func() {
- callHooks(ev, "$<edit>:before-readline", hook.Get().(vals.List))
- })
- }
- func initAfterReadline(appSpec *cli.AppSpec, ev *eval.Evaler, nb eval.NsBuilder) {
- hook := newListVar(vals.EmptyList)
- nb.AddVar("after-readline", hook)
- appSpec.AfterReadline = append(appSpec.AfterReadline, func(code string) {
- callHooks(ev, "$<edit>:after-readline", hook.Get().(vals.List), code)
- })
- }
- func initAddCmdFilters(appSpec *cli.AppSpec, ev *eval.Evaler, nb eval.NsBuilder, s histutil.Store) {
- ignoreLeadingSpace := eval.NewGoFn("<ignore-cmd-with-leading-space>",
- func(s string) bool { return !strings.HasPrefix(s, " ") })
- filters := newListVar(vals.MakeList(ignoreLeadingSpace))
- nb.AddVar("add-cmd-filters", filters)
- appSpec.AfterReadline = append(appSpec.AfterReadline, func(code string) {
- if code != "" &&
- callFilters(ev, "$<edit>:add-cmd-filters",
- filters.Get().(vals.List), code) {
- s.AddCmd(storedefs.Cmd{Text: code, Seq: -1})
- }
- // TODO(xiaq): Handle the error.
- })
- }
- func initGlobalBindings(appSpec *cli.AppSpec, nt notifier, ev *eval.Evaler, nb eval.NsBuilder) {
- bindingVar := newBindingVar(emptyBindingsMap)
- appSpec.GlobalBindings = newMapBindings(nt, ev, bindingVar)
- nb.AddVar("global-binding", bindingVar)
- }
- func callHooks(ev *eval.Evaler, name string, hook vals.List, args ...any) {
- if hook.Len() == 0 {
- return
- }
- ports, cleanup := eval.PortsFromStdFiles(ev.ValuePrefix())
- evalCfg := eval.EvalCfg{Ports: ports[:]}
- defer cleanup()
- i := -1
- for it := hook.Iterator(); it.HasElem(); it.Next() {
- i++
- name := fmt.Sprintf("%s[%d]", name, i)
- fn, ok := it.Elem().(eval.Callable)
- if !ok {
- // TODO(xiaq): This is not testable as it depends on stderr.
- // Make it testable.
- diag.Complainf(os.Stderr, "%s not function", name)
- continue
- }
- err := ev.Call(fn, eval.CallCfg{Args: args, From: name}, evalCfg)
- if err != nil {
- diag.ShowError(os.Stderr, err)
- }
- }
- }
- func callFilters(ev *eval.Evaler, name string, filters vals.List, args ...any) bool {
- if filters.Len() == 0 {
- return true
- }
- i := -1
- for it := filters.Iterator(); it.HasElem(); it.Next() {
- i++
- name := fmt.Sprintf("%s[%d]", name, i)
- fn, ok := it.Elem().(eval.Callable)
- if !ok {
- // TODO(xiaq): This is not testable as it depends on stderr.
- // Make it testable.
- diag.Complainf(os.Stderr, "%s not function", name)
- continue
- }
- port1, collect, err := eval.CapturePort()
- if err != nil {
- diag.Complainf(os.Stderr, "cannot create pipe to run filter")
- return true
- }
- err = ev.Call(fn, eval.CallCfg{Args: args, From: name},
- // TODO: Supply the Chan component of port 2.
- eval.EvalCfg{Ports: []*eval.Port{nil, port1, {File: os.Stderr}}})
- out := collect()
- if err != nil {
- diag.Complainf(os.Stderr, "%s return error", name)
- continue
- }
- if len(out) != 1 {
- diag.Complainf(os.Stderr, "filter %s should only return $true or $false", name)
- continue
- }
- p, ok := out[0].(bool)
- if !ok {
- diag.Complainf(os.Stderr, "filter %s should return bool", name)
- continue
- }
- if !p {
- return false
- }
- }
- return true
- }
- func newIntVar(i int) vars.PtrVar { return vars.FromPtr(&i) }
- func newFloatVar(f float64) vars.PtrVar { return vars.FromPtr(&f) }
- func newBoolVar(b bool) vars.PtrVar { return vars.FromPtr(&b) }
- func newListVar(l vals.List) vars.PtrVar { return vars.FromPtr(&l) }
- func newMapVar(m vals.Map) vars.PtrVar { return vars.FromPtr(&m) }
- func newFnVar(c eval.Callable) vars.PtrVar { return vars.FromPtr(&c) }
- func newBindingVar(b bindingsMap) vars.PtrVar { return vars.FromPtr(&b) }
|