|
1 | 1 | use emmylua_code_analysis::{
|
2 | 2 | DbIndex, LuaDeclId, LuaDocument, LuaMember, LuaMemberId, LuaMemberKey, LuaSemanticDeclId,
|
3 |
| - LuaSignatureId, LuaType, LuaTypeDeclId, RenderLevel, SemanticInfo, SemanticModel, |
| 3 | + LuaSignatureId, LuaType, LuaTypeDeclId, RenderLevel, SemanticDeclLevel, SemanticInfo, |
| 4 | + SemanticModel, |
| 5 | +}; |
| 6 | +use emmylua_parser::{ |
| 7 | + LuaAssignStat, LuaAstNode, LuaExpr, LuaSyntaxKind, LuaSyntaxToken, LuaTableExpr, LuaTableField, |
4 | 8 | };
|
5 |
| -use emmylua_parser::{LuaAssignStat, LuaAstNode, LuaExpr, LuaSyntaxToken}; |
6 | 9 | use lsp_types::{Hover, HoverContents, MarkedString, MarkupContent};
|
| 10 | +use std::collections::HashSet; |
7 | 11 |
|
8 | 12 | use emmylua_code_analysis::humanize_type;
|
9 | 13 |
|
10 |
| -use crate::handlers::hover::{ |
11 |
| - find_function_member_origin_owner, function_humanize::is_function, |
12 |
| - hover_humanize::hover_humanize_type, |
13 |
| -}; |
| 14 | +use crate::handlers::hover::{function_humanize::is_function, hover_humanize::hover_humanize_type}; |
14 | 15 |
|
15 | 16 | use super::{
|
16 |
| - function_humanize::find_function_decl_origin_owner, |
17 | 17 | hover_builder::HoverBuilder,
|
18 | 18 | hover_humanize::{hover_const_type, hover_function_type},
|
19 | 19 | };
|
@@ -119,8 +119,7 @@ fn build_decl_hover(
|
119 | 119 |
|
120 | 120 | // 处理类型签名
|
121 | 121 | if is_function(&typ) {
|
122 |
| - let origin_decl_id = |
123 |
| - find_function_decl_origin_owner(builder.semantic_model, decl_id.clone()); |
| 122 | + let origin_decl_id = find_decl_origin_owner(builder.semantic_model, decl_id.clone()); |
124 | 123 | match origin_decl_id {
|
125 | 124 | Some(LuaSemanticDeclId::Member(member_id)) => {
|
126 | 125 | origin_member = Some(db.get_member_index().get_member(&member_id).unwrap());
|
@@ -209,7 +208,7 @@ fn build_member_hover(
|
209 | 208 | let mut origin_function_member = None;
|
210 | 209 | let mut origin_decl = None;
|
211 | 210 | if is_function(&typ) {
|
212 |
| - let origin_decl_id = find_function_member_origin_owner(&builder.semantic_model, member_id); |
| 211 | + let origin_decl_id = find_member_origin_owner(&builder.semantic_model, member_id); |
213 | 212 | match origin_decl_id {
|
214 | 213 | Some(LuaSemanticDeclId::Member(member_id)) => {
|
215 | 214 | origin_function_member = Some(db.get_member_index().get_member(&member_id)?);
|
@@ -383,3 +382,128 @@ pub fn get_hover_type(builder: &HoverBuilder, semantic_model: &SemanticModel) ->
|
383 | 382 |
|
384 | 383 | None
|
385 | 384 | }
|
| 385 | + |
| 386 | +pub fn find_decl_origin_owner( |
| 387 | + semantic_model: &SemanticModel, |
| 388 | + decl_id: LuaDeclId, |
| 389 | +) -> Option<LuaSemanticDeclId> { |
| 390 | + let root = semantic_model |
| 391 | + .get_db() |
| 392 | + .get_vfs() |
| 393 | + .get_syntax_tree(&decl_id.file_id)? |
| 394 | + .get_red_root(); |
| 395 | + let node = semantic_model |
| 396 | + .get_db() |
| 397 | + .get_decl_index() |
| 398 | + .get_decl(&decl_id)? |
| 399 | + .get_value_syntax_id()? |
| 400 | + .to_node_from_root(&root)?; |
| 401 | + let semantic_decl = semantic_model.find_decl(node.into(), SemanticDeclLevel::default()); |
| 402 | + match semantic_decl { |
| 403 | + Some(LuaSemanticDeclId::Member(member_id)) => { |
| 404 | + find_member_origin_owner(semantic_model, member_id).or(semantic_decl) |
| 405 | + } |
| 406 | + Some(LuaSemanticDeclId::LuaDecl(_)) => semantic_decl, |
| 407 | + _ => None, |
| 408 | + } |
| 409 | +} |
| 410 | + |
| 411 | +pub fn find_member_origin_owner( |
| 412 | + semantic_model: &SemanticModel, |
| 413 | + member_id: LuaMemberId, |
| 414 | +) -> Option<LuaSemanticDeclId> { |
| 415 | + const MAX_ITERATIONS: usize = 50; |
| 416 | + let mut visited_members = HashSet::new(); |
| 417 | + |
| 418 | + let mut current_owner = resolve_member_owner(semantic_model, &member_id); |
| 419 | + let mut final_owner = current_owner.clone(); |
| 420 | + let mut iteration_count = 0; |
| 421 | + |
| 422 | + while let Some(LuaSemanticDeclId::Member(current_member_id)) = ¤t_owner { |
| 423 | + if visited_members.contains(current_member_id) || iteration_count >= MAX_ITERATIONS { |
| 424 | + break; |
| 425 | + } |
| 426 | + |
| 427 | + visited_members.insert(current_member_id.clone()); |
| 428 | + iteration_count += 1; |
| 429 | + |
| 430 | + match resolve_member_owner(semantic_model, current_member_id) { |
| 431 | + Some(next_owner) => { |
| 432 | + final_owner = Some(next_owner.clone()); |
| 433 | + current_owner = Some(next_owner); |
| 434 | + } |
| 435 | + None => break, |
| 436 | + } |
| 437 | + } |
| 438 | + |
| 439 | + final_owner |
| 440 | +} |
| 441 | + |
| 442 | +fn resolve_member_owner( |
| 443 | + semantic_model: &SemanticModel, |
| 444 | + member_id: &LuaMemberId, |
| 445 | +) -> Option<LuaSemanticDeclId> { |
| 446 | + let root = semantic_model |
| 447 | + .get_db() |
| 448 | + .get_vfs() |
| 449 | + .get_syntax_tree(&member_id.file_id)? |
| 450 | + .get_red_root(); |
| 451 | + let current_node = member_id.get_syntax_id().to_node_from_root(&root)?; |
| 452 | + match member_id.get_syntax_id().get_kind() { |
| 453 | + LuaSyntaxKind::TableFieldAssign => { |
| 454 | + if LuaTableField::can_cast(current_node.kind().into()) { |
| 455 | + let table_field = LuaTableField::cast(current_node.clone())?; |
| 456 | + // 如果表是类, 那么通过类型推断获取 owner |
| 457 | + if let Some(owner_id) = |
| 458 | + resolve_table_field_through_type_inference(semantic_model, &table_field) |
| 459 | + { |
| 460 | + return Some(owner_id); |
| 461 | + } |
| 462 | + // 非类, 那么通过右值推断 |
| 463 | + let value_expr = table_field.get_value_expr()?; |
| 464 | + let value_node = value_expr.get_syntax_id().to_node_from_root(&root)?; |
| 465 | + semantic_model.find_decl(value_node.into(), SemanticDeclLevel::default()) |
| 466 | + } else { |
| 467 | + None |
| 468 | + } |
| 469 | + } |
| 470 | + LuaSyntaxKind::IndexExpr => { |
| 471 | + let assign_node = current_node.parent()?; |
| 472 | + let assign_stat = LuaAssignStat::cast(assign_node)?; |
| 473 | + let (vars, exprs) = assign_stat.get_var_and_expr_list(); |
| 474 | + |
| 475 | + for (var, expr) in vars.iter().zip(exprs.iter()) { |
| 476 | + if var.syntax().text_range() == current_node.text_range() { |
| 477 | + let expr_node = expr.get_syntax_id().to_node_from_root(&root)?; |
| 478 | + return semantic_model |
| 479 | + .find_decl(expr_node.into(), SemanticDeclLevel::default()); |
| 480 | + } |
| 481 | + } |
| 482 | + None |
| 483 | + } |
| 484 | + _ => None, |
| 485 | + } |
| 486 | +} |
| 487 | + |
| 488 | +fn resolve_table_field_through_type_inference( |
| 489 | + semantic_model: &SemanticModel, |
| 490 | + table_field: &LuaTableField, |
| 491 | +) -> Option<LuaSemanticDeclId> { |
| 492 | + let parent = table_field.syntax().parent()?; |
| 493 | + let table_expr = LuaTableExpr::cast(parent)?; |
| 494 | + let table_type = semantic_model.infer_table_should_be(table_expr)?; |
| 495 | + |
| 496 | + if !matches!(table_type, LuaType::Ref(_) | LuaType::Def(_)) { |
| 497 | + return None; |
| 498 | + } |
| 499 | + |
| 500 | + let field_key = table_field.get_field_key()?; |
| 501 | + let key = semantic_model.get_member_key(&field_key)?; |
| 502 | + let member_infos = semantic_model.get_member_infos(&table_type)?; |
| 503 | + |
| 504 | + member_infos |
| 505 | + .iter() |
| 506 | + .find(|m| m.key == key)? |
| 507 | + .property_owner_id |
| 508 | + .clone() |
| 509 | +} |
0 commit comments