From 3f4bfe9b3fe650fd780ee0cbe2ce0355a0259861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Tue, 7 Sep 2021 16:39:21 +0200 Subject: [PATCH 01/14] feat(fmt): add support for configuration file --- cli/config_file.rs | 45 +++++++++++++++++++++++++++++++++++++++++++-- cli/flags.rs | 40 ++++++++++++++++++++++++++++++++++++++++ cli/main.rs | 9 +++++++++ cli/tools/fmt.rs | 2 ++ 4 files changed, 94 insertions(+), 2 deletions(-) diff --git a/cli/config_file.rs b/cli/config_file.rs index f2f119a0b768fd..68af6bff4b74f5 100644 --- a/cli/config_file.rs +++ b/cli/config_file.rs @@ -276,7 +276,7 @@ pub struct LintRulesConfig { #[derive(Clone, Debug, Default, Deserialize)] #[serde(default, deny_unknown_fields)] -pub struct LintFilesConfig { +pub struct FilesConfig { pub include: Vec, pub exclude: Vec, } @@ -285,7 +285,20 @@ pub struct LintFilesConfig { #[serde(default, deny_unknown_fields)] pub struct LintConfig { pub rules: LintRulesConfig, - pub files: LintFilesConfig, + pub files: FilesConfig, +} + +#[derive(Clone, Debug, Default, Deserialize)] +#[serde(default, deny_unknown_fields, rename_all = "camelCase")] +pub struct FmtRulesConfig { + pub use_tabs: bool, +} + +#[derive(Clone, Debug, Default, Deserialize)] +#[serde(default, deny_unknown_fields)] +pub struct FmtConfig { + pub rules: FmtRulesConfig, + pub files: FilesConfig, } #[derive(Clone, Debug, Deserialize)] @@ -293,6 +306,7 @@ pub struct LintConfig { pub struct ConfigFileJson { pub compiler_options: Option, pub lint: Option, + pub fmt: Option, } #[derive(Clone, Debug)] @@ -373,6 +387,16 @@ impl ConfigFile { Ok(None) } } + + pub fn to_fmt_config(&self) -> Result, AnyError> { + if let Some(config) = self.json.fmt.clone() { + let fmt_config: FmtConfig = serde_json::from_value(config) + .context("Failed to parse \"fmt\" configuration")?; + Ok(Some(fmt_config)) + } else { + Ok(None) + } + } } #[cfg(test)] @@ -440,6 +464,15 @@ mod tests { "tags": ["recommended"], "include": ["ban-untagged-todo"] } + }, + "fmt": { + "files": { + "include": ["src/"], + "exclude": ["src/testdata/"] + }, + "rules": { + "useTabs": true + } } }"#; let config_path = PathBuf::from("/deno/tsconfig.json"); @@ -473,6 +506,14 @@ mod tests { Some(vec!["recommended".to_string()]) ); assert!(lint_config.rules.exclude.is_none()); + + let fmt_config = config_file + .to_fmt_config() + .expect("error parsing fmt object") + .expect("fmt object should be defined"); + assert_eq!(fmt_config.files.include, vec!["src/"]); + assert_eq!(fmt_config.files.exclude, vec!["src/testdata/"]); + assert!(fmt_config.rules.use_tabs); } #[test] diff --git a/cli/flags.rs b/cli/flags.rs index 8bfcbb3807f12e..3a84c9c5a72fba 100644 --- a/cli/flags.rs +++ b/cli/flags.rs @@ -815,6 +815,7 @@ Ignore formatting a file by adding an ignore comment at the top of the file: // deno-fmt-ignore-file", ) + .arg(config_arg()) .arg( Arg::with_name("check") .long("check") @@ -1733,6 +1734,7 @@ fn eval_parse(flags: &mut Flags, matches: &clap::ArgMatches) { } fn fmt_parse(flags: &mut Flags, matches: &clap::ArgMatches) { + config_arg_parse(flags, matches); flags.watch = matches.is_present("watch"); let files = match matches.values_of("files") { Some(f) => f.map(PathBuf::from).collect(), @@ -2535,6 +2537,44 @@ mod tests { ..Flags::default() } ); + + let r = flags_from_vec(svec!["deno", "fmt", "--config", "deno.jsonc"]); + assert_eq!( + r.unwrap(), + Flags { + subcommand: DenoSubcommand::Fmt(FmtFlags { + ignore: vec![], + check: false, + files: vec![], + ext: "ts".to_string() + }), + config_path: Some("deno.jsonc".to_string()), + ..Flags::default() + } + ); + + let r = flags_from_vec(svec![ + "deno", + "fmt", + "--config", + "deno.jsonc", + "--watch", + "foo.ts" + ]); + assert_eq!( + r.unwrap(), + Flags { + subcommand: DenoSubcommand::Fmt(FmtFlags { + ignore: vec![], + check: false, + files: vec![PathBuf::from("foo.ts")], + ext: "ts".to_string() + }), + config_path: Some("deno.jsonc".to_string()), + watch: true, + ..Flags::default() + } + ); } #[test] diff --git a/cli/main.rs b/cli/main.rs index 4687761353a34e..9b4020f4359054 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -811,11 +811,20 @@ async fn format_command( return tools::fmt::format_stdin(fmt_flags.check, fmt_flags.ext); } + let program_state = ProgramState::build(flags.clone()).await?; + let maybe_fmt_config = + if let Some(config_file) = &program_state.maybe_config_file { + config_file.to_fmt_config()? + } else { + None + }; + tools::fmt::format( fmt_flags.files, fmt_flags.ignore, fmt_flags.check, flags.watch, + maybe_fmt_config, ) .await?; Ok(()) diff --git a/cli/tools/fmt.rs b/cli/tools/fmt.rs index 59f9ddc8bb7425..36542ebdf8ea78 100644 --- a/cli/tools/fmt.rs +++ b/cli/tools/fmt.rs @@ -8,6 +8,7 @@ //! the same functions as ops available in JS runtime. use crate::colors; +use crate::config_file::FmtConfig; use crate::diff::diff; use crate::file_watcher; use crate::file_watcher::ResolutionResult; @@ -34,6 +35,7 @@ pub async fn format( ignore: Vec, check: bool, watch: bool, + _maybe_fmt_config: Option, ) -> Result<(), AnyError> { let resolver = |changed: Option>| { let files_changed = changed.is_some(); From cd239afabd1c01e78eed1fca648398b8f4f0f488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Wed, 8 Sep 2021 23:54:27 +0200 Subject: [PATCH 02/14] more options --- cli/config_file.rs | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/cli/config_file.rs b/cli/config_file.rs index 5aabf0d957c089..5e6e5669e1cad5 100644 --- a/cli/config_file.rs +++ b/cli/config_file.rs @@ -289,16 +289,26 @@ pub struct LintConfig { pub files: FilesConfig, } +// enum TestWrap { +// Always, +// Never, +// Preserve, +// } + #[derive(Clone, Debug, Default, Deserialize)] #[serde(default, deny_unknown_fields, rename_all = "camelCase")] -pub struct FmtRulesConfig { - pub use_tabs: bool, +pub struct FmtOptionsConfig { + pub use_tabs: Option, + pub line_width: Option, + pub indent_width: Option, + pub single_quote: Option, + // pub text_wrap: } #[derive(Clone, Debug, Default, Deserialize)] #[serde(default, deny_unknown_fields)] pub struct FmtConfig { - pub rules: FmtRulesConfig, + pub options: FmtOptionsConfig, pub files: FilesConfig, } @@ -471,8 +481,11 @@ mod tests { "include": ["src/"], "exclude": ["src/testdata/"] }, - "rules": { - "useTabs": true + "options": { + "useTabs": true, + "lineWidth": 80, + "indentWidth": 4, + "singleQuote": true } } }"#; @@ -514,7 +527,10 @@ mod tests { .expect("fmt object should be defined"); assert_eq!(fmt_config.files.include, vec!["src/"]); assert_eq!(fmt_config.files.exclude, vec!["src/testdata/"]); - assert!(fmt_config.rules.use_tabs); + assert_eq!(fmt_config.options.use_tabs, Some(true)); + assert_eq!(fmt_config.options.line_width, Some(80)); + assert_eq!(fmt_config.options.indent_width, Some(4)); + assert_eq!(fmt_config.options.single_quote, Some(true)); } #[test] From 260f582f246f04d3767f5d7f9fbfce9b51a8692c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Fri, 10 Sep 2021 20:16:56 +0200 Subject: [PATCH 03/14] handle TextWrap --- cli/config_file.rs | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/cli/config_file.rs b/cli/config_file.rs index 5e6e5669e1cad5..f93f2bf911ef96 100644 --- a/cli/config_file.rs +++ b/cli/config_file.rs @@ -289,10 +289,33 @@ pub struct LintConfig { pub files: FilesConfig, } -// enum TestWrap { -// Always, -// Never, -// Preserve, +#[derive(Clone, Debug, Deserialize)] +#[serde(deny_unknown_fields, rename_all = "camelCase")] +pub enum TextWrap { + Always, + Never, + Preserve, +} + +// impl std::str::FromStr for TextWrap { +// type Err = &str; + +// fn from_str(s: &str) -> Result { +// match s { +// "always" => Ok(TextWrap::Always), +// "never" => Ok(TextWrap::Always), +// "preserver" => Ok(TextWrap::Always), +// _ => Err(ParseConfigurationError(String::from(s))), +// } +// } +// } + +// impl std::string::ToString for $enum_name { +// fn to_string(&self) -> String { +// match self { +// $($enum_name::$member_name => String::from($string_value)),*, +// } +// } // } #[derive(Clone, Debug, Default, Deserialize)] @@ -302,7 +325,7 @@ pub struct FmtOptionsConfig { pub line_width: Option, pub indent_width: Option, pub single_quote: Option, - // pub text_wrap: + pub text_wrap: Option, } #[derive(Clone, Debug, Default, Deserialize)] @@ -485,7 +508,8 @@ mod tests { "useTabs": true, "lineWidth": 80, "indentWidth": 4, - "singleQuote": true + "singleQuote": true, + "textWrap": "preserve" } } }"#; From 63a67120404555da493198f22b153c48bf122df2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Fri, 10 Sep 2021 20:37:50 +0200 Subject: [PATCH 04/14] pass configuration --- cli/config_file.rs | 21 -------- cli/lsp/language_server.rs | 4 +- cli/main.rs | 12 +++-- cli/tools/fmt.rs | 104 ++++++++++++++++++++++++++----------- 4 files changed, 86 insertions(+), 55 deletions(-) diff --git a/cli/config_file.rs b/cli/config_file.rs index f93f2bf911ef96..b03246729f7276 100644 --- a/cli/config_file.rs +++ b/cli/config_file.rs @@ -297,27 +297,6 @@ pub enum TextWrap { Preserve, } -// impl std::str::FromStr for TextWrap { -// type Err = &str; - -// fn from_str(s: &str) -> Result { -// match s { -// "always" => Ok(TextWrap::Always), -// "never" => Ok(TextWrap::Always), -// "preserver" => Ok(TextWrap::Always), -// _ => Err(ParseConfigurationError(String::from(s))), -// } -// } -// } - -// impl std::string::ToString for $enum_name { -// fn to_string(&self) -> String { -// match self { -// $($enum_name::$member_name => String::from($string_value)),*, -// } -// } -// } - #[derive(Clone, Debug, Default, Deserialize)] #[serde(default, deny_unknown_fields, rename_all = "camelCase")] pub struct FmtOptionsConfig { diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index f2b03a0aaf6ff1..ea0a407ec2b001 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -1009,11 +1009,13 @@ impl Inner { let source = document_data.source().clone(); let text_edits = tokio::task::spawn_blocking(move || { let format_result = match source.module() { + // TODO(bartlomieju): add support for `fmt_config` from config file Some(Ok(parsed_module)) => Ok(format_parsed_module(parsed_module)), Some(Err(err)) => Err(err.to_string()), None => { // it's not a js/ts file, so attempt to format its contents - format_file(&file_path, source.text_info().text_str()) + // TODO(bartlomieju): add support for `fmt_config` from config file + format_file(&file_path, source.text_info().text_str(), None) } }; diff --git a/cli/main.rs b/cli/main.rs index 897c6f89be57f3..6e6ee2cb3f5bc7 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -806,10 +806,6 @@ async fn format_command( flags: Flags, fmt_flags: FmtFlags, ) -> Result<(), AnyError> { - if fmt_flags.files.len() == 1 && fmt_flags.files[0].to_string_lossy() == "-" { - return tools::fmt::format_stdin(fmt_flags.check, fmt_flags.ext); - } - let program_state = ProgramState::build(flags.clone()).await?; let maybe_fmt_config = if let Some(config_file) = &program_state.maybe_config_file { @@ -818,6 +814,14 @@ async fn format_command( None }; + if fmt_flags.files.len() == 1 && fmt_flags.files[0].to_string_lossy() == "-" { + return tools::fmt::format_stdin( + fmt_flags.check, + fmt_flags.ext, + maybe_fmt_config, + ); + } + tools::fmt::format( fmt_flags.files, fmt_flags.ignore, diff --git a/cli/tools/fmt.rs b/cli/tools/fmt.rs index cc4edd2f4c438f..9b43f5dc161eb7 100644 --- a/cli/tools/fmt.rs +++ b/cli/tools/fmt.rs @@ -36,25 +36,55 @@ pub async fn format( ignore: Vec, check: bool, watch: bool, - _maybe_fmt_config: Option, + maybe_fmt_config: Option, ) -> 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::>(); + } + + if exclude_files.is_empty() { + exclude_files = fmt_config + .files + .exclude + .iter() + .map(PathBuf::from) + .collect::>(); + } + } + let resolver = |changed: Option>| { 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::>() - } 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::>() + } else { + files + }; + (collected_files, maybe_fmt_config.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 { @@ -65,25 +95,26 @@ pub async fn format( } } }; - let operation = |paths: Vec| async move { - if check { - check_source_files(paths).await?; - } else { - format_source_files(paths).await?; - } - Ok(()) - }; + let operation = + |(paths, maybe_fmt_config): (Vec, Option)| async move { + if check { + check_source_files(paths, maybe_fmt_config).await?; + } else { + format_source_files(paths, maybe_fmt_config).await?; + } + Ok(()) + }; if watch { file_watcher::watch_func(resolver, operation, "Fmt").await?; } else { - let files = + let (files, maybe_fmt_config) = if let ResolutionResult::Restart { result, .. } = resolver(None).await { result? } else { return Err(generic_error("No target files found.")); }; - operation(files).await?; + operation((files, maybe_fmt_config)).await?; } Ok(()) @@ -151,6 +182,7 @@ fn format_json(file_text: &str) -> Result { pub fn format_file( file_path: &Path, file_text: &str, + _maybe_fmt_config: Option, ) -> Result { let ext = get_extension(file_path).unwrap_or_else(String::new); if ext == "md" { @@ -184,7 +216,10 @@ pub fn format_parsed_module(parsed_source: &ParsedSource) -> String { ) } -async fn check_source_files(paths: Vec) -> Result<(), AnyError> { +async fn check_source_files( + paths: Vec, + maybe_fmt_config: Option, +) -> Result<(), AnyError> { let not_formatted_files_count = Arc::new(AtomicUsize::new(0)); let checked_files_count = Arc::new(AtomicUsize::new(0)); @@ -198,7 +233,7 @@ async fn check_source_files(paths: Vec) -> 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, maybe_fmt_config.clone()) { Ok(formatted_text) => { if formatted_text != file_text { not_formatted_files_count.fetch_add(1, Ordering::Relaxed); @@ -237,7 +272,10 @@ async fn check_source_files(paths: Vec) -> Result<(), AnyError> { } } -async fn format_source_files(paths: Vec) -> Result<(), AnyError> { +async fn format_source_files( + paths: Vec, + maybe_fmt_config: Option, +) -> 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 @@ -249,7 +287,11 @@ async fn format_source_files(paths: Vec) -> 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, + maybe_fmt_config.clone(), + ) { Ok(formatted_text) => { if formatted_text != file_contents.text { write_file_contents( @@ -295,14 +337,18 @@ async fn format_source_files(paths: Vec) -> 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, + maybe_fmt_config: Option, +) -> 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, maybe_fmt_config) { Ok(formatted_text) => { if check { if formatted_text != source { From 2da34cbfca9c482266ed2fe51c180a652e7c74c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Fri, 10 Sep 2021 21:01:29 +0200 Subject: [PATCH 05/14] resolve configs for languages --- cli/config_file.rs | 4 +- cli/lsp/language_server.rs | 6 +- cli/main.rs | 2 +- cli/tools/fmt.rs | 169 ++++++++++++++++++++++++++----------- 4 files changed, 128 insertions(+), 53 deletions(-) diff --git a/cli/config_file.rs b/cli/config_file.rs index b03246729f7276..a3664607984c38 100644 --- a/cli/config_file.rs +++ b/cli/config_file.rs @@ -289,7 +289,7 @@ pub struct LintConfig { pub files: FilesConfig, } -#[derive(Clone, Debug, Deserialize)] +#[derive(Clone, Copy, Debug, Deserialize)] #[serde(deny_unknown_fields, rename_all = "camelCase")] pub enum TextWrap { Always, @@ -302,7 +302,7 @@ pub enum TextWrap { pub struct FmtOptionsConfig { pub use_tabs: Option, pub line_width: Option, - pub indent_width: Option, + pub indent_width: Option, pub single_quote: Option, pub text_wrap: Option, } diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index ea0a407ec2b001..ac0ce91b958be2 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -1015,7 +1015,11 @@ impl Inner { None => { // it's not a js/ts file, so attempt to format its contents // TODO(bartlomieju): add support for `fmt_config` from config file - format_file(&file_path, source.text_info().text_str(), None) + format_file( + &file_path, + source.text_info().text_str(), + Default::default(), + ) } }; diff --git a/cli/main.rs b/cli/main.rs index 6e6ee2cb3f5bc7..8d7ab86ced144c 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -818,7 +818,7 @@ async fn format_command( return tools::fmt::format_stdin( fmt_flags.check, fmt_flags.ext, - maybe_fmt_config, + maybe_fmt_config.map(|c| c.options).unwrap_or_default(), ); } diff --git a/cli/tools/fmt.rs b/cli/tools/fmt.rs index 9b43f5dc161eb7..130a6611af754a 100644 --- a/cli/tools/fmt.rs +++ b/cli/tools/fmt.rs @@ -9,6 +9,8 @@ use crate::colors; use crate::config_file::FmtConfig; +use crate::config_file::FmtOptionsConfig; +use crate::config_file::TextWrap; use crate::diff::diff; use crate::file_watcher; use crate::file_watcher::ResolutionResult; @@ -65,6 +67,8 @@ pub async fn format( } } + let fmt_options = maybe_fmt_config.map(|c| c.options).unwrap_or_default(); + let resolver = |changed: Option>| { let files_changed = changed.is_some(); let result = @@ -78,7 +82,7 @@ pub async fn format( } else { files }; - (collected_files, maybe_fmt_config.clone()) + (collected_files, fmt_options.clone()) }, ); let paths_to_watch = include_files.clone(); @@ -95,26 +99,25 @@ pub async fn format( } } }; - let operation = - |(paths, maybe_fmt_config): (Vec, Option)| async move { - if check { - check_source_files(paths, maybe_fmt_config).await?; - } else { - format_source_files(paths, maybe_fmt_config).await?; - } - Ok(()) - }; + let operation = |(paths, fmt_options): (Vec, FmtOptionsConfig)| async move { + if check { + check_source_files(paths, fmt_options).await?; + } else { + format_source_files(paths, fmt_options).await?; + } + Ok(()) + }; if watch { file_watcher::watch_func(resolver, operation, "Fmt").await?; } else { - let (files, maybe_fmt_config) = + let (files, fmt_options) = if let ResolutionResult::Restart { result, .. } = resolver(None).await { result? } else { return Err(generic_error("No target files found.")); }; - operation((files, maybe_fmt_config)).await?; + operation((files, fmt_options)).await?; } Ok(()) @@ -122,10 +125,14 @@ pub async fn format( /// Formats markdown (using ) and its code blocks /// (ts/tsx, js/jsx). -fn format_markdown(file_text: &str) -> Result { +fn format_markdown( + file_text: &str, + fmt_options: &FmtOptionsConfig, +) -> Result { + 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!( @@ -148,13 +155,14 @@ fn format_markdown(file_text: &str) -> Result { }; 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, @@ -173,29 +181,29 @@ fn format_markdown(file_text: &str) -> Result { /// Formats JSON and JSONC using the rules provided by .deno() /// of configuration builder of . /// See for configuration. -fn format_json(file_text: &str) -> Result { - 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 { + 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, - _maybe_fmt_config: Option, + fmt_options: FmtOptionsConfig, ) -> Result { 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()) } } @@ -212,13 +220,14 @@ pub fn format_parsed_module(parsed_source: &ParsedSource) -> String { module: parsed_source.module(), tokens: parsed_source.tokens(), }, - &TYPESCRIPT_CONFIG, + // TODO(bartlomieju): should use `FmtOptionsConfig` from LSP + &get_resolved_typescript_config(&FmtOptionsConfig::default()), ) } async fn check_source_files( paths: Vec, - maybe_fmt_config: Option, + fmt_options: FmtOptionsConfig, ) -> Result<(), AnyError> { let not_formatted_files_count = Arc::new(AtomicUsize::new(0)); let checked_files_count = Arc::new(AtomicUsize::new(0)); @@ -233,7 +242,7 @@ async fn check_source_files( checked_files_count.fetch_add(1, Ordering::Relaxed); let file_text = read_file_contents(&file_path)?.text; - match format_file(&file_path, &file_text, maybe_fmt_config.clone()) { + 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); @@ -274,7 +283,7 @@ async fn check_source_files( async fn format_source_files( paths: Vec, - maybe_fmt_config: Option, + fmt_options: FmtOptionsConfig, ) -> Result<(), AnyError> { let formatted_files_count = Arc::new(AtomicUsize::new(0)); let checked_files_count = Arc::new(AtomicUsize::new(0)); @@ -287,11 +296,7 @@ async fn format_source_files( checked_files_count.fetch_add(1, Ordering::Relaxed); let file_contents = read_file_contents(&file_path)?; - match format_file( - &file_path, - &file_contents.text, - maybe_fmt_config.clone(), - ) { + match format_file(&file_path, &file_contents.text, fmt_options.clone()) { Ok(formatted_text) => { if formatted_text != file_contents.text { write_file_contents( @@ -340,7 +345,7 @@ async fn format_source_files( pub fn format_stdin( check: bool, ext: String, - maybe_fmt_config: Option, + fmt_options: FmtOptionsConfig, ) -> Result<(), AnyError> { let mut source = String::new(); if stdin().read_to_string(&mut source).is_err() { @@ -348,7 +353,7 @@ pub fn format_stdin( } let file_path = PathBuf::from(format!("_stdin.{}", ext)); - match format_file(&file_path, &source, maybe_fmt_config) { + match format_file(&file_path, &source, fmt_options) { Ok(formatted_text) => { if check { if formatted_text != source { @@ -373,18 +378,84 @@ 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(text_wrap) = &options.text_wrap { + builder.text_wrap(match text_wrap { + TextWrap::Always => { + dprint_plugin_markdown::configuration::TextWrap::Always + } + TextWrap::Never => dprint_plugin_markdown::configuration::TextWrap::Never, + TextWrap::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 { From 73e89bbed9e7acbc82999b549eb4becde8b6c631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Fri, 10 Sep 2021 21:46:38 +0200 Subject: [PATCH 06/14] use config file in LSP --- cli/lsp/language_server.rs | 102 +++++++++++++++++++++++-------------- cli/tools/fmt.rs | 10 ++-- 2 files changed, 71 insertions(+), 41 deletions(-) diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index ac0ce91b958be2..f1ff61cff05a46 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -335,15 +335,15 @@ impl Inner { Ok(navigation_tree) } - fn merge_user_tsconfig( - &mut self, - maybe_config: &Option, - maybe_root_uri: &Option, - tsconfig: &mut TsConfig, - ) -> Result<(), AnyError> { - self.maybe_config_file = None; - self.maybe_config_uri = None; - if let Some(config_str) = maybe_config { + /// Returns a tuple with parsed `ConfigFile` and `Url` pointing to that file. + /// If there's no config file specified in settings returns `None`. + fn get_config_file_and_url( + &self, + ) -> Result, AnyError> { + let workspace_settings = self.config.get_workspace_settings(); + let maybe_root_uri = self.config.root_uri.clone(); + let maybe_config = workspace_settings.config; + if let Some(config_str) = &maybe_config { if !config_str.is_empty() { info!("Setting TypeScript configuration from: \"{}\"", config_str); let config_url = if let Ok(url) = Url::from_file_path(config_str) { @@ -373,18 +373,34 @@ impl Inner { .ok_or_else(|| anyhow!("Bad uri: \"{}\"", config_url))?; ConfigFile::read(path)? }; - let (value, maybe_ignored_options) = - config_file.to_compiler_options()?; - tsconfig.merge(&value); - self.maybe_config_file = Some(config_file); - self.maybe_config_uri = Some(config_url); - if let Some(ignored_options) = maybe_ignored_options { - // TODO(@kitsonk) turn these into diagnostics that can be sent to the - // client - warn!("{}", ignored_options); - } + return Ok(Some((config_file, config_url))); + } + } + + Ok(None) + } + + fn merge_user_tsconfig( + &mut self, + tsconfig: &mut TsConfig, + ) -> Result<(), AnyError> { + self.maybe_config_file = None; + self.maybe_config_uri = None; + + let maybe_file_and_url = self.get_config_file_and_url()?; + + if let Some((config_file, config_url)) = maybe_file_and_url { + let (value, maybe_ignored_options) = config_file.to_compiler_options()?; + tsconfig.merge(&value); + self.maybe_config_file = Some(config_file); + self.maybe_config_uri = Some(config_url); + if let Some(ignored_options) = maybe_ignored_options { + // TODO(@kitsonk) turn these into diagnostics that can be sent to the + // client + warn!("{}", ignored_options); } } + Ok(()) } @@ -566,20 +582,15 @@ impl Inner { // TODO(@kitsonk) remove for Deno 1.15 "useUnknownInCatchVariables": false, })); - let (maybe_config, maybe_root_uri) = { - let config = &self.config; - let workspace_settings = config.get_workspace_settings(); - if workspace_settings.unstable { - let unstable_libs = json!({ - "lib": ["deno.ns", "deno.window", "deno.unstable"] - }); - tsconfig.merge(&unstable_libs); - } - (workspace_settings.config, config.root_uri.clone()) - }; - if let Err(err) = - self.merge_user_tsconfig(&maybe_config, &maybe_root_uri, &mut tsconfig) - { + let config = &self.config; + let workspace_settings = config.get_workspace_settings(); + if workspace_settings.unstable { + let unstable_libs = json!({ + "lib": ["deno.ns", "deno.window", "deno.unstable"] + }); + tsconfig.merge(&unstable_libs); + } + if let Err(err) = self.merge_user_tsconfig(&mut tsconfig) { self.client.show_message(MessageType::Warning, err).await; } let _ok: bool = self @@ -1006,19 +1017,36 @@ impl Inner { PathBuf::from(params.text_document.uri.path()) }; + let maybe_file_and_url = self.get_config_file_and_url().map_err(|err| { + error!("Unable to parse configuration file: {}", err); + LspError::internal_error() + })?; + + let fmt_options = if let Some((config_file, _)) = maybe_file_and_url { + config_file + .to_fmt_config() + .map_err(|err| { + error!("Unable to parse fmt configuration: {}", err); + LspError::internal_error() + })? + .unwrap_or_default() + } else { + Default::default() + }; + let source = document_data.source().clone(); let text_edits = tokio::task::spawn_blocking(move || { let format_result = match source.module() { - // TODO(bartlomieju): add support for `fmt_config` from config file - Some(Ok(parsed_module)) => Ok(format_parsed_module(parsed_module)), + Some(Ok(parsed_module)) => { + Ok(format_parsed_module(parsed_module, fmt_options.options)) + } Some(Err(err)) => Err(err.to_string()), None => { // it's not a js/ts file, so attempt to format its contents - // TODO(bartlomieju): add support for `fmt_config` from config file format_file( &file_path, source.text_info().text_str(), - Default::default(), + fmt_options.options, ) } }; diff --git a/cli/tools/fmt.rs b/cli/tools/fmt.rs index 130a6611af754a..2429bb8039b6cf 100644 --- a/cli/tools/fmt.rs +++ b/cli/tools/fmt.rs @@ -207,7 +207,10 @@ pub fn format_file( } } -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!( @@ -220,8 +223,7 @@ pub fn format_parsed_module(parsed_source: &ParsedSource) -> String { module: parsed_source.module(), tokens: parsed_source.tokens(), }, - // TODO(bartlomieju): should use `FmtOptionsConfig` from LSP - &get_resolved_typescript_config(&FmtOptionsConfig::default()), + &get_resolved_typescript_config(&fmt_options), ) } @@ -420,7 +422,7 @@ fn get_resolved_markdown_config( builder.line_width(line_width); } - if let Some(text_wrap) = &options.text_wrap { + if let Some(text_wrap) = options.text_wrap { builder.text_wrap(match text_wrap { TextWrap::Always => { dprint_plugin_markdown::configuration::TextWrap::Always From 4cae93e10bf254cf0334ba512cc6d5b5efcb0afd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Mon, 13 Sep 2021 14:21:58 +0200 Subject: [PATCH 07/14] add tests for malformed config --- cli/tests/integration/fmt_tests.rs | 12 ++++++++++++ cli/tests/testdata/fmt/Deno.malformed.jsonc | 12 ++++++++++++ cli/tests/testdata/fmt/Deno.malformed2.jsonc | 12 ++++++++++++ cli/tests/testdata/fmt/fmt_with_malformed_config.out | 4 ++++ .../testdata/fmt/fmt_with_malformed_config2.out | 4 ++++ 5 files changed, 44 insertions(+) create mode 100644 cli/tests/testdata/fmt/Deno.malformed.jsonc create mode 100644 cli/tests/testdata/fmt/Deno.malformed2.jsonc create mode 100644 cli/tests/testdata/fmt/fmt_with_malformed_config.out create mode 100644 cli/tests/testdata/fmt/fmt_with_malformed_config2.out diff --git a/cli/tests/integration/fmt_tests.rs b/cli/tests/integration/fmt_tests.rs index 00565a5d04d611..38f553b838bb54 100644 --- a/cli/tests/integration/fmt_tests.rs +++ b/cli/tests/integration/fmt_tests.rs @@ -181,3 +181,15 @@ itest!(fmt_stdin_check_not_formatted { input: Some("const a = 1\n"), output_str: Some("Not formatted stdin\n"), }); + +itest!(fmt_with_malformed_config { + args: "fmt --config fmt/Deno.malformed.jsonc", + output: "fmt/fmt_with_malformed_config.out", + exit_code: 1, +}); + +itest!(fmt_with_malformed_config2 { + args: "fmt --config fmt/Deno.malformed2.jsonc", + output: "fmt/fmt_with_malformed_config2.out", + exit_code: 1, +}); diff --git a/cli/tests/testdata/fmt/Deno.malformed.jsonc b/cli/tests/testdata/fmt/Deno.malformed.jsonc new file mode 100644 index 00000000000000..667480bdcfade1 --- /dev/null +++ b/cli/tests/testdata/fmt/Deno.malformed.jsonc @@ -0,0 +1,12 @@ +{ + "fmt": { + "files": { + "include": ["fmt/fmt_with_config/"], + "exclude": ["fmt/fmt_with_config/b.ts"] + }, + "dont_know_this_field": {}, + "options": { + "useTabs": true + } + } +} diff --git a/cli/tests/testdata/fmt/Deno.malformed2.jsonc b/cli/tests/testdata/fmt/Deno.malformed2.jsonc new file mode 100644 index 00000000000000..2210dc0c753010 --- /dev/null +++ b/cli/tests/testdata/fmt/Deno.malformed2.jsonc @@ -0,0 +1,12 @@ +{ + "fmt": { + "files": { + "include": ["fmt/fmt_with_config/"], + "exclude": ["fmt/fmt_with_config/b.ts"], + "dont_know_this_field": {} + }, + "options": { + "useTabs": true + } + } +} diff --git a/cli/tests/testdata/fmt/fmt_with_malformed_config.out b/cli/tests/testdata/fmt/fmt_with_malformed_config.out new file mode 100644 index 00000000000000..1a55613ef2a05a --- /dev/null +++ b/cli/tests/testdata/fmt/fmt_with_malformed_config.out @@ -0,0 +1,4 @@ +error: Failed to parse "fmt" configuration + +Caused by: + unknown field `dont_know_this_field`, expected `options` or `files` diff --git a/cli/tests/testdata/fmt/fmt_with_malformed_config2.out b/cli/tests/testdata/fmt/fmt_with_malformed_config2.out new file mode 100644 index 00000000000000..948b6b5b85e3bc --- /dev/null +++ b/cli/tests/testdata/fmt/fmt_with_malformed_config2.out @@ -0,0 +1,4 @@ +error: Failed to parse "fmt" configuration + +Caused by: + unknown field `dont_know_this_field`, expected `include` or `exclude` From d7f2801edf6bc4feaaf5435d4b70f79ea472db9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Mon, 13 Sep 2021 14:34:09 +0200 Subject: [PATCH 08/14] add integration tests --- .dprint.json | 1 + cli/tests/integration/fmt_tests.rs | 15 +++++- cli/tests/testdata/fmt/deno.jsonc | 15 ++++++ cli/tests/testdata/fmt/fmt_with_config.out | 1 + cli/tests/testdata/fmt/fmt_with_config/a.ts | 46 +++++++++++++++++++ cli/tests/testdata/fmt/fmt_with_config/b.ts | 15 ++++++ cli/tests/testdata/fmt/fmt_with_config/c.md | 17 +++++++ .../fmt/fmt_with_config_and_flags.out | 1 + 8 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 cli/tests/testdata/fmt/deno.jsonc create mode 100644 cli/tests/testdata/fmt/fmt_with_config.out create mode 100644 cli/tests/testdata/fmt/fmt_with_config/a.ts create mode 100644 cli/tests/testdata/fmt/fmt_with_config/b.ts create mode 100644 cli/tests/testdata/fmt/fmt_with_config/c.md create mode 100644 cli/tests/testdata/fmt/fmt_with_config_and_flags.out diff --git a/.dprint.json b/.dprint.json index fcba86c7b9745e..56397dfd93a7f6 100644 --- a/.dprint.json +++ b/.dprint.json @@ -24,6 +24,7 @@ "cli/tests/testdata/badly_formatted.md", "cli/tests/testdata/badly_formatted.json", "cli/tests/testdata/byte_order_mark.ts", + "cli/tests/testdata/fmt/*", "cli/tsc/*typescript.js", "test_util/std", "test_util/wpt", diff --git a/cli/tests/integration/fmt_tests.rs b/cli/tests/integration/fmt_tests.rs index 38f553b838bb54..721125745facba 100644 --- a/cli/tests/integration/fmt_tests.rs +++ b/cli/tests/integration/fmt_tests.rs @@ -182,14 +182,25 @@ itest!(fmt_stdin_check_not_formatted { output_str: Some("Not formatted stdin\n"), }); +itest!(fmt_with_config { + args: "fmt --config fmt/deno.jsonc fmt/fmt_with_config/", + output: "fmt/fmt_with_config.out", +}); + +// Check if CLI flags take precedence +itest!(fmt_with_config_and_flags { + args: "fmt --config fmt/deno.jsonc --ignore=fmt/fmt_with_config/a.ts,fmt/fmt_with_config/b.ts", + output: "fmt/fmt_with_config_and_flags.out", +}); + itest!(fmt_with_malformed_config { - args: "fmt --config fmt/Deno.malformed.jsonc", + args: "fmt --config fmt/deno.malformed.jsonc", output: "fmt/fmt_with_malformed_config.out", exit_code: 1, }); itest!(fmt_with_malformed_config2 { - args: "fmt --config fmt/Deno.malformed2.jsonc", + args: "fmt --config fmt/deno.malformed2.jsonc", output: "fmt/fmt_with_malformed_config2.out", exit_code: 1, }); diff --git a/cli/tests/testdata/fmt/deno.jsonc b/cli/tests/testdata/fmt/deno.jsonc new file mode 100644 index 00000000000000..645bfcb5b0be2a --- /dev/null +++ b/cli/tests/testdata/fmt/deno.jsonc @@ -0,0 +1,15 @@ +{ + "fmt": { + "files": { + "include": ["fmt/fmt_with_config/"], + "exclude": ["fmt/fmt_with_config/b.ts"] + }, + "options": { + "useTabs": true, + "lineWidth": 40, + "indentWidth": 8, + "singleQuote": true, + "textWrap": "always" + } + } +} diff --git a/cli/tests/testdata/fmt/fmt_with_config.out b/cli/tests/testdata/fmt/fmt_with_config.out new file mode 100644 index 00000000000000..158c556c2968ff --- /dev/null +++ b/cli/tests/testdata/fmt/fmt_with_config.out @@ -0,0 +1 @@ +Checked 2 files diff --git a/cli/tests/testdata/fmt/fmt_with_config/a.ts b/cli/tests/testdata/fmt/fmt_with_config/a.ts new file mode 100644 index 00000000000000..e0f32647b62486 --- /dev/null +++ b/cli/tests/testdata/fmt/fmt_with_config/a.ts @@ -0,0 +1,46 @@ +unitTest( + { perms: { net: true } }, + async function responseClone() { + const response = + await fetch( + 'http://localhost:4545/fixture.json', + ); + const response1 = + response.clone(); + assert( + response !== + response1, + ); + assertEquals( + response.status, + response1 + .status, + ); + assertEquals( + response.statusText, + response1 + .statusText, + ); + const u8a = + new Uint8Array( + await response + .arrayBuffer(), + ); + const u8a1 = + new Uint8Array( + await response1 + .arrayBuffer(), + ); + for ( + let i = 0; + i < + u8a.byteLength; + i++ + ) { + assertEquals( + u8a[i], + u8a1[i], + ); + } + }, +); diff --git a/cli/tests/testdata/fmt/fmt_with_config/b.ts b/cli/tests/testdata/fmt/fmt_with_config/b.ts new file mode 100644 index 00000000000000..5c37d51d2877b3 --- /dev/null +++ b/cli/tests/testdata/fmt/fmt_with_config/b.ts @@ -0,0 +1,15 @@ +// This file should be excluded from formatting +unitTest( + { perms: { net: true } }, + async function fetchBodyUsedCancelStream() { + const response = await fetch( + "http://localhost:4545/fixture.json", + ); + assert(response.body !== null); + + assertEquals(response.bodyUsed, false); + const promise = response.body.cancel(); + assertEquals(response.bodyUsed, true); + await promise; + }, +); \ No newline at end of file diff --git a/cli/tests/testdata/fmt/fmt_with_config/c.md b/cli/tests/testdata/fmt/fmt_with_config/c.md new file mode 100644 index 00000000000000..012f7e3d492ef6 --- /dev/null +++ b/cli/tests/testdata/fmt/fmt_with_config/c.md @@ -0,0 +1,17 @@ +## Permissions + +Deno is secure by default. Therefore, +unless you specifically enable it, a +program run with Deno has no file, +network, or environment access. Access +to security sensitive functionality +requires that permisisons have been +granted to an executing script through +command line flags, or a runtime +permission prompt. + +For the following example `mod.ts` has +been granted read-only access to the +file system. It cannot write to the file +system, or perform any other security +sensitive functions. diff --git a/cli/tests/testdata/fmt/fmt_with_config_and_flags.out b/cli/tests/testdata/fmt/fmt_with_config_and_flags.out new file mode 100644 index 00000000000000..c05ac45a1e7e51 --- /dev/null +++ b/cli/tests/testdata/fmt/fmt_with_config_and_flags.out @@ -0,0 +1 @@ +Checked 1 file From a6aec02e92d968f661f793729eb609c54bb22bc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Mon, 13 Sep 2021 14:47:59 +0200 Subject: [PATCH 09/14] textWrap -> proseWrap --- cli/config_file.rs | 6 +++--- cli/tests/testdata/fmt/deno.jsonc | 2 +- cli/tests/testdata/lsp/deno.fmt.jsonc | 12 ++++++++++++ cli/tools/fmt.rs | 14 ++++++++------ 4 files changed, 24 insertions(+), 10 deletions(-) create mode 100644 cli/tests/testdata/lsp/deno.fmt.jsonc diff --git a/cli/config_file.rs b/cli/config_file.rs index a3664607984c38..dd002ca97ab8a7 100644 --- a/cli/config_file.rs +++ b/cli/config_file.rs @@ -291,7 +291,7 @@ pub struct LintConfig { #[derive(Clone, Copy, Debug, Deserialize)] #[serde(deny_unknown_fields, rename_all = "camelCase")] -pub enum TextWrap { +pub enum ProseWrap { Always, Never, Preserve, @@ -304,7 +304,7 @@ pub struct FmtOptionsConfig { pub line_width: Option, pub indent_width: Option, pub single_quote: Option, - pub text_wrap: Option, + pub prose_wrap: Option, } #[derive(Clone, Debug, Default, Deserialize)] @@ -488,7 +488,7 @@ mod tests { "lineWidth": 80, "indentWidth": 4, "singleQuote": true, - "textWrap": "preserve" + "proseWrap": "preserve" } } }"#; diff --git a/cli/tests/testdata/fmt/deno.jsonc b/cli/tests/testdata/fmt/deno.jsonc index 645bfcb5b0be2a..0e682cf3635fdb 100644 --- a/cli/tests/testdata/fmt/deno.jsonc +++ b/cli/tests/testdata/fmt/deno.jsonc @@ -9,7 +9,7 @@ "lineWidth": 40, "indentWidth": 8, "singleQuote": true, - "textWrap": "always" + "proseWrap": "always" } } } diff --git a/cli/tests/testdata/lsp/deno.fmt.jsonc b/cli/tests/testdata/lsp/deno.fmt.jsonc new file mode 100644 index 00000000000000..c7392574446369 --- /dev/null +++ b/cli/tests/testdata/lsp/deno.fmt.jsonc @@ -0,0 +1,12 @@ +{ + "fmt": { + "options": { + "useTabs": true, + "lineWidth": 40, + "indentWidth": 8, + "singleQuote": true, + "proseWrap": "always" + } + } + } + \ No newline at end of file diff --git a/cli/tools/fmt.rs b/cli/tools/fmt.rs index 2429bb8039b6cf..5f1507d19d4d5e 100644 --- a/cli/tools/fmt.rs +++ b/cli/tools/fmt.rs @@ -10,7 +10,7 @@ use crate::colors; use crate::config_file::FmtConfig; use crate::config_file::FmtOptionsConfig; -use crate::config_file::TextWrap; +use crate::config_file::ProseWrap; use crate::diff::diff; use crate::file_watcher; use crate::file_watcher::ResolutionResult; @@ -422,13 +422,15 @@ fn get_resolved_markdown_config( builder.line_width(line_width); } - if let Some(text_wrap) = options.text_wrap { - builder.text_wrap(match text_wrap { - TextWrap::Always => { + if let Some(prose_wrap) = options.prose_wrap { + builder.text_wrap(match prose_wrap { + ProseWrap::Always => { dprint_plugin_markdown::configuration::TextWrap::Always } - TextWrap::Never => dprint_plugin_markdown::configuration::TextWrap::Never, - TextWrap::Preserve => { + ProseWrap::Never => { + dprint_plugin_markdown::configuration::TextWrap::Never + } + ProseWrap::Preserve => { dprint_plugin_markdown::configuration::TextWrap::Maintain } }); From 5d4e4800a7eaee79f3854f5ccb7571b9cf19c9bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Mon, 13 Sep 2021 14:48:10 +0200 Subject: [PATCH 10/14] add LSP test --- cli/tests/integration/lsp_tests.rs | 71 ++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/cli/tests/integration/lsp_tests.rs b/cli/tests/integration/lsp_tests.rs index 762d8bb94ba403..85f2962815369c 100644 --- a/cli/tests/integration/lsp_tests.rs +++ b/cli/tests/integration/lsp_tests.rs @@ -3170,6 +3170,77 @@ fn lsp_format_markdown() { shutdown(&mut client); } +#[test] +fn lsp_format_with_config() { + let temp_dir = TempDir::new().expect("could not create temp dir"); + let mut params: lsp::InitializeParams = + serde_json::from_value(load_fixture("initialize_params.json")).unwrap(); + let deno_fmt_jsonc = + serde_json::to_vec_pretty(&load_fixture("deno.fmt.jsonc")).unwrap(); + fs::write(temp_dir.path().join("deno.fmt.jsonc"), deno_fmt_jsonc).unwrap(); + + params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap()); + if let Some(Value::Object(mut map)) = params.initialization_options { + map.insert("config".to_string(), json!("./deno.fmt.jsonc")); + params.initialization_options = Some(Value::Object(map)); + } + + let deno_exe = deno_exe_path(); + let mut client = LspClient::new(&deno_exe).unwrap(); + client + .write_request::<_, _, Value>("initialize", params) + .unwrap(); + + client + .write_notification( + "textDocument/didOpen", + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "export async function someVeryLongFunctionName() {\nconst response = fetch(\"http://localhost:4545/some/non/existent/path.json\");\nconsole.log(response.text());\nconsole.log(\"finished!\")\n}" + } + }), + ) + .unwrap(); + + let (maybe_res, maybe_err) = client + .write_request::<_, _, Value>( + "textDocument/formatting", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + }, + "options": {} + }), + ) + .unwrap(); + + eprintln!("{:#?}", maybe_err); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(json!([ + { + "range": { + "start": { "line": 0, "character": 1 }, + "end": { "line": 0, "character": 3 } + }, + "newText": "" + }, + { + "range": { + "start": { "line": 0, "character": 15 }, + "end": { "line": 0, "character": 15 } + }, + "newText": "\n" + } + ])) + ); + shutdown(&mut client); +} + #[test] fn lsp_markdown_no_diagnostics() { let mut client = init("initialize_params.json"); From f6c49b2f1b4631afcebf5df20ba68bc0b5cfb20e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Mon, 13 Sep 2021 18:05:08 +0200 Subject: [PATCH 11/14] try fix test --- cli/tests/integration/lsp_tests.rs | 112 ++++++++++++++++++++++++++--- 1 file changed, 102 insertions(+), 10 deletions(-) diff --git a/cli/tests/integration/lsp_tests.rs b/cli/tests/integration/lsp_tests.rs index 85f2962815369c..6dce35c6e55e87 100644 --- a/cli/tests/integration/lsp_tests.rs +++ b/cli/tests/integration/lsp_tests.rs @@ -3205,6 +3205,7 @@ fn lsp_format_with_config() { ) .unwrap(); + // The options below should be ignored in favor of configuration from config file. let (maybe_res, maybe_err) = client .write_request::<_, _, Value>( "textDocument/formatting", @@ -3212,31 +3213,122 @@ fn lsp_format_with_config() { "textDocument": { "uri": "file:///a/file.ts" }, - "options": {} + "options": { + "tabSize": 2, + "insertSpaces": true + } }), ) .unwrap(); - eprintln!("{:#?}", maybe_err); assert!(maybe_err.is_none()); assert_eq!( maybe_res, - Some(json!([ + Some(json!([{ + "range": { + "start": { + "line": 1, + "character": 0 + }, + "end": { + "line": 1, + "character": 0 + } + }, + "newText": "\t" + }, { "range": { - "start": { "line": 0, "character": 1 }, - "end": { "line": 0, "character": 3 } + "start": { + "line": 1, + "character": 23 + }, + "end": { + "line": 1, + "character": 24 + } }, - "newText": "" + "newText": "\n\t\t'" }, { "range": { - "start": { "line": 0, "character": 15 }, - "end": { "line": 0, "character": 15 } + "start": { + "line": 1, + "character": 73 + }, + "end": { + "line": 1, + "character": 74 + } + }, + "newText": "',\n\t" + }, + { + "range": { + "start": { + "line": 2, + "character": 0 + }, + "end": { + "line": 2, + "character": 0 + } + }, + "newText": "\t" + }, + { + "range": { + "start": { + "line": 3, + "character": 0 + }, + "end": { + "line": 3, + "character": 0 + } + }, + "newText": "\t" + }, + { + "range": { + "start": { + "line": 3, + "character": 12 + }, + "end": { + "line": 3, + "character": 13 + } + }, + "newText": "'" + }, + { + "range": { + "start": { + "line": 3, + "character": 22 + }, + "end": { + "line": 3, + "character": 24 + } + }, + "newText": "');" + }, + { + "range": { + "start": { + "line": 4, + "character": 1 + }, + "end": { + "line": 4, + "character": 1 + } }, "newText": "\n" - } - ])) + }] + )) ); shutdown(&mut client); } From 4267237c343724600c674f0973b840a3b4f83be5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Mon, 13 Sep 2021 18:48:31 +0200 Subject: [PATCH 12/14] fix tests --- cli/tests/integration/fmt_tests.rs | 8 ++++---- .../testdata/fmt/{ => regular}/formatted1.js | 0 .../testdata/fmt/{ => regular}/formatted2.ts | 0 .../testdata/fmt/{ => regular}/formatted3.md | 0 .../testdata/fmt/{ => regular}/formatted4.jsonc | 0 cli/tests/testdata/lsp/deno.fmt.jsonc | 17 ++++++++--------- 6 files changed, 12 insertions(+), 13 deletions(-) rename cli/tests/testdata/fmt/{ => regular}/formatted1.js (100%) rename cli/tests/testdata/fmt/{ => regular}/formatted2.ts (100%) rename cli/tests/testdata/fmt/{ => regular}/formatted3.md (100%) rename cli/tests/testdata/fmt/{ => regular}/formatted4.jsonc (100%) diff --git a/cli/tests/integration/fmt_tests.rs b/cli/tests/integration/fmt_tests.rs index 721125745facba..2d7451694b4965 100644 --- a/cli/tests/integration/fmt_tests.rs +++ b/cli/tests/integration/fmt_tests.rs @@ -129,25 +129,25 @@ fn fmt_ignore_unexplicit_files() { } itest!(fmt_check_tests_dir { - args: "fmt --check ./ --ignore=.test_coverage", + args: "fmt --check ./ --ignore=.test_coverage,fmt/fmt_with_config/", output: "fmt/expected_fmt_check_tests_dir.out", exit_code: 1, }); itest!(fmt_quiet_check_fmt_dir { - args: "fmt --check --quiet fmt/", + args: "fmt --check --quiet fmt/regular/", output_str: Some(""), exit_code: 0, }); itest!(fmt_check_formatted_files { - args: "fmt --check fmt/formatted1.js fmt/formatted2.ts fmt/formatted3.md fmt/formatted4.jsonc", + args: "fmt --check fmt/regular/formatted1.js fmt/regular/formatted2.ts fmt/regular/formatted3.md fmt/regular/formatted4.jsonc", output: "fmt/expected_fmt_check_formatted_files.out", exit_code: 0, }); itest!(fmt_check_ignore { - args: "fmt --check --ignore=fmt/formatted1.js fmt/", + args: "fmt --check --ignore=fmt/regular/formatted1.js fmt/regular/", output: "fmt/expected_fmt_check_ignore.out", exit_code: 0, }); diff --git a/cli/tests/testdata/fmt/formatted1.js b/cli/tests/testdata/fmt/regular/formatted1.js similarity index 100% rename from cli/tests/testdata/fmt/formatted1.js rename to cli/tests/testdata/fmt/regular/formatted1.js diff --git a/cli/tests/testdata/fmt/formatted2.ts b/cli/tests/testdata/fmt/regular/formatted2.ts similarity index 100% rename from cli/tests/testdata/fmt/formatted2.ts rename to cli/tests/testdata/fmt/regular/formatted2.ts diff --git a/cli/tests/testdata/fmt/formatted3.md b/cli/tests/testdata/fmt/regular/formatted3.md similarity index 100% rename from cli/tests/testdata/fmt/formatted3.md rename to cli/tests/testdata/fmt/regular/formatted3.md diff --git a/cli/tests/testdata/fmt/formatted4.jsonc b/cli/tests/testdata/fmt/regular/formatted4.jsonc similarity index 100% rename from cli/tests/testdata/fmt/formatted4.jsonc rename to cli/tests/testdata/fmt/regular/formatted4.jsonc diff --git a/cli/tests/testdata/lsp/deno.fmt.jsonc b/cli/tests/testdata/lsp/deno.fmt.jsonc index c7392574446369..a0a8517318b7d2 100644 --- a/cli/tests/testdata/lsp/deno.fmt.jsonc +++ b/cli/tests/testdata/lsp/deno.fmt.jsonc @@ -1,12 +1,11 @@ { - "fmt": { - "options": { - "useTabs": true, - "lineWidth": 40, - "indentWidth": 8, - "singleQuote": true, - "proseWrap": "always" - } + "fmt": { + "options": { + "useTabs": true, + "lineWidth": 40, + "indentWidth": 8, + "singleQuote": true, + "proseWrap": "always" } } - \ No newline at end of file +} From 2e75c3f842b2c925dfc2681752f85586fc5742dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Mon, 13 Sep 2021 18:49:05 +0200 Subject: [PATCH 13/14] lowercase config file --- .../testdata/fmt/{Deno.malformed.jsonc => deno.malformed.jsonc} | 0 .../testdata/fmt/{Deno.malformed2.jsonc => deno.malformed2.jsonc} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename cli/tests/testdata/fmt/{Deno.malformed.jsonc => deno.malformed.jsonc} (100%) rename cli/tests/testdata/fmt/{Deno.malformed2.jsonc => deno.malformed2.jsonc} (100%) diff --git a/cli/tests/testdata/fmt/Deno.malformed.jsonc b/cli/tests/testdata/fmt/deno.malformed.jsonc similarity index 100% rename from cli/tests/testdata/fmt/Deno.malformed.jsonc rename to cli/tests/testdata/fmt/deno.malformed.jsonc diff --git a/cli/tests/testdata/fmt/Deno.malformed2.jsonc b/cli/tests/testdata/fmt/deno.malformed2.jsonc similarity index 100% rename from cli/tests/testdata/fmt/Deno.malformed2.jsonc rename to cli/tests/testdata/fmt/deno.malformed2.jsonc From 06fdab52f3b6eb829568153bb8d17af8aef47240 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Mon, 13 Sep 2021 19:34:37 +0200 Subject: [PATCH 14/14] reset CI