use glua_code_analysis::{EmmyrcGmodOutlineVerbosity, LuaDeclId, LuaSignatureId, LuaType};
use glua_parser::{
LuaAssignStat, LuaAstNode, LuaAstToken, LuaDoStat, LuaExpr, LuaForRangeStat, LuaForStat,
LuaFuncStat, LuaIfClauseStat, LuaIfStat, LuaLocalFuncStat, LuaLocalStat, LuaSyntaxId,
};
use lsp_types::SymbolKind;
use super::builder::{DocumentSymbolBuilder, LuaSymbol};
#[derive(Clone)]
pub struct SymbolBinding {
pub symbol_id: LuaSyntaxId,
pub value_expr: Option<LuaExpr>,
}
pub struct IfSymbolContext {
pub if_id: LuaSyntaxId,
pub clause_symbols: Vec<(LuaIfClauseStat, LuaSyntaxId)>,
}
pub fn build_local_stat_symbol(
builder: &mut DocumentSymbolBuilder,
local_stat: LuaLocalStat,
parent_id: LuaSyntaxId,
) -> Option<Vec<SymbolBinding>> {
let file_id = builder.get_file_id();
let local_names: Vec<_> = local_stat.get_local_name_list().collect();
let local_values: Vec<_> = local_stat.get_value_exprs().collect();
let simple_local = local_names.len() == 1;
let mut bindings = Vec::new();
for (index, local_name) in local_names.into_iter().enumerate() {
let decl_id = LuaDeclId::new(file_id, local_name.get_position());
let decl = builder.get_decl(&decl_id)?;
let typ = builder.get_type(decl_id.into());
let desc = builder.get_symbol_kind_and_detail(Some(&typ));
let mut symbol_name = decl.get_name().to_string();
let mut symbol_kind = desc.0;
let vgui_name = builder.get_vgui_panel_name(&decl_id).map(str::to_string);
if vgui_name.is_some() && builder.scripted_class_global_name() == Some(decl.get_name()) {
if let Some(scripted_symbol_id) = builder.get_scripted_class_symbol_id() {
builder.bind_decl_symbol(decl_id, scripted_symbol_id);
let value_expr = local_values.get(index).cloned();
bindings.push(SymbolBinding {
symbol_id: scripted_symbol_id,
value_expr,
});
continue;
}
}
if vgui_name.is_none()
&& builder.get_verbosity() != EmmyrcGmodOutlineVerbosity::Verbose
&& !builder.is_type_interesting(&typ)
{
continue;
}
let symbol = if let Some(panel_name) = vgui_name {
symbol_name = panel_name;
symbol_kind = SymbolKind::CLASS;
let range = if simple_local {
local_stat.get_range()
} else {
decl.get_range()
};
LuaSymbol::with_selection_range(
symbol_name,
desc.1,
symbol_kind,
range,
decl.get_range(),
)
} else {
let range = if simple_local {
local_stat.get_range()
} else {
decl.get_range()
};
LuaSymbol::new(symbol_name, desc.1, symbol_kind, range)
};
let symbol_id =
builder.add_node_symbol(local_name.syntax().clone(), symbol, Some(parent_id));
builder.bind_decl_symbol(decl_id, symbol_id);
let value_expr = local_values.get(index).cloned();
bindings.push(SymbolBinding {
symbol_id,
value_expr,
});
}
Some(bindings)
}
pub fn build_assign_stat_symbol(
builder: &mut DocumentSymbolBuilder,
assign_stat: LuaAssignStat,
parent_id: LuaSyntaxId,
) -> Option<Vec<SymbolBinding>> {
let file_id = builder.get_file_id();
let (vars, exprs) = assign_stat.get_var_and_expr_list();
let simple_var = vars.len() == 1;
let mut bindings = Vec::new();
for (index, var) in vars.into_iter().enumerate() {
let decl_id = LuaDeclId::new(file_id, var.get_position());
let decl = match builder.get_decl(&decl_id) {
Some(decl) => decl,
None => continue,
};
let range = if simple_var {
assign_stat.get_range()
} else {
decl.get_range()
};
let typ = builder.get_type(decl_id.into());
let desc = builder.get_symbol_kind_and_detail(Some(&typ));
let mut symbol_name = decl.get_name().to_string();
let mut symbol_kind = desc.0;
let vgui_name = builder.get_vgui_panel_name(&decl_id).map(str::to_string);
if vgui_name.is_some() && builder.scripted_class_global_name() == Some(decl.get_name()) {
if let Some(scripted_symbol_id) = builder.get_scripted_class_symbol_id() {
builder.bind_decl_symbol(decl_id, scripted_symbol_id);
let value_expr = exprs.get(index).cloned();
bindings.push(SymbolBinding {
symbol_id: scripted_symbol_id,
value_expr,
});
continue;
}
}
if vgui_name.is_none()
&& builder.get_verbosity() != EmmyrcGmodOutlineVerbosity::Verbose
&& !builder.is_type_interesting(&typ)
{
continue;
}
let symbol = if let Some(panel_name) = vgui_name {
symbol_name = panel_name;
symbol_kind = SymbolKind::CLASS;
LuaSymbol::with_selection_range(
symbol_name,
desc.1,
symbol_kind,
range,
decl.get_range(),
)
} else {
LuaSymbol::new(symbol_name, desc.1, symbol_kind, range)
};
let symbol_id = builder.add_node_symbol(var.syntax().clone(), symbol, Some(parent_id));
builder.bind_decl_symbol(decl_id, symbol_id);
let value_expr = exprs.get(index).cloned();
bindings.push(SymbolBinding {
symbol_id,
value_expr,
});
}
Some(bindings)
}
pub fn build_for_stat_symbol(
builder: &mut DocumentSymbolBuilder,
for_stat: LuaForStat,
parent_id: LuaSyntaxId,
) -> Option<LuaSyntaxId> {
let file_id = builder.get_file_id();
let for_symbol = LuaSymbol::new(
"for".to_string(),
None,
SymbolKind::MODULE,
for_stat.get_range(),
);
let for_symbol_id =
builder.add_node_symbol(for_stat.syntax().clone(), for_symbol, Some(parent_id));
let iter_token = for_stat.get_var_name()?;
let decl_id = LuaDeclId::new(file_id, iter_token.get_position());
let decl = builder.get_decl(&decl_id)?;
let typ = builder.get_type(decl_id.into());
let desc = builder.get_symbol_kind_and_detail(Some(&typ));
let symbol = LuaSymbol::new(
decl.get_name().to_string(),
desc.1,
desc.0,
decl.get_range(),
);
builder.add_token_symbol(iter_token.syntax().clone(), symbol, Some(for_symbol_id));
Some(for_symbol_id)
}
pub fn build_for_range_stat_symbol(
builder: &mut DocumentSymbolBuilder,
for_range_stat: LuaForRangeStat,
parent_id: LuaSyntaxId,
) -> Option<LuaSyntaxId> {
let file_id = builder.get_file_id();
let for_symbol = LuaSymbol::new(
"for in".to_string(),
None,
SymbolKind::MODULE,
for_range_stat.get_range(),
);
let for_in_id =
builder.add_node_symbol(for_range_stat.syntax().clone(), for_symbol, Some(parent_id));
let vars = for_range_stat.get_var_name_list();
for var in vars {
let decl_id = LuaDeclId::new(file_id, var.get_position());
let decl = builder.get_decl(&decl_id)?;
let typ = builder.get_type(decl_id.into());
let desc = builder.get_symbol_kind_and_detail(Some(&typ));
let symbol = LuaSymbol::new(
decl.get_name().to_string(),
desc.1,
desc.0,
decl.get_range(),
);
builder.add_token_symbol(var.syntax().clone(), symbol, Some(for_in_id));
}
Some(for_in_id)
}
pub fn build_local_func_stat_symbol(
builder: &mut DocumentSymbolBuilder,
local_func: LuaLocalFuncStat,
parent_id: LuaSyntaxId,
) -> Option<LuaSyntaxId> {
let file_id = builder.get_file_id();
let func_name = local_func.get_local_name()?;
let decl_id = LuaDeclId::new(file_id, func_name.get_position());
let decl = builder.get_decl(&decl_id)?;
let typ = builder.get_type(decl_id.into());
let desc = builder.get_symbol_kind_and_detail(Some(&typ));
let full_range = local_func.get_range();
let name_range = decl.get_range();
let symbol = LuaSymbol::with_selection_range(
decl.get_name().to_string(),
desc.1,
desc.0,
full_range,
name_range,
);
let func_id = builder.add_node_symbol(local_func.syntax().clone(), symbol, Some(parent_id));
Some(func_id)
}
pub fn build_func_stat_symbol(
builder: &mut DocumentSymbolBuilder,
func: LuaFuncStat,
parent_id: LuaSyntaxId,
) -> Option<LuaSyntaxId> {
let file_id = builder.get_file_id();
let func_name = func.get_func_name()?;
let name = func_name.syntax().text().to_string();
let closure = func.get_closure()?;
let signature_id = LuaSignatureId::from_closure(file_id, &closure);
let func_ty = LuaType::Signature(signature_id);
let desc = builder.get_symbol_kind_and_detail(Some(&func_ty));
let full_range = func.get_range();
let name_range = func_name.get_range();
let symbol = LuaSymbol::with_selection_range(name, desc.1, desc.0, full_range, name_range);
let func_id = builder.add_node_symbol(func.syntax().clone(), symbol, Some(parent_id));
Some(func_id)
}
pub fn build_if_stat_symbol(
builder: &mut DocumentSymbolBuilder,
if_stat: LuaIfStat,
parent_id: LuaSyntaxId,
) -> Option<IfSymbolContext> {
let if_symbol = LuaSymbol::new(
"if".to_string(),
None,
SymbolKind::MODULE,
if_stat.get_range(),
);
let if_id = builder.add_node_symbol(if_stat.syntax().clone(), if_symbol, Some(parent_id));
let mut clause_symbols = Vec::new();
for branch in if_stat.get_all_clause() {
let name = match &branch {
LuaIfClauseStat::Else(_) => "else",
LuaIfClauseStat::ElseIf(_) => "elseif",
};
let symbol = LuaSymbol::new(
name.to_string(),
None,
SymbolKind::MODULE,
branch.get_range(),
);
let clause_id = builder.add_node_symbol(branch.syntax().clone(), symbol, Some(if_id));
clause_symbols.push((branch, clause_id));
}
Some(IfSymbolContext {
if_id,
clause_symbols,
})
}
pub fn build_do_stat_symbol(
builder: &mut DocumentSymbolBuilder,
do_stat: LuaDoStat,
parent_id: LuaSyntaxId,
) -> Option<LuaSyntaxId> {
let symbol = LuaSymbol::new(
"do end".to_string(),
None,
SymbolKind::MODULE,
do_stat.get_range(),
);
let do_id = builder.add_node_symbol(do_stat.syntax().clone(), symbol, Some(parent_id));
Some(do_id)
}