feat: support one_of syntax shape in user code

This commit is contained in:
Bahex 2025-04-26 10:45:08 +03:00
parent 9976718363
commit ec2b9769c9

View File

@ -60,6 +60,7 @@ pub fn parse_shape_name(
b"int" => SyntaxShape::Int, b"int" => SyntaxShape::Int,
_ if bytes.starts_with(b"list") => parse_list_shape(working_set, bytes, span, use_loc), _ if bytes.starts_with(b"list") => parse_list_shape(working_set, bytes, span, use_loc),
b"nothing" => SyntaxShape::Nothing, b"nothing" => SyntaxShape::Nothing,
_ if bytes.starts_with(b"one_of") => parse_one_of_shape(working_set, bytes, span, use_loc),
b"number" => SyntaxShape::Number, b"number" => SyntaxShape::Number,
b"path" => SyntaxShape::Filepath, b"path" => SyntaxShape::Filepath,
b"range" => SyntaxShape::Range, b"range" => SyntaxShape::Range,
@ -143,7 +144,8 @@ fn parse_collection_shape(
if bytes == name.as_bytes() { if bytes == name.as_bytes() {
mk_shape(vec![]) mk_shape(vec![])
} else if bytes.starts_with(prefix) { } else if bytes.starts_with(prefix) {
let Some(inner_span) = prepare_inner_span(working_set, bytes, span, prefix_len) else { let Some(inner_span) = prepare_inner_span(working_set, bytes, span, prefix_len, b'>')
else {
return SyntaxShape::Any; return SyntaxShape::Any;
}; };
@ -245,6 +247,84 @@ fn parse_collection_shape(
} }
} }
fn parse_one_of_shape(
working_set: &mut StateWorkingSet,
bytes: &[u8],
span: Span,
use_loc: ShapeDescriptorUse,
) -> SyntaxShape {
assert!(bytes.starts_with(b"one_of"));
let name = "one_of";
let prefix = b"one_of(";
let prefix_len = prefix.len();
let mk_shape = |ty| -> SyntaxShape { SyntaxShape::OneOf(ty) };
if bytes == name.as_bytes() {
mk_shape(vec![])
} else if bytes.starts_with(prefix) {
let Some(inner_span) = prepare_inner_span(working_set, bytes, span, prefix_len, b')')
else {
return SyntaxShape::Any;
};
// one_of()
if inner_span.end - inner_span.start == 0 {
return mk_shape(vec![]);
}
let source = working_set.get_span_contents(inner_span);
let (tokens, err) = lex_signature(
source,
inner_span.start,
&[b'\n', b'\r'],
&[b':', b','],
true,
);
if let Some(err) = err {
working_set.error(err);
// lexer errors cause issues with span overflows
return mk_shape(vec![]);
}
let mut sig = vec![];
let mut idx = 0;
let key_error = |span| {
ParseError::LabeledError(
format!("`{name}` type annotations key not string"),
"must be a string".into(),
span,
)
};
while idx < tokens.len() {
let TokenContents::Item = tokens[idx].contents else {
working_set.error(key_error(tokens[idx].span));
return mk_shape(vec![]);
};
if working_set
.get_span_contents(tokens[idx].span)
.starts_with(b",")
{
idx += 1;
continue;
}
let shape_bytes = working_set.get_span_contents(tokens[idx].span).to_vec();
let shape = parse_shape_name(working_set, &shape_bytes, tokens[idx].span, use_loc);
sig.push(shape);
idx += 1;
}
mk_shape(sig)
} else {
working_set.error(ParseError::UnknownType(span));
SyntaxShape::Any
}
}
fn parse_list_shape( fn parse_list_shape(
working_set: &mut StateWorkingSet, working_set: &mut StateWorkingSet,
bytes: &[u8], bytes: &[u8],
@ -256,7 +336,7 @@ fn parse_list_shape(
if bytes == b"list" { if bytes == b"list" {
SyntaxShape::List(Box::new(SyntaxShape::Any)) SyntaxShape::List(Box::new(SyntaxShape::Any))
} else if bytes.starts_with(b"list<") { } else if bytes.starts_with(b"list<") {
let Some(inner_span) = prepare_inner_span(working_set, bytes, span, 5) else { let Some(inner_span) = prepare_inner_span(working_set, bytes, span, 5, b'>') else {
return SyntaxShape::Any; return SyntaxShape::Any;
}; };
@ -284,14 +364,15 @@ fn prepare_inner_span(
bytes: &[u8], bytes: &[u8],
span: Span, span: Span,
prefix_len: usize, prefix_len: usize,
terminator: u8,
) -> Option<Span> { ) -> Option<Span> {
let start = span.start + prefix_len; let start = span.start + prefix_len;
if bytes.ends_with(b">") { if bytes.ends_with(&[terminator]) {
let end = span.end - 1; let end = span.end - 1;
Some(Span::new(start, end)) Some(Span::new(start, end))
} else if bytes.contains(&b'>') { } else if bytes.contains(&terminator) {
let angle_start = bytes.split(|it| it == &b'>').collect::<Vec<_>>()[0].len() + 1; let angle_start = bytes.split(|it| it == &terminator).collect::<Vec<_>>()[0].len() + 1;
let span = Span::new(span.start + angle_start, span.end); let span = Span::new(span.start + angle_start, span.end);
working_set.error(ParseError::LabeledError( working_set.error(ParseError::LabeledError(
@ -302,7 +383,7 @@ fn prepare_inner_span(
None None
} else { } else {
working_set.error(ParseError::Unclosed(">".into(), span)); working_set.error(ParseError::Unclosed((terminator as char).into(), span));
None None
} }
} }