Merging fern line editor implementation
Implement a line editor instead of using Rustyline
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -3,7 +3,7 @@ target
|
||||
.idea
|
||||
*.iml
|
||||
/result*
|
||||
src/tests/snapshots
|
||||
*snapshots*
|
||||
*.log
|
||||
default.nix
|
||||
shell.nix
|
||||
|
||||
153
Cargo.lock
generated
153
Cargo.lock
generated
@@ -47,7 +47,7 @@ version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -58,7 +58,7 @@ checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -119,15 +119,6 @@ version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
version = "5.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892"
|
||||
dependencies = [
|
||||
"error-code",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.3"
|
||||
@@ -143,7 +134,7 @@ dependencies = [
|
||||
"encode_unicode",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -158,39 +149,6 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
||||
|
||||
[[package]]
|
||||
name = "endian-type"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "error-code"
|
||||
version = "3.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f"
|
||||
|
||||
[[package]]
|
||||
name = "fd-lock"
|
||||
version = "4.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"rustix",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fern"
|
||||
version = "0.1.0"
|
||||
@@ -202,7 +160,8 @@ dependencies = [
|
||||
"nix",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"rustyline",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -217,15 +176,6 @@ version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "home"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "insta"
|
||||
version = "1.42.2"
|
||||
@@ -257,33 +207,12 @@ version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "nibble_vec"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.29.0"
|
||||
@@ -350,16 +279,6 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "radix_trie"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd"
|
||||
dependencies = [
|
||||
"endian-type",
|
||||
"nibble_vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
@@ -389,65 +308,12 @@ version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustyline"
|
||||
version = "15.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ee1e066dc922e513bda599c6ccb5f3bb2b0ea5870a579448f2622993f0a9a2f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"clipboard-win",
|
||||
"fd-lock",
|
||||
"home",
|
||||
"libc",
|
||||
"log",
|
||||
"memchr",
|
||||
"nix",
|
||||
"radix_trie",
|
||||
"rustyline-derive",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
"utf8parse",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustyline-derive"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "327e9d075f6df7e25fbf594f1be7ef55cf0d567a6cb5112eeccbbd51ceb48e0d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "similar"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
@@ -489,15 +355,6 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
|
||||
@@ -17,7 +17,8 @@ insta = "1.42.2"
|
||||
nix = { version = "0.29.0", features = ["uio", "term", "user", "hostname", "fs", "default", "signal", "process", "event", "ioctl"] }
|
||||
pretty_assertions = "1.4.1"
|
||||
regex = "1.11.1"
|
||||
rustyline = { version = "15.0.0", features = [ "derive" ] }
|
||||
unicode-segmentation = "1.12.0"
|
||||
unicode-width = "0.2.0"
|
||||
|
||||
[[bin]]
|
||||
name = "fern"
|
||||
|
||||
@@ -24,7 +24,8 @@ use crate::signal::sig_setup;
|
||||
use crate::state::source_rc;
|
||||
use crate::prelude::*;
|
||||
use clap::Parser;
|
||||
use state::{read_vars, write_vars};
|
||||
use shopt::FernEditMode;
|
||||
use state::{read_shopts, read_vars, write_shopts, write_vars};
|
||||
|
||||
#[derive(Parser,Debug)]
|
||||
struct FernArgs {
|
||||
@@ -98,7 +99,11 @@ fn fern_interactive() {
|
||||
let mut readline_err_count: u32 = 0;
|
||||
|
||||
loop { // Main loop
|
||||
let input = match prompt::read_line() {
|
||||
let edit_mode = write_shopts(|opt| opt.query("prompt.edit_mode"))
|
||||
.unwrap()
|
||||
.map(|mode| mode.parse::<FernEditMode>().unwrap_or_default())
|
||||
.unwrap();
|
||||
let input = match prompt::read_line(edit_mode) {
|
||||
Ok(line) => {
|
||||
readline_err_count = 0;
|
||||
line
|
||||
|
||||
@@ -303,12 +303,6 @@ impl From<std::env::VarError> for ShErr {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<rustyline::error::ReadlineError> for ShErr {
|
||||
fn from(value: rustyline::error::ReadlineError) -> Self {
|
||||
ShErr::simple(ShErrKind::ParseErr, value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Errno> for ShErr {
|
||||
fn from(value: Errno) -> Self {
|
||||
ShErr::simple(ShErrKind::Errno, value.to_string())
|
||||
@@ -322,6 +316,7 @@ pub enum ShErrKind {
|
||||
ParseErr,
|
||||
InternalErr,
|
||||
ExecFail,
|
||||
HistoryReadErr,
|
||||
ResourceLimitExceeded,
|
||||
BadPermission,
|
||||
Errno,
|
||||
@@ -331,27 +326,30 @@ pub enum ShErrKind {
|
||||
FuncReturn(i32),
|
||||
LoopContinue(i32),
|
||||
LoopBreak(i32),
|
||||
ReadlineErr,
|
||||
Null
|
||||
}
|
||||
|
||||
impl Display for ShErrKind {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let output = match self {
|
||||
ShErrKind::IoErr => "I/O Error",
|
||||
ShErrKind::SyntaxErr => "Syntax Error",
|
||||
ShErrKind::ParseErr => "Parse Error",
|
||||
ShErrKind::InternalErr => "Internal Error",
|
||||
ShErrKind::ExecFail => "Execution Failed",
|
||||
ShErrKind::ResourceLimitExceeded => "Resource Limit Exceeded",
|
||||
ShErrKind::BadPermission => "Bad Permissions",
|
||||
ShErrKind::Errno => "ERRNO",
|
||||
ShErrKind::FileNotFound(file) => &format!("File not found: {file}"),
|
||||
ShErrKind::CmdNotFound(cmd) => &format!("Command not found: {cmd}"),
|
||||
ShErrKind::CleanExit(_) => "",
|
||||
ShErrKind::FuncReturn(_) => "",
|
||||
ShErrKind::LoopContinue(_) => "",
|
||||
ShErrKind::LoopBreak(_) => "",
|
||||
ShErrKind::Null => "",
|
||||
Self::IoErr => "I/O Error",
|
||||
Self::SyntaxErr => "Syntax Error",
|
||||
Self::ParseErr => "Parse Error",
|
||||
Self::InternalErr => "Internal Error",
|
||||
Self::HistoryReadErr => "History Parse Error",
|
||||
Self::ExecFail => "Execution Failed",
|
||||
Self::ResourceLimitExceeded => "Resource Limit Exceeded",
|
||||
Self::BadPermission => "Bad Permissions",
|
||||
Self::Errno => "ERRNO",
|
||||
Self::FileNotFound(file) => &format!("File not found: {file}"),
|
||||
Self::CmdNotFound(cmd) => &format!("Command not found: {cmd}"),
|
||||
Self::CleanExit(_) => "",
|
||||
Self::FuncReturn(_) => "",
|
||||
Self::LoopContinue(_) => "",
|
||||
Self::LoopBreak(_) => "",
|
||||
Self::ReadlineErr => "Line Read Error",
|
||||
Self::Null => "",
|
||||
};
|
||||
write!(f,"{output}")
|
||||
}
|
||||
|
||||
155
src/parse/mod.rs
155
src/parse/mod.rs
@@ -716,43 +716,7 @@ impl ParseStream {
|
||||
}
|
||||
|
||||
if !from_func_def {
|
||||
while self.check_redir() {
|
||||
let tk = self.next_tk().unwrap();
|
||||
node_tks.push(tk.clone());
|
||||
let redir_bldr = tk.span.as_str().parse::<RedirBldr>().unwrap();
|
||||
if redir_bldr.io_mode.is_none() {
|
||||
let path_tk = self.next_tk();
|
||||
|
||||
if path_tk.clone().is_none_or(|tk| tk.class == TkRule::EOI) {
|
||||
return Err(
|
||||
ShErr::full(
|
||||
ShErrKind::ParseErr,
|
||||
"Expected a filename after this redirection",
|
||||
tk.span.clone()
|
||||
)
|
||||
)
|
||||
};
|
||||
|
||||
let path_tk = path_tk.unwrap();
|
||||
node_tks.push(path_tk.clone());
|
||||
let redir_class = redir_bldr.class.unwrap();
|
||||
let pathbuf = PathBuf::from(path_tk.span.as_str());
|
||||
|
||||
let Ok(file) = get_redir_file(redir_class, pathbuf) else {
|
||||
self.panic_mode(&mut node_tks);
|
||||
return Err(parse_err_full(
|
||||
"Error opening file for redirection",
|
||||
&path_tk.span
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
let io_mode = IoMode::file(redir_bldr.tgt_fd.unwrap(), file);
|
||||
let redir_bldr = redir_bldr.with_io_mode(io_mode);
|
||||
let redir = redir_bldr.build();
|
||||
redirs.push(redir);
|
||||
}
|
||||
}
|
||||
self.parse_redir(&mut redirs, &mut node_tks)?;
|
||||
}
|
||||
|
||||
let node = Node {
|
||||
@@ -763,6 +727,37 @@ impl ParseStream {
|
||||
};
|
||||
Ok(Some(node))
|
||||
}
|
||||
fn parse_redir(&mut self, redirs: &mut Vec<Redir>, node_tks: &mut Vec<Tk>) -> ShResult<()> {
|
||||
while self.check_redir() {
|
||||
let tk = self.next_tk().unwrap();
|
||||
node_tks.push(tk.clone());
|
||||
let redir_bldr = tk.span.as_str().parse::<RedirBldr>().unwrap();
|
||||
if redir_bldr.io_mode.is_none() {
|
||||
let path_tk = self.next_tk();
|
||||
|
||||
if path_tk.clone().is_none_or(|tk| tk.class == TkRule::EOI) {
|
||||
return Err(
|
||||
ShErr::full(
|
||||
ShErrKind::ParseErr,
|
||||
"Expected a filename after this redirection",
|
||||
tk.span.clone()
|
||||
)
|
||||
)
|
||||
};
|
||||
|
||||
let path_tk = path_tk.unwrap();
|
||||
node_tks.push(path_tk.clone());
|
||||
let redir_class = redir_bldr.class.unwrap();
|
||||
let pathbuf = PathBuf::from(path_tk.span.as_str());
|
||||
|
||||
let io_mode = IoMode::file(redir_bldr.tgt_fd.unwrap(), pathbuf, redir_class);
|
||||
let redir_bldr = redir_bldr.with_io_mode(io_mode);
|
||||
let redir = redir_bldr.build();
|
||||
redirs.push(redir);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn parse_case(&mut self) -> ShResult<Option<Node>> {
|
||||
// Needs a pattern token
|
||||
// Followed by any number of CaseNodes
|
||||
@@ -938,42 +933,7 @@ impl ParseStream {
|
||||
}
|
||||
node_tks.push(self.next_tk().unwrap());
|
||||
|
||||
while self.check_redir() {
|
||||
let tk = self.next_tk().unwrap();
|
||||
node_tks.push(tk.clone());
|
||||
let redir_bldr = tk.span.as_str().parse::<RedirBldr>().unwrap();
|
||||
if redir_bldr.io_mode.is_none() {
|
||||
let path_tk = self.next_tk();
|
||||
|
||||
if path_tk.clone().is_none_or(|tk| tk.class == TkRule::EOI) {
|
||||
return Err(
|
||||
ShErr::full(
|
||||
ShErrKind::ParseErr,
|
||||
"Expected a filename after this redirection",
|
||||
tk.span.clone()
|
||||
)
|
||||
)
|
||||
};
|
||||
|
||||
let path_tk = path_tk.unwrap();
|
||||
node_tks.push(path_tk.clone());
|
||||
let redir_class = redir_bldr.class.unwrap();
|
||||
let pathbuf = PathBuf::from(path_tk.span.as_str());
|
||||
|
||||
let Ok(file) = get_redir_file(redir_class, pathbuf) else {
|
||||
self.panic_mode(&mut node_tks);
|
||||
return Err(parse_err_full(
|
||||
"Error opening file for redirection",
|
||||
&path_tk.span
|
||||
));
|
||||
};
|
||||
|
||||
let io_mode = IoMode::file(redir_bldr.tgt_fd.unwrap(), file);
|
||||
let redir_bldr = redir_bldr.with_io_mode(io_mode);
|
||||
let redir = redir_bldr.build();
|
||||
redirs.push(redir);
|
||||
}
|
||||
}
|
||||
self.parse_redir(&mut redirs, &mut node_tks)?;
|
||||
|
||||
self.assert_separator(&mut node_tks)?;
|
||||
|
||||
@@ -1040,42 +1000,7 @@ impl ParseStream {
|
||||
}
|
||||
node_tks.push(self.next_tk().unwrap());
|
||||
|
||||
while self.check_redir() {
|
||||
let tk = self.next_tk().unwrap();
|
||||
node_tks.push(tk.clone());
|
||||
let redir_bldr = tk.span.as_str().parse::<RedirBldr>().unwrap();
|
||||
if redir_bldr.io_mode.is_none() {
|
||||
let path_tk = self.next_tk();
|
||||
|
||||
if path_tk.clone().is_none_or(|tk| tk.class == TkRule::EOI) {
|
||||
return Err(
|
||||
ShErr::full(
|
||||
ShErrKind::ParseErr,
|
||||
"Expected a filename after this redirection",
|
||||
tk.span.clone()
|
||||
)
|
||||
)
|
||||
};
|
||||
|
||||
let path_tk = path_tk.unwrap();
|
||||
node_tks.push(path_tk.clone());
|
||||
let redir_class = redir_bldr.class.unwrap();
|
||||
let pathbuf = PathBuf::from(path_tk.span.as_str());
|
||||
|
||||
let Ok(file) = get_redir_file(redir_class, pathbuf) else {
|
||||
self.panic_mode(&mut node_tks);
|
||||
return Err(parse_err_full(
|
||||
"Error opening file for redirection",
|
||||
&path_tk.span
|
||||
));
|
||||
};
|
||||
|
||||
let io_mode = IoMode::file(redir_bldr.tgt_fd.unwrap(), file);
|
||||
let redir_bldr = redir_bldr.with_io_mode(io_mode);
|
||||
let redir = redir_bldr.build();
|
||||
redirs.push(redir);
|
||||
}
|
||||
}
|
||||
self.parse_redir(&mut redirs, &mut node_tks)?;
|
||||
|
||||
let node = Node {
|
||||
class: NdRule::ForNode { vars, arr, body },
|
||||
@@ -1256,15 +1181,7 @@ impl ParseStream {
|
||||
let redir_class = redir_bldr.class.unwrap();
|
||||
let pathbuf = PathBuf::from(path_tk.span.as_str());
|
||||
|
||||
let Ok(file) = get_redir_file(redir_class, pathbuf) else {
|
||||
self.panic_mode(&mut node_tks);
|
||||
return Err(parse_err_full(
|
||||
"Error opening file for redirection",
|
||||
&path_tk.span
|
||||
));
|
||||
};
|
||||
|
||||
let io_mode = IoMode::file(redir_bldr.tgt_fd.unwrap(), file);
|
||||
let io_mode = IoMode::file(redir_bldr.tgt_fd.unwrap(), pathbuf, redir_class);
|
||||
let redir_bldr = redir_bldr.with_io_mode(io_mode);
|
||||
let redir = redir_bldr.build();
|
||||
redirs.push(redir);
|
||||
@@ -1421,7 +1338,7 @@ fn node_is_punctuated(tokens: &[Tk]) -> bool {
|
||||
})
|
||||
}
|
||||
|
||||
fn get_redir_file(class: RedirType, path: PathBuf) -> ShResult<File> {
|
||||
pub fn get_redir_file(class: RedirType, path: PathBuf) -> ShResult<File> {
|
||||
let result = match class {
|
||||
RedirType::Input => {
|
||||
OpenOptions::new()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::{fmt::Debug, ops::{Deref, DerefMut}};
|
||||
|
||||
use crate::{libsh::{error::{ShErr, ShErrKind, ShResult}, utils::RedirVecUtils}, parse::Redir, prelude::*};
|
||||
use crate::{libsh::{error::{ShErr, ShErrKind, ShResult}, utils::RedirVecUtils}, parse::{get_redir_file, Redir, RedirType}, prelude::*};
|
||||
|
||||
// Credit to fish-shell for many of the implementation ideas present in this module
|
||||
// https://fishshell.com/
|
||||
@@ -8,7 +8,7 @@ use crate::{libsh::{error::{ShErr, ShErrKind, ShResult}, utils::RedirVecUtils},
|
||||
#[derive(Clone,Debug)]
|
||||
pub enum IoMode {
|
||||
Fd { tgt_fd: RawFd, src_fd: Arc<OwnedFd> },
|
||||
File { tgt_fd: RawFd, file: Arc<File> },
|
||||
File { tgt_fd: RawFd, path: PathBuf, mode: RedirType },
|
||||
Pipe { tgt_fd: RawFd, pipe: Arc<OwnedFd> },
|
||||
Buffer { buf: String, pipe: Arc<OwnedFd> }
|
||||
}
|
||||
@@ -18,9 +18,8 @@ impl IoMode {
|
||||
let src_fd = unsafe { OwnedFd::from_raw_fd(src_fd).into() };
|
||||
Self::Fd { tgt_fd, src_fd }
|
||||
}
|
||||
pub fn file(tgt_fd: RawFd, file: File) -> Self {
|
||||
let file = file.into();
|
||||
Self::File { tgt_fd, file }
|
||||
pub fn file(tgt_fd: RawFd, path: PathBuf, mode: RedirType) -> Self {
|
||||
Self::File { tgt_fd, path, mode }
|
||||
}
|
||||
pub fn pipe(tgt_fd: RawFd, pipe: OwnedFd) -> Self {
|
||||
let pipe = pipe.into();
|
||||
@@ -28,20 +27,27 @@ impl IoMode {
|
||||
}
|
||||
pub fn tgt_fd(&self) -> RawFd {
|
||||
match self {
|
||||
IoMode::Fd { tgt_fd, src_fd: _ } |
|
||||
IoMode::File { tgt_fd, file: _ } |
|
||||
IoMode::Pipe { tgt_fd, pipe: _ } => *tgt_fd,
|
||||
IoMode::Fd { tgt_fd, .. } |
|
||||
IoMode::File { tgt_fd, .. } |
|
||||
IoMode::Pipe { tgt_fd, .. } => *tgt_fd,
|
||||
_ => panic!()
|
||||
}
|
||||
}
|
||||
pub fn src_fd(&self) -> RawFd {
|
||||
match self {
|
||||
IoMode::Fd { tgt_fd: _, src_fd } => src_fd.as_raw_fd(),
|
||||
IoMode::File { tgt_fd: _, file } => file.as_raw_fd(),
|
||||
IoMode::File {..} => panic!("Attempted to obtain src_fd from file before opening"),
|
||||
IoMode::Pipe { tgt_fd: _, pipe } => pipe.as_raw_fd(),
|
||||
_ => panic!()
|
||||
}
|
||||
}
|
||||
pub fn open_file(mut self) -> ShResult<Self> {
|
||||
if let IoMode::File { tgt_fd, path, mode } = self {
|
||||
let file = get_redir_file(mode, path)?;
|
||||
self = IoMode::Fd { tgt_fd, src_fd: Arc::new(OwnedFd::from(file)) }
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
pub fn get_pipes() -> (Self,Self) {
|
||||
let (rpipe,wpipe) = pipe().unwrap();
|
||||
(
|
||||
@@ -149,6 +155,11 @@ impl<'e> IoFrame {
|
||||
self.save();
|
||||
for redir in &mut self.redirs {
|
||||
let io_mode = &mut redir.io_mode;
|
||||
flog!(DEBUG, io_mode);
|
||||
if let IoMode::File {..} = io_mode {
|
||||
*io_mode = io_mode.clone().open_file()?;
|
||||
};
|
||||
flog!(DEBUG, io_mode);
|
||||
let tgt_fd = io_mode.tgt_fd();
|
||||
let src_fd = io_mode.src_fd();
|
||||
dup2(src_fd, tgt_fd)?;
|
||||
|
||||
@@ -1,248 +0,0 @@
|
||||
use std::{env, mem, os::unix::fs::PermissionsExt, path::{Path, PathBuf}, sync::Arc};
|
||||
use crate::builtin::BUILTINS;
|
||||
|
||||
use rustyline::highlight::Highlighter;
|
||||
use crate::{libsh::term::{Style, StyleSet, Styled}, parse::lex::{LexFlags, LexStream, Tk, TkFlags, TkRule}, state::read_logic};
|
||||
|
||||
use super::readline::FernReadline;
|
||||
|
||||
fn is_executable(path: &Path) -> bool {
|
||||
path.metadata()
|
||||
.map(|m| m.permissions().mode() & 0o111 != 0)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
#[derive(Default,Debug)]
|
||||
pub struct FernHighlighter {
|
||||
input: String,
|
||||
}
|
||||
|
||||
impl FernHighlighter {
|
||||
pub fn new(input: String) -> Self {
|
||||
Self {
|
||||
input,
|
||||
}
|
||||
}
|
||||
pub fn highlight_subsh(&self, token: Tk) -> String {
|
||||
if token.flags.contains(TkFlags::IS_SUBSH) {
|
||||
let raw = token.as_str();
|
||||
Self::hl_subsh_raw(raw)
|
||||
} else if token.flags.contains(TkFlags::IS_CMDSUB) {
|
||||
let raw = token.as_str();
|
||||
Self::hl_cmdsub_raw(raw)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
pub fn hl_subsh_raw(raw: &str) -> String {
|
||||
let mut body = &raw[1..];
|
||||
let mut closed = false;
|
||||
if body.ends_with(')') {
|
||||
body = &body[..body.len() - 1];
|
||||
closed = true;
|
||||
}
|
||||
let sub_hl = FernHighlighter::new(body.to_string());
|
||||
let body_highlighted = sub_hl.hl_input();
|
||||
let open_paren = "(".styled(Style::BrightBlue);
|
||||
let close_paren = ")".styled(Style::BrightBlue);
|
||||
let mut result = format!("{open_paren}{body_highlighted}");
|
||||
if closed {
|
||||
result.push_str(&close_paren);
|
||||
}
|
||||
result
|
||||
}
|
||||
pub fn hl_cmdsub_raw(raw: &str) -> String {
|
||||
let mut body = &raw[2..];
|
||||
let mut closed = false;
|
||||
if body.ends_with(')') {
|
||||
body = &body[..body.len() - 1];
|
||||
closed = true;
|
||||
}
|
||||
let sub_hl = FernHighlighter::new(body.to_string());
|
||||
let body_highlighted = sub_hl.hl_input();
|
||||
let dollar_paren = "$(".styled(Style::BrightBlue);
|
||||
let close_paren = ")".styled(Style::BrightBlue);
|
||||
let mut result = format!("{dollar_paren}{body_highlighted}");
|
||||
if closed {
|
||||
result.push_str(&close_paren);
|
||||
}
|
||||
result
|
||||
}
|
||||
pub fn hl_command(&self, token: Tk) -> String {
|
||||
let raw = token.as_str();
|
||||
let paths = env::var("PATH")
|
||||
.unwrap_or_default();
|
||||
let mut paths = paths.split(':');
|
||||
|
||||
let is_in_path = {
|
||||
loop {
|
||||
let Some(path) = paths.next() else {
|
||||
break false
|
||||
};
|
||||
|
||||
let mut path = PathBuf::from(path);
|
||||
path.push(PathBuf::from(raw));
|
||||
|
||||
if path.is_file() && is_executable(&path) {
|
||||
break true
|
||||
};
|
||||
}
|
||||
};
|
||||
// TODO: zsh is capable of highlighting an alias red even if it exists, if the command it refers to is not found
|
||||
// Implement some way to find out if the content of the alias is valid as well
|
||||
let is_alias_or_function = read_logic(|l| {
|
||||
l.get_func(raw).is_some() || l.get_alias(raw).is_some()
|
||||
});
|
||||
|
||||
let is_builtin = BUILTINS.contains(&raw);
|
||||
|
||||
if is_alias_or_function || is_in_path || is_builtin {
|
||||
raw.styled(Style::Green)
|
||||
} else {
|
||||
raw.styled(Style::Bold | Style::Red)
|
||||
}
|
||||
}
|
||||
pub fn hl_dquote(&self, token: Tk) -> String {
|
||||
let raw = token.as_str();
|
||||
let mut chars = raw.chars().peekable();
|
||||
const YELLOW: &str = "\x1b[33m";
|
||||
const RESET: &str = "\x1b[0m";
|
||||
let mut result = String::new();
|
||||
let mut dquote_count = 0;
|
||||
|
||||
result.push_str(YELLOW);
|
||||
|
||||
while let Some(ch) = chars.next() {
|
||||
match ch {
|
||||
'\\' => {
|
||||
result.push(ch);
|
||||
if let Some(ch) = chars.next() {
|
||||
result.push(ch);
|
||||
}
|
||||
}
|
||||
'"' => {
|
||||
dquote_count += 1;
|
||||
result.push(ch);
|
||||
if dquote_count >= 2 {
|
||||
break
|
||||
}
|
||||
}
|
||||
'$' if chars.peek() == Some(&'(') => {
|
||||
let mut raw_cmd_sub = String::new();
|
||||
raw_cmd_sub.push(ch);
|
||||
raw_cmd_sub.push(chars.next().unwrap());
|
||||
let mut cmdsub_count = 1;
|
||||
|
||||
while let Some(cmdsub_ch) = chars.next() {
|
||||
match cmdsub_ch {
|
||||
'\\' => {
|
||||
raw_cmd_sub.push(cmdsub_ch);
|
||||
if let Some(ch) = chars.next() {
|
||||
raw_cmd_sub.push(ch);
|
||||
}
|
||||
}
|
||||
'$' if chars.peek() == Some(&'(') => {
|
||||
cmdsub_count += 1;
|
||||
raw_cmd_sub.push(cmdsub_ch);
|
||||
raw_cmd_sub.push(chars.next().unwrap());
|
||||
}
|
||||
')' => {
|
||||
cmdsub_count -= 1;
|
||||
raw_cmd_sub.push(cmdsub_ch);
|
||||
if cmdsub_count <= 0 {
|
||||
let styled = Self::hl_cmdsub_raw(&mem::take(&mut raw_cmd_sub));
|
||||
result.push_str(&styled);
|
||||
result.push_str(YELLOW);
|
||||
break
|
||||
}
|
||||
}
|
||||
_ => raw_cmd_sub.push(cmdsub_ch)
|
||||
}
|
||||
}
|
||||
if !raw_cmd_sub.is_empty() {
|
||||
let styled = Self::hl_cmdsub_raw(&mem::take(&mut raw_cmd_sub));
|
||||
result.push_str(&styled);
|
||||
result.push_str(YELLOW);
|
||||
}
|
||||
}
|
||||
_ => result.push(ch)
|
||||
}
|
||||
}
|
||||
|
||||
result.push_str(RESET);
|
||||
|
||||
result
|
||||
}
|
||||
pub fn hl_input(&self) -> String {
|
||||
let mut output = self.input.clone();
|
||||
|
||||
// TODO: properly implement highlighting for unfinished input
|
||||
let lex_results = LexStream::new(Arc::new(output.clone()), LexFlags::LEX_UNFINISHED);
|
||||
let mut tokens = vec![];
|
||||
|
||||
for result in lex_results {
|
||||
let Ok(token) = result else {
|
||||
return self.input.clone();
|
||||
};
|
||||
tokens.push(token)
|
||||
}
|
||||
|
||||
// Reverse the tokens, because we want to highlight from right to left
|
||||
// Doing it this way allows us to trust the spans in the tokens throughout the entire process
|
||||
let tokens = tokens.into_iter()
|
||||
.rev()
|
||||
.collect::<Vec<Tk>>();
|
||||
for token in tokens {
|
||||
match token.class {
|
||||
_ if token.flags.intersects(TkFlags::IS_CMDSUB | TkFlags::IS_SUBSH) => {
|
||||
let styled = self.highlight_subsh(token.clone());
|
||||
output.replace_range(token.span.start..token.span.end, &styled);
|
||||
}
|
||||
TkRule::Str => {
|
||||
if token.flags.contains(TkFlags::IS_CMD) {
|
||||
let styled = self.hl_command(token.clone());
|
||||
output.replace_range(token.span.start..token.span.end, &styled);
|
||||
} else if is_dquote(&token) {
|
||||
let styled = self.hl_dquote(token.clone());
|
||||
output.replace_range(token.span.start..token.span.end, &styled);
|
||||
} else {
|
||||
output.replace_range(token.span.start..token.span.end, &token.to_string());
|
||||
}
|
||||
}
|
||||
TkRule::Pipe |
|
||||
TkRule::ErrPipe |
|
||||
TkRule::And |
|
||||
TkRule::Or |
|
||||
TkRule::Bg |
|
||||
TkRule::Sep |
|
||||
TkRule::Redir => self.style_with_token(&token,&mut output,Style::Cyan.into()),
|
||||
TkRule::CasePattern => self.style_with_token(&token,&mut output,Style::Blue.into()),
|
||||
TkRule::BraceGrpStart |
|
||||
TkRule::BraceGrpEnd => self.style_with_token(&token,&mut output,Style::Cyan.into()),
|
||||
TkRule::Comment => self.style_with_token(&token,&mut output,Style::BrightBlack.into()),
|
||||
_ => { output.replace_range(token.span.start..token.span.end, &token.to_string()); }
|
||||
}
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
fn style_with_token(&self, token: &Tk, highlighted: &mut String, style: StyleSet) {
|
||||
let styled = token.to_string().styled(style);
|
||||
highlighted.replace_range(token.span.start..token.span.end, &styled);
|
||||
}
|
||||
}
|
||||
|
||||
impl Highlighter for FernReadline {
|
||||
fn highlight<'l>(&self, line: &'l str, _pos: usize) -> std::borrow::Cow<'l, str> {
|
||||
let highlighter = FernHighlighter::new(line.to_string());
|
||||
std::borrow::Cow::Owned(highlighter.hl_input())
|
||||
}
|
||||
fn highlight_char(&self, _line: &str, _pos: usize, _kind: rustyline::highlight::CmdKind) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn is_dquote(token: &Tk) -> bool {
|
||||
let raw = token.as_str();
|
||||
raw.starts_with('"')
|
||||
}
|
||||
|
||||
@@ -3,89 +3,30 @@ pub mod highlight;
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use readline::FernReadline;
|
||||
use rustyline::{error::ReadlineError, history::FileHistory, ColorMode, Config, Editor};
|
||||
use readline::{FernVi, Readline};
|
||||
|
||||
use crate::{expand::expand_prompt, libsh::error::ShResult, prelude::*, state::read_shopts};
|
||||
use crate::{expand::expand_prompt, libsh::error::ShResult, prelude::*, shopt::FernEditMode, state::read_shopts};
|
||||
|
||||
/// Initialize the line editor
|
||||
fn init_rl() -> ShResult<Editor<FernReadline,FileHistory>> {
|
||||
let rl = FernReadline::new();
|
||||
|
||||
let tab_stop = read_shopts(|s| s.prompt.tab_stop);
|
||||
let edit_mode = read_shopts(|s| s.prompt.edit_mode).into();
|
||||
let bell_style = read_shopts(|s| s.core.bell_style).into();
|
||||
let ignore_dups = read_shopts(|s| s.core.hist_ignore_dupes);
|
||||
let comp_limit = read_shopts(|s| s.prompt.comp_limit);
|
||||
let auto_hist = read_shopts(|s| s.core.auto_hist);
|
||||
let max_hist = read_shopts(|s| s.core.max_hist);
|
||||
let color_mode = match read_shopts(|s| s.prompt.prompt_highlight) {
|
||||
true => ColorMode::Enabled,
|
||||
false => ColorMode::Disabled,
|
||||
};
|
||||
|
||||
let config = Config::builder()
|
||||
.tab_stop(tab_stop)
|
||||
.indent_size(1)
|
||||
.edit_mode(edit_mode)
|
||||
.bell_style(bell_style)
|
||||
.color_mode(color_mode)
|
||||
.history_ignore_dups(ignore_dups).unwrap()
|
||||
.completion_prompt_limit(comp_limit)
|
||||
.auto_add_history(auto_hist)
|
||||
.max_history_size(max_hist).unwrap()
|
||||
.build();
|
||||
|
||||
let mut editor = Editor::with_config(config).unwrap();
|
||||
|
||||
editor.set_helper(Some(rl));
|
||||
editor.load_history(&Path::new("/home/pagedmov/.fernhist"))?;
|
||||
Ok(editor)
|
||||
}
|
||||
|
||||
fn get_prompt() -> ShResult<String> {
|
||||
let Ok(prompt) = env::var("PS1") else {
|
||||
// prompt expands to:
|
||||
//
|
||||
// username@hostname
|
||||
// short/path/to/pwd/
|
||||
// $
|
||||
let default = "\\e[1;0m\\u\\e[1;36m@\\e[1;31m\\h\\n\\e[1;36m\\W\\e[1;32m/\\n\\e[1;32m\\$ ";
|
||||
return Ok(format!("\n{}",expand_prompt(default)?))
|
||||
// $ _
|
||||
let default = "\\n\\e[1;0m\\u\\e[1;36m@\\e[1;31m\\h\\n\\e[1;36m\\W\\e[1;32m/\\n\\e[1;32m\\$\\e[0m ";
|
||||
return expand_prompt(default)
|
||||
};
|
||||
|
||||
Ok(format!("\n{}",expand_prompt(&prompt)?))
|
||||
expand_prompt(&prompt)
|
||||
}
|
||||
|
||||
fn get_hist_path() -> ShResult<PathBuf> {
|
||||
if let Ok(path) = env::var("FERN_HIST") {
|
||||
Ok(PathBuf::from(path))
|
||||
} else {
|
||||
let home = env::var("HOME")?;
|
||||
let path = PathBuf::from(format!("{home}/.fernhist"));
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn read_line() -> ShResult<String> {
|
||||
assert!(isatty(STDIN_FILENO).unwrap());
|
||||
let mut editor = init_rl()?;
|
||||
pub fn read_line(edit_mode: FernEditMode) -> ShResult<String> {
|
||||
let prompt = get_prompt()?;
|
||||
match editor.readline(&prompt) {
|
||||
Ok(line) => {
|
||||
if !line.is_empty() {
|
||||
let hist_path = get_hist_path()?;
|
||||
editor.add_history_entry(&line)?;
|
||||
editor.save_history(&hist_path)?;
|
||||
}
|
||||
Ok(line)
|
||||
}
|
||||
Err(ReadlineError::Eof) => {
|
||||
kill(Pid::this(), Signal::SIGQUIT)?;
|
||||
Ok(String::new())
|
||||
}
|
||||
Err(ReadlineError::Interrupted) => Ok(String::new()),
|
||||
Err(e) => {
|
||||
Err(e.into())
|
||||
}
|
||||
}
|
||||
let mut reader: Box<dyn Readline> = match edit_mode {
|
||||
FernEditMode::Vi => Box::new(FernVi::new(Some(prompt))?),
|
||||
FernEditMode::Emacs => todo!()
|
||||
};
|
||||
reader.readline()
|
||||
}
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
use rustyline::{completion::Completer, hint::{Hint, Hinter}, history::SearchDirection, validate::{ValidationResult, Validator}, Helper};
|
||||
|
||||
use crate::{libsh::term::{Style, Styled}, parse::{lex::{LexFlags, LexStream}, ParseStream}};
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Default,Debug)]
|
||||
pub struct FernReadline;
|
||||
|
||||
impl FernReadline {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
pub fn search_hist(value: &str, ctx: &rustyline::Context<'_>) -> Option<String> {
|
||||
let len = ctx.history().len();
|
||||
for i in 0..len {
|
||||
let entry = ctx.history().get(i, SearchDirection::Reverse).unwrap().unwrap();
|
||||
if entry.entry.starts_with(value) {
|
||||
return Some(entry.entry.into_owned())
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Helper for FernReadline {}
|
||||
|
||||
impl Completer for FernReadline {
|
||||
type Candidate = String;
|
||||
}
|
||||
|
||||
pub struct FernHint {
|
||||
raw: String,
|
||||
styled: String
|
||||
}
|
||||
|
||||
impl FernHint {
|
||||
pub fn new(raw: String) -> Self {
|
||||
let styled = (&raw).styled(Style::Dim | Style::BrightBlack);
|
||||
Self { raw, styled }
|
||||
}
|
||||
}
|
||||
|
||||
impl Hint for FernHint {
|
||||
fn display(&self) -> &str {
|
||||
&self.styled
|
||||
}
|
||||
fn completion(&self) -> Option<&str> {
|
||||
if !self.raw.is_empty() {
|
||||
Some(&self.raw)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Hinter for FernReadline {
|
||||
type Hint = FernHint;
|
||||
fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option<Self::Hint> {
|
||||
if line.is_empty() {
|
||||
return None
|
||||
}
|
||||
let ent = Self::search_hist(line,ctx)?;
|
||||
let entry_raw = ent.get(pos..)?.to_string();
|
||||
Some(FernHint::new(entry_raw))
|
||||
}
|
||||
}
|
||||
|
||||
impl Validator for FernReadline {
|
||||
fn validate(&self, ctx: &mut rustyline::validate::ValidationContext) -> rustyline::Result<rustyline::validate::ValidationResult> {
|
||||
let mut tokens = vec![];
|
||||
let tk_stream = LexStream::new(Arc::new(ctx.input().to_string()), LexFlags::empty());
|
||||
for tk in tk_stream {
|
||||
if tk.is_err() {
|
||||
return Ok(ValidationResult::Incomplete)
|
||||
}
|
||||
tokens.push(tk.unwrap());
|
||||
}
|
||||
let nd_stream = ParseStream::new(tokens);
|
||||
for nd in nd_stream {
|
||||
if nd.is_err() {
|
||||
return Ok(ValidationResult::Incomplete)
|
||||
}
|
||||
}
|
||||
Ok(ValidationResult::Valid(None))
|
||||
}
|
||||
}
|
||||
258
src/prompt/readline/history.rs
Normal file
258
src/prompt/readline/history.rs
Normal file
@@ -0,0 +1,258 @@
|
||||
use std::{env, fmt::{Write,Display}, fs::{self, OpenOptions}, io::Write as IoWrite, path::{Path, PathBuf}, str::FromStr, time::{Duration, SystemTime, UNIX_EPOCH}};
|
||||
|
||||
use crate::libsh::error::{ShErr, ShErrKind, ShResult};
|
||||
use crate::prelude::*;
|
||||
|
||||
use super::vicmd::Direction; // surprisingly useful
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HistEntry {
|
||||
id: u32,
|
||||
timestamp: SystemTime,
|
||||
command: String,
|
||||
new: bool
|
||||
}
|
||||
|
||||
impl HistEntry {
|
||||
pub fn id(&self) -> u32 {
|
||||
self.id
|
||||
}
|
||||
pub fn timestamp(&self) -> &SystemTime {
|
||||
&self.timestamp
|
||||
}
|
||||
pub fn command(&self) -> &str {
|
||||
&self.command
|
||||
}
|
||||
fn with_escaped_newlines(&self) -> String {
|
||||
let mut escaped = String::new();
|
||||
let mut chars = self.command.chars();
|
||||
while let Some(ch) = chars.next() {
|
||||
match ch {
|
||||
'\\' => {
|
||||
escaped.push(ch);
|
||||
if let Some(ch) = chars.next() {
|
||||
escaped.push(ch)
|
||||
}
|
||||
}
|
||||
'\n' => {
|
||||
escaped.push_str("\\\n");
|
||||
}
|
||||
_ => escaped.push(ch),
|
||||
}
|
||||
}
|
||||
escaped
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for HistEntry {
|
||||
type Err = ShErr;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let err = Err(
|
||||
ShErr::Simple { kind: ShErrKind::HistoryReadErr, msg: format!("Bad formatting on history entry '{s}'"), notes: vec![] }
|
||||
);
|
||||
|
||||
//: 248972349;148;echo foo; echo bar
|
||||
let Some(cleaned) = s.strip_prefix(": ") else { return err };
|
||||
//248972349;148;echo foo; echo bar
|
||||
let Some((timestamp,id_and_command)) = cleaned.split_once(';') else { return err };
|
||||
//("248972349","148;echo foo; echo bar")
|
||||
let Some((id,command)) = id_and_command.split_once(';') else { return err };
|
||||
//("148","echo foo; echo bar")
|
||||
let Ok(ts_seconds) = timestamp.parse::<u64>() else { return err };
|
||||
let Ok(id) = id.parse::<u32>() else { return err };
|
||||
let timestamp = UNIX_EPOCH + Duration::from_secs(ts_seconds);
|
||||
let command = command.to_string();
|
||||
Ok(Self { id, timestamp, command, new: false })
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for HistEntry {
|
||||
/// Similar to zsh's history format, but not entirely
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let command = self.with_escaped_newlines();
|
||||
let HistEntry { id, timestamp, command: _, new: _ } = self;
|
||||
let timestamp = timestamp.duration_since(UNIX_EPOCH).unwrap().as_secs();
|
||||
writeln!(f, ": {timestamp};{id};{command}")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HistEntries(Vec<HistEntry>);
|
||||
|
||||
|
||||
impl FromStr for HistEntries {
|
||||
type Err = ShErr;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut entries = vec![];
|
||||
|
||||
let mut lines = s.lines().enumerate().peekable();
|
||||
let mut cur_line = String::new();
|
||||
|
||||
while let Some((i,line)) = lines.next() {
|
||||
if !line.starts_with(": ") {
|
||||
return Err(
|
||||
ShErr::Simple { kind: ShErrKind::HistoryReadErr, msg: format!("Bad formatting on line {i}"), notes: vec![] }
|
||||
)
|
||||
}
|
||||
let mut chars = line.chars().peekable();
|
||||
let mut feeding_lines = true;
|
||||
while feeding_lines {
|
||||
feeding_lines = false;
|
||||
while let Some(ch) = chars.next() {
|
||||
match ch {
|
||||
'\\' => {
|
||||
if let Some(esc_ch) = chars.next() {
|
||||
cur_line.push(esc_ch);
|
||||
} else {
|
||||
cur_line.push('\n');
|
||||
feeding_lines = true;
|
||||
}
|
||||
}
|
||||
'\n' => {
|
||||
break
|
||||
}
|
||||
_ => {
|
||||
cur_line.push(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
if feeding_lines {
|
||||
let Some((_,line)) = lines.next() else {
|
||||
return Err(
|
||||
ShErr::Simple { kind: ShErrKind::HistoryReadErr, msg: format!("Bad formatting on line {i}"), notes: vec![] }
|
||||
)
|
||||
};
|
||||
chars = line.chars().peekable();
|
||||
}
|
||||
}
|
||||
let entry = cur_line.parse::<HistEntry>()?;
|
||||
entries.push(entry);
|
||||
cur_line.clear();
|
||||
}
|
||||
|
||||
|
||||
Ok(Self(entries))
|
||||
}
|
||||
}
|
||||
|
||||
fn read_hist_file(path: &Path) -> ShResult<Vec<HistEntry>> {
|
||||
if !path.exists() {
|
||||
fs::File::create(path)?;
|
||||
}
|
||||
let raw = fs::read_to_string(path)?;
|
||||
Ok(raw.parse::<HistEntries>()?.0)
|
||||
}
|
||||
|
||||
pub struct History {
|
||||
path: PathBuf,
|
||||
entries: Vec<HistEntry>,
|
||||
cursor: usize,
|
||||
search_direction: Direction,
|
||||
ignore_dups: bool,
|
||||
max_size: Option<u32>,
|
||||
}
|
||||
|
||||
impl History {
|
||||
pub fn new() -> ShResult<Self> {
|
||||
let path = PathBuf::from(env::var("FERNHIST").unwrap_or({
|
||||
let home = env::var("HOME").unwrap();
|
||||
format!("{home}/.fern_history")
|
||||
}));
|
||||
let entries = read_hist_file(&path)?;
|
||||
let cursor = entries.len();
|
||||
let mut new = Self {
|
||||
path,
|
||||
entries,
|
||||
cursor,
|
||||
search_direction: Direction::Backward,
|
||||
ignore_dups: true,
|
||||
max_size: None,
|
||||
};
|
||||
new.push_empty_entry(); // Current pending command
|
||||
Ok(new)
|
||||
}
|
||||
|
||||
pub fn entries(&self) -> &[HistEntry] {
|
||||
&self.entries
|
||||
}
|
||||
|
||||
pub fn push_empty_entry(&mut self) {
|
||||
let id = self.get_new_id();
|
||||
let timestamp = SystemTime::now();
|
||||
let command = "".into();
|
||||
self.entries.push(HistEntry { id, timestamp, command, new: true })
|
||||
}
|
||||
|
||||
pub fn update_pending_cmd(&mut self, command: &str) {
|
||||
flog!(DEBUG, "updating command");
|
||||
let Some(ent) = self.last_mut() else {
|
||||
return
|
||||
};
|
||||
|
||||
ent.command = command.to_string()
|
||||
}
|
||||
|
||||
pub fn last_mut(&mut self) -> Option<&mut HistEntry> {
|
||||
self.entries.last_mut()
|
||||
}
|
||||
|
||||
pub fn get_new_id(&self) -> u32 {
|
||||
let Some(ent) = self.entries.last() else {
|
||||
return 0
|
||||
};
|
||||
ent.id + 1
|
||||
}
|
||||
|
||||
pub fn ignore_dups(&mut self, yn: bool) {
|
||||
self.ignore_dups = yn
|
||||
}
|
||||
|
||||
pub fn max_hist_size(&mut self, size: Option<u32>) {
|
||||
self.max_size = size
|
||||
}
|
||||
|
||||
pub fn scroll(&mut self, offset: isize) -> Option<&HistEntry> {
|
||||
let new_idx = self.cursor
|
||||
.saturating_add_signed(offset)
|
||||
.clamp(0, self.entries.len());
|
||||
let ent = self.entries.get(new_idx)?;
|
||||
|
||||
self.cursor = new_idx;
|
||||
|
||||
Some(ent)
|
||||
}
|
||||
|
||||
pub fn push(&mut self, command: String) {
|
||||
let timestamp = SystemTime::now();
|
||||
let id = self.get_new_id();
|
||||
if self.ignore_dups && self.is_dup(&command) {
|
||||
return
|
||||
}
|
||||
self.entries.push(HistEntry { id, timestamp, command, new: true });
|
||||
}
|
||||
|
||||
pub fn is_dup(&self, other: &str) -> bool {
|
||||
let Some(ent) = self.entries.last() else {
|
||||
return false
|
||||
};
|
||||
let ent_cmd = &ent.command;
|
||||
ent_cmd == other
|
||||
}
|
||||
|
||||
pub fn save(&mut self) -> ShResult<()> {
|
||||
let mut file = OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(&self.path)?;
|
||||
|
||||
let entries = self.entries.iter_mut().filter(|ent| ent.new);
|
||||
let mut data = String::new();
|
||||
for ent in entries {
|
||||
ent.new = false;
|
||||
write!(data, "{ent}").unwrap();
|
||||
}
|
||||
|
||||
file.write_all(data.as_bytes())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
142
src/prompt/readline/keys.rs
Normal file
142
src/prompt/readline/keys.rs
Normal file
@@ -0,0 +1,142 @@
|
||||
use std::sync::Arc;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
// Credit to Rustyline for the design ideas in this module
|
||||
// https://github.com/kkawakam/rustyline
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct KeyEvent(pub KeyCode, pub ModKeys);
|
||||
|
||||
|
||||
impl KeyEvent {
|
||||
pub fn new(ch: &str, mut mods: ModKeys) -> Self {
|
||||
use {KeyCode as K, KeyEvent as E, ModKeys as M};
|
||||
|
||||
let mut graphemes = ch.graphemes(true);
|
||||
|
||||
let first = match graphemes.next() {
|
||||
Some(g) => g,
|
||||
None => return E(K::Null, mods),
|
||||
};
|
||||
|
||||
// If more than one grapheme, it's not a single key event
|
||||
if graphemes.next().is_some() {
|
||||
return E(K::Null, mods); // Or panic, or wrap in Grapheme if desired
|
||||
}
|
||||
|
||||
let mut chars = first.chars();
|
||||
|
||||
let single_char = chars.next();
|
||||
let is_single_char = chars.next().is_none();
|
||||
|
||||
match single_char {
|
||||
Some(c) if is_single_char && c.is_control() => {
|
||||
match c {
|
||||
'\x00' => E(K::Char('@'), mods | M::CTRL),
|
||||
'\x01' => E(K::Char('A'), mods | M::CTRL),
|
||||
'\x02' => E(K::Char('B'), mods | M::CTRL),
|
||||
'\x03' => E(K::Char('C'), mods | M::CTRL),
|
||||
'\x04' => E(K::Char('D'), mods | M::CTRL),
|
||||
'\x05' => E(K::Char('E'), mods | M::CTRL),
|
||||
'\x06' => E(K::Char('F'), mods | M::CTRL),
|
||||
'\x07' => E(K::Char('G'), mods | M::CTRL),
|
||||
'\x08' => E(K::Backspace, mods),
|
||||
'\x09' => {
|
||||
if mods.contains(M::SHIFT) {
|
||||
mods.remove(M::SHIFT);
|
||||
E(K::BackTab, mods)
|
||||
} else {
|
||||
E(K::Tab, mods)
|
||||
}
|
||||
}
|
||||
'\x0a' => E(K::Char('J'), mods | M::CTRL),
|
||||
'\x0b' => E(K::Char('K'), mods | M::CTRL),
|
||||
'\x0c' => E(K::Char('L'), mods | M::CTRL),
|
||||
'\x0d' => E(K::Enter, mods),
|
||||
'\x0e' => E(K::Char('N'), mods | M::CTRL),
|
||||
'\x0f' => E(K::Char('O'), mods | M::CTRL),
|
||||
'\x10' => E(K::Char('P'), mods | M::CTRL),
|
||||
'\x11' => E(K::Char('Q'), mods | M::CTRL),
|
||||
'\x12' => E(K::Char('R'), mods | M::CTRL),
|
||||
'\x13' => E(K::Char('S'), mods | M::CTRL),
|
||||
'\x14' => E(K::Char('T'), mods | M::CTRL),
|
||||
'\x15' => E(K::Char('U'), mods | M::CTRL),
|
||||
'\x16' => E(K::Char('V'), mods | M::CTRL),
|
||||
'\x17' => E(K::Char('W'), mods | M::CTRL),
|
||||
'\x18' => E(K::Char('X'), mods | M::CTRL),
|
||||
'\x19' => E(K::Char('Y'), mods | M::CTRL),
|
||||
'\x1a' => E(K::Char('Z'), mods | M::CTRL),
|
||||
'\x1b' => E(K::Esc, mods),
|
||||
'\x1c' => E(K::Char('\\'), mods | M::CTRL),
|
||||
'\x1d' => E(K::Char(']'), mods | M::CTRL),
|
||||
'\x1e' => E(K::Char('^'), mods | M::CTRL),
|
||||
'\x1f' => E(K::Char('_'), mods | M::CTRL),
|
||||
'\x7f' => E(K::Backspace, mods),
|
||||
'\u{9b}' => E(K::Esc, mods | M::SHIFT),
|
||||
_ => E(K::Null, mods),
|
||||
}
|
||||
}
|
||||
Some(c) if is_single_char => {
|
||||
if !mods.is_empty() {
|
||||
mods.remove(M::SHIFT);
|
||||
}
|
||||
E(K::Char(c), mods)
|
||||
}
|
||||
_ => {
|
||||
// multi-char grapheme (emoji, accented, etc)
|
||||
if !mods.is_empty() {
|
||||
mods.remove(M::SHIFT);
|
||||
}
|
||||
E(K::Grapheme(Arc::from(first)), mods)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub enum KeyCode {
|
||||
UnknownEscSeq,
|
||||
Backspace,
|
||||
BackTab,
|
||||
BracketedPasteStart,
|
||||
BracketedPasteEnd,
|
||||
Char(char),
|
||||
Grapheme(Arc<str>),
|
||||
Delete,
|
||||
Down,
|
||||
End,
|
||||
Enter,
|
||||
Esc,
|
||||
F(u8),
|
||||
Home,
|
||||
Insert,
|
||||
Left,
|
||||
Null,
|
||||
PageDown,
|
||||
PageUp,
|
||||
Right,
|
||||
Tab,
|
||||
Up,
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct ModKeys: u8 {
|
||||
/// Control modifier
|
||||
const CTRL = 1<<3;
|
||||
/// Escape or Alt modifier
|
||||
const ALT = 1<<2;
|
||||
/// Shift modifier
|
||||
const SHIFT = 1<<1;
|
||||
|
||||
/// No modifier
|
||||
const NONE = 0;
|
||||
/// Ctrl + Shift
|
||||
const CTRL_SHIFT = Self::CTRL.bits() | Self::SHIFT.bits();
|
||||
/// Alt + Shift
|
||||
const ALT_SHIFT = Self::ALT.bits() | Self::SHIFT.bits();
|
||||
/// Ctrl + Alt
|
||||
const CTRL_ALT = Self::CTRL.bits() | Self::ALT.bits();
|
||||
/// Ctrl + Alt + Shift
|
||||
const CTRL_ALT_SHIFT = Self::CTRL.bits() | Self::ALT.bits() | Self::SHIFT.bits();
|
||||
}
|
||||
}
|
||||
1575
src/prompt/readline/linebuf.rs
Normal file
1575
src/prompt/readline/linebuf.rs
Normal file
File diff suppressed because it is too large
Load Diff
371
src/prompt/readline/mod.rs
Normal file
371
src/prompt/readline/mod.rs
Normal file
@@ -0,0 +1,371 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use history::History;
|
||||
use keys::{KeyCode, KeyEvent, ModKeys};
|
||||
use linebuf::{strip_ansi_codes_and_escapes, LineBuf};
|
||||
use mode::{CmdReplay, ViInsert, ViMode, ViNormal, ViReplace};
|
||||
use term::Terminal;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
use vicmd::{Motion, MotionCmd, RegisterName, To, Verb, VerbCmd, ViCmd};
|
||||
|
||||
use crate::libsh::{error::{ShErr, ShErrKind, ShResult}, term::{Style, Styled}};
|
||||
use crate::prelude::*;
|
||||
|
||||
pub mod keys;
|
||||
pub mod term;
|
||||
pub mod linebuf;
|
||||
pub mod vicmd;
|
||||
pub mod mode;
|
||||
pub mod register;
|
||||
pub mod history;
|
||||
|
||||
const LOREM_IPSUM: &str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore\nmagna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\nExcepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
|
||||
|
||||
/// Unified interface for different line editing methods
|
||||
pub trait Readline {
|
||||
fn readline(&mut self) -> ShResult<String>;
|
||||
}
|
||||
|
||||
pub struct FernVi {
|
||||
term: Terminal,
|
||||
line: LineBuf,
|
||||
history: History,
|
||||
prompt: String,
|
||||
mode: Box<dyn ViMode>,
|
||||
last_action: Option<CmdReplay>,
|
||||
last_movement: Option<MotionCmd>,
|
||||
}
|
||||
|
||||
impl Readline for FernVi {
|
||||
fn readline(&mut self) -> ShResult<String> {
|
||||
/*
|
||||
self.term.writeln("This is a line!");
|
||||
self.term.writeln("This is a line!");
|
||||
self.term.writeln("This is a line!");
|
||||
let prompt_thing = "prompt thing -> ";
|
||||
self.term.write(prompt_thing);
|
||||
let line = "And another!";
|
||||
let mut iters: usize = 0;
|
||||
let mut newlines_written = 0;
|
||||
loop {
|
||||
iters += 1;
|
||||
for i in 0..iters {
|
||||
self.term.writeln(line);
|
||||
}
|
||||
std::thread::sleep(Duration::from_secs(1));
|
||||
self.clear_lines(iters,prompt_thing.len() + 1);
|
||||
}
|
||||
panic!()
|
||||
*/
|
||||
self.print_buf(false)?;
|
||||
loop {
|
||||
let key = self.term.read_key();
|
||||
|
||||
if let KeyEvent(KeyCode::Char('V'), ModKeys::CTRL) = key {
|
||||
self.handle_verbatim()?;
|
||||
continue
|
||||
}
|
||||
|
||||
let Some(cmd) = self.mode.handle_key(key) else {
|
||||
continue
|
||||
};
|
||||
|
||||
if self.should_grab_history(&cmd) {
|
||||
flog!(DEBUG, "scrolling");
|
||||
self.scroll_history(cmd);
|
||||
self.print_buf(true)?;
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
if cmd.should_submit() {
|
||||
self.term.write("\n");
|
||||
let command = self.line.to_string();
|
||||
if !command.is_empty() {
|
||||
// We're just going to trim the command
|
||||
// reduces clutter in the case of two history commands whose only difference is insignificant whitespace
|
||||
self.history.push(command.trim().to_string());
|
||||
self.history.save()?;
|
||||
}
|
||||
return Ok(command);
|
||||
}
|
||||
let line = self.line.to_string();
|
||||
self.exec_cmd(cmd.clone())?;
|
||||
let new_line = self.line.as_str();
|
||||
let has_changes = line != new_line;
|
||||
flog!(DEBUG, has_changes);
|
||||
|
||||
if cmd.verb().is_some_and(|v| v.1.is_edit()) && has_changes {
|
||||
self.history.update_pending_cmd(self.line.as_str());
|
||||
}
|
||||
|
||||
self.print_buf(true)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FernVi {
|
||||
pub fn new(prompt: Option<String>) -> ShResult<Self> {
|
||||
let prompt = prompt.unwrap_or("$ ".styled(Style::Green | Style::Bold));
|
||||
let line = LineBuf::new().with_initial(LOREM_IPSUM);
|
||||
let term = Terminal::new();
|
||||
let history = History::new()?;
|
||||
Ok(Self {
|
||||
term,
|
||||
line,
|
||||
history,
|
||||
prompt,
|
||||
mode: Box::new(ViInsert::new()),
|
||||
last_action: None,
|
||||
last_movement: None,
|
||||
})
|
||||
}
|
||||
/// Ctrl+V handler
|
||||
pub fn handle_verbatim(&mut self) -> ShResult<()> {
|
||||
let mut buf = [0u8; 8];
|
||||
let mut collected = Vec::new();
|
||||
|
||||
loop {
|
||||
let n = self.term.read_byte(&mut buf[..1]);
|
||||
if n == 0 {
|
||||
continue;
|
||||
}
|
||||
collected.push(buf[0]);
|
||||
|
||||
// If it starts with ESC, treat as escape sequence
|
||||
if collected[0] == 0x1b {
|
||||
loop {
|
||||
let n = self.term.peek_byte(&mut buf[..1]);
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
collected.push(buf[0]);
|
||||
// Ends a CSI sequence
|
||||
if (0x40..=0x7e).contains(&buf[0]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let Ok(seq) = std::str::from_utf8(&collected) else {
|
||||
return Ok(())
|
||||
};
|
||||
let cmd = ViCmd {
|
||||
register: Default::default(),
|
||||
verb: Some(VerbCmd(1, Verb::Insert(seq.to_string()))),
|
||||
motion: None,
|
||||
raw_seq: seq.to_string(),
|
||||
};
|
||||
self.line.exec_cmd(cmd)?;
|
||||
}
|
||||
|
||||
// Optional: handle other edge cases, e.g., raw control codes
|
||||
if collected[0] < 0x20 || collected[0] == 0x7F {
|
||||
let ctrl_seq = std::str::from_utf8(&collected).unwrap();
|
||||
let cmd = ViCmd {
|
||||
register: Default::default(),
|
||||
verb: Some(VerbCmd(1, Verb::Insert(ctrl_seq.to_string()))),
|
||||
motion: None,
|
||||
raw_seq: ctrl_seq.to_string(),
|
||||
};
|
||||
self.line.exec_cmd(cmd)?;
|
||||
break;
|
||||
}
|
||||
|
||||
// Try to parse as UTF-8 if it's a valid Unicode sequence
|
||||
if let Ok(s) = std::str::from_utf8(&collected) {
|
||||
if s.chars().count() == 1 {
|
||||
let ch = s.chars().next().unwrap();
|
||||
// You got a literal Unicode char
|
||||
eprintln!("Got char: {:?}", ch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn scroll_history(&mut self, cmd: ViCmd) {
|
||||
let count = &cmd.motion().unwrap().0;
|
||||
let motion = &cmd.motion().unwrap().1;
|
||||
flog!(DEBUG,count,motion);
|
||||
let entry = match motion {
|
||||
Motion::LineUp => {
|
||||
let Some(hist_entry) = self.history.scroll(-(*count as isize)) else {
|
||||
return
|
||||
};
|
||||
flog!(DEBUG,"found entry");
|
||||
flog!(DEBUG,hist_entry.command());
|
||||
hist_entry
|
||||
}
|
||||
Motion::LineDown => {
|
||||
let Some(hist_entry) = self.history.scroll(*count as isize) else {
|
||||
return
|
||||
};
|
||||
flog!(DEBUG,"found entry");
|
||||
flog!(DEBUG,hist_entry.command());
|
||||
hist_entry
|
||||
}
|
||||
_ => unreachable!()
|
||||
};
|
||||
let col = self.line.saved_col().unwrap_or(self.line.cursor_column());
|
||||
let mut buf = LineBuf::new().with_initial(entry.command());
|
||||
let line_end = buf.end_of_line();
|
||||
if let Some(dest) = self.mode.hist_scroll_start_pos() {
|
||||
match dest {
|
||||
To::Start => {
|
||||
/* Already at 0 */
|
||||
}
|
||||
To::End => {
|
||||
// History entries cannot be empty
|
||||
// So this subtraction is safe (maybe)
|
||||
buf.cursor_fwd_to(line_end + 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let target = (col + 1).min(line_end + 1);
|
||||
buf.cursor_fwd_to(target);
|
||||
}
|
||||
|
||||
self.line = buf
|
||||
}
|
||||
|
||||
pub fn should_grab_history(&self, cmd: &ViCmd) -> bool {
|
||||
cmd.verb().is_none() &&
|
||||
(
|
||||
cmd.motion().is_some_and(|m| matches!(m, MotionCmd(_, Motion::LineUp))) &&
|
||||
self.line.start_of_line() == 0
|
||||
) ||
|
||||
(
|
||||
cmd.motion().is_some_and(|m| matches!(m, MotionCmd(_, Motion::LineDown))) &&
|
||||
self.line.end_of_line() == self.line.byte_len()
|
||||
)
|
||||
}
|
||||
pub fn print_buf(&mut self, refresh: bool) -> ShResult<()> {
|
||||
let (height,width) = self.term.get_dimensions()?;
|
||||
if refresh {
|
||||
self.term.unwrite()?;
|
||||
}
|
||||
let offset = self.calculate_prompt_offset();
|
||||
self.line.set_first_line_offset(offset);
|
||||
self.line.update_term_dims((height,width));
|
||||
let mut line_buf = self.prompt.clone();
|
||||
line_buf.push_str(self.line.as_str());
|
||||
|
||||
self.term.recorded_write(&line_buf, offset)?;
|
||||
self.term.position_cursor(self.line.cursor_display_coords(width))?;
|
||||
|
||||
self.term.write(&self.mode.cursor_style());
|
||||
Ok(())
|
||||
}
|
||||
pub fn calculate_prompt_offset(&self) -> usize {
|
||||
if self.prompt.ends_with('\n') {
|
||||
return 0
|
||||
}
|
||||
strip_ansi_codes_and_escapes(self.prompt.lines().last().unwrap_or_default()).width() + 1 // 1 indexed
|
||||
}
|
||||
pub fn exec_cmd(&mut self, cmd: ViCmd) -> ShResult<()> {
|
||||
if cmd.is_mode_transition() {
|
||||
let count = cmd.verb_count();
|
||||
let mut mode: Box<dyn ViMode> = match cmd.verb().unwrap().1 {
|
||||
Verb::InsertModeLineBreak(_) |
|
||||
Verb::Change |
|
||||
Verb::InsertMode => {
|
||||
Box::new(ViInsert::new().with_count(count as u16))
|
||||
}
|
||||
Verb::NormalMode => {
|
||||
Box::new(ViNormal::new())
|
||||
}
|
||||
Verb::ReplaceMode => {
|
||||
Box::new(ViReplace::new().with_count(count as u16))
|
||||
}
|
||||
Verb::VisualMode => todo!(),
|
||||
_ => unreachable!()
|
||||
};
|
||||
|
||||
std::mem::swap(&mut mode, &mut self.mode);
|
||||
self.line.set_cursor_clamp(self.mode.clamp_cursor());
|
||||
self.line.set_move_cursor_on_undo(self.mode.move_cursor_on_undo());
|
||||
self.term.write(&mode.cursor_style());
|
||||
|
||||
if mode.is_repeatable() {
|
||||
self.last_action = mode.as_replay();
|
||||
}
|
||||
return self.line.exec_cmd(cmd);
|
||||
} else if cmd.is_cmd_repeat() {
|
||||
let Some(replay) = self.last_action.clone() else {
|
||||
return Ok(())
|
||||
};
|
||||
let ViCmd { verb, .. } = cmd;
|
||||
let VerbCmd(count,_) = verb.unwrap();
|
||||
match replay {
|
||||
CmdReplay::ModeReplay { cmds, mut repeat } => {
|
||||
if count > 1 {
|
||||
repeat = count as u16;
|
||||
}
|
||||
for _ in 0..repeat {
|
||||
let cmds = cmds.clone();
|
||||
for cmd in cmds {
|
||||
self.line.exec_cmd(cmd)?
|
||||
}
|
||||
}
|
||||
}
|
||||
CmdReplay::Single(mut cmd) => {
|
||||
if count > 1 {
|
||||
// Override the counts with the one passed to the '.' command
|
||||
if cmd.verb.is_some() {
|
||||
if let Some(v_mut) = cmd.verb.as_mut() {
|
||||
v_mut.0 = count
|
||||
}
|
||||
if let Some(m_mut) = cmd.motion.as_mut() {
|
||||
m_mut.0 = 1
|
||||
}
|
||||
} else {
|
||||
return Ok(()) // it has to have a verb to be repeatable, something weird happened
|
||||
}
|
||||
}
|
||||
self.line.exec_cmd(cmd)?;
|
||||
}
|
||||
_ => unreachable!("motions should be handled in the other branch")
|
||||
}
|
||||
return Ok(())
|
||||
} else if cmd.is_motion_repeat() {
|
||||
match cmd.motion.as_ref().unwrap() {
|
||||
MotionCmd(count,Motion::RepeatMotion) => {
|
||||
let Some(motion) = self.last_movement.clone() else {
|
||||
return Ok(())
|
||||
};
|
||||
let repeat_cmd = ViCmd {
|
||||
register: RegisterName::default(),
|
||||
verb: None,
|
||||
motion: Some(motion),
|
||||
raw_seq: format!("{count};")
|
||||
};
|
||||
return self.line.exec_cmd(repeat_cmd);
|
||||
}
|
||||
MotionCmd(count,Motion::RepeatMotionRev) => {
|
||||
let Some(motion) = self.last_movement.clone() else {
|
||||
return Ok(())
|
||||
};
|
||||
let mut new_motion = motion.invert_char_motion();
|
||||
new_motion.0 = *count;
|
||||
let repeat_cmd = ViCmd {
|
||||
register: RegisterName::default(),
|
||||
verb: None,
|
||||
motion: Some(new_motion),
|
||||
raw_seq: format!("{count},")
|
||||
};
|
||||
return self.line.exec_cmd(repeat_cmd);
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
if cmd.is_repeatable() {
|
||||
self.last_action = Some(CmdReplay::Single(cmd.clone()));
|
||||
}
|
||||
if cmd.is_char_search() {
|
||||
self.last_movement = cmd.motion.clone()
|
||||
}
|
||||
|
||||
self.line.exec_cmd(cmd.clone())
|
||||
}
|
||||
}
|
||||
816
src/prompt/readline/mode.rs
Normal file
816
src/prompt/readline/mode.rs
Normal file
@@ -0,0 +1,816 @@
|
||||
use std::iter::Peekable;
|
||||
use std::str::Chars;
|
||||
|
||||
use nix::NixPath;
|
||||
|
||||
use super::keys::{KeyEvent as E, KeyCode as K, ModKeys as M};
|
||||
use super::vicmd::{Anchor, Bound, Dest, Direction, Motion, MotionBuilder, MotionCmd, RegisterName, TextObj, To, Verb, VerbBuilder, VerbCmd, ViCmd, Word};
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Debug,Clone)]
|
||||
pub enum CmdReplay {
|
||||
ModeReplay { cmds: Vec<ViCmd>, repeat: u16 },
|
||||
Single(ViCmd),
|
||||
Motion(Motion)
|
||||
}
|
||||
|
||||
impl CmdReplay {
|
||||
pub fn mode(cmds: Vec<ViCmd>, repeat: u16) -> Self {
|
||||
Self::ModeReplay { cmds, repeat }
|
||||
}
|
||||
pub fn single(cmd: ViCmd) -> Self {
|
||||
Self::Single(cmd)
|
||||
}
|
||||
pub fn motion(motion: Motion) -> Self {
|
||||
Self::Motion(motion)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum CmdState {
|
||||
Pending,
|
||||
Complete,
|
||||
Invalid
|
||||
}
|
||||
|
||||
pub trait ViMode {
|
||||
fn handle_key(&mut self, key: E) -> Option<ViCmd>;
|
||||
fn is_repeatable(&self) -> bool;
|
||||
fn as_replay(&self) -> Option<CmdReplay>;
|
||||
fn cursor_style(&self) -> String;
|
||||
fn pending_seq(&self) -> Option<String>;
|
||||
fn move_cursor_on_undo(&self) -> bool;
|
||||
fn clamp_cursor(&self) -> bool;
|
||||
fn hist_scroll_start_pos(&self) -> Option<To>;
|
||||
}
|
||||
|
||||
#[derive(Default,Debug)]
|
||||
pub struct ViInsert {
|
||||
cmds: Vec<ViCmd>,
|
||||
pending_cmd: ViCmd,
|
||||
repeat_count: u16
|
||||
}
|
||||
|
||||
impl ViInsert {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
pub fn with_count(mut self, repeat_count: u16) -> Self {
|
||||
self.repeat_count = repeat_count;
|
||||
self
|
||||
}
|
||||
pub fn register_and_return(&mut self) -> Option<ViCmd> {
|
||||
let cmd = self.take_cmd();
|
||||
self.register_cmd(&cmd);
|
||||
Some(cmd)
|
||||
}
|
||||
pub fn ctrl_w_is_undo(&self) -> bool {
|
||||
let insert_count = self.cmds.iter().filter(|cmd| {
|
||||
matches!(cmd.verb(),Some(VerbCmd(1, Verb::InsertChar(_))))
|
||||
}).count();
|
||||
let backspace_count = self.cmds.iter().filter(|cmd| {
|
||||
matches!(cmd.verb(),Some(VerbCmd(1, Verb::Delete)))
|
||||
}).count();
|
||||
insert_count > backspace_count
|
||||
}
|
||||
pub fn register_cmd(&mut self, cmd: &ViCmd) {
|
||||
self.cmds.push(cmd.clone())
|
||||
}
|
||||
pub fn take_cmd(&mut self) -> ViCmd {
|
||||
std::mem::take(&mut self.pending_cmd)
|
||||
}
|
||||
}
|
||||
|
||||
impl ViMode for ViInsert {
|
||||
fn handle_key(&mut self, key: E) -> Option<ViCmd> {
|
||||
match key {
|
||||
E(K::Char(ch), M::NONE) => {
|
||||
self.pending_cmd.set_verb(VerbCmd(1,Verb::InsertChar(ch)));
|
||||
self.pending_cmd.set_motion(MotionCmd(1,Motion::ForwardChar));
|
||||
self.register_and_return()
|
||||
}
|
||||
E(K::Char('W'), M::CTRL) => {
|
||||
if self.ctrl_w_is_undo() {
|
||||
self.pending_cmd.set_verb(VerbCmd(1,Verb::Undo));
|
||||
self.cmds.clear();
|
||||
Some(self.take_cmd())
|
||||
} else {
|
||||
self.pending_cmd.set_verb(VerbCmd(1, Verb::Delete));
|
||||
self.pending_cmd.set_motion(MotionCmd(1, Motion::BackwardWord(To::Start, Word::Normal)));
|
||||
self.register_and_return()
|
||||
}
|
||||
}
|
||||
E(K::Char('H'), M::CTRL) |
|
||||
E(K::Backspace, M::NONE) => {
|
||||
self.pending_cmd.set_verb(VerbCmd(1,Verb::Delete));
|
||||
self.pending_cmd.set_motion(MotionCmd(1,Motion::BackwardChar));
|
||||
self.register_and_return()
|
||||
}
|
||||
|
||||
E(K::BackTab, M::NONE) => {
|
||||
self.pending_cmd.set_verb(VerbCmd(1,Verb::CompleteBackward));
|
||||
self.register_and_return()
|
||||
}
|
||||
|
||||
E(K::Char('I'), M::CTRL) |
|
||||
E(K::Tab, M::NONE) => {
|
||||
self.pending_cmd.set_verb(VerbCmd(1,Verb::Complete));
|
||||
self.register_and_return()
|
||||
}
|
||||
|
||||
E(K::Esc, M::NONE) => {
|
||||
self.pending_cmd.set_verb(VerbCmd(1,Verb::NormalMode));
|
||||
self.pending_cmd.set_motion(MotionCmd(1,Motion::BackwardChar));
|
||||
self.register_and_return()
|
||||
}
|
||||
_ => common_cmds(key)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_repeatable(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn as_replay(&self) -> Option<CmdReplay> {
|
||||
Some(CmdReplay::mode(self.cmds.clone(), self.repeat_count))
|
||||
}
|
||||
|
||||
fn cursor_style(&self) -> String {
|
||||
"\x1b[6 q".to_string()
|
||||
}
|
||||
fn pending_seq(&self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
fn move_cursor_on_undo(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn clamp_cursor(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn hist_scroll_start_pos(&self) -> Option<To> {
|
||||
Some(To::End)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default,Debug)]
|
||||
pub struct ViReplace {
|
||||
cmds: Vec<ViCmd>,
|
||||
pending_cmd: ViCmd,
|
||||
repeat_count: u16
|
||||
}
|
||||
|
||||
impl ViReplace {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
pub fn with_count(mut self, repeat_count: u16) -> Self {
|
||||
self.repeat_count = repeat_count;
|
||||
self
|
||||
}
|
||||
pub fn register_and_return(&mut self) -> Option<ViCmd> {
|
||||
let cmd = self.take_cmd();
|
||||
self.register_cmd(&cmd);
|
||||
Some(cmd)
|
||||
}
|
||||
pub fn ctrl_w_is_undo(&self) -> bool {
|
||||
let insert_count = self.cmds.iter().filter(|cmd| {
|
||||
matches!(cmd.verb(),Some(VerbCmd(1, Verb::ReplaceChar(_))))
|
||||
}).count();
|
||||
let backspace_count = self.cmds.iter().filter(|cmd| {
|
||||
matches!(cmd.verb(),Some(VerbCmd(1, Verb::Delete)))
|
||||
}).count();
|
||||
insert_count > backspace_count
|
||||
}
|
||||
pub fn register_cmd(&mut self, cmd: &ViCmd) {
|
||||
self.cmds.push(cmd.clone())
|
||||
}
|
||||
pub fn take_cmd(&mut self) -> ViCmd {
|
||||
std::mem::take(&mut self.pending_cmd)
|
||||
}
|
||||
}
|
||||
|
||||
impl ViMode for ViReplace {
|
||||
fn handle_key(&mut self, key: E) -> Option<ViCmd> {
|
||||
match key {
|
||||
E(K::Char(ch), M::NONE) => {
|
||||
self.pending_cmd.set_verb(VerbCmd(1,Verb::ReplaceChar(ch)));
|
||||
self.pending_cmd.set_motion(MotionCmd(1,Motion::ForwardChar));
|
||||
self.register_and_return()
|
||||
}
|
||||
E(K::Char('W'), M::CTRL) => {
|
||||
if self.ctrl_w_is_undo() {
|
||||
self.pending_cmd.set_verb(VerbCmd(1,Verb::Undo));
|
||||
self.cmds.clear();
|
||||
Some(self.take_cmd())
|
||||
} else {
|
||||
self.pending_cmd.set_motion(MotionCmd(1, Motion::BackwardWord(To::Start, Word::Normal)));
|
||||
self.register_and_return()
|
||||
}
|
||||
}
|
||||
E(K::Char('H'), M::CTRL) |
|
||||
E(K::Backspace, M::NONE) => {
|
||||
self.pending_cmd.set_motion(MotionCmd(1,Motion::BackwardChar));
|
||||
self.register_and_return()
|
||||
}
|
||||
|
||||
E(K::BackTab, M::NONE) => {
|
||||
self.pending_cmd.set_verb(VerbCmd(1,Verb::CompleteBackward));
|
||||
self.register_and_return()
|
||||
}
|
||||
|
||||
E(K::Char('I'), M::CTRL) |
|
||||
E(K::Tab, M::NONE) => {
|
||||
self.pending_cmd.set_verb(VerbCmd(1,Verb::Complete));
|
||||
self.register_and_return()
|
||||
}
|
||||
|
||||
E(K::Esc, M::NONE) => {
|
||||
self.pending_cmd.set_verb(VerbCmd(1,Verb::NormalMode));
|
||||
self.pending_cmd.set_motion(MotionCmd(1,Motion::BackwardChar));
|
||||
self.register_and_return()
|
||||
}
|
||||
_ => common_cmds(key)
|
||||
}
|
||||
}
|
||||
fn is_repeatable(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn cursor_style(&self) -> String {
|
||||
"\x1b[4 q".to_string()
|
||||
}
|
||||
fn pending_seq(&self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
fn as_replay(&self) -> Option<CmdReplay> {
|
||||
Some(CmdReplay::mode(self.cmds.clone(), self.repeat_count))
|
||||
}
|
||||
fn move_cursor_on_undo(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn clamp_cursor(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn hist_scroll_start_pos(&self) -> Option<To> {
|
||||
Some(To::End)
|
||||
}
|
||||
}
|
||||
#[derive(Default,Debug)]
|
||||
pub struct ViNormal {
|
||||
pending_seq: String,
|
||||
}
|
||||
|
||||
impl ViNormal {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
pub fn clear_cmd(&mut self) {
|
||||
self.pending_seq = String::new();
|
||||
}
|
||||
pub fn take_cmd(&mut self) -> String {
|
||||
std::mem::take(&mut self.pending_seq)
|
||||
}
|
||||
fn validate_combination(&self, verb: Option<&Verb>, motion: Option<&Motion>) -> CmdState {
|
||||
if verb.is_none() {
|
||||
match motion {
|
||||
Some(Motion::TextObj(_,_)) => return CmdState::Invalid,
|
||||
Some(_) => return CmdState::Complete,
|
||||
None => return CmdState::Pending
|
||||
}
|
||||
}
|
||||
if verb.is_some() && motion.is_none() {
|
||||
match verb.unwrap() {
|
||||
Verb::Put(_) |
|
||||
Verb::DeleteChar(_) => CmdState::Complete,
|
||||
_ => CmdState::Pending
|
||||
}
|
||||
} else {
|
||||
CmdState::Complete
|
||||
}
|
||||
}
|
||||
pub fn parse_count(&self, chars: &mut Peekable<Chars<'_>>) -> Option<usize> {
|
||||
let mut count = String::new();
|
||||
let Some(_digit @ '1'..='9') = chars.peek() else {
|
||||
return None
|
||||
};
|
||||
count.push(chars.next().unwrap());
|
||||
while let Some(_digit @ '0'..='9') = chars.peek() {
|
||||
count.push(chars.next().unwrap());
|
||||
}
|
||||
if !count.is_empty() {
|
||||
count.parse::<usize>().ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// End the parse and clear the pending sequence
|
||||
#[track_caller]
|
||||
pub fn quit_parse(&mut self) -> Option<ViCmd> {
|
||||
flog!(DEBUG, std::panic::Location::caller());
|
||||
flog!(WARN, "exiting parse early with sequence: {}",self.pending_seq);
|
||||
self.clear_cmd();
|
||||
None
|
||||
}
|
||||
pub fn try_parse(&mut self, ch: char) -> Option<ViCmd> {
|
||||
self.pending_seq.push(ch);
|
||||
let mut chars = self.pending_seq.chars().peekable();
|
||||
|
||||
let register = 'reg_parse: {
|
||||
let mut chars_clone = chars.clone();
|
||||
let count = self.parse_count(&mut chars_clone);
|
||||
|
||||
let Some('"') = chars_clone.next() else {
|
||||
break 'reg_parse RegisterName::default()
|
||||
};
|
||||
|
||||
let Some(reg_name) = chars_clone.next() else {
|
||||
return None // Pending register name
|
||||
};
|
||||
match reg_name {
|
||||
'a'..='z' |
|
||||
'A'..='Z' => { /* proceed */ }
|
||||
_ => return self.quit_parse()
|
||||
}
|
||||
|
||||
chars = chars_clone;
|
||||
RegisterName::new(Some(reg_name), count)
|
||||
};
|
||||
|
||||
let verb = 'verb_parse: {
|
||||
let mut chars_clone = chars.clone();
|
||||
let count = self.parse_count(&mut chars_clone).unwrap_or(1);
|
||||
|
||||
let Some(ch) = chars_clone.next() else {
|
||||
break 'verb_parse None
|
||||
};
|
||||
match ch {
|
||||
'.' => {
|
||||
return Some(
|
||||
ViCmd {
|
||||
register,
|
||||
verb: Some(VerbCmd(count, Verb::RepeatLast)),
|
||||
motion: None,
|
||||
raw_seq: self.take_cmd(),
|
||||
}
|
||||
)
|
||||
}
|
||||
'x' => {
|
||||
chars = chars_clone;
|
||||
break 'verb_parse Some(VerbCmd(count, Verb::DeleteChar(Anchor::After)));
|
||||
}
|
||||
'X' => {
|
||||
chars = chars_clone;
|
||||
break 'verb_parse Some(VerbCmd(count, Verb::DeleteChar(Anchor::Before)));
|
||||
}
|
||||
'p' => {
|
||||
chars = chars_clone;
|
||||
break 'verb_parse Some(VerbCmd(count, Verb::Put(Anchor::After)));
|
||||
}
|
||||
'P' => {
|
||||
chars = chars_clone;
|
||||
break 'verb_parse Some(VerbCmd(count, Verb::Put(Anchor::Before)));
|
||||
}
|
||||
'>' => {
|
||||
chars = chars_clone;
|
||||
break 'verb_parse Some(VerbCmd(count, Verb::Indent));
|
||||
}
|
||||
'<' => {
|
||||
chars = chars_clone;
|
||||
break 'verb_parse Some(VerbCmd(count, Verb::Dedent));
|
||||
}
|
||||
'r' => {
|
||||
let ch = chars_clone.next()?;
|
||||
return Some(
|
||||
ViCmd {
|
||||
register,
|
||||
verb: Some(VerbCmd(1, Verb::ReplaceChar(ch))),
|
||||
motion: Some(MotionCmd(count, Motion::ForwardChar)),
|
||||
raw_seq: self.take_cmd()
|
||||
}
|
||||
)
|
||||
}
|
||||
'R' => {
|
||||
return Some(
|
||||
ViCmd {
|
||||
register,
|
||||
verb: Some(VerbCmd(count, Verb::ReplaceMode)),
|
||||
motion: None,
|
||||
raw_seq: self.take_cmd()
|
||||
}
|
||||
)
|
||||
}
|
||||
'~' => {
|
||||
return Some(
|
||||
ViCmd {
|
||||
register,
|
||||
verb: Some(VerbCmd(1, Verb::ToggleCase)),
|
||||
motion: Some(MotionCmd(count, Motion::ForwardChar)),
|
||||
raw_seq: self.take_cmd()
|
||||
}
|
||||
)
|
||||
}
|
||||
'u' => {
|
||||
return Some(
|
||||
ViCmd {
|
||||
register,
|
||||
verb: Some(VerbCmd(count, Verb::Undo)),
|
||||
motion: None,
|
||||
raw_seq: self.take_cmd()
|
||||
}
|
||||
)
|
||||
}
|
||||
'o' => {
|
||||
return Some(
|
||||
ViCmd {
|
||||
register,
|
||||
verb: Some(VerbCmd(count, Verb::InsertModeLineBreak(Anchor::After))),
|
||||
motion: None,
|
||||
raw_seq: self.take_cmd()
|
||||
}
|
||||
)
|
||||
}
|
||||
'O' => {
|
||||
return Some(
|
||||
ViCmd {
|
||||
register,
|
||||
verb: Some(VerbCmd(count, Verb::InsertModeLineBreak(Anchor::Before))),
|
||||
motion: None,
|
||||
raw_seq: self.take_cmd()
|
||||
}
|
||||
)
|
||||
}
|
||||
'a' => {
|
||||
return Some(
|
||||
ViCmd {
|
||||
register,
|
||||
verb: Some(VerbCmd(count, Verb::InsertMode)),
|
||||
motion: Some(MotionCmd(1, Motion::ForwardChar)),
|
||||
raw_seq: self.take_cmd()
|
||||
}
|
||||
)
|
||||
}
|
||||
'A' => {
|
||||
return Some(
|
||||
ViCmd {
|
||||
register,
|
||||
verb: Some(VerbCmd(count, Verb::InsertMode)),
|
||||
motion: Some(MotionCmd(1, Motion::EndOfLine)),
|
||||
raw_seq: self.take_cmd()
|
||||
}
|
||||
)
|
||||
}
|
||||
'i' => {
|
||||
return Some(
|
||||
ViCmd {
|
||||
register,
|
||||
verb: Some(VerbCmd(count, Verb::InsertMode)),
|
||||
motion: None,
|
||||
raw_seq: self.take_cmd()
|
||||
}
|
||||
)
|
||||
}
|
||||
'I' => {
|
||||
return Some(
|
||||
ViCmd {
|
||||
register,
|
||||
verb: Some(VerbCmd(count, Verb::InsertMode)),
|
||||
motion: Some(MotionCmd(1, Motion::BeginningOfFirstWord)),
|
||||
raw_seq: self.take_cmd()
|
||||
}
|
||||
)
|
||||
}
|
||||
'J' => {
|
||||
return Some(
|
||||
ViCmd {
|
||||
register,
|
||||
verb: Some(VerbCmd(count, Verb::JoinLines)),
|
||||
motion: None,
|
||||
raw_seq: self.take_cmd()
|
||||
}
|
||||
)
|
||||
}
|
||||
'y' => {
|
||||
chars = chars_clone;
|
||||
break 'verb_parse Some(VerbCmd(count, Verb::Yank))
|
||||
}
|
||||
'd' => {
|
||||
chars = chars_clone;
|
||||
break 'verb_parse Some(VerbCmd(count, Verb::Delete))
|
||||
}
|
||||
'c' => {
|
||||
chars = chars_clone;
|
||||
break 'verb_parse Some(VerbCmd(count, Verb::Change))
|
||||
}
|
||||
'Y' => {
|
||||
return Some(
|
||||
ViCmd {
|
||||
register,
|
||||
verb: Some(VerbCmd(count, Verb::Yank)),
|
||||
motion: Some(MotionCmd(1, Motion::EndOfLine)),
|
||||
raw_seq: self.take_cmd()
|
||||
}
|
||||
)
|
||||
}
|
||||
'D' => {
|
||||
return Some(
|
||||
ViCmd {
|
||||
register,
|
||||
verb: Some(VerbCmd(count, Verb::Delete)),
|
||||
motion: Some(MotionCmd(1, Motion::EndOfLine)),
|
||||
raw_seq: self.take_cmd()
|
||||
}
|
||||
)
|
||||
}
|
||||
'C' => {
|
||||
return Some(
|
||||
ViCmd {
|
||||
register,
|
||||
verb: Some(VerbCmd(count, Verb::Change)),
|
||||
motion: Some(MotionCmd(1, Motion::EndOfLine)),
|
||||
raw_seq: self.take_cmd()
|
||||
}
|
||||
)
|
||||
}
|
||||
'=' => {
|
||||
chars = chars_clone;
|
||||
break 'verb_parse Some(VerbCmd(count, Verb::Equalize))
|
||||
}
|
||||
_ => break 'verb_parse None
|
||||
}
|
||||
};
|
||||
|
||||
let motion = 'motion_parse: {
|
||||
let mut chars_clone = chars.clone();
|
||||
let count = self.parse_count(&mut chars_clone).unwrap_or(1);
|
||||
|
||||
let Some(ch) = chars_clone.next() else {
|
||||
break 'motion_parse None
|
||||
};
|
||||
match (ch, &verb) {
|
||||
('d', Some(VerbCmd(_,Verb::Delete))) |
|
||||
('c', Some(VerbCmd(_,Verb::Change))) |
|
||||
('y', Some(VerbCmd(_,Verb::Yank))) |
|
||||
('=', Some(VerbCmd(_,Verb::Equalize))) |
|
||||
('>', Some(VerbCmd(_,Verb::Indent))) |
|
||||
('<', Some(VerbCmd(_,Verb::Dedent))) => break 'motion_parse Some(MotionCmd(count, Motion::WholeLine)),
|
||||
_ => {}
|
||||
}
|
||||
match ch {
|
||||
'g' => {
|
||||
if let Some(ch) = chars_clone.peek() {
|
||||
match ch {
|
||||
'g' => {
|
||||
chars_clone.next();
|
||||
chars = chars_clone;
|
||||
break 'motion_parse Some(MotionCmd(count, Motion::BeginningOfBuffer))
|
||||
}
|
||||
'e' => {
|
||||
chars = chars_clone;
|
||||
break 'motion_parse Some(MotionCmd(count, Motion::BackwardWord(To::End, Word::Normal)));
|
||||
}
|
||||
'E' => {
|
||||
chars = chars_clone;
|
||||
break 'motion_parse Some(MotionCmd(count, Motion::BackwardWord(To::End, Word::Big)));
|
||||
}
|
||||
'k' => {
|
||||
chars = chars_clone;
|
||||
break 'motion_parse Some(MotionCmd(count, Motion::ScreenLineUp));
|
||||
}
|
||||
'j' => {
|
||||
chars = chars_clone;
|
||||
break 'motion_parse Some(MotionCmd(count, Motion::ScreenLineDown));
|
||||
}
|
||||
_ => return self.quit_parse()
|
||||
}
|
||||
} else {
|
||||
break 'motion_parse None
|
||||
}
|
||||
}
|
||||
'f' => {
|
||||
let Some(ch) = chars_clone.peek() else {
|
||||
break 'motion_parse None
|
||||
};
|
||||
|
||||
break 'motion_parse Some(MotionCmd(count, Motion::CharSearch(Direction::Forward, Dest::On, (*ch).into())))
|
||||
}
|
||||
'F' => {
|
||||
let Some(ch) = chars_clone.peek() else {
|
||||
break 'motion_parse None
|
||||
};
|
||||
|
||||
break 'motion_parse Some(MotionCmd(count, Motion::CharSearch(Direction::Backward, Dest::On, (*ch).into())))
|
||||
}
|
||||
't' => {
|
||||
let Some(ch) = chars_clone.peek() else {
|
||||
break 'motion_parse None
|
||||
};
|
||||
|
||||
break 'motion_parse Some(MotionCmd(count, Motion::CharSearch(Direction::Forward, Dest::Before, (*ch).into())))
|
||||
}
|
||||
'T' => {
|
||||
let Some(ch) = chars_clone.peek() else {
|
||||
break 'motion_parse None
|
||||
};
|
||||
|
||||
break 'motion_parse Some(MotionCmd(count, Motion::CharSearch(Direction::Backward, Dest::Before, (*ch).into())))
|
||||
}
|
||||
';' => {
|
||||
chars = chars_clone;
|
||||
break 'motion_parse Some(MotionCmd(count, Motion::RepeatMotion));
|
||||
}
|
||||
',' => {
|
||||
chars = chars_clone;
|
||||
break 'motion_parse Some(MotionCmd(count, Motion::RepeatMotionRev));
|
||||
}
|
||||
'|' => {
|
||||
chars = chars_clone;
|
||||
break 'motion_parse Some(MotionCmd(1, Motion::ToColumn(count)));
|
||||
}
|
||||
'0' => {
|
||||
chars = chars_clone;
|
||||
break 'motion_parse Some(MotionCmd(count, Motion::BeginningOfLine));
|
||||
}
|
||||
'$' => {
|
||||
chars = chars_clone;
|
||||
break 'motion_parse Some(MotionCmd(count, Motion::EndOfLine));
|
||||
}
|
||||
'k' => {
|
||||
chars = chars_clone;
|
||||
break 'motion_parse Some(MotionCmd(count, Motion::LineUp));
|
||||
}
|
||||
'j' => {
|
||||
chars = chars_clone;
|
||||
break 'motion_parse Some(MotionCmd(count, Motion::LineDown));
|
||||
}
|
||||
'h' => {
|
||||
chars = chars_clone;
|
||||
break 'motion_parse Some(MotionCmd(count, Motion::BackwardChar));
|
||||
}
|
||||
'l' => {
|
||||
chars = chars_clone;
|
||||
break 'motion_parse Some(MotionCmd(count, Motion::ForwardChar));
|
||||
}
|
||||
'w' => {
|
||||
chars = chars_clone;
|
||||
break 'motion_parse Some(MotionCmd(count, Motion::ForwardWord(To::Start, Word::Normal)));
|
||||
}
|
||||
'W' => {
|
||||
chars = chars_clone;
|
||||
break 'motion_parse Some(MotionCmd(count, Motion::ForwardWord(To::Start, Word::Big)));
|
||||
}
|
||||
'e' => {
|
||||
chars = chars_clone;
|
||||
break 'motion_parse Some(MotionCmd(count, Motion::ForwardWord(To::End, Word::Normal)));
|
||||
}
|
||||
'E' => {
|
||||
chars = chars_clone;
|
||||
break 'motion_parse Some(MotionCmd(count, Motion::ForwardWord(To::End, Word::Big)));
|
||||
}
|
||||
'b' => {
|
||||
chars = chars_clone;
|
||||
break 'motion_parse Some(MotionCmd(count, Motion::BackwardWord(To::Start, Word::Normal)));
|
||||
}
|
||||
'B' => {
|
||||
chars = chars_clone;
|
||||
break 'motion_parse Some(MotionCmd(count, Motion::BackwardWord(To::Start, Word::Big)));
|
||||
}
|
||||
ch if ch == 'i' || ch == 'a' => {
|
||||
let bound = match ch {
|
||||
'i' => Bound::Inside,
|
||||
'a' => Bound::Around,
|
||||
_ => unreachable!()
|
||||
};
|
||||
if chars_clone.peek().is_none() {
|
||||
break 'motion_parse None
|
||||
}
|
||||
let obj = match chars_clone.next().unwrap() {
|
||||
'w' => TextObj::Word(Word::Normal),
|
||||
'W' => TextObj::Word(Word::Big),
|
||||
'"' => TextObj::DoubleQuote,
|
||||
'\'' => TextObj::SingleQuote,
|
||||
'(' | ')' | 'b' => TextObj::Paren,
|
||||
'{' | '}' | 'B' => TextObj::Brace,
|
||||
'[' | ']' => TextObj::Bracket,
|
||||
'<' | '>' => TextObj::Angle,
|
||||
_ => return self.quit_parse()
|
||||
};
|
||||
chars = chars_clone;
|
||||
break 'motion_parse Some(MotionCmd(count, Motion::TextObj(obj, bound)))
|
||||
}
|
||||
_ => return self.quit_parse(),
|
||||
}
|
||||
};
|
||||
|
||||
if chars.peek().is_some() {
|
||||
flog!(WARN, "Unused characters in Vi command parse!");
|
||||
flog!(WARN, "{:?}",chars)
|
||||
}
|
||||
|
||||
let verb_ref = verb.as_ref().map(|v| &v.1);
|
||||
let motion_ref = motion.as_ref().map(|m| &m.1);
|
||||
|
||||
match self.validate_combination(verb_ref, motion_ref) {
|
||||
CmdState::Complete => {
|
||||
let cmd = Some(
|
||||
ViCmd {
|
||||
register,
|
||||
verb,
|
||||
motion,
|
||||
raw_seq: std::mem::take(&mut self.pending_seq)
|
||||
}
|
||||
);
|
||||
cmd
|
||||
}
|
||||
CmdState::Pending => {
|
||||
None
|
||||
}
|
||||
CmdState::Invalid => {
|
||||
self.pending_seq.clear();
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ViMode for ViNormal {
|
||||
fn handle_key(&mut self, key: E) -> Option<ViCmd> {
|
||||
match key {
|
||||
E(K::Char(ch), M::NONE) => self.try_parse(ch),
|
||||
E(K::Backspace, M::NONE) => {
|
||||
Some(ViCmd {
|
||||
register: Default::default(),
|
||||
verb: None,
|
||||
motion: Some(MotionCmd(1, Motion::BackwardChar)),
|
||||
raw_seq: "".into(),
|
||||
})
|
||||
}
|
||||
E(K::Char('R'), M::CTRL) => {
|
||||
let mut chars = self.pending_seq.chars().peekable();
|
||||
let count = self.parse_count(&mut chars).unwrap_or(1);
|
||||
Some(
|
||||
ViCmd {
|
||||
register: RegisterName::default(),
|
||||
verb: Some(VerbCmd(count,Verb::Redo)),
|
||||
motion: None,
|
||||
raw_seq: self.take_cmd()
|
||||
}
|
||||
)
|
||||
}
|
||||
E(K::Esc, M::NONE) => {
|
||||
self.clear_cmd();
|
||||
None
|
||||
}
|
||||
_ => {
|
||||
if let Some(cmd) = common_cmds(key) {
|
||||
self.clear_cmd();
|
||||
Some(cmd)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_repeatable(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn as_replay(&self) -> Option<CmdReplay> {
|
||||
None
|
||||
}
|
||||
|
||||
fn cursor_style(&self) -> String {
|
||||
"\x1b[2 q".to_string()
|
||||
}
|
||||
|
||||
fn pending_seq(&self) -> Option<String> {
|
||||
Some(self.pending_seq.clone())
|
||||
}
|
||||
|
||||
fn move_cursor_on_undo(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn clamp_cursor(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn hist_scroll_start_pos(&self) -> Option<To> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn common_cmds(key: E) -> Option<ViCmd> {
|
||||
let mut pending_cmd = ViCmd::new();
|
||||
match key {
|
||||
E(K::Home, M::NONE) => pending_cmd.set_motion(MotionCmd(1,Motion::BeginningOfLine)),
|
||||
E(K::End, M::NONE) => pending_cmd.set_motion(MotionCmd(1,Motion::EndOfLine)),
|
||||
E(K::Left, M::NONE) => pending_cmd.set_motion(MotionCmd(1,Motion::BackwardChar)),
|
||||
E(K::Right, M::NONE) => pending_cmd.set_motion(MotionCmd(1,Motion::ForwardChar)),
|
||||
E(K::Up, M::NONE) => pending_cmd.set_motion(MotionCmd(1,Motion::LineUp)),
|
||||
E(K::Down, M::NONE) => pending_cmd.set_motion(MotionCmd(1,Motion::LineDown)),
|
||||
E(K::Enter, M::NONE) => pending_cmd.set_verb(VerbCmd(1,Verb::AcceptLine)),
|
||||
E(K::Char('D'), M::CTRL) => pending_cmd.set_verb(VerbCmd(1,Verb::EndOfFile)),
|
||||
E(K::Delete, M::NONE) => pending_cmd.set_verb(VerbCmd(1,Verb::DeleteChar(Anchor::After))),
|
||||
E(K::Backspace, M::NONE) |
|
||||
E(K::Char('H'), M::CTRL) => pending_cmd.set_verb(VerbCmd(1,Verb::DeleteChar(Anchor::Before))),
|
||||
_ => return None
|
||||
}
|
||||
Some(pending_cmd)
|
||||
}
|
||||
168
src/prompt/readline/register.rs
Normal file
168
src/prompt/readline/register.rs
Normal file
@@ -0,0 +1,168 @@
|
||||
use std::sync::Mutex;
|
||||
|
||||
pub static REGISTERS: Mutex<Registers> = Mutex::new(Registers::new());
|
||||
|
||||
pub fn read_register(ch: Option<char>) -> Option<String> {
|
||||
let lock = REGISTERS.lock().unwrap();
|
||||
lock.get_reg(ch).map(|r| r.buf().clone())
|
||||
}
|
||||
|
||||
pub fn write_register(ch: Option<char>, buf: String) {
|
||||
let mut lock = REGISTERS.lock().unwrap();
|
||||
if let Some(r) = lock.get_reg_mut(ch) { r.write(buf) }
|
||||
}
|
||||
|
||||
pub fn append_register(ch: Option<char>, buf: String) {
|
||||
let mut lock = REGISTERS.lock().unwrap();
|
||||
if let Some(r) = lock.get_reg_mut(ch) { r.append(buf) }
|
||||
}
|
||||
|
||||
#[derive(Default,Debug)]
|
||||
pub struct Registers {
|
||||
default: Register,
|
||||
a: Register,
|
||||
b: Register,
|
||||
c: Register,
|
||||
d: Register,
|
||||
e: Register,
|
||||
f: Register,
|
||||
g: Register,
|
||||
h: Register,
|
||||
i: Register,
|
||||
j: Register,
|
||||
k: Register,
|
||||
l: Register,
|
||||
m: Register,
|
||||
n: Register,
|
||||
o: Register,
|
||||
p: Register,
|
||||
q: Register,
|
||||
r: Register,
|
||||
s: Register,
|
||||
t: Register,
|
||||
u: Register,
|
||||
v: Register,
|
||||
w: Register,
|
||||
x: Register,
|
||||
y: Register,
|
||||
z: Register,
|
||||
}
|
||||
|
||||
impl Registers {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
default: Register(String::new()),
|
||||
a: Register(String::new()),
|
||||
b: Register(String::new()),
|
||||
c: Register(String::new()),
|
||||
d: Register(String::new()),
|
||||
e: Register(String::new()),
|
||||
f: Register(String::new()),
|
||||
g: Register(String::new()),
|
||||
h: Register(String::new()),
|
||||
i: Register(String::new()),
|
||||
j: Register(String::new()),
|
||||
k: Register(String::new()),
|
||||
l: Register(String::new()),
|
||||
m: Register(String::new()),
|
||||
n: Register(String::new()),
|
||||
o: Register(String::new()),
|
||||
p: Register(String::new()),
|
||||
q: Register(String::new()),
|
||||
r: Register(String::new()),
|
||||
s: Register(String::new()),
|
||||
t: Register(String::new()),
|
||||
u: Register(String::new()),
|
||||
v: Register(String::new()),
|
||||
w: Register(String::new()),
|
||||
x: Register(String::new()),
|
||||
y: Register(String::new()),
|
||||
z: Register(String::new()),
|
||||
}
|
||||
}
|
||||
pub fn get_reg(&self, ch: Option<char>) -> Option<&Register> {
|
||||
let Some(ch) = ch else {
|
||||
return Some(&self.default)
|
||||
};
|
||||
match ch {
|
||||
'a' => Some(&self.a),
|
||||
'b' => Some(&self.b),
|
||||
'c' => Some(&self.c),
|
||||
'd' => Some(&self.d),
|
||||
'e' => Some(&self.e),
|
||||
'f' => Some(&self.f),
|
||||
'g' => Some(&self.g),
|
||||
'h' => Some(&self.h),
|
||||
'i' => Some(&self.i),
|
||||
'j' => Some(&self.j),
|
||||
'k' => Some(&self.k),
|
||||
'l' => Some(&self.l),
|
||||
'm' => Some(&self.m),
|
||||
'n' => Some(&self.n),
|
||||
'o' => Some(&self.o),
|
||||
'p' => Some(&self.p),
|
||||
'q' => Some(&self.q),
|
||||
'r' => Some(&self.r),
|
||||
's' => Some(&self.s),
|
||||
't' => Some(&self.t),
|
||||
'u' => Some(&self.u),
|
||||
'v' => Some(&self.v),
|
||||
'w' => Some(&self.w),
|
||||
'x' => Some(&self.x),
|
||||
'y' => Some(&self.y),
|
||||
'z' => Some(&self.z),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
pub fn get_reg_mut(&mut self, ch: Option<char>) -> Option<&mut Register> {
|
||||
let Some(ch) = ch else {
|
||||
return Some(&mut self.default)
|
||||
};
|
||||
match ch {
|
||||
'a' => Some(&mut self.a),
|
||||
'b' => Some(&mut self.b),
|
||||
'c' => Some(&mut self.c),
|
||||
'd' => Some(&mut self.d),
|
||||
'e' => Some(&mut self.e),
|
||||
'f' => Some(&mut self.f),
|
||||
'g' => Some(&mut self.g),
|
||||
'h' => Some(&mut self.h),
|
||||
'i' => Some(&mut self.i),
|
||||
'j' => Some(&mut self.j),
|
||||
'k' => Some(&mut self.k),
|
||||
'l' => Some(&mut self.l),
|
||||
'm' => Some(&mut self.m),
|
||||
'n' => Some(&mut self.n),
|
||||
'o' => Some(&mut self.o),
|
||||
'p' => Some(&mut self.p),
|
||||
'q' => Some(&mut self.q),
|
||||
'r' => Some(&mut self.r),
|
||||
's' => Some(&mut self.s),
|
||||
't' => Some(&mut self.t),
|
||||
'u' => Some(&mut self.u),
|
||||
'v' => Some(&mut self.v),
|
||||
'w' => Some(&mut self.w),
|
||||
'x' => Some(&mut self.x),
|
||||
'y' => Some(&mut self.y),
|
||||
'z' => Some(&mut self.z),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Default,Debug)]
|
||||
pub struct Register(String);
|
||||
impl Register {
|
||||
pub fn buf(&self) -> &String {
|
||||
&self.0
|
||||
}
|
||||
pub fn write(&mut self, buf: String) {
|
||||
self.0 = buf
|
||||
}
|
||||
pub fn append(&mut self, buf: String) {
|
||||
self.0.push_str(&buf)
|
||||
}
|
||||
pub fn clear(&mut self) {
|
||||
self.0.clear()
|
||||
}
|
||||
}
|
||||
369
src/prompt/readline/term.rs
Normal file
369
src/prompt/readline/term.rs
Normal file
@@ -0,0 +1,369 @@
|
||||
use std::{os::fd::{BorrowedFd, RawFd}, thread::sleep, time::{Duration, Instant}};
|
||||
use nix::{errno::Errno, fcntl::{fcntl, FcntlArg, OFlag}, libc::{self, STDIN_FILENO}, sys::termios, unistd::{isatty, read, write}};
|
||||
use nix::libc::{winsize, TIOCGWINSZ};
|
||||
use unicode_width::UnicodeWidthChar;
|
||||
use std::mem::zeroed;
|
||||
use std::io;
|
||||
|
||||
use crate::libsh::error::ShResult;
|
||||
use crate::prelude::*;
|
||||
|
||||
use super::keys::{KeyCode, KeyEvent, ModKeys};
|
||||
|
||||
#[derive(Default,Debug)]
|
||||
struct WriteMap {
|
||||
lines: usize,
|
||||
cols: usize,
|
||||
offset: usize
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Terminal {
|
||||
stdin: RawFd,
|
||||
stdout: RawFd,
|
||||
recording: bool,
|
||||
write_records: WriteMap,
|
||||
cursor_records: WriteMap
|
||||
}
|
||||
|
||||
impl Terminal {
|
||||
pub fn new() -> Self {
|
||||
assert!(isatty(STDIN_FILENO).unwrap());
|
||||
Self {
|
||||
stdin: STDIN_FILENO,
|
||||
stdout: 1,
|
||||
recording: false,
|
||||
// Records for buffer writes
|
||||
// Used to find the start of the buffer
|
||||
write_records: WriteMap::default(),
|
||||
// Records for cursor movements after writes
|
||||
// Used to find the end of the buffer
|
||||
cursor_records: WriteMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn raw_mode() -> termios::Termios {
|
||||
let orig = termios::tcgetattr(unsafe{BorrowedFd::borrow_raw(STDIN_FILENO)}).expect("Failed to get terminal attributes");
|
||||
let mut raw = orig.clone();
|
||||
termios::cfmakeraw(&mut raw);
|
||||
termios::tcsetattr(unsafe{BorrowedFd::borrow_raw(STDIN_FILENO)}, termios::SetArg::TCSANOW, &raw)
|
||||
.expect("Failed to set terminal to raw mode");
|
||||
orig
|
||||
}
|
||||
|
||||
pub fn restore_termios(termios: termios::Termios) {
|
||||
termios::tcsetattr(unsafe{BorrowedFd::borrow_raw(STDIN_FILENO)}, termios::SetArg::TCSANOW, &termios)
|
||||
.expect("Failed to restore terminal settings");
|
||||
}
|
||||
|
||||
|
||||
pub fn get_dimensions(&self) -> ShResult<(usize, usize)> {
|
||||
if !isatty(self.stdin).unwrap_or(false) {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "Not a TTY"))?;
|
||||
}
|
||||
|
||||
let mut ws: winsize = unsafe { zeroed() };
|
||||
|
||||
let res = unsafe { libc::ioctl(self.stdin, TIOCGWINSZ, &mut ws) };
|
||||
if res == -1 {
|
||||
return Err(io::Error::last_os_error())?;
|
||||
}
|
||||
|
||||
Ok((ws.ws_row as usize, ws.ws_col as usize))
|
||||
}
|
||||
|
||||
pub fn start_recording(&mut self, offset: usize) {
|
||||
self.recording = true;
|
||||
self.write_records.offset = offset;
|
||||
}
|
||||
|
||||
pub fn stop_recording(&mut self) {
|
||||
self.recording = false;
|
||||
}
|
||||
|
||||
pub fn save_cursor_pos(&mut self) {
|
||||
self.write("\x1b[s")
|
||||
}
|
||||
|
||||
pub fn restore_cursor_pos(&mut self) {
|
||||
self.write("\x1b[u")
|
||||
}
|
||||
|
||||
pub fn move_cursor_to(&mut self, (row,col): (usize,usize)) {
|
||||
self.write(&format!("\x1b[{row};{col}H",))
|
||||
}
|
||||
|
||||
pub fn with_raw_mode<F: FnOnce() -> R, R>(func: F) -> R {
|
||||
let saved = Self::raw_mode();
|
||||
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(func));
|
||||
Self::restore_termios(saved);
|
||||
match result {
|
||||
Ok(r) => r,
|
||||
Err(e) => std::panic::resume_unwind(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_byte(&self, buf: &mut [u8]) -> usize {
|
||||
Self::with_raw_mode(|| {
|
||||
read(self.stdin, buf).expect("Failed to read from stdin")
|
||||
})
|
||||
}
|
||||
|
||||
fn read_blocks_then_read(&self, buf: &mut [u8], timeout: Duration) -> Option<usize> {
|
||||
Self::with_raw_mode(|| {
|
||||
self.read_blocks(false);
|
||||
let start = Instant::now();
|
||||
loop {
|
||||
match read(self.stdin, buf) {
|
||||
Ok(n) if n > 0 => {
|
||||
self.read_blocks(true);
|
||||
return Some(n);
|
||||
}
|
||||
Ok(_) => {}
|
||||
Err(e) if e == Errno::EAGAIN => {}
|
||||
Err(_) => return None,
|
||||
}
|
||||
if start.elapsed() > timeout {
|
||||
self.read_blocks(true);
|
||||
return None;
|
||||
}
|
||||
sleep(Duration::from_millis(1));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Same as read_byte(), only non-blocking with a very short timeout
|
||||
pub fn peek_byte(&self, buf: &mut [u8]) -> usize {
|
||||
const TIMEOUT_DUR: Duration = Duration::from_millis(50);
|
||||
Self::with_raw_mode(|| {
|
||||
self.read_blocks(false);
|
||||
|
||||
let start = Instant::now();
|
||||
loop {
|
||||
match read(self.stdin, buf) {
|
||||
Ok(n) if n > 0 => {
|
||||
self.read_blocks(true);
|
||||
return n
|
||||
}
|
||||
Ok(_) => {}
|
||||
Err(Errno::EAGAIN) => {}
|
||||
Err(e) => panic!("nonblocking read failed: {e}")
|
||||
}
|
||||
|
||||
if start.elapsed() >= TIMEOUT_DUR {
|
||||
self.read_blocks(true);
|
||||
return 0
|
||||
}
|
||||
|
||||
sleep(Duration::from_millis(1));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn read_blocks(&self, yn: bool) {
|
||||
let flags = OFlag::from_bits_truncate(fcntl(self.stdin, FcntlArg::F_GETFL).unwrap());
|
||||
let new_flags = if !yn {
|
||||
flags | OFlag::O_NONBLOCK
|
||||
} else {
|
||||
flags & !OFlag::O_NONBLOCK
|
||||
};
|
||||
fcntl(self.stdin, FcntlArg::F_SETFL(new_flags)).unwrap();
|
||||
}
|
||||
|
||||
pub fn reset_records(&mut self) {
|
||||
self.write_records = Default::default();
|
||||
self.cursor_records = Default::default();
|
||||
}
|
||||
|
||||
pub fn recorded_write(&mut self, buf: &str, offset: usize) -> ShResult<()> {
|
||||
self.start_recording(offset);
|
||||
self.write(buf);
|
||||
self.stop_recording();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Rewinds terminal writing, clears lines and lands on the anchor point of the prompt
|
||||
pub fn unwrite(&mut self) -> ShResult<()> {
|
||||
self.unposition_cursor()?;
|
||||
let WriteMap { lines, cols, offset } = self.write_records;
|
||||
for _ in 0..lines {
|
||||
self.write("\x1b[2K\x1b[A")
|
||||
}
|
||||
let col = offset;
|
||||
self.write(&format!("\x1b[{col}G\x1b[0K"));
|
||||
self.reset_records();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn position_cursor(&mut self, (lines,col): (usize,usize)) -> ShResult<()> {
|
||||
flog!(DEBUG,lines);
|
||||
self.cursor_records.lines = lines;
|
||||
self.cursor_records.cols = col;
|
||||
self.cursor_records.offset = self.cursor_pos().1;
|
||||
|
||||
for _ in 0..lines {
|
||||
self.write("\x1b[A")
|
||||
}
|
||||
|
||||
self.write(&format!("\x1b[{col}G"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Rewinds cursor positioning, lands on the end of the buffer
|
||||
pub fn unposition_cursor(&mut self) ->ShResult<()> {
|
||||
let WriteMap { lines, cols, offset } = self.cursor_records;
|
||||
|
||||
for _ in 0..lines {
|
||||
self.write("\x1b[B")
|
||||
}
|
||||
|
||||
self.write(&format!("\x1b[{offset}G"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_bytes(&mut self, buf: &[u8]) {
|
||||
if self.recording {
|
||||
let (_, width) = self.get_dimensions().unwrap();
|
||||
let mut bytes = buf.iter().map(|&b| b as char).peekable();
|
||||
while let Some(ch) = bytes.next() {
|
||||
match ch {
|
||||
'\n' => {
|
||||
self.write_records.lines += 1;
|
||||
self.write_records.cols = 0;
|
||||
}
|
||||
'\r' => {
|
||||
self.write_records.cols = 0;
|
||||
}
|
||||
// Consume escape sequences
|
||||
'\x1b' if bytes.peek() == Some(&'[') => {
|
||||
bytes.next();
|
||||
while let Some(&ch) = bytes.peek() {
|
||||
if ch.is_ascii_alphabetic() {
|
||||
bytes.next();
|
||||
break
|
||||
} else {
|
||||
bytes.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
'\t' => {
|
||||
let tab_size = 8;
|
||||
let next_tab = tab_size - (self.write_records.cols % tab_size);
|
||||
self.write_records.cols += next_tab;
|
||||
if self.write_records.cols > width {
|
||||
self.write_records.lines += 1;
|
||||
self.write_records.cols = 0;
|
||||
}
|
||||
}
|
||||
_ if ch.is_control() => {
|
||||
// ignore control characters for visual width
|
||||
}
|
||||
_ => {
|
||||
let ch_width = ch.width().unwrap_or(0);
|
||||
if self.write_records.cols + ch_width > width {
|
||||
self.write_records.lines += 1;
|
||||
self.write_records.cols = 0;
|
||||
}
|
||||
self.write_records.cols += ch_width;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
write(unsafe { BorrowedFd::borrow_raw(self.stdout) }, buf).expect("Failed to write to stdout");
|
||||
}
|
||||
|
||||
|
||||
pub fn write(&mut self, s: &str) {
|
||||
self.write_bytes(s.as_bytes());
|
||||
}
|
||||
|
||||
pub fn writeln(&mut self, s: &str) {
|
||||
self.write(s);
|
||||
self.write_bytes(b"\n");
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.write_bytes(b"\x1b[2J\x1b[H");
|
||||
}
|
||||
|
||||
pub fn read_key(&self) -> KeyEvent {
|
||||
use core::str;
|
||||
|
||||
let mut buf = [0u8; 8];
|
||||
let mut collected = Vec::with_capacity(5);
|
||||
|
||||
loop {
|
||||
let n = self.read_byte(&mut buf[..1]); // Read one byte at a time
|
||||
if n == 0 {
|
||||
continue;
|
||||
}
|
||||
collected.push(buf[0]);
|
||||
|
||||
// ESC sequences
|
||||
if collected[0] == 0x1b && collected.len() == 1 {
|
||||
// Peek next byte if any
|
||||
let n = self.peek_byte(&mut buf[..1]);
|
||||
if n == 0 {
|
||||
return KeyEvent(KeyCode::Esc, ModKeys::empty());
|
||||
}
|
||||
collected.push(buf[0]);
|
||||
|
||||
if buf[0] == b'[' {
|
||||
// Read third byte
|
||||
let _ = self.read_byte(&mut buf[..1]);
|
||||
collected.push(buf[0]);
|
||||
|
||||
return match buf[0] {
|
||||
b'A' => KeyEvent(KeyCode::Up, ModKeys::empty()),
|
||||
b'B' => KeyEvent(KeyCode::Down, ModKeys::empty()),
|
||||
b'C' => KeyEvent(KeyCode::Right, ModKeys::empty()),
|
||||
b'D' => KeyEvent(KeyCode::Left, ModKeys::empty()),
|
||||
_ => KeyEvent(KeyCode::Esc, ModKeys::empty()),
|
||||
};
|
||||
}
|
||||
|
||||
return KeyEvent(KeyCode::Esc, ModKeys::empty());
|
||||
}
|
||||
|
||||
// Try parse valid UTF-8 from collected bytes
|
||||
if let Ok(s) = str::from_utf8(&collected) {
|
||||
return KeyEvent::new(s, ModKeys::empty());
|
||||
}
|
||||
|
||||
// If it's not valid UTF-8 yet, loop to collect more bytes
|
||||
if collected.len() >= 4 {
|
||||
// UTF-8 max char length is 4; if it's still invalid, give up
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
KeyEvent(KeyCode::Null, ModKeys::empty())
|
||||
}
|
||||
|
||||
pub fn cursor_pos(&mut self) -> (usize, usize) {
|
||||
self.write("\x1b[6n");
|
||||
let mut buf = [0u8;32];
|
||||
let n = self.read_byte(&mut buf);
|
||||
|
||||
|
||||
let response = std::str::from_utf8(&buf[..n]).unwrap_or("");
|
||||
let mut row = 0;
|
||||
let mut col = 0;
|
||||
if let Some(caps) = response.strip_prefix("\x1b[").and_then(|s| s.strip_suffix("R")) {
|
||||
let mut parts = caps.split(';');
|
||||
if let (Some(rowstr), Some(colstr)) = (parts.next(), parts.next()) {
|
||||
row = rowstr.parse().unwrap_or(1);
|
||||
col = colstr.parse().unwrap_or(1);
|
||||
}
|
||||
}
|
||||
(row,col)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Terminal {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
358
src/prompt/readline/vicmd.rs
Normal file
358
src/prompt/readline/vicmd.rs
Normal file
@@ -0,0 +1,358 @@
|
||||
use super::register::{append_register, read_register, write_register};
|
||||
|
||||
#[derive(Clone,Copy,Debug)]
|
||||
pub struct RegisterName {
|
||||
name: Option<char>,
|
||||
count: usize,
|
||||
append: bool
|
||||
}
|
||||
|
||||
impl RegisterName {
|
||||
pub fn new(name: Option<char>, count: Option<usize>) -> Self {
|
||||
let Some(ch) = name else {
|
||||
return Self::default()
|
||||
};
|
||||
|
||||
let append = ch.is_uppercase();
|
||||
let name = ch.to_ascii_lowercase();
|
||||
Self {
|
||||
name: Some(name),
|
||||
count: count.unwrap_or(1),
|
||||
append
|
||||
}
|
||||
}
|
||||
pub fn name(&self) -> Option<char> {
|
||||
self.name
|
||||
}
|
||||
pub fn is_append(&self) -> bool {
|
||||
self.append
|
||||
}
|
||||
pub fn count(&self) -> usize {
|
||||
self.count
|
||||
}
|
||||
pub fn write_to_register(&self, buf: String) {
|
||||
if self.append {
|
||||
append_register(self.name, buf);
|
||||
} else {
|
||||
write_register(self.name, buf);
|
||||
}
|
||||
}
|
||||
pub fn read_from_register(&self) -> Option<String> {
|
||||
read_register(self.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for RegisterName {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: None,
|
||||
count: 1,
|
||||
append: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Default,Debug)]
|
||||
pub struct ViCmd {
|
||||
pub register: RegisterName,
|
||||
pub verb: Option<VerbCmd>,
|
||||
pub motion: Option<MotionCmd>,
|
||||
pub raw_seq: String,
|
||||
}
|
||||
|
||||
impl ViCmd {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
pub fn set_motion(&mut self, motion: MotionCmd) {
|
||||
self.motion = Some(motion)
|
||||
}
|
||||
pub fn set_verb(&mut self, verb: VerbCmd) {
|
||||
self.verb = Some(verb)
|
||||
}
|
||||
pub fn verb(&self) -> Option<&VerbCmd> {
|
||||
self.verb.as_ref()
|
||||
}
|
||||
pub fn motion(&self) -> Option<&MotionCmd> {
|
||||
self.motion.as_ref()
|
||||
}
|
||||
pub fn verb_count(&self) -> usize {
|
||||
self.verb.as_ref().map(|v| v.0).unwrap_or(1)
|
||||
}
|
||||
pub fn motion_count(&self) -> usize {
|
||||
self.motion.as_ref().map(|m| m.0).unwrap_or(1)
|
||||
}
|
||||
pub fn is_repeatable(&self) -> bool {
|
||||
self.verb.as_ref().is_some_and(|v| v.1.is_repeatable())
|
||||
}
|
||||
pub fn is_cmd_repeat(&self) -> bool {
|
||||
self.verb.as_ref().is_some_and(|v| matches!(v.1,Verb::RepeatLast))
|
||||
}
|
||||
pub fn is_motion_repeat(&self) -> bool {
|
||||
self.motion.as_ref().is_some_and(|m| matches!(m.1,Motion::RepeatMotion | Motion::RepeatMotionRev))
|
||||
}
|
||||
pub fn is_char_search(&self) -> bool {
|
||||
self.motion.as_ref().is_some_and(|m| matches!(m.1, Motion::CharSearch(..)))
|
||||
}
|
||||
pub fn should_submit(&self) -> bool {
|
||||
self.verb.as_ref().is_some_and(|v| matches!(v.1, Verb::AcceptLine))
|
||||
}
|
||||
pub fn is_undo_op(&self) -> bool {
|
||||
self.verb.as_ref().is_some_and(|v| matches!(v.1, Verb::Undo | Verb::Redo))
|
||||
}
|
||||
pub fn is_line_motion(&self) -> bool {
|
||||
self.motion.as_ref().is_some_and(|m| matches!(m.1, Motion::LineUp | Motion::LineDown))
|
||||
}
|
||||
pub fn is_mode_transition(&self) -> bool {
|
||||
self.verb.as_ref().is_some_and(|v| {
|
||||
matches!(v.1,
|
||||
Verb::Change |
|
||||
Verb::InsertMode |
|
||||
Verb::InsertModeLineBreak(_) |
|
||||
Verb::NormalMode |
|
||||
Verb::VisualMode |
|
||||
Verb::ReplaceMode
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct VerbCmd(pub usize,pub Verb);
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct MotionCmd(pub usize,pub Motion);
|
||||
|
||||
impl MotionCmd {
|
||||
pub fn invert_char_motion(self) -> Self {
|
||||
let MotionCmd(count,Motion::CharSearch(dir, dest, ch)) = self else {
|
||||
unreachable!()
|
||||
};
|
||||
let new_dir = match dir {
|
||||
Direction::Forward => Direction::Backward,
|
||||
Direction::Backward => Direction::Forward,
|
||||
};
|
||||
MotionCmd(count,Motion::CharSearch(new_dir, dest, ch))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum Verb {
|
||||
Delete,
|
||||
DeleteChar(Anchor),
|
||||
Change,
|
||||
Yank,
|
||||
ReplaceChar(char),
|
||||
Substitute,
|
||||
ToggleCase,
|
||||
Complete,
|
||||
CompleteBackward,
|
||||
Undo,
|
||||
Redo,
|
||||
RepeatLast,
|
||||
Put(Anchor),
|
||||
ReplaceMode,
|
||||
InsertMode,
|
||||
InsertModeLineBreak(Anchor),
|
||||
NormalMode,
|
||||
VisualMode,
|
||||
JoinLines,
|
||||
InsertChar(char),
|
||||
Insert(String),
|
||||
Breakline(Anchor),
|
||||
Indent,
|
||||
Dedent,
|
||||
Equalize,
|
||||
AcceptLine,
|
||||
Builder(VerbBuilder),
|
||||
EndOfFile
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum VerbBuilder {
|
||||
}
|
||||
|
||||
impl Verb {
|
||||
pub fn needs_motion(&self) -> bool {
|
||||
matches!(self,
|
||||
Self::Indent |
|
||||
Self::Dedent |
|
||||
Self::Delete |
|
||||
Self::Change |
|
||||
Self::Yank
|
||||
)
|
||||
}
|
||||
pub fn is_repeatable(&self) -> bool {
|
||||
matches!(self,
|
||||
Self::Delete |
|
||||
Self::DeleteChar(_) |
|
||||
Self::Change |
|
||||
Self::ReplaceChar(_) |
|
||||
Self::Substitute |
|
||||
Self::ToggleCase |
|
||||
Self::Put(_) |
|
||||
Self::ReplaceMode |
|
||||
Self::InsertModeLineBreak(_) |
|
||||
Self::JoinLines |
|
||||
Self::InsertChar(_) |
|
||||
Self::Insert(_) |
|
||||
Self::Breakline(_) |
|
||||
Self::Indent |
|
||||
Self::Dedent |
|
||||
Self::Equalize
|
||||
)
|
||||
}
|
||||
pub fn is_edit(&self) -> bool {
|
||||
matches!(self,
|
||||
Self::Delete |
|
||||
Self::DeleteChar(_) |
|
||||
Self::Change |
|
||||
Self::ReplaceChar(_) |
|
||||
Self::Substitute |
|
||||
Self::ToggleCase |
|
||||
Self::RepeatLast |
|
||||
Self::Put(_) |
|
||||
Self::ReplaceMode |
|
||||
Self::InsertModeLineBreak(_) |
|
||||
Self::JoinLines |
|
||||
Self::InsertChar(_) |
|
||||
Self::Insert(_) |
|
||||
Self::Breakline(_) |
|
||||
Self::EndOfFile
|
||||
)
|
||||
}
|
||||
pub fn is_char_insert(&self) -> bool {
|
||||
matches!(self,
|
||||
Self::Change |
|
||||
Self::InsertChar(_) |
|
||||
Self::ReplaceChar(_)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum Motion {
|
||||
/// Whole current line (not really a movement but a range)
|
||||
WholeLine,
|
||||
TextObj(TextObj, Bound),
|
||||
BeginningOfFirstWord,
|
||||
/// beginning-of-line
|
||||
BeginningOfLine,
|
||||
/// end-of-line
|
||||
EndOfLine,
|
||||
/// backward-word, vi-prev-word
|
||||
BackwardWord(To, Word), // Backward until start of word
|
||||
/// forward-word, vi-end-word, vi-next-word
|
||||
ForwardWord(To, Word), // Forward until start/end of word
|
||||
/// character-search, character-search-backward, vi-char-search
|
||||
CharSearch(Direction,Dest,char),
|
||||
/// backward-char
|
||||
BackwardChar,
|
||||
/// forward-char
|
||||
ForwardChar,
|
||||
/// move to the same column on the previous line
|
||||
LineUp,
|
||||
/// move to the same column on the previous visual line
|
||||
ScreenLineUp,
|
||||
/// move to the same column on the next line
|
||||
LineDown,
|
||||
/// move to the same column on the next visual line
|
||||
ScreenLineDown,
|
||||
/// Whole user input (not really a movement but a range)
|
||||
WholeBuffer,
|
||||
/// beginning-of-register
|
||||
BeginningOfBuffer,
|
||||
/// end-of-register
|
||||
EndOfBuffer,
|
||||
ToColumn(usize),
|
||||
Range(usize,usize),
|
||||
Builder(MotionBuilder),
|
||||
RepeatMotion,
|
||||
RepeatMotionRev,
|
||||
Null
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum MotionBuilder {
|
||||
CharSearch(Option<Direction>,Option<Dest>,Option<char>),
|
||||
TextObj(Option<TextObj>,Option<Bound>)
|
||||
}
|
||||
|
||||
impl Motion {
|
||||
pub fn needs_verb(&self) -> bool {
|
||||
matches!(self, Self::TextObj(_, _))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum Anchor {
|
||||
After,
|
||||
Before
|
||||
}
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum TextObj {
|
||||
/// `iw`, `aw` — inner word, around word
|
||||
Word(Word),
|
||||
|
||||
/// for stuff like 'dd'
|
||||
Line,
|
||||
|
||||
/// `is`, `as` — inner sentence, around sentence
|
||||
Sentence,
|
||||
|
||||
/// `ip`, `ap` — inner paragraph, around paragraph
|
||||
Paragraph,
|
||||
|
||||
/// `i"`, `a"` — inner/around double quotes
|
||||
DoubleQuote,
|
||||
/// `i'`, `a'`
|
||||
SingleQuote,
|
||||
/// `i\``, `a\``
|
||||
BacktickQuote,
|
||||
|
||||
/// `i)`, `a)` — round parens
|
||||
Paren,
|
||||
/// `i]`, `a]`
|
||||
Bracket,
|
||||
/// `i}`, `a}`
|
||||
Brace,
|
||||
/// `i<`, `a<`
|
||||
Angle,
|
||||
|
||||
/// `it`, `at` — HTML/XML tags (if you support it)
|
||||
Tag,
|
||||
|
||||
/// Custom user-defined objects maybe?
|
||||
Custom(char),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum Word {
|
||||
Big,
|
||||
Normal
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum Bound {
|
||||
Inside,
|
||||
Around
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum Direction {
|
||||
#[default]
|
||||
Forward,
|
||||
Backward
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum Dest {
|
||||
On,
|
||||
Before,
|
||||
After
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum To {
|
||||
Start,
|
||||
End
|
||||
}
|
||||
24
src/shopt.rs
24
src/shopt.rs
@@ -1,6 +1,5 @@
|
||||
use std::{collections::HashMap, fmt::Display, str::FromStr};
|
||||
|
||||
use rustyline::{config::BellStyle, EditMode};
|
||||
|
||||
use crate::{libsh::error::{Note, ShErr, ShErrKind, ShResult}, state::ShFunc};
|
||||
|
||||
@@ -29,17 +28,6 @@ impl FromStr for FernBellStyle {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FernBellStyle> for BellStyle {
|
||||
fn from(val: FernBellStyle) -> Self {
|
||||
match val {
|
||||
FernBellStyle::Audible => BellStyle::Audible,
|
||||
FernBellStyle::Visible => BellStyle::Visible,
|
||||
FernBellStyle::Disable => BellStyle::None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Display for FernBellStyle {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
@@ -50,21 +38,13 @@ impl Display for FernBellStyle {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
pub enum FernEditMode {
|
||||
#[default]
|
||||
Vi,
|
||||
Emacs
|
||||
}
|
||||
|
||||
impl From<FernEditMode> for EditMode {
|
||||
fn from(val: FernEditMode) -> Self {
|
||||
match val {
|
||||
FernEditMode::Vi => EditMode::Vi,
|
||||
FernEditMode::Emacs => EditMode::Emacs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for FernEditMode {
|
||||
type Err = ShErr;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
|
||||
@@ -1,27 +1 @@
|
||||
|
||||
use insta::assert_snapshot;
|
||||
|
||||
use crate::prompt::highlight::FernHighlighter;
|
||||
|
||||
use super::super::*;
|
||||
|
||||
#[test]
|
||||
fn highlight_simple() {
|
||||
let line = "echo foo bar";
|
||||
let styled = FernHighlighter::new(line.to_string()).hl_input();
|
||||
assert_snapshot!(styled)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn highlight_cmd_sub() {
|
||||
let line = "echo foo $(echo bar)";
|
||||
let styled = FernHighlighter::new(line.to_string()).hl_input();
|
||||
assert_snapshot!(styled)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn highlight_cmd_sub_in_dquotes() {
|
||||
let line = "echo \"foo $(echo bar) biz\"";
|
||||
let styled = FernHighlighter::new(line.to_string()).hl_input();
|
||||
assert_snapshot!(styled)
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
source: src/tests/error.rs
|
||||
expression: err_fmt
|
||||
---
|
||||
[31m[1mParse Error[0m - Expected 'esac' after case block
|
||||
[36m[1m->[0m [[36m[1m1[0m;[36m[1m1[0m]
|
||||
[36m[1m |[0m
|
||||
[36m[1m1 |[0m case foo in foo) bar;; bar) foo;;
|
||||
[36m[1m |[0m [31m[1m^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^[0m
|
||||
[36m[1m |[0m
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
source: src/tests/error.rs
|
||||
expression: err_fmt
|
||||
---
|
||||
[31m[1mParse Error[0m - Expected 'in' after case variable name
|
||||
[36m[1m->[0m [[36m[1m1[0m;[36m[1m1[0m]
|
||||
[36m[1m |[0m
|
||||
[36m[1m1 |[0m case foo foo) bar;; bar) foo;; esac
|
||||
[36m[1m |[0m [31m[1m^^^^^^^^^^^^^^^^^^^[0m
|
||||
[36m[1m |[0m
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
source: src/tests/error.rs
|
||||
expression: err_fmt
|
||||
---
|
||||
[31m[1mCommand not found: foo[0m -
|
||||
[36m[1m->[0m [[36m[1m1[0m;[36m[1m1[0m]
|
||||
[36m[1m |[0m
|
||||
[36m[1m1 |[0m foo
|
||||
[36m[1m |[0m
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
source: src/tests/error.rs
|
||||
expression: err_fmt
|
||||
---
|
||||
Execution failed
|
||||
[32mnote[0m: Execution failed for this reason
|
||||
|
||||
[32mnote[0m: Here is how to fix it: blah blah blah
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
source: src/tests/error.rs
|
||||
expression: err_fmt
|
||||
---
|
||||
Execution failed
|
||||
[32mnote[0m: Execution failed for this reason
|
||||
|
||||
[32mnote[0m: Here is how to fix it:
|
||||
[36m[1m-[0m blah
|
||||
[36m[1m-[0m blah
|
||||
[36m[1m-[0m blah
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
source: src/tests/error.rs
|
||||
expression: err_fmt
|
||||
---
|
||||
[31m[1mParse Error[0m - Expected 'fi' after if statement
|
||||
[36m[1m->[0m [[36m[1m1[0m;[36m[1m1[0m]
|
||||
[36m[1m |[0m
|
||||
[36m[1m1 |[0m if foo; then bar;
|
||||
[36m[1m |[0m [31m[1m^^^^^^^^^^^^^^^^^[0m
|
||||
[36m[1m |[0m
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
source: src/tests/error.rs
|
||||
expression: err_fmt
|
||||
---
|
||||
[31m[1mParse Error[0m - Expected 'then' after 'if' condition
|
||||
[36m[1m->[0m [[36m[1m1[0m;[36m[1m1[0m]
|
||||
[36m[1m |[0m
|
||||
[36m[1m1 |[0m if foo; bar; fi
|
||||
[36m[1m |[0m [31m[1m^^^^^^^^^^^^[0m
|
||||
[36m[1m |[0m
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
source: src/tests/error.rs
|
||||
expression: err_fmt
|
||||
---
|
||||
[31m[1mParse Error[0m - Expected 'do' after loop condition
|
||||
[36m[1m->[0m [[36m[1m1[0m;[36m[1m1[0m]
|
||||
[36m[1m |[0m
|
||||
[36m[1m1 |[0m while true; echo foo; done
|
||||
[36m[1m |[0m [31m[1m^^^^^^^^^^^^^^^^^^^^^[0m
|
||||
[36m[1m |[0m
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
source: src/tests/error.rs
|
||||
expression: err_fmt
|
||||
---
|
||||
[31m[1mParse Error[0m - Expected 'done' after loop body
|
||||
[36m[1m->[0m [[36m[1m1[0m;[36m[1m1[0m]
|
||||
[36m[1m |[0m
|
||||
[36m[1m1 |[0m while true; do echo foo;
|
||||
[36m[1m |[0m [31m[1m^^^^^^^^^^^^^^^^^^^^^^^^[0m
|
||||
[36m[1m |[0m
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
source: src/tests/error.rs
|
||||
expression: err_fmt
|
||||
---
|
||||
[31m[1mParse Error[0m - Expected a closing brace for this brace group
|
||||
[36m[1m->[0m [[36m[1m1[0m;[36m[1m1[0m]
|
||||
[36m[1m |[0m
|
||||
[36m[1m1 |[0m { foo bar
|
||||
[36m[1m |[0m [31m[1m^^^^^^^^^[0m
|
||||
[36m[1m |[0m
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
source: src/tests/error.rs
|
||||
expression: err_fmt
|
||||
---
|
||||
[31m[1mParse Error[0m - Unterminated quote
|
||||
[36m[1m->[0m [[36m[1m1[0m;[36m[1m1[0m]
|
||||
[36m[1m |[0m
|
||||
[36m[1m1 |[0m "foo bar
|
||||
[36m[1m |[0m [31m[1m^^^^^^^^[0m
|
||||
[36m[1m |[0m
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
source: src/tests/error.rs
|
||||
expression: err_fmt
|
||||
---
|
||||
[31m[1mParse Error[0m - Unterminated quote
|
||||
[36m[1m->[0m [[36m[1m1[0m;[36m[1m1[0m]
|
||||
[36m[1m |[0m
|
||||
[36m[1m1 |[0m 'foo bar
|
||||
[36m[1m |[0m [31m[1m^^^^^^^^[0m
|
||||
[36m[1m |[0m
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
source: src/tests/error.rs
|
||||
expression: err_fmt
|
||||
---
|
||||
[31m[1mParse Error[0m - Unclosed subshell
|
||||
[36m[1m->[0m [[36m[1m1[0m;[36m[1m2[0m]
|
||||
[36m[1m |[0m
|
||||
[36m[1m1 |[0m (foo
|
||||
[36m[1m |[0m [31m[1m^[0m
|
||||
[36m[1m |[0m
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
source: src/tests/expand.rs
|
||||
expression: exp_tk.get_words()
|
||||
---
|
||||
[
|
||||
"this",
|
||||
"is",
|
||||
"the",
|
||||
"value",
|
||||
"of",
|
||||
"the",
|
||||
"variable",
|
||||
]
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
source: src/tests/expand.rs
|
||||
expression: unescaped
|
||||
---
|
||||
echo foo $bar
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
source: src/tests/getopt.rs
|
||||
expression: opts
|
||||
---
|
||||
[
|
||||
Short(
|
||||
'n',
|
||||
),
|
||||
Short(
|
||||
'e',
|
||||
),
|
||||
]
|
||||
@@ -1,26 +0,0 @@
|
||||
---
|
||||
source: src/tests/getopt.rs
|
||||
expression: words
|
||||
---
|
||||
[
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "echo -n -e foo",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 11..14,
|
||||
source: "echo -n -e foo",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
]
|
||||
@@ -1,15 +0,0 @@
|
||||
---
|
||||
source: src/tests/getopt.rs
|
||||
expression: opts
|
||||
---
|
||||
[
|
||||
Short(
|
||||
'n',
|
||||
),
|
||||
Short(
|
||||
'r',
|
||||
),
|
||||
Short(
|
||||
'e',
|
||||
),
|
||||
]
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
source: src/tests/getopt.rs
|
||||
expression: words
|
||||
---
|
||||
[
|
||||
"echo",
|
||||
"foo",
|
||||
]
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
source: src/tests/getopt.rs
|
||||
expression: opts
|
||||
---
|
||||
[
|
||||
Short(
|
||||
'n',
|
||||
),
|
||||
]
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
source: src/tests/getopt.rs
|
||||
expression: words
|
||||
---
|
||||
[
|
||||
"echo",
|
||||
"foo",
|
||||
]
|
||||
@@ -1,186 +0,0 @@
|
||||
---
|
||||
source: src/tests/lexer.rs
|
||||
expression: tokens
|
||||
---
|
||||
[
|
||||
Ok(
|
||||
Tk {
|
||||
class: SOI,
|
||||
span: Span {
|
||||
range: 0..0,
|
||||
source: "case $foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "case $foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..9,
|
||||
source: "case $foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 10..12,
|
||||
source: "case $foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: CasePattern,
|
||||
span: Span {
|
||||
range: 13..17,
|
||||
source: "case $foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..21,
|
||||
source: "case $foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 21..24,
|
||||
source: "case $foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: CasePattern,
|
||||
span: Span {
|
||||
range: 24..28,
|
||||
source: "case $foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 29..32,
|
||||
source: "case $foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 32..35,
|
||||
source: "case $foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: CasePattern,
|
||||
span: Span {
|
||||
range: 35..39,
|
||||
source: "case $foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 40..43,
|
||||
source: "case $foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 43..46,
|
||||
source: "case $foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 46..50,
|
||||
source: "case $foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: EOI,
|
||||
span: Span {
|
||||
range: 50..50,
|
||||
source: "case $foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,162 +0,0 @@
|
||||
---
|
||||
source: src/tests/lexer.rs
|
||||
expression: tokens
|
||||
---
|
||||
[
|
||||
Ok(
|
||||
Tk {
|
||||
class: SOI,
|
||||
span: Span {
|
||||
range: 0..0,
|
||||
source: "echo hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "echo hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..10,
|
||||
source: "echo hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 11..16,
|
||||
source: "echo hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 16..17,
|
||||
source: "echo hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 17..21,
|
||||
source: "echo hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 22..25,
|
||||
source: "echo hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 26..29,
|
||||
source: "echo hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 29..30,
|
||||
source: "echo hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 30..34,
|
||||
source: "echo hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 35..38,
|
||||
source: "echo hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 39..42,
|
||||
source: "echo hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: EOI,
|
||||
span: Span {
|
||||
range: 42..42,
|
||||
source: "echo hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,78 +0,0 @@
|
||||
---
|
||||
source: src/tests/lexer.rs
|
||||
expression: tokens
|
||||
---
|
||||
[
|
||||
Ok(
|
||||
Tk {
|
||||
class: SOI,
|
||||
span: Span {
|
||||
range: 0..0,
|
||||
source: "echo \"foo bar\" biz baz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "echo \"foo bar\" biz baz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..14,
|
||||
source: "echo \"foo bar\" biz baz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 15..18,
|
||||
source: "echo \"foo bar\" biz baz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 19..22,
|
||||
source: "echo \"foo bar\" biz baz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: EOI,
|
||||
span: Span {
|
||||
range: 22..22,
|
||||
source: "echo \"foo bar\" biz baz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,78 +0,0 @@
|
||||
---
|
||||
source: src/tests/lexer.rs
|
||||
expression: tokens
|
||||
---
|
||||
[
|
||||
Ok(
|
||||
Tk {
|
||||
class: SOI,
|
||||
span: Span {
|
||||
range: 0..0,
|
||||
source: "echo foo > bar.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "echo foo > bar.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..8,
|
||||
source: "echo foo > bar.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Redir,
|
||||
span: Span {
|
||||
range: 9..10,
|
||||
source: "echo foo > bar.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 11..18,
|
||||
source: "echo foo > bar.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: EOI,
|
||||
span: Span {
|
||||
range: 18..18,
|
||||
source: "echo foo > bar.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,66 +0,0 @@
|
||||
---
|
||||
source: src/tests/lexer.rs
|
||||
expression: tokens
|
||||
---
|
||||
[
|
||||
Ok(
|
||||
Tk {
|
||||
class: SOI,
|
||||
span: Span {
|
||||
range: 0..0,
|
||||
source: "echo foo 1>&2",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "echo foo 1>&2",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..8,
|
||||
source: "echo foo 1>&2",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Redir,
|
||||
span: Span {
|
||||
range: 9..13,
|
||||
source: "echo foo 1>&2",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: EOI,
|
||||
span: Span {
|
||||
range: 13..13,
|
||||
source: "echo foo 1>&2",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,66 +0,0 @@
|
||||
---
|
||||
source: src/tests/lexer.rs
|
||||
expression: tokens
|
||||
---
|
||||
[
|
||||
Ok(
|
||||
Tk {
|
||||
class: SOI,
|
||||
span: Span {
|
||||
range: 0..0,
|
||||
source: "echo hello world",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "echo hello world",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..10,
|
||||
source: "echo hello world",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 11..16,
|
||||
source: "echo hello world",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: EOI,
|
||||
span: Span {
|
||||
range: 16..16,
|
||||
source: "echo hello world",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,126 +0,0 @@
|
||||
---
|
||||
source: src/tests/lexer.rs
|
||||
expression: tokens
|
||||
---
|
||||
[
|
||||
Ok(
|
||||
Tk {
|
||||
class: SOI,
|
||||
span: Span {
|
||||
range: 0..0,
|
||||
source: "if true; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..2,
|
||||
source: "if true; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 3..7,
|
||||
source: "if true; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 7..9,
|
||||
source: "if true; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 9..13,
|
||||
source: "if true; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 14..18,
|
||||
source: "if true; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 19..22,
|
||||
source: "if true; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 22..24,
|
||||
source: "if true; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 24..26,
|
||||
source: "if true; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Tk {
|
||||
class: EOI,
|
||||
span: Span {
|
||||
range: 26..26,
|
||||
source: "if true; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,162 +0,0 @@
|
||||
---
|
||||
source: src/tests/parser.rs
|
||||
expression: check_nodes
|
||||
---
|
||||
[
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "echo hello world; echo foo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..10,
|
||||
source: "echo hello world; echo foo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 11..16,
|
||||
source: "echo hello world; echo foo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "echo hello world; echo foo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..10,
|
||||
source: "echo hello world; echo foo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 11..16,
|
||||
source: "echo hello world; echo foo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 16..18,
|
||||
source: "echo hello world; echo foo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..22,
|
||||
source: "echo hello world; echo foo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 23..26,
|
||||
source: "echo hello world; echo foo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 27..30,
|
||||
source: "echo hello world; echo foo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..22,
|
||||
source: "echo hello world; echo foo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 23..26,
|
||||
source: "echo hello world; echo foo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 27..30,
|
||||
source: "echo hello world; echo foo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
@@ -1,595 +0,0 @@
|
||||
---
|
||||
source: src/tests/parser.rs
|
||||
expression: nodes
|
||||
---
|
||||
[
|
||||
Ok(
|
||||
Node {
|
||||
class: Conjunction {
|
||||
elements: [
|
||||
ConjunctNode {
|
||||
cmd: Node {
|
||||
class: CaseNode {
|
||||
pattern: Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..8,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
case_blocks: [
|
||||
CaseNode {
|
||||
pattern: Tk {
|
||||
class: CasePattern,
|
||||
span: Span {
|
||||
range: 13..17,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
body: [
|
||||
Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..21,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..21,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 21..27,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..21,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 21..27,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
CaseNode {
|
||||
pattern: Tk {
|
||||
class: CasePattern,
|
||||
span: Span {
|
||||
range: 27..31,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
body: [
|
||||
Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 32..35,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 32..35,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 35..41,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 32..35,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 35..41,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
CaseNode {
|
||||
pattern: Tk {
|
||||
class: CasePattern,
|
||||
span: Span {
|
||||
range: 41..45,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
body: [
|
||||
Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 46..49,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 46..49,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 49..54,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 46..49,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 49..54,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..8,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 9..11,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 11..13,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: CasePattern,
|
||||
span: Span {
|
||||
range: 13..17,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..21,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 21..27,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: CasePattern,
|
||||
span: Span {
|
||||
range: 27..31,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 32..35,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 35..41,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: CasePattern,
|
||||
span: Span {
|
||||
range: 41..45,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 46..49,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 49..54,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 54..58,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
operator: Null,
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..8,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 9..11,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 11..13,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: CasePattern,
|
||||
span: Span {
|
||||
range: 13..17,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..21,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 21..27,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: CasePattern,
|
||||
span: Span {
|
||||
range: 27..31,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 32..35,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 35..41,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: CasePattern,
|
||||
span: Span {
|
||||
range: 41..45,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 46..49,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 49..54,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 54..58,
|
||||
source: "case foo in\n\tfoo) bar\n\t;;\n\tbar) foo\n\t;;\n\tbiz) baz\n\t;;\nesac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,575 +0,0 @@
|
||||
---
|
||||
source: src/tests/parser.rs
|
||||
expression: nodes
|
||||
---
|
||||
[
|
||||
Ok(
|
||||
Node {
|
||||
class: Conjunction {
|
||||
elements: [
|
||||
ConjunctNode {
|
||||
cmd: Node {
|
||||
class: CaseNode {
|
||||
pattern: Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..8,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
case_blocks: [
|
||||
CaseNode {
|
||||
pattern: Tk {
|
||||
class: CasePattern,
|
||||
span: Span {
|
||||
range: 12..16,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
body: [
|
||||
Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 17..20,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 17..20,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 20..23,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 17..20,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 20..23,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
CaseNode {
|
||||
pattern: Tk {
|
||||
class: CasePattern,
|
||||
span: Span {
|
||||
range: 23..27,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
body: [
|
||||
Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 28..31,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 28..31,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 31..34,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 28..31,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 31..34,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
CaseNode {
|
||||
pattern: Tk {
|
||||
class: CasePattern,
|
||||
span: Span {
|
||||
range: 34..38,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
body: [
|
||||
Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 39..42,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 39..42,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 42..45,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 39..42,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 42..45,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..8,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 9..11,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: CasePattern,
|
||||
span: Span {
|
||||
range: 12..16,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 17..20,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 20..23,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: CasePattern,
|
||||
span: Span {
|
||||
range: 23..27,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 28..31,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 31..34,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: CasePattern,
|
||||
span: Span {
|
||||
range: 34..38,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 39..42,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 42..45,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 45..49,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
operator: Null,
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..8,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 9..11,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: CasePattern,
|
||||
span: Span {
|
||||
range: 12..16,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 17..20,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 20..23,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: CasePattern,
|
||||
span: Span {
|
||||
range: 23..27,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 28..31,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 31..34,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: CasePattern,
|
||||
span: Span {
|
||||
range: 34..38,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 39..42,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 42..45,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 45..49,
|
||||
source: "case foo in foo) bar;; bar) foo;; biz) baz;; esac",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,248 +0,0 @@
|
||||
---
|
||||
source: src/tests/parser.rs
|
||||
expression: nodes
|
||||
---
|
||||
[
|
||||
Ok(
|
||||
Node {
|
||||
class: Conjunction {
|
||||
elements: [
|
||||
ConjunctNode {
|
||||
cmd: Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "echo foo && echo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..8,
|
||||
source: "echo foo && echo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "echo foo && echo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..8,
|
||||
source: "echo foo && echo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "echo foo && echo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..8,
|
||||
source: "echo foo && echo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
operator: And,
|
||||
},
|
||||
ConjunctNode {
|
||||
cmd: Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 12..16,
|
||||
source: "echo foo && echo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 17..20,
|
||||
source: "echo foo && echo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 12..16,
|
||||
source: "echo foo && echo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 17..20,
|
||||
source: "echo foo && echo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 12..16,
|
||||
source: "echo foo && echo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 17..20,
|
||||
source: "echo foo && echo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
operator: Null,
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "echo foo && echo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..8,
|
||||
source: "echo foo && echo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: And,
|
||||
span: Span {
|
||||
range: 9..11,
|
||||
source: "echo foo && echo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 12..16,
|
||||
source: "echo foo && echo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 17..20,
|
||||
source: "echo foo && echo bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,826 +0,0 @@
|
||||
---
|
||||
source: src/tests/parser.rs
|
||||
expression: nodes
|
||||
---
|
||||
[
|
||||
Ok(
|
||||
Node {
|
||||
class: Conjunction {
|
||||
elements: [
|
||||
ConjunctNode {
|
||||
cmd: Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..8,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..8,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 11..14,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 15..25,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 11..14,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 15..25,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..8,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Pipe,
|
||||
span: Span {
|
||||
range: 9..10,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 11..14,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 15..25,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
operator: And,
|
||||
},
|
||||
ConjunctNode {
|
||||
cmd: Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 29..33,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 34..37,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 29..33,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 34..37,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 40..43,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 44..54,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 40..43,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 44..54,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 29..33,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 34..37,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Pipe,
|
||||
span: Span {
|
||||
range: 38..39,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 40..43,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 44..54,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
operator: Or,
|
||||
},
|
||||
ConjunctNode {
|
||||
cmd: Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 58..62,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 63..66,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 67..70,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 58..62,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 63..66,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 67..70,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 73..76,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 77..82,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 83..90,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 91..95,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 73..76,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 77..82,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 83..90,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 91..95,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 58..62,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 63..66,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 67..70,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Pipe,
|
||||
span: Span {
|
||||
range: 71..72,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 73..76,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 77..82,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 83..90,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 91..95,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
operator: Null,
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..8,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Pipe,
|
||||
span: Span {
|
||||
range: 9..10,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 11..14,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 15..25,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: And,
|
||||
span: Span {
|
||||
range: 26..28,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 29..33,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 34..37,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Pipe,
|
||||
span: Span {
|
||||
range: 38..39,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 40..43,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 44..54,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Or,
|
||||
span: Span {
|
||||
range: 55..57,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 58..62,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 63..66,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 67..70,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Pipe,
|
||||
span: Span {
|
||||
range: 71..72,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 73..76,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 77..82,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 83..90,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 91..95,
|
||||
source: "echo foo | sed s/foo/bar/ && echo bar | sed s/bar/foo/ || echo foo bar | sed s/foo bar/bar foo/",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,435 +0,0 @@
|
||||
---
|
||||
source: src/tests/parser.rs
|
||||
expression: nodes
|
||||
---
|
||||
[
|
||||
Ok(
|
||||
Node {
|
||||
class: Conjunction {
|
||||
elements: [
|
||||
ConjunctNode {
|
||||
cmd: Node {
|
||||
class: IfNode {
|
||||
cond_nodes: [
|
||||
CondNode {
|
||||
cond: Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 3..6,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 3..6,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 6..8,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 3..6,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 6..8,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
body: [
|
||||
Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 13..17,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..21,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 13..17,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..21,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 21..23,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 13..17,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..21,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 21..23,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
else_block: [],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [
|
||||
Redir {
|
||||
io_mode: File {
|
||||
tgt_fd: 1,
|
||||
file: File {
|
||||
fd: 3,
|
||||
path: "/home/pagedmov/Coding/projects/rust/fern/file.txt",
|
||||
read: false,
|
||||
write: true,
|
||||
},
|
||||
},
|
||||
class: Output,
|
||||
},
|
||||
],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..2,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 3..6,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 6..8,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 8..12,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 13..17,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..21,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 21..23,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 23..25,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Redir,
|
||||
span: Span {
|
||||
range: 26..27,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 28..36,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
operator: Null,
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..2,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 3..6,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 6..8,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 8..12,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 13..17,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..21,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 21..23,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 23..25,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Redir,
|
||||
span: Span {
|
||||
range: 26..27,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 28..36,
|
||||
source: "if foo; then echo bar; fi > file.txt",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,382 +0,0 @@
|
||||
---
|
||||
source: src/tests/parser.rs
|
||||
expression: nodes
|
||||
---
|
||||
[
|
||||
Ok(
|
||||
Node {
|
||||
class: Conjunction {
|
||||
elements: [
|
||||
ConjunctNode {
|
||||
cmd: Node {
|
||||
class: IfNode {
|
||||
cond_nodes: [
|
||||
CondNode {
|
||||
cond: Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 3..6,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 3..6,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 6..8,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 3..6,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 6..8,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
body: [
|
||||
Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 13..17,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..21,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 13..17,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..21,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 21..23,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 13..17,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..21,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 21..23,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
else_block: [],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..2,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 3..6,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 6..8,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 8..12,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 13..17,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..21,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 21..23,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 23..25,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
operator: Null,
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..2,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 3..6,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 6..8,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 8..12,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 13..17,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..21,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 21..23,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 23..25,
|
||||
source: "if foo; then echo bar; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,708 +0,0 @@
|
||||
---
|
||||
source: src/tests/parser.rs
|
||||
expression: nodes
|
||||
---
|
||||
[
|
||||
Ok(
|
||||
Node {
|
||||
class: Conjunction {
|
||||
elements: [
|
||||
ConjunctNode {
|
||||
cmd: Node {
|
||||
class: IfNode {
|
||||
cond_nodes: [
|
||||
CondNode {
|
||||
cond: Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 3..6,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 3..6,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 6..8,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 3..6,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 6..8,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
body: [
|
||||
Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 13..17,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..21,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 13..17,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..21,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 21..23,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 13..17,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..21,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 21..23,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
CondNode {
|
||||
cond: Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 28..31,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 28..31,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 31..33,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 28..31,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 31..33,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
body: [
|
||||
Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 38..42,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 43..46,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 38..42,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 43..46,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 46..48,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 38..42,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 43..46,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 46..48,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
else_block: [],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..2,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 3..6,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 6..8,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 8..12,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 13..17,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..21,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 21..23,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 23..27,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 28..31,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 31..33,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 33..37,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 38..42,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 43..46,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 46..48,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 48..50,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
operator: Null,
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..2,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 3..6,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 6..8,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 8..12,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 13..17,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..21,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 21..23,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 23..27,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 28..31,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 31..33,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 33..37,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 38..42,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 43..46,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 46..48,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 48..50,
|
||||
source: "if foo; then echo bar; elif bar; then echo foo; fi",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,350 +0,0 @@
|
||||
---
|
||||
source: src/tests/parser.rs
|
||||
expression: nodes
|
||||
---
|
||||
[
|
||||
Ok(
|
||||
Node {
|
||||
class: Conjunction {
|
||||
elements: [
|
||||
ConjunctNode {
|
||||
cmd: Node {
|
||||
class: LoopNode {
|
||||
kind: Until,
|
||||
cond_node: CondNode {
|
||||
cond: Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 7..10,
|
||||
source: "\nuntil foo; do\n\tbar\ndone",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 7..10,
|
||||
source: "\nuntil foo; do\n\tbar\ndone",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 10..12,
|
||||
source: "\nuntil foo; do\n\tbar\ndone",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 7..10,
|
||||
source: "\nuntil foo; do\n\tbar\ndone",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 10..12,
|
||||
source: "\nuntil foo; do\n\tbar\ndone",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
body: [
|
||||
Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 16..19,
|
||||
source: "\nuntil foo; do\n\tbar\ndone",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 16..19,
|
||||
source: "\nuntil foo; do\n\tbar\ndone",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 19..20,
|
||||
source: "\nuntil foo; do\n\tbar\ndone",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 16..19,
|
||||
source: "\nuntil foo; do\n\tbar\ndone",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 19..20,
|
||||
source: "\nuntil foo; do\n\tbar\ndone",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 1..6,
|
||||
source: "\nuntil foo; do\n\tbar\ndone",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 7..10,
|
||||
source: "\nuntil foo; do\n\tbar\ndone",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 10..12,
|
||||
source: "\nuntil foo; do\n\tbar\ndone",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 12..14,
|
||||
source: "\nuntil foo; do\n\tbar\ndone",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 14..16,
|
||||
source: "\nuntil foo; do\n\tbar\ndone",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 16..19,
|
||||
source: "\nuntil foo; do\n\tbar\ndone",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 19..20,
|
||||
source: "\nuntil foo; do\n\tbar\ndone",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 20..24,
|
||||
source: "\nuntil foo; do\n\tbar\ndone",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
operator: Null,
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 1..6,
|
||||
source: "\nuntil foo; do\n\tbar\ndone",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 7..10,
|
||||
source: "\nuntil foo; do\n\tbar\ndone",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 10..12,
|
||||
source: "\nuntil foo; do\n\tbar\ndone",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 12..14,
|
||||
source: "\nuntil foo; do\n\tbar\ndone",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 14..16,
|
||||
source: "\nuntil foo; do\n\tbar\ndone",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 16..19,
|
||||
source: "\nuntil foo; do\n\tbar\ndone",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 19..20,
|
||||
source: "\nuntil foo; do\n\tbar\ndone",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 20..24,
|
||||
source: "\nuntil foo; do\n\tbar\ndone",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,330 +0,0 @@
|
||||
---
|
||||
source: src/tests/parser.rs
|
||||
expression: nodes
|
||||
---
|
||||
[
|
||||
Ok(
|
||||
Node {
|
||||
class: Conjunction {
|
||||
elements: [
|
||||
ConjunctNode {
|
||||
cmd: Node {
|
||||
class: LoopNode {
|
||||
kind: While,
|
||||
cond_node: CondNode {
|
||||
cond: Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 6..9,
|
||||
source: "while foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 6..9,
|
||||
source: "while foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 9..11,
|
||||
source: "while foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 6..9,
|
||||
source: "while foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 9..11,
|
||||
source: "while foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
body: [
|
||||
Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 14..17,
|
||||
source: "while foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 14..17,
|
||||
source: "while foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 17..19,
|
||||
source: "while foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 14..17,
|
||||
source: "while foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 17..19,
|
||||
source: "while foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..5,
|
||||
source: "while foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 6..9,
|
||||
source: "while foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 9..11,
|
||||
source: "while foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 11..13,
|
||||
source: "while foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 14..17,
|
||||
source: "while foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 17..19,
|
||||
source: "while foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 19..23,
|
||||
source: "while foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
operator: Null,
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..5,
|
||||
source: "while foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 6..9,
|
||||
source: "while foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 9..11,
|
||||
source: "while foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 11..13,
|
||||
source: "while foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 14..17,
|
||||
source: "while foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 17..19,
|
||||
source: "while foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 19..23,
|
||||
source: "while foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,330 +0,0 @@
|
||||
---
|
||||
source: src/tests/parser.rs
|
||||
expression: nodes
|
||||
---
|
||||
[
|
||||
Ok(
|
||||
Node {
|
||||
class: Conjunction {
|
||||
elements: [
|
||||
ConjunctNode {
|
||||
cmd: Node {
|
||||
class: LoopNode {
|
||||
kind: Until,
|
||||
cond_node: CondNode {
|
||||
cond: Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 6..9,
|
||||
source: "until foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 6..9,
|
||||
source: "until foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 9..11,
|
||||
source: "until foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 6..9,
|
||||
source: "until foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 9..11,
|
||||
source: "until foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
body: [
|
||||
Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 14..17,
|
||||
source: "until foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 14..17,
|
||||
source: "until foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 17..19,
|
||||
source: "until foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 14..17,
|
||||
source: "until foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 17..19,
|
||||
source: "until foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..5,
|
||||
source: "until foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 6..9,
|
||||
source: "until foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 9..11,
|
||||
source: "until foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 11..13,
|
||||
source: "until foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 14..17,
|
||||
source: "until foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 17..19,
|
||||
source: "until foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 19..23,
|
||||
source: "until foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
operator: Null,
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..5,
|
||||
source: "until foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 6..9,
|
||||
source: "until foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 9..11,
|
||||
source: "until foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 11..13,
|
||||
source: "until foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 14..17,
|
||||
source: "until foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 17..19,
|
||||
source: "until foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 19..23,
|
||||
source: "until foo; do bar; done",
|
||||
},
|
||||
flags: TkFlags(
|
||||
KEYWORD,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,555 +0,0 @@
|
||||
---
|
||||
source: src/tests/parser.rs
|
||||
expression: nodes
|
||||
---
|
||||
[
|
||||
Ok(
|
||||
Node {
|
||||
class: Conjunction {
|
||||
elements: [
|
||||
ConjunctNode {
|
||||
cmd: Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 1..5,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 6..11,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 12..17,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 1..5,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 6..11,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 12..17,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 17..18,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 1..5,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 6..11,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 12..17,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 17..18,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
operator: Null,
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 1..5,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 6..11,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 12..17,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 17..18,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Node {
|
||||
class: Conjunction {
|
||||
elements: [
|
||||
ConjunctNode {
|
||||
cmd: Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..22,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 23..26,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 27..30,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..22,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 23..26,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 27..30,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 30..31,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..22,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 23..26,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 27..30,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 30..31,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
operator: Null,
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 18..22,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 23..26,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 27..30,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Sep,
|
||||
span: Span {
|
||||
range: 30..31,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
Ok(
|
||||
Node {
|
||||
class: Conjunction {
|
||||
elements: [
|
||||
ConjunctNode {
|
||||
cmd: Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 31..35,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 36..39,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 40..43,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 31..35,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 36..39,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 40..43,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 31..35,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 36..39,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 40..43,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
operator: Null,
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 31..35,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 36..39,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 40..43,
|
||||
source: "\necho hello world\necho foo bar\necho boo biz",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,242 +0,0 @@
|
||||
---
|
||||
source: src/tests/parser.rs
|
||||
expression: nodes
|
||||
---
|
||||
[
|
||||
Ok(
|
||||
Node {
|
||||
class: Conjunction {
|
||||
elements: [
|
||||
ConjunctNode {
|
||||
cmd: Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "echo foo | sed s/foo/bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..8,
|
||||
source: "echo foo | sed s/foo/bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "echo foo | sed s/foo/bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..8,
|
||||
source: "echo foo | sed s/foo/bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 11..14,
|
||||
source: "echo foo | sed s/foo/bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 15..24,
|
||||
source: "echo foo | sed s/foo/bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 11..14,
|
||||
source: "echo foo | sed s/foo/bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 15..24,
|
||||
source: "echo foo | sed s/foo/bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "echo foo | sed s/foo/bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..8,
|
||||
source: "echo foo | sed s/foo/bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Pipe,
|
||||
span: Span {
|
||||
range: 9..10,
|
||||
source: "echo foo | sed s/foo/bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 11..14,
|
||||
source: "echo foo | sed s/foo/bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 15..24,
|
||||
source: "echo foo | sed s/foo/bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
operator: Null,
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "echo foo | sed s/foo/bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..8,
|
||||
source: "echo foo | sed s/foo/bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Pipe,
|
||||
span: Span {
|
||||
range: 9..10,
|
||||
source: "echo foo | sed s/foo/bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 11..14,
|
||||
source: "echo foo | sed s/foo/bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 15..24,
|
||||
source: "echo foo | sed s/foo/bar",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,169 +0,0 @@
|
||||
---
|
||||
source: src/tests/parser.rs
|
||||
expression: nodes
|
||||
---
|
||||
[
|
||||
Ok(
|
||||
Node {
|
||||
class: Conjunction {
|
||||
elements: [
|
||||
ConjunctNode {
|
||||
cmd: Node {
|
||||
class: Pipeline {
|
||||
cmds: [
|
||||
Node {
|
||||
class: Command {
|
||||
assignments: [],
|
||||
argv: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "echo hello world",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..10,
|
||||
source: "echo hello world",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 11..16,
|
||||
source: "echo hello world",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "echo hello world",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..10,
|
||||
source: "echo hello world",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 11..16,
|
||||
source: "echo hello world",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
pipe_err: false,
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "echo hello world",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..10,
|
||||
source: "echo hello world",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 11..16,
|
||||
source: "echo hello world",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
operator: Null,
|
||||
},
|
||||
],
|
||||
},
|
||||
flags: NdFlags(
|
||||
0x0,
|
||||
),
|
||||
redirs: [],
|
||||
tokens: [
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 0..4,
|
||||
source: "echo hello world",
|
||||
},
|
||||
flags: TkFlags(
|
||||
IS_CMD | BUILTIN,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 5..10,
|
||||
source: "echo hello world",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
Tk {
|
||||
class: Str,
|
||||
span: Span {
|
||||
range: 11..16,
|
||||
source: "echo hello world",
|
||||
},
|
||||
flags: TkFlags(
|
||||
0x0,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
source: src/tests/term.rs
|
||||
expression: styled
|
||||
---
|
||||
[44m[1mtext with background[0m
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
source: src/tests/term.rs
|
||||
expression: styled
|
||||
---
|
||||
[31m[1m[4mstyled text[0m
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
source: src/tests/term.rs
|
||||
expression: styled
|
||||
---
|
||||
[1m[0mreset test[0m
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
source: src/tests/term.rs
|
||||
expression: styled
|
||||
---
|
||||
[38;2;255;99;71mRGB styled text[0m
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
source: src/tests/term.rs
|
||||
expression: styled
|
||||
---
|
||||
[35m[3mmulti-style text[0m
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
source: src/tests/term.rs
|
||||
expression: styled
|
||||
---
|
||||
[32mhello world[0m
|
||||
Reference in New Issue
Block a user