Giter Club home page Giter Club logo

elvish's Introduction

Elvish: Expressive Programming Language + Versatile Interactive Shell

CI status FreeBSD & gccgo test status Test Coverage Go Reference Packaging status Twitter

Elvish is a powerful scripting language and a versatile interactive shell. It runs on Linux, macOS, BSDs and Windows.

Elvish is pre-1.0. This means that the features set will change between versions, but it's quite stable for both scripting and interactive use.

User groups (all connected thanks to Matrix):

Telegram Group Discord server #users:elv.sh #elvish on libera.chat Gitter

Documentation

Documentation for Elvish lives on the official website https://elv.sh, including:

The source for the documentation is in the website directory.

License

All source files use the BSD 2-clause license (see LICENSE), except for the following:

Building Elvish

Most users do not need to build Elvish from source. Prebuilt binaries for the latest commit are provided for Linux amd64, macOS amd64, macOS arm64, Windows amd64 and many other platforms.

To build Elvish from source, you need

  • A supported OS: Linux, {Free,Net,Open}BSD, macOS, or Windows 10. Windows 10 support is experimental.

  • Go >= 1.21.0.

To build Elvish from source, run one of the following commands:

go install src.elv.sh/cmd/elvish@master # Install latest commit
go install src.elv.sh/cmd/elvish@latest # Install latest released version
go install src.elv.sh/cmd/[email protected] # Install a specific version

Controlling the installation location

The go install command installs Elvish to $GOBIN; the binary name is elvish. You can control the installation location by overriding $GOBIN, for example by prepending env GOBIN=... to the go install command.

If $GOBIN is not set, the installation location defaults to $GOPATH/bin, which in turn defaults to ~/go/bin if $GOPATH is also not set.

The installation directory is probably not in your OS's default $PATH. You should either either add it to $PATH, or manually copy the Elvish binary to a directory already in $PATH.

Building a variant

Elvish has several build variants with slightly different feature sets. For example, the withpprof build variant has profiling support.

These build variants are just alternative main packages. For example, to build the withpprof variant, run the following command (change the part after @ to get different versions):

go install src.elv.sh/cmd/withpprof/elvish@master

Building from a local source tree

If you are modifying Elvish's source code, you will want to clone Elvish's Git repository and build Elvish from the local source tree instead. To do this, run the following from the root of the source tree:

go install ./cmd/elvish

There is no need to specify a version like @master; when inside a source tree, go install will always use the whatever source code is present.

See CONTRIBUTING.md for more notes for contributors.

Building with experimental plugin support

Elvish has experimental support for building and importing plugins, modules written in Go. It relies on Go's plugin support, which is only available on a few platforms.

Plugin support requires building Elvish with cgo. The official prebuilt binaries are built without cgo for compatibility and reproducibility, but by default the Go toolchain builds with cgo enabled.

If you have built Elvish from source on a platform with plugin support, your Elvish build probably already supports plugins. To force cgo to be used when building Elvish, you can do the following:

env CGO_ENABLED=1 go install ./cmd/elvish

To build a plugin, see this example.

Packaging Elvish

See PACKAGING.md for notes for packagers.

Contributing to Elvish

See CONTRIBUTING.md for notes for contributors.

Reporting security issues

See SECURITY.md for how to report security issues.

elvish's People

Contributors

0x005c avatar adracea avatar alschwalm avatar duncan3142 avatar dunsany avatar exactly-one-kas avatar fehnomenal avatar heavyhorst avatar hexchain avatar hoop33 avatar huiyiqun avatar iandol avatar iwoloschin avatar jiujieti avatar kenshaw avatar krader1961 avatar mmlb avatar moko256 avatar muesli avatar saolof avatar sauyon avatar sblundy avatar star-szr avatar supreetsingh10 avatar tw4452852 avatar xchenan avatar xiaq avatar xofyarg avatar zhsj avatar zzamboni avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

elvish's Issues

Namespaces and modules

Namespaces are containers of variables. There are some predefined namespaces, and by importing modules additional namespaces may be introduced.

Modules are self-contained code units that facilitate code reusing. In particular, shell plugins are modules.

As of 74e3db4, nothing of the namespace or module system has been implemented. This issue only keeps track of the early designing phase. Once a prototypical implementation is pushed, this issue should be closed.

Meta-character viability

This note evaluates the viability of meta-characters, namely characters that are part of the syntax. Each is given a score along with the reasons, either historical or practical. Well-known uses in POSIX sh are always given a score of +5; other scores are determined case by case.

  • ~ +5
    • sh: shorthand for $HOME at beginning of a word (+5)
  • # +5
    • sh: comment (+5)
  • $ +5
    • sh: variable leader (+5)
  • & +5
    • sh: background job (+5)
  • * +5
    • sh: glob
  • ? +5
    • sh: glob
  • < > +5
    • sh: redir (+5)
  • " +5
    • sh: string literal (+5)
  • ' +5
    • sh: string literal (+5)
  • - sh: command substitution (+5)
    
  • | +5
    • sh: pipeline
  • \ +5
    • sh: escape
  • ; +5
    • sh: command terminator (+5)
  • { } +5
    • sh: brace expansion (+5)
  • [ ] +4
    • sh: glob (+5)
    • ipv6 address (-1)
  • ^ +4
    • bash: history substitution (+2)
    • fish: stderr redirec (+1)
    • rc: word joiner (+1)
  • ! +2
    • bash, zsh: history expansion (+3)
    • plan9: address delimiter (-1)
  • % +1
    • fish: job leader (+1)
  • ( ) +1
    • zsh: glob (+1)
    • fish: command substitution (+1)
    • common in natural languages (-1)
  • @ -2
    • ssh: username@host (-2)
  • + -2
    • less, sh: +option (-2)
  • : -3
    • lsof: port (-2)
    • ipv6 address (-1)
  • , -3
    • --option-key=v1,v2
  • . -5
    • a.out
  • / -5
    • /path/to/file
  • = -5
    • used in --option-key=value (-5)
  • - -5
    • used in --option (-5)
  • _ -5
    • used in file_names (-5)

Exception/error handling and exit code

The traditional approach is:

  1. Abnormal conditions are signalled by non-zero return values, which is stored into $? and ignored by default (unless -e is set).
  2. The return values of a pipeline is historically obscure. In bash it is stored into $PIPESTATUS, in zsh $pipestatus. However, only the return value of the last command goes into $? and interacts with -e.
  3. if takes a pipeline and if the pipeline has non-zero exit value ($?, not $PIPESTATUS), the enclosed block is executed.

Maybe we can do better, but I'm not sure which way to go. Some choices:

  1. Stay "shellish" and just clean up the old $?/$PIPESTATUS mess a bit;
  2. Adopt the conventional exception system popularized by C++ and also found in Java and Python;
  3. Adopt the success/fail system of Icon;
  4. Go with golang and treat exceptions just as a return value, but errors as non-local control structures.

The decision can affect how if should work.

I wish we could:

  1. Make a clear distinction of Error vs. Exception. Of the aforementioned choices, only golang does this;

  2. Make it easy to reason about intermediate states should an exception/error occur. As an example of intermediate state, in

    var $a $b = (echo a | tee /tmp/a | feedchan)
    

    The assignment fails, but /tmp/a is created.

Closures and flow control commands

In Elvish, blocks in control structures like if, for, etc. are really just closures. This simplifies scoping and encourages use of higher order function.

However, using closures as blocks require one important extension: flow control commands like return, continue and break need to have knowledge of the surrounding closure. For instance, in

fn f {
    if true {
        return 1
    }
}

The return terminates the fn-block, not the if-block.

Open questions:

  1. How should this be done exactly? Should it be part of the type system?
  2. Should the ability to interpret flow control commands be exposed to commands, so that control commands like for can be implemented by the user?

Support invoking pipelines with channel output interactively

Currently you cannot invoke pipelines with channel output interactively, since stdout is a fd port. As a result, arithmetic examples in README are broken and require a trailing | printchan to work:

~> / 1 0
<tty 2>:1:1: error: pipeline output not satisfiable
/ 1 0
^
~> / 1 0 | printchan
+Inf

It would be nice if the printchan above is implicit. However, the printchan is better left explicit in scripts. There are two options of doing this:

  1. Let the Compiler add a trailing printchan command to such pipelines, but only in interactive mode. However this makes code that are correct in interactive mode incorrect in scripts.
  2. Let the Editor add the trailing | printchan before compiling and executing. This requires a bit more work.

Option 2 is better.

Add Subfolder Suggestions

You can't get suggestions cd after the first folder.

I should be able to type cd and a few letters then tab to complete that folder and then type a few letters and tab to complete the sub folder.

(Checker + Evaluator -> Compiler + Evaluator) refactor

Current flow of source code:

source code [parser]-> AST [checker]-> annotated AST [evaluator]-> effects

That is, the checker checks and annotates the AST with information available statically and the evaluator evaluates the annotated AST. This poses two problems:

  1. The AST is walked twice, meaning a change in the AST structs usually affect three places: the parser, the checker and the evaluator.
  2. Getting the types right is tedious. The type of the annotation is declared in the eval package, AST nodes in parse. eval imports parse; since golang forbids cyclic imports, parse cannot import eval and AST nodes cannot contain annotation types directly. To work around this, all annotations are typed interface{} and the Checker and Evaluator make type assertions everywhere.

The plan is to introduce a new component, a compiler that takes up the role of the checker and much of the evaluator. Instead of checking and annotating the AST, the compiler performs deferred evaluation of the AST. For instance, a factor is evaluated into a func(*Evaluator) []Value, where currently, the evaluator evaluates it into a []Value. Similar things apply to all levels of the AST, from terms to chunks.

This would solve the aforementioned problems, but by using closures extensively it could also make debugging a bit harder.

First class port (command redirection) values

Currently ports are only exposed to the user via command redirections. It would be nice if they are also available as first-class values.

Preliminary ideas about the syntax:

  1. Add a new builtin special form open which outputs a port value that would result from applying the redirections it is supplied with. For instance:

    var $f = (open >/dev/null)
    

    is roughly equivalent to the following in Python:

    f = open('/dev/null', 'w')
  2. Using the value is the same as command redirection:

    var $f = (open >/dev/null)
    ls >$f
    

Open questions:

  1. How should open behave when supplied with multiple redirections?
  2. In the previous example the direction of the port (>) appears twice. Should we try to eliminate this reduplication?
  3. What about channel ports?

Idiomatic Clojure Example

In the readme the clojure example is used to show how piping makes it more clear.

(require ['clojure.string :refer '(upper-case)])
(def strs '("aha" "LOLaha" "hahaLOL" "hum?"))
(def lols (sort (map upper-case
                     (filter #(re-find #"LOL" %) strs))))

However, the idiomatic way to express this in Clojure would be using the thread-last macro ->> which uses the result of each successive expression as the last argument for the next expression.

(def lols (->> strs (filter #(re-find #"LOL" %)) (map upper-case) sort))

Which is conceptually similar to piping, albeit with a few more parentheses.

Implement module file search algorithm for "use"

Currently use simply finds the module file relative to the current directory; a more elaborate algorithm is needed:

  1. When the file name is absolute, it is used directly;
  2. When the file name starts with ./ or ../, it is relative to the directory of the source file currently being evaluated, or the current directory when evaluated interactively;
  3. Otherwise the file is relative to ~/.elvish.

Simple job control mechanism

Elvish should support simple job control. One particular use case is to suspend a foreground process with ^Z, and use kill to send it a process or use fg to put it into foreground again.

On the other hand, control of background processes should be rethought and simplified. Today we have screen and tmux, and people now rarely do multiple things in the same shell session.

Gradual typing

Elvish currently lacks a static typing system. The basics could be taken from Typed Racket.

Some points:

  1. Besides typed arguments, functions have typed output. This makes it possible to type-check pipelines.
  2. (Maybe) Instead of requiring type annotations for functions, infer their types.

Open questions:

  1. Should Evaluator be type-aware?

Built-in autojump-like functionality

Hi,

Thanks for your new shell. It looks very promising. I would like to know if you have considered bundling somthing similar to autojump with your elvish?

In case you're not familiar with it, autojump (https://github.com/joelthelion/autojump) is a tool that learns the directories where you spend the most time and allows you to jump quickly to these directories. A lot of people have found it useful. Autojump is not built into the shells it extends, and as a result it is a bit fragile and difficult to maintain. Having it built into elvish could be a great addition to the already very nice features of the new shell.

Feel free to close this issue if you think this is a bad idea, or simply not a priority.

Immutable and persistent data structures (a la Clojure)

It would be nice to follow clojure's approach and have immutable data structures by default.

Pros:

  1. Facilitates concurrency

  2. Avoids the reference vs. value semantics problem; consider the following in Python:

    >>> a = [[1]] * 5
    >>> a
    [[1], [1], [1], [1], [1]]
    >>> a[0][0] = 2
    >>> a
    [[2], [2], [2], [2], [2]]

However, we'd better still have $env and namespaces as mutable maps.

Alternative to Evaler forking

The Evaler contains mutable states, most notably the local scope, IO ports and the failure handler. Sometimes things needs to be evaluated with a different set of states, possibly asynchronously. The current solution is to fork the Evaler at such circumstances - copy the evaluator, change states as needed, and do the evaluation on the forked Evaler. To summarize, forking happens with

  1. Form evaluation. The IO ports are then changed.
  2. Channel output capture. The IO ports (only the output, to be exact) are then changed.
  3. Not when a closure is evaluated, since the closure invocation already got its forked evaluator when the form is being evaluated. The local scope is changed in place.
  4. When a closure is invoked from builtins (currently each and if), since in that case the forking with form evaluation does not happen. This is a consequence of 3.

Evaler forking, though working well for the moment, presents subtle problems (4 for example took a little time to figure out). It also introduces quite some copying, which may or may not be a problem later. Perhaps we can make the mutable states more explicit and somehow separate them from the (mostly) immutable parts of the Evaler.

Command line history API

It should be possible to access the command line history from elvish code.

The session history is simple and can be exposed as a $history array. The persistent history involves database reads and the API should be designed more carefully.

Integrated development environment

A shell is interactive. It would be better if one could not only run code, but also develop code from the shell.

The idea of such a system originates from Smalltalk and is recently re-popularized by LightTable. Some ideas could be stolen from them; however, coming up with a natural TUI for that could be a little challenging.

This issue only keeps track of the early designing phase. Once a prototypical implementation is pushed, this issue should be closed.

Related to #1.

Elvish crashes in Emacs terminal mode

linux/amd64/ubuntu12.04
version:
"commit 0c77f30
Author: Cheer Xiao [email protected]
Date: Tue Dec 9 14:40:04 2014 +0100

Pipes are no longer statically typed.

"

jaten@c03:$ go get -u -t -v github.com/elves/elvish
github.com/elves/elvish (download)
jaten@c03:~$ elvish
panic: runtime error: invalid memory address or nil pointer dereference
    panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x0 pc=0x4592ec]

goroutine 16 [running]:
runtime.panic(0x5302e0, 0x80d6b3)
    /home/jaten/pkg/go1.3.1/go/src/pkg/runtime/panic.c:279 +0xf5
github.com/elves/elvish/edit.(*writer).commitBuffer(0xc208000320, 0x0, 0x0, 0x0)
    /home/jaten/go/src/github.com/elves/elvish/edit/writer.go:216 +0x6c
github.com/elves/elvish/edit.(*writer).refresh(0xc208000320, 0xc20808a040, 0x0, 0x0, 0x0, 0x0, 0x0)
    /home/jaten/go/src/github.com/elves/elvish/edit/writer.go:538 +0x1cd4
github.com/elves/elvish/edit.(*Editor).refresh(0xc20808a000, 0x0, 0x0)
    /home/jaten/go/src/github.com/elves/elvish/edit/editor.go:121 +0x21b
github.com/elves/elvish/edit.(*Editor).finishReadLine(0xc20808a000, 0xc2080247e0)
...

Readme arithmetic examples don't work

On ArchLinux 64 using either go get or the ArchLinux package, if I type the readme example:

~> + 1 2

I get

<tty 1>:1:1: error: pipeline output not satisfiable

It happens in xterm or a virtual terminal. Another minor issue is (not sure if a bug) that if I try to backspace, I get:

Unbound: Ctrl-H

The line editor does work under Emacs shell mode

The Emacs shell mode pseudo-tty has really limited capability. For one thing, it does not implement the TIOCGWINSZ ioctl call properly, which is used is query the window size. Instead of the actual window size it always indicates a width of 0 and a height of 0.

The line editor of Elvish in its currently form is essentially a full-screen application comparable to Vim and thus relies heavily on the availability of the window dimension information and cursor movement control codes.

Some kind of line-oriented (instead of screen-oriented) "fallback mode" of the line editor is needed for it to function properly under Emacs shell mode.

Shared variables and functions

Persistent variables and persistent functions are shared across multiple sessions and preserved on shell exits. They are useful for passing data or code between concurrent sessions without (explicit) serialization/deserialization. In particular, they are ideal for configuration variables and functions and may serve as a more advanced clipboard facility.

The ideas are taken from fish, which has universal variable and autoload functions, with quite different implementations. All universal variables are stored into one file, and fish instances talk to an agent, fishd, to access them. In the case of autoload functions, however, each function is stored into a separate file and there is no agent to coordinate the access. Users are encouraged to manage autoload functions manually.

Open questions:

  1. Is unifying persistent variables and functions necessarily a good idea?
  2. How to statically check persistent variables and functions?
  3. Is an agent needed to coordinate access? If so, how should the protocol be? (Note: The elvished package already includes a prototype of the agent.)

rc file

Elvish should source a rc file on an interactive startup.

Panic when attempting to get value from table

While trying examples from the README, I found that the following line causes a panic with go1.3:

philip@indiana:~$ elvish
~> println [a b c &key value][key]                                                                                                                                                                          
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x30 pc=0x4609ed]

goroutine 92 [running]:
runtime.panic(0x530940, 0x80d6b3)
    /home/philip/projects/f/go/src/pkg/runtime/panic.c:279 +0xf5
github.com/xiaq/elvish/eval.print(0xc20803b320, 0xc20804f360, 0x2, 0x2, 0x0, 0x0)
    /home/philip/projects/go/src/github.com/xiaq/elvish/eval/builtin-func.go:72 +0x11d
github.com/xiaq/elvish/eval.println(0xc20803b320, 0xc20804f360, 0x2, 0x2, 0x0, 0x0)
    /home/philip/projects/go/src/github.com/xiaq/elvish/eval/builtin-func.go:79 +0x175
github.com/xiaq/elvish/eval.func·010()
    /home/philip/projects/go/src/github.com/xiaq/elvish/eval/exec.go:204 +0x5e
created by github.com/xiaq/elvish/eval.(*Evaluator).execBuiltinFunc
    /home/philip/projects/go/src/github.com/xiaq/elvish/eval/exec.go:209 +0xcf

goroutine 16 [chan receive]:
github.com/xiaq/elvish/eval.func·014(0xc20803a120, 0x0, 0x0, 0x0)
    /home/philip/projects/go/src/github.com/xiaq/elvish/eval/op.go:102 +0x5a2
github.com/xiaq/elvish/eval.func·012(0xc20803a120)
    /home/philip/projects/go/src/github.com/xiaq/elvish/eval/op.go:31 +0xb0
github.com/xiaq/elvish/eval.(*Evaluator).eval(0xc20803a120, 0xc208000318, 0x7, 0xc20804e8c0, 0x1f, 0xc2080cd9a0, 0x0, 0x0)
    /home/philip/projects/go/src/github.com/xiaq/elvish/eval/eval.go:145 +0xdd
github.com/xiaq/elvish/eval.(*Evaluator).Eval(0xc20803a120, 0xc208000318, 0x7, 0xc20804e8c0, 0x1f, 0xc20804e9c0, 0x0, 0x0)
    /home/philip/projects/go/src/github.com/xiaq/elvish/eval/eval.go:134 +0x10f
main.interact()
    /home/philip/projects/go/src/github.com/xiaq/elvish/main.go:70 +0x65e
main.main()
    /home/philip/projects/go/src/github.com/xiaq/elvish/main.go:118 +0x2c

goroutine 19 [finalizer wait]:
runtime.park(0x414c00, 0x81b538, 0x8101e9)
    /home/philip/projects/f/go/src/pkg/runtime/proc.c:1369 +0x89
runtime.parkunlock(0x81b538, 0x8101e9)
    /home/philip/projects/f/go/src/pkg/runtime/proc.c:1385 +0x3b
runfinq()
    /home/philip/projects/f/go/src/pkg/runtime/mgc0.c:2644 +0xcf
runtime.goexit()
    /home/philip/projects/f/go/src/pkg/runtime/proc.c:1445

goroutine 20 [syscall]:
os/signal.loop()
    /home/philip/projects/f/go/src/pkg/os/signal/signal_unix.go:21 +0x1e
created by os/signal.init·1
    /home/philip/projects/f/go/src/pkg/os/signal/signal_unix.go:27 +0x32

goroutine 17 [syscall]:
runtime.goexit()
    /home/philip/projects/f/go/src/pkg/runtime/proc.c:1445

goroutine 21 [syscall]:
syscall.Syscall(0x0, 0x3, 0x7fe5d6bf7ea1, 0x1, 0x1, 0x1, 0x417eb5)
    /home/philip/projects/f/go/src/pkg/syscall/asm_linux_amd64.s:21 +0x5
syscall.read(0x3, 0x7fe5d6bf7ea1, 0x1, 0x1, 0x100000002, 0x0, 0x0)
    /home/philip/projects/f/go/src/pkg/syscall/zsyscall_linux_amd64.go:838 +0x75
syscall.Read(0x3, 0x7fe5d6bf7ea1, 0x1, 0x1, 0xc208002120, 0x0, 0x0)
    /home/philip/projects/f/go/src/pkg/syscall/syscall_unix.go:136 +0x5c
os.(*File).read(0xc208038030, 0x7fe5d6bf7ea1, 0x1, 0x1, 0x4ef560, 0x0, 0x0)
    /home/philip/projects/f/go/src/pkg/os/file_unix.go:190 +0x62
os.(*File).Read(0xc208038030, 0x7fe5d6bf7ea1, 0x1, 0x1, 0x1, 0x0, 0x0)
    /home/philip/projects/f/go/src/pkg/os/file.go:95 +0x98
github.com/xiaq/elvish/util.(*AsyncReader).run(0xc208022720)
    /home/philip/projects/go/src/github.com/xiaq/elvish/util/async-reader.go:89 +0x4b7
created by github.com/xiaq/elvish/util.NewAsyncReader
    /home/philip/projects/go/src/github.com/xiaq/elvish/util/async-reader.go:44 +0x15e

goroutine 22 [chan receive]:
github.com/xiaq/elvish/edit.(*Reader).stop(0xc2080227b0, 0xc208004240)
    /home/philip/projects/go/src/github.com/xiaq/elvish/edit/reader.go:245 +0x51
github.com/xiaq/elvish/edit.(*Reader).run(0xc2080227b0)
    /home/philip/projects/go/src/github.com/xiaq/elvish/edit/reader.go:271 +0x1ea
created by github.com/xiaq/elvish/edit.NewReader
    /home/philip/projects/go/src/github.com/xiaq/elvish/edit/reader.go:75 +0xe6
philip@indiana:~$ go version
go version go1.3 linux/amd64

Probably should not be doing that.

Customizable keybinding

The keybinding is now fixed, but should really be customizable. New builtins needs to be introduced to modify the keybinding.

Persistent storage mechanism

A few things about the shell need to be stored persistently:

  • Command history
  • Directory history (#27)
  • Persistent variables and functions (#15)

Traditionally, command history is stored in a file, but this has caused a number of complexities (cf. zsh's history-related options).

It would be better if a devoted agent manages persistent storage and mediates its access, like how fishd manages universal variables for fish.

Boolean and the if command

Traditionally Unix shells have no booleans. Instead of taking a boolean, if takes a command (actually a pipeline) and executes the body depending on whether the command has succeeded or not.

Perhaps we could introduce booleans and see how it fares better.

Related to #13.

No luck on Mac?

I got following error when do go get github.com/xiaq/elvish

image

Bad behaviour when SIGINT

Hi. When writing commands, I use to type Ctrl+C to "reset" and start writing another command (not sure if this is a bad habit xD).

I know of Ctrl+W for deleting a whole word, but after writing a tar command with a filename containing a lot of spaces (example), Ctrl+C is faster than Ctrl+W.

Well, when I type Ctrl+C in elvish, this is what happens:

29

Order of events:

  1. Start elvish
  2. ^C
  3. Enter = "Editor error"... blah. After this error everything works fine...
  4. Enter. I could write a command and it would work.
  5. When I exit (Ctrl+D) and return to a bash shell, pressing Enter doesn't move the cursor to the new line; instead, the prompt is written inline.

I'm running Go v1.3 in a 64-bit Ubuntu 14.04.

Not building on Mac OS X

Cool project. I tried to build it but I'm getting errors:

$ go get github.com/xiaq/elvish
# github.com/xiaq/elvish/edit/tty
dev/go/src/github.com/xiaq/elvish/edit/tty/termios.go:20: undefined: syscall.TCGETS
dev/go/src/github.com/xiaq/elvish/edit/tty/termios.go:24: undefined: syscall.TCSETS
dev/go/src/github.com/xiaq/elvish/edit/tty/termios.go:49: cannot use &term.Lflag (type *uint64) as type *uint32 in argument to setFlag
dev/go/src/github.com/xiaq/elvish/edit/tty/termios.go:53: cannot use &term.Lflag (type *uint64) as type *uint32 in argument to setFlag
# github.com/xiaq/elvish/sys
dev/go/src/github.com/xiaq/elvish/sys/select.go:66: not enough arguments to return

Sigil as shorthand for function invocation

Sigils are characters that are only special at the beginning of a compound expression. In less accurate words, a sigil can be followed by a string literal, but cannot break a string literal apart.

In zsh for instance, = is a sigil. ls =sh expands to something like ls /bin/sh, but the = in ls a=b is taken as part of the bareword. However, $ is not a sigil, since it is special in ls $b as well as in ls a$b.

I'd like to have a general sigil mechanism in Elvish. Some rules for it are:

  1. A closed set of characters are eligible as sigils. The ones that have come to mind are =!%?*\.
  2. A sigil must be followed by a primary expression, otherwise it is a bareword, as in echo =.
  3. A sigil is just shorthand for a command of the same name. Therefore, ls =sh is exactly equivalent as ls (= sh), where = could be defined by the user.
  4. (Maybe) Some sigils have predefined implementations, like = for path expansion and ! for history expansion.
  5. (Maybe) Implement pretty advanced globbing as a sigil to avoid cluttering the syntax. Perhaps use \, which is akin to / used in regular expressions, as a sigil for that.

Persistent command line history

Command line history is a traditionally essential functionality of the shell UI. Elvish now has a per-session, in-memory command line history, but no persistent history yet.

Despite of the apparent simplicity, there is a particularly annoying issue about command line history - how multiple parallel shell sessions should interact with the persistent history and with each other. See http://zsh.sourceforge.net/Doc/Release/Options.html#History for zsh's struggles on this issue. Like always, the zsh way is to implements all (suboptimal) solutions to the problem and let the user choose...

Clojure already has pipelines, FYI

Clojure actually does have a way to avoid the reverse-order problem with Lisps that you lament in the README. Here is another way of writing your example Clojure code from the end of the README:

(require ['clojure.string :refer '(upper-case)])
(def strs '("aha" "LOLaha" "hahaLOL" "hum?"))
(def lols (->> strs
               (filter #(re-find #"LOL" %))
               (map upper-case)
               sort))

As you can see, the functions are written in the order they are applied, by using the ->> macro (similar to the -> "thread" macro). I don't know enough about Clojure to say whether use of that macro is idiomatic or not, but Clojure does support it. You should note it as an alternative in your README.

Since fixing the problems with Lisps' pipelining seems to be one of the goals of your shell, you should consider Clojure's approach as an option.

Tiny inconsistency in README.md

Under the heading "Pipeline, the Good":

First it is said that concatenative programming is (commonly) a form of functional programming:

So what's concatenative programming? In some of its most common use cases, we can say it's just functional programming without lots of irritating superfluous parentheses.

But later, concatenative programming is contrasted with functional programming, seemingly implying that concatenative programming is not functional programming:

is formed by concatenating simpler programs, hence the term "concatenative programming". Compare this to the functional approach, where constructs are nested instead of connected one after another.

Instead, something else could be written, in order to maintain consistency:

  1. "Compare this to the applicative functional approach, "
  2. "Compare this to the approach in languages like lisp, "
  3. "Compare this to the lisp approach, "

etc.

Alternative closure syntax

The current closure syntax {|arg| command} is modeled after ruby blocks. There are two things I don't like about it:

  • When the argument list is absent, a closure literal is distinguished from a list by a whitespace after the opening brace; e.g. {echo} is a list, but { echo} is a closure. This is too subtle. We can make the argument list (along with the bars) compulsory, but {|| echo} isn't too nice either.
  • It doesn't read nice. This is reflected by a need for syntax sugar in fn; fn f a b c { command } is equivalent to fn f {|a b c| command} since I find the latter unnatural. It would be better if we can separate the argument list from the body more clearly.

http://rigaux.org/language-study/syntax-across-languages.html#FnctnnnmsFnct collects the syntax for anonymous functions in other languages.

Record mode

Record mode is a simple way to save part of an interactive session into a script. Like MS office macros, or Vim's recording with q.

Hit ^R to start and stop recording:

~> ^R
RECORD 1 ~> var $a String
RECORD 2 ~> set $a = `lala`
RECORD 3 ~> echo $a
lala
RECORD 4 ~> ^R
save as (^C for discard): a.elv

(Some style could be applied to RECORD #)

undefined: os.Unsetenv

Hi,

On two new machines running debian jessie, I get

go get github.com/xiaq/elvish

github.com/elves/elvish/eval

go/bin/src/github.com/elves/elvish/eval/builtin_special.go:281: undefined: os.Unsetenv

Thanks

Raphael

Semantic highlighter

edit.Highlighter is now backed by parse.Lexer. It processes a flow of tokens, identify commands and variables, resolve their validness and highlight them accordingly. However,

  1. Commands and variables can be difficult to identify properly without syntax knowledge; the current Highlighter does a poor job on this. This requires the use of Parser.
  2. The validness of command or variable can not be determined reliably without semantics knowledge. For instance, identifying that the second x in fn x { }; x is actually valid is impossible without knowing about the fn form. This requires the use of Compiler.
  3. It is very desirable to also highlight syntax errors and static type errors.

The plan: Highlighter should be backed by parser.Parser and eval.Compiler. It parses the (incomplete) text and in case of syntax errors, highlight the affected areas. It then compiles the tree and highlights any static type errors. The plan consists of several parts:

  1. Parser needs to handle incomplete text better; it also needs to be able to continue parsing to some extent in face of errors (or a single syntax error will cause everything after it to not be parsed).
  2. Compiler also needs to continue compiling to some extent in face of errors.
  3. Static type errors cover invalid variable references, but invalid command references are a bit tricky. Currently Compiler simply mark all unresolved commands as externals, and does not attempt to resolve them into actual paths. Compiler needs to make it possible to enumerate such commands after compiling an AST.
  4. Rewrite the Highlighter. The structure of Highlighter may also need a change. Since parsing and compiling can be a bit time-consuming, running the text through Highlighter before displaying them on screen can cause lags. Highlighter may now output a series of highlighting instructions. Editor displays the text immediately (with proper ItemType highlighting), run Highlighter asynchronously and apply the instructions once they are ready.

Table manipulation primaries

While tables are first class values in elvish, it is not yet possible to manipulate them. To break it down, it should be possible to add, remove and modify elements of a table.

We can either go the conventional way of mutable data structures or adopt immutable data structures (#11.)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.