Skip to content

Commit

Permalink
Introduce $edit:command:-duration
Browse files Browse the repository at this point in the history
The `edit:command:-duration` variable is the elapsed seconds of the most
recently run interactive command. It is currently marked experimental by
virtue of a `-` prefix since it might be removed in favor of the command
execution callback solution.

Related elves#1029
  • Loading branch information
krader1961 committed Mar 21, 2021
1 parent 7a18649 commit 8c73bd1
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 11 deletions.
3 changes: 3 additions & 0 deletions 0.16.0-release-notes.md
Expand Up @@ -28,3 +28,6 @@ New features in the interactive editor:
bindings.

- A new `edit:clear` builtin to clear the screen has been added.

- A new, experimental, `edit:command:-duration` variable that is the number of
seconds to run the most recent command.
20 changes: 18 additions & 2 deletions pkg/edit/command_api.go
Expand Up @@ -3,18 +3,34 @@ package edit
import (
"src.elv.sh/pkg/cli/mode"
"src.elv.sh/pkg/eval"
"src.elv.sh/pkg/eval/vars"
)

//elv:fn command:start
//elvdoc:fn command:start
//
// Starts the command mode.
// Start command mode.

//elvdoc:var command:-duration
//
// Duration, in seconds, of the most recent interactive command. This can be useful in your prompt
// (left or right) to provide feedback on how long a command took to run. The initial value of this
// variable is the time to evaluate your *~/.elvish/rc.elv* script before issuing the first prompt.

var CommandDuration float64

func initCommandAPI(ed *Editor, ev *eval.Evaler, nb eval.NsBuilder) {
bindingVar := newBindingVar(emptyBindingsMap)
bindings := newMapBindings(ed, ev, bindingVar)
nb.AddNs("command",
eval.NsBuilder{
"binding": bindingVar,
// This is marked as an experimental variable because it might be preferable to provide
// this info via a callback. See https://github.com/elves/elvish/issues/1029.
//
// TODO: Change this to a read-only var, possibly by introducing a vars.FromPtrReadonly
// function, to guard against attempts to modify the value from Elvish code. Assuming,
// of course, we continue to expose it as a variable rather than via a callback.
"-duration": vars.FromPtr(&CommandDuration),
}.AddGoFns("<edit:command>:", map[string]interface{}{
"start": func() {
w := mode.NewStub(mode.StubSpec{
Expand Down
3 changes: 0 additions & 3 deletions pkg/eval/compile_effect.go
Expand Up @@ -171,7 +171,6 @@ func (cp *compiler) formOp(n *parse.Form) effectOp {
lvalues := cp.parseIndexingLValue(a.Left)
tempLValues = append(tempLValues, lvalues.lvalues...)
}
logger.Println("temporary assignment of", len(n.Assignments), "pairs")
}

redirOps := cp.redirOps(n.Redirs)
Expand Down Expand Up @@ -291,7 +290,6 @@ func (op *formOp) exec(fm *Frame) (errRet Exception) {
}
val := v.Get()
saveVals = append(saveVals, val)
logger.Printf("saved %s = %s", v, val)
}
// Do assignment.
for _, subop := range op.tempAssignOps {
Expand All @@ -315,7 +313,6 @@ func (op *formOp) exec(fm *Frame) (errRet Exception) {
if err != nil {
errRet = fm.errorp(op, err)
}
logger.Printf("restored %s = %s", v, val)
}
}()
}
Expand Down
14 changes: 11 additions & 3 deletions pkg/shell/interact.go
Expand Up @@ -5,6 +5,7 @@ import (
"io"
"os"
"path/filepath"
"strings"
"syscall"
"time"

Expand Down Expand Up @@ -79,7 +80,6 @@ func Interact(fds [3]*os.File, cfg *InteractConfig) {
cmdNum++

line, err := ed.ReadCode()

if err == io.EOF {
break
} else if err != nil {
Expand All @@ -101,8 +101,15 @@ func Interact(fds [3]*os.File, cfg *InteractConfig) {
// No error; reset cooldown.
cooldown = time.Second

err = evalInTTY(ev, fds,
// We only bother evaluating the statement(s) if the line is not entirely whitespace. This
// isn't especially important but keeps things like the `edit:command:-duration` variable
// from being updated when we didn't actually evaluate any statements.
if strings.TrimSpace(line) == "" {
continue
}
duration, err := evalInTTY(ev, fds,
parse.Source{Name: fmt.Sprintf("[tty %v]", cmdNum), Code: line})
edit.CommandDuration = duration
term.Sanitize(fds[0], fds[2])
if err != nil {
diag.ShowError(fds[2], err)
Expand All @@ -122,7 +129,8 @@ func sourceRC(fds [3]*os.File, ev *eval.Evaler, rcPath string) error {
}
return err
}
err = evalInTTY(ev, fds, parse.Source{Name: absPath, Code: code, IsFile: true})
duration, err := evalInTTY(ev, fds, parse.Source{Name: absPath, Code: code, IsFile: true})
edit.CommandDuration = duration
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/shell/script.go
Expand Up @@ -67,7 +67,7 @@ func Script(fds [3]*os.File, args []string, cfg *ScriptConfig) int {
return 2
}
} else {
err := evalInTTY(ev, fds, src)
_, err := evalInTTY(ev, fds, src)
if err != nil {
diag.ShowError(fds[2], err)
return 2
Expand Down
8 changes: 6 additions & 2 deletions pkg/shell/shell.go
Expand Up @@ -5,6 +5,7 @@ import (
"os"
"os/signal"
"strconv"
"time"

"src.elv.sh/pkg/cli/term"
"src.elv.sh/pkg/env"
Expand Down Expand Up @@ -62,11 +63,14 @@ func setupShell(fds [3]*os.File, p Paths, spawn bool) (*eval.Evaler, func()) {
}
}

func evalInTTY(ev *eval.Evaler, fds [3]*os.File, src parse.Source) error {
func evalInTTY(ev *eval.Evaler, fds [3]*os.File, src parse.Source) (float64, error) {
start := time.Now()
ports, cleanup := eval.PortsFromFiles(fds, ev.ValuePrefix())
defer cleanup()
return ev.Eval(src, eval.EvalCfg{
err := ev.Eval(src, eval.EvalCfg{
Ports: ports, Interrupt: eval.ListenInterrupts, PutInFg: true})
end := time.Now()
return end.Sub(start).Seconds(), err
}

func incSHLVL() func() {
Expand Down

0 comments on commit 8c73bd1

Please sign in to comment.