Rust executor in kcl lsp server (just rust side for now) (#2103)

* start of cleaning up executor

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* cleanup executor

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* do nothing if the file does not change

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* execution is lsp

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* add the custom notifications

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* custom notifications

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix spawn local

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* update derive-docs

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* ckeanups

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* emptu

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Jess Frazelle
2024-04-12 21:32:57 -07:00
committed by GitHub
parent 9dedc56b7e
commit 70bc0accad
35 changed files with 811 additions and 457 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -21,9 +21,12 @@ interface LSPRequestMap {
LSP.SemanticTokensParams, LSP.SemanticTokensParams,
LSP.SemanticTokens LSP.SemanticTokens
] ]
getCompletions: [CopilotLspCompletionParams, CopilotCompletionResponse] 'copilot/getCompletions': [
notifyAccepted: [CopilotAcceptCompletionParams, any] CopilotLspCompletionParams,
notifyRejected: [CopilotRejectCompletionParams, any] CopilotCompletionResponse
]
'copilot/notifyAccepted': [CopilotAcceptCompletionParams, any]
'copilot/notifyRejected': [CopilotRejectCompletionParams, any]
} }
// Client to server // Client to server
@ -215,7 +218,7 @@ export class LanguageServerClient {
} }
async getCompletion(params: CopilotLspCompletionParams) { async getCompletion(params: CopilotLspCompletionParams) {
const response = await this.request('getCompletions', params) const response = await this.request('copilot/getCompletions', params)
// //
this.queuedUids = [...response.completions.map((c) => c.uuid)] this.queuedUids = [...response.completions.map((c) => c.uuid)]
return response return response
@ -235,11 +238,11 @@ export class LanguageServerClient {
} }
async acceptCompletion(params: CopilotAcceptCompletionParams) { async acceptCompletion(params: CopilotAcceptCompletionParams) {
return await this.request('notifyAccepted', params) return await this.request('copilot/notifyAccepted', params)
} }
async rejectCompletions(params: CopilotRejectCompletionParams) { async rejectCompletions(params: CopilotRejectCompletionParams) {
return await this.request('notifyRejected', params) return await this.request('copilot/notifyRejected', params)
} }
private processNotifications(notification: LSP.NotificationMessage) { private processNotifications(notification: LSP.NotificationMessage) {

View File

@ -927,7 +927,7 @@ dependencies = [
[[package]] [[package]]
name = "derive-docs" name = "derive-docs"
version = "0.1.13" version = "0.1.14"
dependencies = [ dependencies = [
"Inflector", "Inflector",
"anyhow", "anyhow",

View File

@ -1,7 +1,7 @@
[package] [package]
name = "derive-docs" name = "derive-docs"
description = "A tool for generating documentation from Rust derive macros" description = "A tool for generating documentation from Rust derive macros"
version = "0.1.13" version = "0.1.14"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app" repository = "https://github.com/KittyCAD/modeling-app"

View File

@ -383,7 +383,7 @@ fn do_stdlib_inner(
fn #boxed_fn_name_ident( fn #boxed_fn_name_ident(
args: crate::std::Args, args: crate::std::Args,
) -> std::pin::Pin< ) -> std::pin::Pin<
Box<dyn std::future::Future<Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>>>, Box<dyn std::future::Future<Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>> + Send>,
> { > {
Box::pin(#fn_name_ident(args)) Box::pin(#fn_name_ident(args))
} }
@ -783,11 +783,10 @@ fn generate_code_block_test(
let tokens = crate::token::lexer(#code_block); let tokens = crate::token::lexer(#code_block);
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
let mut mem: crate::executor::ProgramMemory = Default::default();
let units = kittycad::types::UnitLength::Mm; let units = kittycad::types::UnitLength::Mm;
let ctx = crate::executor::ExecutorContext::new(ws, units.clone()).await.unwrap(); let ctx = crate::executor::ExecutorContext::new(ws, units.clone()).await.unwrap();
crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx).await.unwrap(); ctx.run(program, None).await.unwrap();
let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units); let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units);

View File

@ -28,14 +28,11 @@ mod test_examples_show {
let tokens = crate::token::lexer("This is another code block.\nyes sirrr.\nshow"); let tokens = crate::token::lexer("This is another code block.\nyes sirrr.\nshow");
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
let mut mem: crate::executor::ProgramMemory = Default::default();
let units = kittycad::types::UnitLength::Mm; let units = kittycad::types::UnitLength::Mm;
let ctx = crate::executor::ExecutorContext::new(ws, units.clone()) let ctx = crate::executor::ExecutorContext::new(ws, units.clone())
.await .await
.unwrap(); .unwrap();
crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) ctx.run(program, None).await.unwrap();
.await
.unwrap();
let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units); let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units);
ctx.engine ctx.engine
.send_modeling_cmd( .send_modeling_cmd(
@ -123,14 +120,11 @@ mod test_examples_show {
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow"); let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow");
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
let mut mem: crate::executor::ProgramMemory = Default::default();
let units = kittycad::types::UnitLength::Mm; let units = kittycad::types::UnitLength::Mm;
let ctx = crate::executor::ExecutorContext::new(ws, units.clone()) let ctx = crate::executor::ExecutorContext::new(ws, units.clone())
.await .await
.unwrap(); .unwrap();
crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) ctx.run(program, None).await.unwrap();
.await
.unwrap();
let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units); let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units);
ctx.engine ctx.engine
.send_modeling_cmd( .send_modeling_cmd(
@ -205,8 +199,8 @@ fn boxed_show(
) -> std::pin::Pin< ) -> std::pin::Pin<
Box< Box<
dyn std::future::Future< dyn std::future::Future<
Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>, Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>,
>, > + Send,
>, >,
> { > {
Box::pin(show(args)) Box::pin(show(args))

View File

@ -28,14 +28,11 @@ mod test_examples_show {
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow"); let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow");
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
let mut mem: crate::executor::ProgramMemory = Default::default();
let units = kittycad::types::UnitLength::Mm; let units = kittycad::types::UnitLength::Mm;
let ctx = crate::executor::ExecutorContext::new(ws, units.clone()) let ctx = crate::executor::ExecutorContext::new(ws, units.clone())
.await .await
.unwrap(); .unwrap();
crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) ctx.run(program, None).await.unwrap();
.await
.unwrap();
let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units); let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units);
ctx.engine ctx.engine
.send_modeling_cmd( .send_modeling_cmd(
@ -110,8 +107,8 @@ fn boxed_show(
) -> std::pin::Pin< ) -> std::pin::Pin<
Box< Box<
dyn std::future::Future< dyn std::future::Future<
Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>, Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>,
>, > + Send,
>, >,
> { > {
Box::pin(show(args)) Box::pin(show(args))

View File

@ -28,14 +28,11 @@ mod test_examples_my_func {
let tokens = crate::token::lexer("This is another code block.\nyes sirrr.\nmyFunc"); let tokens = crate::token::lexer("This is another code block.\nyes sirrr.\nmyFunc");
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
let mut mem: crate::executor::ProgramMemory = Default::default();
let units = kittycad::types::UnitLength::Mm; let units = kittycad::types::UnitLength::Mm;
let ctx = crate::executor::ExecutorContext::new(ws, units.clone()) let ctx = crate::executor::ExecutorContext::new(ws, units.clone())
.await .await
.unwrap(); .unwrap();
crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) ctx.run(program, None).await.unwrap();
.await
.unwrap();
let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units); let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units);
ctx.engine ctx.engine
.send_modeling_cmd( .send_modeling_cmd(
@ -123,14 +120,11 @@ mod test_examples_my_func {
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nmyFunc"); let tokens = crate::token::lexer("This is code.\nIt does other shit.\nmyFunc");
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
let mut mem: crate::executor::ProgramMemory = Default::default();
let units = kittycad::types::UnitLength::Mm; let units = kittycad::types::UnitLength::Mm;
let ctx = crate::executor::ExecutorContext::new(ws, units.clone()) let ctx = crate::executor::ExecutorContext::new(ws, units.clone())
.await .await
.unwrap(); .unwrap();
crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) ctx.run(program, None).await.unwrap();
.await
.unwrap();
let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units); let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units);
ctx.engine ctx.engine
.send_modeling_cmd( .send_modeling_cmd(
@ -205,8 +199,8 @@ fn boxed_my_func(
) -> std::pin::Pin< ) -> std::pin::Pin<
Box< Box<
dyn std::future::Future< dyn std::future::Future<
Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>, Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>,
>, > + Send,
>, >,
> { > {
Box::pin(my_func(args)) Box::pin(my_func(args))

View File

@ -29,14 +29,11 @@ mod test_examples_import {
let tokens = crate::token::lexer("This is another code block.\nyes sirrr.\nimport"); let tokens = crate::token::lexer("This is another code block.\nyes sirrr.\nimport");
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
let mut mem: crate::executor::ProgramMemory = Default::default();
let units = kittycad::types::UnitLength::Mm; let units = kittycad::types::UnitLength::Mm;
let ctx = crate::executor::ExecutorContext::new(ws, units.clone()) let ctx = crate::executor::ExecutorContext::new(ws, units.clone())
.await .await
.unwrap(); .unwrap();
crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) ctx.run(program, None).await.unwrap();
.await
.unwrap();
let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units); let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units);
ctx.engine ctx.engine
.send_modeling_cmd( .send_modeling_cmd(
@ -125,14 +122,11 @@ mod test_examples_import {
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nimport"); let tokens = crate::token::lexer("This is code.\nIt does other shit.\nimport");
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
let mut mem: crate::executor::ProgramMemory = Default::default();
let units = kittycad::types::UnitLength::Mm; let units = kittycad::types::UnitLength::Mm;
let ctx = crate::executor::ExecutorContext::new(ws, units.clone()) let ctx = crate::executor::ExecutorContext::new(ws, units.clone())
.await .await
.unwrap(); .unwrap();
crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) ctx.run(program, None).await.unwrap();
.await
.unwrap();
let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units); let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units);
ctx.engine ctx.engine
.send_modeling_cmd( .send_modeling_cmd(
@ -207,8 +201,8 @@ fn boxed_import(
) -> std::pin::Pin< ) -> std::pin::Pin<
Box< Box<
dyn std::future::Future< dyn std::future::Future<
Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>, Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>,
>, > + Send,
>, >,
> { > {
Box::pin(import(args)) Box::pin(import(args))

View File

@ -28,14 +28,11 @@ mod test_examples_line_to {
let tokens = crate::token::lexer("This is another code block.\nyes sirrr.\nlineTo"); let tokens = crate::token::lexer("This is another code block.\nyes sirrr.\nlineTo");
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
let mut mem: crate::executor::ProgramMemory = Default::default();
let units = kittycad::types::UnitLength::Mm; let units = kittycad::types::UnitLength::Mm;
let ctx = crate::executor::ExecutorContext::new(ws, units.clone()) let ctx = crate::executor::ExecutorContext::new(ws, units.clone())
.await .await
.unwrap(); .unwrap();
crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) ctx.run(program, None).await.unwrap();
.await
.unwrap();
let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units); let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units);
ctx.engine ctx.engine
.send_modeling_cmd( .send_modeling_cmd(
@ -123,14 +120,11 @@ mod test_examples_line_to {
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nlineTo"); let tokens = crate::token::lexer("This is code.\nIt does other shit.\nlineTo");
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
let mut mem: crate::executor::ProgramMemory = Default::default();
let units = kittycad::types::UnitLength::Mm; let units = kittycad::types::UnitLength::Mm;
let ctx = crate::executor::ExecutorContext::new(ws, units.clone()) let ctx = crate::executor::ExecutorContext::new(ws, units.clone())
.await .await
.unwrap(); .unwrap();
crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) ctx.run(program, None).await.unwrap();
.await
.unwrap();
let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units); let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units);
ctx.engine ctx.engine
.send_modeling_cmd( .send_modeling_cmd(
@ -205,8 +199,8 @@ fn boxed_line_to(
) -> std::pin::Pin< ) -> std::pin::Pin<
Box< Box<
dyn std::future::Future< dyn std::future::Future<
Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>, Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>,
>, > + Send,
>, >,
> { > {
Box::pin(line_to(args)) Box::pin(line_to(args))

View File

@ -28,14 +28,11 @@ mod test_examples_min {
let tokens = crate::token::lexer("This is another code block.\nyes sirrr.\nmin"); let tokens = crate::token::lexer("This is another code block.\nyes sirrr.\nmin");
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
let mut mem: crate::executor::ProgramMemory = Default::default();
let units = kittycad::types::UnitLength::Mm; let units = kittycad::types::UnitLength::Mm;
let ctx = crate::executor::ExecutorContext::new(ws, units.clone()) let ctx = crate::executor::ExecutorContext::new(ws, units.clone())
.await .await
.unwrap(); .unwrap();
crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) ctx.run(program, None).await.unwrap();
.await
.unwrap();
let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units); let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units);
ctx.engine ctx.engine
.send_modeling_cmd( .send_modeling_cmd(
@ -123,14 +120,11 @@ mod test_examples_min {
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nmin"); let tokens = crate::token::lexer("This is code.\nIt does other shit.\nmin");
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
let mut mem: crate::executor::ProgramMemory = Default::default();
let units = kittycad::types::UnitLength::Mm; let units = kittycad::types::UnitLength::Mm;
let ctx = crate::executor::ExecutorContext::new(ws, units.clone()) let ctx = crate::executor::ExecutorContext::new(ws, units.clone())
.await .await
.unwrap(); .unwrap();
crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) ctx.run(program, None).await.unwrap();
.await
.unwrap();
let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units); let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units);
ctx.engine ctx.engine
.send_modeling_cmd( .send_modeling_cmd(
@ -205,8 +199,8 @@ fn boxed_min(
) -> std::pin::Pin< ) -> std::pin::Pin<
Box< Box<
dyn std::future::Future< dyn std::future::Future<
Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>, Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>,
>, > + Send,
>, >,
> { > {
Box::pin(min(args)) Box::pin(min(args))

View File

@ -28,14 +28,11 @@ mod test_examples_show {
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow"); let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow");
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
let mut mem: crate::executor::ProgramMemory = Default::default();
let units = kittycad::types::UnitLength::Mm; let units = kittycad::types::UnitLength::Mm;
let ctx = crate::executor::ExecutorContext::new(ws, units.clone()) let ctx = crate::executor::ExecutorContext::new(ws, units.clone())
.await .await
.unwrap(); .unwrap();
crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) ctx.run(program, None).await.unwrap();
.await
.unwrap();
let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units); let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units);
ctx.engine ctx.engine
.send_modeling_cmd( .send_modeling_cmd(
@ -110,8 +107,8 @@ fn boxed_show(
) -> std::pin::Pin< ) -> std::pin::Pin<
Box< Box<
dyn std::future::Future< dyn std::future::Future<
Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>, Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>,
>, > + Send,
>, >,
> { > {
Box::pin(show(args)) Box::pin(show(args))

View File

@ -28,14 +28,11 @@ mod test_examples_import {
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nimport"); let tokens = crate::token::lexer("This is code.\nIt does other shit.\nimport");
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
let mut mem: crate::executor::ProgramMemory = Default::default();
let units = kittycad::types::UnitLength::Mm; let units = kittycad::types::UnitLength::Mm;
let ctx = crate::executor::ExecutorContext::new(ws, units.clone()) let ctx = crate::executor::ExecutorContext::new(ws, units.clone())
.await .await
.unwrap(); .unwrap();
crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) ctx.run(program, None).await.unwrap();
.await
.unwrap();
let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units); let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units);
ctx.engine ctx.engine
.send_modeling_cmd( .send_modeling_cmd(
@ -110,8 +107,8 @@ fn boxed_import(
) -> std::pin::Pin< ) -> std::pin::Pin<
Box< Box<
dyn std::future::Future< dyn std::future::Future<
Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>, Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>,
>, > + Send,
>, >,
> { > {
Box::pin(import(args)) Box::pin(import(args))

View File

@ -28,14 +28,11 @@ mod test_examples_import {
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nimport"); let tokens = crate::token::lexer("This is code.\nIt does other shit.\nimport");
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
let mut mem: crate::executor::ProgramMemory = Default::default();
let units = kittycad::types::UnitLength::Mm; let units = kittycad::types::UnitLength::Mm;
let ctx = crate::executor::ExecutorContext::new(ws, units.clone()) let ctx = crate::executor::ExecutorContext::new(ws, units.clone())
.await .await
.unwrap(); .unwrap();
crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) ctx.run(program, None).await.unwrap();
.await
.unwrap();
let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units); let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units);
ctx.engine ctx.engine
.send_modeling_cmd( .send_modeling_cmd(
@ -110,8 +107,8 @@ fn boxed_import(
) -> std::pin::Pin< ) -> std::pin::Pin<
Box< Box<
dyn std::future::Future< dyn std::future::Future<
Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>, Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>,
>, > + Send,
>, >,
> { > {
Box::pin(import(args)) Box::pin(import(args))

View File

@ -28,14 +28,11 @@ mod test_examples_import {
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nimport"); let tokens = crate::token::lexer("This is code.\nIt does other shit.\nimport");
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
let mut mem: crate::executor::ProgramMemory = Default::default();
let units = kittycad::types::UnitLength::Mm; let units = kittycad::types::UnitLength::Mm;
let ctx = crate::executor::ExecutorContext::new(ws, units.clone()) let ctx = crate::executor::ExecutorContext::new(ws, units.clone())
.await .await
.unwrap(); .unwrap();
crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) ctx.run(program, None).await.unwrap();
.await
.unwrap();
let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units); let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units);
ctx.engine ctx.engine
.send_modeling_cmd( .send_modeling_cmd(
@ -110,8 +107,8 @@ fn boxed_import(
) -> std::pin::Pin< ) -> std::pin::Pin<
Box< Box<
dyn std::future::Future< dyn std::future::Future<
Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>, Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>,
>, > + Send,
>, >,
> { > {
Box::pin(import(args)) Box::pin(import(args))

View File

@ -28,14 +28,11 @@ mod test_examples_show {
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow"); let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow");
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
let mut mem: crate::executor::ProgramMemory = Default::default();
let units = kittycad::types::UnitLength::Mm; let units = kittycad::types::UnitLength::Mm;
let ctx = crate::executor::ExecutorContext::new(ws, units.clone()) let ctx = crate::executor::ExecutorContext::new(ws, units.clone())
.await .await
.unwrap(); .unwrap();
crate::executor::execute(program, &mut mem, crate::executor::BodyType::Root, &ctx) ctx.run(program, None).await.unwrap();
.await
.unwrap();
let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units); let (x, y) = crate::std::utils::get_camera_zoom_magnitude_per_unit_length(units);
ctx.engine ctx.engine
.send_modeling_cmd( .send_modeling_cmd(
@ -110,8 +107,8 @@ fn boxed_show(
) -> std::pin::Pin< ) -> std::pin::Pin<
Box< Box<
dyn std::future::Future< dyn std::future::Future<
Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>, Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>,
>, > + Send,
>, >,
> { > {
Box::pin(show(args)) Box::pin(show(args))

View File

@ -19,7 +19,7 @@ chrono = "0.4.37"
clap = { version = "4.5.4", features = ["cargo", "derive", "env", "unicode"], optional = true } clap = { version = "4.5.4", features = ["cargo", "derive", "env", "unicode"], optional = true }
dashmap = "5.5.3" dashmap = "5.5.3"
databake = { version = "0.1.7", features = ["derive"] } databake = { version = "0.1.7", features = ["derive"] }
derive-docs = { version = "0.1.13", path = "../derive-docs" } derive-docs = { version = "0.1.14", path = "../derive-docs" }
form_urlencoded = "1.2.1" form_urlencoded = "1.2.1"
futures = { version = "0.3.30" } futures = { version = "0.3.30" }
git_rev = "0.1.0" git_rev = "0.1.0"
@ -44,6 +44,7 @@ zip = { version = "0.6.6", default-features = false }
[target.'cfg(target_arch = "wasm32")'.dependencies] [target.'cfg(target_arch = "wasm32")'.dependencies]
js-sys = { version = "0.3.69" } js-sys = { version = "0.3.69" }
tokio = { version = "1.37.0", features = ["sync"] }
tower-lsp = { version = "0.20.0", default-features = false, features = ["runtime-agnostic"] } tower-lsp = { version = "0.20.0", default-features = false, features = ["runtime-agnostic"] }
wasm-bindgen = "0.2.91" wasm-bindgen = "0.2.91"
wasm-bindgen-futures = "0.4.42" wasm-bindgen-futures = "0.4.42"

View File

@ -711,7 +711,7 @@ impl BinaryPart {
} }
} }
#[async_recursion::async_recursion(?Send)] #[async_recursion::async_recursion]
pub async fn get_result( pub async fn get_result(
&self, &self,
memory: &mut ProgramMemory, memory: &mut ProgramMemory,
@ -1005,7 +1005,7 @@ impl CallExpression {
) )
} }
#[async_recursion::async_recursion(?Send)] #[async_recursion::async_recursion]
pub async fn execute( pub async fn execute(
&self, &self,
memory: &mut ProgramMemory, memory: &mut ProgramMemory,
@ -1112,7 +1112,7 @@ impl CallExpression {
// Call the stdlib function // Call the stdlib function
let p = func.function().clone().body; let p = func.function().clone().body;
let results = match crate::executor::execute(p, &mut fn_memory, BodyType::Block, ctx).await { let results = match ctx.inner_execute(p, &mut fn_memory, BodyType::Block).await {
Ok(results) => results, Ok(results) => results,
Err(err) => { Err(err) => {
// We need to override the source ranges so we don't get the embedded kcl // We need to override the source ranges so we don't get the embedded kcl
@ -1690,7 +1690,7 @@ impl ArrayExpression {
None None
} }
#[async_recursion::async_recursion(?Send)] #[async_recursion::async_recursion]
pub async fn execute( pub async fn execute(
&self, &self,
memory: &mut ProgramMemory, memory: &mut ProgramMemory,
@ -1837,7 +1837,7 @@ impl ObjectExpression {
None None
} }
#[async_recursion::async_recursion(?Send)] #[async_recursion::async_recursion]
pub async fn execute( pub async fn execute(
&self, &self,
memory: &mut ProgramMemory, memory: &mut ProgramMemory,
@ -2271,14 +2271,16 @@ impl BinaryExpression {
if left_source_range.contains(pos) { if left_source_range.contains(pos) {
return self.left.get_hover_value_for_position(pos, code); return self.left.get_hover_value_for_position(pos, code);
} else if right_source_range.contains(pos) { }
if right_source_range.contains(pos) {
return self.right.get_hover_value_for_position(pos, code); return self.right.get_hover_value_for_position(pos, code);
} }
None None
} }
#[async_recursion::async_recursion(?Send)] #[async_recursion::async_recursion]
pub async fn get_result( pub async fn get_result(
&self, &self,
memory: &mut ProgramMemory, memory: &mut ProgramMemory,
@ -2636,7 +2638,6 @@ impl PipeExpression {
} }
} }
#[async_recursion::async_recursion(?Send)]
async fn execute_pipe_body( async fn execute_pipe_body(
memory: &mut ProgramMemory, memory: &mut ProgramMemory,
body: &[Value], body: &[Value],

View File

@ -94,7 +94,6 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
} else { } else {
batched_requests batched_requests
}; };
// println!("Running batch: {final_req:#?}");
// Create the map of original command IDs to source range. // Create the map of original command IDs to source range.
// This is for the wasm side, kurt needs it for selections. // This is for the wasm side, kurt needs it for selections.

View File

@ -296,7 +296,7 @@ pub type MemoryFunction =
expression: Box<FunctionExpression>, expression: Box<FunctionExpression>,
metadata: Vec<Metadata>, metadata: Vec<Metadata>,
ctx: ExecutorContext, ctx: ExecutorContext,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Option<ProgramReturn>, KclError>>>>; ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Option<ProgramReturn>, KclError>> + Send>>;
fn force_memory_function< fn force_memory_function<
F: Fn( F: Fn(
@ -305,7 +305,7 @@ fn force_memory_function<
Box<FunctionExpression>, Box<FunctionExpression>,
Vec<Metadata>, Vec<Metadata>,
ExecutorContext, ExecutorContext,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Option<ProgramReturn>, KclError>>>>, ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Option<ProgramReturn>, KclError>> + Send>>,
>( >(
f: F, f: F,
) -> F { ) -> F {
@ -992,243 +992,257 @@ impl ExecutorContext {
is_mock: false, is_mock: false,
}) })
} }
}
/// Execute an AST's program. /// Perform the execution of a program.
pub async fn execute_outer( /// You can optionally pass in some initialization memory.
program: crate::ast::types::Program, /// Kurt uses this for partial execution.
memory: &mut ProgramMemory, pub async fn run(
_options: BodyType, &self,
ctx: &ExecutorContext, program: crate::ast::types::Program,
) -> Result<ProgramMemory, KclError> { memory: Option<ProgramMemory>,
// Before we even start executing the program, set the units. ) -> Result<ProgramMemory, KclError> {
ctx.engine // Before we even start executing the program, set the units.
.send_modeling_cmd( self.engine
uuid::Uuid::new_v4(), .send_modeling_cmd(
SourceRange::default(), uuid::Uuid::new_v4(),
kittycad::types::ModelingCmd::SetSceneUnits { SourceRange::default(),
unit: ctx.units.clone(), kittycad::types::ModelingCmd::SetSceneUnits {
}, unit: self.units.clone(),
) },
.await?; )
execute(program, memory, _options, ctx).await .await?;
} let mut memory = if let Some(memory) = memory {
memory.clone()
/// Execute an AST's program. } else {
#[async_recursion(?Send)] Default::default()
pub(crate) async fn execute( };
program: crate::ast::types::Program, self.inner_execute(program, &mut memory, crate::executor::BodyType::Root)
memory: &mut ProgramMemory, .await
_options: BodyType,
ctx: &ExecutorContext,
) -> Result<ProgramMemory, KclError> {
let pipe_info = PipeInfo::default();
// Iterate over the body of the program.
for statement in &program.body {
match statement {
BodyItem::ExpressionStatement(expression_statement) => {
if let Value::PipeExpression(pipe_expr) = &expression_statement.expression {
pipe_expr.get_result(memory, &pipe_info, ctx).await?;
} else if let Value::CallExpression(call_expr) = &expression_statement.expression {
let fn_name = call_expr.callee.name.to_string();
let mut args: Vec<MemoryItem> = Vec::new();
for arg in &call_expr.arguments {
match arg {
Value::Literal(literal) => args.push(literal.into()),
Value::Identifier(identifier) => {
let memory_item = memory.get(&identifier.name, identifier.into())?;
args.push(memory_item.clone());
}
Value::CallExpression(call_expr) => {
let result = call_expr.execute(memory, &pipe_info, ctx).await?;
args.push(result);
}
Value::BinaryExpression(binary_expression) => {
let result = binary_expression.get_result(memory, &pipe_info, ctx).await?;
args.push(result);
}
Value::UnaryExpression(unary_expression) => {
let result = unary_expression.get_result(memory, &pipe_info, ctx).await?;
args.push(result);
}
Value::ObjectExpression(object_expression) => {
let result = object_expression.execute(memory, &pipe_info, ctx).await?;
args.push(result);
}
Value::ArrayExpression(array_expression) => {
let result = array_expression.execute(memory, &pipe_info, ctx).await?;
args.push(result);
}
// We do nothing for the rest.
_ => (),
}
}
match ctx.stdlib.get_either(&call_expr.callee.name) {
FunctionKind::Core(func) => {
let args = crate::std::Args::new(args, call_expr.into(), ctx.clone());
let result = func.std_lib_fn()(args).await?;
memory.return_ = Some(ProgramReturn::Value(result));
}
FunctionKind::Std(func) => {
let mut newmem = memory.clone();
let result = execute(func.program().to_owned(), &mut newmem, BodyType::Block, ctx).await?;
memory.return_ = result.return_;
}
FunctionKind::UserDefined => {
if let Some(func) = memory.clone().root.get(&fn_name) {
let result = func.call_fn(args.clone(), memory.clone(), ctx.clone()).await?;
memory.return_ = result;
} else {
return Err(KclError::Semantic(KclErrorDetails {
message: format!("No such name {} defined", fn_name),
source_ranges: vec![call_expr.into()],
}));
}
}
}
}
}
BodyItem::VariableDeclaration(variable_declaration) => {
for declaration in &variable_declaration.declarations {
let var_name = declaration.id.name.to_string();
let source_range: SourceRange = declaration.init.clone().into();
let metadata = Metadata { source_range };
match &declaration.init {
Value::None(none) => {
memory.add(&var_name, none.into(), source_range)?;
}
Value::Literal(literal) => {
memory.add(&var_name, literal.into(), source_range)?;
}
Value::Identifier(identifier) => {
let value = memory.get(&identifier.name, identifier.into())?;
memory.add(&var_name, value.clone(), source_range)?;
}
Value::BinaryExpression(binary_expression) => {
let result = binary_expression.get_result(memory, &pipe_info, ctx).await?;
memory.add(&var_name, result, source_range)?;
}
Value::FunctionExpression(function_expression) => {
let mem_func = force_memory_function(
|args: Vec<MemoryItem>,
memory: ProgramMemory,
function_expression: Box<FunctionExpression>,
_metadata: Vec<Metadata>,
ctx: ExecutorContext| {
Box::pin(async move {
let mut fn_memory =
assign_args_to_params(&function_expression, args, memory.clone())?;
let result = execute(
function_expression.body.clone(),
&mut fn_memory,
BodyType::Block,
&ctx,
)
.await?;
Ok(result.return_)
})
},
);
memory.add(
&var_name,
MemoryItem::Function {
expression: function_expression.clone(),
meta: vec![metadata],
func: Some(mem_func),
},
source_range,
)?;
}
Value::CallExpression(call_expression) => {
let result = call_expression.execute(memory, &pipe_info, ctx).await?;
memory.add(&var_name, result, source_range)?;
}
Value::PipeExpression(pipe_expression) => {
let result = pipe_expression.get_result(memory, &pipe_info, ctx).await?;
memory.add(&var_name, result, source_range)?;
}
Value::PipeSubstitution(pipe_substitution) => {
return Err(KclError::Semantic(KclErrorDetails {
message: format!(
"pipe substitution not implemented for declaration of variable {}",
var_name
),
source_ranges: vec![pipe_substitution.into()],
}));
}
Value::ArrayExpression(array_expression) => {
let result = array_expression.execute(memory, &pipe_info, ctx).await?;
memory.add(&var_name, result, source_range)?;
}
Value::ObjectExpression(object_expression) => {
let result = object_expression.execute(memory, &pipe_info, ctx).await?;
memory.add(&var_name, result, source_range)?;
}
Value::MemberExpression(member_expression) => {
let result = member_expression.get_result(memory)?;
memory.add(&var_name, result, source_range)?;
}
Value::UnaryExpression(unary_expression) => {
let result = unary_expression.get_result(memory, &pipe_info, ctx).await?;
memory.add(&var_name, result, source_range)?;
}
}
}
}
BodyItem::ReturnStatement(return_statement) => match &return_statement.argument {
Value::BinaryExpression(bin_expr) => {
let result = bin_expr.get_result(memory, &pipe_info, ctx).await?;
memory.return_ = Some(ProgramReturn::Value(result));
}
Value::UnaryExpression(unary_expr) => {
let result = unary_expr.get_result(memory, &pipe_info, ctx).await?;
memory.return_ = Some(ProgramReturn::Value(result));
}
Value::Identifier(identifier) => {
let value = memory.get(&identifier.name, identifier.into())?.clone();
memory.return_ = Some(ProgramReturn::Value(value));
}
Value::Literal(literal) => {
memory.return_ = Some(ProgramReturn::Value(literal.into()));
}
Value::ArrayExpression(array_expr) => {
let result = array_expr.execute(memory, &pipe_info, ctx).await?;
memory.return_ = Some(ProgramReturn::Value(result));
}
Value::ObjectExpression(obj_expr) => {
let result = obj_expr.execute(memory, &pipe_info, ctx).await?;
memory.return_ = Some(ProgramReturn::Value(result));
}
Value::CallExpression(call_expr) => {
let result = call_expr.execute(memory, &pipe_info, ctx).await?;
memory.return_ = Some(ProgramReturn::Value(result));
}
Value::MemberExpression(member_expr) => {
let result = member_expr.get_result(memory)?;
memory.return_ = Some(ProgramReturn::Value(result));
}
Value::PipeExpression(pipe_expr) => {
let result = pipe_expr.get_result(memory, &pipe_info, ctx).await?;
memory.return_ = Some(ProgramReturn::Value(result));
}
Value::PipeSubstitution(_) => {}
Value::FunctionExpression(_) => {}
Value::None(none) => {
memory.return_ = Some(ProgramReturn::Value(MemoryItem::from(none)));
}
},
}
} }
// Flush the batch queue. /// Execute an AST's program.
ctx.engine.flush_batch(SourceRange([program.end, program.end])).await?; #[async_recursion]
pub(crate) async fn inner_execute(
&self,
program: crate::ast::types::Program,
memory: &mut ProgramMemory,
_body_type: BodyType,
) -> Result<ProgramMemory, KclError> {
let pipe_info = PipeInfo::default();
Ok(memory.clone()) // Iterate over the body of the program.
for statement in &program.body {
match statement {
BodyItem::ExpressionStatement(expression_statement) => {
if let Value::PipeExpression(pipe_expr) = &expression_statement.expression {
pipe_expr.get_result(memory, &pipe_info, self).await?;
} else if let Value::CallExpression(call_expr) = &expression_statement.expression {
let fn_name = call_expr.callee.name.to_string();
let mut args: Vec<MemoryItem> = Vec::new();
for arg in &call_expr.arguments {
match arg {
Value::Literal(literal) => args.push(literal.into()),
Value::Identifier(identifier) => {
let memory_item = memory.get(&identifier.name, identifier.into())?;
args.push(memory_item.clone());
}
Value::CallExpression(call_expr) => {
let result = call_expr.execute(memory, &pipe_info, self).await?;
args.push(result);
}
Value::BinaryExpression(binary_expression) => {
let result = binary_expression.get_result(memory, &pipe_info, self).await?;
args.push(result);
}
Value::UnaryExpression(unary_expression) => {
let result = unary_expression.get_result(memory, &pipe_info, self).await?;
args.push(result);
}
Value::ObjectExpression(object_expression) => {
let result = object_expression.execute(memory, &pipe_info, self).await?;
args.push(result);
}
Value::ArrayExpression(array_expression) => {
let result = array_expression.execute(memory, &pipe_info, self).await?;
args.push(result);
}
// We do nothing for the rest.
_ => (),
}
}
match self.stdlib.get_either(&call_expr.callee.name) {
FunctionKind::Core(func) => {
let args = crate::std::Args::new(args, call_expr.into(), self.clone());
let result = func.std_lib_fn()(args).await?;
memory.return_ = Some(ProgramReturn::Value(result));
}
FunctionKind::Std(func) => {
let mut newmem = memory.clone();
let result = self
.inner_execute(func.program().to_owned(), &mut newmem, BodyType::Block)
.await?;
memory.return_ = result.return_;
}
FunctionKind::UserDefined => {
if let Some(func) = memory.clone().root.get(&fn_name) {
let result = func.call_fn(args.clone(), memory.clone(), self.clone()).await?;
memory.return_ = result;
} else {
return Err(KclError::Semantic(KclErrorDetails {
message: format!("No such name {} defined", fn_name),
source_ranges: vec![call_expr.into()],
}));
}
}
}
}
}
BodyItem::VariableDeclaration(variable_declaration) => {
for declaration in &variable_declaration.declarations {
let var_name = declaration.id.name.to_string();
let source_range: SourceRange = declaration.init.clone().into();
let metadata = Metadata { source_range };
match &declaration.init {
Value::None(none) => {
memory.add(&var_name, none.into(), source_range)?;
}
Value::Literal(literal) => {
memory.add(&var_name, literal.into(), source_range)?;
}
Value::Identifier(identifier) => {
let value = memory.get(&identifier.name, identifier.into())?;
memory.add(&var_name, value.clone(), source_range)?;
}
Value::BinaryExpression(binary_expression) => {
let result = binary_expression.get_result(memory, &pipe_info, self).await?;
memory.add(&var_name, result, source_range)?;
}
Value::FunctionExpression(function_expression) => {
let mem_func = force_memory_function(
|args: Vec<MemoryItem>,
memory: ProgramMemory,
function_expression: Box<FunctionExpression>,
_metadata: Vec<Metadata>,
ctx: ExecutorContext| {
Box::pin(async move {
let mut fn_memory =
assign_args_to_params(&function_expression, args, memory.clone())?;
let result = ctx
.inner_execute(
function_expression.body.clone(),
&mut fn_memory,
BodyType::Block,
)
.await?;
Ok(result.return_)
})
},
);
memory.add(
&var_name,
MemoryItem::Function {
expression: function_expression.clone(),
meta: vec![metadata],
func: Some(mem_func),
},
source_range,
)?;
}
Value::CallExpression(call_expression) => {
let result = call_expression.execute(memory, &pipe_info, self).await?;
memory.add(&var_name, result, source_range)?;
}
Value::PipeExpression(pipe_expression) => {
let result = pipe_expression.get_result(memory, &pipe_info, self).await?;
memory.add(&var_name, result, source_range)?;
}
Value::PipeSubstitution(pipe_substitution) => {
return Err(KclError::Semantic(KclErrorDetails {
message: format!(
"pipe substitution not implemented for declaration of variable {}",
var_name
),
source_ranges: vec![pipe_substitution.into()],
}));
}
Value::ArrayExpression(array_expression) => {
let result = array_expression.execute(memory, &pipe_info, self).await?;
memory.add(&var_name, result, source_range)?;
}
Value::ObjectExpression(object_expression) => {
let result = object_expression.execute(memory, &pipe_info, self).await?;
memory.add(&var_name, result, source_range)?;
}
Value::MemberExpression(member_expression) => {
let result = member_expression.get_result(memory)?;
memory.add(&var_name, result, source_range)?;
}
Value::UnaryExpression(unary_expression) => {
let result = unary_expression.get_result(memory, &pipe_info, self).await?;
memory.add(&var_name, result, source_range)?;
}
}
}
}
BodyItem::ReturnStatement(return_statement) => match &return_statement.argument {
Value::BinaryExpression(bin_expr) => {
let result = bin_expr.get_result(memory, &pipe_info, self).await?;
memory.return_ = Some(ProgramReturn::Value(result));
}
Value::UnaryExpression(unary_expr) => {
let result = unary_expr.get_result(memory, &pipe_info, self).await?;
memory.return_ = Some(ProgramReturn::Value(result));
}
Value::Identifier(identifier) => {
let value = memory.get(&identifier.name, identifier.into())?.clone();
memory.return_ = Some(ProgramReturn::Value(value));
}
Value::Literal(literal) => {
memory.return_ = Some(ProgramReturn::Value(literal.into()));
}
Value::ArrayExpression(array_expr) => {
let result = array_expr.execute(memory, &pipe_info, self).await?;
memory.return_ = Some(ProgramReturn::Value(result));
}
Value::ObjectExpression(obj_expr) => {
let result = obj_expr.execute(memory, &pipe_info, self).await?;
memory.return_ = Some(ProgramReturn::Value(result));
}
Value::CallExpression(call_expr) => {
let result = call_expr.execute(memory, &pipe_info, self).await?;
memory.return_ = Some(ProgramReturn::Value(result));
}
Value::MemberExpression(member_expr) => {
let result = member_expr.get_result(memory)?;
memory.return_ = Some(ProgramReturn::Value(result));
}
Value::PipeExpression(pipe_expr) => {
let result = pipe_expr.get_result(memory, &pipe_info, self).await?;
memory.return_ = Some(ProgramReturn::Value(result));
}
Value::PipeSubstitution(_) => {}
Value::FunctionExpression(_) => {}
Value::None(none) => {
memory.return_ = Some(ProgramReturn::Value(MemoryItem::from(none)));
}
},
}
}
// Flush the batch queue.
self.engine.flush_batch(SourceRange([program.end, program.end])).await?;
Ok(memory.clone())
}
/// Update the units for the executor.
pub fn update_units(&mut self, units: kittycad::types::UnitLength) {
self.units = units;
}
} }
/// For each argument given, /// For each argument given,
@ -1299,7 +1313,6 @@ mod tests {
let tokens = crate::token::lexer(code); let tokens = crate::token::lexer(code);
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast()?; let program = parser.ast()?;
let mut mem: ProgramMemory = Default::default();
let ctx = ExecutorContext { let ctx = ExecutorContext {
engine: Arc::new(Box::new(crate::engine::conn_mock::EngineConnection::new().await?)), engine: Arc::new(Box::new(crate::engine::conn_mock::EngineConnection::new().await?)),
fs: crate::fs::FileManager::new(), fs: crate::fs::FileManager::new(),
@ -1307,7 +1320,7 @@ mod tests {
units: kittycad::types::UnitLength::Mm, units: kittycad::types::UnitLength::Mm,
is_mock: false, is_mock: false,
}; };
let memory = execute_outer(program, &mut mem, BodyType::Root, &ctx).await?; let memory = ctx.run(program, None).await?;
Ok(memory) Ok(memory)
} }

View File

@ -37,10 +37,21 @@ pub trait Backend {
fn clear_code_state(&self); fn clear_code_state(&self);
/// On change event. /// On change event.
async fn on_change(&self, params: TextDocumentItem); async fn inner_on_change(&self, params: TextDocumentItem);
async fn update_memory(&self, params: TextDocumentItem) { async fn on_change(&self, params: TextDocumentItem) {
self.insert_current_code_map(params.uri.to_string(), params.text.as_bytes().to_vec()); // Check if the document is in the current code map and if it is the same as what we have
// stored.
let filename = params.uri.to_string();
if let Some(current_code) = self.current_code_map().get(&filename) {
if current_code.value() == params.text.as_bytes() {
return;
}
}
// Otherwise update the code map and call the inner on change.
self.insert_current_code_map(filename, params.text.as_bytes().to_vec());
self.inner_on_change(params).await;
} }
async fn update_from_disk<P: AsRef<std::path::Path> + std::marker::Send>(&self, path: P) -> Result<()> { async fn update_from_disk<P: AsRef<std::path::Path> + std::marker::Send>(&self, path: P) -> Result<()> {
@ -171,7 +182,6 @@ pub trait Backend {
version: params.text_document.version, version: params.text_document.version,
language_id: params.text_document.language_id, language_id: params.text_document.language_id,
}; };
self.update_memory(new_params.clone()).await;
self.on_change(new_params).await; self.on_change(new_params).await;
} }
@ -182,7 +192,6 @@ pub trait Backend {
version: params.text_document.version, version: params.text_document.version,
language_id: Default::default(), language_id: Default::default(),
}; };
self.update_memory(new_params.clone()).await;
self.on_change(new_params).await; self.on_change(new_params).await;
} }
@ -194,7 +203,6 @@ pub trait Backend {
version: Default::default(), version: Default::default(),
language_id: Default::default(), language_id: Default::default(),
}; };
self.update_memory(new_params.clone()).await;
self.on_change(new_params).await; self.on_change(new_params).await;
} }
} }

View File

@ -103,7 +103,7 @@ impl crate::lsp::backend::Backend for Backend {
self.current_code_map.clear(); self.current_code_map.clear();
} }
async fn on_change(&self, _params: TextDocumentItem) { async fn inner_on_change(&self, _params: TextDocumentItem) {
// We don't need to do anything here. // We don't need to do anything here.
} }
} }

View File

@ -0,0 +1,29 @@
//! Custom notifications for the KCL LSP server that are not part of the LSP specification.
use serde::{Deserialize, Serialize};
use tower_lsp::lsp_types::{notification::Notification, TextDocumentIdentifier};
/// A notification that the AST has changed.
#[derive(Debug)]
pub enum AstUpdated {}
impl Notification for AstUpdated {
type Params = crate::ast::types::Program;
const METHOD: &'static str = "kcl/astUpdated";
}
/// A notification that the Memory has changed.
#[derive(Debug)]
pub enum MemoryUpdated {}
impl Notification for MemoryUpdated {
type Params = crate::executor::ProgramMemory;
const METHOD: &'static str = "kcl/memoryUpdated";
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct UpdateUnitsParams {
pub text_document: TextDocumentIdentifier,
pub units: kittycad::types::UnitLength,
}

View File

@ -1,6 +1,10 @@
//! Functions for the `kcl` lsp server. //! Functions for the `kcl` lsp server.
use std::{collections::HashMap, io::Write, str::FromStr}; use std::{collections::HashMap, io::Write, str::FromStr, sync::Arc};
use tokio::sync::RwLock;
pub mod custom_notifications;
use anyhow::Result; use anyhow::Result;
#[cfg(feature = "cli")] #[cfg(feature = "cli")]
@ -29,7 +33,10 @@ use tower_lsp::{
Client, LanguageServer, Client, LanguageServer,
}; };
use crate::{ast::types::VariableKind, executor::SourceRange, lsp::backend::Backend as _, parser::PIPE_OPERATOR}; use crate::{
ast::types::VariableKind, errors::KclError, executor::SourceRange, lsp::backend::Backend as _,
parser::PIPE_OPERATOR,
};
/// A subcommand for running the server. /// A subcommand for running the server.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -63,6 +70,8 @@ pub struct Backend {
pub token_map: DashMap<String, Vec<crate::token::Token>>, pub token_map: DashMap<String, Vec<crate::token::Token>>,
/// AST maps. /// AST maps.
pub ast_map: DashMap<String, crate::ast::types::Program>, pub ast_map: DashMap<String, crate::ast::types::Program>,
/// Memory maps.
pub memory_map: DashMap<String, crate::executor::ProgramMemory>,
/// Current code. /// Current code.
pub current_code_map: DashMap<String, Vec<u8>>, pub current_code_map: DashMap<String, Vec<u8>>,
/// Diagnostics. /// Diagnostics.
@ -75,6 +84,8 @@ pub struct Backend {
pub zoo_client: kittycad::Client, pub zoo_client: kittycad::Client,
/// If we can send telemetry for this user. /// If we can send telemetry for this user.
pub can_send_telemetry: bool, pub can_send_telemetry: bool,
/// Optional executor context to use if we want to execute the code.
pub executor_ctx: Arc<RwLock<Option<crate::executor::ExecutorContext>>>,
} }
// Implement the shared backend trait for the language server. // Implement the shared backend trait for the language server.
@ -125,7 +136,7 @@ impl crate::lsp::backend::Backend for Backend {
self.semantic_tokens_map.clear(); self.semantic_tokens_map.clear();
} }
async fn on_change(&self, params: TextDocumentItem) { async fn inner_on_change(&self, params: TextDocumentItem) {
// We already updated the code map in the shared backend. // We already updated the code map in the shared backend.
// Lets update the tokens. // Lets update the tokens.
@ -188,35 +199,40 @@ impl crate::lsp::backend::Backend for Backend {
let result = parser.ast(); let result = parser.ast();
let ast = match result { let ast = match result {
Ok(ast) => ast, Ok(ast) => ast,
Err(e) => { Err(err) => {
let diagnostic = e.to_lsp_diagnostic(&params.text); self.add_to_diagnostics(&params, err).await;
// We got errors, update the diagnostics.
self.diagnostics_map.insert(
params.uri.to_string(),
DocumentDiagnosticReport::Full(RelatedFullDocumentDiagnosticReport {
related_documents: None,
full_document_diagnostic_report: FullDocumentDiagnosticReport {
result_id: None,
items: vec![diagnostic.clone()],
},
}),
);
// Publish the diagnostic.
// If the client supports it.
self.client
.publish_diagnostics(params.uri, vec![diagnostic], None)
.await;
return; return;
} }
}; };
// Update the symbols map. // Check if the ast changed.
self.symbols_map let ast_changed = match self.ast_map.get(&params.uri.to_string()) {
.insert(params.uri.to_string(), ast.get_lsp_symbols(&params.text)); Some(old_ast) => {
// Check if the ast changed.
*old_ast.value() != ast
}
None => true,
};
// If the ast changed update the map and symbols and execute if we need to.
if ast_changed {
// Update the symbols map.
self.symbols_map
.insert(params.uri.to_string(), ast.get_lsp_symbols(&params.text));
self.ast_map.insert(params.uri.to_string(), ast.clone());
// Send the notification to the client that the ast was updated.
self.client
.send_notification::<custom_notifications::AstUpdated>(ast.clone())
.await;
// Execute the code if we have an executor context.
// This function automatically executes if we should & updates the diagnostics if we got
// errors.
self.execute(&params, ast).await;
}
self.ast_map.insert(params.uri.to_string(), ast);
// Lets update the diagnostics, since we got no errors. // Lets update the diagnostics, since we got no errors.
self.diagnostics_map.insert( self.diagnostics_map.insert(
params.uri.to_string(), params.uri.to_string(),
@ -236,6 +252,50 @@ impl crate::lsp::backend::Backend for Backend {
} }
impl Backend { impl Backend {
async fn add_to_diagnostics(&self, params: &TextDocumentItem, err: KclError) {
let diagnostic = err.to_lsp_diagnostic(&params.text);
// We got errors, update the diagnostics.
self.diagnostics_map.insert(
params.uri.to_string(),
DocumentDiagnosticReport::Full(RelatedFullDocumentDiagnosticReport {
related_documents: None,
full_document_diagnostic_report: FullDocumentDiagnosticReport {
result_id: None,
items: vec![diagnostic.clone()],
},
}),
);
// Publish the diagnostic.
// If the client supports it.
self.client
.publish_diagnostics(params.uri.clone(), vec![diagnostic], None)
.await;
}
async fn execute(&self, params: &TextDocumentItem, ast: crate::ast::types::Program) {
// Execute the code if we have an executor context.
let Some(executor_ctx) = self.executor_ctx.read().await.clone() else {
return;
};
let memory = match executor_ctx.run(ast, None).await {
Ok(memory) => memory,
Err(err) => {
self.add_to_diagnostics(params, err).await;
return;
}
};
drop(executor_ctx); // Drop the lock here.
self.memory_map.insert(params.uri.to_string(), memory.clone());
// Send the notification to the client that the memory was updated.
self.client
.send_notification::<custom_notifications::MemoryUpdated>(memory)
.await;
}
fn get_semantic_token_type_index(&self, token_type: SemanticTokenType) -> Option<usize> { fn get_semantic_token_type_index(&self, token_type: SemanticTokenType) -> Option<usize> {
self.token_types.iter().position(|x| *x == token_type) self.token_types.iter().position(|x| *x == token_type)
} }
@ -368,6 +428,51 @@ impl Backend {
Ok(()) Ok(())
} }
pub async fn update_units(&self, params: custom_notifications::UpdateUnitsParams) {
{
let Some(mut executor_ctx) = self.executor_ctx.read().await.clone() else {
self.client
.log_message(MessageType::ERROR, "no executor context set to update units for")
.await;
return;
};
self.client
.log_message(MessageType::INFO, format!("update units: {:?}", params))
.await;
// Set the engine units.
executor_ctx.update_units(params.units);
// Update the locked executor context.
*self.executor_ctx.write().await = Some(executor_ctx.clone());
}
// Lock is dropped here since nested.
// This is IMPORTANT.
let filename = params.text_document.uri.to_string();
// Get the current code.
let Some(current_code) = self.current_code_map.get(&filename) else {
return;
};
let Ok(current_code) = std::str::from_utf8(&current_code) else {
return;
};
// Get the current ast.
let Some(ast) = self.ast_map.get(&filename) else {
return;
};
let new_params = TextDocumentItem {
uri: params.text_document.uri,
text: std::mem::take(&mut current_code.to_string()),
version: Default::default(),
language_id: Default::default(),
};
self.execute(&new_params, ast.value().clone()).await;
}
} }
#[tower_lsp::async_trait] #[tower_lsp::async_trait]

View File

@ -7,21 +7,57 @@ use anyhow::Result;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use tower_lsp::LanguageServer; use tower_lsp::LanguageServer;
fn new_zoo_client() -> kittycad::Client {
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
let http_client = reqwest::Client::builder()
.user_agent(user_agent)
// For file conversions we need this to be long.
.timeout(std::time::Duration::from_secs(600))
.connect_timeout(std::time::Duration::from_secs(60));
let ws_client = reqwest::Client::builder()
.user_agent(user_agent)
// For file conversions we need this to be long.
.timeout(std::time::Duration::from_secs(600))
.connect_timeout(std::time::Duration::from_secs(60))
.connection_verbose(true)
.tcp_keepalive(std::time::Duration::from_secs(600))
.http1_only();
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
// Create the client.
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
// Set a local engine address if it's set.
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
client.set_base_url(addr);
}
client
}
// Create a fake kcl lsp server for testing. // Create a fake kcl lsp server for testing.
fn kcl_lsp_server() -> Result<crate::lsp::kcl::Backend> { async fn kcl_lsp_server(execute: bool) -> Result<crate::lsp::kcl::Backend> {
let stdlib = crate::std::StdLib::new(); let stdlib = crate::std::StdLib::new();
let stdlib_completions = crate::lsp::kcl::get_completions_from_stdlib(&stdlib)?; let stdlib_completions = crate::lsp::kcl::get_completions_from_stdlib(&stdlib)?;
let stdlib_signatures = crate::lsp::kcl::get_signatures_from_stdlib(&stdlib)?; let stdlib_signatures = crate::lsp::kcl::get_signatures_from_stdlib(&stdlib)?;
// We can unwrap here because we know the tokeniser is valid, since // We can unwrap here because we know the tokeniser is valid, since
// we have a test for it. // we have a test for it.
let token_types = crate::token::TokenType::all_semantic_token_types().unwrap(); let token_types = crate::token::TokenType::all_semantic_token_types()?;
// We don't actually need to authenticate to the backend for this test. let zoo_client = new_zoo_client();
let mut zoo_client = kittycad::Client::new("");
zoo_client.set_base_url("https://api.dev.zoo.dev"); let executor_ctx = if execute {
let ws = zoo_client
.modeling()
.commands_ws(None, None, None, None, None, None, Some(false))
.await?;
Some(crate::executor::ExecutorContext::new(ws, kittycad::types::UnitLength::Mm).await?)
} else {
None
};
// Create the backend. // Create the backend.
let (service, _) = tower_lsp::LspService::new(|client| crate::lsp::kcl::Backend { let (service, _) = tower_lsp::LspService::build(|client| crate::lsp::kcl::Backend {
client, client,
fs: crate::fs::FileManager::new(), fs: crate::fs::FileManager::new(),
workspace_folders: Default::default(), workspace_folders: Default::default(),
@ -30,13 +66,18 @@ fn kcl_lsp_server() -> Result<crate::lsp::kcl::Backend> {
token_types, token_types,
token_map: Default::default(), token_map: Default::default(),
ast_map: Default::default(), ast_map: Default::default(),
memory_map: Default::default(),
current_code_map: Default::default(), current_code_map: Default::default(),
diagnostics_map: Default::default(), diagnostics_map: Default::default(),
symbols_map: Default::default(), symbols_map: Default::default(),
semantic_tokens_map: Default::default(), semantic_tokens_map: Default::default(),
zoo_client, zoo_client,
can_send_telemetry: true, can_send_telemetry: true,
}); executor_ctx: Arc::new(tokio::sync::RwLock::new(executor_ctx)),
})
.custom_method("kcl/updateUnits", crate::lsp::kcl::Backend::update_units)
.finish();
let server = service.inner(); let server = service.inner();
Ok(server.clone()) Ok(server.clone())
@ -63,9 +104,9 @@ fn copilot_lsp_server() -> Result<crate::lsp::copilot::Backend> {
Ok(server.clone()) Ok(server.clone())
} }
#[tokio::test] #[tokio::test(flavor = "multi_thread")]
async fn test_updating_kcl_lsp_files() { async fn test_updating_kcl_lsp_files() {
let server = kcl_lsp_server().unwrap(); let server = kcl_lsp_server(false).await.unwrap();
assert_eq!(server.current_code_map.len(), 0); assert_eq!(server.current_code_map.len(), 0);
@ -96,7 +137,7 @@ async fn test_updating_kcl_lsp_files() {
} }
); );
assert_eq!(server.current_code_map.len(), 8); assert_eq!(server.current_code_map.len(), 9);
// Run open file. // Run open file.
server server
@ -111,7 +152,7 @@ async fn test_updating_kcl_lsp_files() {
.await; .await;
// Check the code map. // Check the code map.
assert_eq!(server.current_code_map.len(), 9); assert_eq!(server.current_code_map.len(), 10);
assert_eq!( assert_eq!(
server.current_code_map.get("file:///test.kcl").unwrap().value(), server.current_code_map.get("file:///test.kcl").unwrap().value(),
"test".as_bytes() "test".as_bytes()
@ -127,7 +168,7 @@ async fn test_updating_kcl_lsp_files() {
.await; .await;
// Check the code map. // Check the code map.
assert_eq!(server.current_code_map.len(), 9); assert_eq!(server.current_code_map.len(), 10);
assert_eq!( assert_eq!(
server.current_code_map.get("file:///test.kcl").unwrap().value(), server.current_code_map.get("file:///test.kcl").unwrap().value(),
"test".as_bytes() "test".as_bytes()
@ -146,7 +187,7 @@ async fn test_updating_kcl_lsp_files() {
.await; .await;
// Check the code map. // Check the code map.
assert_eq!(server.current_code_map.len(), 10); assert_eq!(server.current_code_map.len(), 11);
assert_eq!( assert_eq!(
server.current_code_map.get("file:///test.kcl").unwrap().value(), server.current_code_map.get("file:///test.kcl").unwrap().value(),
"test".as_bytes() "test".as_bytes()
@ -172,7 +213,7 @@ async fn test_updating_kcl_lsp_files() {
.await; .await;
// Check the code map. // Check the code map.
assert_eq!(server.current_code_map.len(), 10); assert_eq!(server.current_code_map.len(), 11);
assert_eq!( assert_eq!(
server.current_code_map.get("file:///test.kcl").unwrap().value(), server.current_code_map.get("file:///test.kcl").unwrap().value(),
"test".as_bytes() "test".as_bytes()
@ -193,7 +234,7 @@ async fn test_updating_kcl_lsp_files() {
.await; .await;
// Check the code map. // Check the code map.
assert_eq!(server.current_code_map.len(), 10); assert_eq!(server.current_code_map.len(), 11);
assert_eq!( assert_eq!(
server.current_code_map.get("file:///test.kcl").unwrap().value(), server.current_code_map.get("file:///test.kcl").unwrap().value(),
"test".as_bytes() "test".as_bytes()
@ -213,7 +254,7 @@ async fn test_updating_kcl_lsp_files() {
.await; .await;
// Check the code map. // Check the code map.
assert_eq!(server.current_code_map.len(), 11); assert_eq!(server.current_code_map.len(), 12);
assert_eq!( assert_eq!(
server.current_code_map.get("file:///test.kcl").unwrap().value(), server.current_code_map.get("file:///test.kcl").unwrap().value(),
"test".as_bytes() "test".as_bytes()
@ -237,7 +278,7 @@ async fn test_updating_kcl_lsp_files() {
.await; .await;
// Check the code map. // Check the code map.
assert_eq!(server.current_code_map.len(), 10); assert_eq!(server.current_code_map.len(), 11);
assert_eq!( assert_eq!(
server.current_code_map.get("file:///test.kcl").unwrap().value(), server.current_code_map.get("file:///test.kcl").unwrap().value(),
"test".as_bytes() "test".as_bytes()
@ -271,7 +312,7 @@ async fn test_updating_kcl_lsp_files() {
); );
// Check the code map. // Check the code map.
assert_eq!(server.current_code_map.len(), 10); assert_eq!(server.current_code_map.len(), 11);
assert_eq!( assert_eq!(
server.current_code_map.get("file:///test.kcl").unwrap().value(), server.current_code_map.get("file:///test.kcl").unwrap().value(),
"test".as_bytes() "test".as_bytes()
@ -307,7 +348,7 @@ async fn test_updating_kcl_lsp_files() {
name: "my-project2".to_string(), name: "my-project2".to_string(),
} }
); );
assert_eq!(server.current_code_map.len(), 8); assert_eq!(server.current_code_map.len(), 9);
// Just make sure that one of the current files read from disk is accurate. // Just make sure that one of the current files read from disk is accurate.
assert_eq!( assert_eq!(
server server
@ -319,7 +360,7 @@ async fn test_updating_kcl_lsp_files() {
); );
} }
#[tokio::test] #[tokio::test(flavor = "multi_thread")]
async fn test_updating_copilot_lsp_files() { async fn test_updating_copilot_lsp_files() {
let server = copilot_lsp_server().unwrap(); let server = copilot_lsp_server().unwrap();
@ -352,7 +393,7 @@ async fn test_updating_copilot_lsp_files() {
} }
); );
assert_eq!(server.current_code_map.len(), 8); assert_eq!(server.current_code_map.len(), 9);
// Run open file. // Run open file.
server server
@ -367,7 +408,7 @@ async fn test_updating_copilot_lsp_files() {
.await; .await;
// Check the code map. // Check the code map.
assert_eq!(server.current_code_map.len(), 9); assert_eq!(server.current_code_map.len(), 10);
assert_eq!( assert_eq!(
server.current_code_map.get("file:///test.kcl").unwrap().value(), server.current_code_map.get("file:///test.kcl").unwrap().value(),
"test".as_bytes() "test".as_bytes()
@ -383,7 +424,7 @@ async fn test_updating_copilot_lsp_files() {
.await; .await;
// Check the code map. // Check the code map.
assert_eq!(server.current_code_map.len(), 9); assert_eq!(server.current_code_map.len(), 10);
assert_eq!( assert_eq!(
server.current_code_map.get("file:///test.kcl").unwrap().value(), server.current_code_map.get("file:///test.kcl").unwrap().value(),
"test".as_bytes() "test".as_bytes()
@ -402,7 +443,7 @@ async fn test_updating_copilot_lsp_files() {
.await; .await;
// Check the code map. // Check the code map.
assert_eq!(server.current_code_map.len(), 10); assert_eq!(server.current_code_map.len(), 11);
assert_eq!( assert_eq!(
server.current_code_map.get("file:///test.kcl").unwrap().value(), server.current_code_map.get("file:///test.kcl").unwrap().value(),
"test".as_bytes() "test".as_bytes()
@ -428,7 +469,7 @@ async fn test_updating_copilot_lsp_files() {
.await; .await;
// Check the code map. // Check the code map.
assert_eq!(server.current_code_map.len(), 10); assert_eq!(server.current_code_map.len(), 11);
assert_eq!( assert_eq!(
server.current_code_map.get("file:///test.kcl").unwrap().value(), server.current_code_map.get("file:///test.kcl").unwrap().value(),
"test".as_bytes() "test".as_bytes()
@ -449,7 +490,7 @@ async fn test_updating_copilot_lsp_files() {
.await; .await;
// Check the code map. // Check the code map.
assert_eq!(server.current_code_map.len(), 10); assert_eq!(server.current_code_map.len(), 11);
assert_eq!( assert_eq!(
server.current_code_map.get("file:///test.kcl").unwrap().value(), server.current_code_map.get("file:///test.kcl").unwrap().value(),
"test".as_bytes() "test".as_bytes()
@ -469,7 +510,7 @@ async fn test_updating_copilot_lsp_files() {
.await; .await;
// Check the code map. // Check the code map.
assert_eq!(server.current_code_map.len(), 11); assert_eq!(server.current_code_map.len(), 12);
assert_eq!( assert_eq!(
server.current_code_map.get("file:///test.kcl").unwrap().value(), server.current_code_map.get("file:///test.kcl").unwrap().value(),
"test".as_bytes() "test".as_bytes()
@ -493,7 +534,7 @@ async fn test_updating_copilot_lsp_files() {
.await; .await;
// Check the code map. // Check the code map.
assert_eq!(server.current_code_map.len(), 10); assert_eq!(server.current_code_map.len(), 11);
assert_eq!( assert_eq!(
server.current_code_map.get("file:///test.kcl").unwrap().value(), server.current_code_map.get("file:///test.kcl").unwrap().value(),
"test".as_bytes() "test".as_bytes()
@ -527,7 +568,7 @@ async fn test_updating_copilot_lsp_files() {
); );
// Check the code map. // Check the code map.
assert_eq!(server.current_code_map.len(), 10); assert_eq!(server.current_code_map.len(), 11);
assert_eq!( assert_eq!(
server.current_code_map.get("file:///test.kcl").unwrap().value(), server.current_code_map.get("file:///test.kcl").unwrap().value(),
"test".as_bytes() "test".as_bytes()
@ -558,7 +599,7 @@ async fn test_updating_copilot_lsp_files() {
); );
// Check the code map. // Check the code map.
assert_eq!(server.current_code_map.len(), 10); assert_eq!(server.current_code_map.len(), 11);
assert_eq!( assert_eq!(
server.current_code_map.get("file:///test.kcl").unwrap().value(), server.current_code_map.get("file:///test.kcl").unwrap().value(),
"test".as_bytes() "test".as_bytes()
@ -594,12 +635,12 @@ async fn test_updating_copilot_lsp_files() {
name: "my-project2".to_string(), name: "my-project2".to_string(),
} }
); );
assert_eq!(server.current_code_map.len(), 8); assert_eq!(server.current_code_map.len(), 9);
} }
#[tokio::test] #[tokio::test(flavor = "multi_thread")]
async fn test_kcl_lsp_create_zip() { async fn test_kcl_lsp_create_zip() {
let server = kcl_lsp_server().unwrap(); let server = kcl_lsp_server(false).await.unwrap();
assert_eq!(server.current_code_map.len(), 0); assert_eq!(server.current_code_map.len(), 0);
@ -630,7 +671,7 @@ async fn test_kcl_lsp_create_zip() {
} }
); );
assert_eq!(server.current_code_map.len(), 8); assert_eq!(server.current_code_map.len(), 9);
// Run open file. // Run open file.
server server
@ -645,7 +686,7 @@ async fn test_kcl_lsp_create_zip() {
.await; .await;
// Check the code map. // Check the code map.
assert_eq!(server.current_code_map.len(), 9); assert_eq!(server.current_code_map.len(), 10);
assert_eq!( assert_eq!(
server.current_code_map.get("file:///test.kcl").unwrap().value(), server.current_code_map.get("file:///test.kcl").unwrap().value(),
"test".as_bytes() "test".as_bytes()
@ -669,15 +710,15 @@ async fn test_kcl_lsp_create_zip() {
files.insert(file.name().to_string(), file.size()); files.insert(file.name().to_string(), file.size());
} }
assert_eq!(files.len(), 9); assert_eq!(files.len(), 10);
let util_path = format!("{}/util.rs", string_path).replace("file://", ""); let util_path = format!("{}/util.rs", string_path).replace("file://", "");
assert!(files.get(&util_path).is_some()); assert!(files.get(&util_path).is_some());
assert_eq!(files.get("/test.kcl"), Some(&4)); assert_eq!(files.get("/test.kcl"), Some(&4));
} }
#[tokio::test] #[tokio::test(flavor = "multi_thread")]
async fn test_kcl_lsp_completions() { async fn test_kcl_lsp_completions() {
let server = kcl_lsp_server().unwrap(); let server = kcl_lsp_server(false).await.unwrap();
// Send open file. // Send open file.
server server
@ -718,9 +759,9 @@ st"#
} }
} }
#[tokio::test] #[tokio::test(flavor = "multi_thread")]
async fn test_kcl_lsp_on_hover() { async fn test_kcl_lsp_on_hover() {
let server = kcl_lsp_server().unwrap(); let server = kcl_lsp_server(false).await.unwrap();
// Send open file. // Send open file.
server server
@ -762,9 +803,9 @@ async fn test_kcl_lsp_on_hover() {
} }
} }
#[tokio::test] #[tokio::test(flavor = "multi_thread")]
async fn test_kcl_lsp_signature_help() { async fn test_kcl_lsp_signature_help() {
let server = kcl_lsp_server().unwrap(); let server = kcl_lsp_server(false).await.unwrap();
// Send open file. // Send open file.
server server
@ -810,9 +851,9 @@ async fn test_kcl_lsp_signature_help() {
} }
} }
#[tokio::test] #[tokio::test(flavor = "multi_thread")]
async fn test_kcl_lsp_semantic_tokens() { async fn test_kcl_lsp_semantic_tokens() {
let server = kcl_lsp_server().unwrap(); let server = kcl_lsp_server(false).await.unwrap();
// Send open file. // Send open file.
server server
@ -852,9 +893,9 @@ async fn test_kcl_lsp_semantic_tokens() {
} }
} }
#[tokio::test] #[tokio::test(flavor = "multi_thread")]
async fn test_kcl_lsp_document_symbol() { async fn test_kcl_lsp_document_symbol() {
let server = kcl_lsp_server().unwrap(); let server = kcl_lsp_server(false).await.unwrap();
// Send open file. // Send open file.
server server
@ -892,9 +933,9 @@ startSketchOn('XY')"#
} }
} }
#[tokio::test] #[tokio::test(flavor = "multi_thread")]
async fn test_kcl_lsp_formatting() { async fn test_kcl_lsp_formatting() {
let server = kcl_lsp_server().unwrap(); let server = kcl_lsp_server(false).await.unwrap();
// Send open file. // Send open file.
server server
@ -939,9 +980,9 @@ async fn test_kcl_lsp_formatting() {
); );
} }
#[tokio::test] #[tokio::test(flavor = "multi_thread")]
async fn test_kcl_lsp_rename() { async fn test_kcl_lsp_rename() {
let server = kcl_lsp_server().unwrap(); let server = kcl_lsp_server(false).await.unwrap();
// Send open file. // Send open file.
server server
@ -986,9 +1027,9 @@ async fn test_kcl_lsp_rename() {
); );
} }
#[tokio::test] #[tokio::test(flavor = "multi_thread")]
async fn test_kcl_lsp_diagnostic_no_errors() { async fn test_kcl_lsp_diagnostic_no_errors() {
let server = kcl_lsp_server().unwrap(); let server = kcl_lsp_server(false).await.unwrap();
// Send open file. // Send open file.
server server
@ -1028,9 +1069,9 @@ async fn test_kcl_lsp_diagnostic_no_errors() {
} }
} }
#[tokio::test] #[tokio::test(flavor = "multi_thread")]
async fn test_kcl_lsp_diagnostic_has_errors() { async fn test_kcl_lsp_diagnostic_has_errors() {
let server = kcl_lsp_server().unwrap(); let server = kcl_lsp_server(false).await.unwrap();
// Send open file. // Send open file.
server server
@ -1074,7 +1115,7 @@ async fn test_kcl_lsp_diagnostic_has_errors() {
} }
} }
#[tokio::test] #[tokio::test(flavor = "multi_thread")]
async fn test_copilot_lsp_set_editor_info() { async fn test_copilot_lsp_set_editor_info() {
let server = copilot_lsp_server().unwrap(); let server = copilot_lsp_server().unwrap();
@ -1104,7 +1145,7 @@ async fn test_copilot_lsp_set_editor_info() {
assert_eq!(editor_info.editor_info.version, "1.0.0"); assert_eq!(editor_info.editor_info.version, "1.0.0");
} }
#[tokio::test] #[tokio::test(flavor = "multi_thread")]
#[ignore] // Ignore til hosted model is faster (@jessfraz working on). #[ignore] // Ignore til hosted model is faster (@jessfraz working on).
async fn test_copilot_lsp_completions_raw() { async fn test_copilot_lsp_completions_raw() {
let server = copilot_lsp_server().unwrap(); let server = copilot_lsp_server().unwrap();
@ -1158,7 +1199,7 @@ async fn test_copilot_lsp_completions_raw() {
assert_eq!(completions, completions_hit_cache); assert_eq!(completions, completions_hit_cache);
} }
#[tokio::test] #[tokio::test(flavor = "multi_thread")]
#[ignore] // Ignore til hosted model is faster (@jessfraz working on). #[ignore] // Ignore til hosted model is faster (@jessfraz working on).
async fn test_copilot_lsp_completions() { async fn test_copilot_lsp_completions() {
let server = copilot_lsp_server().unwrap(); let server = copilot_lsp_server().unwrap();
@ -1224,7 +1265,7 @@ async fn test_copilot_lsp_completions() {
.await; .await;
} }
#[tokio::test] #[tokio::test(flavor = "multi_thread")]
async fn test_copilot_on_save() { async fn test_copilot_on_save() {
let server = copilot_lsp_server().unwrap(); let server = copilot_lsp_server().unwrap();
@ -1246,9 +1287,9 @@ async fn test_copilot_on_save() {
); );
} }
#[tokio::test] #[tokio::test(flavor = "multi_thread")]
async fn test_kcl_on_save() { async fn test_kcl_on_save() {
let server = kcl_lsp_server().unwrap(); let server = kcl_lsp_server(false).await.unwrap();
// Send save file. // Send save file.
server server
@ -1268,7 +1309,7 @@ async fn test_kcl_on_save() {
); );
} }
#[tokio::test] #[tokio::test(flavor = "multi_thread")]
async fn test_copilot_rename_not_exists() { async fn test_copilot_rename_not_exists() {
let server = copilot_lsp_server().unwrap(); let server = copilot_lsp_server().unwrap();
@ -1290,7 +1331,7 @@ async fn test_copilot_rename_not_exists() {
); );
} }
#[tokio::test] #[tokio::test(flavor = "multi_thread")]
async fn test_lsp_initialized() { async fn test_lsp_initialized() {
let copilot_server = copilot_lsp_server().unwrap(); let copilot_server = copilot_lsp_server().unwrap();
@ -1309,7 +1350,7 @@ async fn test_lsp_initialized() {
assert_eq!(copilot_server.current_code_map.len(), 0); assert_eq!(copilot_server.current_code_map.len(), 0);
// Now do the same for kcl. // Now do the same for kcl.
let kcl_server = kcl_lsp_server().unwrap(); let kcl_server = kcl_lsp_server(false).await.unwrap();
// Send initialize request. // Send initialize request.
kcl_server kcl_server
@ -1327,3 +1368,200 @@ async fn test_lsp_initialized() {
copilot_server.shutdown().await.unwrap(); copilot_server.shutdown().await.unwrap();
kcl_server.shutdown().await.unwrap(); kcl_server.shutdown().await.unwrap();
} }
#[tokio::test(flavor = "multi_thread")]
async fn test_kcl_lsp_on_change_update_ast() {
let server = kcl_lsp_server(false).await.unwrap();
let same_text = r#"const thing = 1"#.to_string();
// Send open file.
server
.did_open(tower_lsp::lsp_types::DidOpenTextDocumentParams {
text_document: tower_lsp::lsp_types::TextDocumentItem {
uri: "file:///test.kcl".try_into().unwrap(),
language_id: "kcl".to_string(),
version: 1,
text: same_text.clone(),
},
})
.await;
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
// Send change file.
server
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
text_document: tower_lsp::lsp_types::VersionedTextDocumentIdentifier {
uri: "file:///test.kcl".try_into().unwrap(),
version: 1,
},
content_changes: vec![tower_lsp::lsp_types::TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: same_text.clone(),
}],
})
.await;
// Make sure the ast is the same.
assert_eq!(ast, server.ast_map.get("file:///test.kcl").unwrap().clone());
// Update the text.
let new_text = r#"const thing = 2"#.to_string();
// Send change file.
server
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
text_document: tower_lsp::lsp_types::VersionedTextDocumentIdentifier {
uri: "file:///test.kcl".try_into().unwrap(),
version: 2,
},
content_changes: vec![tower_lsp::lsp_types::TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: new_text.clone(),
}],
})
.await;
assert!(ast != server.ast_map.get("file:///test.kcl").unwrap().clone());
// Make sure we never updated the memory since we aren't running the engine.
assert!(server.memory_map.get("file:///test.kcl").is_none());
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_kcl_lsp_on_change_update_memory() {
let server = kcl_lsp_server(true).await.unwrap();
let same_text = r#"const thing = 1"#.to_string();
// Send open file.
server
.did_open(tower_lsp::lsp_types::DidOpenTextDocumentParams {
text_document: tower_lsp::lsp_types::TextDocumentItem {
uri: "file:///test.kcl".try_into().unwrap(),
language_id: "kcl".to_string(),
version: 1,
text: same_text.clone(),
},
})
.await;
// Get the memory.
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
// Send change file.
server
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
text_document: tower_lsp::lsp_types::VersionedTextDocumentIdentifier {
uri: "file:///test.kcl".try_into().unwrap(),
version: 1,
},
content_changes: vec![tower_lsp::lsp_types::TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: same_text.clone(),
}],
})
.await;
// Make sure the memory is the same.
assert_eq!(memory, server.memory_map.get("file:///test.kcl").unwrap().clone());
// Update the text.
let new_text = r#"const thing = 2"#.to_string();
// Send change file.
server
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
text_document: tower_lsp::lsp_types::VersionedTextDocumentIdentifier {
uri: "file:///test.kcl".try_into().unwrap(),
version: 2,
},
content_changes: vec![tower_lsp::lsp_types::TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: new_text.clone(),
}],
})
.await;
assert!(memory != server.memory_map.get("file:///test.kcl").unwrap().clone());
}
#[tokio::test(flavor = "multi_thread", worker_threads = 10)]
async fn serial_test_kcl_lsp_update_units() {
let server = kcl_lsp_server(true).await.unwrap();
let same_text = r#"fn cube = (pos, scale) => {
const sg = startSketchOn('XY')
|> startProfileAt(pos, %)
|> line([0, scale], %)
|> line([scale, 0], %)
|> line([0, -scale], %)
return sg
}
const part001 = cube([0,0], 20)
|> close(%)
|> extrude(20, %)"#
.to_string();
// Send open file.
server
.did_open(tower_lsp::lsp_types::DidOpenTextDocumentParams {
text_document: tower_lsp::lsp_types::TextDocumentItem {
uri: "file:///test.kcl".try_into().unwrap(),
language_id: "kcl".to_string(),
version: 1,
text: same_text.clone(),
},
})
.await;
// Get the memory.
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
// Send change file.
server
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
text_document: tower_lsp::lsp_types::VersionedTextDocumentIdentifier {
uri: "file:///test.kcl".try_into().unwrap(),
version: 1,
},
content_changes: vec![tower_lsp::lsp_types::TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: same_text.clone(),
}],
})
.await;
// Make sure the memory is the same.
assert_eq!(memory, server.memory_map.get("file:///test.kcl").unwrap().clone());
let units = server.executor_ctx.read().await.clone().unwrap().units;
assert_eq!(units, kittycad::types::UnitLength::Mm);
// Update the units.
server
.update_units(crate::lsp::kcl::custom_notifications::UpdateUnitsParams {
text_document: tower_lsp::lsp_types::TextDocumentIdentifier {
uri: "file:///test.kcl".try_into().unwrap(),
},
units: kittycad::types::UnitLength::M,
})
.await;
println!("updated units");
let units = server.executor_ctx.read().await.clone().unwrap().units;
assert_eq!(units, kittycad::types::UnitLength::M);
println!("units are correct");
// Make sure it forced a memory update.
assert!(memory != server.memory_map.get("file:///test.kcl").unwrap().clone());
}

View File

@ -33,7 +33,8 @@ use crate::{
std::{kcl_stdlib::KclStdLibFn, sketch::SketchOnFaceTag}, std::{kcl_stdlib::KclStdLibFn, sketch::SketchOnFaceTag},
}; };
pub type StdFn = fn(Args) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<MemoryItem, KclError>>>>; pub type StdFn = fn(Args) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<MemoryItem, KclError>> + Send>>;
pub type FnMap = HashMap<String, StdFn>; pub type FnMap = HashMap<String, StdFn>;
lazy_static! { lazy_static! {

View File

@ -26,7 +26,7 @@ pub async fn execute_wasm(
use kcl_lib::executor::ExecutorContext; use kcl_lib::executor::ExecutorContext;
let program: kcl_lib::ast::types::Program = serde_json::from_str(program_str).map_err(|e| e.to_string())?; let program: kcl_lib::ast::types::Program = serde_json::from_str(program_str).map_err(|e| e.to_string())?;
let mut mem: kcl_lib::executor::ProgramMemory = serde_json::from_str(memory_str).map_err(|e| e.to_string())?; let memory: kcl_lib::executor::ProgramMemory = serde_json::from_str(memory_str).map_err(|e| e.to_string())?;
let units = kittycad::types::UnitLength::from_str(units).map_err(|e| e.to_string())?; let units = kittycad::types::UnitLength::from_str(units).map_err(|e| e.to_string())?;
let engine = kcl_lib::engine::conn_wasm::EngineConnection::new(engine_manager) let engine = kcl_lib::engine::conn_wasm::EngineConnection::new(engine_manager)
@ -41,9 +41,7 @@ pub async fn execute_wasm(
is_mock, is_mock,
}; };
let memory = kcl_lib::executor::execute_outer(program, &mut mem, kcl_lib::executor::BodyType::Root, &ctx) let memory = ctx.run(program, Some(memory)).await.map_err(String::from)?;
.await
.map_err(String::from)?;
// The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the // The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the
// gloo-serialize crate instead. // gloo-serialize crate instead.
JsValue::from_serde(&memory).map_err(|e| e.to_string()) JsValue::from_serde(&memory).map_err(|e| e.to_string())
@ -210,7 +208,7 @@ pub async fn kcl_lsp_run(config: ServerConfig, token: String, is_dev: bool) -> R
} }
}; };
let (service, socket) = LspService::new(|client| kcl_lib::lsp::kcl::Backend { let (service, socket) = LspService::build(|client| kcl_lib::lsp::kcl::Backend {
client, client,
fs: kcl_lib::fs::FileManager::new(fs), fs: kcl_lib::fs::FileManager::new(fs),
workspace_folders: Default::default(), workspace_folders: Default::default(),
@ -219,13 +217,17 @@ pub async fn kcl_lsp_run(config: ServerConfig, token: String, is_dev: bool) -> R
token_types, token_types,
token_map: Default::default(), token_map: Default::default(),
ast_map: Default::default(), ast_map: Default::default(),
memory_map: Default::default(),
current_code_map: Default::default(), current_code_map: Default::default(),
diagnostics_map: Default::default(), diagnostics_map: Default::default(),
symbols_map: Default::default(), symbols_map: Default::default(),
semantic_tokens_map: Default::default(), semantic_tokens_map: Default::default(),
zoo_client, zoo_client,
can_send_telemetry: privacy_settings.can_train_on_data, can_send_telemetry: privacy_settings.can_train_on_data,
}); executor_ctx: Default::default(),
})
.custom_method("kcl/updateUnits", kcl_lib::lsp::kcl::Backend::update_units)
.finish();
let input = wasm_bindgen_futures::stream::JsStream::from(into_server); let input = wasm_bindgen_futures::stream::JsStream::from(into_server);
let input = input let input = input
@ -279,13 +281,19 @@ pub async fn copilot_lsp_run(config: ServerConfig, token: String, is_dev: bool)
telemetry: Default::default(), telemetry: Default::default(),
zoo_client, zoo_client,
}) })
.custom_method("setEditorInfo", kcl_lib::lsp::copilot::Backend::set_editor_info) .custom_method("copilot/setEditorInfo", kcl_lib::lsp::copilot::Backend::set_editor_info)
.custom_method( .custom_method(
"getCompletions", "copilot/getCompletions",
kcl_lib::lsp::copilot::Backend::get_completions_cycling, kcl_lib::lsp::copilot::Backend::get_completions_cycling,
) )
.custom_method("notifyAccepted", kcl_lib::lsp::copilot::Backend::accept_completion) .custom_method(
.custom_method("notifyRejected", kcl_lib::lsp::copilot::Backend::reject_completions) "copilot/notifyAccepted",
kcl_lib::lsp::copilot::Backend::accept_completion,
)
.custom_method(
"copilot/notifyRejected",
kcl_lib::lsp::copilot::Backend::reject_completions,
)
.finish(); .finish();
let input = wasm_bindgen_futures::stream::JsStream::from(into_server); let input = wasm_bindgen_futures::stream::JsStream::from(into_server);

View File

@ -38,10 +38,9 @@ async fn execute_and_snapshot(code: &str, units: kittycad::types::UnitLength) ->
let tokens = kcl_lib::token::lexer(code); let tokens = kcl_lib::token::lexer(code);
let parser = kcl_lib::parser::Parser::new(tokens); let parser = kcl_lib::parser::Parser::new(tokens);
let program = parser.ast()?; let program = parser.ast()?;
let mut mem: kcl_lib::executor::ProgramMemory = Default::default();
let ctx = kcl_lib::executor::ExecutorContext::new(ws, units.clone()).await?; let ctx = kcl_lib::executor::ExecutorContext::new(ws, units.clone()).await?;
let _ = kcl_lib::executor::execute_outer(program, &mut mem, kcl_lib::executor::BodyType::Root, &ctx).await?; let _ = ctx.run(program, None).await?;
let (x, y) = kcl_lib::std::utils::get_camera_zoom_magnitude_per_unit_length(units); let (x, y) = kcl_lib::std::utils::get_camera_zoom_magnitude_per_unit_length(units);

View File

@ -39,10 +39,8 @@ async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, uuid
let tokens = kcl_lib::token::lexer(code); let tokens = kcl_lib::token::lexer(code);
let parser = kcl_lib::parser::Parser::new(tokens); let parser = kcl_lib::parser::Parser::new(tokens);
let program = parser.ast()?; let program = parser.ast()?;
let mut mem: kcl_lib::executor::ProgramMemory = Default::default();
let ctx = kcl_lib::executor::ExecutorContext::new(ws, kittycad::types::UnitLength::Mm).await?; let ctx = kcl_lib::executor::ExecutorContext::new(ws, kittycad::types::UnitLength::Mm).await?;
let memory = let memory = ctx.run(program.clone(), None).await?;
kcl_lib::executor::execute_outer(program.clone(), &mut mem, kcl_lib::executor::BodyType::Root, &ctx).await?;
// We need to get the sketch ID. // We need to get the sketch ID.
// Get the sketch group ID from memory. // Get the sketch group ID from memory.