diff --git a/.editorconfig b/.editorconfig index ba59a53f..7f421d03 100644 --- a/.editorconfig +++ b/.editorconfig @@ -41,220 +41,48 @@ indent_size = 4 [*.{cmd,bat}] end_of_line = crlf +[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.stylelintrc,bowerrc,jest.config}] +indent_style = space +indent_size = 2 + +[*.{appxmanifest,asax,ascx,aspx,axaml,build,c,c++,cc,cginc,compute,cp,cpp,cs,cshtml,cu,cuh,cxx,dtd,fs,fsi,fsscript,fsx,fx,fxh,h,hh,hlsl,hlsli,hlslinc,hpp,hxx,inc,inl,ino,ipp,ixx,master,ml,mli,mpp,mq4,mq5,mqh,nuspec,paml,razor,resw,resx,shader,skin,tpp,usf,ush,vb,xaml,xamlx,xoml,xsd}] +indent_style = space +indent_size = 4 +tab_width = 4 + # Bash Files [*.sh] end_of_line = lf - # C# files [*.cs] -# Microsoft .NET properties -csharp_new_line_before_members_in_object_initializers = false -csharp_preferred_modifier_order = public, private, protected, internal, file, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async, required:suggestion -csharp_style_prefer_utf8_string_literals = true:suggestion -csharp_style_var_elsewhere = true:suggestion -csharp_style_var_for_built_in_types = true:suggestion -csharp_style_var_when_type_is_apparent = true:suggestion -dotnet_naming_rule.constants_rule.import_to_resharper = as_predefined -dotnet_naming_rule.constants_rule.severity = warning -dotnet_naming_rule.constants_rule.style = pascal_case -dotnet_naming_rule.constants_rule.symbols = constants_symbols -dotnet_naming_rule.event_rule.import_to_resharper = as_predefined -dotnet_naming_rule.event_rule.severity = warning -dotnet_naming_rule.event_rule.style = pascal_case -dotnet_naming_rule.event_rule.symbols = event_symbols -dotnet_naming_rule.interfaces_rule.import_to_resharper = as_predefined -dotnet_naming_rule.interfaces_rule.severity = warning -dotnet_naming_rule.interfaces_rule.style = begins_with_i -dotnet_naming_rule.interfaces_rule.symbols = interfaces_symbols -dotnet_naming_rule.method_rule.import_to_resharper = as_predefined -dotnet_naming_rule.method_rule.severity = warning -dotnet_naming_rule.method_rule.style = pascal_case -dotnet_naming_rule.method_rule.symbols = method_symbols -dotnet_naming_rule.private_constants_rule.import_to_resharper = as_predefined -dotnet_naming_rule.private_constants_rule.severity = warning -dotnet_naming_rule.private_constants_rule.style = pascal_case -dotnet_naming_rule.private_constants_rule.symbols = private_constants_symbols -dotnet_naming_rule.private_instance_fields_rule.import_to_resharper = as_predefined -dotnet_naming_rule.private_instance_fields_rule.severity = warning -dotnet_naming_rule.private_instance_fields_rule.style = begins_with__ -dotnet_naming_rule.private_instance_fields_rule.symbols = private_instance_fields_symbols -dotnet_naming_rule.private_static_fields_rule.import_to_resharper = as_predefined -dotnet_naming_rule.private_static_fields_rule.severity = warning -dotnet_naming_rule.private_static_fields_rule.style = begins_with__ -dotnet_naming_rule.private_static_fields_rule.symbols = private_static_fields_symbols -dotnet_naming_rule.private_static_readonly_rule.import_to_resharper = as_predefined -dotnet_naming_rule.private_static_readonly_rule.severity = warning -dotnet_naming_rule.private_static_readonly_rule.style = pascal_case -dotnet_naming_rule.private_static_readonly_rule.symbols = private_static_readonly_symbols -dotnet_naming_rule.property_rule.import_to_resharper = as_predefined -dotnet_naming_rule.property_rule.severity = warning -dotnet_naming_rule.property_rule.style = pascal_case -dotnet_naming_rule.property_rule.symbols = property_symbols -dotnet_naming_rule.public_fields_rule.import_to_resharper = as_predefined -dotnet_naming_rule.public_fields_rule.severity = warning -dotnet_naming_rule.public_fields_rule.style = pascal_case -dotnet_naming_rule.public_fields_rule.symbols = public_fields_symbols -dotnet_naming_rule.static_readonly_rule.import_to_resharper = as_predefined -dotnet_naming_rule.static_readonly_rule.severity = warning -dotnet_naming_rule.static_readonly_rule.style = pascal_case -dotnet_naming_rule.static_readonly_rule.symbols = static_readonly_symbols -dotnet_naming_rule.types_and_namespaces_rule.import_to_resharper = as_predefined -dotnet_naming_rule.types_and_namespaces_rule.severity = warning -dotnet_naming_rule.types_and_namespaces_rule.style = pascal_case -dotnet_naming_rule.types_and_namespaces_rule.symbols = types_and_namespaces_symbols -dotnet_naming_rule.unity_serialized_field_rule.import_to_resharper = True -dotnet_naming_rule.unity_serialized_field_rule.resharper_description = Unity serialized field -dotnet_naming_rule.unity_serialized_field_rule.resharper_guid = 5f0fdb63-c892-4d2c-9324-15c80b22a7ef -dotnet_naming_rule.unity_serialized_field_rule.severity = warning -dotnet_naming_rule.unity_serialized_field_rule.style = lower_camel_case_style -dotnet_naming_rule.unity_serialized_field_rule.symbols = unity_serialized_field_symbols -dotnet_naming_style.lower_camel_case_style.capitalization = camel_case -dotnet_naming_symbols.constants_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected -dotnet_naming_symbols.constants_symbols.applicable_kinds = field -dotnet_naming_symbols.constants_symbols.required_modifiers = const -dotnet_naming_symbols.event_symbols.applicable_accessibilities = * -dotnet_naming_symbols.event_symbols.applicable_kinds = event -dotnet_naming_symbols.interfaces_symbols.applicable_accessibilities = * -dotnet_naming_symbols.interfaces_symbols.applicable_kinds = interface -dotnet_naming_symbols.method_symbols.applicable_accessibilities = * -dotnet_naming_symbols.method_symbols.applicable_kinds = method -dotnet_naming_symbols.private_constants_symbols.applicable_accessibilities = private -dotnet_naming_symbols.private_constants_symbols.applicable_kinds = field -dotnet_naming_symbols.private_constants_symbols.required_modifiers = const -dotnet_naming_symbols.private_instance_fields_symbols.applicable_accessibilities = private -dotnet_naming_symbols.private_instance_fields_symbols.applicable_kinds = field -dotnet_naming_symbols.private_static_fields_symbols.applicable_accessibilities = private -dotnet_naming_symbols.private_static_fields_symbols.applicable_kinds = field -dotnet_naming_symbols.private_static_fields_symbols.required_modifiers = static -dotnet_naming_symbols.private_static_readonly_symbols.applicable_accessibilities = private -dotnet_naming_symbols.private_static_readonly_symbols.applicable_kinds = field -dotnet_naming_symbols.private_static_readonly_symbols.required_modifiers = static,readonly -dotnet_naming_symbols.property_symbols.applicable_accessibilities = * -dotnet_naming_symbols.property_symbols.applicable_kinds = property -dotnet_naming_symbols.public_fields_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected -dotnet_naming_symbols.public_fields_symbols.applicable_kinds = field -dotnet_naming_symbols.static_readonly_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected -dotnet_naming_symbols.static_readonly_symbols.applicable_kinds = field -dotnet_naming_symbols.static_readonly_symbols.required_modifiers = static,readonly -dotnet_naming_symbols.types_and_namespaces_symbols.applicable_accessibilities = * -dotnet_naming_symbols.types_and_namespaces_symbols.applicable_kinds = namespace,class,struct,enum,delegate -dotnet_naming_symbols.unity_serialized_field_symbols.applicable_accessibilities = * -dotnet_naming_symbols.unity_serialized_field_symbols.applicable_kinds = -dotnet_naming_symbols.unity_serialized_field_symbols.resharper_applicable_kinds = unity_serialised_field -dotnet_naming_symbols.unity_serialized_field_symbols.resharper_required_modifiers = instance -dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none -dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:none -dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none -dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion -dotnet_style_predefined_type_for_member_access = true:suggestion -dotnet_style_qualification_for_event = false:suggestion -dotnet_style_qualification_for_field = false:suggestion -dotnet_style_qualification_for_method = false:suggestion -dotnet_style_qualification_for_property = false:suggestion -dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion - -# ReSharper properties -resharper_autodetect_indent_settings = true -resharper_braces_for_for = required -resharper_braces_for_foreach = required -resharper_braces_for_ifelse = required -resharper_braces_for_while = required -resharper_braces_redundant = false -resharper_csharp_brace_style = next_line -resharper_csharp_naming_rule.constants = AaBb -resharper_csharp_naming_rule.event = AaBb -resharper_csharp_naming_rule.interfaces = I + AaBb -resharper_csharp_naming_rule.method = AaBb -resharper_csharp_naming_rule.private_constants = AaBb -resharper_csharp_naming_rule.private_instance_fields = _ + aaBb -resharper_csharp_naming_rule.private_static_fields = _ + aaBb -resharper_csharp_naming_rule.private_static_readonly = AaBb -resharper_csharp_naming_rule.property = AaBb -resharper_csharp_naming_rule.public_fields = AaBb -resharper_csharp_naming_rule.static_readonly = AaBb -resharper_csharp_naming_rule.types_and_namespaces = AaBb -resharper_formatter_off_tag = @formatter:off -resharper_formatter_on_tag = @formatter:on -resharper_formatter_tags_enabled = true -resharper_keep_existing_declaration_block_arrangement = false -resharper_keep_existing_embedded_block_arrangement = false -resharper_keep_existing_enum_arrangement = false -resharper_use_heuristics_for_body_style = true -resharper_use_indent_from_vs = false - -# ReSharper inspection severities -resharper_arrange_accessor_owner_body_highlighting = suggestion -resharper_arrange_redundant_parentheses_highlighting = hint -resharper_arrange_this_qualifier_highlighting = hint -resharper_arrange_type_member_modifiers_highlighting = hint -resharper_arrange_type_modifiers_highlighting = hint -resharper_built_in_type_reference_style_for_member_access_highlighting = hint -resharper_built_in_type_reference_style_highlighting = hint -resharper_enforce_do_while_statement_braces_highlighting = warning -resharper_enforce_fixed_statement_braces_highlighting = warning -resharper_enforce_foreach_statement_braces_highlighting = warning -resharper_enforce_for_statement_braces_highlighting = warning -resharper_enforce_if_statement_braces_highlighting = warning -resharper_enforce_lock_statement_braces_highlighting = warning -resharper_enforce_using_statement_braces_highlighting = warning -resharper_enforce_while_statement_braces_highlighting = warning -resharper_redundant_base_qualifier_highlighting = warning -resharper_suggest_var_or_type_built_in_types_highlighting = hint -resharper_suggest_var_or_type_elsewhere_highlighting = hint -resharper_suggest_var_or_type_simple_types_highlighting = hint -resharper_web_config_module_not_resolved_highlighting = warning -resharper_web_config_type_not_resolved_highlighting = warning -resharper_web_config_wrong_module_highlighting = warning +#### Core EditorConfig Options #### -dotnet_style_operator_placement_when_wrapping = beginning_of_line -dotnet_style_coalesce_expression = true:suggestion -dotnet_style_null_propagation = true:suggestion -dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion -dotnet_style_prefer_auto_properties = true:silent -dotnet_style_object_initializer = true:suggestion -dotnet_style_collection_initializer = true:suggestion -dotnet_style_prefer_simplified_boolean_expressions = true:suggestion -dotnet_style_prefer_conditional_expression_over_assignment = true:silent -dotnet_style_prefer_conditional_expression_over_return = true:silent -dotnet_style_explicit_tuple_names = true:suggestion -dotnet_style_prefer_inferred_tuple_names = true:suggestion -dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion -dotnet_style_prefer_compound_assignment = true:suggestion -dotnet_style_prefer_simplified_interpolation = true:suggestion -dotnet_style_namespace_match_folder = true:suggestion -dotnet_style_readonly_field = true:suggestion -dotnet_style_predefined_type_for_locals_parameters_members = true:silent -dotnet_style_predefined_type_for_member_access = true:silent -dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent -dotnet_style_allow_multiple_blank_lines_experimental = true:silent -dotnet_style_allow_statement_immediately_after_block_experimental = true:silent -dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent -dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent -dotnet_code_quality_unused_parameters = all:suggestion -dotnet_style_qualification_for_event = false:silent -dotnet_style_qualification_for_method = false:silent -dotnet_style_qualification_for_property = false:silent -dotnet_style_qualification_for_field = false:silent +# Indentation and spacing +indent_size = 4 +indent_style = space +tab_width = 4 + +# New line preferences +end_of_line = crlf +insert_final_newline = false #### .NET Coding Conventions #### # Organize usings dotnet_separate_import_directive_groups = false dotnet_sort_system_directives_first = true -file_header_template = +file_header_template = unset # this. and Me. preferences -dotnet_style_qualification_for_event = false +dotnet_style_qualification_for_event = false:warning dotnet_style_qualification_for_field = false -dotnet_style_qualification_for_method = false -dotnet_style_qualification_for_property = false +dotnet_style_qualification_for_method = false:warning +dotnet_style_qualification_for_property = false:warning # Language keywords vs BCL types preferences -dotnet_style_predefined_type_for_locals_parameters_members = true -dotnet_style_predefined_type_for_member_access = true +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion # Parentheses preferences dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity @@ -273,10 +101,11 @@ dotnet_style_namespace_match_folder = true dotnet_style_null_propagation = true dotnet_style_object_initializer = true dotnet_style_operator_placement_when_wrapping = beginning_of_line -dotnet_style_prefer_auto_properties = true +dotnet_style_prefer_auto_properties = true:suggestion +dotnet_style_prefer_collection_expression = true dotnet_style_prefer_compound_assignment = true -dotnet_style_prefer_conditional_expression_over_assignment = true -dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_return = true:suggestion dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed dotnet_style_prefer_inferred_anonymous_type_member_names = true dotnet_style_prefer_inferred_tuple_names = true @@ -285,7 +114,7 @@ dotnet_style_prefer_simplified_boolean_expressions = true dotnet_style_prefer_simplified_interpolation = true # Field preferences -dotnet_style_readonly_field = true +dotnet_style_readonly_field = true:warning # Parameter preferences dotnet_code_quality_unused_parameters = all @@ -300,65 +129,69 @@ dotnet_style_allow_statement_immediately_after_block_experimental = true #### C# Coding Conventions #### # var preferences -csharp_style_var_elsewhere = false:silent -csharp_style_var_for_built_in_types = false:silent -csharp_style_var_when_type_is_apparent = false:silent +csharp_style_var_elsewhere = true:suggestion +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion # Expression-bodied members -csharp_style_expression_bodied_accessors = true:silent -csharp_style_expression_bodied_constructors = false:silent -csharp_style_expression_bodied_indexers = true:silent -csharp_style_expression_bodied_lambdas = true:silent -csharp_style_expression_bodied_local_functions = false:silent -csharp_style_expression_bodied_methods = false:silent -csharp_style_expression_bodied_operators = false:silent -csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_accessors = true +csharp_style_expression_bodied_constructors = false +csharp_style_expression_bodied_indexers = true +csharp_style_expression_bodied_lambdas = true +csharp_style_expression_bodied_local_functions = false +csharp_style_expression_bodied_methods = false +csharp_style_expression_bodied_operators = false +csharp_style_expression_bodied_properties = true # Pattern matching preferences -csharp_style_pattern_matching_over_as_with_null_check = true:suggestion -csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion -csharp_style_prefer_extended_property_pattern = true:suggestion -csharp_style_prefer_not_pattern = true:suggestion -csharp_style_prefer_pattern_matching = true:silent -csharp_style_prefer_switch_expression = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_extended_property_pattern = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_pattern_matching = true:suggestion +csharp_style_prefer_switch_expression = true # Null-checking preferences -csharp_style_conditional_delegate_call = true:suggestion +csharp_style_conditional_delegate_call = true # Modifier preferences -csharp_prefer_static_local_function = true:suggestion +csharp_prefer_static_local_function = true csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async -csharp_style_prefer_readonly_struct = true:suggestion -csharp_style_prefer_readonly_struct_member = true:suggestion +csharp_style_prefer_readonly_struct = true +csharp_style_prefer_readonly_struct_member = true # Code-block preferences -csharp_prefer_simple_using_statement = true:silent -csharp_style_prefer_method_group_conversion = true:silent -csharp_style_prefer_top_level_statements = true:silent +csharp_prefer_braces = true:warning +csharp_prefer_simple_using_statement = true +csharp_style_namespace_declarations = file_scoped:warning +csharp_style_prefer_method_group_conversion = true +csharp_style_prefer_primary_constructors = true +csharp_style_prefer_top_level_statements = true # Expression-level preferences -csharp_prefer_simple_default_expression = true:suggestion -csharp_style_deconstructed_variable_declaration = true:suggestion -csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion -csharp_style_inlined_variable_declaration = true:suggestion -csharp_style_prefer_index_operator = true:suggestion -csharp_style_prefer_local_over_anonymous_function = true:suggestion -csharp_style_prefer_null_check_over_type_check = true:suggestion -csharp_style_prefer_range_operator = true:suggestion -csharp_style_prefer_tuple_swap = true:suggestion -csharp_style_prefer_utf8_string_literals = true:suggestion -csharp_style_throw_expression = true:suggestion -csharp_style_unused_value_assignment_preference = discard_variable:suggestion -csharp_style_unused_value_expression_statement_preference = discard_variable:silent +csharp_prefer_simple_default_expression = true +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_prefer_index_operator = true +csharp_style_prefer_local_over_anonymous_function = true +csharp_style_prefer_null_check_over_type_check = true +csharp_style_prefer_range_operator = true +csharp_style_prefer_tuple_swap = true +csharp_style_prefer_utf8_string_literals = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable # 'using' directive preferences +csharp_using_directive_placement = outside_namespace:warning # New line preferences -csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent -csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent -csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent -csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent -csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true +csharp_style_allow_embedded_statements_on_same_line_experimental = true #### C# Formatting Rules #### @@ -411,94 +244,78 @@ csharp_preserve_single_line_statements = true # Naming rules -dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.severity = warning dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i -dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.severity = warning dotnet_naming_rule.types_should_be_pascal_case.symbols = types dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case -dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = warning dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.property_should_be_pascal_case.severity = warning +dotnet_naming_rule.property_should_be_pascal_case.symbols = property +dotnet_naming_rule.property_should_be_pascal_case.style = pascal_case + dotnet_naming_rule.private_or_internal_field_should_be_begins_with__.severity = warning dotnet_naming_rule.private_or_internal_field_should_be_begins_with__.symbols = private_or_internal_field dotnet_naming_rule.private_or_internal_field_should_be_begins_with__.style = begins_with__ -dotnet_naming_rule.static_field_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.static_field_should_be_pascal_case.symbols = static_field -dotnet_naming_rule.static_field_should_be_pascal_case.style = pascal_case - # Symbol specifications dotnet_naming_symbols.interface.applicable_kinds = interface dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.interface.required_modifiers = +dotnet_naming_symbols.interface.required_modifiers = -dotnet_naming_symbols.static_field.applicable_kinds = field -dotnet_naming_symbols.static_field.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.static_field.required_modifiers = static +dotnet_naming_symbols.property.applicable_kinds = property +dotnet_naming_symbols.property.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.property.required_modifiers = dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected -dotnet_naming_symbols.private_or_internal_field.required_modifiers = +dotnet_naming_symbols.private_or_internal_field.required_modifiers = dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.types.required_modifiers = +dotnet_naming_symbols.types.required_modifiers = dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.non_field_members.required_modifiers = +dotnet_naming_symbols.non_field_members.required_modifiers = # Naming styles -dotnet_naming_style.pascal_case.required_prefix = -dotnet_naming_style.pascal_case.required_suffix = -dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = dotnet_naming_style.pascal_case.capitalization = pascal_case dotnet_naming_style.begins_with_i.required_prefix = I -dotnet_naming_style.begins_with_i.required_suffix = -dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = dotnet_naming_style.begins_with_i.capitalization = pascal_case dotnet_naming_style.begins_with__.required_prefix = _ -dotnet_naming_style.begins_with__.required_suffix = -dotnet_naming_style.begins_with__.word_separator = +dotnet_naming_style.begins_with__.required_suffix = +dotnet_naming_style.begins_with__.word_separator = dotnet_naming_style.begins_with__.capitalization = camel_case -# -dotnet_style_allow_multiple_blank_lines_experimental = true:silent -dotnet_style_allow_statement_immediately_after_block_experimental = true:silent -# dotnet_analyzer_diagnostic.category-roslynator.severity = warning -# Enable/disable all analyzers by default -# NOTE: This option can be used only in .roslynatorconfig file roslynator_analyzers.enabled_by_default = true - -# Enable/disable all refactorings roslynator_refactorings.enabled = true - -# Enable/disable all compiler diagnostic fixes roslynator_compiler_diagnostic_fixes.enabled = true -tab_width = 4 + +# roslynator_refactoring.<REFACTORING_NAME>.enabled = true +# roslynator_compiler_diagnostic_fix.<COMPILER_DIAGNOSTIC_ID>.enabled = true|false +# dotnet_diagnostic.<ANALYZER_ID>.severity = default|none|silent|suggestion|warning|error -# +dotnet_diagnostic.Ca2016.severity = warning +dotnet_diagnostic.RCS1251.severity = none dotnet_diagnostic.CA1848.severity = silent -dotnet_diagnostic.CA1305.severity = silent -dotnet_diagnostic.CA2016.severity = warning +dotnet_diagnostic.CA1305.severity = silent dotnet_diagnostic.ide0011.severity = warning -dotnet_diagnostic.RCS1251.severity = silent - -[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.stylelintrc,bowerrc,jest.config}] -indent_style = space -indent_size = 2 - -[*.{appxmanifest,asax,ascx,aspx,axaml,build,c,c++,cc,cginc,compute,cp,cpp,cs,cshtml,cu,cuh,cxx,dtd,fs,fsi,fsscript,fsx,fx,fxh,h,hh,hlsl,hlsli,hlslinc,hpp,hxx,inc,inl,ino,ipp,ixx,master,ml,mli,mpp,mq4,mq5,mqh,nuspec,paml,razor,resw,resx,shader,skin,tpp,usf,ush,vb,xaml,xamlx,xoml,xsd}] -indent_style = space -indent_size = 4 -tab_width = 4 +dotnet_diagnostic.RCS1251.severity = silent \ No newline at end of file diff --git a/src/Passingwind.Abp.ElsaModule.Application/Workflow/DesignerAppService.cs b/src/Passingwind.Abp.ElsaModule.Application/Workflow/DesignerAppService.cs index d709ed7b..31c0e9f9 100644 --- a/src/Passingwind.Abp.ElsaModule.Application/Workflow/DesignerAppService.cs +++ b/src/Passingwind.Abp.ElsaModule.Application/Workflow/DesignerAppService.cs @@ -11,6 +11,7 @@ using Elsa.Services; using Elsa.Services.Models; using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.Logging; using Passingwind.Abp.ElsaModule.Common; using Passingwind.Abp.ElsaModule.CSharp; using Passingwind.Abp.ElsaModule.Permissions; @@ -128,12 +129,20 @@ public async Task<WorkflowDesignerCSharpLanguageCompletionProviderResultDto> CSh var workflowDefinition = _storeMapper.MapToModel(version, definition); - var result = await _workflowCSharpEditorService.GetCompletionAsync(workflowDefinition, input.Id, input.Text, input.Position); + try + { + var result = await _workflowCSharpEditorService.GetCompletionAsync(workflowDefinition, input.Id, input.Text, input.Position); - return new WorkflowDesignerCSharpLanguageCompletionProviderResultDto + return new WorkflowDesignerCSharpLanguageCompletionProviderResultDto + { + Items = result?.Items ?? new List<WorkflowCSharpEditorCompletionItem>(), + }; + } + catch (Exception ex) { - Items = result?.Items ?? new List<WorkflowCSharpEditorCompletionItem>(), - }; + Logger.LogError(ex, "Get csharp language completions failed."); + return new WorkflowDesignerCSharpLanguageCompletionProviderResultDto(); + } } public async Task<WorkflowDesignerCSharpLanguageHoverProviderResultDto> CSharpLanguageHoverProviderAsync(Guid id, WorkflowDesignerCSharpLanguageHoverProviderRequestDto input) @@ -143,17 +152,27 @@ public async Task<WorkflowDesignerCSharpLanguageHoverProviderResultDto> CSharpLa var workflowDefinition = _storeMapper.MapToModel(version, definition); - var result = await _workflowCSharpEditorService.GetHoverInfoAsync(workflowDefinition, input.Id, input.Text, input.Position); - - if (result == null) - return null; - - return new WorkflowDesignerCSharpLanguageHoverProviderResultDto + try { - Information = result.Information, - OffsetFrom = result.OffsetFrom, - OffsetTo = result.OffsetTo, - }; + var result = await _workflowCSharpEditorService.GetHoverInfoAsync(workflowDefinition, input.Id, input.Text, input.Position); + + if (result == null) + { + return null; + } + + return new WorkflowDesignerCSharpLanguageHoverProviderResultDto + { + Information = result.Information, + OffsetFrom = result.OffsetFrom, + OffsetTo = result.OffsetTo, + }; + } + catch (Exception ex) + { + Logger.LogError(ex, "Get csharp language hover info failed."); + return null; + } } public async Task<WorkflowDesignerCSharpLanguageSignatureProviderResultDto> CSharpLanguageSignatureProviderAsync(Guid id, WorkflowDesignerCSharpLanguageSignatureProviderRequestDto input) @@ -163,17 +182,25 @@ public async Task<WorkflowDesignerCSharpLanguageSignatureProviderResultDto> CSha var workflowDefinition = _storeMapper.MapToModel(version, definition); - var result = await _workflowCSharpEditorService.GetSignaturesAsync(workflowDefinition, input.Id, input.Text, input.Position); + try + { + var result = await _workflowCSharpEditorService.GetSignaturesAsync(workflowDefinition, input.Id, input.Text, input.Position); - if (result == null) - return null; + if (result == null) + return null; - return new WorkflowDesignerCSharpLanguageSignatureProviderResultDto + return new WorkflowDesignerCSharpLanguageSignatureProviderResultDto + { + Signatures = result.Signatures, + ActiveParameter = result.ActiveParameter, + ActiveSignature = result.ActiveSignature, + }; + } + catch (Exception ex) { - Signatures = result.Signatures, - ActiveParameter = result.ActiveParameter, - ActiveSignature = result.ActiveSignature, - }; + Logger.LogError(ex, "Get csharp language signature failed."); + return null; + } } public async Task<WorkflowDesignerCSharpLanguageAnalysisResultDto> CSharpLanguageCodeAnalysisAsync(Guid id, WorkflowDesignerCSharpLanguageAnalysisRequestDto input) @@ -183,12 +210,20 @@ public async Task<WorkflowDesignerCSharpLanguageAnalysisResultDto> CSharpLanguag var workflowDefinition = _storeMapper.MapToModel(version, definition); - var result = await _workflowCSharpEditorService.GetCodeAnalysisAsync(workflowDefinition, input.Id, input.Text); + try + { + var result = await _workflowCSharpEditorService.GetCodeAnalysisAsync(workflowDefinition, input.Id, input.Text); - return new WorkflowDesignerCSharpLanguageAnalysisResultDto + return new WorkflowDesignerCSharpLanguageAnalysisResultDto + { + Items = result?.Items ?? new List<WorkflowCSharpEditorCodeAnalysis>(), + }; + } + catch (Exception ex) { - Items = result?.Items ?? new List<WorkflowCSharpEditorCodeAnalysis>(), - }; + Logger.LogError(ex, "Get csharp language analysis failed."); + return new WorkflowDesignerCSharpLanguageAnalysisResultDto(); + } } public async Task<WorkflowDesignerCSharpLanguageFormatterResult> CSharpLanguageCodeFormatterAsync(WorkflowDesignerCSharpLanguageFormatterRequestDto input) diff --git a/src/Passingwind.Abp.ElsaModule.ElsaExtensions/Scripting/CSharp/CSharpService.cs b/src/Passingwind.Abp.ElsaModule.ElsaExtensions/Scripting/CSharp/CSharpService.cs index dff9e5d8..a146281c 100644 --- a/src/Passingwind.Abp.ElsaModule.ElsaExtensions/Scripting/CSharp/CSharpService.cs +++ b/src/Passingwind.Abp.ElsaModule.ElsaExtensions/Scripting/CSharp/CSharpService.cs @@ -36,7 +36,7 @@ public async Task<object> EvaluateAsync(string expression, Type returnType, Acti CSharpScriptConfigureNotification scriptConfigure = new(expression); await _mediator.Publish(scriptConfigure, cancellationToken); - string scriptId = $"{context.WorkflowExecutionContext.WorkflowBlueprint.VersionId}:{context.ActivityId}"; + string scriptId = $"{context.WorkflowExecutionContext.WorkflowBlueprint.VersionId}_{context.ActivityId}".Replace("-", null); _logger.LogDebug("Evaluate csharp code with id '{ScriptId}' ", scriptId); @@ -47,7 +47,7 @@ public async Task<object> EvaluateAsync(string expression, Type returnType, Acti Stopwatch sw = Stopwatch.StartNew(); - object resultValue = await _cSharpScriptHost.RunAsync(scriptContext, cancellationToken: cancellationToken); + var resultValue = await _cSharpScriptHost.RunAsync(scriptContext, cancellationToken: cancellationToken); sw.Stop(); diff --git a/src/Passingwind.Abp.ElsaModule.ElsaExtensions/Services/WorkflowCSharpEditorService.cs b/src/Passingwind.Abp.ElsaModule.ElsaExtensions/Services/WorkflowCSharpEditorService.cs index 6ae643f0..3d66564e 100644 --- a/src/Passingwind.Abp.ElsaModule.ElsaExtensions/Services/WorkflowCSharpEditorService.cs +++ b/src/Passingwind.Abp.ElsaModule.ElsaExtensions/Services/WorkflowCSharpEditorService.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; using Microsoft.Extensions.Logging; using Passingwind.Abp.ElsaModule.CSharp; using Passingwind.Abp.ElsaModule.Scripting.CSharp; @@ -20,7 +21,7 @@ namespace Passingwind.Abp.ElsaModule.Services; public class WorkflowCSharpEditorService : IWorkflowCSharpEditorService { - private const string GeneratedTypeClassName = "GeneratedTypes"; + private const string _generatedTypeClassName = "GeneratedTypes"; private readonly ILogger<WorkflowCSharpEditorService> _logger; private readonly ICSharpTypeDefinitionService _cSharpTypeDefinitionService; @@ -33,7 +34,7 @@ public WorkflowCSharpEditorService(ILogger<WorkflowCSharpEditorService> logger, _cSharpScriptWorkspace = cSharpScriptWorkspace; } - public async Task<WorkflowCSharpEditorCodeAnalysisResult> GetCodeAnalysisAsync(WorkflowDefinition workflowDefinition, string textId, string text, CancellationToken cancellationToken = default) + protected async Task<ICSharpScriptProject> GetProjectAsync(WorkflowDefinition workflowDefinition, CancellationToken cancellationToken = default) { var generated = await _cSharpTypeDefinitionService.GenerateAsync(workflowDefinition, cancellationToken); @@ -41,13 +42,28 @@ public async Task<WorkflowCSharpEditorCodeAnalysisResult> GetCodeAnalysisAsync(W var project = _cSharpScriptWorkspace.GetOrCreateProject(projectName); - project.CreateOrUpdateDocument(GeneratedTypeClassName, generated.Text); + // add default + _ = project.CreateOrUpdateDocument(_generatedTypeClassName, generated.Text); + + _ = project.AddImports(generated.Imports) + .AddReferences(generated.Assemblies); + + return project; + } + + public async Task<WorkflowCSharpEditorCodeAnalysisResult> GetCodeAnalysisAsync(WorkflowDefinition workflowDefinition, string textId, string text, CancellationToken cancellationToken = default) + { + var project = await GetProjectAsync(workflowDefinition, cancellationToken); project.CreateOrUpdateDocument(textId, text); +#if DEBUG + await project.SaveAsync(); +#endif + var tmpFile = Path.GetTempFileName(); - var compilation = await _cSharpScriptWorkspace.CreateCompilationAsync(project, cancellationToken); + var compilation = await _cSharpScriptWorkspace.CreateCompilationAsync(project, true, cancellationToken); var emitResult = compilation.Emit(tmpFile, cancellationToken: cancellationToken); @@ -55,7 +71,7 @@ public async Task<WorkflowCSharpEditorCodeAnalysisResult> GetCodeAnalysisAsync(W if (emitResult?.Success == false) { - foreach (var diagnostic in emitResult.Diagnostics) + foreach (var diagnostic in emitResult.Diagnostics.Where(x => x.Severity >= DiagnosticSeverity.Warning)) { var severity = MapDiagnosticSeverity(diagnostic); @@ -77,11 +93,15 @@ public async Task<WorkflowCSharpEditorCodeAnalysisResult> GetCodeAnalysisAsync(W public async Task<WorkflowCSharpEditorFormatterResult> CodeFormatterAsync(string textId, string text, CancellationToken cancellationToken = default) { - var project = ProjectInfo.Create(ProjectId.CreateNewId(), VersionStamp.Create(), "tmp", "tmp", LanguageNames.CSharp); - var documentInfo = DocumentInfo.Create(DocumentId.CreateNewId(project.Id), "tmp"); + using var workspce = new AdhocWorkspace(); + + var projectInfo = ProjectInfo.Create(ProjectId.CreateNewId(), VersionStamp.Create(), "tmp", "tmp", LanguageNames.CSharp); - using AdhocWorkspace workspce = new AdhocWorkspace(); - var document = workspce.AddDocument(documentInfo); + var project = workspce.AddProject(projectInfo); + + var document = workspce.AddDocument(project.Id, "tmp", SourceText.From(text)); + + _ = workspce.TryApplyChanges(workspce.CurrentSolution); var formattedDocument = await Formatter.FormatAsync(document, cancellationToken: cancellationToken); var sourceText = await formattedDocument.GetTextAsync(cancellationToken); @@ -94,23 +114,28 @@ public async Task<WorkflowCSharpEditorFormatterResult> CodeFormatterAsync(string public async Task<WorkflowCSharpEditorCompletionResult> GetCompletionAsync(WorkflowDefinition workflowDefinition, string textId, string text, int position, CancellationToken cancellationToken = default) { - var generated = await _cSharpTypeDefinitionService.GenerateAsync(workflowDefinition, cancellationToken); + var project = await GetProjectAsync(workflowDefinition, cancellationToken); - var projectName = workflowDefinition.DefinitionId.Replace("-", null); + if (position > text.Length) + { + position = text.Length; + } - var project = _cSharpScriptWorkspace.GetOrCreateProject(projectName); + var document = project.CreateOrUpdateDocument(textId, text); - project.CreateOrUpdateDocument(GeneratedTypeClassName, generated.Text); +#if DEBUG + await project.SaveAsync(); +#endif - var document = project.CreateOrUpdateDocument(textId, text); + await _cSharpScriptWorkspace.RestoreReferenceAsync(project, cancellationToken); - var completionItems = await project.GetCompletionsAsync(document, position); + var completionItems = await _cSharpScriptWorkspace.GetCompletionsAsync(project, document.Id, position, cancellationToken: cancellationToken); var result = new List<WorkflowCSharpEditorCompletionItem>(); foreach (var item in completionItems) { - SymbolKind symbolKind = SymbolKind.Local; + var symbolKind = SymbolKind.Local; if (item.Properties.TryGetValue(nameof(SymbolKind), out var kindValue)) { symbolKind = Enum.Parse<SymbolKind>(kindValue); @@ -129,44 +154,33 @@ public async Task<WorkflowCSharpEditorCompletionResult> GetCompletionAsync(Workf return new WorkflowCSharpEditorCompletionResult(result); - WorkflowCSharpEditorCompletionItemKind MapKind(SymbolKind symbolKind) + static WorkflowCSharpEditorCompletionItemKind MapKind(SymbolKind symbolKind) { - switch (symbolKind) + return symbolKind switch { - case SymbolKind.Field: - return WorkflowCSharpEditorCompletionItemKind.Field; - - case SymbolKind.Property: - return WorkflowCSharpEditorCompletionItemKind.Property; - - case SymbolKind.Local: - return WorkflowCSharpEditorCompletionItemKind.Variable; - - case SymbolKind.Method: - return WorkflowCSharpEditorCompletionItemKind.Function; - - case SymbolKind.NamedType: - return WorkflowCSharpEditorCompletionItemKind.Class; - - default: - return WorkflowCSharpEditorCompletionItemKind.Others; - } + SymbolKind.Field => WorkflowCSharpEditorCompletionItemKind.Field, + SymbolKind.Property => WorkflowCSharpEditorCompletionItemKind.Property, + SymbolKind.Local => WorkflowCSharpEditorCompletionItemKind.Variable, + SymbolKind.Method => WorkflowCSharpEditorCompletionItemKind.Function, + SymbolKind.NamedType => WorkflowCSharpEditorCompletionItemKind.Class, + _ => WorkflowCSharpEditorCompletionItemKind.Others, + }; } } public async Task<WorkflowCSharpEditorHoverInfoResult> GetHoverInfoAsync(WorkflowDefinition workflowDefinition, string textId, string text, int position, CancellationToken cancellationToken = default) { - var generated = await _cSharpTypeDefinitionService.GenerateAsync(workflowDefinition, cancellationToken); - - var projectName = workflowDefinition.DefinitionId.Replace("-", null); + var project = await GetProjectAsync(workflowDefinition, cancellationToken); - var project = _cSharpScriptWorkspace.GetOrCreateProject(projectName); + var document = project.CreateOrUpdateDocument(textId, text); - project.CreateOrUpdateDocument(GeneratedTypeClassName, generated.Text); +#if DEBUG + await project.SaveAsync(); +#endif - var document = project.CreateOrUpdateDocument(textId, text); + await _cSharpScriptWorkspace.RestoreReferenceAsync(project, cancellationToken); - var compilation = await _cSharpScriptWorkspace.CreateCompilationAsync(project, cancellationToken); + var compilation = await _cSharpScriptWorkspace.CreateCompilationAsync(project, false, cancellationToken); var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken); @@ -178,7 +192,7 @@ public async Task<WorkflowCSharpEditorHoverInfoResult> GetHoverInfoAsync(Workflo TypeInfo typeInfo; if (expressionNode is VariableDeclaratorSyntax) { - SyntaxNode childNode = expressionNode.ChildNodes()?.FirstOrDefault()?.ChildNodes()?.FirstOrDefault(); + var childNode = expressionNode.ChildNodes()?.FirstOrDefault()?.ChildNodes()?.FirstOrDefault(); if (childNode != null) { typeInfo = semanticModel.GetTypeInfo(childNode, cancellationToken: cancellationToken); @@ -249,29 +263,33 @@ public async Task<WorkflowCSharpEditorHoverInfoResult> GetHoverInfoAsync(Workflo public async Task<WorkflowCSharpEditorSignatureResult> GetSignaturesAsync(WorkflowDefinition workflowDefinition, string textId, string text, int position, CancellationToken cancellationToken = default) { - var generated = await _cSharpTypeDefinitionService.GenerateAsync(workflowDefinition, cancellationToken); - - var projectName = workflowDefinition.DefinitionId.Replace("-", null); - - var project = _cSharpScriptWorkspace.GetOrCreateProject(projectName); - - project.CreateOrUpdateDocument(GeneratedTypeClassName, generated.Text); + var project = await GetProjectAsync(workflowDefinition, cancellationToken); var document = project.CreateOrUpdateDocument(textId, text); - var compilation = await _cSharpScriptWorkspace.CreateCompilationAsync(project, cancellationToken); +#if DEBUG + await project.SaveAsync(); +#endif + await _cSharpScriptWorkspace.RestoreReferenceAsync(project, cancellationToken); + + var compilation = await _cSharpScriptWorkspace.CreateCompilationAsync(project, false, cancellationToken); var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken); var semanticModel = compilation.GetSemanticModel(syntaxTree, true); var invocation = await InvocationContext.GetInvocation(document, position); - if (invocation == null) return null; + if (invocation == null) + { + return null; + } - int activeParameter = 0; + var activeParameter = 0; foreach (var comma in invocation.Separators) { if (comma.Span.Start > invocation.Position) + { break; + } activeParameter++; } @@ -290,7 +308,7 @@ public async Task<WorkflowCSharpEditorSignatureResult> GetSignaturesAsync(Workfl var typeInfo = semanticModel.GetTypeInfo(throughExpression, cancellationToken: cancellationToken); throughSymbol = invocation.SemanticModel.GetSpeculativeSymbolInfo(invocation.Position, throughExpression, SpeculativeBindingOption.BindAsExpression).Symbol; throughType = invocation.SemanticModel.GetSpeculativeTypeInfo(invocation.Position, throughExpression, SpeculativeBindingOption.BindAsTypeOrNamespace).Type; - var includeInstance = (throughSymbol != null && !(throughSymbol is ITypeSymbol)) || + var includeInstance = (throughSymbol != null && throughSymbol is not ITypeSymbol) || throughExpression is LiteralExpressionSyntax || throughExpression is TypeOfExpressionSyntax; var includeStatic = throughSymbol is INamedTypeSymbol || throughType != null; @@ -344,35 +362,52 @@ private static string HoverInfoBuild(SymbolInfo symbolInfo) { var sb = new StringBuilder().Append("(method) ").Append(methodsymbol.DeclaredAccessibility.ToString().ToLower(System.Globalization.CultureInfo.CurrentCulture)).Append(' '); if (methodsymbol.IsStatic) - sb.Append("static").Append(' '); - sb.Append(methodsymbol.Name).Append('('); + { + sb = sb.Append("static").Append(' '); + } + + sb = sb.Append(methodsymbol.Name).Append('('); for (var i = 0; i < methodsymbol.Parameters.Length; i++) { - sb.Append(methodsymbol.Parameters[i].Type).Append(' ').Append(methodsymbol.Parameters[i].Name); - if (i < methodsymbol.Parameters.Length - 1) sb.Append(", "); + sb = sb.Append(methodsymbol.Parameters[i].Type).Append(' ').Append(methodsymbol.Parameters[i].Name); + if (i < methodsymbol.Parameters.Length - 1) + { + sb = sb.Append(", "); + } } - sb.Append(") : ") - .Append(methodsymbol.ReturnType).ToString(); + sb = sb.Append(") : ").Append(methodsymbol.ReturnType); return sb.ToString(); } if (symbolInfo.Symbol is ILocalSymbol localsymbol) { var sb = new StringBuilder().Append(localsymbol.Name).Append(" : "); if (localsymbol.IsConst) - sb.Append("const").Append(' '); - sb.Append(localsymbol.Type); + { + sb = sb.Append("const").Append(' '); + } + + sb = sb.Append(localsymbol.Type); return sb.ToString(); } if (symbolInfo.Symbol is IFieldSymbol fieldSymbol) { var sb = new StringBuilder().Append(fieldSymbol.Name).Append(" : ").Append(fieldSymbol.DeclaredAccessibility.ToString().ToLower(System.Globalization.CultureInfo.CurrentCulture)).Append(' '); if (fieldSymbol.IsStatic) - sb.Append("static").Append(' '); + { + sb = sb.Append("static").Append(' '); + } + if (fieldSymbol.IsReadOnly) - sb.Append("readonly").Append(' '); + { + sb = sb.Append("readonly").Append(' '); + } + if (fieldSymbol.IsConst) - sb.Append("const").Append(' '); - sb.Append(fieldSymbol.Type).ToString(); + { + sb = sb.Append("const").Append(' '); + } + + sb = sb.Append(fieldSymbol.Type); return sb.ToString(); } @@ -398,7 +433,9 @@ private static int InvocationScore(IMethodSymbol symbol, IEnumerable<TypeInfo> t { var parameters = symbol.Parameters; if (parameters.Length < types.Count()) + { return int.MinValue; + } var score = 0; var invocationEnum = types.GetEnumerator(); @@ -406,9 +443,13 @@ private static int InvocationScore(IMethodSymbol symbol, IEnumerable<TypeInfo> t while (invocationEnum.MoveNext() && definitionEnum.MoveNext()) { if (invocationEnum.Current.ConvertedType == null) + { score++; + } else if (SymbolEqualityComparer.Default.Equals(invocationEnum.Current.ConvertedType, definitionEnum.Current.Type)) + { score += 2; + } } return score; } @@ -417,7 +458,7 @@ public class InvocationContext { public static async Task<InvocationContext> GetInvocation(Document document, int position) { - var sourceText = await document.GetTextAsync(); + _ = await document.GetTextAsync(); var tree = await document.GetSyntaxTreeAsync(); var root = await tree.GetRootAsync(); var node = root.FindToken(position).Parent; diff --git a/src/Passingwind.CSharpScript/CSharpScriptContextState.cs b/src/Passingwind.CSharpScript/CSharpScriptContextState.cs index 5c56b352..ec65db8b 100644 --- a/src/Passingwind.CSharpScript/CSharpScriptContextState.cs +++ b/src/Passingwind.CSharpScript/CSharpScriptContextState.cs @@ -4,12 +4,11 @@ namespace Passingwind.CSharpScriptEngine; public class CSharpScriptContextState { - public CSharpScriptContext Context { get; } + public CSharpScriptContext Context { get; } = null!; + public Script Script { get; set; } = null!; public CSharpScriptContextState(CSharpScriptContext context) { Context = context; } - - public Script Script { get; set; } } diff --git a/src/Passingwind.CSharpScript/CSharpScriptEngineOptions.cs b/src/Passingwind.CSharpScript/CSharpScriptEngineOptions.cs index 2d6a6d72..2b18d6ba 100644 --- a/src/Passingwind.CSharpScript/CSharpScriptEngineOptions.cs +++ b/src/Passingwind.CSharpScript/CSharpScriptEngineOptions.cs @@ -6,11 +6,11 @@ namespace Passingwind.CSharpScriptEngine; public class CSharpScriptEngineOptions { public string NuGetCachePath { get; set; } - public List<string> NuGetServer { get; } + public List<string> NuGetServers { get; } public CSharpScriptEngineOptions() { - NuGetServer = new List<string>(); + NuGetServers = new List<string>(); #if DEBUG NuGetCachePath = System.IO.Path.Combine(AppContext.BaseDirectory, ".nuget"); #else diff --git a/src/Passingwind.CSharpScript/CSharpScriptHost.cs b/src/Passingwind.CSharpScript/CSharpScriptHost.cs index a80326bc..4ad76893 100644 --- a/src/Passingwind.CSharpScript/CSharpScriptHost.cs +++ b/src/Passingwind.CSharpScript/CSharpScriptHost.cs @@ -31,7 +31,10 @@ public class CSharpScriptHost : ICSharpScriptHost, IDisposable public static ParseOptions DefaultParseOptions => CSharpParseOptions .Default .WithPreprocessorSymbols(PreprocessorSymbols) - .WithKind(SourceCodeKind.Script); + .WithDocumentationMode(DocumentationMode.Parse) + .WithLanguageVersion(LanguageVersion.Latest) + .WithKind(SourceCodeKind.Script) + .CommonWithKind(SourceCodeKind.Script); #if NET8_0 public static ImmutableArray<MetadataReference> StandardMetadataReference = ImmutableArray.CreateRange(Basic.Reference.Assemblies.Net80.References.All.Select(GetMetadataReference)); @@ -66,22 +69,15 @@ public class CSharpScriptHost : ICSharpScriptHost, IDisposable "System.Collections", "System.Collections.Concurrent", "System.Collections.Generic", - "System.Console", "System.Dynamic", "System.Globalization", "System.IO", "System.Linq", "System.Reflection", "System.Text", - "System.Text", - "System.Text.Encoding", - "System.Text.RegularExpressions", "System.Text.RegularExpressions", "System.Threading", - "System.Threading.Tasks", - "System.Threading.Tasks.Parallel", - "System.Threading.Thread", - "System.ValueTuple", + "System.Threading.Tasks" }.ToImmutableArray(); private static readonly ConcurrentDictionary<string, CSharpScriptContextState> ScriptStateCache = new ConcurrentDictionary<string, CSharpScriptContextState>(); diff --git a/src/Passingwind.CSharpScript/CSharpScriptProject.cs b/src/Passingwind.CSharpScript/CSharpScriptProject.cs index 2ff5e3dd..749c6729 100644 --- a/src/Passingwind.CSharpScript/CSharpScriptProject.cs +++ b/src/Passingwind.CSharpScript/CSharpScriptProject.cs @@ -1,12 +1,10 @@ using System.Collections.Generic; -using System.Collections.Immutable; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Text; namespace Passingwind.CSharpScriptEngine; @@ -16,24 +14,25 @@ public class CSharpScriptProject : ICSharpScriptProject private readonly List<Assembly> _references = new List<Assembly>(); private readonly List<string> _imports = new List<string>(); + private Project _project = null!; + public string Id { get; } public string Name { get; } - - public Project Project { get; private set; } - + public ProjectId ProjectId { get; } protected AdhocWorkspace Workspace { get; } - public CSharpScriptProject(AdhocWorkspace workspace, Project project) + public CSharpScriptProject(AdhocWorkspace workspace, ProjectId projectId, string projectName) { - Id = project.Id.Id.ToString(); - Name = project.Name; + Id = projectId.Id.ToString(); + Name = projectName; Workspace = workspace; - Project = project; + ProjectId = projectId; + ReloadProject(); } - public void Refresh() + public void ReloadProject() { - Project = Workspace.CurrentSolution.GetProject(Project.Id)!; + _project = Workspace.CurrentSolution.GetProject(ProjectId)!; } public IReadOnlyList<string> GetImports() @@ -46,14 +45,20 @@ public IReadOnlyList<Assembly> GetReferences() return _references; } - public void AddReferences(IEnumerable<Assembly> assemblies) + public ICSharpScriptProject AddReferences(IEnumerable<Assembly> assemblies) { _references.AddRange(assemblies); + + _project = _project.AddMetadataReferences(assemblies.Select(x => MetadataReference.CreateFromFile(x.Location))); + + return this; } - public void AddImports(IEnumerable<string> usings) + public ICSharpScriptProject AddImports(IEnumerable<string> usings) { _imports.AddRange(usings); + + return this; } public Document CreateOrUpdateDocument(string fileName, string sourceText) @@ -62,62 +67,44 @@ public Document CreateOrUpdateDocument(string fileName, string sourceText) { var name = Path.GetFileNameWithoutExtension(fileName); - var document = Project.Documents.FirstOrDefault(x => x.Name == name); + var document = _project.Documents.FirstOrDefault(x => x.Name == name); if (document != null) { - var solution = Workspace.CurrentSolution.WithDocumentText(document.Id, SourceText.From(sourceText, Encoding.UTF8)); - // Workspace.TryApplyChanges(solution); - } - else - { - document = Workspace.AddDocument(Project.Id, name, SourceText.From(sourceText)); - Workspace.TryApplyChanges(Project.Solution); + _ = Workspace.CurrentSolution.RemoveDocument(document.Id); } - Refresh(); + document = Workspace.AddDocument(_project.Id, name, SourceText.From(sourceText, Encoding.UTF8)); + + Workspace.TryApplyChanges(Workspace.CurrentSolution); + + ReloadProject(); return document; } } - public async Task<IReadOnlyList<CompletionItem>> GetCompletionsAsync(Document document, int position) + public async Task SaveAsync() { - var completionService = CompletionService.GetService(document); + var root = _project.FilePath; - if (completionService == null) + if (string.IsNullOrWhiteSpace(root)) { - return new List<CompletionItem>(); + return; } - var completionResult = await completionService.GetCompletionsAsync(document, position, CompletionTrigger.Invoke); - - var text = await document.GetTextAsync(); - - var textSpanToText = new Dictionary<TextSpan, string>(); - - return completionResult.ItemsList.Where(item => MatchesFilterText(completionService, document, item, text, textSpanToText)).ToList(); - } - - private static bool MatchesFilterText(CompletionService completionService, Document document, CompletionItem item, SourceText text, Dictionary<TextSpan, string> textSpanToText) - { - var filterText = GetFilterText(item, text, textSpanToText); - if (string.IsNullOrEmpty(filterText)) + if (!Directory.Exists(root)) { - return true; + Directory.CreateDirectory(root); } - return completionService.FilterItems(document, ImmutableArray.Create(item), filterText).Length > 0; - } - - private static string GetFilterText(CompletionItem item, SourceText text, Dictionary<TextSpan, string> textSpanToText) - { - var textSpan = item.Span; - if (!textSpanToText.TryGetValue(textSpan, out var filterText)) + foreach (var document in _project.Documents) { - filterText = text.GetSubText(textSpan).ToString(); - textSpanToText[textSpan] = filterText; + var file = Path.Combine(root, document.FilePath ?? document.Name + ".cs"); + var text = await document.GetTextAsync(); + await File.WriteAllTextAsync(file, text?.ToString()); } - return filterText; + // TODO + // project file ? } } diff --git a/src/Passingwind.CSharpScript/CSharpScriptWorkspace.cs b/src/Passingwind.CSharpScript/CSharpScriptWorkspace.cs index 1d792b0e..886c7392 100644 --- a/src/Passingwind.CSharpScript/CSharpScriptWorkspace.cs +++ b/src/Passingwind.CSharpScript/CSharpScriptWorkspace.cs @@ -10,11 +10,13 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Passingwind.CSharpScriptEngine; @@ -40,11 +42,7 @@ public class CSharpScriptWorkspace : ICSharpScriptWorkspace, IDisposable //public static IEnumerable<Type> GetDiagnosticCompositionTypes() => typeof(AdhocWorkspace).Assembly.GetExportedTypes().Where(x => x.Namespace.StartsWith("Microsoft.CodeAnalysis.Diagnostics") || x.Namespace.StartsWith("Microsoft.CodeAnalysis.CodeFixes")).ToList(); - public static ParseOptions DefaultParseOptions => CSharpParseOptions - .Default - .WithPreprocessorSymbols(PreprocessorSymbols) - .WithKind(SourceCodeKind.Script) - .CommonWithKind(SourceCodeKind.Regular); + public static ParseOptions DefaultParseOptions => CSharpScriptHost.DefaultParseOptions; public static ImmutableArray<string> DefaultSuppressedDiagnostics => ImmutableArray.Create("CS1701", "CS1702", "CS1705", "CS7011", "CS8097"); @@ -93,7 +91,7 @@ public ICSharpScriptProject GetOrCreateProject(string projectId) { var compilationOptions = CreateDefaultCompilationOptions(); var csProject = CreateProject(_workspace, _, compilationOptions); - return new CSharpScriptProject(_workspace, csProject); + return new CSharpScriptProject(_workspace, csProject.Id, csProject.Name); }); } @@ -105,47 +103,31 @@ protected Project CreateProject(AdhocWorkspace workspace, string projectName, CS var projectId = ProjectId.CreateNewId(); - var root = GetProjectFolder(projectId); - var file = Path.Combine(root, projectId.Id.ToString()); + var root = GetProjectFolder(projectName); + var outputFilePath = Path.Combine(root, "bin", projectName + ".dll"); var projectInfo = ProjectInfo.Create( - projectId, - VersionStamp.Create(), - projectName, - projectName, - LanguageNames.CSharp, - filePath: $"{file}.dll", - outputFilePath: file, + id: projectId, + version: VersionStamp.Create(), + name: projectName, + assemblyName: projectName, + language: LanguageNames.CSharp, + filePath: root, + outputFilePath: outputFilePath, compilationOptions: compilationOptions, parseOptions: DefaultParseOptions, metadataReferences: references, analyzerReferences: analyzerReferences ); - return workspace.AddProject(projectInfo); + var project = workspace.AddProject(projectInfo); - //if (compilationOptions.Usings.Length > 0) - //{ - // var globalUsingCode = string.Join("\n", compilationOptions.Usings.Select(x => $"global using {x};")); - // CreateDocument(project.Id, "GeneratedUsings", globalUsingCode); - //} - } + // auto add global using + workspace.AddDocument(projectId, "GlobalUsings", SourceText.From(CSharpScriptWorkspace.GenerateGlobalUsings(compilationOptions.Usings.ToArray()))); - protected CSharpCompilationOptions CreateDefaultCompilationOptions(IEnumerable<string>? AdditionalImports = null) - { - var hostImports = DefaultImports.Concat(AdditionalImports ?? Array.Empty<string>()); + workspace.TryApplyChanges(workspace.CurrentSolution); - return new CSharpCompilationOptions( - OutputKind.DynamicallyLinkedLibrary, - reportSuppressedDiagnostics: false, - usings: hostImports, - checkOverflow: true, - allowUnsafe: false, - generalDiagnosticOption: ReportDiagnostic.Warn, - specificDiagnosticOptions: _specificDiagnosticOptions, - concurrentBuild: false, - metadataReferenceResolver: CSharpScriptMetadataReferenceResolver.Instance, - nullableContextOptions: NullableContextOptions.Enable); + return project; } protected virtual IEnumerable<AnalyzerReference> GetSolutionAnalyzerReferences() @@ -157,9 +139,9 @@ protected virtual IEnumerable<AnalyzerReference> GetSolutionAnalyzerReferences() yield return new AnalyzerFileReference(typeof(CSharpFeaturesResources).Assembly.Location, loader); } - private static string GetProjectFolder(ProjectId projectId) + private static string GetProjectFolder(string project) { - var root = Path.Combine(Path.GetTempPath(), "roslyns", projectId.Id.ToString()); + var root = Path.Combine(Path.GetTempPath(), "workflow", "roslyns", project); if (!Directory.Exists(root)) { Directory.CreateDirectory(root); @@ -176,65 +158,173 @@ public void Dispose() _compositionHost.Dispose(); } - public async Task<CSharpCompilation> CreateCompilationAsync(ICSharpScriptProject scriptProject, CancellationToken cancellationToken = default) + protected static string GenerateGlobalUsings(params string[] imports) + { + return string.Join("\r\n", imports.Select(x => $"global using {x};")); + } + + protected CSharpCompilationOptions CreateDefaultCompilationOptions(IEnumerable<string>? additionalImports = null) + { + var imports = DefaultImports.Concat(additionalImports ?? Array.Empty<string>()); + + return new CSharpCompilationOptions( + OutputKind.DynamicallyLinkedLibrary, + reportSuppressedDiagnostics: false, + scriptClassName: "Program", + usings: imports, + checkOverflow: true, + allowUnsafe: false, + // generalDiagnosticOption: ReportDiagnostic.Default, + specificDiagnosticOptions: _specificDiagnosticOptions, + concurrentBuild: false, + metadataReferenceResolver: CSharpScriptMetadataReferenceResolver.Instance, + nullableContextOptions: NullableContextOptions.Enable); + } + + public async Task<CSharpCompilation> CreateCompilationAsync(ICSharpScriptProject scriptProject, bool removeReferenceDirective = false, CancellationToken cancellationToken = default) { var scriptOptions = CSharpScriptHost.CreateScriptOptions(scriptProject.GetImports(), scriptProject.GetReferences()); var parseOptions = CSharpScriptHost.CreateParseOptions(); scriptOptions = scriptOptions.AddReferences(CSharpScriptHost.StandardPortableReference); + var project = _workspace.CurrentSolution.GetProject(scriptProject.ProjectId) ?? throw new InvalidOperationException("Project not found."); + var syntaxTrees = new List<SyntaxTree>(); - foreach (var item in scriptProject.Project.Documents) + foreach (var item in project.Documents) { - var sourceText = (await item.GetTextAsync()).ToString(); + var sourceText = (await item.GetTextAsync(cancellationToken)).ToString(); - scriptOptions.AddReferences(await _scriptReferenceResolver.ResolveReferencesAsync(sourceText)); + // resolve references + scriptOptions = scriptOptions.AddReferences(await _scriptReferenceResolver.ResolveReferencesAsync(sourceText, cancellationToken)); - var syntaxTree = SyntaxFactory.ParseSyntaxTree(sourceText, options: parseOptions, encoding: Encoding.UTF8, cancellationToken: cancellationToken); + var syntaxTree = await item.GetSyntaxTreeAsync(cancellationToken); - // remove reference directive - syntaxTree = syntaxTree.RemoveReferenceDirectives(parseOptions, cancellationToken: cancellationToken); + if (removeReferenceDirective) + { + syntaxTree = syntaxTree!.RemoveReferenceDirectives(parseOptions, cancellationToken: cancellationToken); + } - syntaxTrees.Add(syntaxTree); + syntaxTrees.Add(syntaxTree!); } var name = scriptProject.Name; - var compilationOptions = new CSharpCompilationOptions( - outputKind: OutputKind.DynamicallyLinkedLibrary, - scriptClassName: "Program", - usings: scriptOptions.Imports, - checkOverflow: scriptOptions.CheckOverflow, - allowUnsafe: scriptOptions.AllowUnsafe, - metadataReferenceResolver: scriptOptions.MetadataResolver, - nullableContextOptions: NullableContextOptions.Enable); + var compilationOptions = CreateDefaultCompilationOptions(scriptOptions.Imports); - return CSharpCompilation.Create( - name, - syntaxTrees: syntaxTrees, - references: scriptOptions.MetadataReferences, - options: compilationOptions); + return CSharpCompilation.Create(name, syntaxTrees: syntaxTrees, references: scriptOptions.MetadataReferences, options: compilationOptions); } public async Task<EmitResult> CompileAsync(ICSharpScriptProject scriptProject, CancellationToken cancellationToken = default) { var name = scriptProject.Name; - var compilation = await CreateCompilationAsync(scriptProject, cancellationToken); + var project = _workspace.CurrentSolution.GetProject(scriptProject.ProjectId) ?? throw new InvalidOperationException("Project not found."); + + var compilation = await CreateCompilationAsync(scriptProject, true, cancellationToken); - var rootFolder = Path.Combine(Path.GetTempPath(), "workflow", "csharp", name); + var rootFolder = GetProjectFolder(name); - if (!Directory.Exists(rootFolder)) + string outputFilePath = project.OutputFilePath ?? Path.Combine(rootFolder, "bin", $"{name}.dll"); + + if (!Directory.Exists(Path.GetDirectoryName(outputFilePath))) { - Directory.CreateDirectory(rootFolder); + Directory.CreateDirectory(Path.GetDirectoryName(outputFilePath)!); } - var file = Path.Combine(rootFolder, name); - - await using var peStream = File.OpenWrite($"{file}.dll"); - await using var pdbStream = File.OpenWrite($"{file}.pdb"); + await using var peStream = File.OpenWrite(outputFilePath); + await using var pdbStream = File.OpenWrite(Path.ChangeExtension(outputFilePath, ".pdb")); return compilation.Emit(peStream, pdbStream, options: new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb), cancellationToken: cancellationToken); } + + public async Task RestoreReferenceAsync(ICSharpScriptProject scriptProject, CancellationToken cancellationToken = default) + { + var projectId = scriptProject.ProjectId; + var project = _workspace.CurrentSolution.GetProject(scriptProject.ProjectId) ?? throw new InvalidOperationException("Project not found."); + + foreach (var document in project.Documents) + { + var sourceText = (await document.GetTextAsync(cancellationToken)).ToString(); + + var references = await _scriptReferenceResolver.ResolveReferencesAsync(sourceText, cancellationToken); + + foreach (var item in references) + { + if (!project.MetadataReferences.Any(x => IsSameMetadataReference(x, item))) + { + var solution = _workspace.CurrentSolution.AddMetadataReferences(projectId, references); + + _workspace.TryApplyChanges(solution); + } + } + } + + scriptProject.ReloadProject(); + + bool IsSameMetadataReference(MetadataReference a, MetadataReference b) + { + if (a.GetType() != b.GetType()) + { + return false; + } + + return a is PortableExecutableReference executableReferenceA && b is PortableExecutableReference executableReferenceB && executableReferenceA.FilePath == executableReferenceB.FilePath; + } + } + + public async Task<IReadOnlyList<CompletionItem>> GetCompletionsAsync(ICSharpScriptProject scriptProject, DocumentId documentId, int position, bool filter = true, CancellationToken cancellationToken = default) + { + var projectId = scriptProject.ProjectId; + var project = _workspace.CurrentSolution.GetProject(scriptProject.ProjectId) ?? throw new InvalidOperationException("Project not found."); + + var document = await project.GetDocumentAsync(documentId, cancellationToken: cancellationToken) ?? throw new InvalidOperationException("The document not found."); + var completionService = CompletionService.GetService(document); + + if (completionService == null) + { + return new List<CompletionItem>(); + } + + var completionResult = await completionService.GetCompletionsAsync(document, position, cancellationToken: cancellationToken); + + var text = await document.GetTextAsync(cancellationToken); + + var textSpanToText = new Dictionary<TextSpan, string>(); + + if (!filter) + { + return completionResult.ItemsList; + } + + return completionResult.ItemsList.Where(item => MatchesFilterText(completionService, document, item, text, textSpanToText)).ToList(); + } + + private static bool MatchesFilterText(CompletionService completionService, Document document, CompletionItem item, SourceText text, Dictionary<TextSpan, string> textSpanToText) + { + var filterText = GetFilterText(item, text, textSpanToText); + if (string.IsNullOrEmpty(filterText)) + { + return true; + } + + return completionService.FilterItems(document, ImmutableArray.Create(item), filterText).Length > 0; + } + + private static string GetFilterText(CompletionItem item, SourceText text, Dictionary<TextSpan, string> textSpanToText) + { + var textSpan = item.Span; + if (!textSpanToText.TryGetValue(textSpan, out var filterText)) + { + filterText = text.GetSubText(textSpan).ToString(); + textSpanToText[textSpan] = filterText; + } + return filterText; + } + + public async Task<Document?> GetDocumentAsync(DocumentId documentId) + { + return await _workspace.CurrentSolution.GetDocumentAsync(documentId); + } } diff --git a/src/Passingwind.CSharpScript/DefaultNuGetLocalPackageAssemblyResolver.cs b/src/Passingwind.CSharpScript/DefaultNuGetLocalPackageAssemblyResolver.cs new file mode 100644 index 00000000..50e4bbab --- /dev/null +++ b/src/Passingwind.CSharpScript/DefaultNuGetLocalPackageAssemblyResolver.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Options; +using NuGet.Frameworks; +using NuGet.Packaging; +using NuGet.Packaging.Core; +using NuGet.Repositories; + +namespace Passingwind.CSharpScriptEngine; + +public class DefaultNuGetLocalPackageAssemblyResolver : INuGetLocalPackageAssemblyResolver +{ + private readonly NuGetv3LocalRepository _localRepository; + + private readonly CSharpScriptEngineOptions _engineOptions; + + public DefaultNuGetLocalPackageAssemblyResolver(IOptions<CSharpScriptEngineOptions> engineOptions) + { + _engineOptions = engineOptions.Value; + + _localRepository = new NuGetv3LocalRepository(_engineOptions.NuGetCachePath); + } + + public async Task<IEnumerable<string>> GetReferencesAsync(PackageIdentity package, NuGetFramework framework, CancellationToken cancellationToken = default) + { + if (!_localRepository.Exists(package.Id, package.Version)) + { + throw new InvalidOperationException($"The package '{package}' not found."); + } + + var packageInfo = _localRepository.FindPackage(package.Id, package.Version); + + var file = packageInfo.ZipPath!; + + var root = Path.GetDirectoryName(file); + + using var archiveReader = new PackageArchiveReader(file); + + var supportedFrameworks = await archiveReader.GetSupportedFrameworksAsync(cancellationToken); + var referenceItemGroups = await archiveReader.GetReferenceItemsAsync(cancellationToken); + // var dependencyGroups = await archiveReader.GetPackageDependenciesAsync(cancellationToken); + + var packageTargetFramework = NuGetFrameworkUtility.GetNearest(supportedFrameworks, framework, x => x); + + var targetReferenceItemGroup = referenceItemGroups.FirstOrDefault(x => x.TargetFramework == packageTargetFramework); + + if (targetReferenceItemGroup == null) + { + return new List<string>(); + } + + var referenceFiles = targetReferenceItemGroup.Items?.Select(x => Path.Combine(root!, x)).ToList(); + + return referenceFiles ?? new List<string>(); + } +} diff --git a/src/Passingwind.CSharpScript/DefaultNuGetPackageService.cs b/src/Passingwind.CSharpScript/DefaultNuGetPackageService.cs new file mode 100644 index 00000000..585bdcf1 --- /dev/null +++ b/src/Passingwind.CSharpScript/DefaultNuGetPackageService.cs @@ -0,0 +1,257 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using NuGet.Configuration; +using NuGet.Frameworks; +using NuGet.Packaging; +using NuGet.Packaging.Core; +using NuGet.Protocol; +using NuGet.Protocol.Core.Types; +using NuGet.Repositories; +using NuGet.Versioning; + +namespace Passingwind.CSharpScriptEngine; + +public class DefaultNuGetPackageService : INuGetPackageService +{ + protected IReadOnlyList<SourceRepository> Repositories { get; } + + private readonly NuGetv3LocalRepository _localRepository; + private readonly SourceCacheContext _sourceCacheContext; + private readonly string _nuGetCacheFolder; + + private readonly CSharpScriptEngineOptions _engineOptions; + private readonly ILogger<DefaultNuGetPackageService> _logger; + private readonly NuGet.Common.ILogger _nuGetLogger = NuGet.Common.NullLogger.Instance; + private readonly INuGetLocalPackageAssemblyResolver _nuGetPackageAssemblyResolver; + + public DefaultNuGetPackageService(IOptions<CSharpScriptEngineOptions> engineOptions, ILogger<DefaultNuGetPackageService> logger, NuGet.Common.ILogger nuGetLogger, INuGetLocalPackageAssemblyResolver nuGetPackageAssemblyResolver) + { + _engineOptions = engineOptions.Value; + _logger = logger; + _nuGetLogger = nuGetLogger; + _nuGetPackageAssemblyResolver = nuGetPackageAssemblyResolver; + + Repositories = GetSourceRepositories(); + _sourceCacheContext = new SourceCacheContext() { IgnoreFailedSources = true, MaxAge = System.DateTimeOffset.Now.AddHours(1), }; + _localRepository = new NuGetv3LocalRepository(_engineOptions.NuGetCachePath); + _nuGetCacheFolder = _engineOptions.NuGetCachePath; + } + + public async Task<IReadOnlyList<string>> GetReferencesAsync(string packageId, string version, string targetFramework, bool resolveDependency = true, CancellationToken cancellationToken = default) + { + var package = new PackageIdentity(packageId, NuGetVersion.Parse(version)); + + var framework = NuGetFramework.Parse(targetFramework); + + return await GetReferencesAsync(package, framework, resolveDependency, cancellationToken); + } + + public async Task<IReadOnlyList<string>> GetReferencesAsync(PackageIdentity package, NuGetFramework targetFramework, bool resolveDependency = true, CancellationToken cancellationToken = default) + { + await DownloadAsync(package, cancellationToken); + + var references = new List<string>(); + references.AddRange(await _nuGetPackageAssemblyResolver.GetReferencesAsync(package, targetFramework, cancellationToken)); + + // dependency + if (resolveDependency) + { + var dependencies = await GetDependenciesAsync(package, targetFramework, cancellationToken); + + foreach (var item in dependencies) + { + references.AddRange(await GetReferencesAsync(item, targetFramework, resolveDependency, cancellationToken)); + } + } + + return references; + } + + public async Task<IReadOnlyList<PackageIdentity>> GetDependenciesAsync(string packageId, string version, string targetFramework, CancellationToken cancellationToken = default) + { + var package = new PackageIdentity(packageId, NuGetVersion.Parse(version)); + + return await GetDependenciesAsync(package, NuGetFramework.Parse(targetFramework), cancellationToken); + } + + protected async Task<IReadOnlyList<PackageIdentity>> GetDependenciesAsync(PackageIdentity package, NuGetFramework targetFramework, CancellationToken cancellationToken = default) + { + await DownloadAsync(package, cancellationToken); + + var file = GetPackageFile(package); + + if (!File.Exists(file)) + { + throw new FileNotFoundException(file); + } + + var reader = new PackageArchiveReader(file); + + return await GetDependenciesAsync(reader, targetFramework, cancellationToken); + } + + protected async Task<List<PackageIdentity>> GetDependenciesAsync(PackageArchiveReader archiveReader, NuGetFramework targetFramework, CancellationToken cancellationToken = default) + { + var dependencyGroups = await archiveReader.GetPackageDependenciesAsync(cancellationToken); + + var packageDependencyGroup = NuGetFrameworkUtility.GetNearest(dependencyGroups, targetFramework); + + var result = new List<PackageIdentity>(); + + if (packageDependencyGroup?.Packages?.Any() == true) + { + foreach (var packageDependency in packageDependencyGroup.Packages) + { + // TODO + var package = new PackageIdentity(packageDependency.Id, packageDependency.VersionRange.MinVersion); + + result.Add(package); + } + } + + return result; + } + + public async Task DownloadAsync(string packageId, string version, CancellationToken cancellationToken = default) + { + var package = new PackageIdentity(packageId, NuGetVersion.Parse(version)); + + await DownloadAsync(package, cancellationToken); + } + + public async Task DownloadAsync(PackageIdentity package, CancellationToken cancellationToken = default) + { + if (IsPackageSaved(package)) + { + // _logger.LogDebug("Package '{Package}' found in local ", package); + return; + } + + await DownloadAndSaveAsync(package, cancellationToken); + } + + protected async Task DownloadAndSaveAsync(PackageIdentity package, CancellationToken cancellationToken = default) + { + _logger.LogDebug("Downloading package '{Package}' ", package); + + var packageExtractionContext = new PackageExtractionContext(PackageSaveMode.Defaultv3, XmlDocFileSaveMode.Compress, null, _nuGetLogger); + + foreach (var repository in Repositories) + { + var resource = await repository.GetResourceAsync<FindPackageByIdResource>(); + if (resource == null) + { + continue; + } + + var _packageId = package; + var _resource = resource; + + await SavePackageAsync(_resource, _packageId, packageExtractionContext, cancellationToken); + } + } + + protected async Task SavePackageAsync(FindPackageByIdResource resource, PackageIdentity package, PackageExtractionContext packageExtractionContext, CancellationToken cancellationToken = default) + { + if (IsPackageSaved(package)) + { + return; + } + + if (await resource.DoesPackageExistAsync(package.Id, package.Version, _sourceCacheContext, _nuGetLogger, cancellationToken)) + { + _logger.LogDebug("Extracting package '{Package}' ", package); + + var downloader = await resource.GetPackageDownloaderAsync(package, _sourceCacheContext, _nuGetLogger, cancellationToken); + + await PackageExtractor.InstallFromSourceAsync(package, downloader, new VersionFolderPathResolver(_nuGetCacheFolder), packageExtractionContext, cancellationToken); + + _logger.LogDebug("Package '{Package}' installed to '{NuGetCacheFolder}' ", package, _nuGetCacheFolder); + } + else + { + _logger.LogError("Package '{Package}' not found int repository", package); + } + } + + public async Task<IReadOnlyList<string>> GetVersionsAsync(string packageId, CancellationToken cancellationToken = default) + { + var tasks = Repositories.Select(x => GetPackageVersionsAsync(x, packageId, cancellationToken)); + + var result = await Task.WhenAll(tasks); + + return (IReadOnlyList<string>)result.SelectMany(x => x).Distinct().OrderByDescending(x => x); + } + + private async Task<IReadOnlyList<string>> GetPackageVersionsAsync(SourceRepository repository, string packageId, CancellationToken cancellationToken = default) + { + _logger.LogError("Fech package '{PackageId}' versions in repository '{repository}'", packageId, repository); + + var resource = await repository.GetResourceAsync<FindPackageByIdResource>(); + + var allVersion = await resource.GetAllVersionsAsync(packageId, _sourceCacheContext, _nuGetLogger, cancellationToken); + + return (IReadOnlyList<string>)allVersion.Select(x => x.Version.ToString()); + } + + public async Task<IReadOnlyList<string>> SearchAsync(string filter, int resultCount = 10, CancellationToken cancellationToken = default) + { + var tasks = Repositories.Select(x => SearchAsync(x, filter, resultCount, cancellationToken)); + + var result = await Task.WhenAll(tasks); + + return (IReadOnlyList<string>)result.SelectMany(x => x).Distinct().OrderBy(x => x); + } + + private async Task<IReadOnlyList<string>> SearchAsync(SourceRepository repository, string filter, int resultCount = 10, CancellationToken cancellationToken = default) + { + _logger.LogError("Search '{Filter}' in repository '{repository}'", filter, repository); + + var resource = await repository.GetResourceAsync<PackageSearchResource>(); + + var result = await resource.SearchAsync(filter, new SearchFilter(true, null), 0, resultCount, _nuGetLogger, cancellationToken); + + return result.Select(x => x.Identity.Id).ToList(); + } + + protected bool IsPackageSaved(PackageIdentity package) + { + return _localRepository.Exists(package.Id, package.Version); + } + + protected string? GetPackageFile(PackageIdentity packageId) + { + var package = _localRepository.FindPackage(packageId.Id, packageId.Version); + + return package?.ZipPath; + } + + protected IReadOnlyList<SourceRepository> GetSourceRepositories() + { + var settings = Settings.LoadDefaultSettings(Directory.GetCurrentDirectory()); + + var packageSourceProvider = new PackageSourceProvider(settings); + + var sourceRepositoryProvider = new SourceRepositoryProvider(packageSourceProvider, Repository.Provider.GetCoreV3()); + + var originSources = sourceRepositoryProvider.GetRepositories(); + + var result = new List<SourceRepository>(); + result.AddRange(originSources); + + if (_engineOptions.NuGetServers.Any()) + { + foreach (var item in _engineOptions.NuGetServers) + { + result.Add(Repository.Factory.GetCoreV3(item)); + } + } + + return result; + } +} diff --git a/src/Passingwind.CSharpScript/ICSharpScriptProject.cs b/src/Passingwind.CSharpScript/ICSharpScriptProject.cs index 945ed4db..694633c4 100644 --- a/src/Passingwind.CSharpScript/ICSharpScriptProject.cs +++ b/src/Passingwind.CSharpScript/ICSharpScriptProject.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Reflection; +using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Completion; @@ -11,17 +12,17 @@ public interface ICSharpScriptProject string Id { get; } string Name { get; } - Project Project { get; } + ProjectId ProjectId { get; } - void Refresh(); + void ReloadProject(); Document CreateOrUpdateDocument(string fileName, string sourceText); - Task<IReadOnlyList<CompletionItem>> GetCompletionsAsync(Document document, int position); - - void AddReferences(IEnumerable<Assembly> assemblies); - void AddImports(IEnumerable<string> usings); + ICSharpScriptProject AddReferences(IEnumerable<Assembly> assemblies); + ICSharpScriptProject AddImports(IEnumerable<string> usings); IReadOnlyList<string> GetImports(); IReadOnlyList<Assembly> GetReferences(); + + Task SaveAsync(); } diff --git a/src/Passingwind.CSharpScript/ICSharpScriptWorkspace.cs b/src/Passingwind.CSharpScript/ICSharpScriptWorkspace.cs index 4221d97a..44dd4e28 100644 --- a/src/Passingwind.CSharpScript/ICSharpScriptWorkspace.cs +++ b/src/Passingwind.CSharpScript/ICSharpScriptWorkspace.cs @@ -1,5 +1,8 @@ -using System.Threading; +using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Emit; @@ -11,7 +14,13 @@ public interface ICSharpScriptWorkspace ICSharpScriptProject GetOrCreateProject(string projectId); - Task<CSharpCompilation> CreateCompilationAsync(ICSharpScriptProject scriptProject, CancellationToken cancellationToken = default); + Task<CSharpCompilation> CreateCompilationAsync(ICSharpScriptProject scriptProject, bool removeReferenceDirective = false, CancellationToken cancellationToken = default); Task<EmitResult> CompileAsync(ICSharpScriptProject scriptProject, CancellationToken cancellationToken = default); + + Task<IReadOnlyList<CompletionItem>> GetCompletionsAsync(ICSharpScriptProject scriptProject, DocumentId documentId, int position, bool filter = true, CancellationToken cancellationToken = default); + + Task RestoreReferenceAsync(ICSharpScriptProject scriptProject, CancellationToken cancellationToken = default); + + Task<Document?> GetDocumentAsync(DocumentId documentId); } diff --git a/src/Passingwind.CSharpScript/INuGetLocalPackageAssemblyResolver.cs b/src/Passingwind.CSharpScript/INuGetLocalPackageAssemblyResolver.cs new file mode 100644 index 00000000..0f889d51 --- /dev/null +++ b/src/Passingwind.CSharpScript/INuGetLocalPackageAssemblyResolver.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using NuGet.Frameworks; +using NuGet.Packaging.Core; + +namespace Passingwind.CSharpScriptEngine; + +public interface INuGetLocalPackageAssemblyResolver +{ + Task<IEnumerable<string>> GetReferencesAsync(PackageIdentity package, NuGetFramework framework, CancellationToken cancellationToken = default); +} diff --git a/src/Passingwind.CSharpScript/INuGetPackageService.cs b/src/Passingwind.CSharpScript/INuGetPackageService.cs index 2191934f..063d894a 100644 --- a/src/Passingwind.CSharpScript/INuGetPackageService.cs +++ b/src/Passingwind.CSharpScript/INuGetPackageService.cs @@ -1,13 +1,34 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using NuGet.Packaging.Core; namespace Passingwind.CSharpScriptEngine; public interface INuGetPackageService { - Task<IEnumerable<string>> SearchAsync(string name, int resultCount = 10, CancellationToken cancellationToken = default); - Task DownloadAsync(string packageId, string version, string tagetFrameworkName, bool dependency = false, CancellationToken cancellationToken = default); - Task<IEnumerable<string>> GetPackageVersionsAsync(string packageId, CancellationToken cancellationToken = default); - Task<IEnumerable<string>> GetReferencesAsync(string packageId, string version, string tagetFrameworkName, bool resolveDependency = false, bool downloadPackage = false, CancellationToken cancellationToken = default); + /// <summary> + /// Search packages from repositores + /// </summary> + Task<IReadOnlyList<string>> SearchAsync(string filter, int resultCount = 10, CancellationToken cancellationToken = default); + + /// <summary> + /// Get package all version from repositores + /// </summary> + Task<IReadOnlyList<string>> GetVersionsAsync(string packageId, CancellationToken cancellationToken = default); + + /// <summary> + /// Download package into local from repositores + /// </summary> + Task DownloadAsync(string packageId, string version, CancellationToken cancellationToken = default); + + /// <summary> + /// Get package dependencies to the specified framework + /// </summary> + Task<IReadOnlyList<PackageIdentity>> GetDependenciesAsync(string packageId, string version, string targetFramework, CancellationToken cancellationToken = default); + + /// <summary> + /// Get package references to the specified framework + /// </summary> + Task<IReadOnlyList<string>> GetReferencesAsync(string packageId, string version, string targetFramework, bool resolveDependency = true, CancellationToken cancellationToken = default); } diff --git a/src/Passingwind.CSharpScript/NuGetLogger.cs b/src/Passingwind.CSharpScript/NuGetLogger.cs index ae763f51..b7214368 100644 --- a/src/Passingwind.CSharpScript/NuGetLogger.cs +++ b/src/Passingwind.CSharpScript/NuGetLogger.cs @@ -6,11 +6,11 @@ namespace Passingwind.CSharpScriptEngine; public class NuGetLogger : LoggerBase { - private readonly ILogger<NuGetLogger> _logger; + private readonly Microsoft.Extensions.Logging.ILogger _logger; - public NuGetLogger(ILogger<NuGetLogger> logger) + public NuGetLogger(ILoggerFactory loggerFactory) { - _logger = logger; + _logger = loggerFactory.CreateLogger("NuGet"); } public override void Log(ILogMessage message) diff --git a/src/Passingwind.CSharpScript/NuGetPackageService.cs b/src/Passingwind.CSharpScript/NuGetPackageService.cs deleted file mode 100644 index 3e111ac8..00000000 --- a/src/Passingwind.CSharpScript/NuGetPackageService.cs +++ /dev/null @@ -1,382 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using NuGet.Configuration; -using NuGet.Frameworks; -using NuGet.Packaging; -using NuGet.Packaging.Core; -using NuGet.Protocol; -using NuGet.Protocol.Core.Types; -using NuGet.Repositories; -using NuGet.Versioning; - -namespace Passingwind.CSharpScriptEngine; - -public class NuGetPackageService : INuGetPackageService -{ - private readonly NuGetv3LocalRepository _localRepository; - private readonly NuGet.Common.ILogger _nuGetLogger = NuGet.Common.NullLogger.Instance; - private readonly SourceCacheContext _sourceCacheContext; - private readonly string _nuGetCacheFolder; - - private readonly ILogger<NuGetPackageService> _logger; - private readonly CSharpScriptEngineOptions _engineOptions; - - protected IEnumerable<SourceRepository> Repositories { get; } - - public NuGetPackageService(NuGetLogger nuGetPackageLogger, ILogger<NuGetPackageService> logger, IOptions<CSharpScriptEngineOptions> engineOptions) - { - _logger = logger; - _engineOptions = engineOptions.Value; - _nuGetLogger = nuGetPackageLogger; - - Repositories = GetSourceRepositories(); - _sourceCacheContext = new SourceCacheContext() { IgnoreFailedSources = true, MaxAge = System.DateTimeOffset.Now.AddHours(1), }; - _localRepository = new NuGetv3LocalRepository(_engineOptions.NuGetCachePath); - _nuGetCacheFolder = _engineOptions.NuGetCachePath; - } - - public async Task<IEnumerable<string>> GetReferencesAsync(string packageId, string version, string tagetFrameworkName, bool resolveDependency = false, bool downloadPackage = false, CancellationToken cancellationToken = default) - { - var packageIdentity = new PackageIdentity(packageId, NuGetVersion.Parse(version)); - // project target framework - var targetFramework = NuGetFramework.Parse(tagetFrameworkName); - - _logger.LogDebug("Starting resolve package '{packageId}' references ", packageIdentity); - - var reposities = Repositories; - - await using MemoryStream nupkgStream = await GetNupkgStreamAsync(reposities, packageIdentity, cancellationToken); - - if (nupkgStream.Length == 0) - { - throw new Exception($"The nuget package {packageIdentity} not found."); - } - - var nuGetAllReferences = new List<NuGetReference>(); - - await GetReferencesAsync(nuGetAllReferences, reposities, nupkgStream, packageIdentity, targetFramework, resolveDependency, downloadPackage, cancellationToken); - - // TODO - var result = new List<NuGetReference>(); - foreach (var item in nuGetAllReferences) - { - if (!result.Any(x => x.Identity.Id == item.Identity.Id && x.Identity.Version >= item.Identity.Version)) - { - result.Add(item); - } - } - - _logger.LogDebug("Resolved package '{packageId}' references, count: {count} ", packageIdentity, result.Count); - - return result.SelectMany(x => x.References); - } - - public async Task<IEnumerable<string>> SearchAsync(string name, int resultCount = 10, CancellationToken cancellationToken = default) - { - var tasks = Repositories.Select(x => SearchAsync(x, name, resultCount, cancellationToken)); - - var result = await Task.WhenAll(tasks); - - return result.SelectMany(x => x).Distinct().OrderBy(x => x); - } - - private async Task<IEnumerable<string>> SearchAsync(SourceRepository repository, string name, int resultCount = 10, CancellationToken cancellationToken = default) - { - var resource = await repository.GetResourceAsync<PackageSearchResource>(); - - var result = await resource.SearchAsync(name, new SearchFilter(true, null), 0, resultCount, _nuGetLogger, cancellationToken); - - return result.Select(x => x.Identity.Id).ToList(); - } - - public async Task<IEnumerable<string>> GetPackageVersionsAsync(string packageId, CancellationToken cancellationToken = default) - { - var tasks = Repositories.Select(x => GetPackageVersionsAsync(x, packageId, cancellationToken)); - - var result = await Task.WhenAll(tasks); - - return result.SelectMany(x => x).Distinct().OrderByDescending(x => x); - } - - private async Task<IEnumerable<string>> GetPackageVersionsAsync(SourceRepository repository, string packageId, CancellationToken cancellationToken = default) - { - var resource = await repository.GetResourceAsync<FindPackageByIdResource>(); - - var allVersion = await resource.GetAllVersionsAsync(packageId, _sourceCacheContext, _nuGetLogger, cancellationToken); - - return allVersion.Select(x => x.Version.ToString()); - } - - public async Task DownloadAsync(string packageId, string version, string tagetFrameworkName, bool dependency = false, CancellationToken cancellationToken = default) - { - var packageIdentity = new PackageIdentity(packageId, NuGetVersion.Parse(version)); - var targetFramework = NuGetFramework.Parse(tagetFrameworkName); - - _logger.LogDebug("Starting download and extracte package '{packageId}'", packageIdentity); - - var reposities = Repositories; - - // local cache - var nupkgFile = GetNupkgFileFromCache(packageIdentity); - - if (!string.IsNullOrWhiteSpace(nupkgFile)) - { - _logger.LogDebug("Find package '{packageId}' nupkg from file: {nupkgFile} ", packageIdentity, nupkgFile); - } - // not found. - else - { - _logger.LogDebug("Package '{packageId}' in local cache folder not found, try find from repositories.", packageIdentity); - - var packageExtractionContext = new PackageExtractionContext(PackageSaveMode.Defaultv3, XmlDocFileSaveMode.None, null, _nuGetLogger); - - var tasks = new List<Task>(); - - // download from repositiy - foreach (var repository in reposities) - { - var resource = await repository.GetResourceAsync<FindPackageByIdResource>(); - if (resource == null) - continue; - - tasks.Add(Task.Factory.StartNew(async () => - { - var _packageId = packageIdentity; - var _resource = resource; - - // download & extract - await DownloadAndExtracteAsync(_resource, _packageId, packageExtractionContext, cancellationToken); - })); - } - - Task.WaitAll(tasks.ToArray(), cancellationToken); - } - - // dependency - if (dependency) - { - var dependencyPackages = new List<PackageIdentity>(); - - // resolve - await ResoveDependencyPackagesAsync(dependencyPackages, reposities, packageIdentity, targetFramework, cancellationToken); - - var tasks = new List<Task>(); - foreach (var item in dependencyPackages.Distinct()) - { - tasks.Add(Task.Factory.StartNew(async () => - { - var id = item.Id; - var version = item.Version; - var package2 = new PackageIdentity(id, version); - - // do not resolve dependencies - await DownloadAsync(id, version.ToNormalizedString(), tagetFrameworkName, false, cancellationToken); - })); - } - - Task.WaitAll(tasks.ToArray(), cancellationToken); - } - - _logger.LogDebug("Download and extracte package '{packageId}' done", packageIdentity); - } - - private async Task DownloadAndExtracteAsync(FindPackageByIdResource resource, PackageIdentity packageId, PackageExtractionContext packageExtractionContext, CancellationToken cancellationToken = default) - { - if (await resource.DoesPackageExistAsync(packageId.Id, packageId.Version, _sourceCacheContext, _nuGetLogger, cancellationToken)) - { - var downloader = await resource.GetPackageDownloaderAsync(packageId, _sourceCacheContext, _nuGetLogger, cancellationToken); - - await PackageExtractor.InstallFromSourceAsync(packageId, downloader, new VersionFolderPathResolver(_nuGetCacheFolder), packageExtractionContext, cancellationToken); - - _logger.LogDebug("Package '{packageId}' installed to '{NuGetCacheFolder}' ", packageId, _nuGetCacheFolder); - } - else - { - _logger.LogError("Package '{packageId}' not found", packageId); - } - } - - private async Task ResoveDependencyPackagesAsync(List<PackageIdentity> packages, IEnumerable<SourceRepository> repositories, PackageIdentity packageId, NuGetFramework targetFramework, CancellationToken cancellationToken = default) - { - foreach (var repository in repositories) - { - var resource = repository.GetResource<DependencyInfoResource>(); - - var dependencies = await ResoveDependenciesAsync(packages, packageId, targetFramework, resource, cancellationToken); - - if (dependencies?.Any() == true) - { - foreach (var item in dependencies) - { - if (packages.Any(x => x.Id == item.Id && x.Version == item.VersionRange.MinVersion)) - continue; - - var dp = new PackageIdentity(item.Id, item.VersionRange.MinVersion); - packages.Add(dp); - - await ResoveDependencyPackagesAsync(packages, repositories, dp, targetFramework, cancellationToken); - } - } - } - } - - private async Task<IEnumerable<PackageDependency>?> ResoveDependenciesAsync(List<PackageIdentity> packages, PackageIdentity packageId, NuGetFramework targetFramework, DependencyInfoResource resource, CancellationToken cancellationToken = default) - { - var packageDependencyInfo = await resource.ResolvePackage(packageId, targetFramework, _sourceCacheContext, _nuGetLogger, cancellationToken); - - if (packageDependencyInfo != null) - { - packages.Add(new PackageIdentity(packageDependencyInfo.Id, packageDependencyInfo.Version)); - - return packageDependencyInfo.Dependencies; - } - - return null; - } - - private async Task GetReferencesAsync(List<NuGetReference> result, IEnumerable<SourceRepository> reposities, Stream nupkgStream, PackageIdentity packageId, NuGetFramework targetFramework, bool resolveDependency = false, bool downloadPackage = false, CancellationToken cancellationToken = default) - { - _logger.LogDebug("Starting resolve package '{packageId}' references from nupkg ", packageId); - - using PackageArchiveReader archiveReader = new PackageArchiveReader(nupkgStream); - - var referenceItemGroups = await archiveReader.GetReferenceItemsAsync(cancellationToken); - var supportedFrameworks = await archiveReader.GetSupportedFrameworksAsync(cancellationToken); - var dependencyGroups = await archiveReader.GetPackageDependenciesAsync(cancellationToken); - - // find package target framework - var packageTargetFramework = NuGetFrameworkUtility.GetNearest(supportedFrameworks, targetFramework, x => x); - - var targetReferenceItemGroup = referenceItemGroups.FirstOrDefault(x => x.TargetFramework == packageTargetFramework); - - if (targetReferenceItemGroup == null) - return; - - var referenceFiles = targetReferenceItemGroup.Items?.Select(x => Path.Combine(_nuGetCacheFolder, packageId.Id.ToLowerInvariant(), packageId.Version.ToNormalizedString(), x)).ToList(); - - if (referenceFiles != null) - { - result.Add(new NuGetReference(packageId, referenceFiles.ToImmutableArray())); - - if (referenceFiles.Any(x => !File.Exists(x)) && downloadPackage) - { - await DownloadAsync(packageId.Id, packageId.Version.ToNormalizedString(), targetFramework.GetShortFolderName(), false, cancellationToken); - } - } - - // var packagePathResolver = new PackagePathResolver(Path.GetFullPath("packages")); - - if (resolveDependency && dependencyGroups.Any()) - { - await ResolveDependencyReferenceAsync(result, reposities, dependencyGroups, targetFramework, downloadPackage, cancellationToken); - } - } - - private async Task ResolveDependencyReferenceAsync(List<NuGetReference> result, IEnumerable<SourceRepository> reposities, IEnumerable<PackageDependencyGroup> dependencyGroups, NuGetFramework targetFramework, bool downloadPackage = false, CancellationToken cancellationToken = default) - { - var packageDependencyGroup = NuGetFrameworkUtility.GetNearest(dependencyGroups, targetFramework); - - if (packageDependencyGroup?.Packages?.Any() == true) - { - foreach (var packageDependency in packageDependencyGroup.Packages) - { - // TODO: VersionRange - var packageId = new PackageIdentity(packageDependency.Id, packageDependency.VersionRange.MinVersion); - - var packageStream = await GetNupkgStreamAsync(reposities, packageId, cancellationToken); - - if (packageStream.Length == 0) - { - throw new Exception($"The nuget package {packageId} not found."); - } - - await GetReferencesAsync(result, reposities, packageStream, packageId, targetFramework, true, downloadPackage, cancellationToken); - } - } - } - - private async Task<MemoryStream> GetNupkgStreamAsync(IEnumerable<SourceRepository> reposities, PackageIdentity packageId, CancellationToken cancellationToken = default) - { - _logger.LogDebug("Starting download package '{packageId}' nupkg ", packageId); - - MemoryStream nupkgStream = new MemoryStream(); - - // local cache - var nupkgFile = GetNupkgFileFromCache(packageId); - if (!string.IsNullOrWhiteSpace(nupkgFile)) - { - var fileBytes = File.ReadAllBytes(nupkgFile); - nupkgStream = new MemoryStream(fileBytes); - - _logger.LogDebug("Find package '{packageId}' nupkg from file: {nupkgFile} ", packageId, nupkgFile); - } - // from reposity - else - { - foreach (var repository in reposities) - { - var resource = await repository.GetResourceAsync<FindPackageByIdResource>(); - if (resource == null) - continue; - - if (await resource.CopyNupkgToStreamAsync(packageId.Id, packageId.Version, nupkgStream, _sourceCacheContext, _nuGetLogger, cancellationToken)) - { - _logger.LogDebug("Download package '{packageId}' nupkg from '{repository}' ", packageId, repository); - - break; - } - } - } - - return nupkgStream; - } - - private string? GetNupkgFileFromCache(PackageIdentity packageId) - { - var package = _localRepository.FindPackage(packageId.Id, packageId.Version); - - return package?.ZipPath; - } - - //protected SourceRepository GetSourceRepository() - //{ - // var settings = Settings.LoadDefaultSettings(Directory.GetCurrentDirectory()); - - // var packageSourceProvider = new PackageSourceProvider(settings); - - // var packageSource = packageSourceProvider.LoadPackageSources().First(); - - // return Repository.Factory.GetCoreV3(packageSource); - //} - - protected IEnumerable<SourceRepository> GetSourceRepositories() - { - var settings = Settings.LoadDefaultSettings(Directory.GetCurrentDirectory()); - - var packageSourceProvider = new PackageSourceProvider(settings); - - var sourceRepositoryProvider = new SourceRepositoryProvider(packageSourceProvider, Repository.Provider.GetCoreV3()); - - var originSources = sourceRepositoryProvider.GetRepositories(); - - var result = new List<SourceRepository>(); - result.AddRange(originSources); - - if (_engineOptions.NuGetServer.Any()) - { - foreach (var item in _engineOptions.NuGetServer) - { - result.Add(Repository.Factory.GetCoreV3(item)); - } - } - - return result; - } -} diff --git a/src/Passingwind.CSharpScript/References/NuGetReferenceResolver.cs b/src/Passingwind.CSharpScript/References/NuGetReferenceResolver.cs index d7d291b8..3b752941 100644 --- a/src/Passingwind.CSharpScript/References/NuGetReferenceResolver.cs +++ b/src/Passingwind.CSharpScript/References/NuGetReferenceResolver.cs @@ -23,16 +23,14 @@ public async Task<ImmutableArray<MetadataReference>> GetReferencesAsync(NuGetDir throw new ArgumentNullException(nameof(reference)); } - // TODO var files = await _nuGetPackageService.GetReferencesAsync( packageId: reference.PackageId, version: reference.Version, - tagetFrameworkName: CSharpEnvironment.GetCurrentShortTargetFramework(), + targetFramework: AppContext.TargetFrameworkName!, resolveDependency: true, - downloadPackage: false, cancellationToken: cancellationToken); - if (files?.Any() == true) + if (files.Any()) { return files.Select(x => MetadataReference.CreateFromFile(x)).ToImmutableArray<MetadataReference>(); } diff --git a/src/Passingwind.CSharpScript/ServiceCollectionExtensions.cs b/src/Passingwind.CSharpScript/ServiceCollectionExtensions.cs index b47f2745..3b658516 100644 --- a/src/Passingwind.CSharpScript/ServiceCollectionExtensions.cs +++ b/src/Passingwind.CSharpScript/ServiceCollectionExtensions.cs @@ -19,8 +19,10 @@ public static IServiceCollection AddCSharpScriptEngine(this IServiceCollection s services.TryAddSingleton<ICSharpScriptWorkspace, CSharpScriptWorkspace>(); + services.TryAddSingleton<NuGet.Common.ILogger, NuGetLogger>(); services.TryAddSingleton<NuGetLogger>(); - services.TryAddSingleton<INuGetPackageService, NuGetPackageService>(); + services.TryAddSingleton<INuGetPackageService, DefaultNuGetPackageService>(); + services.TryAddSingleton<INuGetLocalPackageAssemblyResolver, DefaultNuGetLocalPackageAssemblyResolver>(); return services; }