fix(lsp): context-aware completions now use correct token variants
The Logos lexer produces dedicated token variants for all keywords (Token::Behavior, Token::LifeArc, etc.) — Token::Ident is only for user-defined identifiers. determine_context() was matching against Token::Ident(keyword), so last_keyword was always None and every nested block returned CompletionContext::Unknown, causing all_keyword_completions() to dump top-level declaration keywords everywhere. Changes: - determine_context(): match actual token variants (Token::Behavior, Token::LifeArc, Token::Relationship, Token::Character, etc.) and track last_context directly instead of a String keyword - behavior_keyword_completions(): add all word-based BT keywords (choose, then, repeat, if, when, invert, retry, timeout, cooldown, succeed_always, fail_always) - InFieldBlock arm: always include field_keyword_completions() so generic fields (age, bond, etc.) appear even without a species - Tests: add negative assertions confirming top-level keywords are absent inside behavior/life_arc blocks; add new test test_behavior_no_toplevel_keywords for nested block isolation
This commit is contained in:
@@ -110,10 +110,33 @@ mod tests {
|
||||
if let Some(response) = result {
|
||||
match response {
|
||||
| tower_lsp::lsp_types::CompletionResponse::Array(items) => {
|
||||
// Should have behavior tree keywords
|
||||
// Should have symbolic behavior tree keywords
|
||||
assert!(items.iter().any(|item| item.label == "?"));
|
||||
assert!(items.iter().any(|item| item.label == ">"));
|
||||
assert!(items.iter().any(|item| item.label == "*"));
|
||||
// Should have word-based behavior tree keywords
|
||||
assert!(items.iter().any(|item| item.label == "choose"));
|
||||
assert!(items.iter().any(|item| item.label == "then"));
|
||||
assert!(items.iter().any(|item| item.label == "repeat"));
|
||||
assert!(items.iter().any(|item| item.label == "if"));
|
||||
assert!(items.iter().any(|item| item.label == "when"));
|
||||
// Should NOT have top-level declaration keywords
|
||||
assert!(
|
||||
!items.iter().any(|item| item.label == "institution"),
|
||||
"institution should not appear inside behavior block"
|
||||
);
|
||||
assert!(
|
||||
!items.iter().any(|item| item.label == "species"),
|
||||
"species should not appear inside behavior block"
|
||||
);
|
||||
assert!(
|
||||
!items.iter().any(|item| item.label == "character"),
|
||||
"character should not appear inside behavior block"
|
||||
);
|
||||
assert!(
|
||||
!items.iter().any(|item| item.label == "location"),
|
||||
"location should not appear inside behavior block"
|
||||
);
|
||||
},
|
||||
| _ => panic!("Expected array response"),
|
||||
}
|
||||
@@ -136,6 +159,61 @@ mod tests {
|
||||
// Should have life arc keywords
|
||||
assert!(items.iter().any(|item| item.label == "state"));
|
||||
assert!(items.iter().any(|item| item.label == "on"));
|
||||
// Should NOT have top-level declaration keywords
|
||||
assert!(
|
||||
!items.iter().any(|item| item.label == "institution"),
|
||||
"institution should not appear inside life_arc block"
|
||||
);
|
||||
assert!(
|
||||
!items.iter().any(|item| item.label == "behavior"),
|
||||
"behavior should not appear inside life_arc block"
|
||||
);
|
||||
assert!(
|
||||
!items.iter().any(|item| item.label == "character"),
|
||||
"character should not appear inside life_arc block"
|
||||
);
|
||||
},
|
||||
| _ => panic!("Expected array response"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_behavior_no_toplevel_keywords() {
|
||||
// Cursor inside nested `then` block — context should remain InBehavior
|
||||
let source = "behavior Test {\n then {\n \n }\n}";
|
||||
let doc = Document::new(source.to_string());
|
||||
// Position inside nested then block (line 2, after spaces)
|
||||
let params = make_params(2, 8);
|
||||
|
||||
let result = completion::get_completions(&doc, ¶ms);
|
||||
assert!(result.is_some());
|
||||
|
||||
if let Some(response) = result {
|
||||
match response {
|
||||
| tower_lsp::lsp_types::CompletionResponse::Array(items) => {
|
||||
// Behavior keywords should still be present at any nesting depth
|
||||
assert!(
|
||||
items.iter().any(|item| item.label == "?"),
|
||||
"? should appear in nested behavior block"
|
||||
);
|
||||
assert!(
|
||||
items.iter().any(|item| item.label == "choose"),
|
||||
"choose should appear in nested behavior block"
|
||||
);
|
||||
// Top-level keywords must not appear
|
||||
assert!(
|
||||
!items.iter().any(|item| item.label == "institution"),
|
||||
"institution must not appear in nested behavior block"
|
||||
);
|
||||
assert!(
|
||||
!items.iter().any(|item| item.label == "species"),
|
||||
"species must not appear in nested behavior block"
|
||||
);
|
||||
assert!(
|
||||
!items.iter().any(|item| item.label == "character"),
|
||||
"character must not appear in nested behavior block"
|
||||
);
|
||||
},
|
||||
| _ => panic!("Expected array response"),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user