diff --git a/edit/editor.go b/edit/editor.go index 9e7926fe..1ca9d0fb 100644 --- a/edit/editor.go +++ b/edit/editor.go @@ -29,6 +29,8 @@ type Editor struct { store *store.Store evaler *eval.Evaler cmdSeq int + ps1 Prompt + rps1 Prompt editorState } @@ -93,6 +95,8 @@ func NewEditor(file *os.File, sigs chan os.Signal, ev *eval.Evaler, st *store.St } } + prompt, rprompt := defaultPrompts() + ed := &Editor{ file: file, writer: newWriter(file), @@ -101,6 +105,8 @@ func NewEditor(file *os.File, sigs chan os.Signal, ev *eval.Evaler, st *store.St store: st, evaler: ev, cmdSeq: seq, + ps1: prompt, + rps1: rprompt, } ev.AddModule("le", makeModule(ed)) return ed @@ -282,7 +288,7 @@ func (ed *Editor) finishReadLine(addError func(error)) { } // ReadLine reads a line interactively. -func (ed *Editor) ReadLine(prompt, rprompt func() string) (lr LineRead) { +func (ed *Editor) ReadLine() (lr LineRead) { ed.editorState = editorState{active: true} isExternalCh := make(chan map[string]bool, 1) go getIsExternal(ed.evaler, isExternalCh) @@ -302,8 +308,8 @@ func (ed *Editor) ReadLine(prompt, rprompt func() string) (lr LineRead) { MainLoop: for { - ed.prompt = prompt() - ed.rprompt = rprompt() + ed.prompt = ed.ps1.Call(ed) + ed.rprompt = ed.rps1.Call(ed) err := ed.refresh(false, true) if err != nil { diff --git a/edit/module.go b/edit/module.go index a01c3f74..d73356f1 100644 --- a/edit/module.go +++ b/edit/module.go @@ -39,6 +39,9 @@ func makeModule(ed *Editor) eval.Namespace { ns["binding"] = eval.NewRoVariable(binding) + ns["prompt"] = PromptVariable{&ed.ps1} + ns["rprompt"] = PromptVariable{&ed.rps1} + return ns } diff --git a/edit/prompt.go b/edit/prompt.go index c015380e..1d2b1a86 100644 --- a/edit/prompt.go +++ b/edit/prompt.go @@ -2,11 +2,36 @@ package edit import ( "bytes" + "errors" "os" + "os/user" "github.com/elves/elvish/eval" + "github.com/elves/elvish/util" ) +var ErrPromptMustBeStringOrFunc = errors.New("prompt must be string or function") + +// PromptVariable implements $le:prompt and $le:rprompt. +type PromptVariable struct { + Prompt *Prompt +} + +func (pv PromptVariable) Get() eval.Value { + // XXX Should return a proper eval.Caller + return eval.String("") +} + +func (pv PromptVariable) Set(v eval.Value) { + if s, ok := v.(eval.String); ok { + *pv.Prompt = BuiltinPrompt(func(*Editor) string { return string(s) }) + } else if c, ok := v.(eval.Caller); ok { + *pv.Prompt = CallerPrompt{c} + } else { + throw(ErrPromptMustBeStringOrFunc) + } +} + // Prompt is the interface of prompt functions. type Prompt interface { Call(*Editor) string @@ -29,7 +54,7 @@ func (c CallerPrompt) Call(ed *Editor) string { if err != nil { return "" } - ports := []*eval.Port{in, nil, &eval.Port{File: os.Stderr}} + ports := []*eval.Port{in, &eval.Port{File: os.Stdout}, &eval.Port{File: os.Stderr}} // XXX There is no source to pass to NewTopEvalCtx. ec := eval.NewTopEvalCtx(ed.evaler, "[editor prompt]", "", ports) @@ -44,3 +69,24 @@ func (c CallerPrompt) Call(ed *Editor) string { } return b.String() } + +func defaultPrompts() (Prompt, Prompt) { + // Make default prompts. + username := "???" + user, err := user.Current() + if err == nil { + username = user.Username + } + hostname, err := os.Hostname() + if err != nil { + hostname = "???" + } + rpromptStr := username + "@" + hostname + prompt := func(*Editor) string { + return util.Getwd() + "> " + } + rprompt := func(*Editor) string { + return rpromptStr + } + return BuiltinPrompt(prompt), BuiltinPrompt(rprompt) +} diff --git a/run/run.go b/run/run.go index f084e9c1..dd6cd935 100644 --- a/run/run.go +++ b/run/run.go @@ -8,7 +8,6 @@ import ( "io" "os" "os/signal" - "os/user" "syscall" "time" @@ -127,27 +126,9 @@ func interact(ev *eval.Evaler, st *store.Store) { } } - // Build prompt and rprompt. - username := "???" - user, err := user.Current() - if err == nil { - username = user.Username - } - hostname, err := os.Hostname() - if err != nil { - hostname = "???" - } - rpromptStr := username + "@" + hostname - prompt := func() string { - return util.Getwd() + "> " - } - rprompt := func() string { - return rpromptStr - } - // Build readLine function. readLine := func() edit.LineRead { - return ed.ReadLine(prompt, rprompt) + return ed.ReadLine() } cooldown := time.Second