From a4f08c117f7bb5eb3d77aa5e96b305a6d18ffec2 Mon Sep 17 00:00:00 2001 From: crauzer Date: Wed, 6 Oct 2021 23:24:47 +0200 Subject: [PATCH] Fix stdx::to_snake_case --- .../src/diagnostics/decl_check/case_conv.rs | 3 ++ crates/stdx/src/lib.rs | 51 ++++++++++++++----- 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs b/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs index 7c477d4945c..88d607194f7 100644 --- a/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs +++ b/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs @@ -161,6 +161,7 @@ mod tests { check(to_lower_snake_case, "lowerCamelCase", expect![["lower_camel_case"]]); check(to_lower_snake_case, "a", expect![[""]]); check(to_lower_snake_case, "abc", expect![[""]]); + check(to_lower_snake_case, "foo__bar", expect![["foo_bar"]]); } #[test] @@ -192,5 +193,7 @@ mod tests { check(to_upper_snake_case, "A", expect![[""]]); check(to_upper_snake_case, "ABC", expect![[""]]); check(to_upper_snake_case, "X86_64", expect![[""]]); + check(to_upper_snake_case, "FOO_BAr", expect![["FOO_BAR"]]); + check(to_upper_snake_case, "FOO__BAR", expect![["FOO_BAR"]]); } } diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs index bfa6024679b..ee23f579345 100644 --- a/crates/stdx/src/lib.rs +++ b/crates/stdx/src/lib.rs @@ -1,4 +1,5 @@ //! Missing batteries for standard libraries. +use std::iter; use std::{cmp::Ordering, ops, time::Instant}; mod macros; @@ -37,22 +38,44 @@ pub fn to_lower_snake_case(s: &str) -> String { pub fn to_upper_snake_case(s: &str) -> String { to_snake_case(s, char::to_ascii_uppercase) } -fn to_snake_case char>(s: &str, change_case: F) -> String { - let mut buf = String::with_capacity(s.len()); - let mut prev = false; - for c in s.chars() { - // `&& prev` is required to not insert `_` before the first symbol. - if c.is_ascii_uppercase() && prev { - // This check is required to not translate `Weird_Case` into `weird__case`. - if !buf.ends_with('_') { - buf.push('_'); - } - } - prev = true; - buf.push(change_case(&c)); +// Code partially taken from rust/compiler/rustc_lint/src/nonstandard_style.rs +// commit: 9626f2b +fn to_snake_case char>(mut s: &str, change_case: F) -> String { + let mut words = vec![]; + + // Preserve leading underscores + s = s.trim_start_matches(|c: char| { + if c == '_' { + words.push(String::new()); + true + } else { + false + } + }); + + for s in s.split('_') { + let mut last_upper = false; + let mut buf = String::new(); + + if s.is_empty() { + continue; + } + + for ch in s.chars() { + if !buf.is_empty() && buf != "'" && ch.is_uppercase() && !last_upper { + words.push(buf); + buf = String::new(); + } + + last_upper = ch.is_uppercase(); + buf.extend(iter::once(change_case(&ch))); + } + + words.push(buf); } - buf + + words.join("_") } pub fn replace(buf: &mut String, from: char, to: &str) {