Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/new-dolls-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@biomejs/biome": patch
---

Slightly improved performance of [`noIrregularWhitespace`](https://biomejs.dev/linter/rules/no-irregular-whitespace/) by adding early return optimization and simplifying character detection logic.
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,11 @@ use biome_analyze::{
Ast, Rule, RuleDiagnostic, RuleSource, context::RuleContext, declare_lint_rule,
};
use biome_console::markup;
use biome_css_syntax::{AnyCssRule, CssLanguage};
use biome_css_syntax::{AnyCssRule, CssSyntaxNode};
use biome_diagnostics::Severity;
use biome_rowan::{AstNode, Direction, SyntaxToken, TextRange};
use biome_rowan::{AstNode, Direction, TextRange};
use biome_rule_options::no_irregular_whitespace::NoIrregularWhitespaceOptions;

const IRREGULAR_WHITESPACES: &[char; 22] = &[
'\u{c}', '\u{b}', '\u{85}', '\u{feff}', '\u{a0}', '\u{1680}', '\u{180e}', '\u{2000}',
'\u{2001}', '\u{2002}', '\u{2003}', '\u{2004}', '\u{2005}', '\u{2006}', '\u{2007}', '\u{2008}',
'\u{2009}', '\u{200a}', '\u{200b}', '\u{202f}', '\u{205f}', '\u{3000}',
];

declare_lint_rule! {
/// Disallows the use of irregular whitespace characters.
///
Expand Down Expand Up @@ -53,8 +47,7 @@ impl Rule for NoIrregularWhitespace {
type Options = NoIrregularWhitespaceOptions;

fn run(ctx: &RuleContext<Self>) -> Self::Signals {
let node = ctx.query();
get_irregular_whitespace(node).into_boxed_slice()
get_irregular_whitespace(ctx.query().syntax()).into_boxed_slice()
}

fn diagnostic(_: &RuleContext<Self>, range: &Self::State) -> Option<RuleDiagnostic> {
Expand All @@ -73,24 +66,31 @@ impl Rule for NoIrregularWhitespace {
}
}

fn get_irregular_whitespace(node: &AnyCssRule) -> Vec<TextRange> {
let syntax = node.syntax();
let mut all_whitespaces_token: Vec<TextRange> = vec![];
let matches_irregular_whitespace = |token: &SyntaxToken<CssLanguage>| {
!token.has_leading_comments()
&& !token.has_trailing_comments()
&& token.text().chars().any(|char| {
IRREGULAR_WHITESPACES
.iter()
.any(|irregular_whitespace| &char == irregular_whitespace)
})
};
fn is_irregular_whitespace(c: char) -> bool {
matches!(
c,
'\u{000B}' | '\u{000C}' | '\u{0085}' | '\u{00A0}' | '\u{1680}' | '\u{180E}' | '\u{2000}'
..='\u{200B}' | '\u{202F}' | '\u{205F}' | '\u{3000}' | '\u{FEFF}'
)
}

fn get_irregular_whitespace(syntax: &CssSyntaxNode) -> Vec<TextRange> {
if !syntax
.text_with_trivia()
.chars()
.any(is_irregular_whitespace)
{
return vec![];
}

let mut results = vec![];
for token in syntax.descendants_tokens(Direction::Next) {
if matches_irregular_whitespace(&token) {
all_whitespaces_token.push(token.text_range());
if !token.has_leading_comments()
&& !token.has_trailing_comments()
&& token.text().chars().any(is_irregular_whitespace)
{
results.push(token.text_range());
}
}

all_whitespaces_token
results
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,10 @@ use biome_analyze::RuleSource;
use biome_analyze::{Ast, Rule, RuleDiagnostic, context::RuleContext, declare_lint_rule};
use biome_console::markup;
use biome_diagnostics::Severity;
use biome_js_syntax::{AnyJsRoot, JsLanguage, JsSyntaxNode};
use biome_rowan::{AstNode, Direction, SyntaxTriviaPiece, TextRange};
use biome_js_syntax::{AnyJsRoot, JsSyntaxNode};
use biome_rowan::{AstNode, Direction, TextRange};
use biome_rule_options::no_irregular_whitespace::NoIrregularWhitespaceOptions;

const IRREGULAR_WHITESPACES: &[char; 22] = &[
'\u{c}', '\u{b}', '\u{85}', '\u{feff}', '\u{a0}', '\u{1680}', '\u{180e}', '\u{2000}',
'\u{2001}', '\u{2002}', '\u{2003}', '\u{2004}', '\u{2005}', '\u{2006}', '\u{2007}', '\u{2008}',
'\u{2009}', '\u{200a}', '\u{200b}', '\u{202f}', '\u{205f}', '\u{3000}',
];

declare_lint_rule! {
/// Disallows the use of irregular whitespace characters.
///
Expand Down Expand Up @@ -78,38 +72,41 @@ impl Rule for NoIrregularWhitespace {
}
}

fn is_irregular_whitespace(c: char) -> bool {
matches!(
c,
'\u{000B}'
| '\u{000C}'
| '\u{0085}'
| '\u{00A0}'
| '\u{1680}'
| '\u{180E}'
| '\u{2000}'..='\u{200B}'
| '\u{202F}'
| '\u{205F}'
| '\u{3000}'
| '\u{FEFF}'
)
}

fn get_irregular_whitespace(syntax: &JsSyntaxNode) -> Vec<TextRange> {
let mut all_whitespaces_trivia: Vec<SyntaxTriviaPiece<JsLanguage>> = vec![];
let is_whitespace = |trivia: &SyntaxTriviaPiece<JsLanguage>| {
trivia.is_whitespace() && !trivia.text().replace(' ', "").is_empty()
};
if !syntax.text_with_trivia().chars().any(is_irregular_whitespace) {
return vec![];
}

let mut results = vec![];
for token in syntax.descendants_tokens(Direction::Next) {
let leading_trivia_pieces = token.leading_trivia().pieces();
let trailing_trivia_pieces = token.trailing_trivia().pieces();

for trivia in leading_trivia_pieces {
if is_whitespace(&trivia) {
all_whitespaces_trivia.push(trivia);
}
}

for trivia in trailing_trivia_pieces {
if is_whitespace(&trivia) {
all_whitespaces_trivia.push(trivia);
for trivia in token
.leading_trivia()
.pieces()
.chain(token.trailing_trivia().pieces())
{
if trivia.is_whitespace()
&& trivia.text().chars().any(is_irregular_whitespace)
{
results.push(trivia.text_range());
}
}
}

all_whitespaces_trivia
.iter()
.filter_map(|trivia| {
let has_irregular_whitespace = trivia.text().chars().any(|char| {
IRREGULAR_WHITESPACES
.iter()
.any(|irregular_whitespace| &char == irregular_whitespace)
});
has_irregular_whitespace.then(|| trivia.text_range())
})
.collect::<Vec<TextRange>>()
results
}
Loading