diff options
Diffstat (limited to 'cli/tools/fmt.rs')
-rw-r--r-- | cli/tools/fmt.rs | 223 |
1 files changed, 173 insertions, 50 deletions
diff --git a/cli/tools/fmt.rs b/cli/tools/fmt.rs index b3dfc86e8..5f1507d19 100644 --- a/cli/tools/fmt.rs +++ b/cli/tools/fmt.rs @@ -8,6 +8,9 @@ //! the same functions as ops available in JS runtime. use crate::colors; +use crate::config_file::FmtConfig; +use crate::config_file::FmtOptionsConfig; +use crate::config_file::ProseWrap; use crate::diff::diff; use crate::file_watcher; use crate::file_watcher::ResolutionResult; @@ -35,24 +38,57 @@ pub async fn format( ignore: Vec<PathBuf>, check: bool, watch: bool, + maybe_fmt_config: Option<FmtConfig>, ) -> Result<(), AnyError> { + // First, prepare final configuration. + // Collect included and ignored files. CLI flags take precendence + // over config file, ie. if there's `files.ignore` in config file + // and `--ignore` CLI flag, only the flag value is taken into account. + let mut include_files = args.clone(); + let mut exclude_files = ignore; + + if let Some(fmt_config) = maybe_fmt_config.as_ref() { + if include_files.is_empty() { + include_files = fmt_config + .files + .include + .iter() + .map(PathBuf::from) + .collect::<Vec<PathBuf>>(); + } + + if exclude_files.is_empty() { + exclude_files = fmt_config + .files + .exclude + .iter() + .map(PathBuf::from) + .collect::<Vec<PathBuf>>(); + } + } + + let fmt_options = maybe_fmt_config.map(|c| c.options).unwrap_or_default(); + let resolver = |changed: Option<Vec<PathBuf>>| { let files_changed = changed.is_some(); let result = - collect_files(&args, &ignore, is_supported_ext_fmt).map(|files| { - if let Some(paths) = changed { - files - .into_iter() - .filter(|path| paths.contains(path)) - .collect::<Vec<_>>() - } else { - files - } - }); - let paths_to_watch = args.clone(); + collect_files(&include_files, &exclude_files, is_supported_ext_fmt).map( + |files| { + let collected_files = if let Some(paths) = changed { + files + .into_iter() + .filter(|path| paths.contains(path)) + .collect::<Vec<_>>() + } else { + files + }; + (collected_files, fmt_options.clone()) + }, + ); + let paths_to_watch = include_files.clone(); async move { if (files_changed || !watch) - && matches!(result, Ok(ref files) if files.is_empty()) + && matches!(result, Ok((ref files, _)) if files.is_empty()) { ResolutionResult::Ignore } else { @@ -63,11 +99,11 @@ pub async fn format( } } }; - let operation = |paths: Vec<PathBuf>| async move { + let operation = |(paths, fmt_options): (Vec<PathBuf>, FmtOptionsConfig)| async move { if check { - check_source_files(paths).await?; + check_source_files(paths, fmt_options).await?; } else { - format_source_files(paths).await?; + format_source_files(paths, fmt_options).await?; } Ok(()) }; @@ -75,13 +111,13 @@ pub async fn format( if watch { file_watcher::watch_func(resolver, operation, "Fmt").await?; } else { - let files = + let (files, fmt_options) = if let ResolutionResult::Restart { result, .. } = resolver(None).await { result? } else { return Err(generic_error("No target files found.")); }; - operation(files).await?; + operation((files, fmt_options)).await?; } Ok(()) @@ -89,10 +125,14 @@ pub async fn format( /// Formats markdown (using <https://github.com/dprint/dprint-plugin-markdown>) and its code blocks /// (ts/tsx, js/jsx). -fn format_markdown(file_text: &str) -> Result<String, String> { +fn format_markdown( + file_text: &str, + fmt_options: &FmtOptionsConfig, +) -> Result<String, String> { + let markdown_config = get_resolved_markdown_config(fmt_options); dprint_plugin_markdown::format_text( file_text, - &MARKDOWN_CONFIG, + &markdown_config, move |tag, text, line_width| { let tag = tag.to_lowercase(); if matches!( @@ -115,13 +155,14 @@ fn format_markdown(file_text: &str) -> Result<String, String> { }; if matches!(extension, "json" | "jsonc") { - let mut json_config = JSON_CONFIG.clone(); + let mut json_config = get_resolved_json_config(fmt_options); json_config.line_width = line_width; dprint_plugin_json::format_text(text, &json_config) } else { let fake_filename = PathBuf::from(format!("deno_fmt_stdin.{}", extension)); - let mut codeblock_config = TYPESCRIPT_CONFIG.clone(); + let mut codeblock_config = + get_resolved_typescript_config(fmt_options); codeblock_config.line_width = line_width; dprint_plugin_typescript::format_text( &fake_filename, @@ -140,32 +181,36 @@ fn format_markdown(file_text: &str) -> Result<String, String> { /// Formats JSON and JSONC using the rules provided by .deno() /// of configuration builder of <https://github.com/dprint/dprint-plugin-json>. /// See <https://git.io/Jt4ht> for configuration. -fn format_json(file_text: &str) -> Result<String, String> { - dprint_plugin_json::format_text(file_text, &JSON_CONFIG) - .map_err(|e| e.to_string()) +fn format_json( + file_text: &str, + fmt_options: &FmtOptionsConfig, +) -> Result<String, String> { + let config = get_resolved_json_config(fmt_options); + dprint_plugin_json::format_text(file_text, &config).map_err(|e| e.to_string()) } /// Formats a single TS, TSX, JS, JSX, JSONC, JSON, or MD file. pub fn format_file( file_path: &Path, file_text: &str, + fmt_options: FmtOptionsConfig, ) -> Result<String, String> { let ext = get_extension(file_path).unwrap_or_else(String::new); if ext == "md" { - format_markdown(file_text) + format_markdown(file_text, &fmt_options) } else if matches!(ext.as_str(), "json" | "jsonc") { - format_json(file_text) + format_json(file_text, &fmt_options) } else { - dprint_plugin_typescript::format_text( - file_path, - file_text, - &TYPESCRIPT_CONFIG, - ) - .map_err(|e| e.to_string()) + let config = get_resolved_typescript_config(&fmt_options); + dprint_plugin_typescript::format_text(file_path, file_text, &config) + .map_err(|e| e.to_string()) } } -pub fn format_parsed_module(parsed_source: &ParsedSource) -> String { +pub fn format_parsed_module( + parsed_source: &ParsedSource, + fmt_options: FmtOptionsConfig, +) -> String { dprint_plugin_typescript::format_parsed_file( &dprint_plugin_typescript::SourceFileInfo { is_jsx: matches!( @@ -178,11 +223,14 @@ pub fn format_parsed_module(parsed_source: &ParsedSource) -> String { module: parsed_source.module(), tokens: parsed_source.tokens(), }, - &TYPESCRIPT_CONFIG, + &get_resolved_typescript_config(&fmt_options), ) } -async fn check_source_files(paths: Vec<PathBuf>) -> Result<(), AnyError> { +async fn check_source_files( + paths: Vec<PathBuf>, + fmt_options: FmtOptionsConfig, +) -> Result<(), AnyError> { let not_formatted_files_count = Arc::new(AtomicUsize::new(0)); let checked_files_count = Arc::new(AtomicUsize::new(0)); @@ -196,7 +244,7 @@ async fn check_source_files(paths: Vec<PathBuf>) -> Result<(), AnyError> { checked_files_count.fetch_add(1, Ordering::Relaxed); let file_text = read_file_contents(&file_path)?.text; - match format_file(&file_path, &file_text) { + match format_file(&file_path, &file_text, fmt_options.clone()) { Ok(formatted_text) => { if formatted_text != file_text { not_formatted_files_count.fetch_add(1, Ordering::Relaxed); @@ -235,7 +283,10 @@ async fn check_source_files(paths: Vec<PathBuf>) -> Result<(), AnyError> { } } -async fn format_source_files(paths: Vec<PathBuf>) -> Result<(), AnyError> { +async fn format_source_files( + paths: Vec<PathBuf>, + fmt_options: FmtOptionsConfig, +) -> Result<(), AnyError> { let formatted_files_count = Arc::new(AtomicUsize::new(0)); let checked_files_count = Arc::new(AtomicUsize::new(0)); let output_lock = Arc::new(Mutex::new(0)); // prevent threads outputting at the same time @@ -247,7 +298,7 @@ async fn format_source_files(paths: Vec<PathBuf>) -> Result<(), AnyError> { checked_files_count.fetch_add(1, Ordering::Relaxed); let file_contents = read_file_contents(&file_path)?; - match format_file(&file_path, &file_contents.text) { + match format_file(&file_path, &file_contents.text, fmt_options.clone()) { Ok(formatted_text) => { if formatted_text != file_contents.text { write_file_contents( @@ -293,14 +344,18 @@ async fn format_source_files(paths: Vec<PathBuf>) -> Result<(), AnyError> { /// Format stdin and write result to stdout. /// Treats input as TypeScript or as set by `--ext` flag. /// Compatible with `--check` flag. -pub fn format_stdin(check: bool, ext: String) -> Result<(), AnyError> { +pub fn format_stdin( + check: bool, + ext: String, + fmt_options: FmtOptionsConfig, +) -> Result<(), AnyError> { let mut source = String::new(); if stdin().read_to_string(&mut source).is_err() { return Err(generic_error("Failed to read from stdin")); } let file_path = PathBuf::from(format!("_stdin.{}", ext)); - match format_file(&file_path, &source) { + match format_file(&file_path, &source, fmt_options) { Ok(formatted_text) => { if check { if formatted_text != source { @@ -325,18 +380,86 @@ fn files_str(len: usize) -> &'static str { } } -lazy_static::lazy_static! { - static ref TYPESCRIPT_CONFIG: dprint_plugin_typescript::configuration::Configuration = dprint_plugin_typescript::configuration::ConfigurationBuilder::new() - .deno() - .build(); +fn get_resolved_typescript_config( + options: &FmtOptionsConfig, +) -> dprint_plugin_typescript::configuration::Configuration { + let mut builder = + dprint_plugin_typescript::configuration::ConfigurationBuilder::new(); + builder.deno(); + + if let Some(use_tabs) = options.use_tabs { + builder.use_tabs(use_tabs); + } - static ref MARKDOWN_CONFIG: dprint_plugin_markdown::configuration::Configuration = dprint_plugin_markdown::configuration::ConfigurationBuilder::new() - .deno() - .build(); + if let Some(line_width) = options.line_width { + builder.line_width(line_width); + } + + if let Some(indent_width) = options.indent_width { + builder.indent_width(indent_width); + } + + if let Some(single_quote) = options.single_quote { + if single_quote { + builder.quote_style( + dprint_plugin_typescript::configuration::QuoteStyle::AlwaysSingle, + ); + } + } + + builder.build() +} + +fn get_resolved_markdown_config( + options: &FmtOptionsConfig, +) -> dprint_plugin_markdown::configuration::Configuration { + let mut builder = + dprint_plugin_markdown::configuration::ConfigurationBuilder::new(); + + builder.deno(); + + if let Some(line_width) = options.line_width { + builder.line_width(line_width); + } + + if let Some(prose_wrap) = options.prose_wrap { + builder.text_wrap(match prose_wrap { + ProseWrap::Always => { + dprint_plugin_markdown::configuration::TextWrap::Always + } + ProseWrap::Never => { + dprint_plugin_markdown::configuration::TextWrap::Never + } + ProseWrap::Preserve => { + dprint_plugin_markdown::configuration::TextWrap::Maintain + } + }); + } + + builder.build() +} + +fn get_resolved_json_config( + options: &FmtOptionsConfig, +) -> dprint_plugin_json::configuration::Configuration { + let mut builder = + dprint_plugin_json::configuration::ConfigurationBuilder::new(); + + builder.deno(); + + if let Some(use_tabs) = options.use_tabs { + builder.use_tabs(use_tabs); + } + + if let Some(line_width) = options.line_width { + builder.line_width(line_width); + } + + if let Some(indent_width) = options.indent_width { + builder.indent_width(indent_width); + } - static ref JSON_CONFIG: dprint_plugin_json::configuration::Configuration = dprint_plugin_json::configuration::ConfigurationBuilder::new() - .deno() - .build(); + builder.build() } struct FileContents { |