Skip to content

Commit

Permalink
feat: add option flags to 'deno fmt' (denoland#12060)
Browse files Browse the repository at this point in the history
  • Loading branch information
bartlomieju committed Sep 13, 2021
1 parent 0dbeb77 commit cba1e7b
Show file tree
Hide file tree
Showing 3 changed files with 215 additions and 22 deletions.
162 changes: 159 additions & 3 deletions cli/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ use deno_runtime::permissions::PermissionsOptions;
use log::debug;
use log::Level;
use std::net::SocketAddr;
use std::num::NonZeroU32;
use std::num::NonZeroU8;
use std::num::NonZeroUsize;
use std::path::PathBuf;
use std::str::FromStr;
Expand Down Expand Up @@ -86,6 +88,11 @@ pub struct FmtFlags {
pub files: Vec<PathBuf>,
pub ignore: Vec<PathBuf>,
pub ext: String,
pub use_tabs: Option<bool>,
pub line_width: Option<NonZeroU32>,
pub indent_width: Option<NonZeroU8>,
pub single_quote: Option<bool>,
pub prose_wrap: Option<String>,
}

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
Expand Down Expand Up @@ -845,6 +852,47 @@ Ignore formatting a file by adding an ignore comment at the top of the file:
.required(false),
)
.arg(watch_arg())
.arg(
Arg::with_name("options-use-tabs")
.long("options-use-tabs")
.help("Use tabs instead of spaces for indentation. Defaults to false."),
)
.arg(
Arg::with_name("options-line-width")
.long("options-line-width")
.help("Define maximum line width. Defaults to 80.")
.takes_value(true)
.validator(|val: String| match val.parse::<NonZeroUsize>() {
Ok(_) => Ok(()),
Err(_) => {
Err("options-line-width should be a non zero integer".to_string())
}
}),
)
.arg(
Arg::with_name("options-indent-width")
.long("options-indent-width")
.help("Define indentation width. Defaults to 2.")
.takes_value(true)
.validator(|val: String| match val.parse::<NonZeroUsize>() {
Ok(_) => Ok(()),
Err(_) => {
Err("options-indent-width should be a non zero integer".to_string())
}
}),
)
.arg(
Arg::with_name("options-single-quote")
.long("options-single-quote")
.help("Use single quotes. Defaults to false."),
)
.arg(
Arg::with_name("options-prose-wrap")
.long("options-prose-wrap")
.takes_value(true)
.possible_values(&["always", "never", "preserve"])
.help("Define how prose should be wrapped. Defaults to always."),
)
}

fn info_subcommand<'a, 'b>() -> App<'a, 'b> {
Expand Down Expand Up @@ -1745,11 +1793,54 @@ fn fmt_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
};
let ext = matches.value_of("ext").unwrap().to_string();

let use_tabs = if matches.is_present("options-use-tabs") {
Some(true)
} else {
None
};
let line_width = if matches.is_present("options-line-width") {
Some(
matches
.value_of("options-line-width")
.unwrap()
.parse()
.unwrap(),
)
} else {
None
};
let indent_width = if matches.is_present("options-indent-width") {
Some(
matches
.value_of("options-indent-width")
.unwrap()
.parse()
.unwrap(),
)
} else {
None
};
let single_quote = if matches.is_present("options-single-quote") {
Some(true)
} else {
None
};
let prose_wrap = if matches.is_present("options-prose-wrap") {
Some(matches.value_of("options-prose-wrap").unwrap().to_string())
} else {
None
};

flags.subcommand = DenoSubcommand::Fmt(FmtFlags {
check: matches.is_present("check"),
ext,
files,
ignore,
use_tabs,
line_width,
indent_width,
single_quote,
prose_wrap,
});
}

Expand Down Expand Up @@ -2466,7 +2557,12 @@ mod tests {
PathBuf::from("script_1.ts"),
PathBuf::from("script_2.ts")
],
ext: "ts".to_string()
ext: "ts".to_string(),
use_tabs: None,
line_width: None,
indent_width: None,
single_quote: None,
prose_wrap: None,
}),
..Flags::default()
}
Expand All @@ -2481,6 +2577,11 @@ mod tests {
check: true,
files: vec![],
ext: "ts".to_string(),
use_tabs: None,
line_width: None,
indent_width: None,
single_quote: None,
prose_wrap: None,
}),
..Flags::default()
}
Expand All @@ -2495,6 +2596,11 @@ mod tests {
check: false,
files: vec![],
ext: "ts".to_string(),
use_tabs: None,
line_width: None,
indent_width: None,
single_quote: None,
prose_wrap: None,
}),
..Flags::default()
}
Expand All @@ -2509,6 +2615,11 @@ mod tests {
check: false,
files: vec![],
ext: "ts".to_string(),
use_tabs: None,
line_width: None,
indent_width: None,
single_quote: None,
prose_wrap: None,
}),
watch: true,
..Flags::default()
Expand All @@ -2531,6 +2642,11 @@ mod tests {
check: true,
files: vec![PathBuf::from("foo.ts")],
ext: "ts".to_string(),
use_tabs: None,
line_width: None,
indent_width: None,
single_quote: None,
prose_wrap: None,
}),
watch: true,
..Flags::default()
Expand All @@ -2545,7 +2661,12 @@ mod tests {
ignore: vec![],
check: false,
files: vec![],
ext: "ts".to_string()
ext: "ts".to_string(),
use_tabs: None,
line_width: None,
indent_width: None,
single_quote: None,
prose_wrap: None,
}),
config_path: Some("deno.jsonc".to_string()),
..Flags::default()
Expand All @@ -2567,13 +2688,48 @@ mod tests {
ignore: vec![],
check: false,
files: vec![PathBuf::from("foo.ts")],
ext: "ts".to_string()
ext: "ts".to_string(),
use_tabs: None,
line_width: None,
indent_width: None,
single_quote: None,
prose_wrap: None,
}),
config_path: Some("deno.jsonc".to_string()),
watch: true,
..Flags::default()
}
);

let r = flags_from_vec(svec![
"deno",
"fmt",
"--options-use-tabs",
"--options-line-width",
"60",
"--options-indent-width",
"4",
"--options-single-quote",
"--options-prose-wrap",
"never"
]);
assert_eq!(
r.unwrap(),
Flags {
subcommand: DenoSubcommand::Fmt(FmtFlags {
ignore: vec![],
check: false,
files: vec![],
ext: "ts".to_string(),
use_tabs: Some(true),
line_width: Some(NonZeroU32::new(60).unwrap()),
indent_width: Some(NonZeroU8::new(4).unwrap()),
single_quote: Some(true),
prose_wrap: Some("never".to_string()),
}),
..Flags::default()
}
);
}

#[test]
Expand Down
12 changes: 2 additions & 10 deletions cli/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -815,20 +815,12 @@ async fn format_command(

if fmt_flags.files.len() == 1 && fmt_flags.files[0].to_string_lossy() == "-" {
return tools::fmt::format_stdin(
fmt_flags.check,
fmt_flags.ext,
fmt_flags,
maybe_fmt_config.map(|c| c.options).unwrap_or_default(),
);
}

tools::fmt::format(
fmt_flags.files,
fmt_flags.ignore,
fmt_flags.check,
flags.watch,
maybe_fmt_config,
)
.await?;
tools::fmt::format(fmt_flags, flags.watch, maybe_fmt_config).await?;
Ok(())
}

Expand Down
63 changes: 54 additions & 9 deletions cli/tools/fmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use crate::config_file::ProseWrap;
use crate::diff::diff;
use crate::file_watcher;
use crate::file_watcher::ResolutionResult;
use crate::flags::FmtFlags;
use crate::fs_util::{collect_files, get_extension, is_supported_ext_fmt};
use crate::text_encoding;
use deno_ast::ParsedSource;
Expand All @@ -34,17 +35,22 @@ use std::sync::{Arc, Mutex};

/// Format JavaScript/TypeScript files.
pub async fn format(
args: Vec<PathBuf>,
ignore: Vec<PathBuf>,
check: bool,
fmt_flags: FmtFlags,
watch: bool,
maybe_fmt_config: Option<FmtConfig>,
) -> Result<(), AnyError> {
let FmtFlags {
files,
ignore,
check,
..
} = fmt_flags.clone();

// 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 include_files = files.clone();
let mut exclude_files = ignore;

if let Some(fmt_config) = maybe_fmt_config.as_ref() {
Expand All @@ -67,7 +73,11 @@ pub async fn format(
}
}

let fmt_options = maybe_fmt_config.map(|c| c.options).unwrap_or_default();
// Now do the same for options
let fmt_options = resolve_fmt_options(
&fmt_flags,
maybe_fmt_config.map(|c| c.options).unwrap_or_default(),
);

let resolver = |changed: Option<Vec<PathBuf>>| {
let files_changed = changed.is_some();
Expand Down Expand Up @@ -345,19 +355,19 @@ async fn format_source_files(
/// Treats input as TypeScript or as set by `--ext` flag.
/// Compatible with `--check` flag.
pub fn format_stdin(
check: bool,
ext: String,
fmt_flags: FmtFlags,
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));
let file_path = PathBuf::from(format!("_stdin.{}", fmt_flags.ext));
let fmt_options = resolve_fmt_options(&fmt_flags, fmt_options);

match format_file(&file_path, &source, fmt_options) {
Ok(formatted_text) => {
if check {
if fmt_flags.check {
if formatted_text != source {
println!("Not formatted stdin");
}
Expand All @@ -380,6 +390,41 @@ fn files_str(len: usize) -> &'static str {
}
}

fn resolve_fmt_options(
fmt_flags: &FmtFlags,
options: FmtOptionsConfig,
) -> FmtOptionsConfig {
let mut options = options;

if let Some(use_tabs) = fmt_flags.use_tabs {
options.use_tabs = Some(use_tabs);
}

if let Some(line_width) = fmt_flags.line_width {
options.line_width = Some(line_width.get());
}

if let Some(indent_width) = fmt_flags.indent_width {
options.indent_width = Some(indent_width.get());
}

if let Some(single_quote) = fmt_flags.single_quote {
options.single_quote = Some(single_quote);
}

if let Some(prose_wrap) = &fmt_flags.prose_wrap {
options.prose_wrap = Some(match prose_wrap.as_str() {
"always" => ProseWrap::Always,
"never" => ProseWrap::Never,
"preserve" => ProseWrap::Preserve,
// validators in `flags.rs` makes other values unreachable
_ => unreachable!(),
});
}

options
}

fn get_resolved_typescript_config(
options: &FmtOptionsConfig,
) -> dprint_plugin_typescript::configuration::Configuration {
Expand Down

0 comments on commit cba1e7b

Please sign in to comment.