forked from extern/nushell
Rewrite README and do some touchups
This commit is contained in:
parent
a3c3c4d776
commit
8cdd567b0e
306
README.md
306
README.md
@ -1,22 +1,169 @@
|
||||
# Nu Shell
|
||||
|
||||
A shell for the GitHub era. Like having a playground for a shell.
|
||||
Like having a playground for a shell.
|
||||
|
||||
# Status
|
||||
|
||||
This project has little of what will eventually be necessary for Nu to serve as your day-to-day shell. It already works well enough for contributors to dogfood it as their daily driver, but there are too many basic deficiencies for it to be useful for most people.
|
||||
This project is currently in its early stages, though it already works well enough for contributors to dogfood it as their daily driver. Its design is subject to change as it matures.
|
||||
|
||||
At the moment, executing a command that isn't identified as a built-in new command will fall back to running it as a shell command (using cmd on Windows or bash on Linux and MacOS), correctly passing through stdin, stdout and stderr, so things like your daily git workflows and even `vim` will work just fine.
|
||||
Nu has a list of built-in commands (listed belowed). If a command is unknown, the command will shell-out and execute it (using cmd on Windows or bash on Linux and MacOS), correctly passing through stdin, stdout and stderr, so things like your daily git workflows and even `vim` will work just fine.
|
||||
|
||||
## Commands
|
||||
# Philosophy
|
||||
|
||||
Nu draws heavy inspiration from projects like PowerShell. Rather than thinking of you filesystem and services as raw streams of text, Nu looks at each input as something with structure. When you list the contents of a directory, what you get back in a list of objects, where each object represents an item in that directory.
|
||||
|
||||
## Pipelines
|
||||
|
||||
Nu takes this a step further and builds heavily on the idea of _pipelines_. Just as the Unix philosophy, Nu allows commands to output from stdout and ready from stdin. Additionally, commands can output structured data (you can think of this as a kind of third stream). Commands that work in the pipeline fit into one of three categories
|
||||
|
||||
* Commands that produce a stream (eg, "ls")
|
||||
* Commands that filter a stream (eg, "where size > 10")
|
||||
* Commands that consumes the output of the pipeline (eg, "autoview")
|
||||
|
||||
```
|
||||
/home/jonathan/Source/nushell(master)> ls | where "file type" == "Directory" | autoview
|
||||
-----------+-----------+----------+--------+--------------+----------------
|
||||
file name | file type | readonly | size | accessed | modified
|
||||
-----------+-----------+----------+--------+--------------+----------------
|
||||
target | Directory | | 4.1 KB | 19 hours ago | 19 hours ago
|
||||
images | Directory | | 4.1 KB | 2 weeks ago | a week ago
|
||||
tests | Directory | | 4.1 KB | 2 weeks ago | 18 minutes ago
|
||||
docs | Directory | | 4.1 KB | a week ago | a week ago
|
||||
.git | Directory | | 4.1 KB | 2 weeks ago | 25 minutes ago
|
||||
src | Directory | | 4.1 KB | 2 weeks ago | 25 minutes ago
|
||||
.cargo | Directory | | 4.1 KB | 2 weeks ago | 2 weeks ago
|
||||
-----------+-----------+----------+--------+--------------+----------------
|
||||
```
|
||||
|
||||
Because most of the time you'll want to see the output of a pipeline, `autoview` is assumed, so we could have also written the above:
|
||||
|
||||
```
|
||||
/home/jonathan/Source/nushell(master)> ls | where "file type" == "Directory"
|
||||
```
|
||||
|
||||
Being able to use the same commands and compose them differently is an important philosophy in Nu. For example, we could use the built-in `ps` command as well to get a list of the running processes, using the same `where` as above.
|
||||
|
||||
```text
|
||||
C:\Code\nushell(master)> ps | where cpu > 0
|
||||
------------------ +-----+-------+-------+----------
|
||||
name | cmd | cpu | pid | status
|
||||
------------------ +-----+-------+-------+----------
|
||||
msedge.exe | - | 0.77 | 26472 | Runnable
|
||||
nu.exe | - | 7.83 | 15473 | Runnable
|
||||
SearchIndexer.exe | - | 82.17 | 23476 | Runnable
|
||||
BlueJeans.exe | - | 4.54 | 10000 | Runnable
|
||||
-------------------+-----+-------+-------+----------
|
||||
```
|
||||
|
||||
## Opening files
|
||||
|
||||
Nu can load file and URL contents as raw text or as structured data (if it recognizes the format). For example, you can load a .toml file as structured data and explore it:
|
||||
|
||||
```
|
||||
/home/jonathan/Source/nushell(master)> open Cargo.toml
|
||||
-----------------+------------------+-----------------
|
||||
dependencies | dev-dependencies | package
|
||||
-----------------+------------------+-----------------
|
||||
[object Object] | [object Object] | [object Object]
|
||||
-----------------+------------------+-----------------
|
||||
```
|
||||
|
||||
We can pipeline this into a command that gets the contents of one of the columns:
|
||||
|
||||
```
|
||||
/home/jonathan/Source/nushell(master)> open Cargo.toml | get package
|
||||
-------------+----------------------------+---------+---------+------+---------
|
||||
authors | description | edition | license | name | version
|
||||
-------------+----------------------------+---------+---------+------+---------
|
||||
[list List] | A shell for the GitHub era | 2018 | MIT | nu | 0.1.2
|
||||
-------------+----------------------------+---------+---------+------+---------
|
||||
```
|
||||
|
||||
Finally, we can use commands outside of Nu once we have the data we want:
|
||||
|
||||
```
|
||||
/home/jonathan/Source/nushell(master)> open Cargo.toml | get package.version | echo $it
|
||||
0.1.2
|
||||
```
|
||||
|
||||
Here we use the variable `$it` to refer to the value being piped to the external command.
|
||||
|
||||
|
||||
## Navigation
|
||||
|
||||
By default, Nu opens up into your filesystem and the current working directory. One way to think of this is a pair: the current object and the current path in the object. The filesystem is our first object, and the path is the cwd.
|
||||
|
||||
| object | path |
|
||||
| ------ | ---- |
|
||||
| Filesystem | /home/jonathan/Source/nushell |
|
||||
|
||||
Using the `cd` command allows you to change the path from the current path to a new path, just as you might expect. Using 'ls' allows you to view the contents of the filesystem at the current path (or at the path of your choosing).
|
||||
|
||||
In addition to `cd` and `ls`, we can `enter` an object. Entering an object makes it the current object to navigate (similar to the concept of mounting a filesystem in Unix systems).
|
||||
|
||||
```
|
||||
/home/jonathan/Source/nushell(master)> enter Cargo.toml
|
||||
object/>
|
||||
```
|
||||
|
||||
As we enter, we create a stack of objects we're navigating:
|
||||
|
||||
| object | path |
|
||||
| ------ | ---- |
|
||||
| Filesystem | /home/jonathan/Source/nushell |
|
||||
| object (from Cargo.toml) | / |
|
||||
|
||||
Commands `cd` and `ls` now work on the object being navigated.
|
||||
|
||||
```
|
||||
object/> ls
|
||||
-----------------+------------------+-----------------
|
||||
dependencies | dev-dependencies | package
|
||||
-----------------+------------------+-----------------
|
||||
[object Object] | [object Object] | [object Object]
|
||||
-----------------+------------------+-----------------
|
||||
```
|
||||
|
||||
```
|
||||
object/> cd package/version
|
||||
object/package/version> ls
|
||||
-------
|
||||
value
|
||||
-------
|
||||
0.1.2
|
||||
-------
|
||||
```
|
||||
|
||||
The `exit` command will pop the stack and get us back to a previous object we were navigating.
|
||||
|
||||
# Goals
|
||||
|
||||
Nu adheres closely to a set of goals that make up its design philosophy. As features are added, they are checked against these goals.
|
||||
|
||||
* First and foremost, Nu is cross-platform. Commands and techniques should carry between platforms and offer first-class consistent support for Windows, macOS, and Linux.
|
||||
|
||||
* Nu ensures direct compatibility with existing platform-specific executables that make up people's workflows.
|
||||
|
||||
* Nu's workflow and tools should have the usability in day-to-day experience of using a shell in 2019 (and beyond).
|
||||
|
||||
* Nu views data as both structured and unstructured. It is an object shell like PowerShell.
|
||||
|
||||
These goals are all critical, project-defining priorities. Priority #1 is "direct compatibility" because any new shell absolutely needs a way to use existing executables in a direct and natural way.
|
||||
|
||||
# Commands
|
||||
## Initial commands
|
||||
| command | description |
|
||||
| ------------- | ------------- |
|
||||
| cd directory | Change to the given directory |
|
||||
| ls | View current directory contents |
|
||||
| cd path | Change to a new path |
|
||||
| ls (path) | View the contents of the current or given path |
|
||||
| ps | View current processes |
|
||||
| open filename | Load a file into a cell, convert to table if possible (avoid by appending '--raw') |
|
||||
| open {filename or url} | Load a file into a cell, convert to table if possible (avoid by appending '--raw') |
|
||||
| enter {filename or url} | Enter (mount) the given contents as the current object |
|
||||
| exit | Leave/pop from the current object (exits if in filesystem object) |
|
||||
| save filename | Save the incoming object to this filepath |
|
||||
| tree | View the incoming object as a tree |
|
||||
|
||||
## Commands on tables
|
||||
## Filters on tables (structured data)
|
||||
| command | description |
|
||||
| ------------- | ------------- |
|
||||
| pick ...columns | Down-select table to only these columns |
|
||||
@ -30,7 +177,7 @@ At the moment, executing a command that isn't identified as a built-in new comma
|
||||
| to-json | Convert table into .json text |
|
||||
| to-toml | Convert table into .toml text |
|
||||
|
||||
## Commands on text
|
||||
## Filters on text (unstructured data)
|
||||
| command | description |
|
||||
| ------------- | ------------- |
|
||||
| from-json | Parse text as .json and create table |
|
||||
@ -42,142 +189,15 @@ At the moment, executing a command that isn't identified as a built-in new comma
|
||||
| trim | Trim leading and following whitespace from text data |
|
||||
| {external-command} $it | Run external command with given arguments, replacing $it with each row text |
|
||||
|
||||
# Goals
|
||||
|
||||
Prime Directive: Cross platform workflows, with first-class consistent support for Windows, OSX and Linux.
|
||||
|
||||
Priority #1: direct compatibility with existing platform-specific executables that make up people's workflows
|
||||
|
||||
Priority #2: Create workflow tools that more closely match the day-to-day experience of using a shell in 2019 (and beyond)
|
||||
|
||||
Priority #3: It's an object shell like PowerShell.
|
||||
|
||||
> These goals are all critical, project-defining priorities. Priority #1 is "direct compatibility" because any new shell absolutely needs a way to use existing executables in a direct and natural way.
|
||||
## Consuming commands
|
||||
| command | description |
|
||||
| ------------- | ------------- |
|
||||
| autoview | View the contents of the pipeline as a table or list |
|
||||
| clip | Copy the contents of the pipeline fo the copy/paste buffer |
|
||||
| save filename | Save the contents of the pipeline to a file |
|
||||
| tree | View the contents of the pipeline as a tree |
|
||||
|
||||
# License
|
||||
|
||||
The project is made available under the MIT license. See "LICENSE" for more information.
|
||||
|
||||
# A Taste of Nu
|
||||
|
||||
Nu has built-in commands for ls and ps, loading these results into a table you can work with.
|
||||
|
||||
```text
|
||||
~\Code\nushell> ps | where cpu > 0
|
||||
------------------------------------------------
|
||||
name cmd cpu pid status
|
||||
------------------------------------------------
|
||||
msedge.exe - 0.77 26472 Runnable
|
||||
------------------------------------------------
|
||||
nu.exe - 7.83 15473 Runnable
|
||||
------------------------------------------------
|
||||
SearchIndexer.exe - 82.17 23476 Runnable
|
||||
------------------------------------------------
|
||||
BlueJeans.exe - 4.54 10000 Runnable
|
||||
------------------------------------------------
|
||||
```
|
||||
|
||||
Commands are linked together with pipes, allowing you to select the data you want to use.
|
||||
|
||||
```text
|
||||
~\Code\nushell> ps | where name == chrome.exe | first 5
|
||||
----------------------------------------
|
||||
name cmd cpu pid status
|
||||
----------------------------------------
|
||||
chrome.exe - 0.00 22092 Runnable
|
||||
----------------------------------------
|
||||
chrome.exe - 0.00 17324 Runnable
|
||||
----------------------------------------
|
||||
chrome.exe - 0.00 16376 Runnable
|
||||
----------------------------------------
|
||||
chrome.exe - 0.00 21876 Runnable
|
||||
----------------------------------------
|
||||
chrome.exe - 0.00 13432 Runnable
|
||||
----------------------------------------
|
||||
```
|
||||
|
||||
The name of the columns in the table can be used to sort the table.
|
||||
|
||||
```text
|
||||
~\Code\nushell> ls | sort-by "file type" size
|
||||
----------------------------------------------------------------------------------------
|
||||
file name file type readonly size created accessed modified
|
||||
----------------------------------------------------------------------------------------
|
||||
.cargo Directory Empty a week ago a week ago a week ago
|
||||
----------------------------------------------------------------------------------------
|
||||
.git Directory Empty 2 weeks ago 9 hours ago 9 hours ago
|
||||
----------------------------------------------------------------------------------------
|
||||
images Directory Empty 2 weeks ago 2 weeks ago 2 weeks ago
|
||||
----------------------------------------------------------------------------------------
|
||||
src Directory Empty 2 weeks ago 10 hours ago 10 hours ago
|
||||
----------------------------------------------------------------------------------------
|
||||
target Directory Empty 10 hours ago 10 hours ago 10 hours ago
|
||||
----------------------------------------------------------------------------------------
|
||||
tests Directory Empty 14 hours ago 10 hours ago 10 hours ago
|
||||
----------------------------------------------------------------------------------------
|
||||
tmp Directory Empty 2 days ago 2 days ago 2 days ago
|
||||
----------------------------------------------------------------------------------------
|
||||
rustfmt.toml File 16 B a week ago a week ago a week ago
|
||||
----------------------------------------------------------------------------------------
|
||||
.gitignore File 32 B 2 weeks ago 2 weeks ago 2 weeks ago
|
||||
----------------------------------------------------------------------------------------
|
||||
.editorconfig File 156 B 2 weeks ago 2 weeks ago 2 weeks ago
|
||||
----------------------------------------------------------------------------------------
|
||||
```
|
||||
|
||||
You can also use the names of the columns to down-select to only the data you want.
|
||||
```text
|
||||
~\Code\nushell> ls | pick "file name" "file type" size | sort-by "file type"
|
||||
------------------------------------
|
||||
file name file type size
|
||||
------------------------------------
|
||||
.cargo Directory Empty
|
||||
------------------------------------
|
||||
.git Directory Empty
|
||||
------------------------------------
|
||||
images Directory Empty
|
||||
------------------------------------
|
||||
src Directory Empty
|
||||
------------------------------------
|
||||
target Directory Empty
|
||||
------------------------------------
|
||||
tests Directory Empty
|
||||
------------------------------------
|
||||
rustfmt.toml File 16 B
|
||||
------------------------------------
|
||||
.gitignore File 32 B
|
||||
------------------------------------
|
||||
.editorconfig File 156 B
|
||||
------------------------------------
|
||||
```
|
||||
|
||||
Some file types can be loaded as tables.
|
||||
|
||||
```text
|
||||
~\Code\nushell> open Cargo.toml
|
||||
----------------------------------------------------
|
||||
dependencies dev-dependencies package
|
||||
----------------------------------------------------
|
||||
[object Object] [object Object] [object Object]
|
||||
----------------------------------------------------
|
||||
|
||||
~\Code\nushell> open Cargo.toml | get package
|
||||
--------------------------------------------------------------------------
|
||||
authors description edition license name version
|
||||
--------------------------------------------------------------------------
|
||||
[list List] A shell for the GitHub era 2018 MIT nu 0.1.1
|
||||
--------------------------------------------------------------------------
|
||||
```
|
||||
|
||||
Once you've found the data, you can call out to external applications and use it.
|
||||
|
||||
```text
|
||||
~\Code\nushell> open Cargo.toml | get package.version | echo $it
|
||||
0.1.1
|
||||
```
|
||||
|
||||
Nu currently has fish-style completion of previous commands, as well ctrl-r reverse search.
|
||||
|
||||
![autocompletion][fish-style]
|
||||
|
||||
[fish-style]: ./images/nushell-autocomplete3.gif "Fish-style autocomplete"
|
||||
|
@ -85,43 +85,54 @@ pub fn enter(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
let mut stream = VecDeque::new();
|
||||
|
||||
let open_raw = match args.positional.get(1) {
|
||||
let file_extension = match args.positional.get(1) {
|
||||
Some(Spanned {
|
||||
item: Value::Primitive(Primitive::String(s)),
|
||||
..
|
||||
}) if s == "--raw" => true,
|
||||
Some(v) => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Unknown flag for open",
|
||||
"unknown flag",
|
||||
v.span,
|
||||
))
|
||||
span,
|
||||
}) => {
|
||||
if s == "--raw" {
|
||||
None
|
||||
} else if s == "--json" {
|
||||
Some("json".to_string())
|
||||
} else if s == "--xml" {
|
||||
Some("xml".to_string())
|
||||
} else if s == "--yaml" {
|
||||
Some("yaml".to_string())
|
||||
} else if s == "--toml" {
|
||||
Some("toml".to_string())
|
||||
} else {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Unknown flag for open",
|
||||
"unknown flag",
|
||||
span.clone(),
|
||||
));
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
_ => file_extension,
|
||||
};
|
||||
|
||||
match file_extension {
|
||||
Some(x) if x == "toml" && !open_raw => {
|
||||
Some(x) if x == "toml" => {
|
||||
stream.push_back(ReturnValue::Action(CommandAction::Enter(
|
||||
crate::commands::from_toml::from_toml_string_to_value(contents),
|
||||
)));
|
||||
}
|
||||
Some(x) if x == "json" && !open_raw => {
|
||||
Some(x) if x == "json" => {
|
||||
stream.push_back(ReturnValue::Action(CommandAction::Enter(
|
||||
crate::commands::from_json::from_json_string_to_value(contents),
|
||||
)));
|
||||
}
|
||||
Some(x) if x == "xml" && !open_raw => {
|
||||
Some(x) if x == "xml" => {
|
||||
stream.push_back(ReturnValue::Action(CommandAction::Enter(
|
||||
crate::commands::from_xml::from_xml_string_to_value(contents),
|
||||
)));
|
||||
}
|
||||
Some(x) if x == "yml" && !open_raw => {
|
||||
Some(x) if x == "yml" => {
|
||||
stream.push_back(ReturnValue::Action(CommandAction::Enter(
|
||||
crate::commands::from_yaml::from_yaml_string_to_value(contents),
|
||||
)));
|
||||
}
|
||||
Some(x) if x == "yaml" && !open_raw => {
|
||||
Some(x) if x == "yaml" => {
|
||||
stream.push_back(ReturnValue::Action(CommandAction::Enter(
|
||||
crate::commands::from_yaml::from_yaml_string_to_value(contents),
|
||||
)));
|
||||
|
@ -1,11 +1,8 @@
|
||||
use crate::commands::command::CommandAction;
|
||||
use crate::errors::ShellError;
|
||||
use crate::object::{Primitive, Value};
|
||||
use crate::parser::lexer::Spanned;
|
||||
use crate::prelude::*;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub fn exit(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
pub fn exit(_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let mut stream = VecDeque::new();
|
||||
stream.push_back(ReturnValue::Action(CommandAction::Exit));
|
||||
Ok(stream.boxed())
|
||||
|
@ -84,43 +84,54 @@ pub fn open(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
let mut stream = VecDeque::new();
|
||||
|
||||
let open_raw = match args.positional.get(1) {
|
||||
let file_extension = match args.positional.get(1) {
|
||||
Some(Spanned {
|
||||
item: Value::Primitive(Primitive::String(s)),
|
||||
..
|
||||
}) if s == "--raw" => true,
|
||||
Some(v) => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Unknown flag for open",
|
||||
"unknown flag",
|
||||
v.span,
|
||||
))
|
||||
span,
|
||||
}) => {
|
||||
if s == "--raw" {
|
||||
None
|
||||
} else if s == "--json" {
|
||||
Some("json".to_string())
|
||||
} else if s == "--xml" {
|
||||
Some("xml".to_string())
|
||||
} else if s == "--yaml" {
|
||||
Some("yaml".to_string())
|
||||
} else if s == "--toml" {
|
||||
Some("toml".to_string())
|
||||
} else {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Unknown flag for open",
|
||||
"unknown flag",
|
||||
span.clone(),
|
||||
));
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
_ => file_extension,
|
||||
};
|
||||
|
||||
match file_extension {
|
||||
Some(x) if x == "toml" && !open_raw => {
|
||||
Some(x) if x == "toml" => {
|
||||
stream.push_back(ReturnValue::Value(
|
||||
crate::commands::from_toml::from_toml_string_to_value(contents),
|
||||
));
|
||||
}
|
||||
Some(x) if x == "json" && !open_raw => {
|
||||
Some(x) if x == "json" => {
|
||||
stream.push_back(ReturnValue::Value(
|
||||
crate::commands::from_json::from_json_string_to_value(contents),
|
||||
));
|
||||
}
|
||||
Some(x) if x == "xml" && !open_raw => {
|
||||
Some(x) if x == "xml" => {
|
||||
stream.push_back(ReturnValue::Value(
|
||||
crate::commands::from_xml::from_xml_string_to_value(contents),
|
||||
));
|
||||
}
|
||||
Some(x) if x == "yml" && !open_raw => {
|
||||
Some(x) if x == "yml" => {
|
||||
stream.push_back(ReturnValue::Value(
|
||||
crate::commands::from_yaml::from_yaml_string_to_value(contents),
|
||||
));
|
||||
}
|
||||
Some(x) if x == "yaml" && !open_raw => {
|
||||
Some(x) if x == "yaml" => {
|
||||
stream.push_back(ReturnValue::Value(
|
||||
crate::commands::from_yaml::from_yaml_string_to_value(contents),
|
||||
));
|
||||
|
@ -1,9 +1,9 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::PathBuf;
|
||||
use std::io::prelude::*;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::error::Error;
|
||||
use std::io::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
fn test_helper(test_name: &str) {
|
||||
let mut baseline_path = PathBuf::new();
|
||||
@ -27,10 +27,10 @@ mod tests {
|
||||
let process = match Command::new(executable)
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.spawn() {
|
||||
|
||||
.spawn()
|
||||
{
|
||||
Ok(process) => process,
|
||||
Err(why) => panic!("Can't run test {}", why.description())
|
||||
Err(why) => panic!("Can't run test {}", why.description()),
|
||||
};
|
||||
|
||||
let baseline_out = std::fs::read_to_string(baseline_path).unwrap();
|
||||
@ -38,15 +38,13 @@ mod tests {
|
||||
let input_commands = std::fs::read_to_string(txt_path).unwrap();
|
||||
|
||||
match process.stdin.unwrap().write_all(input_commands.as_bytes()) {
|
||||
Err(why) => panic!("couldn't write to wc stdin: {}",
|
||||
why.description()),
|
||||
Ok(_) => {},
|
||||
Err(why) => panic!("couldn't write to wc stdin: {}", why.description()),
|
||||
Ok(_) => {}
|
||||
}
|
||||
|
||||
let mut s = String::new();
|
||||
match process.stdout.unwrap().read_to_string(&mut s) {
|
||||
Err(why) => panic!("couldn't read stdout: {}",
|
||||
why.description()),
|
||||
Err(why) => panic!("couldn't read stdout: {}", why.description()),
|
||||
Ok(_) => {
|
||||
let s = s.replace("\r\n", "\n");
|
||||
assert_eq!(s, baseline_out);
|
||||
@ -88,4 +86,9 @@ mod tests {
|
||||
fn split() {
|
||||
test_helper("split");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enter() {
|
||||
test_helper("enter");
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user