From 69c96a18045efe453bd46e6a2a1bf56cf24c0079 Mon Sep 17 00:00:00 2001 From: Davis Vaughan Date: Sat, 7 Dec 2024 21:30:37 -0500 Subject: [PATCH 01/16] Add `Parse` utility struct --- crates/air_r_formatter/tests/language.rs | 2 +- crates/air_r_parser/src/lib.rs | 1 + crates/air_r_parser/src/parse.rs | 114 +++++++++++++++++++++-- crates/air_r_parser/tests/spec_test.rs | 6 +- crates/lsp/src/documents.rs | 4 +- 5 files changed, 113 insertions(+), 14 deletions(-) diff --git a/crates/air_r_formatter/tests/language.rs b/crates/air_r_formatter/tests/language.rs index 27299772..d74c6b8b 100644 --- a/crates/air_r_formatter/tests/language.rs +++ b/crates/air_r_formatter/tests/language.rs @@ -16,7 +16,7 @@ impl TestFormatLanguage for RTestFormatLanguage { type FormatLanguage = RFormatLanguage; fn parse(&self, text: &str) -> AnyParse { - air_r_parser::parse(text, RParserOptions::default()) + air_r_parser::parse(text, RParserOptions::default()).into() } fn to_format_language(&self) -> Self::FormatLanguage { diff --git a/crates/air_r_parser/src/lib.rs b/crates/air_r_parser/src/lib.rs index 09290c3b..c5c65ef8 100644 --- a/crates/air_r_parser/src/lib.rs +++ b/crates/air_r_parser/src/lib.rs @@ -8,6 +8,7 @@ use air_r_factory::RSyntaxFactory; pub use options::RParserOptions; pub use parse::parse; pub use parse::parse_r_with_cache; +pub use parse::Parse; use air_r_syntax::RLanguage; use biome_parser::tree_sink::LosslessTreeSink; diff --git a/crates/air_r_parser/src/parse.rs b/crates/air_r_parser/src/parse.rs index 331e9140..b4a48f32 100644 --- a/crates/air_r_parser/src/parse.rs +++ b/crates/air_r_parser/src/parse.rs @@ -1,8 +1,14 @@ +use std::marker::PhantomData; + +use air_r_syntax::RLanguage; +use air_r_syntax::RRoot; use air_r_syntax::RSyntaxKind; +use air_r_syntax::RSyntaxNode; use biome_parser::event::Event; use biome_parser::prelude::ParseDiagnostic; use biome_parser::prelude::Trivia; use biome_parser::AnyParse; +use biome_rowan::AstNode; use biome_rowan::NodeCache; use biome_rowan::TextRange; use biome_rowan::TextSize; @@ -16,20 +22,112 @@ use crate::treesitter::WalkEvent; use crate::RLosslessTreeSink; use crate::RParserOptions; -// TODO(r): These should really return an intermediate `Parse` type which -// can `.into()` an `AnyParse`, see `biome_js_parser`'s `Parse` type -pub fn parse(text: &str, options: RParserOptions) -> AnyParse { +/// A utility struct for managing the result of a parser job +#[derive(Debug, Clone)] +pub struct Parse { + root: RSyntaxNode, + diagnostics: Vec, + _ty: PhantomData, +} + +impl Parse { + pub fn new(root: RSyntaxNode, diagnostics: Vec) -> Parse { + Parse { + root, + diagnostics, + _ty: PhantomData, + } + } + + pub fn cast>(self) -> Option> { + if N::can_cast(self.syntax().kind()) { + Some(Parse::new(self.root, self.diagnostics)) + } else { + None + } + } + + /// The syntax node represented by this Parse result + pub fn syntax(&self) -> RSyntaxNode { + self.root.clone() + } + + /// Get the diagnostics which occurred when parsing + pub fn diagnostics(&self) -> &[ParseDiagnostic] { + self.diagnostics.as_slice() + } + + /// Get the diagnostics which occurred when parsing + pub fn into_diagnostics(self) -> Vec { + self.diagnostics + } + + /// Returns [true] if the parser encountered some errors during the parsing. + pub fn has_errors(&self) -> bool { + self.diagnostics + .iter() + .any(|diagnostic| diagnostic.is_error()) + } +} + +impl> Parse { + /// Convert this parse result into a typed AST node. + /// + /// # Panics + /// Panics if the node represented by this parse result mismatches. + pub fn tree(&self) -> T { + self.try_tree().unwrap_or_else(|| { + panic!( + "Expected tree to be a {} but root is:\n{:#?}", + std::any::type_name::(), + self.syntax() + ) + }) + } + + /// Try to convert this parse's untyped syntax node into an AST node. + pub fn try_tree(&self) -> Option { + T::cast(self.syntax()) + } + + /// Convert this parse into a result + pub fn into_result(self) -> Result> { + if !self.has_errors() { + Ok(self.tree()) + } else { + Err(self.diagnostics) + } + } +} + +impl From> for AnyParse { + fn from(parse: Parse) -> Self { + let root = parse.syntax(); + let diagnostics = parse.into_diagnostics(); + Self::new( + // SAFETY: the parser should always return a root node + root.as_send().unwrap(), + diagnostics, + ) + } +} + +pub fn parse(text: &str, options: RParserOptions) -> Parse { let mut cache = NodeCache::default(); parse_r_with_cache(text, options, &mut cache) } -pub fn parse_r_with_cache(text: &str, options: RParserOptions, cache: &mut NodeCache) -> AnyParse { +pub fn parse_r_with_cache( + text: &str, + options: RParserOptions, + cache: &mut NodeCache, +) -> Parse { tracing::debug_span!("parse").in_scope(move || { - let (events, tokens, errors) = parse_text(text, options); + let (events, tokens, diagnostics) = parse_text(text, options); let mut tree_sink = RLosslessTreeSink::with_cache(text, &tokens, cache); - biome_parser::event::process(&mut tree_sink, events, errors); - let (green, parse_errors) = tree_sink.finish(); - AnyParse::new(green.as_send().unwrap(), parse_errors) + biome_parser::event::process(&mut tree_sink, events, diagnostics); + let (green, diagnostics) = tree_sink.finish(); + Parse::new(green, diagnostics) }) } diff --git a/crates/air_r_parser/tests/spec_test.rs b/crates/air_r_parser/tests/spec_test.rs index 951e5471..fa034fdc 100644 --- a/crates/air_r_parser/tests/spec_test.rs +++ b/crates/air_r_parser/tests/spec_test.rs @@ -1,5 +1,5 @@ use air_r_parser::{parse, RParserOptions}; -use air_r_syntax::{RLanguage, RRoot, RSyntaxNode}; +use air_r_syntax::{RRoot, RSyntaxNode}; use biome_console::fmt::{Formatter, Termcolor}; use biome_console::markup; use biome_diagnostics::display::PrintDiagnostic; @@ -75,7 +75,7 @@ pub fn run(test_case: &str, _snapshot_name: &str, test_directory: &str, outcome_ {:#?} ``` "#, - parsed.syntax::() + parsed.syntax() ) .unwrap(); @@ -124,7 +124,7 @@ pub fn run(test_case: &str, _snapshot_name: &str, test_directory: &str, outcome_ panic!("Parsed tree of a 'OK' test case should not contain any missing required children or bogus nodes: \n {formatted_ast:#?} \n\n {formatted_ast}"); } - let syntax = parsed.syntax::(); + let syntax = parsed.syntax(); if has_bogus_nodes_or_empty_slots(&syntax) { panic!("modified tree has bogus nodes or empty slots:\n{syntax:#?} \n\n {syntax}") } diff --git a/crates/lsp/src/documents.rs b/crates/lsp/src/documents.rs index edb9a871..f5493c75 100644 --- a/crates/lsp/src/documents.rs +++ b/crates/lsp/src/documents.rs @@ -69,7 +69,7 @@ impl Document { }; // Parse document immediately for now - let parse = air_r_parser::parse(&contents, Default::default()); + let parse = air_r_parser::parse(&contents, Default::default()).into(); Self { contents, @@ -117,7 +117,7 @@ impl Document { ); // No incrementality for now - let parse = air_r_parser::parse(&contents, Default::default()); + let parse = air_r_parser::parse(&contents, Default::default()).into(); self.parse = parse; self.contents = contents; From 90b1ed96a973604ec278cd40dadc30793d9da065 Mon Sep 17 00:00:00 2001 From: Davis Vaughan Date: Sat, 7 Dec 2024 21:31:17 -0500 Subject: [PATCH 02/16] Style changes in latest version --- crates/air_r_formatter/src/lib.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/crates/air_r_formatter/src/lib.rs b/crates/air_r_formatter/src/lib.rs index fe5724a0..93fff37f 100644 --- a/crates/air_r_formatter/src/lib.rs +++ b/crates/air_r_formatter/src/lib.rs @@ -44,7 +44,10 @@ impl AsFormat for &T where T: AsFormat, { - type Format<'a> = T::Format<'a> where Self: 'a; + type Format<'a> + = T::Format<'a> + where + Self: 'a; fn format(&self) -> Self::Format<'_> { AsFormat::format(&**self) @@ -58,7 +61,10 @@ impl AsFormat for biome_rowan::SyntaxResult where T: AsFormat, { - type Format<'a> = biome_rowan::SyntaxResult> where Self: 'a; + type Format<'a> + = biome_rowan::SyntaxResult> + where + Self: 'a; fn format(&self) -> Self::Format<'_> { match self { @@ -75,7 +81,10 @@ impl AsFormat for Option where T: AsFormat, { - type Format<'a> = Option> where Self: 'a; + type Format<'a> + = Option> + where + Self: 'a; fn format(&self) -> Self::Format<'_> { self.as_ref().map(|value| value.format()) From bd67cefc656ef15d370731cee08184764aebca3a Mon Sep 17 00:00:00 2001 From: Davis Vaughan Date: Sat, 7 Dec 2024 21:34:28 -0500 Subject: [PATCH 03/16] Add multi-file and directory support, add `--check` --- Cargo.lock | 57 +++++++ Cargo.toml | 4 + crates/air/Cargo.toml | 10 +- crates/air/src/args.rs | 13 +- crates/air/src/commands/format.rs | 258 +++++++++++++++++++++++++---- crates/air_fs/Cargo.toml | 20 +++ crates/air_fs/src/lib.rs | 25 +++ crates/air_workspace/Cargo.toml | 26 +++ crates/air_workspace/src/format.rs | 77 +++++++++ crates/air_workspace/src/lib.rs | 1 + 10 files changed, 451 insertions(+), 40 deletions(-) create mode 100644 crates/air_fs/Cargo.toml create mode 100644 crates/air_fs/src/lib.rs create mode 100644 crates/air_workspace/Cargo.toml create mode 100644 crates/air_workspace/src/format.rs create mode 100644 crates/air_workspace/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 6d586ac4..7a53d551 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,15 +30,23 @@ dependencies = [ name = "air" version = "0.1.0" dependencies = [ + "air_fs", "air_r_formatter", "air_r_parser", + "air_workspace", "anyhow", + "biome_console", + "biome_diagnostics", "biome_formatter", + "biome_parser", "clap", + "ignore", + "itertools", "line_ending", "lsp", "tempfile", "tokio", + "tracing", ] [[package]] @@ -57,6 +65,13 @@ dependencies = [ "similar-asserts", ] +[[package]] +name = "air_fs" +version = "0.0.0" +dependencies = [ + "path-absolutize", +] + [[package]] name = "air_r_factory" version = "0.0.0" @@ -110,6 +125,19 @@ dependencies = [ "serde", ] +[[package]] +name = "air_workspace" +version = "0.0.0" +dependencies = [ + "air_r_formatter", + "air_r_parser", + "biome_console", + "biome_diagnostics", + "biome_formatter", + "biome_parser", + "biome_rowan", +] + [[package]] name = "anstream" version = "0.6.17" @@ -533,6 +561,7 @@ dependencies = [ "anstyle", "clap_lex", "strsim", + "terminal_size", ] [[package]] @@ -1402,6 +1431,24 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "path-absolutize" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4af381fe79fa195b4909485d99f73a80792331df0625188e707854f0b3383f5" +dependencies = [ + "path-dedot", +] + +[[package]] +name = "path-dedot" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07ba0ad7e047712414213ff67533e6dd477af0a4e1d14fb52343e53d30ea9397" +dependencies = [ + "once_cell", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -1882,6 +1929,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "terminal_size" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9" +dependencies = [ + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "tests_macros" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index 158310cd..1fcc7e08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,10 +22,12 @@ inherits = "release" [workspace.dependencies] air_formatter_test = { path = "./crates/air_formatter_test" } +air_fs = { path = "./crates/air_fs" } air_r_factory = { path = "./crates/air_r_factory" } air_r_formatter = { path = "./crates/air_r_formatter" } air_r_parser = { path = "./crates/air_r_parser" } air_r_syntax = { path = "./crates/air_r_syntax" } +air_workspace = { path = "./crates/air_workspace" } biome_ungrammar = { path = "./crates/biome_ungrammar" } line_ending = { path = "./crates/line_ending" } lsp = { path = "./crates/lsp" } @@ -50,11 +52,13 @@ dissimilar = "1.0.9" futures = "0.3.31" futures-util = "0.3.31" httparse = "1.9.5" +ignore = "0.4.23" insta = "1.40.0" itertools = "0.13.0" line-index = "0.1.2" log = "0.4.22" memchr = "2.7.4" +path-absolutize = "3.1.1" proc-macro2 = "1.0.86" serde = { version = "1.0.215", features = ["derive"] } serde_json = "1.0.132" diff --git a/crates/air/Cargo.toml b/crates/air/Cargo.toml index 042f4c2e..a000a1e5 100644 --- a/crates/air/Cargo.toml +++ b/crates/air/Cargo.toml @@ -12,14 +12,22 @@ repository.workspace = true rust-version.workspace = true [dependencies] +air_fs = { workspace = true } air_r_formatter = { workspace = true } air_r_parser = { workspace = true } +air_workspace = { workspace = true } anyhow = { workspace = true } +biome_console = { workspace = true } +biome_diagnostics = { workspace = true } biome_formatter = { workspace = true } -clap = { workspace = true } +biome_parser = { workspace = true } +clap = { workspace = true, features = ["wrap_help"] } +ignore = { workspace = true } +itertools = { workspace = true } line_ending = { workspace = true } lsp = { workspace = true } tokio = "1.41.1" +tracing = { workspace = true } [dev-dependencies] tempfile = "3.9.0" diff --git a/crates/air/src/args.rs b/crates/air/src/args.rs index c65c5831..0d8ef2a9 100644 --- a/crates/air/src/args.rs +++ b/crates/air/src/args.rs @@ -20,7 +20,7 @@ pub(crate) enum Command { /// Start a language server Lsp(LspCommand), - /// Format a file + /// Format a set of files or directories Format(FormatCommand), } @@ -28,7 +28,14 @@ pub(crate) enum Command { pub(crate) struct LspCommand {} #[derive(Clone, Debug, Parser)] +#[command(arg_required_else_help(true))] pub(crate) struct FormatCommand { - /// The file to format - pub file: PathBuf, + /// The files or directories to format + pub paths: Vec, + + /// If enabled, format results are not written back to the file. Instead, + /// exit with a non-zero status code if any files would have been modified, + /// and zero otherwise. + #[arg(long)] + pub check: bool, } diff --git a/crates/air/src/commands/format.rs b/crates/air/src/commands/format.rs index 165e09ae..ecb9002b 100644 --- a/crates/air/src/commands/format.rs +++ b/crates/air/src/commands/format.rs @@ -1,64 +1,250 @@ +use std::fmt::Display; +use std::fmt::Formatter; +use std::io; +use std::io::stdout; +use std::io::Write; use std::path::PathBuf; use air_r_formatter::context::RFormatOptions; -use air_r_parser::RParserOptions; +use air_workspace::format::format_source; +use air_workspace::format::FormatSourceError; +use air_workspace::format::FormattedSource; +use ignore::DirEntry; +use itertools::Either; +use itertools::Itertools; use line_ending::LineEnding; use crate::args::FormatCommand; use crate::ExitStatus; pub(crate) fn format(command: FormatCommand) -> anyhow::Result { - format_file(&command.file) + let mode = FormatMode::from_command(&command); + let paths = resolve_paths(&command.paths); + + let (results, errors): (Vec<_>, Vec<_>) = paths + .into_iter() + .map(|path| match path { + Ok(path) => format_file(path, mode), + Err(err) => Err(FormatCommandError::Ignore(err)), + }) + .partition_map(|result| match result { + Ok(result) => Either::Left(result), + Err(err) => Either::Right(err), + }); + + for error in &errors { + tracing::error!("{error}"); + } + + match mode { + FormatMode::Write => {} + FormatMode::Check => { + write_changed(&results, &mut stdout().lock())?; + } + } + + match mode { + FormatMode::Write => { + if errors.is_empty() { + Ok(ExitStatus::Success) + } else { + Ok(ExitStatus::Error) + } + } + FormatMode::Check => { + if errors.is_empty() { + let any_would_format = results + .iter() + .any(|result| matches!(result, FormatFileResult::Formatted(_))); + + if any_would_format { + Ok(ExitStatus::Failure) + } else { + Ok(ExitStatus::Success) + } + } else { + Ok(ExitStatus::Error) + } + } + } +} + +#[derive(Copy, Clone, Debug)] +pub enum FormatMode { + Write, + Check, } -// TODO: Should you exit after the first failure? Probably not, probably power through -// and elegantly report failures at the end? Since you've formatted stuff up to -// that point already anyways. -// TODO: Hook this up to a command -// TODO: Ignore anything but R files, of course -fn _format_dir(path: &PathBuf) -> anyhow::Result { - let iter = std::fs::read_dir(path)?; - - for file in iter { - let Ok(file) = file else { - continue; - }; - format_file(&file.path())?; +impl FormatMode { + fn from_command(command: &FormatCommand) -> Self { + if command.check { + FormatMode::Check + } else { + FormatMode::Write + } + } +} + +fn write_changed(results: &[FormatFileResult], f: &mut impl Write) -> io::Result<()> { + for path in results + .iter() + .filter_map(|result| match result { + FormatFileResult::Formatted(path) => Some(path), + FormatFileResult::Unchanged => None, + }) + .sorted_unstable() + { + writeln!(f, "Would reformat: {}", path.display())?; } - Ok(ExitStatus::Success) + Ok(()) } -fn format_file(path: &PathBuf) -> anyhow::Result { - let contents = std::fs::read_to_string(path)?; +fn resolve_paths(paths: &[PathBuf]) -> Vec> { + let paths: Vec = paths.iter().map(air_fs::normalize_path).collect(); - let line_ending = line_ending::infer(&contents); + let (first_path, paths) = paths + .split_first() + .expect("Clap should ensure at least 1 path is supplied."); - // Normalize to Unix line endings - let contents = match line_ending { - LineEnding::Lf => contents, - LineEnding::Crlf => line_ending::normalize(contents), - }; + // TODO: Parallel directory visitor + let mut builder = ignore::WalkBuilder::new(first_path); - let parser_options = RParserOptions::default(); - let parsed = air_r_parser::parse(contents.as_str(), parser_options); + for path in paths { + builder.add(path); + } - if parsed.has_errors() { - return Ok(ExitStatus::Error); + let mut out = Vec::new(); + + for path in builder.build() { + match path { + Ok(entry) => { + if let Some(path) = judge_entry(entry) { + out.push(Ok(path)); + } + } + Err(err) => { + out.push(Err(err)); + } + } } - // TODO: Respect user specified `LineEnding` option too, not just inferred line endings - let line_ending = match line_ending { + out +} + +// Decide whether or not to accept an `entry` based on include/exclude rules. +// Non-R files are filtered out later on, this blindly accepts those. +fn judge_entry(entry: DirEntry) -> Option { + // Ignore directories + if entry.file_type().map_or(true, |ft| ft.is_dir()) { + return None; + } + + // Accept all files that are passed-in directly as long as it is an R file + if entry.depth() == 0 { + let path = entry.into_path(); + + if air_fs::has_r_extension(&path) { + return Some(path); + } else { + return None; + } + } + + // Otherwise check if we should accept this entry + // TODO: Many other checks based on user exclude/includes + let path = entry.into_path(); + + if !air_fs::has_r_extension(&path) { + return None; + } + + Some(path) +} + +pub(crate) enum FormatFileResult { + Formatted(PathBuf), + Unchanged, +} + +// TODO: Take workspace `FormatOptions` that get resolved to `RFormatOptions` +// for the formatter here. Respect user specified `LineEnding` option too, and +// only use inferred endings when `FormatOptions::LineEnding::Auto` is used. +fn format_file(path: PathBuf, mode: FormatMode) -> Result { + let source = std::fs::read_to_string(&path) + .map_err(|err| FormatCommandError::Read(path.clone(), err))?; + + let line_ending = match line_ending::infer(&source) { LineEnding::Lf => biome_formatter::LineEnding::Lf, LineEnding::Crlf => biome_formatter::LineEnding::Crlf, }; + let options = RFormatOptions::default().with_line_ending(line_ending); - let formatter_options = RFormatOptions::default().with_line_ending(line_ending); - let formatted = air_r_formatter::format_node(formatter_options, &parsed.syntax())?; - let result = formatted.print()?; - let code = result.as_code(); + let source = line_ending::normalize(source); + let formatted = match format_source(source.as_str(), options) { + Ok(formatted) => formatted, + Err(err) => return Err(FormatCommandError::Format(path.clone(), err)), + }; - std::fs::write(path, code)?; + // TODO: We rarely ever take advantage of this optimization on Windows right + // now. We always normalize on entry but we apply the requested line ending + // on exit (so on Windows we often infer CRLF on entry and normalize to + // LF, but apply CRLF on exit so `source` and `new` always have different + // line endings). We probably need to compare pre-normalized against + // post-formatted output? + match formatted { + FormattedSource::Formatted(new) => { + match mode { + FormatMode::Write => { + std::fs::write(&path, new) + .map_err(|err| FormatCommandError::Write(path.clone(), err))?; + } + FormatMode::Check => {} + } + Ok(FormatFileResult::Formatted(path)) + } + FormattedSource::Unchanged => Ok(FormatFileResult::Unchanged), + } +} + +pub(crate) enum FormatCommandError { + Ignore(ignore::Error), + Format(PathBuf, FormatSourceError), + Read(PathBuf, io::Error), + Write(PathBuf, io::Error), +} - Ok(ExitStatus::Success) +impl Display for FormatCommandError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Self::Ignore(err) => { + if let ignore::Error::WithPath { path, .. } = err { + write!( + f, + "Failed to format {}: {}", + path.display(), + err.io_error() + .map_or_else(|| err.to_string(), std::string::ToString::to_string) + ) + } else { + write!( + f, + "Encountered error: {error}", + error = err + .io_error() + .map_or_else(|| err.to_string(), std::string::ToString::to_string) + ) + } + } + Self::Read(path, err) => { + write!(f, "Failed to read {}: {err}", path.display()) + } + Self::Write(path, err) => { + write!(f, "Failed to write {}: {err}", path.display()) + } + Self::Format(path, err) => { + write!(f, "Failed to format {}: {err}", path.display()) + } + } + } } diff --git a/crates/air_fs/Cargo.toml b/crates/air_fs/Cargo.toml new file mode 100644 index 00000000..cd892e72 --- /dev/null +++ b/crates/air_fs/Cargo.toml @@ -0,0 +1,20 @@ + +[package] +authors.workspace = true +categories.workspace = true +description = "Air filesystem utilities" +edition.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +name = "air_fs" +publish = false +repository.workspace = true +rust-version.workspace = true +version = "0.0.0" + +[dependencies] +path-absolutize = { workspace = true } + +[lints] +workspace = true diff --git a/crates/air_fs/src/lib.rs b/crates/air_fs/src/lib.rs new file mode 100644 index 00000000..a0d220cb --- /dev/null +++ b/crates/air_fs/src/lib.rs @@ -0,0 +1,25 @@ +use path_absolutize::Absolutize; +use std::ffi::OsStr; +use std::path::Path; +use std::path::PathBuf; + +pub fn has_r_extension(path: &Path) -> bool { + path.extension() + .and_then(OsStr::to_str) + .map_or(false, is_r_extension) +} + +pub fn is_r_extension(extension: &str) -> bool { + matches!(extension, "r" | "R") +} + +/// Convert any path to an absolute path (based on the current working +/// directory). +pub fn normalize_path>(path: P) -> PathBuf { + let path = path.as_ref(); + if let Ok(path) = path.absolutize() { + path.to_path_buf() + } else { + path.to_path_buf() + } +} diff --git a/crates/air_workspace/Cargo.toml b/crates/air_workspace/Cargo.toml new file mode 100644 index 00000000..7d1f1913 --- /dev/null +++ b/crates/air_workspace/Cargo.toml @@ -0,0 +1,26 @@ + +[package] +authors.workspace = true +categories.workspace = true +description = "Air workspace utilities" +edition.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +name = "air_workspace" +publish = false +repository.workspace = true +rust-version.workspace = true +version = "0.0.0" + +[dependencies] +air_r_parser = { workspace = true } +air_r_formatter = { workspace = true } +biome_console = { workspace = true } +biome_diagnostics = { workspace = true } +biome_formatter = { workspace = true } +biome_parser = { workspace = true } +biome_rowan = { workspace = true } + +[lints] +workspace = true diff --git a/crates/air_workspace/src/format.rs b/crates/air_workspace/src/format.rs new file mode 100644 index 00000000..79b3c499 --- /dev/null +++ b/crates/air_workspace/src/format.rs @@ -0,0 +1,77 @@ +use std::fmt::Display; +use std::fmt::Formatter; + +use air_r_formatter::context::RFormatOptions; +use air_r_parser::RParserOptions; +use biome_diagnostics::Diagnostic; +use biome_formatter::FormatError; +use biome_formatter::PrintError; +use biome_parser::prelude::ParseDiagnostic; + +#[derive(Debug)] +pub enum FormattedSource { + /// The source was formatted, and the [`String`] contains the transformed source code. + Formatted(String), + /// The source was unchanged. + Unchanged, +} + +#[derive(Debug, Diagnostic)] +pub enum FormatSourceError { + Parse(ParseDiagnostic), + Format(FormatError), + Print(PrintError), +} + +impl Display for FormatSourceError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + FormatSourceError::Parse(err) => err.description(f), + FormatSourceError::Format(err) => err.description(f), + FormatSourceError::Print(err) => err.description(f), + } + } +} + +impl From for FormatSourceError { + fn from(value: ParseDiagnostic) -> Self { + FormatSourceError::Parse(value) + } +} + +impl From for FormatSourceError { + fn from(value: FormatError) -> Self { + FormatSourceError::Format(value) + } +} + +impl From for FormatSourceError { + fn from(value: PrintError) -> Self { + FormatSourceError::Print(value) + } +} + +/// Formats a vector of `source` code +/// +/// Safety: `source` should already be normalized to Unix line endings +pub fn format_source( + source: &str, + options: RFormatOptions, +) -> std::result::Result { + let parsed = air_r_parser::parse(source, RParserOptions::default()); + + if parsed.has_errors() { + let diagnostic = parsed.into_diagnostics().into_iter().next().unwrap(); + return Err(diagnostic.into()); + } + + let formatted = air_r_formatter::format_node(options, &parsed.syntax())?; + let formatted = formatted.print()?; + let formatted = formatted.into_code(); + + if source.len() == formatted.len() && source == formatted.as_str() { + Ok(FormattedSource::Unchanged) + } else { + Ok(FormattedSource::Formatted(formatted)) + } +} diff --git a/crates/air_workspace/src/lib.rs b/crates/air_workspace/src/lib.rs new file mode 100644 index 00000000..db7b59d9 --- /dev/null +++ b/crates/air_workspace/src/lib.rs @@ -0,0 +1 @@ +pub mod format; From 41334cc78b6ffad141dc9a81587aedf368bc627a Mon Sep 17 00:00:00 2001 From: Davis Vaughan Date: Sat, 7 Dec 2024 22:18:00 -0500 Subject: [PATCH 04/16] Wrap `ParseDiagnostic` in our `ParseError` --- crates/air_r_parser/Cargo.toml | 1 + crates/air_r_parser/src/error.rs | 35 +++++++++++++ crates/air_r_parser/src/lib.rs | 2 + crates/air_r_parser/src/parse.rs | 69 +++++++++++++++----------- crates/air_r_parser/tests/spec_test.rs | 14 ++++-- crates/air_workspace/src/format.rs | 22 ++++---- 6 files changed, 99 insertions(+), 44 deletions(-) create mode 100644 crates/air_r_parser/src/error.rs diff --git a/crates/air_r_parser/Cargo.toml b/crates/air_r_parser/Cargo.toml index e2c8f280..6c41d910 100644 --- a/crates/air_r_parser/Cargo.toml +++ b/crates/air_r_parser/Cargo.toml @@ -15,6 +15,7 @@ version = "0.0.0" [dependencies] air_r_factory = { workspace = true } air_r_syntax = { workspace = true } +biome_diagnostics = { workspace = true } biome_parser = { workspace = true } biome_rowan = { workspace = true } biome_unicode_table = { workspace = true } diff --git a/crates/air_r_parser/src/error.rs b/crates/air_r_parser/src/error.rs new file mode 100644 index 00000000..c15ee158 --- /dev/null +++ b/crates/air_r_parser/src/error.rs @@ -0,0 +1,35 @@ +use biome_diagnostics::Diagnostic; +use biome_parser::prelude::ParseDiagnostic; + +/// An error that occurs during parsing +/// +/// Simply wraps a `ParseDiagnostic`, mainly so we can implement +/// `std::error::Error` for it, which it oddly does not implement. +#[derive(Debug, Clone)] +pub struct ParseError { + inner: ParseDiagnostic, +} + +impl std::error::Error for ParseError {} + +impl std::fmt::Display for ParseError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + self.diagnostic().description(f) + } +} + +impl ParseError { + pub fn diagnostic(&self) -> &ParseDiagnostic { + &self.inner + } + + pub fn into_diagnostic(self) -> ParseDiagnostic { + self.inner + } +} + +impl From for ParseError { + fn from(diagnostic: ParseDiagnostic) -> Self { + Self { inner: diagnostic } + } +} diff --git a/crates/air_r_parser/src/lib.rs b/crates/air_r_parser/src/lib.rs index c5c65ef8..cf3c4acf 100644 --- a/crates/air_r_parser/src/lib.rs +++ b/crates/air_r_parser/src/lib.rs @@ -1,3 +1,4 @@ +mod error; mod options; mod parse; @@ -5,6 +6,7 @@ mod parse; mod treesitter; use air_r_factory::RSyntaxFactory; +pub use error::ParseError; pub use options::RParserOptions; pub use parse::parse; pub use parse::parse_r_with_cache; diff --git a/crates/air_r_parser/src/parse.rs b/crates/air_r_parser/src/parse.rs index b4a48f32..df668c43 100644 --- a/crates/air_r_parser/src/parse.rs +++ b/crates/air_r_parser/src/parse.rs @@ -19,6 +19,7 @@ use tree_sitter::Tree; use crate::treesitter::NodeTypeExt; use crate::treesitter::Preorder; use crate::treesitter::WalkEvent; +use crate::ParseError; use crate::RLosslessTreeSink; use crate::RParserOptions; @@ -26,22 +27,22 @@ use crate::RParserOptions; #[derive(Debug, Clone)] pub struct Parse { root: RSyntaxNode, - diagnostics: Vec, + errors: Vec, _ty: PhantomData, } impl Parse { - pub fn new(root: RSyntaxNode, diagnostics: Vec) -> Parse { + pub fn new(root: RSyntaxNode, errors: Vec) -> Parse { Parse { root, - diagnostics, + errors, _ty: PhantomData, } } pub fn cast>(self) -> Option> { if N::can_cast(self.syntax().kind()) { - Some(Parse::new(self.root, self.diagnostics)) + Some(Parse::new(self.root, self.errors)) } else { None } @@ -52,21 +53,19 @@ impl Parse { self.root.clone() } - /// Get the diagnostics which occurred when parsing - pub fn diagnostics(&self) -> &[ParseDiagnostic] { - self.diagnostics.as_slice() + /// Get the errors which occurred when parsing + pub fn errors(&self) -> &[ParseError] { + self.errors.as_slice() } - /// Get the diagnostics which occurred when parsing - pub fn into_diagnostics(self) -> Vec { - self.diagnostics + /// Get the errors which occurred when parsing + pub fn into_errors(self) -> Vec { + self.errors } /// Returns [true] if the parser encountered some errors during the parsing. pub fn has_errors(&self) -> bool { - self.diagnostics - .iter() - .any(|diagnostic| diagnostic.is_error()) + !self.errors.is_empty() } } @@ -91,11 +90,11 @@ impl> Parse { } /// Convert this parse into a result - pub fn into_result(self) -> Result> { + pub fn into_result(self) -> Result> { if !self.has_errors() { Ok(self.tree()) } else { - Err(self.diagnostics) + Err(self.errors) } } } @@ -103,7 +102,11 @@ impl> Parse { impl From> for AnyParse { fn from(parse: Parse) -> Self { let root = parse.syntax(); - let diagnostics = parse.into_diagnostics(); + let errors = parse.into_errors(); + let diagnostics = errors + .into_iter() + .map(ParseError::into_diagnostic) + .collect(); Self::new( // SAFETY: the parser should always return a root node root.as_send().unwrap(), @@ -123,18 +126,26 @@ pub fn parse_r_with_cache( cache: &mut NodeCache, ) -> Parse { tracing::debug_span!("parse").in_scope(move || { - let (events, tokens, diagnostics) = parse_text(text, options); + let (events, tokens, errors) = parse_text(text, options); + + // We've determined that passing diagnostics through does nothing. + // They go into the tree-sink but come right back out. We think they + // are a holdover from rust-analyzer that can be removed now. The real + // errors are in `errors`. + let _diagnostics = vec![]; + let mut tree_sink = RLosslessTreeSink::with_cache(text, &tokens, cache); - biome_parser::event::process(&mut tree_sink, events, diagnostics); - let (green, diagnostics) = tree_sink.finish(); - Parse::new(green, diagnostics) + biome_parser::event::process(&mut tree_sink, events, _diagnostics); + let (green, _diagnostics) = tree_sink.finish(); + + Parse::new(green, errors) }) } pub fn parse_text( text: &str, _options: RParserOptions, -) -> (Vec>, Vec, Vec) { +) -> (Vec>, Vec, Vec) { let mut parser = tree_sitter::Parser::new(); parser .set_language(&tree_sitter_r::LANGUAGE.into()) @@ -152,7 +163,7 @@ pub fn parse_text( parse_tree(ast, text) } -fn parse_failure() -> (Vec>, Vec, Vec) { +fn parse_failure() -> (Vec>, Vec, Vec) { // Must provide a root node on failures, otherwise `tree_sink.finish()` fails let events = vec![ Event::Start { @@ -165,18 +176,16 @@ fn parse_failure() -> (Vec>, Vec, Vec = None; - let error = ParseDiagnostic::new("Tree-sitter failed", span); + let diagnostic = ParseDiagnostic::new("Failed to parse", span); + let error = ParseError::from(diagnostic); let errors = vec![error]; (events, trivia, errors) } -fn parse_tree( - ast: Tree, - text: &str, -) -> (Vec>, Vec, Vec) { +fn parse_tree(ast: Tree, text: &str) -> (Vec>, Vec, Vec) { let mut walker = RWalk::new(text); let root = ast.root_node(); @@ -1056,7 +1065,7 @@ impl<'src> RWalk<'src> { struct RParse { events: Vec>, trivia: Vec, - errors: Vec, + errors: Vec, } impl RParse { @@ -1091,7 +1100,7 @@ impl RParse { self.trivia.push(trivia); } - fn drain(self) -> (Vec>, Vec, Vec) { + fn drain(self) -> (Vec>, Vec, Vec) { (self.events, self.trivia, self.errors) } diff --git a/crates/air_r_parser/tests/spec_test.rs b/crates/air_r_parser/tests/spec_test.rs index fa034fdc..d22860a9 100644 --- a/crates/air_r_parser/tests/spec_test.rs +++ b/crates/air_r_parser/tests/spec_test.rs @@ -1,3 +1,4 @@ +use air_r_parser::ParseError; use air_r_parser::{parse, RParserOptions}; use air_r_syntax::{RRoot, RSyntaxNode}; use biome_console::fmt::{Formatter, Termcolor}; @@ -5,6 +6,7 @@ use biome_console::markup; use biome_diagnostics::display::PrintDiagnostic; use biome_diagnostics::termcolor; use biome_diagnostics::DiagnosticExt; +use biome_parser::prelude::ParseDiagnostic; use biome_rowan::SyntaxNode; use biome_rowan::SyntaxSlot; use biome_rowan::{AstNode, SyntaxKind}; @@ -79,8 +81,14 @@ pub fn run(test_case: &str, _snapshot_name: &str, test_directory: &str, outcome_ ) .unwrap(); - let diagnostics = parsed.diagnostics(); - if !diagnostics.is_empty() { + if parsed.has_errors() { + let diagnostics: Vec = parsed + .errors() + .iter() + .map(ParseError::diagnostic) + .map(ParseDiagnostic::clone) + .collect(); + let mut diagnostics_buffer = termcolor::Buffer::no_color(); let termcolor = &mut Termcolor(&mut diagnostics_buffer); @@ -130,7 +138,7 @@ pub fn run(test_case: &str, _snapshot_name: &str, test_directory: &str, outcome_ } } ExpectedOutcome::Fail => { - if parsed.diagnostics().is_empty() { + if !parsed.has_errors() { panic!("Failing test must have diagnostics"); } } diff --git a/crates/air_workspace/src/format.rs b/crates/air_workspace/src/format.rs index 79b3c499..9280d4d8 100644 --- a/crates/air_workspace/src/format.rs +++ b/crates/air_workspace/src/format.rs @@ -1,12 +1,12 @@ +use std::fmt::Debug; use std::fmt::Display; use std::fmt::Formatter; use air_r_formatter::context::RFormatOptions; +use air_r_parser::ParseError; use air_r_parser::RParserOptions; -use biome_diagnostics::Diagnostic; use biome_formatter::FormatError; use biome_formatter::PrintError; -use biome_parser::prelude::ParseDiagnostic; #[derive(Debug)] pub enum FormattedSource { @@ -16,9 +16,9 @@ pub enum FormattedSource { Unchanged, } -#[derive(Debug, Diagnostic)] +#[derive(Debug)] pub enum FormatSourceError { - Parse(ParseDiagnostic), + Parse(ParseError), Format(FormatError), Print(PrintError), } @@ -26,15 +26,15 @@ pub enum FormatSourceError { impl Display for FormatSourceError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - FormatSourceError::Parse(err) => err.description(f), - FormatSourceError::Format(err) => err.description(f), - FormatSourceError::Print(err) => err.description(f), + FormatSourceError::Parse(err) => std::fmt::Display::fmt(err, f), + FormatSourceError::Format(err) => std::fmt::Display::fmt(err, f), + FormatSourceError::Print(err) => std::fmt::Display::fmt(err, f), } } } -impl From for FormatSourceError { - fn from(value: ParseDiagnostic) -> Self { +impl From for FormatSourceError { + fn from(value: ParseError) -> Self { FormatSourceError::Parse(value) } } @@ -61,8 +61,8 @@ pub fn format_source( let parsed = air_r_parser::parse(source, RParserOptions::default()); if parsed.has_errors() { - let diagnostic = parsed.into_diagnostics().into_iter().next().unwrap(); - return Err(diagnostic.into()); + let error = parsed.into_errors().into_iter().next().unwrap(); + return Err(error.into()); } let formatted = air_r_formatter::format_node(options, &parsed.syntax())?; From 20d478a5642eae291ca7943a5edfa5f7872e29f5 Mon Sep 17 00:00:00 2001 From: Davis Vaughan Date: Sat, 7 Dec 2024 22:29:50 -0500 Subject: [PATCH 05/16] Clean up a bit with thiserror! --- Cargo.lock | 82 +++++++++++++++++++----------- Cargo.toml | 1 + crates/air/Cargo.toml | 1 + crates/air/src/commands/format.rs | 6 ++- crates/air_workspace/Cargo.toml | 1 + crates/air_workspace/src/format.rs | 42 +++------------ 6 files changed, 67 insertions(+), 66 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7a53d551..117468e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,6 +45,7 @@ dependencies = [ "line_ending", "lsp", "tempfile", + "thiserror 2.0.5", "tokio", "tracing", ] @@ -136,6 +137,7 @@ dependencies = [ "biome_formatter", "biome_parser", "biome_rowan", + "thiserror 2.0.5", ] [[package]] @@ -207,7 +209,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -218,7 +220,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -498,7 +500,7 @@ checksum = "cf95d9c7e6aba67f8fc07761091e93254677f4db9e27197adecebc7039a58722" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -573,7 +575,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -706,7 +708,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -762,7 +764,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -862,7 +864,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -1094,7 +1096,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -1404,7 +1406,7 @@ dependencies = [ "serde", "serde_json", "simdutf8", - "thiserror", + "thiserror 1.0.65", "tracing", ] @@ -1472,7 +1474,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -1519,9 +1521,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -1689,7 +1691,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -1715,7 +1717,7 @@ checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -1726,7 +1728,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -1761,7 +1763,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -1865,7 +1867,7 @@ checksum = "a2dbf8b57f3ce20e4bb171a11822b283bdfab6c4bb0fe64fa729f045f23a0938" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -1887,9 +1889,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.85" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -1904,7 +1906,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -1949,7 +1951,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -1958,7 +1960,16 @@ version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.65", +] + +[[package]] +name = "thiserror" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643caef17e3128658ff44d85923ef2d28af81bb71e0d67bbfe1d76f19a73e053" +dependencies = [ + "thiserror-impl 2.0.5", ] [[package]] @@ -1969,7 +1980,18 @@ checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "995d0bbc9995d1f19d28b7215a9352b0fc3cd3a2d2ec95c2cadc485cdedbcdde" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", ] [[package]] @@ -2008,7 +2030,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -2097,7 +2119,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -2107,7 +2129,7 @@ source = "git+https://github.com/lionel-/tower-lsp?branch=bugfix%2Fpatches#49ef5 dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -2135,7 +2157,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -2458,7 +2480,7 @@ checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", "synstructure", ] @@ -2479,7 +2501,7 @@ checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", "synstructure", ] @@ -2508,5 +2530,5 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] diff --git a/Cargo.toml b/Cargo.toml index 1fcc7e08..a33afecd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,6 +63,7 @@ proc-macro2 = "1.0.86" serde = { version = "1.0.215", features = ["derive"] } serde_json = "1.0.132" struct-field-names-as-array = "0.3.0" +thiserror = "2.0.5" tokio = { version = "1.41.1" } tokio-util = "0.7.12" # For https://github.com/ebkalderon/tower-lsp/pull/428 diff --git a/crates/air/Cargo.toml b/crates/air/Cargo.toml index a000a1e5..b0cd979f 100644 --- a/crates/air/Cargo.toml +++ b/crates/air/Cargo.toml @@ -26,6 +26,7 @@ ignore = { workspace = true } itertools = { workspace = true } line_ending = { workspace = true } lsp = { workspace = true } +thiserror = { workspace = true } tokio = "1.41.1" tracing = { workspace = true } diff --git a/crates/air/src/commands/format.rs b/crates/air/src/commands/format.rs index ecb9002b..0adf96b2 100644 --- a/crates/air/src/commands/format.rs +++ b/crates/air/src/commands/format.rs @@ -13,6 +13,7 @@ use ignore::DirEntry; use itertools::Either; use itertools::Itertools; use line_ending::LineEnding; +use thiserror::Error; use crate::args::FormatCommand; use crate::ExitStatus; @@ -25,7 +26,7 @@ pub(crate) fn format(command: FormatCommand) -> anyhow::Result { .into_iter() .map(|path| match path { Ok(path) => format_file(path, mode), - Err(err) => Err(FormatCommandError::Ignore(err)), + Err(err) => Err(err.into()), }) .partition_map(|result| match result { Ok(result) => Either::Left(result), @@ -207,8 +208,9 @@ fn format_file(path: PathBuf, mode: FormatMode) -> Result) -> std::fmt::Result { - match self { - FormatSourceError::Parse(err) => std::fmt::Display::fmt(err, f), - FormatSourceError::Format(err) => std::fmt::Display::fmt(err, f), - FormatSourceError::Print(err) => std::fmt::Display::fmt(err, f), - } - } -} - -impl From for FormatSourceError { - fn from(value: ParseError) -> Self { - FormatSourceError::Parse(value) - } -} - -impl From for FormatSourceError { - fn from(value: FormatError) -> Self { - FormatSourceError::Format(value) - } -} - -impl From for FormatSourceError { - fn from(value: PrintError) -> Self { - FormatSourceError::Print(value) - } + #[error(transparent)] + Parse(#[from] ParseError), + #[error(transparent)] + Format(#[from] FormatError), + #[error(transparent)] + Print(#[from] PrintError), } /// Formats a vector of `source` code From b5d516dc3a95438841e3aaaa2205a7bb2d741349 Mon Sep 17 00:00:00 2001 From: Davis Vaughan Date: Sat, 7 Dec 2024 22:38:10 -0500 Subject: [PATCH 06/16] Add `relativize_path()` --- crates/air/src/commands/format.rs | 30 ++++++++++++++++++++++-------- crates/air_fs/Cargo.toml | 5 ++++- crates/air_fs/src/lib.rs | 15 +++++++++++++++ 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/crates/air/src/commands/format.rs b/crates/air/src/commands/format.rs index 0adf96b2..8e194db5 100644 --- a/crates/air/src/commands/format.rs +++ b/crates/air/src/commands/format.rs @@ -5,6 +5,7 @@ use std::io::stdout; use std::io::Write; use std::path::PathBuf; +use air_fs::relativize_path; use air_r_formatter::context::RFormatOptions; use air_workspace::format::format_source; use air_workspace::format::FormatSourceError; @@ -223,29 +224,42 @@ impl Display for FormatCommandError { if let ignore::Error::WithPath { path, .. } = err { write!( f, - "Failed to format {}: {}", - path.display(), - err.io_error() + "Failed to format {path}: {err}", + path = relativize_path(path), + err = err + .io_error() .map_or_else(|| err.to_string(), std::string::ToString::to_string) ) } else { write!( f, - "Encountered error: {error}", - error = err + "Encountered error: {err}", + err = err .io_error() .map_or_else(|| err.to_string(), std::string::ToString::to_string) ) } } Self::Read(path, err) => { - write!(f, "Failed to read {}: {err}", path.display()) + write!( + f, + "Failed to read {path}: {err}", + path = relativize_path(path), + ) } Self::Write(path, err) => { - write!(f, "Failed to write {}: {err}", path.display()) + write!( + f, + "Failed to write {path}: {err}", + path = relativize_path(path), + ) } Self::Format(path, err) => { - write!(f, "Failed to format {}: {err}", path.display()) + write!( + f, + "Failed to format {path}: {err}", + path = relativize_path(path), + ) } } } diff --git a/crates/air_fs/Cargo.toml b/crates/air_fs/Cargo.toml index cd892e72..33cfde3c 100644 --- a/crates/air_fs/Cargo.toml +++ b/crates/air_fs/Cargo.toml @@ -14,7 +14,10 @@ rust-version.workspace = true version = "0.0.0" [dependencies] -path-absolutize = { workspace = true } +path-absolutize = { workspace = true, features = [ + "once_cell_cache", + "use_unix_paths_on_wasm", +] } [lints] workspace = true diff --git a/crates/air_fs/src/lib.rs b/crates/air_fs/src/lib.rs index a0d220cb..3467597a 100644 --- a/crates/air_fs/src/lib.rs +++ b/crates/air_fs/src/lib.rs @@ -23,3 +23,18 @@ pub fn normalize_path>(path: P) -> PathBuf { path.to_path_buf() } } + +/// Convert an absolute path to be relative to the current working directory. +pub fn relativize_path>(path: P) -> String { + let path = path.as_ref(); + + #[cfg(target_arch = "wasm32")] + let cwd = Path::new("."); + #[cfg(not(target_arch = "wasm32"))] + let cwd = path_absolutize::path_dedot::CWD.as_path(); + + if let Ok(path) = path.strip_prefix(cwd) { + return format!("{}", path.display()); + } + format!("{}", path.display()) +} From d4b893b64087bfc4441ccfab02c97b600f54589e Mon Sep 17 00:00:00 2001 From: Davis Vaughan Date: Sat, 7 Dec 2024 22:40:45 -0500 Subject: [PATCH 07/16] Add a comment about tracing --- crates/air/src/commands/format.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/air/src/commands/format.rs b/crates/air/src/commands/format.rs index 8e194db5..7319a5ec 100644 --- a/crates/air/src/commands/format.rs +++ b/crates/air/src/commands/format.rs @@ -35,6 +35,7 @@ pub(crate) fn format(command: FormatCommand) -> anyhow::Result { }); for error in &errors { + // TODO: Hook up a tracing subscriber! tracing::error!("{error}"); } From ed52e1dc2ae302f9d5435f48c31e9b47dc5cfbe6 Mon Sep 17 00:00:00 2001 From: Davis Vaughan Date: Mon, 9 Dec 2024 11:32:53 -0500 Subject: [PATCH 08/16] Remove `air_workspace` for now for simplicity LSP needs `format_node()` since it holds onto the AST, so this isn't as useful yet over there --- Cargo.lock | 15 --------- Cargo.toml | 1 - crates/air/Cargo.toml | 1 - crates/air/src/commands/format.rs | 47 +++++++++++++++++++++++++-- crates/air_workspace/Cargo.toml | 27 ---------------- crates/air_workspace/src/format.rs | 51 ------------------------------ crates/air_workspace/src/lib.rs | 1 - 7 files changed, 44 insertions(+), 99 deletions(-) delete mode 100644 crates/air_workspace/Cargo.toml delete mode 100644 crates/air_workspace/src/format.rs delete mode 100644 crates/air_workspace/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 117468e7..c26ae56c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,7 +33,6 @@ dependencies = [ "air_fs", "air_r_formatter", "air_r_parser", - "air_workspace", "anyhow", "biome_console", "biome_diagnostics", @@ -126,20 +125,6 @@ dependencies = [ "serde", ] -[[package]] -name = "air_workspace" -version = "0.0.0" -dependencies = [ - "air_r_formatter", - "air_r_parser", - "biome_console", - "biome_diagnostics", - "biome_formatter", - "biome_parser", - "biome_rowan", - "thiserror 2.0.5", -] - [[package]] name = "anstream" version = "0.6.17" diff --git a/Cargo.toml b/Cargo.toml index a33afecd..2d9d65cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,6 @@ air_r_factory = { path = "./crates/air_r_factory" } air_r_formatter = { path = "./crates/air_r_formatter" } air_r_parser = { path = "./crates/air_r_parser" } air_r_syntax = { path = "./crates/air_r_syntax" } -air_workspace = { path = "./crates/air_workspace" } biome_ungrammar = { path = "./crates/biome_ungrammar" } line_ending = { path = "./crates/line_ending" } lsp = { path = "./crates/lsp" } diff --git a/crates/air/Cargo.toml b/crates/air/Cargo.toml index b0cd979f..74441f37 100644 --- a/crates/air/Cargo.toml +++ b/crates/air/Cargo.toml @@ -15,7 +15,6 @@ rust-version.workspace = true air_fs = { workspace = true } air_r_formatter = { workspace = true } air_r_parser = { workspace = true } -air_workspace = { workspace = true } anyhow = { workspace = true } biome_console = { workspace = true } biome_diagnostics = { workspace = true } diff --git a/crates/air/src/commands/format.rs b/crates/air/src/commands/format.rs index 7319a5ec..63b3393f 100644 --- a/crates/air/src/commands/format.rs +++ b/crates/air/src/commands/format.rs @@ -7,9 +7,7 @@ use std::path::PathBuf; use air_fs::relativize_path; use air_r_formatter::context::RFormatOptions; -use air_workspace::format::format_source; -use air_workspace::format::FormatSourceError; -use air_workspace::format::FormattedSource; +use air_r_parser::RParserOptions; use ignore::DirEntry; use itertools::Either; use itertools::Itertools; @@ -210,6 +208,49 @@ fn format_file(path: PathBuf, mode: FormatMode) -> Result std::result::Result { + let parsed = air_r_parser::parse(source, RParserOptions::default()); + + if parsed.has_errors() { + let error = parsed.into_errors().into_iter().next().unwrap(); + return Err(error.into()); + } + + let formatted = air_r_formatter::format_node(options, &parsed.syntax())?; + let formatted = formatted.print()?; + let formatted = formatted.into_code(); + + if source.len() == formatted.len() && source == formatted.as_str() { + Ok(FormattedSource::Unchanged) + } else { + Ok(FormattedSource::Formatted(formatted)) + } +} + #[derive(Error, Debug)] pub(crate) enum FormatCommandError { Ignore(#[from] ignore::Error), diff --git a/crates/air_workspace/Cargo.toml b/crates/air_workspace/Cargo.toml deleted file mode 100644 index b6e9d658..00000000 --- a/crates/air_workspace/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ - -[package] -authors.workspace = true -categories.workspace = true -description = "Air workspace utilities" -edition.workspace = true -homepage.workspace = true -keywords.workspace = true -license.workspace = true -name = "air_workspace" -publish = false -repository.workspace = true -rust-version.workspace = true -version = "0.0.0" - -[dependencies] -air_r_parser = { workspace = true } -air_r_formatter = { workspace = true } -biome_console = { workspace = true } -biome_diagnostics = { workspace = true } -biome_formatter = { workspace = true } -biome_parser = { workspace = true } -biome_rowan = { workspace = true } -thiserror = { workspace = true } - -[lints] -workspace = true diff --git a/crates/air_workspace/src/format.rs b/crates/air_workspace/src/format.rs deleted file mode 100644 index 2b21303d..00000000 --- a/crates/air_workspace/src/format.rs +++ /dev/null @@ -1,51 +0,0 @@ -use std::fmt::Debug; - -use air_r_formatter::context::RFormatOptions; -use air_r_parser::ParseError; -use air_r_parser::RParserOptions; -use biome_formatter::FormatError; -use biome_formatter::PrintError; -use thiserror::Error; - -#[derive(Debug)] -pub enum FormattedSource { - /// The source was formatted, and the [`String`] contains the transformed source code. - Formatted(String), - /// The source was unchanged. - Unchanged, -} - -#[derive(Error, Debug)] -pub enum FormatSourceError { - #[error(transparent)] - Parse(#[from] ParseError), - #[error(transparent)] - Format(#[from] FormatError), - #[error(transparent)] - Print(#[from] PrintError), -} - -/// Formats a vector of `source` code -/// -/// Safety: `source` should already be normalized to Unix line endings -pub fn format_source( - source: &str, - options: RFormatOptions, -) -> std::result::Result { - let parsed = air_r_parser::parse(source, RParserOptions::default()); - - if parsed.has_errors() { - let error = parsed.into_errors().into_iter().next().unwrap(); - return Err(error.into()); - } - - let formatted = air_r_formatter::format_node(options, &parsed.syntax())?; - let formatted = formatted.print()?; - let formatted = formatted.into_code(); - - if source.len() == formatted.len() && source == formatted.as_str() { - Ok(FormattedSource::Unchanged) - } else { - Ok(FormattedSource::Formatted(formatted)) - } -} diff --git a/crates/air_workspace/src/lib.rs b/crates/air_workspace/src/lib.rs deleted file mode 100644 index db7b59d9..00000000 --- a/crates/air_workspace/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod format; From dbf0c41ae984f18d9599d3780abde181057ecded Mon Sep 17 00:00:00 2001 From: Davis Vaughan Date: Mon, 9 Dec 2024 11:37:41 -0500 Subject: [PATCH 09/16] Add `is_changed()` for ergonomics --- crates/air/src/commands/format.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/crates/air/src/commands/format.rs b/crates/air/src/commands/format.rs index 63b3393f..3aca7801 100644 --- a/crates/air/src/commands/format.rs +++ b/crates/air/src/commands/format.rs @@ -54,11 +54,9 @@ pub(crate) fn format(command: FormatCommand) -> anyhow::Result { } FormatMode::Check => { if errors.is_empty() { - let any_would_format = results - .iter() - .any(|result| matches!(result, FormatFileResult::Formatted(_))); + let any_changed = results.iter().any(FormatFileResult::is_changed); - if any_would_format { + if any_changed { Ok(ExitStatus::Failure) } else { Ok(ExitStatus::Success) @@ -168,6 +166,12 @@ pub(crate) enum FormatFileResult { Unchanged, } +impl FormatFileResult { + fn is_changed(&self) -> bool { + matches!(self, FormatFileResult::Formatted(_)) + } +} + // TODO: Take workspace `FormatOptions` that get resolved to `RFormatOptions` // for the formatter here. Respect user specified `LineEnding` option too, and // only use inferred endings when `FormatOptions::LineEnding::Auto` is used. From 08852e7de6b13dc092405e29c2449f3cb3aacdad Mon Sep 17 00:00:00 2001 From: Davis Vaughan Date: Mon, 9 Dec 2024 11:48:27 -0500 Subject: [PATCH 10/16] Remove outdated comment --- crates/air/src/commands/format.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/air/src/commands/format.rs b/crates/air/src/commands/format.rs index 3aca7801..977628f2 100644 --- a/crates/air/src/commands/format.rs +++ b/crates/air/src/commands/format.rs @@ -132,7 +132,6 @@ fn resolve_paths(paths: &[PathBuf]) -> Vec> { } // Decide whether or not to accept an `entry` based on include/exclude rules. -// Non-R files are filtered out later on, this blindly accepts those. fn judge_entry(entry: DirEntry) -> Option { // Ignore directories if entry.file_type().map_or(true, |ft| ft.is_dir()) { From 82d787ff0fe5b884198785ec74c9511b9ebc1551 Mon Sep 17 00:00:00 2001 From: Davis Vaughan Date: Tue, 10 Dec 2024 13:46:57 -0500 Subject: [PATCH 11/16] Use `u8::MAX` as our catch all error code For the "something has gone wrong" unexpected error case --- crates/air/src/status.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/air/src/status.rs b/crates/air/src/status.rs index 55109528..0f984d2f 100644 --- a/crates/air/src/status.rs +++ b/crates/air/src/status.rs @@ -1,4 +1,4 @@ -use std::process::ExitCode; +use std::{process::ExitCode, u8}; #[derive(Copy, Clone, PartialEq, Debug)] pub enum ExitStatus { @@ -15,7 +15,7 @@ impl From for ExitCode { match status { ExitStatus::Success => ExitCode::from(0), ExitStatus::Failure => ExitCode::from(1), - ExitStatus::Error => ExitCode::from(2), + ExitStatus::Error => ExitCode::from(u8::MAX), } } } From 0ae353f18fa14ae95ca98ecf3f4d0fadf48ac2b3 Mon Sep 17 00:00:00 2001 From: Davis Vaughan Date: Tue, 10 Dec 2024 13:47:44 -0500 Subject: [PATCH 12/16] Various tweaks from code review with @lionel- --- crates/air/src/commands/format.rs | 39 ++++++++++++++----------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/crates/air/src/commands/format.rs b/crates/air/src/commands/format.rs index 977628f2..51b5bd77 100644 --- a/crates/air/src/commands/format.rs +++ b/crates/air/src/commands/format.rs @@ -21,7 +21,7 @@ pub(crate) fn format(command: FormatCommand) -> anyhow::Result { let mode = FormatMode::from_command(&command); let paths = resolve_paths(&command.paths); - let (results, errors): (Vec<_>, Vec<_>) = paths + let (actions, errors): (Vec<_>, Vec<_>) = paths .into_iter() .map(|path| match path { Ok(path) => format_file(path, mode), @@ -40,7 +40,7 @@ pub(crate) fn format(command: FormatCommand) -> anyhow::Result { match mode { FormatMode::Write => {} FormatMode::Check => { - write_changed(&results, &mut stdout().lock())?; + write_changed(&actions, &mut stdout().lock())?; } } @@ -54,7 +54,7 @@ pub(crate) fn format(command: FormatCommand) -> anyhow::Result { } FormatMode::Check => { if errors.is_empty() { - let any_changed = results.iter().any(FormatFileResult::is_changed); + let any_changed = actions.iter().any(FormatFileAction::is_changed); if any_changed { Ok(ExitStatus::Failure) @@ -84,12 +84,12 @@ impl FormatMode { } } -fn write_changed(results: &[FormatFileResult], f: &mut impl Write) -> io::Result<()> { - for path in results +fn write_changed(actions: &[FormatFileAction], f: &mut impl Write) -> io::Result<()> { + for path in actions .iter() .filter_map(|result| match result { - FormatFileResult::Formatted(path) => Some(path), - FormatFileResult::Unchanged => None, + FormatFileAction::Formatted(path) => Some(path), + FormatFileAction::Unchanged => None, }) .sorted_unstable() { @@ -118,7 +118,7 @@ fn resolve_paths(paths: &[PathBuf]) -> Vec> { for path in builder.build() { match path { Ok(entry) => { - if let Some(path) = judge_entry(entry) { + if let Some(path) = is_valid_path(entry) { out.push(Ok(path)); } } @@ -132,21 +132,16 @@ fn resolve_paths(paths: &[PathBuf]) -> Vec> { } // Decide whether or not to accept an `entry` based on include/exclude rules. -fn judge_entry(entry: DirEntry) -> Option { +fn is_valid_path(entry: DirEntry) -> Option { // Ignore directories if entry.file_type().map_or(true, |ft| ft.is_dir()) { return None; } - // Accept all files that are passed-in directly as long as it is an R file + // Accept all files that are passed-in directly, even non-R files if entry.depth() == 0 { let path = entry.into_path(); - - if air_fs::has_r_extension(&path) { - return Some(path); - } else { - return None; - } + return Some(path); } // Otherwise check if we should accept this entry @@ -160,21 +155,21 @@ fn judge_entry(entry: DirEntry) -> Option { Some(path) } -pub(crate) enum FormatFileResult { +pub(crate) enum FormatFileAction { Formatted(PathBuf), Unchanged, } -impl FormatFileResult { +impl FormatFileAction { fn is_changed(&self) -> bool { - matches!(self, FormatFileResult::Formatted(_)) + matches!(self, FormatFileAction::Formatted(_)) } } // TODO: Take workspace `FormatOptions` that get resolved to `RFormatOptions` // for the formatter here. Respect user specified `LineEnding` option too, and // only use inferred endings when `FormatOptions::LineEnding::Auto` is used. -fn format_file(path: PathBuf, mode: FormatMode) -> Result { +fn format_file(path: PathBuf, mode: FormatMode) -> Result { let source = std::fs::read_to_string(&path) .map_err(|err| FormatCommandError::Read(path.clone(), err))?; @@ -205,9 +200,9 @@ fn format_file(path: PathBuf, mode: FormatMode) -> Result {} } - Ok(FormatFileResult::Formatted(path)) + Ok(FormatFileAction::Formatted(path)) } - FormattedSource::Unchanged => Ok(FormatFileResult::Unchanged), + FormattedSource::Unchanged => Ok(FormatFileAction::Unchanged), } } From d4a5c6e259aa5a318614f233c0a440548bcfdadf Mon Sep 17 00:00:00 2001 From: Davis Vaughan Date: Tue, 10 Dec 2024 13:48:15 -0500 Subject: [PATCH 13/16] Add a TODO to use user supplied line endings in the LSP --- crates/lsp/src/documents.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/lsp/src/documents.rs b/crates/lsp/src/documents.rs index f5493c75..56ed960e 100644 --- a/crates/lsp/src/documents.rs +++ b/crates/lsp/src/documents.rs @@ -61,6 +61,10 @@ impl Document { LineEnding::Crlf => line_ending::normalize(contents), }; + // TODO: Handle user requested line ending preference here + // by potentially overwriting `endings` if the user didn't + // select `LineEndings::Auto`, and then pass that to `LineIndex`. + // Create line index to keep track of newline offsets let line_index = LineIndex { index: triomphe::Arc::new(line_index::LineIndex::new(&contents)), From 5df379f93ad347a3ac670de80a89bdf9386fc86d Mon Sep 17 00:00:00 2001 From: Davis Vaughan Date: Tue, 10 Dec 2024 13:50:56 -0500 Subject: [PATCH 14/16] Add a TODO about not normalizing line endings in the cli --- crates/air/src/commands/format.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/air/src/commands/format.rs b/crates/air/src/commands/format.rs index 51b5bd77..a2788776 100644 --- a/crates/air/src/commands/format.rs +++ b/crates/air/src/commands/format.rs @@ -191,6 +191,12 @@ fn format_file(path: PathBuf, mode: FormatMode) -> Result { match mode { From a6a7aab8b12e3892242bfd84c7f27b434202d1c3 Mon Sep 17 00:00:00 2001 From: Davis Vaughan Date: Tue, 10 Dec 2024 14:48:55 -0500 Subject: [PATCH 15/16] air_fs -> fs --- Cargo.lock | 16 ++++++++-------- Cargo.toml | 2 +- crates/air/Cargo.toml | 2 +- crates/air/src/commands/format.rs | 6 +++--- crates/{air_fs => fs}/Cargo.toml | 2 +- crates/{air_fs => fs}/src/lib.rs | 0 6 files changed, 14 insertions(+), 14 deletions(-) rename crates/{air_fs => fs}/Cargo.toml (96%) rename crates/{air_fs => fs}/src/lib.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index c26ae56c..8e4a7fc7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,7 +30,6 @@ dependencies = [ name = "air" version = "0.1.0" dependencies = [ - "air_fs", "air_r_formatter", "air_r_parser", "anyhow", @@ -39,6 +38,7 @@ dependencies = [ "biome_formatter", "biome_parser", "clap", + "fs", "ignore", "itertools", "line_ending", @@ -65,13 +65,6 @@ dependencies = [ "similar-asserts", ] -[[package]] -name = "air_fs" -version = "0.0.0" -dependencies = [ - "path-absolutize", -] - [[package]] name = "air_r_factory" version = "0.0.0" @@ -793,6 +786,13 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs" +version = "0.0.0" +dependencies = [ + "path-absolutize", +] + [[package]] name = "futures" version = "0.3.31" diff --git a/Cargo.toml b/Cargo.toml index 2d9d65cb..32920fcf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,12 +22,12 @@ inherits = "release" [workspace.dependencies] air_formatter_test = { path = "./crates/air_formatter_test" } -air_fs = { path = "./crates/air_fs" } air_r_factory = { path = "./crates/air_r_factory" } air_r_formatter = { path = "./crates/air_r_formatter" } air_r_parser = { path = "./crates/air_r_parser" } air_r_syntax = { path = "./crates/air_r_syntax" } biome_ungrammar = { path = "./crates/biome_ungrammar" } +fs = { path = "./crates/fs" } line_ending = { path = "./crates/line_ending" } lsp = { path = "./crates/lsp" } lsp_test = { path = "./crates/lsp_test" } diff --git a/crates/air/Cargo.toml b/crates/air/Cargo.toml index 74441f37..4df75281 100644 --- a/crates/air/Cargo.toml +++ b/crates/air/Cargo.toml @@ -12,7 +12,6 @@ repository.workspace = true rust-version.workspace = true [dependencies] -air_fs = { workspace = true } air_r_formatter = { workspace = true } air_r_parser = { workspace = true } anyhow = { workspace = true } @@ -21,6 +20,7 @@ biome_diagnostics = { workspace = true } biome_formatter = { workspace = true } biome_parser = { workspace = true } clap = { workspace = true, features = ["wrap_help"] } +fs = { workspace = true } ignore = { workspace = true } itertools = { workspace = true } line_ending = { workspace = true } diff --git a/crates/air/src/commands/format.rs b/crates/air/src/commands/format.rs index a2788776..46c127a6 100644 --- a/crates/air/src/commands/format.rs +++ b/crates/air/src/commands/format.rs @@ -5,9 +5,9 @@ use std::io::stdout; use std::io::Write; use std::path::PathBuf; -use air_fs::relativize_path; use air_r_formatter::context::RFormatOptions; use air_r_parser::RParserOptions; +use fs::relativize_path; use ignore::DirEntry; use itertools::Either; use itertools::Itertools; @@ -100,7 +100,7 @@ fn write_changed(actions: &[FormatFileAction], f: &mut impl Write) -> io::Result } fn resolve_paths(paths: &[PathBuf]) -> Vec> { - let paths: Vec = paths.iter().map(air_fs::normalize_path).collect(); + let paths: Vec = paths.iter().map(fs::normalize_path).collect(); let (first_path, paths) = paths .split_first() @@ -148,7 +148,7 @@ fn is_valid_path(entry: DirEntry) -> Option { // TODO: Many other checks based on user exclude/includes let path = entry.into_path(); - if !air_fs::has_r_extension(&path) { + if !fs::has_r_extension(&path) { return None; } diff --git a/crates/air_fs/Cargo.toml b/crates/fs/Cargo.toml similarity index 96% rename from crates/air_fs/Cargo.toml rename to crates/fs/Cargo.toml index 33cfde3c..aa564ed0 100644 --- a/crates/air_fs/Cargo.toml +++ b/crates/fs/Cargo.toml @@ -7,7 +7,7 @@ edition.workspace = true homepage.workspace = true keywords.workspace = true license.workspace = true -name = "air_fs" +name = "fs" publish = false repository.workspace = true rust-version.workspace = true diff --git a/crates/air_fs/src/lib.rs b/crates/fs/src/lib.rs similarity index 100% rename from crates/air_fs/src/lib.rs rename to crates/fs/src/lib.rs From a06b538806c397bdc7a66e3978ce45f0ea7ececa Mon Sep 17 00:00:00 2001 From: Davis Vaughan Date: Tue, 10 Dec 2024 14:53:05 -0500 Subject: [PATCH 16/16] Ah clippy --- crates/air/src/status.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/air/src/status.rs b/crates/air/src/status.rs index 0f984d2f..2645a223 100644 --- a/crates/air/src/status.rs +++ b/crates/air/src/status.rs @@ -1,4 +1,4 @@ -use std::{process::ExitCode, u8}; +use std::process::ExitCode; #[derive(Copy, Clone, PartialEq, Debug)] pub enum ExitStatus {