glua_ls 1.0.27

Language server for Garry's Mod Lua (GLua).
Documentation
use glua_code_analysis::{DbIndex, LuaCompilation, SemanticModel};
use lsp_types::{CompletionItem, Documentation, MarkedString, MarkupContent};

use crate::{
    context::ClientId,
    handlers::hover::{HoverBuilder, build_hover_content_for_completion},
};

use super::{
    add_completions::color_preview_documentation,
    completion_data::{CompletionColorInfo, CompletionData, CompletionDataType},
};

pub fn resolve_completion(
    compilation: &LuaCompilation,
    semantic_model: &SemanticModel,
    db: &DbIndex,
    completion_item: &mut CompletionItem,
    completion_data: CompletionData,
    client_id: ClientId,
) -> Option<()> {
    // todo: resolve completion
    let color = completion_data.color.clone();
    match completion_data.typ {
        CompletionDataType::PropertyOwnerId(property_id) => {
            let hover_builder =
                build_hover_content_for_completion(compilation, semantic_model, db, property_id);
            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);
            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));
                }
            }
        }
        _ => {}
    }
    if let Some(color) = color {
        apply_color_completion_documentation(completion_item, &color);
    }
    Some(())
}

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 realm_badge = hover_builder.realm_badge_markdown();

    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;

        if let Some(realm_badge) = realm_badge {
            result.push_str(&format!("\n{}\n", realm_badge));
        }

        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 realm_badge = hover_builder.realm_badge_markdown();

    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));
    }

    if let Some(realm_badge) = realm_badge {
        result.push_str(&format!("\n{}\n", realm_badge));
    }

    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(())
}

fn apply_color_completion_documentation(
    completion_item: &mut CompletionItem,
    color: &CompletionColorInfo,
) {
    let preview_documentation = color_preview_documentation(color);

    match completion_item.documentation.take() {
        Some(Documentation::MarkupContent(mut markup)) => {
            if !markup.value.contains(&preview_documentation) {
                if !markup.value.trim().is_empty() {
                    markup.value.push_str("\n\n");
                }
                markup.value.push_str(&preview_documentation);
            }
            completion_item.documentation = Some(Documentation::MarkupContent(markup));
        }
        Some(Documentation::String(mut text)) => {
            if !text.contains(&preview_documentation) {
                if !text.trim().is_empty() {
                    text.push_str("\n\n");
                }
                text.push_str(&preview_documentation);
            }
            completion_item.documentation = Some(Documentation::MarkupContent(MarkupContent {
                kind: lsp_types::MarkupKind::Markdown,
                value: text,
            }));
        }
        None => {
            completion_item.documentation = Some(Documentation::MarkupContent(MarkupContent {
                kind: lsp_types::MarkupKind::Markdown,
                value: preview_documentation,
            }));
        }
    }
}