Remove annotations from non-code-meta and store them in nodes (#5360)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
@ -60,7 +60,8 @@ export class KclManager {
|
||||
nonCodeNodes: {},
|
||||
startNodes: [],
|
||||
},
|
||||
trivia: [],
|
||||
innerAttrs: [],
|
||||
outerAttrs: [],
|
||||
}
|
||||
private _execState: ExecState = emptyExecState()
|
||||
private _variables: VariableMap = {}
|
||||
@ -255,7 +256,8 @@ export class KclManager {
|
||||
nonCodeNodes: {},
|
||||
startNodes: [],
|
||||
},
|
||||
trivia: [],
|
||||
innerAttrs: [],
|
||||
outerAttrs: [],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,7 +134,7 @@ describe('Testing findUniqueName', () => {
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
},
|
||||
{
|
||||
type: 'Identifier',
|
||||
@ -142,7 +142,7 @@ describe('Testing findUniqueName', () => {
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
},
|
||||
{
|
||||
type: 'Identifier',
|
||||
@ -150,7 +150,7 @@ describe('Testing findUniqueName', () => {
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
},
|
||||
{
|
||||
type: 'Identifier',
|
||||
@ -158,7 +158,7 @@ describe('Testing findUniqueName', () => {
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
},
|
||||
{
|
||||
type: 'Identifier',
|
||||
@ -166,7 +166,7 @@ describe('Testing findUniqueName', () => {
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
},
|
||||
{
|
||||
type: 'Identifier',
|
||||
@ -174,7 +174,7 @@ describe('Testing findUniqueName', () => {
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
},
|
||||
{
|
||||
type: 'Identifier',
|
||||
@ -182,7 +182,7 @@ describe('Testing findUniqueName', () => {
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
},
|
||||
{
|
||||
type: 'Identifier',
|
||||
@ -190,7 +190,7 @@ describe('Testing findUniqueName', () => {
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
},
|
||||
{
|
||||
type: 'Identifier',
|
||||
@ -198,7 +198,7 @@ describe('Testing findUniqueName', () => {
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
},
|
||||
] satisfies Node<Identifier>[]),
|
||||
'yo',
|
||||
@ -217,7 +217,8 @@ describe('Testing addSketchTo', () => {
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
nonCodeMeta: { nonCodeNodes: {}, startNodes: [] },
|
||||
trivia: [],
|
||||
innerAttrs: [],
|
||||
outerAttrs: [],
|
||||
},
|
||||
'yz'
|
||||
)
|
||||
|
@ -278,7 +278,7 @@ export function mutateObjExpProp(
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -891,7 +891,7 @@ export function createLiteral(value: LiteralValue | number): Node<Literal> {
|
||||
moduleId: 0,
|
||||
value,
|
||||
raw,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
}
|
||||
}
|
||||
|
||||
@ -901,7 +901,7 @@ export function createTagDeclarator(value: string): Node<TagDeclarator> {
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
|
||||
value,
|
||||
}
|
||||
@ -913,7 +913,7 @@ export function createIdentifier(name: string): Node<Identifier> {
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
|
||||
name,
|
||||
}
|
||||
@ -925,7 +925,7 @@ export function createPipeSubstitution(): Node<PipeSubstitution> {
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
}
|
||||
}
|
||||
|
||||
@ -938,13 +938,13 @@ export function createCallExpressionStdLib(
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
callee: {
|
||||
type: 'Identifier',
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
|
||||
name,
|
||||
},
|
||||
@ -962,13 +962,13 @@ export function createCallExpressionStdLibKw(
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
callee: {
|
||||
type: 'Identifier',
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
|
||||
name,
|
||||
},
|
||||
@ -986,13 +986,13 @@ export function createCallExpression(
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
callee: {
|
||||
type: 'Identifier',
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
|
||||
name,
|
||||
},
|
||||
@ -1008,7 +1008,7 @@ export function createArrayExpression(
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
|
||||
nonCodeMeta: nonCodeMetaEmpty(),
|
||||
elements,
|
||||
@ -1023,7 +1023,7 @@ export function createPipeExpression(
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
|
||||
body,
|
||||
nonCodeMeta: nonCodeMetaEmpty(),
|
||||
@ -1041,14 +1041,14 @@ export function createVariableDeclaration(
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
|
||||
declaration: {
|
||||
type: 'VariableDeclarator',
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
|
||||
id: createIdentifier(varName),
|
||||
init,
|
||||
@ -1066,7 +1066,7 @@ export function createObjectExpression(properties: {
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
|
||||
nonCodeMeta: nonCodeMetaEmpty(),
|
||||
properties: Object.entries(properties).map(([key, value]) => ({
|
||||
@ -1074,7 +1074,7 @@ export function createObjectExpression(properties: {
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
key: createIdentifier(key),
|
||||
|
||||
value,
|
||||
@ -1091,7 +1091,7 @@ export function createUnaryExpression(
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
|
||||
operator,
|
||||
argument,
|
||||
@ -1108,7 +1108,7 @@ export function createBinaryExpression([left, operator, right]: [
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
trivia: [],
|
||||
outerAttrs: [],
|
||||
|
||||
operator,
|
||||
left,
|
||||
|
@ -1945,7 +1945,8 @@ export const updateStartProfileAtArgs: SketchLineHelper['updateArgs'] = ({
|
||||
startNodes: [],
|
||||
nonCodeNodes: [],
|
||||
},
|
||||
trivia: [],
|
||||
innerAttrs: [],
|
||||
outerAttrs: [],
|
||||
},
|
||||
pathToNode,
|
||||
}
|
||||
@ -2527,7 +2528,7 @@ function addTagKw(): addTagFn {
|
||||
start: callExpr.node.start,
|
||||
end: callExpr.node.end,
|
||||
moduleId: callExpr.node.moduleId,
|
||||
trivia: callExpr.node.trivia,
|
||||
outerAttrs: callExpr.node.outerAttrs,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -5,14 +5,18 @@ use kittycad_modeling_cmds::coord::{System, KITTYCAD, OPENGL, VULKAN};
|
||||
use crate::{
|
||||
errors::KclErrorDetails,
|
||||
execution::kcl_value::{UnitAngle, UnitLen},
|
||||
parsing::ast::types::{Expr, Node, NonCodeValue, ObjectProperty},
|
||||
parsing::ast::types::{Annotation, Expr, Node, ObjectProperty},
|
||||
KclError, SourceRange,
|
||||
};
|
||||
|
||||
/// Annotations which should cause re-execution if they change.
|
||||
pub(super) const SIGNIFICANT_ATTRS: [&str; 2] = [SETTINGS, NO_PRELUDE];
|
||||
|
||||
pub(crate) const SETTINGS: &str = "settings";
|
||||
pub(crate) const SETTINGS_UNIT_LENGTH: &str = "defaultLengthUnit";
|
||||
pub(crate) const SETTINGS_UNIT_ANGLE: &str = "defaultAngleUnit";
|
||||
pub(super) const NO_PRELUDE: &str = "no_prelude";
|
||||
|
||||
pub(super) const IMPORT_FORMAT: &str = "format";
|
||||
pub(super) const IMPORT_FORMAT_VALUES: [&str; 9] = ["fbx", "gltf", "glb", "obj", "ply", "sldprt", "stp", "step", "stl"];
|
||||
pub(super) const IMPORT_COORDS: &str = "coords";
|
||||
@ -25,35 +29,24 @@ pub(super) enum AnnotationScope {
|
||||
Module,
|
||||
}
|
||||
|
||||
pub(super) fn expect_properties<'a>(
|
||||
for_key: &'static str,
|
||||
annotation: &'a NonCodeValue,
|
||||
source_range: SourceRange,
|
||||
) -> Result<&'a [Node<ObjectProperty>], KclError> {
|
||||
match annotation {
|
||||
NonCodeValue::Annotation { name, properties } => {
|
||||
assert_eq!(name.as_ref().unwrap().name, for_key);
|
||||
Ok(&**properties.as_ref().ok_or_else(|| {
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Empty `{for_key}` annotation"),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
})?)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
pub(super) fn is_significant(attr: &&Node<Annotation>) -> bool {
|
||||
match attr.name() {
|
||||
Some(name) => SIGNIFICANT_ATTRS.contains(&name),
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn unnamed_properties<'a>(
|
||||
annotations: impl Iterator<Item = &'a NonCodeValue>,
|
||||
) -> Option<&'a [Node<ObjectProperty>]> {
|
||||
for annotation in annotations {
|
||||
if let NonCodeValue::Annotation { name: None, properties } = annotation {
|
||||
return properties.as_deref();
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
pub(super) fn expect_properties<'a>(
|
||||
for_key: &'static str,
|
||||
annotation: &'a Node<Annotation>,
|
||||
) -> Result<&'a [Node<ObjectProperty>], KclError> {
|
||||
assert_eq!(annotation.name().unwrap(), for_key);
|
||||
Ok(&**annotation.properties.as_ref().ok_or_else(|| {
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Empty `{for_key}` annotation"),
|
||||
source_ranges: vec![annotation.as_source_range()],
|
||||
})
|
||||
})?)
|
||||
}
|
||||
|
||||
pub(super) fn expect_ident(expr: &Expr) -> Result<&str, KclError> {
|
||||
|
@ -6,8 +6,8 @@ use itertools::{EitherOrBoth, Itertools};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::{
|
||||
execution::{memory::ProgramMemory, ExecState, ExecutorSettings},
|
||||
parsing::ast::types::{Node, NonCodeValue, Program},
|
||||
execution::{annotations, memory::ProgramMemory, ExecState, ExecutorSettings},
|
||||
parsing::ast::types::{Annotation, Node, Program},
|
||||
walk::Node as WalkNode,
|
||||
};
|
||||
|
||||
@ -91,7 +91,7 @@ pub(super) enum CacheResult {
|
||||
/// Returns `None` when there are no changes to the program, i.e. it is
|
||||
/// fully cached.
|
||||
pub(super) async fn get_changed_program(old: CacheInformation<'_>, new: CacheInformation<'_>) -> CacheResult {
|
||||
let mut try_reapply_settings = false;
|
||||
let mut reapply_settings = false;
|
||||
|
||||
// If the settings are different we might need to bust the cache.
|
||||
// We specifically do this before checking if they are the exact same.
|
||||
@ -107,13 +107,13 @@ pub(super) async fn get_changed_program(old: CacheInformation<'_>, new: CacheInf
|
||||
|
||||
// If anything else is different we may not need to re-execute, but rather just
|
||||
// run the settings again.
|
||||
try_reapply_settings = true;
|
||||
reapply_settings = true;
|
||||
}
|
||||
|
||||
// If the ASTs are the EXACT same we return None.
|
||||
// We don't even need to waste time computing the digests.
|
||||
if old.ast == new.ast {
|
||||
return CacheResult::NoAction(try_reapply_settings);
|
||||
return CacheResult::NoAction(reapply_settings);
|
||||
}
|
||||
|
||||
// We have to clone just because the digests are stored inline :-(
|
||||
@ -127,23 +127,27 @@ pub(super) async fn get_changed_program(old: CacheInformation<'_>, new: CacheInf
|
||||
|
||||
// Check if the digest is the same.
|
||||
if old_ast.digest == new_ast.digest {
|
||||
return CacheResult::NoAction(try_reapply_settings);
|
||||
return CacheResult::NoAction(reapply_settings);
|
||||
}
|
||||
|
||||
// Check if the annotations are different.
|
||||
if !old_ast.annotations().zip_longest(new_ast.annotations()).all(|pair| {
|
||||
if !old_ast
|
||||
.inner_attrs
|
||||
.iter()
|
||||
.filter(annotations::is_significant)
|
||||
.zip_longest(new_ast.inner_attrs.iter().filter(annotations::is_significant))
|
||||
.all(|pair| {
|
||||
match pair {
|
||||
EitherOrBoth::Both(old, new) => {
|
||||
// Compare annotations, ignoring source ranges. Digests must
|
||||
// have been computed before this.
|
||||
match (&old.value, &new.value) {
|
||||
(
|
||||
NonCodeValue::Annotation { name, properties },
|
||||
NonCodeValue::Annotation {
|
||||
let Annotation { name, properties, .. } = &old.inner;
|
||||
let Annotation {
|
||||
name: new_name,
|
||||
properties: new_properties,
|
||||
},
|
||||
) => {
|
||||
..
|
||||
} = &new.inner;
|
||||
|
||||
name.as_ref().map(|n| n.digest) == new_name.as_ref().map(|n| n.digest)
|
||||
&& properties
|
||||
.as_ref()
|
||||
@ -154,10 +158,8 @@ pub(super) async fn get_changed_program(old: CacheInformation<'_>, new: CacheInf
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}) {
|
||||
})
|
||||
{
|
||||
// If any of the annotations are different at the beginning of the
|
||||
// program, it's likely the settings, and we have to bust the cache and
|
||||
// re-execute the whole thing.
|
||||
@ -169,12 +171,7 @@ pub(super) async fn get_changed_program(old: CacheInformation<'_>, new: CacheInf
|
||||
}
|
||||
|
||||
// Check if the changes were only to Non-code areas, like comments or whitespace.
|
||||
let (clear_scene, program) = generate_changed_program(old_ast, new_ast);
|
||||
CacheResult::ReExecute {
|
||||
clear_scene,
|
||||
reapply_settings: try_reapply_settings,
|
||||
program,
|
||||
}
|
||||
generate_changed_program(old_ast, new_ast, reapply_settings)
|
||||
}
|
||||
|
||||
/// Force-generate a new CacheResult, even if one shouldn't be made. The
|
||||
@ -183,7 +180,7 @@ pub(super) async fn get_changed_program(old: CacheInformation<'_>, new: CacheInf
|
||||
/// how we construct a new [CacheResult].
|
||||
///
|
||||
/// Digests *must* be computed before calling this.
|
||||
fn generate_changed_program(old_ast: Node<Program>, mut new_ast: Node<Program>) -> (bool, Node<Program>) {
|
||||
fn generate_changed_program(old_ast: Node<Program>, mut new_ast: Node<Program>, reapply_settings: bool) -> CacheResult {
|
||||
if !old_ast.body.iter().zip(new_ast.body.iter()).all(|(old, new)| {
|
||||
let old_node: WalkNode = old.into();
|
||||
let new_node: WalkNode = new.into();
|
||||
@ -194,7 +191,11 @@ fn generate_changed_program(old_ast: Node<Program>, mut new_ast: Node<Program>)
|
||||
// means a single insertion or deletion will result in a cache
|
||||
// bust.
|
||||
|
||||
return (true, new_ast);
|
||||
return CacheResult::ReExecute {
|
||||
clear_scene: true,
|
||||
reapply_settings,
|
||||
program: new_ast,
|
||||
};
|
||||
}
|
||||
|
||||
// otherwise the overlapping section of the ast bodies matches.
|
||||
@ -210,7 +211,11 @@ fn generate_changed_program(old_ast: Node<Program>, mut new_ast: Node<Program>)
|
||||
// supporting that.
|
||||
|
||||
// Cache bust time.
|
||||
(true, new_ast)
|
||||
CacheResult::ReExecute {
|
||||
clear_scene: true,
|
||||
reapply_settings,
|
||||
program: new_ast,
|
||||
}
|
||||
}
|
||||
std::cmp::Ordering::Greater => {
|
||||
// the new AST is longer than the old AST, which means
|
||||
@ -222,7 +227,11 @@ fn generate_changed_program(old_ast: Node<Program>, mut new_ast: Node<Program>)
|
||||
|
||||
new_ast.body = new_ast.body[old_ast.body.len()..].to_owned();
|
||||
|
||||
(false, new_ast)
|
||||
CacheResult::ReExecute {
|
||||
clear_scene: false,
|
||||
reapply_settings,
|
||||
program: new_ast,
|
||||
}
|
||||
}
|
||||
std::cmp::Ordering::Equal => {
|
||||
// currently unreachable, but let's pretend like the code
|
||||
@ -234,9 +243,7 @@ fn generate_changed_program(old_ast: Node<Program>, mut new_ast: Node<Program>)
|
||||
// so but i think many things. This def needs to change
|
||||
// when the code above changes.
|
||||
|
||||
new_ast.body = vec![];
|
||||
|
||||
(false, new_ast)
|
||||
CacheResult::NoAction(reapply_settings)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -368,8 +375,10 @@ shell(firstSketch, faces = ['end'], thickness = 0.25)"#;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_get_changed_program_same_code_changed_code_comments() {
|
||||
let old = r#" // Removed the end face for the extrusion.
|
||||
async fn test_get_changed_program_same_code_changed_code_comments_attrs() {
|
||||
let old = r#"@foo(whatever = whatever)
|
||||
@bar
|
||||
// Removed the end face for the extrusion.
|
||||
firstSketch = startSketchOn('XY')
|
||||
|> startProfileAt([-12, 12], %)
|
||||
|> line(end = [24, 0])
|
||||
@ -381,7 +390,9 @@ firstSketch = startSketchOn('XY')
|
||||
// Remove the end face for the extrusion.
|
||||
shell(firstSketch, faces = ['end'], thickness = 0.25) "#;
|
||||
|
||||
let new = r#"// Remove the end face for the extrusion.
|
||||
let new = r#"@foo(whatever = 42)
|
||||
@baz
|
||||
// Remove the end face for the extrusion.
|
||||
firstSketch = startSketchOn('XY')
|
||||
|> startProfileAt([-12, 12], %)
|
||||
|> line(end = [24, 0])
|
||||
|
@ -17,9 +17,9 @@ use crate::{
|
||||
},
|
||||
modules::{ModuleId, ModulePath, ModuleRepr},
|
||||
parsing::ast::types::{
|
||||
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem, CallExpression,
|
||||
CallExpressionKw, Expr, FunctionExpression, IfExpression, ImportPath, ImportSelector, ItemVisibility,
|
||||
LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Node, NodeRef, NonCodeNode, NonCodeValue,
|
||||
Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem,
|
||||
CallExpression, CallExpressionKw, Expr, FunctionExpression, IfExpression, ImportPath, ImportSelector,
|
||||
ItemVisibility, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Node, NodeRef,
|
||||
ObjectExpression, PipeExpression, Program, TagDeclarator, UnaryExpression, UnaryOperator,
|
||||
},
|
||||
source_range::SourceRange,
|
||||
@ -37,37 +37,36 @@ enum StatementKind<'a> {
|
||||
impl ExecutorContext {
|
||||
async fn handle_annotations(
|
||||
&self,
|
||||
annotations: impl Iterator<Item = (&NonCodeValue, SourceRange)>,
|
||||
annotations: impl Iterator<Item = &Node<Annotation>>,
|
||||
scope: annotations::AnnotationScope,
|
||||
exec_state: &mut ExecState,
|
||||
) -> Result<bool, KclError> {
|
||||
let mut no_prelude = false;
|
||||
for (annotation, source_range) in annotations {
|
||||
if annotation.annotation_name() == Some(annotations::SETTINGS) {
|
||||
for annotation in annotations {
|
||||
if annotation.name() == Some(annotations::SETTINGS) {
|
||||
if scope == annotations::AnnotationScope::Module {
|
||||
let old_units = exec_state.length_unit();
|
||||
exec_state
|
||||
.mod_local
|
||||
.settings
|
||||
.update_from_annotation(annotation, source_range)?;
|
||||
exec_state.mod_local.settings.update_from_annotation(annotation)?;
|
||||
let new_units = exec_state.length_unit();
|
||||
if !self.engine.execution_kind().is_isolated() && old_units != new_units {
|
||||
self.engine.set_units(new_units.into(), source_range).await?;
|
||||
self.engine
|
||||
.set_units(new_units.into(), annotation.as_source_range())
|
||||
.await?;
|
||||
}
|
||||
} else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Settings can only be modified at the top level scope of a file".to_owned(),
|
||||
source_ranges: vec![source_range],
|
||||
source_ranges: vec![annotation.as_source_range()],
|
||||
}));
|
||||
}
|
||||
}
|
||||
if annotation.annotation_name() == Some(annotations::NO_PRELUDE) {
|
||||
if annotation.name() == Some(annotations::NO_PRELUDE) {
|
||||
if scope == annotations::AnnotationScope::Module {
|
||||
no_prelude = true;
|
||||
} else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Prelude can only be skipped at the top level scope of a file".to_owned(),
|
||||
source_ranges: vec![source_range],
|
||||
source_ranges: vec![annotation.as_source_range()],
|
||||
}));
|
||||
}
|
||||
}
|
||||
@ -87,11 +86,7 @@ impl ExecutorContext {
|
||||
if body_type == BodyType::Root {
|
||||
let _no_prelude = self
|
||||
.handle_annotations(
|
||||
program
|
||||
.non_code_meta
|
||||
.start_nodes
|
||||
.iter()
|
||||
.filter_map(|n| n.annotation().map(|result| (result, n.as_source_range()))),
|
||||
program.inner_attrs.iter(),
|
||||
annotations::AnnotationScope::Module,
|
||||
exec_state,
|
||||
)
|
||||
@ -100,7 +95,7 @@ impl ExecutorContext {
|
||||
|
||||
let mut last_expr = None;
|
||||
// Iterate over the body of the program.
|
||||
for (i, statement) in program.body.iter().enumerate() {
|
||||
for statement in &program.body {
|
||||
match statement {
|
||||
BodyItem::ImportStatement(import_stmt) => {
|
||||
if body_type != BodyType::Root {
|
||||
@ -111,9 +106,9 @@ impl ExecutorContext {
|
||||
}
|
||||
|
||||
let source_range = SourceRange::from(import_stmt);
|
||||
let meta_nodes = program.non_code_meta.get(i);
|
||||
let attrs = &import_stmt.outer_attrs;
|
||||
let module_id = self
|
||||
.open_module(&import_stmt.path, meta_nodes, exec_state, source_range)
|
||||
.open_module(&import_stmt.path, attrs, exec_state, source_range)
|
||||
.await?;
|
||||
|
||||
match &import_stmt.selector {
|
||||
@ -209,7 +204,7 @@ impl ExecutorContext {
|
||||
let source_range = SourceRange::from(&variable_declaration.declaration.init);
|
||||
let metadata = Metadata { source_range };
|
||||
|
||||
let _meta_nodes = program.non_code_meta.get(i);
|
||||
let _annotations = &variable_declaration.outer_attrs;
|
||||
|
||||
let memory_item = self
|
||||
.execute_expr(
|
||||
@ -279,7 +274,7 @@ impl ExecutorContext {
|
||||
async fn open_module(
|
||||
&self,
|
||||
path: &ImportPath,
|
||||
non_code_meta: &[Node<NonCodeNode>],
|
||||
attrs: &[Node<Annotation>],
|
||||
exec_state: &mut ExecState,
|
||||
source_range: SourceRange,
|
||||
) -> Result<ModuleId, KclError> {
|
||||
@ -307,7 +302,7 @@ impl ExecutorContext {
|
||||
|
||||
let id = exec_state.next_module_id();
|
||||
let path = resolved_path.expect_path();
|
||||
let format = super::import::format_from_annotations(non_code_meta, path, source_range)?;
|
||||
let format = super::import::format_from_annotations(attrs, path, source_range)?;
|
||||
let geom = super::import::import_foreign(path, format, exec_state, self, source_range).await?;
|
||||
exec_state.add_module(id, resolved_path, ModuleRepr::Foreign(geom));
|
||||
Ok(id)
|
||||
@ -1838,18 +1833,7 @@ mod test {
|
||||
// Run each test.
|
||||
let func_expr = &Node::no_src(FunctionExpression {
|
||||
params,
|
||||
body: Node {
|
||||
inner: crate::parsing::ast::types::Program {
|
||||
body: Vec::new(),
|
||||
non_code_meta: Default::default(),
|
||||
shebang: None,
|
||||
digest: None,
|
||||
},
|
||||
start: 0,
|
||||
end: 0,
|
||||
module_id: ModuleId::default(),
|
||||
trivia: Vec::new(),
|
||||
},
|
||||
body: crate::parsing::ast::types::Program::empty(),
|
||||
return_type: None,
|
||||
digest: None,
|
||||
});
|
||||
|
@ -19,7 +19,7 @@ use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{annotations, kcl_value::UnitLen, ExecState, ExecutorContext, ImportedGeometry},
|
||||
fs::FileSystem,
|
||||
parsing::ast::types::{Node, NonCodeNode},
|
||||
parsing::ast::types::{Annotation, Node},
|
||||
source_range::SourceRange,
|
||||
};
|
||||
|
||||
@ -155,16 +155,18 @@ pub async fn import_foreign(
|
||||
}
|
||||
|
||||
pub(super) fn format_from_annotations(
|
||||
non_code_meta: &[Node<NonCodeNode>],
|
||||
annotations: &[Node<Annotation>],
|
||||
path: &Path,
|
||||
import_source_range: SourceRange,
|
||||
) -> Result<Option<InputFormat>, KclError> {
|
||||
let Some(props) = annotations::unnamed_properties(non_code_meta.iter().map(|n| &n.value)) else {
|
||||
if annotations.is_empty() {
|
||||
return Ok(None);
|
||||
};
|
||||
}
|
||||
|
||||
let props = annotations.iter().flat_map(|a| a.properties.as_deref().unwrap_or(&[]));
|
||||
|
||||
let mut result = None;
|
||||
for p in props {
|
||||
for p in props.clone() {
|
||||
if p.key.name == annotations::IMPORT_FORMAT {
|
||||
result = Some(
|
||||
get_import_format_from_extension(annotations::expect_ident(&p.value)?).map_err(|_| {
|
||||
@ -422,8 +424,8 @@ mod test {
|
||||
// no format, no options
|
||||
let text = "@()\nimport '../foo.gltf' as foo";
|
||||
let parsed = crate::Program::parse_no_errs(text).unwrap().ast;
|
||||
let non_code_meta = parsed.non_code_meta.get(0);
|
||||
let fmt = format_from_annotations(non_code_meta, Path::new("../foo.gltf"), SourceRange::default())
|
||||
let attrs = parsed.body[0].get_attrs();
|
||||
let fmt = format_from_annotations(attrs, Path::new("../foo.gltf"), SourceRange::default())
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
@ -434,8 +436,8 @@ mod test {
|
||||
// format, no options
|
||||
let text = "@(format = gltf)\nimport '../foo.txt' as foo";
|
||||
let parsed = crate::Program::parse_no_errs(text).unwrap().ast;
|
||||
let non_code_meta = parsed.non_code_meta.get(0);
|
||||
let fmt = format_from_annotations(non_code_meta, Path::new("../foo.txt"), SourceRange::default())
|
||||
let attrs = parsed.body[0].get_attrs();
|
||||
let fmt = format_from_annotations(attrs, Path::new("../foo.txt"), SourceRange::default())
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
@ -444,7 +446,7 @@ mod test {
|
||||
);
|
||||
|
||||
// format, no extension (wouldn't parse but might some day)
|
||||
let fmt = format_from_annotations(non_code_meta, Path::new("../foo"), SourceRange::default())
|
||||
let fmt = format_from_annotations(attrs, Path::new("../foo"), SourceRange::default())
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
@ -455,8 +457,8 @@ mod test {
|
||||
// format, options
|
||||
let text = "@(format = obj, coords = vulkan, lengthUnit = ft)\nimport '../foo.txt' as foo";
|
||||
let parsed = crate::Program::parse_no_errs(text).unwrap().ast;
|
||||
let non_code_meta = parsed.non_code_meta.get(0);
|
||||
let fmt = format_from_annotations(non_code_meta, Path::new("../foo.txt"), SourceRange::default())
|
||||
let attrs = parsed.body[0].get_attrs();
|
||||
let fmt = format_from_annotations(attrs, Path::new("../foo.txt"), SourceRange::default())
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
@ -470,8 +472,8 @@ mod test {
|
||||
// no format, options
|
||||
let text = "@(coords = vulkan, lengthUnit = ft)\nimport '../foo.obj' as foo";
|
||||
let parsed = crate::Program::parse_no_errs(text).unwrap().ast;
|
||||
let non_code_meta = parsed.non_code_meta.get(0);
|
||||
let fmt = format_from_annotations(non_code_meta, Path::new("../foo.obj"), SourceRange::default())
|
||||
let attrs = parsed.body[0].get_attrs();
|
||||
let fmt = format_from_annotations(attrs, Path::new("../foo.obj"), SourceRange::default())
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
@ -523,8 +525,8 @@ mod test {
|
||||
#[track_caller]
|
||||
fn assert_annotation_error(src: &str, path: &str, expected: &str) {
|
||||
let parsed = crate::Program::parse_no_errs(src).unwrap().ast;
|
||||
let non_code_meta = parsed.non_code_meta.get(0);
|
||||
let err = format_from_annotations(non_code_meta, Path::new(path), SourceRange::default()).unwrap_err();
|
||||
let attrs = parsed.body[0].get_attrs();
|
||||
let err = format_from_annotations(attrs, Path::new(path), SourceRange::default()).unwrap_err();
|
||||
assert!(
|
||||
err.message().contains(expected),
|
||||
"Expected: `{expected}`, found `{}`",
|
||||
|
@ -12,7 +12,7 @@ use crate::{
|
||||
ExecOutcome, ExecutorSettings, KclValue, Operation, UnitAngle, UnitLen,
|
||||
},
|
||||
modules::{ModuleId, ModuleInfo, ModuleLoader, ModulePath, ModuleRepr},
|
||||
parsing::ast::types::NonCodeValue,
|
||||
parsing::ast::types::Annotation,
|
||||
source_range::SourceRange,
|
||||
};
|
||||
|
||||
@ -236,21 +236,20 @@ pub struct MetaSettings {
|
||||
impl MetaSettings {
|
||||
pub(crate) fn update_from_annotation(
|
||||
&mut self,
|
||||
annotation: &NonCodeValue,
|
||||
source_range: SourceRange,
|
||||
annotation: &crate::parsing::ast::types::Node<Annotation>,
|
||||
) -> Result<(), KclError> {
|
||||
let properties = annotations::expect_properties(annotations::SETTINGS, annotation, source_range)?;
|
||||
let properties = annotations::expect_properties(annotations::SETTINGS, annotation)?;
|
||||
|
||||
for p in properties {
|
||||
match &*p.inner.key.name {
|
||||
annotations::SETTINGS_UNIT_LENGTH => {
|
||||
let value = annotations::expect_ident(&p.inner.value)?;
|
||||
let value = kcl_value::UnitLen::from_str(value, source_range)?;
|
||||
let value = kcl_value::UnitLen::from_str(value, annotation.as_source_range())?;
|
||||
self.default_length_units = value;
|
||||
}
|
||||
annotations::SETTINGS_UNIT_ANGLE => {
|
||||
let value = annotations::expect_ident(&p.inner.value)?;
|
||||
let value = kcl_value::UnitAngle::from_str(value, source_range)?;
|
||||
let value = kcl_value::UnitAngle::from_str(value, annotation.as_source_range())?;
|
||||
self.default_angle_units = value;
|
||||
}
|
||||
name => {
|
||||
@ -260,7 +259,7 @@ impl MetaSettings {
|
||||
annotations::SETTINGS_UNIT_LENGTH,
|
||||
annotations::SETTINGS_UNIT_ANGLE
|
||||
),
|
||||
source_ranges: vec![source_range],
|
||||
source_ranges: vec![annotation.as_source_range()],
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,10 @@
|
||||
use sha2::{Digest as DigestTrait, Sha256};
|
||||
|
||||
use super::types::{
|
||||
DefaultParamVal, ItemVisibility, LabelledExpression, LiteralValue, NonCodeMeta, NonCodeNode, NonCodeValue,
|
||||
VariableKind,
|
||||
};
|
||||
use super::types::{DefaultParamVal, ItemVisibility, LabelledExpression, LiteralValue, VariableKind};
|
||||
use crate::parsing::ast::types::{
|
||||
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryPart, BodyItem, CallExpression, CallExpressionKw,
|
||||
ElseIf, Expr, ExpressionStatement, FnArgType, FunctionExpression, Identifier, IfExpression, ImportItem,
|
||||
ImportSelector, ImportStatement, KclNone, Literal, LiteralIdentifier, MemberExpression, MemberObject,
|
||||
Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryPart, BodyItem, CallExpression,
|
||||
CallExpressionKw, ElseIf, Expr, ExpressionStatement, FnArgType, FunctionExpression, Identifier, IfExpression,
|
||||
ImportItem, ImportSelector, ImportStatement, KclNone, Literal, LiteralIdentifier, MemberExpression, MemberObject,
|
||||
ObjectExpression, ObjectProperty, Parameter, PipeExpression, PipeSubstitution, Program, ReturnStatement,
|
||||
TagDeclarator, UnaryExpression, VariableDeclaration, VariableDeclarator,
|
||||
};
|
||||
@ -82,49 +79,22 @@ impl Program {
|
||||
for body_item in slf.body.iter_mut() {
|
||||
hasher.update(body_item.compute_digest());
|
||||
}
|
||||
// This contains settings annotations.
|
||||
hasher.update(slf.non_code_meta.compute_digest());
|
||||
for attr in &mut slf.inner_attrs {
|
||||
hasher.update(attr.compute_digest());
|
||||
}
|
||||
if let Some(shebang) = &slf.shebang {
|
||||
hasher.update(&shebang.inner.content);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
impl NonCodeMeta {
|
||||
compute_digest!(|slf, hasher| {
|
||||
for non_code_node in slf.start_nodes.iter_mut() {
|
||||
hasher.update(non_code_node.compute_digest());
|
||||
}
|
||||
for (_, non_code_nodes) in slf.non_code_nodes.iter_mut() {
|
||||
for non_code_node in non_code_nodes.iter_mut() {
|
||||
hasher.update(non_code_node.compute_digest());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
impl NonCodeNode {
|
||||
compute_digest!(|slf, hasher| {
|
||||
hasher.update(slf.value.compute_digest());
|
||||
});
|
||||
}
|
||||
|
||||
impl NonCodeValue {
|
||||
impl Annotation {
|
||||
pub fn compute_digest(&mut self) -> Digest {
|
||||
let mut hasher = Sha256::new();
|
||||
match self {
|
||||
NonCodeValue::InlineComment { .. } => {}
|
||||
NonCodeValue::BlockComment { .. } => {}
|
||||
NonCodeValue::NewLineBlockComment { .. } => {}
|
||||
NonCodeValue::NewLine => {}
|
||||
NonCodeValue::Annotation {
|
||||
ref mut name,
|
||||
properties,
|
||||
} => {
|
||||
if let Some(name) = name {
|
||||
if let Some(name) = &mut self.name {
|
||||
hasher.update(name.compute_digest());
|
||||
}
|
||||
if let Some(properties) = properties {
|
||||
if let Some(properties) = &mut self.properties {
|
||||
hasher.update(properties.len().to_ne_bytes());
|
||||
for property in properties.iter_mut() {
|
||||
hasher.update(property.compute_digest());
|
||||
@ -132,20 +102,24 @@ impl NonCodeValue {
|
||||
} else {
|
||||
hasher.update("no_properties");
|
||||
}
|
||||
}
|
||||
}
|
||||
hasher.finalize().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl BodyItem {
|
||||
pub fn compute_digest(&mut self) -> Digest {
|
||||
match self {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(match self {
|
||||
BodyItem::ImportStatement(s) => s.compute_digest(),
|
||||
BodyItem::ExpressionStatement(es) => es.compute_digest(),
|
||||
BodyItem::VariableDeclaration(vs) => vs.compute_digest(),
|
||||
BodyItem::ReturnStatement(rs) => rs.compute_digest(),
|
||||
});
|
||||
|
||||
for a in self.get_attrs_mut() {
|
||||
hasher.update(a.compute_digest());
|
||||
}
|
||||
hasher.finalize().into()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ pub struct Node<T> {
|
||||
#[serde(default, skip_serializing_if = "ModuleId::is_top_level")]
|
||||
pub module_id: ModuleId,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub trivia: NodeList<NonCodeNode>,
|
||||
pub outer_attrs: NodeList<Annotation>,
|
||||
}
|
||||
|
||||
impl<T> Node<T> {
|
||||
@ -96,7 +96,7 @@ impl<T> Node<T> {
|
||||
start,
|
||||
end,
|
||||
module_id,
|
||||
trivia: Vec::new(),
|
||||
outer_attrs: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,7 +106,7 @@ impl<T> Node<T> {
|
||||
start: 0,
|
||||
end: 0,
|
||||
module_id: ModuleId::default(),
|
||||
trivia: Vec::new(),
|
||||
outer_attrs: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,7 +116,7 @@ impl<T> Node<T> {
|
||||
start,
|
||||
end,
|
||||
module_id,
|
||||
trivia: Vec::new(),
|
||||
outer_attrs: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -181,6 +181,8 @@ pub struct Program {
|
||||
pub non_code_meta: NonCodeMeta,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub shebang: Option<Node<Shebang>>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub inner_attrs: NodeList<Annotation>,
|
||||
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
@ -261,28 +263,12 @@ impl Node<Program> {
|
||||
Ok(findings)
|
||||
}
|
||||
|
||||
pub fn annotations(&self) -> impl Iterator<Item = &Node<NonCodeNode>> {
|
||||
self.non_code_meta
|
||||
.start_nodes
|
||||
.iter()
|
||||
.filter(|n| n.value_is_annotation())
|
||||
}
|
||||
|
||||
pub fn annotations_mut(&mut self) -> impl Iterator<Item = &mut Node<NonCodeNode>> {
|
||||
self.non_code_meta
|
||||
.start_nodes
|
||||
.iter_mut()
|
||||
.filter(|n| n.value_is_annotation())
|
||||
}
|
||||
|
||||
/// Get the annotations for the meta settings from the kcl file.
|
||||
pub fn meta_settings(&self) -> Result<Option<crate::execution::MetaSettings>, KclError> {
|
||||
for annotation_node in self.annotations() {
|
||||
let annotation = &annotation_node.value;
|
||||
if annotation.annotation_name() == Some(annotations::SETTINGS) {
|
||||
let source_range = annotation_node.as_source_range();
|
||||
for annotation in &self.inner_attrs {
|
||||
if annotation.name() == Some(annotations::SETTINGS) {
|
||||
let mut meta_settings = crate::execution::MetaSettings::default();
|
||||
meta_settings.update_from_annotation(annotation, source_range)?;
|
||||
meta_settings.update_from_annotation(annotation)?;
|
||||
return Ok(Some(meta_settings));
|
||||
}
|
||||
}
|
||||
@ -293,24 +279,18 @@ impl Node<Program> {
|
||||
pub fn change_meta_settings(&mut self, settings: crate::execution::MetaSettings) -> Result<Self, KclError> {
|
||||
let mut new_program = self.clone();
|
||||
let mut found = false;
|
||||
for node in new_program.annotations_mut() {
|
||||
if node.value.annotation_name() == Some(annotations::SETTINGS) {
|
||||
let annotation = NonCodeValue::new_from_meta_settings(&settings);
|
||||
*node = Node::no_src(NonCodeNode {
|
||||
value: annotation,
|
||||
digest: None,
|
||||
});
|
||||
for node in &mut new_program.inner_attrs {
|
||||
if node.name() == Some(annotations::SETTINGS) {
|
||||
*node = Node::no_src(Annotation::new_from_meta_settings(&settings));
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
let annotation = NonCodeValue::new_from_meta_settings(&settings);
|
||||
new_program.non_code_meta.start_nodes.push(Node::no_src(NonCodeNode {
|
||||
value: annotation,
|
||||
digest: None,
|
||||
}));
|
||||
new_program
|
||||
.inner_attrs
|
||||
.push(Node::no_src(Annotation::new_from_meta_settings(&settings)));
|
||||
}
|
||||
|
||||
Ok(new_program)
|
||||
@ -318,6 +298,10 @@ impl Node<Program> {
|
||||
}
|
||||
|
||||
impl Program {
|
||||
#[cfg(test)]
|
||||
pub fn empty() -> Node<Self> {
|
||||
Node::no_src(Program::default())
|
||||
}
|
||||
/// Is the last body item an expression?
|
||||
pub fn ends_with_expr(&self) -> bool {
|
||||
let Some(ref last) = self.body.last() else {
|
||||
@ -629,6 +613,33 @@ impl BodyItem {
|
||||
BodyItem::ReturnStatement(return_statement) => return_statement.end,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_attrs(&mut self, attr: NodeList<Annotation>) {
|
||||
match self {
|
||||
BodyItem::ImportStatement(node) => node.outer_attrs = attr,
|
||||
BodyItem::ExpressionStatement(node) => node.outer_attrs = attr,
|
||||
BodyItem::VariableDeclaration(node) => node.outer_attrs = attr,
|
||||
BodyItem::ReturnStatement(node) => node.outer_attrs = attr,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_attrs(&self) -> &[Node<Annotation>] {
|
||||
match self {
|
||||
BodyItem::ImportStatement(node) => &node.outer_attrs,
|
||||
BodyItem::ExpressionStatement(node) => &node.outer_attrs,
|
||||
BodyItem::VariableDeclaration(node) => &node.outer_attrs,
|
||||
BodyItem::ReturnStatement(node) => &node.outer_attrs,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_attrs_mut(&mut self) -> &mut [Node<Annotation>] {
|
||||
match self {
|
||||
BodyItem::ImportStatement(node) => &mut node.outer_attrs,
|
||||
BodyItem::ExpressionStatement(node) => &mut node.outer_attrs,
|
||||
BodyItem::VariableDeclaration(node) => &mut node.outer_attrs,
|
||||
BodyItem::ReturnStatement(node) => &mut node.outer_attrs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BodyItem> for SourceRange {
|
||||
@ -1134,24 +1145,6 @@ impl NonCodeNode {
|
||||
NonCodeValue::BlockComment { value, style: _ } => value.clone(),
|
||||
NonCodeValue::NewLineBlockComment { value, style: _ } => value.clone(),
|
||||
NonCodeValue::NewLine => "\n\n".to_string(),
|
||||
n @ NonCodeValue::Annotation { .. } => n.annotation_name().unwrap_or("").to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn annotation(&self) -> Option<&NonCodeValue> {
|
||||
match &self.value {
|
||||
a @ NonCodeValue::Annotation { .. } => Some(a),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value_is_annotation(&self) -> bool {
|
||||
match self.value {
|
||||
NonCodeValue::InlineComment { .. }
|
||||
| NonCodeValue::BlockComment { .. }
|
||||
| NonCodeValue::NewLineBlockComment { .. }
|
||||
| NonCodeValue::NewLine => false,
|
||||
NonCodeValue::Annotation { .. } => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1202,37 +1195,6 @@ pub enum NonCodeValue {
|
||||
// A new line like `\n\n` NOT a new line like `\n`.
|
||||
// This is also not a comment.
|
||||
NewLine,
|
||||
Annotation {
|
||||
name: Option<Node<Identifier>>,
|
||||
properties: Option<Vec<Node<ObjectProperty>>>,
|
||||
},
|
||||
}
|
||||
|
||||
impl NonCodeValue {
|
||||
pub fn annotation_name(&self) -> Option<&str> {
|
||||
match self {
|
||||
NonCodeValue::Annotation { name, .. } => name.as_ref().map(|i| &*i.name),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_from_meta_settings(settings: &crate::execution::MetaSettings) -> NonCodeValue {
|
||||
let mut properties: Vec<Node<ObjectProperty>> = vec![ObjectProperty::new(
|
||||
Identifier::new(annotations::SETTINGS_UNIT_LENGTH),
|
||||
Expr::Identifier(Box::new(Identifier::new(&settings.default_length_units.to_string()))),
|
||||
)];
|
||||
|
||||
if settings.default_angle_units != Default::default() {
|
||||
properties.push(ObjectProperty::new(
|
||||
Identifier::new(annotations::SETTINGS_UNIT_ANGLE),
|
||||
Expr::Identifier(Box::new(Identifier::new(&settings.default_angle_units.to_string()))),
|
||||
));
|
||||
}
|
||||
NonCodeValue::Annotation {
|
||||
name: Some(Identifier::new(annotations::SETTINGS)),
|
||||
properties: Some(properties),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
@ -1314,6 +1276,47 @@ impl<'de> Deserialize<'de> for NonCodeMeta {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type")]
|
||||
pub struct Annotation {
|
||||
pub name: Option<Node<Identifier>>,
|
||||
pub properties: Option<Vec<Node<ObjectProperty>>>,
|
||||
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub digest: Option<Digest>,
|
||||
}
|
||||
|
||||
impl Annotation {
|
||||
pub fn is_inner(&self) -> bool {
|
||||
self.name.is_some()
|
||||
}
|
||||
|
||||
pub fn name(&self) -> Option<&str> {
|
||||
self.name.as_ref().map(|n| &*n.name)
|
||||
}
|
||||
|
||||
pub fn new_from_meta_settings(settings: &crate::execution::MetaSettings) -> Annotation {
|
||||
let mut properties: Vec<Node<ObjectProperty>> = vec![ObjectProperty::new(
|
||||
Identifier::new(annotations::SETTINGS_UNIT_LENGTH),
|
||||
Expr::Identifier(Box::new(Identifier::new(&settings.default_length_units.to_string()))),
|
||||
)];
|
||||
|
||||
if settings.default_angle_units != Default::default() {
|
||||
properties.push(ObjectProperty::new(
|
||||
Identifier::new(annotations::SETTINGS_UNIT_ANGLE),
|
||||
Expr::Identifier(Box::new(Identifier::new(&settings.default_angle_units.to_string()))),
|
||||
));
|
||||
}
|
||||
Annotation {
|
||||
name: Some(Identifier::new(annotations::SETTINGS)),
|
||||
properties: Some(properties),
|
||||
digest: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type")]
|
||||
@ -3823,12 +3826,7 @@ const cylinder = startSketchOn('-XZ')
|
||||
(0..=0),
|
||||
Node::no_src(FunctionExpression {
|
||||
params: vec![],
|
||||
body: Node::no_src(Program {
|
||||
body: Vec::new(),
|
||||
non_code_meta: Default::default(),
|
||||
shebang: None,
|
||||
digest: None,
|
||||
}),
|
||||
body: Program::empty(),
|
||||
return_type: None,
|
||||
digest: None,
|
||||
}),
|
||||
@ -3847,18 +3845,7 @@ const cylinder = startSketchOn('-XZ')
|
||||
labeled: true,
|
||||
digest: None,
|
||||
}],
|
||||
body: Node {
|
||||
inner: Program {
|
||||
body: Vec::new(),
|
||||
non_code_meta: Default::default(),
|
||||
shebang: None,
|
||||
digest: None,
|
||||
},
|
||||
start: 0,
|
||||
end: 0,
|
||||
module_id: ModuleId::default(),
|
||||
trivia: Vec::new(),
|
||||
},
|
||||
body: Program::empty(),
|
||||
return_type: None,
|
||||
digest: None,
|
||||
}),
|
||||
@ -3877,18 +3864,7 @@ const cylinder = startSketchOn('-XZ')
|
||||
labeled: true,
|
||||
digest: None,
|
||||
}],
|
||||
body: Node {
|
||||
inner: Program {
|
||||
body: Vec::new(),
|
||||
non_code_meta: Default::default(),
|
||||
shebang: None,
|
||||
digest: None,
|
||||
},
|
||||
start: 0,
|
||||
end: 0,
|
||||
module_id: ModuleId::default(),
|
||||
trivia: Vec::new(),
|
||||
},
|
||||
body: Program::empty(),
|
||||
return_type: None,
|
||||
digest: None,
|
||||
}),
|
||||
@ -3919,18 +3895,7 @@ const cylinder = startSketchOn('-XZ')
|
||||
digest: None,
|
||||
},
|
||||
],
|
||||
body: Node {
|
||||
inner: Program {
|
||||
body: Vec::new(),
|
||||
non_code_meta: Default::default(),
|
||||
shebang: None,
|
||||
digest: None,
|
||||
},
|
||||
start: 0,
|
||||
end: 0,
|
||||
module_id: ModuleId::default(),
|
||||
trivia: Vec::new(),
|
||||
},
|
||||
body: Program::empty(),
|
||||
return_type: None,
|
||||
digest: None,
|
||||
}),
|
||||
|
@ -21,13 +21,13 @@ use crate::{
|
||||
errors::{CompilationError, Severity, Tag},
|
||||
parsing::{
|
||||
ast::types::{
|
||||
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem, BoxNode,
|
||||
CallExpression, CallExpressionKw, CommentStyle, DefaultParamVal, ElseIf, Expr, ExpressionStatement,
|
||||
FnArgPrimitive, FnArgType, FunctionExpression, Identifier, IfExpression, ImportItem, ImportSelector,
|
||||
ImportStatement, ItemVisibility, LabeledArg, Literal, LiteralIdentifier, LiteralValue, MemberExpression,
|
||||
MemberObject, Node, NodeList, NonCodeMeta, NonCodeNode, NonCodeValue, ObjectExpression, ObjectProperty,
|
||||
Parameter, PipeExpression, PipeSubstitution, Program, ReturnStatement, Shebang, TagDeclarator,
|
||||
UnaryExpression, UnaryOperator, VariableDeclaration, VariableDeclarator, VariableKind,
|
||||
Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem,
|
||||
BoxNode, CallExpression, CallExpressionKw, CommentStyle, DefaultParamVal, ElseIf, Expr,
|
||||
ExpressionStatement, FnArgPrimitive, FnArgType, FunctionExpression, Identifier, IfExpression, ImportItem,
|
||||
ImportSelector, ImportStatement, ItemVisibility, LabeledArg, Literal, LiteralIdentifier, LiteralValue,
|
||||
MemberExpression, MemberObject, Node, NodeList, NonCodeMeta, NonCodeNode, NonCodeValue, ObjectExpression,
|
||||
ObjectProperty, Parameter, PipeExpression, PipeSubstitution, Program, ReturnStatement, Shebang,
|
||||
TagDeclarator, UnaryExpression, UnaryOperator, VariableDeclaration, VariableDeclarator, VariableKind,
|
||||
},
|
||||
math::BinaryExpressionToken,
|
||||
token::{Token, TokenSlice, TokenType},
|
||||
@ -284,7 +284,7 @@ fn non_code_node(i: &mut TokenSlice) -> PResult<Node<NonCodeNode>> {
|
||||
alt((non_code_node_leading_whitespace, non_code_node_no_leading_whitespace)).parse_next(i)
|
||||
}
|
||||
|
||||
fn annotation(i: &mut TokenSlice) -> PResult<Node<NonCodeNode>> {
|
||||
fn annotation(i: &mut TokenSlice) -> PResult<Node<Annotation>> {
|
||||
let at = at_sign.parse_next(i)?;
|
||||
let name = opt(binding_name).parse_next(i)?;
|
||||
let mut end = name.as_ref().map(|n| n.end).unwrap_or(at.end);
|
||||
@ -308,7 +308,7 @@ fn annotation(i: &mut TokenSlice) -> PResult<Node<NonCodeNode>> {
|
||||
value,
|
||||
digest: None,
|
||||
},
|
||||
trivia: Vec::new(),
|
||||
outer_attrs: Vec::new(),
|
||||
}),
|
||||
comma_sep,
|
||||
)
|
||||
@ -327,19 +327,16 @@ fn annotation(i: &mut TokenSlice) -> PResult<Node<NonCodeNode>> {
|
||||
));
|
||||
}
|
||||
|
||||
let value = NonCodeValue::Annotation { name, properties };
|
||||
Ok(Node::new(
|
||||
NonCodeNode { value, digest: None },
|
||||
at.start,
|
||||
end,
|
||||
at.module_id,
|
||||
))
|
||||
let value = Annotation {
|
||||
name,
|
||||
properties,
|
||||
digest: None,
|
||||
};
|
||||
Ok(Node::new(value, at.start, end, at.module_id))
|
||||
}
|
||||
|
||||
// Matches remaining three cases of NonCodeValue
|
||||
fn non_code_node_no_leading_whitespace(i: &mut TokenSlice) -> PResult<Node<NonCodeNode>> {
|
||||
alt((
|
||||
annotation,
|
||||
any.verify_map(|token: Token| {
|
||||
if token.is_code_token() {
|
||||
None
|
||||
@ -369,8 +366,7 @@ fn non_code_node_no_leading_whitespace(i: &mut TokenSlice) -> PResult<Node<NonCo
|
||||
))
|
||||
}
|
||||
})
|
||||
.context(expected("Non-code token (comments or whitespace)")),
|
||||
))
|
||||
.context(expected("Non-code token (comments or whitespace)"))
|
||||
.parse_next(i)
|
||||
}
|
||||
|
||||
@ -427,7 +423,7 @@ fn pipe_expression(i: &mut TokenSlice) -> PResult<Node<PipeExpression>> {
|
||||
non_code_meta,
|
||||
digest: None,
|
||||
},
|
||||
trivia: Vec::new(),
|
||||
outer_attrs: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -830,7 +826,7 @@ fn object_property_same_key_and_val(i: &mut TokenSlice) -> PResult<Node<ObjectPr
|
||||
key,
|
||||
digest: None,
|
||||
},
|
||||
trivia: Vec::new(),
|
||||
outer_attrs: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -859,7 +855,7 @@ fn object_property(i: &mut TokenSlice) -> PResult<Node<ObjectProperty>> {
|
||||
value: expr,
|
||||
digest: None,
|
||||
},
|
||||
trivia: Vec::new(),
|
||||
outer_attrs: Vec::new(),
|
||||
};
|
||||
|
||||
if sep.token_type == TokenType::Colon {
|
||||
@ -1131,17 +1127,7 @@ fn function_decl(i: &mut TokenSlice) -> PResult<(Node<FunctionExpression>, bool)
|
||||
let close: Option<(Vec<Vec<Token>>, Token)> = opt((repeat(0.., whitespace), close_brace)).parse_next(i)?;
|
||||
let (body, end) = match close {
|
||||
Some((_, end)) => (
|
||||
Node::new(
|
||||
Program {
|
||||
body: Vec::new(),
|
||||
non_code_meta: NonCodeMeta::default(),
|
||||
shebang: None,
|
||||
digest: None,
|
||||
},
|
||||
brace.end,
|
||||
brace.end,
|
||||
brace.module_id,
|
||||
),
|
||||
Node::new(Program::default(), brace.end, brace.end, brace.module_id),
|
||||
end.end,
|
||||
),
|
||||
None => (function_body(i)?, close_brace(i)?.end),
|
||||
@ -1277,7 +1263,6 @@ fn noncode_just_after_code(i: &mut TokenSlice) -> PResult<Node<NonCodeNode>> {
|
||||
x @ NonCodeValue::InlineComment { .. } => x,
|
||||
x @ NonCodeValue::NewLineBlockComment { .. } => x,
|
||||
x @ NonCodeValue::NewLine => x,
|
||||
x @ NonCodeValue::Annotation { .. } => x,
|
||||
};
|
||||
Node::new(
|
||||
NonCodeNode { value, ..nc.inner },
|
||||
@ -1298,7 +1283,6 @@ fn noncode_just_after_code(i: &mut TokenSlice) -> PResult<Node<NonCodeNode>> {
|
||||
x @ NonCodeValue::InlineComment { .. } => x,
|
||||
x @ NonCodeValue::NewLineBlockComment { .. } => x,
|
||||
x @ NonCodeValue::NewLine => x,
|
||||
x @ NonCodeValue::Annotation { .. } => x,
|
||||
};
|
||||
Node::new(NonCodeNode { value, ..nc.inner }, nc.start, nc.end, nc.module_id)
|
||||
}
|
||||
@ -1314,6 +1298,7 @@ fn noncode_just_after_code(i: &mut TokenSlice) -> PResult<Node<NonCodeNode>> {
|
||||
#[derive(Debug)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
enum WithinFunction {
|
||||
Annotation(Node<Annotation>),
|
||||
BodyItem((BodyItem, Option<Node<NonCodeNode>>)),
|
||||
NonCode(Node<NonCodeNode>),
|
||||
}
|
||||
@ -1338,9 +1323,12 @@ fn body_items_within_function(i: &mut TokenSlice) -> PResult<WithinFunction> {
|
||||
(import_stmt.map(BodyItem::ImportStatement), opt(noncode_just_after_code)).map(WithinFunction::BodyItem),
|
||||
Token { ref value, .. } if value == "return" =>
|
||||
(return_stmt.map(BodyItem::ReturnStatement), opt(noncode_just_after_code)).map(WithinFunction::BodyItem),
|
||||
token if !token.is_code_token() || token.token_type == TokenType::At => {
|
||||
token if !token.is_code_token() => {
|
||||
non_code_node.map(WithinFunction::NonCode)
|
||||
},
|
||||
token if token.token_type == TokenType::At => {
|
||||
annotation.map(WithinFunction::Annotation)
|
||||
},
|
||||
_ =>
|
||||
alt((
|
||||
(
|
||||
@ -1447,16 +1435,32 @@ fn function_body(i: &mut TokenSlice) -> PResult<Node<Program>> {
|
||||
}
|
||||
|
||||
let mut body = Vec::new();
|
||||
let mut inner_attrs = Vec::new();
|
||||
let mut pending_attrs = Vec::new();
|
||||
let mut non_code_meta = NonCodeMeta::default();
|
||||
let mut end = 0;
|
||||
let mut start = leading_whitespace_start;
|
||||
for thing_in_body in things_within_body {
|
||||
match thing_in_body {
|
||||
WithinFunction::BodyItem((b, maybe_noncode)) => {
|
||||
WithinFunction::Annotation(attr) => {
|
||||
if start.is_none() {
|
||||
start = Some((attr.start, attr.module_id))
|
||||
}
|
||||
if attr.is_inner() {
|
||||
inner_attrs.push(attr);
|
||||
} else {
|
||||
pending_attrs.push(attr);
|
||||
}
|
||||
}
|
||||
WithinFunction::BodyItem((mut b, maybe_noncode)) => {
|
||||
if start.is_none() {
|
||||
start = Some((b.start(), b.module_id()));
|
||||
}
|
||||
end = b.end();
|
||||
if !pending_attrs.is_empty() {
|
||||
b.set_attrs(pending_attrs);
|
||||
pending_attrs = Vec::new();
|
||||
}
|
||||
body.push(b);
|
||||
if let Some(nc) = maybe_noncode {
|
||||
end = nc.end;
|
||||
@ -1476,9 +1480,26 @@ fn function_body(i: &mut TokenSlice) -> PResult<Node<Program>> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let start = start.expect(
|
||||
"the `things_within_body` vec should have looped at least once, and each loop overwrites `start` if it is None",
|
||||
);
|
||||
|
||||
if !pending_attrs.is_empty() {
|
||||
for a in pending_attrs {
|
||||
ParseContext::err(CompilationError::err(
|
||||
a.as_source_range(),
|
||||
"Attribute is not attached to any item",
|
||||
));
|
||||
}
|
||||
return Err(ErrMode::Cut(
|
||||
CompilationError::fatal(
|
||||
SourceRange::new(start.0, end, start.1),
|
||||
"Block contains un-attached attributes",
|
||||
)
|
||||
.into(),
|
||||
));
|
||||
}
|
||||
// Safe to unwrap `body.first()` because `body` is `separated1` therefore guaranteed
|
||||
// to have len >= 1.
|
||||
let end_ws = opt(whitespace)
|
||||
@ -1492,6 +1513,7 @@ fn function_body(i: &mut TokenSlice) -> PResult<Node<Program>> {
|
||||
Program {
|
||||
body,
|
||||
non_code_meta,
|
||||
inner_attrs,
|
||||
shebang: None,
|
||||
digest: None,
|
||||
},
|
||||
@ -1790,7 +1812,7 @@ fn return_stmt(i: &mut TokenSlice) -> PResult<Node<ReturnStatement>> {
|
||||
end: argument.end(),
|
||||
module_id: ret.module_id,
|
||||
inner: ReturnStatement { argument, digest: None },
|
||||
trivia: Vec::new(),
|
||||
outer_attrs: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -2017,13 +2039,13 @@ fn declaration(i: &mut TokenSlice) -> PResult<BoxNode<VariableDeclaration>> {
|
||||
init: val,
|
||||
digest: None,
|
||||
},
|
||||
trivia: Vec::new(),
|
||||
outer_attrs: Vec::new(),
|
||||
},
|
||||
visibility,
|
||||
kind,
|
||||
digest: None,
|
||||
},
|
||||
trivia: Vec::new(),
|
||||
outer_attrs: Vec::new(),
|
||||
}))
|
||||
}
|
||||
|
||||
@ -2229,7 +2251,7 @@ fn unary_expression(i: &mut TokenSlice) -> PResult<Node<UnaryExpression>> {
|
||||
argument,
|
||||
digest: None,
|
||||
},
|
||||
trivia: Vec::new(),
|
||||
outer_attrs: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -2310,7 +2332,7 @@ fn expression_stmt(i: &mut TokenSlice) -> PResult<Node<ExpressionStatement>> {
|
||||
expression: val,
|
||||
digest: None,
|
||||
},
|
||||
trivia: Vec::new(),
|
||||
outer_attrs: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -2750,7 +2772,7 @@ fn fn_call(i: &mut TokenSlice) -> PResult<Node<CallExpression>> {
|
||||
arguments: args,
|
||||
digest: None,
|
||||
},
|
||||
trivia: Vec::new(),
|
||||
outer_attrs: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -2780,7 +2802,7 @@ fn fn_call_kw(i: &mut TokenSlice) -> PResult<Node<CallExpressionKw>> {
|
||||
arguments: args,
|
||||
digest: None,
|
||||
},
|
||||
trivia: Vec::new(),
|
||||
outer_attrs: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -3023,6 +3045,7 @@ mySk1 = startSketchAt([0, 0])"#;
|
||||
)],
|
||||
digest: None,
|
||||
},
|
||||
inner_attrs: Vec::new(),
|
||||
shebang: None,
|
||||
digest: None,
|
||||
},
|
||||
@ -3700,6 +3723,7 @@ mySk1 = startSketchAt([0, 0])"#;
|
||||
))],
|
||||
shebang: None,
|
||||
non_code_meta: NonCodeMeta::default(),
|
||||
inner_attrs: Vec::new(),
|
||||
digest: None,
|
||||
},
|
||||
0,
|
||||
|
@ -2,11 +2,12 @@ use std::fmt::Write;
|
||||
|
||||
use crate::parsing::{
|
||||
ast::types::{
|
||||
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem, CallExpression,
|
||||
CallExpressionKw, CommentStyle, DefaultParamVal, Expr, FnArgType, FormatOptions, FunctionExpression,
|
||||
IfExpression, ImportSelector, ImportStatement, ItemVisibility, LabeledArg, Literal, LiteralIdentifier,
|
||||
LiteralValue, MemberExpression, MemberObject, Node, NonCodeNode, NonCodeValue, ObjectExpression, Parameter,
|
||||
PipeExpression, Program, TagDeclarator, UnaryExpression, VariableDeclaration, VariableKind,
|
||||
Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem,
|
||||
CallExpression, CallExpressionKw, CommentStyle, DefaultParamVal, Expr, FnArgType, FormatOptions,
|
||||
FunctionExpression, IfExpression, ImportSelector, ImportStatement, ItemVisibility, LabeledArg, Literal,
|
||||
LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Node, NonCodeNode, NonCodeValue,
|
||||
ObjectExpression, Parameter, PipeExpression, Program, TagDeclarator, UnaryExpression, VariableDeclaration,
|
||||
VariableKind,
|
||||
},
|
||||
token::NumericSuffix,
|
||||
PIPE_OPERATOR,
|
||||
@ -22,6 +23,9 @@ impl Program {
|
||||
.map(|sh| format!("{}\n\n", sh.inner.content))
|
||||
.unwrap_or_default();
|
||||
|
||||
for attr in &self.inner_attrs {
|
||||
result.push_str(&attr.recast(options, indentation_level));
|
||||
}
|
||||
for start in &self.non_code_meta.start_nodes {
|
||||
result.push_str(&start.recast(options, indentation_level));
|
||||
}
|
||||
@ -30,7 +34,12 @@ impl Program {
|
||||
let result = self
|
||||
.body
|
||||
.iter()
|
||||
.map(|body_item| match body_item.clone() {
|
||||
.map(|body_item| {
|
||||
let mut result = String::new();
|
||||
for attr in body_item.get_attrs() {
|
||||
result.push_str(&attr.recast(options, indentation_level));
|
||||
}
|
||||
result.push_str(&match body_item.clone() {
|
||||
BodyItem::ImportStatement(stmt) => stmt.recast(options, indentation_level),
|
||||
BodyItem::ExpressionStatement(expression_statement) => {
|
||||
expression_statement
|
||||
@ -50,10 +59,13 @@ impl Program {
|
||||
.trim_start()
|
||||
)
|
||||
}
|
||||
});
|
||||
result
|
||||
})
|
||||
.enumerate()
|
||||
.fold(result, |mut output, (index, recast_str)| {
|
||||
let start_string = if index == 0 && self.non_code_meta.start_nodes.is_empty() {
|
||||
let start_string =
|
||||
if index == 0 && self.non_code_meta.start_nodes.is_empty() && self.inner_attrs.is_empty() {
|
||||
// We need to indent.
|
||||
indentation.to_string()
|
||||
} else {
|
||||
@ -113,9 +125,7 @@ impl NonCodeValue {
|
||||
fn should_cause_array_newline(&self) -> bool {
|
||||
match self {
|
||||
Self::InlineComment { .. } => false,
|
||||
Self::BlockComment { .. } | Self::NewLineBlockComment { .. } | Self::NewLine | Self::Annotation { .. } => {
|
||||
true
|
||||
}
|
||||
Self::BlockComment { .. } | Self::NewLineBlockComment { .. } | Self::NewLine => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -156,12 +166,17 @@ impl Node<NonCodeNode> {
|
||||
}
|
||||
}
|
||||
NonCodeValue::NewLine => "\n\n".to_string(),
|
||||
NonCodeValue::Annotation { name, properties } => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Node<Annotation> {
|
||||
fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
|
||||
let mut result = "@".to_owned();
|
||||
if let Some(name) = name {
|
||||
if let Some(name) = &self.name {
|
||||
result.push_str(&name.name);
|
||||
}
|
||||
if let Some(properties) = properties {
|
||||
if let Some(properties) = &self.properties {
|
||||
result.push('(');
|
||||
result.push_str(
|
||||
&properties
|
||||
@ -184,8 +199,6 @@ impl Node<NonCodeNode> {
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ImportStatement {
|
||||
|
@ -103,26 +103,9 @@ description: Result of parsing import_cycle1.kcl
|
||||
}
|
||||
],
|
||||
"end": 110,
|
||||
"nonCodeMeta": {
|
||||
"nonCodeNodes": {
|
||||
"0": [
|
||||
{
|
||||
"end": 71,
|
||||
"start": 69,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "newLine"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"startNodes": [
|
||||
"innerAttrs": [
|
||||
{
|
||||
"end": 33,
|
||||
"start": 0,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "annotation",
|
||||
"name": {
|
||||
"end": 9,
|
||||
"name": "settings",
|
||||
@ -148,11 +131,26 @@ description: Result of parsing import_cycle1.kcl
|
||||
"type": "Identifier"
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"start": 0,
|
||||
"type": "Annotation"
|
||||
}
|
||||
],
|
||||
"nonCodeMeta": {
|
||||
"nonCodeNodes": {
|
||||
"0": [
|
||||
{
|
||||
"end": 71,
|
||||
"start": 69,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "newLine"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"startNodes": []
|
||||
},
|
||||
"start": 0
|
||||
}
|
||||
}
|
||||
|
@ -103,26 +103,9 @@ description: Result of parsing import_function_not_sketch.kcl
|
||||
}
|
||||
],
|
||||
"end": 109,
|
||||
"nonCodeMeta": {
|
||||
"nonCodeNodes": {
|
||||
"0": [
|
||||
{
|
||||
"end": 70,
|
||||
"start": 68,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "newLine"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"startNodes": [
|
||||
"innerAttrs": [
|
||||
{
|
||||
"end": 33,
|
||||
"start": 0,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "annotation",
|
||||
"name": {
|
||||
"end": 9,
|
||||
"name": "settings",
|
||||
@ -148,11 +131,26 @@ description: Result of parsing import_function_not_sketch.kcl
|
||||
"type": "Identifier"
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"start": 0,
|
||||
"type": "Annotation"
|
||||
}
|
||||
],
|
||||
"nonCodeMeta": {
|
||||
"nonCodeNodes": {
|
||||
"0": [
|
||||
{
|
||||
"end": 70,
|
||||
"start": 68,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "newLine"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"startNodes": []
|
||||
},
|
||||
"start": 0
|
||||
}
|
||||
}
|
||||
|
@ -115,26 +115,9 @@ description: Result of parsing import_whole.kcl
|
||||
}
|
||||
],
|
||||
"end": 124,
|
||||
"nonCodeMeta": {
|
||||
"nonCodeNodes": {
|
||||
"0": [
|
||||
{
|
||||
"end": 68,
|
||||
"start": 66,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "newLine"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"startNodes": [
|
||||
"innerAttrs": [
|
||||
{
|
||||
"end": 33,
|
||||
"start": 0,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "annotation",
|
||||
"name": {
|
||||
"end": 9,
|
||||
"name": "settings",
|
||||
@ -160,11 +143,26 @@ description: Result of parsing import_whole.kcl
|
||||
"type": "Identifier"
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"start": 0,
|
||||
"type": "Annotation"
|
||||
}
|
||||
],
|
||||
"nonCodeMeta": {
|
||||
"nonCodeNodes": {
|
||||
"0": [
|
||||
{
|
||||
"end": 68,
|
||||
"start": 66,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "newLine"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"startNodes": []
|
||||
},
|
||||
"start": 0
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user