Improve case insensitivity consistency (#10884)

# Description

Add an extension trait `IgnoreCaseExt` to nu_utils which adds some case
insensitivity helpers, and use them throughout nu to improve the
handling of case insensitivity. Proper case folding is done via unicase,
which is already a dependency via mime_guess from nu-command.

In actuality a lot of code still does `to_lowercase`, because unicase
only provides immediate comparison and doesn't expose a `to_folded_case`
yet. And since we do a lot of `contains`/`starts_with`/`ends_with`, it's
not sufficient to just have `eq_ignore_case`. But if we get access in
the future, this makes us ready to use it with a change in one place.

Plus, it's clearer what the purpose is at the call site to call
`to_folded_case` instead of `to_lowercase` if it's exclusively for the
purpose of case insensitive comparison, even if it just does
`to_lowercase` still.

# User-Facing Changes

- Some commands that were supposed to be case insensitive remained only
insensitive to ASCII case (a-z), and now are case insensitive w.r.t.
non-ASCII characters as well.

# Tests + Formatting

- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
- 🟢 `toolkit test`
- 🟢 `toolkit test stdlib`

---------

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>
This commit is contained in:
Christopher Durham
2023-11-08 17:58:54 -05:00
committed by GitHub
parent aed4b626b8
commit 0f600bc3f5
35 changed files with 176 additions and 122 deletions

View File

@ -28,7 +28,7 @@ pub fn decode(
bytes: &[u8],
) -> Result<Value, ShellError> {
// Workaround for a bug in the Encodings Specification.
let encoding = if encoding_name.item.to_lowercase() == "utf16" {
let encoding = if encoding_name.item.eq_ignore_ascii_case("utf16") {
parse_encoding(encoding_name.span, "utf-16")
} else {
parse_encoding(encoding_name.span, &encoding_name.item)
@ -45,7 +45,7 @@ pub fn encode(
ignore_errors: bool,
) -> Result<Value, ShellError> {
// Workaround for a bug in the Encodings Specification.
let encoding = if encoding_name.item.to_lowercase() == "utf16" {
let encoding = if encoding_name.item.eq_ignore_ascii_case("utf16") {
parse_encoding(encoding_name.span, "utf-16")
} else {
parse_encoding(encoding_name.span, &encoding_name.item)
@ -69,7 +69,7 @@ pub fn encode(
fn parse_encoding(span: Span, label: &str) -> Result<&'static Encoding, ShellError> {
// Workaround for a bug in the Encodings Specification.
let label = if label.to_lowercase() == "utf16" {
let label = if label.eq_ignore_ascii_case("utf16") {
"utf-16"
} else {
label

View File

@ -7,6 +7,7 @@ use nu_protocol::record;
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
};
use nu_utils::IgnoreCaseExt;
#[derive(Clone)]
pub struct SubCommand;
@ -153,11 +154,11 @@ fn action(
match case_insensitive {
true => {
if *not_contain {
!val.to_lowercase()
.contains(substring.to_lowercase().as_str())
!val.to_folded_case()
.contains(substring.to_folded_case().as_str())
} else {
val.to_lowercase()
.contains(substring.to_lowercase().as_str())
val.to_folded_case()
.contains(substring.to_folded_case().as_str())
}
}
false => {

View File

@ -5,6 +5,7 @@ use nu_protocol::ast::CellPath;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::Category;
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value};
use nu_utils::IgnoreCaseExt;
struct Arguments {
substring: String,
@ -98,7 +99,8 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
match input {
Value::String { val: s, .. } => {
let ends_with = if args.case_insensitive {
s.to_lowercase().ends_with(&args.substring.to_lowercase())
s.to_folded_case()
.ends_with(&args.substring.to_folded_case())
} else {
s.ends_with(&args.substring)
};

View File

@ -6,6 +6,7 @@ use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::Category;
use nu_protocol::Spanned;
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value};
use nu_utils::IgnoreCaseExt;
struct Arguments {
substring: String,
@ -111,7 +112,7 @@ fn action(
match input {
Value::String { val: s, .. } => {
let starts_with = if *case_insensitive {
s.to_lowercase().starts_with(&substring.to_lowercase())
s.to_folded_case().starts_with(&substring.to_folded_case())
} else {
s.starts_with(substring)
};