pub fn keyword_hover(token: &cpp_parser::CppSyntaxToken) -> Option<String> {
let kind = cpp_parser::CppTokenKind::from(token.kind());
if !cpp_parser::is_keyword(kind) {
return None;
}
let word = token.text().to_ascii_lowercase();
known(&format!("keyword.{word}"))
}
pub fn command_hover(name: &str) -> Option<String> {
known(&format!("command.{}", name.to_ascii_lowercase()))
}
pub fn cuda_hover(token: &cpp_parser::CppSyntaxToken, cuda: bool) -> Option<String> {
if !cuda {
return None;
}
let word = cpp_code_analysis::cuda::word(token.text())?;
Some(known(word.hover_key).unwrap_or_else(|| word.detail.to_string()))
}
fn known(key: &str) -> Option<String> {
let text = t!(key);
(!text.is_empty() && text != key).then(|| text.to_string())
}
#[cfg(test)]
mod tests {
use super::*;
fn token_of(source: &str, needle: &str, cuda: bool) -> cpp_parser::CppSyntaxToken {
let tree = cpp_parser::CppParser::parse(
source,
cpp_parser::ParserConfig::default().with_cuda(cuda),
);
let at = source.find(needle).expect("the fixture names it") + needle.len() / 2;
cpp_parser::token_at(&tree.get_red_root(), at).expect("a token at the offset")
}
#[test]
fn every_coroutine_keyword_has_its_popup_in_every_locale() {
const EN: &str = include_str!("../../../locales/en.yml");
const ZH: &str = include_str!("../../../locales/zh-CN.yml");
for word in ["co_await", "co_yield", "co_return"] {
for (name, source) in [("en", EN), ("zh-CN", ZH)] {
let key = format!("{word}:");
assert!(
source
.lines()
.any(|line| line.trim_start().starts_with(&key)),
"`{word}` has no entry in {name}.yml"
);
}
}
let source = "int f() {\n co_return 1;\n}\n";
let token = token_of(source, "co_return", false);
let text = keyword_hover(&token).expect("a keyword with an entry has a popup");
assert!(
text.contains("co_return"),
"the popup names the word: {text}"
);
}
#[test]
fn every_cuda_word_has_its_popup_in_every_locale() {
const EN: &str = include_str!("../../../locales/en.yml");
const ZH: &str = include_str!("../../../locales/zh-CN.yml");
for word in cpp_code_analysis::cuda::words() {
let leaf = word
.hover_key
.strip_prefix("cuda.")
.expect("a key under the cuda section");
let line = format!("{leaf}:");
for (name, source) in [("en", EN), ("zh-CN", ZH)] {
assert!(
source
.lines()
.any(|line_of| line_of.trim_start().starts_with(&line)),
"{} has no `{leaf}` entry in {name}.yml",
word.name
);
}
}
}
#[test]
fn a_qualifier_is_answered_only_where_it_is_a_qualifier() {
let source = "__global__ void kernel() { }\n";
let as_cuda = token_of(source, "__global__", true);
let text = cuda_hover(&as_cuda, true).expect("a CUDA project has an answer");
assert!(
text.contains("__global__"),
"the popup names the word: {text}"
);
assert!(text.contains("kernel"), "and says what it is: {text}");
assert!(
cuda_hover(&as_cuda, false).is_none(),
"the same token in a project that is not CUDA is just a name"
);
}
#[test]
fn the_toolkits_own_names_are_answered() {
let source = "void f() { unsigned i = threadIdx.x; float4 v; }\n";
let builtin = token_of(source, "threadIdx", true);
let text = cuda_hover(&builtin, true).expect("`threadIdx` has an answer");
assert!(text.contains("threadIdx"), "{text}");
let vector = token_of(source, "float4", true);
let text = cuda_hover(&vector, true).expect("`float4` has an answer");
assert!(text.contains("float4"), "{text}");
let other = token_of("void f() { int value = 1; }\n", "value", true);
assert!(cuda_hover(&other, true).is_none());
let mistyped = token_of("void f() { int ThreadIdx = 1; }\n", "ThreadIdx", true);
assert!(cuda_hover(&mistyped, true).is_none(), "case matters");
}
}