@@ -2,12 +2,13 @@ use std::collections::HashMap;
22use std:: sync:: Arc ;
33
44use emmylua_code_analysis:: {
5- FileId , InferGuard , LuaFunctionType , LuaMemberId , LuaMemberKey , LuaOperatorId ,
6- LuaOperatorMetaMethod , LuaSemanticDeclId , LuaType , SemanticModel ,
5+ FileId , InferGuard , LuaFunctionType , LuaMember , LuaMemberId , LuaMemberKey , LuaMemberOwner ,
6+ LuaOperatorId , LuaOperatorMetaMethod , LuaSemanticDeclId , LuaType , LuaTypeDecl , SemanticModel ,
77} ;
88use emmylua_parser:: {
99 LuaAst , LuaAstNode , LuaCallExpr , LuaExpr , LuaFuncStat , LuaIndexExpr , LuaIndexKey ,
10- LuaLocalFuncStat , LuaLocalName , LuaLocalStat , LuaStat , LuaSyntaxId , LuaVarExpr ,
10+ LuaLiteralToken , LuaLocalFuncStat , LuaLocalName , LuaLocalStat , LuaStat , LuaSyntaxId ,
11+ LuaVarExpr ,
1112} ;
1213use emmylua_parser:: { LuaAstToken , LuaTokenKind } ;
1314use lsp_types:: { InlayHint , InlayHintKind , InlayHintLabel , InlayHintLabelPart , Location } ;
@@ -30,7 +31,8 @@ pub fn build_inlay_hints(semantic_model: &SemanticModel) -> Option<Vec<InlayHint
3031 LuaAst :: LuaCallExpr ( call_expr) => {
3132 build_call_expr_param_hint ( semantic_model, & mut result, call_expr. clone ( ) ) ;
3233 build_call_expr_await_hint ( semantic_model, & mut result, call_expr. clone ( ) ) ;
33- build_call_expr_meta_call_hint ( semantic_model, & mut result, call_expr) ;
34+ build_call_expr_meta_call_hint ( semantic_model, & mut result, call_expr. clone ( ) ) ;
35+ build_enum_param_hint ( semantic_model, & mut result, call_expr) ;
3436 }
3537 LuaAst :: LuaLocalName ( local_name) => {
3638 build_local_name_hint ( semantic_model, & mut result, local_name) ;
@@ -674,3 +676,181 @@ fn build_index_expr_hint(
674676 result. push ( hint) ;
675677 Some ( ( ) )
676678}
679+
680+ fn build_enum_param_hint (
681+ semantic_model : & SemanticModel ,
682+ result : & mut Vec < InlayHint > ,
683+ call_expr : LuaCallExpr ,
684+ ) -> Option < ( ) > {
685+ if !semantic_model. get_emmyrc ( ) . hint . enum_param_hint {
686+ return Some ( ( ) ) ;
687+ }
688+
689+ let func_type = semantic_model. infer_call_expr_func ( call_expr. clone ( ) , None ) ?;
690+ let call_args = call_expr. get_args_list ( ) ?. get_args ( ) . collect :: < Vec < _ > > ( ) ;
691+ let params = func_type. get_params ( ) ;
692+
693+ let colon_call = call_expr. is_colon_call ( ) ;
694+ let colon_define = func_type. is_colon_define ( ) ;
695+
696+ let param_offset: i32 = match ( colon_call, colon_define) {
697+ ( true , false ) => 1 ,
698+ ( false , true ) => -1 ,
699+ _ => 0 ,
700+ } ;
701+
702+ for ( i, arg) in call_args. iter ( ) . enumerate ( ) {
703+ let param_index = i as i32 + param_offset;
704+ if param_index < 0 {
705+ continue ;
706+ }
707+ process_enum_hint_for_arg ( semantic_model, result, arg, params, param_index as usize ) ;
708+ }
709+
710+ Some ( ( ) )
711+ }
712+
713+ fn process_enum_hint_for_arg (
714+ semantic_model : & SemanticModel ,
715+ result : & mut Vec < InlayHint > ,
716+ arg : & LuaExpr ,
717+ params : & [ ( String , Option < LuaType > ) ] ,
718+ param_index : usize ,
719+ ) -> Option < ( ) > {
720+ let ( _, param_type) = params. get ( param_index) ?;
721+ let param_type = param_type. as_ref ( ) ?;
722+
723+ let type_id = match param_type {
724+ LuaType :: Ref ( id) => id,
725+ _ => return None ,
726+ } ;
727+
728+ let type_decl = semantic_model
729+ . get_db ( )
730+ . get_type_index ( )
731+ . get_type_decl ( type_id) ?;
732+ if !type_decl. is_enum ( ) {
733+ return None ;
734+ }
735+
736+ // 推断参数类型
737+ let arg_type = semantic_model. infer_expr ( arg. clone ( ) ) . ok ( ) ?;
738+
739+ // 查找对应的枚举成员
740+ let member_decl = find_matching_enum_member ( semantic_model, type_decl, & arg_type) ?;
741+ let member_name = member_decl. get_key ( ) . to_path ( ) ;
742+
743+ match arg {
744+ LuaExpr :: LiteralExpr ( literal_expr) => {
745+ if let Some ( literal_token) = literal_expr. get_literal ( ) {
746+ match literal_token {
747+ LuaLiteralToken :: String ( string_token) => {
748+ if string_token. get_value ( ) == member_name {
749+ return None ;
750+ }
751+ }
752+ LuaLiteralToken :: Number ( number_token) => {
753+ if number_token. is_int ( ) {
754+ let number_value = format ! ( "[{}]" , number_token. get_int_value( ) ) ;
755+ if number_value == member_name {
756+ return None ;
757+ }
758+ }
759+ }
760+ _ => { }
761+ }
762+ }
763+ }
764+ LuaExpr :: NameExpr ( name_expr) => {
765+ if let Some ( arg_name) = name_expr. get_name_text ( ) {
766+ if member_name == arg_name {
767+ return None ;
768+ }
769+ // 名称里包含了枚举名和成员名(忽略大小写)也不显示提示
770+ let lower_arg_name = arg_name. to_lowercase ( ) ;
771+ let lower_enum_name = type_decl. get_name ( ) . to_lowercase ( ) ;
772+ let lower_member_name = member_name. to_lowercase ( ) ;
773+ if lower_arg_name. contains ( & lower_enum_name)
774+ && lower_arg_name. contains ( & lower_member_name)
775+ {
776+ return None ;
777+ }
778+ }
779+ }
780+ LuaExpr :: IndexExpr ( index_expr) => {
781+ // 对索引访问需要完全匹配尾名称
782+ if let Some ( index_name_token) = index_expr. get_index_name_token ( ) {
783+ if let Some ( name_token) =
784+ emmylua_parser:: LuaNameToken :: cast ( index_name_token. clone ( ) )
785+ {
786+ let index_name = name_token. get_name_text ( ) ;
787+ if index_name == member_name {
788+ return None ;
789+ }
790+ }
791+ }
792+ }
793+ _ => { }
794+ }
795+
796+ let enum_name = type_decl. get_name ( ) ;
797+ let hint_text = format ! ( "{}.{}" , enum_name, member_name) ;
798+
799+ let document = semantic_model. get_document ( ) ;
800+ let range = arg. get_range ( ) ;
801+ let lsp_range = document. to_lsp_range ( range) ?;
802+
803+ let hint = InlayHint {
804+ kind : Some ( InlayHintKind :: PARAMETER ) ,
805+ label : InlayHintLabel :: String ( hint_text) ,
806+ position : lsp_range. end ,
807+ text_edits : None ,
808+ tooltip : None ,
809+ padding_left : Some ( true ) ,
810+ padding_right : None ,
811+ data : None ,
812+ } ;
813+ result. push ( hint) ;
814+
815+ Some ( ( ) )
816+ }
817+
818+ fn find_matching_enum_member < ' a > (
819+ semantic_model : & ' a SemanticModel ,
820+ type_decl : & LuaTypeDecl ,
821+ arg_type : & LuaType ,
822+ ) -> Option < & ' a LuaMember > {
823+ let enum_member_owner = LuaMemberOwner :: Type ( type_decl. get_id ( ) ) ;
824+ let enum_members = semantic_model
825+ . get_db ( )
826+ . get_member_index ( )
827+ . get_members ( & enum_member_owner) ?;
828+ let is_enum_key = type_decl. is_enum_key ( ) ;
829+
830+ for member_decl in enum_members {
831+ let is_match = if is_enum_key {
832+ let member_key = member_decl. get_key ( ) ;
833+ match ( member_key, arg_type) {
834+ ( LuaMemberKey :: Name ( s) , LuaType :: StringConst ( arg_s) ) => s == arg_s. as_ref ( ) ,
835+ ( LuaMemberKey :: Integer ( i) , LuaType :: IntegerConst ( arg_i) ) => * i == * arg_i,
836+ ( LuaMemberKey :: ExprType ( typ) , _) => typ == arg_type,
837+ _ => false ,
838+ }
839+ } else {
840+ if let Some ( type_cache) = semantic_model
841+ . get_db ( )
842+ . get_type_index ( )
843+ . get_type_cache ( & member_decl. get_id ( ) . into ( ) )
844+ {
845+ type_cache. as_type ( ) == arg_type
846+ } else {
847+ false
848+ }
849+ } ;
850+
851+ if is_match {
852+ return Some ( member_decl) ;
853+ }
854+ }
855+ None
856+ }
0 commit comments