use std::collections::HashSet;
use tower_lsp::lsp_types::*;
use crate::Backend;
use crate::completion::builder::deprecation_tag;
use crate::completion::resolve::CompletionItemData;
use crate::util::strip_fqn_prefix;
fn build_constant_item(
name: String,
value: Option<String>,
sort_text: String,
is_deprecated: bool,
uri: &str,
replace_range: Option<Range>,
) -> CompletionItem {
let data = serde_json::to_value(CompletionItemData {
class_name: String::new(),
member_name: name.clone(),
kind: "global_constant".to_string(),
uri: uri.to_string(),
extra_class_names: vec![],
})
.ok();
let text_edit = replace_range.map(|range| {
CompletionTextEdit::Edit(TextEdit {
range,
new_text: name.clone(),
})
});
CompletionItem {
label: name.clone(),
kind: Some(CompletionItemKind::CONSTANT),
detail: value,
insert_text: Some(name.clone()),
filter_text: Some(name),
sort_text: Some(sort_text),
tags: deprecation_tag(is_deprecated),
text_edit,
data,
..CompletionItem::default()
}
}
impl Backend {
const MAX_CONSTANT_COMPLETIONS: usize = 100;
pub(crate) fn build_constant_completions(
&self,
prefix: &str,
uri: &str,
position: Position,
) -> (Vec<CompletionItem>, bool) {
let prefix_lower = strip_fqn_prefix(prefix).to_lowercase();
let mut seen: HashSet<String> = HashSet::new();
let mut items: Vec<CompletionItem> = Vec::new();
let replace_range = if prefix.contains('\\') {
Some(Range {
start: Position {
line: position.line,
character: position
.character
.saturating_sub(prefix.chars().count() as u32),
},
end: position,
})
} else {
None
};
{
let dmap = self.global_defines.read();
for (name, info) in dmap.iter() {
if !name.to_lowercase().contains(&prefix_lower) {
continue;
}
if !seen.insert(name.clone()) {
continue;
}
items.push(build_constant_item(
name.clone(),
info.value.clone(),
format!("5_{}", name.to_lowercase()),
false,
uri,
replace_range,
));
}
}
{
let idx = self.autoload_constant_index.read();
let dmap = self.global_defines.read();
for (name, _path) in idx.iter() {
if !name.to_lowercase().contains(&prefix_lower) {
continue;
}
if !seen.insert(name.clone()) {
continue;
}
let value = dmap.get(name.as_str()).and_then(|info| info.value.clone());
items.push(build_constant_item(
name.clone(),
value,
format!("5_{}", name.to_lowercase()),
false,
uri,
replace_range,
));
}
}
let stub_const_idx = self.stub_constant_index.read();
for &name in stub_const_idx.keys() {
if !name.to_lowercase().contains(&prefix_lower) {
continue;
}
if !seen.insert(name.to_string()) {
continue;
}
items.push(build_constant_item(
name.to_string(),
None,
format!("6_{}", name.to_lowercase()),
false,
uri,
replace_range,
));
}
let is_incomplete = items.len() > Self::MAX_CONSTANT_COMPLETIONS;
if is_incomplete {
items.sort_by(|a, b| a.sort_text.cmp(&b.sort_text));
items.truncate(Self::MAX_CONSTANT_COMPLETIONS);
}
(items, is_incomplete)
}
}