Merge nucleic/clever-quartz-marten-uqnx into dev
This commit is contained in:
@@ -4,7 +4,7 @@ use brush_core::Shell;
|
||||
|
||||
use crate::{
|
||||
InputBackend, ShellError, completion,
|
||||
input_backend::{InteractivePrompt, ReadResult},
|
||||
input_backend::{InteractivePrompt, ReadResult, parse_result_is_complete},
|
||||
};
|
||||
|
||||
use super::{non_term_line_reader, term_line_reader};
|
||||
@@ -82,18 +82,7 @@ impl BasicInputBackend {
|
||||
}
|
||||
|
||||
fn is_valid_input(shell: &Shell<impl brush_core::ShellExtensions>, input: &str) -> bool {
|
||||
match shell.parse_string(input.to_owned()) {
|
||||
// Incomplete tokenizing (unclosed quotes, etc.) - need more input
|
||||
Err(brush_parser::ParseError::Tokenizing { inner, position: _ })
|
||||
if inner.is_incomplete() =>
|
||||
{
|
||||
false
|
||||
}
|
||||
// Parse error at end of input - could be incomplete
|
||||
Err(brush_parser::ParseError::ParsingAtEndOfInput) => false,
|
||||
// Parse error at a specific position OR successful parse - complete
|
||||
_ => true,
|
||||
}
|
||||
parse_result_is_complete(&shell.parse_string(input.to_owned()))
|
||||
}
|
||||
|
||||
fn generate_completions(
|
||||
|
||||
@@ -49,3 +49,24 @@ pub struct InteractivePrompt {
|
||||
/// Prompt to display on a continuation line of input.
|
||||
pub continuation_prompt: String,
|
||||
}
|
||||
|
||||
/// Returns whether a parser result represents a complete command that can be
|
||||
/// executed, as opposed to a command that needs more physical input lines.
|
||||
pub(crate) fn parse_result_is_complete(
|
||||
result: &Result<brush_parser::ast::Program, brush_parser::ParseError>,
|
||||
) -> bool {
|
||||
match result {
|
||||
// Unclosed quotes and similar tokenizer states need another line.
|
||||
Err(brush_parser::ParseError::Tokenizing { inner, position: _ })
|
||||
if inner.is_incomplete() =>
|
||||
{
|
||||
false
|
||||
}
|
||||
// An otherwise-valid compound command may simply be missing its
|
||||
// closing token (`fi`, `done`, `}`, ...).
|
||||
Err(brush_parser::ParseError::ParsingAtEndOfInput) => false,
|
||||
// A located syntax error is complete input and should be surfaced.
|
||||
// A successful parse is ready to execute.
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,21 @@ use std::io::{IsTerminal, Write};
|
||||
|
||||
use crate::{
|
||||
InputBackend, ShellError,
|
||||
input_backend::{InteractivePrompt, ReadResult},
|
||||
input_backend::{InteractivePrompt, ReadResult, parse_result_is_complete},
|
||||
};
|
||||
|
||||
/// Represents a minimal shell input backend, capable of taking commands from standard input.
|
||||
#[derive(Default)]
|
||||
pub struct MinimalInputBackend;
|
||||
pub struct MinimalInputBackend {
|
||||
parser_options: brush_parser::ParserOptions,
|
||||
}
|
||||
|
||||
impl MinimalInputBackend {
|
||||
/// Creates a minimal backend using the shell's active parser options.
|
||||
pub const fn new(parser_options: brush_parser::ParserOptions) -> Self {
|
||||
Self { parser_options }
|
||||
}
|
||||
}
|
||||
|
||||
impl InputBackend for MinimalInputBackend {
|
||||
fn read_line(
|
||||
@@ -17,12 +26,26 @@ impl InputBackend for MinimalInputBackend {
|
||||
) -> Result<ReadResult, ShellError> {
|
||||
self.display_prompt(&prompt)?;
|
||||
|
||||
let result = match Self::read_input_line()? {
|
||||
ReadResult::Input(s) => s,
|
||||
ReadResult::BoundCommand(s) => s,
|
||||
ReadResult::Eof => return Ok(ReadResult::Eof),
|
||||
let mut result = String::new();
|
||||
loop {
|
||||
match Self::read_input_line()? {
|
||||
ReadResult::Input(s) | ReadResult::BoundCommand(s) => {
|
||||
result.push_str(&s);
|
||||
if self.is_complete_input(&result) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ReadResult::Eof => {
|
||||
if result.is_empty() {
|
||||
return Ok(ReadResult::Eof);
|
||||
}
|
||||
// Return the partial command so the normal parser path can
|
||||
// report the incomplete construct instead of dropping it.
|
||||
break;
|
||||
}
|
||||
ReadResult::Interrupted => return Ok(ReadResult::Interrupted),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if result.is_empty() {
|
||||
Ok(ReadResult::Eof)
|
||||
@@ -33,6 +56,11 @@ impl InputBackend for MinimalInputBackend {
|
||||
}
|
||||
|
||||
impl MinimalInputBackend {
|
||||
fn is_complete_input(&self, input: &str) -> bool {
|
||||
let mut parser = brush_parser::Parser::new(input.as_bytes(), &self.parser_options);
|
||||
parse_result_is_complete(&parser.parse_program())
|
||||
}
|
||||
|
||||
#[expect(clippy::unused_self)]
|
||||
fn should_display_prompt(&self) -> bool {
|
||||
std::io::stdin().is_terminal()
|
||||
|
||||
@@ -307,7 +307,8 @@ async fn run_async(
|
||||
|
||||
#[cfg(feature = "minimal")]
|
||||
InputBackendType::Minimal => {
|
||||
let mut input_backend = brush_interactive::MinimalInputBackend;
|
||||
let parser_options = shell.lock().await.parser_options();
|
||||
let mut input_backend = brush_interactive::MinimalInputBackend::new(parser_options);
|
||||
run_in_shell(&shell, args.clone(), &mut input_backend, &ui_options).await
|
||||
}
|
||||
#[cfg(not(feature = "minimal"))]
|
||||
|
||||
Reference in New Issue
Block a user