Cleanup rust/ts interface a but more w new rustContext (#5848)
* do the rust side Signed-off-by: Jess Frazelle <github@jessfraz.com> * cleanup ts side Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * typo Signed-off-by: Jess Frazelle <github@jessfraz.com> --------- Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
@ -430,21 +430,14 @@ impl ExecutorContext {
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub async fn new_mock(
|
||||
fs_manager: crate::fs::wasm::FileSystemManager,
|
||||
settings: ExecutorSettings,
|
||||
) -> Result<Self, String> {
|
||||
Ok(ExecutorContext {
|
||||
engine: Arc::new(Box::new(
|
||||
crate::engine::conn_mock::EngineConnection::new()
|
||||
.await
|
||||
.map_err(|e| format!("{:?}", e))?,
|
||||
)),
|
||||
fs: Arc::new(FileManager::new(fs_manager)),
|
||||
pub fn new_mock(engine: Arc<Box<dyn EngineManager>>, fs: Arc<FileManager>, settings: ExecutorSettings) -> Self {
|
||||
ExecutorContext {
|
||||
engine,
|
||||
fs,
|
||||
stdlib: Arc::new(StdLib::new()),
|
||||
settings,
|
||||
context_type: ContextType::Mock,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
|
@ -114,6 +114,10 @@ pub mod wasm_engine {
|
||||
};
|
||||
}
|
||||
|
||||
pub mod mock_engine {
|
||||
pub use crate::engine::conn_mock::EngineConnection;
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod native_engine {
|
||||
pub use crate::engine::conn::EngineConnection;
|
||||
|
@ -10,6 +10,7 @@ use wasm_bindgen::prelude::*;
|
||||
pub struct Context {
|
||||
engine: Arc<Box<dyn EngineManager>>,
|
||||
fs: Arc<FileManager>,
|
||||
mock_engine: Arc<Box<dyn EngineManager>>,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
@ -28,16 +29,34 @@ impl Context {
|
||||
.map_err(|e| format!("{:?}", e))?,
|
||||
)),
|
||||
fs: Arc::new(FileManager::new(fs_manager)),
|
||||
mock_engine: Arc::new(Box::new(
|
||||
kcl_lib::mock_engine::EngineConnection::new()
|
||||
.await
|
||||
.map_err(|e| format!("{:?}", e))?,
|
||||
)),
|
||||
})
|
||||
}
|
||||
|
||||
fn create_executor_ctx(&self, settings: &str, path: Option<String>) -> Result<kcl_lib::ExecutorContext, String> {
|
||||
fn create_executor_ctx(
|
||||
&self,
|
||||
settings: &str,
|
||||
path: Option<String>,
|
||||
is_mock: bool,
|
||||
) -> Result<kcl_lib::ExecutorContext, String> {
|
||||
let config: kcl_lib::Configuration = serde_json::from_str(settings).map_err(|e| e.to_string())?;
|
||||
let mut settings: kcl_lib::ExecutorSettings = config.into();
|
||||
if let Some(path) = path {
|
||||
settings.with_current_file(std::path::PathBuf::from(path));
|
||||
}
|
||||
|
||||
if is_mock {
|
||||
return Ok(kcl_lib::ExecutorContext::new_mock(
|
||||
self.mock_engine.clone(),
|
||||
self.fs.clone(),
|
||||
settings.into(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(kcl_lib::ExecutorContext::new(
|
||||
self.engine.clone(),
|
||||
self.fs.clone(),
|
||||
@ -57,7 +76,7 @@ impl Context {
|
||||
|
||||
let program: Program = serde_json::from_str(program_ast_json).map_err(|e| e.to_string())?;
|
||||
|
||||
let ctx = self.create_executor_ctx(settings, path)?;
|
||||
let ctx = self.create_executor_ctx(settings, path, false)?;
|
||||
match ctx.run_with_caching(program).await {
|
||||
// The serde-wasm-bindgen does not work here because of weird HashMap issues.
|
||||
// DO NOT USE serde_wasm_bindgen::to_value it will break the frontend.
|
||||
@ -65,4 +84,26 @@ impl Context {
|
||||
Err(err) => Err(serde_json::to_string(&err).map_err(|serde_err| serde_err.to_string())?),
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute a program in mock mode.
|
||||
#[wasm_bindgen(js_name = executeMock)]
|
||||
pub async fn execute_mock(
|
||||
&self,
|
||||
program_ast_json: &str,
|
||||
path: Option<String>,
|
||||
settings: &str,
|
||||
use_prev_memory: bool,
|
||||
) -> Result<JsValue, String> {
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
let program: Program = serde_json::from_str(program_ast_json).map_err(|e| e.to_string())?;
|
||||
|
||||
let ctx = self.create_executor_ctx(settings, path, true)?;
|
||||
match ctx.run_mock(program, use_prev_memory).await {
|
||||
// The serde-wasm-bindgen does not work here because of weird HashMap issues.
|
||||
// DO NOT USE serde_wasm_bindgen::to_value it will break the frontend.
|
||||
Ok(outcome) => JsValue::from_serde(&outcome).map_err(|e| e.to_string()),
|
||||
Err(err) => Err(serde_json::to_string(&err).map_err(|serde_err| serde_err.to_string())?),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,13 @@
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
mod context;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
mod lsp;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
mod wasm;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub use context::*;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub use lsp::*;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub use wasm::*;
|
||||
|
147
rust/kcl-wasm-lib/src/lsp.rs
Normal file
147
rust/kcl-wasm-lib/src/lsp.rs
Normal file
@ -0,0 +1,147 @@
|
||||
//! Wasm interface for our LSP servers.
|
||||
|
||||
use futures::stream::TryStreamExt;
|
||||
use tower_lsp::{LspService, Server};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct LspServerConfig {
|
||||
into_server: js_sys::AsyncIterator,
|
||||
from_server: web_sys::WritableStream,
|
||||
fs: kcl_lib::wasm_engine::FileSystemManager,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl LspServerConfig {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(
|
||||
into_server: js_sys::AsyncIterator,
|
||||
from_server: web_sys::WritableStream,
|
||||
fs: kcl_lib::wasm_engine::FileSystemManager,
|
||||
) -> Self {
|
||||
Self {
|
||||
into_server,
|
||||
from_server,
|
||||
fs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Run the `kcl` lsp server.
|
||||
//
|
||||
// NOTE: we don't use web_sys::ReadableStream for input here because on the
|
||||
// browser side we need to use a ReadableByteStreamController to construct it
|
||||
// and so far only Chromium-based browsers support that functionality.
|
||||
|
||||
// NOTE: input needs to be an AsyncIterator<Uint8Array, never, void> specifically
|
||||
#[wasm_bindgen]
|
||||
pub async fn lsp_run_kcl(config: LspServerConfig, token: String, baseurl: String) -> Result<(), JsValue> {
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
let LspServerConfig {
|
||||
into_server,
|
||||
from_server,
|
||||
fs,
|
||||
} = config;
|
||||
|
||||
let executor_ctx = None;
|
||||
|
||||
let mut zoo_client = kittycad::Client::new(token);
|
||||
zoo_client.set_base_url(baseurl.as_str());
|
||||
|
||||
// Check if we can send telemetry for this user.
|
||||
let can_send_telemetry = match zoo_client.users().get_privacy_settings().await {
|
||||
Ok(privacy_settings) => privacy_settings.can_train_on_data,
|
||||
Err(err) => {
|
||||
// In the case of dev we don't always have a sub set, but prod we should.
|
||||
if err
|
||||
.to_string()
|
||||
.contains("The modeling app subscription type is missing.")
|
||||
{
|
||||
true
|
||||
} else {
|
||||
web_sys::console::warn_1(&format!("Failed to get privacy settings: {err:?}").into());
|
||||
false
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let (service, socket) = LspService::build(|client| {
|
||||
kcl_lib::KclLspBackend::new_wasm(client, executor_ctx, fs, zoo_client, can_send_telemetry).unwrap()
|
||||
})
|
||||
.custom_method("kcl/updateUnits", kcl_lib::KclLspBackend::update_units)
|
||||
.custom_method("kcl/updateCanExecute", kcl_lib::KclLspBackend::update_can_execute)
|
||||
.finish();
|
||||
|
||||
let input = wasm_bindgen_futures::stream::JsStream::from(into_server);
|
||||
let input = input
|
||||
.map_ok(|value| {
|
||||
value
|
||||
.dyn_into::<js_sys::Uint8Array>()
|
||||
.expect("could not cast stream item to Uint8Array")
|
||||
.to_vec()
|
||||
})
|
||||
.map_err(|_err| std::io::Error::from(std::io::ErrorKind::Other))
|
||||
.into_async_read();
|
||||
|
||||
let output = wasm_bindgen::JsCast::unchecked_into::<wasm_streams::writable::sys::WritableStream>(from_server);
|
||||
let output = wasm_streams::WritableStream::from_raw(output);
|
||||
let output = output.try_into_async_write().map_err(|err| err.0)?;
|
||||
|
||||
Server::new(input, output, socket).serve(service).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run the `copilot` lsp server.
|
||||
//
|
||||
// NOTE: we don't use web_sys::ReadableStream for input here because on the
|
||||
// browser side we need to use a ReadableByteStreamController to construct it
|
||||
// and so far only Chromium-based browsers support that functionality.
|
||||
|
||||
// NOTE: input needs to be an AsyncIterator<Uint8Array, never, void> specifically
|
||||
#[wasm_bindgen]
|
||||
pub async fn lsp_run_copilot(config: LspServerConfig, token: String, baseurl: String) -> Result<(), JsValue> {
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
let LspServerConfig {
|
||||
into_server,
|
||||
from_server,
|
||||
fs,
|
||||
} = config;
|
||||
|
||||
let mut zoo_client = kittycad::Client::new(token);
|
||||
zoo_client.set_base_url(baseurl.as_str());
|
||||
|
||||
let dev_mode = baseurl == "https://api.dev.zoo.dev";
|
||||
|
||||
let (service, socket) =
|
||||
LspService::build(|client| kcl_lib::CopilotLspBackend::new_wasm(client, fs, zoo_client, dev_mode))
|
||||
.custom_method("copilot/setEditorInfo", kcl_lib::CopilotLspBackend::set_editor_info)
|
||||
.custom_method(
|
||||
"copilot/getCompletions",
|
||||
kcl_lib::CopilotLspBackend::get_completions_cycling,
|
||||
)
|
||||
.custom_method("copilot/notifyAccepted", kcl_lib::CopilotLspBackend::accept_completion)
|
||||
.custom_method("copilot/notifyRejected", kcl_lib::CopilotLspBackend::reject_completions)
|
||||
.finish();
|
||||
|
||||
let input = wasm_bindgen_futures::stream::JsStream::from(into_server);
|
||||
let input = input
|
||||
.map_ok(|value| {
|
||||
value
|
||||
.dyn_into::<js_sys::Uint8Array>()
|
||||
.expect("could not cast stream item to Uint8Array")
|
||||
.to_vec()
|
||||
})
|
||||
.map_err(|_err| std::io::Error::from(std::io::ErrorKind::Other))
|
||||
.into_async_read();
|
||||
|
||||
let output = wasm_bindgen::JsCast::unchecked_into::<wasm_streams::writable::sys::WritableStream>(from_server);
|
||||
let output = wasm_streams::WritableStream::from_raw(output);
|
||||
let output = output.try_into_async_write().map_err(|err| err.0)?;
|
||||
|
||||
Server::new(input, output, socket).serve(service).await;
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,38 +1,9 @@
|
||||
//! Wasm bindings for `kcl`.
|
||||
|
||||
use futures::stream::TryStreamExt;
|
||||
use gloo_utils::format::JsValueSerdeExt;
|
||||
use kcl_lib::{pretty::NumericSuffix, CoreDump, Point2d, Program};
|
||||
use tower_lsp::{LspService, Server};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
// wasm_bindgen wrapper for mock execute
|
||||
#[wasm_bindgen]
|
||||
pub async fn execute_mock(
|
||||
program_ast_json: &str,
|
||||
path: Option<String>,
|
||||
settings: &str,
|
||||
use_prev_memory: bool,
|
||||
fs_manager: kcl_lib::wasm_engine::FileSystemManager,
|
||||
) -> Result<JsValue, String> {
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
let program: Program = serde_json::from_str(program_ast_json).map_err(|e| e.to_string())?;
|
||||
let config: kcl_lib::Configuration = serde_json::from_str(settings).map_err(|e| e.to_string())?;
|
||||
let mut settings: kcl_lib::ExecutorSettings = config.into();
|
||||
if let Some(path) = path {
|
||||
settings.with_current_file(std::path::PathBuf::from(path));
|
||||
}
|
||||
|
||||
let ctx = kcl_lib::ExecutorContext::new_mock(fs_manager, settings.into()).await?;
|
||||
match ctx.run_mock(program, use_prev_memory).await {
|
||||
// The serde-wasm-bindgen does not work here because of weird HashMap issues.
|
||||
// DO NOT USE serde_wasm_bindgen::to_value it will break the frontend.
|
||||
Ok(outcome) => JsValue::from_serde(&outcome).map_err(|e| e.to_string()),
|
||||
Err(err) => Err(serde_json::to_string(&err).map_err(|serde_err| serde_err.to_string())?),
|
||||
}
|
||||
}
|
||||
|
||||
// wasm_bindgen wrapper for execute
|
||||
#[wasm_bindgen]
|
||||
pub async fn kcl_lint(program_ast_json: &str) -> Result<JsValue, JsValue> {
|
||||
@ -94,148 +65,6 @@ pub fn format_number(value: f64, suffix_json: &str) -> Result<String, JsError> {
|
||||
Ok(kcl_lib::pretty::format_number(value, suffix))
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct ServerConfig {
|
||||
into_server: js_sys::AsyncIterator,
|
||||
from_server: web_sys::WritableStream,
|
||||
fs: kcl_lib::wasm_engine::FileSystemManager,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl ServerConfig {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(
|
||||
into_server: js_sys::AsyncIterator,
|
||||
from_server: web_sys::WritableStream,
|
||||
fs: kcl_lib::wasm_engine::FileSystemManager,
|
||||
) -> Self {
|
||||
Self {
|
||||
into_server,
|
||||
from_server,
|
||||
fs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Run the `kcl` lsp server.
|
||||
//
|
||||
// NOTE: we don't use web_sys::ReadableStream for input here because on the
|
||||
// browser side we need to use a ReadableByteStreamController to construct it
|
||||
// and so far only Chromium-based browsers support that functionality.
|
||||
|
||||
// NOTE: input needs to be an AsyncIterator<Uint8Array, never, void> specifically
|
||||
#[wasm_bindgen]
|
||||
pub async fn kcl_lsp_run(config: ServerConfig, token: String, baseurl: String) -> Result<(), JsValue> {
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
let ServerConfig {
|
||||
into_server,
|
||||
from_server,
|
||||
fs,
|
||||
} = config;
|
||||
|
||||
let executor_ctx = None;
|
||||
|
||||
let mut zoo_client = kittycad::Client::new(token);
|
||||
zoo_client.set_base_url(baseurl.as_str());
|
||||
|
||||
// Check if we can send telemetry for this user.
|
||||
let can_send_telemetry = match zoo_client.users().get_privacy_settings().await {
|
||||
Ok(privacy_settings) => privacy_settings.can_train_on_data,
|
||||
Err(err) => {
|
||||
// In the case of dev we don't always have a sub set, but prod we should.
|
||||
if err
|
||||
.to_string()
|
||||
.contains("The modeling app subscription type is missing.")
|
||||
{
|
||||
true
|
||||
} else {
|
||||
web_sys::console::warn_1(&format!("Failed to get privacy settings: {err:?}").into());
|
||||
false
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let (service, socket) = LspService::build(|client| {
|
||||
kcl_lib::KclLspBackend::new_wasm(client, executor_ctx, fs, zoo_client, can_send_telemetry).unwrap()
|
||||
})
|
||||
.custom_method("kcl/updateUnits", kcl_lib::KclLspBackend::update_units)
|
||||
.custom_method("kcl/updateCanExecute", kcl_lib::KclLspBackend::update_can_execute)
|
||||
.finish();
|
||||
|
||||
let input = wasm_bindgen_futures::stream::JsStream::from(into_server);
|
||||
let input = input
|
||||
.map_ok(|value| {
|
||||
value
|
||||
.dyn_into::<js_sys::Uint8Array>()
|
||||
.expect("could not cast stream item to Uint8Array")
|
||||
.to_vec()
|
||||
})
|
||||
.map_err(|_err| std::io::Error::from(std::io::ErrorKind::Other))
|
||||
.into_async_read();
|
||||
|
||||
let output = wasm_bindgen::JsCast::unchecked_into::<wasm_streams::writable::sys::WritableStream>(from_server);
|
||||
let output = wasm_streams::WritableStream::from_raw(output);
|
||||
let output = output.try_into_async_write().map_err(|err| err.0)?;
|
||||
|
||||
Server::new(input, output, socket).serve(service).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run the `copilot` lsp server.
|
||||
//
|
||||
// NOTE: we don't use web_sys::ReadableStream for input here because on the
|
||||
// browser side we need to use a ReadableByteStreamController to construct it
|
||||
// and so far only Chromium-based browsers support that functionality.
|
||||
|
||||
// NOTE: input needs to be an AsyncIterator<Uint8Array, never, void> specifically
|
||||
#[wasm_bindgen]
|
||||
pub async fn copilot_lsp_run(config: ServerConfig, token: String, baseurl: String) -> Result<(), JsValue> {
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
let ServerConfig {
|
||||
into_server,
|
||||
from_server,
|
||||
fs,
|
||||
} = config;
|
||||
|
||||
let mut zoo_client = kittycad::Client::new(token);
|
||||
zoo_client.set_base_url(baseurl.as_str());
|
||||
|
||||
let dev_mode = baseurl == "https://api.dev.zoo.dev";
|
||||
|
||||
let (service, socket) =
|
||||
LspService::build(|client| kcl_lib::CopilotLspBackend::new_wasm(client, fs, zoo_client, dev_mode))
|
||||
.custom_method("copilot/setEditorInfo", kcl_lib::CopilotLspBackend::set_editor_info)
|
||||
.custom_method(
|
||||
"copilot/getCompletions",
|
||||
kcl_lib::CopilotLspBackend::get_completions_cycling,
|
||||
)
|
||||
.custom_method("copilot/notifyAccepted", kcl_lib::CopilotLspBackend::accept_completion)
|
||||
.custom_method("copilot/notifyRejected", kcl_lib::CopilotLspBackend::reject_completions)
|
||||
.finish();
|
||||
|
||||
let input = wasm_bindgen_futures::stream::JsStream::from(into_server);
|
||||
let input = input
|
||||
.map_ok(|value| {
|
||||
value
|
||||
.dyn_into::<js_sys::Uint8Array>()
|
||||
.expect("could not cast stream item to Uint8Array")
|
||||
.to_vec()
|
||||
})
|
||||
.map_err(|_err| std::io::Error::from(std::io::ErrorKind::Other))
|
||||
.into_async_read();
|
||||
|
||||
let output = wasm_bindgen::JsCast::unchecked_into::<wasm_streams::writable::sys::WritableStream>(from_server);
|
||||
let output = wasm_streams::WritableStream::from_raw(output);
|
||||
let output = output.try_into_async_write().map_err(|err| err.0)?;
|
||||
|
||||
Server::new(input, output, socket).serve(service).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn is_points_ccw(points: &[f64]) -> i32 {
|
||||
console_error_panic_hook::set_once();
|
||||
|
@ -39,7 +39,7 @@ import { getConstraintInfo, getConstraintInfoKw } from 'lang/std/sketch'
|
||||
import { Dialog, Popover, Transition } from '@headlessui/react'
|
||||
import toast from 'react-hot-toast'
|
||||
import { InstanceProps, create } from 'react-modal-promise'
|
||||
import { executeAst } from 'lang/langHelpers'
|
||||
import { executeAstMock } from 'lang/langHelpers'
|
||||
import {
|
||||
deleteSegmentFromPipeExpression,
|
||||
removeSingleConstraintInfo,
|
||||
@ -437,12 +437,10 @@ export async function deleteSegment({
|
||||
if (err(pResult) || !resultIsOk(pResult)) return Promise.reject(pResult)
|
||||
modifiedAst = pResult.program
|
||||
|
||||
const testExecute = await executeAst({
|
||||
const testExecute = await executeAstMock({
|
||||
ast: modifiedAst,
|
||||
engineCommandManager: engineCommandManager,
|
||||
isMock: true,
|
||||
rustContext,
|
||||
usePrevMemory: false,
|
||||
rustContext: rustContext,
|
||||
})
|
||||
if (testExecute.errors.length) {
|
||||
toast.error('Segment tag used outside of current Sketch. Could not delete.')
|
||||
|
@ -66,7 +66,7 @@ import {
|
||||
} from 'lib/singletons'
|
||||
import { getNodeFromPath } from 'lang/queryAst'
|
||||
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
||||
import { executeAst, ToolTip } from 'lang/langHelpers'
|
||||
import { executeAstMock, ToolTip } from 'lang/langHelpers'
|
||||
import {
|
||||
createProfileStartHandle,
|
||||
dashedStraight,
|
||||
@ -91,7 +91,6 @@ import {
|
||||
createLabeledArg,
|
||||
createLiteral,
|
||||
createNodeFromExprSnippet,
|
||||
createObjectExpression,
|
||||
createPipeExpression,
|
||||
createPipeSubstitution,
|
||||
createVariableDeclaration,
|
||||
@ -661,11 +660,9 @@ export class SceneEntities {
|
||||
if (err(prepared)) return Promise.reject(prepared)
|
||||
const { truncatedAst, variableDeclarationName } = prepared
|
||||
|
||||
const { execState } = await executeAst({
|
||||
const { execState } = await executeAstMock({
|
||||
ast: truncatedAst,
|
||||
engineCommandManager: this.engineCommandManager,
|
||||
rustContext,
|
||||
isMock: true,
|
||||
})
|
||||
const sketchesInfo = getSketchesInfo({
|
||||
sketchNodePaths,
|
||||
@ -1239,11 +1236,9 @@ export class SceneEntities {
|
||||
updateRectangleSketch(sketchInit, x, y, tag)
|
||||
}
|
||||
|
||||
const { execState } = await executeAst({
|
||||
const { execState } = await executeAstMock({
|
||||
ast: truncatedAst,
|
||||
engineCommandManager: this.engineCommandManager,
|
||||
rustContext,
|
||||
isMock: true,
|
||||
})
|
||||
const sketch = sketchFromKclValue(execState.variables[varName], varName)
|
||||
if (err(sketch)) return Promise.reject(sketch)
|
||||
@ -1445,11 +1440,9 @@ export class SceneEntities {
|
||||
)
|
||||
}
|
||||
|
||||
const { execState } = await executeAst({
|
||||
const { execState } = await executeAstMock({
|
||||
ast: truncatedAst,
|
||||
engineCommandManager: this.engineCommandManager,
|
||||
rustContext,
|
||||
isMock: true,
|
||||
})
|
||||
const sketch = sketchFromKclValue(execState.variables[varName], varName)
|
||||
if (err(sketch)) return Promise.reject(sketch)
|
||||
@ -1624,11 +1617,9 @@ export class SceneEntities {
|
||||
modded = moddedResult.modifiedAst
|
||||
}
|
||||
|
||||
const { execState } = await executeAst({
|
||||
const { execState } = await executeAstMock({
|
||||
ast: modded,
|
||||
engineCommandManager: this.engineCommandManager,
|
||||
rustContext,
|
||||
isMock: true,
|
||||
})
|
||||
const sketch = sketchFromKclValue(execState.variables[varName], varName)
|
||||
if (err(sketch)) return
|
||||
@ -2307,11 +2298,9 @@ export class SceneEntities {
|
||||
modded = moddedResult.modifiedAst
|
||||
}
|
||||
|
||||
const { execState } = await executeAst({
|
||||
const { execState } = await executeAstMock({
|
||||
ast: modded,
|
||||
engineCommandManager: this.engineCommandManager,
|
||||
rustContext,
|
||||
isMock: true,
|
||||
})
|
||||
const sketch = sketchFromKclValue(execState.variables[varName], varName)
|
||||
if (err(sketch)) return
|
||||
@ -2886,11 +2875,9 @@ export class SceneEntities {
|
||||
// don't want to mod the user's code yet as they have't committed to the change yet
|
||||
// plus this would be the truncated ast being recast, it would be wrong
|
||||
codeManager.updateCodeEditor(code)
|
||||
const { execState } = await executeAst({
|
||||
const { execState } = await executeAstMock({
|
||||
ast: truncatedAst,
|
||||
engineCommandManager: this.engineCommandManager,
|
||||
rustContext,
|
||||
isMock: true,
|
||||
})
|
||||
const variables = execState.variables
|
||||
const sketchesInfo = getSketchesInfo({
|
||||
|
@ -6,9 +6,9 @@ import {
|
||||
} from '@kittycad/codemirror-lsp-client'
|
||||
import { fileSystemManager } from 'lang/std/fileSystemManager'
|
||||
import init, {
|
||||
ServerConfig,
|
||||
copilot_lsp_run,
|
||||
kcl_lsp_run,
|
||||
LspServerConfig,
|
||||
lsp_run_copilot,
|
||||
lsp_run_kcl,
|
||||
} from '@rust/kcl-wasm-lib/pkg/kcl_wasm_lib'
|
||||
import * as jsrpc from 'json-rpc-2.0'
|
||||
import {
|
||||
@ -30,13 +30,13 @@ const initialise = async (wasmUrl: string) => {
|
||||
}
|
||||
|
||||
export async function copilotLspRun(
|
||||
config: ServerConfig,
|
||||
config: LspServerConfig,
|
||||
token: string,
|
||||
baseUrl: string
|
||||
) {
|
||||
try {
|
||||
console.log('starting copilot lsp')
|
||||
await copilot_lsp_run(config, token, baseUrl)
|
||||
await lsp_run_copilot(config, token, baseUrl)
|
||||
} catch (e: any) {
|
||||
console.log('copilot lsp failed', e)
|
||||
// We can't restart here because a moved value, we should do this another way.
|
||||
@ -44,13 +44,13 @@ export async function copilotLspRun(
|
||||
}
|
||||
|
||||
export async function kclLspRun(
|
||||
config: ServerConfig,
|
||||
config: LspServerConfig,
|
||||
token: string,
|
||||
baseUrl: string
|
||||
) {
|
||||
try {
|
||||
console.log('start kcl lsp')
|
||||
await kcl_lsp_run(config, token, baseUrl)
|
||||
await lsp_run_kcl(config, token, baseUrl)
|
||||
} catch (e: any) {
|
||||
console.log('kcl lsp failed', e)
|
||||
// We can't restart here because a moved value, we should do this another way.
|
||||
@ -70,7 +70,7 @@ onmessage = function (event: MessageEvent) {
|
||||
initialise(wasmUrl)
|
||||
.then(async (instantiatedModule) => {
|
||||
console.log('Worker: WASM module loaded', worker, instantiatedModule)
|
||||
const config = new ServerConfig(
|
||||
const config = new LspServerConfig(
|
||||
intoServer,
|
||||
fromServer,
|
||||
fileSystemManager
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { executeAst, lintAst } from 'lang/langHelpers'
|
||||
import { executeAst, executeAstMock, lintAst } from 'lang/langHelpers'
|
||||
import { handleSelectionBatch, Selections } from 'lib/selections'
|
||||
import {
|
||||
KCLError,
|
||||
@ -338,7 +338,7 @@ export class KclManager {
|
||||
if (this.isExecuting) {
|
||||
this.executeIsStale = args
|
||||
|
||||
// The previous execteAst will be rejected and cleaned up. The execution will be marked as stale.
|
||||
// The previous executeAst will be rejected and cleaned up. The execution will be marked as stale.
|
||||
// A new executeAst will start.
|
||||
this.engineCommandManager.rejectAllModelingCommands(
|
||||
EXECUTE_AST_INTERRUPT_ERROR_MESSAGE
|
||||
@ -358,9 +358,7 @@ export class KclManager {
|
||||
const { logs, errors, execState, isInterrupted } = await executeAst({
|
||||
ast,
|
||||
path: codeManager.currentFilePath || undefined,
|
||||
engineCommandManager: this.engineCommandManager,
|
||||
rustContext,
|
||||
isMock: false,
|
||||
})
|
||||
|
||||
// Program was not interrupted, setup the scene
|
||||
@ -476,11 +474,9 @@ export class KclManager {
|
||||
}
|
||||
this._ast = { ...newAst }
|
||||
|
||||
const { logs, errors, execState } = await executeAst({
|
||||
const { logs, errors, execState } = await executeAstMock({
|
||||
ast: newAst,
|
||||
engineCommandManager: this.engineCommandManager,
|
||||
rustContext,
|
||||
isMock: true,
|
||||
})
|
||||
|
||||
this._logs = logs
|
||||
|
@ -1,12 +1,10 @@
|
||||
import {
|
||||
Program,
|
||||
executeMock,
|
||||
kclLint,
|
||||
emptyExecState,
|
||||
ExecState,
|
||||
jsAppSettings,
|
||||
} from 'lang/wasm'
|
||||
import { EngineCommandManager } from 'lang/std/engineConnection'
|
||||
import { KCLError } from 'lang/errors'
|
||||
import { Diagnostic } from '@codemirror/lint'
|
||||
import { Node } from '@rust/kcl-lib/bindings/Node'
|
||||
@ -50,33 +48,27 @@ export const toolTips: Array<ToolTip> = [
|
||||
'arcTo',
|
||||
]
|
||||
|
||||
export async function executeAst({
|
||||
ast,
|
||||
path,
|
||||
engineCommandManager,
|
||||
isMock,
|
||||
usePrevMemory,
|
||||
rustContext,
|
||||
}: {
|
||||
ast: Node<Program>
|
||||
path?: string
|
||||
engineCommandManager: EngineCommandManager
|
||||
rustContext: RustContext
|
||||
isMock: boolean
|
||||
usePrevMemory?: boolean
|
||||
isInterrupted?: boolean
|
||||
}): Promise<{
|
||||
interface ExecutionResult {
|
||||
logs: string[]
|
||||
errors: KCLError[]
|
||||
execState: ExecState
|
||||
isInterrupted: boolean
|
||||
}> {
|
||||
try {
|
||||
const execState = await (isMock
|
||||
? executeMock(ast, usePrevMemory, path)
|
||||
: rustContext.execute(ast, { settings: await jsAppSettings() }, path))
|
||||
}
|
||||
|
||||
await engineCommandManager.waitForAllCommands()
|
||||
export async function executeAst({
|
||||
ast,
|
||||
rustContext,
|
||||
path,
|
||||
}: {
|
||||
ast: Node<Program>
|
||||
rustContext: RustContext
|
||||
path?: string
|
||||
}): Promise<ExecutionResult> {
|
||||
try {
|
||||
const settings = { settings: await jsAppSettings() }
|
||||
const execState = await rustContext.execute(ast, settings, path)
|
||||
|
||||
await rustContext.waitForAllEngineCommands()
|
||||
return {
|
||||
logs: [],
|
||||
errors: [],
|
||||
@ -84,29 +76,65 @@ export async function executeAst({
|
||||
isInterrupted: false,
|
||||
}
|
||||
} catch (e: any) {
|
||||
let isInterrupted = false
|
||||
if (e instanceof KCLError) {
|
||||
// Detect if it is a force interrupt error which is not a KCL processing error.
|
||||
if (
|
||||
e.msg ===
|
||||
'Failed to wait for promise from engine: JsValue("Force interrupt, executionIsStale, new AST requested")'
|
||||
) {
|
||||
isInterrupted = true
|
||||
}
|
||||
return {
|
||||
errors: [e],
|
||||
logs: [],
|
||||
execState: emptyExecState(),
|
||||
isInterrupted,
|
||||
}
|
||||
} else {
|
||||
console.log(e)
|
||||
return {
|
||||
logs: [e],
|
||||
errors: [],
|
||||
execState: emptyExecState(),
|
||||
isInterrupted,
|
||||
}
|
||||
return handleExecuteError(e)
|
||||
}
|
||||
}
|
||||
|
||||
export async function executeAstMock({
|
||||
ast,
|
||||
rustContext,
|
||||
path,
|
||||
usePrevMemory,
|
||||
}: {
|
||||
ast: Node<Program>
|
||||
rustContext: RustContext
|
||||
path?: string
|
||||
usePrevMemory?: boolean
|
||||
}): Promise<ExecutionResult> {
|
||||
try {
|
||||
const settings = { settings: await jsAppSettings() }
|
||||
const execState = await rustContext.executeMock(
|
||||
ast,
|
||||
settings,
|
||||
path,
|
||||
usePrevMemory
|
||||
)
|
||||
|
||||
await rustContext.waitForAllEngineCommands()
|
||||
return {
|
||||
logs: [],
|
||||
errors: [],
|
||||
execState,
|
||||
isInterrupted: false,
|
||||
}
|
||||
} catch (e: any) {
|
||||
return handleExecuteError(e)
|
||||
}
|
||||
}
|
||||
|
||||
function handleExecuteError(e: any): ExecutionResult {
|
||||
let isInterrupted = false
|
||||
if (e instanceof KCLError) {
|
||||
// Detect if it is a force interrupt error which is not a KCL processing error.
|
||||
if (
|
||||
e.msg ===
|
||||
'Failed to wait for promise from engine: JsValue("Force interrupt, executionIsStale, new AST requested")'
|
||||
) {
|
||||
isInterrupted = true
|
||||
}
|
||||
return {
|
||||
errors: [e],
|
||||
logs: [],
|
||||
execState: emptyExecState(),
|
||||
isInterrupted,
|
||||
}
|
||||
} else {
|
||||
console.log(e)
|
||||
return {
|
||||
logs: [e],
|
||||
errors: [],
|
||||
execState: emptyExecState(),
|
||||
isInterrupted,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
rustContext,
|
||||
} from 'lib/singletons'
|
||||
import { err } from 'lib/trap'
|
||||
import { executeAst } from 'lang/langHelpers'
|
||||
import { executeAstMock } from 'lang/langHelpers'
|
||||
|
||||
export const deletionErrorMessage =
|
||||
'Unable to delete selection. Please edit manually in code pane.'
|
||||
@ -29,11 +29,9 @@ export async function deleteSelectionPromise(
|
||||
return new Error(deletionErrorMessage)
|
||||
}
|
||||
|
||||
const testExecute = await executeAst({
|
||||
const testExecute = await executeAstMock({
|
||||
ast: modifiedAst,
|
||||
engineCommandManager,
|
||||
rustContext,
|
||||
isMock: true,
|
||||
rustContext: rustContext,
|
||||
})
|
||||
if (testExecute.errors.length) {
|
||||
return new Error(deletionErrorMessage)
|
||||
|
@ -3,7 +3,6 @@ import {
|
||||
parse_wasm,
|
||||
recast_wasm,
|
||||
format_number,
|
||||
execute_mock,
|
||||
kcl_lint,
|
||||
is_points_ccw,
|
||||
get_tangential_arc_to_info,
|
||||
@ -340,7 +339,7 @@ export function execStateFromRust(
|
||||
}
|
||||
}
|
||||
|
||||
function mockExecStateFromRust(execOutcome: RustExecOutcome): ExecState {
|
||||
export function mockExecStateFromRust(execOutcome: RustExecOutcome): ExecState {
|
||||
return {
|
||||
variables: execOutcome.variables,
|
||||
operations: execOutcome.operations,
|
||||
@ -404,34 +403,6 @@ export function sketchFromKclValue(
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a KCL program.
|
||||
* @param node The AST of the program to execute.
|
||||
* @param path The full path of the file being executed. Use `null` for
|
||||
* expressions that don't have a file, like expressions in the command bar.
|
||||
*/
|
||||
export const executeMock = async (
|
||||
node: Node<Program>,
|
||||
usePrevMemory?: boolean,
|
||||
path?: string
|
||||
): Promise<ExecState> => {
|
||||
try {
|
||||
if (usePrevMemory === undefined) {
|
||||
usePrevMemory = true
|
||||
}
|
||||
const execOutcome: RustExecOutcome = await execute_mock(
|
||||
JSON.stringify(node),
|
||||
path,
|
||||
JSON.stringify({ settings: await jsAppSettings() }),
|
||||
usePrevMemory,
|
||||
fileSystemManager
|
||||
)
|
||||
return mockExecStateFromRust(execOutcome)
|
||||
} catch (e: any) {
|
||||
return Promise.reject(errFromErrWithOutputs(e))
|
||||
}
|
||||
}
|
||||
|
||||
export const jsAppSettings = async () => {
|
||||
let jsAppSettings = default_app_settings()
|
||||
if (!TEST) {
|
||||
|
@ -1,9 +1,8 @@
|
||||
import { err } from './trap'
|
||||
import { engineCommandManager, rustContext } from 'lib/singletons'
|
||||
import { parse, resultIsOk, VariableMap } from 'lang/wasm'
|
||||
import { PrevVariable } from 'lang/queryAst'
|
||||
import { executeAst } from 'lang/langHelpers'
|
||||
import { parse, resultIsOk } from 'lang/wasm'
|
||||
import { executeAstMock } from 'lang/langHelpers'
|
||||
import { KclExpression } from './commandTypes'
|
||||
import { rustContext } from './singletons'
|
||||
|
||||
const DUMMY_VARIABLE_NAME = '__result__'
|
||||
|
||||
@ -19,11 +18,9 @@ export async function getCalculatedKclExpressionValue(value: string) {
|
||||
const ast = pResult.program
|
||||
|
||||
// Execute the program without hitting the engine
|
||||
const { execState } = await executeAst({
|
||||
const { execState } = await executeAstMock({
|
||||
ast,
|
||||
engineCommandManager,
|
||||
rustContext,
|
||||
isMock: true,
|
||||
rustContext: rustContext,
|
||||
})
|
||||
|
||||
// Find the variable declaration for the result
|
||||
|
@ -4,6 +4,7 @@ import {
|
||||
ExecState,
|
||||
execStateFromRust,
|
||||
initPromise,
|
||||
mockExecStateFromRust,
|
||||
} from 'lang/wasm'
|
||||
import { getModule, ModuleType } from 'lib/wasm_lib_wrapper'
|
||||
import { fileSystemManager } from 'lang/std/fileSystemManager'
|
||||
@ -48,6 +49,9 @@ export default class RustContext {
|
||||
// Create a new context instance
|
||||
async create() {
|
||||
this.rustInstance = getModule()
|
||||
// We need this await here, DO NOT REMOVE it even if your editor says it's
|
||||
// unnecessary. The constructor of the module is async and it will not
|
||||
// resolve if you don't await it.
|
||||
this.ctxInstance = await new this.rustInstance.Context(
|
||||
this.engineCommandManager,
|
||||
fileSystemManager
|
||||
@ -87,6 +91,41 @@ export default class RustContext {
|
||||
return Promise.reject(emptyExecState())
|
||||
}
|
||||
|
||||
// Execute a program with in mock mode.
|
||||
async executeMock(
|
||||
node: Node<Program>,
|
||||
settings: DeepPartial<Configuration>,
|
||||
path?: string,
|
||||
usePrevMemory?: boolean
|
||||
): Promise<ExecState> {
|
||||
await this._checkInstance()
|
||||
|
||||
if (this.ctxInstance) {
|
||||
try {
|
||||
if (usePrevMemory === undefined) {
|
||||
usePrevMemory = true
|
||||
}
|
||||
|
||||
const result = await this.ctxInstance.executeMock(
|
||||
JSON.stringify(node),
|
||||
path,
|
||||
JSON.stringify(settings),
|
||||
usePrevMemory
|
||||
)
|
||||
return mockExecStateFromRust(result)
|
||||
} catch (e: any) {
|
||||
return Promise.reject(errFromErrWithOutputs(e))
|
||||
}
|
||||
}
|
||||
|
||||
// You will never get here.
|
||||
return Promise.reject(emptyExecState())
|
||||
}
|
||||
|
||||
async waitForAllEngineCommands() {
|
||||
await this.engineCommandManager.waitForAllCommands()
|
||||
}
|
||||
|
||||
get defaultPlanes() {
|
||||
return this._defaultPlanes
|
||||
}
|
||||
|
@ -1,87 +1,12 @@
|
||||
import {
|
||||
Program,
|
||||
executeMock,
|
||||
SourceRange,
|
||||
ExecState,
|
||||
VariableMap,
|
||||
} from '../lang/wasm'
|
||||
import { EngineCommandManager } from 'lang/std/engineConnection'
|
||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||
import { Models } from '@kittycad/lib'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { DefaultPlanes } from '@rust/kcl-lib/bindings/DefaultPlanes'
|
||||
import { err } from 'lib/trap'
|
||||
import { Program, ExecState, jsAppSettings } from '../lang/wasm'
|
||||
import { Node } from '@rust/kcl-lib/bindings/Node'
|
||||
|
||||
type WebSocketResponse = Models['WebSocketResponse_type']
|
||||
|
||||
const defaultPlanes: DefaultPlanes = {
|
||||
xy: uuidv4(),
|
||||
xz: uuidv4(),
|
||||
yz: uuidv4(),
|
||||
negXy: uuidv4(),
|
||||
negXz: uuidv4(),
|
||||
negYz: uuidv4(),
|
||||
}
|
||||
|
||||
class MockEngineCommandManager {
|
||||
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
|
||||
constructor(mockParams: {
|
||||
setIsStreamReady: (isReady: boolean) => void
|
||||
setMediaStream: (stream: MediaStream) => void
|
||||
}) {}
|
||||
startNewSession() {}
|
||||
waitForAllCommands() {}
|
||||
waitForReady = new Promise<void>((resolve) => resolve())
|
||||
sendModelingCommand({
|
||||
id,
|
||||
range,
|
||||
command,
|
||||
}: {
|
||||
id: string
|
||||
range: SourceRange
|
||||
command: EngineCommand
|
||||
}): Promise<any> {
|
||||
const response: WebSocketResponse = {
|
||||
success: true,
|
||||
resp: {
|
||||
type: 'modeling',
|
||||
data: {
|
||||
modeling_response: { type: 'empty' },
|
||||
},
|
||||
},
|
||||
}
|
||||
return Promise.resolve(JSON.stringify(response))
|
||||
}
|
||||
async wasmGetDefaultPlanes(): Promise<string> {
|
||||
return JSON.stringify(defaultPlanes)
|
||||
}
|
||||
sendModelingCommandFromWasm(
|
||||
id: string,
|
||||
rangeStr: string,
|
||||
commandStr: string
|
||||
): Promise<any> {
|
||||
if (id === undefined) {
|
||||
return Promise.reject(new Error('id is undefined'))
|
||||
}
|
||||
if (rangeStr === undefined) {
|
||||
return Promise.reject(new Error('rangeStr is undefined'))
|
||||
}
|
||||
if (commandStr === undefined) {
|
||||
return Promise.reject(new Error('commandStr is undefined'))
|
||||
}
|
||||
const command: EngineCommand = JSON.parse(commandStr)
|
||||
const range: SourceRange = JSON.parse(rangeStr)
|
||||
|
||||
return this.sendModelingCommand({ id, range, command })
|
||||
}
|
||||
sendSceneCommand() {}
|
||||
}
|
||||
import { rustContext } from './singletons'
|
||||
|
||||
export async function enginelessExecutor(
|
||||
ast: Node<Program>,
|
||||
usePrevMemory?: boolean,
|
||||
path?: string
|
||||
): Promise<ExecState> {
|
||||
return await executeMock(ast, usePrevMemory, path)
|
||||
const settings = { settings: await jsAppSettings() }
|
||||
return await rustContext.executeMock(ast, settings, path, usePrevMemory)
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ import {
|
||||
parse_wasm as ParseWasm,
|
||||
recast_wasm as RecastWasm,
|
||||
format_number as FormatNumber,
|
||||
execute_mock as ExecuteMock,
|
||||
kcl_lint as KclLint,
|
||||
is_points_ccw as IsPointsCcw,
|
||||
get_tangential_arc_to_info as GetTangentialArcToInfo,
|
||||
@ -55,9 +54,6 @@ export const recast_wasm: typeof RecastWasm = (...args) => {
|
||||
export const format_number: typeof FormatNumber = (...args) => {
|
||||
return getModule().format_number(...args)
|
||||
}
|
||||
export const execute_mock: typeof ExecuteMock = (...args) => {
|
||||
return getModule().execute_mock(...args)
|
||||
}
|
||||
export const kcl_lint: typeof KclLint = (...args) => {
|
||||
return getModule().kcl_lint(...args)
|
||||
}
|
||||
|
Reference in New Issue
Block a user