Fix to consider only @settings to not be worth preserving

This commit is contained in:
Jonathan Tran
2025-03-18 13:48:18 -04:00
parent 27c2c50508
commit 0409b3159c
8 changed files with 108 additions and 4 deletions

View File

@ -1252,7 +1252,7 @@ impl std::fmt::Display for UnitType {
// TODO called UnitLen so as not to clash with UnitLength in settings)
/// A unit of length.
#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Eq)]
#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(tag = "type")]
pub enum UnitLen {
@ -1334,7 +1334,7 @@ impl From<UnitLen> for kittycad_modeling_cmds::units::UnitLength {
}
/// A unit of angle.
#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Eq)]
#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(tag = "type")]
pub enum UnitAngle {

View File

@ -301,7 +301,7 @@ impl ModuleState {
}
}
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(rename_all = "camelCase")]
pub struct MetaSettings {

View File

@ -195,6 +195,10 @@ impl Program {
})
}
pub fn is_empty_or_only_settings(&self) -> bool {
self.ast.is_empty_or_only_settings()
}
pub fn lint_all(&self) -> Result<Vec<lint::Discovered>, anyhow::Error> {
self.ast.lint_all()
}

View File

@ -346,6 +346,25 @@ impl Node<Program> {
Ok(new_program)
}
/// Returns true if the given KCL is empty or only contains settings that
/// would be auto-generated.
///
/// TODO: Don't consider comments to be empty since they will get blown away
/// by the UI.
pub fn is_empty_or_only_settings(&self) -> bool {
if !self.body.is_empty() {
return false;
}
for item in &self.inner_attrs {
if item.name() != Some(annotations::SETTINGS) {
return false;
}
}
true
}
}
impl Program {
@ -3452,6 +3471,34 @@ mod tests {
use super::*;
#[track_caller]
fn parse(code: &str) -> Node<Program> {
crate::parsing::top_level_parse(code).unwrap()
}
#[test]
fn test_empty_or_only_settings() {
// Empty is empty.
assert!(parse("").is_empty_or_only_settings());
// Whitespace is empty.
assert!(parse(" ").is_empty_or_only_settings());
// Settings are empty.
assert!(parse(r#"@settings(defaultLengthUnit = mm)"#).is_empty_or_only_settings());
// Any statement is not empty.
assert!(!parse("5").is_empty_or_only_settings());
// Any statement is not empty, even with settings.
let code = r#"@settings(defaultLengthUnit = mm)
5"#;
assert!(!parse(code).is_empty_or_only_settings());
// Non-settings attributes are not empty.
assert!(!parse("@foo").is_empty_or_only_settings());
}
// We have this as a test so we can ensure it never panics with an unwrap in the server.
#[test]
fn test_variable_kind_to_completion() {

View File

@ -269,6 +269,17 @@ pub fn change_kcl_settings(code: &str, settings_str: &str) -> Result<String, Str
Ok(formatted)
}
/// Returns true if the given KCL is empty or only contains settings that would
/// be auto-generated.
#[wasm_bindgen]
pub fn is_kcl_empty_or_only_settings(program_json: &str) -> Result<JsValue, String> {
console_error_panic_hook::set_once();
let program: Program = serde_json::from_str(program_json).map_err(|e| e.to_string())?;
JsValue::from_serde(&program.is_empty_or_only_settings()).map_err(|e| e.to_string())
}
/// Get the version of the kcl library.
#[wasm_bindgen]
pub fn get_kcl_version() -> String {

View File

@ -18,6 +18,7 @@ import {
serialize_project_configuration,
serialize_configuration,
reloadModule,
is_kcl_empty_or_only_settings,
} from 'lib/wasm_lib_wrapper'
import { KCLError } from './errors'
@ -609,6 +610,40 @@ export function changeKclSettings(
}
}
/**
* Returns true if the given KCL is empty or only contains settings that would
* be auto-generated.
*/
export function isKclEmptyOrOnlySettings(
kcl: string | Node<Program>
): boolean | Error {
if (kcl === '') {
// Fast path.
return true
}
let program: Node<Program>
if (typeof kcl === 'string') {
const parseResult = parse(kcl)
if (err(parseResult)) return parseResult
if (!resultIsOk(parseResult)) {
return new Error(`parse result had errors`, { cause: parseResult })
}
program = parseResult.program
} else {
program = kcl
}
try {
return is_kcl_empty_or_only_settings(JSON.stringify(program))
} catch (e) {
console.error('Caught error checking if KCL is empty', e)
return new Error('Caught error checking if KCL is empty', {
cause: e,
})
}
}
/**
* Convert a `UnitLength` (used in settings and modeling commands) to a
* `UnitLen` (used in execution).

View File

@ -22,6 +22,7 @@ import {
base64_decode as Base64Decode,
kcl_settings as KclSettings,
change_kcl_settings as ChangeKclSettings,
is_kcl_empty_or_only_settings as IsKclEmptyOrOnlySettings,
get_kcl_version as GetKclVersion,
serialize_configuration as SerializeConfiguration,
serialize_project_configuration as SerializeProjectConfiguration,
@ -93,6 +94,11 @@ export const kcl_settings: typeof KclSettings = (...args) => {
export const change_kcl_settings: typeof ChangeKclSettings = (...args) => {
return getModule().change_kcl_settings(...args)
}
export const is_kcl_empty_or_only_settings: typeof IsKclEmptyOrOnlySettings = (
...args
) => {
return getModule().is_kcl_empty_or_only_settings(...args)
}
export const get_kcl_version: typeof GetKclVersion = () => {
return getModule().get_kcl_version()
}

View File

@ -14,6 +14,7 @@ import { useFileContext } from 'hooks/useFileContext'
import { useLspContext } from 'components/LspProvider'
import { reportRejection } from 'lib/trap'
import { useSettings } from 'machines/appMachine'
import { isKclEmptyOrOnlySettings } from 'lang/wasm'
/**
* Show either a welcome screen or a warning screen
@ -21,7 +22,7 @@ import { useSettings } from 'machines/appMachine'
*/
export default function OnboardingIntroduction() {
const [shouldShowWarning, setShouldShowWarning] = useState(
codeManager.code !== '' && codeManager.code !== bracket
!isKclEmptyOrOnlySettings(codeManager.code) && codeManager.code !== bracket
)
return shouldShowWarning ? (