Skip to content

Commit

Permalink
Add support for reading air.toml in the CLI and LSP
Browse files Browse the repository at this point in the history
  • Loading branch information
DavisVaughan committed Dec 20, 2024
1 parent 5446d7e commit 8045f6c
Show file tree
Hide file tree
Showing 38 changed files with 1,616 additions and 168 deletions.
78 changes: 76 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ line_ending = { path = "./crates/line_ending" }
lsp = { path = "./crates/lsp" }
lsp_test = { path = "./crates/lsp_test" }
tests_macros = { path = "./crates/tests_macros" }
workspace = { path = "./crates/workspace" }

anyhow = "1.0.89"
assert_matches = "1.5.0"
Expand Down Expand Up @@ -59,14 +60,17 @@ line-index = "0.1.2"
memchr = "2.7.4"
path-absolutize = "3.1.1"
proc-macro2 = "1.0.86"
serde = { version = "1.0.215", features = ["derive"] }
rustc-hash = "2.1.0"
serde = "1.0.215"
serde_json = "1.0.132"
struct-field-names-as-array = "0.3.0"
strum = "0.26"
tempfile = "3.9.0"
time = "0.3.37"
thiserror = "2.0.5"
tokio = { version = "1.41.1" }
tokio-util = "0.7.12"
toml = "0.8.19"
# For https://github.com/ebkalderon/tower-lsp/pull/428
tower-lsp = { git = "https://github.com/lionel-/tower-lsp", branch = "bugfix/patches" }
tracing = { version = "0.1.40", default-features = false, features = ["std"] }
Expand Down Expand Up @@ -124,7 +128,6 @@ unnecessary_join = "warn"
unnested_or_patterns = "warn"
unreadable_literal = "warn"
verbose_bit_mask = "warn"
zero_sized_map_values = "warn"

# restriction
cfg_not_test = "warn"
Expand Down
3 changes: 2 additions & 1 deletion crates/air/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ lsp = { workspace = true }
thiserror = { workspace = true }
tokio = "1.41.1"
tracing = { workspace = true }
workspace = { workspace = true }

[dev-dependencies]
tempfile = "3.9.0"
tempfile = { workspace = true }

[lints]
workspace = true
88 changes: 19 additions & 69 deletions crates/air/src/commands/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,32 @@ use std::path::PathBuf;
use air_r_formatter::context::RFormatOptions;
use air_r_parser::RParserOptions;
use fs::relativize_path;
use ignore::DirEntry;
use itertools::Either;
use itertools::Itertools;
use line_ending::LineEnding;
use thiserror::Error;
use workspace::resolve::discover_r_file_paths;
use workspace::resolve::SettingsResolver;
use workspace::settings::FormatSettings;
use workspace::settings::Settings;

use crate::args::FormatCommand;
use crate::ExitStatus;

pub(crate) fn format(command: FormatCommand) -> anyhow::Result<ExitStatus> {
let mode = FormatMode::from_command(&command);
let paths = resolve_paths(&command.paths);

let paths = discover_r_file_paths(&command.paths);

let mut resolver = SettingsResolver::new(Settings::default());
resolver.load_from_paths(&command.paths)?;

let (actions, errors): (Vec<_>, Vec<_>) = paths
.into_iter()
.map(|path| match path {
Ok(path) => format_file(path, mode),
Ok(path) => {
let settings = resolver.resolve_or_fallback(&path);
format_file(path, mode, &settings.format)
}
Err(err) => Err(err.into()),
})
.partition_map(|result| match result {
Expand Down Expand Up @@ -99,62 +108,6 @@ fn write_changed(actions: &[FormatFileAction], f: &mut impl Write) -> io::Result
Ok(())
}

fn resolve_paths(paths: &[PathBuf]) -> Vec<Result<PathBuf, ignore::Error>> {
let paths: Vec<PathBuf> = paths.iter().map(fs::normalize_path).collect();

let (first_path, paths) = paths
.split_first()
.expect("Clap should ensure at least 1 path is supplied.");

// TODO: Parallel directory visitor
let mut builder = ignore::WalkBuilder::new(first_path);

for path in paths {
builder.add(path);
}

let mut out = Vec::new();

for path in builder.build() {
match path {
Ok(entry) => {
if let Some(path) = is_valid_path(entry) {
out.push(Ok(path));
}
}
Err(err) => {
out.push(Err(err));
}
}
}

out
}

// Decide whether or not to accept an `entry` based on include/exclude rules.
fn is_valid_path(entry: DirEntry) -> Option<PathBuf> {
// Ignore directories
if entry.file_type().map_or(true, |ft| ft.is_dir()) {
return None;
}

// Accept all files that are passed-in directly, even non-R files
if entry.depth() == 0 {
let path = entry.into_path();
return Some(path);
}

// Otherwise check if we should accept this entry
// TODO: Many other checks based on user exclude/includes
let path = entry.into_path();

if !fs::has_r_extension(&path) {
return None;
}

Some(path)
}

pub(crate) enum FormatFileAction {
Formatted(PathBuf),
Unchanged,
Expand All @@ -166,18 +119,15 @@ impl FormatFileAction {
}
}

// 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<FormatFileAction, FormatCommandError> {
fn format_file(
path: PathBuf,
mode: FormatMode,
settings: &FormatSettings,
) -> Result<FormatFileAction, FormatCommandError> {
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 options = settings.to_format_options(&source);

let source = line_ending::normalize(source);
let formatted = match format_source(source.as_str(), options) {
Expand Down
14 changes: 14 additions & 0 deletions crates/air_r_formatter/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use biome_formatter::TransformSourceMap;
use crate::comments::FormatRLeadingComment;
use crate::comments::RCommentStyle;
use crate::comments::RComments;
use crate::options::MagicLineBreak;

pub struct RFormatContext {
options: RFormatOptions,
Expand Down Expand Up @@ -77,6 +78,10 @@ pub struct RFormatOptions {

/// The max width of a line. Defaults to 80.
line_width: LineWidth,

// TODO: Actually use this internally!
/// The behavior of magic line breaks.
magic_line_break: MagicLineBreak,
}

impl RFormatOptions {
Expand Down Expand Up @@ -106,6 +111,11 @@ impl RFormatOptions {
self
}

pub fn with_magic_line_break(mut self, magic_line_break: MagicLineBreak) -> Self {
self.magic_line_break = magic_line_break;
self
}

pub fn set_indent_style(&mut self, indent_style: IndentStyle) {
self.indent_style = indent_style;
}
Expand All @@ -121,6 +131,10 @@ impl RFormatOptions {
pub fn set_line_width(&mut self, line_width: LineWidth) {
self.line_width = line_width;
}

pub fn set_magic_line_break(&mut self, magic_line_break: MagicLineBreak) {
self.magic_line_break = magic_line_break;
}
}

impl FormatOptions for RFormatOptions {
Expand Down
1 change: 1 addition & 0 deletions crates/air_r_formatter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use crate::cst::FormatRSyntaxNode;
pub mod comments;
pub mod context;
mod cst;
pub mod options;
mod prelude;
mod r;
pub(crate) mod separated;
Expand Down
3 changes: 3 additions & 0 deletions crates/air_r_formatter/src/options.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod magic_line_break;

pub use magic_line_break::*;
44 changes: 44 additions & 0 deletions crates/air_r_formatter/src/options/magic_line_break.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use std::fmt::Display;
use std::str::FromStr;

#[derive(Debug, Default, Clone, Copy, Eq, Hash, PartialEq)]
pub enum MagicLineBreak {
/// Respect
#[default]
Respect,
/// Ignore
Ignore,
}

impl MagicLineBreak {
/// Returns `true` if magic line breaks should be respected.
pub const fn is_respect(&self) -> bool {
matches!(self, MagicLineBreak::Respect)
}

/// Returns `true` if magic line breaks should be ignored.
pub const fn is_ignore(&self) -> bool {
matches!(self, MagicLineBreak::Ignore)
}
}

impl FromStr for MagicLineBreak {
type Err = &'static str;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"respect" => Ok(Self::Respect),
"ignore" => Ok(Self::Ignore),
_ => Err("Unsupported value for this option"),
}
}
}

impl Display for MagicLineBreak {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MagicLineBreak::Respect => std::write!(f, "Respect"),
MagicLineBreak::Ignore => std::write!(f, "Ignore"),
}
}
}
Loading

0 comments on commit 8045f6c

Please sign in to comment.