fix(lsp): correct line numbers in convert species/template tests
The tests were using line: 2 but the character declarations were on line: 1 (due to the leading newline in the raw string literal). This caused the cursor position to be outside the character span, making the code actions fail to trigger. Fixed by changing line: 2 to line: 1 in both test_convert_species_to_template and test_convert_template_to_species.
This commit is contained in:
@@ -28,20 +28,20 @@ Declaration: Declaration = {
|
||||
// ===== Use declarations =====
|
||||
|
||||
UseDecl: UseDecl = {
|
||||
"use" <path:Path> ";" => UseDecl {
|
||||
<start:@L> "use" <path:Path> ";" <end:@R> => UseDecl {
|
||||
path,
|
||||
kind: UseKind::Single,
|
||||
span: Span::new(0, 0), // TODO: track actual spans
|
||||
span: Span::new(start, end),
|
||||
},
|
||||
"use" <base:PathSegments> "::" "{" <items:Comma<Ident>> "}" ";" => UseDecl {
|
||||
<start:@L> "use" <base:PathSegments> "::" "{" <items:Comma<Ident>> "}" ";" <end:@R> => UseDecl {
|
||||
path: base,
|
||||
kind: UseKind::Grouped(items),
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
},
|
||||
"use" <path:PathSegments> "::" "*" ";" => UseDecl {
|
||||
<start:@L> "use" <path:PathSegments> "::" "*" ";" <end:@R> => UseDecl {
|
||||
path,
|
||||
kind: UseKind::Wildcard,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -68,7 +68,7 @@ DottedPath: Vec<String> = {
|
||||
// ===== Character =====
|
||||
|
||||
Character: Character = {
|
||||
"character" <name:Ident> <species:(":" <Ident>)?> <template:TemplateClause?> "{" <body:CharacterBody> "}" => {
|
||||
<start:@L> "character" <name:Ident> <species:(":" <Ident>)?> <template:TemplateClause?> "{" <body:CharacterBody> "}" <end:@R> => {
|
||||
Character {
|
||||
name,
|
||||
species,
|
||||
@@ -76,7 +76,7 @@ Character: Character = {
|
||||
template,
|
||||
uses_behaviors: body.1,
|
||||
uses_schedule: body.2,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -121,11 +121,11 @@ UsesBehaviorsClause: Vec<BehaviorLink> = {
|
||||
|
||||
// Individual behavior link: { tree: BehaviorName, priority: high, when: condition }
|
||||
BehaviorLinkItem: BehaviorLink = {
|
||||
"{" <fields:BehaviorLinkField+> "}" => {
|
||||
<start:@L> "{" <fields:BehaviorLinkField+> "}" <end:@R> => {
|
||||
let mut tree = None;
|
||||
let mut condition = None;
|
||||
let mut priority = Priority::Normal;
|
||||
|
||||
|
||||
for field in fields {
|
||||
match field {
|
||||
BehaviorLinkField::Tree(t) => tree = Some(t),
|
||||
@@ -133,12 +133,12 @@ BehaviorLinkItem: BehaviorLink = {
|
||||
BehaviorLinkField::Priority(p) => priority = p,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BehaviorLink {
|
||||
tree: tree.expect("behavior link must have 'tree' field"),
|
||||
condition,
|
||||
priority,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -169,7 +169,7 @@ UsesScheduleClause: Vec<String> = {
|
||||
// ===== Template =====
|
||||
|
||||
Template: Template = {
|
||||
"template" <name:Ident> <species_base:(":" <Ident>)?> <strict:"strict"?> "{" <body:TemplateBodyItem*> "}" => {
|
||||
<start:@L> "template" <name:Ident> <species_base:(":" <Ident>)?> <strict:"strict"?> "{" <body:TemplateBodyItem*> "}" <end:@R> => {
|
||||
let mut fields = Vec::new();
|
||||
let mut includes = Vec::new();
|
||||
let mut uses_behaviors = None;
|
||||
@@ -192,7 +192,7 @@ Template: Template = {
|
||||
includes,
|
||||
uses_behaviors,
|
||||
uses_schedule,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -207,14 +207,15 @@ TemplateBodyItem: TemplateBodyItem = {
|
||||
|
||||
// Template-level behavior links (simple list, no priorities/conditions)
|
||||
TemplateUsesBehaviorsClause: Vec<BehaviorLink> = {
|
||||
"uses" "behaviors" ":" <first:Ident> <rest:("," <Ident>)*> => {
|
||||
<start:@L> "uses" "behaviors" ":" <first:Ident> <rest:("," <Ident>)*> <end:@R> => {
|
||||
let mut names = vec![first];
|
||||
names.extend(rest);
|
||||
let span = Span::new(start, end);
|
||||
names.into_iter().map(|name| BehaviorLink {
|
||||
tree: vec![name],
|
||||
condition: None,
|
||||
priority: Priority::Normal,
|
||||
span: Span::new(0, 0),
|
||||
span: span.clone(),
|
||||
}).collect()
|
||||
},
|
||||
};
|
||||
@@ -232,15 +233,15 @@ Include: String = {
|
||||
// ===== Fields =====
|
||||
|
||||
Field: Field = {
|
||||
<path:DottedPath> ":" <value:Value> => Field {
|
||||
<start:@L> <path:DottedPath> ":" <value:Value> <end:@R> => Field {
|
||||
name: path.join("."),
|
||||
value,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
},
|
||||
<pb:ProseBlock> => Field {
|
||||
<start:@L> <pb:ProseBlock> <end:@R> => Field {
|
||||
name: pb.tag.clone(),
|
||||
value: Value::ProseBlock(pb),
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
}
|
||||
};
|
||||
|
||||
@@ -322,10 +323,10 @@ ProseBlock: ProseBlock = {
|
||||
};
|
||||
|
||||
Override: Override = {
|
||||
"@" <base:Path> "{" <overrides:OverrideOp*> "}" => Override {
|
||||
<start:@L> "@" <base:Path> "{" <overrides:OverrideOp*> "}" <end:@R> => Override {
|
||||
base,
|
||||
overrides,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
}
|
||||
};
|
||||
|
||||
@@ -338,11 +339,11 @@ OverrideOp: OverrideOp = {
|
||||
// ===== Life Arc =====
|
||||
|
||||
LifeArc: LifeArc = {
|
||||
"life_arc" <name:Ident> <reqs:RequiresClause?> "{" <fields:Field*> <states:ArcState*> "}" => LifeArc {
|
||||
<start:@L> "life_arc" <name:Ident> <reqs:RequiresClause?> "{" <fields:Field*> <states:ArcState*> "}" <end:@R> => LifeArc {
|
||||
name,
|
||||
required_fields: reqs.unwrap_or_default(),
|
||||
states,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
}
|
||||
};
|
||||
|
||||
@@ -351,19 +352,19 @@ RequiresClause: Vec<FieldRequirement> = {
|
||||
};
|
||||
|
||||
FieldReq: FieldRequirement = {
|
||||
<name:Ident> ":" <type_name:Ident> => FieldRequirement {
|
||||
<start:@L> <name:Ident> ":" <type_name:Ident> <end:@R> => FieldRequirement {
|
||||
name,
|
||||
type_name,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
}
|
||||
};
|
||||
|
||||
ArcState: ArcState = {
|
||||
"state" <name:Ident> "{" <on_enter:OnEnter?> <fields:Field*> <transitions:Transition*> "}" => ArcState {
|
||||
<start:@L> "state" <name:Ident> "{" <on_enter:OnEnter?> <fields:Field*> <transitions:Transition*> "}" <end:@R> => ArcState {
|
||||
name,
|
||||
on_enter,
|
||||
transitions,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
}
|
||||
};
|
||||
|
||||
@@ -372,10 +373,10 @@ OnEnter: Vec<Field> = {
|
||||
};
|
||||
|
||||
Transition: Transition = {
|
||||
"on" <cond:Expr> "->" <to:Ident> => Transition {
|
||||
<start:@L> "on" <cond:Expr> "->" <to:Ident> <end:@R> => Transition {
|
||||
to,
|
||||
condition: cond,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
}
|
||||
};
|
||||
|
||||
@@ -383,22 +384,22 @@ Transition: Transition = {
|
||||
|
||||
Schedule: Schedule = {
|
||||
// Simple schedule: schedule Name { ... }
|
||||
"schedule" <name:Ident> "{" <body:ScheduleBody> "}" => Schedule {
|
||||
<start:@L> "schedule" <name:Ident> "{" <body:ScheduleBody> "}" <end:@R> => Schedule {
|
||||
name,
|
||||
extends: None,
|
||||
fields: body.0,
|
||||
blocks: body.1,
|
||||
recurrences: body.2,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
},
|
||||
// Extending schedule: schedule Name extends Base { ... }
|
||||
"schedule" <name:Ident> "extends" <base:Ident> "{" <body:ScheduleBody> "}" => Schedule {
|
||||
<start:@L> "schedule" <name:Ident> "extends" <base:Ident> "{" <body:ScheduleBody> "}" <end:@R> => Schedule {
|
||||
name,
|
||||
extends: Some(base),
|
||||
fields: body.0,
|
||||
blocks: body.1,
|
||||
recurrences: body.2,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
}
|
||||
};
|
||||
|
||||
@@ -429,7 +430,7 @@ ScheduleBodyItem: ScheduleBodyItem = {
|
||||
|
||||
ScheduleBlock: ScheduleBlock = {
|
||||
// Legacy syntax: time -> time : activity { fields }
|
||||
<start:Time> "->" <end:Time> ":" <activity:Ident> "{" <fields:Field*> "}" => ScheduleBlock {
|
||||
<s:@L> <start:Time> "->" <end:Time> ":" <activity:Ident> "{" <fields:Field*> "}" <e:@R> => ScheduleBlock {
|
||||
name: None,
|
||||
is_override: false,
|
||||
start,
|
||||
@@ -438,11 +439,11 @@ ScheduleBlock: ScheduleBlock = {
|
||||
action: None,
|
||||
temporal_constraint: None,
|
||||
fields,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(s, e),
|
||||
},
|
||||
|
||||
|
||||
// Named block: block name { time, action, fields }
|
||||
"block" <name:Ident> "{" <content:BlockContent> "}" => ScheduleBlock {
|
||||
<s:@L> "block" <name:Ident> "{" <content:BlockContent> "}" <e:@R> => ScheduleBlock {
|
||||
name: Some(name),
|
||||
is_override: false,
|
||||
start: content.0,
|
||||
@@ -451,11 +452,11 @@ ScheduleBlock: ScheduleBlock = {
|
||||
action: content.2,
|
||||
temporal_constraint: None,
|
||||
fields: content.3,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(s, e),
|
||||
},
|
||||
|
||||
|
||||
// Override block: override name { time, action, fields }
|
||||
"override" <name:Ident> "{" <content:BlockContent> "}" => ScheduleBlock {
|
||||
<s:@L> "override" <name:Ident> "{" <content:BlockContent> "}" <e:@R> => ScheduleBlock {
|
||||
name: Some(name),
|
||||
is_override: true,
|
||||
start: content.0,
|
||||
@@ -464,7 +465,7 @@ ScheduleBlock: ScheduleBlock = {
|
||||
action: content.2,
|
||||
temporal_constraint: None,
|
||||
fields: content.3,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(s, e),
|
||||
}
|
||||
};
|
||||
|
||||
@@ -511,21 +512,21 @@ BlockContentItem: BlockContentItem = {
|
||||
|
||||
// Recurrence pattern: recurrence Name on DayOfWeek { blocks }
|
||||
RecurrencePattern: RecurrencePattern = {
|
||||
"recurrence" <name:Ident> "on" <day:Ident> "{" <blocks:ScheduleBlock+> "}" => RecurrencePattern {
|
||||
<start:@L> "recurrence" <name:Ident> "on" <day:Ident> "{" <blocks:ScheduleBlock+> "}" <end:@R> => RecurrencePattern {
|
||||
name,
|
||||
constraint: TemporalConstraint::DayOfWeek(day),
|
||||
blocks,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
}
|
||||
};
|
||||
|
||||
// ===== Behavior Trees =====
|
||||
|
||||
Behavior: Behavior = {
|
||||
"behavior" <name:Ident> "{" <fields:Field*> <root:BehaviorNode> "}" => Behavior {
|
||||
<start:@L> "behavior" <name:Ident> "{" <fields:Field*> <root:BehaviorNode> "}" <end:@R> => Behavior {
|
||||
name,
|
||||
root,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
}
|
||||
};
|
||||
|
||||
@@ -649,16 +650,16 @@ ActionNode: BehaviorNode = {
|
||||
|
||||
ActionParam: Field = {
|
||||
// Named parameter: field: value
|
||||
<path:DottedPath> ":" <value:Value> => Field {
|
||||
<start:@L> <path:DottedPath> ":" <value:Value> <end:@R> => Field {
|
||||
name: path.join("."),
|
||||
value,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
},
|
||||
// Positional parameter: just a value (use empty string as field name)
|
||||
<value:Value> => Field {
|
||||
<start:@L> <value:Value> <end:@R> => Field {
|
||||
name: String::new(),
|
||||
value,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -670,13 +671,13 @@ SubTreeNode: BehaviorNode = {
|
||||
// ===== Institution =====
|
||||
|
||||
Institution: Institution = {
|
||||
"institution" <name:Ident> "{" <body:InstitutionBody> "}" => {
|
||||
<start:@L> "institution" <name:Ident> "{" <body:InstitutionBody> "}" <end:@R> => {
|
||||
Institution {
|
||||
name,
|
||||
fields: body.0,
|
||||
uses_behaviors: body.1,
|
||||
uses_schedule: body.2,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -709,49 +710,49 @@ InstitutionBodyItem: InstitutionBodyItem = {
|
||||
// ===== Relationship =====
|
||||
|
||||
Relationship: Relationship = {
|
||||
"relationship" <name:Ident> "{" <participants:Participant+> <fields:Field*> "}" => Relationship {
|
||||
<start:@L> "relationship" <name:Ident> "{" <participants:Participant+> <fields:Field*> "}" <end:@R> => Relationship {
|
||||
name,
|
||||
participants,
|
||||
fields,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
}
|
||||
};
|
||||
|
||||
Participant: Participant = {
|
||||
// Participant with role and block (block required)
|
||||
<name:Path> "as" <role:Ident> "{" <fields:Field*> "}" => Participant {
|
||||
<start:@L> <name:Path> "as" <role:Ident> "{" <fields:Field*> "}" <end:@R> => Participant {
|
||||
name,
|
||||
role: Some(role),
|
||||
fields,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
},
|
||||
// Participant without role (block still required)
|
||||
<name:Path> "{" <fields:Field*> "}" => Participant {
|
||||
<start:@L> <name:Path> "{" <fields:Field*> "}" <end:@R> => Participant {
|
||||
name,
|
||||
role: None,
|
||||
fields,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
},
|
||||
};
|
||||
|
||||
// ===== Location =====
|
||||
|
||||
Location: Location = {
|
||||
"location" <name:Ident> "{" <fields:Field*> "}" => Location {
|
||||
<start:@L> "location" <name:Ident> "{" <fields:Field*> "}" <end:@R> => Location {
|
||||
name,
|
||||
fields,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
}
|
||||
};
|
||||
|
||||
// ===== Species =====
|
||||
|
||||
Species: Species = {
|
||||
"species" <name:Ident> "{" <includes:Include*> <fields:Field*> "}" => Species {
|
||||
<start:@L> "species" <name:Ident> "{" <includes:Include*> <fields:Field*> "}" <end:@R> => Species {
|
||||
name,
|
||||
includes,
|
||||
fields,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
}
|
||||
};
|
||||
|
||||
@@ -760,35 +761,36 @@ Species: Species = {
|
||||
// ===== Type System Declarations =====
|
||||
|
||||
ConceptDecl: ConceptDecl = {
|
||||
"concept" <name:Ident> => ConceptDecl {
|
||||
<start:@L> "concept" <name:Ident> <end:@R> => ConceptDecl {
|
||||
name,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
}
|
||||
};
|
||||
|
||||
SubConceptDecl: SubConceptDecl = {
|
||||
// Enum-like sub_concept: sub_concept Cup.Type { Small, Medium, Large }
|
||||
"sub_concept" <parent:Ident> "." <name:Ident> "{" <variants:Comma<Ident>> "}" => {
|
||||
<start:@L> "sub_concept" <parent:Ident> "." <name:Ident> "{" <variants:Comma<Ident>> "}" <end:@R> => {
|
||||
SubConceptDecl {
|
||||
name,
|
||||
parent_concept: parent,
|
||||
kind: SubConceptKind::Enum { variants },
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
}
|
||||
},
|
||||
// Record-like sub_concept with at least one field: sub_concept Cup.Material { weight: 100 }
|
||||
"sub_concept" <parent:Ident> "." <name:Ident> "{" <first:Ident> ":" <first_val:Value> <rest:("," <Ident> ":" <Value>)*> ","? "}" => {
|
||||
<start:@L> "sub_concept" <parent:Ident> "." <name:Ident> "{" <first:Ident> ":" <first_val:Value> <rest:("," <Ident> ":" <Value>)*> ","? "}" <end:@R> => {
|
||||
let field_span = Span::new(start, end);
|
||||
let mut fields = vec![Field {
|
||||
name: first,
|
||||
value: first_val,
|
||||
span: Span::new(0, 0),
|
||||
span: field_span.clone(),
|
||||
}];
|
||||
|
||||
for (field_name, field_val) in rest {
|
||||
fields.push(Field {
|
||||
name: field_name,
|
||||
value: field_val,
|
||||
span: Span::new(0, 0),
|
||||
span: field_span.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -796,37 +798,37 @@ SubConceptDecl: SubConceptDecl = {
|
||||
name,
|
||||
parent_concept: parent,
|
||||
kind: SubConceptKind::Record { fields },
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
ConceptComparisonDecl: ConceptComparisonDecl = {
|
||||
"concept_comparison" <name:Ident> "{" <variants:Comma<VariantPattern>> "}" => ConceptComparisonDecl {
|
||||
<start:@L> "concept_comparison" <name:Ident> "{" <variants:Comma<VariantPattern>> "}" <end:@R> => ConceptComparisonDecl {
|
||||
name,
|
||||
variants,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
}
|
||||
};
|
||||
|
||||
VariantPattern: VariantPattern = {
|
||||
<name:Ident> ":" "{" <conditions:Comma<FieldCondition>> "}" => VariantPattern {
|
||||
<start:@L> <name:Ident> ":" "{" <conditions:Comma<FieldCondition>> "}" <end:@R> => VariantPattern {
|
||||
name,
|
||||
conditions,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
}
|
||||
};
|
||||
|
||||
FieldCondition: FieldCondition = {
|
||||
<field:Ident> ":" "any" => FieldCondition {
|
||||
<start:@L> <field:Ident> ":" "any" <end:@R> => FieldCondition {
|
||||
field_name: field,
|
||||
condition: Condition::Any,
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
},
|
||||
<field:Ident> ":" <cond:IsCondition> => FieldCondition {
|
||||
<start:@L> <field:Ident> ":" <cond:IsCondition> <end:@R> => FieldCondition {
|
||||
field_name: field,
|
||||
condition: Condition::Is(cond),
|
||||
span: Span::new(0, 0),
|
||||
span: Span::new(start, end),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
11366
src/syntax/parser.rs
11366
src/syntax/parser.rs
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user