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>
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
@ -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) {
|
||||||
|
2
src/wasm-lib/Cargo.lock
generated
@ -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",
|
||||||
|
@ -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"
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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))
|
||||||
|
@ -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))
|
||||||
|
@ -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))
|
||||||
|
@ -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))
|
||||||
|
@ -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))
|
||||||
|
@ -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))
|
||||||
|
@ -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))
|
||||||
|
@ -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))
|
||||||
|
@ -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))
|
||||||
|
@ -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))
|
||||||
|
@ -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))
|
||||||
|
@ -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"
|
||||||
|
@ -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],
|
||||||
|
@ -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.
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
29
src/wasm-lib/kcl/src/lsp/kcl/custom_notifications.rs
Normal 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,
|
||||||
|
}
|
@ -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(¶ms.text);
|
self.add_to_diagnostics(¶ms, 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(¶ms.uri.to_string()) {
|
||||||
.insert(params.uri.to_string(), ast.get_lsp_symbols(¶ms.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(¶ms.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(¶ms, 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(¶ms.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(¤t_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]
|
||||||
|
@ -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());
|
||||||
|
}
|
||||||
|
@ -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! {
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|