From 1616d55f3d27687f723bf06de7ea7dede5432f9d Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Thu, 25 Jan 2024 17:03:09 +0000 Subject: [PATCH 01/13] chore(linter): simplify visitor logic of `useSortedClasses` (#1667) --- .../nursery/use_sorted_classes.rs | 25 +- .../any_class_string_like.rs | 232 ++++-------------- .../nursery/use_sorted_classes/options.rs | 22 ++ .../nursery/use_sorted_classes/sort.rs | 11 +- 4 files changed, 86 insertions(+), 204 deletions(-) diff --git a/crates/biome_js_analyze/src/semantic_analyzers/nursery/use_sorted_classes.rs b/crates/biome_js_analyze/src/semantic_analyzers/nursery/use_sorted_classes.rs index 4d61fb14c4ec..f7aa9663a5d8 100644 --- a/crates/biome_js_analyze/src/semantic_analyzers/nursery/use_sorted_classes.rs +++ b/crates/biome_js_analyze/src/semantic_analyzers/nursery/use_sorted_classes.rs @@ -7,7 +7,7 @@ mod sort; mod sort_config; use biome_analyze::{ - context::RuleContext, declare_rule, ActionCategory, FixKind, Rule, RuleDiagnostic, + context::RuleContext, declare_rule, ActionCategory, Ast, FixKind, Rule, RuleDiagnostic, }; use biome_console::markup; use biome_diagnostics::Applicability; @@ -150,25 +150,24 @@ lazy_static! { } impl Rule for UseSortedClasses { - type Query = AnyClassStringLike; + type Query = Ast; type State = String; type Signals = Option; type Options = UtilityClassSortingOptions; fn run(ctx: &RuleContext) -> Option { - // TODO: unsure if options are needed here. The sort config should ideally be created once - // from the options and then reused for all queries. - // let options = &ctx.options(); + let options = ctx.options(); + let node = ctx.query(); - let value = ctx.query().value()?; - // TODO: the sort config should already exist at this point, and be generated from the options, - // including the preset and extended options as well. - let sorted_value = sort_class_name(&value, &SORT_CONFIG); - if value.text() != sorted_value { - Some(sorted_value) - } else { - None + if node.should_visit(options).is_some() { + if let Some(value) = node.value() { + let sorted_value = sort_class_name(&value, &SORT_CONFIG); + if value.text() != sorted_value { + return Some(sorted_value); + } + } } + None } fn diagnostic(ctx: &RuleContext, _: &Self::State) -> Option { diff --git a/crates/biome_js_analyze/src/semantic_analyzers/nursery/use_sorted_classes/any_class_string_like.rs b/crates/biome_js_analyze/src/semantic_analyzers/nursery/use_sorted_classes/any_class_string_like.rs index d2e5e8d3258e..6fe6ae4b5835 100644 --- a/crates/biome_js_analyze/src/semantic_analyzers/nursery/use_sorted_classes/any_class_string_like.rs +++ b/crates/biome_js_analyze/src/semantic_analyzers/nursery/use_sorted_classes/any_class_string_like.rs @@ -1,31 +1,9 @@ -use biome_analyze::{ - AddVisitor, AnalyzerOptions, Phases, QueryMatch, Queryable, RuleKey, ServiceBag, Visitor, - VisitorContext, -}; +use super::UtilityClassSortingOptions; use biome_js_syntax::{ - JsCallArguments, JsCallExpression, JsLanguage, JsStringLiteralExpression, - JsTemplateChunkElement, JsxAttribute, JsxString, + JsCallArguments, JsCallExpression, JsStringLiteralExpression, JsTemplateChunkElement, + JsxAttribute, JsxString, }; -use biome_rowan::{ - declare_node_union, AstNode, Language, SyntaxNode, TextRange, TokenText, WalkEvent, -}; - -use super::UtilityClassSortingOptions; - -// utils -// ----- - -fn get_options_from_analyzer(analyzer_options: &AnalyzerOptions) -> UtilityClassSortingOptions { - analyzer_options - .configuration - .rules - .get_rule_options::(&RuleKey::new( - "nursery", - "useSortedClasses", - )) - .cloned() - .unwrap_or_default() -} +use biome_rowan::{declare_node_union, AstNode, TokenText}; fn get_callee_name(call_expression: &JsCallExpression) -> Option { call_expression @@ -40,10 +18,9 @@ fn get_callee_name(call_expression: &JsCallExpression) -> Option { fn is_call_expression_of_target_function( call_expression: &JsCallExpression, - target_functions: &[String], + options: &UtilityClassSortingOptions, ) -> bool { - get_callee_name(call_expression) - .is_some_and(|name| target_functions.contains(&name.to_string())) + get_callee_name(call_expression).is_some_and(|name| options.has_function(name.text())) } fn get_attribute_name(attribute: &JsxAttribute) -> Option { @@ -58,180 +35,65 @@ fn get_attribute_name(attribute: &JsxAttribute) -> Option { ) } -fn is_target_attribute(attribute: &JsxAttribute, target_attributes: &[String]) -> bool { - match get_attribute_name(attribute) { - Some(name) => target_attributes.contains(&name.to_string()), - None => false, - } -} - -// attributes visitor -// ------------------ - -#[derive(Default)] -struct StringLiteralInAttributeVisitor { - in_target_attribute: bool, +declare_node_union! { + /// A string literal, JSX string, or template chunk representing a CSS class string. + pub AnyClassStringLike = JsStringLiteralExpression | JsxString | JsTemplateChunkElement } -// Finds class-like strings in JSX attributes, including class, className, and others defined in the options. -// TODO: match object properties too -impl Visitor for StringLiteralInAttributeVisitor { - type Language = JsLanguage; - fn visit( - &mut self, - event: &WalkEvent>, - mut ctx: VisitorContext, - ) { - let options = get_options_from_analyzer(ctx.options); - let attributes = match &options.attributes { - Some(attributes) => attributes, - None => return, - }; - match event { - WalkEvent::Enter(node) => { - // When entering an attribute node, track if we are in a target attribute. - if let Some(attribute) = JsxAttribute::cast_ref(node) { - self.in_target_attribute = is_target_attribute(&attribute, attributes); - } - - // When entering a JSX string node, and we are in a target attribute, emit. - if let Some(jsx_string) = JsxString::cast_ref(node) { - if self.in_target_attribute { - ctx.match_query(AnyClassStringLike::JsxString(jsx_string)); +impl AnyClassStringLike { + pub(crate) fn should_visit(&self, options: &UtilityClassSortingOptions) -> Option { + match self { + AnyClassStringLike::JsStringLiteralExpression(string_literal) => { + let mut in_arguments = false; + let mut in_function = false; + for ancestor in string_literal.syntax().ancestors().skip(1) { + if let Some(jsx_attribute) = JsxAttribute::cast_ref(&ancestor) { + let attribute_name = get_attribute_name(&jsx_attribute)?; + if options.has_attribute(attribute_name.text()) { + return Some(true); + } } - } - // When entering a string literal node, and we are in a target attribute, emit. - if let Some(string_literal) = JsStringLiteralExpression::cast_ref(node) { - if self.in_target_attribute { - ctx.match_query(AnyClassStringLike::JsStringLiteralExpression( - string_literal, - )); + if let Some(call_expression) = JsCallExpression::cast_ref(&ancestor) { + in_function = + is_call_expression_of_target_function(&call_expression, options); } - } - } - WalkEvent::Leave(node) => { - // When leaving an attribute node, reset in_target_attribute. - if JsxAttribute::cast_ref(node).is_some() { - self.in_target_attribute = false; - } - } - } - } -} - -// functions (call expression) visitor -// ----------------------------------- - -#[derive(Default)] -struct StringLiteralInCallExpressionVisitor { - in_target_function: bool, - in_arguments: bool, -} - -// Finds class-like strings inside function calls defined in the options, e.g. clsx(classes). -// TODO: match object properties too -impl Visitor for StringLiteralInCallExpressionVisitor { - type Language = JsLanguage; - - fn visit( - &mut self, - event: &WalkEvent>, - mut ctx: VisitorContext, - ) { - let options = get_options_from_analyzer(ctx.options); - let functions = match &options.functions { - Some(functions) => functions, - None => return, - }; - match event { - WalkEvent::Enter(node) => { - // When entering a call expression node, track if we are in a target function and reset - // in_arguments. - if let Some(call_expression) = JsCallExpression::cast_ref(node) { - self.in_target_function = - is_call_expression_of_target_function(&call_expression, functions); - self.in_arguments = false; - } - // When entering a call arguments node, set in_arguments. - if JsCallArguments::cast_ref(node).is_some() { - self.in_arguments = true; - } + if JsCallArguments::can_cast(ancestor.kind()) { + in_arguments = true; + } - // When entering a string literal node, and we are in a target function and in arguments, emit. - if let Some(string_literal) = JsStringLiteralExpression::cast_ref(node) { - if self.in_target_function && self.in_arguments { - ctx.match_query(AnyClassStringLike::JsStringLiteralExpression( - string_literal, - )); + if in_function && in_arguments { + return Some(true); } } + + None } - WalkEvent::Leave(node) => { - // When leaving a call arguments node, reset in_arguments. - if JsCallArguments::cast_ref(node).is_some() { - self.in_arguments = false; + AnyClassStringLike::JsxString(jsx_string) => { + let jsx_attribute = jsx_string + .syntax() + .ancestors() + .skip(1) + .find_map(JsxAttribute::cast)?; + let name = get_attribute_name(&jsx_attribute)?; + if options.has_attribute(name.text()) { + return Some(true); } + + None } + AnyClassStringLike::JsTemplateChunkElement(_) => None, } } -} - -// functions (template chunk) visitor -// ---------------------------------- - -// Finds class-like template chunks in tagged template calls defined in the options, e.g. tw`classes`. -// TODO: template chunk visitor - -// query -// ----- -declare_node_union! { - /// A string literal, JSX string, or template chunk representing a CSS class string. - pub AnyClassStringLike = JsStringLiteralExpression | JsxString | JsTemplateChunkElement -} - -impl AnyClassStringLike { - /// Returns the value of the string literal, JSX string, or template chunk. pub fn value(&self) -> Option { - match self { - Self::JsStringLiteralExpression(string_literal) => { - Some(string_literal.inner_string_text().ok()?) - } - Self::JsxString(jsx_string) => Some(jsx_string.inner_string_text().ok()?), - Self::JsTemplateChunkElement(template_chunk) => { + match &self { + AnyClassStringLike::JsStringLiteralExpression(node) => node.inner_string_text().ok(), + AnyClassStringLike::JsxString(node) => node.inner_string_text().ok(), + AnyClassStringLike::JsTemplateChunkElement(template_chunk) => { Some(template_chunk.template_chunk_token().ok()?.token_text()) } } } } - -impl QueryMatch for AnyClassStringLike { - fn text_range(&self) -> TextRange { - self.range() - } -} - -impl Queryable for AnyClassStringLike { - type Input = Self; - type Language = JsLanguage; - type Output = AnyClassStringLike; - type Services = (); - - fn build_visitor( - analyzer: &mut impl AddVisitor, - _: &::Root, - ) { - analyzer.add_visitor(Phases::Syntax, || { - StringLiteralInAttributeVisitor::default() - }); - analyzer.add_visitor(Phases::Syntax, || { - StringLiteralInCallExpressionVisitor::default() - }); - } - - fn unwrap_match(_: &ServiceBag, query: &Self::Input) -> Self::Output { - query.clone() - } -} diff --git a/crates/biome_js_analyze/src/semantic_analyzers/nursery/use_sorted_classes/options.rs b/crates/biome_js_analyze/src/semantic_analyzers/nursery/use_sorted_classes/options.rs index dfb0ff3adf0d..dca44aa0c002 100644 --- a/crates/biome_js_analyze/src/semantic_analyzers/nursery/use_sorted_classes/options.rs +++ b/crates/biome_js_analyze/src/semantic_analyzers/nursery/use_sorted_classes/options.rs @@ -31,6 +31,28 @@ impl Default for UtilityClassSortingOptions { } } +impl UtilityClassSortingOptions { + pub(crate) fn has_function(&self, name: &str) -> bool { + let iter = self.functions.iter().flatten(); + for v in iter { + if v.as_str() == name { + return true; + } + } + false + } + + pub(crate) fn has_attribute(&self, name: &str) -> bool { + let iter = self.attributes.iter().flatten(); + for v in iter { + if v.as_str() == name { + return true; + } + } + false + } +} + const ALLOWED_OPTIONS: &[&str] = &["attributes", "functions"]; impl Deserializable for UtilityClassSortingOptions { diff --git a/crates/biome_js_analyze/src/semantic_analyzers/nursery/use_sorted_classes/sort.rs b/crates/biome_js_analyze/src/semantic_analyzers/nursery/use_sorted_classes/sort.rs index 9efa87347ea6..a4e17c3cc517 100644 --- a/crates/biome_js_analyze/src/semantic_analyzers/nursery/use_sorted_classes/sort.rs +++ b/crates/biome_js_analyze/src/semantic_analyzers/nursery/use_sorted_classes/sort.rs @@ -1,6 +1,5 @@ -use std::cmp::Ordering; - use biome_rowan::TokenText; +use std::cmp::Ordering; use super::{ class_info::{get_class_info, ClassInfo}, @@ -91,16 +90,16 @@ pub fn sort_class_name(class_name: &TokenText, sort_config: &SortConfig) -> Stri // Custom classes always go first, in the order that they appear in. let mut sorted_classes: Vec<&str> = Vec::new(); let mut classes_info: Vec = Vec::new(); - classes - .iter() - .for_each(|&class| match get_class_info(class, sort_config) { + for class in classes { + match get_class_info(class, sort_config) { Some(class_info) => { classes_info.push(class_info); } None => { sorted_classes.push(class); } - }); + } + } // TODO: make this the last step of compare instead? From 6a768edb143ec8d971723b916e89ec933aa3dc70 Mon Sep 17 00:00:00 2001 From: mdm317 Date: Fri, 26 Jan 2024 02:05:01 +0900 Subject: [PATCH 02/13] fix(js_formatter): fix comment with single instruction within a control flow body (#1655) --- CHANGELOG.md | 2 + crates/biome_js_formatter/src/comments.rs | 8 +- ...ntinue-and-break-comment-without-blocks.js | 17 +- ...ak-comment-without-blocks.js.prettier-snap | 2 +- ...e-and-break-comment-without-blocks.js.snap | 216 ------------------ .../src/content/docs/internals/changelog.mdx | 2 + 6 files changed, 19 insertions(+), 228 deletions(-) delete mode 100644 crates/biome_js_formatter/tests/specs/prettier/js/for/continue-and-break-comment-without-blocks.js.snap diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b21a710a685..ae46ab65605c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ Read our [guidelines for writing a good changelog entry](https://github.com/biom - Fix [#1508](https://github.com/biomejs/biome/issues/1508) by excluding deleted files from being processed. Contributed by @ematipico +- Fix [#1173](https://github.com/biomejs/biome/issues/1173). Fix the formatting of a single instruction with commented in a control flow body to ensure consistency. Contributed by @mdm317 + ### Configuration ### Editors diff --git a/crates/biome_js_formatter/src/comments.rs b/crates/biome_js_formatter/src/comments.rs index 3016bc9d1038..9e93585f31c7 100644 --- a/crates/biome_js_formatter/src/comments.rs +++ b/crates/biome_js_formatter/src/comments.rs @@ -280,8 +280,12 @@ fn handle_continue_break_comment( let enclosing = comment.enclosing_node(); - if let Some(preceding) = comment.preceding_node() { - if preceding.kind() == JsSyntaxKind::JS_LABEL { + if let (Some(preceding), Some(parent)) = + (comment.preceding_node(), comment.enclosing_node().parent()) + { + if preceding.kind() == JsSyntaxKind::JS_LABEL + && parent.kind() != JsSyntaxKind::JS_FOR_STATEMENT + { return CommentPlacement::trailing(preceding.clone(), comment); } } diff --git a/crates/biome_js_formatter/tests/specs/prettier/js/for/continue-and-break-comment-without-blocks.js b/crates/biome_js_formatter/tests/specs/prettier/js/for/continue-and-break-comment-without-blocks.js index d7c99fb7ee3d..d68bba16e79c 100644 --- a/crates/biome_js_formatter/tests/specs/prettier/js/for/continue-and-break-comment-without-blocks.js +++ b/crates/biome_js_formatter/tests/specs/prettier/js/for/continue-and-break-comment-without-blocks.js @@ -85,14 +85,13 @@ for (const f in {}) break label1: for (;;) continue label1 /* comment */ ; -// FIXME: TODO: reformat issue -// label1: for (;;) continue label1 -// /* comment */ -// ; +label1: for (;;) continue label1 +/* comment */ +; -// label1: for (;;) continue label1 // comment -// ; +label1: for (;;) continue label1 // comment +; -// label1: for (;;) continue label1 -// // comment -// ; +label1: for (;;) continue label1 +// comment +; diff --git a/crates/biome_js_formatter/tests/specs/prettier/js/for/continue-and-break-comment-without-blocks.js.prettier-snap b/crates/biome_js_formatter/tests/specs/prettier/js/for/continue-and-break-comment-without-blocks.js.prettier-snap index 1eae371a4024..39ec64beac9f 100644 --- a/crates/biome_js_formatter/tests/specs/prettier/js/for/continue-and-break-comment-without-blocks.js.prettier-snap +++ b/crates/biome_js_formatter/tests/specs/prettier/js/for/continue-and-break-comment-without-blocks.js.prettier-snap @@ -58,7 +58,7 @@ for (const f in {}) continue; for (const f in {}) break; /* comment */ -label1: for (;;) continue label1 /* comment */; +label1: for (;;) continue label1; /* comment */ label1: for (;;) continue label1; /* comment */ diff --git a/crates/biome_js_formatter/tests/specs/prettier/js/for/continue-and-break-comment-without-blocks.js.snap b/crates/biome_js_formatter/tests/specs/prettier/js/for/continue-and-break-comment-without-blocks.js.snap deleted file mode 100644 index c0cfde75a4f9..000000000000 --- a/crates/biome_js_formatter/tests/specs/prettier/js/for/continue-and-break-comment-without-blocks.js.snap +++ /dev/null @@ -1,216 +0,0 @@ ---- -source: crates/biome_formatter_test/src/snapshot_builder.rs -info: js/for/continue-and-break-comment-without-blocks.js ---- - -# Input - -```js -for(;;) continue -// comment -; - -for (;;) break -// comment -; - -for (const f of []) continue -// comment -; - -for (const f of []) break -// comment -; - -for (const f in {}) continue -// comment -; - -for (const f in {}) break -// comment -; - -for(;;) continue // comment -; - -for (;;) break // comment -; - -for (const f of []) continue // comment -; - -for (const f of []) break // comment -; - -for (const f in {}) continue // comment -; - -for (const f in {}) break // comment -; - -for(;;) continue /* comment */ -; - -for (;;) break /* comment */ -; - -for (const f of []) continue /* comment */ -; - -for (const f of []) break /* comment */ -; - -for (const f in {}) continue /* comment */ -; - -for (const f in {}) break /* comment */ -; - -for(;;) continue -/* comment */ -; - -for (;;) break -/* comment */ -; - -for (const f of []) continue -/* comment */ -; - -for (const f of []) break -/* comment */ -; - -for (const f in {}) continue -/* comment */ -; - -for (const f in {}) break -/* comment */ -; - -label1: for (;;) continue label1 /* comment */ -; - -// FIXME: TODO: reformat issue -// label1: for (;;) continue label1 -// /* comment */ -// ; - -// label1: for (;;) continue label1 // comment -// ; - -// label1: for (;;) continue label1 -// // comment -// ; - -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Biome -@@ -60,10 +60,14 @@ - - label1: for (;;) continue label1 /* comment */; - --label1: for (;;) continue label1; --/* comment */ -+// FIXME: TODO: reformat issue -+// label1: for (;;) continue label1 -+// /* comment */ -+// ; - --label1: for (;;) continue label1; // comment -+// label1: for (;;) continue label1 // comment -+// ; - --label1: for (;;) continue label1; --// comment -+// label1: for (;;) continue label1 -+// // comment -+// ; -``` - -# Output - -```js -for (;;) continue; -// comment - -for (;;) break; -// comment - -for (const f of []) continue; -// comment - -for (const f of []) break; -// comment - -for (const f in {}) continue; -// comment - -for (const f in {}) break; -// comment - -for (;;) continue; // comment - -for (;;) break; // comment - -for (const f of []) continue; // comment - -for (const f of []) break; // comment - -for (const f in {}) continue; // comment - -for (const f in {}) break; // comment - -for (;;) continue; /* comment */ - -for (;;) break; /* comment */ - -for (const f of []) continue; /* comment */ - -for (const f of []) break; /* comment */ - -for (const f in {}) continue; /* comment */ - -for (const f in {}) break; /* comment */ - -for (;;) continue; -/* comment */ - -for (;;) break; -/* comment */ - -for (const f of []) continue; -/* comment */ - -for (const f of []) break; -/* comment */ - -for (const f in {}) continue; -/* comment */ - -for (const f in {}) break; -/* comment */ - -label1: for (;;) continue label1 /* comment */; - -// FIXME: TODO: reformat issue -// label1: for (;;) continue label1 -// /* comment */ -// ; - -// label1: for (;;) continue label1 // comment -// ; - -// label1: for (;;) continue label1 -// // comment -// ; -``` - - diff --git a/website/src/content/docs/internals/changelog.mdx b/website/src/content/docs/internals/changelog.mdx index 9bb2ef04b9bf..5f4f7b668bb3 100644 --- a/website/src/content/docs/internals/changelog.mdx +++ b/website/src/content/docs/internals/changelog.mdx @@ -24,6 +24,8 @@ Read our [guidelines for writing a good changelog entry](https://github.com/biom - Fix [#1508](https://github.com/biomejs/biome/issues/1508) by excluding deleted files from being processed. Contributed by @ematipico +- Fix [#1173](https://github.com/biomejs/biome/issues/1173). Fix the formatting of a single instruction with commented in a control flow body to ensure consistency. Contributed by @mdm317 + ### Configuration ### Editors From fce82bd94655ba3c94bdf2f1d617045ba1dd30e5 Mon Sep 17 00:00:00 2001 From: mongolyy Date: Fri, 26 Jan 2024 02:15:05 +0900 Subject: [PATCH 03/13] docs(website): update third-party tool version in continuous integration section (#1670) --- website/src/content/docs/recipes/continuous-integration.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/src/content/docs/recipes/continuous-integration.mdx b/website/src/content/docs/recipes/continuous-integration.mdx index 456f2241a6b2..1d0f50dc668f 100644 --- a/website/src/content/docs/recipes/continuous-integration.mdx +++ b/website/src/content/docs/recipes/continuous-integration.mdx @@ -35,7 +35,7 @@ jobs: These are actions maintained by other communities, that you use in your runner: -- [reviewdog-action-biome](https://github.com/marketplace/actions/run-biome-with-reviewdog): run Biome with reviewdog and make comments and commit suggestions on the pull request. +- [reviewdog-action-biome](https://github.com/marketplace/actions/run-biome-with-reviewdog): run Biome with reviewdog and make comments and commit suggestions on the pull request. ```yaml title="pull_request.yml" name: reviewdog @@ -49,7 +49,7 @@ jobs: pull-requests: write steps: - uses: actions/checkout@v4 - - uses: mongolyy/reviewdog-action-biome@v0 + - uses: mongolyy/reviewdog-action-biome@v1 with: github_token: ${{ secrets.github_token }} reporter: github-pr-review From ee013e527fd8403556fa05acb01d5470b4478b46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cosmo=20Shin=20=28=EC=8B=A0=EC=9D=98=ED=95=98=29?= Date: Fri, 26 Jan 2024 04:05:56 +0900 Subject: [PATCH 04/13] fix(lint/useExhaustiveDependencies): handle dynamic computed property access correctly (#1671) --- .../correctness/use_exhaustive_dependencies.rs | 9 +++------ .../correctness/useExhaustiveDependencies/valid.js | 14 ++++++++++++++ .../useExhaustiveDependencies/valid.js.snap | 14 ++++++++++++++ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/crates/biome_js_analyze/src/semantic_analyzers/correctness/use_exhaustive_dependencies.rs b/crates/biome_js_analyze/src/semantic_analyzers/correctness/use_exhaustive_dependencies.rs index 5d2d5a8da100..e83cfe5140dc 100644 --- a/crates/biome_js_analyze/src/semantic_analyzers/correctness/use_exhaustive_dependencies.rs +++ b/crates/biome_js_analyze/src/semantic_analyzers/correctness/use_exhaustive_dependencies.rs @@ -425,13 +425,10 @@ fn into_member_vec(node: &JsSyntaxNode) -> Vec { let member_name = member_expr .member_name() .and_then(|it| it.as_string_constant().map(|it| it.to_owned())); - match member_name { - Some(name) => { - vec.insert(0, name); - next = member_expr.object().ok().map(AstNode::into_syntax); - } - None => break, + if let Some(member_name) = member_name { + vec.insert(0, member_name); } + next = member_expr.object().ok().map(AstNode::into_syntax); } None => { vec.insert(0, node.text_trimmed().to_string()); diff --git a/crates/biome_js_analyze/tests/specs/correctness/useExhaustiveDependencies/valid.js b/crates/biome_js_analyze/tests/specs/correctness/useExhaustiveDependencies/valid.js index 1fd330dd96de..51395cd8313f 100644 --- a/crates/biome_js_analyze/tests/specs/correctness/useExhaustiveDependencies/valid.js +++ b/crates/biome_js_analyze/tests/specs/correctness/useExhaustiveDependencies/valid.js @@ -239,3 +239,17 @@ function MyComponent22() { outer = true; }, []) } + +// Dynamic computed property access +// https://github.com/biomejs/biome/issues/1637 +function MyComponent23 ({ arr }) { + useMemo(() => { + for (let i = 0; i < arr.length; i++) { + if (i + 1 === arr.length) { + // Do something with the last item + } else { + const _nextItem = arr[i + 1] + } + } + }, [arr]) +} diff --git a/crates/biome_js_analyze/tests/specs/correctness/useExhaustiveDependencies/valid.js.snap b/crates/biome_js_analyze/tests/specs/correctness/useExhaustiveDependencies/valid.js.snap index 5c723559bbcd..22b087d19cd5 100644 --- a/crates/biome_js_analyze/tests/specs/correctness/useExhaustiveDependencies/valid.js.snap +++ b/crates/biome_js_analyze/tests/specs/correctness/useExhaustiveDependencies/valid.js.snap @@ -246,6 +246,20 @@ function MyComponent22() { }, []) } +// Dynamic computed property access +// https://github.com/biomejs/biome/issues/1637 +function MyComponent23 ({ arr }) { + useMemo(() => { + for (let i = 0; i < arr.length; i++) { + if (i + 1 === arr.length) { + // Do something with the last item + } else { + const _nextItem = arr[i + 1] + } + } + }, [arr]) +} + ``` From db2a6c6b41269ea3b12db0b66f6e034613762013 Mon Sep 17 00:00:00 2001 From: Levi Eber <104157600+Levieber@users.noreply.github.com> Date: Thu, 25 Jan 2024 16:28:00 -0300 Subject: [PATCH 05/13] fix(cli): use biome instead of rome on pipe (#1673) --- crates/biome_cli/src/service/windows.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/biome_cli/src/service/windows.rs b/crates/biome_cli/src/service/windows.rs index 6e6e348e4696..66e6d599a96d 100644 --- a/crates/biome_cli/src/service/windows.rs +++ b/crates/biome_cli/src/service/windows.rs @@ -24,7 +24,7 @@ use tracing::Instrument; /// Returns the name of the global named pipe used to communicate with the /// server daemon fn get_pipe_name() -> String { - format!(r"\\.\pipe\rome-service-{}", biome_service::VERSION) + format!(r"\\.\pipe\biome-service-{}", biome_service::VERSION) } pub(crate) fn enumerate_pipes() -> io::Result> { From de49139575cbfd14a4805d679a54789e88f81e15 Mon Sep 17 00:00:00 2001 From: Victorien Elvinger Date: Thu, 25 Jan 2024 22:12:37 +0100 Subject: [PATCH 06/13] refactor(rowan): use niching for Checkpoint (#1675) --- crates/biome_rowan/src/tree_builder.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/crates/biome_rowan/src/tree_builder.rs b/crates/biome_rowan/src/tree_builder.rs index 4f33696d9292..f1588d9ef3dc 100644 --- a/crates/biome_rowan/src/tree_builder.rs +++ b/crates/biome_rowan/src/tree_builder.rs @@ -4,11 +4,21 @@ use crate::{ syntax::TriviaPiece, GreenNode, Language, NodeOrToken, ParsedChildren, SyntaxFactory, SyntaxKind, SyntaxNode, }; -use std::marker::PhantomData; +use std::{marker::PhantomData, num::NonZeroUsize}; /// A checkpoint for maybe wrapping a node. See `GreenNodeBuilder::checkpoint` for details. #[derive(Clone, Copy, Debug)] -pub struct Checkpoint(usize); +pub struct Checkpoint(NonZeroUsize); + +impl Checkpoint { + fn new(inner: usize) -> Self { + Self(NonZeroUsize::new(inner + 1).unwrap()) + } + + fn into_inner(self) -> usize { + self.0.get() - 1 + } +} /// A builder for a syntax tree. #[derive(Debug)] @@ -158,14 +168,14 @@ impl> TreeBuilder<'_, L, S> { /// ``` #[inline] pub fn checkpoint(&self) -> Checkpoint { - Checkpoint(self.children.len()) + Checkpoint::new(self.children.len()) } /// Wrap the previous branch marked by `checkpoint` in a new branch and /// make it current. #[inline] pub fn start_node_at(&mut self, checkpoint: Checkpoint, kind: L::Kind) { - let Checkpoint(checkpoint) = checkpoint; + let checkpoint = checkpoint.into_inner(); assert!( checkpoint <= self.children.len(), "checkpoint no longer valid, was finish_node called early?" From a94f26bd825323bfe7877484b32948ef27a4a110 Mon Sep 17 00:00:00 2001 From: Nicolas Hedger <649677+nhedger@users.noreply.github.com> Date: Thu, 25 Jan 2024 22:31:42 +0100 Subject: [PATCH 07/13] docs(internal): fix typos and reword some sentences (#1676) --- .../content/docs/internals/architecture.mdx | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/website/src/content/docs/internals/architecture.mdx b/website/src/content/docs/internals/architecture.mdx index 3564cc1571aa..c18f5c0c4a14 100644 --- a/website/src/content/docs/internals/architecture.mdx +++ b/website/src/content/docs/internals/architecture.mdx @@ -10,20 +10,20 @@ This document covers some of the internals of Biome, and how they are used insid The architecture of the parser is bumped by an internal fork of [rowan], a library that implements the [Green and Red tree] pattern. -The CST (Concrete Syntax Tree) is data structure very similar to AST (Abstract Syntax Tree) that keeps track of all the information of a program, trivia included. +The CST (Concrete Syntax Tree) is a data structure very similar to an AST (Abstract Syntax Tree) that keeps track of all the information of a program, trivia included. -**Trivia** are represented by all that information that are important to a program to run: +**Trivia** is represented by all that information that is important to a program to run: - spaces - tabs - comments -Trivia are attached to a node. A node can have leading trivia and trailing trivia. If you read code from left-to-right, leading trivia appear before a keyword, and trialing trivia appear after a keyword. +Trivia is attached to a node. A node can have leading trivia and trailing trivia. If you read code from left to right, leading trivia appears before a keyword, and trialing trivia appears after a keyword. Leading trivia and trailing trivia are categorized as follows: - Every trivia up to the token/keyword (including line breaks) will be the **leading trivia**; - Everything until the next linebreak (but not including it) will be the **trailing trivia**; -Given the following JavaScript snippet, `// comment 1` is a trailing trivia of the token `;`, and `// comment 2` is a leading trivia to the keyword `const`. Below a minimized version of the CST represented by Biome: : +Given the following JavaScript snippet, `// comment 1` is a trailing trivia of the token `;`, and `// comment 2` is a leading trivia to the keyword `const`. Below is a minimized version of the CST represented by Biome: ```js const a = "foo"; // comment 1 @@ -41,20 +41,20 @@ const b = "bar"; 3: EOF@55..55 "" [] [] ``` -The CST is never directly accessible by design, a developer can read its information using the Red tree, using a number of APIs that are autogenerated from the grammar of the language. +The CST is never directly accessible by design; a developer can read its information using the Red tree, using a number of APIs that are autogenerated from the grammar of the language. #### Resilient and recoverable parser -In order to construct a CST, a parser needs to be error resilient and recoverable: -- resilient: a parser that is able to resume parsing after encountering syntax error that belong to the language; -- recoverable: a parser that is able to **understand** where an error occurred, and being able to resume the parsing by creating **correct** information; +In order to construct a CST, a parser needs to be error-resilient and recoverable: +- resilient: a parser that is able to resume parsing after encountering syntax errors that belong to the language; +- recoverable: a parser that is able to **understand** where an error occurred and being able to resume the parsing by creating **correct** information; -The recoverable part of the parser is not a science, and there aren't any rules set on stone. This means that depending on what the parser was parsing and where an error occurred, the parser might be able to recover itself in an expected ways. +The recoverable part of the parser is not a science, and no rules are set in stone. This means that depending on what the parser was parsing and where an error occurred, the parser might be able to recover itself in an expected way. -To protect the consumers from consuming incorrect syntax, the parser also uses `Bogus` nodes. These nodes are used decorate the broken code caused by an syntax error. +The parser also uses' Bogus' nodes to protect the consumers from consuming incorrect syntax. These nodes are used to decorate the broken code caused by a syntax error. -In the following example, the parenthesis in the `while` are missing, although parser is able to recover itself in a good manner, and it's able to represent the code with a decent CST. The parenthesis and condition of the loop are marked as missing, and the code block is correctly parsed: +In the following example, the parentheses in the `while` are missing, although the parser can recover itself in a good manner and can represent the code with a decent CST. The parenthesis and condition of the loop are marked as missing, and the code block is correctly parsed: ```js @@ -82,7 +82,7 @@ JsModule { } ``` -This is error emitted during parsing: +This is an error emitted during parsing: ``` main.tsx:1:7 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ From 9aece50f85160b7073e07ce49560608a9b62b6bd Mon Sep 17 00:00:00 2001 From: ty <62130798+togami2864@users.noreply.github.com> Date: Fri, 26 Jan 2024 21:08:24 +0900 Subject: [PATCH 08/13] fix(lint/useForOf): handle shorthand property (#1666) --- CHANGELOG.md | 2 ++ .../semantic_analyzers/nursery/use_for_of.rs | 19 ++++++++++++------- .../tests/specs/nursery/useForOf/valid.js | 4 ++++ .../specs/nursery/useForOf/valid.js.snap | 4 ++++ .../src/content/docs/internals/changelog.mdx | 2 ++ 5 files changed, 24 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae46ab65605c..6b678810896b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,6 +74,8 @@ Read our [guidelines for writing a good changelog entry](https://github.com/biom - Fix [#1640](https://github.com/biomejs/biome/issues/1640). [useEnumInitializers](https://biomejs.dev/linter/rules/use-enum-initializers) code action now generates valid code when last member has a comment but no comma. Contributed by @kalleep +- Fix [#1653](https://github.com/biomejs/biome/issues/1653). Handle a shorthand value in `useForOf` to avoid the false-positive case. Contributed by @togami2864 + ### Parser ## 1.5.3 (2024-01-22) diff --git a/crates/biome_js_analyze/src/semantic_analyzers/nursery/use_for_of.rs b/crates/biome_js_analyze/src/semantic_analyzers/nursery/use_for_of.rs index 9821cc890254..3431cf65df2b 100644 --- a/crates/biome_js_analyze/src/semantic_analyzers/nursery/use_for_of.rs +++ b/crates/biome_js_analyze/src/semantic_analyzers/nursery/use_for_of.rs @@ -2,10 +2,11 @@ use biome_analyze::{context::RuleContext, declare_rule, Rule, RuleDiagnostic, Ru use biome_console::markup; use biome_js_semantic::{ReferencesExtensions, SemanticModel}; use biome_js_syntax::{ - AnyJsExpression, AnyJsForInitializer, AnyJsStatement, JsAssignmentExpression, - JsAssignmentOperator, JsBinaryExpression, JsBinaryOperator, JsForStatement, - JsIdentifierBinding, JsIdentifierExpression, JsPostUpdateExpression, JsPostUpdateOperator, - JsPreUpdateExpression, JsPreUpdateOperator, JsSyntaxKind, JsSyntaxToken, JsUnaryOperator, + AnyJsExpression, AnyJsForInitializer, AnyJsObjectMember, AnyJsStatement, + JsAssignmentExpression, JsAssignmentOperator, JsBinaryExpression, JsBinaryOperator, + JsForStatement, JsIdentifierBinding, JsIdentifierExpression, JsPostUpdateExpression, + JsPostUpdateOperator, JsPreUpdateExpression, JsPreUpdateOperator, + JsShorthandPropertyObjectMember, JsSyntaxKind, JsSyntaxToken, JsUnaryOperator, JsVariableDeclarator, }; use biome_rowan::{declare_node_union, AstNode, AstSeparatedList, TextRange}; @@ -52,7 +53,7 @@ declare_node_union! { } declare_node_union! { - pub(crate) AnyBindingExpression = JsPostUpdateExpression | JsPreUpdateExpression | JsIdentifierExpression + pub(crate) AnyBindingExpression = JsPostUpdateExpression | JsPreUpdateExpression | JsIdentifierExpression | JsShorthandPropertyObjectMember } impl Rule for UseForOf { @@ -99,7 +100,6 @@ impl Rule for UseForOf { .as_js_static_member_expression()? .object() .ok()?; - let index_only_used_with_array = |reference| { let array_in_use = reference_being_used_by_array(reference, &array_used_in_for) .is_some_and(|array_in_use| array_in_use); @@ -107,7 +107,6 @@ impl Rule for UseForOf { array_in_use && !is_delete }; - if references.iter().all(index_only_used_with_array) { Some(()) } else { @@ -147,6 +146,12 @@ fn list_initializer_references( return None; } + if let Some(AnyJsObjectMember::JsShorthandPropertyObjectMember(expr)) = + AnyJsObjectMember::cast(reference.syntax().parent()?) + { + return Some(AnyBindingExpression::from(expr)); + } + AnyBindingExpression::try_from(AnyJsExpression::cast(reference.syntax().parent()?)?) .ok() }) diff --git a/crates/biome_js_analyze/tests/specs/nursery/useForOf/valid.js b/crates/biome_js_analyze/tests/specs/nursery/useForOf/valid.js index de11bf17fb97..969f3f06aaa3 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useForOf/valid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/useForOf/valid.js @@ -12,6 +12,10 @@ for (let i = 0; i < arr.length; i++) { arr[i] = 0; } +for (let i = 0; i < array.length; i++) { + console.log({ i }); +} + for (var c = 0; c < arr.length; c++) { doMath(c); } diff --git a/crates/biome_js_analyze/tests/specs/nursery/useForOf/valid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useForOf/valid.js.snap index 0f29e14c007c..be174fe5cbf7 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useForOf/valid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useForOf/valid.js.snap @@ -18,6 +18,10 @@ for (let i = 0; i < arr.length; i++) { arr[i] = 0; } +for (let i = 0; i < array.length; i++) { + console.log({ i }); +} + for (var c = 0; c < arr.length; c++) { doMath(c); } diff --git a/website/src/content/docs/internals/changelog.mdx b/website/src/content/docs/internals/changelog.mdx index 5f4f7b668bb3..f51516915638 100644 --- a/website/src/content/docs/internals/changelog.mdx +++ b/website/src/content/docs/internals/changelog.mdx @@ -80,6 +80,8 @@ Read our [guidelines for writing a good changelog entry](https://github.com/biom - Fix [#1640](https://github.com/biomejs/biome/issues/1640). [useEnumInitializers](https://biomejs.dev/linter/rules/use-enum-initializers) code action now generates valid code when last member has a comment but no comma. Contributed by @kalleep +- Fix [#1653](https://github.com/biomejs/biome/issues/1653). Handle a shorthand value in `useForOf` to avoid the false-positive case. Contributed by @togami2864 + ### Parser ## 1.5.3 (2024-01-22) From 4b44057ad10170d17fcb3503f765a6ebfa218426 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Fri, 26 Jan 2024 12:20:19 +0000 Subject: [PATCH 09/13] fix(lsp): don't format ignored files (#1680) --- CHANGELOG.md | 4 + crates/biome_lsp/src/handlers/formatting.rs | 201 +++++++++++------- crates/biome_lsp/src/server.rs | 31 ++- crates/biome_lsp/src/session.rs | 11 +- crates/biome_lsp/tests/server.rs | 82 ++++++- crates/biome_service/src/workspace/server.rs | 6 +- .../src/content/docs/internals/changelog.mdx | 4 + 7 files changed, 242 insertions(+), 97 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b678810896b..c9404e0af196 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,10 @@ Read our [guidelines for writing a good changelog entry](https://github.com/biom ### Editors +#### Bug fixes + +- Fix a regression where ignored files where formatted in the editor. Contributed by @ematipico + ### Formatter #### Bug fixes diff --git a/crates/biome_lsp/src/handlers/formatting.rs b/crates/biome_lsp/src/handlers/formatting.rs index ae5a5e226f88..f49a89bee1ac 100644 --- a/crates/biome_lsp/src/handlers/formatting.rs +++ b/crates/biome_lsp/src/handlers/formatting.rs @@ -2,7 +2,12 @@ use crate::converters::{from_proto, to_proto}; use crate::diagnostics::LspError; use crate::session::Session; use anyhow::Context; -use biome_service::workspace::{FormatFileParams, FormatOnTypeParams, FormatRangeParams}; +use biome_fs::RomePath; +use biome_service::workspace::{ + FeatureName, FeaturesBuilder, FileFeaturesResult, FormatFileParams, FormatOnTypeParams, + FormatRangeParams, SupportsFeatureParams, +}; +use biome_service::{extension_error, WorkspaceError}; use tower_lsp::lsp_types::*; use tracing::debug; @@ -16,27 +21,36 @@ pub(crate) fn format( let doc = session.document(&url)?; - debug!("Formatting..."); - let printed = session - .workspace - .format_file(FormatFileParams { path: rome_path })?; + let file_features = session.workspace.file_features(SupportsFeatureParams { + path: rome_path.clone(), + feature: FeaturesBuilder::new().with_formatter().build(), + })?; - let num_lines: u32 = doc.line_index.len(); + if file_features.supports_for(&FeatureName::Format) { + debug!("Formatting..."); + let printed = session + .workspace + .format_file(FormatFileParams { path: rome_path })?; - let range = Range { - start: Position::default(), - end: Position { - line: num_lines, - character: 0, - }, - }; + let num_lines: u32 = doc.line_index.len(); + + let range = Range { + start: Position::default(), + end: Position { + line: num_lines, + character: 0, + }, + }; - let edits = vec![TextEdit { - range, - new_text: printed.into_code(), - }]; + let edits = vec![TextEdit { + range, + new_text: printed.into_code(), + }]; - Ok(Some(edits)) + Ok(Some(edits)) + } else { + notify_user(file_features, rome_path) + } } #[tracing::instrument(level = "debug", skip(session), err)] @@ -46,41 +60,51 @@ pub(crate) fn format_range( ) -> Result>, LspError> { let url = params.text_document.uri; let rome_path = session.file_path(&url)?; - let doc = session.document(&url)?; - let position_encoding = session.position_encoding(); - let format_range = from_proto::text_range(&doc.line_index, params.range, position_encoding) - .with_context(|| { - format!( - "failed to convert range {:?} in document {url}", - params.range.end - ) - })?; - - let formatted = session.workspace.format_range(FormatRangeParams { - path: rome_path, - range: format_range, + let file_features = session.workspace.file_features(SupportsFeatureParams { + path: rome_path.clone(), + feature: FeaturesBuilder::new().with_formatter().build(), })?; - // Recalculate the actual range that was reformatted from the formatter result - let formatted_range = match formatted.range() { - Some(range) => { - let position_encoding = session.position_encoding(); - to_proto::range(&doc.line_index, range, position_encoding)? - } - None => Range { - start: Position::default(), - end: Position { - line: doc.line_index.len(), - character: 0, - }, - }, - }; + if file_features.supports_for(&FeatureName::Format) { + let doc = session.document(&url)?; + + let position_encoding = session.position_encoding(); + let format_range = from_proto::text_range(&doc.line_index, params.range, position_encoding) + .with_context(|| { + format!( + "failed to convert range {:?} in document {url}", + params.range.end + ) + })?; + + let formatted = session.workspace.format_range(FormatRangeParams { + path: rome_path, + range: format_range, + })?; - Ok(Some(vec![TextEdit { - range: formatted_range, - new_text: formatted.into_code(), - }])) + // Recalculate the actual range that was reformatted from the formatter result + let formatted_range = match formatted.range() { + Some(range) => { + let position_encoding = session.position_encoding(); + to_proto::range(&doc.line_index, range, position_encoding)? + } + None => Range { + start: Position::default(), + end: Position { + line: doc.line_index.len(), + character: 0, + }, + }, + }; + + Ok(Some(vec![TextEdit { + range: formatted_range, + new_text: formatted.into_code(), + }])) + } else { + notify_user(file_features, rome_path) + } } #[tracing::instrument(level = "debug", skip(session), err)] @@ -92,39 +116,62 @@ pub(crate) fn format_on_type( let position = params.text_document_position.position; let rome_path = session.file_path(&url)?; - let doc = session.document(&url)?; - let position_encoding = session.position_encoding(); - let offset = from_proto::offset(&doc.line_index, position, position_encoding) - .with_context(|| format!("failed to access position {position:?} in document {url}"))?; - - let formatted = session.workspace.format_on_type(FormatOnTypeParams { - path: rome_path, - offset, + let file_features = session.workspace.file_features(SupportsFeatureParams { + path: rome_path.clone(), + feature: FeaturesBuilder::new().with_formatter().build(), })?; - // Recalculate the actual range that was reformatted from the formatter result - let formatted_range = match formatted.range() { - Some(range) => { - let position_encoding = session.position_encoding(); - let start_loc = to_proto::position(&doc.line_index, range.start(), position_encoding)?; - let end_loc = to_proto::position(&doc.line_index, range.end(), position_encoding)?; - Range { - start: start_loc, - end: end_loc, + if file_features.supports_for(&FeatureName::Format) { + let doc = session.document(&url)?; + + let position_encoding = session.position_encoding(); + let offset = from_proto::offset(&doc.line_index, position, position_encoding) + .with_context(|| format!("failed to access position {position:?} in document {url}"))?; + + let formatted = session.workspace.format_on_type(FormatOnTypeParams { + path: rome_path, + offset, + })?; + + // Recalculate the actual range that was reformatted from the formatter result + let formatted_range = match formatted.range() { + Some(range) => { + let position_encoding = session.position_encoding(); + let start_loc = + to_proto::position(&doc.line_index, range.start(), position_encoding)?; + let end_loc = to_proto::position(&doc.line_index, range.end(), position_encoding)?; + Range { + start: start_loc, + end: end_loc, + } } - } - None => Range { - start: Position::default(), - end: Position { - line: doc.line_index.len(), - character: 0, + None => Range { + start: Position::default(), + end: Position { + line: doc.line_index.len(), + character: 0, + }, }, - }, + }; + + Ok(Some(vec![TextEdit { + range: formatted_range, + new_text: formatted.into_code(), + }])) + } else { + notify_user(file_features, rome_path) + } +} + +fn notify_user(file_features: FileFeaturesResult, rome_path: RomePath) -> Result { + let error = if file_features.is_ignored() { + WorkspaceError::file_ignored(rome_path.display().to_string()) + } else if file_features.is_protected() { + WorkspaceError::protected_file(rome_path.display().to_string()) + } else { + extension_error(&rome_path) }; - Ok(Some(vec![TextEdit { - range: formatted_range, - new_text: formatted.into_code(), - }])) + Err(error.into()) } diff --git a/crates/biome_lsp/src/server.rs b/crates/biome_lsp/src/server.rs index 9751874466a5..865a88ca30dd 100644 --- a/crates/biome_lsp/src/server.rs +++ b/crates/biome_lsp/src/server.rs @@ -8,9 +8,9 @@ use crate::utils::{into_lsp_error, panic_to_lsp_error}; use crate::{handlers, requests}; use biome_console::markup; use biome_diagnostics::panic::PanicError; -use biome_fs::{BIOME_JSON, ROME_JSON}; +use biome_fs::{FileSystem, OsFileSystem, BIOME_JSON, ROME_JSON}; use biome_service::workspace::{RageEntry, RageParams, RageResult}; -use biome_service::{workspace, Workspace}; +use biome_service::{workspace, DynRef, Workspace}; use futures::future::ready; use futures::FutureExt; use rustc_hash::FxHashMap; @@ -270,7 +270,7 @@ impl LanguageServer for LSPServer { Ok(init) } - #[tracing::instrument(level = "debug", skip(self))] + #[tracing::instrument(level = "trace", skip(self))] async fn initialized(&self, params: InitializedParams) { let _ = params; @@ -297,16 +297,16 @@ impl LanguageServer for LSPServer { Ok(()) } - /// Called when the user changed the editor settings. - #[tracing::instrument(level = "debug", skip(self))] + #[tracing::instrument(level = "trace", skip(self))] async fn did_change_configuration(&self, params: DidChangeConfigurationParams) { let _ = params; + self.session.load_workspace_settings().await; self.session.load_extension_settings().await; self.setup_capabilities().await; self.session.update_all_diagnostics().await; } - #[tracing::instrument(level = "debug", skip(self))] + #[tracing::instrument(level = "trace", skip(self))] async fn did_change_watched_files(&self, params: DidChangeWatchedFilesParams) { let file_paths = params .changes @@ -510,8 +510,16 @@ impl ServerFactory { } } - /// Create a new [ServerConnection] from this factory pub fn create(&self, config_path: Option) -> ServerConnection { + self.create_with_fs(config_path, DynRef::Owned(Box::::default())) + } + + /// Create a new [ServerConnection] from this factory + pub fn create_with_fs( + &self, + config_path: Option, + fs: DynRef<'static, dyn FileSystem>, + ) -> ServerConnection { let workspace = self .workspace .clone() @@ -520,8 +528,13 @@ impl ServerFactory { let session_key = SessionKey(self.next_session_key.fetch_add(1, Ordering::Relaxed)); let mut builder = LspService::build(move |client| { - let mut session = - Session::new(session_key, client, workspace, self.cancellation.clone()); + let mut session = Session::new( + session_key, + client, + workspace, + self.cancellation.clone(), + fs, + ); if let Some(path) = config_path { session.set_config_path(path); } diff --git a/crates/biome_lsp/src/session.rs b/crates/biome_lsp/src/session.rs index 4854be889bbc..636b2f4be61f 100644 --- a/crates/biome_lsp/src/session.rs +++ b/crates/biome_lsp/src/session.rs @@ -7,7 +7,7 @@ use anyhow::Result; use biome_analyze::RuleCategories; use biome_console::markup; use biome_diagnostics::PrintDescription; -use biome_fs::{FileSystem, OsFileSystem, RomePath}; +use biome_fs::{FileSystem, RomePath}; use biome_service::configuration::{load_configuration, LoadedConfiguration}; use biome_service::workspace::{ FeatureName, FeaturesBuilder, PullDiagnosticsParams, SupportsFeatureParams, @@ -136,6 +136,7 @@ impl Session { client: tower_lsp::Client, workspace: Arc, cancellation: Arc, + fs: DynRef<'static, dyn FileSystem>, ) -> Self { let documents = Default::default(); let config = RwLock::new(ExtensionSettings::new()); @@ -147,7 +148,7 @@ impl Session { configuration_status: AtomicU8::new(ConfigurationStatus::Missing as u8), documents, extension_settings: config, - fs: DynRef::Owned(Box::::default()), + fs, cancellation, config_path: None, } @@ -276,7 +277,7 @@ impl Session { /// Computes diagnostics for the file matching the provided url and publishes /// them to the client. Called from [`handlers::text_document`] when a file's /// contents changes. - #[tracing::instrument(level = "debug", skip_all, fields(url = display(&url), diagnostic_count), err)] + #[tracing::instrument(level = "trace", skip_all, fields(url = display(&url), diagnostic_count), err)] pub(crate) async fn update_diagnostics(&self, url: lsp_types::Url) -> Result<()> { let rome_path = self.file_path(&url)?; let doc = self.document(&url)?; @@ -396,7 +397,7 @@ impl Session { /// This function attempts to read the `biome.json` configuration file from /// the root URI and update the workspace settings accordingly - #[tracing::instrument(level = "debug", skip(self))] + #[tracing::instrument(level = "trace", skip(self))] pub(crate) async fn load_workspace_settings(&self) { let base_path = if let Some(config_path) = &self.config_path { ConfigurationBasePath::FromUser(config_path.clone()) @@ -463,7 +464,7 @@ impl Session { } /// Requests "workspace/configuration" from client and updates Session config - #[tracing::instrument(level = "debug", skip(self))] + #[tracing::instrument(level = "trace", skip(self))] pub(crate) async fn load_extension_settings(&self) { let item = lsp_types::ConfigurationItem { scope_uri: None, diff --git a/crates/biome_lsp/tests/server.rs b/crates/biome_lsp/tests/server.rs index 97f1f2a0cb55..35768cd6c287 100644 --- a/crates/biome_lsp/tests/server.rs +++ b/crates/biome_lsp/tests/server.rs @@ -2,12 +2,13 @@ use anyhow::bail; use anyhow::Context; use anyhow::Error; use anyhow::Result; -use biome_fs::RomePath; +use biome_fs::{MemoryFileSystem, RomePath}; use biome_lsp::LSPServer; use biome_lsp::ServerFactory; use biome_lsp::WorkspaceSettings; use biome_service::workspace::GetSyntaxTreeResult; use biome_service::workspace::{GetFileContentParams, GetSyntaxTreeParams}; +use biome_service::DynRef; use futures::channel::mpsc::{channel, Sender}; use futures::Sink; use futures::SinkExt; @@ -27,7 +28,6 @@ use tower::{Service, ServiceExt}; use tower_lsp::jsonrpc; use tower_lsp::jsonrpc::Response; use tower_lsp::lsp_types as lsp; -use tower_lsp::lsp_types::DidChangeTextDocumentParams; use tower_lsp::lsp_types::DidCloseTextDocumentParams; use tower_lsp::lsp_types::DidOpenTextDocumentParams; use tower_lsp::lsp_types::DocumentFormattingParams; @@ -44,6 +44,7 @@ use tower_lsp::lsp_types::TextEdit; use tower_lsp::lsp_types::VersionedTextDocumentIdentifier; use tower_lsp::lsp_types::WorkDoneProgressParams; use tower_lsp::lsp_types::{ClientCapabilities, CodeDescription, Url}; +use tower_lsp::lsp_types::{DidChangeConfigurationParams, DidChangeTextDocumentParams}; use tower_lsp::LspService; use tower_lsp::{jsonrpc::Request, lsp_types::InitializeParams}; @@ -239,6 +240,15 @@ impl Server { ) .await } + async fn load_configuration(&mut self) -> Result<()> { + self.notify( + "workspace/didChangeConfiguration", + DidChangeConfigurationParams { + settings: to_value(()).unwrap(), + }, + ) + .await + } async fn change_document( &mut self, @@ -320,7 +330,6 @@ where let settings = WorkspaceSettings { ..WorkspaceSettings::default() }; - let result = to_value(slice::from_ref(&settings)).context("failed to serialize settings")?; @@ -1867,6 +1876,73 @@ async fn format_with_syntax_errors() -> Result<()> { Ok(()) } +#[tokio::test] +async fn does_not_format_ignored_files() -> Result<()> { + let factory = ServerFactory::default(); + let mut fs = MemoryFileSystem::default(); + let config = r#"{ + "files": { + "ignore": ["document.js"] + } + }"#; + + fs.insert(url!("biome.json").to_file_path().unwrap(), config); + let (service, client) = factory + .create_with_fs(None, DynRef::Owned(Box::new(fs))) + .into_inner(); + let (stream, sink) = client.split(); + let mut server = Server::new(service); + + let (sender, _) = channel(CHANNEL_BUFFER_SIZE); + let reader = tokio::spawn(client_handler(stream, sink, sender)); + + server.initialize().await?; + server.initialized().await?; + + server + .open_named_document(config, url!("biome.json"), "json") + .await?; + + server + .open_named_document("statement ( );", url!("document.js"), "js") + .await?; + + server.load_configuration().await?; + + let res: Option> = server + .request( + "textDocument/formatting", + "formatting", + DocumentFormattingParams { + text_document: TextDocumentIdentifier { + uri: url!("document.js"), + }, + options: FormattingOptions { + tab_size: 4, + insert_spaces: false, + properties: HashMap::default(), + trim_trailing_whitespace: None, + insert_final_newline: None, + trim_final_newlines: None, + }, + work_done_progress_params: WorkDoneProgressParams { + work_done_token: None, + }, + }, + ) + .await? + .context("formatting returned None")?; + + assert!(res.is_none()); + + server.close_document().await?; + + server.shutdown().await?; + reader.abort(); + + Ok(()) +} + #[tokio::test] async fn server_shutdown() -> Result<()> { let factory = ServerFactory::default(); diff --git a/crates/biome_service/src/workspace/server.rs b/crates/biome_service/src/workspace/server.rs index 5a06dd7fb62d..317343f1b0fd 100644 --- a/crates/biome_service/src/workspace/server.rs +++ b/crates/biome_service/src/workspace/server.rs @@ -291,7 +291,7 @@ impl Workspace for WorkspaceServer { /// ## Panics /// This function may panic if the internal settings mutex has been poisoned /// by another thread having previously panicked while holding the lock - #[tracing::instrument(level = "debug", skip(self))] + #[tracing::instrument(level = "trace", skip(self))] fn update_settings(&self, params: UpdateSettingsParams) -> Result<(), WorkspaceError> { let mut settings = self.settings.write().unwrap(); @@ -405,7 +405,7 @@ impl Workspace for WorkspaceServer { } /// Retrieves the list of diagnostics associated with a file - #[tracing::instrument(level = "debug", skip(self))] + #[tracing::instrument(level = "trace", skip(self))] fn pull_diagnostics( &self, params: PullDiagnosticsParams, @@ -481,7 +481,7 @@ impl Workspace for WorkspaceServer { /// Retrieves the list of code actions available for a given cursor /// position within a file - #[tracing::instrument(level = "debug", skip(self))] + #[tracing::instrument(level = "trace", skip(self))] fn pull_actions(&self, params: PullActionsParams) -> Result { let capabilities = self.get_file_capabilities(¶ms.path); let code_actions = capabilities diff --git a/website/src/content/docs/internals/changelog.mdx b/website/src/content/docs/internals/changelog.mdx index f51516915638..a9934deca8e0 100644 --- a/website/src/content/docs/internals/changelog.mdx +++ b/website/src/content/docs/internals/changelog.mdx @@ -30,6 +30,10 @@ Read our [guidelines for writing a good changelog entry](https://github.com/biom ### Editors +#### Bug fixes + +- Fix a regression where ignored files where formatted in the editor. Contributed by @ematipico + ### Formatter #### Bug fixes From 5d14c949d2873fdabbf82a11723a8d4d0ef00e5f Mon Sep 17 00:00:00 2001 From: Illia Panasenko Date: Fri, 26 Jan 2024 13:23:48 +0100 Subject: [PATCH 10/13] docs(changelog): fix incorrect rule name (`noSortedClasses` => `useSortedClasses`) (#1681) --- CHANGELOG.md | 2 +- website/src/content/docs/internals/changelog.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9404e0af196..238a12f9a808 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,7 +66,7 @@ Read our [guidelines for writing a good changelog entry](https://github.com/biom ``` Contributed by @ematipico -- Add rule [noSortedClasses](https://biomejs.dev/linter/rules/use-sorted-classes), to sort CSS utility classes: +- Add rule [useSortedClasses](https://biomejs.dev/linter/rules/use-sorted-classes), to sort CSS utility classes: ```diff -
diff --git a/website/src/content/docs/internals/changelog.mdx b/website/src/content/docs/internals/changelog.mdx index a9934deca8e0..81cb94a4392f 100644 --- a/website/src/content/docs/internals/changelog.mdx +++ b/website/src/content/docs/internals/changelog.mdx @@ -72,7 +72,7 @@ Read our [guidelines for writing a good changelog entry](https://github.com/biom ``` Contributed by @ematipico -- Add rule [noSortedClasses](https://biomejs.dev/linter/rules/use-sorted-classes), to sort CSS utility classes: +- Add rule [useSortedClasses](https://biomejs.dev/linter/rules/use-sorted-classes), to sort CSS utility classes: ```diff -
From c2dd5f0beedea17158279fd72844f523e374acc2 Mon Sep 17 00:00:00 2001 From: Vasu Singh Date: Fri, 26 Jan 2024 18:54:21 +0530 Subject: [PATCH 11/13] fix: `noVar` ignores TsGlobalDeclaration (#1669) --- CHANGELOG.md | 2 ++ .../src/semantic_analyzers/style/no_var.rs | 17 +++++++++++++++-- .../style/noVar/validTsGlobalDeclaration.ts | 3 +++ .../noVar/validTsGlobalDeclaration.ts.snap | 12 ++++++++++++ .../src/content/docs/internals/changelog.mdx | 2 ++ 5 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 crates/biome_js_analyze/tests/specs/style/noVar/validTsGlobalDeclaration.ts create mode 100644 crates/biome_js_analyze/tests/specs/style/noVar/validTsGlobalDeclaration.ts.snap diff --git a/CHANGELOG.md b/CHANGELOG.md index 238a12f9a808..d50787ecadbe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,8 @@ Read our [guidelines for writing a good changelog entry](https://github.com/biom #### Bug fixes +- Fix [#1651](https://github.com/biomejs/biome/issues/1651). [noVar](https://biomejs.dev/linter/rules/no-var/) now ignores TsGlobalDeclaration. Contributed by @vasucp1207 + - Fix [#1640](https://github.com/biomejs/biome/issues/1640). [useEnumInitializers](https://biomejs.dev/linter/rules/use-enum-initializers) code action now generates valid code when last member has a comment but no comma. Contributed by @kalleep - Fix [#1653](https://github.com/biomejs/biome/issues/1653). Handle a shorthand value in `useForOf` to avoid the false-positive case. Contributed by @togami2864 diff --git a/crates/biome_js_analyze/src/semantic_analyzers/style/no_var.rs b/crates/biome_js_analyze/src/semantic_analyzers/style/no_var.rs index 3482f5f5572d..8838dadf3775 100644 --- a/crates/biome_js_analyze/src/semantic_analyzers/style/no_var.rs +++ b/crates/biome_js_analyze/src/semantic_analyzers/style/no_var.rs @@ -5,7 +5,9 @@ use biome_analyze::{ use biome_console::markup; use biome_diagnostics::Applicability; use biome_js_factory::make; -use biome_js_syntax::{AnyJsVariableDeclaration, JsModule, JsScript, JsSyntaxKind}; +use biome_js_syntax::{ + AnyJsVariableDeclaration, JsModule, JsScript, JsSyntaxKind, TsGlobalDeclaration, +}; use biome_rowan::{AstNode, BatchMutationExt}; @@ -49,7 +51,18 @@ impl Rule for NoVar { fn run(ctx: &RuleContext) -> Self::Signals { let declaration = ctx.query(); - declaration.is_var().then_some(()) + if declaration.is_var() { + let ts_global_declaratio = &declaration + .syntax() + .ancestors() + .find_map(TsGlobalDeclaration::cast); + + if ts_global_declaratio.is_some() { + return None; + } + return Some(()); + } + None } fn diagnostic(ctx: &RuleContext, _state: &Self::State) -> Option { diff --git a/crates/biome_js_analyze/tests/specs/style/noVar/validTsGlobalDeclaration.ts b/crates/biome_js_analyze/tests/specs/style/noVar/validTsGlobalDeclaration.ts new file mode 100644 index 000000000000..932867a65e21 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/style/noVar/validTsGlobalDeclaration.ts @@ -0,0 +1,3 @@ +declare global { + var Txt: CustomWindow['Txt']; +} \ No newline at end of file diff --git a/crates/biome_js_analyze/tests/specs/style/noVar/validTsGlobalDeclaration.ts.snap b/crates/biome_js_analyze/tests/specs/style/noVar/validTsGlobalDeclaration.ts.snap new file mode 100644 index 000000000000..a2c6eeb5b81b --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/style/noVar/validTsGlobalDeclaration.ts.snap @@ -0,0 +1,12 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: validTsGlobalDeclaration.ts +--- +# Input +```ts +declare global { + var Txt: CustomWindow['Txt']; +} +``` + + diff --git a/website/src/content/docs/internals/changelog.mdx b/website/src/content/docs/internals/changelog.mdx index 81cb94a4392f..2e7385690b19 100644 --- a/website/src/content/docs/internals/changelog.mdx +++ b/website/src/content/docs/internals/changelog.mdx @@ -82,6 +82,8 @@ Read our [guidelines for writing a good changelog entry](https://github.com/biom #### Bug fixes +- Fix [#1651](https://github.com/biomejs/biome/issues/1651). [noVar](https://biomejs.dev/linter/rules/no-var/) now ignores TsGlobalDeclaration. Contributed by @vasucp1207 + - Fix [#1640](https://github.com/biomejs/biome/issues/1640). [useEnumInitializers](https://biomejs.dev/linter/rules/use-enum-initializers) code action now generates valid code when last member has a comment but no comma. Contributed by @kalleep - Fix [#1653](https://github.com/biomejs/biome/issues/1653). Handle a shorthand value in `useForOf` to avoid the false-positive case. Contributed by @togami2864 From c343d4026482d04c1ca3fbd3a99a589f562fc41b Mon Sep 17 00:00:00 2001 From: Victorien Elvinger Date: Fri, 26 Jan 2024 15:36:52 +0100 Subject: [PATCH 12/13] refactor(biome_service): cleanup get_parse (#1682) --- crates/biome_lsp/src/documents.rs | 2 +- crates/biome_service/src/workspace.rs | 2 +- crates/biome_service/src/workspace/server.rs | 62 +++++++++----------- 3 files changed, 29 insertions(+), 37 deletions(-) diff --git a/crates/biome_lsp/src/documents.rs b/crates/biome_lsp/src/documents.rs index 7e33e6a3f149..1e8e762a5049 100644 --- a/crates/biome_lsp/src/documents.rs +++ b/crates/biome_lsp/src/documents.rs @@ -2,7 +2,7 @@ use crate::converters::line_index::LineIndex; /// Represents an open [`textDocument`]. Can be cheaply cloned. /// -/// [`textDocument`]: https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#textDocumentItem +/// [`textDocument`]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentItem #[derive(Clone)] pub(crate) struct Document { pub(crate) version: i32, diff --git a/crates/biome_service/src/workspace.rs b/crates/biome_service/src/workspace.rs index f0c0b753d765..c06ebf564480 100644 --- a/crates/biome_service/src/workspace.rs +++ b/crates/biome_service/src/workspace.rs @@ -355,7 +355,7 @@ impl SupportKind { } } -#[derive(Debug, Clone, Hash, serde::Serialize, serde::Deserialize, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Hash, serde::Serialize, serde::Deserialize, Eq, PartialEq)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub enum FeatureName { Format, diff --git a/crates/biome_service/src/workspace/server.rs b/crates/biome_service/src/workspace/server.rs index 317343f1b0fd..6dd78dc23a6f 100644 --- a/crates/biome_service/src/workspace/server.rs +++ b/crates/biome_service/src/workspace/server.rs @@ -135,11 +135,7 @@ impl WorkspaceServer { /// /// Returns and error if no file exists in the workspace with this path or /// if the language associated with the file has no parser capability - fn get_parse( - &self, - rome_path: RomePath, - _feature: Option, - ) -> Result { + fn get_parse(&self, rome_path: RomePath) -> Result { match self.syntax.entry(rome_path) { Entry::Occupied(entry) => Ok(entry.get().clone()), Entry::Vacant(entry) => { @@ -187,6 +183,17 @@ impl WorkspaceServer { } } + /// Check whether a file is ignored in the top-level config `files.ignore`/`files.include` + /// or in the feature `ignore`/`include` + fn is_ignored(&self, path: &Path, feature: FeatureName) -> bool { + // Never ignore Biome's config file regardless `include`/`ignore` + path.file_name().and_then(|s| s.to_str()) != Some(BIOME_JSON) && + // Apply top-level `include`/`ignore` + (self.is_ignored_by_top_level_config(path) || + // Apply feature-level `include`/`ignore` + self.is_ignored_by_feature_config(path, feature)) + } + /// Check whether a file is ignored in the top-level config `files.ignore`/`files.include` fn is_ignored_by_top_level_config(&self, path: &Path) -> bool { let settings = self.settings(); @@ -195,7 +202,8 @@ impl WorkspaceServer { !is_included || settings.as_ref().files.ignored_files.matches_path(path) } - fn is_ignored_by_feature_config(&self, path: &Path, feature: &FeatureName) -> bool { + /// Check whether a file is ignored in the feature `ignore`/`include` + fn is_ignored_by_feature_config(&self, path: &Path, feature: FeatureName) -> bool { let settings = self.settings(); let (feature_included_files, feature_ignored_files) = match feature { FeatureName::Format => { @@ -253,7 +261,7 @@ impl Workspace for WorkspaceServer { file_features.set_ignored_for_all_features(); } else { for feature in params.feature { - if self.is_ignored_by_feature_config(path, &feature) { + if self.is_ignored_by_feature_config(path, feature) { file_features.ignored(feature); } } @@ -273,17 +281,7 @@ impl Workspace for WorkspaceServer { } fn is_path_ignored(&self, params: IsPathIgnoredParams) -> Result { - let path = params.rome_path.as_path(); - // Never ignore Biome's config file regardless `include`/`ignore` - if path.file_name().and_then(|s| s.to_str()) == Some(BIOME_JSON) { - return Ok(false); - } - // Apply top-level `include`/`ignore` - if self.is_ignored_by_top_level_config(path) { - return Ok(true); - } - // Apply feature-level `include`/`ignore` - Ok(self.is_ignored_by_feature_config(path, ¶ms.feature)) + Ok(self.is_ignored(params.rome_path.as_path(), params.feature)) } /// Update the global settings for this workspace @@ -333,7 +331,7 @@ impl Workspace for WorkspaceServer { .ok_or_else(self.build_capability_error(¶ms.path))?; // The feature name here can be any feature, in theory - let parse = self.get_parse(params.path.clone(), None)?; + let parse = self.get_parse(params.path.clone())?; let printed = debug_syntax_tree(¶ms.path, parse); Ok(printed) @@ -349,7 +347,7 @@ impl Workspace for WorkspaceServer { .debug_control_flow .ok_or_else(self.build_capability_error(¶ms.path))?; - let parse = self.get_parse(params.path.clone(), None)?; + let parse = self.get_parse(params.path.clone())?; let printed = debug_control_flow(parse, params.cursor); Ok(printed) @@ -362,7 +360,7 @@ impl Workspace for WorkspaceServer { .debug_formatter_ir .ok_or_else(self.build_capability_error(¶ms.path))?; let settings = self.settings(); - let parse = self.get_parse(params.path.clone(), Some(FeatureName::Format))?; + let parse = self.get_parse(params.path.clone())?; if !settings.as_ref().formatter().format_with_errors && parse.has_errors() { return Err(WorkspaceError::format_with_errors_disabled()); @@ -410,13 +408,7 @@ impl Workspace for WorkspaceServer { &self, params: PullDiagnosticsParams, ) -> Result { - let feature = if params.categories.is_syntax() { - FeatureName::Format - } else { - FeatureName::Lint - }; - - let parse = self.get_parse(params.path.clone(), Some(feature))?; + let parse = self.get_parse(params.path.clone())?; let settings = self.settings.read().unwrap(); let (diagnostics, errors, skipped_diagnostics) = if let Some(lint) = @@ -489,7 +481,7 @@ impl Workspace for WorkspaceServer { .code_actions .ok_or_else(self.build_capability_error(¶ms.path))?; - let parse = self.get_parse(params.path.clone(), Some(FeatureName::Lint))?; + let parse = self.get_parse(params.path.clone())?; let settings = self.settings.read().unwrap(); let rules = settings.linter().rules.as_ref(); Ok(code_actions( @@ -510,7 +502,7 @@ impl Workspace for WorkspaceServer { .format .ok_or_else(self.build_capability_error(¶ms.path))?; let settings = self.settings(); - let parse = self.get_parse(params.path.clone(), Some(FeatureName::Format))?; + let parse = self.get_parse(params.path.clone())?; if !settings.as_ref().formatter().format_with_errors && parse.has_errors() { return Err(WorkspaceError::format_with_errors_disabled()); @@ -526,7 +518,7 @@ impl Workspace for WorkspaceServer { .format_range .ok_or_else(self.build_capability_error(¶ms.path))?; let settings = self.settings(); - let parse = self.get_parse(params.path.clone(), Some(FeatureName::Format))?; + let parse = self.get_parse(params.path.clone())?; if !settings.as_ref().formatter().format_with_errors && parse.has_errors() { return Err(WorkspaceError::format_with_errors_disabled()); @@ -543,7 +535,7 @@ impl Workspace for WorkspaceServer { .ok_or_else(self.build_capability_error(¶ms.path))?; let settings = self.settings(); - let parse = self.get_parse(params.path.clone(), Some(FeatureName::Format))?; + let parse = self.get_parse(params.path.clone())?; if !settings.as_ref().formatter().format_with_errors && parse.has_errors() { return Err(WorkspaceError::format_with_errors_disabled()); } @@ -558,7 +550,7 @@ impl Workspace for WorkspaceServer { .fix_all .ok_or_else(self.build_capability_error(¶ms.path))?; let settings = self.settings.read().unwrap(); - let parse = self.get_parse(params.path.clone(), Some(FeatureName::Lint))?; + let parse = self.get_parse(params.path.clone())?; // Compite final rules (taking `overrides` into account) let rules = settings.as_rules(params.path.as_path()); let rule_filter_list = rules @@ -586,7 +578,7 @@ impl Workspace for WorkspaceServer { .rename .ok_or_else(self.build_capability_error(¶ms.path))?; - let parse = self.get_parse(params.path.clone(), None)?; + let parse = self.get_parse(params.path.clone())?; let result = rename(¶ms.path, parse, params.symbol_at, params.new_name)?; Ok(result) @@ -615,7 +607,7 @@ impl Workspace for WorkspaceServer { .organize_imports .ok_or_else(self.build_capability_error(¶ms.path))?; - let parse = self.get_parse(params.path, None)?; + let parse = self.get_parse(params.path)?; let result = organize_imports(parse)?; Ok(result) From 49aa1eee04e410f00912905d84eb85b03fbe7c09 Mon Sep 17 00:00:00 2001 From: Victorien Elvinger Date: Fri, 26 Jan 2024 15:52:43 +0100 Subject: [PATCH 13/13] refactor(website): remove underused glob pattern docs (#1683) --- website/src/content/docs/guides/how-biome-works.mdx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/website/src/content/docs/guides/how-biome-works.mdx b/website/src/content/docs/guides/how-biome-works.mdx index 51df6739db7c..ae2e4e0a8575 100644 --- a/website/src/content/docs/guides/how-biome-works.mdx +++ b/website/src/content/docs/guides/how-biome-works.mdx @@ -154,7 +154,6 @@ When both `include` and `ignore` are specified, Biome gives **precedence** to `i The syntax and meaning of these two options loosely follow the [globset rules](https://docs.rs/globset/latest/globset/#syntax) but without the ability to set options. -> - `?` matches any single character. > - `*` matches zero or more characters. > - `**` recursively matches directories but are only legal in three situations. > First, if the glob starts with \*\*/, then it matches @@ -166,11 +165,7 @@ The syntax and meaning of these two options loosely follow the [globset rules](h > the pattern, then it matches zero or more directories. Using `**` anywhere > else is illegal (N.B. the glob `**` is allowed and means "match everything"). > -> - `[ab]` matches `a` or `b` where `a` and `b` are characters. Use -> `[!ab]` to match any character except for `a` and `b`. > - Metacharacters such as `*` and `?` can be escaped with character class > notation. e.g., `[*]` matches `*`. Check the [wikipedia page](https://en.wikipedia.org/wiki/Glob_(programming)) for more information. - -