diff --git a/Cargo.lock b/Cargo.lock index 519dda471d..114ce37af0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -238,7 +238,7 @@ dependencies = [ [[package]] name = "air_r_factory" version = "0.0.0" -source = "git+https://github.com/posit-dev/air?rev=b8ead5f024da1ddaf118722045bcda430bac1530#b8ead5f024da1ddaf118722045bcda430bac1530" +source = "git+https://github.com/posit-dev/air?rev=aa552899dece399c7f6234d0741b66c628696b0f#aa552899dece399c7f6234d0741b66c628696b0f" dependencies = [ "air_r_syntax", "biome_rowan", @@ -247,22 +247,19 @@ dependencies = [ [[package]] name = "air_r_parser" version = "0.0.0" -source = "git+https://github.com/posit-dev/air?rev=b8ead5f024da1ddaf118722045bcda430bac1530#b8ead5f024da1ddaf118722045bcda430bac1530" +source = "git+https://github.com/posit-dev/air?rev=aa552899dece399c7f6234d0741b66c628696b0f#aa552899dece399c7f6234d0741b66c628696b0f" dependencies = [ "air_r_factory", "air_r_syntax", "biome_parser", "biome_rowan", - "biome_unicode_table", - "tracing", - "tree-sitter", - "tree-sitter-r", + "unicode-ident", ] [[package]] name = "air_r_syntax" version = "0.0.0" -source = "git+https://github.com/posit-dev/air?rev=b8ead5f024da1ddaf118722045bcda430bac1530#b8ead5f024da1ddaf118722045bcda430bac1530" +source = "git+https://github.com/posit-dev/air?rev=aa552899dece399c7f6234d0741b66c628696b0f#aa552899dece399c7f6234d0741b66c628696b0f" dependencies = [ "biome_rowan", "serde", @@ -393,7 +390,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -404,7 +401,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1353,7 +1350,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1510,7 +1507,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.52.0", ] [[package]] @@ -2308,7 +2305,7 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde_core", - "windows-sys 0.60.2", + "windows-sys 0.52.0", ] [[package]] @@ -2654,7 +2651,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3451,7 +3448,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.60.2", + "windows-sys 0.52.0", ] [[package]] @@ -3840,7 +3837,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3859,7 +3856,7 @@ dependencies = [ "cfg-if", "libc", "psm", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -4035,7 +4032,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.60.2", + "windows-sys 0.52.0", ] [[package]] @@ -4064,7 +4061,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ "rustix", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -4859,7 +4856,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.52.0", ] [[package]] @@ -5105,7 +5102,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d6f32a0ff4a9f6f01231eb2059cc85479330739333e0e58cadf03b6af2cca10" dependencies = [ "cfg-if", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 9440c2cb7b..5b68260e8a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,10 +25,10 @@ too_many_arguments = "allow" [workspace.dependencies] actix-web = "4.13.0" -aether_factory = { git = "https://github.com/posit-dev/air", package = "air_r_factory", rev = "b8ead5f024da1ddaf118722045bcda430bac1530" } +aether_factory = { git = "https://github.com/posit-dev/air", package = "air_r_factory", rev = "aa552899dece399c7f6234d0741b66c628696b0f" } aether_lsp_utils = { path = "crates/aether_lsp_utils" } -aether_parser = { git = "https://github.com/posit-dev/air", package = "air_r_parser", rev = "b8ead5f024da1ddaf118722045bcda430bac1530" } -aether_syntax = { git = "https://github.com/posit-dev/air", package = "air_r_syntax", rev = "b8ead5f024da1ddaf118722045bcda430bac1530" } +aether_parser = { git = "https://github.com/posit-dev/air", package = "air_r_parser", rev = "aa552899dece399c7f6234d0741b66c628696b0f" } +aether_syntax = { git = "https://github.com/posit-dev/air", package = "air_r_syntax", rev = "aa552899dece399c7f6234d0741b66c628696b0f" } aether_path = { path = "crates/aether_path" } amalthea = { path = "crates/amalthea" } annotate-snippets = "0.12.16" diff --git a/crates/oak_core/src/syntax_ext.rs b/crates/oak_core/src/syntax_ext.rs index 76119bda25..f9e5656000 100644 --- a/crates/oak_core/src/syntax_ext.rs +++ b/crates/oak_core/src/syntax_ext.rs @@ -3,8 +3,6 @@ use aether_syntax::AnyRSelector; use aether_syntax::AnyRStatement; use aether_syntax::RCall; use aether_syntax::RIdentifier; -use aether_syntax::RStringValue; -use aether_syntax::RSyntaxKind; use biome_rowan::AstNode; use biome_rowan::AstSeparatedList; @@ -29,57 +27,10 @@ impl RIdentifierExt for RIdentifier { } } -pub trait RStringValueExt { - /// Return the string contents without surrounding delimiters. - /// - /// TODO(air): Replace with `RStringValue::inner_string_text()` once Air is - /// fully updated. - fn string_text(&self) -> Option; -} - -impl RStringValueExt for RStringValue { - fn string_text(&self) -> Option { - let token = self.value().ok()?; - let text = token.text_trimmed(); - - match token.kind() { - RSyntaxKind::R_DOUBLE_QUOTED_STRING_LITERAL => { - Some(text.strip_prefix('"')?.strip_suffix('"')?.to_string()) - }, - RSyntaxKind::R_SINGLE_QUOTED_STRING_LITERAL => { - Some(text.strip_prefix('\'')?.strip_suffix('\'')?.to_string()) - }, - RSyntaxKind::R_RAW_STRING_LITERAL => Some(raw_string_inner(text)?.to_string()), - _ => None, - } - } -} - -fn raw_string_inner(text: &str) -> Option<&str> { - let rest = text.strip_prefix(['r', 'R'])?; - - let quote = rest.chars().next().filter(|c| matches!(c, '"' | '\''))?; - let rest = &rest[quote.len_utf8()..]; - - let dashes = rest.len() - rest.trim_start_matches('-').len(); - let rest = &rest[dashes..]; - - let close_bracket = match rest.chars().next()? { - '(' => ')', - '[' => ']', - '{' => '}', - _ => return None, - }; - let rest = &rest[1..]; - - let rest = rest.strip_suffix(quote)?; - let rest = rest.strip_suffix(&"-".repeat(dashes))?; - rest.strip_suffix(close_bracket) -} - pub trait AnyRSelectorExt { - /// Uses [RIdentifierExt::name_text()] and [RStringValueExt::string_text()] to report - /// the name without backticks or quotes + /// Uses [RIdentifierExt::name_text()] and + /// [aether_syntax::RStringValue::inner_string_text()] to report the name + /// without backticks or quotes /// /// Returns [None] for [aether_syntax::AnyRSelector::RDotDotI] and /// [aether_syntax::AnyRSelector::RDots], which we don't consider standard @@ -91,7 +42,7 @@ impl AnyRSelectorExt for AnyRSelector { fn identifier_text(&self) -> Option { match self { AnyRSelector::RIdentifier(node) => Some(node.name_text()), - AnyRSelector::RStringValue(node) => node.string_text(), + AnyRSelector::RStringValue(node) => Some(node.inner_string_text().ok()?.to_string()), AnyRSelector::RDots(_) | AnyRSelector::RDotDotI(_) => None, AnyRSelector::RBogusSelector(_) => None, } @@ -166,35 +117,42 @@ mod tests { #[test] fn string_double_quoted() { assert_matches!(parse_single_expr("\"hello\""), AnyRExpression::AnyRValue(AnyRValue::RStringValue(s)) => { - assert_eq!(s.string_text().unwrap(), "hello"); + assert_eq!(s.inner_string_text().unwrap().text(), "hello"); }); } #[test] fn string_single_quoted() { assert_matches!(parse_single_expr("'world'"), AnyRExpression::AnyRValue(AnyRValue::RStringValue(s)) => { - assert_eq!(s.string_text().unwrap(), "world"); + assert_eq!(s.inner_string_text().unwrap().text(), "world"); }); } #[test] fn string_raw() { assert_matches!(parse_single_expr("r\"(hello)\""), AnyRExpression::AnyRValue(AnyRValue::RStringValue(s)) => { - assert_eq!(s.string_text().unwrap(), "hello"); + assert_eq!(s.inner_string_text().unwrap().text(), "hello"); }); } #[test] fn string_raw_with_dashes_and_brackets() { assert_matches!(parse_single_expr("R'--[a)-\"b]--'"), AnyRExpression::AnyRValue(AnyRValue::RStringValue(s)) => { - assert_eq!(s.string_text().unwrap(), "a)-\"b"); + assert_eq!(s.inner_string_text().unwrap().text(), "a)-\"b"); }); } #[test] fn string_raw_braces() { assert_matches!(parse_single_expr("r\"{x}\""), AnyRExpression::AnyRValue(AnyRValue::RStringValue(s)) => { - assert_eq!(s.string_text().unwrap(), "x"); + assert_eq!(s.inner_string_text().unwrap().text(), "x"); + }); + } + + #[test] + fn string_raw_pipes() { + assert_matches!(parse_single_expr("r\"-|a|-\""), AnyRExpression::AnyRValue(AnyRValue::RStringValue(s)) => { + assert_eq!(s.inner_string_text().unwrap().text(), "a"); }); } } diff --git a/crates/oak_package_metadata/src/namespace.rs b/crates/oak_package_metadata/src/namespace.rs index 4d0b5a10c0..ffc5dab62a 100644 --- a/crates/oak_package_metadata/src/namespace.rs +++ b/crates/oak_package_metadata/src/namespace.rs @@ -7,7 +7,6 @@ use biome_rowan::AstSeparatedList; use biome_rowan::SyntaxResult; use oak_core::syntax_ext::AnyRStatementExt; use oak_core::syntax_ext::RIdentifierExt; -use oak_core::syntax_ext::RStringValueExt; use stdext::SortedVec; /// Parsed NAMESPACE file @@ -137,7 +136,9 @@ fn collect_arg_identifiers( fn directive_name(value: &AnyRExpression) -> Option { match value { AnyRExpression::RIdentifier(ident) => Some(ident.name_text()), - AnyRExpression::AnyRValue(AnyRValue::RStringValue(string)) => string.string_text(), + AnyRExpression::AnyRValue(AnyRValue::RStringValue(string)) => { + Some(string.inner_string_text().ok()?.to_string()) + }, _ => None, } } diff --git a/crates/oak_semantic/src/builder.rs b/crates/oak_semantic/src/builder.rs index 80e2e5f5a5..8c22f538c1 100644 --- a/crates/oak_semantic/src/builder.rs +++ b/crates/oak_semantic/src/builder.rs @@ -39,7 +39,6 @@ use biome_rowan::AstNode; use biome_rowan::TextRange; use biome_rowan::TextSize; use oak_core::syntax_ext::RIdentifierExt; -use oak_core::syntax_ext::RStringValueExt; use oak_index_vec::Idx; use oak_index_vec::IndexVec; use rustc_hash::FxHashMap; @@ -447,7 +446,7 @@ fn assignment_name(target: &AnyRExpression) -> Option<(String, TextRange)> { }, // `"x" <- 1` is equivalent to `x <- 1` in R AnyRExpression::AnyRValue(AnyRValue::RStringValue(s)) => { - let name = s.string_text()?; + let name = s.inner_string_text().ok()?.to_string(); let range = s.syntax().text_trimmed_range(); Some((name, range)) }, diff --git a/crates/oak_semantic/src/effects.rs b/crates/oak_semantic/src/effects.rs index 4deca62c77..eca5309c44 100644 --- a/crates/oak_semantic/src/effects.rs +++ b/crates/oak_semantic/src/effects.rs @@ -13,7 +13,6 @@ use biome_rowan::TextRange; // can name the `name_expr` field's type without depending on oak_core directly. pub use oak_core::range::RangedAstPtr; use oak_core::syntax_ext::RIdentifierExt; -use oak_core::syntax_ext::RStringValueExt; use rustc_hash::FxHashMap; use crate::semantic_index::AmbiguityReason; @@ -497,7 +496,7 @@ fn match_named(arg: &RArgument, formals: Formals) -> Option { let name = clause.name().ok()?; let name_text = match &name { AnyRArgumentName::RIdentifier(ident) => ident.name_text(), - AnyRArgumentName::RStringValue(s) => s.string_text()?, + AnyRArgumentName::RStringValue(s) => s.inner_string_text().ok()?.to_string(), _ => return None, }; formals @@ -744,7 +743,9 @@ impl AssignHandler for BindingOperatorHandler { pub fn resolve_quoted_symbol_or_string(value: &AnyRExpression) -> Option { match value { AnyRExpression::RIdentifier(ident) => Some(ident.name_text()), - AnyRExpression::AnyRValue(AnyRValue::RStringValue(s)) => s.string_text(), + AnyRExpression::AnyRValue(AnyRValue::RStringValue(s)) => { + Some(s.inner_string_text().ok()?.to_string()) + }, _ => None, } } diff --git a/crates/oak_semantic/src/effects/value_eval.rs b/crates/oak_semantic/src/effects/value_eval.rs index 240a119532..44a06829dc 100644 --- a/crates/oak_semantic/src/effects/value_eval.rs +++ b/crates/oak_semantic/src/effects/value_eval.rs @@ -4,7 +4,6 @@ use aether_syntax::RCall; use biome_rowan::AstNode; use biome_rowan::TextRange; use oak_core::syntax_ext::RIdentifierExt; -use oak_core::syntax_ext::RStringValueExt; use crate::effects::CallContext; use crate::effects::CalleeOrigin; @@ -46,7 +45,10 @@ impl CallContext<'_> { pub fn resolve_static_value(&mut self, value: &AnyRExpression) -> Option { match value { AnyRExpression::AnyRValue(AnyRValue::RStringValue(s)) => { - Some(StaticValue::Character(vec![s.string_text()?])) + Some(StaticValue::Character(vec![s + .inner_string_text() + .ok()? + .to_string()])) }, AnyRExpression::RNullExpression(_) => Some(StaticValue::Null), AnyRExpression::RCall(call) => { diff --git a/crates/oak_semantic/tests/integration/fuzz.rs b/crates/oak_semantic/tests/integration/fuzz.rs index 2aacddb86e..0b629e1be0 100644 --- a/crates/oak_semantic/tests/integration/fuzz.rs +++ b/crates/oak_semantic/tests/integration/fuzz.rs @@ -755,7 +755,7 @@ fn test_source_path_with_quote_and_backslash_renders_valid_r() { .text; assert_eq!(text, "source(\"a\\\"b\\\\c.R\")\n"); - // `RStringValueExt::string_text()` strips the delimiters and returns the + // `RStringValue::inner_string_text()` strips the delimiters and returns the // token's raw text, so the path Oak sees keeps the escapes rather than the // characters R would resolve them to. let index = index_with_base(&text);