Merge branch 'main' into pierremtb/issue5301-Expose-the-sectional-argument-in-the-Sweep-command-flow

This commit is contained in:
Pierre Jacquier
2025-02-28 09:26:16 -05:00
committed by GitHub
20 changed files with 290 additions and 185 deletions

View File

@ -3,6 +3,7 @@ import { HomePageFixture } from './fixtures/homePageFixture'
import { getUtils } from './test-utils'
import { EngineCommand } from 'lang/std/artifactGraph'
import { uuidv4 } from 'lib/utils'
import { SceneFixture } from './fixtures/sceneFixture'
test.describe(
'Can create sketches on all planes and their back sides',
@ -11,16 +12,17 @@ test.describe(
const sketchOnPlaneAndBackSideTest = async (
page: Page,
homePage: HomePageFixture,
scene: SceneFixture,
plane: string,
clickCoords: { x: number; y: number }
) => {
const u = await getUtils(page)
const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
// FIXME: Cannot use scene.waitForExecutionDone() since there is no KCL code
await page.waitForTimeout(10000)
const XYPlanRed: [number, number, number] = [98, 50, 51]
await scene.expectPixelColor(XYPlanRed, { x: 700, y: 300 }, 15)
await u.openDebugPanel()
const coord =
@ -43,7 +45,7 @@ test.describe(
},
}
const code = `sketch001 = startSketchOn('${plane}')profile001 = startProfileAt([0.9, -1.22], sketch001)`
const code = `sketch001 = startSketchOn('${plane}')profile001 = startProfileAt([0.91, -1.22], sketch001)`
await u.openDebugPanel()
@ -56,17 +58,14 @@ test.describe(
await u.closeDebugPanel()
await page.mouse.click(clickCoords.x, clickCoords.y)
await page.waitForTimeout(300) // wait for animation
await page.waitForTimeout(600) // wait for animation
await expect(
page.getByRole('button', { name: 'line Line', exact: true })
).toBeVisible()
// draw a line
const startXPx = 600
await u.closeDebugPanel()
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
await page.mouse.click(707, 393)
await expect(page.locator('.cm-content')).toHaveText(code)
@ -81,49 +80,50 @@ test.describe(
await u.clearCommandLogs()
await u.removeCurrentCode()
}
test('XY', async ({ page, homePage }) => {
await sketchOnPlaneAndBackSideTest(
page,
homePage,
'XY',
{ x: 600, y: 388 } // red plane
// { x: 600, y: 400 }, // red plane // clicks grid helper and that causes problems, should fix so that these coords work too.
)
})
test('YZ', async ({ page, homePage }) => {
await sketchOnPlaneAndBackSideTest(page, homePage, 'YZ', {
x: 700,
y: 250,
}) // green plane
})
const planeConfigs = [
{
plane: 'XY',
coords: { x: 600, y: 388 },
description: 'red plane',
},
{
plane: 'YZ',
coords: { x: 700, y: 250 },
description: 'green plane',
},
{
plane: 'XZ',
coords: { x: 684, y: 427 },
description: 'blue plane',
},
{
plane: '-XY',
coords: { x: 600, y: 118 },
description: 'back of red plane',
},
{
plane: '-YZ',
coords: { x: 700, y: 219 },
description: 'back of green plane',
},
{
plane: '-XZ',
coords: { x: 700, y: 80 },
description: 'back of blue plane',
},
]
test('XZ', async ({ page, homePage }) => {
await sketchOnPlaneAndBackSideTest(page, homePage, '-XZ', {
x: 700,
y: 80,
}) // blue plane
})
test('-XY', async ({ page, homePage }) => {
await sketchOnPlaneAndBackSideTest(page, homePage, '-XY', {
x: 600,
y: 118,
}) // back of red plane
})
test('-YZ', async ({ page, homePage }) => {
await sketchOnPlaneAndBackSideTest(page, homePage, '-YZ', {
x: 700,
y: 219,
}) // back of green plan
})
test('-XZ', async ({ page, homePage }) => {
await sketchOnPlaneAndBackSideTest(page, homePage, 'XZ', {
x: 700,
y: 427,
}) // back of blue plane
})
for (const config of planeConfigs) {
test(config.plane, async ({ page, homePage, scene }) => {
await sketchOnPlaneAndBackSideTest(
page,
homePage,
scene,
config.plane,
config.coords
)
})
}
}
)

View File

@ -629,6 +629,39 @@ extrude001 = extrude(sketch001, length = 50)
)
})
})
test(`Toolbar doesn't show modeling tools during sketch plane selection animation`, async ({
page,
homePage,
toolbar,
}) => {
await test.step('Load an empty file', async () => {
await page.addInitScript(async () => {
localStorage.setItem('persistCode', '')
})
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
})
const toolBarMode = () =>
page.locator('[data-currentMode]').getAttribute('data-currentMode')
await test.step('Start sketch and select a plane', async () => {
await expect.poll(toolBarMode).toEqual('modeling')
// Click the start sketch button
await toolbar.startSketchPlaneSelection()
// Click on a default plane at position [700, 200]
await page.mouse.click(700, 200)
// Check that the modeling toolbar doesn't appear during the animation
// The animation typically takes around 500ms, so we'll check for a second
await expect.poll(toolBarMode, { timeout: 1000 }).not.toEqual('modeling')
// After animation completes, we should see the sketching toolbar
await expect.poll(toolBarMode).toEqual('sketching')
})
})
})
async function clickExportButton(page: Page) {

View File

@ -167,7 +167,10 @@ export function Toolbar({
}, [currentMode, disableAllButtons, configCallbackProps])
return (
<menu className="max-w-full whitespace-nowrap rounded-b px-2 py-1 bg-chalkboard-10 dark:bg-chalkboard-90 relative border border-chalkboard-30 dark:border-chalkboard-80 border-t-0 shadow-sm">
<menu
data-currentMode={currentMode}
className="max-w-full whitespace-nowrap rounded-b px-2 py-1 bg-chalkboard-10 dark:bg-chalkboard-90 relative border border-chalkboard-30 dark:border-chalkboard-80 border-t-0 shadow-sm"
>
<ul
{...props}
ref={toolbarButtonsRef}

View File

@ -1,5 +1,6 @@
.editor {
@apply text-base flex-1;
@apply text-base flex-1 overflow-auto;
scrollbar-width: thin;
}
.editor :global(.cm-editor) {

View File

@ -12,7 +12,6 @@ import {
get_kcl_version,
make_default_planes,
coredump,
toml_stringify,
default_app_settings,
parse_app_settings,
parse_project_settings,
@ -21,6 +20,8 @@ import {
clear_scene_and_bust_cache,
kcl_settings,
change_kcl_settings,
serialize_project_configuration,
serialize_configuration,
reloadModule,
} from 'lib/wasm_lib_wrapper'
@ -636,10 +637,6 @@ export async function coreDump(
}
}
export function tomlStringify(toml: any): string | Error {
return toml_stringify(JSON.stringify(toml))
}
export function defaultAppSettings(): DeepPartial<Configuration> | Error {
return default_app_settings()
}
@ -786,3 +783,27 @@ export function unitAngToUnitAngle(input: UnitAng): UnitAngle {
export function getKclVersion(): string {
return get_kcl_version()
}
/**
* Serialize a project configuration to a TOML string.
*/
export function serializeConfiguration(configuration: any): string | Error {
try {
return serialize_configuration(configuration)
} catch (e: any) {
return new Error(`Error serializing configuration: ${e}`)
}
}
/**
* Serialize a project configuration to a TOML string.
*/
export function serializeProjectConfiguration(
configuration: any
): string | Error {
try {
return serialize_project_configuration(configuration)
} catch (e: any) {
return new Error(`Error serializing project configuration: ${e}`)
}
}

View File

@ -4,7 +4,8 @@ import {
initPromise,
parseAppSettings,
parseProjectSettings,
tomlStringify,
serializeConfiguration,
serializeProjectConfiguration,
} from 'lang/wasm'
import { mouseControlsToCameraSystem } from 'lib/cameraControls'
import { BROWSER_PROJECT_NAME } from 'lib/constants'
@ -131,7 +132,7 @@ export function readLocalStorageAppSettingsFile():
} catch (e) {
const settings = defaultAppSettings()
if (err(settings)) return settings
const tomlStr = tomlStringify(settings)
const tomlStr = serializeConfiguration(settings)
if (err(tomlStr)) return tomlStr
localStorage.setItem(localStorageAppSettingsPath(), tomlStr)
@ -152,7 +153,7 @@ function readLocalStorageProjectSettingsFile():
const projectSettings = parseProjectSettings(stored)
if (err(projectSettings)) {
const settings = defaultProjectSettings()
const tomlStr = tomlStringify(settings)
const tomlStr = serializeProjectConfiguration(settings)
if (err(tomlStr)) return tomlStr
localStorage.setItem(localStorageProjectSettingsPath(), tomlStr)
@ -229,7 +230,7 @@ export async function saveSettings(
// Get the user settings.
const jsAppSettings = getChangedSettingsAtLevel(allSettings, 'user')
const appTomlString = tomlStringify({ settings: jsAppSettings })
const appTomlString = serializeConfiguration({ settings: jsAppSettings })
if (err(appTomlString)) return
// Write the app settings.
@ -246,7 +247,9 @@ export async function saveSettings(
// Get the project settings.
const jsProjectSettings = getChangedSettingsAtLevel(allSettings, 'project')
const projectTomlString = tomlStringify({ settings: jsProjectSettings })
const projectTomlString = serializeProjectConfiguration({
settings: jsProjectSettings,
})
if (err(projectTomlString)) return
// Write the project settings.

View File

@ -56,7 +56,12 @@ export type ToolbarItemResolved = Omit<
export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
modeling: {
check: (state) =>
!(state.matches('Sketch') || state.matches('Sketch no face')),
!(
state.matches('Sketch') ||
state.matches('Sketch no face') ||
state.matches('animating to existing sketch') ||
state.matches('animating to plane')
),
items: [
{
id: 'sketch',
@ -330,7 +335,10 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
},
sketching: {
check: (state) =>
state.matches('Sketch') || state.matches('Sketch no face'),
state.matches('Sketch') ||
state.matches('Sketch no face') ||
state.matches('animating to existing sketch') ||
state.matches('animating to plane'),
items: [
{
id: 'sketch-exit',

View File

@ -19,7 +19,6 @@ import {
get_tangential_arc_to_info as GetTangentialArcToInfo,
make_default_planes as MakeDefaultPlanes,
coredump as CoreDump,
toml_stringify as TomlStringify,
default_app_settings as DefaultAppSettings,
parse_app_settings as ParseAppSettings,
parse_project_settings as ParseProjectSettings,
@ -29,6 +28,8 @@ import {
kcl_settings as KclSettings,
change_kcl_settings as ChangeKclSettings,
get_kcl_version as GetKclVersion,
serialize_configuration as SerializeConfiguration,
serialize_project_configuration as SerializeProjectConfiguration,
} from '../wasm-lib/pkg/wasm_lib'
type ModuleType = typeof import('../wasm-lib/pkg/wasm_lib')
@ -86,9 +87,6 @@ export const make_default_planes: typeof MakeDefaultPlanes = (...args) => {
export const coredump: typeof CoreDump = (...args) => {
return getModule().coredump(...args)
}
export const toml_stringify: typeof TomlStringify = (...args) => {
return getModule().toml_stringify(...args)
}
export const default_app_settings: typeof DefaultAppSettings = (...args) => {
return getModule().default_app_settings(...args)
}
@ -122,3 +120,12 @@ export const change_kcl_settings: typeof ChangeKclSettings = (...args) => {
export const get_kcl_version: typeof GetKclVersion = () => {
return getModule().get_kcl_version()
}
export const serialize_configuration: typeof SerializeConfiguration = (
...args
) => {
return getModule().serialize_configuration(...args)
}
export const serialize_project_configuration: typeof SerializeProjectConfiguration =
(...args) => {
return getModule().serialize_project_configuration(...args)
}

View File

@ -150,7 +150,7 @@ impl KclErrorWithOutputs {
source_files: Default::default(),
}
}
pub fn into_miette_report_with_outputs(self, code: &str) -> anyhow::Result<ReportWithOutputs> {
pub fn into_miette_report_with_outputs(self) -> anyhow::Result<ReportWithOutputs> {
let mut source_ranges = self.error.source_ranges();
// Pop off the first source range to get the filename.
@ -162,33 +162,23 @@ impl KclErrorWithOutputs {
.source_files
.get(&first_source_range.module_id())
.cloned()
.unwrap_or(ModuleSource {
source: code.to_string(),
path: self
.filenames
.get(&first_source_range.module_id())
.ok_or_else(|| {
anyhow::anyhow!(
"Could not find filename for module id: {:?}",
first_source_range.module_id()
)
})?
.clone(),
});
.ok_or_else(|| {
anyhow::anyhow!(
"Could not find source file for module id: {:?}",
first_source_range.module_id()
)
})?;
let filename = source.path.to_string();
let kcl_source = source.source.to_string();
let mut related = Vec::new();
for source_range in source_ranges {
let module_id = source_range.module_id();
let source = self.source_files.get(&module_id).cloned().unwrap_or(ModuleSource {
source: code.to_string(),
path: self
.filenames
.get(&module_id)
.ok_or_else(|| anyhow::anyhow!("Could not find filename for module id: {:?}", module_id))?
.clone(),
});
let source = self
.source_files
.get(&module_id)
.cloned()
.ok_or_else(|| anyhow::anyhow!("Could not find source file for module id: {:?}", module_id))?;
let error = self.error.override_source_ranges(vec![source_range]);
let report = Report {
error,

View File

@ -34,7 +34,7 @@ use crate::{
},
fs::FileManager,
modules::{ModuleId, ModulePath},
parsing::ast::types::{Expr, ImportPath, Node, NodeRef, Program},
parsing::ast::types::{Expr, ImportPath, NodeRef},
settings::types::UnitLength,
source_range::SourceRange,
std::StdLib,
@ -532,7 +532,7 @@ impl ExecutorContext {
}
}
let result = self.inner_run(&program.ast, &mut exec_state, true).await?;
let result = self.inner_run(&program, &mut exec_state, true).await?;
// Restore any temporary variables, then save any newly created variables back to
// memory in case another run wants to use them. Note this is just saved to the preserved
@ -585,9 +585,15 @@ impl ExecutorContext {
.await
.is_err()
{
(true, program.ast)
(true, program)
} else {
(clear_scene, changed_program)
(
clear_scene,
crate::Program {
ast: changed_program,
original_file_contents: program.original_file_contents,
},
)
}
}
CacheResult::NoAction(true) => {
@ -608,7 +614,7 @@ impl ExecutorContext {
return Ok(old_state.to_wasm_outcome(result_env));
}
(true, program.ast)
(true, program)
}
CacheResult::NoAction(false) => return Ok(old_state.to_wasm_outcome(result_env)),
};
@ -636,7 +642,7 @@ impl ExecutorContext {
self.send_clear_scene(&mut exec_state, Default::default())
.await
.map_err(KclErrorWithOutputs::no_outputs)?;
(program.ast, exec_state, false)
(program, exec_state, false)
};
let result = self.inner_run(&program, &mut exec_state, preserve_mem).await;
@ -650,7 +656,7 @@ impl ExecutorContext {
// Save this as the last successful execution to the cache.
cache::write_old_ast(OldAstState {
ast: program,
ast: program.ast,
exec_state: exec_state.clone(),
settings: self.settings.clone(),
result_env: result.0,
@ -691,18 +697,19 @@ impl ExecutorContext {
self.send_clear_scene(exec_state, Default::default())
.await
.map_err(KclErrorWithOutputs::no_outputs)?;
self.inner_run(&program.ast, exec_state, false).await
self.inner_run(program, exec_state, false).await
}
/// Perform the execution of a program. Accept all possible parameters and
/// output everything.
async fn inner_run(
&self,
program: &Node<Program>,
program: &crate::Program,
exec_state: &mut ExecState,
preserve_mem: bool,
) -> Result<(EnvironmentRef, Option<ModelingSessionData>), KclErrorWithOutputs> {
let _stats = crate::log::LogPerfStats::new("Interpretation");
exec_state.add_root_module_contents(program);
// Re-apply the settings, in case the cache was busted.
self.engine
@ -711,7 +718,7 @@ impl ExecutorContext {
.map_err(KclErrorWithOutputs::no_outputs)?;
let env_ref = self
.execute_and_build_graph(program, exec_state, preserve_mem)
.execute_and_build_graph(&program.ast, exec_state, preserve_mem)
.await
.map_err(|e| {
let module_id_to_module_path: IndexMap<ModuleId, ModulePath> = exec_state

View File

@ -182,8 +182,27 @@ impl ExecState {
self.global.path_to_source_id.insert(path.clone(), id);
}
pub(crate) fn add_root_module_contents(&mut self, program: &crate::Program) {
let root_id = ModuleId::default();
// Get the path for the root module.
let path = self
.global
.path_to_source_id
.iter()
.find(|(_, v)| **v == root_id)
.unwrap()
.0
.clone();
self.add_id_to_source(
root_id,
ModuleSource {
path,
source: program.original_file_contents.to_string(),
},
);
}
pub(super) fn add_id_to_source(&mut self, id: ModuleId, source: ModuleSource) {
debug_assert!(!self.global.id_to_source.contains_key(&id));
self.global.id_to_source.insert(id, source.clone());
}
@ -251,8 +270,6 @@ impl GlobalState {
global
.path_to_source_id
.insert(ModulePath::Local { value: root_path }, root_id);
// Ideally we'd have a way to set the root module's source here, but
// we don't have a way to get the source from the executor settings.
global
}
}

View File

@ -134,6 +134,11 @@ use crate::log::{log, logln};
pub struct Program {
#[serde(flatten)]
pub ast: parsing::ast::types::Node<parsing::ast::types::Program>,
// The ui doesn't need to know about this.
// It's purely used for saving the contents of the original file, so we can use it for errors.
// Because in the case of the root file, we don't want to read the file from disk again.
#[serde(skip)]
pub original_file_contents: String,
}
#[cfg(any(test, feature = "lsp-test-util"))]
@ -147,7 +152,13 @@ impl Program {
let tokens = parsing::token::lex(input, module_id)?;
let (ast, errs) = parsing::parse_tokens(tokens).0?;
Ok((ast.map(|ast| Program { ast }), errs))
Ok((
ast.map(|ast| Program {
ast,
original_file_contents: input.to_string(),
}),
errs,
))
}
pub fn parse_no_errs(input: &str) -> Result<Program, KclError> {
@ -155,7 +166,10 @@ impl Program {
let tokens = parsing::token::lex(input, module_id)?;
let ast = parsing::parse_tokens(tokens).parse_errs_as_err()?;
Ok(Program { ast })
Ok(Program {
ast,
original_file_contents: input.to_string(),
})
}
pub fn compute_digest(&mut self) -> parsing::ast::digest::Digest {
@ -168,9 +182,10 @@ impl Program {
}
/// Change the meta settings for the kcl file.
pub fn change_meta_settings(&mut self, settings: crate::MetaSettings) -> Result<Self, KclError> {
pub fn change_meta_settings(&self, settings: crate::MetaSettings) -> Result<Self, KclError> {
Ok(Self {
ast: self.ast.change_meta_settings(settings)?,
original_file_contents: self.original_file_contents.clone(),
})
}
@ -192,12 +207,6 @@ impl Program {
}
}
impl From<parsing::ast::types::Node<parsing::ast::types::Program>> for Program {
fn from(ast: parsing::ast::types::Node<parsing::ast::types::Program>) -> Program {
Self { ast }
}
}
#[inline]
fn try_f64_to_usize(f: f64) -> Option<usize> {
let i = f as usize;

View File

@ -46,7 +46,7 @@ use crate::{
errors::Suggestion,
lsp::{backend::Backend as _, util::IntoDiagnostic},
parsing::{
ast::types::{Expr, Node, VariableKind},
ast::types::{Expr, VariableKind},
token::TokenStream,
PIPE_OPERATOR,
},
@ -102,7 +102,7 @@ pub struct Backend {
/// Token maps.
pub(super) token_map: DashMap<String, TokenStream>,
/// AST maps.
pub ast_map: DashMap<String, Node<crate::parsing::ast::types::Program>>,
pub ast_map: DashMap<String, crate::Program>,
/// Current code.
pub code_map: DashMap<String, Vec<u8>>,
/// Diagnostics.
@ -327,11 +327,17 @@ impl crate::lsp::backend::Backend for Backend {
// this if it backfires and only hork the LSP.
ast.compute_digest();
// Save it as a program.
let ast = crate::Program {
ast,
original_file_contents: params.text.clone(),
};
// Check if the ast changed.
let ast_changed = match self.ast_map.get(&filename) {
Some(old_ast) => {
// Check if the ast changed.
*old_ast != ast
*old_ast.ast != *ast.ast
}
None => true,
};
@ -346,7 +352,7 @@ impl crate::lsp::backend::Backend for Backend {
// Update the symbols map.
self.symbols_map.insert(
params.uri.to_string(),
ast.get_lsp_symbols(&params.text).unwrap_or_default(),
ast.ast.get_lsp_symbols(&params.text).unwrap_or_default(),
);
// Update our semantic tokens.
@ -361,14 +367,14 @@ impl crate::lsp::backend::Backend for Backend {
// Only send the notification if we can execute.
// Otherwise it confuses the client.
self.client
.send_notification::<custom_notifications::AstUpdated>(ast.clone())
.send_notification::<custom_notifications::AstUpdated>(ast.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.
if self.execute(&params, &ast.into()).await.is_err() {
if self.execute(&params, &ast).await.is_err() {
return;
}
@ -421,7 +427,7 @@ impl Backend {
let token_modifiers_bitset = if let Some(ast) = self.ast_map.get(params.uri.as_str()) {
let token_index = Arc::new(Mutex::new(token_type_index));
let modifier_index: Arc<Mutex<u32>> = Arc::new(Mutex::new(0));
crate::walk::walk(&ast, |node: crate::walk::Node| {
crate::walk::walk(&ast.ast, |node: crate::walk::Node| {
let Ok(node_range): Result<SourceRange, _> = (&node).try_into() else {
return Ok(true);
};
@ -1021,7 +1027,7 @@ impl LanguageServer for Backend {
return Ok(None);
};
let Some(hover) = ast.get_hover_value_for_position(pos, current_code) else {
let Some(hover) = ast.ast.get_hover_value_for_position(pos, current_code) else {
return Ok(None);
};
@ -1150,13 +1156,13 @@ impl LanguageServer for Backend {
};
let position = position_to_char_index(params.text_document_position.position, current_code);
if ast.get_non_code_meta_for_position(position).is_some() {
if ast.ast.get_non_code_meta_for_position(position).is_some() {
// If we are in a code comment we don't want to show completions.
return Ok(None);
}
// Get the completion items for the ast.
let Ok(variables) = ast.completion_items() else {
let Ok(variables) = ast.ast.completion_items() else {
return Ok(Some(CompletionResponse::Array(completions)));
};
@ -1211,7 +1217,7 @@ impl LanguageServer for Backend {
return Ok(None);
};
let Some(value) = ast.get_expr_for_position(pos) else {
let Some(value) = ast.ast.get_expr_for_position(pos) else {
return Ok(None);
};
@ -1360,7 +1366,7 @@ impl LanguageServer for Backend {
};
// Get the folding ranges.
let folding_ranges = ast.get_lsp_folding_ranges();
let folding_ranges = ast.ast.get_lsp_folding_ranges();
if folding_ranges.is_empty() {
return Ok(None);

View File

@ -1138,7 +1138,7 @@ fn myFn = (param1) => {
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != Node::<Program>::default());
assert!(ast.ast != Node::<Program>::default());
// Send semantic tokens request.
let semantic_tokens = server
@ -2251,7 +2251,7 @@ part001 = cube([0,0], 20)
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert_eq!(ast.body.len(), 2);
assert_eq!(ast.ast.body.len(), 2);
// Send change file.
server
@ -2428,7 +2428,7 @@ async fn kcl_test_kcl_lsp_full_to_empty_file_updates_ast_and_memory() {
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != Node::<Program>::default());
assert!(ast.ast != Node::<Program>::default());
// Send change file.
server
@ -2450,7 +2450,7 @@ async fn kcl_test_kcl_lsp_full_to_empty_file_updates_ast_and_memory() {
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert_eq!(ast, default_hashed);
assert_eq!(ast.ast, default_hashed);
}
#[tokio::test(flavor = "multi_thread")]
@ -2479,7 +2479,7 @@ async fn kcl_test_kcl_lsp_code_unchanged_but_has_diagnostics_reexecute() {
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != Node::<Program>::default());
assert!(ast.ast != Node::<Program>::default());
// Assure we have no diagnostics.
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
@ -2506,11 +2506,15 @@ async fn kcl_test_kcl_lsp_code_unchanged_but_has_diagnostics_reexecute() {
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 1);
// Clear the ast and memory.
server
.ast_map
.insert("file:///test.kcl".to_string(), Node::<Program>::default());
server.ast_map.insert(
"file:///test.kcl".to_string(),
crate::Program {
ast: Default::default(),
original_file_contents: Default::default(),
},
);
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert_eq!(ast, Node::<Program>::default());
assert_eq!(ast.ast, Node::<Program>::default());
// Send change file, but the code is the same.
server
@ -2529,7 +2533,7 @@ async fn kcl_test_kcl_lsp_code_unchanged_but_has_diagnostics_reexecute() {
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != Node::<Program>::default());
assert!(ast.ast != Node::<Program>::default());
// Assure we have no diagnostics.
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
@ -2561,7 +2565,7 @@ async fn kcl_test_kcl_lsp_code_and_ast_unchanged_but_has_diagnostics_reexecute()
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != Node::<Program>::default());
assert!(ast.ast != Node::<Program>::default());
// Assure we have no diagnostics.
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
@ -2604,7 +2608,7 @@ async fn kcl_test_kcl_lsp_code_and_ast_unchanged_but_has_diagnostics_reexecute()
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != Node::<Program>::default());
assert!(ast.ast != Node::<Program>::default());
// Assure we have no diagnostics.
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
@ -2636,7 +2640,7 @@ async fn kcl_test_kcl_lsp_code_and_ast_units_unchanged_but_has_diagnostics_reexe
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != Node::<Program>::default());
assert!(ast.ast != Node::<Program>::default());
// Assure we have no diagnostics.
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
@ -2682,7 +2686,7 @@ async fn kcl_test_kcl_lsp_code_and_ast_units_unchanged_but_has_diagnostics_reexe
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != Node::<Program>::default());
assert!(ast.ast != Node::<Program>::default());
// Assure we have no diagnostics.
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
@ -2714,7 +2718,7 @@ async fn kcl_test_kcl_lsp_code_and_ast_units_unchanged_but_has_memory_reexecute_
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != Node::<Program>::default());
assert!(ast.ast != Node::<Program>::default());
// Assure we have no diagnostics.
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
@ -2739,7 +2743,7 @@ async fn kcl_test_kcl_lsp_code_and_ast_units_unchanged_but_has_memory_reexecute_
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != Node::<Program>::default());
assert!(ast.ast != Node::<Program>::default());
// Assure we have no diagnostics.
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
@ -2771,7 +2775,7 @@ async fn kcl_test_kcl_lsp_cant_execute_set() {
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != Node::<Program>::default());
assert!(ast.ast != Node::<Program>::default());
// Assure we have no diagnostics.
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
@ -2795,7 +2799,7 @@ async fn kcl_test_kcl_lsp_cant_execute_set() {
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != Node::<Program>::default());
assert!(ast.ast != Node::<Program>::default());
// Assure we have no diagnostics.
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
@ -2831,7 +2835,7 @@ async fn kcl_test_kcl_lsp_cant_execute_set() {
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != default_hashed);
assert!(ast.ast != default_hashed);
// Assure we have no diagnostics.
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
@ -2862,7 +2866,7 @@ async fn kcl_test_kcl_lsp_cant_execute_set() {
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != Node::<Program>::default());
assert!(ast.ast != Node::<Program>::default());
// Assure we have no diagnostics.
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
@ -2995,7 +2999,7 @@ part001 = startSketchOn('XY')
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != Node::<Program>::default());
assert!(ast.ast != Node::<Program>::default());
// Assure we have one diagnostics.
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 1);
@ -3016,7 +3020,7 @@ part001 = startSketchOn('XY')
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != Node::<Program>::default());
assert!(ast.ast != Node::<Program>::default());
// Assure we have one diagnostics.
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 1);
@ -3109,7 +3113,7 @@ part001 = startSketchOn('XY')
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != Node::<Program>::default());
assert!(ast.ast != Node::<Program>::default());
// Send change file, but the code is the same.
server
@ -3128,7 +3132,7 @@ part001 = startSketchOn('XY')
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != Node::<Program>::default());
assert!(ast.ast != Node::<Program>::default());
// Assure we have diagnostics.
@ -3168,7 +3172,7 @@ part001 = startSketchOn('XY')
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != Node::<Program>::default());
assert!(ast.ast != Node::<Program>::default());
// Send change file, but the code is the same.
server
@ -3195,7 +3199,7 @@ NEW_LINT = 1"#
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != Node::<Program>::default());
assert!(ast.ast != Node::<Program>::default());
// Assure we have diagnostics.
@ -3302,7 +3306,7 @@ part001 = startSketchOn('XY')
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != Node::<Program>::default());
assert!(ast.ast != Node::<Program>::default());
// Get the symbols map.
let symbols_map = server.symbols_map.get("file:///test.kcl").unwrap().clone();
@ -3389,7 +3393,7 @@ part001 = startSketchOn('XY')
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != Node::<Program>::default());
assert!(ast.ast != Node::<Program>::default());
// Get the symbols map.
let symbols_map = server.symbols_map.get("file:///test.kcl").unwrap().clone();
@ -3428,7 +3432,7 @@ part001 = startSketchOn('XY')
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != Node::<Program>::default());
assert!(ast.ast != Node::<Program>::default());
// Get the symbols map.
let symbols_map = server.symbols_map.get("file:///test.kcl").unwrap().clone();

View File

@ -182,9 +182,7 @@ pub async fn modify_ast_for_sketch(
let recasted = program.ast.recast(&FormatOptions::default(), 0);
// Re-parse the ast so we get the correct source ranges.
*program = crate::parsing::parse_str(&recasted, module_id)
.parse_errs_as_err()?
.into();
program.ast = crate::parsing::parse_str(&recasted, module_id).parse_errs_as_err()?;
Ok(recasted)
}

View File

@ -275,7 +275,7 @@ impl Node<Program> {
Ok(None)
}
pub fn change_meta_settings(&mut self, settings: crate::execution::MetaSettings) -> Result<Self, KclError> {
pub fn change_meta_settings(&self, settings: crate::execution::MetaSettings) -> Result<Self, KclError> {
let mut new_program = self.clone();
let mut found = false;
for node in &mut new_program.inner_attrs {
@ -4035,7 +4035,7 @@ startSketchOn('XY')"#;
let some_program_string = r#"@settings(defaultLengthUnit = inch)
startSketchOn('XY')"#;
let mut program = crate::parsing::top_level_parse(some_program_string).unwrap();
let program = crate::parsing::top_level_parse(some_program_string).unwrap();
let result = program.meta_settings().unwrap();
assert!(result.is_some());
let meta_settings = result.unwrap();
@ -4077,7 +4077,7 @@ startSketchOn('XY')
#[tokio::test(flavor = "multi_thread")]
async fn test_parse_get_meta_settings_nothing_to_mm() {
let some_program_string = r#"startSketchOn('XY')"#;
let mut program = crate::parsing::top_level_parse(some_program_string).unwrap();
let program = crate::parsing::top_level_parse(some_program_string).unwrap();
let result = program.meta_settings().unwrap();
assert!(result.is_none());

View File

@ -84,10 +84,14 @@ async fn execute(test_name: &str, render_to_png: bool) {
let Ok(ast) = ast_res else {
return;
};
let ast = crate::Program {
ast,
original_file_contents: read("input.kcl", test_name),
};
// Run the program.
let exec_res = crate::test_server::execute_and_snapshot_ast(
ast.into(),
ast,
crate::settings::types::UnitLength::Mm,
Some(Path::new("tests").join(test_name).join("input.kcl").to_owned()),
)
@ -132,10 +136,7 @@ async fn execute(test_name: &str, render_to_png: bool) {
Box::new(miette::MietteHandlerOpts::new().show_related_errors_as_nested().build())
}))
.unwrap();
let report = error
.clone()
.into_miette_report_with_outputs(&read("input.kcl", test_name))
.unwrap();
let report = error.clone().into_miette_report_with_outputs().unwrap();
let report = miette::Report::new(report);
if previously_passed {
eprintln!("This test case failed, but it previously passed. If this is intended, and the test should actually be failing now, please delete kcl/{ok_path_str} and other associated passing artifacts");

View File

@ -1,11 +1,7 @@
//! Wasm bindings for `kcl`.
#[cfg(target_arch = "wasm32")]
mod toml;
#[cfg(target_arch = "wasm32")]
mod wasm;
#[cfg(target_arch = "wasm32")]
pub use toml::*;
#[cfg(target_arch = "wasm32")]
pub use wasm::*;

View File

@ -1,13 +0,0 @@
//! Functions for interacting with TOML files.
//! We do this in rust because the Javascript TOML libraries are actual trash.
use wasm_bindgen::prelude::wasm_bindgen;
#[wasm_bindgen]
pub fn toml_stringify(json: &str) -> Result<String, String> {
console_error_panic_hook::set_once();
let value: serde_json::Value = serde_json::from_str(json).map_err(|e| e.to_string())?;
toml::to_string_pretty(&value).map_err(|e| e.to_string())
}

View File

@ -483,9 +483,9 @@ pub fn parse_project_settings(toml_str: &str) -> Result<JsValue, String> {
JsValue::from_serde(&settings).map_err(|e| e.to_string())
}
/// Serialize the project settings.
/// Serialize the configuration settings.
#[wasm_bindgen]
pub fn serialize_project_settings(val: JsValue) -> Result<JsValue, String> {
pub fn serialize_configuration(val: JsValue) -> Result<JsValue, String> {
console_error_panic_hook::set_once();
let config: kcl_lib::Configuration = val.into_serde().map_err(|e| e.to_string())?;
@ -497,6 +497,20 @@ pub fn serialize_project_settings(val: JsValue) -> Result<JsValue, String> {
Ok(JsValue::from_str(&toml_str))
}
/// Serialize the project configuration settings.
#[wasm_bindgen]
pub fn serialize_project_configuration(val: JsValue) -> Result<JsValue, String> {
console_error_panic_hook::set_once();
let config: kcl_lib::ProjectConfiguration = val.into_serde().map_err(|e| e.to_string())?;
let toml_str = toml::to_string_pretty(&config).map_err(|e| e.to_string())?;
// The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the
// gloo-serialize crate instead.
Ok(JsValue::from_str(&toml_str))
}
static ALLOWED_DECODING_FORMATS: &[data_encoding::Encoding] = &[
data_encoding::BASE64,
data_encoding::BASE64URL,
@ -561,7 +575,7 @@ pub fn change_kcl_settings(code: &str, settings_str: &str) -> Result<String, Str
console_error_panic_hook::set_once();
let settings: kcl_lib::MetaSettings = serde_json::from_str(settings_str).map_err(|e| e.to_string())?;
let mut program = Program::parse_no_errs(code).map_err(|e| e.to_string())?;
let program = Program::parse_no_errs(code).map_err(|e| e.to_string())?;
let new_program = program.change_meta_settings(settings).map_err(|e| e.to_string())?;