diff --git a/crates/emmylua_code_analysis/resources/schema.json b/crates/emmylua_code_analysis/resources/schema.json index f49853b9..7e2bd827 100644 --- a/crates/emmylua_code_analysis/resources/schema.json +++ b/crates/emmylua_code_analysis/resources/schema.json @@ -82,7 +82,8 @@ "hover": { "$ref": "#/$defs/EmmyrcHover", "default": { - "enable": true + "enable": true, + "verbose": false } }, "inlineValues": { @@ -716,6 +717,12 @@ "type": "boolean", "default": true, "x-vscode-setting": true + }, + "verbose": { + "description": "Increase verbosity of types shown in hovers.\n\nEnabling this option will increase maximum nesting of displayed types,\nadd alias expansions on top level, increase number of shown members.", + "type": "boolean", + "default": false, + "x-vscode-setting": true } } }, diff --git a/crates/emmylua_code_analysis/src/config/configs/hover.rs b/crates/emmylua_code_analysis/src/config/configs/hover.rs index 3a7508c3..e9334ff4 100644 --- a/crates/emmylua_code_analysis/src/config/configs/hover.rs +++ b/crates/emmylua_code_analysis/src/config/configs/hover.rs @@ -8,12 +8,21 @@ pub struct EmmyrcHover { #[serde(default = "default_true")] #[schemars(extend("x-vscode-setting" = true))] pub enable: bool, + + /// Increase verbosity of types shown in hovers. + /// + /// Enabling this option will increase maximum nesting of displayed types, + /// add alias expansions on top level, increase number of shown members. + #[serde(default)] + #[schemars(extend("x-vscode-setting" = true))] + pub verbose: bool, } impl Default for EmmyrcHover { fn default() -> Self { Self { enable: default_true(), + verbose: false, } } } diff --git a/crates/emmylua_code_analysis/src/db_index/type/humanize_type.rs b/crates/emmylua_code_analysis/src/db_index/type/humanize_type.rs index 74d82943..ba06fc1e 100644 --- a/crates/emmylua_code_analysis/src/db_index/type/humanize_type.rs +++ b/crates/emmylua_code_analysis/src/db_index/type/humanize_type.rs @@ -1,6 +1,5 @@ -use std::collections::HashSet; - use itertools::Itertools; +use std::collections::HashSet; use crate::{ DbIndex, GenericTpl, LuaAliasCallType, LuaFunctionType, LuaGenericType, LuaInstanceType, @@ -13,6 +12,7 @@ use super::{LuaAliasCallKind, LuaMultiLineUnion}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum RenderLevel { Documentation, + Verbose, Detailed, Simple, Normal, @@ -24,6 +24,7 @@ impl RenderLevel { pub fn next_level(self) -> RenderLevel { match self { RenderLevel::Documentation => RenderLevel::Simple, + RenderLevel::Verbose => RenderLevel::Detailed, RenderLevel::Detailed => RenderLevel::Simple, RenderLevel::Simple => RenderLevel::Normal, RenderLevel::Normal => RenderLevel::Brief, @@ -74,7 +75,7 @@ pub fn humanize_type(db: &DbIndex, ty: &LuaType, level: RenderLevel) -> String { LuaType::Ref(id) => { if let Some(type_decl) = db.get_type_index().get_type_decl(id) { let name = type_decl.get_full_name().to_string(); - humanize_simple_type(db, id, &name, level).unwrap_or(name) + humanize_simple_type(db, id, &name, level) } else { id.get_name().to_string() } @@ -114,10 +115,7 @@ fn humanize_def_type(db: &DbIndex, id: &LuaTypeDeclId, level: RenderLevel) -> St let full_name = type_decl.get_full_name(); let generic = match db.get_type_index().get_generic_params(id) { Some(generic) => generic, - None => { - return humanize_simple_type(db, id, &full_name, level) - .unwrap_or(full_name.to_string()); - } + None => return humanize_simple_type(db, id, &full_name, level), }; let generic_names = generic @@ -133,15 +131,20 @@ fn humanize_simple_type( id: &LuaTypeDeclId, name: &str, level: RenderLevel, -) -> Option { - if !matches!(level, RenderLevel::Detailed | RenderLevel::Documentation) { - return Some(name.to_string()); +) -> String { + if !matches!( + level, + RenderLevel::Detailed | RenderLevel::Verbose | RenderLevel::Documentation + ) { + return name.to_string(); } let max_display_count = 12; let member_owner = LuaMemberOwner::Type(id.clone()); let member_index = db.get_member_index(); - let members = member_index.get_sorted_members(&member_owner)?; + let members = member_index + .get_sorted_members(&member_owner) + .unwrap_or_default(); let mut member_vec = Vec::new(); let mut function_vec = Vec::new(); for member in members { @@ -159,7 +162,23 @@ fn humanize_simple_type( } if member_vec.is_empty() && function_vec.is_empty() { - return Some(name.to_string()); + if matches!(level, RenderLevel::Detailed | RenderLevel::Verbose) { + 'expand: { + let Some(type_decl) = db.get_type_index().get_type_decl(id) else { + break 'expand; + }; + let Some(origin) = type_decl.get_alias_ref() else { + break 'expand; + }; + return format!( + "{} {}", + name, + humanize_type(db, &origin, level.next_level()) + ); + } + } + + return name.to_string(); } let all_count = member_vec.len() + function_vec.len(); @@ -171,7 +190,8 @@ fn humanize_simple_type( typ, humanize_type(db, typ, level.next_level()), level, - ); + ) + .replace("\n", "\n "); member_strings.push_str(&format!(" {},\n", member_string)); count += 1; @@ -186,7 +206,8 @@ fn humanize_simple_type( &LuaType::Function, "function".to_string(), level, - ); + ) + .replace("\n", "\n "); member_strings.push_str(&format!(" {},\n", member_string)); count += 1; @@ -198,7 +219,7 @@ fn humanize_simple_type( if count >= max_display_count { member_strings.push_str(&format!(" ...(+{})\n", all_count - max_display_count)); } - Some(format!("{} {{\n{}}}", name, member_strings)) + format!("{} {{\n{}}}", name, member_strings) } fn humanize_union_type(db: &DbIndex, union: &LuaUnionType, level: RenderLevel) -> String { @@ -218,6 +239,7 @@ where let types = union.into_vec(); let num = match level { RenderLevel::Documentation => 500, + RenderLevel::Verbose => 10, RenderLevel::Detailed => 8, RenderLevel::Simple => 6, RenderLevel::Normal => 4, @@ -243,7 +265,7 @@ where } let dots = if type_strings.len() > num { "..." } else { "" }; let display_types: Vec<_> = type_strings.into_iter().take(num).collect(); - let type_str = display_types.join("|"); + let type_str = display_types.join(" | "); if display_types.len() == 1 { if has_function && has_nil { @@ -264,6 +286,7 @@ fn humanize_multi_line_union_type( let members = multi_union.get_unions(); let num = match level { RenderLevel::Documentation => 500, + RenderLevel::Verbose => 15, RenderLevel::Detailed => 10, RenderLevel::Simple => 8, RenderLevel::Normal => 4, @@ -277,7 +300,7 @@ fn humanize_multi_line_union_type( .take(num) .map(|(ty, _)| humanize_type(db, ty, level.next_level())) .collect::>() - .join("|"); + .join(" | "); let mut text = format!("({}{})", type_str, dots); if level != RenderLevel::Detailed { @@ -304,6 +327,7 @@ fn humanize_tuple_type(db: &DbIndex, tuple: &LuaTupleType, level: RenderLevel) - let types = tuple.get_types(); let num = match level { RenderLevel::Documentation => 500, + RenderLevel::Verbose => 15, RenderLevel::Detailed => 10, RenderLevel::Simple => 8, RenderLevel::Normal => 4, @@ -318,7 +342,7 @@ fn humanize_tuple_type(db: &DbIndex, tuple: &LuaTupleType, level: RenderLevel) - .take(num) .map(|ty| humanize_type(db, ty, level.next_level())) .collect::>() - .join(","); + .join(", "); format!("({}{})", type_str, dots) } @@ -344,7 +368,7 @@ fn humanize_call_type(db: &DbIndex, inner: &LuaAliasCallType, level: RenderLevel .iter() .map(|ty| humanize_type(db, ty, level.next_level())) .collect::>() - .join(","); + .join(", "); format!("{}<{}>", basic, operands) } @@ -398,6 +422,7 @@ fn humanize_doc_function_type( fn humanize_object_type(db: &DbIndex, object: &LuaObjectType, level: RenderLevel) -> String { let num = match level { RenderLevel::Documentation => 500, + RenderLevel::Verbose => 15, RenderLevel::Detailed => 10, RenderLevel::Simple => 8, RenderLevel::Normal => 4, @@ -440,7 +465,7 @@ fn humanize_object_type(db: &DbIndex, object: &LuaObjectType, level: RenderLevel format!("[{}]: {}", key_str, value_str) }) .collect::>() - .join(","); + .join(", "); if access.is_empty() { return format!("{{ {}{} }}", fields, dots); @@ -457,6 +482,7 @@ fn humanize_intersect_type( ) -> String { let num = match level { RenderLevel::Documentation => 500, + RenderLevel::Verbose => 15, RenderLevel::Detailed => 10, RenderLevel::Simple => 8, RenderLevel::Normal => 4, @@ -490,7 +516,7 @@ fn humanize_generic_type(db: &DbIndex, generic: &LuaGenericType, level: RenderLe .iter() .map(|ty| humanize_type(db, ty, level.next_level())) .collect::>() - .join(","); + .join(", "); format!("{}<{}>", full_name, generic_params) } @@ -518,7 +544,8 @@ fn humanize_table_const_type_detail_and_simple( &type_cache.as_type(), humanize_type(db, &type_cache.as_type(), level.next_level()), level, - ); + ) + .replace("\n", "\n "); match level { RenderLevel::Detailed => { @@ -575,6 +602,7 @@ fn humanize_table_generic_type( ) -> String { let num = match level { RenderLevel::Documentation => 500, + RenderLevel::Verbose => 15, RenderLevel::Detailed => 10, RenderLevel::Simple => 8, RenderLevel::Normal => 4, @@ -595,7 +623,7 @@ fn humanize_table_generic_type( .take(num) .map(|ty| humanize_type(db, ty, level.next_level())) .collect::>() - .join(","); + .join(", "); format!("table<{}{}>", generic_params, dots) } @@ -622,6 +650,7 @@ fn humanize_variadic_type(db: &DbIndex, multi: &VariadicType, level: RenderLevel VariadicType::Multi(types) => { let max_num = match level { RenderLevel::Documentation => 500, + RenderLevel::Verbose => 15, RenderLevel::Detailed => 10, RenderLevel::Simple => 8, RenderLevel::Normal => 4, @@ -637,7 +666,7 @@ fn humanize_variadic_type(db: &DbIndex, multi: &VariadicType, level: RenderLevel .take(max_num) .map(|ty| humanize_type(db, ty, level.next_level())) .collect::>() - .join(","); + .join(", "); format!("({}{})", type_str, dots) } } @@ -709,7 +738,7 @@ fn humanize_signature_type( if rets.is_empty() { "".to_string() } else { - format!(" -> {}", rets.join(",")) + format!(" -> {}", rets.join(", ")) } } }; diff --git a/crates/emmylua_ls/src/handlers/hover/build_hover.rs b/crates/emmylua_ls/src/handlers/hover/build_hover.rs index 16e88170..01b4ca77 100644 --- a/crates/emmylua_ls/src/handlers/hover/build_hover.rs +++ b/crates/emmylua_ls/src/handlers/hover/build_hover.rs @@ -30,9 +30,10 @@ pub fn build_semantic_info_hover( semantic_info: SemanticInfo, range: TextRange, ) -> Option { + let verbose = semantic_model.get_emmyrc().hover.verbose; let typ = semantic_info.clone().typ; if semantic_info.semantic_decl.is_none() { - return build_hover_without_property(db, document, token, typ); + return build_hover_without_property(db, document, token, typ, verbose); } let hover_builder = build_hover_content( compilation, @@ -42,6 +43,7 @@ pub fn build_semantic_info_hover( semantic_info.semantic_decl.unwrap(), false, Some(token.clone()), + verbose, ); if let Some(hover_builder) = hover_builder { hover_builder.build_hover_result(document.to_lsp_range(range)) @@ -55,8 +57,15 @@ fn build_hover_without_property( document: &LuaDocument, token: LuaSyntaxToken, typ: LuaType, + verbose: bool, ) -> Option { - let hover = humanize_type(db, &typ, RenderLevel::Detailed); + let render_level = if verbose { + RenderLevel::Verbose + } else { + RenderLevel::Detailed + }; + + let hover = humanize_type(db, &typ, render_level); Some(Hover { contents: HoverContents::Markup(MarkupContent { kind: lsp_types::MarkupKind::Markdown, @@ -89,6 +98,7 @@ pub fn build_hover_content_for_completion<'a>( property_id, true, None, + semantic_model.get_emmyrc().hover.verbose, ) } @@ -100,19 +110,20 @@ fn build_hover_content<'a>( property_id: LuaSemanticDeclId, is_completion: bool, token: Option, + verbose: bool, ) -> Option> { let mut builder = HoverBuilder::new(compilation, semantic_model, token, is_completion); match property_id { LuaSemanticDeclId::LuaDecl(decl_id) => { let typ = typ?; - build_decl_hover(&mut builder, db, typ, decl_id); + build_decl_hover(&mut builder, db, typ, decl_id, verbose); } LuaSemanticDeclId::Member(member_id) => { let typ = typ?; - build_member_hover(&mut builder, db, typ, member_id); + build_member_hover(&mut builder, db, typ, member_id, verbose); } LuaSemanticDeclId::TypeDecl(type_decl_id) => { - build_type_decl_hover(&mut builder, db, type_decl_id); + build_type_decl_hover(&mut builder, db, type_decl_id, verbose); } _ => return None, } @@ -124,6 +135,7 @@ fn build_decl_hover( db: &DbIndex, typ: LuaType, decl_id: LuaDeclId, + verbose: bool, ) -> Option<()> { let decl = db.get_decl_index().get_decl(&decl_id)?; @@ -151,7 +163,7 @@ fn build_decl_hover( semantic_decls.push((semantic_decl, typ.clone())); } - hover_function_type(builder, db, &semantic_decls); + hover_function_type(builder, db, &semantic_decls, verbose); if let Some((LuaSemanticDeclId::Member(member_id), _)) = semantic_decls .iter() @@ -166,7 +178,7 @@ fn build_decl_hover( .add_signature_params_rets_description(builder.semantic_model.get_type(decl_id.into())); } else { if typ.is_const() { - let const_value = hover_const_type(db, &typ); + let const_value = hover_const_type(db, &typ, verbose); let prefix = if decl.is_local() { "local " } else { @@ -177,7 +189,7 @@ fn build_decl_hover( let decl_hover_type = get_hover_type(builder, builder.semantic_model).unwrap_or(typ.clone()); let type_humanize_text = - hover_humanize_type(builder, &decl_hover_type, Some(RenderLevel::Detailed)); + hover_humanize_type(builder, &decl_hover_type, verbose, RenderLevel::Detailed); let prefix = if decl.is_local() { "local " } else { @@ -209,6 +221,7 @@ fn build_member_hover( db: &DbIndex, typ: LuaType, member_id: LuaMemberId, + verbose: bool, ) -> Option<()> { let member = db.get_member_index().get_member(&member_id)?; let mut semantic_decls = find_member_origin_owners( @@ -245,7 +258,7 @@ fn build_member_hover( semantic_decls.push((semantic_decl, typ.clone())); } - hover_function_type(builder, db, &semantic_decls); + hover_function_type(builder, db, &semantic_decls, verbose); builder.set_location_path(Some(&member)); @@ -255,14 +268,14 @@ fn build_member_hover( ); } else { if typ.is_const() { - let const_value = hover_const_type(db, &typ); + let const_value = hover_const_type(db, &typ, verbose); builder.set_type_description(format!("(field) {}: {}", member_name, const_value)); builder.set_location_path(Some(&member)); } else { let member_hover_type = get_hover_type(builder, builder.semantic_model).unwrap_or(typ.clone()); let type_humanize_text = - hover_humanize_type(builder, &member_hover_type, Some(RenderLevel::Simple)); + hover_humanize_type(builder, &member_hover_type, verbose, RenderLevel::Simple); builder .set_type_description(format!("(field) {}: {}", member_name, type_humanize_text)); builder.set_location_path(Some(&member)); @@ -285,23 +298,27 @@ fn build_type_decl_hover( builder: &mut HoverBuilder, db: &DbIndex, type_decl_id: LuaTypeDeclId, + verbose: bool, ) -> Option<()> { + let render_level = if verbose { + RenderLevel::Verbose + } else { + RenderLevel::Detailed + }; + let type_decl = db.get_type_index().get_type_decl(&type_decl_id)?; let type_description = if type_decl.is_alias() { if let Some(origin) = type_decl.get_alias_origin(db, None) { - let origin_type = humanize_type(db, &origin, RenderLevel::Detailed); + let origin_type = humanize_type(db, &origin, render_level); format!("(alias) {} = {}", type_decl.get_name(), origin_type) } else { "".to_string() } } else if type_decl.is_enum() { - format!("(enum) {}", type_decl.get_name()) + let humanize_text = humanize_type(db, &LuaType::Ref(type_decl_id.clone()), render_level); + format!("(enum) {}", humanize_text) } else { - let humanize_text = humanize_type( - db, - &LuaType::Def(type_decl_id.clone()), - RenderLevel::Detailed, - ); + let humanize_text = humanize_type(db, &LuaType::Ref(type_decl_id.clone()), render_level); format!("(class) {}", humanize_text) }; diff --git a/crates/emmylua_ls/src/handlers/hover/function_humanize.rs b/crates/emmylua_ls/src/handlers/hover/function_humanize.rs index cb0caa71..a9fe7f07 100644 --- a/crates/emmylua_ls/src/handlers/hover/function_humanize.rs +++ b/crates/emmylua_ls/src/handlers/hover/function_humanize.rs @@ -30,6 +30,7 @@ pub fn hover_function_type( builder: &mut HoverBuilder, db: &DbIndex, semantic_decls: &[(LuaSemanticDeclId, LuaType)], + verbose: bool, ) -> Option<()> { let (name, is_local) = { let Some((semantic_decl, _)) = semantic_decls.first() else { @@ -134,6 +135,7 @@ pub fn hover_function_type( is_local, call_function.as_ref(), &processed_types, + verbose, ), _ => { // 记录非 Union 类型 @@ -146,6 +148,7 @@ pub fn hover_function_type( &name, is_local, call_function.as_ref(), + verbose, ) } }; @@ -243,7 +246,14 @@ fn hover_doc_function_type( lua_func: &LuaFunctionType, owner_member: Option<&LuaMember>, func_name: &str, + verbose: bool, ) -> String { + let render_level = if verbose { + RenderLevel::Simple + } else { + RenderLevel::Normal + }; + let async_label = if lua_func.is_async() { "async " } else { "" }; let mut is_method = lua_func.is_colon_define(); let mut type_label = "function "; @@ -304,7 +314,7 @@ fn hover_doc_function_type( if index == 0 && is_method && !lua_func.is_colon_define() { "".to_string() } else if let Some(ty) = ¶m.1 { - format!("{}: {}", name, humanize_type(db, ty, RenderLevel::Normal)) + format!("{}: {}", name, humanize_type(db, ty, render_level)) } else { name.to_string() } @@ -318,7 +328,7 @@ fn hover_doc_function_type( match ret_type { LuaType::Nil => "".to_string(), _ => { - format!(" -> {}", humanize_type(db, ret_type, RenderLevel::Simple)) + format!(" -> {}", humanize_type(db, ret_type, render_level)) } } }; @@ -339,7 +349,14 @@ fn hover_signature_type( func_name: &str, is_local: bool, call_function: Option<&LuaFunctionType>, + verbose: bool, ) -> Option { + let render_level = if verbose { + RenderLevel::Simple + } else { + RenderLevel::Normal + }; + let signature = db.get_signature_index().get(&signature_id)?; let mut is_method = signature.is_colon_define; @@ -410,7 +427,7 @@ fn hover_signature_type( if index == 0 && !signature.is_colon_define && is_method { "".to_string() } else if let Some(ty) = ¶m.1 { - format!("{}: {}", name, humanize_type(db, ty, RenderLevel::Simple)) + format!("{}: {}", name, humanize_type(db, ty, render_level)) } else { name } @@ -418,7 +435,7 @@ fn hover_signature_type( .filter(|s| !s.is_empty()) .collect::>() .join(", "); - let rets = build_signature_rets(builder, signature, builder.is_completion, None); + let rets = build_signature_rets(builder, signature, builder.is_completion, None, verbose); let result = format_function_type(type_label, async_label, full_name.clone(), params, rets); // 由于 @field 定义的`docfunction`会被视为`signature`, 因此这里额外处理 if let Some(call_function) = call_function { @@ -450,7 +467,7 @@ fn hover_signature_type( { "".to_string() } else if let Some(ty) = ¶m.1 { - format!("{}: {}", name, humanize_type(db, ty, RenderLevel::Simple)) + format!("{}: {}", name, humanize_type(db, ty, render_level)) } else { name } @@ -458,8 +475,13 @@ fn hover_signature_type( .filter(|s| !s.is_empty()) .collect::>() .join(", "); - let rets = - build_signature_rets(builder, signature, builder.is_completion, Some(overload)); + let rets = build_signature_rets( + builder, + signature, + builder.is_completion, + Some(overload), + verbose, + ); let result = format_function_type(type_label, async_label, full_name.clone(), params, rets); @@ -490,7 +512,14 @@ fn build_signature_rets( signature: &LuaSignature, is_completion: bool, overload: Option<&LuaFunctionType>, + verbose: bool, ) -> String { + let render_level = if verbose { + RenderLevel::Simple + } else { + RenderLevel::Normal + }; + let db = builder.semantic_model.get_db(); let mut result = String::new(); // overload 的返回值固定为单行 @@ -499,7 +528,7 @@ fn build_signature_rets( match ret_type { LuaType::Nil => "".to_string(), _ => { - format!(" -> {}", humanize_type(db, ret_type, RenderLevel::Simple)) + format!(" -> {}", humanize_type(db, ret_type, render_level)) } } } else { @@ -518,7 +547,7 @@ fn build_signature_rets( " -> {}", rets.iter() .enumerate() - .map(|(i, ret)| build_signature_ret_type(builder, ret, i)) + .map(|(i, ret)| build_signature_ret_type(builder, ret, i, verbose)) .collect::>() .join(", ") ) @@ -539,7 +568,7 @@ fn build_signature_rets( rets_string_multiline.push_str("\n"); for (i, ret) in rets.iter().enumerate() { - let type_text = build_signature_ret_type(builder, ret, i); + let type_text = build_signature_ret_type(builder, ret, i, verbose); let prefix = if i == 0 { "-> ".to_string() } else { @@ -569,9 +598,16 @@ fn build_signature_ret_type( builder: &mut HoverBuilder, ret_info: &LuaDocReturnInfo, i: usize, + verbose: bool, ) -> String { + let render_level = if verbose { + RenderLevel::Simple + } else { + RenderLevel::Normal + }; + let type_expansion_count = builder.get_type_expansion_count(); - let type_text = hover_humanize_type(builder, &ret_info.type_ref, Some(RenderLevel::Simple)); + let type_text = hover_humanize_type(builder, &ret_info.type_ref, verbose, render_level); if builder.get_type_expansion_count() > type_expansion_count { // 重新设置`type_expansion` if let Some(pop_type_expansion) = @@ -623,6 +659,7 @@ fn process_single_function_type( name: &str, is_local: bool, call_function: Option<&LuaFunctionType>, + verbose: bool, ) -> ProcessFunctionTypeResult { match typ { LuaType::Function => ProcessFunctionTypeResult::Single(HoverFunctionInfo { @@ -633,7 +670,7 @@ fn process_single_function_type( }), LuaType::DocFunction(lua_func) => { let type_description = - hover_doc_function_type(builder, db, &lua_func, function_member, &name); + hover_doc_function_type(builder, db, &lua_func, function_member, &name, verbose); let is_call_function = if let Some(call_function) = call_function { call_function.get_params() == lua_func.get_params() } else { @@ -656,6 +693,7 @@ fn process_single_function_type( name, is_local, call_function, + verbose, ) .unwrap_or_else(|| HoverSignatureResult { type_description: format!("function {}", name), @@ -683,6 +721,7 @@ fn process_single_function_type( name, is_local, call_function, + verbose, ) { ProcessFunctionTypeResult::Single(info) => { results.push(info); @@ -718,6 +757,7 @@ fn process_single_function_type_with_exclusions( is_local: bool, call_function: Option<&LuaFunctionType>, processed_types: &HashSet, + verbose: bool, ) -> ProcessFunctionTypeResult { match typ { LuaType::Union(union) => { @@ -737,6 +777,7 @@ fn process_single_function_type_with_exclusions( is_local, call_function, processed_types, + verbose, ) { ProcessFunctionTypeResult::Single(info) => { results.push(info); @@ -764,6 +805,7 @@ fn process_single_function_type_with_exclusions( name, is_local, call_function, + verbose, ) } } diff --git a/crates/emmylua_ls/src/handlers/hover/hover_humanize.rs b/crates/emmylua_ls/src/handlers/hover/hover_humanize.rs index c7c6589f..90e1dcf4 100644 --- a/crates/emmylua_ls/src/handlers/hover/hover_humanize.rs +++ b/crates/emmylua_ls/src/handlers/hover/hover_humanize.rs @@ -10,8 +10,14 @@ use rowan::TextRange; use super::hover_builder::HoverBuilder; -pub fn hover_const_type(db: &DbIndex, typ: &LuaType) -> String { - let const_value = humanize_type(db, typ, RenderLevel::Detailed); +pub fn hover_const_type(db: &DbIndex, typ: &LuaType, verbose: bool) -> String { + let render_level = if verbose { + RenderLevel::Verbose + } else { + RenderLevel::Detailed + }; + + let const_value = humanize_type(db, typ, render_level); match typ { LuaType::IntegerConst(_) | LuaType::DocIntegerConst(_) => { @@ -26,8 +32,15 @@ pub fn hover_const_type(db: &DbIndex, typ: &LuaType) -> String { pub fn hover_humanize_type( builder: &mut HoverBuilder, ty: &LuaType, - fallback_level: Option, // 当有值时, 若获取类型描述为空会回退到使用`humanize_type()` + verbose: bool, + fallback_level: RenderLevel, // 当有值时, 若获取类型描述为空会回退到使用`humanize_type()` ) -> String { + let render_level = if verbose { + RenderLevel::Verbose + } else { + RenderLevel::Detailed + }; + let db = builder.semantic_model.get_db(); match ty { LuaType::Ref(type_decl_id) => { @@ -40,17 +53,19 @@ pub fn hover_humanize_type( db, multi_union.as_ref(), Some(type_decl.get_full_name()), + verbose, ) .unwrap_or_default(); } } - humanize_type(db, ty, fallback_level.unwrap_or(RenderLevel::Simple)) + humanize_type(db, ty, fallback_level) } LuaType::MultiLineUnion(multi_union) => { - hover_multi_line_union_type(builder, db, multi_union.as_ref(), None).unwrap_or_default() + hover_multi_line_union_type(builder, db, multi_union.as_ref(), None, verbose) + .unwrap_or_default() } - LuaType::Union(union) => hover_union_type(builder, union, RenderLevel::Detailed), - _ => humanize_type(db, ty, fallback_level.unwrap_or(RenderLevel::Simple)), + LuaType::Union(union) => hover_union_type(builder, union, render_level, verbose), + _ => humanize_type(db, ty, fallback_level), } } @@ -58,9 +73,10 @@ fn hover_union_type( builder: &mut HoverBuilder, union: &LuaUnionType, level: RenderLevel, + verbose: bool, ) -> String { format_union_type(union, level, |ty, level| { - hover_humanize_type(builder, ty, Some(level)) + hover_humanize_type(builder, ty, verbose, level) }) } @@ -69,14 +85,21 @@ fn hover_multi_line_union_type( db: &DbIndex, multi_union: &LuaMultiLineUnion, ty_name: Option<&str>, + verbose: bool, ) -> Option { + let render_level = if verbose { + RenderLevel::Simple + } else { + RenderLevel::Normal + }; + let members = multi_union.get_unions(); let type_name = if ty_name.is_none() { let members = multi_union.get_unions(); let type_str = members .iter() .take(10) - .map(|(ty, _)| humanize_type(db, ty, RenderLevel::Simple)) + .map(|(ty, _)| humanize_type(db, ty, render_level)) .collect::>() .join("|"); Some(format!("({})", type_str)) @@ -85,7 +108,7 @@ fn hover_multi_line_union_type( }; let mut text = format!("{}:\n", type_name.clone().unwrap_or_default()); for (typ, description) in members { - let type_humanize_text = humanize_type(db, &typ, RenderLevel::Minimal); + let type_humanize_text = humanize_type(db, &typ, render_level.next_level().next_level()); if let Some(description) = description { text.push_str(&format!( " | {} -- {}\n", diff --git a/crates/emmylua_ls/src/handlers/test/hover_function_test.rs b/crates/emmylua_ls/src/handlers/test/hover_function_test.rs index 726979f8..49c73a52 100644 --- a/crates/emmylua_ls/src/handlers/test/hover_function_test.rs +++ b/crates/emmylua_ls/src/handlers/test/hover_function_test.rs @@ -266,7 +266,7 @@ mod tests { } "#, VirtualHoverResult { - value: "```lua\n(field) T.func(a: (string|number))\n```\n\n---\n\n注释1\n\n注释2\n\n---\n\n```lua\n(field) T.func(a: string)\n```\n\n```lua\n(field) T.func(a: number)\n```" + value: "```lua\n(field) T.func(a: (string | number))\n```\n\n---\n\n注释1\n\n注释2\n\n---\n\n```lua\n(field) T.func(a: string)\n```\n\n```lua\n(field) T.func(a: number)\n```" .to_string(), }, )); diff --git a/crates/emmylua_ls/src/handlers/test/hover_test.rs b/crates/emmylua_ls/src/handlers/test/hover_test.rs index 279f686c..e66e4206 100644 --- a/crates/emmylua_ls/src/handlers/test/hover_test.rs +++ b/crates/emmylua_ls/src/handlers/test/hover_test.rs @@ -231,7 +231,7 @@ mod tests { ---| 'B' # A2 "#, VirtualHoverResult { - value: "```lua\n(alias) TesAlias = (\"A\"|\"B\")\n | \"A\" -- A1\n | \"B\" -- A2\n\n```".to_string(), + value: "```lua\n(alias) TesAlias = (\"A\" | \"B\")\n | \"A\" -- A1\n | \"B\" -- A2\n\n```".to_string(), }, )); Ok(()) @@ -365,4 +365,57 @@ mod tests { Ok(()) } + + #[gtest] + fn test_verbose() -> Result<()> { + let mut ws = ProviderVirtualWorkspace::new(); + let mut emmyrc = ws.get_emmyrc(); + emmyrc.hover.verbose = true; + ws.update_emmyrc(emmyrc); + + ws.def( + r#" + --- @alias AA [1, AA, BB] + + --- @class BB + --- @field a AA + --- @field b BB + --- @field c CC + + --- @enum CC + local CC = { a = 1, b = 2 } + "#, + ); + + check!(ws.check_hover( + r#" + --- @type AA + local _ + "#, + VirtualHoverResult { + value: "```lua\n(alias) AA = (1, AA (1, AA, BB), BB {\n a: AA,\n b: BB,\n c: CC,\n})\n```".to_string(), + }, + )); + + check!(ws.check_hover( + r#" + --- @type BB + local _ + "#, + VirtualHoverResult { + value: "```lua\n(class) BB {\n a = AA (1, AA, BB),\n b = BB {\n a: AA,\n b: BB,\n c: CC,\n },\n c = CC {\n a: integer = 1,\n b: integer = 2,\n },\n}\n```".to_string(), + }, + )); + + check!(ws.check_hover( + r#" + --- @type CC + local _ + "#, + VirtualHoverResult { + value: "```lua\n(enum) CC {\n a = 1,\n b = 2,\n}\n```".to_string(), + }, + )); + Ok(()) + } }