mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-05 03:17:50 +08:00
pkg/eval: Move elvdoc comments before the implementations.
This commit is contained in:
parent
d5736120e5
commit
9b2b33b429
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 < <= == != > >=
|
||||
//
|
||||
|
@ -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
|
||||
|
|
|
@ -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]) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user