Prompt can now be customized by modifying le:{,r}prompt.

This fixes #103.
This commit is contained in:
Qi Xiao 2016-02-23 13:24:28 +01:00
parent 9d7e5de667
commit 67e4b5df11
4 changed files with 60 additions and 24 deletions

View File

@ -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 {

View File

@ -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
}

View File

@ -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("<prompt>")
}
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)
}

View File

@ -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