dune - A shell by the beach!

Overview

dune

A shell by the beach!

NOTE: Click the image above for a video demonstration.

About the Author

I'm a bored sophomore in college working on projects to fill the time. If you enjoy my work, consider supporting me by buying me a coffee!

Why write another shell?

I feel that bash is great in a lot of ways, but it doesn't exactly feel cozy: it's lacking a sort of personal touch, and it's also missing quick and easy customizability. With my last shell, Atom, I had accomplished some of the coziness that bash was missing, but I also introduced a lot of really fatal flaws in the syntax and the type system.

Dune, however, is designed completely differently from Atom (although you might notice the similarities in their widget systems). The interpreter itself is standalone, and it holds almost none of the functionality you see in the default distribution of Dune. If you wanted to, you could write a custom frontend and make a unique Dune based shell of your own!

This frontend implementation turns the coziness dial to 11. Just check out the shell's default startup script!

I put a lot of work into making Dune just fun to use. It's like a neat little operating system itself!

Dune also attempts to be a usable scripting language, and even offers a few niche metaprogramming features such as quoting (borrowed from Lisp), operator overloading, and macros!

Overall, I wrote Dune to have a complete shell of my own: one that's fast, useful, and pretty.

(Also, writing a shell is just kinda really fun)

Usage

Dune has a bunch of customizable components. Here's how you can change them and make your shell your own!

The Prelude

Before entering interactive mode, Dune executes the prelude. The prelude is just the startup file .dune-prelude stored in the home directory for your user. If you don't provide your own prelude file, Dune will execute its own default prelude with an introduction to the shell.

You can see my example personal prelude here.

The REPL

Dune's REPL is entirely customizable by overloading the following functions:

Name Purpose Default Implementation
prompt This function is called to generate the text which prompts the user for input. It takes the current working directory, and returns a string.
let prompt = cwd -> fmt@bold ((fmt@dark@blue "(dune) ") +
(fmt@bold (fmt@dark@green cwd)) +
(fmt@bold (fmt@dark@blue "$ ")))
incomplete_prompt This function is called to generate the text which prompts the user for input when they have entered an incomplete expression. It takes the current working directory, and returns a string.
let incomplete_prompt = cwd -> ((len cwd) +
(len "(dune) ")) * " " +
(fmt@bold (fmt@dark@yellow "> "));
report This function is called to print a value to the console after evaluation. The default implementation is a builtin function (implemented in Rust), but you can overload it with any callable value nonetheless.

I highly recommend using the fmt module when implementing your own customizations for your prompt!

Aliases

This distribution of Dune uses the Symbol type (the type of variable names and paths) to implement calling programs. Whenever an expression of type Symbol is evaluated as a command in interactive mode, it is invoked as a program.

Because of this, you can define aliases by assigning a variable to a program's name like so!

If you have defined a variable that overshadows your program's name (such as an alias), you can quote the program name to run it.

Overshadowed

Macros

To write functions that modify your shell's environment and act like commands or programs themselves, use a macro!

Macros

Macros, when called with zero arguments, are passed the current working directory. When invoked, they assume the environment of the callee: if you execute a macro, it will execute as if you executed the contents of the macro itself with the parameter defined as the argument passed.

Standard Library

Dune offers an extensive standard library, and also provides a pretty interface to see all the functions available in each module!

Dune offers the following builtin libraries:

Name Description
rand A library for randomness
time A library with date and time functions
math A module for math and trig functionality
fs A module for interacting with the file system
fn A functional programming library
fmt A library for text formatting on the console (color, styling, hyperlinks, text wrapping, etc.)
os A small module with the host's OS info
widget A module for creating text widgets
console A library for manipulating the console

For more information about each, just run echo library-name.

Installation

To install, you must download Rust from here.

Development Build

# Install directly from git with cargo
cargo install --git https://github.com/adam-mcdaniel/dune

# Or, alternatively, the repo and install from source
git clone https://github.com/adam-mcdaniel/dune
cd dune
cargo install -f --path .

Releases

To get the current release build, install from crates.io.

# Also works for updating dune
cargo install -f dune

After Install

# Just run the dune executable!
dune
Comments
  • Package name

    Package name

    Hello,

    Thanks for creating dune, I just tried it, and it's pretty fun.

    If this package hypothetically were to be packaged for a Linux distro, what should the package name be?

    dune is already taken by the build system for OCaml.

    Would you prefer adam-mcdaniel-dune or something like dune-shell? Have you considered renaming the project to avoid name collisions?

    Best regards, Alexander F. Rødseth

    question 
    opened by xyproto 6
  • builtin@exec seems to clash with exec subcommand

    builtin@exec seems to clash with exec subcommand

    When using docker compose, every time I use the exec command e.g.

    docker compose exec container_name
    

    I get the following error:

    unknown docker command: "compose builtin@exec"
    

    It seems to me like the exec subcommand clashes with the builtin@exec 🤔

    opened by sphinxc0re 5
  • Use tokenizer for parsing as well

    Use tokenizer for parsing as well

    This is a rather big change, aiming to simplify parsing. Here's a summary:

    • tokenizer.rs was refactored to use a struct containing a TokenKind instead of an enum. This makes it easier to match on the token's string content

    • the tokenizer now also parses comments. This improves syntax highlighting.

    • the tokenizer now also parses invalid characters. These are highlighted red.

    • the parse_script function now tokenizes the input and removes all whitespace and comment tokens, and then proceeds with parsing. This improves the parser for several reasons:

      • no need to account for whitespace in the parser
      • no need to check if keywords or number literals are valid in the parser
      • robustness, separation of concerns
      • consistency: the syntax highlighter is guaranteed to have the same behaviour as the parser
      • no code duplication
    • all parser functions in parser.rs now accept and return a new Tokens<'a> type, which is a wrapper for &'a [Token<'a>].

    • usages of the tag parser have been replaced with a new text parser. That parser checks if the text in the next token matches the provided string. It doesn't check if the TokenKind is correct, but that is usually not necessary (for example, a token with the text "for" is guaranteed to be a keyword). Note that this was quite error-prone before, because tag("for") also parses the text "forager"

    • to make sure that no invalid numbers are parsed (such as 1.05px, which is tokenized as FloatLiteral("1.05"), Symbol("px")), the parse_script function checks that "symbol-like" tokens can't appear directly after another without whitespace or punctuation in between.

      "symbol-like" tokens are number literals, bool literals, keywords, operators and symbols, because they can all contain the same characters.

    • because the tokenizer parses comments, the comment crate was removed from dependencies

    P.S. This also slightly changes how = is parsed: It is now nowhere required to put spaces around the = sign, so let foo={ bar=baz } works fine.

    opened by Aloso 5
  • Shell doesn't terminate on Ctrl-D or EOF

    Shell doesn't terminate on Ctrl-D or EOF

    I tried exiting the shell after testing it using Ctrl-D and this didn't work. When I do something like echo "1 + 1" | dune I would expect to see "2" but I see the welcome screen of dune and it just hangs even without a prompt. Ctrl-C also doesnt work as expected then

    opened by trusch 5
  • Cannot redirect output to a file with `>`

    Cannot redirect output to a file with `>`

    Hello,

    I'd like to redirect output to a file like:

    curl https://loon.lib.utk.edu/scout-pdfs/MS.2271.pdf > MS_2271.pdf
    

    Dune doesn't like this and ultimately I end up with something like this:

    curl https://loon.lib.utk.edu/scout-pdfs/MS.2271.pdf > MS_2271.pdf
                                > 
    syntax error:
     | on input `://loon.lib.utk.edu/scout-pdfs/MS.2271.pdf > MS_2271.pdf`
     | expected Eof
    
    enhancement 
    opened by markpbaggett 4
  • thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value

    thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value

    When starting Dune for the first time and typing ls, it works fine. However, after cd / I get this output when running ls:

    (dune) /$ ls
    thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Io(Os { code: 13, kind: PermissionDenied, message: "Permission denied" })', src/bin.rs:440:48
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
    

    There are no permission issues when running this under zsh, it calls ls and lists the files normally.

    bug 
    opened by xyproto 3
  • Implemented JSON and TOML parsing, added parse module

    Implemented JSON and TOML parsing, added parse module

    This adds a new module parse, and implements a function json which parses a JSON value into a Dune expression. This allows users to interact with web APIs and build more complex dashboards like this one I made while messing around:

    image

    enhancement 
    opened by adam-mcdaniel 3
  • Add benchmark

    Add benchmark

    Adds parser benchmark using criterion. Run it with cargo bench.

    On my computer, parsing the prelude takes about 450 ms. This is a lot, so I profiled with cargo-flamegraph. It seems that a lot of time is spent allocating strings.

    I managed to reduce the parse time by 66% just by removing string allocations in SyntaxError::from_error_kind. However, this is currently not possible without making error messages less helpful.

    opened by Aloso 3
  • History file location

    History file location

    The history file is created as ~/history.txt by default. It would be better, and more conventional, if it was stored as ie. ~/.dunesh_history, ~/.cache/dune/history.txt or $XDG_CACHE_DIR/dune/history.txt.

    opened by xyproto 2
  • Reduce dependencies

    Reduce dependencies

    Makes chess-engine optional. It also disables default features of several dependencies to cut down compilation time. cargo run now only compiles 84 crates instead of 100.

    It also adds Cargo.lock to git, which was previously ignored.

    opened by Aloso 2
  • Optimize parser

    Optimize parser

    This PR introduces a significant speedup from reducing string allocations (pointed out by @Aloso in #43) in the parser by limiting the number of tokens converted to a string at once.

    opened by adam-mcdaniel 2
  • Bump prettytable-rs from 0.8.0 to 0.10.0

    Bump prettytable-rs from 0.8.0 to 0.10.0

    Bumps prettytable-rs from 0.8.0 to 0.10.0.

    Release notes

    Sourced from prettytable-rs's releases.

    v0.10.0

    Fixed

    • Fix panic due to incorrect ANSI escape handling #137
    • Fix display of empty tables #127

    Changed

    • Remove the unsafe code in Table::as_ref #146
    • Switch atty to is-terminal #151
    • Minimal Supported Rust Version bumped to 1.56

    Thanks

    v0.9.0

    This release has been updated with latest dependencies versions.

    This crate has been abandonned without notice for quite a while due to some personnal reasons. My apologies for that. I'll try to do my best to continue to maintain it, at least for security updates. If I can't the find time to do it, I'll have no other option than deprecating it, or find new contributors to handover the maintenance to. Feel free to raise your hand if you're interrested. In the meantime, please expect a low update rate, and again please accept my apologies.

    I'll do a pass on opened PRs after summer vacations.

    Changelog

    Sourced from prettytable-rs's changelog.

    0.10.0 (2022-12-27)

    Fixed

    • Fix panic due to incorrect ANSI escape handling (#137)
    • Fix display of empty tables (#127)

    Changed

    • Remove the unsafe code in Table::as_ref (#146)
    • Switch atty to is-terminal (#151)
    • Minimal Supported Rust Version bumped to 1.56

    Thanks

    #127: phsym/prettytable-rs#127 #137: phsym/prettytable-rs#137 #145: phsym/prettytable-rs#145 #146: phsym/prettytable-rs#146 #151: phsym/prettytable-rs#151

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • how can i add dune as a default shell? (sorry, are questions allowed?)

    how can i add dune as a default shell? (sorry, are questions allowed?)

    hey, i've been meaning to do so for a while, but i haven't gotten around to it how can i add dune as a default shell? online guides say whatever shells are installed should have an entry in /etc/shells but dune's installed to my /home directory!! (i think) it's in /home/.cargo/bin and it works fine, would copying it as root to /bin and/or /usr/bin, then adding an entry for it to /etc/shells work?

    opened by akirapink 0
  • Create guide/examples for doing common things in dune

    Create guide/examples for doing common things in dune

    There are already a lot of features that are very well explained through the README. I feel like the project would profit from a step-by-step guide through some of the most common scenarios:

    • completion
    • aliases (e.g. dc for docker compose)
    • examples for integrating common developer tools like nvm, volta, rbenv, frum, zoxide, etc...

    I would also like to offer my input on these topics but feel unable to write it myself as I don't really know that much about the shell.

    EDIT: Maybe there could be a section detailing the differences between a posix-like shell and dune or even a table to compare implementations of different features in each shell.

    documentation enhancement 
    opened by sphinxc0re 0
  • Optimizations, correctly handle stdout

    Optimizations, correctly handle stdout

    Fixes #71. Closes #70.

    This PR adds a variable to the environment that tracks whether the currently evaluated expression's output should be printed directly or stored in a string. This is done to allow things like let x = which vim or "[" + (hostname ()) + "]".

    I also refactored some parts to clone objects less often, allocate fewer Boxes and Vecs and use .to_string() instead of format!("{}", ..) when possible.

    I replaced occurrences of impl ToString with impl Into<String> or just String, because ToString uses the Display implementation which might be more expensive.

    opened by Aloso 0
  • Error handling

    Error handling

    When I'm not connected to the internet, curl fails to get weather data which causes parse@json to fail, and also terminates the execution of my prelude early. I think that we should try to make a convenient way to handle errors (although the parsing functions should be implemented such that they return None instead of throwing errors).

    I'm thinking of implementing a try builtin function that might behave like the following:

    try 2 // 0 { echo "divide by zero"; 0 }
    

    try would attempt to evaluate the first argument If it does not throw an error, then try will return that result. If the first argument fails to evaluate, it would evaluate the second argument, and return its result.

    enhancement 
    opened by adam-mcdaniel 1
Releases(v0.1.8)
Owner
adam mcdaniel
Programmer and Musician
adam mcdaniel
A new type of shell

A new type of shell

Nushell Project 22.5k Jan 8, 2023
🐢 Atuin replaces your existing shell history with a SQLite database, and records additional context for your commands

Atuin replaces your existing shell history with a SQLite database, and records additional context for your commands. Additionally, it provides optional and fully encrypted synchronisation of your history between machines, via an Atuin server.

Ellie Huxtable 4.6k Jan 1, 2023
a cute shell thingy that written in rust

a cute shell thingy that written in rust

奥田 龍馬 12 Dec 29, 2021
A shell for research papers

Reason: A Shell for Research Papers Did I ever read this paper? Which OSDI 2021 papers did I read? Which ones have the word 'Distributed' in their tit

Jae-Won Chung 121 Nov 27, 2022
Explore from the safety of your shell

turtlescan Explore from the safety of your shell Installation: cargo install turtlescan tui Starts a little tui which connects to your JSON-RPC server

Brian Cloutier 4 Sep 13, 2022
A command-line shell like fish, but POSIX compatible.

A command-line shell like fish, but POSIX compatible.

Seiya Nuta 813 Dec 29, 2022
Zash - A Zuper Awesome Shell

Zash - A Zuper Awesome Shell Welcome to zash, its activily being developed and is not near a stable release. Installation Arch: yay -S zash Paru seem

Robiot 27 May 22, 2022
A very opinionated, zero-configuration shell prompt

A very opinionated, zero-configuration shell prompt

amy null 8 Nov 4, 2021
An interactive shell environment for exploring the p2panda protocol

An interactive shell environment for exploring the p2panda protocol. Uses a mock node and clients to simulate network logic.

null 4 Dec 12, 2021
Self-contained template system with Handlebars and inline shell scripts

Handlematters Self-contained template system with Handlebars and inline shell scripts Introduction Handlematters is a template system that combines Ha

Keita Urashima 3 Sep 9, 2022
A shell Made in rust 🦀

vsh A Blazingly fast shell made in Rust ?? Why make another shell? Because the current leading rust shell is very opinionated, atleast to me. As it br

null 89 Dec 18, 2022
dye is a tool to easily color text in shell.

Dye dye is a tool to easily color text in shell. Usage See the gif below to see these commands in action. echo $(dye --red WARN) This tool will knock

Kurt Wolf 15 Nov 1, 2022
A Unix shell written and implemented in rust 🦀

vsh A Blazingly fast shell made in Rust ?? Installation Copy and paste the following command and choose the appropriate installtion method for you. Yo

XMantle 89 Dec 18, 2022
Shellcheck - a static analysis tool for shell scripts

ShellCheck - A shell script static analysis tool ShellCheck is a GPLv3 tool that gives warnings and suggestions for bash/sh shell scripts: The goals o

Vidar Holen 31.1k Jan 9, 2023
Application microframework with command-line option parsing, configuration, error handling, logging, and shell interactions

Abscissa is a microframework for building Rust applications (either CLI tools or network/web services), aiming to provide a large number of features w

iqlusion 524 Dec 26, 2022
☄🌌️ The minimal, blazing-fast, and infinitely customizable prompt for any shell

☄??️ The minimal, blazing-fast, and infinitely customizable prompt for any shell

Starship Command 31.6k Dec 30, 2022
A command line interface meant to bridge the gap between Rust and shell scripting

clawbang A command line interface meant to bridge the gap between Rust and shell scripting. Intended for use with HEREDOCs and shebangs: $ clawbang <<

Chris Dickinson 52 Mar 25, 2022
A super simple prompt for Fish shell, just shows git info and Vi mode.

vifi is a portmandeau of 'Vi' and 'Fish', because it's a prompt for Fish shell, primarily focused around showing proper indicators when using Vi key bindings.

Mat Jones 1 Sep 15, 2022
A Rust-based shell script to create a folder structure to use for a single class every semester. Mostly an excuse to use Rust.

A Rust Course Folder Shell Script PROJECT IN PROGRESS (Spring 2022) When completed, script will create a folder structure of the following schema: [ro

Sebastián Romero Cruz 1 Apr 10, 2022