emmylua_ls 0.4.1

A language server for emmylua.
use emmylua_code_analysis::{LuaMemberKey, LuaMemberOwner, SemanticModel};
use emmylua_parser::{LuaAstToken, LuaDocTagSee, LuaNameToken};
use lsp_types::GotoDefinitionResponse;

pub fn goto_doc_see(
    semantic_model: &SemanticModel,
    doc_see: LuaDocTagSee,
    see_name: LuaNameToken,
) -> Option<GotoDefinitionResponse> {
    let name_tokens = doc_see.get_names();
    let mut name_parts = Vec::new();
    for name_token in name_tokens {
        let name = name_token.get_name_text();
        name_parts.push(name.to_string());
        if name_token.get_position() == see_name.get_position() {
            break;
        }
    }

    match name_parts.len() {
        1 => {
            let name = &name_parts[0];
            return goto_type(semantic_model, &name);
        }
        2 => {
            let type_name = &name_parts[0];
            let member_name = &name_parts[1];
            return goto_type_member(semantic_model, &type_name, &member_name);
        }
        _ => {}
    }

    None
}

fn goto_type(semantic_model: &SemanticModel, type_name: &str) -> Option<GotoDefinitionResponse> {
    let file_id = semantic_model.get_file_id();
    let type_decl = semantic_model
        .get_db()
        .get_type_index()
        .find_type_decl(file_id, type_name)?;
    let locations = type_decl.get_locations();
    let mut result = Vec::new();
    for location in locations {
        let document = semantic_model.get_document_by_file_id(location.file_id)?;
        let lsp_location = document.to_lsp_location(location.range)?;
        result.push(lsp_location);
    }

    Some(GotoDefinitionResponse::Array(result))
}

fn goto_type_member(
    semantic_model: &SemanticModel,
    type_name: &str,
    member_name: &str,
) -> Option<GotoDefinitionResponse> {
    let file_id = semantic_model.get_file_id();
    let type_decl = semantic_model
        .get_db()
        .get_type_index()
        .find_type_decl(file_id, type_name)?;
    let type_id = type_decl.get_id();
    let member_owner = LuaMemberOwner::Type(type_id);
    let member_map = semantic_model
        .get_db()
        .get_member_index()
        .get_member_map(member_owner)?;
    let member_id = member_map.get(&LuaMemberKey::Name(member_name.to_string().into()))?;
    let member = semantic_model
        .get_db()
        .get_member_index()
        .get_member(member_id)?;
    let file_id = member.get_file_id();
    let member_range = member.get_range();
    let document = semantic_model.get_document_by_file_id(file_id)?;
    let lsp_location = document.to_lsp_location(member_range)?;

    Some(GotoDefinitionResponse::Scalar(lsp_location))
}