use emmylua_code_analysis::{DbIndex, LuaCompilation, SemanticModel};
use emmylua_parser::{LuaAstNode, LuaSyntaxToken};
use lsp_types::{CompletionItem, Documentation, MarkedString, MarkupContent};
use rowan::{TextSize, TokenAtOffset};
use crate::{
context::ClientId,
handlers::hover::{HoverBuilder, build_hover_content_for_completion},
};
use super::completion_data::{CompletionData, CompletionDataType};
pub fn resolve_completion(
compilation: &LuaCompilation,
semantic_model: &SemanticModel,
db: &DbIndex,
completion_item: &mut CompletionItem,
completion_data: CompletionData,
client_id: ClientId,
) -> Option<()> {
let trigger_token =
get_completion_trigger_token(semantic_model, completion_data.trigger_offset);
match completion_data.typ {
CompletionDataType::PropertyOwnerId(property_id) => {
let hover_builder = build_hover_content_for_completion(
compilation,
semantic_model,
db,
property_id,
trigger_token.clone(),
);
if let Some(mut hover_builder) = hover_builder {
update_function_signature_info(&mut hover_builder, completion_data.overload_count);
if client_id.is_vscode() {
build_vscode_completion_item(completion_item, hover_builder, None);
} else {
build_other_completion_item(completion_item, hover_builder, None);
}
}
}
CompletionDataType::Overload((property_id, index)) => {
let hover_builder = build_hover_content_for_completion(
compilation,
semantic_model,
db,
property_id,
trigger_token.clone(),
);
if let Some(mut hover_builder) = hover_builder {
update_function_signature_info(&mut hover_builder, completion_data.overload_count);
if client_id.is_vscode() {
build_vscode_completion_item(completion_item, hover_builder, Some(index));
} else {
build_other_completion_item(completion_item, hover_builder, Some(index));
}
}
}
_ => {}
}
Some(())
}
fn get_completion_trigger_token(
semantic_model: &SemanticModel,
trigger_offset: Option<u32>,
) -> Option<LuaSyntaxToken> {
let offset = TextSize::from(trigger_offset?);
let root = semantic_model.get_root();
if offset > root.syntax().text_range().end() {
return None;
}
match root.syntax().token_at_offset(offset) {
TokenAtOffset::Single(token) => Some(token),
TokenAtOffset::Between(left, _) => Some(left),
TokenAtOffset::None => None,
}
}
pub fn update_function_signature_info(
hover_builder: &mut HoverBuilder,
overload_count: Option<usize>,
) {
if let Some(overload_count) = overload_count
&& overload_count > 0
{
if let Some(signature_overload) = &mut hover_builder.signature_overload {
for signature in signature_overload.iter_mut() {
if let MarkedString::LanguageString(s) = signature {
s.value = format!("{} (+{} overloads)", s.value, overload_count);
}
}
}
if let MarkedString::LanguageString(s) = &mut hover_builder.primary {
s.value = format!("{} (+{} overloads)", s.value, overload_count);
}
}
}
fn build_vscode_completion_item(
completion_item: &mut CompletionItem,
hover_builder: HoverBuilder,
overload_index: Option<usize>,
) -> Option<()> {
let type_description = overload_index
.and_then(|index| {
hover_builder
.signature_overload
.and_then(|overloads| overloads.get(index).cloned())
})
.unwrap_or_else(|| hover_builder.primary.clone());
match type_description {
MarkedString::String(s) => {
completion_item.detail = Some(s);
}
MarkedString::LanguageString(s) => {
completion_item.detail = Some(s.value);
}
}
let documentation = {
let mut result = String::new();
let mut first_line = true;
for description in hover_builder.annotation_description {
match description {
MarkedString::String(s) => {
if first_line && s == "---" {
first_line = false;
} else {
result.push_str(&format!("\n{}\n", s));
}
}
MarkedString::LanguageString(s) => {
result.push_str(&format!("\n```{}\n{}\n```\n", s.language, s.value));
}
}
}
if let Some(type_expansion) = hover_builder.type_expansion {
for type_expansion in type_expansion {
result.push_str(&format!("\n```{}\n{}\n```\n", "lua", type_expansion));
}
}
result.trim_end().to_string()
};
if !documentation.is_empty() {
completion_item.documentation = Some(Documentation::MarkupContent(MarkupContent {
kind: lsp_types::MarkupKind::Markdown,
value: documentation,
}));
}
Some(())
}
fn build_other_completion_item(
completion_item: &mut CompletionItem,
hover_builder: HoverBuilder,
overload_index: Option<usize>,
) -> Option<()> {
let mut result = String::new();
let type_description = overload_index
.and_then(|index| {
hover_builder
.signature_overload
.and_then(|overloads| overloads.get(index).cloned())
})
.unwrap_or_else(|| hover_builder.primary.clone());
match type_description {
MarkedString::String(s) => {
result.push_str(&format!("\n{}\n", s));
}
MarkedString::LanguageString(s) => {
result.push_str(&format!("\n```{}\n{}\n```\n", s.language, s.value));
}
}
if let Some(MarkedString::String(s)) = hover_builder.location_path {
result.push_str(&format!("\n{}\n", s));
}
for marked_string in hover_builder.annotation_description {
match marked_string {
MarkedString::String(s) => {
result.push_str(&format!("\n{}\n", s));
}
MarkedString::LanguageString(s) => {
result.push_str(&format!("\n```{}\n{}\n```\n", s.language, s.value));
}
}
}
if let Some(type_expansion) = hover_builder.type_expansion {
for type_expansion in type_expansion {
result.push_str(&format!("\n```{}\n{}\n```\n", "lua", type_expansion));
}
}
completion_item.documentation = Some(Documentation::MarkupContent(MarkupContent {
kind: lsp_types::MarkupKind::Markdown,
value: result,
}));
Some(())
}