Add unknown ranges and open object values

This commit is contained in:
2026-08-14 08:00:22 +09:00
parent 8c50dd202d
commit 848c7f169f
53 changed files with 9047 additions and 6572 deletions
@@ -1,7 +1,7 @@
use std::collections::{BTreeMap, HashMap};
use decodal::{
Engine, HostEnvironment, HostValue, ImportLoader, LoadedImport, Result, RuntimeValue,
Engine, HostEnvironment, ImportLoader, LoadedImport, Result, Value, runtime::RuntimeValue,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -116,12 +116,12 @@ pub(crate) fn complete<E: HostEnvironment>(
let mut loader = environment.create_loader();
let loaded = loader.load(Some(key), specifier)?;
detail = match &loaded {
LoadedImport::Source(source) => source.name.clone(),
LoadedImport::Value(value) => value.key.clone(),
LoadedImport::Source { name, .. } => name.clone(),
LoadedImport::Value { key, .. } => key.clone(),
};
fields = match loaded {
LoadedImport::Source(source) => collect_fields(&tokenize(&source.source)),
LoadedImport::Value(value) => fields_from_host_value(&value.value),
LoadedImport::Source { source, .. } => collect_fields(&tokenize(&source)),
LoadedImport::Value { value, .. } => fields_from_value(&value),
};
} else if let Some(local) = local_fields.get(root) {
fields = local.clone();
@@ -229,6 +229,7 @@ fn builtin_items() -> Vec<CompletionItem> {
("Int", CompletionKind::Type, "integer constraint", 5),
("Float", CompletionKind::Type, "float constraint", 5),
("Bool", CompletionKind::Type, "boolean constraint", 5),
("Unknown", CompletionKind::Type, "unknown top range", 5),
]
.into_iter()
.map(|(label, kind, detail, priority)| CompletionItem {
@@ -267,24 +268,24 @@ fn fields_from_runtime<L: ImportLoader>(
Ok(tree)
}
fn fields_from_host_value(value: &HostValue) -> FieldTree {
fn fields_from_value(value: &Value) -> FieldTree {
match value {
HostValue::Object(fields) => fields
Value::Object { fields, .. } => fields
.iter()
.map(|field| (field.name.clone(), fields_from_host_value(&field.value)))
.map(|field| (field.name.clone(), fields_from_value(&field.value)))
.collect(),
HostValue::ArrayConstraint {
Value::ArrayRange {
default: Some(value),
..
}
| HostValue::MapConstraint {
| Value::MapRange {
default: Some(value),
..
}
| HostValue::Abstract {
| Value::Range {
default: Some(value),
..
} => fields_from_host_value(value),
} => fields_from_value(value),
_ => FieldTree::new(),
}
}
@@ -688,7 +689,7 @@ mod tests {
use std::collections::BTreeMap;
use decodal::{
Diagnostic, DiagnosticKind, EmptyLoader, HostValue, ImportCandidate, LoadedImport, Span,
Diagnostic, DiagnosticKind, EmptyLoader, ImportCandidate, LoadedImport, Span, Value,
};
use super::*;
@@ -708,15 +709,15 @@ mod tests {
if specifier == "./post.md" {
return Ok(LoadedImport::value(
"post.md",
HostValue::object([
Value::object([
(
"frontmatter",
HostValue::object([
("title", HostValue::string("Hello")),
("draft", HostValue::bool(false)),
Value::object([
("title", Value::string("Hello")),
("draft", Value::bool(false)),
]),
),
("body", HostValue::string("# Hello")),
("body", Value::string("# Hello")),
]),
));
}
@@ -758,12 +759,9 @@ mod tests {
fn configure_engine(&self, engine: &mut Engine<Self::Loader>) -> Result<()> {
engine.bind_global(
"App",
HostValue::object([(
Value::object([(
"config",
HostValue::object([
("enabled", HostValue::bool_type()),
("port", HostValue::int_type()),
]),
Value::object([("enabled", Value::bool_type()), ("port", Value::int_type())]),
)]),
)?;
Ok(())
@@ -854,6 +852,7 @@ mod tests {
.unwrap();
let labels = labels(result);
assert!(labels.contains(&"String".into()));
assert!(labels.contains(&"Unknown".into()));
assert!(labels.contains(&"service".into()));
assert!(labels.contains(&"value".into()));
assert!(labels.contains(&"App".into()));
@@ -881,6 +880,10 @@ mod tests {
.unwrap()
.unwrap();
assert!(labels(result).contains(&"String".into()));
let result = complete(&EmptyEnvironment, "main.dcdl", "Unk", 3, false)
.unwrap()
.unwrap();
assert!(labels(result).contains(&"Unknown".into()));
}
struct EmptyEnvironment;
+14 -14
View File
@@ -92,7 +92,7 @@ impl SemanticAnalysis {
#[cfg(test)]
mod tests {
use decodal::{
Data, Diagnostic, DiagnosticKind, Engine, HostValue, ImportLoader, LoadedImport, Span,
Data, Diagnostic, DiagnosticKind, Engine, ImportLoader, LoadedImport, Span, Value,
};
use super::*;
@@ -100,11 +100,11 @@ mod tests {
const ROOT: &str = r#"Post & import "./post.md""#;
struct ContentEnvironment {
draft: HostValue,
draft: Value,
}
struct ContentLoader {
draft: HostValue,
draft: Value,
}
impl ImportLoader for ContentLoader {
@@ -122,15 +122,15 @@ mod tests {
}
Ok(LoadedImport::value(
"content/post.md",
HostValue::object([
Value::object([
(
"frontmatter",
HostValue::object([
("title", HostValue::string("Hello")),
Value::object([
("title", Value::string("Hello")),
("draft", self.draft.clone()),
]),
),
("body", HostValue::string("# Hello")),
("body", Value::string("# Hello")),
]),
))
}
@@ -148,15 +148,15 @@ mod tests {
fn configure_engine(&self, engine: &mut Engine<Self::Loader>) -> decodal::Result<()> {
engine.bind_global(
"Post",
HostValue::object([
Value::object([
(
"frontmatter",
HostValue::object([
("title", HostValue::string_type()),
("draft", HostValue::bool_type()),
Value::object([
("title", Value::string_type()),
("draft", Value::bool_type()),
]),
),
("body", HostValue::string_type()),
("body", Value::string_type()),
]),
)?;
Ok(())
@@ -173,7 +173,7 @@ mod tests {
#[test]
fn injected_environment_matches_direct_evaluation() {
let environment = ContentEnvironment {
draft: HostValue::bool(false),
draft: Value::bool(false),
};
let direct = evaluate_direct(&environment).unwrap();
let service = LanguageService::new(&environment);
@@ -186,7 +186,7 @@ mod tests {
#[test]
fn injected_environment_preserves_import_diagnostics() {
let service = LanguageService::new(ContentEnvironment {
draft: HostValue::string("maybe"),
draft: Value::string("maybe"),
});
let analysis = service.analyze("main.dcdl", "main.dcdl", ROOT);