pkg/eval: Move elvdoc comments before the implementations.

This commit is contained in:
Qi Xiao 2020-08-16 18:46:29 +01:00
parent d5736120e5
commit 9b2b33b429
13 changed files with 1340 additions and 1347 deletions

View File

@ -8,6 +8,22 @@ import (
// Command and process control.
// TODO(xiaq): Document "fg".
func init() {
addBuiltinFns(map[string]interface{}{
// Command resolution
"external": external,
"has-external": hasExternal,
"search-external": searchExternal,
// Process control
"fg": fg,
"exec": execFn,
"exit": exit,
})
}
//elvdoc:fn external
//
// ```elvish
@ -23,6 +39,10 @@ import (
//
// @cf has-external search-external
func external(cmd string) ExternalCmd {
return ExternalCmd{cmd}
}
//elvdoc:fn has-external
//
// ```elvish
@ -41,6 +61,11 @@ import (
//
// @cf external search-external
func hasExternal(cmd string) bool {
_, err := exec.LookPath(cmd)
return err == nil
}
//elvdoc:fn search-external
//
// ```elvish
@ -57,16 +82,9 @@ import (
//
// @cf external has-external
// TODO(xiaq): Document "fg".
//elvdoc:fn exec
//
// ```elvish
// exec $command?
// ```
//
// Replace the Elvish process with an external `$command`, defaulting to
// `elvish`. This decrements `$E:SHLVL` before starting the new process.
func searchExternal(cmd string) (string, error) {
return exec.LookPath(cmd)
}
//elvdoc:fn exit
//
@ -76,33 +94,6 @@ import (
//
// Exit the Elvish process with `$status` (defaulting to 0).
func init() {
addBuiltinFns(map[string]interface{}{
// Command resolution
"external": external,
"has-external": hasExternal,
"search-external": searchExternal,
// Process control
"fg": fg,
"exec": execFn,
"exit": exit,
})
}
func external(cmd string) ExternalCmd {
return ExternalCmd{cmd}
}
func hasExternal(cmd string) bool {
_, err := exec.LookPath(cmd)
return err == nil
}
func searchExternal(cmd string) (string, error) {
return exec.LookPath(cmd)
}
func exit(fm *Frame, codes ...int) error {
code := 0
switch len(codes) {

View File

@ -18,6 +18,17 @@ import (
// in the same process group.
var ErrNotInSameProcessGroup = errors.New("not in the same process group")
//elvdoc:fn exec
//
// ```elvish
// exec $command?
// ```
//
// Replace the Elvish process with an external `$command`, defaulting to
// `elvish`. This decrements `$E:SHLVL` before starting the new process.
//
// This command always raises an exception on Windows.
func execFn(fm *Frame, args ...interface{}) error {
var argstrings []string
if len(args) == 0 {

View File

@ -16,337 +16,6 @@ import (
// TODO(xiaq): Document "ns".
//elvdoc:fn range
//
// ```elvish
// range &step=1 $low? $high
// ```
//
// Output `$low`, `$low` + `$step`, ..., proceeding as long as smaller than
// `$high`. If not given, `$low` defaults to 0.
//
// Examples:
//
// ```elvish-transcript
// ~> range 4
// ▶ 0
// ▶ 1
// ▶ 2
// ▶ 3
// ~> range 1 6 &step=2
// ▶ 1
// ▶ 3
// ▶ 5
// ```
//
// Beware floating point oddities:
//
// ```elvish-transcript
// ~> range 0 0.8 &step=.1
// ▶ 0
// ▶ 0.1
// ▶ 0.2
// ▶ 0.30000000000000004
// ▶ 0.4
// ▶ 0.5
// ▶ 0.6
// ▶ 0.7
// ▶ 0.7999999999999999
// ```
//
// Etymology:
// [Python](https://docs.python.org/3/library/functions.html#func-range).
//elvdoc:fn repeat
//
// ```elvish
// repeat $n $value
// ```
//
// Output `$value` for `$n` times. Example:
//
// ```elvish-transcript
// ~> repeat 0 lorem
// ~> repeat 4 NAN
// ▶ NAN
// ▶ NAN
// ▶ NAN
// ▶ NAN
// ```
//
// Etymology: [Clojure](https://clojuredocs.org/clojure.core/repeat).
//elvdoc:fn assoc
//
// ```elvish
// assoc $container $k $v
// ```
//
// Output a slightly modified version of `$container`, such that its value at `$k`
// is `$v`. Applies to both lists and to maps.
//
// When `$container` is a list, `$k` may be a negative index. However, slice is not
// yet supported.
//
// ```elvish-transcript
// ~> assoc [foo bar quux] 0 lorem
// ▶ [lorem bar quux]
// ~> assoc [foo bar quux] -1 ipsum
// ▶ [foo bar ipsum]
// ~> assoc [&k=v] k v2
// ▶ [&k=v2]
// ~> assoc [&k=v] k2 v2
// ▶ [&k2=v2 &k=v]
// ```
//
// Etymology: [Clojure](https://clojuredocs.org/clojure.core/assoc).
//
// @cf dissoc
//elvdoc:fn dissoc
//
// ```elvish
// dissoc $map $k
// ```
//
// Output a slightly modified version of `$map`, with the key `$k` removed. If
// `$map` does not contain `$k` as a key, the same map is returned.
//
// ```elvish-transcript
// ~> dissoc [&foo=bar &lorem=ipsum] foo
// ▶ [&lorem=ipsum]
// ~> dissoc [&foo=bar &lorem=ipsum] k
// ▶ [&lorem=ipsum &foo=bar]
// ```
//
// @cf assoc
//elvdoc:fn all
//
// ```elvish
// all $input-list?
// ```
//
// Passes inputs to the output as is. Byte inputs into values, one per line.
//
// This is an identity function for commands with value outputs: `a | all` is
// equivalent to `a` if it only outputs values.
//
// This function is useful for turning inputs into arguments, like:
//
// ```elvish-transcript
// ~> put 'lorem,ipsum' | splits , (all)
// ▶ lorem
// ▶ ipsum
// ```
//
// Or capturing all inputs in a variable:
//
// ```elvish-transcript
// ~> x = [(all)]
// foo
// bar
// (Press ^D)
// ~> put $x
// ▶ [foo bar]
// ```
//
// When given a list, it outputs all elements of the list:
//
// ```elvish-transcript
// ~> all [foo bar]
// ▶ foo
// ▶ bar
// ```
//
// @cf one
//elvdoc:fn one
//
// ```elvish
// one $input-list?
// ```
//
// Passes inputs to outputs, if there is only a single one. Otherwise raises an
// exception.
//
// This function can be used in a similar way to [`all`](#all), but is a better
// choice when you expect that there is exactly one output:
//
// @cf all
//elvdoc:fn has-key
//
// ```elvish
// has-key $container $key
// ```
//
// Determine whether `$key` is a key in `$container`. A key could be a map key or
// an index on a list or string. This includes a range of indexes.
//
// Examples, maps:
//
// ```elvish-transcript
// ~> has-key [&k1=v1 &k2=v2] k1
// ▶ $true
// ~> has-key [&k1=v1 &k2=v2] v1
// ▶ $false
// ```
//
// Examples, lists:
//
// ```elvish-transcript
// ~> has-key [v1 v2] 0
// ▶ $true
// ~> has-key [v1 v2] 1
// ▶ $true
// ~> has-key [v1 v2] 2
// ▶ $false
// ~> has-key [v1 v2] 0:2
// ▶ $true
// ~> has-key [v1 v2] 0:3
// ▶ $false
// ```
//
// Examples, strings:
//
// ```elvish-transcript
// ~> has-key ab 0
// ▶ $true
// ~> has-key ab 1
// ▶ $true
// ~> has-key ab 2
// ▶ $false
// ~> has-key ab 0:2
// ▶ $true
// ~> has-key ab 0:3
// ▶ $false
// ```
//elvdoc:fn has-value
//
// ```elvish
// has-value $container $value
// ```
//
// Determine whether `$value` is a value in `$container`.
//
// Examples, maps:
//
// ```elvish-transcript
// ~> has-value [&k1=v1 &k2=v2] v1
// ▶ $true
// ~> has-value [&k1=v1 &k2=v2] k1
// ▶ $false
// ```
//
// Examples, lists:
//
// ```elvish-transcript
// ~> has-value [v1 v2] v1
// ▶ $true
// ~> has-value [v1 v2] k1
// ▶ $false
// ```
//
// Examples, strings:
//
// ```elvish-transcript
// ~> has-value ab b
// ▶ $true
// ~> has-value ab c
// ▶ $false
// ```
//elvdoc:fn take
//
// ```elvish
// take $n $input-list?
// ```
//
// Retain the first `$n` input elements. If `$n` is larger than the number of input
// elements, the entire input is retained. Examples:
//
// ```elvish-transcript
// ~> take 3 [a b c d e]
// ▶ a
// ▶ b
// ▶ c
// ~> splits ' ' 'how are you?' | take 1
// ▶ how
// ~> range 2 | take 10
// ▶ 0
// ▶ 1
// ```
//
// Etymology: Haskell.
//elvdoc:fn drop
//
// ```elvish
// drop $n $input-list?
// ```
//
// Drop the first `$n` elements of the input. If `$n` is larger than the number of
// input elements, the entire input is dropped.
//
// Example:
//
// ```elvish-transcript
// ~> drop 2 [a b c d e]
// ▶ c
// ▶ d
// ▶ e
// ~> splits ' ' 'how are you?' | drop 1
// ▶ are
// ▶ 'you?'
// ~> range 2 | drop 10
// ```
//
// Etymology: Haskell.
//
// @cf take
//elvdoc:fn count
//
// ```elvish
// count $input-list?
// ```
//
// Count the number of inputs.
//
// Examples:
//
// ```elvish-transcript
// ~> count lorem # count bytes in a string
// ▶ 5
// ~> count [lorem ipsum]
// ▶ 2
// ~> range 100 | count
// ▶ 100
// ~> seq 100 | count
// ▶ 100
// ```
//elvdoc:fn keys
//
// ```elvish
// keys $map
// ```
//
// Put all keys of `$map` on the structured stdout.
//
// Example:
//
// ```elvish-transcript
// ~> keys [&a=foo &b=bar &c=baz]
// ▶ a
// ▶ c
// ▶ b
// ```
//
// Note that there is no guaranteed order for the keys of a map.
func init() {
addBuiltinFns(map[string]interface{}{
"ns": nsFn,
@ -447,6 +116,47 @@ func makeMap(input Inputs) (vals.Map, error) {
return m, errMakeMap
}
//elvdoc:fn range
//
// ```elvish
// range &step=1 $low? $high
// ```
//
// Output `$low`, `$low` + `$step`, ..., proceeding as long as smaller than
// `$high`. If not given, `$low` defaults to 0.
//
// Examples:
//
// ```elvish-transcript
// ~> range 4
// ▶ 0
// ▶ 1
// ▶ 2
// ▶ 3
// ~> range 1 6 &step=2
// ▶ 1
// ▶ 3
// ▶ 5
// ```
//
// Beware floating point oddities:
//
// ```elvish-transcript
// ~> range 0 0.8 &step=.1
// ▶ 0
// ▶ 0.1
// ▶ 0.2
// ▶ 0.30000000000000004
// ▶ 0.4
// ▶ 0.5
// ▶ 0.6
// ▶ 0.7
// ▶ 0.7999999999999999
// ```
//
// Etymology:
// [Python](https://docs.python.org/3/library/functions.html#func-range).
type rangeOpts struct{ Step float64 }
func (o *rangeOpts) SetDefaultOptions() { o.Step = 1 }
@ -470,6 +180,25 @@ func rangeFn(fm *Frame, opts rangeOpts, args ...float64) error {
return nil
}
//elvdoc:fn repeat
//
// ```elvish
// repeat $n $value
// ```
//
// Output `$value` for `$n` times. Example:
//
// ```elvish-transcript
// ~> repeat 0 lorem
// ~> repeat 4 NAN
// ▶ NAN
// ▶ NAN
// ▶ NAN
// ▶ NAN
// ```
//
// Etymology: [Clojure](https://clojuredocs.org/clojure.core/repeat).
func repeat(fm *Frame, n int, v interface{}) {
out := fm.OutputChan()
for i := 0; i < n; i++ {
@ -477,12 +206,57 @@ func repeat(fm *Frame, n int, v interface{}) {
}
}
//elvdoc:fn assoc
//
// ```elvish
// assoc $container $k $v
// ```
//
// Output a slightly modified version of `$container`, such that its value at `$k`
// is `$v`. Applies to both lists and to maps.
//
// When `$container` is a list, `$k` may be a negative index. However, slice is not
// yet supported.
//
// ```elvish-transcript
// ~> assoc [foo bar quux] 0 lorem
// ▶ [lorem bar quux]
// ~> assoc [foo bar quux] -1 ipsum
// ▶ [foo bar ipsum]
// ~> assoc [&k=v] k v2
// ▶ [&k=v2]
// ~> assoc [&k=v] k2 v2
// ▶ [&k2=v2 &k=v]
// ```
//
// Etymology: [Clojure](https://clojuredocs.org/clojure.core/assoc).
//
// @cf dissoc
func assoc(a, k, v interface{}) (interface{}, error) {
return vals.Assoc(a, k, v)
}
var errCannotDissoc = errors.New("cannot dissoc")
//elvdoc:fn dissoc
//
// ```elvish
// dissoc $map $k
// ```
//
// Output a slightly modified version of `$map`, with the key `$k` removed. If
// `$map` does not contain `$k` as a key, the same map is returned.
//
// ```elvish-transcript
// ~> dissoc [&foo=bar &lorem=ipsum] foo
// ▶ [&lorem=ipsum]
// ~> dissoc [&foo=bar &lorem=ipsum] k
// ▶ [&lorem=ipsum &foo=bar]
// ```
//
// @cf assoc
func dissoc(a, k interface{}) (interface{}, error) {
a2 := vals.Dissoc(a, k)
if a2 == nil {
@ -491,11 +265,65 @@ func dissoc(a, k interface{}) (interface{}, error) {
return a2, nil
}
//elvdoc:fn all
//
// ```elvish
// all $input-list?
// ```
//
// Passes inputs to the output as is. Byte inputs into values, one per line.
//
// This is an identity function for commands with value outputs: `a | all` is
// equivalent to `a` if it only outputs values.
//
// This function is useful for turning inputs into arguments, like:
//
// ```elvish-transcript
// ~> put 'lorem,ipsum' | splits , (all)
// ▶ lorem
// ▶ ipsum
// ```
//
// Or capturing all inputs in a variable:
//
// ```elvish-transcript
// ~> x = [(all)]
// foo
// bar
// (Press ^D)
// ~> put $x
// ▶ [foo bar]
// ```
//
// When given a list, it outputs all elements of the list:
//
// ```elvish-transcript
// ~> all [foo bar]
// ▶ foo
// ▶ bar
// ```
//
// @cf one
func all(fm *Frame, inputs Inputs) {
out := fm.ports[1].Chan
inputs(func(v interface{}) { out <- v })
}
//elvdoc:fn one
//
// ```elvish
// one $input-list?
// ```
//
// Passes inputs to outputs, if there is only a single one. Otherwise raises an
// exception.
//
// This function can be used in a similar way to [`all`](#all), but is a better
// choice when you expect that there is exactly one output:
//
// @cf all
func one(fm *Frame, inputs Inputs) error {
var val interface{}
n := 0
@ -512,6 +340,29 @@ func one(fm *Frame, inputs Inputs) error {
return fmt.Errorf("expect a single value, got %d", n)
}
//elvdoc:fn take
//
// ```elvish
// take $n $input-list?
// ```
//
// Retain the first `$n` input elements. If `$n` is larger than the number of input
// elements, the entire input is retained. Examples:
//
// ```elvish-transcript
// ~> take 3 [a b c d e]
// ▶ a
// ▶ b
// ▶ c
// ~> splits ' ' 'how are you?' | take 1
// ▶ how
// ~> range 2 | take 10
// ▶ 0
// ▶ 1
// ```
//
// Etymology: Haskell.
func take(fm *Frame, n int, inputs Inputs) {
out := fm.ports[1].Chan
i := 0
@ -523,6 +374,32 @@ func take(fm *Frame, n int, inputs Inputs) {
})
}
//elvdoc:fn drop
//
// ```elvish
// drop $n $input-list?
// ```
//
// Drop the first `$n` elements of the input. If `$n` is larger than the number of
// input elements, the entire input is dropped.
//
// Example:
//
// ```elvish-transcript
// ~> drop 2 [a b c d e]
// ▶ c
// ▶ d
// ▶ e
// ~> splits ' ' 'how are you?' | drop 1
// ▶ are
// ▶ 'you?'
// ~> range 2 | drop 10
// ```
//
// Etymology: Haskell.
//
// @cf take
func drop(fm *Frame, n int, inputs Inputs) {
out := fm.ports[1].Chan
i := 0
@ -534,6 +411,41 @@ func drop(fm *Frame, n int, inputs Inputs) {
})
}
//elvdoc:fn has-value
//
// ```elvish
// has-value $container $value
// ```
//
// Determine whether `$value` is a value in `$container`.
//
// Examples, maps:
//
// ```elvish-transcript
// ~> has-value [&k1=v1 &k2=v2] v1
// ▶ $true
// ~> has-value [&k1=v1 &k2=v2] k1
// ▶ $false
// ```
//
// Examples, lists:
//
// ```elvish-transcript
// ~> has-value [v1 v2] v1
// ▶ $true
// ~> has-value [v1 v2] k1
// ▶ $false
// ```
//
// Examples, strings:
//
// ```elvish-transcript
// ~> has-value ab b
// ▶ $true
// ~> has-value ab c
// ▶ $false
// ```
func hasValue(container, value interface{}) (bool, error) {
switch container := container.(type) {
case hashmap.Map:
@ -554,10 +466,79 @@ func hasValue(container, value interface{}) (bool, error) {
}
}
//elvdoc:fn has-key
//
// ```elvish
// has-key $container $key
// ```
//
// Determine whether `$key` is a key in `$container`. A key could be a map key or
// an index on a list or string. This includes a range of indexes.
//
// Examples, maps:
//
// ```elvish-transcript
// ~> has-key [&k1=v1 &k2=v2] k1
// ▶ $true
// ~> has-key [&k1=v1 &k2=v2] v1
// ▶ $false
// ```
//
// Examples, lists:
//
// ```elvish-transcript
// ~> has-key [v1 v2] 0
// ▶ $true
// ~> has-key [v1 v2] 1
// ▶ $true
// ~> has-key [v1 v2] 2
// ▶ $false
// ~> has-key [v1 v2] 0:2
// ▶ $true
// ~> has-key [v1 v2] 0:3
// ▶ $false
// ```
//
// Examples, strings:
//
// ```elvish-transcript
// ~> has-key ab 0
// ▶ $true
// ~> has-key ab 1
// ▶ $true
// ~> has-key ab 2
// ▶ $false
// ~> has-key ab 0:2
// ▶ $true
// ~> has-key ab 0:3
// ▶ $false
// ```
func hasKey(container, key interface{}) bool {
return vals.HasKey(container, key)
}
//elvdoc:fn count
//
// ```elvish
// count $input-list?
// ```
//
// Count the number of inputs.
//
// Examples:
//
// ```elvish-transcript
// ~> count lorem # count bytes in a string
// ▶ 5
// ~> count [lorem ipsum]
// ▶ 2
// ~> range 100 | count
// ▶ 100
// ~> seq 100 | count
// ▶ 100
// ```
// The count implmentation uses a custom varargs based implementation rather
// than the more common `Inputs` API (see pkg/eval/go_fn.go) because this
// allows the implmentation to be O(1) for the common cases rather than O(n).
@ -592,6 +573,25 @@ func count(fm *Frame, args ...interface{}) (int, error) {
return n, nil
}
//elvdoc:fn keys
//
// ```elvish
// keys $map
// ```
//
// Put all keys of `$map` on the structured stdout.
//
// Example:
//
// ```elvish-transcript
// ~> keys [&a=foo &b=bar &c=baz]
// ▶ a
// ▶ c
// ▶ b
// ```
//
// Note that there is no guaranteed order for the keys of a map.
func keys(fm *Frame, v interface{}) error {
out := fm.ports[1].Chan
return vals.IterateKeys(v, func(k interface{}) bool {

View File

@ -7,6 +7,15 @@ import (
"github.com/elves/elvish/pkg/util"
)
func init() {
addBuiltinFns(map[string]interface{}{
"src": src,
"-gc": _gc,
"-stack": _stack,
"-log": _log,
})
}
//elvdoc:fn src
//
// ```elvish
@ -46,6 +55,10 @@ import (
// ▶ /home/elf/.elvish/lib/src-util.elv
// ```
func src(fm *Frame) parse.Source {
return fm.srcMeta
}
//elvdoc:fn -gc
//
// ```elvish
@ -56,6 +69,10 @@ import (
//
// This is only useful for debug purposes.
func _gc() {
runtime.GC()
}
//elvdoc:fn -stack
//
// ```elvish
@ -66,33 +83,6 @@ import (
//
// This is only useful for debug purposes.
//elvdoc:fn -log
//
// ```elvish
// -log $filename
// ```
//
// Direct internal debug logs to the named file.
//
// This is only useful for debug purposes.
func init() {
addBuiltinFns(map[string]interface{}{
"src": src,
"-gc": _gc,
"-stack": _stack,
"-log": _log,
})
}
func src(fm *Frame) parse.Source {
return fm.srcMeta
}
func _gc() {
runtime.GC()
}
func _stack(fm *Frame) {
out := fm.ports[1].File
// TODO(xiaq): Dup with main.go.
@ -103,6 +93,16 @@ func _stack(fm *Frame) {
out.Write(buf)
}
//elvdoc:fn -log
//
// ```elvish
// -log $filename
// ```
//
// Direct internal debug logs to the named file.
//
// This is only useful for debug purposes.
func _log(fname string) error {
return util.SetOutputFile(fname)
}

View File

@ -7,42 +7,6 @@ import (
var errNonExistentEnvVar = errors.New("non-existent environment variable")
//elvdoc:fn has-env
//
// ```elvish
// has-env $name
// ```
//
// Test whether an environment variable exists. Examples:
//
// ```elvish-transcript
// ~> has-env PATH
// ▶ $true
// ~> has-env NO_SUCH_ENV
// ▶ $false
// ```
//
// @cf get-env set-env unset-env
//elvdoc:fn get-env
//
// ```elvish
// get-env $name
// ```
//
// Gets the value of an environment variable. Throws an exception if the
// environment variable does not exist. Examples:
//
// ```elvish-transcript
// ~> get-env LANG
// ▶ zh_CN.UTF-8
// ~> get-env NO_SUCH_ENV
// Exception: non-existent environment variable
// [tty], line 1: get-env NO_SUCH_ENV
// ```
//
// @cf has-env set-env unset-env
//elvdoc:fn set-env
//
// ```elvish
@ -87,11 +51,47 @@ func init() {
})
}
//elvdoc:fn has-env
//
// ```elvish
// has-env $name
// ```
//
// Test whether an environment variable exists. Examples:
//
// ```elvish-transcript
// ~> has-env PATH
// ▶ $true
// ~> has-env NO_SUCH_ENV
// ▶ $false
// ```
//
// @cf get-env set-env unset-env
func hasEnv(key string) bool {
_, ok := os.LookupEnv(key)
return ok
}
//elvdoc:fn get-env
//
// ```elvish
// get-env $name
// ```
//
// Gets the value of an environment variable. Throws an exception if the
// environment variable does not exist. Examples:
//
// ```elvish-transcript
// ~> get-env LANG
// ▶ zh_CN.UTF-8
// ~> get-env NO_SUCH_ENV
// Exception: non-existent environment variable
// [tty], line 1: get-env NO_SUCH_ENV
// ```
//
// @cf has-env set-env unset-env
func getEnv(key string) (string, error) {
value, ok := os.LookupEnv(key)
if !ok {

View File

@ -9,6 +9,23 @@ import (
// Flow control.
// TODO(xiaq): Document "multi-error".
func init() {
addBuiltinFns(map[string]interface{}{
"run-parallel": runParallel,
// Exception and control
"fail": fail,
"multi-error": multiErrorFn,
"return": returnFn,
"break": breakFn,
"continue": continueFn,
// Iterations.
"each": each,
"peach": peach,
})
}
//elvdoc:fn run-parallel
//
// ```elvish
@ -48,87 +65,23 @@ import (
//
// @cf peach
// TODO(xiaq): Document "multi-error".
func runParallel(fm *Frame, functions ...Callable) error {
var waitg sync.WaitGroup
waitg.Add(len(functions))
exceptions := make([]*Exception, len(functions))
for i, function := range functions {
go func(fm2 *Frame, function Callable, exception **Exception) {
err := function.Call(fm2, NoArgs, NoOpts)
if err != nil {
*exception = err.(*Exception)
}
waitg.Done()
}(fm.fork("[run-parallel function]"), function, &exceptions[i])
}
//elvdoc:fn break
//
// Raises the special "break" exception. When raised inside a loop it is
// captured and causes the loop to terminate.
//
// Because `break` raises an exception it can be caught by a
// [`try`](language.html#exception-control-try) block. If not caught, either
// implicitly by a loop or explicitly, it causes a failure like any other
// uncaught exception.
//
// See the discussion about [flow commands and exceptions](language.html#exception-and-flow-commands)
//
// **Note**: You can create a `break` function and it will shadow the builtin
// command. If you do so you should explicitly invoke the builtin. For example:
//
// ```elvish-transcript
// > fn break []{ put 'break'; builtin:break; put 'should not appear' }
// > for x [a b c] { put $x; break; put 'unexpected' }
// ▶ a
// ▶ break
// ```
//elvdoc:fn continue
//
// Raises the special "continue" exception. When raised inside a loop it is
// captured and causes the loop to begin its next iteration.
//
// Because `continue` raises an exception it can be caught by a
// [`try`](language.html#exception-control-try) block. If not caught, either
// implicitly by a loop or explicitly, it causes a failure like any other
// uncaught exception.
//
// See the discussion about [flow commands and exceptions](language.html#exception-and-flow-commands)
//
// **Note**: You can create a `continue` function and it will shadow the builtin
// command. If you do so you should explicitly invoke the builtin. For example:
//
// ```elvish-transcript
// > fn break []{ put 'continue'; builtin:continue; put 'should not appear' }
// > for x [a b c] { put $x; continue; put 'unexpected' }
// ▶ a
// ▶ continue
// ▶ b
// ▶ continue
// ▶ c
// ▶ continue
// ```
//elvdoc:fn return
//
// Raises the special "return" exception. When raised inside a named function
// (defined by the [`fn` keyword](../language.html#function-definition-fn)) it
// is captured by the function and causes the function to terminate. It is not
// captured by an anonymous function (aka [lambda](../language.html#lambda)).
//
// Because `return` raises an exception it can be caught by a
// [`try`](language.html#exception-control-try) block. If not caught, either
// implicitly by a named function or explicitly, it causes a failure like any
// other uncaught exception.
//
// See the discussion about [flow commands and exceptions](language.html#exception-and-flow-commands)
//
// **Note**: While defining a `return~ = { builtin:return }` lambda should
// work it currently results results in infinite recursion. **Do not do
// this!**
//
// **Note**: You can create a `return` function and it will shadow the builtin
// command. **Do not do this!**. You cannot propagate the exception to the
// calling function. In this example you might, incorrectly, expect "no" to
// not appear in the output:
//
// ```elvish-transcript
// > fn return []{ put 'return'; builtin:return; put 'should not appear' }
// > fn test-return []{ put 'yes'; return; put 'no' }
// > test-return
// ▶ yes
// ▶ return
// ▶ no
// ```
waitg.Wait()
return makePipelineError(exceptions)
}
//elvdoc:fn each
//
@ -154,6 +107,32 @@ import (
// the iteration construct of
// [Factor](http://docs.factorcode.org/content/word-each,sequences.html).
func each(fm *Frame, f Callable, inputs Inputs) error {
broken := false
var err error
inputs(func(v interface{}) {
if broken {
return
}
newFm := fm.fork("closure of each")
ex := f.Call(newFm, []interface{}{v}, NoOpts)
newFm.Close()
if ex != nil {
switch Cause(ex) {
case nil, Continue:
// nop
case Break:
broken = true
default:
broken = true
err = ex
}
}
})
return err
}
//elvdoc:fn peach
//
// ```elvish
@ -180,67 +159,6 @@ import (
//
// @cf each run-parallel
func init() {
addBuiltinFns(map[string]interface{}{
"run-parallel": runParallel,
// Exception and control
"fail": fail,
"multi-error": multiErrorFn,
"return": returnFn,
"break": breakFn,
"continue": continueFn,
// Iterations.
"each": each,
"peach": peach,
})
}
func runParallel(fm *Frame, functions ...Callable) error {
var waitg sync.WaitGroup
waitg.Add(len(functions))
exceptions := make([]*Exception, len(functions))
for i, function := range functions {
go func(fm2 *Frame, function Callable, exception **Exception) {
err := function.Call(fm2, NoArgs, NoOpts)
if err != nil {
*exception = err.(*Exception)
}
waitg.Done()
}(fm.fork("[run-parallel function]"), function, &exceptions[i])
}
waitg.Wait()
return makePipelineError(exceptions)
}
// each takes a single closure and applies it to all input values.
func each(fm *Frame, f Callable, inputs Inputs) error {
broken := false
var err error
inputs(func(v interface{}) {
if broken {
return
}
newFm := fm.fork("closure of each")
ex := f.Call(newFm, []interface{}{v}, NoOpts)
newFm.Close()
if ex != nil {
switch Cause(ex) {
case nil, Continue:
// nop
case Break:
broken = true
default:
broken = true
err = ex
}
}
})
return err
}
// peach takes a single closure and applies it to all input values in parallel.
func peach(fm *Frame, f Callable, inputs Inputs) error {
var w sync.WaitGroup
broken := false
@ -328,14 +246,94 @@ func multiErrorFn(excs ...*Exception) error {
return PipelineError{excs}
}
//elvdoc:fn return
//
// Raises the special "return" exception. When raised inside a named function
// (defined by the [`fn` keyword](../language.html#function-definition-fn)) it
// is captured by the function and causes the function to terminate. It is not
// captured by an anonymous function (aka [lambda](../language.html#lambda)).
//
// Because `return` raises an exception it can be caught by a
// [`try`](language.html#exception-control-try) block. If not caught, either
// implicitly by a named function or explicitly, it causes a failure like any
// other uncaught exception.
//
// See the discussion about [flow commands and exceptions](language.html#exception-and-flow-commands)
//
// **Note**: While defining a `return~ = { builtin:return }` lambda should
// work it currently results results in infinite recursion. **Do not do
// this!**
//
// **Note**: You can create a `return` function and it will shadow the builtin
// command. **Do not do this!**. You cannot propagate the exception to the
// calling function. In this example you might, incorrectly, expect "no" to
// not appear in the output:
//
// ```elvish-transcript
// > fn return []{ put 'return'; builtin:return; put 'should not appear' }
// > fn test-return []{ put 'yes'; return; put 'no' }
// > test-return
// ▶ yes
// ▶ return
// ▶ no
// ```
func returnFn() error {
return Return
}
//elvdoc:fn break
//
// Raises the special "break" exception. When raised inside a loop it is
// captured and causes the loop to terminate.
//
// Because `break` raises an exception it can be caught by a
// [`try`](language.html#exception-control-try) block. If not caught, either
// implicitly by a loop or explicitly, it causes a failure like any other
// uncaught exception.
//
// See the discussion about [flow commands and exceptions](language.html#exception-and-flow-commands)
//
// **Note**: You can create a `break` function and it will shadow the builtin
// command. If you do so you should explicitly invoke the builtin. For example:
//
// ```elvish-transcript
// > fn break []{ put 'break'; builtin:break; put 'should not appear' }
// > for x [a b c] { put $x; break; put 'unexpected' }
// ▶ a
// ▶ break
// ```
func breakFn() error {
return Break
}
//elvdoc:fn continue
//
// Raises the special "continue" exception. When raised inside a loop it is
// captured and causes the loop to begin its next iteration.
//
// Because `continue` raises an exception it can be caught by a
// [`try`](language.html#exception-control-try) block. If not caught, either
// implicitly by a loop or explicitly, it causes a failure like any other
// uncaught exception.
//
// See the discussion about [flow commands and exceptions](language.html#exception-and-flow-commands)
//
// **Note**: You can create a `continue` function and it will shadow the builtin
// command. If you do so you should explicitly invoke the builtin. For example:
//
// ```elvish-transcript
// > fn break []{ put 'continue'; builtin:continue; put 'should not appear' }
// > for x [a b c] { put $x; continue; put 'unexpected' }
// ▶ a
// ▶ continue
// ▶ b
// ▶ continue
// ▶ c
// ▶ continue
// ```
func continueFn() error {
return Continue
}

View File

@ -9,37 +9,11 @@ import (
"github.com/elves/elvish/pkg/util"
)
// Filesystem.
// Filesystem commands.
// ErrStoreNotConnected is thrown by dir-history when the store is not connected.
var ErrStoreNotConnected = errors.New("store not connected")
//elvdoc:fn cd
//
// ```elvish
// cd $dirname
// ```
//
// Change directory.
//
// Note that Elvish's `cd` does not support `cd -`.
//elvdoc:fn dir-history
//
// ```elvish
// dir-history
// ```
//
// Return a list containing the directory history. Each element is a map with two
// keys: `path` and `score`. The list is sorted by descending score.
//
// Example:
//
// ```elvish-transcript
// ~> dir-history | take 1
// ▶ [&path=/Users/foo/.elvish &score=96.79928]
// ```
//elvdoc:fn path-\*
//
// ```elvish
@ -55,26 +29,6 @@ var ErrStoreNotConnected = errors.New("store not connected")
// TODO(xiaq): Document eval-symlinks.
//elvdoc:fn tilde-abbr
//
// ```elvish
// tilde-abbr $path
// ```
//
// If `$path` represents a path under the home directory, replace the home
// directory with `~`. Examples:
//
// ```elvish-transcript
// ~> echo $E:HOME
// /Users/foo
// ~> tilde-abbr /Users/foo
// ▶ '~'
// ~> tilde-abbr /Users/foobar
// ▶ /Users/foobar
// ~> tilde-abbr /Users/foo/a/b
// ▶ '~/a/b'
// ```
// TODO(xiaq): Document -is-dir.
func init() {
@ -97,6 +51,16 @@ func init() {
})
}
//elvdoc:fn cd
//
// ```elvish
// cd $dirname
// ```
//
// Change directory.
//
// Note that Elvish's `cd` does not support `cd -`.
func cd(fm *Frame, args ...string) error {
var dir string
switch len(args) {
@ -115,6 +79,22 @@ func cd(fm *Frame, args ...string) error {
return fm.Chdir(dir)
}
//elvdoc:fn dir-history
//
// ```elvish
// dir-history
// ```
//
// Return a list containing the directory history. Each element is a map with two
// keys: `path` and `score`. The list is sorted by descending score.
//
// Example:
//
// ```elvish-transcript
// ~> dir-history | take 1
// ▶ [&path=/Users/foo/.elvish &score=96.79928]
// ```
type dirHistoryEntry struct {
Path string
Score float64
@ -137,6 +117,26 @@ func dirs(fm *Frame) error {
return nil
}
//elvdoc:fn tilde-abbr
//
// ```elvish
// tilde-abbr $path
// ```
//
// If `$path` represents a path under the home directory, replace the home
// directory with `~`. Examples:
//
// ```elvish-transcript
// ~> echo $E:HOME
// /Users/foo
// ~> tilde-abbr /Users/foo
// ▶ '~'
// ~> tilde-abbr /Users/foobar
// ▶ /Users/foobar
// ~> tilde-abbr /Users/foo/a/b
// ▶ '~/a/b'
// ```
func tildeAbbr(path string) string {
return util.TildeAbbr(path)
}

View File

@ -13,329 +13,6 @@ import (
// Input and output.
//elvdoc:fn put
//
// ```elvish
// put $value...
// ```
//
// Takes arbitrary arguments and write them to the structured stdout.
//
// Examples:
//
// ```elvish-transcript
// ~> put a
// ▶ a
// ~> put lorem ipsum [a b] { ls }
// ▶ lorem
// ▶ ipsum
// ▶ [a b]
// ▶ <closure 0xc4202607e0>
// ```
//
// Etymology: Various languages, in particular
// [C](https://manpages.debian.org/stretch/manpages-dev/puts.3.en.html) and
// [Ruby](https://ruby-doc.org/core-2.2.2/IO.html#method-i-puts) as `puts`.
//elvdoc:fn read-upto
//
// ```elvish
// read-upto $delim
// ```
//
// Reads byte input until `$delim` or end-of-file is encountered, and outputs
// the part of the input read as a string value. The output contains the
// trailing `$delim`, unless `read-upto` terminated at end-of-file.
//
// The `$delim` argument must be a single rune in the ASCII range.
//
// Examples:
//
// ```elvish-transcript
// ~> echo "a,b,c" | read-upto ","
// ▶ 'a,'
// ~> echo "foo\nbar" | read-upto "\n"
// ▶ "foo\n"
// ~> echo "a.elv\x00b.elv" | read-upto "\x00"
// ▶ "a.elv\x00"
// ~> print "foobar" | read-upto "\n"
// ▶ foobar
// ```
//elvdoc:fn print
//
// ```elvish
// print &sep=' ' $value...
// ```
//
// Like `echo`, just without the newline.
//
// @cf echo
//
// Etymology: Various languages, in particular
// [Perl](https://perldoc.perl.org/functions/print.html) and
// [zsh](http://zsh.sourceforge.net/Doc/Release/Shell-Builtin-Commands.html), whose
// `print`s do not print a trailing newline.
//elvdoc:fn echo
//
// ```elvish
// echo &sep=' ' $value...
// ```
//
// Print all arguments, joined by the `sep` option, and followed by a newline.
//
// Examples:
//
// ```elvish-transcript
// ~> echo Hello elvish
// Hello elvish
// ~> echo "Hello elvish"
// Hello elvish
// ~> echo &sep=, lorem ipsum
// lorem,ipsum
// ```
//
// Notes: The `echo` builtin does not treat `-e` or `-n` specially. For instance,
// `echo -n` just prints `-n`. Use double-quoted strings to print special
// characters, and `print` to suppress the trailing newline.
//
// @cf print
//
// Etymology: Bourne sh.
//elvdoc:fn pprint
//
// ```elvish
// pprint $value...
// ```
//
// Pretty-print representations of Elvish values. Examples:
//
// ```elvish-transcript
// ~> pprint [foo bar]
// [
// foo
// bar
// ]
// ~> pprint [&k1=v1 &k2=v2]
// [
// &k2=
// v2
// &k1=
// v1
// ]
// ```
//
// The output format is subject to change.
//
// @cf repr
//elvdoc:fn repr
//
// ```elvish
// repr $value...
// ```
//
// Writes representation of `$value`s, separated by space and followed by a
// newline. Example:
//
// ```elvish-transcript
// ~> repr [foo 'lorem ipsum'] "aha\n"
// [foo 'lorem ipsum'] "aha\n"
// ```
//
// @cf pprint
//
// Etymology: [Python](https://docs.python.org/3/library/functions.html#repr).
//elvdoc:fn only-bytes
//
// ```elvish
// only-bytes
// ```
//
// Passes byte input to output, and discards value inputs.
//
// Example:
//
// ```elvish-transcript
// ~> { put value; echo bytes } | only-bytes
// bytes
// ```
//elvdoc:fn only-values
//
// ```elvish
// only-values
// ```
//
// Passes value input to output, and discards byte inputs.
//
// Example:
//
// ```elvish-transcript
// ~> { put value; echo bytes } | only-values
// ▶ value
// ```
//elvdoc:fn slurp
//
// ```elvish
// slurp
// ```
//
// Reads bytes input into a single string, and put this string on structured
// stdout.
//
// Example:
//
// ```elvish-transcript
// ~> echo "a\nb" | slurp
// ▶ "a\nb\n"
// ```
//
// Etymology: Perl, as
// [`File::Slurp`](http://search.cpan.org/~uri/File-Slurp-9999.19/lib/File/Slurp.pm).
//elvdoc:fn from-json
//
// ```elvish
// from-json
// ```
//
// Takes bytes stdin, parses it as JSON and puts the result on structured stdout.
// The input can contain multiple JSONs, which can, but do not have to, be
// separated with whitespaces.
//
// Examples:
//
// ```elvish-transcript
// ~> echo '"a"' | from-json
// ▶ a
// ~> echo '["lorem", "ipsum"]' | from-json
// ▶ [lorem ipsum]
// ~> echo '{"lorem": "ipsum"}' | from-json
// ▶ [&lorem=ipsum]
// ~> # multiple JSONs running together
// echo '"a""b"["x"]' | from-json
// ▶ a
// ▶ b
// ▶ [x]
// ~> # multiple JSONs separated by newlines
// echo '"a"
// {"k": "v"}' | from-json
// ▶ a
// ▶ [&k=v]
// ```
//
// @cf to-json
//elvdoc:fn to-json
//
// ```elvish
// to-json
// ```
//
// Takes structured stdin, convert it to JSON and puts the result on bytes stdout.
//
// ```elvish-transcript
// ~> put a | to-json
// "a"
// ~> put [lorem ipsum] | to-json
// ["lorem","ipsum"]
// ~> put [&lorem=ipsum] | to-json
// {"lorem":"ipsum"}
// ```
//
// @cf from-json
//elvdoc:fn fopen
//
// ```elvish
// fopen $filename
// ```
//
// Open a file. Currently, `fopen` only supports opening a file for reading. File
// must be closed with `fclose` explicitly. Example:
//
// ```elvish-transcript
// ~> cat a.txt
// This is
// a file.
// ~> f = (fopen a.txt)
// ~> cat < $f
// This is
// a file.
// ~> fclose $f
// ```
//
// @cf fclose
//elvdoc:fn fclose
//
// ```elvish
// fclose $file
// ```
//
// Close a file opened with `fopen`.
//
// @cf fopen
//elvdoc:fn pipe
//
// ```elvish
// pipe
// ```
//
// Create a new Unix pipe that can be used in redirections.
//
// A pipe contains both the read FD and the write FD. When redirecting command
// input to a pipe with `<`, the read FD is used. When redirecting command output
// to a pipe with `>`, the write FD is used. It is not supported to redirect both
// input and output with `<>` to a pipe.
//
// Pipes have an OS-dependent buffer, so writing to a pipe without an active reader
// does not necessarily block. Pipes **must** be explicitly closed with `prclose`
// and `pwclose`.
//
// Putting values into pipes will cause those values to be discarded.
//
// Examples (assuming the pipe has a large enough buffer):
//
// ```elvish-transcript
// ~> p = (pipe)
// ~> echo 'lorem ipsum' > $p
// ~> head -n1 < $p
// lorem ipsum
// ~> put 'lorem ipsum' > $p
// ~> head -n1 < $p
// # blocks
// # $p should be closed with prclose and pwclose afterwards
// ```
//
// @cf prclose pwclose
//elvdoc:fn prclose
//
// ```elvish
// prclose $pipe
// ```
//
// Close the read end of a pipe.
//
// @cf pwclose pipe
//elvdoc:fn pwclose
//
// ```elvish
// pwclose $pipe
// ```
//
// Close the write end of a pipe.
//
// @cf prclose pipe
func init() {
addBuiltinFns(map[string]interface{}{
// Value output
@ -379,6 +56,30 @@ func init() {
})
}
//elvdoc:fn put
//
// ```elvish
// put $value...
// ```
//
// Takes arbitrary arguments and write them to the structured stdout.
//
// Examples:
//
// ```elvish-transcript
// ~> put a
// ▶ a
// ~> put lorem ipsum [a b] { ls }
// ▶ lorem
// ▶ ipsum
// ▶ [a b]
// ▶ <closure 0xc4202607e0>
// ```
//
// Etymology: Various languages, in particular
// [C](https://manpages.debian.org/stretch/manpages-dev/puts.3.en.html) and
// [Ruby](https://ruby-doc.org/core-2.2.2/IO.html#method-i-puts) as `puts`.
func put(fm *Frame, args ...interface{}) {
out := fm.ports[1].Chan
for _, a := range args {
@ -386,6 +87,31 @@ func put(fm *Frame, args ...interface{}) {
}
}
//elvdoc:fn read-upto
//
// ```elvish
// read-upto $delim
// ```
//
// Reads byte input until `$delim` or end-of-file is encountered, and outputs
// the part of the input read as a string value. The output contains the
// trailing `$delim`, unless `read-upto` terminated at end-of-file.
//
// The `$delim` argument must be a single rune in the ASCII range.
//
// Examples:
//
// ```elvish-transcript
// ~> echo "a,b,c" | read-upto ","
// ▶ 'a,'
// ~> echo "foo\nbar" | read-upto "\n"
// ▶ "foo\n"
// ~> echo "a.elv\x00b.elv" | read-upto "\x00"
// ▶ "a.elv\x00"
// ~> print "foobar" | read-upto "\n"
// ▶ foobar
// ```
func readUpto(fm *Frame, last string) (string, error) {
if len(last) != 1 {
return "", ErrArgs
@ -438,6 +164,21 @@ func readLine(fm *Frame) (string, error) {
return ChopLineEnding(s), nil
}
//elvdoc:fn print
//
// ```elvish
// print &sep=' ' $value...
// ```
//
// Like `echo`, just without the newline.
//
// @cf echo
//
// Etymology: Various languages, in particular
// [Perl](https://perldoc.perl.org/functions/print.html) and
// [zsh](http://zsh.sourceforge.net/Doc/Release/Shell-Builtin-Commands.html), whose
// `print`s do not print a trailing newline.
type printOpts struct{ Sep string }
func (o *printOpts) SetDefaultOptions() { o.Sep = " " }
@ -452,11 +193,65 @@ func print(fm *Frame, opts printOpts, args ...interface{}) {
}
}
//elvdoc:fn echo
//
// ```elvish
// echo &sep=' ' $value...
// ```
//
// Print all arguments, joined by the `sep` option, and followed by a newline.
//
// Examples:
//
// ```elvish-transcript
// ~> echo Hello elvish
// Hello elvish
// ~> echo "Hello elvish"
// Hello elvish
// ~> echo &sep=, lorem ipsum
// lorem,ipsum
// ```
//
// Notes: The `echo` builtin does not treat `-e` or `-n` specially. For instance,
// `echo -n` just prints `-n`. Use double-quoted strings to print special
// characters, and `print` to suppress the trailing newline.
//
// @cf print
//
// Etymology: Bourne sh.
func echo(fm *Frame, opts printOpts, args ...interface{}) {
print(fm, opts, args...)
fm.ports[1].File.WriteString("\n")
}
//elvdoc:fn pprint
//
// ```elvish
// pprint $value...
// ```
//
// Pretty-print representations of Elvish values. Examples:
//
// ```elvish-transcript
// ~> pprint [foo bar]
// [
// foo
// bar
// ]
// ~> pprint [&k1=v1 &k2=v2]
// [
// &k2=
// v2
// &k1=
// v1
// ]
// ```
//
// The output format is subject to change.
//
// @cf repr
func pprint(fm *Frame, args ...interface{}) {
out := fm.ports[1].File
for _, arg := range args {
@ -465,6 +260,24 @@ func pprint(fm *Frame, args ...interface{}) {
}
}
//elvdoc:fn repr
//
// ```elvish
// repr $value...
// ```
//
// Writes representation of `$value`s, separated by space and followed by a
// newline. Example:
//
// ```elvish-transcript
// ~> repr [foo 'lorem ipsum'] "aha\n"
// [foo 'lorem ipsum'] "aha\n"
// ```
//
// @cf pprint
//
// Etymology: [Python](https://docs.python.org/3/library/functions.html#repr).
func repr(fm *Frame, args ...interface{}) {
out := fm.ports[1].File
for i, arg := range args {
@ -504,6 +317,21 @@ func show(fm *Frame, v diag.Shower) {
const bytesReadBufferSize = 512
//elvdoc:fn only-bytes
//
// ```elvish
// only-bytes
// ```
//
// Passes byte input to output, and discards value inputs.
//
// Example:
//
// ```elvish-transcript
// ~> { put value; echo bytes } | only-bytes
// bytes
// ```
func onlyBytes(fm *Frame) error {
// Discard values in a goroutine.
valuesDone := make(chan struct{})
@ -533,6 +361,21 @@ func onlyBytes(fm *Frame) error {
}
}
//elvdoc:fn only-values
//
// ```elvish
// only-values
// ```
//
// Passes value input to output, and discards byte inputs.
//
// Example:
//
// ```elvish-transcript
// ~> { put value; echo bytes } | only-values
// ▶ value
// ```
func onlyValues(fm *Frame) error {
// Forward values in a goroutine.
valuesDone := make(chan struct{})
@ -558,6 +401,25 @@ func onlyValues(fm *Frame) error {
}
}
//elvdoc:fn slurp
//
// ```elvish
// slurp
// ```
//
// Reads bytes input into a single string, and put this string on structured
// stdout.
//
// Example:
//
// ```elvish-transcript
// ~> echo "a\nb" | slurp
// ▶ "a\nb\n"
// ```
//
// Etymology: Perl, as
// [`File::Slurp`](http://search.cpan.org/~uri/File-Slurp-9999.19/lib/File/Slurp.pm).
func slurp(fm *Frame) (string, error) {
b, err := ioutil.ReadAll(fm.ports[0].File)
return string(b), err
@ -586,7 +448,39 @@ func fromLines(fm *Frame) {
linesToChan(fm.ports[0].File, fm.ports[1].Chan)
}
// fromJSON parses a stream of JSON data into Value's.
//elvdoc:fn from-json
//
// ```elvish
// from-json
// ```
//
// Takes bytes stdin, parses it as JSON and puts the result on structured stdout.
// The input can contain multiple JSONs, which can, but do not have to, be
// separated with whitespaces.
//
// Examples:
//
// ```elvish-transcript
// ~> echo '"a"' | from-json
// ▶ a
// ~> echo '["lorem", "ipsum"]' | from-json
// ▶ [lorem ipsum]
// ~> echo '{"lorem": "ipsum"}' | from-json
// ▶ [&lorem=ipsum]
// ~> # multiple JSONs running together
// echo '"a""b"["x"]' | from-json
// ▶ a
// ▶ b
// ▶ [x]
// ~> # multiple JSONs separated by newlines
// echo '"a"
// {"k": "v"}' | from-json
// ▶ a
// ▶ [&k=v]
// ```
//
// @cf to-json
func fromJSON(fm *Frame) error {
in := fm.ports[0].File
out := fm.ports[1].Chan
@ -640,7 +534,25 @@ func toLines(fm *Frame, inputs Inputs) {
})
}
// toJSON converts a stream of Value's to JSON data.
//elvdoc:fn to-json
//
// ```elvish
// to-json
// ```
//
// Takes structured stdin, convert it to JSON and puts the result on bytes stdout.
//
// ```elvish-transcript
// ~> put a | to-json
// "a"
// ~> put [lorem ipsum] | to-json
// ["lorem","ipsum"]
// ~> put [&lorem=ipsum] | to-json
// {"lorem":"ipsum"}
// ```
//
// @cf from-json
func toJSON(fm *Frame, inputs Inputs) error {
encoder := json.NewEncoder(fm.OutputFile())
@ -654,24 +566,110 @@ func toJSON(fm *Frame, inputs Inputs) error {
return errEncode
}
//elvdoc:fn fopen
//
// ```elvish
// fopen $filename
// ```
//
// Open a file. Currently, `fopen` only supports opening a file for reading. File
// must be closed with `fclose` explicitly. Example:
//
// ```elvish-transcript
// ~> cat a.txt
// This is
// a file.
// ~> f = (fopen a.txt)
// ~> cat < $f
// This is
// a file.
// ~> fclose $f
// ```
//
// @cf fclose
func fopen(name string) (vals.File, error) {
// TODO support opening files for writing etc as well.
return os.Open(name)
}
//elvdoc:fn fclose
//
// ```elvish
// fclose $file
// ```
//
// Close a file opened with `fopen`.
//
// @cf fopen
func fclose(f vals.File) error {
return f.Close()
}
//elvdoc:fn pipe
//
// ```elvish
// pipe
// ```
//
// Create a new Unix pipe that can be used in redirections.
//
// A pipe contains both the read FD and the write FD. When redirecting command
// input to a pipe with `<`, the read FD is used. When redirecting command output
// to a pipe with `>`, the write FD is used. It is not supported to redirect both
// input and output with `<>` to a pipe.
//
// Pipes have an OS-dependent buffer, so writing to a pipe without an active reader
// does not necessarily block. Pipes **must** be explicitly closed with `prclose`
// and `pwclose`.
//
// Putting values into pipes will cause those values to be discarded.
//
// Examples (assuming the pipe has a large enough buffer):
//
// ```elvish-transcript
// ~> p = (pipe)
// ~> echo 'lorem ipsum' > $p
// ~> head -n1 < $p
// lorem ipsum
// ~> put 'lorem ipsum' > $p
// ~> head -n1 < $p
// # blocks
// # $p should be closed with prclose and pwclose afterwards
// ```
//
// @cf prclose pwclose
func pipe() (vals.Pipe, error) {
r, w, err := os.Pipe()
return vals.NewPipe(r, w), err
}
//elvdoc:fn prclose
//
// ```elvish
// prclose $pipe
// ```
//
// Close the read end of a pipe.
//
// @cf pwclose pipe
func prclose(p vals.Pipe) error {
return p.ReadEnd.Close()
}
//elvdoc:fn pwclose
//
// ```elvish
// pwclose $pipe
// ```
//
// Close the write end of a pipe.
//
// @cf prclose pipe
func pwclose(p vals.Pipe) error {
return p.WriteEnd.Close()
}

View File

@ -17,6 +17,29 @@ import (
// Builtins that have not been put into their own groups go here.
// TODO(xiaq): Document esleep.
func init() {
addBuiltinFns(map[string]interface{}{
"nop": nop,
"kind-of": kindOf,
"constantly": constantly,
"resolve": resolve,
"-source": source,
// Time
"esleep": sleep,
"time": timeCmd,
"-ifaddrs": _ifaddrs,
})
// For rand and randint.
rand.Seed(time.Now().UTC().UnixNano())
}
//elvdoc:fn nop
//
// ```elvish
@ -36,6 +59,10 @@ import (
// Etymology: Various languages, in particular NOP in
// [assembly languages](https://en.wikipedia.org/wiki/NOP).
func nop(opts RawOptions, args ...interface{}) {
// Do nothing
}
//elvdoc:fn kind-of
//
// ```elvish
@ -53,6 +80,13 @@ import (
//
// The terminology and definition of "kind" is subject to change.
func kindOf(fm *Frame, args ...interface{}) {
out := fm.ports[1].Chan
for _, a := range args {
out <- vals.Kind(a)
}
}
//elvdoc:fn constantly
//
// ```elvish
@ -85,6 +119,19 @@ import (
//
// Etymology: [Clojure](https://clojuredocs.org/clojure.core/constantly).
func constantly(args ...interface{}) Callable {
// TODO(xiaq): Repr of this function is not right.
return NewGoFn(
"created by constantly",
func(fm *Frame) {
out := fm.ports[1].Chan
for _, v := range args {
out <- v
}
},
)
}
//elvdoc:fn resolve
//
// ```elvish
@ -106,6 +153,21 @@ import (
// ▶ <external cat>
// ```
func resolve(fm *Frame, head string) string {
// Emulate static resolution of a command head. This needs to be kept in
// sync with (*compiler).form.
_, special := builtinSpecials[head]
if special {
return "special"
}
sigil, qname := SplitVariableRef(head)
if sigil == "" && fm.ResolveVar(qname+FnSuffix) != nil {
return "$" + qname + FnSuffix
}
return "(external " + parse.Quote(head) + ")"
}
//elvdoc:fn -source
//
// ```elvish
@ -160,110 +222,6 @@ import (
// bar
// ```
// TODO(xiaq): Document esleep.
//elvdoc:fn time
//
// ```elvish
// time &on-end=$nil $callable
// ```
//
// Runs the callable, and call `$on-end` with the duration it took, as a
// number in seconds. If `$on-end` is `$nil` (the default), prints the
// duration in human-readable form.
//
// If `$callable` throws an exception, the exception is propagated after the
// on-end or default printing is done.
//
// If `$on-end` throws an exception, it is propagated, unless `$callable` has
// already thrown an exception.
//
// Example:
//
// ```elvish-transcript
// ~> time { sleep 1 }
// 1.006060647s
// ~> time { sleep 0.01 }
// 1.288977ms
// ~> t = ''
// ~> time &on-end=[x]{ t = $x } { sleep 1 }
// ~> put $t
// ▶ (float64 1.000925004)
// ~> time &on-end=[x]{ t = $x } { sleep 0.01 }
// ~> put $t
// ▶ (float64 0.011030208)
// ```
//elvdoc:fn -ifaddrs
//
// ```elvish
// -ifaddrs
// ```
//
// Output all IP addresses of the current host.
//
// This should be part of a networking module instead of the builtin module.
func init() {
addBuiltinFns(map[string]interface{}{
"nop": nop,
"kind-of": kindOf,
"constantly": constantly,
"resolve": resolve,
"-source": source,
// Time
"esleep": sleep,
"time": timeCmd,
"-ifaddrs": _ifaddrs,
})
// For rand and randint.
rand.Seed(time.Now().UTC().UnixNano())
}
func nop(opts RawOptions, args ...interface{}) {
// Do nothing
}
func kindOf(fm *Frame, args ...interface{}) {
out := fm.ports[1].Chan
for _, a := range args {
out <- vals.Kind(a)
}
}
func constantly(args ...interface{}) Callable {
// TODO(xiaq): Repr of this function is not right.
return NewGoFn(
"created by constantly",
func(fm *Frame) {
out := fm.ports[1].Chan
for _, v := range args {
out <- v
}
},
)
}
func resolve(fm *Frame, head string) string {
// Emulate static resolution of a command head. This needs to be kept in
// sync with (*compiler).form.
_, special := builtinSpecials[head]
if special {
return "special"
}
sigil, qname := SplitVariableRef(head)
if sigil == "" && fm.ResolveVar(qname+FnSuffix) != nil {
return "$" + qname + FnSuffix
}
return "(external " + parse.Quote(head) + ")"
}
func source(fm *Frame, fname string) error {
path, err := filepath.Abs(fname)
if err != nil {
@ -310,6 +268,38 @@ func sleep(fm *Frame, t float64) error {
}
}
//elvdoc:fn time
//
// ```elvish
// time &on-end=$nil $callable
// ```
//
// Runs the callable, and call `$on-end` with the duration it took, as a
// number in seconds. If `$on-end` is `$nil` (the default), prints the
// duration in human-readable form.
//
// If `$callable` throws an exception, the exception is propagated after the
// on-end or default printing is done.
//
// If `$on-end` throws an exception, it is propagated, unless `$callable` has
// already thrown an exception.
//
// Example:
//
// ```elvish-transcript
// ~> time { sleep 1 }
// 1.006060647s
// ~> time { sleep 0.01 }
// 1.288977ms
// ~> t = ''
// ~> time &on-end=[x]{ t = $x } { sleep 1 }
// ~> put $t
// ▶ (float64 1.000925004)
// ~> time &on-end=[x]{ t = $x } { sleep 0.01 }
// ~> put $t
// ▶ (float64 0.011030208)
// ```
type timeOpt struct{ OnEnd Callable }
func (o *timeOpt) SetDefaultOptions() {}
@ -333,6 +323,16 @@ func timeCmd(fm *Frame, opts timeOpt, f Callable) error {
return err
}
//elvdoc:fn -ifaddrs
//
// ```elvish
// -ifaddrs
// ```
//
// Output all IP addresses of the current host.
//
// This should be part of a networking module instead of the builtin module.
func _ifaddrs(fm *Frame) error {
addrs, err := net.InterfaceAddrs()
if err != nil {

View File

@ -8,6 +8,45 @@ import (
// Numerical operations.
//elvdoc:fn rand
//
// ```elvish
// rand
// ```
//
// Output a pseudo-random number in the interval [0, 1). Example:
//
// ```elvish-transcript
// ~> rand
// ▶ 0.17843564133528436
// ```
func init() {
addBuiltinFns(map[string]interface{}{
// Constructor
"float64": toFloat64,
// Comparison
"<": lt,
"<=": le,
"==": eqNum,
"!=": ne,
">": gt,
">=": ge,
// Arithmetics
"+": plus,
"-": minus,
"*": times,
"/": slash,
"%": mod,
// Random
"rand": rand.Float64,
"randint": randint,
})
}
//elvdoc:fn float64
//
// ```elvish
@ -22,58 +61,9 @@ import (
// convert a string to a number. See the discussion of the
// [number](language.html#number) data type.
//elvdoc:fn + - * /
//
// ```elvish
// + $summand...
// - $minuend $subtrahend...
// * $factor...
// / $dividend $divisor...
// ```
//
// Basic arithmetic operations of adding, subtraction, multiplication and division
// respectively.
//
// All of them can take multiple arguments:
//
// ```elvish-transcript
// ~> + 2 5 7 # 2 + 5 + 7
// ▶ 14
// ~> - 2 5 7 # 2 - 5 - 7
// ▶ -10
// ~> * 2 5 7 # 2 * 5 * 7
// ▶ 70
// ~> / 2 5 7 # 2 / 5 / 7
// ▶ 0.05714285714285715
// ```
//
// When given one element, they all output their sole argument (given that it is a
// valid number). When given no argument,
//
// - `+` outputs 0, and `*` outputs 1. You can think that they both have a
// "hidden" argument of 0 or 1, which does not alter their behaviors (in
// mathematical terms, 0 and 1 are
// [identity elements](https://en.wikipedia.org/wiki/Identity_element) of
// addition and multiplication, respectively).
//
// - `-` throws an exception.
//
// - `/` becomes a synonym for `cd /`, due to the implicit cd feature. (The
// implicit cd feature will probably change to avoid this oddity).
//elvdoc:fn %
//
// ```elvish
// % $dividend $divisor
// ```
//
// Output the remainder after dividing `$dividend` by `$divisor`. Both must be
// integers. Example:
//
// ```elvish-transcript
// ~> % 23 7
// ▶ 2
// ```
func toFloat64(f float64) float64 {
return f
}
//elvdoc:fn &lt; &lt;= == != &gt; &gt;=
//
@ -120,63 +110,6 @@ import (
// ▶ $true
// ```
//elvdoc:fn rand
//
// ```elvish
// rand
// ```
//
// Output a pseudo-random number in the interval [0, 1). Example:
//
// ```elvish-transcript
// ~> rand
// ▶ 0.17843564133528436
// ```
//elvdoc:fn randint
//
// ```elvish
// randint $low $high
// ```
//
// Output a pseudo-random integer in the interval [$low, $high). Example:
//
// ```elvish-transcript
// ~> # Emulate dice
// randint 1 7
// ▶ 6
// ```
func init() {
addBuiltinFns(map[string]interface{}{
// Constructor
"float64": toFloat64,
// Comparison
"<": lt,
"<=": le,
"==": eqNum,
"!=": ne,
">": gt,
">=": ge,
// Arithmetics
"+": plus,
"-": minus,
"*": times,
"/": slash,
"%": mod,
// Random
"rand": rand.Float64,
"randint": randint,
})
}
func toFloat64(f float64) float64 {
return f
}
func lt(nums ...float64) bool {
for i := 0; i < len(nums)-1; i++ {
if !(nums[i] < nums[i+1]) {
@ -231,6 +164,45 @@ func ge(nums ...float64) bool {
return true
}
//elvdoc:fn + - * /
//
// ```elvish
// + $summand...
// - $minuend $subtrahend...
// * $factor...
// / $dividend $divisor...
// ```
//
// Basic arithmetic operations of adding, subtraction, multiplication and division
// respectively.
//
// All of them can take multiple arguments:
//
// ```elvish-transcript
// ~> + 2 5 7 # 2 + 5 + 7
// ▶ 14
// ~> - 2 5 7 # 2 - 5 - 7
// ▶ -10
// ~> * 2 5 7 # 2 * 5 * 7
// ▶ 70
// ~> / 2 5 7 # 2 / 5 / 7
// ▶ 0.05714285714285715
// ```
//
// When given one element, they all output their sole argument (given that it is a
// valid number). When given no argument,
//
// - `+` outputs 0, and `*` outputs 1. You can think that they both have a
// "hidden" argument of 0 or 1, which does not alter their behaviors (in
// mathematical terms, 0 and 1 are
// [identity elements](https://en.wikipedia.org/wiki/Identity_element) of
// addition and multiplication, respectively).
//
// - `-` throws an exception.
//
// - `/` becomes a synonym for `cd /`, due to the implicit cd feature. (The
// implicit cd feature will probably change to avoid this oddity).
func plus(nums ...float64) float64 {
sum := 0.0
for _, f := range nums {
@ -276,6 +248,20 @@ func divide(fm *Frame, prod float64, nums ...float64) {
out <- vals.FromGo(prod)
}
//elvdoc:fn %
//
// ```elvish
// % $dividend $divisor
// ```
//
// Output the remainder after dividing `$dividend` by `$divisor`. Both must be
// integers. Example:
//
// ```elvish-transcript
// ~> % 23 7
// ▶ 2
// ```
func mod(a, b int) (int, error) {
if b == 0 {
return 0, ErrArgs
@ -283,6 +269,20 @@ func mod(a, b int) (int, error) {
return a % b, nil
}
//elvdoc:fn randint
//
// ```elvish
// randint $low $high
// ```
//
// Output a pseudo-random integer in the interval [$low, $high). Example:
//
// ```elvish-transcript
// ~> # Emulate dice
// randint 1 7
// ▶ 6
// ```
func randint(low, high int) (int, error) {
if low >= high {
return 0, ErrArgs

View File

@ -33,6 +33,16 @@ import "github.com/elves/elvish/pkg/eval/vals"
//
// @cf not
func init() {
addBuiltinFns(map[string]interface{}{
"bool": vals.Bool,
"not": not,
"is": is,
"eq": eq,
"not-eq": notEq,
})
}
//elvdoc:fn not
//
// ```elvish
@ -56,6 +66,10 @@ import "github.com/elves/elvish/pkg/eval/vals"
//
// @cf bool
func not(v interface{}) bool {
return !vals.Bool(v)
}
//elvdoc:fn is
//
// ```elvish
@ -82,6 +96,15 @@ import "github.com/elves/elvish/pkg/eval/vals"
//
// Etymology: [Python](https://docs.python.org/3/reference/expressions.html#is).
func is(args ...interface{}) bool {
for i := 0; i+1 < len(args); i++ {
if args[i] != args[i+1] {
return false
}
}
return true
}
//elvdoc:fn eq
//
// ```elvish
@ -106,6 +129,15 @@ import "github.com/elves/elvish/pkg/eval/vals"
//
// Etymology: [Perl](https://perldoc.perl.org/perlop.html#Equality-Operators).
func eq(args ...interface{}) bool {
for i := 0; i+1 < len(args); i++ {
if !vals.Equal(args[i], args[i+1]) {
return false
}
}
return true
}
//elvdoc:fn not-eq
//
// ```elvish
@ -126,38 +158,6 @@ import "github.com/elves/elvish/pkg/eval/vals"
//
// @cf eq
func init() {
addBuiltinFns(map[string]interface{}{
"bool": vals.Bool,
"not": not,
"is": is,
"eq": eq,
"not-eq": notEq,
})
}
func not(v interface{}) bool {
return !vals.Bool(v)
}
func is(args ...interface{}) bool {
for i := 0; i+1 < len(args); i++ {
if args[i] != args[i+1] {
return false
}
}
return true
}
func eq(args ...interface{}) bool {
for i := 0; i+1 < len(args); i++ {
if !vals.Equal(args[i], args[i+1]) {
return false
}
}
return true
}
func notEq(args ...interface{}) bool {
for i := 0; i+1 < len(args); i++ {
if vals.Equal(args[i], args[i+1]) {

View File

@ -41,90 +41,6 @@ var ErrInputOfEawkMustBeString = errors.New("input of eawk must be string")
// ▶ $true
// ```
//elvdoc:fn to-string
//
// ```elvish
// to-string $value...
// ```
//
// Convert arguments to string values.
//
// ```elvish-transcript
// ~> to-string foo [a] [&k=v]
// ▶ foo
// ▶ '[a]'
// ▶ '[&k=v]'
// ```
//elvdoc:fn ord
//
// ```elvish
// ord $string
// ```
//
// This function is deprecated; use [str:to-codepoints](str.html#strto-codepoints) instead.
//
// Output value of each codepoint in `$string`, in hexadecimal. Examples:
//
// ```elvish-transcript
// ~> ord a
// ▶ 0x61
// ~> ord 你好
// ▶ 0x4f60
// ▶ 0x597d
// ```
//
// The output format is subject to change.
//
// Etymology: [Python](https://docs.python.org/3/library/functions.html#ord).
//
// @cf chr
//elvdoc:fn chr
//
// ```elvish
// chr $number...
// ```
//
// This function is deprecated; use [str:from-codepoints](str.html#strfrom-codepoints) instead.
//
// Outputs a string consisting of the given Unicode codepoints. Example:
//
// ```elvish-transcript
// ~> chr 0x61
// ▶ a
// ~> chr 0x4f60 0x597d
// ▶ 你好
// ```
//
// Etymology: [Python](https://docs.python.org/3/library/functions.html#chr).
//
// @cf ord
//elvdoc:fn base
//
// ```elvish
// base $base $number...
// ```
//
// Outputs a string for each `$number` written in `$base`. The `$base` must be
// between 2 and 36, inclusive. Examples:
//
// ```elvish-transcript
// ~> base 2 1 3 4 16 255
// ▶ 1
// ▶ 11
// ▶ 100
// ▶ 10000
// ▶ 11111111
// ~> base 16 1 3 4 16 255
// ▶ 1
// ▶ 3
// ▶ 4
// ▶ 10
// ▶ ff
// ```
//elvdoc:fn wcswidth
//
// ```elvish
@ -180,40 +96,6 @@ var ErrInputOfEawkMustBeString = errors.New("input of eawk must be string")
// This function is deprecated; use [str:has-suffix](str.html#strhas-suffix)
// instead.
//elvdoc:fn eawk
//
// ```elvish
// eawk $f $input-list?
// ```
//
// For each input, call `$f` with the input followed by all its fields.
//
// It should behave the same as the following functions:
//
// ```elvish
// fn eawk [f @rest]{
// each [line]{
// @fields = (re:split '[ \t]+'
// (re:replace '^[ \t]+|[ \t]+$' '' $line))
// $f $line $@fields
// } $@rest
// }
// ```
//
// This command allows you to write code very similar to `awk` scripts using
// anonymous functions. Example:
//
// ```elvish-transcript
// ~> echo ' lorem ipsum
// 1 2' | awk '{ print $1 }'
// lorem
// 1
// ~> echo ' lorem ipsum
// 1 2' | eawk [line a b]{ put $a }
// ▶ lorem
// ▶ 1
// ```
func init() {
addBuiltinFns(map[string]interface{}{
"<s": func(a, b string) bool { return a < b },
@ -239,7 +121,21 @@ func init() {
})
}
// toString converts all arguments to strings.
//elvdoc:fn to-string
//
// ```elvish
// to-string $value...
// ```
//
// Convert arguments to string values.
//
// ```elvish-transcript
// ~> to-string foo [a] [&k=v]
// ▶ foo
// ▶ '[a]'
// ▶ '[&k=v]'
// ```
func toString(fm *Frame, args ...interface{}) {
out := fm.OutputChan()
for _, a := range args {
@ -247,6 +143,30 @@ func toString(fm *Frame, args ...interface{}) {
}
}
//elvdoc:fn ord
//
// ```elvish
// ord $string
// ```
//
// This function is deprecated; use [str:to-codepoints](str.html#strto-codepoints) instead.
//
// Output value of each codepoint in `$string`, in hexadecimal. Examples:
//
// ```elvish-transcript
// ~> ord a
// ▶ 0x61
// ~> ord 你好
// ▶ 0x4f60
// ▶ 0x597d
// ```
//
// The output format is subject to change.
//
// Etymology: [Python](https://docs.python.org/3/library/functions.html#ord).
//
// @cf chr
func ord(fm *Frame, s string) {
out := fm.ports[1].Chan
for _, r := range s {
@ -254,6 +174,27 @@ func ord(fm *Frame, s string) {
}
}
//elvdoc:fn chr
//
// ```elvish
// chr $number...
// ```
//
// This function is deprecated; use [str:from-codepoints](str.html#strfrom-codepoints) instead.
//
// Outputs a string consisting of the given Unicode codepoints. Example:
//
// ```elvish-transcript
// ~> chr 0x61
// ▶ a
// ~> chr 0x4f60 0x597d
// ▶ 你好
// ```
//
// Etymology: [Python](https://docs.python.org/3/library/functions.html#chr).
//
// @cf ord
func chr(nums ...int) (string, error) {
var b bytes.Buffer
for _, num := range nums {
@ -265,6 +206,30 @@ func chr(nums ...int) (string, error) {
return b.String(), nil
}
//elvdoc:fn base
//
// ```elvish
// base $base $number...
// ```
//
// Outputs a string for each `$number` written in `$base`. The `$base` must be
// between 2 and 36, inclusive. Examples:
//
// ```elvish-transcript
// ~> base 2 1 3 4 16 255
// ▶ 1
// ▶ 11
// ▶ 100
// ▶ 10000
// ▶ 11111111
// ~> base 16 1 3 4 16 255
// ▶ 1
// ▶ 3
// ▶ 4
// ▶ 10
// ▶ ff
// ```
// ErrBadBase is thrown by the "base" builtin if the base is smaller than 2 or
// greater than 36.
var ErrBadBase = errors.New("bad base")
@ -283,11 +248,41 @@ func base(fm *Frame, b int, nums ...int) error {
var eawkWordSep = regexp.MustCompile("[ \t]+")
// eawk takes a function. For each line in the input stream, it calls the
// function with the line and the words in the line. The words are found by
// stripping the line and splitting the line by whitespaces. The function may
// call break and continue. Overall this provides a similar functionality to
// awk, hence the name.
//elvdoc:fn eawk
//
// ```elvish
// eawk $f $input-list?
// ```
//
// For each input, call `$f` with the input followed by all its fields. The
// function may call `break` and `continue`.
//
// It should behave the same as the following functions:
//
// ```elvish
// fn eawk [f @rest]{
// each [line]{
// @fields = (re:split '[ \t]+'
// (re:replace '^[ \t]+|[ \t]+$' '' $line))
// $f $line $@fields
// } $@rest
// }
// ```
//
// This command allows you to write code very similar to `awk` scripts using
// anonymous functions. Example:
//
// ```elvish-transcript
// ~> echo ' lorem ipsum
// 1 2' | awk '{ print $1 }'
// lorem
// 1
// ~> echo ' lorem ipsum
// 1 2' | eawk [line a b]{ put $a }
// ▶ lorem
// ▶ 1
// ```
func eawk(fm *Frame, f Callable, inputs Inputs) error {
broken := false
var err error

View File

@ -11,6 +11,13 @@ import (
var errStyledSegmentArgType = errors.New("argument to styled-segment must be a string or a styled segment")
func init() {
addBuiltinFns(map[string]interface{}{
"styled-segment": styledSegment,
"styled": Styled,
})
}
//elvdoc:fn styled-segment
//
// ```elvish
@ -36,6 +43,33 @@ var errStyledSegmentArgType = errors.New("argument to styled-segment must be a s
// put $s[bold]
// ```
// Turns a string or ui.Segment into a new ui.Segment with the attributes
// from the supplied options applied to it. If the input is already a Segment its
// attributes are copied and modified.
func styledSegment(options RawOptions, input interface{}) (*ui.Segment, error) {
var text string
var style ui.Style
switch input := input.(type) {
case string:
text = input
case *ui.Segment:
text = input.Text
style = input.Style
default:
return nil, errStyledSegmentArgType
}
if err := style.MergeFromOptions(options); err != nil {
return nil, err
}
return &ui.Segment{
Text: text,
Style: style,
}, nil
}
//elvdoc:fn styled
//
// ```elvish
@ -98,40 +132,6 @@ var errStyledSegmentArgType = errors.New("argument to styled-segment must be a s
// put $s[0] $s[1]
// ```
func init() {
addBuiltinFns(map[string]interface{}{
"styled-segment": styledSegment,
"styled": Styled,
})
}
// Turns a string or ui.Segment into a new ui.Segment with the attributes
// from the supplied options applied to it. If the input is already a Segment its
// attributes are copied and modified.
func styledSegment(options RawOptions, input interface{}) (*ui.Segment, error) {
var text string
var style ui.Style
switch input := input.(type) {
case string:
text = input
case *ui.Segment:
text = input.Text
style = input.Style
default:
return nil, errStyledSegmentArgType
}
if err := style.MergeFromOptions(options); err != nil {
return nil, err
}
return &ui.Segment{
Text: text,
Style: style,
}, nil
}
// Styled turns a string, a ui.Segment or a ui.Text into a ui.Text by applying
// the given stylings.
func Styled(fm *Frame, input interface{}, stylings ...interface{}) (ui.Text, error) {