Skip to content

Commit c4fa6ad

Browse files
Ertanicalerque
authored andcommitted
feat(fluent-bundle,fluent-syntax): Add source location spans to AST nodes
1 parent f2033ce commit c4fa6ad

File tree

15 files changed

+711
-113
lines changed

15 files changed

+711
-113
lines changed

fluent-bundle/src/resolver/errors.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ where
3131
InlineExpression::FunctionReference { id, .. } => Self::Function {
3232
id: id.name.to_string(),
3333
},
34-
InlineExpression::MessageReference { id, attribute } => Self::Message {
34+
InlineExpression::MessageReference { id, attribute, .. } => Self::Message {
3535
id: id.name.to_string(),
3636
attribute: attribute.as_ref().map(|i| i.name.to_string()),
3737
},

fluent-bundle/src/resolver/expression.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,17 @@ impl<'bundle> WriteValue<'bundle> for ast::Expression<&'bundle str> {
2323
M: MemoizerKind,
2424
{
2525
match self {
26-
Self::Inline(exp) => exp.write(w, scope),
27-
Self::Select { selector, variants } => {
26+
Self::Inline(exp, ..) => exp.write(w, scope),
27+
Self::Select {
28+
selector, variants, ..
29+
} => {
2830
let selector = selector.resolve(scope);
2931
match selector {
3032
FluentValue::String(_) | FluentValue::Number(_) => {
3133
for variant in variants {
3234
let key = match variant.key {
33-
ast::VariantKey::Identifier { name } => name.into(),
34-
ast::VariantKey::NumberLiteral { value } => {
35+
ast::VariantKey::Identifier { name, .. } => name.into(),
36+
ast::VariantKey::NumberLiteral { value, .. } => {
3537
FluentValue::try_number(value)
3638
}
3739
};
@@ -59,7 +61,7 @@ impl<'bundle> WriteValue<'bundle> for ast::Expression<&'bundle str> {
5961
W: fmt::Write,
6062
{
6163
match self {
62-
Self::Inline(exp) => exp.write_error(w),
64+
Self::Inline(exp, ..) => exp.write_error(w),
6365
Self::Select { selector, .. } => selector.write_error(w),
6466
}
6567
}

fluent-bundle/src/resolver/inline_expression.rs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ impl<'bundle> WriteValue<'bundle> for ast::InlineExpression<&'bundle str> {
2424
M: MemoizerKind,
2525
{
2626
match self {
27-
Self::StringLiteral { value } => unescape_unicode(w, value),
28-
Self::MessageReference { id, attribute } => {
27+
Self::StringLiteral { value, .. } => unescape_unicode(w, value),
28+
Self::MessageReference { id, attribute, .. } => {
2929
if let Some(msg) = scope.bundle.get_entry_message(id.name) {
3030
if let Some(attr) = attribute {
3131
msg.attributes
@@ -53,11 +53,12 @@ impl<'bundle> WriteValue<'bundle> for ast::InlineExpression<&'bundle str> {
5353
scope.write_ref_error(w, self)
5454
}
5555
}
56-
Self::NumberLiteral { value } => FluentValue::try_number(value).write(w, scope),
56+
Self::NumberLiteral { value, .. } => FluentValue::try_number(value).write(w, scope),
5757
Self::TermReference {
5858
id,
5959
attribute,
6060
arguments,
61+
..
6162
} => {
6263
let (_, resolved_named_args) = scope.get_arguments(arguments.as_ref());
6364

@@ -82,7 +83,7 @@ impl<'bundle> WriteValue<'bundle> for ast::InlineExpression<&'bundle str> {
8283
scope.local_args = None;
8384
result
8485
}
85-
Self::FunctionReference { id, arguments } => {
86+
Self::FunctionReference { id, arguments, .. } => {
8687
let (resolved_positional_args, resolved_named_args) =
8788
scope.get_arguments(Some(arguments));
8889

@@ -99,7 +100,7 @@ impl<'bundle> WriteValue<'bundle> for ast::InlineExpression<&'bundle str> {
99100
scope.write_ref_error(w, self)
100101
}
101102
}
102-
Self::VariableReference { id } => {
103+
Self::VariableReference { id, .. } => {
103104
let args = scope.local_args.as_ref().or(scope.args);
104105

105106
if let Some(arg) = args.and_then(|args| args.get(id.name)) {
@@ -113,7 +114,7 @@ impl<'bundle> WriteValue<'bundle> for ast::InlineExpression<&'bundle str> {
113114
w.write_char('}')
114115
}
115116
}
116-
Self::Placeable { expression } => expression.write(w, scope),
117+
Self::Placeable { expression, .. } => expression.write(w, scope),
117118
}
118119
}
119120

@@ -125,10 +126,12 @@ impl<'bundle> WriteValue<'bundle> for ast::InlineExpression<&'bundle str> {
125126
Self::MessageReference {
126127
id,
127128
attribute: Some(attribute),
129+
..
128130
} => write!(w, "{}.{}", id.name, attribute.name),
129131
Self::MessageReference {
130132
id,
131133
attribute: None,
134+
..
132135
} => w.write_str(id.name),
133136
Self::TermReference {
134137
id,
@@ -141,7 +144,7 @@ impl<'bundle> WriteValue<'bundle> for ast::InlineExpression<&'bundle str> {
141144
..
142145
} => write!(w, "-{}", id.name),
143146
Self::FunctionReference { id, .. } => write!(w, "{}()", id.name),
144-
Self::VariableReference { id } => write!(w, "${}", id.name),
147+
Self::VariableReference { id, .. } => write!(w, "${}", id.name),
145148
_ => unreachable!(),
146149
}
147150
}
@@ -157,9 +160,9 @@ impl<'bundle> ResolveValue<'bundle> for ast::InlineExpression<&'bundle str> {
157160
M: MemoizerKind,
158161
{
159162
match self {
160-
Self::StringLiteral { value } => unescape_unicode_to_string(value).into(),
161-
Self::NumberLiteral { value } => FluentValue::try_number(value),
162-
Self::VariableReference { id } => {
163+
Self::StringLiteral { value, .. } => unescape_unicode_to_string(value).into(),
164+
Self::NumberLiteral { value, .. } => FluentValue::try_number(value),
165+
Self::VariableReference { id, .. } => {
163166
if let Some(local_args) = &scope.local_args {
164167
if let Some(arg) = local_args.get(id.name) {
165168
return arg.clone();
@@ -173,7 +176,7 @@ impl<'bundle> ResolveValue<'bundle> for ast::InlineExpression<&'bundle str> {
173176
}
174177
FluentValue::Error
175178
}
176-
Self::FunctionReference { id, arguments } => {
179+
Self::FunctionReference { id, arguments, .. } => {
177180
let (resolved_positional_args, resolved_named_args) =
178181
scope.get_arguments(Some(arguments));
179182

fluent-bundle/src/resolver/pattern.rs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@ impl<'bundle> WriteValue<'bundle> for ast::Pattern<&'bundle str> {
3232
}
3333

3434
match elem {
35-
ast::PatternElement::TextElement { value } => {
35+
ast::PatternElement::TextElement { value, .. } => {
3636
if let Some(ref transform) = scope.bundle.transform {
3737
w.write_str(&transform(value))?;
3838
} else {
3939
w.write_str(value)?;
4040
}
4141
}
42-
ast::PatternElement::Placeable { ref expression } => {
42+
ast::PatternElement::Placeable { ref expression, .. } => {
4343
scope.placeables += 1;
4444
if scope.placeables > MAX_PLACEABLES {
4545
scope.dirty = true;
@@ -51,13 +51,16 @@ impl<'bundle> WriteValue<'bundle> for ast::Pattern<&'bundle str> {
5151
&& len > 1
5252
&& !matches!(
5353
expression,
54-
ast::Expression::Inline(ast::InlineExpression::MessageReference { .. },)
55-
| ast::Expression::Inline(
56-
ast::InlineExpression::TermReference { .. },
57-
)
58-
| ast::Expression::Inline(
59-
ast::InlineExpression::StringLiteral { .. },
60-
)
54+
ast::Expression::Inline(
55+
ast::InlineExpression::MessageReference { .. },
56+
..
57+
) | ast::Expression::Inline(
58+
ast::InlineExpression::TermReference { .. },
59+
..
60+
) | ast::Expression::Inline(
61+
ast::InlineExpression::StringLiteral { .. },
62+
..
63+
)
6164
);
6265
if needs_isolation {
6366
w.write_char('\u{2068}')?;
@@ -92,7 +95,7 @@ impl<'bundle> ResolveValue<'bundle> for ast::Pattern<&'bundle str> {
9295
let len = self.elements.len();
9396

9497
if len == 1 {
95-
if let ast::PatternElement::TextElement { value } = self.elements[0] {
98+
if let ast::PatternElement::TextElement { value, .. } = self.elements[0] {
9699
return scope
97100
.bundle
98101
.transform

fluent-bundle/src/resolver/scope.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,10 @@ impl<'bundle, 'ast, 'args, 'errors, R, M> Scope<'bundle, 'ast, 'args, 'errors, R
124124
R: Borrow<FluentResource>,
125125
M: MemoizerKind,
126126
{
127-
if let Some(ast::CallArguments { positional, named }) = arguments {
127+
if let Some(ast::CallArguments {
128+
positional, named, ..
129+
}) = arguments
130+
{
128131
let positional = positional.iter().map(|expr| expr.resolve(self)).collect();
129132

130133
let named = named

fluent-syntax/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ glob = "0.3"
3838

3939
[features]
4040
default = []
41+
spans = []
4142
serde = ["dep:serde"]
4243
json = ["serde", "dep:serde_json"]
4344
all-benchmarks = []

fluent-syntax/src/ast/helper.rs

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,78 @@
1+
use super::Comment;
2+
#[cfg(feature = "spans")]
3+
use super::Span;
14
#[cfg(feature = "serde")]
25
use serde::{Deserialize, Serialize};
36

4-
use super::Comment;
57
// This is a helper struct used to properly deserialize referential
68
// JSON comments which are single continuous String, into a vec of
79
// content slices.
8-
#[derive(Debug, PartialEq, Eq, Clone)]
10+
#[derive(Debug, Clone)]
11+
#[cfg_attr(not(feature = "spans"), derive(PartialEq, Eq))]
912
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1013
#[cfg_attr(feature = "serde", serde(untagged))]
1114
pub enum CommentDef<S> {
12-
Single { content: S },
13-
Multi { content: Vec<S> },
15+
Single {
16+
content: S,
17+
#[cfg(feature = "spans")]
18+
span: Span,
19+
},
20+
Multi {
21+
content: Vec<S>,
22+
#[cfg(feature = "spans")]
23+
span: Span,
24+
},
25+
}
26+
27+
#[cfg(feature = "spans")]
28+
impl<S: Eq> Eq for CommentDef<S> {}
29+
30+
#[cfg(feature = "spans")]
31+
impl<S: PartialEq> PartialEq for CommentDef<S> {
32+
fn eq(&self, other: &Self) -> bool {
33+
match (self, other) {
34+
(
35+
Self::Single {
36+
content: l_content, ..
37+
},
38+
Self::Single {
39+
content: r_content, ..
40+
},
41+
) => l_content == r_content,
42+
(
43+
Self::Multi {
44+
content: l_content, ..
45+
},
46+
Self::Multi {
47+
content: r_content, ..
48+
},
49+
) => l_content == r_content,
50+
_ => false,
51+
}
52+
}
1453
}
1554

1655
impl<S> From<CommentDef<S>> for Comment<S> {
1756
fn from(input: CommentDef<S>) -> Self {
1857
match input {
19-
CommentDef::Single { content } => Self {
58+
CommentDef::Single {
59+
content,
60+
#[cfg(feature = "spans")]
61+
span,
62+
} => Self {
2063
content: vec![content],
64+
#[cfg(feature = "spans")]
65+
span,
66+
},
67+
CommentDef::Multi {
68+
content,
69+
#[cfg(feature = "spans")]
70+
span,
71+
} => Self {
72+
content,
73+
#[cfg(feature = "spans")]
74+
span,
2175
},
22-
CommentDef::Multi { content } => Self { content },
2376
}
2477
}
2578
}

0 commit comments

Comments
 (0)