AST: Factor shebangs out of non-code metadata and into Program (#4557)

* AST: Factor shebangs out of non-code metadata and into Progam

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Empty commit to try to unstick CI

---------

Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
Nick Cameron
2024-11-26 16:39:57 +13:00
committed by GitHub
parent 30bc85add8
commit 5424252dac
13 changed files with 386 additions and 254 deletions

View File

@ -96310,6 +96310,14 @@
"nonCodeMeta": { "nonCodeMeta": {
"$ref": "#/components/schemas/NonCodeMeta" "$ref": "#/components/schemas/NonCodeMeta"
}, },
"shebang": {
"allOf": [
{
"$ref": "#/components/schemas/Shebang"
}
],
"nullable": true
},
"digest": { "digest": {
"type": "array", "type": "array",
"items": { "items": {
@ -97944,25 +97952,6 @@
}, },
"NonCodeValue": { "NonCodeValue": {
"oneOf": [ "oneOf": [
{
"description": "A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```",
"type": "object",
"required": [
"type",
"value"
],
"properties": {
"type": {
"type": "string",
"enum": [
"shebang"
]
},
"value": {
"type": "string"
}
}
},
{ {
"description": "An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.", "description": "An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.",
"type": "object", "type": "object",
@ -98173,6 +98162,28 @@
} }
] ]
}, },
"Shebang": {
"description": "A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```",
"type": "object",
"required": [
"content"
],
"properties": {
"content": {
"type": "string"
},
"start": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"end": {
"type": "integer",
"format": "uint",
"minimum": 0.0
}
}
},
"ProgramMemory": { "ProgramMemory": {
"type": "object", "type": "object",
"required": [ "required": [
@ -100282,6 +100293,14 @@
"nonCodeMeta": { "nonCodeMeta": {
"$ref": "#/components/schemas/NonCodeMeta" "$ref": "#/components/schemas/NonCodeMeta"
}, },
"shebang": {
"allOf": [
{
"$ref": "#/components/schemas/Shebang"
}
],
"nullable": true
},
"digest": { "digest": {
"type": "array", "type": "array",
"items": { "items": {
@ -101916,25 +101935,6 @@
}, },
"NonCodeValue": { "NonCodeValue": {
"oneOf": [ "oneOf": [
{
"description": "A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```",
"type": "object",
"required": [
"type",
"value"
],
"properties": {
"type": {
"type": "string",
"enum": [
"shebang"
]
},
"value": {
"type": "string"
}
}
},
{ {
"description": "An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.", "description": "An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.",
"type": "object", "type": "object",
@ -102145,6 +102145,28 @@
} }
] ]
}, },
"Shebang": {
"description": "A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```",
"type": "object",
"required": [
"content"
],
"properties": {
"content": {
"type": "string"
},
"start": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"end": {
"type": "integer",
"format": "uint",
"minimum": 0.0
}
}
},
"ProgramMemory": { "ProgramMemory": {
"type": "object", "type": "object",
"required": [ "required": [
@ -104258,6 +104280,14 @@
"nonCodeMeta": { "nonCodeMeta": {
"$ref": "#/components/schemas/NonCodeMeta" "$ref": "#/components/schemas/NonCodeMeta"
}, },
"shebang": {
"allOf": [
{
"$ref": "#/components/schemas/Shebang"
}
],
"nullable": true
},
"digest": { "digest": {
"type": "array", "type": "array",
"items": { "items": {
@ -105892,25 +105922,6 @@
}, },
"NonCodeValue": { "NonCodeValue": {
"oneOf": [ "oneOf": [
{
"description": "A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```",
"type": "object",
"required": [
"type",
"value"
],
"properties": {
"type": {
"type": "string",
"enum": [
"shebang"
]
},
"value": {
"type": "string"
}
}
},
{ {
"description": "An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.", "description": "An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.",
"type": "object", "type": "object",
@ -106121,6 +106132,28 @@
} }
] ]
}, },
"Shebang": {
"description": "A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```",
"type": "object",
"required": [
"content"
],
"properties": {
"content": {
"type": "string"
},
"start": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"end": {
"type": "integer",
"format": "uint",
"minimum": 0.0
}
}
},
"ProgramMemory": { "ProgramMemory": {
"type": "object", "type": "object",
"required": [ "required": [
@ -137489,6 +137522,14 @@
"nonCodeMeta": { "nonCodeMeta": {
"$ref": "#/components/schemas/NonCodeMeta" "$ref": "#/components/schemas/NonCodeMeta"
}, },
"shebang": {
"allOf": [
{
"$ref": "#/components/schemas/Shebang"
}
],
"nullable": true
},
"digest": { "digest": {
"type": "array", "type": "array",
"items": { "items": {
@ -139123,25 +139164,6 @@
}, },
"NonCodeValue": { "NonCodeValue": {
"oneOf": [ "oneOf": [
{
"description": "A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```",
"type": "object",
"required": [
"type",
"value"
],
"properties": {
"type": {
"type": "string",
"enum": [
"shebang"
]
},
"value": {
"type": "string"
}
}
},
{ {
"description": "An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.", "description": "An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.",
"type": "object", "type": "object",
@ -139352,6 +139374,28 @@
} }
] ]
}, },
"Shebang": {
"description": "A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```",
"type": "object",
"required": [
"content"
],
"properties": {
"content": {
"type": "string"
},
"start": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"end": {
"type": "integer",
"format": "uint",
"minimum": 0.0
}
}
},
"ProgramMemory": { "ProgramMemory": {
"type": "object", "type": "object",
"required": [ "required": [
@ -142077,6 +142121,14 @@
"nonCodeMeta": { "nonCodeMeta": {
"$ref": "#/components/schemas/NonCodeMeta" "$ref": "#/components/schemas/NonCodeMeta"
}, },
"shebang": {
"allOf": [
{
"$ref": "#/components/schemas/Shebang"
}
],
"nullable": true
},
"digest": { "digest": {
"type": "array", "type": "array",
"items": { "items": {
@ -143711,25 +143763,6 @@
}, },
"NonCodeValue": { "NonCodeValue": {
"oneOf": [ "oneOf": [
{
"description": "A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```",
"type": "object",
"required": [
"type",
"value"
],
"properties": {
"type": {
"type": "string",
"enum": [
"shebang"
]
},
"value": {
"type": "string"
}
}
},
{ {
"description": "An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.", "description": "An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.",
"type": "object", "type": "object",
@ -143940,6 +143973,28 @@
} }
] ]
}, },
"Shebang": {
"description": "A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```",
"type": "object",
"required": [
"content"
],
"properties": {
"content": {
"type": "string"
},
"start": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"end": {
"type": "integer",
"format": "uint",
"minimum": 0.0
}
}
},
"ProgramMemory": { "ProgramMemory": {
"type": "object", "type": "object",
"required": [ "required": [
@ -146666,6 +146721,14 @@
"nonCodeMeta": { "nonCodeMeta": {
"$ref": "#/components/schemas/NonCodeMeta" "$ref": "#/components/schemas/NonCodeMeta"
}, },
"shebang": {
"allOf": [
{
"$ref": "#/components/schemas/Shebang"
}
],
"nullable": true
},
"digest": { "digest": {
"type": "array", "type": "array",
"items": { "items": {
@ -148300,25 +148363,6 @@
}, },
"NonCodeValue": { "NonCodeValue": {
"oneOf": [ "oneOf": [
{
"description": "A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```",
"type": "object",
"required": [
"type",
"value"
],
"properties": {
"type": {
"type": "string",
"enum": [
"shebang"
]
},
"value": {
"type": "string"
}
}
},
{ {
"description": "An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.", "description": "An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.",
"type": "object", "type": "object",
@ -148529,6 +148573,28 @@
} }
] ]
}, },
"Shebang": {
"description": "A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```",
"type": "object",
"required": [
"content"
],
"properties": {
"content": {
"type": "string"
},
"start": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"end": {
"type": "integer",
"format": "uint",
"minimum": 0.0
}
}
},
"ProgramMemory": { "ProgramMemory": {
"type": "object", "type": "object",
"required": [ "required": [
@ -150653,6 +150719,14 @@
"nonCodeMeta": { "nonCodeMeta": {
"$ref": "#/components/schemas/NonCodeMeta" "$ref": "#/components/schemas/NonCodeMeta"
}, },
"shebang": {
"allOf": [
{
"$ref": "#/components/schemas/Shebang"
}
],
"nullable": true
},
"digest": { "digest": {
"type": "array", "type": "array",
"items": { "items": {
@ -152287,25 +152361,6 @@
}, },
"NonCodeValue": { "NonCodeValue": {
"oneOf": [ "oneOf": [
{
"description": "A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```",
"type": "object",
"required": [
"type",
"value"
],
"properties": {
"type": {
"type": "string",
"enum": [
"shebang"
]
},
"value": {
"type": "string"
}
}
},
{ {
"description": "An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.", "description": "An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.",
"type": "object", "type": "object",
@ -152516,6 +152571,28 @@
} }
] ]
}, },
"Shebang": {
"description": "A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```",
"type": "object",
"required": [
"content"
],
"properties": {
"content": {
"type": "string"
},
"start": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"end": {
"type": "integer",
"format": "uint",
"minimum": 0.0
}
}
},
"ProgramMemory": { "ProgramMemory": {
"type": "object", "type": "object",
"required": [ "required": [
@ -155241,6 +155318,14 @@
"nonCodeMeta": { "nonCodeMeta": {
"$ref": "#/components/schemas/NonCodeMeta" "$ref": "#/components/schemas/NonCodeMeta"
}, },
"shebang": {
"allOf": [
{
"$ref": "#/components/schemas/Shebang"
}
],
"nullable": true
},
"digest": { "digest": {
"type": "array", "type": "array",
"items": { "items": {
@ -156875,25 +156960,6 @@
}, },
"NonCodeValue": { "NonCodeValue": {
"oneOf": [ "oneOf": [
{
"description": "A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```",
"type": "object",
"required": [
"type",
"value"
],
"properties": {
"type": {
"type": "string",
"enum": [
"shebang"
]
},
"value": {
"type": "string"
}
}
},
{ {
"description": "An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.", "description": "An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.",
"type": "object", "type": "object",
@ -157104,6 +157170,28 @@
} }
] ]
}, },
"Shebang": {
"description": "A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```",
"type": "object",
"required": [
"content"
],
"properties": {
"content": {
"type": "string"
},
"start": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"end": {
"type": "integer",
"format": "uint",
"minimum": 0.0
}
}
},
"ProgramMemory": { "ProgramMemory": {
"type": "object", "type": "object",
"required": [ "required": [
@ -159213,6 +159301,14 @@
"nonCodeMeta": { "nonCodeMeta": {
"$ref": "#/components/schemas/NonCodeMeta" "$ref": "#/components/schemas/NonCodeMeta"
}, },
"shebang": {
"allOf": [
{
"$ref": "#/components/schemas/Shebang"
}
],
"nullable": true
},
"digest": { "digest": {
"type": "array", "type": "array",
"items": { "items": {
@ -160847,25 +160943,6 @@
}, },
"NonCodeValue": { "NonCodeValue": {
"oneOf": [ "oneOf": [
{
"description": "A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```",
"type": "object",
"required": [
"type",
"value"
],
"properties": {
"type": {
"type": "string",
"enum": [
"shebang"
]
},
"value": {
"type": "string"
}
}
},
{ {
"description": "An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.", "description": "An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.",
"type": "object", "type": "object",
@ -161076,6 +161153,28 @@
} }
] ]
}, },
"Shebang": {
"description": "A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```",
"type": "object",
"required": [
"content"
],
"properties": {
"content": {
"type": "string"
},
"start": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"end": {
"type": "integer",
"format": "uint",
"minimum": 0.0
}
}
},
"ProgramMemory": { "ProgramMemory": {
"type": "object", "type": "object",
"required": [ "required": [
@ -163802,6 +163901,14 @@
"nonCodeMeta": { "nonCodeMeta": {
"$ref": "#/components/schemas/NonCodeMeta" "$ref": "#/components/schemas/NonCodeMeta"
}, },
"shebang": {
"allOf": [
{
"$ref": "#/components/schemas/Shebang"
}
],
"nullable": true
},
"digest": { "digest": {
"type": "array", "type": "array",
"items": { "items": {
@ -165436,25 +165543,6 @@
}, },
"NonCodeValue": { "NonCodeValue": {
"oneOf": [ "oneOf": [
{
"description": "A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```",
"type": "object",
"required": [
"type",
"value"
],
"properties": {
"type": {
"type": "string",
"enum": [
"shebang"
]
},
"value": {
"type": "string"
}
}
},
{ {
"description": "An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.", "description": "An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.",
"type": "object", "type": "object",
@ -165665,6 +165753,28 @@
} }
] ]
}, },
"Shebang": {
"description": "A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```",
"type": "object",
"required": [
"content"
],
"properties": {
"content": {
"type": "string"
},
"start": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"end": {
"type": "integer",
"format": "uint",
"minimum": 0.0
}
}
},
"ProgramMemory": { "ProgramMemory": {
"type": "object", "type": "object",
"required": [ "required": [

View File

@ -11,23 +11,6 @@ layout: manual
**This schema accepts exactly one of the following:** **This schema accepts exactly one of the following:**
A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```
**Type:** `object`
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `type` |enum: `shebang`| | No |
| `value` |`string`| | No |
----
An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`. An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.
**Type:** `object` **Type:** `object`

View File

@ -18,6 +18,7 @@ A KCL program top level, or function body.
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `body` |`[` [`BodyItem`](/docs/kcl/types/BodyItem) `]`| | No | | `body` |`[` [`BodyItem`](/docs/kcl/types/BodyItem) `]`| | No |
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| A KCL program top level, or function body. | No | | `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| A KCL program top level, or function body. | No |
| `shebang` |[`Shebang`](/docs/kcl/types/Shebang)| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No | | `start` |`integer`| | No |
| `end` |`integer`| | No | | `end` |`integer`| | No |

23
docs/kcl/types/Shebang.md Normal file
View File

@ -0,0 +1,23 @@
---
title: "Shebang"
excerpt: "A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```"
layout: manual
---
A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```
**Type:** `object`
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `content` |`string`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -37,6 +37,7 @@ interface ExecuteArgs {
export class KclManager { export class KclManager {
private _ast: Node<Program> = { private _ast: Node<Program> = {
body: [], body: [],
shebang: null,
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
@ -204,6 +205,7 @@ export class KclManager {
clearAst() { clearAst() {
this._ast = { this._ast = {
body: [], body: [],
shebang: null,
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,

View File

@ -123,6 +123,7 @@ describe('Testing addSketchTo', () => {
const result = addSketchTo( const result = addSketchTo(
{ {
body: [], body: [],
shebang: null,
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,

View File

@ -1823,6 +1823,7 @@ export const updateStartProfileAtArgs: SketchLineHelper['updateArgs'] = ({
modifiedAst: { modifiedAst: {
start: 0, start: 0,
end: 0, end: 0,
shebang: null,
moduleId: 0, moduleId: 0,
body: [], body: [],

View File

@ -65,6 +65,10 @@ impl<T> Node<T> {
source_range: SourceRange([self.start, self.end, self.module_id.0 as usize]), source_range: SourceRange([self.start, self.end, self.module_id.0 as usize]),
} }
} }
pub fn contains(&self, pos: usize) -> bool {
self.start <= pos && pos <= self.end
}
} }
impl<T: JsonSchema> schemars::JsonSchema for Node<T> { impl<T: JsonSchema> schemars::JsonSchema for Node<T> {
@ -177,6 +181,8 @@ pub struct Program {
pub body: Vec<BodyItem>, pub body: Vec<BodyItem>,
#[serde(default, skip_serializing_if = "NonCodeMeta::is_empty")] #[serde(default, skip_serializing_if = "NonCodeMeta::is_empty")]
pub non_code_meta: NonCodeMeta, pub non_code_meta: NonCodeMeta,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub shebang: Option<Node<Shebang>>,
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)] #[ts(optional)]
@ -265,12 +271,9 @@ impl Program {
} }
pub fn get_hover_value_for_position(&self, pos: usize, code: &str) -> Option<Hover> { pub fn get_hover_value_for_position(&self, pos: usize, code: &str) -> Option<Hover> {
// Check if we are in the non code meta. // Check if we are in shebang.
if let Some(meta) = self.get_non_code_meta_for_position(pos) { if let Some(node) = &self.shebang {
for node in &meta.start_nodes {
if node.contains(pos) { if node.contains(pos) {
// We only care about the shebang.
if let NonCodeValue::Shebang { value: _ } = &node.value {
let source_range: SourceRange = node.into(); let source_range: SourceRange = node.into();
return Some(Hover::Comment { return Some(Hover::Comment {
value: r#"The `#!` at the start of a script, known as a shebang, specifies the path to the interpreter that should execute the script. This line is not necessary for your `kcl` to run in the modeling-app. You can safely delete it. If you wish to learn more about what you _can_ do with a shebang, read this doc: [zoo.dev/docs/faq/shebang](https://zoo.dev/docs/faq/shebang)."#.to_string(), value: r#"The `#!` at the start of a script, known as a shebang, specifies the path to the interpreter that should execute the script. This line is not necessary for your `kcl` to run in the modeling-app. You can safely delete it. If you wish to learn more about what you _can_ do with a shebang, read this doc: [zoo.dev/docs/faq/shebang](https://zoo.dev/docs/faq/shebang)."#.to_string(),
@ -278,8 +281,6 @@ impl Program {
}); });
} }
} }
}
}
let value = self.get_expr_for_position(pos)?; let value = self.get_expr_for_position(pos)?;
@ -532,6 +533,26 @@ impl Program {
} }
} }
/// A shebang.
/// This is a special type of comment that is at the top of the file.
/// It looks like this:
/// ```python,no_run
/// #!/usr/bin/env python
/// ```
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, ts_rs::TS, JsonSchema, Bake)]
#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
#[databake(path = kcl_lib::ast::types)]
#[ts(export)]
pub struct Shebang {
pub content: String,
}
impl Shebang {
pub fn new(content: String) -> Self {
Shebang { content }
}
}
/// Identifier of a source file. Uses a u32 to keep the size small. /// Identifier of a source file. Uses a u32 to keep the size small.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize, ts_rs::TS, JsonSchema, Bake)] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize, ts_rs::TS, JsonSchema, Bake)]
#[cfg_attr(feature = "pyo3", pyo3::pyclass)] #[cfg_attr(feature = "pyo3", pyo3::pyclass)]
@ -952,13 +973,8 @@ pub struct NonCodeNode {
} }
impl Node<NonCodeNode> { impl Node<NonCodeNode> {
pub fn contains(&self, pos: usize) -> bool {
self.start <= pos && pos <= self.end
}
pub fn format(&self, indentation: &str) -> String { pub fn format(&self, indentation: &str) -> String {
match &self.value { match &self.value {
NonCodeValue::Shebang { value } => format!("{}\n\n", value),
NonCodeValue::InlineComment { NonCodeValue::InlineComment {
value, value,
style: CommentStyle::Line, style: CommentStyle::Line,
@ -998,7 +1014,6 @@ impl Node<NonCodeNode> {
impl NonCodeNode { impl NonCodeNode {
pub fn value(&self) -> String { pub fn value(&self) -> String {
match &self.value { match &self.value {
NonCodeValue::Shebang { value } => value.clone(),
NonCodeValue::InlineComment { value, style: _ } => value.clone(), NonCodeValue::InlineComment { value, style: _ } => value.clone(),
NonCodeValue::BlockComment { value, style: _ } => value.clone(), NonCodeValue::BlockComment { value, style: _ } => value.clone(),
NonCodeValue::NewLineBlockComment { value, style: _ } => value.clone(), NonCodeValue::NewLineBlockComment { value, style: _ } => value.clone(),
@ -1032,15 +1047,6 @@ impl CommentStyle {
#[ts(export)] #[ts(export)]
#[serde(tag = "type", rename_all = "camelCase")] #[serde(tag = "type", rename_all = "camelCase")]
pub enum NonCodeValue { pub enum NonCodeValue {
/// A shebang.
/// This is a special type of comment that is at the top of the file.
/// It looks like this:
/// ```python,no_run
/// #!/usr/bin/env python
/// ```
Shebang {
value: String,
},
/// An inline comment. /// An inline comment.
/// Here are examples: /// Here are examples:
/// `1 + 1 // This is an inline comment`. /// `1 + 1 // This is an inline comment`.
@ -3342,6 +3348,7 @@ const cylinder = startSketchOn('-XZ')
body: Node::no_src(Program { body: Node::no_src(Program {
body: Vec::new(), body: Vec::new(),
non_code_meta: Default::default(), non_code_meta: Default::default(),
shebang: None,
digest: None, digest: None,
}), }),
return_type: None, return_type: None,
@ -3365,6 +3372,7 @@ const cylinder = startSketchOn('-XZ')
inner: Program { inner: Program {
body: Vec::new(), body: Vec::new(),
non_code_meta: Default::default(), non_code_meta: Default::default(),
shebang: None,
digest: None, digest: None,
}, },
start: 0, start: 0,
@ -3392,6 +3400,7 @@ const cylinder = startSketchOn('-XZ')
inner: Program { inner: Program {
body: Vec::new(), body: Vec::new(),
non_code_meta: Default::default(), non_code_meta: Default::default(),
shebang: None,
digest: None, digest: None,
}, },
start: 0, start: 0,
@ -3430,6 +3439,7 @@ const cylinder = startSketchOn('-XZ')
inner: Program { inner: Program {
body: Vec::new(), body: Vec::new(),
non_code_meta: Default::default(), non_code_meta: Default::default(),
shebang: None,
digest: None, digest: None,
}, },
start: 0, start: 0,

View File

@ -66,6 +66,9 @@ impl Program {
for body_item in slf.body.iter_mut() { for body_item in slf.body.iter_mut() {
hasher.update(body_item.compute_digest()); hasher.update(body_item.compute_digest());
} }
if let Some(shebang) = &slf.shebang {
hasher.update(&shebang.inner.content);
}
hasher.update(slf.non_code_meta.compute_digest()); hasher.update(slf.non_code_meta.compute_digest());
}); });
} }
@ -207,9 +210,6 @@ impl ReturnStatement {
impl NonCodeNode { impl NonCodeNode {
compute_digest!(|slf, hasher| { compute_digest!(|slf, hasher| {
match &slf.value { match &slf.value {
NonCodeValue::Shebang { value } => {
hasher.update(value);
}
NonCodeValue::InlineComment { value, style } => { NonCodeValue::InlineComment { value, style } => {
hasher.update(value); hasher.update(value);
hasher.update(style.digestable_id()); hasher.update(style.digestable_id());

View File

@ -3191,6 +3191,7 @@ let w = f() + f()
inner: crate::ast::types::Program { inner: crate::ast::types::Program {
body: Vec::new(), body: Vec::new(),
non_code_meta: Default::default(), non_code_meta: Default::default(),
shebang: None,
digest: None, digest: None,
}, },
start: 0, start: 0,

View File

@ -15,7 +15,7 @@ use crate::{
CallExpression, CommentStyle, ElseIf, Expr, ExpressionStatement, FnArgPrimitive, FnArgType, FunctionExpression, CallExpression, CommentStyle, ElseIf, Expr, ExpressionStatement, FnArgPrimitive, FnArgType, FunctionExpression,
Identifier, IfExpression, ImportItem, ImportStatement, ItemVisibility, Literal, LiteralIdentifier, Identifier, IfExpression, ImportItem, ImportStatement, ItemVisibility, Literal, LiteralIdentifier,
LiteralValue, MemberExpression, MemberObject, Node, NonCodeMeta, NonCodeNode, NonCodeValue, ObjectExpression, LiteralValue, MemberExpression, MemberObject, Node, NonCodeMeta, NonCodeNode, NonCodeValue, ObjectExpression,
ObjectProperty, Parameter, PipeExpression, PipeSubstitution, Program, ReturnStatement, TagDeclarator, ObjectProperty, Parameter, PipeExpression, PipeSubstitution, Program, ReturnStatement, Shebang, TagDeclarator,
UnaryExpression, UnaryOperator, VariableDeclaration, VariableDeclarator, VariableKind, UnaryExpression, UnaryOperator, VariableDeclaration, VariableDeclarator, VariableKind,
}, },
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
@ -141,11 +141,8 @@ fn expected(what: &'static str) -> StrContext {
fn program(i: TokenSlice) -> PResult<Node<Program>> { fn program(i: TokenSlice) -> PResult<Node<Program>> {
let shebang = opt(shebang).parse_next(i)?; let shebang = opt(shebang).parse_next(i)?;
let mut out: Node<Program> = function_body.parse_next(i)?; let mut out: Node<Program> = function_body.parse_next(i)?;
out.shebang = shebang;
// Add the shebang to the non-code meta.
if let Some(shebang) = shebang {
out.non_code_meta.start_nodes.insert(0, shebang);
}
// Match original parser behaviour, for now. // Match original parser behaviour, for now.
// Once this is merged and stable, consider changing this as I think it's more accurate // Once this is merged and stable, consider changing this as I think it's more accurate
// without the -1. // without the -1.
@ -533,7 +530,7 @@ fn whitespace(i: TokenSlice) -> PResult<Vec<Token>> {
/// A shebang is a line at the start of a file that starts with `#!`. /// A shebang is a line at the start of a file that starts with `#!`.
/// If the shebang is present it takes up the whole line. /// If the shebang is present it takes up the whole line.
fn shebang(i: TokenSlice) -> PResult<Node<NonCodeNode>> { fn shebang(i: TokenSlice) -> PResult<Node<Shebang>> {
// Parse the hash and the bang. // Parse the hash and the bang.
hash.parse_next(i)?; hash.parse_next(i)?;
bang.parse_next(i)?; bang.parse_next(i)?;
@ -556,12 +553,7 @@ fn shebang(i: TokenSlice) -> PResult<Node<NonCodeNode>> {
opt(whitespace).parse_next(i)?; opt(whitespace).parse_next(i)?;
Ok(Node::new( Ok(Node::new(
NonCodeNode { Shebang::new(format!("#!{}", value)),
value: NonCodeValue::Shebang {
value: format!("#!{}", value),
},
digest: None,
},
0, 0,
tokens.last().unwrap().end, tokens.last().unwrap().end,
tokens.first().unwrap().module_id, tokens.first().unwrap().module_id,
@ -1079,7 +1071,6 @@ fn noncode_just_after_code(i: TokenSlice) -> PResult<Node<NonCodeNode>> {
// There's an empty line between the body item and the comment, // There's an empty line between the body item and the comment,
// This means the comment is a NewLineBlockComment! // This means the comment is a NewLineBlockComment!
let value = match nc.inner.value { let value = match nc.inner.value {
NonCodeValue::Shebang { value } => NonCodeValue::Shebang { value },
// Change block comments to inline, as discussed above // Change block comments to inline, as discussed above
NonCodeValue::BlockComment { value, style } => NonCodeValue::NewLineBlockComment { value, style }, NonCodeValue::BlockComment { value, style } => NonCodeValue::NewLineBlockComment { value, style },
// Other variants don't need to change. // Other variants don't need to change.
@ -1100,7 +1091,6 @@ fn noncode_just_after_code(i: TokenSlice) -> PResult<Node<NonCodeNode>> {
// There's no newline between the body item and comment, // There's no newline between the body item and comment,
// so if this is a comment, it must be inline with code. // so if this is a comment, it must be inline with code.
let value = match nc.inner.value { let value = match nc.inner.value {
NonCodeValue::Shebang { value } => NonCodeValue::Shebang { value },
// Change block comments to inline, as discussed above // Change block comments to inline, as discussed above
NonCodeValue::BlockComment { value, style } => NonCodeValue::InlineComment { value, style }, NonCodeValue::BlockComment { value, style } => NonCodeValue::InlineComment { value, style },
// Other variants don't need to change. // Other variants don't need to change.
@ -1300,6 +1290,7 @@ pub fn function_body(i: TokenSlice) -> PResult<Node<Program>> {
Program { Program {
body, body,
non_code_meta, non_code_meta,
shebang: None,
digest: None, digest: None,
}, },
start.0, start.0,
@ -2419,6 +2410,7 @@ const mySk1 = startSketchAt([0, 0])"#;
)], )],
digest: None, digest: None,
}, },
shebang: None,
digest: None, digest: None,
}, },
7, 7,
@ -3156,6 +3148,7 @@ const mySk1 = startSketchAt([0, 0])"#;
4, 4,
module_id, module_id,
))], ))],
shebang: None,
non_code_meta: NonCodeMeta::default(), non_code_meta: NonCodeMeta::default(),
digest: None, digest: None,
}, },

View File

@ -13,6 +13,13 @@ use crate::{
impl Program { impl Program {
pub fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String { pub fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
let indentation = options.get_indentation(indentation_level); let indentation = options.get_indentation(indentation_level);
let result = self
.shebang
.as_ref()
.map(|sh| format!("{}\n\n", sh.inner.content))
.unwrap_or_default();
let result = self let result = self
.body .body
.iter() .iter()
@ -38,7 +45,7 @@ impl Program {
} }
}) })
.enumerate() .enumerate()
.fold(String::new(), |mut output, (index, recast_str)| { .fold(result, |mut output, (index, recast_str)| {
let start_string = if index == 0 { let start_string = if index == 0 {
// We need to indent. // We need to indent.
if self.non_code_meta.start_nodes.is_empty() { if self.non_code_meta.start_nodes.is_empty() {
@ -107,7 +114,7 @@ impl NonCodeValue {
fn should_cause_array_newline(&self) -> bool { fn should_cause_array_newline(&self) -> bool {
match self { match self {
Self::InlineComment { .. } => false, Self::InlineComment { .. } => false,
Self::Shebang { .. } | Self::BlockComment { .. } | Self::NewLineBlockComment { .. } | Self::NewLine => true, Self::BlockComment { .. } | Self::NewLineBlockComment { .. } | Self::NewLine => true,
} }
} }
} }