feat(rslint_parser): Add `parse` method
Adds a method that allows parsing a file with the provided syntax.
MODIFY
crates/rome_analyze/src/analysis_server.rs
crates/rome_analyze/src/analysis_server.rs
use std::{collections::HashMap, sync::Arc};
-use rslint_parser::{parse_text, AstNode, SyntaxNode, TextRange};
+use rslint_parser::{parse_script, AstNode, SyntaxNode, TextRange};
use tracing::trace;
use crate::{
@@ -35,7 +35,7 @@ impl AnalysisServer {
let text = self
.get_file_text(file_id)
.expect("File contents missing while parsing");
- parse_text(&text, file_id).syntax()
+ parse_script(&text, file_id).syntax()
}
pub fn suppressions(&self, file_id: FileId) -> Suppressions {

MODIFY
crates/rome_core/src/file_handlers/javascript.rs
crates/rome_core/src/file_handlers/javascript.rs
@@ -4,21 +4,6 @@ use std::fmt::Debug;
#[derive(Debug, PartialEq, Eq)]
pub struct JsFileHandler;
-/// Features that belong to a single file
-#[derive(Debug)]
-pub struct JsFileFeatures {
- /// Whether the file is executed as a script
- pub script: bool,
- /// Whether the file is executed as a module
- pub module: bool,
- /// Whether the TS grammar is supported
- pub typescript: bool,
- /// Whether the JSX syntax is supported
- pub jsx: bool,
- /// Whether the TSX syntax is supported
- pub tsx: bool,
-}
-
impl ExtensionHandler for JsFileHandler {
fn capabilities(&self) -> super::Capabilities {
super::Capabilities {
@@ -39,44 +24,3 @@ impl ExtensionHandler for JsFileHandler {
true
}
}
-
-impl Default for JsFileFeatures {
- fn default() -> Self {
- Self {
- module: true,
- script: false,
- jsx: false,
- tsx: false,
- typescript: false,
- }
- }
-}
-
-impl JsFileFeatures {
- /// Mark the file as module
- pub fn module(mut self) -> Self {
- self.module = true;
- self.script = false;
- self
- }
-
- /// Mark the file as a script
- ///
- /// With this, most of the other features are turned off
- pub fn script(mut self) -> Self {
- self.script = true;
- self.module = false;
- self.typescript = false;
- self.tsx = false;
- self.jsx = false;
- self
- }
-
- /// Enables TypeScript
- pub fn typescript(mut self) -> Self {
- self.script = false;
- self.module = true;
- self.typescript = true;
- self
- }
-}

MODIFY
crates/rome_core/src/file_handlers/json.rs
crates/rome_core/src/file_handlers/json.rs
@@ -2,9 +2,6 @@ use super::{ExtensionHandler, Mime};
#[derive(Debug, Default, PartialEq, Eq)]
pub struct JsonFileHandler;
-#[derive(Default)]
-pub struct JsonFileFeatures {}
-
impl ExtensionHandler for JsonFileHandler {
fn capabilities(&self) -> super::Capabilities {
super::Capabilities {

MODIFY
crates/rome_core/src/file_handlers/unknown.rs
crates/rome_core/src/file_handlers/unknown.rs
use super::ExtensionHandler;
+
#[derive(Debug, Default, PartialEq, Eq)]
pub struct UnknownFileHandler {}

MODIFY
crates/rome_formatter/src/lib.rs
crates/rome_formatter/src/lib.rs
@@ -55,7 +55,7 @@ mod intersperse;
mod printer;
mod ts;
pub use formatter::Formatter;
-use rslint_parser::{parse_module, SyntaxError, SyntaxNode};
+use rslint_parser::{parse, Syntax, SyntaxError, SyntaxNode};
pub use format_element::{
block_indent, concat_elements, empty_element, empty_line, group_elements, hard_line_break,
@@ -65,10 +65,8 @@ pub use format_element::{
};
pub use printer::Printer;
pub use printer::PrinterOptions;
-use rome_core::file_handlers::javascript::JsFileFeatures;
use rome_core::App;
use rome_path::RomePath;
-use rslint_parser::parse_text;
use std::str::FromStr;
/// This trait should be implemented on each node/value that should have a formatted representation
@@ -184,13 +182,9 @@ pub fn format_element(element: &FormatElement, options: FormatOptions) -> Format
pub fn format_file_and_save(rome_path: &mut RomePath, options: FormatOptions, app: &App) {
let result = if app.can_format(rome_path) {
let buffer = rome_path.get_buffer_from_file();
- let features = JsFileFeatures::default().module();
- let parsed_result = if features.module {
- parse_module(buffer.as_str(), 0).syntax()
- } else {
- parse_text(buffer.as_str(), 0).syntax()
- };
- Some(format(options, &parsed_result))
+ let syntax = Syntax::default().module();
+ let root = parse(buffer.as_str(), 0, syntax).syntax();
+ Some(format(options, &root))
} else {
None
};

MODIFY
crates/rome_formatter/src/ts/mod.rs
crates/rome_formatter/src/ts/mod.rs
@@ -17,14 +17,14 @@ mod unknown;
#[cfg(test)]
mod test {
- use rslint_parser::parse_text;
+ use rslint_parser::parse_script;
use crate::Formatter;
#[test]
fn arrow_function() {
let src = "let v = (value , second_value) => true";
- let tree = parse_text(src, 0);
+ let tree = parse_script(src, 0);
let result = Formatter::default().format_root(&tree.syntax()).unwrap();
assert_eq!(
result.code(),
@@ -37,7 +37,7 @@ mod test {
fn function_block() {
let src = r#"function foo() { return 'something' }"#;
- let tree = parse_text(src, 0);
+ let tree = parse_script(src, 0);
let result = Formatter::default().format_root(&tree.syntax()).unwrap();
assert_eq!(
result.code(),
@@ -51,7 +51,7 @@ mod test {
#[test]
fn array() {
let src = r#"let users = [ 'john', 'chandler', true ]"#;
- let tree = parse_text(src, 0);
+ let tree = parse_script(src, 0);
let result = Formatter::default().format_root(&tree.syntax()).unwrap();
assert_eq!(
result.code(),
@@ -64,7 +64,7 @@ mod test {
fn poc() {
let src = r#"let a1 = [{}, {}];
"#;
- let tree = parse_text(src, 0);
+ let tree = parse_script(src, 0);
let result = Formatter::default().format_root(&tree.syntax()).unwrap();
assert_eq!(
result.code(),

MODIFY
crates/rome_formatter/tests/spec_test.rs
crates/rome_formatter/tests/spec_test.rs
-use rome_core::file_handlers::javascript::JsFileFeatures;
use rome_core::App;
use rome_formatter::{format, FormatOptions};
use rome_path::RomePath;
-use rslint_parser::{parse_module, parse_text};
+use rslint_parser::{parse, Syntax};
use std::fs;
use std::path::Path;
@@ -37,19 +36,14 @@ pub fn run(spec_input_file: &str, _: &str, file_type: &str) {
let mut rome_path = RomePath::new(file_path);
if app.can_format(&rome_path) {
let buffer = rome_path.get_buffer_from_file();
- let features = if file_type == "module" {
- JsFileFeatures::default().module()
+ let syntax = if file_type == "module" {
+ Syntax::default().module()
} else {
- JsFileFeatures::default().script()
+ Syntax::default()
};
- let parsed_result = if features.module {
- parse_module(buffer.as_str(), 0).syntax()
- } else {
- parse_text(buffer.as_str(), 0).syntax()
- };
-
- let formatted_result = format(FormatOptions::default(), &parsed_result);
+ let root = parse(buffer.as_str(), 0, syntax).syntax();
+ let formatted_result = format(FormatOptions::default(), &root);
let file_name = spec_input_file.file_name().unwrap().to_str().unwrap();
let input = fs::read_to_string(file_path).unwrap();
let result = formatted_result.unwrap();

MODIFY
crates/rome_lsp/src/handlers.rs
crates/rome_lsp/src/handlers.rs
@@ -2,12 +2,12 @@ use anyhow::Result;
use lspower::lsp::*;
use rome_analyze::FileId;
use rome_formatter::{FormatOptions, Formatter, IndentStyle};
-use rslint_parser::parse_text;
+use rslint_parser::parse_script;
use crate::line_index;
pub fn format(text: &str, file_id: FileId) -> Result<Vec<TextEdit>> {
- let tree = parse_text(text, file_id).syntax();
+ let tree = parse_script(text, file_id).syntax();
let options = FormatOptions {
indent_style: IndentStyle::Tab,

MODIFY
crates/rslint_parser/src/ast/stmt_ext.rs
crates/rslint_parser/src/ast/stmt_ext.rs
@@ -43,7 +43,7 @@ mod tests {
#[test]
fn var_decl_let_token() {
- let parsed = parse_text("/* */let a = 5;", 0).tree();
+ let parsed = parse_script("/* */let a = 5;", 0).tree();
let var_decl = parsed
.statements()
.iter()
@@ -54,7 +54,7 @@ mod tests {
#[test]
fn is_var_check() {
- let root = parse_text("var a = 5;", 0).syntax();
+ let root = parse_script("var a = 5;", 0).syntax();
let var_decl = root
.descendants()
.find_map(ast::JsVariableDeclarations::cast);

MODIFY
crates/rslint_parser/src/numbers.rs
crates/rslint_parser/src/numbers.rs
@@ -45,13 +45,13 @@ pub fn parse_js_big_int(num: &str) -> Option<BigInt> {
mod tests {
use crate::{
ast::{JsAnyExpression, JsAnyLiteralExpression},
- parse_expr,
+ parse_expression,
};
use num_bigint::ToBigInt;
macro_rules! assert_float {
($literal:literal, $value:expr) => {
- let parsed = parse_expr($literal, 0);
+ let parsed = parse_expression($literal, 0);
match parsed.tree().expression().unwrap() {
JsAnyExpression::JsAnyLiteralExpression(
JsAnyLiteralExpression::JsNumberLiteralExpression(literal),
@@ -68,7 +68,7 @@ mod tests {
macro_rules! assert_bigint {
($literal:literal, $value:expr) => {
- let parsed = parse_expr($literal, 0);
+ let parsed = parse_expression($literal, 0);
match parsed.tree().expression().unwrap() {
JsAnyExpression::JsAnyLiteralExpression(
JsAnyLiteralExpression::JsBigIntLiteralExpression(literal),

MODIFY
crates/rslint_parser/src/parse.rs
crates/rslint_parser/src/parse.rs
//! Utilities for high level parsing of js code.
-use crate::ast::{JsExpressionSnipped, JsModule, JsScript};
+use crate::ast::{JsAnyRoot, JsExpressionSnipped, JsModule, JsScript};
use crate::*;
use rslint_errors::Severity;
use std::marker::PhantomData;
@@ -11,6 +11,16 @@ pub enum JsSourceType {
Module,
}
+impl From<Syntax> for JsSourceType {
+ fn from(syntax: Syntax) -> Self {
+ match syntax.file_kind {
+ FileKind::Script => JsSourceType::Script,
+ FileKind::Module => JsSourceType::Module,
+ FileKind::TypeScript => JsSourceType::Module,
+ }
+ }
+}
+
/// A utility struct for managing the result of a parser job
#[derive(Debug, Clone)]
pub struct Parse<T> {
@@ -49,9 +59,9 @@ impl<T> Parse<T> {
/// The syntax node represented by this Parse result
///
/// ```
- /// use rslint_parser::{parse_text, ast::JsIfStatement, SyntaxNodeExt, JsSyntaxKind, AstNode, AstNodeList};
+ /// use rslint_parser::{parse_script, ast::JsIfStatement, SyntaxNodeExt, JsSyntaxKind, AstNode, AstNodeList};
///
- /// let parse = parse_text(
+ /// let parse = parse_script(
/// "
/// if (a > 5) {
/// /* something */
@@ -138,9 +148,9 @@ fn parse_common(
/// Or turned into a typed [`Script`](Script) with [`tree`](Parse::tree).
///
/// ```
-/// use rslint_parser::{ast::JsComputedMemberExpression, parse_text, AstNode, SyntaxToken, SyntaxNodeExt, util, SyntaxList};
+/// use rslint_parser::{ast::JsComputedMemberExpression, parse_script, AstNode, SyntaxToken, SyntaxNodeExt, util, SyntaxList};
///
-/// let parse = parse_text("foo.bar[2]", 0);
+/// let parse = parse_script("foo.bar[2]", 0);
/// // Parse returns a JS Root which contains two lists, the directives and the statements, let's get the statements
/// let stmt = parse.syntax().children().nth(1).unwrap();
/// // The untyped syntax node of `foo.bar[2]`, the root node is `Script`.
@@ -163,12 +173,10 @@ fn parse_common(
///
/// assert_eq!(&util::concat_tokens(&tokens), "foo.bar[2]")
/// ```
-pub fn parse_text(text: &str, file_id: usize) -> Parse<JsScript> {
- let (events, errors, tokens) = parse_common(text, file_id, Syntax::default());
- let mut tree_sink = LosslessTreeSink::new(text, &tokens);
- crate::process(&mut tree_sink, events, errors);
- let (green, parse_errors) = tree_sink.finish();
- Parse::new_script(green, parse_errors)
+pub fn parse_script(text: &str, file_id: usize) -> Parse<JsScript> {
+ parse(text, file_id, Syntax::default())
+ .cast::<JsScript>()
+ .unwrap()
}
/// Lossly parse text into a [`Parse`](Parse) which can then be turned into an untyped root [`SyntaxNode`](SyntaxNode).
@@ -180,9 +188,9 @@ pub fn parse_text(text: &str, file_id: usize) -> Parse<JsScript> {
/// The [`util`](crate::util) module has utility functions for dealing with this easily.
///
/// ```
-/// use rslint_parser::{ast::JsComputedMemberExpression, parse_text_lossy, AstNode, SyntaxToken, SyntaxNodeExt, util, SyntaxList};
+/// use rslint_parser::{ast::JsComputedMemberExpression, parse_script_lossy, AstNode, SyntaxToken, SyntaxNodeExt, util, SyntaxList};
///
-/// let parse = parse_text_lossy("foo.bar[2]", 0);
+/// let parse = parse_script_lossy("foo.bar[2]", 0);
/// // Parse returns a JS Root with two children, an empty list of directives and the list of statements, let's get the statements
/// let stmt = parse.syntax().children().nth(1).unwrap();
/// // The untyped syntax node of `foo.bar[2]`, the root node is `Script`.
@@ -206,7 +214,7 @@ pub fn parse_text(text: &str, file_id: usize) -> Parse<JsScript> {
/// // End result does not include whitespace because the parsing is lossy in this case
/// assert_eq!(&util::concat_tokens(&tokens), "foo.bar[2]")
/// ```
-pub fn parse_text_lossy(text: &str, file_id: usize) -> Parse<JsScript> {
+pub fn parse_script_lossy(text: &str, file_id: usize) -> Parse<JsScript> {
let (events, errors, tokens) = parse_common(text, file_id, Syntax::default());
let mut tree_sink = LossyTreeSink::new(text, &tokens);
crate::process(&mut tree_sink, events, errors);
@@ -225,16 +233,23 @@ pub fn parse_module_lossy(text: &str, file_id: usize) -> Parse<JsModule> {
/// Same as [`parse_text`] but configures the parser to parse an ECMAScript module instead of a script
pub fn parse_module(text: &str, file_id: usize) -> Parse<JsModule> {
- let (events, errors, tokens) = parse_common(text, file_id, Syntax::default().module());
+ parse(text, file_id, Syntax::default().module())
+ .cast::<JsModule>()
+ .unwrap()
+}
+
+/// Parses the provided string as a EcmaScript program using the provided syntax features.
+pub fn parse(text: &str, file_id: usize, syntax: Syntax) -> Parse<JsAnyRoot> {
+ let (events, errors, tokens) = parse_common(text, file_id, syntax);
let mut tree_sink = LosslessTreeSink::new(text, &tokens);
crate::process(&mut tree_sink, events, errors);
let (green, parse_errors) = tree_sink.finish();
- Parse::new_module(green, parse_errors)
+ Parse::new(green, parse_errors, syntax.into())
}
/// Losslessly Parse text into an expression [`Parse`](Parse) which can then be turned into an untyped root [`SyntaxNode`](SyntaxNode).
/// Or turned into a typed [`Expr`](Expr) with [`tree`](Parse::tree).
-pub fn parse_expr(text: &str, file_id: usize) -> Parse<JsExpressionSnipped> {
+pub fn parse_expression(text: &str, file_id: usize) -> Parse<JsExpressionSnipped> {
let (tokens, mut errors) = tokenize(text, file_id);
let tok_source = TokenSource::new(text, &tokens);
let mut parser = crate::Parser::new(tok_source, file_id, Syntax::default());

MODIFY
crates/rslint_parser/src/tests.rs
crates/rslint_parser/src/tests.rs
use crate::ast::{JsAnyRoot, JsCallArguments};
-use crate::{parse_module, parse_text, AstNode, Parse, ParserError, SyntaxNode, SyntaxToken};
+use crate::{parse, parse_module, AstNode, Parse, ParserError, Syntax, SyntaxNode, SyntaxToken};
use expect_test::expect_file;
use rome_rowan::TextSize;
use rslint_errors::file::SimpleFile;
@@ -54,12 +54,16 @@ fn try_parse(path: &str, text: &str) -> Parse<JsAnyRoot> {
let res = catch_unwind(|| {
// Files containing a // SCRIPT comment are parsed as script and not as module
// This is needed to test features that are restricted in strict mode.
- let parse = if text.contains("// SCRIPT") {
- parse_text(text, 0).cast::<JsAnyRoot>().unwrap()
+ let syntax = if text.contains("// SCRIPT") {
+ Syntax::default()
+ } else if text.contains("// TYPESCRIPT") {
+ Syntax::default().typescript()
} else {
- parse_module(text, 0).cast::<JsAnyRoot>().unwrap()
+ Syntax::default().module()
};
+ let parse = parse(text, 0, syntax);
+
assert_eq!(
parse.syntax().to_string(),
text,

MODIFY
crates/rslint_parser/src/util.rs
crates/rslint_parser/src/util.rs
@@ -49,10 +49,10 @@ pub trait SyntaxNodeExt {
/// # Examples
///
/// ```
- /// use rslint_parser::{SyntaxNodeExt, parse_expr};
+ /// use rslint_parser::{SyntaxNodeExt, parse_expression};
///
- /// let left = parse_expr("foo. bar", 0).syntax();
- /// let right = parse_expr("foo.bar", 0).syntax();
+ /// let left = parse_expression("foo. bar", 0).syntax();
+ /// let right = parse_expression("foo.bar", 0).syntax();
///
/// assert!(left.lexical_eq(&right));
///

MODIFY
xtask/src/coverage/test262.rs
xtask/src/coverage/test262.rs
use super::{files::*, *};
use colored::Colorize;
-use rslint_parser::{parse_module, parse_text};
+use rslint_parser::{parse, parse_module, Syntax};
use std::path::PathBuf;
use yastl::Pool;
@@ -120,7 +120,7 @@ pub fn run_test_file(file: TestFile) -> TestResult {
let TestFile { code, meta, path } = file;
if meta.flags.contains(&TestFlag::OnlyStrict) {
- let (code, res) = exec_test(code, true, false);
+ let (code, res) = exec_test(code, true, Syntax::default());
let fail = passed(res, meta);
let outcome = extract_outcome(&fail);
TestResult {
@@ -130,7 +130,7 @@ pub fn run_test_file(file: TestFile) -> TestResult {
outcome,
}
} else if meta.flags.contains(&TestFlag::Module) {
- let (code, res) = exec_test(code, false, true);
+ let (code, res) = exec_test(code, false, Syntax::default().module());
let fail = passed(res, meta);
let outcome = extract_outcome(&fail);
TestResult {
@@ -140,7 +140,7 @@ pub fn run_test_file(file: TestFile) -> TestResult {
outcome,
}
} else if meta.flags.contains(&TestFlag::NoStrict) || meta.flags.contains(&TestFlag::Raw) {
- let (code, res) = exec_test(code, false, false);
+ let (code, res) = exec_test(code, false, Syntax::default());
let fail = passed(res, meta);
let outcome = extract_outcome(&fail);
TestResult {
@@ -150,8 +150,8 @@ pub fn run_test_file(file: TestFile) -> TestResult {
outcome,
}
} else {
- let (_, l) = exec_test(code.clone(), false, false);
- let (code, r) = exec_test(code, true, false);
+ let (_, l) = exec_test(code.clone(), false, Syntax::default());
+ let (code, r) = exec_test(code, true, Syntax::default());
merge_tests(code, l, r, meta, path)
}
}
@@ -251,19 +251,12 @@ fn extract_outcome(fail: &Option<FailReason>) -> Outcome {
}
}
-fn exec_test(mut code: String, append_use_strict: bool, module: bool) -> (String, ExecRes) {
+fn exec_test(mut code: String, append_use_strict: bool, syntax: Syntax) -> (String, ExecRes) {
if append_use_strict {
code.insert_str(0, "\"use strict\";\n");
}
- let result = std::panic::catch_unwind(|| {
- if module {
- parse_module(&code, 0).ok().map(drop)
- } else {
- parse_text(&code, 0).ok().map(drop)
- }
- });
-
+ let result = std::panic::catch_unwind(|| parse(&code, 0, syntax).ok().map(drop));
let result = result
.map(|res| {
if let Err(errors) = res {

MODIFY
xtask/src/coverage/typescript.rs
xtask/src/coverage/typescript.rs
use super::*;
use crate::coverage::{FailReason, Outcome, TestResult};
use colored::Colorize;
-use rslint_parser::parse_text;
+use rslint_parser::{parse, Syntax};
use walkdir::{DirEntry, WalkDir};
use yastl::Pool;
@@ -79,7 +79,7 @@ pub fn run_ts(
}
let code = code.unwrap();
let result = std::panic::catch_unwind(|| {
- let r = parse_text(&code, 0);
+ let r = parse(&code, 0, Syntax::default().typescript());
if is_detailed && show_rast {
println!("{:#?}", r.syntax());

MODIFY
xtask/src/libs/mod.rs
xtask/src/libs/mod.rs
@@ -113,13 +113,13 @@ pub fn run(filter: String, criterion: bool, baseline: Option<String>) {
group.throughput(criterion::Throughput::Bytes(code.len() as u64));
group.bench_function(&id, |b| {
b.iter(|| {
- let _ = criterion::black_box(rslint_parser::parse_text(code, 0));
+ let _ = criterion::black_box(rslint_parser::parse_module(code, 0));
})
});
group.finish();
} else {
//warmup
- rslint_parser::parse_text(code, 0);
+ rslint_parser::parse_module(code, 0);
}
let result = benchmark_lib(&id, code);

feat(rslint_parser): Add `parse` method (#2000)

Adds a method that allows parsing a file with the provided syntax.