Repository: nushell/engine-q
Pull request #362 (2021-11-23 21:09:35)

Hiding of environment variables
This changes environment variables to be arranged in a stack-like structure so that the values can be recovered from the parent scopes. This allows us to implement deactivating overlays.

To detect at eval time whether a parser hid a definition under the same name or not, I kind of hacked the import pattern but I couldn't think of a better way that wouldn't be overly complicated.


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 16942f9..ed8cfd3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -93,9 +93,9 @@ dependencies = [
 
 [[package]]
 name = "anyhow"
-version = "1.0.47"
+version = "1.0.48"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38d9ff5d688f1c13395289f67db01d4826b46dd694e7580accdc3e8430f2d98e"
+checksum = "62e1f47f7dc0422027a4e370dd4548d4d66b26782e513e98dca1e689e058a80e"
 
 [[package]]
 name = "arrayref"
@@ -456,9 +456,9 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
 
 [[package]]
 name = "crc32fast"
-version = "1.2.1"
+version = "1.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
+checksum = "3825b1e8580894917dc4468cb634a1b4e9745fddc854edad72d9c04644c0319f"
 dependencies = [
  "cfg-if",
 ]
@@ -786,9 +786,9 @@ dependencies = [
 
 [[package]]
 name = "futures"
-version = "0.3.17"
+version = "0.3.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a12aa0eb539080d55c3f2d45a67c3b58b6b0773c1a3ca2dfec66d58c97fd66ca"
+checksum = "8cd0210d8c325c245ff06fd95a3b13689a1a276ac8cfa8e8720cb840bfb84b9e"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -801,9 +801,9 @@ dependencies = [
 
 [[package]]
 name = "futures-channel"
-version = "0.3.17"
+version = "0.3.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888"
+checksum = "7fc8cd39e3dbf865f7340dce6a2d401d24fd37c6fe6c4f0ee0de8bfca2252d27"
 dependencies = [
  "futures-core",
  "futures-sink",
@@ -811,15 +811,15 @@ dependencies = [
 
 [[package]]
 name = "futures-core"
-version = "0.3.17"
+version = "0.3.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d"
+checksum = "629316e42fe7c2a0b9a65b47d159ceaa5453ab14e8f0a3c5eedbb8cd55b4a445"
 
 [[package]]
 name = "futures-executor"
-version = "0.3.17"
+version = "0.3.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45025be030969d763025784f7f355043dc6bc74093e4ecc5000ca4dc50d8745c"
+checksum = "7b808bf53348a36cab739d7e04755909b9fcaaa69b7d7e588b37b6ec62704c97"
 dependencies = [
  "futures-core",
  "futures-task",
@@ -828,18 +828,16 @@ dependencies = [
 
 [[package]]
 name = "futures-io"
-version = "0.3.17"
+version = "0.3.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377"
+checksum = "e481354db6b5c353246ccf6a728b0c5511d752c08da7260546fc0933869daa11"
 
 [[package]]
 name = "futures-macro"
-version = "0.3.17"
+version = "0.3.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb"
+checksum = "a89f17b21645bc4ed773c69af9c9a0effd4a3f1a3876eadd453469f8854e7fdd"
 dependencies = [
- "autocfg",
- "proc-macro-hack",
  "proc-macro2",
  "quote",
  "syn",
@@ -847,23 +845,22 @@ dependencies = [
 
 [[package]]
 name = "futures-sink"
-version = "0.3.17"
+version = "0.3.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11"
+checksum = "996c6442437b62d21a32cd9906f9c41e7dc1e19a9579843fad948696769305af"
 
 [[package]]
 name = "futures-task"
-version = "0.3.17"
+version = "0.3.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99"
+checksum = "dabf1872aaab32c886832f2276d2f5399887e2bd613698a02359e4ea83f8de12"
 
 [[package]]
 name = "futures-util"
-version = "0.3.17"
+version = "0.3.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481"
+checksum = "41d22213122356472061ac0f1ab2cee28d2bac8491410fd68c2af53d1cedb83e"
 dependencies = [
- "autocfg",
  "futures-channel",
  "futures-core",
  "futures-io",
@@ -873,8 +870,6 @@ dependencies = [
  "memchr",
  "pin-project-lite",
  "pin-utils",
- "proc-macro-hack",
- "proc-macro-nested",
  "slab",
 ]
 
@@ -997,6 +992,15 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "ical"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a9f7215ad0d77e69644570dee000c7678a47ba7441062c1b5f918adde0d73cf"
+dependencies = [
+ "thiserror",
+]
+
 [[package]]
 name = "im"
 version = "15.0.0"
@@ -1319,11 +1323,11 @@ dependencies = [
 
 [[package]]
 name = "miow"
-version = "0.3.8"
+version = "0.3.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c9b9524d5b9d60d55bd3f6ca13180cfc06b5d7e54df308c8842d3f66c914cc4"
+checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
 dependencies = [
- "windows-sys",
+ "winapi",
 ]
 
 [[package]]
@@ -1430,16 +1434,18 @@ dependencies = [
  "chrono",
  "chrono-humanize",
  "chrono-tz",
+ "crossterm",
  "csv",
  "dialoguer",
  "eml-parser",
  "glob",
+ "ical",
  "indexmap",
  "itertools",
+ "lazy_static",
  "lscolors",
  "meval",
  "nu-ansi-term 0.39.0",
- "nu-dataframe",
  "nu-engine",
  "nu-json",
  "nu-parser",
@@ -1447,13 +1453,17 @@ dependencies = [
  "nu-protocol",
  "nu-table",
  "nu-term-grid",
+ "num",
  "polars",
  "rand",
  "rayon",
  "regex",
+ "roxmltree",
  "serde",
+ "serde_ini",
  "serde_urlencoded",
  "serde_yaml",
+ "strip-ansi-escapes",
  "sysinfo",
  "terminal_size",
  "thiserror",
@@ -1463,19 +1473,6 @@ dependencies = [
  "unicode-segmentation",
 ]
 
-[[package]]
-name = "nu-dataframe"
-version = "0.1.0"
-dependencies = [
- "chrono",
- "indexmap",
- "nu-json",
- "nu-protocol",
- "num",
- "polars",
- "serde",
-]
-
 [[package]]
 name = "nu-engine"
 version = "0.1.0"
@@ -1537,6 +1534,7 @@ dependencies = [
  "chrono",
  "chrono-humanize",
  "im",
+ "indexmap",
  "miette",
  "nu-json",
  "serde",
@@ -1929,9 +1927,9 @@ checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
 
 [[package]]
 name = "predicates"
-version = "2.0.3"
+version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c6ce811d0b2e103743eec01db1c50612221f173084ce2f7941053e94b6bb474"
+checksum = "95e5a7689e456ab905c22c2b48225bb921aba7c8dfa58440d68ba13f6222a715"
 dependencies = [
  "difflib",
  "itertools",
@@ -1980,18 +1978,6 @@ dependencies = [
  "unicode-width",
 ]
 
-[[package]]
-name = "proc-macro-hack"
-version = "0.5.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
-
-[[package]]
-name = "proc-macro-nested"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086"
-
 [[package]]
 name = "proc-macro2"
 version = "1.0.32"
@@ -2139,7 +2125,7 @@ dependencies = [
 [[package]]
 name = "reedline"
 version = "0.2.0"
-source = "git+https://github.com/nushell/reedline?branch=main#c11aef2d9b4eaf0c762f1349f641534597815295"
+source = "git+https://github.com/nushell/reedline?branch=main#75d9b4b52d0ea60334ef96d49cd00002acc5720c"
 dependencies = [
  "chrono",
  "crossterm",
@@ -2181,6 +2167,21 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "result"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "194d8e591e405d1eecf28819740abed6d719d1a2db87fc0bcdedee9a26d55560"
+
+[[package]]
+name = "roxmltree"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b"
+dependencies = [
+ "xmlparser",
+]
+
 [[package]]
 name = "rust-argon2"
 version = "0.8.3"
@@ -2249,6 +2250,17 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "serde_ini"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb236687e2bb073a7521c021949be944641e671b8505a94069ca37b656c81139"
+dependencies = [
+ "result",
+ "serde",
+ "void",
+]
+
 [[package]]
 name = "serde_json"
 version = "1.0.71"
@@ -2689,6 +2701,12 @@ version = "0.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
 
+[[package]]
+name = "void"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
+
 [[package]]
 name = "vte"
 version = "0.10.1"
@@ -2754,47 +2772,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
-name = "windows-sys"
-version = "0.28.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82ca39602d5cbfa692c4b67e3bcbb2751477355141c1ed434c94da4186836ff6"
-dependencies = [
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_msvc",
-]
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.28.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52695a41e536859d5308cc613b4a022261a274390b25bd29dfff4bf08505f3c2"
-
-[[package]]
-name = "windows_i686_gnu"
-version = "0.28.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f54725ac23affef038fecb177de6c9bf065787c2f432f79e3c373da92f3e1d8a"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.28.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51d5158a43cc43623c0729d1ad6647e62fa384a3d135fd15108d37c683461f64"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.28.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc31f409f565611535130cfe7ee8e6655d3fa99c1c61013981e491921b5ce954"
-
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.28.0"
+name = "xmlparser"
+version = "0.13.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f2b8c7cbd3bfdddd9ab98769f9746a7fad1bca236554cd032b78d768bc0e89f"
+checksum = "114ba2b24d2167ef6d67d7d04c8cc86522b87f490025f39f0303b7db5bf5e3d8"
 
 [[package]]
 name = "yaml-rust"
diff --git a/Cargo.toml b/Cargo.toml
index 12eb7e1..6861e2a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,7 +13,6 @@ members = [
 	"crates/nu-command",
 	"crates/nu-protocol",
 	"crates/nu-plugin",
-	"crates/nu-dataframe",
 	"crates/nu_plugin_inc",
 ]
 
@@ -38,9 +37,8 @@ ctrlc = "3.2.1"
 
 [features]
 plugin = ["nu-plugin", "nu-parser/plugin", "nu-command/plugin", "nu-protocol/plugin"]
-custom = ["nu-command/custom", "nu-protocol/custom"]
-dataframe = ["custom", "nu-command/dataframe"]
-default = ["plugin", "custom"]
+dataframe = ["nu-command/dataframe"]
+default = ["plugin"]
 
 [dev-dependencies]
 tempfile = "3.2.0"
diff --git a/TODO.md b/TODO.md
index f8b7e23..e0480bf 100644
--- a/TODO.md
+++ b/TODO.md
@@ -49,6 +49,7 @@
 - [ ] finish adding config properties
 - [ ] system-agnostic test cases
 - [ ] exit codes
+- [ ] auto-cd
 - [ ] length of time the command runs put in the env (CMD_DURATION_MS)
 
 ## Post-nushell merge:
@@ -68,4 +69,4 @@ module git {
     ] ;
 }
 
-plugin git { ... }
\ No newline at end of file
+plugin git { ... }
diff --git a/crates/nu-command/Cargo.toml b/crates/nu-command/Cargo.toml
index 73708e2..ca96af7 100644
--- a/crates/nu-command/Cargo.toml
+++ b/crates/nu-command/Cargo.toml
@@ -13,10 +13,7 @@ nu-protocol = { path = "../nu-protocol" }
 nu-table = { path = "../nu-table" }
 nu-term-grid = { path = "../nu-term-grid" }
 nu-parser = { path = "../nu-parser" }
-nu-dataframe = { path = "../nu-dataframe", optional = true }
 nu-ansi-term = { path = "../nu-ansi-term" }
-trash = { version = "1.3.0", optional = true }
-unicode-segmentation = "1.8.0"
 
 # Potential dependencies for extras
 csv = "1.1.3"
@@ -39,19 +36,28 @@ meval = "0.2.0"
 serde = { version="1.0.123", features=["derive"] }
 serde_yaml = "0.8.16"
 serde_urlencoded = "0.7.0"
+serde_ini = "0.2.0"
 eml-parser = "0.1.0"
 toml = "0.5.8"
 itertools = "0.10.0"
+ical = "0.7.0"
 calamine = "0.18.0"
+roxmltree = "0.14.0"
 rand = "0.8"
+trash = { version = "1.3.0", optional = true }
+unicode-segmentation = "1.8.0"
+lazy_static = "1.4.0"
+strip-ansi-escapes = "0.1.1"
+crossterm = "0.22.1"
+
+num = {version="0.4.0", optional=true}
 
 [dependencies.polars]
 version = "0.17.0"
 optional = true
-features = ["default", "parquet", "json"]
+features = ["default", "parquet", "json", "serde", "object", "checked_arithmetic", "strings"]
 
 [features]
 trash-support = ["trash"]
 plugin = ["nu-parser/plugin"]
-custom = ["nu-protocol/custom"]
-dataframe = ["custom", "nu-dataframe", "polars"]
+dataframe = ["polars", "num"]
diff --git a/crates/nu-command/src/core_commands/debug.rs b/crates/nu-command/src/core_commands/debug.rs
index 78a52e6..72023ea 100644
--- a/crates/nu-command/src/core_commands/debug.rs
+++ b/crates/nu-command/src/core_commands/debug.rs
@@ -15,7 +15,7 @@ impl Command for Debug {
     }
 
     fn signature(&self) -> Signature {
-        Signature::build("describe").category(Category::Core)
+        Signature::build("debug").category(Category::Core)
     }
 
     fn run(
diff --git a/crates/nu-command/src/core_commands/help.rs b/crates/nu-command/src/core_commands/help.rs
index 3f86eb8..c03dfa2 100644
--- a/crates/nu-command/src/core_commands/help.rs
+++ b/crates/nu-command/src/core_commands/help.rs
@@ -187,7 +187,6 @@ fn help(
                 .into_pipeline_data(engine_state.ctrlc.clone()))
         } else {
             let mut name = String::new();
-            let mut output = String::new();
 
             for r in &rest {
                 if !name.is_empty() {
@@ -196,16 +195,15 @@ fn help(
                 name.push_str(&r.item);
             }
 
-            for cmd in full_commands {
-                if cmd.0.name == name {
-                    let help = get_full_help(&cmd.0, &cmd.1, engine_state);
-                    output.push_str(&help);
-                }
-            }
+            let output = full_commands
+                .iter()
+                .filter(|(signature, _, _)| signature.name == name)
+                .map(|(signature, examples, _)| get_full_help(signature, examples, engine_state))
+                .collect::<Vec<String>>();
 
             if !output.is_empty() {
                 Ok(Value::String {
-                    val: output,
+                    val: output.join("======================\n\n"),
                     span: call.head,
                 }
                 .into_pipeline_data())
diff --git a/crates/nu-command/src/core_commands/hide.rs b/crates/nu-command/src/core_commands/hide.rs
index 700280e..818649f 100644
--- a/crates/nu-command/src/core_commands/hide.rs
+++ b/crates/nu-command/src/core_commands/hide.rs
@@ -1,6 +1,6 @@
-use nu_protocol::ast::Call;
+use nu_protocol::ast::{Call, Expr, Expression, ImportPatternMember};
 use nu_protocol::engine::{Command, EngineState, Stack};
-use nu_protocol::{Category, PipelineData, Signature, SyntaxShape};
+use nu_protocol::{Category, PipelineData, ShellError, Signature, SyntaxShape};
 
 #[derive(Clone)]
 pub struct Hide;
@@ -20,13 +20,94 @@ impl Command for Hide {
         "Hide definitions in the current scope"
     }
 
+    fn extra_usage(&self) -> &str {
+        "If there is a definition and an environment variable with the same name in the current scope, first the definition will be hidden, then the environment variable."
+    }
+
     fn run(
         &self,
-        _engine_state: &EngineState,
-        _stack: &mut Stack,
+        engine_state: &EngineState,
+        stack: &mut Stack,
         call: &Call,
         _input: PipelineData,
     ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
+        let import_pattern = if let Some(Expression {
+            expr: Expr::ImportPattern(pat),
+            ..
+        }) = call.positional.get(0)
+        {
+            pat
+        } else {
+            return Err(ShellError::InternalError(
+                "Got something else than import pattern".into(),
+            ));
+        };
+
+        let head_name_str = if let Ok(s) = String::from_utf8(import_pattern.head.name.clone()) {
+            s
+        } else {
+            return Err(ShellError::NonUtf8(import_pattern.head.span));
+        };
+
+        if let Some(overlay_id) = engine_state.find_overlay(&import_pattern.head.name) {
+            // The first word is a module
+            let overlay = engine_state.get_overlay(overlay_id);
+
+            let env_vars_to_hide = if import_pattern.members.is_empty() {
+                overlay.env_vars_with_head(&import_pattern.head.name)
+            } else {
+                match &import_pattern.members[0] {
+                    ImportPatternMember::Glob { .. } => {
+                        overlay.env_vars_with_head(&import_pattern.head.name)
+                    }
+                    ImportPatternMember::Name { name, span } => {
+                        let mut output = vec![];
+
+                        if let Some((name, id)) =
+                            overlay.env_var_with_head(name, &import_pattern.head.name)
+                        {
+                            output.push((name, id));
+                        } else if !overlay.has_decl(name) {
+                            return Err(ShellError::EnvVarNotFoundAtRuntime(*span));
+                        }
+
+                        output
+                    }
+                    ImportPatternMember::List { names } => {
+                        let mut output = vec![];
+
+                        for (name, span) in names {
+                            if let Some((name, id)) =
+                                overlay.env_var_with_head(name, &import_pattern.head.name)
+                            {
+                                output.push((name, id));
+                            } else if !overlay.has_decl(name) {
+                                return Err(ShellError::EnvVarNotFoundAtRuntime(*span));
+                            }
+                        }
+
+                        output
+                    }
+                }
+            };
+
+            for (name, _) in env_vars_to_hide {
+                let name = if let Ok(s) = String::from_utf8(name.clone()) {
+                    s
+                } else {
+                    return Err(ShellError::NonUtf8(import_pattern.span()));
+                };
+
+                if stack.remove_env_var(&name).is_none() {
+                    return Err(ShellError::NotFound(call.positional[0].span));
+                }
+            }
+        } else if !import_pattern.hidden.contains(&import_pattern.head.name)
+            && stack.remove_env_var(&head_name_str).is_none()
+        {
+            return Err(ShellError::NotFound(call.positional[0].span));
+        }
+
         Ok(PipelineData::new(call.head))
     }
 }
diff --git a/crates/nu-command/src/core_commands/if_.rs b/crates/nu-command/src/core_commands/if_.rs
index 9de1bb1..df91d75 100644
--- a/crates/nu-command/src/core_commands/if_.rs
+++ b/crates/nu-command/src/core_commands/if_.rs
@@ -2,7 +2,7 @@ use nu_engine::{eval_block, eval_expression};
 use nu_protocol::ast::Call;
 use nu_protocol::engine::{Command, EngineState, Stack};
 use nu_protocol::{
-    Category, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
+    Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
 };
 
 #[derive(Clone)]
@@ -19,12 +19,16 @@ impl Command for If {
 
     fn signature(&self) -> nu_protocol::Signature {
         Signature::build("if")
-            .required("cond", SyntaxShape::Expression, "condition")
-            .required("then_block", SyntaxShape::Block(Some(vec![])), "then block")
+            .required("cond", SyntaxShape::Expression, "condition to check")
+            .required(
+                "then_block",
+                SyntaxShape::Block(Some(vec![])),
+                "block to run if check succeeds",
+            )
             .optional(
-                "else",
+                "else_expression",
                 SyntaxShape::Keyword(b"else".to_vec(), Box::new(SyntaxShape::Expression)),
-                "optional else followed by else block",
+                "expression or block to run if check fails",
             )
             .category(Category::Core)
     }
@@ -74,4 +78,36 @@ impl Command for If {
             )),
         }
     }
+
+    fn examples(&self) -> Vec<Example> {
+        vec![
+            Example {
+                description: "Output a value if a condition matches, otherwise return nothing",
+                example: "if 2 < 3 { 'yes!' }",
+                result: Some(Value::test_string("yes!")),
+            },
+            Example {
+                description: "Output a value if a condition matches, else return another value",
+                example: "if 5 < 3 { 'yes!' } else { 'no!' }",
+                result: Some(Value::test_string("no!")),
+            },
+            Example {
+                description: "Chain multiple if's together",
+                example: "if 5 < 3 { 'yes!' } else if 4 < 5 { 'no!' } else { 'okay!' }",
+                result: Some(Value::test_string("no!")),
+            },
+        ]
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn test_examples() {
+        use crate::test_examples;
+
+        test_examples(If {})
+    }
 }
diff --git a/crates/nu-command/src/core_commands/let_.rs b/crates/nu-command/src/core_commands/let_.rs
index 5742f1a..10bfacd 100644
--- a/crates/nu-command/src/core_commands/let_.rs
+++ b/crates/nu-command/src/core_commands/let_.rs
@@ -1,7 +1,7 @@
 use nu_engine::eval_expression;
 use nu_protocol::ast::Call;
 use nu_protocol::engine::{Command, EngineState, Stack};
-use nu_protocol::{Category, PipelineData, Signature, SyntaxShape};
+use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape};
 
 #[derive(Clone)]
 pub struct Let;
@@ -48,4 +48,31 @@ impl Command for Let {
         stack.add_var(var_id, rhs);
         Ok(PipelineData::new(call.head))
     }
+
+    fn examples(&self) -> Vec<Example> {
+        vec![
+            Example {
+                description: "Set a variable to a value",
+                example: "let x = 10",
+                result: None,
+            },
+            Example {
+                description: "Set a variable to the result of an expression",
+                example: "let x = 10 + 100",
+                result: None,
+            },
+        ]
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn test_examples() {
+        use crate::test_examples;
+
+        test_examples(Let {})
+    }
 }
diff --git a/crates/nu-dataframe/README.md b/crates/nu-command/src/dataframe/README.md
similarity index 100%
rename from crates/nu-dataframe/README.md
rename to crates/nu-command/src/dataframe/README.md
diff --git a/crates/nu-command/src/dataframe/mod.rs b/crates/nu-command/src/dataframe/mod.rs
index b9cd1bb..f77f2eb 100644
--- a/crates/nu-command/src/dataframe/mod.rs
+++ b/crates/nu-command/src/dataframe/mod.rs
@@ -1,5 +1,3 @@
-mod open;
-mod to_df;
+mod nu_dataframe;
 
-pub use open::OpenDataFrame;
-pub use to_df::ToDataFrame;
+pub use nu_dataframe::commands::{DataTypes, DescribeDF, OpenDataFrame, ToDataFrame};
diff --git a/crates/nu-dataframe/src/between_values.rs b/crates/nu-command/src/dataframe/nu_dataframe/between_values.rs
similarity index 94%
rename from crates/nu-dataframe/src/between_values.rs
rename to crates/nu-command/src/dataframe/nu_dataframe/between_values.rs
index 25e7135..528777b 100644
--- a/crates/nu-dataframe/src/between_values.rs
+++ b/crates/nu-command/src/dataframe/nu_dataframe/between_values.rs
@@ -1,6 +1,6 @@
 use super::{operations::Axis, NuDataFrame};
 
-use nu_protocol::{ast::Operator, ShellError, Span, Spanned, Value};
+use nu_protocol::{ast::Operator, span, ShellError, Span, Spanned, Value};
 use num::Zero;
 use polars::prelude::{
     BooleanType, ChunkCompare, ChunkedArray, DataType, Float64Type, Int64Type, IntoSeries,
@@ -10,15 +10,15 @@ use std::ops::{Add, BitAnd, BitOr, Div, Mul, Sub};
 
 pub fn between_dataframes(
     operator: Spanned<Operator>,
-    left: Value,
+    left: &Value,
     lhs: &NuDataFrame,
     right: &Value,
     rhs: &NuDataFrame,
-    operation_span: Span,
 ) -> Result<Value, ShellError> {
+    let operation_span = span(&[left.span()?, right.span()?]);
     match operator.item {
         Operator::Plus => match lhs.append_df(rhs, Axis::Row, operation_span) {
-            Ok(df) => Ok(df.to_value(operation_span)),
+            Ok(df) => Ok(df.into_value(operation_span)),
             Err(e) => Err(e),
         },
         _ => Err(ShellError::OperatorMismatch {
@@ -33,12 +33,12 @@ pub fn between_dataframes(
 
 pub fn compute_between_series(
     operator: Spanned<Operator>,
-    left: Value,
+    left: &Value,
     lhs: &Series,
     right: &Value,
     rhs: &Series,
-    operation_span: Span,
 ) -> Result<Value, ShellError> {
+    let operation_span = span(&[left.span()?, right.span()?]);
     match operator.item {
         Operator::Plus => {
             let mut res = lhs + rhs;
@@ -167,9 +167,8 @@ pub fn compute_between_series(
 
 pub fn compute_series_single_value(
     operator: Spanned<Operator>,
+    left: &Value,
     lhs: &NuDataFrame,
-    lhs_span: &Span,
-    left: Value,
     right: &Value,
 ) -> Result<Value, ShellError> {
     if !lhs.is_series() {
@@ -182,15 +181,16 @@ pub fn compute_series_single_value(
         });
     }
 
-    let lhs = lhs.as_series(*lhs_span)?;
+    let lhs_span = left.span()?;
+    let lhs = lhs.as_series(lhs_span)?;
 
     match operator.item {
         Operator::Plus => match &right {
             Value::Int { val, .. } => {
-                compute_series_i64(&lhs, *val, <ChunkedArray<Int64Type>>::add, *lhs_span)
+                compute_series_i64(&lhs, *val, <ChunkedArray<Int64Type>>::add, lhs_span)
             }
             Value::Float { val, .. } => {
-                compute_series_decimal(&lhs, *val, <ChunkedArray<Float64Type>>::add, *lhs_span)
+                compute_series_decimal(&lhs, *val, <ChunkedArray<Float64Type>>::add, lhs_span)
             }
             _ => Err(ShellError::OperatorMismatch {
                 op_span: operator.span,
@@ -202,10 +202,10 @@ pub fn compute_series_single_value(
         },
         Operator::Minus => match &right {
             Value::Int { val, .. } => {
-                compute_series_i64(&lhs, *val, <ChunkedArray<Int64Type>>::sub, *lhs_span)
+                compute_series_i64(&lhs, *val, <ChunkedArray<Int64Type>>::sub, lhs_span)
             }
             Value::Float { val, .. } => {
-                compute_series_decimal(&lhs, *val, <ChunkedArray<Float64Type>>::sub, *lhs_span)
+                compute_series_decimal(&lhs, *val, <ChunkedArray<Float64Type>>::sub, lhs_span)
             }
             _ => Err(ShellError::OperatorMismatch {
                 op_span: operator.span,
@@ -217,10 +217,10 @@ pub fn compute_series_single_value(
         },
         Operator::Multiply => match &right {
             Value::Int { val, .. } => {
-                compute_series_i64(&lhs, *val, <ChunkedArray<Int64Type>>::mul, *lhs_span)
+                compute_series_i64(&lhs, *val, <ChunkedArray<Int64Type>>::mul, lhs_span)
             }
             Value::Float { val, .. } => {
-                compute_series_decimal(&lhs, *val, <ChunkedArray<Float64Type>>::mul, *lhs_span)
+                compute_series_decimal(&lhs, *val, <ChunkedArray<Float64Type>>::mul, lhs_span)
             }
             _ => Err(ShellError::OperatorMismatch {
                 op_span: operator.span,
@@ -235,14 +235,14 @@ pub fn compute_series_single_value(
                 if *val == 0 {
                     Err(ShellError::DivisionByZero(*span))
                 } else {
-                    compute_series_i64(&lhs, *val, <ChunkedArray<Int64Type>>::div, *lhs_span)
+                    compute_series_i64(&lhs, *val, <ChunkedArray<Int64Type>>::div, lhs_span)
                 }
             }
             Value::Float { val, span } => {
                 if val.is_zero() {
                     Err(ShellError::DivisionByZero(*span))
                 } else {
-                    compute_series_decimal(&lhs, *val, <ChunkedArray<Float64Type>>::div, *lhs_span)
+                    compute_series_decimal(&lhs, *val, <ChunkedArray<Float64Type>>::div, lhs_span)
                 }
             }
             _ => Err(ShellError::OperatorMismatch {
@@ -254,9 +254,9 @@ pub fn compute_series_single_value(
             }),
         },
         Operator::Equal => match &right {
-            Value::Int { val, .. } => compare_series_i64(&lhs, *val, ChunkedArray::eq, *lhs_span),
+            Value::Int { val, .. } => compare_series_i64(&lhs, *val, ChunkedArray::eq, lhs_span),
             Value::Float { val, .. } => {
-                compare_series_decimal(&lhs, *val, ChunkedArray::eq, *lhs_span)
+                compare_series_decimal(&lhs, *val, ChunkedArray::eq, lhs_span)
             }
             _ => Err(ShellError::OperatorMismatch {
                 op_span: operator.span,
@@ -267,9 +267,9 @@ pub fn compute_series_single_value(
             }),
         },
         Operator::NotEqual => match &right {
-            Value::Int { val, .. } => compare_series_i64(&lhs, *val, ChunkedArray::neq, *lhs_span),
+            Value::Int { val, .. } => compare_series_i64(&lhs, *val, ChunkedArray::neq, lhs_span),
             Value::Float { val, .. } => {
-                compare_series_decimal(&lhs, *val, ChunkedArray::neq, *lhs_span)
+                compare_series_decimal(&lhs, *val, ChunkedArray::neq, lhs_span)
             }
             _ => Err(ShellError::OperatorMismatch {
                 op_span: operator.span,
@@ -280,9 +280,9 @@ pub fn compute_series_single_value(
             }),
         },
         Operator::LessThan => match &right {
-            Value::Int { val, .. } => compare_series_i64(&lhs, *val, ChunkedArray::lt, *lhs_span),
+            Value::Int { val, .. } => compare_series_i64(&lhs, *val, ChunkedArray::lt, lhs_span),
             Value::Float { val, .. } => {
-                compare_series_decimal(&lhs, *val, ChunkedArray::lt, *lhs_span)
+                compare_series_decimal(&lhs, *val, ChunkedArray::lt, lhs_span)
             }
             _ => Err(ShellError::OperatorMismatch {
                 op_span: operator.span,
@@ -293,11 +293,9 @@ pub fn compute_series_single_value(
             }),
         },
         Operator::LessThanOrEqual => match &right {
-            Value::Int { val, .. } => {
-                compare_series_i64(&lhs, *val, ChunkedArray::lt_eq, *lhs_span)
-            }
+            Value::Int { val, .. } => compare_series_i64(&lhs, *val, ChunkedArray::lt_eq, lhs_span),
             Value::Float { val, .. } => {
-                compare_series_decimal(&lhs, *val, ChunkedArray::lt_eq, *lhs_span)
+                compare_series_decimal(&lhs, *val, ChunkedArray::lt_eq, lhs_span)
             }
             _ => Err(ShellError::OperatorMismatch {
                 op_span: operator.span,
@@ -308,9 +306,9 @@ pub fn compute_series_single_value(
             }),
         },
         Operator::GreaterThan => match &right {
-            Value::Int { val, .. } => compare_series_i64(&lhs, *val, ChunkedArray::gt, *lhs_span),
+            Value::Int { val, .. } => compare_series_i64(&lhs, *val, ChunkedArray::gt, lhs_span),
             Value::Float { val, .. } => {
-                compare_series_decimal(&lhs, *val, ChunkedArray::gt, *lhs_span)
+                compare_series_decimal(&lhs, *val, ChunkedArray::gt, lhs_span)
             }
             _ => Err(ShellError::OperatorMismatch {
                 op_span: operator.span,
@@ -321,11 +319,9 @@ pub fn compute_series_single_value(
             }),
         },
         Operator::GreaterThanOrEqual => match &right {
-            Value::Int { val, .. } => {
-                compare_series_i64(&lhs, *val, ChunkedArray::gt_eq, *lhs_span)
-            }
+            Value::Int { val, .. } => compare_series_i64(&lhs, *val, ChunkedArray::gt_eq, lhs_span),
             Value::Float { val, .. } => {
-                compare_series_decimal(&lhs, *val, ChunkedArray::gt_eq, *lhs_span)
+                compare_series_decimal(&lhs, *val, ChunkedArray::gt_eq, lhs_span)
             }
             _ => Err(ShellError::OperatorMismatch {
                 op_span: operator.span,
@@ -336,7 +332,7 @@ pub fn compute_series_single_value(
             }),
         },
         Operator::Contains => match &right {
-            Value::String { val, .. } => contains_series_pat(&lhs, val, *lhs_span),
+            Value::String { val, .. } => contains_series_pat(&lhs, val, lhs_span),
             _ => Err(ShellError::OperatorMismatch {
                 op_span: operator.span,
                 lhs_ty: left.get_type(),
diff --git a/crates/nu-command/src/dataframe/nu_dataframe/commands/describe.rs b/crates/nu-command/src/dataframe/nu_dataframe/commands/describe.rs
new file mode 100644
index 0000000..1a0a06a
--- /dev/null
+++ b/crates/nu-command/src/dataframe/nu_dataframe/commands/describe.rs
@@ -0,0 +1,246 @@
+use crate::dataframe::nu_dataframe::Column;
+
+use super::super::NuDataFrame;
+
+use nu_protocol::{
+    ast::Call,
+    engine::{Command, EngineState, Stack},
+    Category, Example, PipelineData, ShellError, Signature, Span,
+};
+use polars::{
+    chunked_array::ChunkedArray,
+    prelude::{
+        AnyValue, DataFrame, DataType, Float64Type, IntoSeries, NewChunkedArray, Series, Utf8Type,
+    },
+};
+
+#[derive(Clone)]
+pub struct DescribeDF;
+
+impl Command for DescribeDF {
+    fn name(&self) -> &str {
+        "describe"
+    }
+
+    fn usage(&self) -> &str {
+        "Describes dataframes numeric columns"
+    }
+
+    fn signature(&self) -> Signature {
+        Signature::build(self.name().to_string()).category(Category::Custom("dataframe".into()))
+    }
+
+    fn examples(&self) -> Vec<Example> {
+        vec![Example {
+            description: "dataframe description",
+            example: "[[a b]; [1 1] [1 1]] | to df | describe",
+            result: Some(
+                NuDataFrame::try_from_columns(vec![
+                    Column::new(
+                        "descriptor".to_string(),
+                        vec![
+                            "count".to_string().into(),
+                            "sum".to_string().into(),
+                            "mean".to_string().into(),
+                            "median".to_string().into(),
+                            "std".to_string().into(),
+                            "min".to_string().into(),
+                            "25%".to_string().into(),
+                            "50%".to_string().into(),
+                            "75%".to_string().into(),
+                            "max".to_string().into(),
+                        ],
+                    ),
+                    Column::new(
+                        "a (i64)".to_string(),
+                        vec![
+                            2.0.into(),
+                            2.0.into(),
+                            1.0.into(),
+                            1.0.into(),
+                            0.0.into(),
+                            1.0.into(),
+                            1.0.into(),
+                            1.0.into(),
+                            1.0.into(),
+                            1.0.into(),
+                        ],
+                    ),
+                    Column::new(
+                        "b (i64)".to_string(),
+                        vec![
+                            2.0.into(),
+                            2.0.into(),
+                            1.0.into(),
+                            1.0.into(),
+                            0.0.into(),
+                            1.0.into(),
+                            1.0.into(),
+                            1.0.into(),
+                            1.0.into(),
+                            1.0.into(),
+                        ],
+                    ),
+                ])
+                .expect("simple df for test should not fail")
+                .into_value(Span::unknown()),
+            ),
+        }]
+    }
+
+    fn run(
+        &self,
+        engine_state: &EngineState,
+        stack: &mut Stack,
+        call: &Call,
+        input: PipelineData,
+    ) -> Result<PipelineData, ShellError> {
+        command(engine_state, stack, call, input)
+    }
+}
+
+fn command(
+    _engine_state: &EngineState,
+    _stack: &mut Stack,
+    call: &Call,
+    input: PipelineData,
+) -> Result<PipelineData, ShellError> {
+    let df = NuDataFrame::try_from_pipeline(input, call.head.clone())?;
+
+    let names = ChunkedArray::<Utf8Type>::new_from_opt_slice(
+        "descriptor",
+        &[
+            Some("count"),
+            Some("sum"),
+            Some("mean"),
+            Some("median"),
+            Some("std"),
+            Some("min"),
+            Some("25%"),
+            Some("50%"),
+            Some("75%"),
+            Some("max"),
+        ],
+    )
+    .into_series();
+
+    let head = std::iter::once(names);
+
+    let tail = df
+        .as_ref()
+        .get_columns()
+        .iter()
+        .filter(|col| col.dtype() != &DataType::Object("object"))
+        .map(|col| {
+            let count = col.len() as f64;
+
+            let sum = match col.sum_as_series().cast(&DataType::Float64) {
+                Ok(ca) => match ca.get(0) {
+                    AnyValue::Float64(v) => Some(v),
+                    _ => None,
+                },
+                Err(_) => None,
+            };
+
+            let mean = match col.mean_as_series().get(0) {
+                AnyValue::Float64(v) => Some(v),
+                _ => None,
+            };
+
+            let median = match col.median_as_series().get(0) {
+                AnyValue::Float64(v) => Some(v),
+                _ => None,
+            };
+
+            let std = match col.std_as_series().get(0) {
+                AnyValue::Float64(v) => Some(v),
+                _ => None,
+            };
+
+            let min = match col.min_as_series().cast(&DataType::Float64) {
+                Ok(ca) => match ca.get(0) {
+                    AnyValue::Float64(v) => Some(v),
+                    _ => None,
+                },
+                Err(_) => None,
+            };
+
+            let q_25 = match col.quantile_as_series(0.25) {
+                Ok(ca) => match ca.cast(&DataType::Float64) {
+                    Ok(ca) => match ca.get(0) {
+                        AnyValue::Float64(v) => Some(v),
+                        _ => None,
+                    },
+                    Err(_) => None,
+                },
+                Err(_) => None,
+            };
+
+            let q_50 = match col.quantile_as_series(0.50) {
+                Ok(ca) => match ca.cast(&DataType::Float64) {
+                    Ok(ca) => match ca.get(0) {
+                        AnyValue::Float64(v) => Some(v),
+                        _ => None,
+                    },
+                    Err(_) => None,
+                },
+                Err(_) => None,
+            };
+
+            let q_75 = match col.quantile_as_series(0.75) {
+                Ok(ca) => match ca.cast(&DataType::Float64) {
+                    Ok(ca) => match ca.get(0) {
+                        AnyValue::Float64(v) => Some(v),
+                        _ => None,
+                    },
+                    Err(_) => None,
+                },
+                Err(_) => None,
+            };
+
+            let max = match col.max_as_series().cast(&DataType::Float64) {
+                Ok(ca) => match ca.get(0) {
+                    AnyValue::Float64(v) => Some(v),
+                    _ => None,
+                },
+                Err(_) => None,
+            };
+
+            let name = format!("{} ({})", col.name(), col.dtype());
+            ChunkedArray::<Float64Type>::new_from_opt_slice(
+                &name,
+                &[
+                    Some(count),
+                    sum,
+                    mean,
+                    median,
+                    std,
+                    min,
+                    q_25,
+                    q_50,
+                    q_75,
+                    max,
+                ],
+            )
+            .into_series()
+        });
+
+    let res = head.chain(tail).collect::<Vec<Series>>();
+    let df = DataFrame::new(res).map_err(|e| {
+        ShellError::LabeledError("Dataframe Error".into(), e.to_string(), call.head)
+    })?;
+    Ok(PipelineData::Value(NuDataFrame::dataframe_into_value(
+        df, call.head,
+    )))
+}
+
+#[cfg(test)]
+mod test {
+    use super::super::test_dataframe::test_dataframe;
+    use super::*;
+
+    #[test]
+    fn test_examples() {
+        test_dataframe(DescribeDF {})
+    }
+}
diff --git a/crates/nu-command/src/dataframe/nu_dataframe/commands/dtypes.rs b/crates/nu-command/src/dataframe/nu_dataframe/commands/dtypes.rs
new file mode 100644
index 0000000..db6e7b5
--- /dev/null
+++ b/crates/nu-command/src/dataframe/nu_dataframe/commands/dtypes.rs
@@ -0,0 +1,106 @@
+use super::super::{Column, NuDataFrame};
+use nu_protocol::{
+    ast::Call,
+    engine::{Command, EngineState, Stack},
+    Category, Example, PipelineData, ShellError, Signature, Span, Value,
+};
+
+#[derive(Clone)]
+pub struct DataTypes;
+
+impl Command for DataTypes {
+    fn name(&self) -> &str {
+        "dtypes"
+    }
+
+    fn usage(&self) -> &str {
+        "Show dataframe data types"
+    }
+
+    fn signature(&self) -> Signature {
+        Signature::build(self.name().to_string()).category(Category::Custom("dataframe".into()))
+    }
+
+    fn examples(&self) -> Vec<Example> {
+        vec![Example {
+            description: "drop column a",
+            example: "[[a b]; [1 2] [3 4]] | to df | dtypes",
+            result: Some(
+                NuDataFrame::try_from_columns(vec![
+                    Column::new(
+                        "column".to_string(),
+                        vec!["a".to_string().into(), "b".to_string().into()],
+                    ),
+                    Column::new(
+                        "dtype".to_string(),
+                        vec!["i64".to_string().into(), "i64".to_string().into()],
+                    ),
+                ])
+                .expect("simple df for test should not fail")
+                .into_value(Span::unknown()),
+            ),
+        }]
+    }
+
+    fn run(
+        &self,
+        engine_state: &EngineState,
+        stack: &mut Stack,
+        call: &Call,
+        input: PipelineData,
+    ) -> Result<PipelineData, ShellError> {
+        command(engine_state, stack, call, input)
+    }
+}
+
+#[allow(clippy::needless_collect)]
+fn command(
+    _engine_state: &EngineState,
+    _stack: &mut Stack,
+    call: &Call,
+    input: PipelineData,
+) -> Result<PipelineData, ShellError> {
+    let df = NuDataFrame::try_from_pipeline(input, call.head.clone())?;
+
+    let mut dtypes: Vec<Value> = Vec::new();
+    let names: Vec<Value> = df
+        .as_ref()
+        .get_column_names()
+        .iter()
+        .map(|v| {
+            let dtype = df
+                .as_ref()
+                .column(v)
+                .expect("using name from list of names from dataframe")
+                .dtype();
+
+            let dtype_str = dtype.to_string();
+            dtypes.push(Value::String {
+                val: dtype_str.into(),
+                span: call.head,
+            });
+
+            Value::String {
+                val: v.to_string().into(),
+                span: call.head,
+            }
+        })
+        .collect();
+
+    let names_col = Column::new("column".to_string(), names);
+    let dtypes_col = Column::new("dtype".to_string(), dtypes);
+
+    let df = NuDataFrame::try_from_columns(vec![names_col, dtypes_col])?;
+    Ok(PipelineData::Value(df.into_value(call.head)))
+}
+
+#[cfg(test)]
+mod test {
+    use super::super::test_dataframe::test_dataframe;
+    use super::*;
+
+    #[test]
+    fn test_examples() {
+        test_dataframe(DataTypes {})
+    }
+}
diff --git a/crates/nu-command/src/dataframe/nu_dataframe/commands/mod.rs b/crates/nu-command/src/dataframe/nu_dataframe/commands/mod.rs
new file mode 100644
index 0000000..c08d349
--- /dev/null
+++ b/crates/nu-command/src/dataframe/nu_dataframe/commands/mod.rs
@@ -0,0 +1,12 @@
+mod describe;
+mod dtypes;
+mod open;
+mod to_df;
+
+pub use describe::DescribeDF;
+pub use dtypes::DataTypes;
+pub use open::OpenDataFrame;
+pub use to_df::ToDataFrame;
+
+#[cfg(test)]
+mod test_dataframe;
diff --git a/crates/nu-command/src/dataframe/open.rs b/crates/nu-command/src/dataframe/nu_dataframe/commands/open.rs
similarity index 97%
rename from crates/nu-command/src/dataframe/open.rs
rename to crates/nu-command/src/dataframe/nu_dataframe/commands/open.rs
index 1c926f1..d309676 100644
--- a/crates/nu-command/src/dataframe/open.rs
+++ b/crates/nu-command/src/dataframe/nu_dataframe/commands/open.rs
@@ -1,12 +1,11 @@
-use std::{fs::File, path::PathBuf};
-
-use nu_dataframe::NuDataFrame;
+use super::super::NuDataFrame;
 use nu_engine::CallExt;
 use nu_protocol::{
     ast::Call,
     engine::{Command, EngineState, Stack},
     Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape,
 };
+use std::{fs::File, path::PathBuf};
 
 use polars::prelude::{CsvEncoding, CsvReader, JsonReader, ParquetReader, SerReader};
 
@@ -23,7 +22,7 @@ impl Command for OpenDataFrame {
     }
 
     fn signature(&self) -> Signature {
-        Signature::build("open-df")
+        Signature::build(self.name().to_string())
             .required(
                 "file",
                 SyntaxShape::Filepath,
@@ -64,7 +63,7 @@ impl Command for OpenDataFrame {
     fn examples(&self) -> Vec<Example> {
         vec![Example {
             description: "Takes a file name and creates a dataframe",
-            example: "dataframe open test.csv",
+            example: "open-df test.csv",
             result: None,
         }]
     }
diff --git a/crates/nu-command/src/dataframe/nu_dataframe/commands/to_df.rs b/crates/nu-command/src/dataframe/nu_dataframe/commands/to_df.rs
new file mode 100644
index 0000000..6ce8845
--- /dev/null
+++ b/crates/nu-command/src/dataframe/nu_dataframe/commands/to_df.rs
@@ -0,0 +1,111 @@
+use super::super::{Column, NuDataFrame};
+
+use nu_protocol::{
+    ast::Call,
+    engine::{Command, EngineState, Stack},
+    Category, Example, PipelineData, ShellError, Signature, Span,
+};
+
+#[derive(Clone)]
+pub struct ToDataFrame;
+
+impl Command for ToDataFrame {
+    fn name(&self) -> &str {
+        "to df"
+    }
+
+    fn usage(&self) -> &str {
+        "Converts a List, Table or Dictionary into a dataframe"
+    }
+
+    fn signature(&self) -> Signature {
+        Signature::build(self.name().to_string()).category(Category::Custom("dataframe".into()))
+    }
+
+    fn examples(&self) -> Vec<Example> {
+        vec![
+            Example {
+                description: "Takes a dictionary and creates a dataframe",
+                example: "[[a b];[1 2] [3 4]] | to df",
+                result: Some(
+                    NuDataFrame::try_from_columns(vec![
+                        Column::new("a".to_string(), vec![1.into(), 3.into()]),
+                        Column::new("b".to_string(), vec![2.into(), 4.into()]),
+                    ])
+                    .expect("simple df for test should not fail")
+                    .into_value(Span::unknown()),
+                ),
+            },
+            Example {
+                description: "Takes a list of tables and creates a dataframe",
+                example: "[[1 2 a] [3 4 b] [5 6 c]] | to df",
+                result: Some(
+                    NuDataFrame::try_from_columns(vec![
+                        Column::new("0".to_string(), vec![1.into(), 3.into(), 5.into()]),
+                        Column::new("1".to_string(), vec![2.into(), 4.into(), 6.into()]),
+                        Column::new(
+                            "2".to_string(),
+                            vec![
+                                "a".to_string().into(),
+                                "b".to_string().into(),
+                                "c".to_string().into(),
+                            ],
+                        ),
+                    ])
+                    .expect("simple df for test should not fail")
+                    .into_value(Span::unknown()),
+                ),
+            },
+            Example {
+                description: "Takes a list and creates a dataframe",
+                example: "[a b c] | to df",
+                result: Some(
+                    NuDataFrame::try_from_columns(vec![Column::new(
+                        "0".to_string(),
+                        vec![
+                            "a".to_string().into(),
+                            "b".to_string().into(),
+                            "c".to_string().into(),
+                        ],
+                    )])
+                    .expect("simple df for test should not fail")
+                    .into_value(Span::unknown()),
+                ),
+            },
+            Example {
+                description: "Takes a list of booleans and creates a dataframe",
+                example: "[$true $true $false] | to df",
+                result: Some(
+                    NuDataFrame::try_from_columns(vec![Column::new(
+                        "0".to_string(),
+                        vec![true.into(), true.into(), false.into()],
+                    )])
+                    .expect("simple df for test should not fail")
+                    .into_value(Span::unknown()),
+                ),
+            },
+        ]
+    }
+
+    fn run(
+        &self,
+        _engine_state: &EngineState,
+        _stack: &mut Stack,
+        call: &Call,
+        input: PipelineData,
+    ) -> Result<PipelineData, ShellError> {
+        let df = NuDataFrame::try_from_iter(input.into_iter())?;
+        Ok(PipelineData::Value(NuDataFrame::into_value(df, call.head)))
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::super::test_dataframe::test_dataframe;
+    use super::*;
+
+    #[test]
+    fn test_examples() {
+        test_dataframe(ToDataFrame {})
+    }
+}
diff --git a/crates/nu-dataframe/src/conversion.rs b/crates/nu-command/src/dataframe/nu_dataframe/conversion.rs
similarity index 99%
rename from crates/nu-dataframe/src/conversion.rs
rename to crates/nu-command/src/dataframe/nu_dataframe/conversion.rs
index fdd834f..daccded 100644
--- a/crates/nu-dataframe/src/conversion.rs
+++ b/crates/nu-command/src/dataframe/nu_dataframe/conversion.rs
@@ -1,5 +1,5 @@
-use super::NuDataFrame;
-use crate::DataFrameValue;
+use super::{DataFrameValue, NuDataFrame};
+
 use chrono::{DateTime, FixedOffset, NaiveDateTime};
 use indexmap::map::{Entry, IndexMap};
 use nu_protocol::{ShellError, Span, Value};
@@ -35,9 +35,9 @@ impl Column {
         self.name.as_str()
     }
 
-    pub fn iter(&self) -> impl Iterator<Item = &Value> {
-        self.values.iter()
-    }
+    //pub fn iter(&self) -> impl Iterator<Item = &Value> {
+    //    self.values.iter()
+    //}
 }
 
 impl IntoIterator for Column {
diff --git a/crates/nu-dataframe/src/custom_value.rs b/crates/nu-command/src/dataframe/nu_dataframe/custom_value.rs
similarity index 71%
rename from crates/nu-dataframe/src/custom_value.rs
rename to crates/nu-command/src/dataframe/nu_dataframe/custom_value.rs
index 6ffb067..842d0b1 100644
--- a/crates/nu-dataframe/src/custom_value.rs
+++ b/crates/nu-command/src/dataframe/nu_dataframe/custom_value.rs
@@ -1,5 +1,5 @@
-use crate::NuDataFrame;
-use nu_protocol::{ast::Operator, CustomValue, ShellError, Span, Value};
+use super::NuDataFrame;
+use nu_protocol::{ast::Operator, Category, CustomValue, ShellError, Span, Value};
 
 // CustomValue implementation for NuDataFrame
 impl CustomValue for NuDataFrame {
@@ -20,6 +20,10 @@ impl CustomValue for NuDataFrame {
         }
     }
 
+    fn category(&self) -> Category {
+        Category::Custom(self.typetag_name().into())
+    }
+
     fn value_string(&self) -> String {
         self.typetag_name().to_string()
     }
@@ -44,7 +48,17 @@ impl CustomValue for NuDataFrame {
 
     fn follow_path_string(&self, column_name: String, span: Span) -> Result<Value, ShellError> {
         let column = self.column(&column_name, span)?;
-        Ok(column.to_value(span))
+        Ok(column.into_value(span))
+    }
+
+    fn partial_cmp(&self, other: &Value) -> Option<std::cmp::Ordering> {
+        match other {
+            Value::CustomValue { val, .. } => val
+                .as_any()
+                .downcast_ref::<Self>()
+                .and_then(|other| self.is_equal(other)),
+            _ => None,
+        }
     }
 
     fn operation(
diff --git a/crates/nu-dataframe/src/lib.rs b/crates/nu-command/src/dataframe/nu_dataframe/mod.rs
similarity index 70%
rename from crates/nu-dataframe/src/lib.rs
rename to crates/nu-command/src/dataframe/nu_dataframe/mod.rs
index 70196d6..8517836 100644
--- a/crates/nu-dataframe/src/lib.rs
+++ b/crates/nu-command/src/dataframe/nu_dataframe/mod.rs
@@ -1,15 +1,17 @@
+pub mod commands;
+
 mod between_values;
 mod conversion;
 mod custom_value;
 mod operations;
 
-use std::{cmp::Ordering, fmt::Display, hash::Hasher};
-
 use conversion::{Column, ColumnMap};
+
 use indexmap::map::IndexMap;
-use nu_protocol::{did_you_mean, ShellError, Span, Value};
-use polars::prelude::{DataFrame, PolarsObject, Series};
+use nu_protocol::{did_you_mean, PipelineData, ShellError, Span, Value};
+use polars::prelude::{DataFrame, DataType, PolarsObject, Series};
 use serde::{Deserialize, Serialize};
+use std::{cmp::Ordering, fmt::Display, hash::Hasher};
 
 // DataFrameValue is an encapsulation of Nushell Value that can be used
 // to define the PolarsObject Trait. The polars object trait allows to
@@ -62,13 +64,25 @@ impl std::hash::Hash for DataFrameValue {
 
 impl PolarsObject for DataFrameValue {
     fn type_name() -> &'static str {
-        "value"
+        "object"
     }
 }
 
 #[derive(Debug, Serialize, Deserialize)]
 pub struct NuDataFrame(DataFrame);
 
+impl AsRef<DataFrame> for NuDataFrame {
+    fn as_ref(&self) -> &polars::prelude::DataFrame {
+        &self.0
+    }
+}
+
+impl AsMut<DataFrame> for NuDataFrame {
+    fn as_mut(&mut self) -> &mut polars::prelude::DataFrame {
+        &mut self.0
+    }
+}
+
 impl NuDataFrame {
     pub fn new(dataframe: DataFrame) -> Self {
         Self(dataframe)
@@ -86,7 +100,7 @@ impl NuDataFrame {
         }
     }
 
-    pub fn to_value(self, span: Span) -> Value {
+    pub fn into_value(self, span: Span) -> Value {
         Value::CustomValue {
             val: Box::new(self),
             span,
@@ -131,12 +145,12 @@ impl NuDataFrame {
         conversion::from_parsed_columns(column_values)
     }
 
-    pub fn try_from_series(columns: Vec<Series>) -> Result<Self, ShellError> {
-        let dataframe = DataFrame::new(columns)
-            .map_err(|e| ShellError::InternalError(format!("Unable to create DataFrame: {}", e)))?;
+    //pub fn try_from_series(columns: Vec<Series>) -> Result<Self, ShellError> {
+    //    let dataframe = DataFrame::new(columns)
+    //        .map_err(|e| ShellError::InternalError(format!("Unable to create DataFrame: {}", e)))?;
 
-        Ok(Self::new(dataframe))
-    }
+    //    Ok(Self::new(dataframe))
+    //}
 
     pub fn try_from_columns(columns: Vec<Column>) -> Result<Self, ShellError> {
         let mut column_values: ColumnMap = IndexMap::new();
@@ -151,6 +165,24 @@ impl NuDataFrame {
         conversion::from_parsed_columns(column_values)
     }
 
+    pub fn try_from_pipeline(input: PipelineData, span: Span) -> Result<Self, ShellError> {
+        match input.into_value(span) {
+            Value::CustomValue { val, span } => match val.as_any().downcast_ref::<NuDataFrame>() {
+                Some(df) => Ok(NuDataFrame(df.0.clone())),
+                None => Err(ShellError::CantConvert(
+                    "Dataframe not found".into(),
+                    "value is not a dataframe".into(),
+                    span,
+                )),
+            },
+            _ => Err(ShellError::CantConvert(
+                "Dataframe not found".into(),
+                "value is not a dataframe".into(),
+                span,
+            )),
+        }
+    }
+
     pub fn column(&self, column: &str, span: Span) -> Result<Self, ShellError> {
         let s = self.0.column(column).map_err(|_| {
             let possibilities = self
@@ -295,4 +327,64 @@ impl NuDataFrame {
 
         Ok(values)
     }
+
+    // Dataframes are considered equal if they have the same shape, column name and values
+    pub fn is_equal(&self, other: &Self) -> Option<Ordering> {
+        if self.as_ref().width() == 0 {
+            // checking for empty dataframe
+            return None;
+        }
+
+        if self.as_ref().get_column_names() != other.as_ref().get_column_names() {
+            // checking both dataframes share the same names
+            return None;
+        }
+
+        if self.as_ref().height() != other.as_ref().height() {
+            // checking both dataframes have the same row size
+            return None;
+        }
+
+        // sorting dataframe by the first column
+        let column_names = self.as_ref().get_column_names();
+        let first_col = column_names
+            .get(0)
+            .expect("already checked that dataframe is different than 0");
+
+        // if unable to sort, then unable to compare
+        let lhs = match self.as_ref().sort(*first_col, false) {
+            Ok(df) => df,
+            Err(_) => return None,
+        };
+
+        let rhs = match other.as_ref().sort(*first_col, false) {
+            Ok(df) => df,
+            Err(_) => return None,
+        };
+
+        for name in self.as_ref().get_column_names() {
+            let self_series = lhs.column(name).expect("name from dataframe names");
+
+            let other_series = rhs
+                .column(name)
+                .expect("already checked that name in other");
+
+            let self_series = match self_series.dtype() {
+                // Casting needed to compare other numeric types with nushell numeric type.
+                // In nushell we only have i64 integer numeric types and any array created
+                // with nushell untagged primitives will be of type i64
+                DataType::UInt32 => match self_series.cast(&DataType::Int64) {
+                    Ok(series) => series,
+                    Err(_) => return None,
+                },
+                _ => self_series.clone(),
+            };
+
+            if !self_series.series_equal(other_series) {
+                return None;
+            }
+        }
+
+        Some(Ordering::Equal)
+    }
 }
diff --git a/crates/nu-dataframe/src/operations.rs b/crates/nu-command/src/dataframe/nu_dataframe/operations.rs
similarity index 61%
rename from crates/nu-dataframe/src/operations.rs
rename to crates/nu-command/src/dataframe/nu_dataframe/operations.rs
index bcd57f3..c37db6c 100644
--- a/crates/nu-dataframe/src/operations.rs
+++ b/crates/nu-command/src/dataframe/nu_dataframe/operations.rs
@@ -1,7 +1,7 @@
-use nu_protocol::{ast::Operator, span, ShellError, Span, Spanned, Value};
+use nu_protocol::{ast::Operator, ShellError, Span, Spanned, Value};
 use polars::prelude::{DataFrame, Series};
 
-use crate::between_values::{
+use super::between_values::{
     between_dataframes, compute_between_series, compute_series_single_value,
 };
 
@@ -9,18 +9,18 @@ use super::NuDataFrame;
 
 pub enum Axis {
     Row,
-    Column,
+    //Column,
 }
 
-impl Axis {
-    pub fn try_from_str(axis: &str, span: Span) -> Result<Axis, ShellError> {
-        match axis {
-            "row" => Ok(Axis::Row),
-            "col" => Ok(Axis::Column),
-            _ => Err(ShellError::DidYouMean("'row' or 'col'".into(), span)),
-        }
-    }
-}
+//impl Axis {
+//    pub fn try_from_str(axis: &str, span: Span) -> Result<Axis, ShellError> {
+//        match axis {
+//            "row" => Ok(Axis::Row),
+//            "col" => Ok(Axis::Column),
+//            _ => Err(ShellError::DidYouMean("'row' or 'col'".into(), span)),
+//        }
+//    }
+//}
 
 impl NuDataFrame {
     pub fn compute_with_value(
@@ -42,7 +42,6 @@ impl NuDataFrame {
                     )
                 })?;
 
-                let operation_span = span(&[lhs_span, *rhs_span]);
                 match (self.is_series(), rhs.is_series()) {
                     (true, true) => {
                         let lhs = &self
@@ -77,11 +76,10 @@ impl NuDataFrame {
 
                         compute_between_series(
                             op,
-                            NuDataFrame::default_value(lhs_span),
+                            &NuDataFrame::default_value(lhs_span),
                             lhs,
                             right,
                             rhs,
-                            operation_span,
                         )
                     }
                     _ => {
@@ -101,11 +99,10 @@ impl NuDataFrame {
 
                         between_dataframes(
                             op,
-                            NuDataFrame::default_value(lhs_span),
+                            &NuDataFrame::default_value(lhs_span),
                             self,
                             right,
                             rhs,
-                            operation_span,
                         )
                     }
                 }
@@ -116,13 +113,7 @@ impl NuDataFrame {
                     span: op_span,
                 };
 
-                compute_series_single_value(
-                    op,
-                    self,
-                    &lhs_span,
-                    NuDataFrame::default_value(lhs_span),
-                    right,
-                )
+                compute_series_single_value(op, &NuDataFrame::default_value(lhs_span), self, right)
             }
         }
     }
@@ -131,7 +122,7 @@ impl NuDataFrame {
         &self,
         other: &NuDataFrame,
         axis: Axis,
-        span: Span,
+        _span: Span,
     ) -> Result<Self, ShellError> {
         match axis {
             Axis::Row => {
@@ -160,61 +151,60 @@ impl NuDataFrame {
                     .map_err(|e| ShellError::InternalError(e.to_string()))?;
 
                 Ok(NuDataFrame::new(df_new))
-            }
-            Axis::Column => {
-                if self.0.width() != other.0.width() {
-                    return Err(ShellError::IncompatibleParametersSingle(
-                        "Dataframes with different number of columns".into(),
-                        span,
-                    ));
-                }
-
-                if !self
-                    .0
-                    .get_column_names()
-                    .iter()
-                    .all(|col| other.0.get_column_names().contains(col))
-                {
-                    return Err(ShellError::IncompatibleParametersSingle(
-                        "Dataframes with different columns names".into(),
-                        span,
-                    ));
-                }
-
-                let new_cols = self
-                    .0
-                    .get_columns()
-                    .iter()
-                    .map(|s| {
-                        let other_col = other
-                            .0
-                            .column(s.name())
-                            .expect("Already checked that dataframes have same columns");
-
-                        let mut tmp = s.clone();
-                        let res = tmp.append(other_col);
-
-                        match res {
-                            Ok(s) => Ok(s.clone()),
-                            Err(e) => Err({
-                                ShellError::InternalError(format!(
-                                    "Unable to append dataframes: {}",
-                                    e
-                                ))
-                            }),
-                        }
-                    })
-                    .collect::<Result<Vec<Series>, ShellError>>()?;
-
-                let df_new = DataFrame::new(new_cols).map_err(|e| {
-                    ShellError::InternalError(format!(
-                        "Unable to append dataframes: {}",
-                        e.to_string()
-                    ))
-                })?;
-
-                Ok(NuDataFrame::new(df_new))
-            }
+            } //Axis::Column => {
+              //    if self.0.width() != other.0.width() {
+              //        return Err(ShellError::IncompatibleParametersSingle(
+              //            "Dataframes with different number of columns".into(),
+              //            span,
+              //        ));
+              //    }
+
+              //    if !self
+              //        .0
+              //        .get_column_names()
+              //        .iter()
+              //        .all(|col| other.0.get_column_names().contains(col))
+              //    {
+              //        return Err(ShellError::IncompatibleParametersSingle(
+              //            "Dataframes with different columns names".into(),
+              //            span,
+              //        ));
+              //    }
+
+              //    let new_cols = self
+              //        .0
+              //        .get_columns()
+              //        .iter()
+              //        .map(|s| {
+              //            let other_col = other
+              //                .0
+              //                .column(s.name())
+              //                .expect("Already checked that dataframes have same columns");
+
+              //            let mut tmp = s.clone();
+              //            let res = tmp.append(other_col);
+
+              //            match res {
+              //                Ok(s) => Ok(s.clone()),
+              //                Err(e) => Err({
+              //                    ShellError::InternalError(format!(
+              //                        "Unable to append dataframes: {}",
+              //                        e
+              //                    ))
+              //                }),
+              //            }
+              //        })
+              //        .collect::<Result<Vec<Series>, ShellError>>()?;
+
+              //    let df_new = DataFrame::new(new_cols).map_err(|e| {
+              //        ShellError::InternalError(format!(
+              //            "Unable to append dataframes: {}",
+              //            e.to_string()
+              //        ))
+              //    })?;
+
+              //    Ok(NuDataFrame::new(df_new))
+              //}
         }
     }
 }
diff --git a/crates/nu-command/src/dataframe/to_df.rs b/crates/nu-command/src/dataframe/to_df.rs
deleted file mode 100644
index a9cd503..0000000
--- a/crates/nu-command/src/dataframe/to_df.rs
+++ /dev/null
@@ -1,59 +0,0 @@
-use nu_dataframe::NuDataFrame;
-use nu_protocol::{
-    ast::Call,
-    engine::{Command, EngineState, Stack},
-    Category, Example, PipelineData, ShellError, Signature,
-};
-
-#[derive(Clone)]
-pub struct ToDataFrame;
-
-impl Command for ToDataFrame {
-    fn name(&self) -> &str {
-        "to-df"
-    }
-
-    fn usage(&self) -> &str {
-        "Converts a List, Table or Dictionary into a dataframe"
-    }
-
-    fn signature(&self) -> Signature {
-        Signature::build("to-df").category(Category::Custom("dataframe".into()))
-    }
-
-    fn examples(&self) -> Vec<Example> {
-        vec![
-            Example {
-                description: "Takes a dictionary and creates a dataframe",
-                example: "[[a b];[1 2] [3 4]] | to-df",
-                result: None,
-            },
-            Example {
-                description: "Takes a list of tables and creates a dataframe",
-                example: "[[1 2 a] [3 4 b] [5 6 c]] | to-df",
-                result: None,
-            },
-            Example {
-                description: "Takes a list and creates a dataframe",
-                example: "[a b c] | to-df",
-                result: None,
-            },
-            Example {
-                description: "Takes a list of booleans and creates a dataframe",
-                example: "[$true $true $false] | to-df",
-                result: None,
-            },
-        ]
-    }
-
-    fn run(
-        &self,
-        _engine_state: &EngineState,
-        _stack: &mut Stack,
-        call: &Call,
-        input: PipelineData,
-    ) -> Result<PipelineData, ShellError> {
-        let df = NuDataFrame::try_from_iter(input.into_iter())?;
-        Ok(PipelineData::Value(NuDataFrame::to_value(df, call.head)))
-    }
-}
diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs
index e5bc4a0..7b4e93e 100644
--- a/crates/nu-command/src/default_context.rs
+++ b/crates/nu-command/src/default_context.rs
@@ -1,7 +1,4 @@
-use nu_protocol::{
-    engine::{EngineState, StateWorkingSet},
-    Signature,
-};
+use nu_protocol::engine::{EngineState, StateWorkingSet};
 
 use crate::*;
 
@@ -20,13 +17,23 @@ pub fn create_default_context() -> EngineState {
             };
         }
 
+        // If there are commands that have the same name as default declarations,
+        // they have to be registered before the main declarations. This helps to make
+        // them only accessible if the correct input value category is used with the
+        // declaration
+        #[cfg(feature = "dataframe")]
+        bind_command!(DataTypes, DescribeDF, OpenDataFrame, ToDataFrame);
+
         // TODO: sort default context items categorically
         bind_command!(
             Alias,
+            All,
+            Any,
             Append,
             Benchmark,
             BuildString,
             Cd,
+            Clear,
             Collect,
             Cp,
             Date,
@@ -43,6 +50,7 @@ pub fn create_default_context() -> EngineState {
             Drop,
             Each,
             Echo,
+            Exit,
             ExportCommand,
             ExportDef,
             ExportEnv,
@@ -60,6 +68,11 @@ pub fn create_default_context() -> EngineState {
             FromUrl,
             FromEml,
             FromOds,
+            FromIcs,
+            FromIni,
+            FromVcf,
+            FromSsv,
+            FromXml,
             FromXlsx,
             Get,
             Griddle,
@@ -71,6 +84,7 @@ pub fn create_default_context() -> EngineState {
             IntoFilesize,
             IntoInt,
             IntoString,
+            Kill,
             Last,
             Length,
             Let,
@@ -100,11 +114,17 @@ pub fn create_default_context() -> EngineState {
             Parse,
             Ps,
             Range,
+            Random,
             Reverse,
             Rm,
             Select,
             Shuffle,
             Size,
+            Skip,
+            SkipUntil,
+            SkipWhile,
+            Sleep,
+            Source,
             Split,
             SplitChars,
             SplitColumn,
@@ -143,14 +163,8 @@ pub fn create_default_context() -> EngineState {
         #[cfg(feature = "plugin")]
         bind_command!(Register);
 
-        #[cfg(feature = "dataframe")]
-        bind_command!(OpenDataFrame, ToDataFrame);
-
         // This is a WIP proof of concept
-        bind_command!(ListGitBranches, Git, GitCheckout, Source);
-
-        let sig = Signature::build("exit");
-        working_set.add_decl(sig.predeclare());
+        // bind_command!(ListGitBranches, Git, GitCheckout, Source);
 
         working_set.render()
     };
diff --git a/crates/nu-command/src/env/with_env.rs b/crates/nu-command/src/env/with_env.rs
index 3f9b3ae..3f59c1f 100644
--- a/crates/nu-command/src/env/with_env.rs
+++ b/crates/nu-command/src/env/with_env.rs
@@ -163,10 +163,10 @@ fn with_env(
     for (k, v) in env {
         match v {
             EnvVar::Nothing => {
-                stack.env_vars.remove(&k);
+                stack.remove_env_var(&k);
             }
             EnvVar::Proper(s) => {
-                stack.env_vars.insert(k, s);
+                stack.add_env_var(k, s);
             }
         }
     }
diff --git a/crates/nu-command/src/filters/all.rs b/crates/nu-command/src/filters/all.rs
new file mode 100644
index 0000000..8389789
--- /dev/null
+++ b/crates/nu-command/src/filters/all.rs
@@ -0,0 +1,94 @@
+use nu_engine::eval_block;
+use nu_protocol::{
+    ast::Call,
+    engine::{Command, EngineState, Stack},
+    Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape,
+};
+
+#[derive(Clone)]
+pub struct All;
+
+impl Command for All {
+    fn name(&self) -> &str {
+        "all?"
+    }
+
+    fn signature(&self) -> Signature {
+        Signature::build(self.name())
+            .required(
+                "predicate",
+                SyntaxShape::RowCondition,
+                "the predicate that must match",
+            )
+            .category(Category::Filters)
+    }
+
+    fn usage(&self) -> &str {
+        "Test if every element of the input matches a predicate."
+    }
+
+    fn examples(&self) -> Vec<Example> {
+        use nu_protocol::Value;
+
+        vec![
+            Example {
+                description: "Find if services are running",
+                example: "echo [[status]; [UP] [UP]] | all? status == UP",
+                result: Some(Value::from(true)),
+            },
+            Example {
+                description: "Check that all values are even",
+                example: "echo [2 4 6 8] | all? ($it mod 2) == 0",
+                result: Some(Value::from(true)),
+            },
+        ]
+    }
+
+    fn run(
+        &self,
+        engine_state: &EngineState,
+        stack: &mut Stack,
+        call: &Call,
+        input: PipelineData,
+    ) -> Result<PipelineData, ShellError> {
+        let predicate = &call.positional[0];
+        let block_id = predicate
+            .as_row_condition_block()
+            .ok_or_else(|| ShellError::InternalError("Expected row condition".to_owned()))?;
+
+        let span = call.head;
+
+        let block = engine_state.get_block(block_id);
+        let var_id = block.signature.get_positional(0).and_then(|arg| arg.var_id);
+        let mut stack = stack.collect_captures(&block.captures);
+
+        let ctrlc = engine_state.ctrlc.clone();
+        let engine_state = engine_state.clone();
+
+        Ok(input
+            .into_interruptible_iter(ctrlc)
+            .all(move |value| {
+                if let Some(var_id) = var_id {
+                    stack.add_var(var_id, value);
+                }
+
+                eval_block(&engine_state, &mut stack, block, PipelineData::new(span))
+                    .map_or(false, |pipeline_data| {
+                        pipeline_data.into_value(span).is_true()
+                    })
+            })
+            .into_pipeline_data())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_examples() {
+        use crate::test_examples;
+
+        test_examples(All)
+    }
+}
diff --git a/crates/nu-command/src/filters/any.rs b/crates/nu-command/src/filters/any.rs
new file mode 100644
index 0000000..36bfe91
--- /dev/null
+++ b/crates/nu-command/src/filters/any.rs
@@ -0,0 +1,94 @@
+use nu_engine::eval_block;
+use nu_protocol::{
+    ast::Call,
+    engine::{Command, EngineState, Stack},
+    Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape,
+};
+
+#[derive(Clone)]
+pub struct Any;
+
+impl Command for Any {
+    fn name(&self) -> &str {
+        "any?"
+    }
+
+    fn signature(&self) -> Signature {
+        Signature::build(self.name())
+            .required(
+                "predicate",
+                SyntaxShape::RowCondition,
+                "the predicate that must match",
+            )
+            .category(Category::Filters)
+    }
+
+    fn usage(&self) -> &str {
+        "Tests if any element of the input matches a predicate."
+    }
+
+    fn examples(&self) -> Vec<Example> {
+        use nu_protocol::Value;
+
+        vec![
+            Example {
+                description: "Find if a service is not running",
+                example: "echo [[status]; [UP] [DOWN] [UP]] | any? status == DOWN",
+                result: Some(Value::from(true)),
+            },
+            Example {
+                description: "Check if any of the values is odd",
+                example: "echo [2 4 1 6 8] | any? ($it mod 2) == 1",
+                result: Some(Value::from(true)),
+            },
+        ]
+    }
+
+    fn run(
+        &self,
+        engine_state: &EngineState,
+        stack: &mut Stack,
+        call: &Call,
+        input: PipelineData,
+    ) -> Result<PipelineData, ShellError> {
+        let predicate = &call.positional[0];
+        let block_id = predicate
+            .as_row_condition_block()
+            .ok_or_else(|| ShellError::InternalError("Expected row condition".to_owned()))?;
+
+        let span = call.head;
+
+        let block = engine_state.get_block(block_id);
+        let var_id = block.signature.get_positional(0).and_then(|arg| arg.var_id);
+        let mut stack = stack.collect_captures(&block.captures);
+
+        let ctrlc = engine_state.ctrlc.clone();
+        let engine_state = engine_state.clone();
+
+        Ok(input
+            .into_interruptible_iter(ctrlc)
+            .any(move |value| {
+                if let Some(var_id) = var_id {
+                    stack.add_var(var_id, value);
+                }
+
+                eval_block(&engine_state, &mut stack, block, PipelineData::new(span))
+                    .map_or(false, |pipeline_data| {
+                        pipeline_data.into_value(span).is_true()
+                    })
+            })
+            .into_pipeline_data())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_examples() {
+        use crate::test_examples;
+
+        test_examples(Any)
+    }
+}
diff --git a/crates/nu-command/src/filters/mod.rs b/crates/nu-command/src/filters/mod.rs
index aba51e4..61f4f15 100644
--- a/crates/nu-command/src/filters/mod.rs
+++ b/crates/nu-command/src/filters/mod.rs
@@ -1,3 +1,5 @@
+mod all;
+mod any;
 mod append;
 mod collect;
 mod drop;
@@ -12,11 +14,14 @@ mod range;
 mod reverse;
 mod select;
 mod shuffle;
+mod skip;
 mod update;
 mod where_;
 mod wrap;
 mod zip;
 
+pub use all::All;
+pub use any::Any;
 pub use append::Append;
 pub use collect::Collect;
 pub use drop::*;
@@ -31,6 +36,7 @@ pub use range::Range;
 pub use reverse::Reverse;
 pub use select::Select;
 pub use shuffle::Shuffle;
+pub use skip::*;
 pub use update::Update;
 pub use where_::Where;
 pub use wrap::Wrap;
diff --git a/crates/nu-command/src/filters/skip/command.rs b/crates/nu-command/src/filters/skip/command.rs
new file mode 100644
index 0000000..9a416af
--- /dev/null
+++ b/crates/nu-command/src/filters/skip/command.rs
@@ -0,0 +1,90 @@
+use std::convert::TryInto;
+
+use nu_engine::CallExt;
+use nu_protocol::{
+    ast::Call,
+    engine::{Command, EngineState, Stack},
+    Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span,
+    SyntaxShape, Value,
+};
+
+#[derive(Clone)]
+pub struct Skip;
+
+impl Command for Skip {
+    fn name(&self) -> &str {
+        "skip"
+    }
+
+    fn signature(&self) -> Signature {
+        Signature::build(self.name())
+            .optional("n", SyntaxShape::Int, "the number of elements to skip")
+            .category(Category::Filters)
+    }
+
+    fn usage(&self) -> &str {
+        "Skip the first n elements of the input."
+    }
+
+    fn examples(&self) -> Vec<Example> {
+        vec![
+            Example {
+                description: "Skip two elements",
+                example: "echo [[editions]; [2015] [2018] [2021]] | skip 2",
+                result: Some(Value::List {
+                    vals: vec![Value::Record {
+                        cols: vec!["editions".to_owned()],
+                        vals: vec![Value::from(2021)],
+                        span: Span::unknown(),
+                    }],
+                    span: Span::unknown(),
+                }),
+            },
+            Example {
+                description: "Skip the first value",
+                example: "echo [2 4 6 8] | skip",
+                result: Some(Value::List {
+                    vals: vec![Value::from(4), Value::from(6), Value::from(8)],
+                    span: Span::unknown(),
+                }),
+            },
+        ]
+    }
+
+    fn run(
+        &self,
+        engine_state: &EngineState,
+        stack: &mut Stack,
+        call: &Call,
+        input: PipelineData,
+    ) -> Result<PipelineData, ShellError> {
+        let n: Option<Value> = call.opt(engine_state, stack, 0)?;
+
+        let n: usize = match n {
+            Some(Value::Int { val, span }) => val.try_into().map_err(|err| {
+                ShellError::UnsupportedInput(
+                    format!("Could not convert {} to unsigned integer: {}", val, err),
+                    span,
+                )
+            })?,
+            Some(_) => return Err(ShellError::InternalError("Expected integer".into())),
+            None => 1,
+        };
+
+        let ctrlc = engine_state.ctrlc.clone();
+
+        Ok(input.into_iter().skip(n).into_pipeline_data(ctrlc))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_examples() {
+        use crate::test_examples;
+
+        test_examples(Skip {})
+    }
+}
diff --git a/crates/nu-command/src/filters/skip/mod.rs b/crates/nu-command/src/filters/skip/mod.rs
new file mode 100644
index 0000000..164cf50
--- /dev/null
+++ b/crates/nu-command/src/filters/skip/mod.rs
@@ -0,0 +1,7 @@
+mod command;
+mod until;
+mod while_;
+
+pub use command::Skip;
+pub use until::SkipUntil;
+pub use while_::SkipWhile;
diff --git a/crates/nu-command/src/filters/skip/until.rs b/crates/nu-command/src/filters/skip/until.rs
new file mode 100644
index 0000000..5c09adc
--- /dev/null
+++ b/crates/nu-command/src/filters/skip/until.rs
@@ -0,0 +1,89 @@
+use nu_engine::eval_block;
+use nu_protocol::{
+    ast::Call,
+    engine::{Command, EngineState, Stack},
+    Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span,
+    SyntaxShape, Value,
+};
+
+#[derive(Clone)]
+pub struct SkipUntil;
+
+impl Command for SkipUntil {
+    fn name(&self) -> &str {
+        "skip until"
+    }
+
+    fn signature(&self) -> Signature {
+        Signature::build(self.name())
+            .required(
+                "predicate",
+                SyntaxShape::RowCondition,
+                "the predicate that skipped element must not match",
+            )
+            .category(Category::Filters)
+    }
+
+    fn usage(&self) -> &str {
+        "Skip elements of the input until a predicate is true."
+    }
+
+    fn examples(&self) -> Vec<Example> {
+        vec![Example {
+            description: "Skip until the element is positive",
+            example: "echo [-2 0 2 -1] | skip until $it > 0",
+            result: Some(Value::List {
+                vals: vec![Value::from(2), Value::from(-1)],
+                span: Span::unknown(),
+            }),
+        }]
+    }
+
+    fn run(
+        &self,
+        engine_state: &EngineState,
+        stack: &mut Stack,
+        call: &Call,
+        input: PipelineData,
+    ) -> Result<PipelineData, ShellError> {
+        let predicate = &call.positional[0];
+        let block_id = predicate
+            .as_row_condition_block()
+            .ok_or_else(|| ShellError::InternalError("Expected row condition".to_owned()))?;
+
+        let span = call.head;
+
+        let block = engine_state.get_block(block_id).clone();
+        let var_id = block.signature.get_positional(0).and_then(|arg| arg.var_id);
+        let mut stack = stack.collect_captures(&block.captures);
+
+        let ctrlc = engine_state.ctrlc.clone();
+        let engine_state = engine_state.clone();
+
+        Ok(input
+            .into_iter()
+            .skip_while(move |value| {
+                if let Some(var_id) = var_id {
+                    stack.add_var(var_id, value.clone());
+                }
+
+                !eval_block(&engine_state, &mut stack, &block, PipelineData::new(span))
+                    .map_or(false, |pipeline_data| {
+                        pipeline_data.into_value(span).is_true()
+                    })
+            })
+            .into_pipeline_data(ctrlc))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_examples() {
+        use crate::test_examples;
+
+        test_examples(SkipUntil)
+    }
+}
diff --git a/crates/nu-command/src/filters/skip/while_.rs b/crates/nu-command/src/filters/skip/while_.rs
new file mode 100644
index 0000000..cc125d4
--- /dev/null
+++ b/crates/nu-command/src/filters/skip/while_.rs
@@ -0,0 +1,89 @@
+use nu_engine::eval_block;
+use nu_protocol::{
+    ast::Call,
+    engine::{Command, EngineState, Stack},
+    Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span,
+    SyntaxShape, Value,
+};
+
+#[derive(Clone)]
+pub struct SkipWhile;
+
+impl Command for SkipWhile {
+    fn name(&self) -> &str {
+        "skip while"
+    }
+
+    fn signature(&self) -> Signature {
+        Signature::build(self.name())
+            .required(
+                "predicate",
+                SyntaxShape::RowCondition,
+                "the predicate that skipped element must match",
+            )
+            .category(Category::Filters)
+    }
+
+    fn usage(&self) -> &str {
+        "Skip elements of the input while a predicate is true."
+    }
+
+    fn examples(&self) -> Vec<Example> {
+        vec![Example {
+            description: "Skip while the element is negative",
+            example: "echo [-2 0 2 -1] | skip while $it < 0",
+            result: Some(Value::List {
+                vals: vec![Value::from(0), Value::from(2), Value::from(-1)],
+                span: Span::unknown(),
+            }),
+        }]
+    }
+
+    fn run(
+        &self,
+        engine_state: &EngineState,
+        stack: &mut Stack,
+        call: &Call,
+        input: PipelineData,
+    ) -> Result<PipelineData, ShellError> {
+        let predicate = &call.positional[0];
+        let block_id = predicate
+            .as_row_condition_block()
+            .ok_or_else(|| ShellError::InternalError("Expected row condition".to_owned()))?;
+
+        let span = call.head;
+
+        let block = engine_state.get_block(block_id).clone();
+        let var_id = block.signature.get_positional(0).and_then(|arg| arg.var_id);
+        let mut stack = stack.collect_captures(&block.captures);
+
+        let ctrlc = engine_state.ctrlc.clone();
+        let engine_state = engine_state.clone();
+
+        Ok(input
+            .into_iter()
+            .skip_while(move |value| {
+                if let Some(var_id) = var_id {
+                    stack.add_var(var_id, value.clone());
+                }
+
+                eval_block(&engine_state, &mut stack, &block, PipelineData::new(span))
+                    .map_or(false, |pipeline_data| {
+                        pipeline_data.into_value(span).is_true()
+                    })
+            })
+            .into_pipeline_data(ctrlc))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_examples() {
+        use crate::test_examples;
+
+        test_examples(SkipWhile)
+    }
+}
diff --git a/crates/nu-command/src/filters/where_.rs b/crates/nu-command/src/filters/where_.rs
index e556b8f..7adc547 100644
--- a/crates/nu-command/src/filters/where_.rs
+++ b/crates/nu-command/src/filters/where_.rs
@@ -1,5 +1,5 @@
-use nu_engine::eval_expression;
-use nu_protocol::ast::{Call, Expr, Expression};
+use nu_engine::eval_block;
+use nu_protocol::ast::Call;
 use nu_protocol::engine::{Command, EngineState, Stack};
 use nu_protocol::{Category, PipelineData, ShellError, Signature, SyntaxShape};
 
@@ -28,30 +28,29 @@ impl Command for Where {
         call: &Call,
         input: PipelineData,
     ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
-        let cond = call.positional[0].clone();
+        let span = call.head;
+        let cond = &call.positional[0];
+        let block_id = cond
+            .as_row_condition_block()
+            .ok_or_else(|| ShellError::InternalError("Expected row condition".to_owned()))?;
 
         let ctrlc = engine_state.ctrlc.clone();
         let engine_state = engine_state.clone();
 
-        // FIXME: expensive
-        let mut stack = stack.clone();
-
-        let (var_id, cond) = match cond {
-            Expression {
-                expr: Expr::RowCondition(var_id, expr),
-                ..
-            } => (var_id, expr),
-            _ => return Err(ShellError::InternalError("Expected row condition".into())),
-        };
+        let block = engine_state.get_block(block_id).clone();
+        let mut stack = stack.collect_captures(&block.captures);
 
         input.filter(
             move |value| {
-                stack.add_var(var_id, value.clone());
-
-                let result = eval_expression(&engine_state, &mut stack, &cond);
+                if let Some(var) = block.signature.get_positional(0) {
+                    if let Some(var_id) = &var.var_id {
+                        stack.add_var(*var_id, value.clone());
+                    }
+                }
+                let result = eval_block(&engine_state, &mut stack, &block, PipelineData::new(span));
 
                 match result {
-                    Ok(result) => result.is_true(),
+                    Ok(result) => result.into_value(span).is_true(),
                     _ => false,
                 }
             },
diff --git a/crates/nu-command/src/formats/from/eml.rs b/crates/nu-command/src/formats/from/eml.rs
index 629db00..7a4d1a9 100644
--- a/crates/nu-command/src/formats/from/eml.rs
+++ b/crates/nu-command/src/formats/from/eml.rs
@@ -230,21 +230,10 @@ fn from_eml(
         );
     }
 
-    let (cols, vals) = collected
-        .into_iter()
-        .fold((vec![], vec![]), |mut acc, (k, v)| {
-            acc.0.push(k);
-            acc.1.push(v);
-            acc
-        });
-
-    let record = Value::Record {
-        cols,
-        vals,
+    Ok(PipelineData::Value(Value::from(Spanned {
+        item: collected,
         span: head,
-    };
-
-    Ok(PipelineData::Value(record))
+    })))
 }
 
 #[cfg(test)]
diff --git a/crates/nu-command/src/formats/from/ics.rs b/crates/nu-command/src/formats/from/ics.rs
new file mode 100644
index 0000000..55c4f46
--- /dev/null
+++ b/crates/nu-command/src/formats/from/ics.rs
@@ -0,0 +1,327 @@
+extern crate ical;
+use ical::parser::ical::component::*;
+use ical::property::Property;
+use indexmap::map::IndexMap;
+use nu_protocol::ast::Call;
+use nu_protocol::engine::{Command, EngineState, Stack};
+use nu_protocol::{
+    Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span,
+    Spanned, Value,
+};
+use std::io::BufReader;
+
+#[derive(Clone)]
+pub struct FromIcs;
+
+impl Command for FromIcs {
+    fn name(&self) -> &str {
+        "from ics"
+    }
+
+    fn signature(&self) -> Signature {
+        Signature::build("from ics").category(Category::Formats)
+    }
+
+    fn usage(&self) -> &str {
+        "Parse text as .ics and create table."
+    }
+
+    fn run(
+        &self,
+        _engine_state: &EngineState,
+        stack: &mut Stack,
+        call: &Call,
+        input: PipelineData,
+    ) -> Result<nu_protocol::PipelineData, ShellError> {
+        let head = call.head;
+        let config = stack.get_config()?;
+        from_ics(input, head, &config)
+    }
+
+    fn examples(&self) -> Vec<Example> {
+        vec![Example {
+            example: "'BEGIN:VCALENDAR
+END:VCALENDAR' | from ics",
+            description: "Converts ics formatted string to table",
+            result: Some(Value::List {
+                vals: vec![Value::Record {
+                    cols: vec![
+                        "properties".to_string(),
+                        "events".to_string(),
+                        "alarms".to_string(),
+                        "to-Dos".to_string(),
+                        "journals".to_string(),
+                        "free-busys".to_string(),
+                        "timezones".to_string(),
+                    ],
+                    vals: vec![
+                        Value::List {
+                            vals: vec![],
+                            span: Span::unknown(),
+                        },
+                        Value::List {
+                            vals: vec![],
+                            span: Span::unknown(),
+                        },
+                        Value::List {
+                            vals: vec![],
+                            span: Span::unknown(),
+                        },
+                        Value::List {
+                            vals: vec![],
+                            span: Span::unknown(),
+                        },
+                        Value::List {
+                            vals: vec![],
+                            span: Span::unknown(),
+                        },
+                        Value::List {
+                            vals: vec![],
+                            span: Span::unknown(),
+                        },
+                        Value::List {
+                            vals: vec![],
+                            span: Span::unknown(),
+                        },
+                    ],
+                    span: Span::unknown(),
+                }],
+                span: Span::unknown(),
+            }),
+        }]
+    }
+}
+
+fn from_ics(input: PipelineData, head: Span, config: &Config) -> Result<PipelineData, ShellError> {
+    let input_string = input.collect_string("", config);
+    let input_bytes = input_string.as_bytes();
+    let buf_reader = BufReader::new(input_bytes);
+    let parser = ical::IcalParser::new(buf_reader);
+
+    let mut output = vec![];
+
+    for calendar in parser {
+        match calendar {
+            Ok(c) => output.push(calendar_to_value(c, head)),
+            Err(_) => output.push(Value::Error {
+                error: ShellError::UnsupportedInput(
+                    "input cannot be parsed as .ics".to_string(),
+                    head,
+                ),
+            }),
+        }
+    }
+    Ok(Value::List {
+        vals: output,
+        span: head,
+    }
+    .into_pipeline_data())
+}
+
+fn calendar_to_value(calendar: IcalCalendar, span: Span) -> Value {
+    let mut row = IndexMap::new();
+
+    row.insert(
+        "properties".to_string(),
+        properties_to_value(calendar.properties, span),
+    );
+    row.insert("events".to_string(), events_to_value(calendar.events, span));
+    row.insert("alarms".to_string(), alarms_to_value(calendar.alarms, span));
+    row.insert("to-Dos".to_string(), todos_to_value(calendar.todos, span));
+    row.insert(
+        "journals".to_string(),
+        journals_to_value(calendar.journals, span),
+    );
+    row.insert(
+        "free-busys".to_string(),
+        free_busys_to_value(calendar.free_busys, span),
+    );
+    row.insert(
+        "timezones".to_string(),
+        timezones_to_value(calendar.timezones, span),
+    );
+
+    Value::from(Spanned { item: row, span })
+}
+
+fn events_to_value(events: Vec<IcalEvent>, span: Span) -> Value {
+    Value::List {
+        vals: events
+            .into_iter()
+            .map(|event| {
+                let mut row = IndexMap::new();
+                row.insert(
+                    "properties".to_string(),
+                    properties_to_value(event.properties, span),
+                );
+                row.insert("alarms".to_string(), alarms_to_value(event.alarms, span));
+                Value::from(Spanned { item: row, span })
+            })
+            .collect::<Vec<Value>>(),
+        span,
+    }
+}
+
+fn alarms_to_value(alarms: Vec<IcalAlarm>, span: Span) -> Value {
+    Value::List {
+        vals: alarms
+            .into_iter()
+            .map(|alarm| {
+                let mut row = IndexMap::new();
+                row.insert(
+                    "properties".to_string(),
+                    properties_to_value(alarm.properties, span),
+                );
+                Value::from(Spanned { item: row, span })
+            })
+            .collect::<Vec<Value>>(),
+        span,
+    }
+}
+
+fn todos_to_value(todos: Vec<IcalTodo>, span: Span) -> Value {
+    Value::List {
+        vals: todos
+            .into_iter()
+            .map(|todo| {
+                let mut row = IndexMap::new();
+                row.insert(
+                    "properties".to_string(),
+                    properties_to_value(todo.properties, span),
+                );
+                row.insert("alarms".to_string(), alarms_to_value(todo.alarms, span));
+                Value::from(Spanned { item: row, span })
+            })
+            .collect::<Vec<Value>>(),
+        span,
+    }
+}
+
+fn journals_to_value(journals: Vec<IcalJournal>, span: Span) -> Value {
+    Value::List {
+        vals: journals
+            .into_iter()
+            .map(|journal| {
+                let mut row = IndexMap::new();
+                row.insert(
+                    "properties".to_string(),
+                    properties_to_value(journal.properties, span),
+                );
+                Value::from(Spanned { item: row, span })
+            })
+            .collect::<Vec<Value>>(),
+        span,
+    }
+}
+
+fn free_busys_to_value(free_busys: Vec<IcalFreeBusy>, span: Span) -> Value {
+    Value::List {
+        vals: free_busys
+            .into_iter()
+            .map(|free_busy| {
+                let mut row = IndexMap::new();
+                row.insert(
+                    "properties".to_string(),
+                    properties_to_value(free_busy.properties, span),
+                );
+                Value::from(Spanned { item: row, span })
+            })
+            .collect::<Vec<Value>>(),
+        span,
+    }
+}
+
+fn timezones_to_value(timezones: Vec<IcalTimeZone>, span: Span) -> Value {
+    Value::List {
+        vals: timezones
+            .into_iter()
+            .map(|timezone| {
+                let mut row = IndexMap::new();
+                row.insert(
+                    "properties".to_string(),
+                    properties_to_value(timezone.properties, span),
+                );
+                row.insert(
+                    "transitions".to_string(),
+                    timezone_transitions_to_value(timezone.transitions, span),
+                );
+                Value::from(Spanned { item: row, span })
+            })
+            .collect::<Vec<Value>>(),
+        span,
+    }
+}
+
+fn timezone_transitions_to_value(transitions: Vec<IcalTimeZoneTransition>, span: Span) -> Value {
+    Value::List {
+        vals: transitions
+            .into_iter()
+            .map(|transition| {
+                let mut row = IndexMap::new();
+                row.insert(
+                    "properties".to_string(),
+                    properties_to_value(transition.properties, span),
+                );
+                Value::from(Spanned { item: row, span })
+            })
+            .collect::<Vec<Value>>(),
+        span,
+    }
+}
+
+fn properties_to_value(properties: Vec<Property>, span: Span) -> Value {
+    Value::List {
+        vals: properties
+            .into_iter()
+            .map(|prop| {
+                let mut row = IndexMap::new();
+
+                let name = Value::String {
+                    val: prop.name,
+                    span,
+                };
+                let value = match prop.value {
+                    Some(val) => Value::String { val, span },
+                    None => Value::nothing(span),
+                };
+                let params = match prop.params {
+                    Some(param_list) => params_to_value(param_list, span),
+                    None => Value::nothing(span),
+                };
+
+                row.insert("name".to_string(), name);
+                row.insert("value".to_string(), value);
+                row.insert("params".to_string(), params);
+                Value::from(Spanned { item: row, span })
+            })
+            .collect::<Vec<Value>>(),
+        span,
+    }
+}
+
+fn params_to_value(params: Vec<(String, Vec<String>)>, span: Span) -> Value {
+    let mut row = IndexMap::new();
+
+    for (param_name, param_values) in params {
+        let values: Vec<Value> = param_values
+            .into_iter()
+            .map(|val| Value::string(val, span))
+            .collect();
+        let values = Value::List { vals: values, span };
+        row.insert(param_name, values);
+    }
+
+    Value::from(Spanned { item: row, span })
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_examples() {
+        use crate::test_examples;
+
+        test_examples(FromIcs {})
+    }
+}
diff --git a/crates/nu-command/src/formats/from/ini.rs b/crates/nu-command/src/formats/from/ini.rs
new file mode 100644
index 0000000..40666dc
--- /dev/null
+++ b/crates/nu-command/src/formats/from/ini.rs
@@ -0,0 +1,109 @@
+use indexmap::map::IndexMap;
+use nu_protocol::ast::Call;
+use nu_protocol::engine::{Command, EngineState, Stack};
+use nu_protocol::{
+    Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Value,
+};
+
+#[derive(Clone)]
+pub struct FromIni;
+
+impl Command for FromIni {
+    fn name(&self) -> &str {
+        "from ini"
+    }
+
+    fn signature(&self) -> Signature {
+        Signature::build("from ini").category(Category::Formats)
+    }
+
+    fn usage(&self) -> &str {
+        "Parse text as .ini and create table"
+    }
+
+    fn examples(&self) -> Vec<Example> {
+        vec![Example {
+            example: "'[foo]
+a=1
+b=2' | from ini",
+            description: "Converts ini formatted string to table",
+            result: Some(Value::Record {
+                cols: vec!["foo".to_string()],
+                vals: vec![Value::Record {
+                    cols: vec!["a".to_string(), "b".to_string()],
+                    vals: vec![
+                        Value::String {
+                            val: "1".to_string(),
+                            span: Span::unknown(),
+                        },
+                        Value::String {
+                            val: "2".to_string(),
+                            span: Span::unknown(),
+                        },
+                    ],
+                    span: Span::unknown(),
+                }],
+                span: Span::unknown(),
+            }),
+        }]
+    }
+
+    fn run(
+        &self,
+        _engine_state: &EngineState,
+        stack: &mut Stack,
+        call: &Call,
+        input: PipelineData,
+    ) -> Result<nu_protocol::PipelineData, ShellError> {
+        let head = call.head;
+        let config = stack.get_config()?;
+        from_ini(input, head, &config)
+    }
+}
+
+pub fn from_ini_string_to_value(s: String, span: Span) -> Result<Value, ShellError> {
+    let v: Result<IndexMap<String, IndexMap<String, String>>, serde_ini::de::Error> =
+        serde_ini::from_str(&s);
+    match v {
+        Ok(index_map) => {
+            let (cols, vals) = index_map
+                .into_iter()
+                .fold((vec![], vec![]), |mut acc, (k, v)| {
+                    let (cols, vals) = v.into_iter().fold((vec![], vec![]), |mut acc, (k, v)| {
+                        acc.0.push(k);
+                        acc.1.push(Value::String { val: v, span });
+                        acc
+                    });
+                    acc.0.push(k);
+                    acc.1.push(Value::Record { cols, vals, span });
+                    acc
+                });
+            Ok(Value::Record { cols, vals, span })
+        }
+        Err(err) => Err(ShellError::UnsupportedInput(
+            format!("Could not load ini: {}", err),
+            span,
+        )),
+    }
+}
+
+fn from_ini(input: PipelineData, head: Span, config: &Config) -> Result<PipelineData, ShellError> {
+    let concat_string = input.collect_string("", config);
+
+    match from_ini_string_to_value(concat_string, head) {
+        Ok(x) => Ok(x.into_pipeline_data()),
+        Err(other) => Err(other),
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_examples() {
+        use crate::test_examples;
+
+        test_examples(FromIni {})
+    }
+}
diff --git a/crates/nu-command/src/formats/from/mod.rs b/crates/nu-command/src/formats/from/mod.rs
index 9fab220..54f007d 100644
--- a/crates/nu-command/src/formats/from/mod.rs
+++ b/crates/nu-command/src/formats/from/mod.rs
@@ -2,22 +2,32 @@ mod command;
 mod csv;
 mod delimited;
 mod eml;
+mod ics;
+mod ini;
 mod json;
 mod ods;
+mod ssv;
 mod toml;
 mod tsv;
 mod url;
+mod vcf;
 mod xlsx;
+mod xml;
 mod yaml;
 
 pub use self::csv::FromCsv;
 pub use self::toml::FromToml;
 pub use command::From;
 pub use eml::FromEml;
+pub use ics::FromIcs;
+pub use ini::FromIni;
 pub use json::FromJson;
 pub use ods::FromOds;
+pub use ssv::FromSsv;
 pub use tsv::FromTsv;
 pub use url::FromUrl;
+pub use vcf::FromVcf;
 pub use xlsx::FromXlsx;
+pub use xml::FromXml;
 pub use yaml::FromYaml;
 pub use yaml::FromYml;
diff --git a/crates/nu-command/src/formats/from/ssv.rs b/crates/nu-command/src/formats/from/ssv.rs
new file mode 100644
index 0000000..f213fdb
--- /dev/null
+++ b/crates/nu-command/src/formats/from/ssv.rs
@@ -0,0 +1,500 @@
+use indexmap::map::IndexMap;
+use nu_engine::CallExt;
+use nu_protocol::ast::Call;
+use nu_protocol::engine::{Command, EngineState, Stack};
+use nu_protocol::{
+    Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned,
+    SyntaxShape, Value,
+};
+
+#[derive(Clone)]
+pub struct FromSsv;
+
+const DEFAULT_MINIMUM_SPACES: usize = 2;
+
+impl Command for FromSsv {
+    fn name(&self) -> &str {
+        "from ssv"
+    }
+
+    fn signature(&self) -> Signature {
+        Signature::build("from ssv")
+            .switch(
+                "noheaders",
+                "don't treat the first row as column names",
+                Some('n'),
+            )
+            .switch("aligned-columns", "assume columns are aligned", Some('a'))
+            .named(
+                "minimum-spaces",
+                SyntaxShape::Int,
+                "the minimum spaces to separate columns",
+                Some('m'),
+            )
+            .category(Category::Formats)
+    }
+
+    fn usage(&self) -> &str {
+        "Parse text as space-separated values and create a table. The default minimum number of spaces counted as a separator is 2."
+    }
+
+    fn examples(&self) -> Vec<Example> {
+        vec![Example {
+            example: r#"'FOO   BAR
+1   2' | from ssv"#,
+            description: "Converts ssv formatted string to table",
+            result: Some(Value::List { vals: vec![Value::Record { cols: vec!["FOO".to_string(), "BAR".to_string()], vals: vec![Value::String { val: "1".to_string(), span: Span::unknown() }, Value::String { val: "2".to_string(), span: Span::unknown() }], span: Span::unknown() }], span: Span::unknown() }),
+        }, Example {
+            example: r#"'FOO   BAR
+1   2' | from ssv -n"#,
+            description: "Converts ssv formatted string to table but not treating the first row as column names",
+            result: Some(
+                Value::List { vals: vec![Value::Record { cols: vec!["Column1".to_string(), "Column2".to_string()], vals: vec![Value::String { val: "FOO".to_string(), span: Span::unknown() }, Value::String { val: "BAR".to_string(), span: Span::unknown() }], span: Span::unknown() }, Value::Record { cols: vec!["Column1".to_string(), "Column2".to_string()], vals: vec![Value::String { val: "1".to_string(), span: Span::unknown() }, Value::String { val: "2".to_string(), span: Span::unknown() }], span: Span::unknown() }], span: Span::unknown() }),
+        }]
+    }
+
+    fn run(
+        &self,
+        engine_state: &EngineState,
+        stack: &mut Stack,
+        call: &Call,
+        input: PipelineData,
+    ) -> Result<nu_protocol::PipelineData, ShellError> {
+        from_ssv(engine_state, stack, call, input)
+    }
+}
+
+enum HeaderOptions<'a> {
+    WithHeaders(&'a str),
+    WithoutHeaders,
+}
+
+fn parse_aligned_columns<'a>(
+    lines: impl Iterator<Item = &'a str>,
+    headers: HeaderOptions,
+    separator: &str,
+) -> Vec<Vec<(String, String)>> {
+    fn construct<'a>(
+        lines: impl Iterator<Item = &'a str>,
+        headers: Vec<(String, usize)>,
+    ) -> Vec<Vec<(String, String)>> {
+        lines
+            .map(|l| {
+                headers
+                    .iter()
+                    .enumerate()
+                    .map(|(i, (header_name, start_position))| {
+                        let val = match headers.get(i + 1) {
+                            Some((_, end)) => {
+                                if *end < l.len() {
+                                    l.get(*start_position..*end)
+                                } else {
+                                    l.get(*start_position..)
+                                }
+                            }
+                            None => l.get(*start_position..),
+                        }
+                        .unwrap_or("")
+                        .trim()
+                        .into();
+                        (header_name.clone(), val)
+                    })
+                    .collect()
+            })
+            .collect()
+    }
+
+    let find_indices = |line: &str| {
+        let values = line
+            .split(&separator)
+            .map(str::trim)
+            .filter(|s| !s.is_empty());
+        values
+            .fold(
+                (0, vec![]),
+                |(current_pos, mut indices), value| match line[current_pos..].find(value) {
+                    None => (current_pos, indices),
+                    Some(index) => {
+                        let absolute_index = current_pos + index;
+                        indices.push(absolute_index);
+                        (absolute_index + value.len(), indices)
+                    }
+                },
+            )
+            .1
+    };
+
+    let parse_with_headers = |lines, headers_raw: &str| {
+        let indices = find_indices(headers_raw);
+        let headers = headers_raw
+            .split(&separator)
+            .map(str::trim)
+            .filter(|s| !s.is_empty())
+            .map(String::from)
+            .zip(indices);
+
+        let columns = headers.collect::<Vec<(String, usize)>>();
+
+        construct(lines, columns)
+    };
+
+    let parse_without_headers = |ls: Vec<&str>| {
+        let mut indices = ls
+            .iter()
+            .flat_map(|s| find_indices(*s))
+            .collect::<Vec<usize>>();
+
+        indices.sort_unstable();
+        indices.dedup();
+
+        let headers: Vec<(String, usize)> = indices
+            .iter()
+            .enumerate()
+            .map(|(i, position)| (format!("Column{}", i + 1), *position))
+            .collect();
+
+        construct(ls.iter().map(|s| s.to_owned()), headers)
+    };
+
+    match headers {
+        HeaderOptions::WithHeaders(headers_raw) => parse_with_headers(lines, headers_raw),
+        HeaderOptions::WithoutHeaders => parse_without_headers(lines.collect()),
+    }
+}
+
+fn parse_separated_columns<'a>(
+    lines: impl Iterator<Item = &'a str>,
+    headers: HeaderOptions,
+    separator: &str,
+) -> Vec<Vec<(String, String)>> {
+    fn collect<'a>(
+        headers: Vec<String>,
+        rows: impl Iterator<Item = &'a str>,
+        separator: &str,
+    ) -> Vec<Vec<(String, String)>> {
+        rows.map(|r| {
+            headers
+                .iter()
+                .zip(r.split(separator).map(str::trim).filter(|s| !s.is_empty()))
+                .map(|(a, b)| (a.to_owned(), b.to_owned()))
+                .collect()
+        })
+        .collect()
+    }
+
+    let parse_with_headers = |lines, headers_raw: &str| {
+        let headers = headers_raw
+            .split(&separator)
+            .map(str::trim)
+            .map(str::to_owned)
+            .filter(|s| !s.is_empty())
+            .collect();
+        collect(headers, lines, separator)
+    };
+
+    let parse_without_headers = |ls: Vec<&str>| {
+        let num_columns = ls.iter().map(|r| r.len()).max().unwrap_or(0);
+
+        let headers = (1..=num_columns)
+            .map(|i| format!("Column{}", i))
+            .collect::<Vec<String>>();
+        collect(headers, ls.into_iter(), separator)
+    };
+
+    match headers {
+        HeaderOptions::WithHeaders(headers_raw) => parse_with_headers(lines, headers_raw),
+        HeaderOptions::WithoutHeaders => parse_without_headers(lines.collect()),
+    }
+}
+
+fn string_to_table(
+    s: &str,
+    noheaders: bool,
+    aligned_columns: bool,
+    split_at: usize,
+) -> Vec<Vec<(String, String)>> {
+    let mut lines = s.lines().filter(|l| !l.trim().is_empty());
+    let separator = " ".repeat(std::cmp::max(split_at, 1));
+
+    let (ls, header_options) = if noheaders {
+        (lines, HeaderOptions::WithoutHeaders)
+    } else {
+        match lines.next() {
+            Some(header) => (lines, HeaderOptions::WithHeaders(header)),
+            None => return vec![],
+        }
+    };
+
+    let f = if aligned_columns {
+        parse_aligned_columns
+    } else {
+        parse_separated_columns
+    };
+
+    f(ls, header_options, &separator)
+}
+
+fn from_ssv_string_to_value(
+    s: &str,
+    noheaders: bool,
+    aligned_columns: bool,
+    split_at: usize,
+    span: Span,
+) -> Value {
+    let rows = string_to_table(s, noheaders, aligned_columns, split_at)
+        .iter()
+        .map(|row| {
+            let mut dict = IndexMap::new();
+            for (col, entry) in row {
+                dict.insert(
+                    col.to_string(),
+                    Value::String {
+                        val: entry.to_string(),
+                        span,
+                    },
+                );
+            }
+            Value::from(Spanned { item: dict, span })
+        })
+        .collect();
+
+    Value::List { vals: rows, span }
+}
+
+fn from_ssv(
+    engine_state: &EngineState,
+    stack: &mut Stack,
+    call: &Call,
+    input: PipelineData,
+) -> Result<PipelineData, ShellError> {
+    let config = stack.get_config()?;
+    let name = call.head;
+
+    let noheaders = call.has_flag("noheaders");
+    let aligned_columns = call.has_flag("aligned-columns");
+    let minimum_spaces: Option<Spanned<usize>> =
+        call.get_flag(engine_state, stack, "minimum-spaces")?;
+
+    let concat_string = input.collect_string("", &config);
+    let split_at = match minimum_spaces {
+        Some(number) => number.item,
+        None => DEFAULT_MINIMUM_SPACES,
+    };
+
+    Ok(
+        from_ssv_string_to_value(&concat_string, noheaders, aligned_columns, split_at, name)
+            .into_pipeline_data(),
+    )
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    fn owned(x: &str, y: &str) -> (String, String) {
+        (String::from(x), String::from(y))
+    }
+
+    #[test]
+    fn it_trims_empty_and_whitespace_only_lines() {
+        let input = r#"
+
+            a       b
+
+            1       2
+
+            3       4
+        "#;
+        let result = string_to_table(input, false, true, 1);
+        assert_eq!(
+            result,
+            vec![
+                vec![owned("a", "1"), owned("b", "2")],
+                vec![owned("a", "3"), owned("b", "4")]
+            ]
+        );
+    }
+
+    #[test]
+    fn it_deals_with_single_column_input() {
+        let input = r#"
+            a
+            1
+            2
+        "#;
+        let result = string_to_table(input, false, true, 1);
+        assert_eq!(result, vec![vec![owned("a", "1")], vec![owned("a", "2")]]);
+    }
+
+    #[test]
+    fn it_uses_first_row_as_data_when_noheaders() {
+        let input = r#"
+            a b
+            1 2
+            3 4
+        "#;
+        let result = string_to_table(input, true, true, 1);
+        assert_eq!(
+            result,
+            vec![
+                vec![owned("Column1", "a"), owned("Column2", "b")],
+                vec![owned("Column1", "1"), owned("Column2", "2")],
+                vec![owned("Column1", "3"), owned("Column2", "4")]
+            ]
+        );
+    }
+
+    #[test]
+    fn it_allows_a_predefined_number_of_spaces() {
+        let input = r#"
+            column a   column b
+            entry 1    entry number  2
+            3          four
+        "#;
+
+        let result = string_to_table(input, false, true, 3);
+        assert_eq!(
+            result,
+            vec![
+                vec![
+                    owned("column a", "entry 1"),
+                    owned("column b", "entry number  2")
+                ],
+                vec![owned("column a", "3"), owned("column b", "four")]
+            ]
+        );
+    }
+
+    #[test]
+    fn it_trims_remaining_separator_space() {
+        let input = r#"
+            colA   colB     colC
+            val1   val2     val3
+        "#;
+
+        let trimmed = |s: &str| s.trim() == s;
+
+        let result = string_to_table(input, false, true, 2);
+        assert!(result
+            .iter()
+            .all(|row| row.iter().all(|(a, b)| trimmed(a) && trimmed(b))));
+    }
+
+    #[test]
+    fn it_keeps_empty_columns() {
+        let input = r#"
+            colA   col B     col C
+                   val2      val3
+            val4   val 5     val 6
+            val7             val8
+        "#;
+
+        let result = string_to_table(input, false, true, 2);
+        assert_eq!(
+            result,
+            vec![
+                vec![
+                    owned("colA", ""),
+                    owned("col B", "val2"),
+                    owned("col C", "val3")
+                ],
+                vec![
+                    owned("colA", "val4"),
+                    owned("col B", "val 5"),
+                    owned("col C", "val 6")
+                ],
+                vec![
+                    owned("colA", "val7"),
+                    owned("col B", ""),
+                    owned("col C", "val8")
+                ],
+            ]
+        );
+    }
+
+    #[test]
+    fn it_can_produce_an_empty_stream_for_header_only_input() {
+        let input = "colA   col B";
+
+        let result = string_to_table(input, false, true, 2);
+        let expected: Vec<Vec<(String, String)>> = vec![];
+        assert_eq!(expected, result);
+    }
+
+    #[test]
+    fn it_uses_the_full_final_column() {
+        let input = r#"
+            colA   col B
+            val1   val2   trailing value that should be included
+        "#;
+
+        let result = string_to_table(input, false, true, 2);
+        assert_eq!(
+            result,
+            vec![vec![
+                owned("colA", "val1"),
+                owned("col B", "val2   trailing value that should be included"),
+            ]]
+        );
+    }
+
+    #[test]
+    fn it_handles_empty_values_when_noheaders_and_aligned_columns() {
+        let input = r#"
+            a multi-word value  b           d
+            1                        3-3    4
+                                                       last
+        "#;
+
+        let result = string_to_table(input, true, true, 2);
+        assert_eq!(
+            result,
+            vec![
+                vec![
+                    owned("Column1", "a multi-word value"),
+                    owned("Column2", "b"),
+                    owned("Column3", ""),
+                    owned("Column4", "d"),
+                    owned("Column5", "")
+                ],
+                vec![
+                    owned("Column1", "1"),
+                    owned("Column2", ""),
+                    owned("Column3", "3-3"),
+                    owned("Column4", "4"),
+                    owned("Column5", "")
+                ],
+                vec![
+                    owned("Column1", ""),
+                    owned("Column2", ""),
+                    owned("Column3", ""),
+                    owned("Column4", ""),
+                    owned("Column5", "last")
+                ],
+            ]
+        );
+    }
+
+    #[test]
+    fn input_is_parsed_correctly_if_either_option_works() {
+        let input = r#"
+                docker-registry   docker-registry=default                   docker-registry=default   172.30.78.158   5000/TCP
+                kubernetes        component=apiserver,provider=kubernetes   <none>                    172.30.0.2      443/TCP
+                kubernetes-ro     component=apiserver,provider=kubernetes   <none>                    172.30.0.1      80/TCP
+            "#;
+
+        let aligned_columns_noheaders = string_to_table(input, true, true, 2);
+        let separator_noheaders = string_to_table(input, true, false, 2);
+        let aligned_columns_with_headers = string_to_table(input, false, true, 2);
+        let separator_with_headers = string_to_table(input, false, false, 2);
+        assert_eq!(aligned_columns_noheaders, separator_noheaders);
+        assert_eq!(aligned_columns_with_headers, separator_with_headers);
+    }
+
+    #[test]
+    fn test_examples() {
+        use crate::test_examples;
+
+        test_examples(FromSsv {})
+    }
+}
diff --git a/crates/nu-command/src/formats/from/vcf.rs b/crates/nu-command/src/formats/from/vcf.rs
new file mode 100644
index 0000000..cf60f7a
--- /dev/null
+++ b/crates/nu-command/src/formats/from/vcf.rs
@@ -0,0 +1,211 @@
+use ical::parser::vcard::component::*;
+use ical::property::Property;
+use indexmap::map::IndexMap;
+use nu_protocol::ast::Call;
+use nu_protocol::engine::{Command, EngineState, Stack};
+use nu_protocol::{
+    Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span,
+    Spanned, Value,
+};
+
+#[derive(Clone)]
+pub struct FromVcf;
+
+impl Command for FromVcf {
+    fn name(&self) -> &str {
+        "from vcf"
+    }
+
+    fn signature(&self) -> Signature {
+        Signature::build("from vcf").category(Category::Formats)
+    }
+
+    fn usage(&self) -> &str {
+        "Parse text as .vcf and create table."
+    }
+
+    fn run(
+        &self,
+        _engine_state: &EngineState,
+        stack: &mut Stack,
+        call: &Call,
+        input: PipelineData,
+    ) -> Result<nu_protocol::PipelineData, ShellError> {
+        let head = call.head;
+        let config = stack.get_config()?;
+        from_vcf(input, head, &config)
+    }
+
+    fn examples(&self) -> Vec<Example> {
+        vec![Example {
+            example: "'BEGIN:VCARD
+N:Foo
+FN:Bar
+EMAIL:foo@bar.com
+END:VCARD' | from vcf",
+            description: "Converts ics formatted string to table",
+            result: Some(Value::List {
+                vals: vec![Value::Record {
+                    cols: vec!["properties".to_string()],
+                    vals: vec![Value::List {
+                        vals: vec![
+                            Value::Record {
+                                cols: vec![
+                                    "name".to_string(),
+                                    "value".to_string(),
+                                    "params".to_string(),
+                                ],
+                                vals: vec![
+                                    Value::String {
+                                        val: "N".to_string(),
+                                        span: Span::unknown(),
+                                    },
+                                    Value::String {
+                                        val: "Foo".to_string(),
+                                        span: Span::unknown(),
+                                    },
+                                    Value::Nothing {
+                                        span: Span::unknown(),
+                                    },
+                                ],
+                                span: Span::unknown(),
+                            },
+                            Value::Record {
+                                cols: vec![
+                                    "name".to_string(),
+                                    "value".to_string(),
+                                    "params".to_string(),
+                                ],
+                                vals: vec![
+                                    Value::String {
+                                        val: "FN".to_string(),
+                                        span: Span::unknown(),
+                                    },
+                                    Value::String {
+                                        val: "Bar".to_string(),
+                                        span: Span::unknown(),
+                                    },
+                                    Value::Nothing {
+                                        span: Span::unknown(),
+                                    },
+                                ],
+                                span: Span::unknown(),
+                            },
+                            Value::Record {
+                                cols: vec![
+                                    "name".to_string(),
+                                    "value".to_string(),
+                                    "params".to_string(),
+                                ],
+                                vals: vec![
+                                    Value::String {
+                                        val: "EMAIL".to_string(),
+                                        span: Span::unknown(),
+                                    },
+                                    Value::String {
+                                        val: "foo@bar.com".to_string(),
+                                        span: Span::unknown(),
+                                    },
+                                    Value::Nothing {
+                                        span: Span::unknown(),
+                                    },
+                                ],
+                                span: Span::unknown(),
+                            },
+                        ],
+                        span: Span::unknown(),
+                    }],
+                    span: Span::unknown(),
+                }],
+                span: Span::unknown(),
+            }),
+        }]
+    }
+}
+
+fn from_vcf(input: PipelineData, head: Span, config: &Config) -> Result<PipelineData, ShellError> {
+    let input_string = input.collect_string("", config);
+    let input_bytes = input_string.as_bytes();
+    let cursor = std::io::Cursor::new(input_bytes);
+    let parser = ical::VcardParser::new(cursor);
+
+    let iter = parser.map(move |contact| match contact {
+        Ok(c) => contact_to_value(c, head),
+        Err(_) => Value::Error {
+            error: ShellError::UnsupportedInput("input cannot be parsed as .vcf".to_string(), head),
+        },
+    });
+
+    let collected: Vec<_> = iter.collect();
+    Ok(Value::List {
+        vals: collected,
+        span: head,
+    }
+    .into_pipeline_data())
+}
+
+fn contact_to_value(contact: VcardContact, span: Span) -> Value {
+    let mut row = IndexMap::new();
+    row.insert(
+        "properties".to_string(),
+        properties_to_value(contact.properties, span),
+    );
+    Value::from(Spanned { item: row, span })
+}
+
+fn properties_to_value(properties: Vec<Property>, span: Span) -> Value {
+    Value::List {
+        vals: properties
+            .into_iter()
+            .map(|prop| {
+                let mut row = IndexMap::new();
+
+                let name = Value::String {
+                    val: prop.name,
+                    span,
+                };
+                let value = match prop.value {
+                    Some(val) => Value::String { val, span },
+                    None => Value::Nothing { span },
+                };
+                let params = match prop.params {
+                    Some(param_list) => params_to_value(param_list, span),
+                    None => Value::Nothing { span },
+                };
+
+                row.insert("name".to_string(), name);
+                row.insert("value".to_string(), value);
+                row.insert("params".to_string(), params);
+                Value::from(Spanned { item: row, span })
+            })
+            .collect::<Vec<Value>>(),
+        span,
+    }
+}
+
+fn params_to_value(params: Vec<(String, Vec<String>)>, span: Span) -> Value {
+    let mut row = IndexMap::new();
+
+    for (param_name, param_values) in params {
+        let values: Vec<Value> = param_values
+            .into_iter()
+            .map(|val| Value::string(val, span))
+            .collect();
+        let values = Value::List { vals: values, span };
+        row.insert(param_name, values);
+    }
+
+    Value::from(Spanned { item: row, span })
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_examples() {
+        use crate::test_examples;
+
+        test_examples(FromVcf {})
+    }
+}
diff --git a/crates/nu-command/src/formats/from/xml.rs b/crates/nu-command/src/formats/from/xml.rs
new file mode 100644
index 0000000..8c579f6
--- /dev/null
+++ b/crates/nu-command/src/formats/from/xml.rs
@@ -0,0 +1,379 @@
+use indexmap::map::IndexMap;
+use nu_protocol::ast::Call;
+use nu_protocol::engine::{Command, EngineState, Stack};
+use nu_protocol::{
+    Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span,
+    Spanned, Value,
+};
+
+#[derive(Clone)]
+pub struct FromXml;
+
+impl Command for FromXml {
+    fn name(&self) -> &str {
+        "from xml"
+    }
+
+    fn signature(&self) -> Signature {
+        Signature::build("from xml").category(Category::Formats)
+    }
+
+    fn usage(&self) -> &str {
+        "Parse text as .xml and create table."
+    }
+
+    fn run(
+        &self,
+        _engine_state: &EngineState,
+        stack: &mut Stack,
+        call: &Call,
+        input: PipelineData,
+    ) -> Result<nu_protocol::PipelineData, ShellError> {
+        let head = call.head;
+        let config = stack.get_config()?;
+        from_xml(input, head, &config)
+    }
+
+    fn examples(&self) -> Vec<Example> {
+        vec![Example {
+            example: r#"'<?xml version="1.0" encoding="UTF-8"?>
+<note>
+  <remember>Event</remember>
+</note>' | from xml"#,
+            description: "Converts xml formatted string to table",
+            result: Some(Value::Record {
+                cols: vec!["note".to_string()],
+                vals: vec![Value::Record {
+                    cols: vec!["children".to_string(), "attributes".to_string()],
+                    vals: vec![
+                        Value::List {
+                            vals: vec![Value::Record {
+                                cols: vec!["remember".to_string()],
+                                vals: vec![Value::Record {
+                                    cols: vec!["children".to_string(), "attributes".to_string()],
+                                    vals: vec![
+                                        Value::List {
+                                            vals: vec![Value::String {
+                                                val: "Event".to_string(),
+                                                span: Span::unknown(),
+                                            }],
+                                            span: Span::unknown(),
+                                        },
+                                        Value::Record {
+                                            cols: vec![],
+                                            vals: vec![],
+                                            span: Span::unknown(),
+                                        },
+                                    ],
+                                    span: Span::unknown(),
+                                }],
+                                span: Span::unknown(),
+                            }],
+                            span: Span::unknown(),
+                        },
+                        Value::Record {
+                            cols: vec![],
+                            vals: vec![],
+                            span: Span::unknown(),
+                        },
+                    ],
+                    span: Span::unknown(),
+                }],
+                span: Span::unknown(),
+            }),
+        }]
+    }
+}
+
+fn from_attributes_to_value(attributes: &[roxmltree::Attribute], span: Span) -> Value {
+    let mut collected = IndexMap::new();
+    for a in attributes {
+        collected.insert(String::from(a.name()), Value::string(a.value(), span));
+    }
+
+    let (cols, vals) = collected
+        .into_iter()
+        .fold((vec![], vec![]), |mut acc, (k, v)| {
+            acc.0.push(k);
+            acc.1.push(v);
+            acc
+        });
+
+    Value::Record { cols, vals, span }
+}
+
+fn from_node_to_value(n: &roxmltree::Node, span: Span) -> Value {
+    if n.is_element() {
+        let name = n.tag_name().name().trim().to_string();
+
+        let mut children_values = vec![];
+        for c in n.children() {
+            children_values.push(from_node_to_value(&c, span));
+        }
+
+        let children_values: Vec<Value> = children_values
+            .into_iter()
+            .filter(|x| match x {
+                Value::String { val: f, .. } => {
+                    !f.trim().is_empty() // non-whitespace characters?
+                }
+                _ => true,
+            })
+            .collect();
+
+        let mut collected = IndexMap::new();
+
+        let attribute_value: Value = from_attributes_to_value(n.attributes(), span);
+
+        let mut row = IndexMap::new();
+        row.insert(
+            String::from("children"),
+            Value::List {
+                vals: children_values,
+                span,
+            },
+        );
+        row.insert(String::from("attributes"), attribute_value);
+        collected.insert(name, Value::from(Spanned { item: row, span }));
+
+        Value::from(Spanned {
+            item: collected,
+            span,
+        })
+    } else if n.is_comment() {
+        Value::String {
+            val: "<comment>".to_string(),
+            span,
+        }
+    } else if n.is_pi() {
+        Value::String {
+            val: "<processing_instruction>".to_string(),
+            span,
+        }
+    } else if n.is_text() {
+        match n.text() {
+            Some(text) => Value::String {
+                val: text.to_string(),
+                span,
+            },
+            None => Value::String {
+                val: "<error>".to_string(),
+                span,
+            },
+        }
+    } else {
+        Value::String {
+            val: "<unknown>".to_string(),
+            span,
+        }
+    }
+}
+
+fn from_document_to_value(d: &roxmltree::Document, span: Span) -> Value {
+    from_node_to_value(&d.root_element(), span)
+}
+
+pub fn from_xml_string_to_value(s: String, span: Span) -> Result<Value, roxmltree::Error> {
+    let parsed = roxmltree::Document::parse(&s)?;
+    Ok(from_document_to_value(&parsed, span))
+}
+
+fn from_xml(input: PipelineData, head: Span, config: &Config) -> Result<PipelineData, ShellError> {
+    let concat_string = input.collect_string("", config);
+
+    match from_xml_string_to_value(concat_string, head) {
+        Ok(x) => Ok(x.into_pipeline_data()),
+        _ => Err(ShellError::UnsupportedInput(
+            "Could not parse string as xml".to_string(),
+            head,
+        )),
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use indexmap::indexmap;
+    use indexmap::IndexMap;
+    use nu_protocol::{Spanned, Value};
+
+    fn string(input: impl Into<String>) -> Value {
+        Value::String {
+            val: input.into(),
+            span: Span::unknown(),
+        }
+    }
+
+    fn row(entries: IndexMap<String, Value>) -> Value {
+        Value::from(Spanned {
+            item: entries,
+            span: Span::unknown(),
+        })
+    }
+
+    fn table(list: &[Value]) -> Value {
+        Value::List {
+            vals: list.to_vec(),
+            span: Span::unknown(),
+        }
+    }
+
+    fn parse(xml: &str) -> Result<Value, roxmltree::Error> {
+        from_xml_string_to_value(xml.to_string(), Span::unknown())
+    }
+
+    #[test]
+    fn parses_empty_element() -> Result<(), roxmltree::Error> {
+        let source = "<nu></nu>";
+
+        assert_eq!(
+            parse(source)?,
+            row(indexmap! {
+                "nu".into() => row(indexmap! {
+                    "children".into() => table(&[]),
+                    "attributes".into() => row(indexmap! {})
+                })
+            })
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn parses_element_with_text() -> Result<(), roxmltree::Error> {
+        let source = "<nu>La era de los tres caballeros</nu>";
+
+        assert_eq!(
+            parse(source)?,
+            row(indexmap! {
+                "nu".into() => row(indexmap! {
+                    "children".into() => table(&[string("La era de los tres caballeros")]),
+                    "attributes".into() => row(indexmap! {})
+                })
+            })
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn parses_element_with_elements() -> Result<(), roxmltree::Error> {
+        let source = "\
+<nu>
+    <dev>Andrés</dev>
+    <dev>Jonathan</dev>
+    <dev>Yehuda</dev>
+</nu>";
+
+        assert_eq!(
+            parse(source)?,
+            row(indexmap! {
+                "nu".into() => row(indexmap! {
+                    "children".into() => table(&[
+                        row(indexmap! {
+                            "dev".into() => row(indexmap! {
+                                "children".into() => table(&[string("Andrés")]),
+                                "attributes".into() => row(indexmap! {})
+                            })
+                        }),
+                        row(indexmap! {
+                            "dev".into() => row(indexmap! {
+                                "children".into() => table(&[string("Jonathan")]),
+                                "attributes".into() => row(indexmap! {})
+                            })
+                        }),
+                        row(indexmap! {
+                            "dev".into() => row(indexmap! {
+                                "children".into() => table(&[string("Yehuda")]),
+                                "attributes".into() => row(indexmap! {})
+                            })
+                        })
+                    ]),
+                    "attributes".into() => row(indexmap! {})
+                })
+            })
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn parses_element_with_attribute() -> Result<(), roxmltree::Error> {
+        let source = "\
+<nu version=\"2.0\">
+</nu>";
+
+        assert_eq!(
+            parse(source)?,
+            row(indexmap! {
+                "nu".into() => row(indexmap! {
+                    "children".into() => table(&[]),
+                    "attributes".into() => row(indexmap! {
+                        "version".into() => string("2.0")
+                    })
+                })
+            })
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn parses_element_with_attribute_and_element() -> Result<(), roxmltree::Error> {
+        let source = "\
+<nu version=\"2.0\">
+    <version>2.0</version>
+</nu>";
+
+        assert_eq!(
+            parse(source)?,
+            row(indexmap! {
+                "nu".into() => row(indexmap! {
+                    "children".into() => table(&[
+                           row(indexmap! {
+                                "version".into() => row(indexmap! {
+                                    "children".into() => table(&[string("2.0")]),
+                                    "attributes".into() => row(indexmap! {})
+                                })
+                          })
+                    ]),
+                    "attributes".into() => row(indexmap! {
+                        "version".into() => string("2.0")
+                    })
+                })
+            })
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn parses_element_with_multiple_attributes() -> Result<(), roxmltree::Error> {
+        let source = "\
+<nu version=\"2.0\" age=\"25\">
+</nu>";
+
+        assert_eq!(
+            parse(source)?,
+            row(indexmap! {
+                "nu".into() => row(indexmap! {
+                    "children".into() => table(&[]),
+                    "attributes".into() => row(indexmap! {
+                        "version".into() => string("2.0"),
+                        "age".into() => string("25")
+                    })
+                })
+            })
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_examples() {
+        use crate::test_examples;
+
+        test_examples(FromXml {})
+    }
+}
diff --git a/crates/nu-command/src/formats/to/json.rs b/crates/nu-command/src/formats/to/json.rs
index 15804f1..8e35048 100644
--- a/crates/nu-command/src/formats/to/json.rs
+++ b/crates/nu-command/src/formats/to/json.rs
@@ -1,8 +1,7 @@
 use nu_protocol::ast::{Call, PathMember};
 use nu_protocol::engine::{Command, EngineState, Stack};
 use nu_protocol::{
-    Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError,
-    Signature, Value,
+    Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Value,
 };
 
 #[derive(Clone)]
@@ -15,12 +14,6 @@ impl Command for ToJson {
 
     fn signature(&self) -> Signature {
         Signature::build("to json").category(Category::Formats)
-        // .named(
-        //     "pretty",
-        //     SyntaxShape::Int,
-        //     "Formats the JSON text with the provided indentation setting",
-        //     Some('p'),
-        // )
     }
 
     fn usage(&self) -> &str {
@@ -29,12 +22,12 @@ impl Command for ToJson {
 
     fn run(
         &self,
-        engine_state: &EngineState,
+        _engine_state: &EngineState,
         _stack: &mut Stack,
         call: &Call,
         input: PipelineData,
     ) -> Result<nu_protocol::PipelineData, ShellError> {
-        to_json(engine_state, call, input)
+        to_json(call, input)
     }
 
     fn examples(&self) -> Vec<Example> {
@@ -80,7 +73,6 @@ pub fn value_to_json_value(v: &Value) -> Result<nu_json::Value, ShellError> {
             }
             nu_json::Value::Object(m)
         }
-        #[cfg(feature = "custom")]
         Value::CustomValue { val, .. } => val.to_json(),
     })
 }
@@ -95,143 +87,23 @@ fn json_list(input: &[Value]) -> Result<Vec<nu_json::Value>, ShellError> {
     Ok(out)
 }
 
-fn to_json(
-    engine_state: &EngineState,
-    call: &Call,
-    input: PipelineData,
-) -> Result<PipelineData, ShellError> {
-    let name_span = call.head;
-    // let pretty: Option<Value> = args.get_flag("pretty")?;
-
-    //let input: Vec<Value> = input.collect();
-
-    // let to_process_input = match input.len() {
-    //     x if x > 1 => {
-    //         let tag = input[0].tag.clone();
-    //         vec![Value:: {
-    //             value: UntaggedValue::Table(input),
-    //             tag,
-    //         }]
-    //     }
-    //     1 => input,
-    //     _ => vec![],
-    // };
-
-    match input {
-        PipelineData::Value(value) => {
-            let json_value = value_to_json_value(&value)?;
-            match nu_json::to_string(&json_value) {
-                Ok(serde_json_string) => Ok(Value::String {
-                    val: serde_json_string,
-                    span: name_span,
-                }
-                .into_pipeline_data()),
-                _ => Ok(Value::Error {
-                    error: ShellError::CantConvert(
-                        "JSON".into(),
-                        value.get_type().to_string(),
-                        name_span,
-                    ),
-                }
-                .into_pipeline_data()),
-            }
+fn to_json(call: &Call, input: PipelineData) -> Result<PipelineData, ShellError> {
+    let span = call.head;
+
+    let value = input.into_value(span);
+
+    let json_value = value_to_json_value(&value)?;
+    match nu_json::to_string(&json_value) {
+        Ok(serde_json_string) => Ok(Value::String {
+            val: serde_json_string,
+            span,
+        }
+        .into_pipeline_data()),
+        _ => Ok(Value::Error {
+            error: ShellError::CantConvert("JSON".into(), value.get_type().to_string(), span),
         }
-        PipelineData::Stream(stream) => Ok(stream
-            .map(move |value| {
-                if let Ok(json_value) = value_to_json_value(&value) {
-                    match nu_json::to_string(&json_value) {
-                        Ok(serde_json_string) => Value::String {
-                            val: serde_json_string,
-                            span: name_span,
-                        },
-                        _ => Value::Error {
-                            error: ShellError::CantConvert(
-                                "JSON".into(),
-                                value.get_type().to_string(),
-                                name_span,
-                            ),
-                        },
-                    }
-                } else {
-                    Value::Error {
-                        error: ShellError::CantConvert(
-                            "JSON".into(),
-                            value.get_type().to_string(),
-                            name_span,
-                        ),
-                    }
-                }
-            })
-            .into_pipeline_data(engine_state.ctrlc.clone())),
+        .into_pipeline_data()),
     }
-    // input
-    //     // .into_iter()
-    //     .map(
-    //         move |value| {
-    //             let value_span = value.span().expect("non-error");
-    //             match value_to_json_value(&value) {
-    //                 Ok(json_value) => {
-    //                     match nu_json::to_string(&json_value) {
-    //                         Ok(serde_json_string) => {
-    //                             // if let Some(pretty_value) = &pretty {
-    //                             //     let mut pretty_format_failed = true;
-
-    //                             //     if let Ok(pretty_u64) = pretty_value.as_u64() {
-    //                             //         if let Ok(serde_json_value) =
-    //                             //             serde_json::from_str::<serde_json::Value>(&serde_json_string)
-    //                             //         {
-    //                             //             let indentation_string = " ".repeat(pretty_u64 as usize);
-    //                             //             let serde_formatter =
-    //                             //                 serde_json::ser::PrettyFormatter::with_indent(
-    //                             //                     indentation_string.as_bytes(),
-    //                             //                 );
-    //                             //             let serde_buffer = Vec::new();
-    //                             //             let mut serde_serializer =
-    //                             //                 serde_json::Serializer::with_formatter(
-    //                             //                     serde_buffer,
-    //                             //                     serde_formatter,
-    //                             //                 );
-    //                             //             let serde_json_object = json!(serde_json_value);
-
-    //                             //             if let Ok(()) =
-    //                             //                 serde_json_object.serialize(&mut serde_serializer)
-    //                             //             {
-    //                             //                 if let Ok(ser_json_string) =
-    //                             //                     String::from_utf8(serde_serializer.into_inner())
-    //                             //                 {
-    //                             //                     pretty_format_failed = false;
-    //                             //                     serde_json_string = ser_json_string
-    //                             //                 }
-    //                             //             }
-    //                             //         }
-    //                             //     }
-
-    //                             //     if pretty_format_failed {
-    //                             //         return Value::error(ShellError::labeled_error(
-    //                             //             "Pretty formatting failed",
-    //                             //             "failed",
-    //                             //             pretty_value.tag(),
-    //                             //         ));
-    //                             //     }
-    //                             // }
-
-    //                             Value::String {
-    //                                 val: serde_json_string,
-    //                                 span: value_span,
-    //                             }
-    //                         }
-    //                         _ => Value::Error {
-    //                             error: ShellError::CantConvert("JSON".into(), value_span),
-    //                         },
-    //                     }
-    //                 }
-    //                 _ => Value::Error {
-    //                     error: ShellError::CantConvert("JSON".into(), value_span),
-    //                 },
-    //             }
-    //         },
-    //         engine_state.ctrlc.clone(),
-    //     )
 }
 
 #[cfg(test)]
diff --git a/crates/nu-command/src/lib.rs b/crates/nu-command/src/lib.rs
index bb7e60d..12b0d9b 100644
--- a/crates/nu-command/src/lib.rs
+++ b/crates/nu-command/src/lib.rs
@@ -9,6 +9,9 @@ mod filesystem;
 mod filters;
 mod formats;
 mod math;
+mod platform;
+mod random;
+mod shells;
 mod strings;
 mod system;
 mod viewers;
@@ -27,6 +30,9 @@ pub use filesystem::*;
 pub use filters::*;
 pub use formats::*;
 pub use math::*;
+pub use platform::*;
+pub use random::*;
+pub use shells::*;
 pub use strings::*;
 pub use system::*;
 pub use viewers::*;
diff --git a/crates/nu-command/src/math/utils.rs b/crates/nu-command/src/math/utils.rs
index aef8f2c..13228b7 100644
--- a/crates/nu-command/src/math/utils.rs
+++ b/crates/nu-command/src/math/utils.rs
@@ -1,6 +1,6 @@
+use indexmap::map::IndexMap;
 use nu_protocol::ast::Call;
-use nu_protocol::{IntoPipelineData, PipelineData, ShellError, Span, Value};
-use std::collections::HashMap;
+use nu_protocol::{IntoPipelineData, PipelineData, ShellError, Span, Spanned, Value};
 
 pub fn run_with_function(
     call: &Call,
@@ -22,7 +22,7 @@ fn helper_for_tables(
 ) -> Result<Value, ShellError> {
     // If we are not dealing with Primitives, then perhaps we are dealing with a table
     // Create a key for each column name
-    let mut column_values = HashMap::new();
+    let mut column_values = IndexMap::new();
     for val in values {
         if let Value::Record { cols, vals, .. } = val {
             for (key, value) in cols.iter().zip(vals.iter()) {
@@ -37,7 +37,7 @@ fn helper_for_tables(
         }
     }
     // The mathematical function operates over the columns of the table
-    let mut column_totals = HashMap::new();
+    let mut column_totals = IndexMap::new();
     for (col_name, col_vals) in column_values {
         if let Ok(out) = mf(&col_vals, &name) {
             column_totals.insert(col_name, out);
@@ -49,19 +49,11 @@ fn helper_for_tables(
             name,
         ));
     }
-    let (cols, vals) = column_totals
-        .into_iter()
-        .fold((vec![], vec![]), |mut acc, (k, v)| {
-            acc.0.push(k);
-            acc.1.push(v);
-            acc
-        });
 
-    Ok(Value::Record {
-        cols,
-        vals,
+    Ok(Value::from(Spanned {
+        item: column_totals,
         span: name,
-    })
+    }))
 }
 
 pub fn calculate(
diff --git a/crates/nu-command/src/platform/clear.rs b/crates/nu-command/src/platform/clear.rs
new file mode 100644
index 0000000..fdc540b
--- /dev/null
+++ b/crates/nu-command/src/platform/clear.rs
@@ -0,0 +1,53 @@
+use nu_protocol::ast::Call;
+use nu_protocol::engine::{Command, EngineState, Stack};
+use nu_protocol::{
+    Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Value,
+};
+use std::process::Command as CommandSys;
+
+#[derive(Clone)]
+pub struct Clear;
+
+impl Command for Clear {
+    fn name(&self) -> &str {
+        "clear"
+    }
+
+    fn usage(&self) -> &str {
+        "Clear the terminal."
+    }
+
+    fn signature(&self) -> Signature {
+        Signature::build("clear").category(Category::Platform)
+    }
+
+    fn run(
+        &self,
+        _engine_state: &EngineState,
+        _stack: &mut Stack,
+        call: &Call,
+        _input: PipelineData,
+    ) -> Result<PipelineData, ShellError> {
+        if cfg!(windows) {
+            CommandSys::new("cmd")
+                .args(["/C", "cls"])
+                .status()
+                .expect("failed to execute process");
+        } else if cfg!(unix) {
+            CommandSys::new("/bin/sh")
+                .args(["-c", "clear"])
+                .status()
+                .expect("failed to execute process");
+        }
+
+        Ok(Value::Nothing { span: call.head }.into_pipeline_data())
+    }
+
+    fn examples(&self) -> Vec<Example> {
+        vec![Example {
+            description: "Clear the terminal",
+            example: "clear",
+            result: None,
+        }]
+    }
+}
diff --git a/crates/nu-command/src/platform/kill.rs b/crates/nu-command/src/platform/kill.rs
new file mode 100644
index 0000000..9ab4f07
--- /dev/null
+++ b/crates/nu-command/src/platform/kill.rs
@@ -0,0 +1,149 @@
+use nu_engine::CallExt;
+use nu_protocol::engine::{Command, EngineState, Stack};
+use nu_protocol::{ast::Call, span};
+use nu_protocol::{
+    Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Spanned, SyntaxShape,
+    Value,
+};
+use std::process::{Command as CommandSys, Stdio};
+
+#[derive(Clone)]
+pub struct Kill;
+
+impl Command for Kill {
+    fn name(&self) -> &str {
+        "kill"
+    }
+
+    fn usage(&self) -> &str {
+        "Kill a process using the process id."
+    }
+
+    fn signature(&self) -> Signature {
+        let signature = Signature::build("kill")
+            .required(
+                "pid",
+                SyntaxShape::Int,
+                "process id of process that is to be killed",
+            )
+            .rest("rest", SyntaxShape::Int, "rest of processes to kill")
+            .switch("force", "forcefully kill the process", Some('f'))
+            .switch("quiet", "won't print anything to the console", Some('q'))
+            .category(Category::Platform);
+
+        if cfg!(windows) {
+            return signature;
+        }
+
+        signature.named(
+            "signal",
+            SyntaxShape::Int,
+            "signal decimal number to be sent instead of the default 15 (unsupported on Windows)",
+            Some('s'),
+        )
+    }
+
+    fn run(
+        &self,
+        engine_state: &EngineState,
+        stack: &mut Stack,
+        call: &Call,
+        _input: PipelineData,
+    ) -> Result<PipelineData, ShellError> {
+        let pid: i64 = call.req(engine_state, stack, 0)?;
+        let rest: Vec<i64> = call.rest(engine_state, stack, 1)?;
+        let force: bool = call.has_flag("force");
+        let signal: Option<Spanned<i64>> = call.get_flag(engine_state, stack, "signal")?;
+        let quiet: bool = call.has_flag("quiet");
+
+        let mut cmd = if cfg!(windows) {
+            let mut cmd = CommandSys::new("taskkill");
+
+            if force {
+                cmd.arg("/F");
+            }
+
+            cmd.arg("/PID");
+            cmd.arg(pid.to_string());
+
+            // each pid must written as `/PID 0` otherwise
+            // taskkill will act as `killall` unix command
+            for id in &rest {
+                cmd.arg("/PID");
+                cmd.arg(id.to_string());
+            }
+
+            cmd
+        } else {
+            let mut cmd = CommandSys::new("kill");
+            if force {
+                if let Some(Spanned {
+                    item: _,
+                    span: signal_span,
+                }) = signal
+                {
+                    return Err(ShellError::IncompatibleParameters {
+                        left_message: "force".to_string(),
+                        left_span: call.get_named_arg("force").unwrap().span,
+                        right_message: "signal".to_string(),
+                        right_span: span(&[
+                            call.get_named_arg("signal").unwrap().span,
+                            signal_span,
+                        ]),
+                    });
+                }
+                cmd.arg("-9");
+            } else if let Some(signal_value) = signal {
+                cmd.arg(format!("-{}", signal_value.item.to_string()));
+            }
+
+            cmd.arg(pid.to_string());
+
+            cmd.args(rest.iter().map(move |id| id.to_string()));
+
+            cmd
+        };
+
+        // pipe everything to null
+        if quiet {
+            cmd.stdin(Stdio::null())
+                .stdout(Stdio::null())
+                .stderr(Stdio::null());
+        }
+
+        cmd.status().expect("failed to execute shell command");
+
+        Ok(Value::Nothing { span: call.head }.into_pipeline_data())
+    }
+
+    fn examples(&self) -> Vec<Example> {
+        vec![
+            Example {
+                description: "Kill the pid using the most memory",
+                example: "ps | sort-by mem | last | kill $it.pid",
+                result: None,
+            },
+            Example {
+                description: "Force kill a given pid",
+                example: "kill --force 12345",
+                result: None,
+            },
+            Example {
+                description: "Send INT signal",
+                example: "kill -s 2 12345",
+                result: None,
+            },
+        ]
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::Kill;
+
+    #[test]
+    fn examples_work_as_expected() {
+        use crate::test_examples;
+        test_examples(Kill {})
+    }
+}
diff --git a/crates/nu-command/src/platform/mod.rs b/crates/nu-command/src/platform/mod.rs
new file mode 100644
index 0000000..bb50c30
--- /dev/null
+++ b/crates/nu-command/src/platform/mod.rs
@@ -0,0 +1,7 @@
+mod clear;
+mod kill;
+mod sleep;
+
+pub use clear::Clear;
+pub use kill::Kill;
+pub use sleep::Sleep;
diff --git a/crates/nu-command/src/platform/sleep.rs b/crates/nu-command/src/platform/sleep.rs
new file mode 100644
index 0000000..9591de7
--- /dev/null
+++ b/crates/nu-command/src/platform/sleep.rs
@@ -0,0 +1,108 @@
+use nu_engine::CallExt;
+use nu_protocol::ast::Call;
+use nu_protocol::engine::{Command, EngineState, Stack};
+use nu_protocol::{
+    Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
+};
+use std::{
+    sync::atomic::Ordering,
+    thread,
+    time::{Duration, Instant},
+};
+
+const CTRL_C_CHECK_INTERVAL: Duration = Duration::from_millis(100);
+
+#[derive(Clone)]
+pub struct Sleep;
+
+impl Command for Sleep {
+    fn name(&self) -> &str {
+        "sleep"
+    }
+
+    fn usage(&self) -> &str {
+        "Delay for a specified amount of time."
+    }
+
+    fn signature(&self) -> Signature {
+        Signature::build("sleep")
+            .required("duration", SyntaxShape::Duration, "time to sleep")
+            .rest("rest", SyntaxShape::Duration, "additional time")
+            .category(Category::Platform)
+    }
+
+    fn run(
+        &self,
+        engine_state: &EngineState,
+        stack: &mut Stack,
+        call: &Call,
+        _input: PipelineData,
+    ) -> Result<PipelineData, ShellError> {
+        fn duration_from_i64(val: i64) -> Duration {
+            Duration::from_nanos(if val < 0 { 0 } else { val as u64 })
+        }
+
+        let duration: i64 = call.req(engine_state, stack, 0)?;
+        let rest: Vec<i64> = call.rest(engine_state, stack, 1)?;
+
+        let total_dur =
+            duration_from_i64(duration) + rest.into_iter().map(duration_from_i64).sum::<Duration>();
+
+        let ctrlc_ref = &engine_state.ctrlc.clone();
+        let start = Instant::now();
+        loop {
+            thread::sleep(CTRL_C_CHECK_INTERVAL);
+            if start.elapsed() >= total_dur {
+                break;
+            }
+
+            if let Some(ctrlc) = ctrlc_ref {
+                if ctrlc.load(Ordering::SeqCst) {
+                    break;
+                }
+            }
+        }
+
+        Ok(Value::Nothing { span: call.head }.into_pipeline_data())
+    }
+
+    fn examples(&self) -> Vec<Example> {
+        vec![
+            Example {
+                description: "Sleep for 1sec",
+                example: "sleep 1sec",
+                result: None,
+            },
+            Example {
+                description: "Sleep for 3sec",
+                example: "sleep 1sec 1sec 1sec",
+                result: None,
+            },
+            Example {
+                description: "Send output after 1sec",
+                example: "sleep 1sec; echo done",
+                result: Some(Value::test_string("done")),
+            },
+        ]
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::Sleep;
+
+    #[test]
+    fn examples_work_as_expected() {
+        use crate::test_examples;
+        use std::time::Instant;
+
+        let start = Instant::now();
+        test_examples(Sleep {});
+
+        let elapsed = start.elapsed();
+
+        // only examples with actual output are run
+        assert!(elapsed >= std::time::Duration::from_secs(1));
+        assert!(elapsed < std::time::Duration::from_secs(2));
+    }
+}
diff --git a/crates/nu-command/src/random/bool.rs b/crates/nu-command/src/random/bool.rs
new file mode 100644
index 0000000..bd462d5
--- /dev/null
+++ b/crates/nu-command/src/random/bool.rs
@@ -0,0 +1,97 @@
+use nu_engine::CallExt;
+use nu_protocol::ast::Call;
+use nu_protocol::engine::{Command, EngineState, Stack};
+use nu_protocol::{
+    Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Value,
+};
+use rand::prelude::{thread_rng, Rng};
+
+#[derive(Clone)]
+pub struct SubCommand;
+
+impl Command for SubCommand {
+    fn name(&self) -> &str {
+        "random bool"
+    }
+
+    fn signature(&self) -> Signature {
+        Signature::build("random bool")
+            .named(
+                "bias",
+                SyntaxShape::Number,
+                "Adjusts the probability of a \"true\" outcome",
+                Some('b'),
+            )
+            .category(Category::Random)
+    }
+
+    fn usage(&self) -> &str {
+        "Generate a random boolean value"
+    }
+
+    fn run(
+        &self,
+        engine_state: &EngineState,
+        stack: &mut Stack,
+        call: &Call,
+        _input: PipelineData,
+    ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
+        bool(engine_state, stack, call)
+    }
+
+    fn examples(&self) -> Vec<Example> {
+        vec![
+            Example {
+                description: "Generate a random boolean value",
+                example: "random bool",
+                result: None,
+            },
+            Example {
+                description: "Generate a random boolean value with a 75% chance of \"true\"",
+                example: "random bool --bias 0.75",
+                result: None,
+            },
+        ]
+    }
+}
+
+fn bool(
+    engine_state: &EngineState,
+    stack: &mut Stack,
+    call: &Call,
+) -> Result<PipelineData, ShellError> {
+    let span = call.head;
+    let bias: Option<Spanned<f64>> = call.get_flag(engine_state, stack, "bias")?;
+
+    let mut probability = 0.5;
+
+    if let Some(prob) = bias {
+        probability = prob.item;
+
+        let probability_is_valid = (0.0..=1.0).contains(&probability);
+
+        if !probability_is_valid {
+            return Err(ShellError::InvalidProbability(prob.span));
+        }
+    }
+
+    let mut rng = thread_rng();
+    let bool_result: bool = rng.gen_bool(probability);
+
+    Ok(PipelineData::Value(Value::Bool {
+        val: bool_result,
+        span,
+    }))
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn test_examples() {
+        use crate::test_examples;
+
+        test_examples(SubCommand {})
+    }
+}
diff --git a/crates/nu-command/src/random/command.rs b/crates/nu-command/src/random/command.rs
new file mode 100644
index 0000000..f7d0ece
--- /dev/null
+++ b/crates/nu-command/src/random/command.rs
@@ -0,0 +1,41 @@
+use nu_engine::get_full_help;
+use nu_protocol::{
+    ast::Call,
+    engine::{Command, EngineState, Stack},
+    Category, IntoPipelineData, PipelineData, Signature, Value,
+};
+
+#[derive(Clone)]
+pub struct RandomCommand;
+
+impl Command for RandomCommand {
+    fn name(&self) -> &str {
+        "random"
+    }
+
+    fn signature(&self) -> Signature {
+        Signature::build("random").category(Category::Random)
+    }
+
+    fn usage(&self) -> &str {
+        "Generate a random values."
+    }
+
+    fn run(
+        &self,
+        engine_state: &EngineState,
+        _stack: &mut Stack,
+        call: &Call,
+        _input: PipelineData,
+    ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
+        Ok(Value::String {
+            val: get_full_help(
+                &RandomCommand.signature(),
+                &RandomCommand.examples(),
+                engine_state,
+            ),
+            span: call.head,
+        }
+        .into_pipeline_data())
+    }
+}
diff --git a/crates/nu-command/src/random/mod.rs b/crates/nu-command/src/random/mod.rs
new file mode 100644
index 0000000..1e3cdf0
--- /dev/null
+++ b/crates/nu-command/src/random/mod.rs
@@ -0,0 +1,5 @@
+mod bool;
+mod command;
+
+pub use self::bool::SubCommand as Bool;
+pub use command::RandomCommand as Random;
diff --git a/crates/nu-command/src/shells/exit.rs b/crates/nu-command/src/shells/exit.rs
new file mode 100644
index 0000000..092c6ef
--- /dev/null
+++ b/crates/nu-command/src/shells/exit.rs
@@ -0,0 +1,33 @@
+use nu_protocol::ast::Call;
+use nu_protocol::engine::{Command, EngineState, Stack};
+use nu_protocol::{Category, PipelineData, ShellError, Signature};
+
+/// Source a file for environment variables.
+#[derive(Clone)]
+pub struct Exit;
+
+impl Command for Exit {
+    fn name(&self) -> &str {
+        "exit"
+    }
+
+    fn signature(&self) -> Signature {
+        Signature::build("exit").category(Category::Shells)
+    }
+
+    fn usage(&self) -> &str {
+        "Runs a script file in the current context."
+    }
+
+    fn run(
+        &self,
+        _engine_state: &EngineState,
+        _stack: &mut Stack,
+        _call: &Call,
+        _input: PipelineData,
+    ) -> Result<PipelineData, ShellError> {
+        //TODO: add more shell support
+
+        std::process::exit(0);
+    }
+}
diff --git a/crates/nu-command/src/shells/mod.rs b/crates/nu-command/src/shells/mod.rs
new file mode 100644
index 0000000..11d875e
--- /dev/null
+++ b/crates/nu-command/src/shells/mod.rs
@@ -0,0 +1,3 @@
+mod exit;
+
+pub use exit::Exit;
diff --git a/crates/nu-command/src/viewers/griddle.rs b/crates/nu-command/src/viewers/griddle.rs
index 6ad5c28..a04457f 100644
--- a/crates/nu-command/src/viewers/griddle.rs
+++ b/crates/nu-command/src/viewers/griddle.rs
@@ -1,3 +1,5 @@
+// use super::icons::{icon_for_file, iconify_style_ansi_to_nu};
+use super::icons::icon_for_file;
 use lscolors::{LsColors, Style};
 use nu_engine::CallExt;
 use nu_protocol::{
@@ -57,23 +59,23 @@ prints out the list properly."#
         let width_param: Option<String> = call.get_flag(engine_state, stack, "width")?;
         let color_param: bool = call.has_flag("color");
         let separator_param: Option<String> = call.get_flag(engine_state, stack, "separator")?;
-
         let config = stack.get_config()?;
-
         let env_str = stack.get_env_var("LS_COLORS");
+        let use_grid_icons = config.use_grid_icons;
 
         match input {
             PipelineData::Value(Value::List { vals, .. }) => {
                 // dbg!("value::list");
-                let data = convert_to_list2(vals, &config);
+                let data = convert_to_list(vals, &config);
                 if let Some(items) = data {
-                    Ok(create_grid_output2(
+                    Ok(create_grid_output(
                         items,
                         call,
                         width_param,
                         color_param,
                         separator_param,
                         env_str,
+                        use_grid_icons,
                     ))
                 } else {
                     Ok(PipelineData::new(call.head))
@@ -81,15 +83,16 @@ prints out the list properly."#
             }
             PipelineData::Stream(stream) => {
                 // dbg!("value::stream");
-                let data = convert_to_list2(stream, &config);
+                let data = convert_to_list(stream, &config);
                 if let Some(items) = data {
-                    Ok(create_grid_output2(
+                    Ok(create_grid_output(
                         items,
                         call,
                         width_param,
                         color_param,
                         separator_param,
                         env_str,
+                        use_grid_icons,
                     ))
                 } else {
                     // dbg!(data);
@@ -104,13 +107,14 @@ prints out the list properly."#
                     items.push((i, c, v.into_string(", ", &config)))
                 }
 
-                Ok(create_grid_output2(
+                Ok(create_grid_output(
                     items,
                     call,
                     width_param,
                     color_param,
                     separator_param,
                     env_str,
+                    use_grid_icons,
                 ))
             }
             x => {
@@ -122,13 +126,22 @@ prints out the list properly."#
     }
 }
 
-fn create_grid_output2(
+fn strip_ansi(astring: &str) -> String {
+    if let Ok(bytes) = strip_ansi_escapes::strip(astring) {
+        String::from_utf8_lossy(&bytes).to_string()
+    } else {
+        astring.to_string()
+    }
+}
+
+fn create_grid_output(
     items: Vec<(usize, String, String)>,
     call: &Call,
     width_param: Option<String>,
     color_param: bool,
     separator_param: Option<String>,
     env_str: Option<String>,
+    use_grid_icons: bool,
 ) -> PipelineData {
     let ls_colors = match env_str {
         Some(s) => LsColors::from_string(&s),
@@ -157,14 +170,43 @@ fn create_grid_output2(
         // only output value if the header name is 'name'
         if header == "name" {
             if color_param {
-                let style = ls_colors.style_for_path(value.clone());
-                let ansi_style = style.map(Style::to_crossterm_style).unwrap_or_default();
-                let mut cell = Cell::from(ansi_style.apply(value).to_string());
-                cell.alignment = Alignment::Right;
-                grid.add(cell);
+                if use_grid_icons {
+                    let no_ansi = strip_ansi(&value);
+                    let path = std::path::Path::new(&no_ansi);
+                    let icon = icon_for_file(path);
+                    let ls_colors_style = ls_colors.style_for_path(path);
+                    // eprintln!("ls_colors_style: {:?}", &ls_colors_style);
+
+                    let icon_style = match ls_colors_style {
+                        Some(c) => c.to_crossterm_style(),
+                        None => crossterm::style::ContentStyle::default(),
+                    };
+                    // eprintln!("icon_style: {:?}", &icon_style);
+
+                    let ansi_style = ls_colors_style
+                        .map(Style::to_crossterm_style)
+                        .unwrap_or_default();
+                    // eprintln!("ansi_style: {:?}", &ansi_style);
+
+                    let item = format!(
+                        "{} {}",
+                        icon_style.apply(icon).to_string(),
+                        ansi_style.apply(value).to_string()
+                    );
+
+                    let mut cell = Cell::from(item);
+                    cell.alignment = Alignment::Left;
+                    grid.add(cell);
+                } else {
+                    let style = ls_colors.style_for_path(value.clone());
+                    let ansi_style = style.map(Style::to_crossterm_style).unwrap_or_default();
+                    let mut cell = Cell::from(ansi_style.apply(value).to_string());
+                    cell.alignment = Alignment::Left;
+                    grid.add(cell);
+                }
             } else {
                 let mut cell = Cell::from(value);
-                cell.alignment = Alignment::Right;
+                cell.alignment = Alignment::Left;
                 grid.add(cell);
             }
         }
@@ -184,7 +226,7 @@ fn create_grid_output2(
     .into_pipeline_data()
 }
 
-fn convert_to_list2(
+fn convert_to_list(
     iter: impl IntoIterator<Item = Value>,
     config: &Config,
 ) -> Option<Vec<(usize, String, String)>> {
diff --git a/crates/nu-command/src/viewers/icons.rs b/crates/nu-command/src/viewers/icons.rs
new file mode 100644
index 0000000..e504e10
--- /dev/null
+++ b/crates/nu-command/src/viewers/icons.rs
@@ -0,0 +1,580 @@
+use lazy_static::lazy_static;
+use std::collections::HashMap;
+use std::path::Path;
+
+// Attribution: Thanks exa. Most of this file is taken from around here
+// https://github.com/ogham/exa/blob/dbd11d38042284cc890fdd91760c2f93b65e8553/src/output/icons.rs
+
+pub trait FileIcon {
+    fn icon_file(&self, file: &Path) -> Option<char>;
+}
+
+#[derive(Copy, Clone)]
+pub enum Icons {
+    Audio,
+    Image,
+    Video,
+}
+
+impl Icons {
+    pub fn value(self) -> char {
+        match self {
+            Self::Audio => '\u{f001}',
+            Self::Image => '\u{f1c5}',
+            Self::Video => '\u{f03d}',
+        }
+    }
+}
+
+// keeping this for now in case we have to revert to ansi style instead of crossterm style
+// Helper function to convert ansi_term style to nu_ansi_term. unfortunately
+// this is necessary because ls_colors has a dependency on ansi_term vs nu_ansi_term
+// double unfortunately, now we have a dependency on both. we may have to bring
+// in ls_colors crate to nushell
+// pub fn iconify_style_ansi_to_nu<'a>(style: ansi_term::Style) -> nu_ansi_term::Style {
+//     let bg = match style.background {
+//         Some(c) => match c {
+//             ansi_term::Color::Black => Some(nu_ansi_term::Color::Black),
+//             ansi_term::Color::Red => Some(nu_ansi_term::Color::Red),
+//             ansi_term::Color::Green => Some(nu_ansi_term::Color::Green),
+//             ansi_term::Color::Yellow => Some(nu_ansi_term::Color::Yellow),
+//             ansi_term::Color::Blue => Some(nu_ansi_term::Color::Blue),
+//             ansi_term::Color::Purple => Some(nu_ansi_term::Color::Purple),
+//             ansi_term::Color::Cyan => Some(nu_ansi_term::Color::Cyan),
+//             ansi_term::Color::White => Some(nu_ansi_term::Color::White),
+//             ansi_term::Color::Fixed(f) => Some(nu_ansi_term::Color::Fixed(f)),
+//             ansi_term::Color::RGB(r, g, b) => Some(nu_ansi_term::Color::Rgb(r, g, b)),
+//         },
+//         None => None,
+//     };
+
+//     let fg = match style.foreground {
+//         Some(c) => match c {
+//             ansi_term::Color::Black => Some(nu_ansi_term::Color::Black),
+//             ansi_term::Color::Red => Some(nu_ansi_term::Color::Red),
+//             ansi_term::Color::Green => Some(nu_ansi_term::Color::Green),
+//             ansi_term::Color::Yellow => Some(nu_ansi_term::Color::Yellow),
+//             ansi_term::Color::Blue => Some(nu_ansi_term::Color::Blue),
+//             ansi_term::Color::Purple => Some(nu_ansi_term::Color::Purple),
+//             ansi_term::Color::Cyan => Some(nu_ansi_term::Color::Cyan),
+//             ansi_term::Color::White => Some(nu_ansi_term::Color::White),
+//             ansi_term::Color::Fixed(f) => Some(nu_ansi_term::Color::Fixed(f)),
+//             ansi_term::Color::RGB(r, g, b) => Some(nu_ansi_term::Color::Rgb(r, g, b)),
+//         },
+//         None => None,
+//     };
+
+//     let nu_style = nu_ansi_term::Style {
+//         foreground: fg,
+//         background: bg,
+//         is_blink: style.is_blink,
+//         is_bold: style.is_bold,
+//         is_dimmed: style.is_dimmed,
+//         is_hidden: style.is_hidden,
+//         is_italic: style.is_italic,
+//         is_underline: style.is_underline,
+//         is_reverse: style.is_reverse,
+//         is_strikethrough: style.is_strikethrough,
+//     };
+
+//     nu_style
+//         .background
+//         .or(nu_style.foreground)
+//         .map(nu_ansi_term::Style::from)
+//         .unwrap_or_default()
+// }
+
+lazy_static! {
+    static ref MAP_BY_NAME: HashMap<&'static str, char> = {
+        let mut m = HashMap::new();
+        m.insert(".Trash", '\u{f1f8}'); // 
+        m.insert(".atom", '\u{e764}'); // 
+        m.insert(".bashprofile", '\u{e615}'); // 
+        m.insert(".bashrc", '\u{f489}'); // 
+        m.insert(".git", '\u{f1d3}'); // 
+        m.insert(".gitattributes", '\u{f1d3}'); // 
+        m.insert(".gitconfig", '\u{f1d3}'); // 
+        m.insert(".github", '\u{f408}'); // 
+        m.insert(".gitignore", '\u{f1d3}'); // 
+        m.insert(".gitmodules", '\u{f1d3}'); // 
+        m.insert(".rvm", '\u{e21e}'); // 
+        m.insert(".vimrc", '\u{e62b}'); // 
+        m.insert(".vscode", '\u{e70c}'); // 
+        m.insert(".zshrc", '\u{f489}'); // 
+        m.insert("Cargo.lock", '\u{e7a8}'); // 
+        m.insert("bin", '\u{e5fc}'); // 
+        m.insert("config", '\u{e5fc}'); // 
+        m.insert("docker-compose.yml", '\u{f308}'); // 
+        m.insert("Dockerfile", '\u{f308}'); // 
+        m.insert("ds_store", '\u{f179}'); // 
+        m.insert("gitignore_global", '\u{f1d3}'); // 
+        m.insert("gradle", '\u{e70e}'); // 
+        m.insert("gruntfile.coffee", '\u{e611}'); // 
+        m.insert("gruntfile.js", '\u{e611}'); // 
+        m.insert("gruntfile.ls", '\u{e611}'); // 
+        m.insert("gulpfile.coffee", '\u{e610}'); // 
+        m.insert("gulpfile.js", '\u{e610}'); // 
+        m.insert("gulpfile.ls", '\u{e610}'); // 
+        m.insert("hidden", '\u{f023}'); // 
+        m.insert("include", '\u{e5fc}'); // 
+        m.insert("lib", '\u{f121}'); // 
+        m.insert("localized", '\u{f179}'); // 
+        m.insert("Makefile", '\u{e779}'); // 
+        m.insert("node_modules", '\u{e718}'); // 
+        m.insert("npmignore", '\u{e71e}'); // 
+        m.insert("rubydoc", '\u{e73b}'); // 
+        m.insert("yarn.lock", '\u{e718}'); // 
+
+        m
+    };
+}
+
+pub fn icon_for_file(file_path: &Path) -> char {
+    let extensions = Box::new(FileExtensions);
+    let fp = format!("{}", file_path.display());
+
+    if let Some(icon) = MAP_BY_NAME.get(&fp[..]) {
+        *icon
+    } else if file_path.is_dir() {
+        match file_path.file_name().unwrap().to_str().unwrap() {
+            "bin" => '\u{e5fc}',   // 
+            ".git" => '\u{f1d3}',  // 
+            ".idea" => '\u{e7b5}', // 
+            _ => '\u{f115}',       // 
+        }
+    } else if let Some(icon) = extensions.icon_file(file_path) {
+        icon
+    } else if let Some(ext) = file_path.extension().as_ref() {
+        match ext.to_str().unwrap() {
+            "ai" => '\u{e7b4}',             // 
+            "android" => '\u{e70e}',        // 
+            "apk" => '\u{e70e}',            // 
+            "apple" => '\u{f179}',          // 
+            "avi" => '\u{f03d}',            // 
+            "avro" => '\u{e60b}',           // 
+            "awk" => '\u{f489}',            // 
+            "bash" => '\u{f489}',           // 
+            "bash_history" => '\u{f489}',   // 
+            "bash_profile" => '\u{f489}',   // 
+            "bashrc" => '\u{f489}',         // 
+            "bat" => '\u{f17a}',            // 
+            "bmp" => '\u{f1c5}',            // 
+            "bz" => '\u{f410}',             // 
+            "bz2" => '\u{f410}',            // 
+            "c" => '\u{e61e}',              // 
+            "c++" => '\u{e61d}',            // 
+            "cab" => '\u{e70f}',            // 
+            "cc" => '\u{e61d}',             // 
+            "cfg" => '\u{e615}',            // 
+            "class" => '\u{e256}',          // 
+            "clj" => '\u{e768}',            // 
+            "cljs" => '\u{e76a}',           // 
+            "cls" => '\u{e600}',            // 
+            "cmd" => '\u{e70f}',            // 
+            "coffee" => '\u{f0f4}',         // 
+            "conf" => '\u{e615}',           // 
+            "cp" => '\u{e61d}',             // 
+            "cpp" => '\u{e61d}',            // 
+            "cs" => '\u{f81a}',             // 
+            "csh" => '\u{f489}',            // 
+            "cshtml" => '\u{f1fa}',         // 
+            "csproj" => '\u{f81a}',         // 
+            "css" => '\u{e749}',            // 
+            "csv" => '\u{f1c3}',            // 
+            "csx" => '\u{f81a}',            // 
+            "cxx" => '\u{e61d}',            // 
+            "d" => '\u{e7af}',              // 
+            "dart" => '\u{e798}',           // 
+            "db" => '\u{f1c0}',             // 
+            "deb" => '\u{e77d}',            // 
+            "diff" => '\u{f440}',           // 
+            "djvu" => '\u{f02d}',           // 
+            "dll" => '\u{e70f}',            // 
+            "doc" => '\u{f1c2}',            // 
+            "docx" => '\u{f1c2}',           // 
+            "ds_store" => '\u{f179}',       // 
+            "DS_store" => '\u{f179}',       // 
+            "dump" => '\u{f1c0}',           // 
+            "ebook" => '\u{e28b}',          // 
+            "editorconfig" => '\u{e615}',   // 
+            "ejs" => '\u{e618}',            // 
+            "elm" => '\u{e62c}',            // 
+            "env" => '\u{f462}',            // 
+            "eot" => '\u{f031}',            // 
+            "epub" => '\u{e28a}',           // 
+            "erb" => '\u{e73b}',            // 
+            "erl" => '\u{e7b1}',            // 
+            "ex" => '\u{e62d}',             // 
+            "exe" => '\u{f17a}',            // 
+            "exs" => '\u{e62d}',            // 
+            "fish" => '\u{f489}',           // 
+            "flac" => '\u{f001}',           // 
+            "flv" => '\u{f03d}',            // 
+            "font" => '\u{f031}',           // 
+            "gdoc" => '\u{f1c2}',           // 
+            "gem" => '\u{e21e}',            // 
+            "gemfile" => '\u{e21e}',        // 
+            "gemspec" => '\u{e21e}',        // 
+            "gform" => '\u{f298}',          // 
+            "gif" => '\u{f1c5}',            // 
+            "git" => '\u{f1d3}',            // 
+            "gitattributes" => '\u{f1d3}',  // 
+            "gitignore" => '\u{f1d3}',      // 
+            "gitmodules" => '\u{f1d3}',     // 
+            "go" => '\u{e626}',             // 
+            "gradle" => '\u{e70e}',         // 
+            "groovy" => '\u{e775}',         // 
+            "gsheet" => '\u{f1c3}',         // 
+            "gslides" => '\u{f1c4}',        // 
+            "guardfile" => '\u{e21e}',      // 
+            "gz" => '\u{f410}',             // 
+            "h" => '\u{f0fd}',              // 
+            "hbs" => '\u{e60f}',            // 
+            "hpp" => '\u{f0fd}',            // 
+            "hs" => '\u{e777}',             // 
+            "htm" => '\u{f13b}',            // 
+            "html" => '\u{f13b}',           // 
+            "hxx" => '\u{f0fd}',            // 
+            "ico" => '\u{f1c5}',            // 
+            "image" => '\u{f1c5}',          // 
+            "iml" => '\u{e7b5}',            // 
+            "ini" => '\u{f17a}',            // 
+            "ipynb" => '\u{e606}',          // 
+            "iso" => '\u{e271}',            // 
+            "jad" => '\u{e256}',            // 
+            "jar" => '\u{e204}',            // 
+            "java" => '\u{e204}',           // 
+            "jpeg" => '\u{f1c5}',           // 
+            "jpg" => '\u{f1c5}',            // 
+            "js" => '\u{e74e}',             // 
+            "json" => '\u{e60b}',           // 
+            "jsx" => '\u{e7ba}',            // 
+            "ksh" => '\u{f489}',            // 
+            "latex" => '\u{e600}',          // 
+            "less" => '\u{e758}',           // 
+            "lhs" => '\u{e777}',            // 
+            "license" => '\u{f718}',        // 
+            "localized" => '\u{f179}',      // 
+            "lock" => '\u{f023}',           // 
+            "log" => '\u{f18d}',            // 
+            "lua" => '\u{e620}',            // 
+            "lz" => '\u{f410}',             // 
+            "lzh" => '\u{f410}',            // 
+            "lzma" => '\u{f410}',           // 
+            "lzo" => '\u{f410}',            // 
+            "m" => '\u{e61e}',              // 
+            "mm" => '\u{e61d}',             // 
+            "m4a" => '\u{f001}',            // 
+            "markdown" => '\u{f48a}',       // 
+            "md" => '\u{f48a}',             // 
+            "mjs" => '\u{e74e}',            // 
+            "mkd" => '\u{f48a}',            // 
+            "mkv" => '\u{f03d}',            // 
+            "mobi" => '\u{e28b}',           // 
+            "mov" => '\u{f03d}',            // 
+            "mp3" => '\u{f001}',            // 
+            "mp4" => '\u{f03d}',            // 
+            "msi" => '\u{e70f}',            // 
+            "mustache" => '\u{e60f}',       // 
+            "nix" => '\u{f313}',            // 
+            "node" => '\u{f898}',           // 
+            "npmignore" => '\u{e71e}',      // 
+            "odp" => '\u{f1c4}',            // 
+            "ods" => '\u{f1c3}',            // 
+            "odt" => '\u{f1c2}',            // 
+            "ogg" => '\u{f001}',            // 
+            "ogv" => '\u{f03d}',            // 
+            "otf" => '\u{f031}',            // 
+            "patch" => '\u{f440}',          // 
+            "pdf" => '\u{f1c1}',            // 
+            "php" => '\u{e73d}',            // 
+            "pl" => '\u{e769}',             // 
+            "png" => '\u{f1c5}',            // 
+            "ppt" => '\u{f1c4}',            // 
+            "pptx" => '\u{f1c4}',           // 
+            "procfile" => '\u{e21e}',       // 
+            "properties" => '\u{e60b}',     // 
+            "ps1" => '\u{f489}',            // 
+            "psd" => '\u{e7b8}',            // 
+            "pxm" => '\u{f1c5}',            // 
+            "py" => '\u{e606}',             // 
+            "pyc" => '\u{e606}',            // 
+            "r" => '\u{f25d}',              // 
+            "rakefile" => '\u{e21e}',       // 
+            "rar" => '\u{f410}',            // 
+            "razor" => '\u{f1fa}',          // 
+            "rb" => '\u{e21e}',             // 
+            "rdata" => '\u{f25d}',          // 
+            "rdb" => '\u{e76d}',            // 
+            "rdoc" => '\u{f48a}',           // 
+            "rds" => '\u{f25d}',            // 
+            "readme" => '\u{f48a}',         // 
+            "rlib" => '\u{e7a8}',           // 
+            "rmd" => '\u{f48a}',            // 
+            "rpm" => '\u{e7bb}',            // 
+            "rs" => '\u{e7a8}',             // 
+            "rspec" => '\u{e21e}',          // 
+            "rspec_parallel" => '\u{e21e}', // 
+            "rspec_status" => '\u{e21e}',   // 
+            "rss" => '\u{f09e}',            // 
+            "rtf" => '\u{f718}',            // 
+            "ru" => '\u{e21e}',             // 
+            "rubydoc" => '\u{e73b}',        // 
+            "sass" => '\u{e603}',           // 
+            "scala" => '\u{e737}',          // 
+            "scss" => '\u{e749}',           // 
+            "sh" => '\u{f489}',             // 
+            "shell" => '\u{f489}',          // 
+            "slim" => '\u{e73b}',           // 
+            "sln" => '\u{e70c}',            // 
+            "so" => '\u{f17c}',             // 
+            "sql" => '\u{f1c0}',            // 
+            "sqlite3" => '\u{e7c4}',        // 
+            "styl" => '\u{e600}',           // 
+            "stylus" => '\u{e600}',         // 
+            "svg" => '\u{f1c5}',            // 
+            "swift" => '\u{e755}',          // 
+            "tar" => '\u{f410}',            // 
+            "taz" => '\u{f410}',            // 
+            "tbz" => '\u{f410}',            // 
+            "tbz2" => '\u{f410}',           // 
+            "tex" => '\u{e600}',            // 
+            "tiff" => '\u{f1c5}',           // 
+            "toml" => '\u{e615}',           // 
+            "ts" => '\u{e628}',             // 
+            "tsv" => '\u{f1c3}',            // 
+            "tsx" => '\u{e7ba}',            // 
+            "ttf" => '\u{f031}',            // 
+            "twig" => '\u{e61c}',           // 
+            "txt" => '\u{f15c}',            // 
+            "tz" => '\u{f410}',             // 
+            "tzo" => '\u{f410}',            // 
+            "video" => '\u{f03d}',          // 
+            "vim" => '\u{e62b}',            // 
+            "vue" => '\u{fd42}',            // ﵂
+            "war" => '\u{e256}',            // 
+            "wav" => '\u{f001}',            // 
+            "webm" => '\u{f03d}',           // 
+            "webp" => '\u{f1c5}',           // 
+            "windows" => '\u{f17a}',        // 
+            "woff" => '\u{f031}',           // 
+            "woff2" => '\u{f031}',          // 
+            "xhtml" => '\u{f13b}',          // 
+            "xls" => '\u{f1c3}',            // 
+            "xlsx" => '\u{f1c3}',           // 
+            "xml" => '\u{fabf}',            // 謹
+            "xul" => '\u{fabf}',            // 謹
+            "xz" => '\u{f410}',             // 
+            "yaml" => '\u{f481}',           // 
+            "yml" => '\u{f481}',            // 
+            "zip" => '\u{f410}',            // 
+            "zsh" => '\u{f489}',            // 
+            "zsh-theme" => '\u{f489}',      // 
+            "zshrc" => '\u{f489}',          // 
+            _ => '\u{f15b}',                // 
+        }
+    } else {
+        '\u{f016}'
+    }
+}
+
+/// Whether this file’s extension is any of the strings that get passed in.
+///
+/// This will always return `false` if the file has no extension.
+pub fn extension_is_one_of(path: &Path, choices: &[&str]) -> bool {
+    match path.extension() {
+        Some(os_ext) => match os_ext.to_str() {
+            Some(ext) => choices.contains(&ext),
+            None => false,
+        },
+        None => false,
+    }
+}
+
+/// Whether this file’s name, including extension, is any of the strings
+/// that get passed in.
+// pub fn name_is_one_of(name: &str, choices: &[&str]) -> bool {
+//     choices.contains(&&name[..])
+// }
+
+#[derive(Debug, Default, PartialEq)]
+pub struct FileExtensions;
+
+// TODO: We may want to re-add these FileExtensions impl fns back. I have disabled
+// it now because it's hard coding colors which kind of defeats the LS_COLORS
+// functionality. We may want to enable and augment at some point.
+
+impl FileExtensions {
+    //     /// An “immediate” file is something that can be run or activated somehow
+    //     /// in order to kick off the build of a project. It’s usually only present
+    //     /// in directories full of source code.
+    //     #[allow(clippy::case_sensitive_file_extension_comparisons)]
+    //     #[allow(dead_code)]
+    //     fn is_immediate(&self, file_path: &Path) -> bool {
+    //         file_path
+    //             .file_name()
+    //             .unwrap()
+    //             .to_str()
+    //             .unwrap()
+    //             .to_lowercase()
+    //             .starts_with("readme")
+    //             || file_path
+    //                 .file_name()
+    //                 .unwrap()
+    //                 .to_str()
+    //                 .unwrap()
+    //                 .ends_with(".ninja")
+    //             || name_is_one_of(
+    //                 file_path.file_name().unwrap().to_str().unwrap(),
+    //                 &[
+    //                     "Makefile",
+    //                     "Cargo.toml",
+    //                     "SConstruct",
+    //                     "CMakeLists.txt",
+    //                     "build.gradle",
+    //                     "pom.xml",
+    //                     "Rakefile",
+    //                     "package.json",
+    //                     "Gruntfile.js",
+    //                     "Gruntfile.coffee",
+    //                     "BUILD",
+    //                     "BUILD.bazel",
+    //                     "WORKSPACE",
+    //                     "build.xml",
+    //                     "Podfile",
+    //                     "webpack.config.js",
+    //                     "meson.build",
+    //                     "composer.json",
+    //                     "RoboFile.php",
+    //                     "PKGBUILD",
+    //                     "Justfile",
+    //                     "Procfile",
+    //                     "Dockerfile",
+    //                     "Containerfile",
+    //                     "Vagrantfile",
+    //                     "Brewfile",
+    //                     "Gemfile",
+    //                     "Pipfile",
+    //                     "build.sbt",
+    //                     "mix.exs",
+    //                     "bsconfig.json",
+    //                     "tsconfig.json",
+    //                 ],
+    //             )
+    //     }
+
+    fn is_image(&self, file: &Path) -> bool {
+        extension_is_one_of(
+            file,
+            &[
+                "png", "jfi", "jfif", "jif", "jpe", "jpeg", "jpg", "gif", "bmp", "tiff", "tif",
+                "ppm", "pgm", "pbm", "pnm", "webp", "raw", "arw", "svg", "stl", "eps", "dvi", "ps",
+                "cbr", "jpf", "cbz", "xpm", "ico", "cr2", "orf", "nef", "heif", "avif", "jxl",
+            ],
+        )
+    }
+
+    fn is_video(&self, file: &Path) -> bool {
+        extension_is_one_of(
+            file,
+            &[
+                "avi", "flv", "m2v", "m4v", "mkv", "mov", "mp4", "mpeg", "mpg", "ogm", "ogv",
+                "vob", "wmv", "webm", "m2ts", "heic",
+            ],
+        )
+    }
+
+    fn is_music(&self, file: &Path) -> bool {
+        extension_is_one_of(file, &["aac", "m4a", "mp3", "ogg", "wma", "mka", "opus"])
+    }
+
+    // Lossless music, rather than any other kind of data...
+    fn is_lossless(&self, file: &Path) -> bool {
+        extension_is_one_of(file, &["alac", "ape", "flac", "wav"])
+    }
+
+    //     #[allow(dead_code)]
+    //     fn is_crypto(&self, file: &Path) -> bool {
+    //         extension_is_one_of(
+    //             file,
+    //             &["asc", "enc", "gpg", "pgp", "sig", "signature", "pfx", "p12"],
+    //         )
+    //     }
+
+    //     #[allow(dead_code)]
+    //     fn is_document(&self, file: &Path) -> bool {
+    //         extension_is_one_of(
+    //             file,
+    //             &[
+    //                 "djvu", "doc", "docx", "dvi", "eml", "eps", "fotd", "key", "keynote", "numbers",
+    //                 "odp", "odt", "pages", "pdf", "ppt", "pptx", "rtf", "xls", "xlsx",
+    //             ],
+    //         )
+    //     }
+
+    //     #[allow(dead_code)]
+    //     fn is_compressed(&self, file: &Path) -> bool {
+    //         extension_is_one_of(
+    //             file,
+    //             &[
+    //                 "zip", "tar", "Z", "z", "gz", "bz2", "a", "ar", "7z", "iso", "dmg", "tc", "rar",
+    //                 "par", "tgz", "xz", "txz", "lz", "tlz", "lzma", "deb", "rpm", "zst", "lz4",
+    //             ],
+    //         )
+    //     }
+
+    //     #[allow(dead_code)]
+    //     fn is_temp(&self, file: &Path) -> bool {
+    //         file.file_name().unwrap().to_str().unwrap().ends_with('~')
+    //             || (file.file_name().unwrap().to_str().unwrap().starts_with('#')
+    //                 && file.file_name().unwrap().to_str().unwrap().ends_with('#'))
+    //             || extension_is_one_of(file, &["tmp", "swp", "swo", "swn", "bak", "bkp", "bk"])
+    //     }
+
+    //     #[allow(dead_code)]
+    //     fn is_compiled(&self, file: &Path) -> bool {
+    //         if extension_is_one_of(file, &["class", "elc", "hi", "o", "pyc", "zwc", "ko"]) {
+    //             true
+    //         // } else if let Some(dir) = file.parent() {
+    //         //     file.get_source_files()
+    //         //         .iter()
+    //         // .any(|path| dir.contains(path))
+    //         } else {
+    //             false
+    //         }
+    //     }
+    // }
+
+    // impl FileColours for FileExtensions {
+    //     fn colour_file(&self, file: &Path) -> Option<Style> {
+    //         use ansi_term::Colour::*;
+
+    //         Some(match file {
+    //             f if self.is_temp(f)        => Fixed(244).normal(),
+    //             f if self.is_immediate(f)   => Yellow.bold().underline(),
+    //             f if self.is_image(f)       => Fixed(133).normal(),
+    //             f if self.is_video(f)       => Fixed(135).normal(),
+    //             f if self.is_music(f)       => Fixed(92).normal(),
+    //             f if self.is_lossless(f)    => Fixed(93).normal(),
+    //             f if self.is_crypto(f)      => Fixed(109).normal(),
+    //             f if self.is_document(f)    => Fixed(105).normal(),
+    //             f if self.is_compressed(f)  => Red.normal(),
+    //             f if self.is_compiled(f)    => Fixed(137).normal(),
+    //             _                           => return None,
+    //         })
+    //     }
+}
+
+impl FileIcon for FileExtensions {
+    fn icon_file(&self, file: &Path) -> Option<char> {
+        if self.is_music(file) || self.is_lossless(file) {
+            Some(Icons::Audio.value())
+        } else if self.is_image(file) {
+            Some(Icons::Image.value())
+        } else if self.is_video(file) {
+            Some(Icons::Video.value())
+        } else {
+            None
+        }
+    }
+}
diff --git a/crates/nu-command/src/viewers/mod.rs b/crates/nu-command/src/viewers/mod.rs
index 6715fb4..29eca7c 100644
--- a/crates/nu-command/src/viewers/mod.rs
+++ b/crates/nu-command/src/viewers/mod.rs
@@ -1,5 +1,6 @@
 mod color_config;
 mod griddle;
+mod icons;
 mod table;
 
 pub use griddle::Griddle;
diff --git a/crates/nu-command/src/viewers/table.rs b/crates/nu-command/src/viewers/table.rs
index 16a08a2..74e0317 100644
--- a/crates/nu-command/src/viewers/table.rs
+++ b/crates/nu-command/src/viewers/table.rs
@@ -107,7 +107,6 @@ impl Command for Table {
                 .into_pipeline_data())
             }
             PipelineData::Value(Value::Error { error }) => Err(error),
-            #[cfg(feature = "custom")]
             PipelineData::Value(Value::CustomValue { val, span }) => {
                 let base_pipeline = val.to_base_value(span)?.into_pipeline_data();
                 self.run(engine_state, stack, call, base_pipeline)
diff --git a/crates/nu-dataframe/Cargo.toml b/crates/nu-dataframe/Cargo.toml
deleted file mode 100644
index 86b86f3..0000000
--- a/crates/nu-dataframe/Cargo.toml
+++ /dev/null
@@ -1,14 +0,0 @@
-[package]
-name = "nu-dataframe"
-version = "0.1.0"
-edition = "2018"
-
-[dependencies]
-chrono = { version="0.4.19", features=["serde"] }
-serde = {version = "1.0.130", features = ["derive"]}
-num = "0.4.0"
-nu-protocol = { path = "../nu-protocol", features = ["custom"] }
-nu-json = { path = "../nu-json"}
-indexmap = { version="1.7.0", features=["serde-1"] }
-polars = { version = "0.17.0", features = ["default", "serde", "object", "checked_arithmetic", "strings"] }
-
diff --git a/crates/nu-engine/src/documentation.rs b/crates/nu-engine/src/documentation.rs
index caa6af5..a921f35 100644
--- a/crates/nu-engine/src/documentation.rs
+++ b/crates/nu-engine/src/documentation.rs
@@ -1,5 +1,7 @@
 use itertools::Itertools;
-use nu_protocol::{engine::EngineState, Example, Signature, Span, Value};
+use nu_protocol::{
+    engine::EngineState, Example, PositionalArg, Signature, Span, SyntaxShape, Value,
+};
 use std::collections::HashMap;
 
 const COMMANDS_DOCS_DIR: &str = "docs/commands";
@@ -171,10 +173,10 @@ pub fn get_documentation(
     one_liner.push(' ');
 
     for positional in &sig.required_positional {
-        one_liner.push_str(&format!("<{}> ", positional.name));
+        one_liner.push_str(&get_positional_short_name(positional, true));
     }
     for positional in &sig.optional_positional {
-        one_liner.push_str(&format!("({}) ", positional.name));
+        one_liner.push_str(&get_positional_short_name(positional, false));
     }
 
     if sig.rest_positional.is_some() {
@@ -204,10 +206,13 @@ pub fn get_documentation(
     {
         long_desc.push_str("\nParameters:\n");
         for positional in &sig.required_positional {
-            long_desc.push_str(&format!("  <{}> {}\n", positional.name, positional.desc));
+            long_desc.push_str(&format!("  {}: {}\n", positional.name, positional.desc));
         }
         for positional in &sig.optional_positional {
-            long_desc.push_str(&format!("  ({}) {}\n", positional.name, positional.desc));
+            long_desc.push_str(&format!(
+                "  (optional) {}: {}\n",
+                positional.name, positional.desc
+            ));
         }
 
         if let Some(rest_positional) = &sig.rest_positional {
@@ -241,6 +246,25 @@ pub fn get_documentation(
     long_desc
 }
 
+fn get_positional_short_name(arg: &PositionalArg, is_required: bool) -> String {
+    match &arg.shape {
+        SyntaxShape::Keyword(name, ..) => {
+            if is_required {
+                format!("{} <{}> ", String::from_utf8_lossy(name), arg.name)
+            } else {
+                format!("({} <{}>) ", String::from_utf8_lossy(name), arg.name)
+            }
+        }
+        _ => {
+            if is_required {
+                format!("<{}> ", arg.name)
+            } else {
+                format!("({}) ", arg.name)
+            }
+        }
+    }
+}
+
 fn get_flags_section(signature: &Signature) -> String {
     let mut long_desc = String::new();
     long_desc.push_str("\nFlags:\n");
diff --git a/crates/nu-engine/src/eval.rs b/crates/nu-engine/src/eval.rs
index ae1c4e9..59c4214 100644
--- a/crates/nu-engine/src/eval.rs
+++ b/crates/nu-engine/src/eval.rs
@@ -24,7 +24,8 @@ fn eval_call(
     call: &Call,
     input: PipelineData,
 ) -> Result<PipelineData, ShellError> {
-    let decl = engine_state.get_decl(call.decl_id);
+    let decl = engine_state.get_decl_with_input(call.decl_id, &input);
+
     if call.named.iter().any(|(flag, _)| flag.item == "help") {
         let full_help = get_full_help(&decl.signature(), &decl.examples(), engine_state);
         Ok(Value::String {
@@ -228,7 +229,6 @@ pub fn eval_expression(
         Expr::ImportPattern(_) => Ok(Value::Nothing {
             span: Span::unknown(),
         }),
-        Expr::RowCondition(_, expr) => eval_expression(engine_state, stack, expr),
         Expr::Call(call) => {
             // FIXME: protect this collect with ctrl-c
             Ok(
@@ -277,7 +277,7 @@ pub fn eval_expression(
                 Operator::Pow => lhs.pow(op_span, &rhs),
             }
         }
-        Expr::Subexpression(block_id) => {
+        Expr::RowCondition(block_id) | Expr::Subexpression(block_id) => {
             let block = engine_state.get_block(*block_id);
 
             // FIXME: protect this collect with ctrl-c
@@ -468,9 +468,9 @@ pub fn eval_variable(
         let mut output_cols = vec![];
         let mut output_vals = vec![];
 
-        let env_columns: Vec<_> = stack.get_env_vars().keys().map(|x| x.to_string()).collect();
-        let env_values: Vec<_> = stack
-            .get_env_vars()
+        let env_vars = stack.get_env_vars();
+        let env_columns: Vec<_> = env_vars.keys().map(|x| x.to_string()).collect();
+        let env_values: Vec<_> = env_vars
             .values()
             .map(|x| Value::String {
                 val: x.to_string(),
diff --git a/crates/nu-parser/src/flatten.rs b/crates/nu-parser/src/flatten.rs
index 1f4ea5e..dffef0c 100644
--- a/crates/nu-parser/src/flatten.rs
+++ b/crates/nu-parser/src/flatten.rs
@@ -194,7 +194,7 @@ pub fn flatten_expression(
             output
         }
         Expr::Keyword(_, span, expr) => {
-            let mut output = vec![(*span, FlatShape::Operator)];
+            let mut output = vec![(*span, FlatShape::InternalCall)];
             output.extend(flatten_expression(working_set, expr));
             output
         }
@@ -207,8 +207,7 @@ pub fn flatten_expression(
         Expr::String(_) => {
             vec![(expr.span, FlatShape::String)]
         }
-        Expr::RowCondition(_, expr) => flatten_expression(working_set, expr),
-        Expr::Subexpression(block_id) => {
+        Expr::RowCondition(block_id) | Expr::Subexpression(block_id) => {
             flatten_block(working_set, working_set.get_block(*block_id))
         }
         Expr::Table(headers, cells) => {
diff --git a/crates/nu-parser/src/parse_keywords.rs b/crates/nu-parser/src/parse_keywords.rs
index 8a3402f..8ee625f 100644
--- a/crates/nu-parser/src/parse_keywords.rs
+++ b/crates/nu-parser/src/parse_keywords.rs
@@ -6,7 +6,7 @@ use nu_protocol::{
     engine::StateWorkingSet,
     span, Exportable, Overlay, Span, SyntaxShape, Type, CONFIG_VARIABLE_ID,
 };
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
 use std::path::Path;
 
 #[cfg(feature = "plugin")]
@@ -711,6 +711,7 @@ pub fn parse_use(
                                     span: spans[1],
                                 },
                                 members: import_pattern.members,
+                                hidden: HashSet::new(),
                             },
                             overlay,
                         )
@@ -836,11 +837,14 @@ pub fn parse_hide(
                         },
                     )
                 } else {
-                    // TODO: Or it could be an env var
-                    return (
-                        garbage_statement(spans),
-                        Some(ParseError::ModuleNotFound(spans[1])),
-                    );
+                    // Or it could be an env var
+                    (
+                        false,
+                        Overlay {
+                            decls: HashMap::new(),
+                            env_vars: HashMap::new(),
+                        },
+                    )
                 }
             } else {
                 return (
@@ -893,6 +897,8 @@ pub fn parse_hide(
         // TODO: `use spam; use spam foo; hide foo` will hide both `foo` and `spam foo` since
         // they point to the same DeclId. Do we want to keep it that way?
         working_set.hide_decls(&decls_to_hide);
+        let import_pattern = import_pattern
+            .with_hidden(decls_to_hide.iter().map(|(name, _)| name.clone()).collect());
 
         // Create the Hide command call
         let hide_decl_id = working_set
diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs
index 120fe9f..1631cee 100644
--- a/crates/nu-parser/src/parser.rs
+++ b/crates/nu-parser/src/parser.rs
@@ -20,6 +20,8 @@ use crate::parse_keywords::{
     parse_alias, parse_def, parse_def_predecl, parse_hide, parse_let, parse_module, parse_use,
 };
 
+use std::collections::HashSet;
+
 #[cfg(feature = "plugin")]
 use crate::parse_keywords::parse_plugin;
 
@@ -1807,6 +1809,7 @@ pub fn parse_import_pattern(
                     span: Span::unknown(),
                 },
                 members: vec![],
+                hidden: HashSet::new(),
             },
             Some(ParseError::WrongImportPattern(span(spans))),
         );
@@ -1823,6 +1826,7 @@ pub fn parse_import_pattern(
                         span: *head_span,
                     },
                     members: vec![ImportPatternMember::Glob { span: *tail_span }],
+                    hidden: HashSet::new(),
                 },
                 error,
             )
@@ -1850,6 +1854,7 @@ pub fn parse_import_pattern(
                                 span: *head_span,
                             },
                             members: vec![ImportPatternMember::List { names: output }],
+                            hidden: HashSet::new(),
                         },
                         error,
                     )
@@ -1861,6 +1866,7 @@ pub fn parse_import_pattern(
                             span: *head_span,
                         },
                         members: vec![],
+                        hidden: HashSet::new(),
                     },
                     Some(ParseError::ExportNotFound(result.span)),
                 ),
@@ -1877,6 +1883,7 @@ pub fn parse_import_pattern(
                         name: tail.to_vec(),
                         span: *tail_span,
                     }],
+                    hidden: HashSet::new(),
                 },
                 error,
             )
@@ -1889,6 +1896,7 @@ pub fn parse_import_pattern(
                     span: *head_span,
                 },
                 members: vec![],
+                hidden: HashSet::new(),
             },
             None,
         )
@@ -1990,11 +1998,38 @@ pub fn parse_row_condition(
     let var_id = working_set.add_variable(b"$it".to_vec(), Type::Unknown);
     let (expression, err) = parse_math_expression(working_set, spans, Some(var_id));
     let span = span(spans);
+
+    let block_id = match expression.expr {
+        Expr::Block(block_id) => block_id,
+        _ => {
+            // We have an expression, so let's convert this into a block.
+            let mut block = Block::new();
+            let mut pipeline = Pipeline::new();
+            pipeline.expressions.push(expression);
+
+            block.stmts.push(Statement::Pipeline(pipeline));
+
+            block.signature.required_positional.push(PositionalArg {
+                name: "$it".into(),
+                desc: "row condition".into(),
+                shape: SyntaxShape::Any,
+                var_id: Some(var_id),
+            });
+
+            let mut seen = vec![];
+            let captures = find_captures_in_block(working_set, &block, &mut seen);
+
+            block.captures = captures;
+
+            working_set.add_block(block)
+        }
+    };
+
     (
         Expression {
             ty: Type::Bool,
             span,
-            expr: Expr::RowCondition(var_id, Box::new(expression)),
+            expr: Expr::RowCondition(block_id),
             custom_completion: None,
         },
         err,
@@ -3481,15 +3516,9 @@ pub fn find_captures_in_expr(
                 output.extend(&find_captures_in_expr(working_set, field_value, seen));
             }
         }
-        Expr::RowCondition(var_id, expr) => {
-            seen.push(*var_id);
-
-            let result = find_captures_in_expr(working_set, expr, seen);
-            output.extend(&result);
-        }
         Expr::Signature(_) => {}
         Expr::String(_) => {}
-        Expr::Subexpression(block_id) => {
+        Expr::RowCondition(block_id) | Expr::Subexpression(block_id) => {
             let block = working_set.get_block(*block_id);
             let result = find_captures_in_block(working_set, block, seen);
             output.extend(&result);
diff --git a/crates/nu-protocol/Cargo.toml b/crates/nu-protocol/Cargo.toml
index 84767c5..179518b 100644
--- a/crates/nu-protocol/Cargo.toml
+++ b/crates/nu-protocol/Cargo.toml
@@ -10,6 +10,7 @@ thiserror = "1.0.29"
 miette = "3.0.0"
 serde = {version = "1.0.130", features = ["derive"]}
 chrono = { version="0.4.19", features=["serde"] }
+indexmap = { version="1.7", features=["serde-1"] }
 chrono-humanize = "0.2.1"
 byte-unit = "4.0.9"
 im = "15.0.0"
@@ -19,7 +20,6 @@ typetag = "0.1.8"
 
 [features]
 plugin = ["serde_json"]
-custom = []
 
 [dev-dependencies]
 serde_json = "1.0"
diff --git a/crates/nu-protocol/src/ast/call.rs b/crates/nu-protocol/src/ast/call.rs
index 815e18a..f2f9a9c 100644
--- a/crates/nu-protocol/src/ast/call.rs
+++ b/crates/nu-protocol/src/ast/call.rs
@@ -46,6 +46,16 @@ impl Call {
         None
     }
 
+    pub fn get_named_arg(&self, flag_name: &str) -> Option<Spanned<String>> {
+        for name in &self.named {
+            if flag_name == name.0.item {
+                return Some(name.0.clone());
+            }
+        }
+
+        None
+    }
+
     pub fn nth(&self, pos: usize) -> Option<Expression> {
         self.positional.get(pos).cloned()
     }
diff --git a/crates/nu-protocol/src/ast/expr.rs b/crates/nu-protocol/src/ast/expr.rs
index 763347f..bdd0c61 100644
--- a/crates/nu-protocol/src/ast/expr.rs
+++ b/crates/nu-protocol/src/ast/expr.rs
@@ -17,7 +17,7 @@ pub enum Expr {
     Call(Box<Call>),
     ExternalCall(String, Span, Vec<Expression>),
     Operator(Operator),
-    RowCondition(VarId, Box<Expression>),
+    RowCondition(BlockId),
     BinaryOp(Box<Expression>, Box<Expression>, Box<Expression>), //lhs, op, rhs
     Subexpression(BlockId),
     Block(BlockId),
diff --git a/crates/nu-protocol/src/ast/expression.rs b/crates/nu-protocol/src/ast/expression.rs
index 11b0ac7..374a970 100644
--- a/crates/nu-protocol/src/ast/expression.rs
+++ b/crates/nu-protocol/src/ast/expression.rs
@@ -53,6 +53,13 @@ impl Expression {
         }
     }
 
+    pub fn as_row_condition_block(&self) -> Option<BlockId> {
+        match self.expr {
+            Expr::RowCondition(block_id) => Some(block_id),
+            _ => None,
+        }
+    }
+
     pub fn as_signature(&self) -> Option<Box<Signature>> {
         match &self.expr {
             Expr::Signature(sig) => Some(sig.clone()),
@@ -182,10 +189,9 @@ impl Expression {
                 }
                 false
             }
-            Expr::RowCondition(_, expr) => expr.has_in_variable(working_set),
             Expr::Signature(_) => false,
             Expr::String(_) => false,
-            Expr::Subexpression(block_id) => {
+            Expr::RowCondition(block_id) | Expr::Subexpression(block_id) => {
                 let block = working_set.get_block(*block_id);
 
                 if let Some(Statement::Pipeline(pipeline)) = block.stmts.get(0) {
@@ -311,10 +317,9 @@ impl Expression {
                     field_value.replace_in_variable(working_set, new_var_id);
                 }
             }
-            Expr::RowCondition(_, expr) => expr.replace_in_variable(working_set, new_var_id),
             Expr::Signature(_) => {}
             Expr::String(_) => {}
-            Expr::Subexpression(block_id) => {
+            Expr::RowCondition(block_id) | Expr::Subexpression(block_id) => {
                 let block = working_set.get_block(*block_id);
 
                 let new_expr = if let Some(Statement::Pipeline(pipeline)) = block.stmts.get(0) {
diff --git a/crates/nu-protocol/src/ast/import_pattern.rs b/crates/nu-protocol/src/ast/import_pattern.rs
index 0029c45..28f9a4a 100644
--- a/crates/nu-protocol/src/ast/import_pattern.rs
+++ b/crates/nu-protocol/src/ast/import_pattern.rs
@@ -1,4 +1,5 @@
 use crate::{span, Span};
+use std::collections::HashSet;
 
 #[derive(Debug, Clone)]
 pub enum ImportPatternMember {
@@ -17,6 +18,9 @@ pub struct ImportPatternHead {
 pub struct ImportPattern {
     pub head: ImportPatternHead,
     pub members: Vec<ImportPatternMember>,
+    // communicate to eval which decls/aliases were hidden during `parse_hide()` so it does not
+    // interpret these as env var names:
+    pub hidden: HashSet<Vec<u8>>,
 }
 
 impl ImportPattern {
@@ -37,4 +41,12 @@ impl ImportPattern {
 
         span(&spans)
     }
+
+    pub fn with_hidden(self, hidden: HashSet<Vec<u8>>) -> Self {
+        ImportPattern {
+            head: self.head,
+            members: self.members,
+            hidden,
+        }
+    }
 }
diff --git a/crates/nu-protocol/src/config.rs b/crates/nu-protocol/src/config.rs
index 1df1871..1dc9045 100644
--- a/crates/nu-protocol/src/config.rs
+++ b/crates/nu-protocol/src/config.rs
@@ -8,6 +8,7 @@ pub struct Config {
     pub table_mode: String,
     pub use_ls_colors: bool,
     pub color_config: HashMap<String, String>,
+    pub use_grid_icons: bool,
 }
 
 impl Default for Config {
@@ -17,6 +18,7 @@ impl Default for Config {
             table_mode: "rounded".into(),
             use_ls_colors: true,
             color_config: HashMap::new(),
+            use_grid_icons: false,
         }
     }
 }
@@ -46,6 +48,9 @@ impl Value {
                     }
                     config.color_config = hm;
                 }
+                "use_grid_icons" => {
+                    config.use_grid_icons = value.as_bool()?;
+                }
                 _ => {}
             }
         }
diff --git a/crates/nu-protocol/src/engine/engine_state.rs b/crates/nu-protocol/src/engine/engine_state.rs
index 176033b..7474e18 100644
--- a/crates/nu-protocol/src/engine/engine_state.rs
+++ b/crates/nu-protocol/src/engine/engine_state.rs
@@ -1,7 +1,7 @@
 use super::Command;
 use crate::{
-    ast::Block, BlockId, DeclId, Example, Overlay, OverlayId, ShellError, Signature, Span, Type,
-    VarId,
+    ast::Block, BlockId, DeclId, Example, Overlay, OverlayId, PipelineData, ShellError, Signature,
+    Span, Type, Value, VarId,
 };
 use core::panic;
 use std::{
@@ -357,6 +357,39 @@ impl EngineState {
             .expect("internal error: missing declaration")
     }
 
+    #[allow(clippy::borrowed_box)]
+    pub fn get_decl_with_input(&self, decl_id: DeclId, input: &PipelineData) -> &Box<dyn Command> {
+        let decl = self.get_decl(decl_id);
+
+        match input {
+            PipelineData::Stream(_) => decl,
+            PipelineData::Value(value) => match value {
+                Value::CustomValue { val, .. } => {
+                    // This filter works because the custom definitions were declared
+                    // before the default nushell declarations. This means that the custom
+                    // declarations that get overridden by the default declarations can only
+                    // be accessed if the input value has the required category
+                    let decls = self
+                        .decls
+                        .iter()
+                        .enumerate()
+                        .filter(|(_, decl_inner)| {
+                            decl.name() == decl_inner.name()
+                                && decl_inner.signature().category == val.category()
+                        })
+                        .map(|(index, _)| index)
+                        .collect::<Vec<usize>>();
+
+                    match decls.first() {
+                        Some(index) => self.get_decl(*index),
+                        None => decl,
+                    }
+                }
+                _ => decl,
+            },
+        }
+    }
+
     pub fn get_signatures(&self) -> Vec<Signature> {
         let mut output = vec![];
         for decl in self.decls.iter() {
@@ -384,6 +417,7 @@ impl EngineState {
             }
         }
 
+        output.sort_by(|a, b| a.0.name.cmp(&b.0.name));
         output
     }
 
diff --git a/crates/nu-protocol/src/engine/stack.rs b/crates/nu-protocol/src/engine/stack.rs
index 4457809..4a2e79a 100644
--- a/crates/nu-protocol/src/engine/stack.rs
+++ b/crates/nu-protocol/src/engine/stack.rs
@@ -21,8 +21,10 @@ use crate::{Config, ShellError, Value, VarId, CONFIG_VARIABLE_ID};
 /// use the Stack as a way of representing the local and closure-captured state.
 #[derive(Debug, Clone)]
 pub struct Stack {
+    /// Variables
     pub vars: HashMap<VarId, Value>,
-    pub env_vars: HashMap<String, String>,
+    /// Environment variables arranged as a stack to be able to recover values from parent scopes
+    pub env_vars: Vec<HashMap<String, String>>,
 }
 
 impl Default for Stack {
@@ -35,7 +37,7 @@ impl Stack {
     pub fn new() -> Stack {
         Stack {
             vars: HashMap::new(),
-            env_vars: HashMap::new(),
+            env_vars: vec![],
         }
     }
 
@@ -52,7 +54,11 @@ impl Stack {
     }
 
     pub fn add_env_var(&mut self, var: String, value: String) {
-        self.env_vars.insert(var, value);
+        if let Some(scope) = self.env_vars.last_mut() {
+            scope.insert(var, value);
+        } else {
+            self.env_vars.push(HashMap::from([(var, value)]));
+        }
     }
 
     pub fn collect_captures(&self, captures: &[VarId]) -> Stack {
@@ -68,6 +74,7 @@ impl Stack {
 
         // FIXME: this is probably slow
         output.env_vars = self.env_vars.clone();
+        output.env_vars.push(HashMap::new());
 
         let config = self
             .get_var(CONFIG_VARIABLE_ID)
@@ -77,19 +84,35 @@ impl Stack {
         output
     }
 
+    /// Flatten the env var scope frames into one frame
     pub fn get_env_vars(&self) -> HashMap<String, String> {
-        self.env_vars.clone()
+        let mut result = HashMap::new();
+
+        for scope in &self.env_vars {
+            result.extend(scope.clone());
+        }
+
+        result
     }
 
     pub fn get_env_var(&self, name: &str) -> Option<String> {
-        if let Some(v) = self.env_vars.get(name) {
-            return Some(v.to_string());
+        for scope in self.env_vars.iter().rev() {
+            if let Some(v) = scope.get(name) {
+                return Some(v.to_string());
+            }
         }
+
         None
     }
 
     pub fn remove_env_var(&mut self, name: &str) -> Option<String> {
-        self.env_vars.remove(name)
+        for scope in self.env_vars.iter_mut().rev() {
+            if let Some(v) = scope.remove(name) {
+                return Some(v);
+            }
+        }
+
+        None
     }
 
     pub fn get_config(&self) -> Result<Config, ShellError> {
@@ -109,9 +132,11 @@ impl Stack {
         for (var, val) in &self.vars {
             println!("  {}: {:?}", var, val);
         }
-        println!("env vars:");
-        for (var, val) in &self.env_vars {
-            println!("  {}: {:?}", var, val);
+        for (i, scope) in self.env_vars.iter().rev().enumerate() {
+            println!("env vars, scope {} (from the last);", i);
+            for (var, val) in scope {
+                println!("  {}: {:?}", var, val);
+            }
         }
     }
 }
diff --git a/crates/nu-protocol/src/lib.rs b/crates/nu-protocol/src/lib.rs
index f8d81b6..2e667f1 100644
--- a/crates/nu-protocol/src/lib.rs
+++ b/crates/nu-protocol/src/lib.rs
@@ -26,7 +26,5 @@ pub use signature::*;
 pub use span::*;
 pub use syntax_shape::*;
 pub use ty::*;
-pub use value::*;
-
-#[cfg(feature = "custom")]
 pub use value::CustomValue;
+pub use value::*;
diff --git a/crates/nu-protocol/src/pipeline_data.rs b/crates/nu-protocol/src/pipeline_data.rs
index 67f63ca..c47dfae 100644
--- a/crates/nu-protocol/src/pipeline_data.rs
+++ b/crates/nu-protocol/src/pipeline_data.rs
@@ -31,6 +31,7 @@ use crate::{ast::PathMember, Config, ShellError, Span, Value, ValueStream};
 /// * A balance of the two approaches is what we've landed on: Values are thread-safe to pass, and we can stream
 /// them into any sources. Streams are still available to model the infinite streams approach of original
 /// Nushell.
+#[derive(Debug)]
 pub enum PipelineData {
     Value(Value),
     Stream(ValueStream),
@@ -51,6 +52,16 @@ impl PipelineData {
         }
     }
 
+    pub fn into_interruptible_iter(self, ctrlc: Option<Arc<AtomicBool>>) -> PipelineIterator {
+        let mut iter = self.into_iter();
+
+        if let PipelineIterator(PipelineData::Stream(s)) = &mut iter {
+            s.ctrlc = ctrlc;
+        }
+
+        iter
+    }
+
     pub fn collect_string(self, separator: &str, config: &Config) -> String {
         match self {
             PipelineData::Value(v) => v.into_string(separator, config),
@@ -104,13 +115,10 @@ impl PipelineData {
             PipelineData::Value(Value::Range { val, .. }) => {
                 Ok(val.into_range_iter()?.map(f).into_pipeline_data(ctrlc))
             }
-            PipelineData::Value(v) => {
-                let output = f(v);
-                match output {
-                    Value::Error { error } => Err(error),
-                    v => Ok(v.into_pipeline_data()),
-                }
-            }
+            PipelineData::Value(v) => match f(v) {
+                Value::Error { error } => Err(error),
+                v => Ok(v.into_pipeline_data()),
+            },
         }
     }
 
@@ -153,10 +161,9 @@ impl PipelineData {
                 Ok(vals.into_iter().filter(f).into_pipeline_data(ctrlc))
             }
             PipelineData::Stream(stream) => Ok(stream.filter(f).into_pipeline_data(ctrlc)),
-            PipelineData::Value(Value::Range { val, .. }) => match val.into_range_iter() {
-                Ok(iter) => Ok(iter.filter(f).into_pipeline_data(ctrlc)),
-                Err(error) => Err(error),
-            },
+            PipelineData::Value(Value::Range { val, .. }) => {
+                Ok(val.into_range_iter()?.filter(f).into_pipeline_data(ctrlc))
+            }
             PipelineData::Value(v) => {
                 if f(&v) {
                     Ok(v.into_pipeline_data())
@@ -190,12 +197,12 @@ impl IntoIterator for PipelineData {
                 }))
             }
             PipelineData::Value(Value::Range { val, .. }) => match val.into_range_iter() {
-                Ok(val) => PipelineIterator(PipelineData::Stream(ValueStream {
-                    stream: Box::new(val),
+                Ok(iter) => PipelineIterator(PipelineData::Stream(ValueStream {
+                    stream: Box::new(iter),
                     ctrlc: None,
                 })),
-                Err(e) => PipelineIterator(PipelineData::Stream(ValueStream {
-                    stream: Box::new(vec![Value::Error { error: e }].into_iter()),
+                Err(error) => PipelineIterator(PipelineData::Stream(ValueStream {
+                    stream: Box::new(std::iter::once(Value::Error { error })),
                     ctrlc: None,
                 })),
             },
@@ -210,10 +217,7 @@ impl Iterator for PipelineIterator {
     fn next(&mut self) -> Option<Self::Item> {
         match &mut self.0 {
             PipelineData::Value(Value::Nothing { .. }) => None,
-            PipelineData::Value(v) => {
-                let prev = std::mem::take(v);
-                Some(prev)
-            }
+            PipelineData::Value(v) => Some(std::mem::take(v)),
             PipelineData::Stream(stream) => stream.next(),
         }
     }
@@ -223,9 +227,12 @@ pub trait IntoPipelineData {
     fn into_pipeline_data(self) -> PipelineData;
 }
 
-impl IntoPipelineData for Value {
+impl<V> IntoPipelineData for V
+where
+    V: Into<Value>,
+{
     fn into_pipeline_data(self) -> PipelineData {
-        PipelineData::Value(self)
+        PipelineData::Value(self.into())
     }
 }
 
@@ -233,13 +240,15 @@ pub trait IntoInterruptiblePipelineData {
     fn into_pipeline_data(self, ctrlc: Option<Arc<AtomicBool>>) -> PipelineData;
 }
 
-impl<T> IntoInterruptiblePipelineData for T
+impl<I> IntoInterruptiblePipelineData for I
 where
-    T: Iterator<Item = Value> + Send + 'static,
+    I: IntoIterator + Send + 'static,
+    I::IntoIter: Send + 'static,
+    <I::IntoIter as Iterator>::Item: Into<Value>,
 {
     fn into_pipeline_data(self, ctrlc: Option<Arc<AtomicBool>>) -> PipelineData {
         PipelineData::Stream(ValueStream {
-            stream: Box::new(self),
+            stream: Box::new(self.into_iter().map(Into::into)),
             ctrlc,
         })
     }
diff --git a/crates/nu-protocol/src/shell_error.rs b/crates/nu-protocol/src/shell_error.rs
index cdc1a84..56def77 100644
--- a/crates/nu-protocol/src/shell_error.rs
+++ b/crates/nu-protocol/src/shell_error.rs
@@ -76,6 +76,10 @@ pub enum ShellError {
     #[diagnostic(code(nu::shell::external_commands), url(docsrs))]
     ExternalNotSupported(#[label = "external not supported"] Span),
 
+    #[error("Invalid Probability.")]
+    #[diagnostic(code(nu::shell::invalid_probability), url(docsrs))]
+    InvalidProbability(#[label = "invalid probability"] Span),
+
     #[error("Internal error: {0}.")]
     #[diagnostic(code(nu::shell::internal_error), url(docsrs))]
     InternalError(String),
@@ -210,6 +214,10 @@ pub enum ShellError {
     #[error("Casting error")]
     #[diagnostic(code(nu::parser::downcast_not_possible), url(docsrs))]
     DowncastNotPossible(String, #[label("{0}")] Span),
+
+    #[error("{0}")]
+    #[diagnostic()]
+    LabeledError(String, String, #[label("{1}")] Span),
 }
 
 impl From<std::io::Error> for ShellError {
diff --git a/crates/nu-protocol/src/signature.rs b/crates/nu-protocol/src/signature.rs
index 81d8152..0b6b740 100644
--- a/crates/nu-protocol/src/signature.rs
+++ b/crates/nu-protocol/src/signature.rs
@@ -42,6 +42,9 @@ pub enum Category {
     Filters,
     Formats,
     Math,
+    Random,
+    Platform,
+    Shells,
     Strings,
     System,
     Viewers,
@@ -61,6 +64,9 @@ impl std::fmt::Display for Category {
             Category::Filters => "filters",
             Category::Formats => "formats",
             Category::Math => "math",
+            Category::Random => "random",
+            Category::Platform => "platform",
+            Category::Shells => "shells",
             Category::Strings => "strings",
             Category::System => "system",
             Category::Viewers => "viewers",
diff --git a/crates/nu-protocol/src/value/custom_value.rs b/crates/nu-protocol/src/value/custom_value.rs
index c11d352..92423d5 100644
--- a/crates/nu-protocol/src/value/custom_value.rs
+++ b/crates/nu-protocol/src/value/custom_value.rs
@@ -1,12 +1,14 @@
-use std::fmt;
+use std::{cmp::Ordering, fmt};
 
-use crate::{ast::Operator, ShellError, Span, Value};
+use crate::{ast::Operator, Category, ShellError, Span, Value};
 
 // Trait definition for a custom value
 #[typetag::serde(tag = "type")]
 pub trait CustomValue: fmt::Debug + Send + Sync {
     fn clone_value(&self, span: Span) -> Value;
 
+    fn category(&self) -> Category;
+
     // Define string representation of the custom value
     fn value_string(&self) -> String;
 
@@ -27,6 +29,9 @@ pub trait CustomValue: fmt::Debug + Send + Sync {
     fn follow_path_int(&self, count: usize, span: Span) -> Result<Value, ShellError>;
     fn follow_path_string(&self, column_name: String, span: Span) -> Result<Value, ShellError>;
 
+    // ordering with other value
+    fn partial_cmp(&self, other: &Value) -> Option<Ordering>;
+
     // Definition of an operation between the object that implements the trait
     // and another Value.
     // The Operator enum is used to indicate the expected operation
diff --git a/crates/nu-protocol/src/value/from.rs b/crates/nu-protocol/src/value/from.rs
index c5e3c25..5185bb9 100644
--- a/crates/nu-protocol/src/value/from.rs
+++ b/crates/nu-protocol/src/value/from.rs
@@ -1,5 +1,23 @@
 use crate::{ShellError, Span, Value};
 
+impl From<String> for Value {
+    fn from(val: String) -> Self {
+        Value::String {
+            val,
+            span: Span::unknown(),
+        }
+    }
+}
+
+impl From<bool> for Value {
+    fn from(val: bool) -> Self {
+        Value::Bool {
+            val,
+            span: Span::unknown(),
+        }
+    }
+}
+
 impl From<u8> for Value {
     fn from(val: u8) -> Self {
         Value::Int {
diff --git a/crates/nu-protocol/src/value/mod.rs b/crates/nu-protocol/src/value/mod.rs
index 7205816..8e041bc 100644
--- a/crates/nu-protocol/src/value/mod.rs
+++ b/crates/nu-protocol/src/value/mod.rs
@@ -6,6 +6,7 @@ mod unit;
 
 use chrono::{DateTime, FixedOffset};
 use chrono_humanize::HumanTime;
+use indexmap::map::IndexMap;
 pub use range::*;
 use serde::{Deserialize, Serialize};
 pub use stream::*;
@@ -17,10 +18,7 @@ use std::{cmp::Ordering, fmt::Debug};
 use crate::ast::{CellPath, PathMember};
 use crate::{did_you_mean, span, BlockId, Config, Span, Spanned, Type};
 
-#[cfg(feature = "custom")]
 use crate::ast::Operator;
-
-#[cfg(feature = "custom")]
 pub use custom_value::CustomValue;
 
 use crate::ShellError;
@@ -87,7 +85,6 @@ pub enum Value {
         val: CellPath,
         span: Span,
     },
-    #[cfg(feature = "custom")]
     CustomValue {
         val: Box<dyn CustomValue>,
         span: Span,
@@ -154,7 +151,6 @@ impl Clone for Value {
                 val: val.clone(),
                 span: *span,
             },
-            #[cfg(feature = "custom")]
             Value::CustomValue { val, span } => val.clone_value(*span),
         }
     }
@@ -223,7 +219,6 @@ impl Value {
             Value::Nothing { span, .. } => Ok(*span),
             Value::Binary { span, .. } => Ok(*span),
             Value::CellPath { span, .. } => Ok(*span),
-            #[cfg(feature = "custom")]
             Value::CustomValue { span, .. } => Ok(*span),
         }
     }
@@ -246,7 +241,6 @@ impl Value {
             Value::Error { .. } => {}
             Value::Binary { span, .. } => *span = new_span,
             Value::CellPath { span, .. } => *span = new_span,
-            #[cfg(feature = "custom")]
             Value::CustomValue { span, .. } => *span = new_span,
         }
 
@@ -276,7 +270,6 @@ impl Value {
             Value::Error { .. } => Type::Error,
             Value::Binary { .. } => Type::Binary,
             Value::CellPath { .. } => Type::CellPath,
-            #[cfg(feature = "custom")]
             Value::CustomValue { .. } => Type::Custom,
         }
     }
@@ -318,7 +311,6 @@ impl Value {
             Value::Error { error } => format!("{:?}", error),
             Value::Binary { val, .. } => format!("{:?}", val),
             Value::CellPath { val, .. } => val.into_string(),
-            #[cfg(feature = "custom")]
             Value::CustomValue { val, .. } => val.value_string(),
         }
     }
@@ -360,7 +352,6 @@ impl Value {
             Value::Error { error } => format!("{:?}", error),
             Value::Binary { val, .. } => format!("{:?}", val),
             Value::CellPath { val, .. } => val.into_string(),
-            #[cfg(feature = "custom")]
             Value::CustomValue { val, .. } => val.value_string(),
         }
     }
@@ -407,7 +398,6 @@ impl Value {
                                 return Err(ShellError::AccessBeyondEndOfStream(*origin_span));
                             }
                         }
-                        #[cfg(feature = "custom")]
                         Value::CustomValue { val, .. } => {
                             current = val.follow_path_int(*count, *origin_span)?;
                         }
@@ -458,7 +448,6 @@ impl Value {
                             span: *span,
                         };
                     }
-                    #[cfg(feature = "custom")]
                     Value::CustomValue { val, .. } => {
                         current = val.follow_path_string(column_name.clone(), *origin_span)?;
                     }
@@ -651,6 +640,7 @@ impl PartialOrd for Value {
             (Value::Binary { val: lhs, .. }, Value::Binary { val: rhs, .. }) => {
                 lhs.partial_cmp(rhs)
             }
+            (Value::CustomValue { val: lhs, .. }, rhs) => lhs.partial_cmp(rhs),
             (Value::Nothing { .. }, Value::Nothing { .. }) => Some(Ordering::Equal),
             (_, _) => None,
         }
@@ -724,7 +714,6 @@ impl Value {
                 }
             }
 
-            #[cfg(feature = "custom")]
             (Value::CustomValue { val: lhs, span }, rhs) => {
                 lhs.operation(*span, Operator::Plus, op, rhs)
             }
@@ -794,7 +783,6 @@ impl Value {
                 }
             }
 
-            #[cfg(feature = "custom")]
             (Value::CustomValue { val: lhs, span }, rhs) => {
                 lhs.operation(*span, Operator::Minus, op, rhs)
             }
@@ -834,7 +822,6 @@ impl Value {
                 val: lhs * rhs,
                 span,
             }),
-            #[cfg(feature = "custom")]
             (Value::CustomValue { val: lhs, span }, rhs) => {
                 lhs.operation(*span, Operator::Multiply, op, rhs)
             }
@@ -899,7 +886,6 @@ impl Value {
                     Err(ShellError::DivisionByZero(op))
                 }
             }
-            #[cfg(feature = "custom")]
             (Value::CustomValue { val: lhs, span }, rhs) => {
                 lhs.operation(*span, Operator::Divide, op, rhs)
             }
@@ -916,7 +902,6 @@ impl Value {
     pub fn lt(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
         let span = span(&[self.span()?, rhs.span()?]);
 
-        #[cfg(feature = "custom")]
         if let (Value::CustomValue { val: lhs, span }, rhs) = (self, rhs) {
             return lhs.operation(*span, Operator::LessThan, op, rhs);
         }
@@ -938,7 +923,6 @@ impl Value {
     pub fn lte(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
         let span = span(&[self.span()?, rhs.span()?]);
 
-        #[cfg(feature = "custom")]
         if let (Value::CustomValue { val: lhs, span }, rhs) = (self, rhs) {
             return lhs.operation(*span, Operator::LessThanOrEqual, op, rhs);
         }
@@ -960,7 +944,6 @@ impl Value {
     pub fn gt(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
         let span = span(&[self.span()?, rhs.span()?]);
 
-        #[cfg(feature = "custom")]
         if let (Value::CustomValue { val: lhs, span }, rhs) = (self, rhs) {
             return lhs.operation(*span, Operator::GreaterThan, op, rhs);
         }
@@ -982,7 +965,6 @@ impl Value {
     pub fn gte(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
         let span = span(&[self.span()?, rhs.span()?]);
 
-        #[cfg(feature = "custom")]
         if let (Value::CustomValue { val: lhs, span }, rhs) = (self, rhs) {
             return lhs.operation(*span, Operator::GreaterThanOrEqual, op, rhs);
         }
@@ -1004,7 +986,6 @@ impl Value {
     pub fn eq(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
         let span = span(&[self.span()?, rhs.span()?]);
 
-        #[cfg(feature = "custom")]
         if let (Value::CustomValue { val: lhs, span }, rhs) = (self, rhs) {
             return lhs.operation(*span, Operator::Equal, op, rhs);
         }
@@ -1026,7 +1007,6 @@ impl Value {
     pub fn ne(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
         let span = span(&[self.span()?, rhs.span()?]);
 
-        #[cfg(feature = "custom")]
         if let (Value::CustomValue { val: lhs, span }, rhs) = (self, rhs) {
             return lhs.operation(*span, Operator::NotEqual, op, rhs);
         }
@@ -1066,7 +1046,6 @@ impl Value {
                 val: rhs.contains(lhs),
                 span,
             }),
-            #[cfg(feature = "custom")]
             (Value::CustomValue { val: lhs, span }, rhs) => {
                 lhs.operation(*span, Operator::In, op, rhs)
             }
@@ -1100,7 +1079,6 @@ impl Value {
                 val: !rhs.contains(lhs),
                 span,
             }),
-            #[cfg(feature = "custom")]
             (Value::CustomValue { val: lhs, span }, rhs) => {
                 lhs.operation(*span, Operator::NotIn, op, rhs)
             }
@@ -1122,7 +1100,6 @@ impl Value {
                 val: lhs.contains(rhs),
                 span,
             }),
-            #[cfg(feature = "custom")]
             (Value::CustomValue { val: lhs, span }, rhs) => {
                 lhs.operation(*span, Operator::Contains, op, rhs)
             }
@@ -1144,7 +1121,6 @@ impl Value {
                 val: !lhs.contains(rhs),
                 span,
             }),
-            #[cfg(feature = "custom")]
             (Value::CustomValue { val: lhs, span }, rhs) => {
                 lhs.operation(*span, Operator::NotContains, op, rhs)
             }
@@ -1202,7 +1178,6 @@ impl Value {
                     Err(ShellError::DivisionByZero(op))
                 }
             }
-            #[cfg(feature = "custom")]
             (Value::CustomValue { val: lhs, span }, rhs) => {
                 lhs.operation(*span, Operator::Modulo, op, rhs)
             }
@@ -1225,7 +1200,6 @@ impl Value {
                 val: *lhs && *rhs,
                 span,
             }),
-            #[cfg(feature = "custom")]
             (Value::CustomValue { val: lhs, span }, rhs) => {
                 lhs.operation(*span, Operator::And, op, rhs)
             }
@@ -1247,7 +1221,6 @@ impl Value {
                 val: *lhs || *rhs,
                 span,
             }),
-            #[cfg(feature = "custom")]
             (Value::CustomValue { val: lhs, span }, rhs) => {
                 lhs.operation(*span, Operator::Or, op, rhs)
             }
@@ -1287,7 +1260,6 @@ impl Value {
                 val: lhs.powf(*rhs),
                 span,
             }),
-            #[cfg(feature = "custom")]
             (Value::CustomValue { val: lhs, span }, rhs) => {
                 lhs.operation(*span, Operator::Pow, op, rhs)
             }
@@ -1320,6 +1292,23 @@ impl From<Spanned<HashMap<String, Value>>> for Value {
     }
 }
 
+/// Create a Value::Record from a spanned indexmap
+impl From<Spanned<IndexMap<String, Value>>> for Value {
+    fn from(input: Spanned<IndexMap<String, Value>>) -> Self {
+        let span = input.span;
+        let (cols, vals) = input
+            .item
+            .into_iter()
+            .fold((vec![], vec![]), |mut acc, (k, v)| {
+                acc.0.push(k);
+                acc.1.push(v);
+                acc
+            });
+
+        Value::Record { cols, vals, span }
+    }
+}
+
 /// Format a duration in nanoseconds into a string
 pub fn format_duration(duration: i64) -> String {
     let (sign, duration) = if duration >= 0 {
diff --git a/src/main.rs b/src/main.rs
index ce096c1..d5dbe49 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -126,7 +126,7 @@ fn main() -> Result<()> {
         let mut stack = nu_protocol::engine::Stack::new();
 
         for (k, v) in std::env::vars() {
-            stack.env_vars.insert(k, v);
+            stack.add_env_var(k, v);
         }
 
         // Set up our initial config to start from
@@ -170,7 +170,7 @@ fn main() -> Result<()> {
         let mut stack = nu_protocol::engine::Stack::new();
 
         for (k, v) in std::env::vars() {
-            stack.env_vars.insert(k, v);
+            stack.add_env_var(k, v);
         }
 
         // Set up our initial config to start from
@@ -230,6 +230,18 @@ fn main() -> Result<()> {
             //Reset the ctrl-c handler
             ctrlc.store(false, Ordering::SeqCst);
 
+            // Get animation milliseconds every loop, if not set, the animation
+            // is defaulted to 1 second. If set to some non-number value,
+            // like "OFF", then the animation is disabled. If set to some number
+            // like "5000" then the animation will run at 5 seconds.
+            let animate_ms = match std::env::var("EQ_PROMPT_ANIMATE_MS") {
+                Ok(ms_str) => match ms_str.parse::<u64>() {
+                    Ok(ms_int) => Some(ms_int),
+                    _ => None,
+                },
+                _ => Some(1000),
+            };
+
             let line_editor = Reedline::create()
                 .into_diagnostic()?
                 .with_completion_action_handler(Box::new(FuzzyCompletion {
@@ -238,6 +250,7 @@ fn main() -> Result<()> {
                 .with_highlighter(Box::new(NuHighlighter {
                     engine_state: engine_state.clone(),
                 }))
+                .with_repaint(animate_ms)
                 // .with_completion_action_handler(Box::new(
                 //     ListCompletionHandler::default().with_completer(Box::new(completer)),
                 // ))
@@ -269,10 +282,6 @@ fn main() -> Result<()> {
             let input = line_editor.read_line(prompt);
             match input {
                 Ok(Signal::Success(s)) => {
-                    if s.trim() == "exit" {
-                        break;
-                    }
-
                     eval_source(
                         &mut engine_state,
                         &mut stack,


Test patch:
diff --git a/crates/nu-command/src/dataframe/nu_dataframe/commands/test_dataframe.rs b/crates/nu-command/src/dataframe/nu_dataframe/commands/test_dataframe.rs
new file mode 100644
index 0000000..653f393
--- /dev/null
+++ b/crates/nu-command/src/dataframe/nu_dataframe/commands/test_dataframe.rs
@@ -0,0 +1,87 @@
+use nu_engine::eval_block;
+use nu_parser::parse;
+use nu_protocol::{
+    engine::{Command, EngineState, Stack, StateWorkingSet},
+    PipelineData, Span, Value, CONFIG_VARIABLE_ID,
+};
+
+use super::ToDataFrame;
+
+pub fn test_dataframe(cmd: impl Command + 'static) {
+    let examples = cmd.examples();
+    let mut engine_state = Box::new(EngineState::new());
+
+    let delta = {
+        // Base functions that are needed for testing
+        // Try to keep this working set small to keep tests running as fast as possible
+        let mut working_set = StateWorkingSet::new(&*engine_state);
+        working_set.add_decl(Box::new(ToDataFrame));
+
+        // Adding the command that is being tested to the working set
+        working_set.add_decl(Box::new(cmd));
+
+        working_set.render()
+    };
+
+    let _ = engine_state.merge_delta(delta);
+
+    for example in examples {
+        // Skip tests that don't have results to compare to
+        if example.result.is_none() {
+            continue;
+        }
+        let start = std::time::Instant::now();
+
+        let (block, delta) = {
+            let mut working_set = StateWorkingSet::new(&*engine_state);
+            let (output, err) = parse(&mut working_set, None, example.example.as_bytes(), false);
+
+            if let Some(err) = err {
+                panic!("test parse error in `{}`: {:?}", example.example, err)
+            }
+
+            (output, working_set.render())
+        };
+
+        let _ = engine_state.merge_delta(delta);
+
+        let mut stack = Stack::new();
+
+        // Set up our initial config to start from
+        stack.vars.insert(
+            CONFIG_VARIABLE_ID,
+            Value::Record {
+                cols: vec![],
+                vals: vec![],
+                span: Span::unknown(),
+            },
+        );
+
+        match eval_block(
+            &engine_state,
+            &mut stack,
+            &block,
+            PipelineData::new(Span::unknown()),
+        ) {
+            Err(err) => panic!("test eval error in `{}`: {:?}", example.example, err),
+            Ok(result) => {
+                let result = result.into_value(Span::unknown());
+                println!("input: {}", example.example);
+                println!("result: {:?}", result);
+                println!("done: {:?}", start.elapsed());
+
+                // Note. Value implements PartialEq for Bool, Int, Float, String and Block
+                // If the command you are testing requires to compare another case, then
+                // you need to define its equality in the Value struct
+                if let Some(expected) = example.result {
+                    if result != expected {
+                        panic!(
+                            "the example result is different to expected value: {:?} != {:?}",
+                            result, expected
+                        )
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/crates/nu-command/src/example_test.rs b/crates/nu-command/src/example_test.rs
index 1c7d006..da5789c 100644
--- a/crates/nu-command/src/example_test.rs
+++ b/crates/nu-command/src/example_test.rs
@@ -7,7 +7,7 @@ use nu_protocol::{
 
 use crate::To;
 
-use super::{Date, From, Into, Math, Split, Str};
+use super::{Date, From, Into, Math, Random, Split, Str};
 
 pub fn test_examples(cmd: impl Command + 'static) {
     let examples = cmd.examples();
@@ -21,6 +21,7 @@ pub fn test_examples(cmd: impl Command + 'static) {
         working_set.add_decl(Box::new(From));
         working_set.add_decl(Box::new(To));
         working_set.add_decl(Box::new(Into));
+        working_set.add_decl(Box::new(Random));
         working_set.add_decl(Box::new(Split));
         working_set.add_decl(Box::new(Math));
         working_set.add_decl(Box::new(Date));
diff --git a/src/tests.rs b/src/tests.rs
index 1777777..aacf23f 100644
--- a/src/tests.rs
+++ b/src/tests.rs
@@ -474,6 +474,22 @@ fn module_env_imports_5() -> TestResult {
     )
 }
 
+#[test]
+fn module_def_and_env_imports_1() -> TestResult {
+    run_test(
+        r#"module spam { export env foo { "foo" }; export def foo [] { "bar" } }; use spam foo; $nu.env.foo"#,
+        "foo",
+    )
+}
+
+#[test]
+fn module_def_and_env_imports_2() -> TestResult {
+    run_test(
+        r#"module spam { export env foo { "foo" }; export def foo [] { "bar" } }; use spam foo; foo"#,
+        "bar",
+    )
+}
+
 #[test]
 fn module_def_import_uses_internal_command() -> TestResult {
     run_test(
@@ -496,7 +512,7 @@ fn hides_def() -> TestResult {
     fail_test(r#"def foo [] { "foo" }; hide foo; foo"#, not_found_msg())
 }
 
-#[ignore]
+#[test]
 fn hides_env() -> TestResult {
     fail_test(
         r#"let-env foo = "foo"; hide foo; $nu.env.foo"#,
@@ -514,7 +530,7 @@ fn hides_def_then_redefines() -> TestResult {
     )
 }
 
-#[ignore]
+#[test]
 fn hides_env_then_redefines() -> TestResult {
     run_test(
         r#"let-env foo = "foo"; hide foo; let-env foo = "bar"; $nu.env.foo"#,
@@ -554,7 +570,7 @@ fn hides_def_in_scope_4() -> TestResult {
     )
 }
 
-#[ignore]
+#[test]
 fn hides_env_in_scope_1() -> TestResult {
     fail_test(
         r#"let-env foo = "foo"; do { hide foo; $nu.env.foo }"#,
@@ -562,16 +578,15 @@ fn hides_env_in_scope_1() -> TestResult {
     )
 }
 
-#[ignore]
+#[test]
 fn hides_env_in_scope_2() -> TestResult {
-    // TODO: Revisit this -- 'hide foo' should restore the env, not hide it completely
     run_test(
         r#"let-env foo = "foo"; do { let-env foo = "bar"; hide foo; $nu.env.foo }"#,
         "foo",
     )
 }
 
-#[ignore]
+#[test]
 fn hides_env_in_scope_3() -> TestResult {
     fail_test(
         r#"let-env foo = "foo"; do { hide foo; let-env foo = "bar"; hide foo; $nu.env.foo }"#,
@@ -579,9 +594,8 @@ fn hides_env_in_scope_3() -> TestResult {
     )
 }
 
-#[ignore]
+#[test]
 fn hides_env_in_scope_4() -> TestResult {
-    // TODO: Revisit this -- 'hide foo' should restore the env, not hide it completely
     fail_test(
         r#"let-env foo = "foo"; do { let-env foo = "bar"; hide foo; hide foo; $nu.env.foo }"#,
         "did you mean",
@@ -590,31 +604,38 @@ fn hides_env_in_scope_4() -> TestResult {
 
 #[test]
 fn hide_def_twice_not_allowed() -> TestResult {
-    fail_test(r#"def foo [] { "foo" }; hide foo; hide foo"#, "not found")
+    fail_test(
+        r#"def foo [] { "foo" }; hide foo; hide foo"#,
+        "did not find",
+    )
 }
 
-#[ignore]
+#[test]
 fn hide_env_twice_not_allowed() -> TestResult {
     fail_test(r#"let-env foo = "foo"; hide foo; hide foo"#, "did not find")
 }
 
-#[ignore]
-fn hides_def_runs_env() -> TestResult {
-    // TODO: We need some precedence system to handle this. Currently, 'hide foo' hides both the
-    // def and env var.
+#[test]
+fn hides_def_runs_env_1() -> TestResult {
     run_test(
         r#"let-env foo = "bar"; def foo [] { "foo" }; hide foo; $nu.env.foo"#,
         "bar",
     )
 }
 
-#[ignore]
+#[test]
+fn hides_def_runs_env_2() -> TestResult {
+    run_test(
+        r#"def foo [] { "foo" }; let-env foo = "bar"; hide foo; $nu.env.foo"#,
+        "bar",
+    )
+}
+
+#[test]
 fn hides_def_and_env() -> TestResult {
-    // TODO: We need some precedence system to handle this. Currently, 'hide foo' hides both the
-    // def and env var.
     fail_test(
         r#"let-env foo = "bar"; def foo [] { "foo" }; hide foo; hide foo; $nu.env.foo"#,
-        not_found_msg(),
+        "did you mean",
     )
 }
 
@@ -666,7 +687,7 @@ fn hides_def_import_6() -> TestResult {
     )
 }
 
-#[ignore]
+#[test]
 fn hides_env_import_1() -> TestResult {
     fail_test(
         r#"module spam { export env foo { "foo" } }; use spam; hide spam foo; $nu.env.'spam foo'"#,
@@ -674,7 +695,7 @@ fn hides_env_import_1() -> TestResult {
     )
 }
 
-#[ignore]
+#[test]
 fn hides_env_import_2() -> TestResult {
     fail_test(
         r#"module spam { export env foo { "foo" } }; use spam; hide spam *; $nu.env.'spam foo'"#,
@@ -682,15 +703,15 @@ fn hides_env_import_2() -> TestResult {
     )
 }
 
-#[ignore]
+#[test]
 fn hides_env_import_3() -> TestResult {
     fail_test(
-        r#"module spam { export env foo { "foo" }; } use spam; hide spam [foo]; $nu.env.'spam foo'"#,
+        r#"module spam { export env foo { "foo" } }; use spam; hide spam [foo]; $nu.env.'spam foo'"#,
         "did you mean",
     )
 }
 
-#[ignore]
+#[test]
 fn hides_env_import_4() -> TestResult {
     fail_test(
         r#"module spam { export env foo { "foo" } }; use spam foo; hide foo; $nu.env.foo"#,
@@ -698,7 +719,7 @@ fn hides_env_import_4() -> TestResult {
     )
 }
 
-#[ignore]
+#[test]
 fn hides_env_import_5() -> TestResult {
     fail_test(
         r#"module spam { export env foo { "foo" } }; use spam *; hide foo; $nu.env.foo"#,
@@ -706,7 +727,7 @@ fn hides_env_import_5() -> TestResult {
     )
 }
 
-#[ignore]
+#[test]
 fn hides_env_import_6() -> TestResult {
     fail_test(
         r#"module spam { export env foo { "foo" } }; use spam; hide spam; $nu.env.'spam foo'"#,
@@ -714,6 +735,30 @@ fn hides_env_import_6() -> TestResult {
     )
 }
 
+#[test]
+fn hides_def_runs_env_import() -> TestResult {
+    run_test(
+        r#"module spam { export env foo { "foo" }; export def foo [] { "bar" } }; use spam foo; hide foo; $nu.env.foo"#,
+        "foo",
+    )
+}
+
+#[test]
+fn hides_def_and_env_import_1() -> TestResult {
+    fail_test(
+        r#"module spam { export env foo { "foo" }; export def foo [] { "bar" } }; use spam foo; hide foo; hide foo; $nu.env.foo"#,
+        "did you mean",
+    )
+}
+
+#[test]
+fn hides_def_and_env_import_2() -> TestResult {
+    fail_test(
+        r#"module spam { export env foo { "foo" }; export def foo [] { "bar" } }; use spam foo; hide foo; hide foo; foo"#,
+        not_found_msg(),
+    )
+}
+
 #[test]
 fn def_twice_should_fail() -> TestResult {
     fail_test(
@@ -730,7 +775,7 @@ fn use_def_import_after_hide() -> TestResult {
     )
 }
 
-#[ignore]
+#[test]
 fn use_env_import_after_hide() -> TestResult {
     run_test(
         r#"module spam { export env foo { "foo" } }; use spam foo; hide foo; use spam foo; $nu.env.foo"#,
@@ -746,9 +791,8 @@ fn hide_shadowed_decl() -> TestResult {
     )
 }
 
-#[ignore]
+#[test]
 fn hide_shadowed_env() -> TestResult {
-    // TODO: waiting for a fix
     run_test(
         r#"module spam { export env foo { "bar" } }; let-env foo = "foo"; do { use spam foo; hide foo; $nu.env.foo }"#,
         "foo",
@@ -763,7 +807,7 @@ fn hides_all_decls_within_scope() -> TestResult {
     )
 }
 
-#[ignore]
+#[test]
 fn hides_all_envs_within_scope() -> TestResult {
     fail_test(
         r#"module spam { export env foo { "bar" } }; let-env foo = "foo"; use spam foo; hide foo; $nu.env.foo"#,