From 45fa4459022999156451b79af822ecd9585505ab Mon Sep 17 00:00:00 2001 From: Reese Williams Date: Sun, 13 Nov 2022 16:23:16 -0800 Subject: [PATCH 1/2] Fixtures --- fixtures/small/gemfile_actual.rb | 12 ++++++++++++ fixtures/small/gemfile_expected.rb | 12 ++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 fixtures/small/gemfile_actual.rb create mode 100644 fixtures/small/gemfile_expected.rb diff --git a/fixtures/small/gemfile_actual.rb b/fixtures/small/gemfile_actual.rb new file mode 100644 index 00000000..0b65410e --- /dev/null +++ b/fixtures/small/gemfile_actual.rb @@ -0,0 +1,12 @@ +source "https://rubygems.org" +ruby "2.7.3" +gem "nokogiri" + +source "https://my_special_hosted_thing.io" do + gem "my_hosted_gem", '~>1.2.3' + gem "rake", :require => false +end + +group :test do + gem 'rspec' +end diff --git a/fixtures/small/gemfile_expected.rb b/fixtures/small/gemfile_expected.rb new file mode 100644 index 00000000..19bc132c --- /dev/null +++ b/fixtures/small/gemfile_expected.rb @@ -0,0 +1,12 @@ +source "https://rubygems.org" +ruby "2.7.3" +gem "nokogiri" + +source "https://my_special_hosted_thing.io" do + gem "my_hosted_gem", "~>1.2.3" + gem "rake", :require => false +end + +group :test do + gem "rspec" +end From 168d8b2628d04ff5850235c423b210683be5a2f6 Mon Sep 17 00:00:00 2001 From: Reese Williams Date: Sun, 13 Nov 2022 16:23:34 -0800 Subject: [PATCH 2/2] Allow Gemfile methods to stay without parens --- Cargo.lock | 1 + librubyfmt/Cargo.toml | 1 + librubyfmt/src/format.rs | 40 ++++++++++++++++++++++++++++++++++------ librubyfmt/src/lib.rs | 3 +++ 4 files changed, 39 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ada0da90..a621e25d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -608,6 +608,7 @@ dependencies = [ "backtrace", "cc", "jemallocator", + "lazy_static", "libc", "log", "ripper_deserialize", diff --git a/librubyfmt/Cargo.toml b/librubyfmt/Cargo.toml index 1a90c2f0..5bffb9b4 100644 --- a/librubyfmt/Cargo.toml +++ b/librubyfmt/Cargo.toml @@ -13,6 +13,7 @@ serde_json = "1.0.40" backtrace = "0.3.45" libc = "0.2.68" ripper_deserialize = { path = "ripper_deserialize" } +lazy_static = "1.4.0" log = { version = "0.4.8", features = ["max_level_debug", "release_max_level_warn"] } simplelog = "0.8" diff --git a/librubyfmt/src/format.rs b/librubyfmt/src/format.rs index db3bcd80..31a4a24e 100644 --- a/librubyfmt/src/format.rs +++ b/librubyfmt/src/format.rs @@ -1,3 +1,5 @@ +use std::collections::HashSet; + use crate::delimiters::BreakableDelims; use crate::heredoc_string::HeredocKind; use crate::parser_state::{BaseParserState, ConcreteParserState, FormattingContext, RenderFunc}; @@ -661,6 +663,21 @@ pub fn args_has_single_def_expression(args: &ArgsAddStarOrExpressionListOrArgsFo false } +lazy_static! { + static ref RSPEC_METHODS: HashSet<&'static str> = vec!["it", "describe"].into_iter().collect(); + static ref GEMFILE_METHODS: HashSet<&'static str> = vec![ + // Gemfile + "gem", + "source", + "ruby", + "group", + ].into_iter().collect(); + static ref OPTIONALLY_PARENTHESIZED_METHODS: HashSet<&'static str> = + vec!["super", "require", "require_relative",] + .into_iter() + .collect::>(); +} + pub fn use_parens_for_method_call( ps: &dyn ConcreteParserState, chain: &[CallChainElement], @@ -702,7 +719,9 @@ pub fn use_parens_for_method_call( } } - if name == "super" || name == "require" || name == "require_relative" { + if OPTIONALLY_PARENTHESIZED_METHODS.contains(name.as_str()) + || GEMFILE_METHODS.contains(name.as_str()) + { return original_used_parens; } @@ -2663,18 +2682,27 @@ pub fn format_backref(ps: &mut dyn ConcreteParserState, backref: Backref) { } } -fn can_elide_parens_for_rspec_dsl_call(cc: &[CallChainElement]) -> bool { +/// Matches call chains on common special-cased names, like +/// `it`/`describe` for tests and `gem`/`source`/etc. for Gemfiles. +fn can_elide_parens_for_reserved_names(cc: &[CallChainElement]) -> bool { if let Some(CallChainElement::Block(Block::BraceBlock(_))) = cc.last() { return false; }; - let is_bare_it_or_describe = match cc.get(0) { + let is_bare_reserved_method_name = match cc.get(0) { Some(CallChainElement::IdentOrOpOrKeywordOrConst(IdentOrOpOrKeywordOrConst::Ident( Ident(_, ident, _), - ))) => ident == "it" || ident == "describe", + ))) => { + let ident = ident.as_str(); + RSPEC_METHODS.contains(ident) || GEMFILE_METHODS.contains(ident) + } _ => false, }; + if is_bare_reserved_method_name { + return true; + } + let is_rspec_describe = match (cc.get(0), cc.get(2)) { ( Some(CallChainElement::VarRef(VarRef(_, VarRefType::Const(Const(_, c, _))))), @@ -2685,7 +2713,7 @@ fn can_elide_parens_for_rspec_dsl_call(cc: &[CallChainElement]) -> bool { _ => false, }; - is_bare_it_or_describe || is_rspec_describe + is_rspec_describe } /// Returns `true` if the call chain is indented, `false` if not @@ -2709,7 +2737,7 @@ fn format_call_chain_elements( cc: Vec, render_multiline_chain: bool, ) { - let elide_parens = can_elide_parens_for_rspec_dsl_call(&cc); + let elide_parens = can_elide_parens_for_reserved_names(&cc); let mut has_indented = false; // When set, force all `CallChainElement::ArgsAddStarOrExpressionListOrArgsForward` // to use parens, even when empty. This handles cases like `super()` where parens matter diff --git a/librubyfmt/src/lib.rs b/librubyfmt/src/lib.rs index 8c6085f0..c6ab5d18 100644 --- a/librubyfmt/src/lib.rs +++ b/librubyfmt/src/lib.rs @@ -6,6 +6,9 @@ use std::io::{Cursor, Write}; use std::slice; use std::str; +#[macro_use] +extern crate lazy_static; + #[cfg(all(feature = "use_jemalloc", not(target_env = "msvc")))] #[global_allocator] static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;