mirror of
https://github.com/denoland/deno.git
synced 2025-01-05 05:49:20 -05:00
fix(repl): improve validator to mark more code as incomplete (#17443)
Closes #17442
This commit is contained in:
parent
df4d0c55c0
commit
9d3483d4eb
1 changed files with 89 additions and 52 deletions
|
@ -235,17 +235,34 @@ impl Validator for EditorHelper {
|
||||||
&self,
|
&self,
|
||||||
ctx: &mut ValidationContext,
|
ctx: &mut ValidationContext,
|
||||||
) -> Result<ValidationResult, ReadlineError> {
|
) -> Result<ValidationResult, ReadlineError> {
|
||||||
|
Ok(validate(ctx.input()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate(input: &str) -> ValidationResult {
|
||||||
|
let line_info = text_lines::TextLines::new(input);
|
||||||
let mut stack: Vec<Token> = Vec::new();
|
let mut stack: Vec<Token> = Vec::new();
|
||||||
let mut in_template = false;
|
let mut in_template = false;
|
||||||
|
let mut div_token_count_on_current_line = 0;
|
||||||
|
let mut last_line_index = 0;
|
||||||
|
|
||||||
for item in deno_ast::lex(ctx.input(), deno_ast::MediaType::TypeScript) {
|
for item in deno_ast::lex(input, deno_ast::MediaType::TypeScript) {
|
||||||
|
let current_line_index = line_info.line_index(item.range.start);
|
||||||
|
if current_line_index != last_line_index {
|
||||||
|
div_token_count_on_current_line = 0;
|
||||||
|
last_line_index = current_line_index;
|
||||||
|
}
|
||||||
if let deno_ast::TokenOrComment::Token(token) = item.inner {
|
if let deno_ast::TokenOrComment::Token(token) = item.inner {
|
||||||
match token {
|
match token {
|
||||||
Token::BinOp(BinOpToken::Div)
|
Token::BinOp(BinOpToken::Div)
|
||||||
| Token::AssignOp(AssignOp::DivAssign) => {
|
| Token::AssignOp(AssignOp::DivAssign) => {
|
||||||
// it's too complicated to write code to detect regular expression literals
|
// it's too complicated to write code to detect regular expression literals
|
||||||
// which are no longer tokenized, so if a `/` or `/=` happens, then we bail
|
// which are no longer tokenized, so if a `/` or `/=` happens twice on the same
|
||||||
return Ok(ValidationResult::Valid(None));
|
// line, then we bail
|
||||||
|
div_token_count_on_current_line += 1;
|
||||||
|
if div_token_count_on_current_line >= 2 {
|
||||||
|
return ValidationResult::Valid(None);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Token::BackQuote => in_template = !in_template,
|
Token::BackQuote => in_template = !in_template,
|
||||||
Token::LParen
|
Token::LParen
|
||||||
|
@ -259,15 +276,15 @@ impl Validator for EditorHelper {
|
||||||
| (Some(Token::LBrace), Token::RBrace)
|
| (Some(Token::LBrace), Token::RBrace)
|
||||||
| (Some(Token::DollarLBrace), Token::RBrace) => {}
|
| (Some(Token::DollarLBrace), Token::RBrace) => {}
|
||||||
(Some(left), _) => {
|
(Some(left), _) => {
|
||||||
return Ok(ValidationResult::Invalid(Some(format!(
|
return ValidationResult::Invalid(Some(format!(
|
||||||
"Mismatched pairs: {:?} is not properly closed",
|
"Mismatched pairs: {:?} is not properly closed",
|
||||||
left
|
left
|
||||||
))))
|
)))
|
||||||
}
|
}
|
||||||
(None, _) => {
|
(None, _) => {
|
||||||
// While technically invalid when unpaired, it should be V8's task to output error instead.
|
// While technically invalid when unpaired, it should be V8's task to output error instead.
|
||||||
// Thus marked as valid with no info.
|
// Thus marked as valid with no info.
|
||||||
return Ok(ValidationResult::Valid(None));
|
return ValidationResult::Valid(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -278,7 +295,7 @@ impl Validator for EditorHelper {
|
||||||
_ => {
|
_ => {
|
||||||
// If it failed parsing, it should be V8's task to output error instead.
|
// If it failed parsing, it should be V8's task to output error instead.
|
||||||
// Thus marked as valid with no info.
|
// Thus marked as valid with no info.
|
||||||
return Ok(ValidationResult::Valid(None));
|
return ValidationResult::Valid(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -288,11 +305,10 @@ impl Validator for EditorHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !stack.is_empty() || in_template {
|
if !stack.is_empty() || in_template {
|
||||||
return Ok(ValidationResult::Incomplete);
|
return ValidationResult::Incomplete;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ValidationResult::Valid(None))
|
ValidationResult::Valid(None)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Highlighter for EditorHelper {
|
impl Highlighter for EditorHelper {
|
||||||
|
@ -512,3 +528,24 @@ impl ConditionalEventHandler for TabEventHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use rustyline::validate::ValidationResult;
|
||||||
|
|
||||||
|
use super::validate;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validate_only_one_forward_slash_per_line() {
|
||||||
|
let code = r#"function test(arr){
|
||||||
|
if( arr.length <= 1) return arr.map(a => a / 2)
|
||||||
|
let left = test( arr.slice( 0 , arr.length/2 ) )"#;
|
||||||
|
assert!(matches!(validate(code), ValidationResult::Incomplete));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validate_regex_looking_code() {
|
||||||
|
let code = r#"/testing/;"#;
|
||||||
|
assert!(matches!(validate(code), ValidationResult::Valid(_)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue