diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 9860f54..852656f 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -1,10 +1,10 @@ -{ - "version": 1, - "isRoot": true, - "tools": { - "CSharpier": { - "version": "1.1.2", - "commands": ["csharpier"] - } - } -} \ No newline at end of file +{ + "version": 1, + "isRoot": true, + "tools": { + "CSharpier": { + "version": "1.1.2", + "commands": ["csharpier"] + } + } +} diff --git a/.csharpierignore b/.csharpierignore new file mode 100644 index 0000000..54ac238 --- /dev/null +++ b/.csharpierignore @@ -0,0 +1 @@ +Runtime/Binaries/*.xml diff --git a/.csharpierrc.json b/.csharpierrc.json new file mode 100644 index 0000000..a024fc9 --- /dev/null +++ b/.csharpierrc.json @@ -0,0 +1,3 @@ +{ + "endOfLine": "crlf" +} diff --git a/.editorconfig b/.editorconfig index d88bef4..6b8819e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,184 +1,184 @@ - -[*] -charset = utf-8-bom -end_of_line = crlf -trim_trailing_whitespace = false -insert_final_newline = false -indent_style = space -indent_size = 4 - -# 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_prefer_braces = true:suggestion -csharp_style_prefer_utf8_string_literals = true:suggestion -csharp_style_var_elsewhere = false:suggestion -csharp_style_var_for_built_in_types = false:suggestion -csharp_style_var_when_type_is_apparent = false:suggestion -csharp_using_directive_placement = inside_namespace:silent -dotnet_naming_rule.event_rule.import_to_resharper = True -dotnet_naming_rule.event_rule.resharper_description = Events -dotnet_naming_rule.event_rule.resharper_guid = 0c4c6401-2a1f-4db1-a21f-562f51542cf8 -dotnet_naming_rule.event_rule.severity = warning -dotnet_naming_rule.event_rule.style = on_upper_camel_case_style -dotnet_naming_rule.event_rule.symbols = event_symbols -dotnet_naming_rule.interfaces_rule.import_to_resharper = True -dotnet_naming_rule.interfaces_rule.resharper_description = Interfaces -dotnet_naming_rule.interfaces_rule.resharper_guid = a7a3339e-4e89-4319-9735-a9dc4cb74cc7 -dotnet_naming_rule.interfaces_rule.severity = warning -dotnet_naming_rule.interfaces_rule.style = i_upper_camel_case_style -dotnet_naming_rule.interfaces_rule.symbols = interfaces_symbols -dotnet_naming_rule.local_constants_rule.import_to_resharper = True -dotnet_naming_rule.local_constants_rule.resharper_description = Local constants -dotnet_naming_rule.local_constants_rule.resharper_guid = a4f433b8-abcd-4e55-a08f-82e78cef0f0c -dotnet_naming_rule.local_constants_rule.resharper_style = AaBb, aaBb -dotnet_naming_rule.local_constants_rule.severity = warning -dotnet_naming_rule.local_constants_rule.style = upper_camel_case_style -dotnet_naming_rule.local_constants_rule.symbols = local_constants_symbols -dotnet_naming_rule.public_fields_override_rule.import_to_resharper = False -dotnet_naming_rule.public_fields_override_rule.severity = warning -dotnet_naming_rule.public_fields_override_rule.style = upper_camel_case_style -dotnet_naming_rule.public_fields_override_rule.symbols = public_fields_override_symbols -dotnet_naming_rule.public_fields_override_rule_1.import_to_resharper = False -dotnet_naming_rule.public_fields_override_rule_1.severity = warning -dotnet_naming_rule.public_fields_override_rule_1.style = upper_camel_case_style -dotnet_naming_rule.public_fields_override_rule_1.symbols = public_fields_override_symbols_1 -dotnet_naming_rule.public_fields_rule.import_to_resharper = True -dotnet_naming_rule.public_fields_rule.resharper_description = Instance fields (not private) -dotnet_naming_rule.public_fields_rule.resharper_guid = 53eecf85-d821-40e8-ac97-fdb734542b84 -dotnet_naming_rule.public_fields_rule.severity = warning -dotnet_naming_rule.public_fields_rule.style = lower_camel_case_style -dotnet_naming_rule.public_fields_rule.symbols = public_fields_symbols -dotnet_naming_rule.public_static_fields_rule.import_to_resharper = True -dotnet_naming_rule.public_static_fields_rule.resharper_description = Static fields (not private) -dotnet_naming_rule.public_static_fields_rule.resharper_guid = 70345118-4b40-4ece-937c-bbeb7a0b2e70 -dotnet_naming_rule.public_static_fields_rule.severity = warning -dotnet_naming_rule.public_static_fields_rule.style = upper_camel_case_style -dotnet_naming_rule.public_static_fields_rule.symbols = public_static_fields_symbols -dotnet_naming_rule.type_parameters_rule.import_to_resharper = True -dotnet_naming_rule.type_parameters_rule.resharper_description = Type parameters -dotnet_naming_rule.type_parameters_rule.resharper_guid = 2c62818f-621b-4425-adc9-78611099bfcb -dotnet_naming_rule.type_parameters_rule.severity = warning -dotnet_naming_rule.type_parameters_rule.style = t_upper_camel_case_style -dotnet_naming_rule.type_parameters_rule.symbols = type_parameters_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 = none -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.i_upper_camel_case_style.capitalization = pascal_case -dotnet_naming_style.i_upper_camel_case_style.required_prefix = I -dotnet_naming_style.lower_camel_case_style.capitalization = camel_case -dotnet_naming_style.on_upper_camel_case_style.capitalization = pascal_case -dotnet_naming_style.on_upper_camel_case_style.required_prefix = On -dotnet_naming_style.t_upper_camel_case_style.capitalization = pascal_case -dotnet_naming_style.t_upper_camel_case_style.required_prefix = T -dotnet_naming_style.upper_camel_case_style.capitalization = pascal_case -dotnet_naming_symbols.event_symbols.applicable_accessibilities = * -dotnet_naming_symbols.event_symbols.applicable_kinds = event -dotnet_naming_symbols.event_symbols.resharper_applicable_kinds = event -dotnet_naming_symbols.event_symbols.resharper_required_modifiers = any -dotnet_naming_symbols.interfaces_symbols.applicable_accessibilities = * -dotnet_naming_symbols.interfaces_symbols.applicable_kinds = interface -dotnet_naming_symbols.interfaces_symbols.resharper_applicable_kinds = interface -dotnet_naming_symbols.interfaces_symbols.resharper_required_modifiers = any -dotnet_naming_symbols.local_constants_symbols.applicable_accessibilities = * -dotnet_naming_symbols.local_constants_symbols.applicable_kinds = local -dotnet_naming_symbols.local_constants_symbols.required_modifiers = const -dotnet_naming_symbols.local_constants_symbols.resharper_applicable_kinds = local_constant -dotnet_naming_symbols.local_constants_symbols.resharper_required_modifiers = any -dotnet_naming_symbols.public_fields_override_symbols.applicable_accessibilities = public -dotnet_naming_symbols.public_fields_override_symbols.applicable_kinds = field -dotnet_naming_symbols.public_fields_override_symbols.required_modifiers = const -dotnet_naming_symbols.public_fields_override_symbols_1.applicable_accessibilities = public -dotnet_naming_symbols.public_fields_override_symbols_1.applicable_kinds = field -dotnet_naming_symbols.public_fields_override_symbols_1.required_modifiers = readonly,static -dotnet_naming_symbols.public_fields_symbols.applicable_accessibilities = public -dotnet_naming_symbols.public_fields_symbols.applicable_kinds = field -dotnet_naming_symbols.public_fields_symbols.resharper_applicable_kinds = field,readonly_field -dotnet_naming_symbols.public_fields_symbols.resharper_required_modifiers = instance -dotnet_naming_symbols.public_static_fields_symbols.applicable_accessibilities = public -dotnet_naming_symbols.public_static_fields_symbols.applicable_kinds = field -dotnet_naming_symbols.public_static_fields_symbols.required_modifiers = static -dotnet_naming_symbols.public_static_fields_symbols.resharper_applicable_kinds = field -dotnet_naming_symbols.public_static_fields_symbols.resharper_required_modifiers = static -dotnet_naming_symbols.type_parameters_symbols.applicable_accessibilities = * -dotnet_naming_symbols.type_parameters_symbols.applicable_kinds = type_parameter -dotnet_naming_symbols.type_parameters_symbols.resharper_applicable_kinds = type_parameter -dotnet_naming_symbols.type_parameters_symbols.resharper_required_modifiers = any -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_redundant = true -resharper_csharp_wrap_lines = false -resharper_formatter_off_tag = @formatter:off -resharper_formatter_on_tag = @formatter:on -resharper_formatter_tags_enabled = true -resharper_indent_preprocessor_directives = normal -resharper_keep_existing_attribute_arrangement = true -resharper_place_attribute_on_same_line = false -resharper_show_autodetect_configure_formatting_tip = false -resharper_use_indent_from_vs = false - -# ReSharper inspection severities -resharper_arrange_redundant_parentheses_highlighting = hint -resharper_arrange_this_qualifier_highlighting = hint -resharper_arrange_trailing_comma_in_multiline_lists_highlighting = none -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_mvc_action_not_resolved_highlighting = warning -resharper_mvc_area_not_resolved_highlighting = warning -resharper_mvc_controller_not_resolved_highlighting = warning -resharper_mvc_masterpage_not_resolved_highlighting = warning -resharper_mvc_partial_view_not_resolved_highlighting = warning -resharper_mvc_template_not_resolved_highlighting = warning -resharper_mvc_view_component_not_resolved_highlighting = warning -resharper_mvc_view_component_view_not_resolved_highlighting = warning -resharper_mvc_view_not_resolved_highlighting = warning -resharper_prefer_concrete_value_over_default_highlighting = none -resharper_razor_assembly_not_resolved_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 - -[{*.har,*.inputactions,*.jsb2,*.jsb3,*.json,*.jsonc,*.postman_collection,*.postman_collection.json,*.postman_environment,*.postman_environment.json,.babelrc,.eslintrc,.prettierrc,.stylelintrc,.ws-context,jest.config}] -indent_style = space -indent_size = 2 - -[{*.yaml,*.yml}] -indent_style = space -indent_size = 2 - -[*.asmdef] -indent_style = space -indent_size = 2 - -[*.asmref] -indent_style = space -indent_size = 2 - -[*.{appxmanifest,asax,ascx,aspx,axaml,blockshader,build,c,c++,c++m,cc,ccm,cginc,compute,cp,cpp,cppm,cs,cshtml,cu,cuh,cxx,cxxm,dtd,fs,fsi,fsscript,fsx,fx,fxh,h,h++,hh,hlsl,hlsli,hlslinc,hp,hpp,hxx,icc,inc,inl,ino,ipp,ixx,master,ml,mli,mpp,mq4,mq5,mqh,mxx,nuspec,paml,razor,resw,resx,shader,shaderFoundry,skin,tcc,tpp,urtshader,usf,ush,uxml,vb,xaml,xamlx,xoml,xsd}] -indent_style = space -indent_size = 4 -tab_width = 4 + +[*] +charset = utf-8 +end_of_line = crlf +trim_trailing_whitespace = false +insert_final_newline = false +indent_style = space +indent_size = 4 + +# 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_prefer_braces = true:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_var_elsewhere = false:suggestion +csharp_style_var_for_built_in_types = false:suggestion +csharp_style_var_when_type_is_apparent = false:suggestion +csharp_using_directive_placement = inside_namespace:silent +dotnet_naming_rule.event_rule.import_to_resharper = True +dotnet_naming_rule.event_rule.resharper_description = Events +dotnet_naming_rule.event_rule.resharper_guid = 0c4c6401-2a1f-4db1-a21f-562f51542cf8 +dotnet_naming_rule.event_rule.severity = warning +dotnet_naming_rule.event_rule.style = on_upper_camel_case_style +dotnet_naming_rule.event_rule.symbols = event_symbols +dotnet_naming_rule.interfaces_rule.import_to_resharper = True +dotnet_naming_rule.interfaces_rule.resharper_description = Interfaces +dotnet_naming_rule.interfaces_rule.resharper_guid = a7a3339e-4e89-4319-9735-a9dc4cb74cc7 +dotnet_naming_rule.interfaces_rule.severity = warning +dotnet_naming_rule.interfaces_rule.style = i_upper_camel_case_style +dotnet_naming_rule.interfaces_rule.symbols = interfaces_symbols +dotnet_naming_rule.local_constants_rule.import_to_resharper = True +dotnet_naming_rule.local_constants_rule.resharper_description = Local constants +dotnet_naming_rule.local_constants_rule.resharper_guid = a4f433b8-abcd-4e55-a08f-82e78cef0f0c +dotnet_naming_rule.local_constants_rule.resharper_style = AaBb, aaBb +dotnet_naming_rule.local_constants_rule.severity = warning +dotnet_naming_rule.local_constants_rule.style = upper_camel_case_style +dotnet_naming_rule.local_constants_rule.symbols = local_constants_symbols +dotnet_naming_rule.public_fields_override_rule.import_to_resharper = False +dotnet_naming_rule.public_fields_override_rule.severity = warning +dotnet_naming_rule.public_fields_override_rule.style = upper_camel_case_style +dotnet_naming_rule.public_fields_override_rule.symbols = public_fields_override_symbols +dotnet_naming_rule.public_fields_override_rule_1.import_to_resharper = False +dotnet_naming_rule.public_fields_override_rule_1.severity = warning +dotnet_naming_rule.public_fields_override_rule_1.style = upper_camel_case_style +dotnet_naming_rule.public_fields_override_rule_1.symbols = public_fields_override_symbols_1 +dotnet_naming_rule.public_fields_rule.import_to_resharper = True +dotnet_naming_rule.public_fields_rule.resharper_description = Instance fields (not private) +dotnet_naming_rule.public_fields_rule.resharper_guid = 53eecf85-d821-40e8-ac97-fdb734542b84 +dotnet_naming_rule.public_fields_rule.severity = warning +dotnet_naming_rule.public_fields_rule.style = lower_camel_case_style +dotnet_naming_rule.public_fields_rule.symbols = public_fields_symbols +dotnet_naming_rule.public_static_fields_rule.import_to_resharper = True +dotnet_naming_rule.public_static_fields_rule.resharper_description = Static fields (not private) +dotnet_naming_rule.public_static_fields_rule.resharper_guid = 70345118-4b40-4ece-937c-bbeb7a0b2e70 +dotnet_naming_rule.public_static_fields_rule.severity = warning +dotnet_naming_rule.public_static_fields_rule.style = upper_camel_case_style +dotnet_naming_rule.public_static_fields_rule.symbols = public_static_fields_symbols +dotnet_naming_rule.type_parameters_rule.import_to_resharper = True +dotnet_naming_rule.type_parameters_rule.resharper_description = Type parameters +dotnet_naming_rule.type_parameters_rule.resharper_guid = 2c62818f-621b-4425-adc9-78611099bfcb +dotnet_naming_rule.type_parameters_rule.severity = warning +dotnet_naming_rule.type_parameters_rule.style = t_upper_camel_case_style +dotnet_naming_rule.type_parameters_rule.symbols = type_parameters_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 = none +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.i_upper_camel_case_style.capitalization = pascal_case +dotnet_naming_style.i_upper_camel_case_style.required_prefix = I +dotnet_naming_style.lower_camel_case_style.capitalization = camel_case +dotnet_naming_style.on_upper_camel_case_style.capitalization = pascal_case +dotnet_naming_style.on_upper_camel_case_style.required_prefix = On +dotnet_naming_style.t_upper_camel_case_style.capitalization = pascal_case +dotnet_naming_style.t_upper_camel_case_style.required_prefix = T +dotnet_naming_style.upper_camel_case_style.capitalization = pascal_case +dotnet_naming_symbols.event_symbols.applicable_accessibilities = * +dotnet_naming_symbols.event_symbols.applicable_kinds = event +dotnet_naming_symbols.event_symbols.resharper_applicable_kinds = event +dotnet_naming_symbols.event_symbols.resharper_required_modifiers = any +dotnet_naming_symbols.interfaces_symbols.applicable_accessibilities = * +dotnet_naming_symbols.interfaces_symbols.applicable_kinds = interface +dotnet_naming_symbols.interfaces_symbols.resharper_applicable_kinds = interface +dotnet_naming_symbols.interfaces_symbols.resharper_required_modifiers = any +dotnet_naming_symbols.local_constants_symbols.applicable_accessibilities = * +dotnet_naming_symbols.local_constants_symbols.applicable_kinds = local +dotnet_naming_symbols.local_constants_symbols.required_modifiers = const +dotnet_naming_symbols.local_constants_symbols.resharper_applicable_kinds = local_constant +dotnet_naming_symbols.local_constants_symbols.resharper_required_modifiers = any +dotnet_naming_symbols.public_fields_override_symbols.applicable_accessibilities = public +dotnet_naming_symbols.public_fields_override_symbols.applicable_kinds = field +dotnet_naming_symbols.public_fields_override_symbols.required_modifiers = const +dotnet_naming_symbols.public_fields_override_symbols_1.applicable_accessibilities = public +dotnet_naming_symbols.public_fields_override_symbols_1.applicable_kinds = field +dotnet_naming_symbols.public_fields_override_symbols_1.required_modifiers = readonly,static +dotnet_naming_symbols.public_fields_symbols.applicable_accessibilities = public +dotnet_naming_symbols.public_fields_symbols.applicable_kinds = field +dotnet_naming_symbols.public_fields_symbols.resharper_applicable_kinds = field,readonly_field +dotnet_naming_symbols.public_fields_symbols.resharper_required_modifiers = instance +dotnet_naming_symbols.public_static_fields_symbols.applicable_accessibilities = public +dotnet_naming_symbols.public_static_fields_symbols.applicable_kinds = field +dotnet_naming_symbols.public_static_fields_symbols.required_modifiers = static +dotnet_naming_symbols.public_static_fields_symbols.resharper_applicable_kinds = field +dotnet_naming_symbols.public_static_fields_symbols.resharper_required_modifiers = static +dotnet_naming_symbols.type_parameters_symbols.applicable_accessibilities = * +dotnet_naming_symbols.type_parameters_symbols.applicable_kinds = type_parameter +dotnet_naming_symbols.type_parameters_symbols.resharper_applicable_kinds = type_parameter +dotnet_naming_symbols.type_parameters_symbols.resharper_required_modifiers = any +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_redundant = true +resharper_csharp_wrap_lines = false +resharper_formatter_off_tag = @formatter:off +resharper_formatter_on_tag = @formatter:on +resharper_formatter_tags_enabled = true +resharper_indent_preprocessor_directives = normal +resharper_keep_existing_attribute_arrangement = true +resharper_place_attribute_on_same_line = false +resharper_show_autodetect_configure_formatting_tip = false +resharper_use_indent_from_vs = false + +# ReSharper inspection severities +resharper_arrange_redundant_parentheses_highlighting = hint +resharper_arrange_this_qualifier_highlighting = hint +resharper_arrange_trailing_comma_in_multiline_lists_highlighting = none +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_mvc_action_not_resolved_highlighting = warning +resharper_mvc_area_not_resolved_highlighting = warning +resharper_mvc_controller_not_resolved_highlighting = warning +resharper_mvc_masterpage_not_resolved_highlighting = warning +resharper_mvc_partial_view_not_resolved_highlighting = warning +resharper_mvc_template_not_resolved_highlighting = warning +resharper_mvc_view_component_not_resolved_highlighting = warning +resharper_mvc_view_component_view_not_resolved_highlighting = warning +resharper_mvc_view_not_resolved_highlighting = warning +resharper_prefer_concrete_value_over_default_highlighting = none +resharper_razor_assembly_not_resolved_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 + +[{*.har,*.inputactions,*.jsb2,*.jsb3,*.json,*.jsonc,*.postman_collection,*.postman_collection.json,*.postman_environment,*.postman_environment.json,.babelrc,.eslintrc,.prettierrc,.stylelintrc,.ws-context,jest.config}] +indent_style = space +indent_size = 2 + +[{*.yaml,*.yml}] +indent_style = space +indent_size = 2 + +[*.asmdef] +indent_style = space +indent_size = 2 + +[*.asmref] +indent_style = space +indent_size = 2 + +[*.{appxmanifest,asax,ascx,aspx,axaml,blockshader,build,c,c++,c++m,cc,ccm,cginc,compute,cp,cpp,cppm,cs,cshtml,cu,cuh,cxx,cxxm,dtd,fs,fsi,fsscript,fsx,fx,fxh,h,h++,hh,hlsl,hlsli,hlslinc,hp,hpp,hxx,icc,inc,inl,ino,ipp,ixx,master,ml,mli,mpp,mq4,mq5,mqh,mxx,nuspec,paml,razor,resw,resx,shader,shaderFoundry,skin,tcc,tpp,urtshader,usf,ush,uxml,vb,xaml,xamlx,xoml,xsd}] +indent_style = space +indent_size = 4 +tab_width = 4 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2e3ef54 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,42 @@ +# Normalize text and enforce CRLF in working tree for key files +* text=auto + +# Enforce CRLF for source and docs (matches Prettier/CSharpier + CI checks) +*.cs text eol=crlf +*.csproj text eol=crlf +*.sln text eol=crlf +*.props text eol=crlf +*.targets text eol=crlf + +*.md text eol=crlf +*.markdown text eol=crlf + +*.json text eol=crlf +*.asmdef text eol=crlf +*.asmref text eol=crlf + +*.yml text eol=crlf +*.yaml text eol=crlf + +# Common config treated as text with CRLF +.editorconfig text eol=crlf +.prettierrc text eol=crlf +.prettierrc.json text eol=crlf +.markdownlint.json text eol=crlf +.yamllint.yaml text eol=crlf + +# Unity / assets and binaries (do not modify EOL) +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.mp4 binary +*.otf binary +*.ttf binary +*.woff binary +*.woff2 binary +*.unity binary +*.prefab binary +*.mat binary +*.asset binary +*.dll binary diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 61664ba..e808065 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,10 +1,32 @@ version: 2 updates: + # GitHub Actions workflow updates - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "weekly" + interval: "daily" assignees: - wallstop reviewers: - - wallstop \ No newline at end of file + - wallstop + + # NuGet: .csproj/props/targets and .config/dotnet-tools.json (local tools) + - package-ecosystem: "nuget" + directory: "/" + schedule: + interval: "daily" + assignees: + - wallstop + reviewers: + - wallstop + + # npm/UPM: package.json at repo root (Unity package manifest) + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "daily" + versioning-strategy: increase + assignees: + - wallstop + reviewers: + - wallstop diff --git a/.github/scripts/check-markdown-links.ps1 b/.github/scripts/check-markdown-links.ps1 new file mode 100644 index 0000000..b12cf76 --- /dev/null +++ b/.github/scripts/check-markdown-links.ps1 @@ -0,0 +1,77 @@ +Param( + [string]$Root = "." +) + +$ErrorActionPreference = 'Stop' + +function Normalize-Name { + param([string]$s) + if ([string]::IsNullOrWhiteSpace($s)) { return "" } + # Remove extension (like .md), collapse non-alphanumerics, lowercase + $noExt = $s -replace '\.[^\.]+$','' + $normalized = ($noExt -replace '[^A-Za-z0-9]', '') + return $normalized.ToLowerInvariant() +} + +$issueCount = 0 + +# Exclude typical directories that shouldn't be scanned +$excludeDirs = @('.git', 'node_modules', '.vs') + +$mdFiles = Get-ChildItem -Path $Root -Recurse -File -Filter *.md | + Where-Object { $excludeDirs -notcontains $_.Directory.Name } + +# Regex for inline markdown links (exclude images), capture optional title +$pattern = '(?[^\]]+)\]\((?[^)\s]+)(?:\s+"[^"]*")?\)' + +foreach ($file in $mdFiles) { + $lines = Get-Content -LiteralPath $file.FullName -Encoding UTF8 + for ($i = 0; $i -lt $lines.Count; $i++) { + $line = $lines[$i] + $matches = [System.Text.RegularExpressions.Regex]::Matches($line, $pattern) + foreach ($m in $matches) { + $text = $m.Groups['text'].Value.Trim() + $targetRaw = $m.Groups['target'].Value.Trim() + + # Skip anchors, external links, and mailto + if ($targetRaw -match '^(#|https?://|mailto:|tel:|data:)') { continue } + + # Remove query/anchor for file checks + $targetCore = $targetRaw -replace '[?#].*$','' + + # Decode URL-encoded chars + try { $targetCore = [uri]::UnescapeDataString($targetCore) } catch { } + + # Only care about links to markdown files + if (-not ($targetCore -match '\.md$')) { continue } + + $fileName = [System.IO.Path]::GetFileName($targetCore) + $baseName = [System.IO.Path]::GetFileNameWithoutExtension($targetCore) + + # Fail when the visible link text is the raw file name + $isExactFileName = $text.Equals($fileName, [System.StringComparison]::OrdinalIgnoreCase) + + # Also fail when the visible text looks like a path or ends with .md + # contains path separators and no whitespace (heuristic for raw paths) + $looksLikePath = ($text -match '[\\/]' -and -not ($text -match '\\s')) + $looksLikeMarkdownFileName = $text.Trim().ToLowerInvariant().EndsWith('.md') + + if ($isExactFileName -or $looksLikePath -or $looksLikeMarkdownFileName) { + $issueCount++ + $lineNo = $i + 1 + $msg = "Link text '$text' should be human-readable, not a raw file name or path" + # GitHub Actions annotation + Write-Output "::error file=$($file.FullName),line=$lineNo::$msg (target: $targetRaw)" + } + } + } +} + +if ($issueCount -gt 0) { + Write-Host "Found $issueCount documentation link(s) with non-human-readable text." -ForegroundColor Red + Write-Host "Use a descriptive phrase instead of the raw file name." + exit 1 +} +else { + Write-Host "All markdown links have human-readable text." +} diff --git a/.github/scripts/check_markdown_links.py b/.github/scripts/check_markdown_links.py new file mode 100644 index 0000000..4fe61dd --- /dev/null +++ b/.github/scripts/check_markdown_links.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +import os +import re +import sys +import urllib.parse + + +EXCLUDE_DIRS = {".git", "node_modules", ".vs"} + + +def normalize_name(s: str) -> str: + if not s: + return "" + # remove extension, strip non-alphanumerics, lowercase + base = re.sub(r"\.[^.]+$", "", s) + return re.sub(r"[^A-Za-z0-9]", "", base).lower() + + +LINK_RE = re.compile(r"(?[^\]]+)\]\((?P[^)\s]+)(?:\s+\"[^\"]*\")?\)") + + +def should_check_target(target: str) -> bool: + if re.match(r"^(#|https?://|mailto:|tel:|data:)", target): + return False + # only check links that end in .md (ignoring anchors/query) + core = re.sub(r"[?#].*$", "", target) + try: + core = urllib.parse.unquote(core) + except Exception: + pass + return core.lower().endswith(".md") + + +def main(root: str) -> int: + issues = 0 + for dirpath, dirnames, filenames in os.walk(root): + # prune excluded directories + dirnames[:] = [d for d in dirnames if d not in EXCLUDE_DIRS] + for filename in filenames: + if not filename.lower().endswith(".md"): + continue + path = os.path.join(dirpath, filename) + try: + with open(path, "r", encoding="utf-8") as f: + lines = f.readlines() + except Exception: + continue + for idx, line in enumerate(lines, start=1): + for m in LINK_RE.finditer(line): + text = m.group("text").strip() + target_raw = m.group("target").strip() + if not should_check_target(target_raw): + continue + target_core = re.sub(r"[?#].*$", "", target_raw) + try: + target_core = urllib.parse.unquote(target_core) + except Exception: + pass + file_name = os.path.basename(target_core) + base_name, _ = os.path.splitext(file_name) + + is_exact_file_name = text.lower() == file_name.lower() + looks_like_path = (("/" in text) or ("\\" in text)) and not re.search(r"\s", text) + looks_like_markdown = text.strip().lower().endswith(".md") + + if ( + is_exact_file_name + or looks_like_path + or looks_like_markdown + ): + issues += 1 + msg = f"{path}:{idx}: Link text '{text}' should be human-readable, not a raw file name or path (target: {target_raw})" + print(msg) + + if issues: + print( + f"Found {issues} documentation link(s) with non-human-readable text.", + file=sys.stderr, + ) + print( + "Use a descriptive phrase instead of the raw file name.", file=sys.stderr + ) + return 1 + return 0 + + +if __name__ == "__main__": + root = sys.argv[1] if len(sys.argv) > 1 else "." + sys.exit(main(root)) diff --git a/.github/scripts/check_markdown_url_encoding.py b/.github/scripts/check_markdown_url_encoding.py new file mode 100644 index 0000000..a44da3e --- /dev/null +++ b/.github/scripts/check_markdown_url_encoding.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +import os +import re +import sys + + +EXCLUDE_DIRS = {".git", "node_modules", ".vs", ".vscode", "Library", "Temp"} + + +# Inline markdown link or image: ![alt](target "title") or [text](target "title") +INLINE_LINK_RE = re.compile( + r"!?(?P\[(?P[^\]]+)\]\((?P[^)\s]+)(?:\s+\"[^\"]*\")?\))" +) + +# Reference-style link definitions: [id]: target "title" +REF_DEF_RE = re.compile(r"^\s*\[[^\]]+\]:\s*(?P\S+)(?:\s+\"[^\"]*\")?\s*$") + + +def is_external(target: str) -> bool: + return target.startswith("http://") or target.startswith("https://") or target.startswith("mailto:") or target.startswith("tel:") or target.startswith("data:") + + +def has_unencoded_chars(target: str) -> bool: + # Only flag raw spaces or plus signs in the path/query/fragment + return (" " in target) or ("+" in target) + + +def scan_file(path: str) -> int: + issues = 0 + try: + with open(path, "r", encoding="utf-8") as f: + lines = f.readlines() + except Exception: + return 0 + + for idx, line in enumerate(lines, start=1): + # Inline links/images + for m in INLINE_LINK_RE.finditer(line): + target = m.group("target").strip() + if is_external(target): + continue + if has_unencoded_chars(target): + issues += 1 + print(f"{path}:{idx}: Unencoded character(s) in link target: '{target}'. Encode spaces as %20 and '+' as %2B.") + + # Reference-style link definitions + m = REF_DEF_RE.match(line) + if m: + target = m.group("target").strip() + if not is_external(target) and has_unencoded_chars(target): + issues += 1 + print(f"{path}:{idx}: Unencoded character(s) in link definition: '{target}'. Encode spaces as %20 and '+' as %2B.") + + return issues + + +def main(root: str) -> int: + issues = 0 + for dirpath, dirnames, filenames in os.walk(root): + dirnames[:] = [d for d in dirnames if d not in EXCLUDE_DIRS] + for filename in filenames: + if filename.lower().endswith(".md"): + issues += scan_file(os.path.join(dirpath, filename)) + if issues: + print(f"Found {issues} markdown link(s) with unencoded spaces or plus signs.", file=sys.stderr) + print("Please URL-encode spaces as %20 and '+' as %2B in relative links.", file=sys.stderr) + return 1 + return 0 + + +if __name__ == "__main__": + root = sys.argv[1] if len(sys.argv) > 1 else "." + sys.exit(main(root)) + diff --git a/.github/scripts/validate_markdown_links.py b/.github/scripts/validate_markdown_links.py new file mode 100644 index 0000000..31498e3 --- /dev/null +++ b/.github/scripts/validate_markdown_links.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python3 +import os +import re +import sys +import urllib.parse +from typing import Dict, List, Set, Tuple + + +EXCLUDE_DIRS = {".git", "node_modules", ".vs"} + + +LINK_RE = re.compile(r"(?[^\]]+)\]\((?P[^)\s]+)(?:\s+\"[^\"]*\")?\)") + + +def unescape_uri(s: str) -> str: + try: + return urllib.parse.unquote(s) + except Exception: + return s + + +def normalize_heading_to_id(text: str) -> str: + """ + Approximate GitHub-style anchor slug generation (GFM): + - Lowercase + - Strip Markdown formatting and code ticks + - Remove most punctuation + - Replace whitespace with hyphens + - Collapse multiple hyphens + - Trim leading/trailing hyphens + """ + if not text: + return "" + t = text + # Remove inline code ticks + t = t.replace("`", "") + # Remove Markdown emphasis markers + t = t.replace("*", "").replace("_", "").replace("~", "") + # Remove link/image markup inside headings e.g. [text](url) + t = re.sub(r"!?\[[^\]]*\]\([^)]*\)", "", t) + # Remove HTML tags + t = re.sub(r"<[^>]+>", "", t) + # Lowercase + t = t.lower() + # Replace whitespace with hyphens + t = re.sub(r"\s+", "-", t) + # Remove punctuation except hyphens and alphanumerics + t = re.sub(r"[^a-z0-9-]", "", t) + # Collapse multiple hyphens + t = re.sub(r"-+", "-", t) + # Trim hyphens + t = t.strip("-") + return t + + +def collect_heading_ids(file_path: str) -> Set[str]: + """ + Build the set of anchor IDs generated by headings within a markdown file. + Handles ATX (# ...) and Setext (underlined) headings. Accounts for duplicate + slugs by adding -1, -2, ... suffixes (GitHub behavior). + """ + try: + with open(file_path, "r", encoding="utf-8") as f: + lines = f.readlines() + except Exception: + return set() + + ids: Set[str] = set() + slug_counts: Dict[str, int] = {} + + def add_slug_from_text(text: str): + slug = normalize_heading_to_id(text) + if slug == "": + return + count = slug_counts.get(slug, 0) + if count == 0: + final = slug + else: + final = f"{slug}-{count}" + slug_counts[slug] = count + 1 + ids.add(final) + + # ATX headings + atx_re = re.compile(r"^\s{0,3}#{1,6}\s+(.*)$") + + # Walk lines, handle setext headings by looking ahead + i = 0 + while i < len(lines): + line = lines[i].rstrip("\n") + m = atx_re.match(line) + if m: + add_slug_from_text(m.group(1).strip()) + i += 1 + continue + # Setext H1/H2 + if i + 1 < len(lines): + underline = lines[i + 1].rstrip("\n") + if re.match(r"^\s{0,3}=+\s*$", underline) or re.match(r"^\s{0,3}-+\s*$", underline): + add_slug_from_text(line.strip()) + i += 2 + continue + i += 1 + + return ids + + +def is_external(target: str) -> bool: + return bool(re.match(r"^(https?://|mailto:|tel:|data:)", target)) + + +def resolve_path(base_dir: str, target_path: str) -> str: + return os.path.normpath(os.path.join(base_dir, target_path)) + + +def check_internal_link(src_file: str, target_raw: str) -> Tuple[bool, str]: + # Separate fragment + if target_raw.startswith("#"): + # Anchor within same file + frag = target_raw[1:] + anchor = unescape_uri(frag) + anchor = anchor.strip() + anchor = anchor.lower() + ids = collect_heading_ids(src_file) + if anchor in ids: + return True, "" + return False, f"dangling anchor '#{frag}' (no matching heading)" + + # Split off query/fragment + core = re.sub(r"[?#].*$", "", target_raw) + core = unescape_uri(core) + base_dir = os.path.dirname(src_file) + target_fs = resolve_path(base_dir, core) + if not os.path.exists(target_fs): + return False, f"target file not found: {core}" + + # Fragment check if present + m = re.search(r"#(.+)$", target_raw) + if m: + frag = m.group(1) + anchor = unescape_uri(frag).strip().lower() + ids = collect_heading_ids(target_fs) + if anchor not in ids: + return False, f"dangling anchor '#{frag}' in {core}" + + return True, "" + + +def main(paths: List[str]) -> int: + issues = 0 + + def iter_markdown_files() -> List[str]: + files: List[str] = [] + for p in paths: + if os.path.isdir(p): + for dirpath, dirnames, filenames in os.walk(p): + dirnames[:] = [d for d in dirnames if d not in EXCLUDE_DIRS] + for filename in filenames: + if filename.lower().endswith(".md"): + files.append(os.path.join(dirpath, filename)) + else: + if p.lower().endswith(".md") and os.path.exists(p): + files.append(p) + return files + + files = iter_markdown_files() + for path in files: + try: + with open(path, "r", encoding="utf-8") as f: + lines = f.readlines() + except Exception: + continue + for idx, line in enumerate(lines, start=1): + for m in LINK_RE.finditer(line): + target = m.group("target").strip() + # Skip images, external links, anchors we can't resolve externally here + if is_external(target): + continue + ok, reason = check_internal_link(path, target) + if not ok: + issues += 1 + print(f"{path}:{idx}: Broken link '{target}': {reason}") + + if issues: + print(f"Found {issues} broken internal markdown link(s).", file=sys.stderr) + print("Fix the paths or anchors so links resolve.", file=sys.stderr) + return 1 + return 0 + + +if __name__ == "__main__": + args = sys.argv[1:] + if not args: + args = ["."] + sys.exit(main(args)) diff --git a/.github/workflows/csharpier-autofix.yml b/.github/workflows/csharpier-autofix.yml new file mode 100644 index 0000000..a020c3d --- /dev/null +++ b/.github/workflows/csharpier-autofix.yml @@ -0,0 +1,152 @@ +name: CSharpier Auto Format + +on: + pull_request: + pull_request_target: + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +jobs: + format: + name: Format and propose changes + if: ${{ github.event_name != 'pull_request_target' }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: "8.0.x" + + - name: Restore .NET tools + run: dotnet tool restore + + - name: Run CSharpier (format repository) + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.user.login == 'dependabot[bot]' }} + run: dotnet tool run csharpier format + + - name: Commit formatting changes to PR branch (Dependabot only) + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.user.login == 'dependabot[bot]' }} + uses: stefanzweifel/git-auto-commit-action@v7 + with: + commit_message: "chore(format): apply CSharpier formatting" + branch: ${{ github.head_ref }} + file_pattern: | + **/*.cs + + - name: Verify formatting (CI gate) + run: dotnet tool run csharpier check . + + format_fork: + name: Fork PR bot formatting PR (Dependabot only) + if: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository && github.event.pull_request.user.login == 'dependabot[bot]' }} + runs-on: ubuntu-latest + steps: + - name: Checkout fork PR HEAD + uses: actions/checkout@v5 + with: + repository: ${{ github.event.pull_request.head.repo.full_name }} + ref: ${{ github.event.pull_request.head.ref }} + persist-credentials: false + fetch-depth: 0 + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: "8.0.x" + + - name: Install CSharpier (pinned) + run: dotnet tool install -g csharpier --version 1.1.2 + + - name: Run CSharpier (format repository) + run: ~/.dotnet/tools/csharpier . + + - name: Detect changes + id: changes + shell: bash + run: | + if git diff --quiet; then + echo "has_changes=false" >> $GITHUB_OUTPUT + else + echo "has_changes=true" >> $GITHUB_OUTPUT + fi + + - name: Create bot branch and push to base repo + if: steps.changes.outputs.has_changes == 'true' + shell: bash + env: + GH_REPO: ${{ github.repository }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + BRANCH="bot/csharpier/pr-${{ github.event.pull_request.number }}" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git checkout -B "$BRANCH" + # Only stage C# files to avoid touching workflow YAML (requires special permissions) + git add '**/*.cs' + git commit -m "chore(format): apply CSharpier formatting for PR #${{ github.event.pull_request.number }}" + git remote add upstream "https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git" + git fetch upstream + git push upstream "$BRANCH" --force + echo "branch=$BRANCH" >> $GITHUB_OUTPUT + + - name: Open or update formatting PR in base repo + if: steps.changes.outputs.has_changes == 'true' + uses: actions/github-script@v7 + with: + script: | + const prNumber = context.payload.pull_request.number; + const baseRef = context.payload.pull_request.base.ref; + const headBranch = `bot/csharpier/pr-${prNumber}`; + const {owner, repo} = context.repo; + const title = `chore(format): Apply CSharpier to PR #${prNumber}`; + const body = [ + `This automated PR applies CSharpier formatting to the changes from PR #${prNumber}.`, + '', + `- Source PR (fork): #${prNumber}`, + `- Target branch: ${baseRef}`, + '', + 'If this PR is merged, it will include the contributor\'s changes plus required formatting.', + 'You can then close the original PR or ask the author to rebase.', + ].join('\n'); + + // Check if a PR from this branch already exists + const existing = await github.rest.pulls.list({ owner, repo, state: 'open', head: `${owner}:${headBranch}` }); + if (existing.data.length === 0) { + await github.rest.pulls.create({ owner, repo, head: headBranch, base: baseRef, title, body }); + } + + - name: Comment link on original PR + if: steps.changes.outputs.has_changes == 'true' + uses: actions/github-script@v7 + with: + script: | + const prNumber = context.payload.pull_request.number; + const {owner, repo} = context.repo; + const headBranch = `bot/csharpier/pr-${prNumber}`; + // Find the formatting PR we just created or updated + const resp = await github.rest.pulls.list({ owner, repo, state: 'open', head: `${owner}:${headBranch}` }); + if (resp.data.length > 0) { + const fmtPr = resp.data[0]; + const body = `A formatting PR has been opened: #${fmtPr.number} (applies CSharpier to this PR).`; + // Avoid duplicate comments by checking recent comments + const comments = await github.rest.issues.listComments({ owner, repo, issue_number: prNumber, per_page: 50 }); + const already = comments.data.some( + (c) => + c.body && + c.body.includes(`#${fmtPr.number}`) && + c.user && + c.user.login === 'github-actions[bot]' + ); + if (!already) { + await github.rest.issues.createComment({ owner, repo, issue_number: prNumber, body }); + } + } diff --git a/.github/workflows/format-on-demand.yml b/.github/workflows/format-on-demand.yml new file mode 100644 index 0000000..2c3fa0e --- /dev/null +++ b/.github/workflows/format-on-demand.yml @@ -0,0 +1,305 @@ +name: Opt-in Formatting + +on: + issue_comment: + types: [created] + workflow_dispatch: + inputs: + pr_number: + description: PR number to format + required: true + +permissions: + contents: write + pull-requests: write + +jobs: + by_comment: + name: Run on /format comment + if: >- + ${{ github.event_name == 'issue_comment' && + github.event.action == 'created' && + github.event.issue.pull_request && + (contains(github.event.comment.body, '/format') || contains(github.event.comment.body, '/autofix') || contains(github.event.comment.body, '/lint-fix')) }} + runs-on: ubuntu-latest + steps: + - name: Resolve PR metadata and authorize request + id: meta + uses: actions/github-script@v7 + with: + script: | + const prNumber = context.payload.issue.number; + const pr = (await github.rest.pulls.get({ ...context.repo, pull_number: prNumber })).data; + const commenter = context.payload.comment.user.login; + const prAuthor = pr.user.login; + const assoc = context.payload.comment.author_association; // OWNER, MEMBER, COLLABORATOR, CONTRIBUTOR, etc. + + const isMaintainer = ['OWNER','MEMBER','COLLABORATOR'].includes(assoc); + const isAuthor = commenter === prAuthor; + const allowed = isMaintainer || isAuthor; + + core.setOutput('allowed', String(allowed)); + core.setOutput('pr', String(prNumber)); + core.setOutput('base_ref', pr.base.ref); + core.setOutput('head_repo', pr.head.repo.full_name); + core.setOutput('head_ref', pr.head.ref); + core.setOutput('same_repo', String(pr.head.repo.full_name === `${context.repo.owner}/${context.repo.repo}`)); + + - name: Exit if not authorized + if: ${{ steps.meta.outputs.allowed != 'true' }} + run: | + echo "Commenter is not authorized to trigger formatting." 1>&2 + exit 1 + + - name: Checkout PR branch (same-repo) + if: ${{ steps.meta.outputs.same_repo == 'true' }} + uses: actions/checkout@v5 + with: + ref: ${{ steps.meta.outputs.head_ref }} + fetch-depth: 0 + + - name: Checkout PR fork head + if: ${{ steps.meta.outputs.same_repo != 'true' }} + uses: actions/checkout@v5 + with: + repository: ${{ steps.meta.outputs.head_repo }} + ref: ${{ steps.meta.outputs.head_ref }} + persist-credentials: false + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v5 + with: + node-version: '20' + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: '8.0.x' + + - name: Restore .NET tools + run: dotnet tool restore + + - name: Apply Prettier fixes + run: | + npx --yes prettier@3.3.3 --write "**/*.{md,markdown}" + npx --yes prettier@3.3.3 --write "**/*.{json,asmdef,asmref}" + npx --yes prettier@3.3.3 --write "**/*.{yml,yaml}" + + - name: Apply markdownlint fixes + run: | + npx --yes markdownlint-cli@0.40.0 "**/*.md" "**/*.markdown" --config .markdownlint.json --ignore-path .markdownlintignore --fix + + - name: Apply CSharpier formatting + run: dotnet tool run csharpier format + + - name: Detect changes + id: changes + shell: bash + run: | + if git diff --quiet; then + echo "has_changes=false" >> $GITHUB_OUTPUT + else + echo "has_changes=true" >> $GITHUB_OUTPUT + fi + + - name: Commit changes to PR branch (same-repo) + if: ${{ steps.meta.outputs.same_repo == 'true' && steps.changes.outputs.has_changes == 'true' }} + uses: stefanzweifel/git-auto-commit-action@v7 + with: + commit_message: "chore(format): apply requested formatting" + branch: ${{ steps.meta.outputs.head_ref }} + file_pattern: | + **/*.cs + **/*.md + **/*.markdown + **/*.json + **/*.asmdef + **/*.asmref + + - name: Create bot branch and PR (fork) + if: ${{ steps.meta.outputs.same_repo != 'true' && steps.changes.outputs.has_changes == 'true' }} + shell: bash + env: + GH_REPO: ${{ github.repository }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + BRANCH="bot/format/pr-${{ steps.meta.outputs.pr }}" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git checkout -B "$BRANCH" + # Stage only supported text files; avoid workflows to prevent permission issues + git add '**/*.cs' '**/*.md' '**/*.markdown' '**/*.json' '**/*.asmdef' '**/*.asmref' + git commit -m "chore(format): apply requested formatting for PR #${{ steps.meta.outputs.pr }}" + git remote add upstream "https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git" + git fetch upstream + git push upstream "$BRANCH" --force + + - name: Open/Update formatting PR (fork) + if: ${{ steps.meta.outputs.same_repo != 'true' && steps.changes.outputs.has_changes == 'true' }} + uses: actions/github-script@v7 + with: + script: | + const prNumber = Number(core.getInput('pr')) || Number('${{ steps.meta.outputs.pr }}'); + const baseRef = '${{ steps.meta.outputs.base_ref }}'; + const headBranch = `bot/format/pr-${prNumber}`; + const {owner, repo} = context.repo; + const title = `chore(format): Apply formatting to PR #${prNumber}`; + const body = [ + `This automated PR applies Prettier/markdownlint/CSharpier formatting to the changes from PR #${prNumber}.`, + '', + `- Source PR (fork): #${prNumber}`, + `- Target branch: ${baseRef}`, + ].join('\n'); + const existing = await github.rest.pulls.list({ owner, repo, state: 'open', head: `${owner}:${headBranch}` }); + if (existing.data.length === 0) { + await github.rest.pulls.create({ owner, repo, head: headBranch, base: baseRef, title, body }); + } + + - name: Comment result on PR + if: ${{ steps.changes.outputs.has_changes == 'true' }} + uses: actions/github-script@v7 + with: + script: | + const prNumber = Number('${{ steps.meta.outputs.pr }}'); + const sameRepo = '${{ steps.meta.outputs.same_repo }}' === 'true'; + const body = sameRepo + ? 'Applied formatting as requested and pushed commits to this PR branch.' + : 'Opened a formatting PR against the base repository with requested fixes.'; + await github.rest.issues.createComment({ ...context.repo, issue_number: prNumber, body }); + + - name: No-op comment (nothing to change) + if: ${{ steps.changes.outputs.has_changes != 'true' }} + uses: actions/github-script@v7 + with: + script: | + const prNumber = Number('${{ steps.meta.outputs.pr }}'); + const body = 'No formatting changes were necessary.'; + await github.rest.issues.createComment({ ...context.repo, issue_number: prNumber, body }); + + by_dispatch: + name: Run via manual dispatch + if: ${{ github.event_name == 'workflow_dispatch' }} + runs-on: ubuntu-latest + steps: + - name: Resolve PR metadata + id: meta + uses: actions/github-script@v7 + with: + script: | + const prNumber = Number(core.getInput('pr_number')); + if (!prNumber) core.setFailed('pr_number is required'); + const pr = (await github.rest.pulls.get({ ...context.repo, pull_number: prNumber })).data; + core.setOutput('pr', String(prNumber)); + core.setOutput('base_ref', pr.base.ref); + core.setOutput('head_repo', pr.head.repo.full_name); + core.setOutput('head_ref', pr.head.ref); + core.setOutput('same_repo', String(pr.head.repo.full_name === `${context.repo.owner}/${context.repo.repo}`)); + + - name: Checkout PR branch (same-repo) + if: ${{ steps.meta.outputs.same_repo == 'true' }} + uses: actions/checkout@v5 + with: + ref: ${{ steps.meta.outputs.head_ref }} + fetch-depth: 0 + + - name: Checkout PR fork head + if: ${{ steps.meta.outputs.same_repo != 'true' }} + uses: actions/checkout@v5 + with: + repository: ${{ steps.meta.outputs.head_repo }} + ref: ${{ steps.meta.outputs.head_ref }} + persist-credentials: false + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v5 + with: + node-version: '20' + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: '8.0.x' + + - name: Restore .NET tools + run: dotnet tool restore + + - name: Apply Prettier fixes + run: | + npx --yes prettier@3.3.3 --write "**/*.{md,markdown}" + npx --yes prettier@3.3.3 --write "**/*.{json,asmdef,asmref}" + npx --yes prettier@3.3.3 --write "**/*.{yml,yaml}" + + - name: Apply markdownlint fixes + run: | + npx --yes markdownlint-cli@0.40.0 "**/*.md" "**/*.markdown" --config .markdownlint.json --ignore-path .markdownlintignore --fix + + - name: Apply CSharpier formatting + run: dotnet tool run csharpier format + + - name: Detect changes + id: changes + shell: bash + run: | + if git diff --quiet; then + echo "has_changes=false" >> $GITHUB_OUTPUT + else + echo "has_changes=true" >> $GITHUB_OUTPUT + fi + + - name: Commit changes to PR branch (same-repo) + if: ${{ steps.meta.outputs.same_repo == 'true' && steps.changes.outputs.has_changes == 'true' }} + uses: stefanzweifel/git-auto-commit-action@v7 + with: + commit_message: "chore(format): apply requested formatting" + branch: ${{ steps.meta.outputs.head_ref }} + file_pattern: | + **/*.cs + **/*.md + **/*.markdown + **/*.json + **/*.asmdef + **/*.asmref + + - name: Create bot branch and PR (fork) + if: ${{ steps.meta.outputs.same_repo != 'true' && steps.changes.outputs.has_changes == 'true' }} + shell: bash + env: + GH_REPO: ${{ github.repository }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + BRANCH="bot/format/pr-${{ steps.meta.outputs.pr }}" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git checkout -B "$BRANCH" + # Stage only supported text files; avoid workflows to prevent permission issues + git add '**/*.cs' '**/*.md' '**/*.markdown' '**/*.json' '**/*.asmdef' '**/*.asmref' + git commit -m "chore(format): apply requested formatting for PR #${{ steps.meta.outputs.pr }}" + git remote add upstream "https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git" + git fetch upstream + git push upstream "$BRANCH" --force + + - name: Open/Update formatting PR (fork) + if: ${{ steps.meta.outputs.same_repo != 'true' && steps.changes.outputs.has_changes == 'true' }} + uses: actions/github-script@v7 + with: + script: | + const prNumber = Number('${{ steps.meta.outputs.pr }}'); + const baseRef = '${{ steps.meta.outputs.base_ref }}'; + const headBranch = `bot/format/pr-${prNumber}`; + const {owner, repo} = context.repo; + const title = `chore(format): Apply formatting to PR #${prNumber}`; + const body = [ + `This automated PR applies Prettier/markdownlint/CSharpier formatting to the changes from PR #${prNumber}.`, + '', + `- Source PR (fork): #${prNumber}`, + `- Target branch: ${baseRef}`, + ].join('\n'); + const existing = await github.rest.pulls.list({ owner, repo, state: 'open', head: `${owner}:${headBranch}` }); + if (existing.data.length === 0) { + await github.rest.pulls.create({ owner, repo, head: headBranch, base: baseRef, title, body }); + } + diff --git a/.github/workflows/lint-doc-links.yml b/.github/workflows/lint-doc-links.yml new file mode 100644 index 0000000..c1e9ef2 --- /dev/null +++ b/.github/workflows/lint-doc-links.yml @@ -0,0 +1,30 @@ +name: Lint Docs Links + +on: + push: + branches: + - main + pull_request: + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Run Markdown link linter + shell: pwsh + run: ./scripts/lint-doc-links.ps1 -VerboseOutput + + - name: Check dead links (lychee) + uses: lycheeverse/lychee-action@v2 + with: + args: >- + -c .lychee.toml + --no-progress + --include-fragments + --verbose + "./**/*.md" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/markdown-json.yml b/.github/workflows/markdown-json.yml new file mode 100644 index 0000000..279e583 --- /dev/null +++ b/.github/workflows/markdown-json.yml @@ -0,0 +1,45 @@ +name: Markdown & JSON Lint/Format + +on: + push: + branches: + - main + pull_request: + +permissions: + contents: read + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Setup Node.js + uses: actions/setup-node@v5 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: package.json + + - name: Install dependencies + run: | + if [ -f package-lock.json ]; then + npm ci + else + npm i --no-audit --no-fund + fi + + - name: Prettier check (Markdown) + run: npm run format:md:check + + - name: Prettier check (JSON / asmdef / asmref) + run: npm run format:json:check + + - name: Markdown lint + run: npm run lint:markdown + + - name: Enforce EOL (CRLF) and No BOM + shell: pwsh + run: ./scripts/check-eol.ps1 -VerboseOutput diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 20eb999..f9ac52e 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -1,75 +1,75 @@ -name: Publish to NPM - -on: - push: - branches: - - master - paths: - - 'package.json' - workflow_dispatch: - -jobs: - publish_npm: - runs-on: ubuntu-latest - - steps: - - name: Checkout Repository - uses: actions/checkout@v5 - with: - fetch-depth: 0 # Ensure full commit history for version comparison - - - name: Set up Node.js - uses: actions/setup-node@v5 - with: - node-version: 18 - registry-url: 'https://registry.npmjs.org/' - - - name: Check if version changed - id: version_check - run: | - if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - echo "Manual trigger detected. Skipping version check." - NEW_VERSION=$(jq -r '.version' package.json) - echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV - echo "should_publish=true" >> $GITHUB_ENV - if [[ "$NEW_VERSION" == *"rc"* ]]; then - echo "This is a pre-release (next tag)." - echo "NPM_TAG=next" >> $GITHUB_ENV - else - echo "This is a stable release (latest tag)." - echo "NPM_TAG=latest" >> $GITHUB_ENV - fi - else - PREV_VERSION=$(git show HEAD~1:package.json | jq -r '.version' || echo "0.0.0") - NEW_VERSION=$(jq -r '.version' package.json) - echo "Previous version: $PREV_VERSION" - echo "New version: $NEW_VERSION" - - if [ "$PREV_VERSION" != "$NEW_VERSION" ]; then - echo "Version changed, proceeding..." - echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV - echo "should_publish=true" >> $GITHUB_ENV - - # Detect pre-releases (versions with "rc" or similar tags) - if [[ "$NEW_VERSION" == *"rc"* ]]; then - echo "This is a pre-release (next tag)." - echo "NPM_TAG=next" >> $GITHUB_ENV - else - echo "This is a stable release (latest tag)." - echo "NPM_TAG=latest" >> $GITHUB_ENV - fi - else - echo "Version did not change, skipping..." - echo "should_publish=false" >> $GITHUB_ENV - fi - fi - - - name: Install Dependencies - if: env.should_publish == 'true' - run: npm install - - - name: Publish to NPM - if: env.should_publish == 'true' - run: npm publish --access public --tag ${{ env.NPM_TAG }} - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} +name: Publish to NPM + +on: + push: + branches: + - main + paths: + - 'package.json' + workflow_dispatch: + +jobs: + publish_npm: + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v5 + with: + fetch-depth: 0 # Ensure full commit history for version comparison + + - name: Set up Node.js + uses: actions/setup-node@v5 + with: + node-version: 18 + registry-url: 'https://registry.npmjs.org/' + + - name: Check if version changed + id: version_check + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + echo "Manual trigger detected. Skipping version check." + NEW_VERSION=$(jq -r '.version' package.json) + echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV + echo "should_publish=true" >> $GITHUB_ENV + if [[ "$NEW_VERSION" == *"rc"* ]]; then + echo "This is a pre-release (next tag)." + echo "NPM_TAG=next" >> $GITHUB_ENV + else + echo "This is a stable release (latest tag)." + echo "NPM_TAG=latest" >> $GITHUB_ENV + fi + else + PREV_VERSION=$(git show HEAD~1:package.json | jq -r '.version' || echo "0.0.0") + NEW_VERSION=$(jq -r '.version' package.json) + echo "Previous version: $PREV_VERSION" + echo "New version: $NEW_VERSION" + + if [ "$PREV_VERSION" != "$NEW_VERSION" ]; then + echo "Version changed, proceeding..." + echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV + echo "should_publish=true" >> $GITHUB_ENV + + # Detect pre-releases (versions with "rc" or similar tags) + if [[ "$NEW_VERSION" == *"rc"* ]]; then + echo "This is a pre-release (next tag)." + echo "NPM_TAG=next" >> $GITHUB_ENV + else + echo "This is a stable release (latest tag)." + echo "NPM_TAG=latest" >> $GITHUB_ENV + fi + else + echo "Version did not change, skipping..." + echo "should_publish=false" >> $GITHUB_ENV + fi + fi + + - name: Install Dependencies + if: env.should_publish == 'true' + run: npm install + + - name: Publish to NPM + if: env.should_publish == 'true' + run: npm publish --access public --tag ${{ env.NPM_TAG }} + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/prettier-autofix.yml b/.github/workflows/prettier-autofix.yml new file mode 100644 index 0000000..6737cfb --- /dev/null +++ b/.github/workflows/prettier-autofix.yml @@ -0,0 +1,195 @@ +name: Prettier Auto Fix + +on: + pull_request: + pull_request_target: + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +jobs: + autofix: + name: Format and propose changes + if: ${{ github.event_name != 'pull_request_target' }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v5 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: package.json + + - name: Install dependencies + run: | + if [ -f package-lock.json ]; then + npm ci + else + npm i --no-audit --no-fund + fi + + - name: Run Prettier (write fixes) + if: ${{ github.event.pull_request.user.login == 'dependabot[bot]' }} + run: | + npm run format:md + npm run format:json + npm run format:yaml + + - name: Markdownlint (auto-fix) + if: ${{ github.event.pull_request.user.login == 'dependabot[bot]' }} + run: npx --yes markdownlint-cli "**/*.md" "**/*.markdown" --config .markdownlint.json --ignore-path .markdownlintignore --fix + + - name: Commit formatting changes to PR branch (Dependabot only) + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.user.login == 'dependabot[bot]' }} + uses: stefanzweifel/git-auto-commit-action@v7 + with: + commit_message: "chore(format): apply Prettier/markdownlint fixes" + branch: ${{ github.head_ref }} + file_pattern: | + **/*.md + **/*.markdown + **/*.json + **/*.asmdef + **/*.asmref + **/*.yml + **/*.yaml + + - name: Prettier check (Markdown) + run: npm run format:md:check + + - name: Prettier check (JSON / asmdef / asmref) + run: npm run format:json:check + + - name: Prettier check (YAML) + run: npm run format:yaml:check + + - name: Markdown lint (CI gate) + run: npm run lint:markdown + + - name: Enforce EOL (CRLF) and No BOM + shell: pwsh + run: ./scripts/check-eol.ps1 -VerboseOutput + + autofix_fork: + name: Fork PR bot formatting PR (Dependabot only) + if: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository && github.event.pull_request.user.login == 'dependabot[bot]' }} + runs-on: ubuntu-latest + steps: + - name: Checkout fork PR HEAD + uses: actions/checkout@v5 + with: + repository: ${{ github.event.pull_request.head.repo.full_name }} + ref: ${{ github.event.pull_request.head.ref }} + persist-credentials: false + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v5 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: package.json + + - name: Install dependencies + run: | + if [ -f package-lock.json ]; then + npm ci + else + npm i --no-audit --no-fund + fi + + - name: Run Prettier (write fixes) + run: | + npm run format:md + npm run format:json + npm run format:yaml + + - name: Markdownlint (auto-fix) + run: npx --yes markdownlint-cli "**/*.md" "**/*.markdown" --config .markdownlint.json --ignore-path .markdownlintignore --fix + + - name: Detect changes + id: changes + shell: bash + run: | + if git diff --quiet; then + echo "has_changes=false" >> $GITHUB_OUTPUT + else + echo "has_changes=true" >> $GITHUB_OUTPUT + fi + + - name: Create bot branch and push to base repo + if: steps.changes.outputs.has_changes == 'true' + shell: bash + env: + GH_REPO: ${{ github.repository }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + BRANCH="bot/prettier/pr-${{ github.event.pull_request.number }}" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git checkout -B "$BRANCH" + # Stage only files we format; avoid workflows to prevent permission issues + git add '**/*.md' '**/*.markdown' '**/*.json' '**/*.asmdef' '**/*.asmref' '**/*.yml' '**/*.yaml' + git commit -m "chore(format): apply Prettier/markdownlint fixes for PR #${{ github.event.pull_request.number }}" + git remote add upstream "https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git" + git fetch upstream + git push upstream "$BRANCH" --force + echo "branch=$BRANCH" >> $GITHUB_OUTPUT + + - name: Open or update formatting PR in base repo + if: steps.changes.outputs.has_changes == 'true' + uses: actions/github-script@v7 + with: + script: | + const prNumber = context.payload.pull_request.number; + const baseRef = context.payload.pull_request.base.ref; + const headBranch = `bot/prettier/pr-${prNumber}`; + const {owner, repo} = context.repo; + const title = `chore(format): Apply Prettier/markdownlint to PR #${prNumber}`; + const body = [ + `This automated PR applies Prettier and markdownlint fixes to the changes from PR #${prNumber}.`, + '', + `- Source PR (fork): #${prNumber}`, + `- Target branch: ${baseRef}`, + '', + 'If this PR is merged, it will include the contributor\'s changes plus required formatting.', + 'You can then close the original PR or ask the author to rebase.', + ].join('\n'); + + const existing = await github.rest.pulls.list({ owner, repo, state: 'open', head: `${owner}:${headBranch}` }); + if (existing.data.length === 0) { + await github.rest.pulls.create({ owner, repo, head: headBranch, base: baseRef, title, body }); + } + + - name: Comment link on original PR + if: steps.changes.outputs.has_changes == 'true' + uses: actions/github-script@v7 + with: + script: | + const prNumber = context.payload.pull_request.number; + const {owner, repo} = context.repo; + const headBranch = `bot/prettier/pr-${prNumber}`; + const resp = await github.rest.pulls.list({ owner, repo, state: 'open', head: `${owner}:${headBranch}` }); + if (resp.data.length > 0) { + const fmtPr = resp.data[0]; + const body = `A formatting PR has been opened: #${fmtPr.number} (applies Prettier/markdownlint fixes to this PR).`; + const comments = await github.rest.issues.listComments({ owner, repo, issue_number: prNumber, per_page: 50 }); + const already = comments.data.some( + (c) => + c.body && + c.body.includes(`#${fmtPr.number}`) && + c.user && + c.user.login === 'github-actions[bot]' + ); + if (!already) { + await github.rest.issues.createComment({ owner, repo, issue_number: prNumber, body }); + } + } diff --git a/.github/workflows/update-dotnet-tools.yml b/.github/workflows/update-dotnet-tools.yml new file mode 100644 index 0000000..8613ee7 --- /dev/null +++ b/.github/workflows/update-dotnet-tools.yml @@ -0,0 +1,80 @@ +name: Update .NET Tools + +on: + schedule: + - cron: '25 5 * * *' + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +jobs: + update-tools: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Setup .NET SDK + uses: actions/setup-dotnet@v5 + with: + dotnet-version: '8.0.x' + + - name: Restore dotnet tools + run: dotnet tool restore + + - name: Update local dotnet tools + shell: pwsh + run: | + $manifest = Join-Path $PWD ".config/dotnet-tools.json" + if (!(Test-Path $manifest)) { + Write-Host "No dotnet tool manifest found. Skipping." + exit 0 + } + + $json = Get-Content $manifest -Raw | ConvertFrom-Json + $toolIds = @() + if ($json.tools) { + $toolIds = $json.tools.PSObject.Properties.Name + } + + if ($toolIds.Count -eq 0) { + Write-Host "No tools defined in manifest." + exit 0 + } + + foreach ($id in $toolIds) { + Write-Host "Updating $id..." + dotnet tool update $id --local + } + + - name: Detect manifest changes + id: git_changes + run: | + if git diff --quiet; then + echo "changed=false" >> "$GITHUB_OUTPUT" + else + echo "changed=true" >> "$GITHUB_OUTPUT" + fi + + - name: Create Pull Request + if: steps.git_changes.outputs.changed == 'true' + uses: peter-evans/create-pull-request@v7 + with: + branch: chore/update-dotnet-tools + title: "chore: update .NET local tools" + commit-message: "chore(dotnet-tools): update .config/dotnet-tools.json" + body: | + Automated update of local .NET tools defined in `.config/dotnet-tools.json`. + labels: dependencies + assignees: wallstop + reviewers: wallstop + + - name: No changes summary + if: steps.git_changes.outputs.changed != 'true' + run: | + echo "## .NET tools are up to date" >> $GITHUB_STEP_SUMMARY + diff --git a/.github/workflows/yaml-format-lint.yml b/.github/workflows/yaml-format-lint.yml new file mode 100644 index 0000000..299188e --- /dev/null +++ b/.github/workflows/yaml-format-lint.yml @@ -0,0 +1,41 @@ +name: YAML Format + Lint + +on: + pull_request: + push: + branches: + - main + workflow_dispatch: + +jobs: + yaml-checks: + name: Prettier and yamllint + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Setup Node + uses: actions/setup-node@v5 + with: + node-version: "20" + cache: 'npm' + cache-dependency-path: package.json + + - name: Install dependencies + run: | + if [ -f package-lock.json ]; then + npm ci + else + npm i --no-audit --no-fund + fi + + - name: Prettier check (YAML) + run: npm run format:yaml:check + + - name: yamllint + uses: ibiqlik/action-yamllint@v3.1.1 + with: + file_or_dir: . + config_file: .yamllint.yaml + strict: true diff --git a/.gitignore b/.gitignore index 9eef072..fb447e1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ - -# NPM -node_modules/ \ No newline at end of file + +# NPM +node_modules/ +node_modules.meta \ No newline at end of file diff --git a/.lychee.toml b/.lychee.toml new file mode 100644 index 0000000..07ac01b --- /dev/null +++ b/.lychee.toml @@ -0,0 +1,26 @@ +verbosity = "info" +no_progress = true +max_concurrency = 4 +exclude_mail = true + +# Network tuning +timeout = 20 # seconds per request +retries = 3 # retry transient failures +retry_wait_time = 2 # seconds between retries +max_redirects = 10 +user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0 Safari/537.36" + +# Treat successes and rate-limiting as acceptable in CI +# Accept all 2xx as valid plus 429 (rate limited) +accept = ["200..=299", 429] + +# Only check web links +scheme = ["https", "http"] + +# Ignore common local/test URLs +exclude = [ + "^https?://localhost", + "^http://127\\.0\\.0\\.1", + "^https?://0\\.0\\.0\\.0", + "^file://" +] diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 0000000..c8c41f1 --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,23 @@ +{ + "default": true, + // Prefer ATX-style headings ("#"), avoids mixed setext/atx issues + "MD003": { "style": "atx" }, + // Allow long lines; Prettier handles wrapping policy + "MD013": false, + // Allow inline HTML when needed + "MD033": false, + // First line need not be a top-level heading (many docs start with badges) + "MD041": false, + // Use fenced code blocks consistently (matches Prettier) + "MD046": { "style": "fenced" }, + // Duplicate headings only matter among siblings + "MD024": { "siblings_only": true }, + // Accept ascending ordered list numbers (matches Prettier behavior) + "MD029": { "style": "ordered" }, + // Permit bare URLs; Prettier won’t auto-wrap, and link checks are handled separately + "MD034": false, + // Allow exactly one blank line between blocks (Prettier’s behavior) + "MD012": { "maximum": 1 }, + // Don’t flag the two trailing spaces used for hard line breaks in Markdown + "MD009": { "strict": false } +} diff --git a/.markdownlint.jsonc b/.markdownlint.jsonc new file mode 100644 index 0000000..35e1232 --- /dev/null +++ b/.markdownlint.jsonc @@ -0,0 +1,21 @@ +{ + "default": true, + // Allow long lines; Prettier handles wrapping policy + "MD013": false, + // Allow inline HTML when needed + "MD033": false, + // First line need not be a top-level heading (many docs start with badges) + "MD041": false, + // Use fenced code blocks consistently (matches Prettier) + "MD046": { "style": "fenced" }, + // Duplicate headings only matter among siblings + "MD024": { "siblings_only": true }, + // Accept ascending ordered list numbers (matches Prettier behavior) + "MD029": { "style": "ordered" }, + // Permit bare URLs; Prettier won’t auto-wrap, and link checks are handled separately + "MD034": false, + // Allow exactly one blank line between blocks (Prettier’s behavior) + "MD012": { "maximum": 1 }, + // Don’t flag the two trailing spaces used for hard line breaks in Markdown + "MD009": { "strict": false } +} diff --git a/.markdownlintignore b/.markdownlintignore new file mode 100644 index 0000000..96a60de --- /dev/null +++ b/.markdownlintignore @@ -0,0 +1,8 @@ +node_modules/ +.git/ +Library/ +obj/ +Temp/ +Samples~/ +/.github/ + diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3311a9f..da513a9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,22 +1,67 @@ -repos: - - repo: local - hooks: - - id: dotnet-tool-restore - name: Install .NET tools - entry: dotnet tool restore - language: system - always_run: true - pass_filenames: false - stages: - - pre-commit - - pre-push - - post-checkout - - post-rewrite - description: Install the .NET tools listed at .config/dotnet-tools.json. - - id: csharpier - name: Run CSharpier on C# files - entry: dotnet tool run Csharpier format - language: system - types: - - c# - description: CSharpier is an opinionated C# formatter inspired by Prettier. \ No newline at end of file +repos: + - repo: local + hooks: + - id: dotnet-tool-restore + name: Install .NET tools + entry: dotnet tool restore + language: system + always_run: true + pass_filenames: false + stages: + - pre-commit + - pre-push + - post-checkout + - post-rewrite + description: Install the .NET tools listed at .config/dotnet-tools.json. + - id: csharpier + name: Run CSharpier on C# files + entry: dotnet tool run csharpier format . + language: system + # Run across the whole repo so it matches CI's full-repo check + pass_filenames: false + always_run: true + description: CSharpier is an opinionated C# formatter inspired by Prettier. + + - repo: local + hooks: + - id: prettier + name: Prettier (Markdown, JSON, asmdef, asmref, YAML) + entry: npx --yes prettier --write . + language: system + # Run across the whole repo to match CI (filter still documents scope) + pass_filenames: false + always_run: true + files: '(?i)\.(md|markdown|json|asmdef|asmref|ya?ml)$' + description: Use the repo's Prettier version from package.json. + + - repo: local + hooks: + - id: markdownlint + name: markdownlint (respect repo config) + entry: npx --yes markdownlint-cli --config .markdownlint.json --ignore-path .markdownlintignore + language: system + files: '(?i)\.(md|markdown)$' + + - repo: local + hooks: + - id: yamllint + name: yamllint (if available) + entry: bash -c 'if command -v yamllint >/dev/null 2>&1; then yamllint -c .yamllint.yaml "$@"; else echo "yamllint not installed; skipping"; fi' -- + language: system + files: '(?i)\.(ya?ml)$' + + - repo: local + hooks: + - id: check-eol-bom + name: Enforce CRLF + no BOM (auto-fix) + entry: pwsh -NoProfile -File scripts/check-eol.ps1 -Fix + language: system + pass_filenames: false + always_run: true + description: Normalizes line endings to CRLF and strips UTF-8 BOM. + - id: lint-doc-links + name: Lint relative Markdown links + entry: pwsh -NoProfile -File scripts/lint-doc-links.ps1 + language: system + pass_filenames: false + files: '(?i)\.(md|markdown)$' diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..a98a735 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,22 @@ +# Node / tooling +node_modules/ + +# Unity / build artifacts +Library/ +obj/ +Temp/ +Logs/ + +# GitHub +.github/ + +# Samples and external content +Samples~/ + +# Only format supported text/assets; Prettier globbing will still be constrained by scripts +**/*.meta +**/*.unity +**/*.prefab +**/*.mat +**/*.asset + diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..f165df7 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://json.schemastore.org/prettierrc", + "printWidth": 100, + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": false, + "trailingComma": "none", + "bracketSpacing": true, + "proseWrap": "preserve", + "endOfLine": "crlf", + "overrides": [ + { + "files": ["*.asmdef", "*.asmref"], + "options": { + "parser": "json" + } + } + ] +} diff --git a/.yamllint.yaml b/.yamllint.yaml new file mode 100644 index 0000000..725d438 --- /dev/null +++ b/.yamllint.yaml @@ -0,0 +1,31 @@ +extends: default + +ignore: | + node_modules/ + .git/ + Library/ + obj/ + Temp/ + Samples~/ + +rules: + line-length: + max: 200 + allow-non-breakable-words: true + allow-non-breakable-inline-mappings: true + truthy: disable + document-start: disable + comments-indentation: disable + comments: + min-spaces-from-content: 1 + indentation: + spaces: 2 + indent-sequences: consistent + new-lines: + type: dos + new-line-at-end-of-file: enable + trailing-spaces: enable + empty-lines: + max: 1 + max-start: 0 + max-end: 1 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..1fc30ca --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,57 @@ +# Repository Guidelines + +## Project Structure & Module Organization + +- `Runtime/` — core C# code (`WallstopStudios.DxCommandTerminal.asmdef`). +- `Editor/` — editor tooling and drawers (`*.Editor.asmdef`). +- `Tests/Runtime/` — Unity Test Framework (NUnit) tests (`*Tests.cs`, `*.asmdef`). +- `Styles/` — UI Toolkit styles (`.uss`) and theme assets (`.tss`). +- `Fonts/`, `Media/`, `Packs/` — assets used by the terminal. +- `package.json` — Unity package manifest + npm metadata. See `README.md` for usage. + +## Build, Test, and Development Commands + +- Install tools: `dotnet tool restore` +- Format C#: `dotnet tool run csharpier .` +- Optional hooks: install pre-commit — `pre-commit install` +- Unity tests (CLI example, Windows): + `"C:\\Program Files\\Unity\\Hub\\Editor\\2021.3.x\\Editor\\Unity.exe" -batchmode -projectPath -runTests -testPlatform playmode -assemblyNames WallstopStudios.DxCommandTerminal.Tests.Runtime -logfile - -quit` + Or use Unity’s Test Runner UI. + +## Coding Style & Naming Conventions + +- Follow `.editorconfig`: + - Indentation: spaces (C# 4), JSON/YAML/asmdef 2. + - Line endings: CRLF; encoding: UTF-8 (no BOM). + - C#: prefer braces; explicit types over `var` unless obvious; `using` inside namespace. + - Naming: Interfaces `IType`, type params `TType`, events/types/methods PascalCase; tests end with `Tests`. +- Use CSharpier for formatting before committing. +- Do not use underscores in function names, especially test function names. +- Do not use regions, anywhere, ever. +- Avoid `var` wherever possible, use expressive types. + +## Testing Guidelines + +- Framework: Unity Test Framework (NUnit) under `Tests/Runtime`. +- Conventions: file names `*Tests.cs`, one feature per fixture, deterministic tests. +- Run via Unity CLI (above) or Test Runner. Add/adjust tests when changing parsing, history, UI behavior, or input handling. +- Do not use regions. +- Try to use minimal comments and instead rely on expressive naming conventions and assertions. +- Do not use Description annotations for tests. +- Do not create `async Task` test methods - the Unity test runner does not support this. Make do with `IEnumerator` based UnityTestMethods. +- Do not use `Assert.ThrowsAsync`, it does not exist. +- When asserting that UnityEngine.Objects are null or not null, please check for null directly (thing != null, thing == null), to properly adhere to Unity Object existence checks. +- Do not use underscores in function names, especially test function names. +- Do not use regions, anywhere, ever. +- Avoid `var` wherever possible, use expressive types. + +## Commit & Pull Request Guidelines + +- Commits: imperative, concise subject (≤72 chars), explain “what/why”. Link issues/PRs (`#123`). +- PRs: include description, motivation, and test coverage. For UI/USS changes, add before/after screenshots. +- Versioning/CI: do not change `package.json` version unless preparing a release; npm publishing is automated via GitHub Actions on version bumps. + +## Security & Configuration Tips + +- No secrets in repo; publishing uses `NPM_TOKEN` in GitHub secrets. +- Target Unity `2021.3+`. Keep `asmdef` names and folder layout intact to preserve assembly boundaries. diff --git a/doc.md.meta b/AGENTS.md.meta similarity index 75% rename from doc.md.meta rename to AGENTS.md.meta index 3b792c1..494b049 100644 --- a/doc.md.meta +++ b/AGENTS.md.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 97dd482203a34cd43afbdc5d9f452813 +guid: 32fc9ec590fb00f42b06b82b7954c868 TextScriptImporter: externalObjects: {} userData: diff --git a/AssemblyInfo.cs b/AssemblyInfo.cs new file mode 100644 index 0000000..7cd60f0 --- /dev/null +++ b/AssemblyInfo.cs @@ -0,0 +1,5 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("WallstopStudios.DxCommandTerminal.Editor")] +[assembly: InternalsVisibleTo("WallstopStudios.DxCommandTerminal.Tests.Runtime")] +[assembly: InternalsVisibleTo("WallstopStudios.DxCommandTerminal.Tests")] diff --git a/AssemblyInfo.cs.meta b/AssemblyInfo.cs.meta new file mode 100644 index 0000000..572154c --- /dev/null +++ b/AssemblyInfo.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 584828a4e116479aa35e297ecb0599e7 +timeCreated: 1760547381 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ffcc7a0..3ddd1b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,17 +1,19 @@ -Changelog -========= +# Changelog ## 1.02 `08a66da` - 2018-09-14 ### Added + - Variables: defined with `set name value`, accessed with `$name`. Run `set` with no arguments to display all variables and their values. - Option to change the alpha value of the input background texture. - Command hint argument for better error messages. Use `RegisterCommand(Hint = "Command $a")]` to show a command's usage. ### Changed + - Better autocompletion: autocomplete can now partially complete words when there are multiple suggestions available. ### Fixed + - Fix background texture being destroyed when loading a scene with the Terminal set to `DontDestroyOnLoad`. - Fix hotkeys bound to function keys causing the input to not register the first character. - Fix formatting on autocomplete suggestions. @@ -19,16 +21,19 @@ Changelog ## 1.01 `9a1b0b3` - 2018-08-09 ### Added + - Option to to change the position of the toggle GUI buttons. - Option to change the window size ratio between the partial and full window height. - Optional GUI button to run a command (useful for mobile devices). ### Changed + - Autocomplete now uses the last word in the input text, rather than just completing the first word. -## 1.0 `db07b43` - 2018-07-15 +## 1.0 `db07b43` - 2018-07-15 ### Added + - Customizable toggle hotkey. - Two new terminal colors (customizable). - Option to change prompt character (or remove it). @@ -38,8 +43,10 @@ Changelog - Option to customize the input background contrast. ### Fixed + - Input registering hotkey character when hotkey was pressed. - Inspector presentation. ### Removed + - `LS` command in favor of `HELP` with no arguments to list all registered commands. diff --git a/Editor/CustomEditors/TerminalFontPackEditor.cs b/Editor/CustomEditors/TerminalFontPackEditor.cs index 22c45be..fca36ec 100644 --- a/Editor/CustomEditors/TerminalFontPackEditor.cs +++ b/Editor/CustomEditors/TerminalFontPackEditor.cs @@ -1,282 +1,282 @@ -namespace WallstopStudios.DxCommandTerminal.Editor.CustomEditors -{ -#if UNITY_EDITOR - using System; - using System.Collections.Generic; - using System.IO; - using DxCommandTerminal.Helper; - using Extensions; - using Themes; - using UnityEditor; - using UnityEngine; - using Object = UnityEngine.Object; - - [CustomEditor(typeof(TerminalFontPack))] - public sealed class TerminalFontPackEditor : Editor - { - [Flags] - private enum FontType - { - None = 0, - Normal = 1 << 0, - Bold = 1 << 1, - Italic = 1 << 2, - BoldItalic = 1 << 3, - ExtraBold = 1 << 4, - ExtraBoldItalic = 1 << 5, - ExtraLight = 1 << 6, - ExtraLightItalic = 1 << 7, - Light = 1 << 8, - LightItalic = 1 << 9, - Medium = 1 << 10, - MediumItalic = 1 << 11, - SemiBold = 1 << 12, - SemiBoldItalic = 1 << 13, - Thin = 1 << 14, - ThinItalic = 1 << 15, - Black = 1 << 16, - BlackItalic = 1 << 17, - Regular = 1 << 18, - Variable = 1 << 19, - Monospace = 1 << 20, - Condensed = 1 << 21, - CondensedBold = 1 << 22, - CondensedExtraBold = 1 << 23, - CondensedExtraLight = 1 << 24, - CondensedLight = 1 << 25, - CondensedMedium = 1 << 26, - CondensedSemiBold = 1 << 27, - CondensedThin = 1 << 28, - VariableFont_wght = 1 << 29, - VariableFont_width = 1 << 30, - } - - private readonly HashSet _fontCache = new(); - private FontType _fontRemovalType = FontType.None; - private FontType _fontAdditionType = FontType.None; - private string _lastSelectedDirectory; - private GUIStyle _impactButtonStyle; - - private void OnEnable() - { - _fontCache.Clear(); - _fontRemovalType = FontType.None; - } - - public override void OnInspectorGUI() - { - _impactButtonStyle ??= new GUIStyle(GUI.skin.button) - { - normal = { textColor = Color.yellow }, - fontStyle = FontStyle.Bold, - }; - - serializedObject.Update(); - TerminalFontPack fontPack = target as TerminalFontPack; - base.OnInspectorGUI(); - - if (fontPack == null) - { - return; - } - - bool anyChanged = false; - if (fontPack._fonts == null) - { - anyChanged = true; - fontPack._fonts = new List(); - } - - _fontCache.Clear(); - bool anyNullFont = false; - foreach (Font font in fontPack._fonts) - { - if (font == null) - { - anyNullFont = true; - } - _fontCache.Add(font); - } - - EditorGUILayout.Space(10); - EditorGUILayout.LabelField("Data Manipulation", EditorStyles.boldLabel); - - if (anyNullFont || _fontCache.Count != fontPack._fonts.Count) - { - if (GUILayout.Button("Fix Invalid Fonts", _impactButtonStyle)) - { - _fontCache.Clear(); - for (int i = fontPack._fonts.Count - 1; 0 <= i; --i) - { - Font font = fontPack._fonts[i]; - if (font != null && _fontCache.Add(font)) - { - continue; - } - - anyChanged = true; - fontPack._fonts.RemoveAt(i); - } - } - } - - if (0 < fontPack._fonts.Count) - { - EditorGUILayout.BeginHorizontal(); - try - { - _fontRemovalType = (FontType)EditorGUILayout.EnumFlagsField(_fontRemovalType); - if (GUILayout.Button("Remove Fonts Of Type", _impactButtonStyle)) - { - foreach (FontType fontType in Enum.GetValues(typeof(FontType))) - { - int removed = fontPack._fonts.RemoveAll(font => - Matches(font, fontType) - ); - anyChanged |= removed != 0; - } - } - } - finally - { - EditorGUILayout.EndHorizontal(); - } - } - - EditorGUILayout.BeginHorizontal(); - try - { - _fontAdditionType = (FontType)EditorGUILayout.EnumFlagsField(_fontAdditionType); - Object activeObject = Selection.activeObject; - if (activeObject == null) - { - activeObject = fontPack; - } - - string assetPath = AssetDatabase.GetAssetPath(activeObject); - string dataPath = Application.dataPath; - if ( - dataPath.EndsWith("/", StringComparison.OrdinalIgnoreCase) - || dataPath.EndsWith("\\", StringComparison.OrdinalIgnoreCase) - ) - { - dataPath = dataPath.Substring(0, dataPath.Length - 1); - } - if (dataPath.EndsWith("Assets", StringComparison.OrdinalIgnoreCase)) - { - dataPath = dataPath.Substring(0, dataPath.Length - "Assets".Length); - } - - if (!Directory.Exists(Path.Combine(dataPath, assetPath))) - { - assetPath = Path.GetDirectoryName(assetPath) ?? assetPath; - } - - assetPath = assetPath.Replace('\\', '/'); - - GUIContent loadFromCurrentDirectoryContent = new( - "Load From Current Directory", - $"Loads all fonts from '{assetPath}'" - ); - - if (GUILayout.Button(loadFromCurrentDirectoryContent)) - { - UpdateFromDirectory(assetPath); - } - else if (GUILayout.Button("Load From Directory (Select)")) - { - _lastSelectedDirectory = EditorUtility.OpenFolderPanel( - "Select Directory", - string.IsNullOrWhiteSpace(_lastSelectedDirectory) - ? Application.dataPath - : _lastSelectedDirectory, - string.Empty - ); - UpdateFromDirectory(_lastSelectedDirectory); - } - } - finally - { - EditorGUILayout.EndHorizontal(); - } - - if ( - anyChanged - || ( - !fontPack._fonts.IsSorted(UnityObjectNameComparer.Instance) - && GUILayout.Button("Sort Fonts") - ) - ) - { - fontPack._fonts.SortByName(); - EditorUtility.SetDirty(fontPack); - serializedObject.ApplyModifiedProperties(); - } - - return; - - void UpdateFromDirectory(string directory) - { - if (string.IsNullOrWhiteSpace(directory)) - { - return; - } - - DirectoryInfo directoryInfo = new(directory); - if (!directoryInfo.Exists) - { - return; - } - - directory = DirectoryHelper.AbsoluteToUnityRelativePath(directoryInfo.FullName); - - string[] fontGuids = AssetDatabase.FindAssets("t:Font", new[] { directory }); - foreach (string guid in fontGuids) - { - string assetPath = AssetDatabase.GUIDToAssetPath(guid); - - if (string.IsNullOrWhiteSpace(assetPath)) - { - continue; - } - - Font font = AssetDatabase.LoadAssetAtPath(assetPath); - if (Matches(font, _fontAdditionType) && _fontCache.Add(font)) - { - anyChanged = true; - fontPack._fonts.Add(font); - } - } - } - } - - private static bool Matches(Font font, FontType toCheck) - { - if (font == null) - { - return false; - } - - if (toCheck == FontType.None) - { - return false; - } - - foreach (FontType fontType in Enum.GetValues(typeof(FontType))) - { - if ((fontType & toCheck) == 0) - { - continue; - } - - if (font.name.EndsWith(fontType.ToString(), StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } - - return false; - } - } -#endif -} +namespace WallstopStudios.DxCommandTerminal.Editor.CustomEditors +{ +#if UNITY_EDITOR + using System; + using System.Collections.Generic; + using System.IO; + using DxCommandTerminal.Helper; + using Extensions; + using Themes; + using UnityEditor; + using UnityEngine; + using Object = UnityEngine.Object; + + [CustomEditor(typeof(TerminalFontPack))] + public sealed class TerminalFontPackEditor : Editor + { + [Flags] + private enum FontType + { + None = 0, + Normal = 1 << 0, + Bold = 1 << 1, + Italic = 1 << 2, + BoldItalic = 1 << 3, + ExtraBold = 1 << 4, + ExtraBoldItalic = 1 << 5, + ExtraLight = 1 << 6, + ExtraLightItalic = 1 << 7, + Light = 1 << 8, + LightItalic = 1 << 9, + Medium = 1 << 10, + MediumItalic = 1 << 11, + SemiBold = 1 << 12, + SemiBoldItalic = 1 << 13, + Thin = 1 << 14, + ThinItalic = 1 << 15, + Black = 1 << 16, + BlackItalic = 1 << 17, + Regular = 1 << 18, + Variable = 1 << 19, + Monospace = 1 << 20, + Condensed = 1 << 21, + CondensedBold = 1 << 22, + CondensedExtraBold = 1 << 23, + CondensedExtraLight = 1 << 24, + CondensedLight = 1 << 25, + CondensedMedium = 1 << 26, + CondensedSemiBold = 1 << 27, + CondensedThin = 1 << 28, + VariableFont_wght = 1 << 29, + VariableFont_width = 1 << 30, + } + + private readonly HashSet _fontCache = new(); + private FontType _fontRemovalType = FontType.None; + private FontType _fontAdditionType = FontType.None; + private string _lastSelectedDirectory; + private GUIStyle _impactButtonStyle; + + private void OnEnable() + { + _fontCache.Clear(); + _fontRemovalType = FontType.None; + } + + public override void OnInspectorGUI() + { + _impactButtonStyle ??= new GUIStyle(GUI.skin.button) + { + normal = { textColor = Color.yellow }, + fontStyle = FontStyle.Bold, + }; + + serializedObject.Update(); + TerminalFontPack fontPack = target as TerminalFontPack; + base.OnInspectorGUI(); + + if (fontPack == null) + { + return; + } + + bool anyChanged = false; + if (fontPack._fonts == null) + { + anyChanged = true; + fontPack._fonts = new List(); + } + + _fontCache.Clear(); + bool anyNullFont = false; + foreach (Font font in fontPack._fonts) + { + if (font == null) + { + anyNullFont = true; + } + _fontCache.Add(font); + } + + EditorGUILayout.Space(10); + EditorGUILayout.LabelField("Data Manipulation", EditorStyles.boldLabel); + + if (anyNullFont || _fontCache.Count != fontPack._fonts.Count) + { + if (GUILayout.Button("Fix Invalid Fonts", _impactButtonStyle)) + { + _fontCache.Clear(); + for (int i = fontPack._fonts.Count - 1; 0 <= i; --i) + { + Font font = fontPack._fonts[i]; + if (font != null && _fontCache.Add(font)) + { + continue; + } + + anyChanged = true; + fontPack._fonts.RemoveAt(i); + } + } + } + + if (0 < fontPack._fonts.Count) + { + EditorGUILayout.BeginHorizontal(); + try + { + _fontRemovalType = (FontType)EditorGUILayout.EnumFlagsField(_fontRemovalType); + if (GUILayout.Button("Remove Fonts Of Type", _impactButtonStyle)) + { + foreach (FontType fontType in Enum.GetValues(typeof(FontType))) + { + int removed = fontPack._fonts.RemoveAll(font => + Matches(font, fontType) + ); + anyChanged |= removed != 0; + } + } + } + finally + { + EditorGUILayout.EndHorizontal(); + } + } + + EditorGUILayout.BeginHorizontal(); + try + { + _fontAdditionType = (FontType)EditorGUILayout.EnumFlagsField(_fontAdditionType); + Object activeObject = Selection.activeObject; + if (activeObject == null) + { + activeObject = fontPack; + } + + string assetPath = AssetDatabase.GetAssetPath(activeObject); + string dataPath = Application.dataPath; + if ( + dataPath.EndsWith("/", StringComparison.OrdinalIgnoreCase) + || dataPath.EndsWith("\\", StringComparison.OrdinalIgnoreCase) + ) + { + dataPath = dataPath.Substring(0, dataPath.Length - 1); + } + if (dataPath.EndsWith("Assets", StringComparison.OrdinalIgnoreCase)) + { + dataPath = dataPath.Substring(0, dataPath.Length - "Assets".Length); + } + + if (!Directory.Exists(Path.Combine(dataPath, assetPath))) + { + assetPath = Path.GetDirectoryName(assetPath) ?? assetPath; + } + + assetPath = assetPath.Replace('\\', '/'); + + GUIContent loadFromCurrentDirectoryContent = new( + "Load From Current Directory", + $"Loads all fonts from '{assetPath}'" + ); + + if (GUILayout.Button(loadFromCurrentDirectoryContent)) + { + UpdateFromDirectory(assetPath); + } + else if (GUILayout.Button("Load From Directory (Select)")) + { + _lastSelectedDirectory = EditorUtility.OpenFolderPanel( + "Select Directory", + string.IsNullOrWhiteSpace(_lastSelectedDirectory) + ? Application.dataPath + : _lastSelectedDirectory, + string.Empty + ); + UpdateFromDirectory(_lastSelectedDirectory); + } + } + finally + { + EditorGUILayout.EndHorizontal(); + } + + if ( + anyChanged + || ( + !fontPack._fonts.IsSorted(UnityObjectNameComparer.Instance) + && GUILayout.Button("Sort Fonts") + ) + ) + { + fontPack._fonts.SortByName(); + EditorUtility.SetDirty(fontPack); + serializedObject.ApplyModifiedProperties(); + } + + return; + + void UpdateFromDirectory(string directory) + { + if (string.IsNullOrWhiteSpace(directory)) + { + return; + } + + DirectoryInfo directoryInfo = new(directory); + if (!directoryInfo.Exists) + { + return; + } + + directory = DirectoryHelper.AbsoluteToUnityRelativePath(directoryInfo.FullName); + + string[] fontGuids = AssetDatabase.FindAssets("t:Font", new[] { directory }); + foreach (string guid in fontGuids) + { + string assetPath = AssetDatabase.GUIDToAssetPath(guid); + + if (string.IsNullOrWhiteSpace(assetPath)) + { + continue; + } + + Font font = AssetDatabase.LoadAssetAtPath(assetPath); + if (Matches(font, _fontAdditionType) && _fontCache.Add(font)) + { + anyChanged = true; + fontPack._fonts.Add(font); + } + } + } + } + + private static bool Matches(Font font, FontType toCheck) + { + if (font == null) + { + return false; + } + + if (toCheck == FontType.None) + { + return false; + } + + foreach (FontType fontType in Enum.GetValues(typeof(FontType))) + { + if ((fontType & toCheck) == 0) + { + continue; + } + + if (font.name.EndsWith(fontType.ToString(), StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + + return false; + } + } +#endif +} diff --git a/Editor/CustomEditors/TerminalThemePackEditor.cs b/Editor/CustomEditors/TerminalThemePackEditor.cs index 0a72655..022531b 100644 --- a/Editor/CustomEditors/TerminalThemePackEditor.cs +++ b/Editor/CustomEditors/TerminalThemePackEditor.cs @@ -1,209 +1,220 @@ -namespace WallstopStudios.DxCommandTerminal.Editor.CustomEditors -{ -#if UNITY_EDITOR - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using DxCommandTerminal.Helper; - using Extensions; - using Helper; - using Themes; - using UnityEditor; - using UnityEngine; - using UnityEngine.UIElements; - using Object = UnityEngine.Object; - - [CustomEditor(typeof(TerminalThemePack))] - public sealed class TerminalThemePackEditor : Editor - { - private readonly HashSet _styleCache = new(); - private readonly HashSet _invalidStyles = new(); - private string _lastSelectedDirectory; - private GUIStyle _impactButtonStyle; - - private void OnEnable() - { - _styleCache.Clear(); - _invalidStyles.Clear(); - } - - public override void OnInspectorGUI() - { - _impactButtonStyle ??= new GUIStyle(GUI.skin.button) - { - normal = { textColor = Color.yellow }, - fontStyle = FontStyle.Bold, - }; - - serializedObject.Update(); - TerminalThemePack themePack = target as TerminalThemePack; - DrawPropertiesExcluding( - serializedObject, - "m_Script", - nameof(TerminalThemePack._themeNames) - ); - - if (themePack == null) - { - return; - } - - bool anyChanged = false; - if (themePack._themes == null) - { - anyChanged = true; - themePack._themes = new List(); - } - - _styleCache.Clear(); - _invalidStyles.Clear(); - bool anyInvalidTheme = false; - foreach (StyleSheet theme in themePack._themes) - { - if (theme == null) - { - anyInvalidTheme = true; - continue; - } - _styleCache.Add(theme); - if (!TerminalThemeStyleSheetHelper.GetAvailableThemes(theme).Any()) - { - _invalidStyles.Add(theme); - } - } - - EditorGUILayout.Space(10); - EditorGUILayout.LabelField("Data Manipulation", EditorStyles.boldLabel); - - if ( - anyInvalidTheme - || _styleCache.Count != themePack._themes.Count - || _invalidStyles.Any() - ) - { - if (GUILayout.Button("Fix Invalid Themes", _impactButtonStyle)) - { - anyChanged = true; - _styleCache.Clear(); - themePack._themes.RemoveAll(theme => theme == null || !_styleCache.Add(theme)); - } - } - - Object activeObject = Selection.activeObject; - if (activeObject == null) - { - activeObject = themePack; - } - - string assetPath = AssetDatabase.GetAssetPath(activeObject); - string dataPath = Application.dataPath; - if ( - dataPath.EndsWith("/", StringComparison.OrdinalIgnoreCase) - || dataPath.EndsWith("\\", StringComparison.OrdinalIgnoreCase) - ) - { - dataPath = dataPath.Substring(0, dataPath.Length - 1); - } - if (dataPath.EndsWith("Assets", StringComparison.OrdinalIgnoreCase)) - { - dataPath = dataPath.Substring(0, dataPath.Length - "Assets".Length); - } - - if (!Directory.Exists(Path.Combine(dataPath, assetPath))) - { - assetPath = Path.GetDirectoryName(assetPath) ?? assetPath; - } - - assetPath = assetPath.Replace('\\', '/'); - - GUIContent loadFromCurrentDirectoryContent = new( - "Load From Current Directory", - $"Loads all themes from '{assetPath}'" - ); - - if (GUILayout.Button(loadFromCurrentDirectoryContent)) - { - UpdateFromDirectory(assetPath); - } - else if (GUILayout.Button("Load From Directory (Select)")) - { - _lastSelectedDirectory = EditorUtility.OpenFolderPanel( - "Select Directory", - string.IsNullOrWhiteSpace(_lastSelectedDirectory) - ? Application.dataPath - : _lastSelectedDirectory, - string.Empty - ); - UpdateFromDirectory(_lastSelectedDirectory); - } - - if ( - anyChanged - || ( - !themePack._themes.IsSorted(UnityObjectNameComparer.Instance) - && GUILayout.Button("Sort Themes") - ) - || (themePack._themes.Count != themePack._themeNames.Count) - ) - { - SortThemes(); - EditorUtility.SetDirty(themePack); - serializedObject.ApplyModifiedProperties(); - } - - return; - - void SortThemes() - { - themePack._themes.SortByName(); - themePack._themeNames ??= new List(); - themePack._themeNames.Clear(); - themePack._themeNames.AddRange( - themePack._themes.SelectMany(TerminalThemeStyleSheetHelper.GetAvailableThemes) - ); - } - - void UpdateFromDirectory(string directory) - { - if (string.IsNullOrWhiteSpace(directory)) - { - return; - } - - DirectoryInfo directoryInfo = new(directory); - if (!directoryInfo.Exists) - { - return; - } - - directory = DirectoryHelper.AbsoluteToUnityRelativePath(directoryInfo.FullName); - - string[] fontGuids = AssetDatabase.FindAssets("t:StyleSheet", new[] { directory }); - foreach (string guid in fontGuids) - { - string styleAssetPath = AssetDatabase.GUIDToAssetPath(guid); - - if (string.IsNullOrWhiteSpace(styleAssetPath)) - { - continue; - } - - StyleSheet styleSheet = AssetDatabase.LoadAssetAtPath( - styleAssetPath - ); - if ( - styleSheet != null - && TerminalThemeStyleSheetHelper.GetAvailableThemes(styleSheet).Any() - && _styleCache.Add(styleSheet) - ) - { - anyChanged = true; - themePack._themes.Add(styleSheet); - } - } - } - } - } -#endif -} +namespace WallstopStudios.DxCommandTerminal.Editor.CustomEditors +{ +#if UNITY_EDITOR + using System; + using System.Collections.Generic; + using System.IO; + using DxCommandTerminal.Helper; + using Extensions; + using Helper; + using Themes; + using UnityEditor; + using UnityEngine; + using UnityEngine.UIElements; + using Object = UnityEngine.Object; + + [CustomEditor(typeof(TerminalThemePack))] + public sealed class TerminalThemePackEditor : Editor + { + private readonly HashSet _styleCache = new(); + private readonly HashSet _invalidStyles = new(); + private string _lastSelectedDirectory; + private GUIStyle _impactButtonStyle; + + private void OnEnable() + { + _styleCache.Clear(); + _invalidStyles.Clear(); + } + + public override void OnInspectorGUI() + { + _impactButtonStyle ??= new GUIStyle(GUI.skin.button) + { + normal = { textColor = Color.yellow }, + fontStyle = FontStyle.Bold, + }; + + serializedObject.Update(); + TerminalThemePack themePack = target as TerminalThemePack; + DrawPropertiesExcluding( + serializedObject, + "m_Script", + nameof(TerminalThemePack._themeNames) + ); + + if (themePack == null) + { + return; + } + + bool anyChanged = false; + if (themePack._themes == null) + { + anyChanged = true; + themePack._themes = new List(); + } + + _styleCache.Clear(); + _invalidStyles.Clear(); + bool anyInvalidTheme = false; + foreach (StyleSheet theme in themePack._themes) + { + if (theme == null) + { + anyInvalidTheme = true; + continue; + } + _styleCache.Add(theme); + string[] available = TerminalThemeStyleSheetHelper.GetAvailableThemes(theme); + if (available == null || available.Length == 0) + { + _invalidStyles.Add(theme); + } + } + + EditorGUILayout.Space(10); + EditorGUILayout.LabelField("Data Manipulation", EditorStyles.boldLabel); + + if ( + anyInvalidTheme + || _styleCache.Count != themePack._themes.Count + || _invalidStyles.Count != 0 + ) + { + if (GUILayout.Button("Fix Invalid Themes", _impactButtonStyle)) + { + anyChanged = true; + _styleCache.Clear(); + themePack._themes.RemoveAll(theme => theme == null || !_styleCache.Add(theme)); + } + } + + Object activeObject = Selection.activeObject; + if (activeObject == null) + { + activeObject = themePack; + } + + string assetPath = AssetDatabase.GetAssetPath(activeObject); + string dataPath = Application.dataPath; + if ( + dataPath.EndsWith("/", StringComparison.OrdinalIgnoreCase) + || dataPath.EndsWith("\\", StringComparison.OrdinalIgnoreCase) + ) + { + dataPath = dataPath.Substring(0, dataPath.Length - 1); + } + if (dataPath.EndsWith("Assets", StringComparison.OrdinalIgnoreCase)) + { + dataPath = dataPath.Substring(0, dataPath.Length - "Assets".Length); + } + + if (!Directory.Exists(Path.Combine(dataPath, assetPath))) + { + assetPath = Path.GetDirectoryName(assetPath) ?? assetPath; + } + + assetPath = assetPath.Replace('\\', '/'); + + GUIContent loadFromCurrentDirectoryContent = new( + "Load From Current Directory", + $"Loads all themes from '{assetPath}'" + ); + + if (GUILayout.Button(loadFromCurrentDirectoryContent)) + { + UpdateFromDirectory(assetPath); + } + else if (GUILayout.Button("Load From Directory (Select)")) + { + _lastSelectedDirectory = EditorUtility.OpenFolderPanel( + "Select Directory", + string.IsNullOrWhiteSpace(_lastSelectedDirectory) + ? Application.dataPath + : _lastSelectedDirectory, + string.Empty + ); + UpdateFromDirectory(_lastSelectedDirectory); + } + + if ( + anyChanged + || ( + !themePack._themes.IsSorted(UnityObjectNameComparer.Instance) + && GUILayout.Button("Sort Themes") + ) + || (themePack._themes.Count != themePack._themeNames.Count) + ) + { + SortThemes(); + EditorUtility.SetDirty(themePack); + serializedObject.ApplyModifiedProperties(); + } + + return; + + void SortThemes() + { + themePack._themes.SortByName(); + themePack._themeNames ??= new List(); + themePack._themeNames.Clear(); + foreach (StyleSheet style in themePack._themes) + { + string[] themes = TerminalThemeStyleSheetHelper.GetAvailableThemes(style); + if (themes == null) + { + continue; + } + for (int i = 0; i < themes.Length; ++i) + { + themePack._themeNames.Add(themes[i]); + } + } + } + + void UpdateFromDirectory(string directory) + { + if (string.IsNullOrWhiteSpace(directory)) + { + return; + } + + DirectoryInfo directoryInfo = new(directory); + if (!directoryInfo.Exists) + { + return; + } + + directory = DirectoryHelper.AbsoluteToUnityRelativePath(directoryInfo.FullName); + + string[] fontGuids = AssetDatabase.FindAssets("t:StyleSheet", new[] { directory }); + foreach (string guid in fontGuids) + { + string styleAssetPath = AssetDatabase.GUIDToAssetPath(guid); + + if (string.IsNullOrWhiteSpace(styleAssetPath)) + { + continue; + } + + StyleSheet styleSheet = AssetDatabase.LoadAssetAtPath( + styleAssetPath + ); + if (styleSheet != null) + { + string[] themes = TerminalThemeStyleSheetHelper.GetAvailableThemes( + styleSheet + ); + if ((themes != null && themes.Length > 0) && _styleCache.Add(styleSheet)) + { + anyChanged = true; + themePack._themes.Add(styleSheet); + } + } + } + } + } + } +#endif +} diff --git a/Editor/CustomEditors/TerminalUIEditor.cs b/Editor/CustomEditors/TerminalUIEditor.cs index 07f3e24..7729910 100644 --- a/Editor/CustomEditors/TerminalUIEditor.cs +++ b/Editor/CustomEditors/TerminalUIEditor.cs @@ -1,1359 +1,1620 @@ -namespace WallstopStudios.DxCommandTerminal.Editor.CustomEditors -{ -#if UNITY_EDITOR - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.IO; - using System.Linq; - using Backend; - using DxCommandTerminal.Helper; - using UnityEditor; - using UnityEngine; - using UnityEngine.UIElements; - using Input; - using Persistence; - using Themes; - using UI; - using Object = UnityEngine.Object; -#if ENABLE_INPUT_SYSTEM - using UnityEngine.InputSystem; -#endif - - [InitializeOnLoad] - [CustomEditor(typeof(TerminalUI))] - public sealed class TerminalUIEditor : Editor - { - private static readonly TimeSpan CycleInterval = TimeSpan.FromSeconds(0.75); - - private int _commandIndex; - private TerminalUI _lastSeen; - - private readonly HashSet _allCommands = new(StringComparer.OrdinalIgnoreCase); - private readonly HashSet _defaultCommands = new(StringComparer.OrdinalIgnoreCase); - private readonly HashSet _nonDefaultCommands = new( - StringComparer.OrdinalIgnoreCase - ); - private readonly HashSet _seenCommands = new(StringComparer.OrdinalIgnoreCase); - private readonly SortedSet _intermediateResults = new( - StringComparer.OrdinalIgnoreCase - ); - private readonly SortedDictionary> _fontsByPrefix = - new(StringComparer.OrdinalIgnoreCase); - - private readonly Dictionary _seenLogTypes = new(); - private readonly List _fontPacks = new(); - private readonly List _themePacks = new(); - - private int _themeIndex = -1; - private int _fontKey = -1; - private int _secondFontKey = -1; - private int _themePackIndex = -1; - private int _fontPackIndex = -1; - private bool _isCyclingThemes; - private bool _isCyclingFonts; - private bool _persistThemeChanges; - - private TimeSpan? _lastFontCycleTime; - private TimeSpan? _lastThemeCycleTime; - - private readonly Stopwatch _timer = Stopwatch.StartNew(); - - private bool _editorUpdateAttached; - private GUIStyle _impactButtonStyle; - private GUIStyle _impactLabelStyle; - - static TerminalUIEditor() - { - ObjectFactory.componentWasAdded += HandleComponentAdded; - } - - private static void HandleComponentAdded(Component addedComponent) - { - bool anyChange = false; - if (addedComponent is TerminalUI terminal && terminal != null) - { - CheckForUIDocumentProblems(terminal); - - if (terminal._themePack == null) - { - TerminalThemePack[] themePacks = LoadAll(); - int themePackIndex = Array.FindIndex( - themePacks, - themePack => - string.Equals( - themePack.name, - "Minimal", - StringComparison.OrdinalIgnoreCase - ) - ); - if (themePackIndex < 0) - { - themePackIndex = themePacks.Length - 1; - } - - if (0 <= themePackIndex && themePackIndex < themePacks.Length) - { - TerminalThemePack themePack = themePacks[themePackIndex]; - terminal._themePack = themePack; - _ = TrySetupDefaultTheme(terminal); - } - } - - if (terminal._fontPack == null) - { - TerminalFontPack[] fontPacks = LoadAll(); - int fontPackIndex = Array.FindIndex( - fontPacks, - fontPack => - string.Equals( - fontPack.name, - "Minimal", - StringComparison.OrdinalIgnoreCase - ) - ); - if (fontPackIndex < 0) - { - fontPackIndex = fontPacks.Length - 1; - } - - if (0 <= fontPackIndex && fontPackIndex < fontPacks.Length) - { - TerminalFontPack fontPack = fontPacks[fontPackIndex]; - terminal._fontPack = fontPack; - _ = TrySetupDefaultFont(terminal); - } - } - - terminal.gameObject.AddComponent(); - terminal.gameObject.AddComponent(); - - EditorUtility.SetDirty(terminal); - EditorUtility.SetDirty(terminal.gameObject); - anyChange = true; - } - else - { - switch (addedComponent) - { - case TerminalKeyboardController keyboardController - when keyboardController != null: - { - if (keyboardController.TryGetComponent(out terminal)) - { - keyboardController.terminal = terminal; - EditorUtility.SetDirty(keyboardController); - anyChange = true; - } - - break; - } - case TerminalThemePersister themePersister when themePersister != null: - { - if (themePersister.TryGetComponent(out terminal)) - { - themePersister.terminal = terminal; - EditorUtility.SetDirty(themePersister); - anyChange = true; - } - - break; - } -#if ENABLE_INPUT_SYSTEM - case PlayerInput playerInput when playerInput != null: - { - if (playerInput.TryGetComponent(out terminal)) - { - if ( - !playerInput.TryGetComponent( - out TerminalPlayerInputController playerInputController - ) - ) - { - playerInputController = - playerInput.gameObject.AddComponent(); - playerInputController.terminal = terminal; - EditorUtility.SetDirty(playerInputController); - EditorUtility.SetDirty(playerInput.gameObject); - anyChange = true; - } - - if ( - playerInput.TryGetComponent( - out TerminalKeyboardController keyboardController - ) - ) - { - keyboardController.enabled = false; - EditorUtility.SetDirty(keyboardController); - anyChange = true; - } - } - - break; - } -#endif - } - } - - if (anyChange) - { - AssetDatabase.SaveAssets(); - } - } - - private void OnEnable() - { - _allCommands.Clear(); - _allCommands.UnionWith( - CommandShell - .RegisteredCommands.Value.Select(tuple => tuple.attribute) - .Select(attribute => attribute.Name) - ); - _defaultCommands.Clear(); - _defaultCommands.UnionWith( - CommandShell - .RegisteredCommands.Value.Select(tuple => tuple.attribute) - .Where(tuple => tuple.Default) - .Select(attribute => attribute.Name) - ); - _nonDefaultCommands.Clear(); - _nonDefaultCommands.UnionWith( - CommandShell - .RegisteredCommands.Value.Select(tuple => tuple.attribute) - .Where(tuple => !tuple.Default) - .Select(attribute => attribute.Name) - ); - _fontsByPrefix.Clear(); - - ResetStateIdempotent(force: true); - - if (!_editorUpdateAttached) - { - EditorApplication.update += EditorUpdate; - _editorUpdateAttached = true; - } - } - - private static T[] LoadAll() - where T : Object - { - List directories = new(); - - string directory = DirectoryHelper.GetCallerScriptDirectory(); - if (!string.IsNullOrWhiteSpace(directory)) - { - DirectoryInfo directoryInfo = new(directory); - if (directoryInfo.Exists) - { - directory = DirectoryHelper.FindPackageRootPath(directory); - directory = DirectoryHelper.AbsoluteToUnityRelativePath(directory); - directories.Add(directory); - } - } - - if (Directory.Exists(Path.Combine(Application.dataPath, "Packages"))) - { - directories.Add("Packages"); - } - - if (Directory.Exists(Path.Combine(Application.dataPath, "Library"))) - { - directories.Add("Library"); - } - - directories.Add("Assets"); - - HashSet unique = new(); - List ordered = new(); - string[] assetGuids = AssetDatabase.FindAssets( - $"t:{typeof(T).Name}", - directories.ToArray() - ); - foreach (string guid in assetGuids) - { - string assetPath = AssetDatabase.GUIDToAssetPath(guid); - - if (string.IsNullOrWhiteSpace(assetPath)) - { - continue; - } - - T item = AssetDatabase.LoadAssetAtPath(assetPath); - if (item != null && unique.Add(item)) - { - ordered.Add(item); - } - } - return ordered.ToArray(); - } - - private void OnDisable() - { - if (_editorUpdateAttached) - { - EditorApplication.update -= EditorUpdate; - _editorUpdateAttached = false; - } - } - - private void ResetStateIdempotent(bool force) - { - foreach (TerminalThemePack themePack in TerminalAssetPackPostProcessor.NewThemePacks) - { - _themePacks.Add(themePack); - } - TerminalAssetPackPostProcessor.NewThemePacks.Clear(); - foreach (TerminalFontPack fontPack in TerminalAssetPackPostProcessor.NewFontPacks) - { - _fontPacks.Add(fontPack); - } - TerminalAssetPackPostProcessor.NewFontPacks.Clear(); - - if (!_fontPacks.Any()) - { - _fontPacks.Clear(); - _fontPacks.AddRange(LoadAll()); - } - - if (!_themePacks.Any()) - { - _themePacks.Clear(); - _themePacks.AddRange(LoadAll()); - } - - TerminalUI terminal = target as TerminalUI; - if (!force && _lastSeen == terminal) - { - return; - } - - _fontsByPrefix.Clear(); - CollectFonts(terminal, _fontsByPrefix); - - _persistThemeChanges = false; - StopCyclingFonts(); - StopCyclingThemes(); - _themeIndex = -1; - _fontKey = -1; - _secondFontKey = -1; - _lastSeen = terminal; - } - - private void EditorUpdate() - { - if (_isCyclingThemes) - { - if (_timer.Elapsed < _lastThemeCycleTime + CycleInterval) - { - return; - } - - TerminalUI terminal = target as TerminalUI; - if (terminal != null && terminal._themePack._themeNames is { Count: > 0 }) - { - int newThemeIndex = (_themeIndex + 1) % terminal._themePack._themeNames.Count; - newThemeIndex = - (newThemeIndex + terminal._themePack._themeNames.Count) - % terminal._themePack._themeNames.Count; - terminal.SetTheme( - terminal._themePack._themeNames[newThemeIndex], - persist: _persistThemeChanges - ); - _themeIndex = newThemeIndex; - } - - _lastThemeCycleTime = _timer.Elapsed; - } - - if (_isCyclingFonts) - { - if (_timer.Elapsed < _lastFontCycleTime + CycleInterval) - { - return; - } - - TerminalUI terminal = target as TerminalUI; - if (terminal != null && terminal._fontPack._fonts is { Count: > 0 }) - { - Font currentFont = GetCurrentlySelectedFont(terminal); - int fontIndex = terminal._fontPack._fonts.IndexOf(currentFont); - int newFontIndex = (fontIndex + 1) % terminal._fontPack._fonts.Count; - Font newFont = terminal._fontPack._fonts[newFontIndex]; - terminal.SetFont(newFont, persist: _persistThemeChanges); - TrySetFontKeysFromFont(newFont); - } - - _lastFontCycleTime = _timer.Elapsed; - } - } - - private Font GetCurrentlySelectedFont(TerminalUI terminal) - { - if (_fontKey < 0 || _secondFontKey < 0) - { - return terminal.CurrentFont; - } - - try - { - return _fontsByPrefix.ToArray()[_fontKey].Value.ToArray()[_secondFontKey].Value; - } - catch - { - return terminal.CurrentFont; - } - } - - public override void OnInspectorGUI() - { - _impactButtonStyle ??= new GUIStyle(GUI.skin.button) - { - normal = { textColor = Color.yellow }, - fontStyle = FontStyle.Bold, - }; - _impactLabelStyle ??= new GUIStyle(GUI.skin.label) - { - normal = { textColor = new Color(1f, 0.3f, 0.3f, 1f) }, - fontStyle = FontStyle.Bold, - }; - - if (_allCommands.Count == 0 || _defaultCommands.Count == 0) - { - HydrateCommandCaches(); - } - - TerminalUI terminal = target as TerminalUI; - if (terminal == null) - { - return; - } - - serializedObject.Update(); - ResetStateIdempotent(force: false); - - bool anyChanged = false; - - bool uiDocumentChanged = CheckForUIDocumentProblems(terminal); - anyChanged |= uiDocumentChanged; - - bool themesChanged = CheckForThemingAndFontChanges(terminal); - anyChanged |= themesChanged; - - RenderCyclingPreviews(); - - DrawPropertiesExcluding( - serializedObject, - "m_Script", - nameof(TerminalUI._themePack), - nameof(TerminalUI._fontPack), - nameof(TerminalUI._persistedTheme), - nameof(TerminalUI._uiDocument) - ); - - bool propertiesDirty = CheckForSimpleProperties(terminal); - anyChanged |= propertiesDirty; - - RenderCommandManipulationHeader(); - - bool ignoredCommandsUpdated = CheckForIgnoredCommandUpdates(terminal); - anyChanged |= ignoredCommandsUpdated; - - bool commandsUpdated = CheckForDisabledCommandProblems(terminal); - anyChanged |= commandsUpdated; - - serializedObject.ApplyModifiedProperties(); - if (anyChanged) - { - EditorUtility.SetDirty(terminal); - } - } - - private void HydrateCommandCaches() - { - _allCommands.Clear(); - _allCommands.UnionWith( - CommandShell - .RegisteredCommands.Value.Select(tuple => tuple.attribute) - .Select(attribute => attribute.Name) - ); - _defaultCommands.Clear(); - _defaultCommands.UnionWith( - CommandShell - .RegisteredCommands.Value.Select(tuple => tuple.attribute) - .Where(tuple => tuple.Default) - .Select(attribute => attribute.Name) - ); - _nonDefaultCommands.Clear(); - _nonDefaultCommands.UnionWith( - CommandShell - .RegisteredCommands.Value.Select(tuple => tuple.attribute) - .Where(tuple => !tuple.Default) - .Select(attribute => attribute.Name) - ); - } - - private void RenderCyclingPreviews() - { - EditorGUILayout.Space(10); - EditorGUILayout.BeginHorizontal(); - try - { - EditorGUILayout.LabelField("Preview", EditorStyles.boldLabel); - bool oldPersistThemeChanges = _persistThemeChanges; - _persistThemeChanges = GUILayout.Toggle( - _persistThemeChanges, - "Persist Theme Changes" - ); - - if (oldPersistThemeChanges != _persistThemeChanges) - { - StopCyclingFonts(); - StopCyclingThemes(); - } - } - finally - { - EditorGUILayout.EndHorizontal(); - } - - if (Application.isPlaying) - { - EditorGUILayout.BeginHorizontal(); - try - { - TryCyclingThemes(); - TryCyclingFonts(); - } - finally - { - EditorGUILayout.EndHorizontal(); - } - } - - EditorGUILayout.BeginHorizontal(); - try - { - TrySetRandomTheme(); - TrySetRandomFont(); - } - finally - { - EditorGUILayout.EndHorizontal(); - } - - EditorGUILayout.Space(); - } - - private void TrySetRandomTheme() - { - if (_isCyclingThemes) - { - return; - } - - bool clicked = _persistThemeChanges - ? GUILayout.Button("Set Random Theme", _impactButtonStyle) - : GUILayout.Button("Set Random Theme "); - - if (clicked) - { - TerminalUI terminal = target as TerminalUI; - if ( - terminal != null - && terminal._themePack != null - && terminal._themePack._themeNames is { Count: > 0 } - ) - { - int newThemeIndex; - do - { - newThemeIndex = ThreadLocalRandom.Instance.Next( - terminal._themePack._themeNames.Count - ); - } while ( - newThemeIndex == _themeIndex && terminal._themePack._themeNames.Count != 1 - ); - terminal.SetTheme( - terminal._themePack._themeNames[newThemeIndex], - persist: _persistThemeChanges - ); - _themeIndex = newThemeIndex; - } - } - } - - private void TrySetRandomFont() - { - if (_isCyclingFonts) - { - return; - } - - bool clicked = _persistThemeChanges - ? GUILayout.Button("Set Random Font", _impactButtonStyle) - : GUILayout.Button("Set Random Font "); - - if (clicked) - { - TerminalUI terminal = target as TerminalUI; - if ( - terminal != null - && terminal._fontPack != null - && terminal._fontPack._fonts is { Count: > 0 } - ) - { - Font currentlySelectedFont = GetCurrentlySelectedFont(terminal); - int oldFontIndex = terminal._fontPack._fonts.IndexOf(currentlySelectedFont); - int newFontIndex; - do - { - newFontIndex = ThreadLocalRandom.Instance.Next( - terminal._fontPack._fonts.Count - ); - } while (newFontIndex == oldFontIndex && terminal._fontPack._fonts.Count != 1); - - Font newFont = terminal._fontPack._fonts[newFontIndex]; - terminal.SetFont(newFont, persist: _persistThemeChanges); - TrySetFontKeysFromFont(newFont); - } - } - } - - private void TryCyclingFonts() - { - if (_isCyclingFonts) - { - if (GUILayout.Button("Stop Cycling Fonts")) - { - StopCyclingFonts(); - } - } - else - { - bool clicked = _persistThemeChanges - ? GUILayout.Button("Start Cycling Fonts", _impactButtonStyle) - : GUILayout.Button("Start Cycling Fonts"); - if (clicked) - { - StartCyclingFonts(); - } - } - } - - private void StartCyclingFonts() - { - if (_isCyclingFonts) - { - return; - } - _isCyclingFonts = true; - _lastFontCycleTime = null; - } - - private void StopCyclingFonts() - { - _isCyclingFonts = false; - } - - private void TryCyclingThemes() - { - if (_isCyclingThemes) - { - if (GUILayout.Button("Stop Cycling Themes")) - { - StopCyclingThemes(); - } - } - else - { - bool clicked = _persistThemeChanges - ? GUILayout.Button("Start Cycling Themes", _impactButtonStyle) - : GUILayout.Button("Start Cycling Themes"); - if (clicked) - { - StartCyclingThemes(); - } - } - } - - private void StartCyclingThemes() - { - if (_isCyclingThemes) - { - return; - } - - _isCyclingThemes = true; - _lastThemeCycleTime = null; - } - - private void StopCyclingThemes() - { - _isCyclingThemes = false; - } - - private void TrySetFontKeysFromFont(Font font) - { - int firstIndex = 0; - bool foundFont = false; - foreach ( - KeyValuePair> fontKeyEntry in _fontsByPrefix - ) - { - int secondIndex = 0; - foreach (KeyValuePair secondFontKeyEntry in fontKeyEntry.Value) - { - if (secondFontKeyEntry.Value == font) - { - _fontKey = firstIndex; - _secondFontKey = secondIndex; - foundFont = true; - break; - } - - ++secondIndex; - } - - if (foundFont) - { - break; - } - - ++firstIndex; - } - } - - private bool CheckForThemingAndFontChanges(TerminalUI terminal) - { - bool anyChanged = false; - EditorGUILayout.Space(10); - EditorGUILayout.LabelField("Pack Selection", EditorStyles.boldLabel); - - EditorGUILayout.BeginHorizontal(); - try - { - if (!_themePacks.Any()) - { - GUILayout.Label("NO THEME PACKS", _impactLabelStyle); - } - else - { - if (_themePackIndex < 0) - { - _themePackIndex = _themePacks.IndexOf(terminal._themePack); - } - - if (_themePackIndex < 0) - { - GUILayout.Label("Select Theme Pack:"); - } - - _themePackIndex = EditorGUILayout.Popup( - _themePackIndex, - _themePacks.Select(themePack => themePack.name).ToArray() - ); - if (0 <= _themePackIndex && _themePackIndex < _themePacks.Count) - { - TerminalThemePack themePack = _themePacks[_themePackIndex]; - bool clicked = - themePack != terminal._themePack - ? GUILayout.Button("Set Theme Pack", _impactButtonStyle) - : GUILayout.Button("Set Theme Pack"); - if (clicked) - { - if (themePack != terminal._themePack) - { - terminal._themePack = themePack; - _themeIndex = themePack._themeNames.IndexOf(terminal.CurrentTheme); - if (_themeIndex < 0) - { - terminal._persistedTheme = string.Empty; - } - - anyChanged = true; - } - } - } - } - } - finally - { - EditorGUILayout.EndHorizontal(); - } - - EditorGUILayout.BeginHorizontal(); - try - { - if (!_fontPacks.Any()) - { - GUILayout.Label("NO FONT PACKS", _impactLabelStyle); - } - else - { - if (_fontPackIndex < 0) - { - _fontPackIndex = _fontPacks.IndexOf(terminal._fontPack); - } - - if (_fontPackIndex < 0) - { - GUILayout.Label("Select Font Pack:"); - } - - _fontPackIndex = EditorGUILayout.Popup( - _fontPackIndex, - _fontPacks.Select(fontPack => fontPack.name).ToArray() - ); - if (0 <= _fontPackIndex && _fontPackIndex < _fontPacks.Count) - { - TerminalFontPack fontPack = _fontPacks[_fontPackIndex]; - bool clicked = - fontPack != terminal._fontPack - ? GUILayout.Button("Set Font Pack", _impactButtonStyle) - : GUILayout.Button("Set Font Pack"); - if (clicked) - { - if (fontPack != terminal._fontPack) - { - _fontsByPrefix.Clear(); - _fontKey = -1; - _secondFontKey = -1; - terminal._fontPack = fontPack; - if (!terminal._fontPack._fonts.Contains(terminal.CurrentFont)) - { - terminal._persistedFont = null; - } - - anyChanged = true; - } - } - } - } - } - finally - { - EditorGUILayout.EndHorizontal(); - } - - EditorGUILayout.Space(10); - EditorGUILayout.LabelField("Theming", EditorStyles.boldLabel); - - EditorGUILayout.BeginHorizontal(); - try - { - if (terminal._themePack != null) - { - if (_themeIndex < 0) - { - _themeIndex = terminal._themePack._themeNames.IndexOf( - terminal.CurrentTheme - ); - } - - if (_themeIndex < 0) - { - GUILayout.Label("Select Theme:"); - } - - _themeIndex = EditorGUILayout.Popup( - _themeIndex, - terminal - ._themePack._themeNames.Select(theme => - theme - .Replace( - "-theme", - string.Empty, - StringComparison.OrdinalIgnoreCase - ) - .Replace( - "theme-", - string.Empty, - StringComparison.OrdinalIgnoreCase - ) - ) - .ToArray() - ); - - if (0 <= _themeIndex && _themeIndex < terminal._themePack._themeNames.Count) - { - string selectedTheme = terminal._themePack._themeNames[_themeIndex]; - GUIContent setThemeContent = new( - "Set Theme", - $"Will set the current theme to {selectedTheme}" - ); - bool clicked = !string.Equals( - selectedTheme, - terminal._persistedTheme, - StringComparison.OrdinalIgnoreCase - ) - ? GUILayout.Button(setThemeContent, _impactButtonStyle) - : GUILayout.Button(setThemeContent); - if (clicked) - { - terminal.SetTheme(selectedTheme, persist: true); - anyChanged = true; - } - } - } - } - finally - { - EditorGUILayout.EndHorizontal(); - } - - CollectFonts(terminal, _fontsByPrefix); - bool fontsUpdated = RenderSelectableFonts(terminal); - return anyChanged || fontsUpdated; - } - - private bool CheckForSimpleProperties(TerminalUI terminal) - { - bool anyChanged = false; - - if (terminal._ignoredLogTypes == null) - { - terminal._ignoredLogTypes = new List(); - anyChanged = true; - } - - if (terminal._disabledCommands == null) - { - terminal._disabledCommands = new List(); - anyChanged = true; - } - - _seenLogTypes.Clear(); - for (int i = terminal._ignoredLogTypes.Count - 1; 0 <= i; --i) - { - TerminalLogType logType = terminal._ignoredLogTypes[i]; - int count = 0; - if ( - Enum.IsDefined(typeof(TerminalLogType), logType) - && (!_seenLogTypes.TryGetValue(logType, out count) || count <= 1) - ) - { - _seenLogTypes[logType] = count + 1; - continue; - } - - _seenLogTypes[logType] = count + 1; - anyChanged = true; - terminal._ignoredLogTypes.RemoveAt(i); - } - - return anyChanged; - } - - private static bool CheckForUIDocumentProblems(TerminalUI terminal) - { - bool anyChanged = false; - if (terminal._uiDocument == null) - { - terminal._uiDocument = terminal.TryGetComponent(out UIDocument uiDocument) - ? uiDocument - : terminal.gameObject.AddComponent(); - anyChanged = true; - } - - if (terminal._uiDocument.panelSettings != null) - { - return anyChanged; - } - - string[] panelSettingGuids; - string absoluteStylesPath = DirectoryHelper.FindAbsolutePathToDirectory("Styles"); - if (!string.IsNullOrWhiteSpace(absoluteStylesPath)) - { - panelSettingGuids = AssetDatabase.FindAssets( - "t:PanelSettings", - new[] { absoluteStylesPath } - ); - TryFindTerminalSettings(); - if (terminal._uiDocument.panelSettings != null) - { - return true; - } - } - - List directories = new(); - if (Directory.Exists(Path.Combine(Application.dataPath, "Library"))) - { - directories.Add("Library"); - } - - if (Directory.Exists(Path.Combine(Application.dataPath, "Packages"))) - { - directories.Add("Packages"); - } - - directories.Add("Assets"); - panelSettingGuids = AssetDatabase.FindAssets("t:PanelSettings", directories.ToArray()); - TryFindTerminalSettings(); - return anyChanged; - - void TryFindTerminalSettings() - { - foreach (string guid in panelSettingGuids) - { - string assetPath = AssetDatabase.GUIDToAssetPath(guid); - if (string.IsNullOrWhiteSpace(assetPath)) - { - continue; - } - - if (!assetPath.Contains("TerminalSettings", StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - PanelSettings panelSettings = AssetDatabase.LoadAssetAtPath( - assetPath - ); - - if (panelSettings == null) - { - continue; - } - - terminal._uiDocument.panelSettings = panelSettings; - anyChanged = true; - return; - } - } - } - - private static void RenderCommandManipulationHeader() - { - EditorGUILayout.Space(); - EditorGUILayout.Space(10); - EditorGUILayout.LabelField("Command Manipulation", EditorStyles.boldLabel); - } - - private bool CheckForIgnoredCommandUpdates(TerminalUI terminal) - { - bool anyChanged = false; - _intermediateResults.Clear(); - _intermediateResults.UnionWith(_nonDefaultCommands); - if (!terminal.ignoreDefaultCommands) - { - _intermediateResults.UnionWith(_defaultCommands); - } - _intermediateResults.ExceptWith(terminal._disabledCommands); - - if (0 < _intermediateResults.Count) - { - string[] ignorableCommands = _intermediateResults.ToArray(); - - EditorGUILayout.BeginHorizontal(); - try - { - _commandIndex = EditorGUILayout.Popup(_commandIndex, ignorableCommands); - - if (0 <= _commandIndex && _commandIndex < ignorableCommands.Length) - { - GUIContent ignoreContent = new( - "Ignore Command", - $"Ignores the {ignorableCommands[_commandIndex]} command" - ); - if (GUILayout.Button(ignoreContent)) - { - string command = ignorableCommands[_commandIndex]; - terminal._disabledCommands.Add(command); - anyChanged = true; - } - } - } - finally - { - EditorGUILayout.EndHorizontal(); - } - } - - return anyChanged; - } - - private bool CheckForDisabledCommandProblems(TerminalUI terminal) - { - bool anyChanged = false; - _seenCommands.Clear(); - _seenCommands.UnionWith(terminal._disabledCommands); - - if ( - _seenCommands.Count != terminal._disabledCommands.Count - || terminal._disabledCommands.Exists(command => !_allCommands.Contains(command)) - ) - { - EditorGUILayout.BeginHorizontal(); - try - { - GUILayout.FlexibleSpace(); - if (GUILayout.Button("Cleanup Disabled Commands")) - { - _seenCommands.Clear(); - for (int i = terminal._disabledCommands.Count - 1; 0 <= i; --i) - { - string command = terminal._disabledCommands[i]; - if (!_seenCommands.Add(command)) - { - terminal._disabledCommands.RemoveAt(i); - anyChanged = true; - continue; - } - - if (!_allCommands.Contains(command)) - { - terminal._disabledCommands.RemoveAt(i); - anyChanged = true; - } - } - } - GUILayout.FlexibleSpace(); - } - finally - { - EditorGUILayout.EndHorizontal(); - } - } - - return anyChanged; - } - - private static void CollectFonts( - TerminalUI terminal, - SortedDictionary> fontsByPrefix - ) - { - if ( - terminal == null - || terminal._fontPack == null - || terminal._fontPack._fonts is not { Count: > 0 } - ) - { - return; - } - - if (fontsByPrefix.Count != 0) - { - return; - } - - foreach (Font font in terminal._fontPack._fonts) - { - string fontName = font.name; - int indexOfSplit = fontName.IndexOf('-', StringComparison.OrdinalIgnoreCase); - if (indexOfSplit < 0) - { - indexOfSplit = fontName.IndexOf('_', StringComparison.OrdinalIgnoreCase); - } - - string key; - string secondKey; - if (0 <= indexOfSplit) - { - key = fontName[..indexOfSplit]; - secondKey = fontName[Mathf.Min(indexOfSplit + 1, fontName.Length)..]; - } - else - { - key = fontName; - secondKey = string.Empty; - } - - if (!fontsByPrefix.TryGetValue(key, out SortedDictionary fontMapping)) - { - fontMapping = new SortedDictionary( - StringComparer.OrdinalIgnoreCase - ); - fontsByPrefix[key] = fontMapping; - } - - fontMapping[secondKey] = font; - } - } - - private void TryMatchExistingFont(TerminalUI terminal) - { - if (0 <= _fontKey || 0 <= _secondFontKey || terminal.CurrentFont == null) - { - return; - } - - TrySetFontKeysFromFont(terminal.CurrentFont); - } - - private static bool TrySetupDefaultTheme(TerminalUI terminal) - { - if ( - !string.IsNullOrWhiteSpace(terminal.CurrentTheme) - && terminal._themePack != null - && terminal._themePack._themeNames.Contains( - terminal.CurrentTheme, - StringComparer.OrdinalIgnoreCase - ) - ) - { - return false; - } - - if (terminal._themePack == null || terminal._themePack._themeNames.Count == 0) - { - return false; - } - - string defaultTheme = terminal._themePack._themeNames.FirstOrDefault(theme => - theme.Contains("Dark", StringComparison.OrdinalIgnoreCase) - ); - if (string.IsNullOrWhiteSpace(defaultTheme)) - { - defaultTheme = terminal._themePack._themeNames.FirstOrDefault(theme => - theme.Contains("Light", StringComparison.OrdinalIgnoreCase) - ); - } - - if (string.IsNullOrWhiteSpace(defaultTheme)) - { - defaultTheme = terminal._themePack._themeNames.FirstOrDefault(); - } - - terminal.SetTheme(defaultTheme, persist: true); - return true; - } - - private static bool TrySetupDefaultFont(TerminalUI terminal) - { - if ( - terminal.CurrentFont != null - && terminal._fontPack != null - && terminal._fontPack._fonts.Contains(terminal.CurrentFont) - ) - { - return false; - } - - if (terminal._fontPack == null || terminal._fontPack._fonts is not { Count: > 0 }) - { - return false; - } - - Font defaultFont = terminal._fontPack._fonts.FirstOrDefault(font => - font.name.Contains("SourceCodePro", StringComparison.OrdinalIgnoreCase) - && font.name.Contains("Regular", StringComparison.OrdinalIgnoreCase) - ); - if (defaultFont == null) - { - defaultFont = terminal._fontPack._fonts.FirstOrDefault(font => - font.name.Contains("Mono", StringComparison.OrdinalIgnoreCase) - && font.name.Contains("Regular", StringComparison.OrdinalIgnoreCase) - ); - } - if (defaultFont == null) - { - defaultFont = terminal._fontPack._fonts.FirstOrDefault(font => - font.name.Contains("Mono", StringComparison.OrdinalIgnoreCase) - ); - } - if (defaultFont == null) - { - defaultFont = terminal._fontPack._fonts.FirstOrDefault(font => - font.name.Contains("Regular", StringComparison.OrdinalIgnoreCase) - ); - } - if (defaultFont == null) - { - defaultFont = terminal._fontPack._fonts.FirstOrDefault(); - } - - terminal.SetFont(defaultFont, persist: true); - return true; - } - - private bool RenderSelectableFonts(TerminalUI terminal) - { - if (_fontsByPrefix is not { Count: > 0 }) - { - return false; - } - - TryMatchExistingFont(terminal); - - bool anyChanged = false; - int currentFontKey = _fontKey; - EditorGUILayout.BeginHorizontal(); - try - { - if (terminal._fontPack != null) - { - if (_fontKey < 0 || _secondFontKey < 0) - { - GUILayout.Label("Select Font:"); - } - - string[] fontKeys = _fontsByPrefix.Keys.ToArray(); - _fontKey = EditorGUILayout.Popup(_fontKey, fontKeys); - - if (currentFontKey != _fontKey) - { - _secondFontKey = -1; - } - - if (0 <= _fontKey && _fontKey < fontKeys.Length) - { - string selectedFontKey = fontKeys[_fontKey]; - SortedDictionary availableFonts = _fontsByPrefix[ - selectedFontKey - ]; - string[] secondFontKeys = availableFonts.Keys.ToArray(); - Font selectedFont = null; - switch (secondFontKeys.Length) - { - case > 1: - { - _secondFontKey = EditorGUILayout.Popup( - _secondFontKey, - secondFontKeys - ); - - if (0 <= _secondFontKey && _secondFontKey < secondFontKeys.Length) - { - selectedFont = availableFonts[secondFontKeys[_secondFontKey]]; - } - - break; - } - case 1: - { - selectedFont = availableFonts.Values.Single(); - break; - } - } - - if (selectedFont != null) - { - GUIContent setFontContent = new( - "Set Font", - $"Update the terminal's font to {selectedFont.name}" - ); - bool clicked = - selectedFont != terminal._persistedFont - ? GUILayout.Button(setFontContent, _impactButtonStyle) - : GUILayout.Button(setFontContent); - if (clicked) - { - terminal.SetFont(selectedFont, persist: true); - anyChanged = true; - } - } - } - } - } - finally - { - GUILayout.EndHorizontal(); - } - - return anyChanged; - } - } -#endif -} +namespace WallstopStudios.DxCommandTerminal.Editor.CustomEditors +{ +#if UNITY_EDITOR + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.IO; + using System.Linq; + using System.Reflection; + using Attributes; + using Backend; + using DxCommandTerminal.Helper; + using UnityEditor; + using UnityEngine; + using UnityEngine.UIElements; + using Input; + using Persistence; + using Themes; + using UI; + using Service; + using Object = UnityEngine.Object; +#if ENABLE_INPUT_SYSTEM + using UnityEngine.InputSystem; +#endif + + [InitializeOnLoad] + [CustomEditor(typeof(TerminalUI))] + public sealed class TerminalUIEditor : Editor + { + private static readonly TimeSpan CycleInterval = TimeSpan.FromSeconds(0.75); + private const string ResourcesRoot = "Assets/Resources"; + private const string SettingsAssetPath = + ResourcesRoot + "/TerminalServiceBindingSettings.asset"; + private const string DefaultBindingAssetPath = + ResourcesRoot + "/TerminalServiceBinding.asset"; + private int _commandIndex; + private TerminalUI _lastSeen; + + private readonly HashSet _allCommands = new(StringComparer.OrdinalIgnoreCase); + private readonly HashSet _defaultCommands = new(StringComparer.OrdinalIgnoreCase); + private readonly HashSet _nonDefaultCommands = new( + StringComparer.OrdinalIgnoreCase + ); + private readonly HashSet _seenCommands = new(StringComparer.OrdinalIgnoreCase); + private readonly SortedSet _intermediateResults = new( + StringComparer.OrdinalIgnoreCase + ); + private readonly SortedDictionary> _fontsByPrefix = + new(StringComparer.OrdinalIgnoreCase); + + private readonly Dictionary _seenLogTypes = new(); + private readonly List _fontPacks = new(); + private readonly List _themePacks = new(); + + private int _themeIndex = -1; + private int _fontKey = -1; + private int _secondFontKey = -1; + private int _themePackIndex = -1; + private int _fontPackIndex = -1; + private bool _isCyclingThemes; + private bool _isCyclingFonts; + private bool _persistThemeChanges; + + private TimeSpan? _lastFontCycleTime; + private TimeSpan? _lastThemeCycleTime; + + private readonly Stopwatch _timer = Stopwatch.StartNew(); + + private bool _editorUpdateAttached; + private GUIStyle _impactButtonStyle; + private GUIStyle _impactLabelStyle; + + static TerminalUIEditor() + { + ObjectFactory.componentWasAdded += HandleComponentAdded; + } + + private static void HandleComponentAdded(Component addedComponent) + { + bool anyChange = false; + if (addedComponent is TerminalUI terminal && terminal != null) + { + CheckForUIDocumentProblems(terminal); + + if (terminal._themePack == null) + { + TerminalThemePack[] themePacks = LoadAll(); + int themePackIndex = Array.FindIndex( + themePacks, + themePack => + string.Equals( + themePack.name, + "Minimal", + StringComparison.OrdinalIgnoreCase + ) + ); + if (themePackIndex < 0) + { + themePackIndex = themePacks.Length - 1; + } + + if (0 <= themePackIndex && themePackIndex < themePacks.Length) + { + TerminalThemePack themePack = themePacks[themePackIndex]; + terminal._themePack = themePack; + _ = TrySetupDefaultTheme(terminal); + } + } + + if (terminal._fontPack == null) + { + TerminalFontPack[] fontPacks = LoadAll(); + int fontPackIndex = Array.FindIndex( + fontPacks, + fontPack => + string.Equals( + fontPack.name, + "Minimal", + StringComparison.OrdinalIgnoreCase + ) + ); + if (fontPackIndex < 0) + { + fontPackIndex = fontPacks.Length - 1; + } + + if (0 <= fontPackIndex && fontPackIndex < fontPacks.Length) + { + TerminalFontPack fontPack = fontPacks[fontPackIndex]; + terminal._fontPack = fontPack; + _ = TrySetupDefaultFont(terminal); + } + } + + if (EnsureServiceBindingAssigned(terminal)) + { + anyChange = true; + } + + terminal.gameObject.AddComponent(); + terminal.gameObject.AddComponent(); + + EditorUtility.SetDirty(terminal); + EditorUtility.SetDirty(terminal.gameObject); + anyChange = true; + } + else + { + switch (addedComponent) + { + case TerminalKeyboardController keyboardController + when keyboardController != null: + { + if (keyboardController.TryGetComponent(out terminal)) + { + keyboardController.terminal = terminal; + EditorUtility.SetDirty(keyboardController); + anyChange = true; + } + + break; + } + case TerminalThemePersister themePersister when themePersister != null: + { + if (themePersister.TryGetComponent(out terminal)) + { + themePersister.terminal = terminal; + EditorUtility.SetDirty(themePersister); + anyChange = true; + } + + break; + } +#if ENABLE_INPUT_SYSTEM + case PlayerInput playerInput when playerInput != null: + { + if (playerInput.TryGetComponent(out terminal)) + { + if ( + !playerInput.TryGetComponent( + out TerminalPlayerInputController playerInputController + ) + ) + { + playerInputController = + playerInput.gameObject.AddComponent(); + playerInputController.terminal = terminal; + EditorUtility.SetDirty(playerInputController); + EditorUtility.SetDirty(playerInput.gameObject); + anyChange = true; + } + + if ( + playerInput.TryGetComponent( + out TerminalKeyboardController keyboardController + ) + ) + { + keyboardController.enabled = false; + EditorUtility.SetDirty(keyboardController); + anyChange = true; + } + } + + break; + } +#endif + } + } + + if (anyChange) + { + AssetDatabase.SaveAssets(); + } + } + + private void OnEnable() + { + _allCommands.Clear(); + _defaultCommands.Clear(); + _nonDefaultCommands.Clear(); + (MethodInfo method, RegisterCommandAttribute attribute)[] reg = CommandShell + .RegisteredCommands + .Value; + for (int i = 0; i < reg.Length; ++i) + { + RegisterCommandAttribute attr = reg[i].attribute; + if (attr == null || string.IsNullOrWhiteSpace(attr.Name)) + { + continue; + } + _allCommands.Add(attr.Name); + if (attr.Default) + { + _defaultCommands.Add(attr.Name); + } + else + { + _nonDefaultCommands.Add(attr.Name); + } + } + _fontsByPrefix.Clear(); + + ResetStateIdempotent(force: true); + + if (!_editorUpdateAttached) + { + EditorApplication.update += EditorUpdate; + _editorUpdateAttached = true; + } + } + + private static T[] LoadAll() + where T : Object + { + List directories = new(); + + string directory = DirectoryHelper.GetCallerScriptDirectory(); + if (!string.IsNullOrWhiteSpace(directory)) + { + DirectoryInfo directoryInfo = new(directory); + if (directoryInfo.Exists) + { + directory = DirectoryHelper.FindPackageRootPath(directory); + directory = DirectoryHelper.AbsoluteToUnityRelativePath(directory); + directories.Add(directory); + } + } + + string projectRoot = Path.GetDirectoryName(Application.dataPath); + if (!string.IsNullOrWhiteSpace(projectRoot)) + { + string packagesAbsolute = Path.Combine(projectRoot, "Packages"); + if (Directory.Exists(packagesAbsolute)) + { + directories.Add("Packages"); + } + + string libraryAbsolute = Path.Combine(projectRoot, "Library"); + if (Directory.Exists(libraryAbsolute)) + { + directories.Add("Library"); + } + } + + directories.Add("Assets"); + + HashSet unique = new(); + List ordered = new(); + string[] assetGuids = AssetDatabase.FindAssets( + $"t:{typeof(T).Name}", + directories.ToArray() + ); + foreach (string guid in assetGuids) + { + string assetPath = AssetDatabase.GUIDToAssetPath(guid); + + if (string.IsNullOrWhiteSpace(assetPath)) + { + continue; + } + + T item = AssetDatabase.LoadAssetAtPath(assetPath); + if (item != null && unique.Add(item)) + { + ordered.Add(item); + } + } + return ordered.ToArray(); + } + + private void OnDisable() + { + if (_editorUpdateAttached) + { + EditorApplication.update -= EditorUpdate; + _editorUpdateAttached = false; + } + } + + private void ResetStateIdempotent(bool force) + { + foreach (TerminalThemePack themePack in TerminalAssetPackPostProcessor.NewThemePacks) + { + _themePacks.Add(themePack); + } + TerminalAssetPackPostProcessor.NewThemePacks.Clear(); + foreach (TerminalFontPack fontPack in TerminalAssetPackPostProcessor.NewFontPacks) + { + _fontPacks.Add(fontPack); + } + TerminalAssetPackPostProcessor.NewFontPacks.Clear(); + + if (_fontPacks.Count == 0) + { + _fontPacks.Clear(); + _fontPacks.AddRange(LoadAll()); + } + + if (_themePacks.Count == 0) + { + _themePacks.Clear(); + _themePacks.AddRange(LoadAll()); + } + + TerminalUI terminal = target as TerminalUI; + if (!force && _lastSeen == terminal) + { + return; + } + + _fontsByPrefix.Clear(); + CollectFonts(terminal, _fontsByPrefix); + + _persistThemeChanges = false; + StopCyclingFonts(); + StopCyclingThemes(); + _themeIndex = -1; + _fontKey = -1; + _secondFontKey = -1; + _lastSeen = terminal; + } + + private void EditorUpdate() + { + if (_isCyclingThemes) + { + if (_timer.Elapsed < _lastThemeCycleTime + CycleInterval) + { + return; + } + + TerminalUI terminal = target as TerminalUI; + if (terminal != null && terminal._themePack._themeNames is { Count: > 0 }) + { + int newThemeIndex = (_themeIndex + 1) % terminal._themePack._themeNames.Count; + newThemeIndex = + (newThemeIndex + terminal._themePack._themeNames.Count) + % terminal._themePack._themeNames.Count; + terminal.SetTheme( + terminal._themePack._themeNames[newThemeIndex], + persist: _persistThemeChanges + ); + _themeIndex = newThemeIndex; + } + + _lastThemeCycleTime = _timer.Elapsed; + } + + if (_isCyclingFonts) + { + if (_timer.Elapsed < _lastFontCycleTime + CycleInterval) + { + return; + } + + TerminalUI terminal = target as TerminalUI; + if (terminal != null && terminal._fontPack._fonts is { Count: > 0 }) + { + Font currentFont = GetCurrentlySelectedFont(terminal); + int fontIndex = terminal._fontPack._fonts.IndexOf(currentFont); + int newFontIndex = (fontIndex + 1) % terminal._fontPack._fonts.Count; + Font newFont = terminal._fontPack._fonts[newFontIndex]; + terminal.SetFont(newFont, persist: _persistThemeChanges); + TrySetFontKeysFromFont(newFont); + } + + _lastFontCycleTime = _timer.Elapsed; + } + } + + private Font GetCurrentlySelectedFont(TerminalUI terminal) + { + if (_fontKey < 0 || _secondFontKey < 0) + { + return terminal.CurrentFont; + } + + try + { + int i = 0; + foreach ( + KeyValuePair> outer in _fontsByPrefix + ) + { + if (i++ != _fontKey) + { + continue; + } + int j = 0; + foreach (KeyValuePair inner in outer.Value) + { + if (j++ == _secondFontKey) + { + return inner.Value; + } + } + break; + } + return terminal.CurrentFont; + } + catch + { + return terminal.CurrentFont; + } + } + + private bool RenderServiceBindingSummary(TerminalUI terminal) + { + bool changed = false; + SerializedProperty bindingAssetProperty = serializedObject.FindProperty( + nameof(TerminalUI._serviceBindingAsset) + ); + + EditorGUILayout.LabelField("Service Binding", EditorStyles.boldLabel); + using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) + { + EditorGUILayout.LabelField( + "Active Source", + DescribeServiceBindingSource(terminal), + EditorStyles.miniLabel + ); + + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField( + bindingAssetProperty, + new GUIContent("Binding Asset", "Optional asset providing service overrides.") + ); + if (EditorGUI.EndChangeCheck()) + { + changed = true; + } + + if (bindingAssetProperty.objectReferenceValue != null) + { + using (new EditorGUILayout.HorizontalScope()) + { + GUILayout.Space(EditorGUIUtility.labelWidth); + if (GUILayout.Button("Select Asset", GUILayout.ExpandWidth(false))) + { + Selection.activeObject = bindingAssetProperty.objectReferenceValue; + } + } + } + + TerminalServiceBindingComponent component = + terminal._serviceBindingComponent + ?? terminal.GetComponent(); + + EditorGUI.BeginDisabledGroup(true); + EditorGUILayout.ObjectField( + "Binding Component", + component, + typeof(TerminalServiceBindingComponent), + true + ); + EditorGUI.EndDisabledGroup(); + + using (new EditorGUILayout.HorizontalScope()) + { + GUILayout.Space(EditorGUIUtility.labelWidth); + if (component == null) + { + if (GUILayout.Button("Add Component", GUILayout.ExpandWidth(false))) + { + if (EnsureServiceBindingAssigned(terminal)) + { + serializedObject.Update(); + component = + terminal._serviceBindingComponent + ?? terminal.GetComponent(); + changed = true; + } + } + } + else + { + if (GUILayout.Button("Select Component", GUILayout.ExpandWidth(false))) + { + Selection.activeObject = component; + } + } + } + + TerminalServiceBindingAsset projectDefault = + TerminalServiceBindingSettings.DefaultBinding; + + EditorGUI.BeginDisabledGroup(true); + EditorGUILayout.ObjectField( + "Project Default", + projectDefault, + typeof(TerminalServiceBindingAsset), + false + ); + EditorGUI.EndDisabledGroup(); + + using (new EditorGUILayout.HorizontalScope()) + { + GUILayout.Space(EditorGUIUtility.labelWidth); + if (projectDefault != null) + { + if ( + GUILayout.Button("Select Project Default", GUILayout.ExpandWidth(false)) + ) + { + Selection.activeObject = projectDefault; + } + } + else if ( + GUILayout.Button("Create Project Default", GUILayout.ExpandWidth(false)) + ) + { + projectDefault = EnsureProjectDefaultBindingAssets(); + Selection.activeObject = projectDefault; + changed = true; + } + } + } + + EditorGUILayout.Space(); + return changed; + } + + private static string DescribeServiceBindingSource(TerminalUI terminal) + { + if (terminal._serviceBindingAsset != null) + { + return $"Asset: {terminal._serviceBindingAsset.name}"; + } + + if ( + terminal._serviceBindingComponent != null + || terminal.GetComponent() != null + ) + { + return "Component Overrides"; + } + + if (TerminalServiceBindingSettings.DefaultBinding != null) + { + return $"Project Default: {TerminalServiceBindingSettings.DefaultBinding.name}"; + } + + return "Runtime Defaults"; + } + + private static TerminalServiceBindingAsset EnsureProjectDefaultBindingAssets() + { + EnsureResourcesFolderExists(); + + TerminalServiceBindingAsset binding = + AssetDatabase.LoadAssetAtPath(DefaultBindingAssetPath); + if (binding == null) + { + binding = ScriptableObject.CreateInstance(); + AssetDatabase.CreateAsset(binding, DefaultBindingAssetPath); + } + + TerminalServiceBindingSettings settings = + AssetDatabase.LoadAssetAtPath(SettingsAssetPath); + if (settings == null) + { + settings = ScriptableObject.CreateInstance(); + AssetDatabase.CreateAsset(settings, SettingsAssetPath); + } + + settings.SetDefaultBinding(binding); + EditorUtility.SetDirty(settings); + AssetDatabase.SaveAssets(); + return binding; + } + + private static void EnsureResourcesFolderExists() + { + if (AssetDatabase.IsValidFolder(ResourcesRoot)) + { + return; + } + + string[] segments = ResourcesRoot.Split('/'); + string currentPath = segments[0]; + for (int i = 1; i < segments.Length; ++i) + { + string nextPath = $"{currentPath}/{segments[i]}"; + if (!AssetDatabase.IsValidFolder(nextPath)) + { + AssetDatabase.CreateFolder(currentPath, segments[i]); + } + currentPath = nextPath; + } + } + + public override void OnInspectorGUI() + { + _impactButtonStyle ??= new GUIStyle(GUI.skin.button) + { + normal = { textColor = Color.yellow }, + fontStyle = FontStyle.Bold, + }; + _impactLabelStyle ??= new GUIStyle(GUI.skin.label) + { + normal = { textColor = new Color(1f, 0.3f, 0.3f, 1f) }, + fontStyle = FontStyle.Bold, + }; + + if (_allCommands.Count == 0 || _defaultCommands.Count == 0) + { + HydrateCommandCaches(); + } + + TerminalUI terminal = target as TerminalUI; + if (terminal == null) + { + return; + } + + serializedObject.Update(); + ResetStateIdempotent(force: false); + + bool anyChanged = false; + + bool uiDocumentChanged = CheckForUIDocumentProblems(terminal); + anyChanged |= uiDocumentChanged; + + bool themesChanged = CheckForThemingAndFontChanges(terminal); + anyChanged |= themesChanged; + + RenderCyclingPreviews(); + + anyChanged |= RenderServiceBindingSummary(terminal); + + DrawPropertiesExcluding( + serializedObject, + "m_Script", + nameof(TerminalUI._themePack), + nameof(TerminalUI._fontPack), + nameof(TerminalUI._persistedTheme), + nameof(TerminalUI._uiDocument), + nameof(TerminalUI._serviceBindingAsset), + nameof(TerminalUI._serviceBindingComponent) + ); + + bool propertiesDirty = CheckForSimpleProperties(terminal); + anyChanged |= propertiesDirty; + + RenderCommandManipulationHeader(); + + bool ignoredCommandsUpdated = CheckForIgnoredCommandUpdates(terminal); + anyChanged |= ignoredCommandsUpdated; + + bool commandsUpdated = CheckForDisabledCommandProblems(terminal); + anyChanged |= commandsUpdated; + + serializedObject.ApplyModifiedProperties(); + if (anyChanged) + { + EditorUtility.SetDirty(terminal); + } + } + + private void HydrateCommandCaches() + { + _allCommands.Clear(); + _defaultCommands.Clear(); + _nonDefaultCommands.Clear(); + (MethodInfo method, RegisterCommandAttribute attribute)[] reg = CommandShell + .RegisteredCommands + .Value; + for (int i = 0; i < reg.Length; ++i) + { + RegisterCommandAttribute attr = reg[i].attribute; + if (attr == null || string.IsNullOrWhiteSpace(attr.Name)) + { + continue; + } + _allCommands.Add(attr.Name); + if (attr.Default) + { + _defaultCommands.Add(attr.Name); + } + else + { + _nonDefaultCommands.Add(attr.Name); + } + } + } + + private void RenderCyclingPreviews() + { + EditorGUILayout.Space(10); + EditorGUILayout.BeginHorizontal(); + try + { + EditorGUILayout.LabelField("Preview", EditorStyles.boldLabel); + bool oldPersistThemeChanges = _persistThemeChanges; + _persistThemeChanges = GUILayout.Toggle( + _persistThemeChanges, + "Persist Theme Changes" + ); + + if (oldPersistThemeChanges != _persistThemeChanges) + { + StopCyclingFonts(); + StopCyclingThemes(); + } + } + finally + { + EditorGUILayout.EndHorizontal(); + } + + if (Application.isPlaying) + { + EditorGUILayout.BeginHorizontal(); + try + { + TryCyclingThemes(); + TryCyclingFonts(); + } + finally + { + EditorGUILayout.EndHorizontal(); + } + } + + EditorGUILayout.BeginHorizontal(); + try + { + TrySetRandomTheme(); + TrySetRandomFont(); + } + finally + { + EditorGUILayout.EndHorizontal(); + } + + EditorGUILayout.Space(); + } + + private void TrySetRandomTheme() + { + if (_isCyclingThemes) + { + return; + } + + bool clicked = _persistThemeChanges + ? GUILayout.Button("Set Random Theme", _impactButtonStyle) + : GUILayout.Button("Set Random Theme "); + + if (clicked) + { + TerminalUI terminal = target as TerminalUI; + if ( + terminal != null + && terminal._themePack != null + && terminal._themePack._themeNames is { Count: > 0 } + ) + { + int newThemeIndex; + do + { + newThemeIndex = ThreadLocalRandom.Instance.Next( + terminal._themePack._themeNames.Count + ); + } while ( + newThemeIndex == _themeIndex && terminal._themePack._themeNames.Count != 1 + ); + terminal.SetTheme( + terminal._themePack._themeNames[newThemeIndex], + persist: _persistThemeChanges + ); + _themeIndex = newThemeIndex; + } + } + } + + private void TrySetRandomFont() + { + if (_isCyclingFonts) + { + return; + } + + bool clicked = _persistThemeChanges + ? GUILayout.Button("Set Random Font", _impactButtonStyle) + : GUILayout.Button("Set Random Font "); + + if (clicked) + { + TerminalUI terminal = target as TerminalUI; + if ( + terminal != null + && terminal._fontPack != null + && terminal._fontPack._fonts is { Count: > 0 } + ) + { + Font currentlySelectedFont = GetCurrentlySelectedFont(terminal); + int oldFontIndex = terminal._fontPack._fonts.IndexOf(currentlySelectedFont); + int newFontIndex; + do + { + newFontIndex = ThreadLocalRandom.Instance.Next( + terminal._fontPack._fonts.Count + ); + } while (newFontIndex == oldFontIndex && terminal._fontPack._fonts.Count != 1); + + Font newFont = terminal._fontPack._fonts[newFontIndex]; + terminal.SetFont(newFont, persist: _persistThemeChanges); + TrySetFontKeysFromFont(newFont); + } + } + } + + private void TryCyclingFonts() + { + if (_isCyclingFonts) + { + if (GUILayout.Button("Stop Cycling Fonts")) + { + StopCyclingFonts(); + } + } + else + { + bool clicked = _persistThemeChanges + ? GUILayout.Button("Start Cycling Fonts", _impactButtonStyle) + : GUILayout.Button("Start Cycling Fonts"); + if (clicked) + { + StartCyclingFonts(); + } + } + } + + private void StartCyclingFonts() + { + if (_isCyclingFonts) + { + return; + } + _isCyclingFonts = true; + _lastFontCycleTime = null; + } + + private void StopCyclingFonts() + { + _isCyclingFonts = false; + } + + private void TryCyclingThemes() + { + if (_isCyclingThemes) + { + if (GUILayout.Button("Stop Cycling Themes")) + { + StopCyclingThemes(); + } + } + else + { + bool clicked = _persistThemeChanges + ? GUILayout.Button("Start Cycling Themes", _impactButtonStyle) + : GUILayout.Button("Start Cycling Themes"); + if (clicked) + { + StartCyclingThemes(); + } + } + } + + private void StartCyclingThemes() + { + if (_isCyclingThemes) + { + return; + } + + _isCyclingThemes = true; + _lastThemeCycleTime = null; + } + + private void StopCyclingThemes() + { + _isCyclingThemes = false; + } + + private void TrySetFontKeysFromFont(Font font) + { + int firstIndex = 0; + bool foundFont = false; + foreach ( + KeyValuePair> fontKeyEntry in _fontsByPrefix + ) + { + int secondIndex = 0; + foreach (KeyValuePair secondFontKeyEntry in fontKeyEntry.Value) + { + if (secondFontKeyEntry.Value == font) + { + _fontKey = firstIndex; + _secondFontKey = secondIndex; + foundFont = true; + break; + } + + ++secondIndex; + } + + if (foundFont) + { + break; + } + + ++firstIndex; + } + } + + private bool CheckForThemingAndFontChanges(TerminalUI terminal) + { + bool anyChanged = false; + EditorGUILayout.Space(10); + EditorGUILayout.LabelField("Pack Selection", EditorStyles.boldLabel); + + EditorGUILayout.BeginHorizontal(); + try + { + if (_themePacks.Count == 0) + { + GUILayout.Label("NO THEME PACKS", _impactLabelStyle); + } + else + { + if (_themePackIndex < 0) + { + _themePackIndex = _themePacks.IndexOf(terminal._themePack); + } + + if (_themePackIndex < 0) + { + GUILayout.Label("Select Theme Pack:"); + } + + string[] themePackNames = new string[_themePacks.Count]; + for (int i = 0; i < _themePacks.Count; ++i) + { + themePackNames[i] = + _themePacks[i] != null ? _themePacks[i].name : string.Empty; + } + _themePackIndex = EditorGUILayout.Popup(_themePackIndex, themePackNames); + if (0 <= _themePackIndex && _themePackIndex < _themePacks.Count) + { + TerminalThemePack themePack = _themePacks[_themePackIndex]; + bool clicked = + themePack != terminal._themePack + ? GUILayout.Button("Set Theme Pack", _impactButtonStyle) + : GUILayout.Button("Set Theme Pack"); + if (clicked) + { + if (themePack != terminal._themePack) + { + terminal._themePack = themePack; + _themeIndex = themePack._themeNames.IndexOf(terminal.CurrentTheme); + if (_themeIndex < 0) + { + terminal._persistedTheme = string.Empty; + } + + anyChanged = true; + } + } + } + } + } + finally + { + EditorGUILayout.EndHorizontal(); + } + + EditorGUILayout.BeginHorizontal(); + try + { + if (_fontPacks.Count == 0) + { + GUILayout.Label("NO FONT PACKS", _impactLabelStyle); + } + else + { + if (_fontPackIndex < 0) + { + _fontPackIndex = _fontPacks.IndexOf(terminal._fontPack); + } + + if (_fontPackIndex < 0) + { + GUILayout.Label("Select Font Pack:"); + } + + string[] fontPackNames = new string[_fontPacks.Count]; + for (int i = 0; i < _fontPacks.Count; ++i) + { + fontPackNames[i] = + _fontPacks[i] != null ? _fontPacks[i].name : string.Empty; + } + _fontPackIndex = EditorGUILayout.Popup(_fontPackIndex, fontPackNames); + if (0 <= _fontPackIndex && _fontPackIndex < _fontPacks.Count) + { + TerminalFontPack fontPack = _fontPacks[_fontPackIndex]; + bool clicked = + fontPack != terminal._fontPack + ? GUILayout.Button("Set Font Pack", _impactButtonStyle) + : GUILayout.Button("Set Font Pack"); + if (clicked) + { + if (fontPack != terminal._fontPack) + { + _fontsByPrefix.Clear(); + _fontKey = -1; + _secondFontKey = -1; + terminal._fontPack = fontPack; + if (!terminal._fontPack._fonts.Contains(terminal.CurrentFont)) + { + terminal._persistedFont = null; + } + + anyChanged = true; + } + } + } + } + } + finally + { + EditorGUILayout.EndHorizontal(); + } + + EditorGUILayout.Space(10); + EditorGUILayout.LabelField("Theming", EditorStyles.boldLabel); + + EditorGUILayout.BeginHorizontal(); + try + { + if (terminal._themePack != null) + { + if (_themeIndex < 0) + { + _themeIndex = terminal._themePack._themeNames.IndexOf( + terminal.CurrentTheme + ); + } + + if (_themeIndex < 0) + { + GUILayout.Label("Select Theme:"); + } + + string[] themeOptions = new string[terminal._themePack._themeNames.Count]; + for (int i = 0; i < themeOptions.Length; ++i) + { + string t = terminal._themePack._themeNames[i] ?? string.Empty; + t = t.Replace("-theme", string.Empty, StringComparison.OrdinalIgnoreCase); + t = t.Replace("theme-", string.Empty, StringComparison.OrdinalIgnoreCase); + themeOptions[i] = t; + } + _themeIndex = EditorGUILayout.Popup(_themeIndex, themeOptions); + + if (0 <= _themeIndex && _themeIndex < terminal._themePack._themeNames.Count) + { + string selectedTheme = terminal._themePack._themeNames[_themeIndex]; + GUIContent setThemeContent = new( + "Set Theme", + $"Will set the current theme to {selectedTheme}" + ); + bool clicked = !string.Equals( + selectedTheme, + terminal._persistedTheme, + StringComparison.OrdinalIgnoreCase + ) + ? GUILayout.Button(setThemeContent, _impactButtonStyle) + : GUILayout.Button(setThemeContent); + if (clicked) + { + terminal.SetTheme(selectedTheme, persist: true); + anyChanged = true; + } + } + } + } + finally + { + EditorGUILayout.EndHorizontal(); + } + + CollectFonts(terminal, _fontsByPrefix); + bool fontsUpdated = RenderSelectableFonts(terminal); + return anyChanged || fontsUpdated; + } + + private bool CheckForSimpleProperties(TerminalUI terminal) + { + bool anyChanged = false; + + if (terminal._blockedLogTypes == null) + { + terminal._blockedLogTypes = new List(); + anyChanged = true; + } + + if (terminal._blockedCommands == null) + { + terminal._blockedCommands = new List(); + anyChanged = true; + } + + _seenLogTypes.Clear(); + for (int i = terminal._blockedLogTypes.Count - 1; 0 <= i; --i) + { + TerminalLogType logType = terminal._blockedLogTypes[i]; + int count = 0; + if ( + Enum.IsDefined(typeof(TerminalLogType), logType) + && (!_seenLogTypes.TryGetValue(logType, out count) || count <= 1) + ) + { + _seenLogTypes[logType] = count + 1; + continue; + } + + _seenLogTypes[logType] = count + 1; + anyChanged = true; + terminal._blockedLogTypes.RemoveAt(i); + } + + return anyChanged; + } + + private static bool CheckForUIDocumentProblems(TerminalUI terminal) + { + bool anyChanged = false; + if (terminal._uiDocument == null) + { + terminal._uiDocument = terminal.TryGetComponent(out UIDocument uiDocument) + ? uiDocument + : terminal.gameObject.AddComponent(); + anyChanged = true; + } + + if (terminal._uiDocument.panelSettings != null) + { + return anyChanged; + } + + string[] panelSettingGuids; + string absoluteStylesPath = DirectoryHelper.FindAbsolutePathToDirectory("Styles"); + if (!string.IsNullOrWhiteSpace(absoluteStylesPath)) + { + panelSettingGuids = AssetDatabase.FindAssets( + "t:PanelSettings", + new[] { absoluteStylesPath } + ); + TryFindTerminalSettings(); + if (terminal._uiDocument.panelSettings != null) + { + return true; + } + } + + List directories = new(); + string projectRoot = Path.GetDirectoryName(Application.dataPath); + if (!string.IsNullOrWhiteSpace(projectRoot)) + { + string libraryAbsolute = Path.Combine(projectRoot, "Library"); + if (Directory.Exists(libraryAbsolute)) + { + directories.Add("Library"); + } + + string packagesAbsolute = Path.Combine(projectRoot, "Packages"); + if (Directory.Exists(packagesAbsolute)) + { + directories.Add("Packages"); + } + } + + directories.Add("Assets"); + panelSettingGuids = AssetDatabase.FindAssets("t:PanelSettings", directories.ToArray()); + TryFindTerminalSettings(); + return anyChanged; + + void TryFindTerminalSettings() + { + foreach (string guid in panelSettingGuids) + { + string assetPath = AssetDatabase.GUIDToAssetPath(guid); + if (string.IsNullOrWhiteSpace(assetPath)) + { + continue; + } + + if (!assetPath.Contains("TerminalSettings", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + PanelSettings panelSettings = AssetDatabase.LoadAssetAtPath( + assetPath + ); + + if (panelSettings == null) + { + continue; + } + + terminal._uiDocument.panelSettings = panelSettings; + anyChanged = true; + return; + } + } + } + + private static void RenderCommandManipulationHeader() + { + EditorGUILayout.Space(); + EditorGUILayout.Space(10); + EditorGUILayout.LabelField("Command Manipulation", EditorStyles.boldLabel); + } + + private bool CheckForIgnoredCommandUpdates(TerminalUI terminal) + { + bool anyChanged = false; + _intermediateResults.Clear(); + _intermediateResults.UnionWith(_nonDefaultCommands); + if (!terminal.ignoreDefaultCommands) + { + _intermediateResults.UnionWith(_defaultCommands); + } + _intermediateResults.ExceptWith(terminal._blockedCommands); + + if (0 < _intermediateResults.Count) + { + string[] ignorableCommands = _intermediateResults.ToArray(); + + EditorGUILayout.BeginHorizontal(); + try + { + _commandIndex = EditorGUILayout.Popup(_commandIndex, ignorableCommands); + + if (0 <= _commandIndex && _commandIndex < ignorableCommands.Length) + { + GUIContent ignoreContent = new( + "Ignore Command", + $"Ignores the {ignorableCommands[_commandIndex]} command" + ); + if (GUILayout.Button(ignoreContent)) + { + string command = ignorableCommands[_commandIndex]; + terminal._blockedCommands.Add(command); + anyChanged = true; + } + } + } + finally + { + EditorGUILayout.EndHorizontal(); + } + } + + return anyChanged; + } + + private bool CheckForDisabledCommandProblems(TerminalUI terminal) + { + bool anyChanged = false; + _seenCommands.Clear(); + _seenCommands.UnionWith(terminal._blockedCommands); + + if ( + _seenCommands.Count != terminal._blockedCommands.Count + || terminal._blockedCommands.Exists(command => !_allCommands.Contains(command)) + ) + { + EditorGUILayout.BeginHorizontal(); + try + { + GUILayout.FlexibleSpace(); + if (GUILayout.Button("Cleanup Disabled Commands")) + { + _seenCommands.Clear(); + for (int i = terminal._blockedCommands.Count - 1; 0 <= i; --i) + { + string command = terminal._blockedCommands[i]; + if (!_seenCommands.Add(command)) + { + terminal._blockedCommands.RemoveAt(i); + anyChanged = true; + continue; + } + + if (!_allCommands.Contains(command)) + { + terminal._blockedCommands.RemoveAt(i); + anyChanged = true; + } + } + } + GUILayout.FlexibleSpace(); + } + finally + { + EditorGUILayout.EndHorizontal(); + } + } + + return anyChanged; + } + + private static void CollectFonts( + TerminalUI terminal, + SortedDictionary> fontsByPrefix + ) + { + if ( + terminal == null + || terminal._fontPack == null + || terminal._fontPack._fonts is not { Count: > 0 } + ) + { + return; + } + + if (fontsByPrefix.Count != 0) + { + return; + } + + foreach (Font font in terminal._fontPack._fonts) + { + string fontName = font.name; + int indexOfSplit = fontName.IndexOf('-', StringComparison.OrdinalIgnoreCase); + if (indexOfSplit < 0) + { + indexOfSplit = fontName.IndexOf('_', StringComparison.OrdinalIgnoreCase); + } + + string key; + string secondKey; + if (0 <= indexOfSplit) + { + key = fontName[..indexOfSplit]; + secondKey = fontName[Mathf.Min(indexOfSplit + 1, fontName.Length)..]; + } + else + { + key = fontName; + secondKey = string.Empty; + } + + if (!fontsByPrefix.TryGetValue(key, out SortedDictionary fontMapping)) + { + fontMapping = new SortedDictionary( + StringComparer.OrdinalIgnoreCase + ); + fontsByPrefix[key] = fontMapping; + } + + fontMapping[secondKey] = font; + } + } + + private void TryMatchExistingFont(TerminalUI terminal) + { + if (0 <= _fontKey || 0 <= _secondFontKey || terminal.CurrentFont == null) + { + return; + } + + TrySetFontKeysFromFont(terminal.CurrentFont); + } + + private static bool TrySetupDefaultTheme(TerminalUI terminal) + { + if ( + !string.IsNullOrWhiteSpace(terminal.CurrentTheme) + && terminal._themePack != null + && terminal._themePack._themeNames.Contains( + terminal.CurrentTheme, + StringComparer.OrdinalIgnoreCase + ) + ) + { + return false; + } + + if (terminal._themePack == null || terminal._themePack._themeNames.Count == 0) + { + return false; + } + + string defaultTheme = terminal._themePack._themeNames.FirstOrDefault(theme => + theme.Contains("Dark", StringComparison.OrdinalIgnoreCase) + ); + if (string.IsNullOrWhiteSpace(defaultTheme)) + { + defaultTheme = terminal._themePack._themeNames.FirstOrDefault(theme => + theme.Contains("Light", StringComparison.OrdinalIgnoreCase) + ); + } + + if (string.IsNullOrWhiteSpace(defaultTheme)) + { + defaultTheme = terminal._themePack._themeNames.FirstOrDefault(); + } + + terminal.SetTheme(defaultTheme, persist: true); + return true; + } + + private static bool TrySetupDefaultFont(TerminalUI terminal) + { + if ( + terminal.CurrentFont != null + && terminal._fontPack != null + && terminal._fontPack._fonts.Contains(terminal.CurrentFont) + ) + { + return false; + } + + if (terminal._fontPack == null || terminal._fontPack._fonts is not { Count: > 0 }) + { + return false; + } + + Font defaultFont = terminal._fontPack._fonts.FirstOrDefault(font => + font.name.Contains("SourceCodePro", StringComparison.OrdinalIgnoreCase) + && font.name.Contains("Regular", StringComparison.OrdinalIgnoreCase) + ); + if (defaultFont == null) + { + defaultFont = terminal._fontPack._fonts.FirstOrDefault(font => + font.name.Contains("Mono", StringComparison.OrdinalIgnoreCase) + && font.name.Contains("Regular", StringComparison.OrdinalIgnoreCase) + ); + } + if (defaultFont == null) + { + defaultFont = terminal._fontPack._fonts.FirstOrDefault(font => + font.name.Contains("Mono", StringComparison.OrdinalIgnoreCase) + ); + } + if (defaultFont == null) + { + defaultFont = terminal._fontPack._fonts.FirstOrDefault(font => + font.name.Contains("Regular", StringComparison.OrdinalIgnoreCase) + ); + } + if (defaultFont == null) + { + defaultFont = terminal._fontPack._fonts.FirstOrDefault(); + } + + terminal.SetFont(defaultFont, persist: true); + return true; + } + + private static bool EnsureServiceBindingAssigned(TerminalUI terminal) + { + TerminalServiceBindingComponent bindingComponent = + terminal.GetComponent(); + bool anyChange = false; + + if (bindingComponent == null) + { + bindingComponent = Undo.AddComponent( + terminal.gameObject + ); + EditorUtility.SetDirty(terminal.gameObject); + anyChange = true; + } + + if (terminal._serviceBindingComponent != bindingComponent) + { + Undo.RecordObject(terminal, "Assign Terminal Service Binding Component"); + terminal.SetServiceBindingComponentForTests(bindingComponent); + EditorUtility.SetDirty(terminal); + anyChange = true; + } + + return anyChange; + } + + private bool RenderSelectableFonts(TerminalUI terminal) + { + if (_fontsByPrefix is not { Count: > 0 }) + { + return false; + } + + TryMatchExistingFont(terminal); + + bool anyChanged = false; + int currentFontKey = _fontKey; + EditorGUILayout.BeginHorizontal(); + try + { + if (terminal._fontPack != null) + { + if (_fontKey < 0 || _secondFontKey < 0) + { + GUILayout.Label("Select Font:"); + } + + string[] fontKeys = _fontsByPrefix.Keys.ToArray(); + _fontKey = EditorGUILayout.Popup(_fontKey, fontKeys); + + if (currentFontKey != _fontKey) + { + _secondFontKey = -1; + } + + if (0 <= _fontKey && _fontKey < fontKeys.Length) + { + string selectedFontKey = fontKeys[_fontKey]; + SortedDictionary availableFonts = _fontsByPrefix[ + selectedFontKey + ]; + string[] secondFontKeys = availableFonts.Keys.ToArray(); + Font selectedFont = null; + switch (secondFontKeys.Length) + { + case > 1: + { + _secondFontKey = EditorGUILayout.Popup( + _secondFontKey, + secondFontKeys + ); + + if (0 <= _secondFontKey && _secondFontKey < secondFontKeys.Length) + { + selectedFont = availableFonts[secondFontKeys[_secondFontKey]]; + } + + break; + } + case 1: + { + selectedFont = availableFonts.Values.Single(); + break; + } + } + + if (selectedFont != null) + { + GUIContent setFontContent = new( + "Set Font", + $"Update the terminal's font to {selectedFont.name}" + ); + bool clicked = + selectedFont != terminal._persistedFont + ? GUILayout.Button(setFontContent, _impactButtonStyle) + : GUILayout.Button(setFontContent); + if (clicked) + { + terminal.SetFont(selectedFont, persist: true); + anyChanged = true; + } + } + } + } + } + finally + { + GUILayout.EndHorizontal(); + } + + return anyChanged; + } + } +#endif +} diff --git a/Editor/Diagnostics.meta b/Editor/Diagnostics.meta new file mode 100644 index 0000000..ac8c483 --- /dev/null +++ b/Editor/Diagnostics.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2ccadf07f3ca4d9aa70699b88ab0e9f1 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Diagnostics/TerminalRuntimeInspectorWindow.cs b/Editor/Diagnostics/TerminalRuntimeInspectorWindow.cs new file mode 100644 index 0000000..011dcfb --- /dev/null +++ b/Editor/Diagnostics/TerminalRuntimeInspectorWindow.cs @@ -0,0 +1,45 @@ +#if UNITY_EDITOR +namespace WallstopStudios.DxCommandTerminal.Editor.Diagnostics +{ + using Backend; + using UnityEditor; + using WallstopStudios.DxCommandTerminal.UI; + + public sealed class TerminalRuntimeInspectorWindow : EditorWindow + { + [MenuItem("Window/DX Command Terminal/Runtime Inspector")] + private static void Open() + { + TerminalRuntimeInspectorWindow window = GetWindow(); + window.titleContent = new UnityEngine.GUIContent("Terminal Runtime Inspector"); + window.Show(); + } + + private void OnGUI() + { + ITerminalRuntimeScope runtimeScope = TerminalUI.ServiceLocator?.RuntimeScope; + ITerminalRuntime runtime = runtimeScope?.ActiveRuntime; + if (runtime == null) + { + EditorGUILayout.HelpBox("No active terminal runtime detected.", MessageType.Info); + return; + } + + EditorGUILayout.LabelField("Active Runtime", EditorStyles.boldLabel); + EditorGUILayout.LabelField( + "Commands", + runtime.Shell?.Commands?.Count.ToString() ?? "n/a" + ); + EditorGUILayout.LabelField( + "History Entries", + runtime.History?.Count.ToString() ?? "n/a" + ); + EditorGUILayout.LabelField("Log Capacity", runtime.Log?.Capacity.ToString() ?? "n/a"); + EditorGUILayout.LabelField( + "Autocomplete", + runtime.AutoComplete != null ? "Available" : "Missing" + ); + } + } +} +#endif diff --git a/Editor/Diagnostics/TerminalRuntimeInspectorWindow.cs.meta b/Editor/Diagnostics/TerminalRuntimeInspectorWindow.cs.meta new file mode 100644 index 0000000..5b89f9e --- /dev/null +++ b/Editor/Diagnostics/TerminalRuntimeInspectorWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9a445d7ba65743548857fd7fa5bb42a2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/DxShowIfPropertyDrawer.cs b/Editor/DxShowIfPropertyDrawer.cs index 5d184a8..fd751ac 100644 --- a/Editor/DxShowIfPropertyDrawer.cs +++ b/Editor/DxShowIfPropertyDrawer.cs @@ -1,62 +1,62 @@ -namespace WallstopStudios.DxCommandTerminal.Editor -{ -#if UNITY_EDITOR - using System.Reflection; - using Attributes; - using Extensions; - using UnityEditor; - using UnityEngine; - - [CustomPropertyDrawer(typeof(DxShowIfAttribute))] - public sealed class DxShowIfPropertyDrawer : PropertyDrawer - { - public override float GetPropertyHeight(SerializedProperty property, GUIContent label) - { - return !ShouldShow(property) ? 0f : EditorGUI.GetPropertyHeight(property, label, true); - } - - public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) - { - if (ShouldShow(property)) - { - EditorGUI.PropertyField(position, property, label, true); - } - } - - private bool ShouldShow(SerializedProperty property) - { - if (attribute is not DxShowIfAttribute showIf) - { - return true; - } - - SerializedProperty conditionProperty = property.serializedObject.FindProperty( - showIf.conditionField - ); - if (conditionProperty is not { propertyType: SerializedPropertyType.Boolean }) - { - if (conditionProperty != null) - { - return true; - } - - object enclosingObject = property.GetEnclosingObject(out _); - FieldInfo conditionField = enclosingObject - ?.GetType() - .GetField( - showIf.conditionField, - BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic - ); - if (conditionField?.GetValue(enclosingObject) is bool maybeCondition) - { - return showIf.inverse ? !maybeCondition : maybeCondition; - } - return true; - } - - bool condition = conditionProperty.boolValue; - return showIf.inverse ? !condition : condition; - } - } -#endif -} +namespace WallstopStudios.DxCommandTerminal.Editor +{ +#if UNITY_EDITOR + using System.Reflection; + using Attributes; + using Extensions; + using UnityEditor; + using UnityEngine; + + [CustomPropertyDrawer(typeof(DxShowIfAttribute))] + public sealed class DxShowIfPropertyDrawer : PropertyDrawer + { + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + { + return !ShouldShow(property) ? 0f : EditorGUI.GetPropertyHeight(property, label, true); + } + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + if (ShouldShow(property)) + { + EditorGUI.PropertyField(position, property, label, true); + } + } + + private bool ShouldShow(SerializedProperty property) + { + if (attribute is not DxShowIfAttribute showIf) + { + return true; + } + + SerializedProperty conditionProperty = property.serializedObject.FindProperty( + showIf.conditionField + ); + if (conditionProperty is not { propertyType: SerializedPropertyType.Boolean }) + { + if (conditionProperty != null) + { + return true; + } + + object enclosingObject = property.GetEnclosingObject(out _); + FieldInfo conditionField = enclosingObject + ?.GetType() + .GetField( + showIf.conditionField, + BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic + ); + if (conditionField?.GetValue(enclosingObject) is bool maybeCondition) + { + return showIf.inverse ? !maybeCondition : maybeCondition; + } + return true; + } + + bool condition = conditionProperty.boolValue; + return showIf.inverse ? !condition : condition; + } + } +#endif +} diff --git a/Editor/Helper/TerminalThemeStyleSheetHelper.cs b/Editor/Helper/TerminalThemeStyleSheetHelper.cs index eecfb6f..d14e425 100644 --- a/Editor/Helper/TerminalThemeStyleSheetHelper.cs +++ b/Editor/Helper/TerminalThemeStyleSheetHelper.cs @@ -1,156 +1,161 @@ -namespace WallstopStudios.DxCommandTerminal.Editor.Helper -{ -#if UNITY_EDITOR - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Text.RegularExpressions; - using Themes; - using UnityEditor; - using UnityEngine; - using UnityEngine.UIElements; - - public static class TerminalThemeStyleSheetHelper - { - private static readonly List RequiredVariables = new() - { - "--terminal-bg", - "--button-bg", - "--input-field-bg", - "--button-selected-bg", - "--button-hover-bg", - "--scroll-bg", - "--scroll-inverse-bg", - "--scroll-active-bg", - "--button-text", - "--button-selected-text", - "--button-hover-text", - "--input-text-color", - "--text-message", - "--text-warning", - "--text-input-echo", - "--text-shell", - "--text-error", - "--scroll-color", - "--caret-color", - }; - - public static string[] GetAvailableThemes(StyleSheet styleSheetAsset) - { - if (styleSheetAsset == null) - { - return Array.Empty(); - } - - string assetPath = AssetDatabase.GetAssetPath(styleSheetAsset); - if ( - string.IsNullOrWhiteSpace(assetPath) - || !assetPath.EndsWith(".uss", StringComparison.OrdinalIgnoreCase) - ) - { - return Array.Empty(); - } - - try - { - string ussContent = File.ReadAllText(assetPath); - - // Remove block comments /* ... */ - ussContent = Regex.Replace( - ussContent, - @"/\*.*?\*/", - string.Empty, - RegexOptions.Singleline - ); - - // Remove line comments // ... - ussContent = Regex.Replace( - ussContent, - "//.*$", - string.Empty, - RegexOptions.Multiline - ); - - SortedSet selectors = new(StringComparer.OrdinalIgnoreCase); - - int lastIndex = 0; - while (lastIndex < ussContent.Length) - { - int braceIndex = ussContent.IndexOf('{', lastIndex); - if (braceIndex < 0) - { - break; - } - - int previousRuleEnd = ussContent.LastIndexOf( - '}', - braceIndex - 1, - braceIndex - lastIndex - ); - int selectorStartIndex = previousRuleEnd < 0 ? lastIndex : previousRuleEnd + 1; - - string selectorPart = ussContent - .Substring(selectorStartIndex, braceIndex - selectorStartIndex) - .Trim(); - - if (!string.IsNullOrWhiteSpace(selectorPart)) - { - string[] individualSelectors = selectorPart.Split(','); - foreach (string sel in individualSelectors) - { - string trimmedSelector = sel.Trim(); - if (trimmedSelector.StartsWith('.')) - { - trimmedSelector = trimmedSelector[1..]; - } - - if (string.IsNullOrWhiteSpace(trimmedSelector)) - { - continue; - } - - if (ThemeNameHelper.IsThemeName(trimmedSelector)) - { - int nextObjectBraceIndex = ussContent.IndexOf('}', braceIndex + 1); - if (nextObjectBraceIndex < 0) - { - nextObjectBraceIndex = ussContent.Length; - } - string objectContents = ussContent.Substring( - selectorStartIndex, - nextObjectBraceIndex - selectorStartIndex - ); - - if ( - RequiredVariables.Exists(requiredVariable => - !objectContents.Contains( - requiredVariable, - StringComparison.OrdinalIgnoreCase - ) - ) - ) - { - return Array.Empty(); - } - - selectors.Add(trimmedSelector); - } - } - } - - int nextBraceIndex = ussContent.IndexOf('}', braceIndex + 1); - lastIndex = nextBraceIndex < 0 ? ussContent.Length : nextBraceIndex + 1; - } - - return selectors.ToArray(); - } - catch (Exception e) - { - Debug.LogException(e); - return Array.Empty(); - } - } - } -#endif -} +namespace WallstopStudios.DxCommandTerminal.Editor.Helper +{ +#if UNITY_EDITOR + using System; + using System.Collections.Generic; + using System.IO; + using System.Text.RegularExpressions; + using Themes; + using UnityEditor; + using UnityEngine; + using UnityEngine.UIElements; + + public static class TerminalThemeStyleSheetHelper + { + private static readonly List RequiredVariables = new() + { + "--terminal-bg", + "--button-bg", + "--input-field-bg", + "--button-selected-bg", + "--button-hover-bg", + "--scroll-bg", + "--scroll-inverse-bg", + "--scroll-active-bg", + "--button-text", + "--button-selected-text", + "--button-hover-text", + "--input-text-color", + "--text-message", + "--text-warning", + "--text-input-echo", + "--text-shell", + "--text-error", + "--scroll-color", + "--caret-color", + }; + + public static string[] GetAvailableThemes(StyleSheet styleSheetAsset) + { + if (styleSheetAsset == null) + { + return Array.Empty(); + } + + string assetPath = AssetDatabase.GetAssetPath(styleSheetAsset); + if ( + string.IsNullOrWhiteSpace(assetPath) + || !assetPath.EndsWith(".uss", StringComparison.OrdinalIgnoreCase) + ) + { + return Array.Empty(); + } + + try + { + string ussContent = File.ReadAllText(assetPath); + + // Remove block comments /* ... */ + ussContent = Regex.Replace( + ussContent, + @"/\*.*?\*/", + string.Empty, + RegexOptions.Singleline + ); + + // Remove line comments // ... + ussContent = Regex.Replace( + ussContent, + "//.*$", + string.Empty, + RegexOptions.Multiline + ); + + SortedSet selectors = new(StringComparer.OrdinalIgnoreCase); + + int lastIndex = 0; + while (lastIndex < ussContent.Length) + { + int braceIndex = ussContent.IndexOf('{', lastIndex); + if (braceIndex < 0) + { + break; + } + + int previousRuleEnd = ussContent.LastIndexOf( + '}', + braceIndex - 1, + braceIndex - lastIndex + ); + int selectorStartIndex = previousRuleEnd < 0 ? lastIndex : previousRuleEnd + 1; + + string selectorPart = ussContent + .Substring(selectorStartIndex, braceIndex - selectorStartIndex) + .Trim(); + + if (!string.IsNullOrWhiteSpace(selectorPart)) + { + string[] individualSelectors = selectorPart.Split(','); + foreach (string sel in individualSelectors) + { + string trimmedSelector = sel.Trim(); + if (trimmedSelector.StartsWith('.')) + { + trimmedSelector = trimmedSelector[1..]; + } + + if (string.IsNullOrWhiteSpace(trimmedSelector)) + { + continue; + } + + if (ThemeNameHelper.IsThemeName(trimmedSelector)) + { + int nextObjectBraceIndex = ussContent.IndexOf('}', braceIndex + 1); + if (nextObjectBraceIndex < 0) + { + nextObjectBraceIndex = ussContent.Length; + } + string objectContents = ussContent.Substring( + selectorStartIndex, + nextObjectBraceIndex - selectorStartIndex + ); + + if ( + RequiredVariables.Exists(requiredVariable => + !objectContents.Contains( + requiredVariable, + StringComparison.OrdinalIgnoreCase + ) + ) + ) + { + return Array.Empty(); + } + + selectors.Add(trimmedSelector); + } + } + } + + int nextBraceIndex = ussContent.IndexOf('}', braceIndex + 1); + lastIndex = nextBraceIndex < 0 ? ussContent.Length : nextBraceIndex + 1; + } + + if (selectors.Count == 0) + { + return Array.Empty(); + } + string[] arr = new string[selectors.Count]; + selectors.CopyTo(arr); + return arr; + } + catch (Exception e) + { + Debug.LogException(e); + return Array.Empty(); + } + } + } +#endif +} diff --git a/Editor/LauncherLayoutDiagnostics.cs b/Editor/LauncherLayoutDiagnostics.cs new file mode 100644 index 0000000..61aeb31 --- /dev/null +++ b/Editor/LauncherLayoutDiagnostics.cs @@ -0,0 +1,58 @@ +#if UNITY_EDITOR +namespace WallstopStudios.DxCommandTerminal.Editor +{ + using UnityEditor; + using UnityEngine; + using WallstopStudios.DxCommandTerminal.UI; + + [InitializeOnLoad] + internal static class LauncherLayoutDiagnostics + { + private const string MenuItemPath = "DX Command Terminal/Diagnostics/Log Launcher Layout"; + private const string PreferenceKey = + "WallstopStudios.DxCommandTerminal.Diagnostics.LauncherLayout"; + private static bool isEnabled; + + static LauncherLayoutDiagnostics() + { + bool savedPreference = EditorPrefs.GetBool(PreferenceKey, false); + SetEnabled(savedPreference); + } + + [MenuItem(MenuItemPath)] + private static void Toggle() + { + SetEnabled(!isEnabled); + } + + [MenuItem(MenuItemPath, true)] + private static bool ToggleValidate() + { + Menu.SetChecked(MenuItemPath, isEnabled); + return true; + } + + private static void SetEnabled(bool enabled) + { + if (enabled == isEnabled) + { + return; + } + + isEnabled = enabled; + EditorPrefs.SetBool(PreferenceKey, isEnabled); + + TerminalUI.LauncherLayoutComputed -= OnLauncherLayoutComputed; + if (isEnabled) + { + TerminalUI.LauncherLayoutComputed += OnLauncherLayoutComputed; + } + } + + private static void OnLauncherLayoutComputed(TerminalUI.LauncherLayoutSnapshot snapshot) + { + Debug.Log($"[DxCommandTerminal] {snapshot}"); + } + } +} +#endif diff --git a/Editor/LauncherLayoutDiagnostics.cs.meta b/Editor/LauncherLayoutDiagnostics.cs.meta new file mode 100644 index 0000000..78cd53c --- /dev/null +++ b/Editor/LauncherLayoutDiagnostics.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4fa20755f5ff49c09076d2626100c251 +timeCreated: 1760645663 diff --git a/Editor/Parsers.meta b/Editor/Parsers.meta new file mode 100644 index 0000000..41710cf --- /dev/null +++ b/Editor/Parsers.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ea07f328d83288f468eb2a0b261abe34 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Parsers/ParserAutoDiscovery.cs b/Editor/Parsers/ParserAutoDiscovery.cs new file mode 100644 index 0000000..2ce2372 --- /dev/null +++ b/Editor/Parsers/ParserAutoDiscovery.cs @@ -0,0 +1,23 @@ +namespace WallstopStudios.DxCommandTerminal.Editor +{ +#if UNITY_EDITOR + using UnityEditor; + using Backend; + + [InitializeOnLoad] + internal static class ParserAutoDiscovery + { + static ParserAutoDiscovery() + { + // Editor convenience: allow auto-discovery via config flags + if ( + TerminalRuntimeConfig.ShouldEnableEditorFeatures() + && TerminalRuntimeConfig.EditorAutoDiscover + ) + { + CommandArg.DiscoverAndRegisterParsers(replaceExisting: false); + } + } + } +#endif +} diff --git a/Editor/Parsers/ParserAutoDiscovery.cs.meta b/Editor/Parsers/ParserAutoDiscovery.cs.meta new file mode 100644 index 0000000..d3b4921 --- /dev/null +++ b/Editor/Parsers/ParserAutoDiscovery.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 865175dec92d20c4d98ce50059e70caf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/TerminalAssetPackPostProcessor.cs b/Editor/TerminalAssetPackPostProcessor.cs index 1386c7a..f0b979c 100644 --- a/Editor/TerminalAssetPackPostProcessor.cs +++ b/Editor/TerminalAssetPackPostProcessor.cs @@ -1,45 +1,45 @@ -namespace WallstopStudios.DxCommandTerminal.Editor -{ - using System; - using System.Collections.Generic; - using Themes; - using UnityEditor; - - internal sealed class TerminalAssetPackPostProcessor : AssetPostprocessor - { - internal static readonly List NewThemePacks = new(); - internal static readonly List NewFontPacks = new(); - - private static void OnPostprocessAllAssets( - string[] importedAssets, - string[] deletedAssets, - string[] movedAssets, - string[] movedFromAssetPaths - ) - { - foreach (string importedAsset in importedAssets) - { - if (!importedAsset.EndsWith(".asset", StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - TerminalThemePack themePack = AssetDatabase.LoadAssetAtPath( - importedAsset - ); - if (themePack != null) - { - NewThemePacks.Add(themePack); - } - - TerminalFontPack fontPack = AssetDatabase.LoadAssetAtPath( - importedAsset - ); - if (fontPack != null) - { - NewFontPacks.Add(fontPack); - } - } - } - } -} +namespace WallstopStudios.DxCommandTerminal.Editor +{ + using System; + using System.Collections.Generic; + using Themes; + using UnityEditor; + + internal sealed class TerminalAssetPackPostProcessor : AssetPostprocessor + { + internal static readonly List NewThemePacks = new(); + internal static readonly List NewFontPacks = new(); + + private static void OnPostprocessAllAssets( + string[] importedAssets, + string[] deletedAssets, + string[] movedAssets, + string[] movedFromAssetPaths + ) + { + foreach (string importedAsset in importedAssets) + { + if (!importedAsset.EndsWith(".asset", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + TerminalThemePack themePack = AssetDatabase.LoadAssetAtPath( + importedAsset + ); + if (themePack != null) + { + NewThemePacks.Add(themePack); + } + + TerminalFontPack fontPack = AssetDatabase.LoadAssetAtPath( + importedAsset + ); + if (fontPack != null) + { + NewFontPacks.Add(fontPack); + } + } + } + } +} diff --git a/Editor/Utils.meta b/Editor/Utils.meta new file mode 100644 index 0000000..50a216d --- /dev/null +++ b/Editor/Utils.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 032967df8482c5340adb0e8ce2dee016 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Utils/ScriptableObjectSingletonCreator.cs b/Editor/Utils/ScriptableObjectSingletonCreator.cs new file mode 100644 index 0000000..9d105a7 --- /dev/null +++ b/Editor/Utils/ScriptableObjectSingletonCreator.cs @@ -0,0 +1,256 @@ +namespace WallstopStudios.DxCommandTerminal.Editor.Utils +{ +#if UNITY_EDITOR + using System; + using System.Collections.Generic; + using System.IO; + using System.Text; + using System.Reflection; + using UnityEditor; + using UnityEngine; + using Internal; + + [InitializeOnLoad] + public static class ScriptableObjectSingletonCreator + { + private const string ResourcesRoot = "Assets/Resources"; + private static bool _ensuring; + + static ScriptableObjectSingletonCreator() + { + EnsureSingletonAssets(); + } + + private static bool IsDerivedFromScriptableSingleton(Type t) + { + if (t == null || t.IsAbstract || t.IsGenericType) + { + return false; + } + Type baseType = t; + while (baseType != null) + { + if (baseType.IsGenericType) + { + Type def = baseType.GetGenericTypeDefinition(); + if (def == typeof(ScriptableObjectSingleton<>)) + { + return true; + } + } + baseType = baseType.BaseType; + } + return false; + } + + public static void EnsureSingletonAssets() + { + if (_ensuring) + { + return; + } + + _ensuring = true; + try + { + EnsureFolder(ResourcesRoot); + + // Collect all concrete types deriving from our singleton base + List candidates = new(); + foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) + { + Type[] types; + try + { + types = asm.GetTypes(); + } + catch (ReflectionTypeLoadException ex) + { + List tmp = new(); + if (ex.Types != null) + { + for (int i = 0; i < ex.Types.Length; ++i) + { + Type t = ex.Types[i]; + if (t != null) + { + tmp.Add(t); + } + } + } + types = tmp.ToArray(); + } + + foreach (Type t in types) + { + if (IsDerivedFromScriptableSingleton(t)) + { + candidates.Add(t); + } + } + } + + // Simple collision detection by simple name + Dictionary> nameGroups = new(StringComparer.OrdinalIgnoreCase); + for (int i = 0; i < candidates.Count; ++i) + { + Type t = candidates[i]; + string key = t.Name ?? string.Empty; + if (!nameGroups.TryGetValue(key, out List list)) + { + list = new List(); + nameGroups[key] = list; + } + list.Add(t); + } + Dictionary> collisions = new(StringComparer.OrdinalIgnoreCase); + foreach (KeyValuePair> kv in nameGroups) + { + if (kv.Value != null && kv.Value.Count > 1) + { + collisions[kv.Key] = kv.Value; + } + } + + foreach (Type type in candidates) + { + if (collisions.ContainsKey(type.Name)) + { + List coll = collisions[type.Name]; + StringBuilder sb = new(); + for (int i = 0; i < coll.Count; ++i) + { + if (i > 0) + { + sb.Append(", "); + } + sb.Append(coll[i]?.FullName); + } + Debug.LogWarning( + $"ScriptableObjectSingletonCreator: Multiple types share the name '{type.Name}'. Skipping auto-creation. Add [ScriptableSingletonPath] to disambiguate or rename. Types: {sb.ToString()}" + ); + continue; + } + + string sub = GetResourcesSubFolder(type); + string parent = string.IsNullOrWhiteSpace(sub) + ? ResourcesRoot + : PathCombine(ResourcesRoot, sub); + + EnsureFolder(parent); + + string assetPath = PathCombine(parent, type.Name + ".asset"); + + UnityEngine.Object atPath = AssetDatabase.LoadAssetAtPath(assetPath, type); + if (atPath != null) + { + continue; + } + + // Try to find any existing asset of exact type and move it + string[] guids = AssetDatabase.FindAssets("t:" + type.Name); + bool moved = false; + foreach (string guid in guids) + { + string path = AssetDatabase.GUIDToAssetPath(guid); + if (string.IsNullOrWhiteSpace(path)) + { + continue; + } + UnityEngine.Object obj = AssetDatabase.LoadAssetAtPath(path, type); + if (obj == null) + { + continue; + } + + // Don't overwrite an existing file at the intended target + if ( + File.Exists(assetPath) + && !string.Equals(path, assetPath, StringComparison.OrdinalIgnoreCase) + ) + { + Debug.LogWarning( + $"ScriptableObjectSingletonCreator: Target path already occupied at {assetPath}. Skipping move for {type.FullName}." + ); + moved = true; // treat as handled to avoid creating duplicate + break; + } + + string result = AssetDatabase.MoveAsset(path, assetPath); + if (string.IsNullOrEmpty(result)) + { + moved = true; + break; + } + else + { + Debug.LogWarning( + $"ScriptableObjectSingletonCreator: Failed to move existing {type.FullName} from {path} to {assetPath}: {result}" + ); + } + } + + if (!moved) + { + ScriptableObject created = ScriptableObject.CreateInstance(type); + AssetDatabase.CreateAsset(created, assetPath); + } + } + + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } + finally + { + _ensuring = false; + } + } + + private static string GetResourcesSubFolder(Type type) + { + ScriptableSingletonPathAttribute attr = + type.GetCustomAttribute(); + if (attr == null) + { + return string.Empty; + } + string p = (attr.resourcesPath ?? string.Empty).Trim(); + if (p.StartsWith("/")) + { + p = p.TrimStart('/'); + } + return p.Replace('\\', '/'); + } + + private static void EnsureFolder(string folder) + { + folder = folder.Replace('\\', '/'); + if (AssetDatabase.IsValidFolder(folder)) + { + return; + } + + string[] parts = folder.Split('/', StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 0) + { + return; + } + string current = parts[0]; + for (int i = 1; i < parts.Length; i++) + { + string next = current + "/" + parts[i]; + if (!AssetDatabase.IsValidFolder(next)) + { + AssetDatabase.CreateFolder(current, parts[i]); + } + current = next; + } + } + + private static string PathCombine(string a, string b) + { + return (a.TrimEnd('/', '\\') + "/" + b.TrimStart('/', '\\')).Replace('\\', '/'); + } + } +#endif +} diff --git a/Editor/Utils/ScriptableObjectSingletonCreator.cs.meta b/Editor/Utils/ScriptableObjectSingletonCreator.cs.meta new file mode 100644 index 0000000..a482744 --- /dev/null +++ b/Editor/Utils/ScriptableObjectSingletonCreator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b3ff06218af7bc043b9aa7bb1892633c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/WallstopStudios.DxCommandTerminal.Editor.asmdef b/Editor/WallstopStudios.DxCommandTerminal.Editor.asmdef index 6727904..53017c1 100644 --- a/Editor/WallstopStudios.DxCommandTerminal.Editor.asmdef +++ b/Editor/WallstopStudios.DxCommandTerminal.Editor.asmdef @@ -1,19 +1,14 @@ { - "name": "WallstopStudios.DxCommandTerminal.Editor", - "rootNamespace": "WallstopStudios.DxCommandTerminal", - "references": [ - "WallstopStudios.DxCommandTerminal", - "Unity.InputSystem" - ], - "includePlatforms": [ - "Editor" - ], - "excludePlatforms": [], - "allowUnsafeCode": false, - "overrideReferences": false, - "precompiledReferences": [], - "autoReferenced": true, - "defineConstraints": [], - "versionDefines": [], - "noEngineReferences": false -} \ No newline at end of file + "name": "WallstopStudios.DxCommandTerminal.Editor", + "rootNamespace": "WallstopStudios.DxCommandTerminal", + "references": ["WallstopStudios.DxCommandTerminal", "Unity.InputSystem"], + "includePlatforms": ["Editor"], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} diff --git a/Fonts/Anonymous_Pro/OFL.txt b/Fonts/Anonymous_Pro/OFL.txt index d89cf45..d62ff04 100644 --- a/Fonts/Anonymous_Pro/OFL.txt +++ b/Fonts/Anonymous_Pro/OFL.txt @@ -1,94 +1,94 @@ -Copyright (c) 2009, Mark Simonson (http://www.ms-studio.com, mark@marksimonson.com), -with Reserved Font Name Anonymous Pro. - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright (c) 2009, Mark Simonson (http://www.ms-studio.com, mark@marksimonson.com), +with Reserved Font Name Anonymous Pro. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Atkinson_Hyperlegible_Mono/OFL.txt b/Fonts/Atkinson_Hyperlegible_Mono/OFL.txt index de65832..63eb4ba 100644 --- a/Fonts/Atkinson_Hyperlegible_Mono/OFL.txt +++ b/Fonts/Atkinson_Hyperlegible_Mono/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2020-2024 The Atkinson Hyperlegible Mono Project Authors (https://github.com/googlefonts/atkinson-hyperlegible-next-mono) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2020-2024 The Atkinson Hyperlegible Mono Project Authors (https://github.com/googlefonts/atkinson-hyperlegible-next-mono) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Azeret_Mono/OFL.txt b/Fonts/Azeret_Mono/OFL.txt index c561cb1..736e22b 100644 --- a/Fonts/Azeret_Mono/OFL.txt +++ b/Fonts/Azeret_Mono/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2021 The Azeret Project Authors (https://github.com/displaay/azeret) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2021 The Azeret Project Authors (https://github.com/displaay/azeret) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/B612_Mono/OFL.txt b/Fonts/B612_Mono/OFL.txt index 0b5d530..1c257f7 100644 --- a/Fonts/B612_Mono/OFL.txt +++ b/Fonts/B612_Mono/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2012 The B612 Project Authors (https://github.com/polarsys/b612) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2012 The B612 Project Authors (https://github.com/polarsys/b612) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Barlow_Condensed/OFL.txt b/Fonts/Barlow_Condensed/OFL.txt index 500b734..175e823 100644 --- a/Fonts/Barlow_Condensed/OFL.txt +++ b/Fonts/Barlow_Condensed/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2017 The Barlow Project Authors (https://github.com/jpt/barlow) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2017 The Barlow Project Authors (https://github.com/jpt/barlow) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Courier_Prime/OFL.txt b/Fonts/Courier_Prime/OFL.txt index 7072510..28f87c1 100644 --- a/Fonts/Courier_Prime/OFL.txt +++ b/Fonts/Courier_Prime/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2015 The Courier Prime Project Authors (https://github.com/quoteunquoteapps/CourierPrime). - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2015 The Courier Prime Project Authors (https://github.com/quoteunquoteapps/CourierPrime). + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Cousine/LICENSE.txt b/Fonts/Cousine/LICENSE.txt index 75b5248..d645695 100644 --- a/Fonts/Cousine/LICENSE.txt +++ b/Fonts/Cousine/LICENSE.txt @@ -1,202 +1,202 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Fonts/Cutive_Mono/OFL.txt b/Fonts/Cutive_Mono/OFL.txt index 835e350..ef10074 100644 --- a/Fonts/Cutive_Mono/OFL.txt +++ b/Fonts/Cutive_Mono/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2012 The Cutive Project Authors (https://github.com/googlefonts/cutivemono) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2012 The Cutive Project Authors (https://github.com/googlefonts/cutivemono) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/DM_Mono/OFL.txt b/Fonts/DM_Mono/OFL.txt index 14fde1b..5b17f0c 100644 --- a/Fonts/DM_Mono/OFL.txt +++ b/Fonts/DM_Mono/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2020 The DM Mono Project Authors (https://www.github.com/googlefonts/dm-mono) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2020 The DM Mono Project Authors (https://www.github.com/googlefonts/dm-mono) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Fira_Code/OFL.txt b/Fonts/Fira_Code/OFL.txt index 0e38b88..b1540b0 100644 --- a/Fonts/Fira_Code/OFL.txt +++ b/Fonts/Fira_Code/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2014-2020 The Fira Code Project Authors (https://github.com/tonsky/FiraCode) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2014-2020 The Fira Code Project Authors (https://github.com/tonsky/FiraCode) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Fira_Mono/OFL.txt b/Fonts/Fira_Mono/OFL.txt index 8014188..029b036 100644 --- a/Fonts/Fira_Mono/OFL.txt +++ b/Fonts/Fira_Mono/OFL.txt @@ -1,93 +1,93 @@ -Copyright (c) 2012-2013, The Mozilla Corporation and Telefonica S.A. - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright (c) 2012-2013, The Mozilla Corporation and Telefonica S.A. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Fragment_Mono/OFL.txt b/Fonts/Fragment_Mono/OFL.txt index ac1fd0e..0a08c50 100644 --- a/Fonts/Fragment_Mono/OFL.txt +++ b/Fonts/Fragment_Mono/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2022 The Fragment-Mono Project Authors (https://github.com/weiweihuanghuang/fragment-mono) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2022 The Fragment-Mono Project Authors (https://github.com/weiweihuanghuang/fragment-mono) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Geist_Mono/OFL.txt b/Fonts/Geist_Mono/OFL.txt index b48941f..679a685 100644 --- a/Fonts/Geist_Mono/OFL.txt +++ b/Fonts/Geist_Mono/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2024 The Geist Project Authors (https://github.com/vercel/geist-font.git) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2024 The Geist Project Authors (https://github.com/vercel/geist-font.git) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/IBM_Plex_Mono/OFL.txt b/Fonts/IBM_Plex_Mono/OFL.txt index 5bb330e..e423b74 100644 --- a/Fonts/IBM_Plex_Mono/OFL.txt +++ b/Fonts/IBM_Plex_Mono/OFL.txt @@ -1,93 +1,93 @@ -Copyright © 2017 IBM Corp. with Reserved Font Name "Plex" - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright © 2017 IBM Corp. with Reserved Font Name "Plex" + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Inconsolata/OFL.txt b/Fonts/Inconsolata/OFL.txt index 55533e1..1089cbf 100644 --- a/Fonts/Inconsolata/OFL.txt +++ b/Fonts/Inconsolata/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2006 The Inconsolata Project Authors - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2006 The Inconsolata Project Authors + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/JetBrains_Mono/OFL.txt b/Fonts/JetBrains_Mono/OFL.txt index 3f34847..5ceee00 100644 --- a/Fonts/JetBrains_Mono/OFL.txt +++ b/Fonts/JetBrains_Mono/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Kode_Mono/OFL.txt b/Fonts/Kode_Mono/OFL.txt index 20c4909..2631492 100644 --- a/Fonts/Kode_Mono/OFL.txt +++ b/Fonts/Kode_Mono/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2023 The Kode Mono Project Authors (https://github.com/isaozler/kode-mono) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2023 The Kode Mono Project Authors (https://github.com/isaozler/kode-mono) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/M_PLUS_1_Code/OFL.txt b/Fonts/M_PLUS_1_Code/OFL.txt index 05e42fb..c0131b3 100644 --- a/Fonts/M_PLUS_1_Code/OFL.txt +++ b/Fonts/M_PLUS_1_Code/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2021 The M+ FONTS Project Authors (https://github.com/coz-m/MPLUS_FONTS) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2021 The M+ FONTS Project Authors (https://github.com/coz-m/MPLUS_FONTS) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/M_PLUS_Code_Latin/OFL.txt b/Fonts/M_PLUS_Code_Latin/OFL.txt index 05e42fb..c0131b3 100644 --- a/Fonts/M_PLUS_Code_Latin/OFL.txt +++ b/Fonts/M_PLUS_Code_Latin/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2021 The M+ FONTS Project Authors (https://github.com/coz-m/MPLUS_FONTS) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2021 The M+ FONTS Project Authors (https://github.com/coz-m/MPLUS_FONTS) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Major_Mono_Display/OFL.txt b/Fonts/Major_Mono_Display/OFL.txt index 30961b9..d32c8d6 100644 --- a/Fonts/Major_Mono_Display/OFL.txt +++ b/Fonts/Major_Mono_Display/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2018 The Major Mono Project Authors (https://github.com/googlefonts/majormono) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2018 The Major Mono Project Authors (https://github.com/googlefonts/majormono) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Martian_Mono/OFL.txt b/Fonts/Martian_Mono/OFL.txt index adffdd1..e83393d 100644 --- a/Fonts/Martian_Mono/OFL.txt +++ b/Fonts/Martian_Mono/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2021 The Martian Mono Project Authors (https://github.com/evilmartians/mono) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2021 The Martian Mono Project Authors (https://github.com/evilmartians/mono) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Nanum_Gothic_Coding/OFL.txt b/Fonts/Nanum_Gothic_Coding/OFL.txt index abbf68d..42085a7 100644 --- a/Fonts/Nanum_Gothic_Coding/OFL.txt +++ b/Fonts/Nanum_Gothic_Coding/OFL.txt @@ -1,96 +1,96 @@ -Copyright (c) 2010, NHN Corporation (http://www.nhncorp.com), -with Reserved Font Name Nanum, Naver Nanum, NanumGothic, Naver -NanumGothic, NanumMyeongjo, Naver NanumMyeongjo, NanumBrush, Naver -NanumBrush, NanumPen, Naver NanumPen. - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright (c) 2010, NHN Corporation (http://www.nhncorp.com), +with Reserved Font Name Nanum, Naver Nanum, NanumGothic, Naver +NanumGothic, NanumMyeongjo, Naver NanumMyeongjo, NanumBrush, Naver +NanumBrush, NanumPen, Naver NanumPen. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Noto_Sans_Mono/OFL.txt b/Fonts/Noto_Sans_Mono/OFL.txt index 09f020b..0373e14 100644 --- a/Fonts/Noto_Sans_Mono/OFL.txt +++ b/Fonts/Noto_Sans_Mono/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2022 The Noto Project Authors (https://github.com/notofonts/latin-greek-cyrillic) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2022 The Noto Project Authors (https://github.com/notofonts/latin-greek-cyrillic) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Nova_Mono/OFL.txt b/Fonts/Nova_Mono/OFL.txt index 15d4aa3..0522b35 100644 --- a/Fonts/Nova_Mono/OFL.txt +++ b/Fonts/Nova_Mono/OFL.txt @@ -1,94 +1,94 @@ -Copyright (c) 2011, wmk69 (wmk69@o2.pl), -with Reserved Font Name NovaMono. - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright (c) 2011, wmk69 (wmk69@o2.pl), +with Reserved Font Name NovaMono. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Overpass_Mono/OFL.txt b/Fonts/Overpass_Mono/OFL.txt index 40284ee..b875eef 100644 --- a/Fonts/Overpass_Mono/OFL.txt +++ b/Fonts/Overpass_Mono/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2021 The Overpass Project Authors (https://github.com/RedHatOfficial/Overpass) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2021 The Overpass Project Authors (https://github.com/RedHatOfficial/Overpass) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Oxygen_Mono/OFL.txt b/Fonts/Oxygen_Mono/OFL.txt index 5ab613f..fec3ac5 100644 --- a/Fonts/Oxygen_Mono/OFL.txt +++ b/Fonts/Oxygen_Mono/OFL.txt @@ -1,93 +1,93 @@ -Copyright (c) 2012, vernon adams (vern@newtypography.co.uk), with Reserved Font Names 'Oxygen' - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright (c) 2012, vernon adams (vern@newtypography.co.uk), with Reserved Font Names 'Oxygen' + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/PT_Mono/OFL.txt b/Fonts/PT_Mono/OFL.txt index e2939d9..ff1880c 100644 --- a/Fonts/PT_Mono/OFL.txt +++ b/Fonts/PT_Mono/OFL.txt @@ -1,94 +1,94 @@ -Copyright (c) 2011, ParaType Ltd. (http://www.paratype.com/public), -with Reserved Font Names "PT Sans", "PT Serif", "PT Mono" and "ParaType". - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright (c) 2011, ParaType Ltd. (http://www.paratype.com/public), +with Reserved Font Names "PT Sans", "PT Serif", "PT Mono" and "ParaType". + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Press_Start_2P/OFL.txt b/Fonts/Press_Start_2P/OFL.txt index 70041e1..ea3079c 100644 --- a/Fonts/Press_Start_2P/OFL.txt +++ b/Fonts/Press_Start_2P/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2012 The Press Start 2P Project Authors (cody@zone38.net), with Reserved Font Name "Press Start 2P". - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2012 The Press Start 2P Project Authors (cody@zone38.net), with Reserved Font Name "Press Start 2P". + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Red_Hat_Mono/OFL.txt b/Fonts/Red_Hat_Mono/OFL.txt index 1856021..16cf394 100644 --- a/Fonts/Red_Hat_Mono/OFL.txt +++ b/Fonts/Red_Hat_Mono/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2024 The Red Hat Project Authors (https://github.com/RedHatOfficial/RedHatFont) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2024 The Red Hat Project Authors (https://github.com/RedHatOfficial/RedHatFont) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Roboto_Condensed/OFL.txt b/Fonts/Roboto_Condensed/OFL.txt index a417551..9c48e05 100644 --- a/Fonts/Roboto_Condensed/OFL.txt +++ b/Fonts/Roboto_Condensed/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2011 The Roboto Project Authors (https://github.com/googlefonts/roboto-classic) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2011 The Roboto Project Authors (https://github.com/googlefonts/roboto-classic) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Roboto_Mono/LICENSE.txt b/Fonts/Roboto_Mono/LICENSE.txt index 75b5248..d645695 100644 --- a/Fonts/Roboto_Mono/LICENSE.txt +++ b/Fonts/Roboto_Mono/LICENSE.txt @@ -1,202 +1,202 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Fonts/Rubik_Mono_One/OFL.txt b/Fonts/Rubik_Mono_One/OFL.txt index f6684df..cb5ac7d 100644 --- a/Fonts/Rubik_Mono_One/OFL.txt +++ b/Fonts/Rubik_Mono_One/OFL.txt @@ -1,93 +1,93 @@ -Copyright (c) 2013, 2014, Hubert and Fischer, Philipp Hubert (philipp@hubertfischer.com), Sebastian Fischer (sebastian@hubertfischer.com) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright (c) 2013, 2014, Hubert and Fischer, Philipp Hubert (philipp@hubertfischer.com), Sebastian Fischer (sebastian@hubertfischer.com) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Share_Tech_Mono/OFL.txt b/Fonts/Share_Tech_Mono/OFL.txt index db759a6..1c3c287 100644 --- a/Fonts/Share_Tech_Mono/OFL.txt +++ b/Fonts/Share_Tech_Mono/OFL.txt @@ -1,93 +1,93 @@ -Copyright (c) 2012, Carrois Type Design, Ralph du Carrois (post@carrois.com www.carrois.com), with Reserved Font Name 'Share' - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright (c) 2012, Carrois Type Design, Ralph du Carrois (post@carrois.com www.carrois.com), with Reserved Font Name 'Share' + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Silkscreen/OFL.txt b/Fonts/Silkscreen/OFL.txt index bf7d153..a1fe7d5 100644 --- a/Fonts/Silkscreen/OFL.txt +++ b/Fonts/Silkscreen/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2001 The Silkscreen Project Authors (https://github.com/googlefonts/silkscreen) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2001 The Silkscreen Project Authors (https://github.com/googlefonts/silkscreen) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Sono/OFL.txt b/Fonts/Sono/OFL.txt index 8ff5120..199edaf 100644 --- a/Fonts/Sono/OFL.txt +++ b/Fonts/Sono/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2020 The Sono Project Authors (https://github.com/sursly/sono) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2020 The Sono Project Authors (https://github.com/sursly/sono) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Source_Code_Pro/OFL.txt b/Fonts/Source_Code_Pro/OFL.txt index 54da4a4..653ff7d 100644 --- a/Fonts/Source_Code_Pro/OFL.txt +++ b/Fonts/Source_Code_Pro/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Space_Mono/OFL.txt b/Fonts/Space_Mono/OFL.txt index 2c5ae56..2805642 100644 --- a/Fonts/Space_Mono/OFL.txt +++ b/Fonts/Space_Mono/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2016 The Space Mono Project Authors (https://github.com/googlefonts/spacemono) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2016 The Space Mono Project Authors (https://github.com/googlefonts/spacemono) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Syne_Mono/OFL.txt b/Fonts/Syne_Mono/OFL.txt index 02fcbaf..146e2e0 100644 --- a/Fonts/Syne_Mono/OFL.txt +++ b/Fonts/Syne_Mono/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2017 The Syne Project Authors (https://gitlab.com/bonjour-monde/fonderie/syne-typeface) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2017 The Syne Project Authors (https://gitlab.com/bonjour-monde/fonderie/syne-typeface) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Ubuntu_Mono/UFL.txt b/Fonts/Ubuntu_Mono/UFL.txt index 6e722c8..ae78a8f 100644 --- a/Fonts/Ubuntu_Mono/UFL.txt +++ b/Fonts/Ubuntu_Mono/UFL.txt @@ -1,96 +1,96 @@ -------------------------------- -UBUNTU FONT LICENCE Version 1.0 -------------------------------- - -PREAMBLE -This licence allows the licensed fonts to be used, studied, modified and -redistributed freely. The fonts, including any derivative works, can be -bundled, embedded, and redistributed provided the terms of this licence -are met. The fonts and derivatives, however, cannot be released under -any other licence. The requirement for fonts to remain under this -licence does not require any document created using the fonts or their -derivatives to be published under this licence, as long as the primary -purpose of the document is not to be a vehicle for the distribution of -the fonts. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this licence and clearly marked as such. This may -include source files, build scripts and documentation. - -"Original Version" refers to the collection of Font Software components -as received under this licence. - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to -a new environment. - -"Copyright Holder(s)" refers to all individuals and companies who have a -copyright ownership of the Font Software. - -"Substantially Changed" refers to Modified Versions which can be easily -identified as dissimilar to the Font Software by users of the Font -Software comparing the Original Version with the Modified Version. - -To "Propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification and with or without charging -a redistribution fee), making available to the public, and in some -countries other activities as well. - -PERMISSION & CONDITIONS -This licence does not grant any rights under trademark law and all such -rights are reserved. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of the Font Software, to propagate the Font Software, subject to -the below conditions: - -1) Each copy of the Font Software must contain the above copyright -notice and this licence. These can be included either as stand-alone -text files, human-readable headers or in the appropriate machine- -readable metadata fields within text or binary files as long as those -fields can be easily viewed by the user. - -2) The font name complies with the following: -(a) The Original Version must retain its name, unmodified. -(b) Modified Versions which are Substantially Changed must be renamed to -avoid use of the name of the Original Version or similar names entirely. -(c) Modified Versions which are not Substantially Changed must be -renamed to both (i) retain the name of the Original Version and (ii) add -additional naming elements to distinguish the Modified Version from the -Original Version. The name of such Modified Versions must be the name of -the Original Version, with "derivative X" where X represents the name of -the new work, appended to that name. - -3) The name(s) of the Copyright Holder(s) and any contributor to the -Font Software shall not be used to promote, endorse or advertise any -Modified Version, except (i) as required by this licence, (ii) to -acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with -their explicit written permission. - -4) The Font Software, modified or unmodified, in part or in whole, must -be distributed entirely under this licence, and must not be distributed -under any other licence. The requirement for fonts to remain under this -licence does not affect any document created using the Font Software, -except any version of the Font Software extracted from a document -created using the Font Software may only be distributed under this -licence. - -TERMINATION -This licence becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF -COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER -DEALINGS IN THE FONT SOFTWARE. +------------------------------- +UBUNTU FONT LICENCE Version 1.0 +------------------------------- + +PREAMBLE +This licence allows the licensed fonts to be used, studied, modified and +redistributed freely. The fonts, including any derivative works, can be +bundled, embedded, and redistributed provided the terms of this licence +are met. The fonts and derivatives, however, cannot be released under +any other licence. The requirement for fonts to remain under this +licence does not require any document created using the fonts or their +derivatives to be published under this licence, as long as the primary +purpose of the document is not to be a vehicle for the distribution of +the fonts. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this licence and clearly marked as such. This may +include source files, build scripts and documentation. + +"Original Version" refers to the collection of Font Software components +as received under this licence. + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to +a new environment. + +"Copyright Holder(s)" refers to all individuals and companies who have a +copyright ownership of the Font Software. + +"Substantially Changed" refers to Modified Versions which can be easily +identified as dissimilar to the Font Software by users of the Font +Software comparing the Original Version with the Modified Version. + +To "Propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification and with or without charging +a redistribution fee), making available to the public, and in some +countries other activities as well. + +PERMISSION & CONDITIONS +This licence does not grant any rights under trademark law and all such +rights are reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of the Font Software, to propagate the Font Software, subject to +the below conditions: + +1) Each copy of the Font Software must contain the above copyright +notice and this licence. These can be included either as stand-alone +text files, human-readable headers or in the appropriate machine- +readable metadata fields within text or binary files as long as those +fields can be easily viewed by the user. + +2) The font name complies with the following: +(a) The Original Version must retain its name, unmodified. +(b) Modified Versions which are Substantially Changed must be renamed to +avoid use of the name of the Original Version or similar names entirely. +(c) Modified Versions which are not Substantially Changed must be +renamed to both (i) retain the name of the Original Version and (ii) add +additional naming elements to distinguish the Modified Version from the +Original Version. The name of such Modified Versions must be the name of +the Original Version, with "derivative X" where X represents the name of +the new work, appended to that name. + +3) The name(s) of the Copyright Holder(s) and any contributor to the +Font Software shall not be used to promote, endorse or advertise any +Modified Version, except (i) as required by this licence, (ii) to +acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with +their explicit written permission. + +4) The Font Software, modified or unmodified, in part or in whole, must +be distributed entirely under this licence, and must not be distributed +under any other licence. The requirement for fonts to remain under this +licence does not affect any document created using the Font Software, +except any version of the Font Software extracted from a document +created using the Font Software may only be distributed under this +licence. + +TERMINATION +This licence becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER +DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/VT323/OFL.txt b/Fonts/VT323/OFL.txt index e229272..2011c93 100644 --- a/Fonts/VT323/OFL.txt +++ b/Fonts/VT323/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2011, The VT323 Project Authors (peter.hull@oikoi.com) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2011, The VT323 Project Authors (peter.hull@oikoi.com) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Varela_Round/OFL.txt b/Fonts/Varela_Round/OFL.txt index cca0a98..a2dc7ea 100644 --- a/Fonts/Varela_Round/OFL.txt +++ b/Fonts/Varela_Round/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2023 The Varela Round Project Authors (https://github.com/alefalefalef/Varela-Round-Hebrew/), with Reserved Font Names 'Varela' and ‘Varela Round’. - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2023 The Varela Round Project Authors (https://github.com/alefalefalef/Varela-Round-Hebrew/), with Reserved Font Names 'Varela' and ‘Varela Round’. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Fonts/Xanh_Mono/OFL.txt b/Fonts/Xanh_Mono/OFL.txt index 06202ab..0766b08 100644 --- a/Fonts/Xanh_Mono/OFL.txt +++ b/Fonts/Xanh_Mono/OFL.txt @@ -1,93 +1,93 @@ -Copyright 2020 The XanhMono Project Authors (https://github.com/yellow-type-foundry/xanhmono). - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2020 The XanhMono Project Authors (https://github.com/yellow-type-foundry/xanhmono). + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/PLAN.md b/PLAN.md new file mode 100644 index 0000000..7843c92 --- /dev/null +++ b/PLAN.md @@ -0,0 +1,217 @@ +# Terminal Architecture Modernization Plan + +## Vision + +Deliver a SOLID, dependency-injected terminal stack free of legacy singletons: + +- Runtime core (log/history/shell) remains framework-agnostic and receives settings through well-defined interfaces. +- UI/presentation layers bind to runtime via injected services or ScriptableObject registries; no hard-coded `TerminalUI.Instance`. +- Editor tooling manipulates configuration assets rather than reaching into runtime objects. +- Tests target interfaces and profiles directly, eliminating “for tests” hooks. + +## Phase 0 – Baseline Audit (1 day) + +**Status:** _Working_ + +### 0.1 Inventory static/singleton entry points ✅ + +- `Runtime/CommandTerminal/UI/TerminalUI` exposes multiple globals: + - `Instance`, `ActiveTerminal`, `ActiveTerminals`, plus static provider hooks (`TerminalProvider`, `RuntimeConfigurator`, `InputProvider`, `RuntimeProvider`). + - Static `UnityLogCallback` registered via `Application.logMessageReceived`. +- `Runtime/CommandTerminal/UI/TerminalRegistry.Default` (and similar `*Proxy.Default` types) bake in lazy singletons for provider interfaces. +- `Runtime/CommandTerminal/Backend/TerminalRuntimeConfig` retains the legacy static configuration surface (`SetMode`, `EditorAutoDiscover`, `TryAutoDiscoverParsers`) with private fallback fields. +- `Runtime/CommandTerminal/Backend/TerminalRuntimeCache` holds pooled `TerminalRuntime` instances via static members. +- Editor-side tooling (`Editor/CustomEditors/TerminalUIEditor`, `Editor/LauncherLayoutDiagnostics`, `Editor/TerminalAssetPackPostProcessor`) declares additional static state and event hooks. +- Utility singletons (array pools, parser instances) are intentionally stateless helpers—note for dependency review but lower priority. + +### Next Steps (0.1) + +1. Map dependency graph (profiles → runtime → UI/editor) highlighting where Unity-specific code crosses layers. +2. Catalogue other legacy assumptions uncovered during the audit (e.g., `internal` serialized fields, editor accessors) for mitigation planning in later phases. + +### 0.2 Legacy coupling observations ✅ + +- `TerminalUI` exposes many `[SerializeField] internal` members (`_runtime`, `_commandProfile`, `_fontPack`, etc.) relied upon by editor tooling and tests, reinforcing tight coupling. +- Editor scripts (`TerminalUIEditor`, diagnostics utilities) invoke `internal` setters (`SetBlockedCommandsForTests`, `_terminalContainer`, etc.) and manipulate runtime collections directly, bypassing encapsulation. +- `TerminalRuntimeCache` and `TerminalRuntimeConfig` maintain static fallback state that bypasses profile-driven configuration and can reintroduce singleton behaviour even if providers are swapped. +- Tests depend on `ConfigureForTests` helpers and other backdoor APIs, signalling the need for DI-friendly constructors and factories in subsequent phases. + +### Next Steps (0.2) + +Begin Phase 0.3: produce a lightweight dependency map showing flow between configuration assets, runtime services, UI, and editor tooling to inform Phase 1 refactors. + +### 0.3 Dependency Map ✅ + +#### Configuration Assets / Profiles + +- `TerminalRuntimeProfile`, `TerminalCommandProfile`, `TerminalInputProfile`, `TerminalAppearanceProfile`, `TerminalThemePersistenceProfile`. +- Aggregated ad hoc inside `TerminalUI`; editor scripts reach directly into serialized fields. + +#### Runtime Core + +- `TerminalRuntime` orchestrates `CommandLog`, `CommandHistory`, `CommandShell`, `CommandAutoComplete`. +- `TerminalRuntimeCache` (static) optionally stores runtime instances for reuse. +- `TerminalRuntimeConfig` exposes static configuration (mode flags, parser autodiscovery). + +#### Presentation Layer + +- `TerminalUI` MonoBehaviour owns serialized references to profiles, runtime containers, input controllers, layout metrics, etc. +- `TerminalKeyboardController`, presenters (`LauncherViewController`, `TerminalUI.LogView`, etc.) are partial classes on `TerminalUI`. +- `TerminalUI` interacts with runtime via static providers (`TerminalRuntimeProvider`, `TerminalRuntimeConfigurator`). + +#### Editor Tooling + +- `TerminalUIEditor` manipulates `TerminalUI` serialized/internal fields; registers menu items. +- Diagnostics (`LauncherLayoutDiagnostics`, `TerminalRuntimeInspectorWindow`) subscribe to static events and reference `TerminalUI`. +- Asset post-processors create theme/font packs using static lists. + +#### Key Flows + +1. Scene/Prefab serialized `TerminalUI` references profiles → `TerminalUI` builds `TerminalRuntimeSettings` → calls static providers to configure `TerminalRuntime`. +2. Editor scripts mutate `TerminalUI` internal state directly, bypassing profiles and services. +3. Tests consume `TerminalUI` and runtime via “ForTests” setters, implying tight coupling. + +#### Implications + +- Phases 1–2 must introduce service factories/DI to isolate runtime from `TerminalUI`. +- Need aggregated configuration asset to replace scattered serialized fields. +- Editor tooling should pivot to profile-focused inspectors once runtime wiring is refactored. + +### Next Steps (0.3) + +Transition to Phase 1: define interfaces/factories to decouple runtime creation (`ITerminalRuntimeFactory`, `ITerminalSettingsProvider`) and plan removal of static caches/configs. + +### Phase 1 Prep – Interface & Seam Identification ✅ + +#### Static entry points earmarked for replacement + +- `TerminalUI.Instance`, `ActiveTerminal`, `ActiveTerminals` → replace with `ITerminalProvider` instances resolved via injected or serialized service registry. +- Provider proxies (`TerminalRegistry.Default`, `TerminalRuntimeConfiguratorProxy.Default`, `TerminalRuntimeProviderProxy.Default`, `TerminalInputProviderProxy.Default`) → convert into ScriptableObject assets or DI registrations that honour per-scene configuration. +- `TerminalRuntimeCache` → remove or hide behind `ITerminalRuntimeFactory` that manages pooling explicitly; eliminate static reuse. +- `TerminalRuntimeConfig` static surface → wrap in `ITerminalRuntimeConfigurator` backed by configuration assets; deprecate global fallback fields. +- Editor diagnostics hooking static events (`LauncherLayoutDiagnostics`, `TerminalRuntimeInspectorWindow`) → rework to subscribe via provider interfaces once DI is in place. + +#### Proposed interface seams + +- `ITerminalSettingsProvider`: supplies immutable runtime settings (`TerminalRuntimeSettings`) per terminal instance; implemented by a new `TerminalConfigurationAsset` or `TerminalConfigurationComponent`. +- `ITerminalRuntimeFactory`: responsible for creating/configuring `ITerminalRuntime` given an `ITerminalSettingsProvider` plus optional services (logging, time, diagnostics). +- `ITerminalServiceLocator`: ScriptableObject/MonoBehaviour that exposes configured providers to both runtime and editor tooling (small DI container). +- `ITerminalLog`, `ITerminalShell`, `ITerminalHistory` (optional wrappers) to hide concrete types if further decoupling required. + +#### Separation plan + +- Phase 1.1: Introduce `ITerminalSettingsProvider` + `TerminalRuntimeFactory` alongside existing static creation. TerminalUI swaps to new factory; static pathways left for backward compat temporarily. +- Phase 1.2: Refactor provider proxies to resolve from `ITerminalServiceLocator`; deprecate static `Default` singletons. +- Phase 1.3: Remove `TerminalRuntimeCache` & static config fallbacks, ensuring tests use factories rather than backdoor helpers. + +### Next Steps (Phase 1 Prep) + +- Kick off Phase 1.1 by scaffolding `ITerminalSettingsProvider` and `TerminalRuntimeFactory` interfaces/classes. +- Update tests to construct runtime via the new factory to validate the seam before removing static singletons. + +## Phase 1 – Core Runtime Decoupling (3–4 days) + +1. Extract interfaces: + - `ITerminalRuntimeFactory`, `ITerminalLog`, `ITerminalShell`. + - `ITerminalSettingsProvider` (wrap `TerminalRuntimeSettings` / profiles). +2. Convert `TerminalRuntime`/friends to pure C# services: + - Constructors accept `ITerminalSettingsProvider` and optional dependencies (`ITimeProvider`, `ILogger`). + - Replace scratch lists with pooled structures or dedicated `FilterState` structs. +3. Introduce DI-friendly bootstrapper (ScriptableObject or MonoBehaviour) that instantiates runtime services per terminal instance. + +## Phase 2 – UI Presenter Refactor (4–5 days) + +1. Split `TerminalUI` into: + - `TerminalView` (MonoBehaviour managing UIDocument bindings). + - `TerminalController` (non-Mono class handling mode transitions, animations, fade logic). + - `TerminalServiceConnector` (wires controller to DI container). +2. Replace static `TerminalUI.Instance` with: + - `ITerminalProvider` registered via serialized reference (ScriptableObject asset or Scene service locator). + - Views request providers via serialized field or `FindObjectOfType()` (temporary). +3. Update inspector/editor scripts to work against profiles and view components; remove direct field access to runtime internals. + +## Phase 3 – Configuration & Profiles (2–3 days) + +1. Formalize “chunks”: + - `TerminalRuntimeProfile` handles capacities + filters. + - `TerminalInputProfile`, `TerminalAppearanceProfile`, `TerminalThemePersistenceProfile` already exist; add `TerminalAnimationProfile` if needed. +2. Create a `TerminalConfigurationAsset` ScriptableObject aggregating active profiles; `TerminalServiceConnector` consumes this. +3. Provide migration tool/editor menu: convert legacy serialized fields to new asset references. + +## Phase 4 – Editor Tooling Overhaul (3 days) + +1. Replace `TerminalUIEditor` with: + - Profile-specific custom editors (`TerminalRuntimeProfileEditor`, `TerminalCommandProfileEditor`). + - Lightweight `TerminalViewEditor` for layout/preview controls only. +2. Implement runtime dashboards (e.g., `TerminalRuntimeInspector`) that operate via public interfaces rather than internal fields. +3. Update diagnostics toggles/logging to route through service interfaces. + +## Phase 5 – Testing & CI Updates (2 days) + +1. Rewrite unit tests to construct runtime services via interfaces and test fixtures. +2. Introduce integration tests for: + - Dependency injection end-to-end (instantiate TerminalConfigurationAsset, spawn TerminalView, assert behaviours). + - Editor-time profile editing (using UnityEditor tests). +3. Update CI scripts to run new test suites; ensure coverage remains high. + +## Phase 6 – Cleanup & Release Prep (1–2 days) + +1. Remove deprecated `*ForTests` APIs and obsolete fields. +2. Update README/CHANGELOG to reflect new architecture and migration path. +3. Provide upgrade guide for users migrating from singleton-based access to service-based approach. + +## Sequencing Notes + +- Phase 1 dependency: None (can start immediately). +- Phase 2 depends on Phase 1 interfaces being available. +- Phase 3 requires Phase 2 connectors to consume new configuration asset. +- Phases 4 and 5 can run in parallel once phases 1–3 stabilize. +- Each phase should conclude with regression tests + documentation updates. + +### Phase 1.1 Progress (Completed) + +- Introduced ITerminalSettingsProvider and ITerminalRuntimeFactory interfaces, plus default TerminalRuntimeFactory implementation. +- Added TerminalConfigurationAsset ScriptableObject wrapping existing runtime profile to bridge new provider pipeline. +- TerminalUI now acquires runtime instances through ResolveRuntimeFactory/SettingsProvider, defaulting to new factory while keeping TerminalRuntimeCache compatibility. +- Updated serialized state to track allowed/blocked command/log lists accordingly. +- Added test-only seams on TerminalUI so runtime factories can be injected and settings provider selection can be observed. +- Extended playmode tests to validate configuration-asset, runtime-profile, and default-provider paths; tear-down now cleans configuration assets to isolate cases. + +### Phase 1.2 Progress (In Progress) + +- Introduced ITerminalServiceLocator with default and mutable implementations to broker provider access. +- TerminalUI now resolves ITerminalProvider/ITerminalRuntimeConfigurator/ITerminalInputProvider/ITerminalRuntimeProvider through the locator, keeping legacy static setters as thin shims. +- Added runtime tests verifying locator overrides and mutable fallbacks so future DI work has safety nets. +- Added TerminalServiceBindingAsset ScriptableObject plus a scene-level TerminalServiceBindingComponent so bindings can be serialized or applied automatically. TerminalUI now falls back to the global TerminalServiceBindingSettings default when no local binding is provided. +- TerminalUIEditor now provisions service binding/settings assets, attaches a binding component on the same prefab, and exposes inspector summaries with quick navigation so dependency wiring is visible and consistent by default. +- Editor diagnostics now read runtime data through the locator-backed scope, and runtime provider/scope abstractions no longer depend on the legacy `Terminal` static (now marked obsolete). +- Built-in commands now consume the locator-backed runtime scope (`ITerminalServiceLocator` + `ITerminalRuntimeScope`) instead of hitting `Terminal.*` singletons directly, paving the way for Editor/runtime tooling migration. +- Drafted ITerminalRuntimeScope and ITerminalRuntimeConfiguratorService interfaces with default adapters so existing static surfaces can be composed through the locator. TerminalUI now delegates runtime registration/logging to the scoped service. +- TerminalUI lifecycle now clears and returns runtime instances through the locator-provided `ITerminalRuntimePool`, only falling back to the legacy cache when no pool is present so existing overrides continue to function. +- TerminalRuntimeScope now bridges registration back into the deprecated `Terminal` facade so existing editor tooling and tests relying on static access remain functional while new consumers adopt the locator. +- Static dependency audit highlights remaining targets: + - `Terminal` static facade still mirrors runtime state; we need to migrate remaining direct callers onto the locator-driven scope and ultimately remove the facade. + - `TerminalRuntimeConfig` retains static setters/getters plus fallback fields; `TerminalRuntimeConfiguratorProxy` depends on these and will need a service-backed configuration asset. + - `TerminalRuntimeCache` now serves purely as a compatibility fallback; once pool-backed tests land we can remove the remaining references. + - Editor utilities (`TerminalUIEditor`, diagnostics windows) set/reset `TerminalUI.Instance` and static providers directly; they should be updated to request the locator or serialized references instead. +- Runtime test shims now expose the locator-provided runtime pool (`StubRuntimePool`) so the suite compiles against the expanded interface surface and continues validating locator overrides. +- Playmode harness (`TestRuntimeScope`) now routes launcher UI assertions through `TerminalUI`'s existing test hooks instead of relying on internal/runtime scope knowledge, keeping coverage intact while respecting the new locator boundaries. +- Appearance-profile assertions in playmode tests now pull history fade targets and Unity-log capture status through `TerminalUI`'s public test hooks, eliminating the last direct scope-only reads in the harness and aligning them with the locator-driven architecture. +- Service locator regression tests inject stub runtime pools and assert binding overrides against the asset itself, ensuring the expanded interface surface and binding workflow stay covered by unit tests. + +#### TerminalRuntimeCache Retirement Draft + +- ✅ Introduce an `ITerminalRuntimePool` abstraction exposed via the service locator so runtime reuse becomes an injectable concern. +- ✅ Provide a default pool implementation that mirrors current cache semantics while honouring scene/prefab overrides. +- ✅ Update `TerminalUI` (and tests) to obtain runtimes from the pool rather than the static cache, ensuring deterministic lifetimes. +- Mark `TerminalRuntimeCache` obsolete with a transition shim delegating to the pool, then remove once all call sites migrate. +- Add regression tests covering pooled reuse/disposal scenarios to guard against regressions when the cache is retired. + +Next: + +- Capture runtime pooling behaviour in playmode tests (rent/return cycle, reset clearing). +- Migrate remaining `Terminal.*` call sites to request `ITerminalRuntimeScope` from the service locator so the facade can be retired safely. +- Update configurator proxies and editor tooling to consume locator-provided services instead of static fallbacks. +- Plan removal window for `TerminalRuntimeCache` fallback once consumers and tests migrate. +- Audit remaining test helpers for `Terminal` static usage and transition them to locator-backed access to unblock facade removal. +- Extend editor tooling coverage to exercise the binding asset/component flow, validating the new pool injection path in the editor pipeline. diff --git a/PLAN.md.meta b/PLAN.md.meta new file mode 100644 index 0000000..09aa529 --- /dev/null +++ b/PLAN.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b215f2943aa6f434ea64ce6d734e5d64 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plans.meta b/Plans.meta new file mode 100644 index 0000000..4b492c6 --- /dev/null +++ b/Plans.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f1c2bf2ef8b84b97a4bec9fe0f5bbe60 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plans/2024-05-GEOMETRY-REFINE.md b/Plans/2024-05-GEOMETRY-REFINE.md new file mode 100644 index 0000000..3d13488 --- /dev/null +++ b/Plans/2024-05-GEOMETRY-REFINE.md @@ -0,0 +1,5 @@ +# Geometry Measurement Refactor Notes + +- Launcher layout now delegates suggestion reservation and history clamping to `LayoutMeasurementUtility`. +- Next step: apply utility to standard terminal measurements (if any height heuristics remain) and audit animation entry points. +- Future idea: expose measurement snapshots for unit testing outside launcher. diff --git a/Plans/2024-05-GEOMETRY-REFINE.md.meta b/Plans/2024-05-GEOMETRY-REFINE.md.meta new file mode 100644 index 0000000..3663448 --- /dev/null +++ b/Plans/2024-05-GEOMETRY-REFINE.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 604f36a64fb1e558caa8180889c55ae0 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plans/2024-05-SINGLETON-AUDIT.md b/Plans/2024-05-SINGLETON-AUDIT.md new file mode 100644 index 0000000..5214572 --- /dev/null +++ b/Plans/2024-05-SINGLETON-AUDIT.md @@ -0,0 +1,15 @@ +# Singleton Audit Notes + +## Candidates to Replace or Wrap + +- `TerminalUI.Instance` – now backed by `ITerminalProvider`; further work: remove direct usages in tests and utilities. +- `Terminal.ActiveRuntime` – consider exposing through provider or runtime service locator. +- `DefaultTerminalInput.Instance` – expose through `ITerminalInputProvider` when wiring `TerminalUI`. +- `TerminalRuntimeConfig` static helpers – evaluate whether configuration can be injected per terminal or via ScriptableObject references. + +## Follow-up Ideas + +- Provide onboarding docs demonstrating how to swap out the provider in multi-terminal scenes. +- Add editor diagnostics to highlight when multiple terminals are active and which one is considered primary. + +- Added ITerminalRuntimeConfigurator to make runtime config injectable; remaining statics to evaluate. diff --git a/Plans/2024-05-SINGLETON-AUDIT.md.meta b/Plans/2024-05-SINGLETON-AUDIT.md.meta new file mode 100644 index 0000000..71efb36 --- /dev/null +++ b/Plans/2024-05-SINGLETON-AUDIT.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e8a531b73af67ee09b45bb28ae99ed6a +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/README.md b/README.md index f909d9c..345ae3d 100644 --- a/README.md +++ b/README.md @@ -1,48 +1,53 @@ -DX Command Terminal -====================== +# DX Command Terminal + +## CI/CD Status -# CI/CD Status ![Npm Publish](https://github.com/wallstop/dxcommandterminal/actions/workflows/npm-publish.yml/badge.svg) -# Notice +## Notice This is a fork of [Command Terminal](https://github.com/stillwwater/command_terminal) for Unity, mainly to address usability gaps and add maintenance -# Compatibility -| Platform | Compatible | -| --- | --- | +## Compatibility + +| Platform | Compatible | +| ---------- | -------------------- | | Unity 2021 | Likely, but untested | -| Unity 2022 | ✓ | -| Unity 2023 | ✓ | -| Unity 6 | ✓ | -| URP | ✓ | -| HDRP | ✓ | +| Unity 2022 | ✓ | +| Unity 2023 | ✓ | +| Unity 6 | ✓ | +| URP | ✓ | +| HDRP | ✓ | -# Installation +## Installation ## From Releases + Check out the latest [Releases](https://github.com/wallstop/DxCommandTerminal/releases) to grab the Unity Package and import to your project. ## To Install as Unity Package + 1. Open Unity Package Manager 2. (Optional) Enable Pre-release packages to get the latest, cutting-edge builds 3. Open the Advanced Package Settings 4. Add an entry for a new "Scoped Registry" - - Name: `NPM` - - URL: `https://registry.npmjs.org` - - Scope(s): `com.wallstop-studios.dxcommandterminal` + - Name: `NPM` + - URL: `https://registry.npmjs.org` + - Scope(s): `com.wallstop-studios.dxcommandterminal` 5. Resolve the latest `com.wallstop-studios.dxcommandterminal` ## From Source + Grab a copy of this repo (either `git clone` or [download a zip of the source](https://github.com/wallstop/DxCommandTerminal/archive/refs/heads/master.zip)) and copy the contents to your project's `Assets` folder. ## Improvements Over Baseline + - [Enhanced Auto-Complete + Hint system + styling](#hints) - Fixed Input handling bugs related to [WebGL](#web-gl) - Fully integrated with Unity's [new Input System](#new-input-system) - Fully [configurable and bindable controls](#hotkeys) for every action -- Add ability to ignore commands that have been annotated with `RegisterCommandAttribute`. In this way, your terminals can ignore any built-in commands, for cleanliness. A custom editor has been added to provide users with the ability to identify what commands are available to ignore, and selectively ignore them. -- Add ability to ignore certain (or all) log levels, such that unwanted logs do not clutter terminal output +- Add ability to block or explicitly allow commands that have been annotated with `RegisterCommandAttribute`. In this way, your terminals can trim or whitelist built-in commands for cleanliness. A custom editor has been added to provide users with the ability to identify what commands are available to allow/block, and selectively configure them. +- Add ability to block or allow specific log levels via profile driven filters so only relevant logs are routed into the terminal buffer. - Add ability to optionally have Unity log messages routed to the terminal, default on, but can be turned off - Add ability to optionally ignore all default commands, such that you can write your own commands that conflict with the default provided ones, such as `HELP`, `TIME`, etc - CommandArg value parsing has been overhauled. Instead of specific properties like `Float` and `Int` that always return a value, regardless of if parsing succeeded or not, a singular `bool TryGet(out T parsed)` method is supplied. This method provides robust parsing for all common types and has the ability to take in user-specified custom parsers for any fancy logic. See [Custom Parsing](#custom-parsing) for details. @@ -53,6 +58,21 @@ Grab a copy of this repo (either `git clone` or [download a zip of the source](h - Fixed a bug where only the latest error message was preserved - errors are now queued - Fixed a bug where attempting to access static `Terminal` properties would throw if the Terminal had been enabled yet. - Fixed a bug where moving through command history did not update the cursor position + +## Architecture Notes + +### Terminal Provider + +Terminals register themselves with an `ITerminalProvider` (`TerminalRegistry` by default). Use `TerminalUI.ActiveTerminal` or `TerminalUI.ActiveTerminals` instead of touching the singleton directly. Consumers that need custom lifetimes can swap the provider at runtime or in tests to manage their own registry of terminals. + +### Runtime Configuration + +### Input Provider + +`TerminalUI` obtains its input via an `ITerminalInputProvider`. The default proxy returns `DefaultTerminalInput.Instance`, but you can supply your own provider (e.g., during tests or to integrate with a custom input system) by assigning `TerminalUI.InputProvider`. + +`TerminalUI` resolves runtime modes through an `ITerminalRuntimeConfigurator`. The default proxy wraps the legacy static `TerminalRuntimeConfig`, but you can inject your own implementation (for example, in tests or specialized runtime scenarios) via `TerminalUI.RuntimeConfigurator`. + - Fixed a bug where enabling and disabling the Terminal would break AutoComplete - Fixed a bug where you could interact with the terminal when it was in closed state - Fixed a bug where commands run programmatically were not added to history @@ -73,7 +93,31 @@ Grab a copy of this repo (either `git clone` or [download a zip of the source](h - Extra input validation has been added on all public methods, such that user code is sanitized where appropriate, or rejected if invalid. - The concept of "FrontCommands" has been exterminated +## Modern Architecture Highlights (2024 Refactor) + +- **Instance-based runtime** – `TerminalRuntime` now owns log/history/autocomplete state per terminal instance. The legacy static façade delegates to the active runtime, enabling multiple terminals in the same scene and cleaner testing. +- **Profile-driven configuration** – `TerminalRuntimeProfile` ScriptableObjects capture buffer capacities plus command/log allow and block lists. Terminals can reuse or swap profiles without code changes. +- **Modular UI presenters** – The 3.6k line `TerminalUI` has been split into partials (`TerminalUI.LogView`, `TerminalUI.AutoCompleteView`, `TerminalUI.LayoutView`) that focus on specific responsibilities while the core MonoBehaviour handles lifecycle and runtime wiring. +- **Input abstraction** – `TerminalKeyboardController` targets the new `ITerminalInputTarget` interface, so custom terminals or headless tests can drive command execution without a concrete `TerminalUI`. Playmode tests confirm dispatch behaviour and fallback to the built-in UI. +- **Profile-driven input** – `TerminalInputProfile` ScriptableObjects package hotkeys and control ordering so multiple controllers can share consistent bindings. +- **Appearance presets** – `TerminalAppearanceProfile` captures button labels, hint behaviour, history fade, and cursor settings to standardise the terminal look across scenes. +- **Command/persistence profiles** – `TerminalCommandProfile` exposes allow/block filters while `TerminalThemePersistenceProfile` handles theme persistence toggles without code changes. +- **Runtime inspector** – Editor window `Terminal Runtime Inspector` (Window ▸ DX Command Terminal) surfaces active runtime state for debugging. +- **Editor interoperability** – Serialized-property utilities expose an override hook so editor drawers (like `DxShowIfPropertyDrawer`) can access backing objects without relying on runtime internals, preserving assembly boundaries. +- **Allocation guardrails** – An automated playmode test (`AllocationRegressionTests`) monitors `GC.Alloc` while issuing commands and toggling terminal state to catch regressions immediately. + +### Test Coverage Snapshot + +- `TerminalRuntimeTests` validate runtime reuse/reset logic across multiple terminal spawns. +- `TerminalKeyboardControllerTests` exercise the new input abstraction path with both mocked targets and real `TerminalUI` fallbacks. +- `AllocationRegressionTests` enforce zero-allocation behaviour during command spam, history navigation, and resize toggles. +- The existing parsing/history/builtin command suites remain intact, ensuring command semantics and log persistence continue to function after refactors. +- `TerminalTests` now verify runtime, appearance, and command profiles override serialized defaults end-to-end. + +For a deeper look at ongoing modernization goals, check `PLAN.md`. + ## Code changes + - All code is formatted via [Csharpier](https://csharpier.com/) - All variables are now consistently named - Access modifiers have been explicitly applied to every field @@ -85,9 +129,11 @@ Grab a copy of this repo (either `git clone` or [download a zip of the source](h - Validation around command ignoring and log level ignoring has been added to Terminal, to prevent invalid data ## The Future + More improvements coming soon, stick around :) Planned improvements: + - More and better documentation - Wikification - A `command bar` for quick commands, instead of waiting for a terminal to fold into the screen @@ -116,7 +162,7 @@ Enter `help` in the console to view all available commands, use the up and down There are 2 options to register commands to be used in the Command Terminal. -### 1. Using the RegisterCommand attribute: +### 1. Using the RegisterCommand attribute The command method must be static (public or non-public). @@ -132,6 +178,7 @@ static void CommandAdd(CommandArg[] args) { Terminal.Log("{0} + {1} = {2}", a, b, result); } ``` + `MinArgCount` and `MaxArgCount` allows the Command Interpreter to issue an error if arguments have been passed incorrectly, this way you can index the `CommandArg` array, knowing the array will have the correct size. In this case the command name (`add`) will be inferred from the method name, you can override this by setting `Name` in `RegisterCommand`. @@ -140,7 +187,7 @@ In this case the command name (`add`) will be inferred from the method name, you [RegisterCommand(Name = "MyAdd", Help = "Adds 2 numbers", MinArgCount = 2, MaxArgCount = 2)] ``` -### 2. Manually adding Commands: +### 2. Manually adding Commands `RegisterCommand` only works for static methods. If you want to use a non-static method, you may add the command manually. @@ -150,7 +197,8 @@ Terminal.Shell.AddCommand("add", CommandAdd, 2, 2, "Adds 2 numbers"); --- -# Custom Parsing +## Custom Parsing + One change from the original Command Parser is the usage / functionality exposed for parsing the parameters to the CommandArgs themselves. The original library exposed four methods for retrieving arguments - `String`, `Float`, `Int`, and `bool`. If the input was invalid, these parsing methods would simply return the default value and log an error, making it very difficult to programmatically react to bad input. To remedy this, DxCommandTerminal exposes a single method, `TryGet`, and a readonly string field `contents`. `contents` returns the original input parameters as-is. `TryGet` attempts to parse the provided input parameter as the given type, taking in an optional parser. You can also register and deregister parsers for any type, which will override the built-in ones, making use of whatever custom logic you want (JSON, for example). @@ -177,6 +225,7 @@ Assert.IsFalse(arg.TryGet(out int invalidInt)); // Failed to parse ``` `TryGet` supports the following types out of the box: + - string - char - bool @@ -197,6 +246,7 @@ Assert.IsFalse(arg.TryGet(out int invalidInt)); // Failed to parse - Enums **Unity Types**: + - Vector2 - Vector3 - Vector4 @@ -214,6 +264,7 @@ In addition to parsing values directly, `TryGet` will automatically attempt to m - "MinValue" Similarly, for Colors: + - "RGBA(0.7, 0.5, 0.1, 1.0)" - "(0.7, 0.5, 0.1, 1.0)" - "(0.7, 0.5, 0.1)" @@ -225,6 +276,7 @@ For all built-in types, the parsers are guaranteed to work with the type's defau If you would like to have built-in parsers for a type that is not listed above, please open an issue! ## Advanced Parsing - Custom Parsers + `TryGet` has an overload that takes a nullable `CommandArgParser`, which is a function with the following definition: ```csharp @@ -277,11 +329,97 @@ or even int CommandArgParser.UnregisterAllParsers(); ``` -All of these unregistration functions will return you information on whether the unregistration functions were successful. +All of these unregistration functions will return you information on whether the unregistration functions were successful. Note: Built in parser functions cannot be unregistered. +### Object Parsers (IArgParser) + +For stronger typing and lower allocations, you can also use object parsers that implement `IArgParser`. + +- Built-ins: All common numeric, date/time, IP, and Unity types ship with sealed parsers and public singletons, e.g. `IntArgParser.Instance`, `Vector3ArgParser.Instance`, `ColorArgParser.Instance`. +- Registration: Register any custom parser once at startup. + +```csharp +using WallstopStudios.DxCommandTerminal.Backend.Parsers; + +// Register built-in or custom parsers +CommandArg.RegisterObjectParser(IntArgParser.Instance); // int +CommandArg.RegisterObjectParser(Vector3ArgParser.Instance); // UnityEngine.Vector3 + +// Or discover all IArgParser implementations across loaded assemblies +int discovered = CommandArg.DiscoverAndRegisterParsers(replaceExisting: false); +``` + +Create your own low-allocation parser by deriving from `ArgParser`: + +```csharp +public sealed class PathArgParser : ArgParser +{ + public static readonly PathArgParser Instance = new PathArgParser(); + protected override bool TryParseTyped(string input, out System.IO.FileInfo value) + { + if (string.IsNullOrWhiteSpace(input)) { value = null; return false; } + value = new System.IO.FileInfo(input.Trim()); + return true; + } +} + +// Register once (e.g., game init) +CommandArg.RegisterObjectParser(PathArgParser.Instance); + +// Usage in a command +public static void LoadFile(CommandArg a) +{ + if (!a.TryGet(out System.IO.FileInfo file)) { Terminal.LogError("Invalid path"); return; } + // ... +} +``` + +Enum parsing uses a hot path with cached name and ordinal lookups (`EnumArgParser`) for fast, case-insensitive matching and integer ordinals. + +Editor convenience + +- In the Unity Editor, parsers are auto-discovered on domain reload to ease development (`Editor/Parsers/ParserAutoDiscovery.cs`). This does not affect players. +- Inspect what parsers are currently registered: + +```csharp +var types = CommandArg.GetRegisteredObjectParserTypes(); +foreach (var t in types) UnityEngine.Debug.Log($"Parser for type: {t}"); +``` + +Static members parsing + +- Constant/Property name parsing (e.g., `MaxValue`, `IPAddress.Any`) is handled by dedicated static-member parsers and no longer lives inside `CommandArg`. + +## Runtime Modes & Editor Toggle + +DxCommandTerminal exposes a runtime mode enum to control environment-specific behavior (e.g., parser auto-discovery), plus an Editor toggle wired through the Terminal component. + +- Enum: `TerminalRuntimeModeFlags` (flags, explicit numeric values) + + - `None` (0) — Obsolete; choose explicit modes. + - `Editor` (1) — Enable editor-only features (when running in Editor). + - `Development` (2) — Enable features only for development builds. + - `Production` (4) — Enable features only for non-development builds. + - `All` (7) — Enable all. + +- Configure modes on `TerminalUI` via the `Runtime Mode Options` list: + + - Define one or more options (identifier, label, flags) to describe your environments. + - `Selected Runtime Mode Id` chooses which option applies when the terminal initializes. + - The legacy `Runtime Mode` enum field remains as a fallback for existing assets. + - `Editor > Auto-Discover Parsers` still toggles automatic parser discovery when the `Editor` flag is active. + +- Programmatic checks (no allocations): + - `TerminalRuntimeConfig.HasFlagNoAlloc(value, flag)` bit-tests without boxing. + - `TerminalRuntimeConfig.ShouldEnableEditorFeatures()` respects `Editor` flag and `UNITY_EDITOR`. + - `TerminalRuntimeConfig.ShouldEnableDevelopmentFeatures()` respects `Development` and `Debug.isDebugBuild`. + - `TerminalRuntimeConfig.ShouldEnableProductionFeatures()` respects `Production` and non-development builds. + - `TerminalRuntimeConfig.TryAutoDiscoverParsers()` conditionally registers all IArgParser implementations based on mode + toggle. + ## Advanced Parsing - Changing Control Sets + By default, command parameter input is stripped of whitespace characters. This, along with several other parsing-specific behaviors, are controlled via public static sets on `CommandArg` itself. If you would like to change this behavior in your code, you can modify the contents of these sets to be whatever you'd like. Below are a description of the sets and what they control. **Delimiters**: For complex, multi-value types like `Vector2`, `Quaternion`, `Color`, etc, the parameter is split using the first match, if any, found in this set. ie, `1,2,3` will get split into the array `["1", "2", "3"]` for parsing @@ -294,22 +432,26 @@ By default, command parameter input is stripped of whitespace characters. This, **IgnoredValuesForComplexTypes**: For complex, multi-value types like `Vector2`, `Quaternion`, `Color`, etc, the parameter input will replace all strings found in this set with the empty string. -# Hotkeys +## Hotkeys + All actions are now fully configurable by either explicit keybindings, for keyboard controls, or via [new Input System](#new-input-system) bindings. ![png](./Media/Hotkeys.png) Keyboard hotkey bindings are now intelligent as they can be about shift key interaction. There are three ways to create a `shift+`: + 1. Prefix the binding with the `#` symbol. For example, `#tab` will be interpreted as `shift+tab` 2. For keys with shifted versions, such as alpha numeric keys, simply use the shifted version. For example, `A` will be interpreted as `shift+a` 3. When using the new input system, hotkeys can be represented as `shift+`. `shift+tab` will be interpreted as the combination `left shift` + `tab`. The only combination keys that are supported without using custom bindings via the new Input System are `shift+`. -# New Input System -DxCommandTerminal is now fully integrated with Unity's new Input System, if it is found in the project and enabled. +## New Input System + +DxCommandTerminal is now fully integrated with Unity's new Input System, if it is found in the project and enabled. You can also use `PlayerInput` or similar to bind InputActions to all available command terminal behavior. The available message are: + - `HandlePrevious`: Selects the previously issued command form current command history location. - `HandleNext`: Selects the next issued command from current command history location. - `Close`: If the terminal is open, closes it. @@ -320,9 +462,11 @@ You can also use `PlayerInput` or similar to bind InputActions to all available - `EnterCommand`: Takes the current buffer and attempts to execute it as a command + parameters. ## Note + If using PlayerInput to bind to the above controls, you will need to uncheck `Use Hotkeys` under the `Hotkeys` header in the Terminal script. When InputActions are not bound, there is an order of precedence for input checking. It is: + - Close - Enter Command - Previous @@ -334,14 +478,16 @@ When InputActions are not bound, there is an order of precedence for input check This order is irrelevant when using PlayerInput. -# Web GL +## Web GL + If you are relying on `RegisterCommandAttribute` to wire up your commands to the CommandShell instead of manually registering them, you will need to set `Managed Stripping Level` to `Low`, `Minimal`, or `None` under `Player > WebGL > Other Settings > Optimizations > Managed Stripping Level` in order for command registration to work. Settings of `Medium` or higher will break the reflection code that loads the commands, causing the terminal to forget about its capabilities. ![png](./Media/ManagedStrippingLevel.png) See [Unity docs on Managed Stripping Level](https://docs.unity3d.com/2022.3/Documentation/ScriptReference/ManagedStrippingLevel.html) for more details. -# Hints +## Hints + AutoComplete has gotten a major upgrade in this fork. Completion is now not only case-insensitive, but it will now also search (unique) commands that have been executed, ignoring any irrelevant input. Pressing the complete key multiple times now selects available options in a persistent fashion. Completion can be walked both forward and backwards. Results are now presented in a new UI that intelligently adapts to screen space and current selection position. When completion is no longer relevant, the UI is disabled. However, you can opt to always show the available commands by toggling the new `Display Hints` option in the Terminal configuration. There are also several new theming options for hints, with controls over the currently selected hint v unselected hints. ![png](./Media/AutoComplete.png) diff --git a/Runtime/AssemblyInfo.cs b/Runtime/AssemblyInfo.cs new file mode 100644 index 0000000..eaf478c --- /dev/null +++ b/Runtime/AssemblyInfo.cs @@ -0,0 +1,5 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("WallstopStudios.DxCommandTerminal.Tests.Runtime")] +[assembly: InternalsVisibleTo("WallstopStudios.DxCommandTerminal.Editor")] +[assembly: InternalsVisibleTo("WallstopStudios.DxCommandTerminal.Tests")] diff --git a/Runtime/AssemblyInfo.cs.meta b/Runtime/AssemblyInfo.cs.meta new file mode 100644 index 0000000..4149535 --- /dev/null +++ b/Runtime/AssemblyInfo.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4cbbf6127c01481d9ba98e9bad59e641 +timeCreated: 1760315470 \ No newline at end of file diff --git a/Runtime/Attributes/CommandCompleterAttribute.cs b/Runtime/Attributes/CommandCompleterAttribute.cs new file mode 100644 index 0000000..62eb0bf --- /dev/null +++ b/Runtime/Attributes/CommandCompleterAttribute.cs @@ -0,0 +1,34 @@ +namespace WallstopStudios.DxCommandTerminal.Attributes +{ + using System; + using Backend; + + /// + /// Attach to a command method to specify a dynamic argument completer. + /// Type must implement IArgumentCompleter and have either a public parameterless + /// constructor or a public static Instance property/field. + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class CommandCompleterAttribute : Attribute + { + public Type CompleterType { get; } + + public CommandCompleterAttribute(Type completerType) + { + if (completerType == null) + { + throw new ArgumentNullException(nameof(completerType)); + } + + if (!typeof(IArgumentCompleter).IsAssignableFrom(completerType)) + { + throw new ArgumentException( + $"{completerType.FullName} must implement {nameof(IArgumentCompleter)}", + nameof(completerType) + ); + } + + CompleterType = completerType; + } + } +} diff --git a/Runtime/Attributes/CommandCompleterAttribute.cs.meta b/Runtime/Attributes/CommandCompleterAttribute.cs.meta new file mode 100644 index 0000000..91795e7 --- /dev/null +++ b/Runtime/Attributes/CommandCompleterAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b721ee9f6a3153547bce3b4af4cc2cf9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Attributes/DxShowIfAttribute.cs b/Runtime/Attributes/DxShowIfAttribute.cs index 0d00ab6..db0143a 100644 --- a/Runtime/Attributes/DxShowIfAttribute.cs +++ b/Runtime/Attributes/DxShowIfAttribute.cs @@ -1,16 +1,16 @@ -namespace WallstopStudios.DxCommandTerminal.Attributes -{ - using UnityEngine; - - public sealed class DxShowIfAttribute : PropertyAttribute - { - public readonly string conditionField; - public readonly bool inverse; - - public DxShowIfAttribute(string conditionField, bool inverse = false) - { - this.conditionField = conditionField; - this.inverse = inverse; - } - } -} +namespace WallstopStudios.DxCommandTerminal.Attributes +{ + using UnityEngine; + + public sealed class DxShowIfAttribute : PropertyAttribute + { + public readonly string conditionField; + public readonly bool inverse; + + public DxShowIfAttribute(string conditionField, bool inverse = false) + { + this.conditionField = conditionField; + this.inverse = inverse; + } + } +} diff --git a/Runtime/Attributes/RegisterCommandAttribute.cs b/Runtime/Attributes/RegisterCommandAttribute.cs index e97841b..388e353 100644 --- a/Runtime/Attributes/RegisterCommandAttribute.cs +++ b/Runtime/Attributes/RegisterCommandAttribute.cs @@ -1,53 +1,58 @@ -namespace WallstopStudios.DxCommandTerminal.Attributes -{ - using System; - using System.Reflection; - - [AttributeUsage(AttributeTargets.Method)] - public sealed class RegisterCommandAttribute : Attribute - { - public string Name { get; set; } - public int MinArgCount { get; set; } = 0; - public int MaxArgCount { get; set; } = -1; - public string Help { get; set; } - public string Hint { get; set; } - - // Should not be used by client code - internal flag to indicate that this is a "Default", or in-built command - internal bool Default { get; set; } - - public bool EditorOnly { get; set; } - - public bool DevelopmentOnly { get; set; } - - public RegisterCommandAttribute(string commandName = null) - { - commandName = commandName?.Replace(" ", string.Empty).Trim(); - Name = commandName; - } - - internal RegisterCommandAttribute(bool isDefault) - : this(string.Empty) { } - - public void NormalizeName(MethodInfo method) - { - if (string.IsNullOrWhiteSpace(Name)) - { - Name = InferCommandName(method.Name); - } - - Name = Name.Replace(" ", string.Empty).Trim(); - } - - private static string InferCommandName(string methodName) - { - const string commandId = "COMMAND"; - int index = methodName.IndexOf(commandId, StringComparison.OrdinalIgnoreCase); - - // Method is prefixed, suffixed with, or contains "COMMAND". - string commandName = - 0 <= index ? methodName.Remove(index, commandId.Length) : methodName; - - return commandName; - } - } -} +namespace WallstopStudios.DxCommandTerminal.Attributes +{ + using System; + using System.Reflection; + + [AttributeUsage(AttributeTargets.Method)] + public sealed class RegisterCommandAttribute : Attribute + { + public string Name { get; set; } + public int MinArgCount { get; set; } = 0; + public int MaxArgCount { get; set; } = -1; + public string Help { get; set; } + public string Hint { get; set; } + + // Should not be used by client code - internal flag to indicate that this is a "Default", or in-built command + internal bool Default { get; set; } + + public bool EditorOnly { get; set; } + + public bool DevelopmentOnly { get; set; } + + public bool IncludeInHistory { get; set; } = true; + + public RegisterCommandAttribute(string commandName = null) + { + commandName = commandName?.Replace(" ", string.Empty).Trim(); + Name = commandName; + } + + internal RegisterCommandAttribute(bool isDefault) + : this(string.Empty) + { + Default = isDefault; + } + + public void NormalizeName(MethodInfo method) + { + if (string.IsNullOrWhiteSpace(Name)) + { + Name = InferCommandName(method.Name); + } + + Name = Name.Replace(" ", string.Empty).Trim(); + } + + private static string InferCommandName(string methodName) + { + const string commandId = "COMMAND"; + int index = methodName.IndexOf(commandId, StringComparison.OrdinalIgnoreCase); + + // Method is prefixed, suffixed with, or contains "COMMAND". + string commandName = + 0 <= index ? methodName.Remove(index, commandId.Length) : methodName; + + return commandName; + } + } +} diff --git a/Runtime/CommandTerminal/Backend/BuiltinCommands.cs b/Runtime/CommandTerminal/Backend/BuiltinCommands.cs index a246852..fe8b78e 100644 --- a/Runtime/CommandTerminal/Backend/BuiltinCommands.cs +++ b/Runtime/CommandTerminal/Backend/BuiltinCommands.cs @@ -1,617 +1,796 @@ -namespace WallstopStudios.DxCommandTerminal.Backend -{ - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.Linq; - using System.Text; - using Attributes; - using Themes; - using UI; - using UnityEngine; - - // ReSharper disable once UnusedType.Global - public static class BuiltInCommands - { - private const string BulkSeparator = " "; - - private static readonly StringBuilder StringBuilder = new(); - - [RegisterCommand( - isDefault: true, - Name = "list-themes", - Help = "Lists all currently available themes", - MaxArgCount = 0 - )] - public static void CommandListThemes(CommandArg[] args) - { - TerminalUI terminal = TerminalUI.Instance; - if (terminal == null) - { - Terminal.Log(TerminalLogType.Warning, "No Terminal UI found."); - return; - } - - if (terminal._themePack == null) - { - Terminal.Log(TerminalLogType.Warning, "No theme pack found."); - return; - } - - string themes = string.Join( - BulkSeparator, - terminal._themePack._themeNames.Select(ThemeNameHelper.GetFriendlyThemeName) - ); - Terminal.Log(TerminalLogType.Message, themes); - } - - [RegisterCommand( - isDefault: true, - Name = "list-fonts", - Help = "Lists all currently available fonts", - MaxArgCount = 0 - )] - public static void CommandListFonts(CommandArg[] args) - { - TerminalUI terminal = TerminalUI.Instance; - if (terminal == null) - { - Terminal.Log(TerminalLogType.Warning, "No Terminal UI found."); - return; - } - - if (terminal._fontPack == null) - { - Terminal.Log(TerminalLogType.Warning, "No font pack found."); - return; - } - - string themes = string.Join( - BulkSeparator, - terminal._fontPack._fonts.Select(font => font.name) - ); - Terminal.Log(TerminalLogType.Message, themes); - } - - [RegisterCommand( - isDefault: true, - Name = "set-theme", - Help = "Sets the current Terminal UI theme", - MinArgCount = 1, - MaxArgCount = 1 - )] - public static void CommandSetTheme(CommandArg[] args) - { - TerminalUI terminal = TerminalUI.Instance; - if (terminal == null) - { - Terminal.Log(TerminalLogType.Warning, "No Terminal UI found."); - return; - } - - string theme = args[0].contents; - string friendlyThemeName = ThemeNameHelper.GetFriendlyThemeName(theme); - - if ( - string.Equals( - friendlyThemeName, - terminal.CurrentFriendlyTheme, - StringComparison.OrdinalIgnoreCase - ) - ) - { - Terminal.Log(TerminalLogType.Message, $"Theme '{theme}' is already set."); - return; - } - - int newThemeIndex = -1; - foreach (string themeName in ThemeNameHelper.GetPossibleThemeNames(theme)) - { - newThemeIndex = terminal._themePack._themeNames.FindIndex(existingTheme => - string.Equals(existingTheme, themeName, StringComparison.OrdinalIgnoreCase) - ); - if (0 <= newThemeIndex) - { - break; - } - } - - if (newThemeIndex < 0) - { - Terminal.Log(TerminalLogType.Warning, $"Theme '{theme}' not found."); - return; - } - - terminal.SetTheme(theme); - Terminal.Log(TerminalLogType.Message, $"Theme '{theme}' set."); - } - - [RegisterCommand( - isDefault: true, - Name = "get-theme", - Help = "Gets the current Terminal UI theme" - )] - public static void CommandGetTheme(CommandArg[] args) - { - TerminalUI terminal = TerminalUI.Instance; - if (terminal == null) - { - Terminal.Log(TerminalLogType.Warning, "No Terminal UI found."); - return; - } - - Terminal.Log( - TerminalLogType.Message, - $"Current terminal theme is '{terminal.CurrentFriendlyTheme}'." - ); - } - - [RegisterCommand( - isDefault: true, - Name = "get-font", - Help = "Gets the current Terminal UI font" - )] - public static void CommandGetFont(CommandArg[] args) - { - TerminalUI terminal = TerminalUI.Instance; - if (terminal == null) - { - Terminal.Log(TerminalLogType.Warning, "No Terminal UI found."); - return; - } - - Font currentFont = terminal.CurrentFont; - Terminal.Log( - TerminalLogType.Message, - $"Current terminal font is '{(currentFont == null ? "null" : currentFont.name)}'." - ); - } - - [RegisterCommand( - isDefault: true, - Name = "set-random-theme", - Help = "Sets the current Terminal UI theme to a randomly selected one", - MinArgCount = 0, - MaxArgCount = 0 - )] - public static void CommandSetRandomTheme(CommandArg[] args) - { - TerminalUI terminal = TerminalUI.Instance; - if (terminal == null) - { - Terminal.Log(TerminalLogType.Warning, "No Terminal UI found."); - return; - } - - string newTheme = terminal.SetRandomTheme(); - Terminal.Log( - TerminalLogType.Message, - $"Randomly selected and set theme to '{newTheme}'." - ); - } - - [RegisterCommand( - isDefault: true, - Name = "set-font", - Help = "Sets the current Terminal UI font", - MinArgCount = 1, - MaxArgCount = 1 - )] - public static void CommandSetFont(CommandArg[] args) - { - TerminalUI terminal = TerminalUI.Instance; - if (terminal == null) - { - Terminal.Log(TerminalLogType.Warning, "No Terminal UI found."); - return; - } - - if (terminal._fontPack == null) - { - Terminal.Log(TerminalLogType.Warning, "No font pack found."); - return; - } - - string fontName = args[0].contents; - - int newFontIndex = terminal._fontPack._fonts.FindIndex(font => - string.Equals(font.name, fontName, StringComparison.OrdinalIgnoreCase) - ); - if (newFontIndex < 0) - { - Terminal.Log(TerminalLogType.Warning, $"Font '{fontName}' not found."); - return; - } - - Font font = terminal._fontPack._fonts[newFontIndex]; - - terminal.SetFont(font); - Terminal.Log(TerminalLogType.Message, $"Font '{font.name}' set."); - } - - [RegisterCommand( - isDefault: true, - Name = "set-random-font", - Help = "Sets the current Terminal UI font to a randomly selected one", - MinArgCount = 0, - MaxArgCount = 0 - )] - public static void CommandSetRandomFont(CommandArg[] args) - { - TerminalUI terminal = TerminalUI.Instance; - if (terminal == null) - { - Terminal.Log(TerminalLogType.Warning, "No Terminal UI found."); - return; - } - - Font font = terminal.SetRandomFont(); - Terminal.Log( - TerminalLogType.Message, - $"Randomly selected and set font to '{font.name}'." - ); - } - - [RegisterCommand( - isDefault: true, - Name = "clear-console", - Help = "Clear the command console", - MaxArgCount = 0 - )] - public static void CommandClearConsole(CommandArg[] args) - { - Terminal.Buffer?.Clear(); - } - - [RegisterCommand( - isDefault: true, - Name = "clear-history", - Help = "Clear the command console's history", - MaxArgCount = 0 - )] - public static void CommandClearHistory(CommandArg[] args) - { - Terminal.History?.Clear(); - } - - [RegisterCommand( - isDefault: true, - Name = "help", - Help = "Display help information about a command", - MaxArgCount = 1 - )] - public static void CommandHelp(CommandArg[] args) - { - CommandShell shell = Terminal.Shell; - if (shell == null) - { - return; - } - - if (args.Length == 0) - { - foreach (KeyValuePair command in shell.Commands) - { - Terminal.Log($"{command.Key.ToUpperInvariant(), -16}: {command.Value.help}"); - } - return; - } - - string commandName = args[0].contents ?? string.Empty; - - if (!shell.Commands.TryGetValue(commandName, out CommandInfo info)) - { - shell.IssueErrorMessage($"Command {commandName} could not be found."); - return; - } - - if (string.IsNullOrWhiteSpace(info.help)) - { - Terminal.Log($"{commandName} does not provide any help documentation."); - } - else if (string.IsNullOrWhiteSpace(info.hint)) - { - Terminal.Log(info.help); - } - else - { - Terminal.Log($"{info.help}\nUsage: {info.hint}"); - } - } - - [RegisterCommand( - isDefault: true, - Name = "time", - Help = "Time the execution of a command", - MinArgCount = 1 - )] - public static void CommandTime(CommandArg[] args) - { - CommandShell shell = Terminal.Shell; - if (shell == null) - { - return; - } - - Stopwatch sw = Stopwatch.StartNew(); - shell.RunCommand(JoinArguments(args)); - sw.Stop(); - Terminal.Log($"Time: {sw.ElapsedMilliseconds}ms"); - } - - [RegisterCommand( - isDefault: true, - Name = "time-scale", - Help = "Sets Time.timeScale", - MinArgCount = 1, - MaxArgCount = 1 - )] - public static void CommandTimeScale(CommandArg[] args) - { - CommandShell shell = Terminal.Shell; - if (shell == null) - { - return; - } - - CommandArg arg = args[0]; - if (!arg.TryGet(out float timeScale)) - { - Terminal.Log(TerminalLogType.Warning, $"Invalid time scale {arg}."); - return; - } - - Time.timeScale = timeScale; - } - - [RegisterCommand( - isDefault: true, - Name = "log-terminal", - Help = "Output message via Terminal.Log" - )] - public static void CommandLogTerminal(CommandArg[] args) - { - Terminal.Log(JoinArguments(args)); - } - - [RegisterCommand(isDefault: true, Name = "log", Help = "Output message via Debug.Log")] - public static void CommandLog(CommandArg[] args) - { - UnityEngine.Debug.Log(JoinArguments(args)); - } - - [RegisterCommand( - isDefault: true, - Name = "trace", - Help = "Output the stack trace of the previous message", - MaxArgCount = 0 - )] - public static void CommandTrace(CommandArg[] args) - { - CommandLog buffer = Terminal.Buffer; - if (buffer == null) - { - return; - } - - int logCount = buffer.Logs.Count; - - if (logCount - 2 < 0) - { - Terminal.Log(TerminalLogType.Warning, "Nothing to trace."); - return; - } - - LogItem logItem = buffer.Logs[logCount - 2]; - - if (string.IsNullOrWhiteSpace(logItem.stackTrace)) - { - Terminal.Log( - logItem.message.EndsWith(" (no trace)", StringComparison.OrdinalIgnoreCase) - ? logItem.message - : $"{logItem.message} (no trace)" - ); - } - else - { - Terminal.Log(logItem.stackTrace); - } - } - - [RegisterCommand( - isDefault: true, - Name = "clear-variable", - Help = "Clears a variable value", - MinArgCount = 1, - MaxArgCount = 1 - )] - public static void CommandClearVariable(CommandArg[] args) - { - CommandShell shell = Terminal.Shell; - if (shell == null) - { - return; - } - - string variableName = args[0].contents; - bool cleared = shell.ClearVariable(variableName); - if (cleared) - { - Terminal.Log($"Variable '{variableName}' cleared successfully."); - } - else - { - Terminal.Log( - TerminalLogType.Warning, - $"Warning: Variable '{variableName}' not found." - ); - } - } - - [RegisterCommand( - isDefault: true, - Name = "clear-all-variables", - Help = "Clears all variable values", - MinArgCount = 0, - MaxArgCount = 0 - )] - public static void CommandClearAllVariable(CommandArg[] args) - { - CommandShell shell = Terminal.Shell; - if (shell == null) - { - return; - } - - int variableCount = shell.Variables.Count; - foreach (string variable in shell.Variables.Keys.ToArray()) - { - shell.ClearVariable(variable); - } - - Terminal.Log( - variableCount == 0 - ? "No variables found - nothing to clear." - : $"Cleared {variableCount} variables." - ); - } - - [RegisterCommand( - isDefault: true, - Name = "set-variable", - Help = "Sets a variable value", - MinArgCount = 2, - MaxArgCount = 2 - )] - public static void CommandSetVariable(CommandArg[] args) - { - CommandShell shell = Terminal.Shell; - if (shell == null) - { - return; - } - - string variableName = args[0].contents; - - if (string.IsNullOrWhiteSpace(variableName) || variableName.StartsWith('$')) - { - Terminal.Log( - TerminalLogType.Warning, - $"Warning: Possibly invalid variable name '{variableName}'." - ); - } - - string variableValue = JoinArguments(args, 1); - bool set = shell.SetVariable(variableName, variableValue); - if (set) - { - Terminal.Log($"Variable '{variableName}' set to '{variableValue}' successfully."); - } - else if (shell.Variables.TryGetValue(variableName, out CommandArg existingVariable)) - { - Terminal.Log( - TerminalLogType.Warning, - $"Variable '{variableName}' failed to set. Existing value: {existingVariable}." - ); - } - else - { - Terminal.Log( - TerminalLogType.Warning, - $"Variable '{variableName}' failed to set. No existing value found." - ); - } - } - - [RegisterCommand( - isDefault: true, - Name = "get-variable", - Help = "Gets a variable value", - MinArgCount = 1, - MaxArgCount = 1 - )] - public static void CommandGetVariable(CommandArg[] args) - { - CommandShell shell = Terminal.Shell; - if (shell == null) - { - return; - } - - string variableName = args[0].contents; - - if (shell.Variables.TryGetValue(variableName, out CommandArg variable)) - { - Terminal.Log($"Variable '{variableName}' is set to '{variable}'."); - } - else - { - Terminal.Log(TerminalLogType.Warning, $"Variable '{variableName}' not found."); - } - } - - [RegisterCommand( - isDefault: true, - Name = "list-variables", - Help = "Gets all variables and their associated values", - MinArgCount = 0, - MaxArgCount = 0 - )] - public static void CommandGetAllVariables(CommandArg[] args) - { - CommandShell shell = Terminal.Shell; - if (shell == null) - { - return; - } - - if (!shell.Variables.Any()) - { - Terminal.Log(TerminalLogType.Warning, "No variables found."); - return; - } - - foreach (KeyValuePair entry in shell.Variables) - { - Terminal.Log($"Variable '{entry.Key}' is set to '{entry.Value}'."); - } - } - - [RegisterCommand(isDefault: true, Name = "no-op", Help = "No operation")] - public static void CommandNoOperation(CommandArg[] args) - { - // No-op - } - - [RegisterCommand( - isDefault: true, - Name = "quit", - Help = "Quit running application", - MaxArgCount = 0 - )] - public static void CommandQuit(CommandArg[] args) - { -#if UNITY_EDITOR - UnityEditor.EditorApplication.isPlaying = false; -#else - UnityEngine.Application.Quit(); -#endif - } - - private static string JoinArguments(CommandArg[] args, int start = 0) - { - StringBuilder.Clear(); - for (int i = start; i < args.Length; i++) - { - StringBuilder.Append(args[i].contents); - - if (i < args.Length - 1) - { - StringBuilder.Append(' '); - } - } - - return StringBuilder.ToString(); - } - } -} +namespace WallstopStudios.DxCommandTerminal.Backend +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Text; + using Attributes; + using Themes; + using UI; + using UnityEngine; + using Debug = UnityEngine.Debug; + + // ReSharper disable once UnusedType.Global + public static class BuiltInCommands + { + private const string BulkSeparator = " "; + + private static readonly StringBuilder StringBuilder = new(); + + private static ITerminalServiceLocator ServiceLocator => TerminalUI.ServiceLocator; + + private static ITerminalProvider TerminalProvider => ServiceLocator?.TerminalProvider; + + private static ITerminalRuntimeScope RuntimeScope => ServiceLocator?.RuntimeScope; + + private static TerminalUI GetActiveTerminal() + { + return TerminalProvider?.ActiveTerminal; + } + + private static CommandShell ActiveShell => RuntimeScope?.Shell; + + private static CommandLog ActiveLog => RuntimeScope?.Buffer; + + private static CommandHistory ActiveHistory => RuntimeScope?.History; + + private static void Log(TerminalLogType type, string format, params object[] arguments) + { + ITerminalRuntimeScope scope = RuntimeScope; + if (scope != null) + { + scope.Log(type, format, arguments); + return; + } + + Debug.unityLogger.Log( + type.ToString(), + arguments is { Length: > 0 } ? string.Format(format, arguments) : format + ); + } + + private static void Log(string format, params object[] arguments) + { + Log(TerminalLogType.Message, format, arguments); + } + + [RegisterCommand( + isDefault: true, + Name = "list-themes", + Help = "Lists all currently available themes", + MaxArgCount = 0 + )] + public static void CommandListThemes(CommandArg[] args) + { + TerminalUI terminal = GetActiveTerminal(); + if (terminal == null) + { + Log(TerminalLogType.Warning, "No Terminal UI found."); + return; + } + + if (terminal._themePack == null) + { + Log(TerminalLogType.Warning, "No theme pack found."); + return; + } + + StringBuilder.Clear(); + List names = terminal._themePack._themeNames; + for (int i = 0; i < names.Count; ++i) + { + if (i > 0) + { + StringBuilder.Append(BulkSeparator); + } + StringBuilder.Append(ThemeNameHelper.GetFriendlyThemeName(names[i])); + } + string themes = StringBuilder.ToString(); + Log(TerminalLogType.Message, themes); + } + + [RegisterCommand( + isDefault: true, + Name = "list-fonts", + Help = "Lists all currently available fonts", + MaxArgCount = 0 + )] + public static void CommandListFonts(CommandArg[] args) + { + TerminalUI terminal = GetActiveTerminal(); + if (terminal == null) + { + Log(TerminalLogType.Warning, "No Terminal UI found."); + return; + } + + if (terminal._fontPack == null) + { + Log(TerminalLogType.Warning, "No font pack found."); + Log(TerminalLogType.Message, "No fonts available."); + return; + } + + StringBuilder.Clear(); + List fonts = terminal._fontPack._fonts; + if (fonts.Count == 0) + { + Log(TerminalLogType.Message, "No fonts available."); + return; + } + + for (int i = 0; i < fonts.Count; ++i) + { + if (i > 0) + { + StringBuilder.Append(BulkSeparator); + } + Font f = fonts[i]; + StringBuilder.Append(f != null ? f.name : string.Empty); + } + string themes = StringBuilder.ToString(); + Log(TerminalLogType.Message, themes); + } + + [RegisterCommand( + isDefault: true, + Name = "set-theme", + Help = "Sets the current Terminal UI theme", + MinArgCount = 1, + MaxArgCount = 1, + Hint = "set-theme " + )] + [CommandCompleter(typeof(Completers.ThemeArgumentCompleter))] + public static void CommandSetTheme(CommandArg[] args) + { + TerminalUI terminal = GetActiveTerminal(); + if (terminal == null) + { + Log(TerminalLogType.Warning, "No Terminal UI found."); + return; + } + + string theme = args[0].contents; + string friendlyThemeName = ThemeNameHelper.GetFriendlyThemeName(theme); + + if ( + string.Equals( + friendlyThemeName, + terminal.CurrentFriendlyTheme, + StringComparison.OrdinalIgnoreCase + ) + ) + { + Log(TerminalLogType.Message, $"Theme '{theme}' is already set."); + return; + } + + int newThemeIndex = -1; + foreach (string themeName in ThemeNameHelper.GetPossibleThemeNames(theme)) + { + newThemeIndex = terminal._themePack._themeNames.FindIndex(existingTheme => + string.Equals(existingTheme, themeName, StringComparison.OrdinalIgnoreCase) + ); + if (0 <= newThemeIndex) + { + break; + } + } + + if (newThemeIndex < 0) + { + Log(TerminalLogType.Warning, $"Theme '{theme}' not found."); + return; + } + + terminal.SetTheme(theme); + Log(TerminalLogType.Message, $"Theme '{theme}' set."); + } + + [RegisterCommand( + isDefault: true, + Name = "set-font", + Help = "Sets the current Terminal UI font", + MinArgCount = 1, + MaxArgCount = 1, + Hint = "set-font " + )] + [CommandCompleter(typeof(Completers.FontArgumentCompleter))] + public static void CommandSetFont(CommandArg[] args) + { + TerminalUI terminal = GetActiveTerminal(); + if (terminal == null) + { + Log(TerminalLogType.Warning, "No Terminal UI found."); + return; + } + + if (terminal._fontPack == null) + { + Log(TerminalLogType.Warning, "No font pack found."); + return; + } + + string fontName = args[0].contents ?? string.Empty; + if (string.IsNullOrWhiteSpace(fontName)) + { + Log(TerminalLogType.Warning, "Invalid font name."); + return; + } + + Font font = null; + foreach (Font existingFont in terminal._fontPack._fonts) + { + if ( + existingFont != null + && string.Equals( + existingFont.name, + fontName, + StringComparison.OrdinalIgnoreCase + ) + ) + { + font = existingFont; + break; + } + } + + if (font == null) + { + Log(TerminalLogType.Warning, $"Font '{fontName}' not found."); + return; + } + + terminal.SetFont(font); + Log(TerminalLogType.Message, $"Font '{font.name}' set."); + } + + [RegisterCommand( + isDefault: true, + Name = "get-theme", + Help = "Gets the current Terminal UI theme" + )] + public static void CommandGetTheme(CommandArg[] args) + { + TerminalUI terminal = GetActiveTerminal(); + if (terminal == null) + { + Log(TerminalLogType.Warning, "No Terminal UI found."); + return; + } + + Log( + TerminalLogType.Message, + $"Current terminal theme is '{terminal.CurrentFriendlyTheme}'." + ); + } + + [RegisterCommand( + isDefault: true, + Name = "get-font", + Help = "Gets the current Terminal UI font" + )] + public static void CommandGetFont(CommandArg[] args) + { + TerminalUI terminal = GetActiveTerminal(); + if (terminal == null) + { + Log(TerminalLogType.Warning, "No Terminal UI found."); + return; + } + + Font currentFont = terminal.CurrentFont; + Log( + TerminalLogType.Message, + $"Current terminal font is '{(currentFont == null ? "null" : currentFont.name)}'." + ); + } + + [RegisterCommand( + isDefault: true, + Name = "set-random-theme", + Help = "Sets the current Terminal UI theme to a randomly selected one", + MinArgCount = 0, + MaxArgCount = 0 + )] + public static void CommandSetRandomTheme(CommandArg[] args) + { + TerminalUI terminal = GetActiveTerminal(); + if (terminal == null) + { + Log(TerminalLogType.Warning, "No Terminal UI found."); + return; + } + + string newTheme = terminal.SetRandomTheme(); + Log(TerminalLogType.Message, $"Randomly selected and set theme to '{newTheme}'."); + } + + // [RegisterCommand( + // isDefault: true, + // Name = "set-font", + // Help = "Sets the current Terminal UI font", + // MinArgCount = 1, + // MaxArgCount = 1 + // )] + // public static void CommandSetFont(CommandArg[] args) + // { + // TerminalUI terminal = GetActiveTerminal(); + // if (terminal == null) + // { + // Log(TerminalLogType.Warning, "No Terminal UI found."); + // return; + // } + // + // if (terminal._fontPack == null) + // { + // Log(TerminalLogType.Warning, "No font pack found."); + // return; + // } + // + // string fontName = args[0].contents; + // + // int newFontIndex = terminal._fontPack._fonts.FindIndex(font => + // string.Equals(font.name, fontName, StringComparison.OrdinalIgnoreCase) + // ); + // if (newFontIndex < 0) + // { + // Log(TerminalLogType.Warning, $"Font '{fontName}' not found."); + // return; + // } + // + // Font font = terminal._fontPack._fonts[newFontIndex]; + // + // terminal.SetFont(font); + // Log(TerminalLogType.Message, $"Font '{font.name}' set."); + // } + + [RegisterCommand( + isDefault: true, + Name = "set-random-font", + Help = "Sets the current Terminal UI font to a randomly selected one", + MinArgCount = 0, + MaxArgCount = 0 + )] + public static void CommandSetRandomFont(CommandArg[] args) + { + TerminalUI terminal = GetActiveTerminal(); + if (terminal == null) + { + Log(TerminalLogType.Warning, "No Terminal UI found."); + return; + } + + Font font = terminal.SetRandomFont(); + if (font == null) + { + Log(TerminalLogType.Warning, "No fonts available to select."); + return; + } + Log(TerminalLogType.Message, $"Randomly selected and set font to '{font.name}'."); + } + + [RegisterCommand( + isDefault: true, + Name = "clear-console", + Help = "Clear the command console", + MaxArgCount = 0 + )] + public static void CommandClearConsole(CommandArg[] args) + { + ActiveLog?.Clear(); + } + + [RegisterCommand( + isDefault: true, + Name = "clear-history", + Help = "Clear the command console's history", + MaxArgCount = 0, + IncludeInHistory = false + )] + public static void CommandClearHistory(CommandArg[] args) + { + ActiveHistory?.Clear(); + + CommandLog buffer = ActiveLog; + if (buffer != null) + { + buffer.DrainPending(); + buffer.Clear(); + } + } + + [RegisterCommand( + isDefault: true, + Name = "help", + Help = "Display help information about a command", + MaxArgCount = 1 + )] + public static void CommandHelp(CommandArg[] args) + { + CommandShell shell = ActiveShell; + if (shell == null) + { + return; + } + + if (args.Length == 0) + { + foreach (KeyValuePair command in shell.Commands) + { + string name = command.Key; + CommandInfo info = command.Value; + string usage = BuildUsage(name, info.minArgCount, info.maxArgCount, info.hint); + string helpLine = + $"{name.ToUpperInvariant(), -16}: {info.help}\n -> {usage}"; + Log(helpLine); + UnityEngine.Debug.Log(helpLine); + } + return; + } + else + { + string commandName = args[0].contents ?? string.Empty; + + if (!shell.Commands.TryGetValue(commandName, out CommandInfo info)) + { + shell.IssueErrorMessage($"Command {commandName} could not be found."); + return; + } + + string usageLine = BuildUsage( + commandName, + info.minArgCount, + info.maxArgCount, + info.hint + ); + if (string.IsNullOrWhiteSpace(info.help)) + { + string line = $"{commandName}: {usageLine}"; + Log(line); + UnityEngine.Debug.Log(line); + } + else + { + string line = $"{info.help}\n{usageLine}"; + Log(line); + UnityEngine.Debug.Log(line); + } + } + } + + private static string BuildUsage(string name, int minArgs, int maxArgs, string hint) + { + if (!string.IsNullOrWhiteSpace(hint)) + { + return $"Usage: {hint}"; + } + + StringBuilder.Clear(); + StringBuilder.Append("Usage: "); + StringBuilder.Append(name); + if (minArgs <= 0 && (maxArgs == 0 || maxArgs < 0)) + { + return StringBuilder.ToString(); + } + + int max = maxArgs < 0 ? minArgs : Math.Max(minArgs, maxArgs); + for (int i = 1; i <= minArgs; ++i) + { + StringBuilder.Append(' '); + StringBuilder.Append('<'); + StringBuilder.Append("arg"); + StringBuilder.Append(i); + StringBuilder.Append('>'); + } + for (int i = minArgs + 1; i <= max; ++i) + { + StringBuilder.Append(' '); + StringBuilder.Append('['); + StringBuilder.Append("arg"); + StringBuilder.Append(i); + StringBuilder.Append(']'); + } + if (maxArgs < 0) + { + StringBuilder.Append(' '); + StringBuilder.Append("[args...]"); + } + + return StringBuilder.ToString(); + } + + [RegisterCommand( + isDefault: true, + Name = "time", + Help = "Time the execution of a command", + MinArgCount = 1 + )] + public static void CommandTime(CommandArg[] args) + { + CommandShell shell = ActiveShell; + if (shell == null) + { + return; + } + + Stopwatch sw = Stopwatch.StartNew(); + shell.RunCommand(JoinArguments(args)); + sw.Stop(); + Log($"Time: {sw.ElapsedMilliseconds}ms"); + } + + [RegisterCommand( + isDefault: true, + Name = "time-scale", + Help = "Sets Time.timeScale", + MinArgCount = 1, + MaxArgCount = 1 + )] + public static void CommandTimeScale(CommandArg[] args) + { + CommandShell shell = ActiveShell; + if (shell == null) + { + return; + } + + CommandArg arg = args[0]; + if (!arg.TryGet(out float timeScale)) + { + Log(TerminalLogType.Warning, $"Invalid time scale {arg}."); + return; + } + + Time.timeScale = timeScale; + } + + [RegisterCommand( + isDefault: true, + Name = "log-terminal", + Help = "Output message via Terminal.Log" + )] + public static void CommandLogTerminal(CommandArg[] args) + { + Log(JoinArguments(args)); + } + + [RegisterCommand(isDefault: true, Name = "log", Help = "Output message via Debug.Log")] + public static void CommandLog(CommandArg[] args) + { + UnityEngine.Debug.Log(JoinArguments(args)); + } + + [RegisterCommand( + isDefault: true, + Name = "trace", + Help = "Output the stack trace of the previous message", + MaxArgCount = 0 + )] + public static void CommandTrace(CommandArg[] args) + { + CommandLog buffer = ActiveLog; + if (buffer == null) + { + return; + } + + int logCount = buffer.Logs.Count; + + if (logCount - 2 < 0) + { + Log(TerminalLogType.Warning, "Nothing to trace."); + return; + } + + LogItem logItem = buffer.Logs[logCount - 2]; + + if (string.IsNullOrWhiteSpace(logItem.stackTrace)) + { + Log( + logItem.message.EndsWith(" (no trace)", StringComparison.OrdinalIgnoreCase) + ? logItem.message + : $"{logItem.message} (no trace)" + ); + } + else + { + Log(logItem.stackTrace); + } + } + + [RegisterCommand( + isDefault: true, + Name = "clear-variable", + Help = "Clears a variable value", + MinArgCount = 1, + MaxArgCount = 1 + )] + public static void CommandClearVariable(CommandArg[] args) + { + CommandShell shell = ActiveShell; + if (shell == null) + { + return; + } + + string variableName = args[0].contents; + bool cleared = shell.ClearVariable(variableName); + if (cleared) + { + Log($"Variable '{variableName}' cleared successfully."); + } + else + { + Log(TerminalLogType.Warning, $"Warning: Variable '{variableName}' not found."); + } + } + + [RegisterCommand( + isDefault: true, + Name = "clear-all-variables", + Help = "Clears all variable values", + MinArgCount = 0, + MaxArgCount = 0 + )] + public static void CommandClearAllVariable(CommandArg[] args) + { + CommandShell shell = ActiveShell; + if (shell == null) + { + return; + } + + int variableCount = shell.Variables.Count; + List variableNames = new(shell.Variables.Keys); + foreach (string variable in variableNames) + { + shell.ClearVariable(variable); + } + + Log( + variableCount == 0 + ? "No variables found - nothing to clear." + : $"Cleared {variableCount} variables." + ); + } + + [RegisterCommand( + isDefault: true, + Name = "set-variable", + Help = "Sets a variable value", + MinArgCount = 2, + MaxArgCount = 2 + )] + public static void CommandSetVariable(CommandArg[] args) + { + CommandShell shell = ActiveShell; + if (shell == null) + { + return; + } + + string variableName = args[0].contents; + + if (string.IsNullOrWhiteSpace(variableName) || variableName.StartsWith('$')) + { + Log( + TerminalLogType.Warning, + $"Warning: Possibly invalid variable name '{variableName}'." + ); + } + + string variableValue = JoinArguments(args, 1); + bool set = shell.SetVariable(variableName, variableValue); + if (set) + { + Log($"Variable '{variableName}' set to '{variableValue}' successfully."); + } + else if (shell.Variables.TryGetValue(variableName, out CommandArg existingVariable)) + { + Log( + TerminalLogType.Warning, + $"Variable '{variableName}' failed to set. Existing value: {existingVariable}." + ); + } + else + { + Log( + TerminalLogType.Warning, + $"Variable '{variableName}' failed to set. No existing value found." + ); + } + } + + [RegisterCommand( + isDefault: true, + Name = "get-variable", + Help = "Gets a variable value", + MinArgCount = 1, + MaxArgCount = 1 + )] + public static void CommandGetVariable(CommandArg[] args) + { + CommandShell shell = ActiveShell; + if (shell == null) + { + return; + } + + string variableName = args[0].contents; + + if (shell.Variables.TryGetValue(variableName, out CommandArg variable)) + { + Log($"Variable '{variableName}' is set to '{variable}'."); + } + else + { + Log(TerminalLogType.Warning, $"Variable '{variableName}' not found."); + } + } + + [RegisterCommand( + isDefault: true, + Name = "list-variables", + Help = "Gets all variables and their associated values", + MinArgCount = 0, + MaxArgCount = 0 + )] + public static void CommandGetAllVariables(CommandArg[] args) + { + CommandShell shell = ActiveShell; + if (shell == null) + { + return; + } + + if (shell.Variables.Count == 0) + { + Log(TerminalLogType.Warning, "No variables found."); + return; + } + + foreach (KeyValuePair entry in shell.Variables) + { + Log($"Variable '{entry.Key}' is set to '{entry.Value}'."); + } + } + + [RegisterCommand(isDefault: true, Name = "no-op", Help = "No operation")] + public static void CommandNoOperation(CommandArg[] args) + { + // No-op + } + + [RegisterCommand( + isDefault: true, + Name = "quit", + Help = "Quit running application", + MaxArgCount = 0 + )] + public static void CommandQuit(CommandArg[] args) + { +#if UNITY_EDITOR + UnityEditor.EditorApplication.isPlaying = false; +#else + UnityEngine.Application.Quit(); +#endif + } + + private static string JoinArguments(CommandArg[] args, int start = 0) + { + StringBuilder.Clear(); + for (int i = start; i < args.Length; i++) + { + StringBuilder.Append(args[i].contents); + + if (i < args.Length - 1) + { + StringBuilder.Append(' '); + } + } + + return StringBuilder.ToString(); + } + } +} diff --git a/Runtime/CommandTerminal/Backend/CommandArg.cs b/Runtime/CommandTerminal/Backend/CommandArg.cs index eef9d5b..99b8aae 100644 --- a/Runtime/CommandTerminal/Backend/CommandArg.cs +++ b/Runtime/CommandTerminal/Backend/CommandArg.cs @@ -1,629 +1,392 @@ -namespace WallstopStudios.DxCommandTerminal.Backend -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Net; - using System.Numerics; - using System.Reflection; - using UnityEngine; - using Quaternion = UnityEngine.Quaternion; - using Vector2 = UnityEngine.Vector2; - using Vector3 = UnityEngine.Vector3; - using Vector4 = UnityEngine.Vector4; - - public delegate bool CommandArgParser(string input, out T parsed); - - public readonly struct CommandArg - { - private static readonly Lazy TryGetMethod = new(() => - typeof(CommandArg) - .GetMethods(BindingFlags.Instance | BindingFlags.Public) - .Where(method => method.Name == nameof(TryGet)) - .FirstOrDefault(method => method.GetParameters().Length == 1) - ); - private static readonly Dictionary RegisteredParsers = new(); - private static readonly Dictionary< - Type, - Dictionary - > StaticProperties = new(); - private static readonly Dictionary> ConstFields = new(); - private static readonly Dictionary EnumValues = new(); - - // Public to allow custom-mutation, if desired - public static readonly HashSet Delimiters = new() { ',', ';', ':', '_', '/', '\\' }; - public static readonly List Quotes = new() { '"', '\'' }; - public static readonly HashSet IgnoredValuesForCleanedTypes = new() { "\r", "\n" }; - public static readonly HashSet DoNotCleanTypes = new() - { - typeof(string), - typeof(char), - typeof(DateTime), - typeof(DateTimeOffset), - }; - public static readonly HashSet IgnoredValuesForComplexTypes = new() - { - "(", - ")", - "[", - "]", - "'", - "`", - "|", - "{", - "}", - "<", - ">", - }; - - public readonly string contents; - public readonly char? startQuote; - public readonly char? endQuote; - - public string CleanedContents - { - get - { - string cleanedString = contents; - cleanedString = IgnoredValuesForCleanedTypes.Aggregate( - cleanedString, - (current, ignoredValue) => - current.Replace( - ignoredValue, - string.Empty, - StringComparison.OrdinalIgnoreCase - ) - ); - return cleanedString; - } - } - - public bool TryGet(Type type, out object parsed) - { - // TODO: Convert into delegates and cache for performance - MethodInfo genericMethod = TryGetMethod.Value; - if (genericMethod == null) - { - parsed = default; - return false; - } - - MethodInfo constructed = genericMethod.MakeGenericMethod(type); - object[] parameters = { null }; - bool success = (bool)constructed.Invoke(this, parameters); - parsed = parameters[0]; - return success; - } - - public bool TryGet(out T parsed) - { - return TryGet(out parsed, parserOverride: null); - } - - public bool TryGet(out T parsed, CommandArgParser parserOverride) - { - Type type = typeof(T); - string stringValue = DoNotCleanTypes.Contains(type) ? contents : CleanedContents; - - if (parserOverride != null) - { - return parserOverride(stringValue, out parsed); - } - - if (TryGetParser(out CommandArgParser parser)) - { - return parser(stringValue, out parsed); - } - - if (type == typeof(string)) - { - parsed = (T)Convert.ChangeType(stringValue, type); - return true; - } - if (TryGetTypeDefined(stringValue, out parsed)) - { - return true; - } - - // TODO: Slap into a dictionary of built-in type -> parser mapping - if (type == typeof(bool)) - { - return InnerParse(stringValue, bool.TryParse, out parsed); - } - if (type == typeof(float)) - { - return InnerParse(stringValue, float.TryParse, out parsed); - } - if (type == typeof(int)) - { - return InnerParse(stringValue, int.TryParse, out parsed); - } - if (type == typeof(uint)) - { - return InnerParse(stringValue, uint.TryParse, out parsed); - } - if (type == typeof(long)) - { - return InnerParse(stringValue, long.TryParse, out parsed); - } - if (type == typeof(ulong)) - { - return InnerParse(stringValue, ulong.TryParse, out parsed); - } - if (type == typeof(double)) - { - return InnerParse(stringValue, double.TryParse, out parsed); - } - if (type == typeof(short)) - { - return InnerParse(stringValue, short.TryParse, out parsed); - } - if (type == typeof(ushort)) - { - return InnerParse(stringValue, ushort.TryParse, out parsed); - } - if (type == typeof(byte)) - { - return InnerParse(stringValue, byte.TryParse, out parsed); - } - if (type == typeof(sbyte)) - { - return InnerParse(stringValue, sbyte.TryParse, out parsed); - } - if (type == typeof(Guid)) - { - return InnerParse(stringValue, Guid.TryParse, out parsed); - } - if (type == typeof(DateTime)) - { - return InnerParse(stringValue, DateTime.TryParse, out parsed); - } - if (type == typeof(DateTimeOffset)) - { - return InnerParse(stringValue, DateTimeOffset.TryParse, out parsed); - } - if (type == typeof(char)) - { - return InnerParse(stringValue, char.TryParse, out parsed); - } - if (type == typeof(decimal)) - { - return InnerParse(stringValue, decimal.TryParse, out parsed); - } - if (type == typeof(BigInteger)) - { - return InnerParse(stringValue, BigInteger.TryParse, out parsed); - } - if (type == typeof(TimeSpan)) - { - return InnerParse(stringValue, TimeSpan.TryParse, out parsed); - } - if (type == typeof(Version)) - { - return InnerParse(stringValue, Version.TryParse, out parsed); - } - if (type == typeof(IPAddress)) - { - return InnerParse(stringValue, IPAddress.TryParse, out parsed); - } - if (type.IsEnum) - { - if (Enum.IsDefined(type, stringValue)) - { - bool parseOk = Enum.TryParse(type, stringValue, out object parsedObject); - if (parseOk) - { - parsed = (T)Convert.ChangeType(parsedObject, type); - return true; - } - } - - if (int.TryParse(stringValue, out int enumIntValue)) - { - if (!EnumValues.TryGetValue(type, out object enumValues)) - { - enumValues = Enum.GetValues(type).OfType().ToArray(); - EnumValues[type] = enumValues; - } - - T[] values = (T[])enumValues; - if (0 <= enumIntValue && enumIntValue < values.Length) - { - parsed = values[enumIntValue]; - return true; - } - } - } - if (type == typeof(Vector2)) - { - string[] split = StripAndSplit(stringValue); - switch (split.Length) - { - case 2 - when float.TryParse(split[0], out float x) - && float.TryParse(split[1], out float y): - parsed = (T)Convert.ChangeType(new Vector2(x, y), type); - return true; - case 3 - when float.TryParse(split[0], out float x) - && float.TryParse(split[1], out float y) - && float.TryParse(split[2], out float z): - parsed = (T)Convert.ChangeType((Vector2)new Vector3(x, y, z), type); - return true; - } - } - else if (type == typeof(Vector3)) - { - string[] split = StripAndSplit(stringValue); - switch (split.Length) - { - case 2 - when float.TryParse(split[0], out float x) - && float.TryParse(split[1], out float y): - parsed = (T)Convert.ChangeType(new Vector3(x, y), type); - return true; - case 3 - when float.TryParse(split[0], out float x) - && float.TryParse(split[1], out float y) - && float.TryParse(split[2], out float z): - parsed = (T)Convert.ChangeType(new Vector3(x, y, z), type); - return true; - } - } - else if (type == typeof(Vector4)) - { - string[] split = StripAndSplit(stringValue); - switch (split.Length) - { - case 2 - when float.TryParse(split[0], out float x) - && float.TryParse(split[1], out float y): - parsed = (T)Convert.ChangeType(new Vector4(x, y), type); - return true; - case 3 - when float.TryParse(split[0], out float x) - && float.TryParse(split[1], out float y) - && float.TryParse(split[2], out float z): - parsed = (T)Convert.ChangeType(new Vector4(x, y, z), type); - return true; - case 4 - when float.TryParse(split[0], out float x) - && float.TryParse(split[1], out float y) - && float.TryParse(split[2], out float z) - && float.TryParse(split[3], out float w): - parsed = (T)Convert.ChangeType(new Vector4(x, y, z, w), type); - return true; - } - } - else if (type == typeof(Vector2Int)) - { - string[] split = StripAndSplit(stringValue); - switch (split.Length) - { - case 2 - when int.TryParse(split[0], out int x) && int.TryParse(split[1], out int y): - parsed = (T)Convert.ChangeType(new Vector2Int(x, y), type); - return true; - case 3 - when int.TryParse(split[0], out int x) - && int.TryParse(split[1], out int y) - && int.TryParse(split[2], out int z): - parsed = (T)Convert.ChangeType((Vector2Int)new Vector3Int(x, y, z), type); - return true; - } - } - else if (type == typeof(Vector3Int)) - { - string[] split = StripAndSplit(stringValue); - switch (split.Length) - { - case 2 - when int.TryParse(split[0], out int x) && int.TryParse(split[1], out int y): - parsed = (T)Convert.ChangeType(new Vector3Int(x, y), type); - return true; - case 3 - when int.TryParse(split[0], out int x) - && int.TryParse(split[1], out int y) - && int.TryParse(split[2], out int z): - parsed = (T)Convert.ChangeType(new Vector3Int(x, y, z), type); - return true; - } - } - else if (type == typeof(Color)) - { - string colorString = stringValue; - if (colorString.StartsWith("RGBA", StringComparison.OrdinalIgnoreCase)) - { - colorString = colorString.Replace( - "RGBA", - string.Empty, - StringComparison.OrdinalIgnoreCase - ); - } - - string[] split = StripAndSplit(colorString); - switch (split.Length) - { - case 3 - when float.TryParse(split[0], out float r) - && float.TryParse(split[1], out float g) - && float.TryParse(split[2], out float b): - parsed = (T)Convert.ChangeType(new Color(r, g, b), type); - return true; - case 4 - when float.TryParse(split[0], out float r) - && float.TryParse(split[1], out float g) - && float.TryParse(split[2], out float b) - && float.TryParse(split[3], out float a): - parsed = (T)Convert.ChangeType(new Color(r, g, b, a), type); - return true; - } - } - else if (type == typeof(Quaternion)) - { - string[] split = StripAndSplit(stringValue); - switch (split.Length) - { - case 4 - when float.TryParse(split[0], out float x) - && float.TryParse(split[1], out float y) - && float.TryParse(split[2], out float z) - && float.TryParse(split[3], out float w): - parsed = (T)Convert.ChangeType(new Quaternion(x, y, z, w), type); - return true; - } - } - else if (type == typeof(Rect)) - { - string[] split = StripAndSplit(stringValue); - switch (split.Length) - { - case 4 - when float.TryParse( - split[0] - .Replace("x:", string.Empty, StringComparison.OrdinalIgnoreCase), - out float x - ) - && float.TryParse( - split[1] - .Replace( - "y:", - string.Empty, - StringComparison.OrdinalIgnoreCase - ), - out float y - ) - && float.TryParse( - split[2] - .Replace( - "width:", - string.Empty, - StringComparison.OrdinalIgnoreCase - ), - out float width - ) - && float.TryParse( - split[3] - .Replace( - "height:", - string.Empty, - StringComparison.OrdinalIgnoreCase - ), - out float height - ): - parsed = (T)Convert.ChangeType(new Rect(x, y, width, height), type); - return true; - } - } - else if (type == typeof(RectInt)) - { - string[] split = StripAndSplit(stringValue); - switch (split.Length) - { - case 4 - when int.TryParse( - split[0] - .Replace("x:", string.Empty, StringComparison.OrdinalIgnoreCase), - out int x - ) - && int.TryParse( - split[1] - .Replace( - "y:", - string.Empty, - StringComparison.OrdinalIgnoreCase - ), - out int y - ) - && int.TryParse( - split[2] - .Replace( - "width:", - string.Empty, - StringComparison.OrdinalIgnoreCase - ), - out int width - ) - && int.TryParse( - split[3] - .Replace( - "height:", - string.Empty, - StringComparison.OrdinalIgnoreCase - ), - out int height - ): - parsed = (T)Convert.ChangeType(new RectInt(x, y, width, height), type); - return true; - } - } - - parsed = default; - return false; - - static bool InnerParse( - string input, - CommandArgParser typedParser, - out T parsed - ) - { - bool parseOk = typedParser(input, out TParsed value); - if (parseOk) - { - parsed = (T)Convert.ChangeType(value, typeof(T)); - } - else - { - parsed = default; - } - - return parseOk; - } - - static string[] StripAndSplit(string input) - { - string strippedInput = IgnoredValuesForComplexTypes - .Where(ignored => !string.IsNullOrEmpty(ignored)) - .Aggregate( - input, - (current, ignored) => - current.Replace( - ignored, - string.Empty, - StringComparison.OrdinalIgnoreCase - ) - ); - - foreach (char delimiter in Delimiters) - { - if (strippedInput.Contains(delimiter)) - { - return strippedInput.Split(delimiter); - } - } - - return new[] { strippedInput }; - } - - static bool TryGetTypeDefined(string input, out T value) - { - Type type = typeof(T); - if ( - !StaticProperties.TryGetValue( - type, - out Dictionary properties - ) - ) - { - properties = LoadStaticPropertiesForType(); - StaticProperties[type] = properties; - } - - if (properties.TryGetValue(input, out PropertyInfo property)) - { - object resolved = property.GetValue(null); - value = (T)Convert.ChangeType(resolved, type); - return true; - } - - if (!ConstFields.TryGetValue(type, out Dictionary fields)) - { - fields = LoadStaticFieldsForType(); - ConstFields[type] = fields; - } - - if (fields.TryGetValue(input, out FieldInfo field)) - { - object resolved = field.GetValue(null); - value = (T)Convert.ChangeType(resolved, type); - return true; - } - - value = default; - return false; - } - } - - public CommandArg(string contents, char? startQuote = null, char? endQuote = null) - { - this.contents = contents ?? string.Empty; - this.startQuote = startQuote; - this.endQuote = endQuote; - } - - public static bool RegisterParser(CommandArgParser parser, bool force = false) - { - if (parser == null) - { - return false; - } - - Type type = typeof(T); - if (force) - { - RegisteredParsers[type] = parser; - return true; - } - - return RegisteredParsers.TryAdd(type, parser); - } - - public static bool TryGetParser(out CommandArgParser parser) - { - if (RegisteredParsers.TryGetValue(typeof(T), out object untypedParser)) - { - parser = (CommandArgParser)untypedParser; - return true; - } - - parser = null; - return false; - } - - public static bool UnregisterParser() - { - return UnregisterParser(typeof(T)); - } - - public static bool UnregisterParser(Type type) - { - return RegisteredParsers.Remove(type); - } - - public static int UnregisterAllParsers() - { - int parserCount = RegisteredParsers.Count; - RegisteredParsers.Clear(); - return parserCount; - } - - private static Dictionary LoadStaticPropertiesForType() - { - Type type = typeof(T); - return type.GetProperties(BindingFlags.Static | BindingFlags.Public) - .Where(property => property.PropertyType == type) - .ToDictionary( - property => property.Name, - property => property, - StringComparer.OrdinalIgnoreCase - ); - } - - private static Dictionary LoadStaticFieldsForType() - { - Type type = typeof(T); - return type.GetFields(BindingFlags.Static | BindingFlags.Public) - .Where(field => field.FieldType == type) - .ToDictionary( - field => field.Name, - field => field, - StringComparer.OrdinalIgnoreCase - ); - } - - public override string ToString() - { - return contents; - } - } -} +namespace WallstopStudios.DxCommandTerminal.Backend +{ + using System; + using System.Collections.Generic; + using System.Reflection; + using Parsers; + + public delegate bool CommandArgParser(string input, out T parsed); + + public readonly struct CommandArg + { + static CommandArg() + { + // Register built-in object parsers once for quick lookup + RegisterDefaultObjectParsers(); + } + + private static readonly Lazy TryGetMethod = new(() => + { + MethodInfo[] methods = typeof(CommandArg).GetMethods( + BindingFlags.Instance | BindingFlags.Public + ); + for (int i = 0; i < methods.Length; ++i) + { + MethodInfo m = methods[i]; + if (m.Name != nameof(TryGet)) + { + continue; + } + ParameterInfo[] p = m.GetParameters(); + if (p != null && p.Length == 1) + { + return m; + } + } + return null; + }); + private static readonly Dictionary RegisteredParsers = new(); + private static readonly Dictionary RegisteredObjectParsers = new(); + + // Removed caches for static members and enum values; moved to dedicated parsers + + // Public to allow custom-mutation, if desired + public static readonly HashSet Delimiters = new() { ',', ';', ':', '_', '/', '\\' }; + public static readonly List Quotes = new() { '"', '\'' }; + public static readonly HashSet IgnoredValuesForCleanedTypes = new() { "\r", "\n" }; + public static readonly HashSet DoNotCleanTypes = new() + { + typeof(string), + typeof(char), + typeof(DateTime), + typeof(DateTimeOffset), + }; + public static readonly HashSet IgnoredValuesForComplexTypes = new() + { + "(", + ")", + "[", + "]", + "'", + "`", + "|", + "{", + "}", + "<", + ">", + }; + + public readonly string contents; + public readonly char? startQuote; + public readonly char? endQuote; + + public string CleanedContents + { + get + { + string cleanedString = contents; + foreach (string ignoredValue in IgnoredValuesForCleanedTypes) + { + cleanedString = cleanedString.Replace( + ignoredValue, + string.Empty, + StringComparison.OrdinalIgnoreCase + ); + } + return cleanedString; + } + } + + public bool TryGet(Type type, out object parsed) + { + // TODO: Convert into delegates and cache for performance + MethodInfo genericMethod = TryGetMethod.Value; + if (genericMethod == null) + { + parsed = default; + return false; + } + + MethodInfo constructed = genericMethod.MakeGenericMethod(type); + object[] parameters = { null }; + bool success = (bool)constructed.Invoke(this, parameters); + parsed = parameters[0]; + return success; + } + + public bool TryGet(out T parsed) + { + return TryGet(out parsed, parserOverride: null); + } + + public bool TryGet(out T parsed, CommandArgParser parserOverride) + { + Type type = typeof(T); + string stringValue = DoNotCleanTypes.Contains(type) ? contents : CleanedContents; + + if (parserOverride != null) + { + return parserOverride(stringValue, out parsed); + } + + if (TryGetParser(out CommandArgParser parser)) + { + return parser(stringValue, out parsed); + } + + if (type == typeof(string)) + { + parsed = (T)Convert.ChangeType(stringValue, type); + return true; + } + if (StaticMemberParser.TryParse(stringValue, out parsed)) + { + return true; + } + + if (TryGetObjectParser(type, out IArgParser objectParser)) + { + if (objectParser.TryParse(stringValue, out object objectValue)) + { + parsed = (T)objectValue; + return true; + } + } + + // Enums (hot path via cached values) + if (type.IsEnum) + { + if (EnumArgParser.TryParse(type, stringValue, out object enumObject)) + { + parsed = (T)enumObject; + return true; + } + } + + parsed = default; + return false; + // static member resolution moved to StaticMemberParser + } + + // Consolidated parsing helpers moved to Backend.Parsers.CommandArgParserCommon + + public CommandArg(string contents, char? startQuote = null, char? endQuote = null) + { + this.contents = contents ?? string.Empty; + this.startQuote = startQuote; + this.endQuote = endQuote; + } + + public static bool RegisterParser(CommandArgParser parser, bool force = false) + { + if (parser == null) + { + return false; + } + + Type type = typeof(T); + if (force) + { + RegisteredParsers[type] = parser; + return true; + } + + return RegisteredParsers.TryAdd(type, parser); + } + + public static bool TryGetParser(out CommandArgParser parser) + { + if (RegisteredParsers.TryGetValue(typeof(T), out object untypedParser)) + { + parser = (CommandArgParser)untypedParser; + return true; + } + + parser = null; + return false; + } + + public static bool UnregisterParser() + { + return UnregisterParser(typeof(T)); + } + + public static bool UnregisterParser(Type type) + { + return RegisteredParsers.Remove(type); + } + + public static int UnregisterAllParsers() + { + int parserCount = RegisteredParsers.Count; + RegisteredParsers.Clear(); + return parserCount; + } + + // Object parser registration (IArgParser) + public static bool RegisterObjectParser(IArgParser parser, bool force = false) + { + if (parser == null || parser.TargetType == null) + { + return false; + } + + Type type = parser.TargetType; + if (force) + { + RegisteredObjectParsers[type] = parser; + return true; + } + + return RegisteredObjectParsers.TryAdd(type, parser); + } + + public static bool TryGetObjectParser(Type type, out IArgParser parser) + { + return RegisteredObjectParsers.TryGetValue(type, out parser); + } + + public static bool UnregisterObjectParser(Type type) + { + return RegisteredObjectParsers.Remove(type); + } + + public static int UnregisterAllObjectParsers() + { + int count = RegisteredObjectParsers.Count; + RegisteredObjectParsers.Clear(); + return count; + } + + public static IReadOnlyCollection GetRegisteredObjectParserTypes() + { + // Snapshot for thread-safety and immutability to callers + return new List(RegisteredObjectParsers.Keys); + } + + public static int DiscoverAndRegisterParsers(bool replaceExisting = false) + { + int added = 0; + foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) + { + Type[] types; + try + { + types = asm.GetTypes(); + } + catch (ReflectionTypeLoadException e) + { + List tmp = new(); + if (e.Types != null) + { + for (int i = 0; i < e.Types.Length; ++i) + { + Type t = e.Types[i]; + if (t != null) + { + tmp.Add(t); + } + } + } + types = tmp.ToArray(); + } + + foreach (Type t in types) + { + if (t == null || t.IsAbstract || t.IsGenericTypeDefinition) + { + continue; + } + if (!typeof(IArgParser).IsAssignableFrom(t)) + { + continue; + } + + IArgParser instance = null; + // Prefer public static Instance singleton if available + PropertyInfo instProp = t.GetProperty( + "Instance", + BindingFlags.Public | BindingFlags.Static + ); + if ( + instProp != null + && typeof(IArgParser).IsAssignableFrom(instProp.PropertyType) + ) + { + instance = (IArgParser)instProp.GetValue(null); + } + else + { + FieldInfo instField = t.GetField( + "Instance", + BindingFlags.Public | BindingFlags.Static + ); + if ( + instField != null + && typeof(IArgParser).IsAssignableFrom(instField.FieldType) + ) + { + instance = (IArgParser)instField.GetValue(null); + } + } + + if (instance == null) + { + // Fall back to parameterless constructor + ConstructorInfo ctor = t.GetConstructor(Type.EmptyTypes); + if (ctor != null) + { + instance = (IArgParser)Activator.CreateInstance(t); + } + } + + if (instance == null || instance.TargetType == null) + { + continue; + } + + if (RegisterObjectParser(instance, replaceExisting)) + { + added++; + } + } + } + return added; + } + + private static void RegisterDefaultObjectParsers() + { + // Numerics + RegisterObjectParser(BoolArgParser.Instance, true); + RegisterObjectParser(FloatArgParser.Instance, true); + RegisterObjectParser(IntArgParser.Instance, true); + RegisterObjectParser(UIntArgParser.Instance, true); + RegisterObjectParser(LongArgParser.Instance, true); + RegisterObjectParser(ULongArgParser.Instance, true); + RegisterObjectParser(DoubleArgParser.Instance, true); + RegisterObjectParser(ShortArgParser.Instance, true); + RegisterObjectParser(UShortArgParser.Instance, true); + RegisterObjectParser(ByteArgParser.Instance, true); + RegisterObjectParser(SByteArgParser.Instance, true); + RegisterObjectParser(DecimalArgParser.Instance, true); + RegisterObjectParser(BigIntegerArgParser.Instance, true); + + // Misc + RegisterObjectParser(GuidArgParser.Instance, true); + RegisterObjectParser(DateTimeArgParser.Instance, true); + RegisterObjectParser(DateTimeOffsetArgParser.Instance, true); + RegisterObjectParser(CharArgParser.Instance, true); + RegisterObjectParser(TimeSpanArgParser.Instance, true); + RegisterObjectParser(VersionArgParser.Instance, true); + RegisterObjectParser(IPAddressArgParser.Instance, true); + + // Unity types + RegisterObjectParser(Vector2ArgParser.Instance, true); + RegisterObjectParser(Vector3ArgParser.Instance, true); + RegisterObjectParser(Vector4ArgParser.Instance, true); + RegisterObjectParser(Vector2IntArgParser.Instance, true); + RegisterObjectParser(Vector3IntArgParser.Instance, true); + RegisterObjectParser(ColorArgParser.Instance, true); + RegisterObjectParser(QuaternionArgParser.Instance, true); + RegisterObjectParser(RectArgParser.Instance, true); + RegisterObjectParser(RectIntArgParser.Instance, true); + } + + // Static member reflection helpers moved to Parsers.StaticMemberParser + + public override string ToString() + { + return contents; + } + } +} diff --git a/Runtime/CommandTerminal/Backend/CommandAutoComplete.cs b/Runtime/CommandTerminal/Backend/CommandAutoComplete.cs index bb54e1b..9dea0e1 100644 --- a/Runtime/CommandTerminal/Backend/CommandAutoComplete.cs +++ b/Runtime/CommandTerminal/Backend/CommandAutoComplete.cs @@ -1,77 +1,296 @@ -namespace WallstopStudios.DxCommandTerminal.Backend -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Extensions; - - public sealed class CommandAutoComplete - { - private readonly SortedSet _knownWords = new(StringComparer.OrdinalIgnoreCase); - private readonly HashSet _duplicateBuffer = new(StringComparer.OrdinalIgnoreCase); - private readonly List _buffer = new(); - - private readonly CommandHistory _history; - private readonly CommandShell _shell; - - public CommandAutoComplete( - CommandHistory history, - CommandShell shell, - IEnumerable commands = null - ) - { - _history = history ?? throw new ArgumentNullException(nameof(history)); - _shell = shell ?? throw new ArgumentNullException(nameof(shell)); - _knownWords.UnionWith(commands ?? Enumerable.Empty()); - } - - public string[] Complete(string text) - { - return Complete(text: text, buffer: _buffer).ToArray(); - } - - public List Complete(string text, List buffer) - { - WalkHistory(text, onlySuccess: true, onlyErrorFree: false, buffer: buffer); - return buffer; - } - - private void WalkHistory( - string input, - bool onlySuccess, - bool onlyErrorFree, - List buffer - ) - { - if (input.NeedsTrim()) - { - input = input.Trim(); - } - _duplicateBuffer.Clear(); - buffer.Clear(); - foreach ( - string known in _shell - .Commands.Keys.Select(command => - command.NeedsLowerInvariantConversion() - ? command.ToLowerInvariant() - : command - ) - .Concat(_knownWords) - .Concat( - _history.GetHistory(onlySuccess: onlySuccess, onlyErrorFree: onlyErrorFree) - ) - ) - { - if (!known.StartsWith(input, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - if (_duplicateBuffer.Add(known)) - { - buffer.Add(known); - } - } - } - } -} +namespace WallstopStudios.DxCommandTerminal.Backend +{ + using System; + using System.Collections.Generic; + using System.Text; + using Extensions; + + public sealed class CommandAutoComplete + { + private readonly SortedSet _knownWords = new(StringComparer.OrdinalIgnoreCase); + private readonly HashSet _duplicateBuffer = new(StringComparer.OrdinalIgnoreCase); + private readonly List _buffer = new(); + private readonly List _historyScratch = new(); + private readonly StringBuilder _sb = new(); + + private readonly CommandHistory _history; + private readonly CommandShell _shell; + + public string LastCompleterPrefix { get; private set; } + + public bool LastCompletionUsedCompleter { get; private set; } + + public CommandAutoComplete( + CommandHistory history, + CommandShell shell, + IEnumerable commands = null + ) + { + _history = history ?? throw new ArgumentNullException(nameof(history)); + _shell = shell ?? throw new ArgumentNullException(nameof(shell)); + if (commands != null) + { + _knownWords.UnionWith(commands); + } + } + + public string[] Complete(string text) + { + return Complete(text: text, buffer: _buffer).ToArray(); + } + + public List Complete(string text, List buffer) + { + int caret = text?.Length ?? 0; + Complete(text, caret, buffer); + return buffer; + } + + public List Complete(string text, int caretIndex, List buffer) + { + string input = text ?? string.Empty; + buffer.Clear(); + _duplicateBuffer.Clear(); + LastCompleterPrefix = null; + LastCompletionUsedCompleter = false; + + if (string.IsNullOrWhiteSpace(input)) + { + WalkHistory(input, onlySuccess: true, onlyErrorFree: false, buffer: buffer); + return buffer; + } + + int safeCaret = Math.Max(0, Math.Min(caretIndex, input.Length)); + string uptoCaret = + safeCaret <= 0 + ? string.Empty + : (safeCaret < input.Length ? input.Substring(0, safeCaret) : input); + + // Parse command + args up to caret + string working = uptoCaret; + if (!CommandShell.TryEatArgument(ref working, out CommandArg cmdArg)) + { + WalkHistory(input, onlySuccess: true, onlyErrorFree: false, buffer: buffer); + return buffer; + } + + string commandName = cmdArg.contents; + if (!_shell.Commands.TryGetValue(commandName, out CommandInfo cmdInfo)) + { + // Fall back to default behavior if not a known command yet + WalkHistory(input, onlySuccess: true, onlyErrorFree: false, buffer: buffer); + return buffer; + } + + // Collect args typed before cursor + List args = new(); + string lastToken = string.Empty; + bool trailingWhitespace = + uptoCaret.Length > 0 && char.IsWhiteSpace(uptoCaret[uptoCaret.Length - 1]); + while (CommandShell.TryEatArgument(ref working, out CommandArg arg)) + { + lastToken = arg.contents; + args.Add(arg); + } + + string partialArg = trailingWhitespace ? string.Empty : lastToken; + int argIndex = trailingWhitespace ? args.Count : (args.Count - 1); + if (!trailingWhitespace && 0 <= argIndex) + { + // Exclude the partial token from finalized args + args.RemoveAt(args.Count - 1); + } + + // Special case: caret is immediately after the command name with no space. + // Treat this as requesting suggestions for the first argument, but preserve any partial text. + if (!trailingWhitespace && args.Count == 0) + { + argIndex = 0; + } + + // If the command provides a completer, ask it first + bool inArgContext = argIndex >= 0; + if (cmdInfo.completer != null) + { + CommandCompletionContext ctx = new( + input, + commandName, + args, + partialArg, + argIndex, + _shell + ); + + IEnumerable suggestions = + cmdInfo.completer.Complete(ctx) ?? Array.Empty(); + + _sb.Clear(); + _sb.Append(commandName); + if (0 < args.Count) + { + _sb.Append(' '); + for (int i = 0; i < args.Count; ++i) + { + if (i > 0) + { + _sb.Append(' '); + } + _sb.Append(args[i].contents); + } + } + if (argIndex >= 0) + { + _sb.Append(' '); + } + string prefixBase = _sb.ToString(); + + bool addedSuggestions = false; + foreach (string suggestion in suggestions) + { + if (string.IsNullOrWhiteSpace(suggestion)) + { + continue; + } + + string insertion = suggestion; + bool needsQuoting = false; + if (!string.IsNullOrEmpty(insertion)) + { + for (int i = 0; i < insertion.Length; ++i) + { + if (char.IsWhiteSpace(insertion[i])) + { + needsQuoting = true; + break; + } + } + } + if (needsQuoting) + { + // Basic quoting to keep single argument with whitespace + // Escape embedded quotes minimally + insertion = "\"" + insertion.Replace("\"", "\\\"") + "\""; + } + + _sb.Clear(); + _sb.Append(prefixBase); + _sb.Append(insertion); + string full = _sb.ToString(); + string key = full.NeedsLowerInvariantConversion() + ? full.ToLowerInvariant() + : full; + if (_duplicateBuffer.Add(key)) + { + buffer.Add(insertion); + addedSuggestions = true; + } + } + + // If we got any results from the completer, return them. + if (addedSuggestions) + { + LastCompleterPrefix = prefixBase; + LastCompletionUsedCompleter = true; + return buffer; + } + + // If we are in argument context for a command that supports completion, + // prefer context (even if empty) and do not fall back to history/known words. + if (inArgContext) + { + return buffer; + } + } + + // Fallback to built-in completion sources + WalkHistory(input, onlySuccess: true, onlyErrorFree: false, buffer: buffer); + return buffer; + } + + private void WalkHistory( + string input, + bool onlySuccess, + bool onlyErrorFree, + List buffer + ) + { + if (input.NeedsTrim()) + { + input = input.Trim(); + } + string normalizedInput = CommandShell.NormalizeCommandKey(input); + bool useNormalizedMatch = !string.IsNullOrEmpty(normalizedInput); + _duplicateBuffer.Clear(); + buffer.Clear(); + + // Commands + _historyScratch.Clear(); + _shell.CopyCommandNamesTo(_historyScratch); + for (int ci = 0; ci < _historyScratch.Count; ++ci) + { + string command = _historyScratch[ci]; + string normalizedCommand = CommandShell.NormalizeCommandKey(command); + bool matches = useNormalizedMatch + ? normalizedCommand.StartsWith(normalizedInput, StringComparison.Ordinal) + : command.StartsWith(input, StringComparison.OrdinalIgnoreCase); + if (!matches) + { + continue; + } + string duplicateKey = + !string.IsNullOrEmpty(normalizedCommand) ? normalizedCommand + : command.NeedsLowerInvariantConversion() ? command.ToLowerInvariant() + : command; + string display = command.NeedsLowerInvariantConversion() + ? command.ToLowerInvariant() + : command; + if (_duplicateBuffer.Add(duplicateKey)) + { + buffer.Add(display); + } + } + + // Known words + foreach (string known in _knownWords) + { + string normalizedKnown = CommandShell.NormalizeCommandKey(known); + bool matches = useNormalizedMatch + ? normalizedKnown.StartsWith(normalizedInput, StringComparison.Ordinal) + : known.StartsWith(input, StringComparison.OrdinalIgnoreCase); + if (!matches) + { + continue; + } + string duplicateKey = !string.IsNullOrEmpty(normalizedKnown) + ? normalizedKnown + : known; + if (_duplicateBuffer.Add(duplicateKey)) + { + buffer.Add(known); + } + } + + // History + _history.CopyHistoryTo(_historyScratch, onlySuccess, onlyErrorFree); + for (int hi = 0; hi < _historyScratch.Count; ++hi) + { + string known = _historyScratch[hi]; + string normalizedKnown = CommandShell.NormalizeCommandKey(known); + bool matches = useNormalizedMatch + ? normalizedKnown.StartsWith(normalizedInput, StringComparison.Ordinal) + : known.StartsWith(input, StringComparison.OrdinalIgnoreCase); + if (!matches) + { + continue; + } + string duplicateKey = !string.IsNullOrEmpty(normalizedKnown) + ? normalizedKnown + : known; + if (_duplicateBuffer.Add(duplicateKey)) + { + buffer.Add(known); + } + } + } + } +} diff --git a/Runtime/CommandTerminal/Backend/CommandHistory.cs b/Runtime/CommandTerminal/Backend/CommandHistory.cs index eb5b0f3..0edeade 100644 --- a/Runtime/CommandTerminal/Backend/CommandHistory.cs +++ b/Runtime/CommandTerminal/Backend/CommandHistory.cs @@ -1,130 +1,227 @@ -namespace WallstopStudios.DxCommandTerminal.Backend -{ - using System; - using System.Collections.Generic; - using System.Linq; - using DataStructures; - - public sealed class CommandHistory - { - public int Capacity => _history.Capacity; - - private readonly CyclicBuffer<(string text, bool? success, bool? errorFree)> _history; - - private int _position; - - public CommandHistory(int capacity) - { - _history = new CyclicBuffer<(string text, bool? success, bool? errorFree)>(capacity); - } - - public IEnumerable GetHistory(bool onlySuccess, bool onlyErrorFree) - { - return _history - .Where(value => !onlySuccess || value.success == true) - .Where(value => !onlyErrorFree || value.errorFree == true) - .Select(value => value.text); - } - - public void Resize(int newCapacity) - { - _history.Resize(newCapacity); - } - - public bool Push(string commandString, bool? success, bool? errorFree) - { - if (string.IsNullOrWhiteSpace(commandString)) - { - return false; - } - - _history.Add((commandString, success, errorFree)); - _position = _history.Count; - return true; - } - - public string Next(bool skipSameCommands) - { - int initialPosition = _position; - ++_position; - - while ( - skipSameCommands - && 0 <= initialPosition - && initialPosition < _history.Count - && 0 <= _position - && _position < _history.Count - ) - { - if ( - string.Equals( - _history[initialPosition].text, - _history[_position].text, - StringComparison.OrdinalIgnoreCase - ) - ) - { - ++_position; - } - else - { - break; - } - } - - if (0 <= _position && _position < _history.Count) - { - return _history[_position].text; - } - - _position = _history.Count; - return string.Empty; - } - - public string Previous(bool skipSameCommands) - { - int initialPosition = _position; - --_position; - - while ( - skipSameCommands - && 0 <= initialPosition - && initialPosition < _history.Count - && 0 <= _position - && _position < _history.Count - ) - { - if ( - string.Equals( - _history[initialPosition].text, - _history[_position].text, - StringComparison.OrdinalIgnoreCase - ) - ) - { - --_position; - } - else - { - break; - } - } - - if (0 <= _position && _position < _history.Count) - { - return _history[_position].text; - } - - _position = -1; - return string.Empty; - } - - public int Clear() - { - int count = _history.Count; - _history.Clear(); - _position = 0; - return count; - } - } -} +namespace WallstopStudios.DxCommandTerminal.Backend +{ + using System; + using System.Collections.Generic; + using DataStructures; + + public readonly struct CommandHistoryEntry + { + public CommandHistoryEntry(string text, bool? success, bool? errorFree) + { + Text = text ?? string.Empty; + Success = success; + ErrorFree = errorFree; + } + + public string Text { get; } + public bool? Success { get; } + public bool? ErrorFree { get; } + } + + public sealed class CommandHistory + { + public int Capacity => _history.Capacity; + public int Count => _history.Count; + public long Version => _version; + + private readonly CyclicBuffer<(string text, bool? success, bool? errorFree)> _history; + + private int _position; + private long _version; + + public CommandHistory(int capacity) + { + _history = new CyclicBuffer<(string text, bool? success, bool? errorFree)>(capacity); + } + + public IEnumerable GetHistory(bool onlySuccess, bool onlyErrorFree) + { + foreach ((string text, bool? success, bool? errorFree) entry in _history) + { + if (onlySuccess && entry.success != true) + { + continue; + } + if (onlyErrorFree && entry.errorFree != true) + { + continue; + } + yield return entry.text; + } + } + + public void CopyHistoryTo(List buffer, bool onlySuccess, bool onlyErrorFree) + { + if (buffer == null) + { + return; + } + buffer.Clear(); + int count = _history.Count; + for (int i = 0; i < count; ++i) + { + (string text, bool? success, bool? errorFree) entry = _history[i]; + if (onlySuccess && entry.success != true) + { + continue; + } + if (onlyErrorFree && entry.errorFree != true) + { + continue; + } + buffer.Add(entry.text); + } + } + + public void CopyEntriesTo( + List buffer, + bool onlySuccess, + bool onlyErrorFree + ) + { + if (buffer == null) + { + return; + } + + buffer.Clear(); + int count = _history.Count; + for (int i = 0; i < count; ++i) + { + (string text, bool? success, bool? errorFree) entry = _history[i]; + if (onlySuccess && entry.success != true) + { + continue; + } + if (onlyErrorFree && entry.errorFree != true) + { + continue; + } + + buffer.Add(new CommandHistoryEntry(entry.text, entry.success, entry.errorFree)); + } + } + + public void CopyEntriesTo(List buffer) + { + CopyEntriesTo(buffer, onlySuccess: false, onlyErrorFree: false); + } + + public void Resize(int newCapacity) + { + int previousCount = _history.Count; + _history.Resize(newCapacity); + if (_history.Count != previousCount) + { + _version++; + _position = Math.Min(_position, _history.Count); + } + } + + public bool Push(string commandString, bool? success, bool? errorFree) + { + if (string.IsNullOrWhiteSpace(commandString)) + { + return false; + } + + if (_history.Capacity <= 0) + { + return false; + } + + _history.Add((commandString, success, errorFree)); + _position = _history.Count; + _version++; + return true; + } + + public string Next(bool skipSameCommands) + { + int initialPosition = _position; + ++_position; + + while ( + skipSameCommands + && 0 <= initialPosition + && initialPosition < _history.Count + && 0 <= _position + && _position < _history.Count + ) + { + if ( + string.Equals( + _history[initialPosition].text, + _history[_position].text, + StringComparison.OrdinalIgnoreCase + ) + ) + { + ++_position; + } + else + { + break; + } + } + + if (0 <= _position && _position < _history.Count) + { + return _history[_position].text; + } + + _position = _history.Count; + return string.Empty; + } + + public string Previous(bool skipSameCommands) + { + int initialPosition = _position; + --_position; + + while ( + skipSameCommands + && 0 <= initialPosition + && initialPosition < _history.Count + && 0 <= _position + && _position < _history.Count + ) + { + if ( + string.Equals( + _history[initialPosition].text, + _history[_position].text, + StringComparison.OrdinalIgnoreCase + ) + ) + { + --_position; + } + else + { + break; + } + } + + if (0 <= _position && _position < _history.Count) + { + return _history[_position].text; + } + + _position = -1; + return string.Empty; + } + + public int Clear() + { + int count = _history.Count; + _history.Clear(); + _position = 0; + if (0 < count) + { + _version++; + } + return count; + } + } +} diff --git a/Runtime/CommandTerminal/Backend/CommandInfo.cs b/Runtime/CommandTerminal/Backend/CommandInfo.cs index cde4b8f..0b8f776 100644 --- a/Runtime/CommandTerminal/Backend/CommandInfo.cs +++ b/Runtime/CommandTerminal/Backend/CommandInfo.cs @@ -1,28 +1,34 @@ -namespace WallstopStudios.DxCommandTerminal.Backend -{ - using System; - - public readonly struct CommandInfo - { - public readonly Action proc; - public readonly int minArgCount; - public readonly int maxArgCount; - public readonly string help; - public readonly string hint; - - public CommandInfo( - Action proc, - int minArgCount, - int maxArgCount, - string help, - string hint - ) - { - this.proc = proc; - this.maxArgCount = maxArgCount; - this.minArgCount = minArgCount; - this.help = help; - this.hint = hint; - } - } -} +namespace WallstopStudios.DxCommandTerminal.Backend +{ + using System; + + public readonly struct CommandInfo + { + public readonly Action proc; + public readonly int minArgCount; + public readonly int maxArgCount; + public readonly string help; + public readonly string hint; + public readonly IArgumentCompleter completer; + public readonly bool includeInHistory; + + public CommandInfo( + Action proc, + int minArgCount, + int maxArgCount, + string help, + string hint, + IArgumentCompleter completer = null, + bool includeInHistory = true + ) + { + this.proc = proc; + this.maxArgCount = maxArgCount; + this.minArgCount = minArgCount; + this.help = help; + this.hint = hint; + this.completer = completer; + this.includeInHistory = includeInHistory; + } + } +} diff --git a/Runtime/CommandTerminal/Backend/CommandLog.cs b/Runtime/CommandTerminal/Backend/CommandLog.cs index f5fd2c3..3f4b079 100644 --- a/Runtime/CommandTerminal/Backend/CommandLog.cs +++ b/Runtime/CommandTerminal/Backend/CommandLog.cs @@ -1,121 +1,264 @@ -namespace WallstopStudios.DxCommandTerminal.Backend -{ - using System; - using System.Collections.Generic; - using System.Linq; - using DataStructures; - using UnityEngine; - - public enum TerminalLogType - { - Error = LogType.Error, - Assert = LogType.Assert, - Warning = LogType.Warning, - Message = LogType.Log, - Exception = LogType.Exception, - Input, - ShellMessage, - } - - public readonly struct LogItem - { - public readonly TerminalLogType type; - public readonly string message; - public readonly string stackTrace; - - public LogItem(TerminalLogType type, string message, string stackTrace) - { - this.type = type; - this.message = message ?? string.Empty; - this.stackTrace = stackTrace ?? string.Empty; - } - } - - public sealed class CommandLog - { - private static readonly string[] NewlineSeparators = { "\r\n", "\n", "\r" }; - private static readonly string JoinSeparator = Environment.NewLine; - - public IReadOnlyList Logs => _logs; - public int Capacity => _logs.Capacity; - public long Version => _version; - - public readonly HashSet ignoredLogTypes; - - private readonly CyclicBuffer _logs; - - private long _version; - - public CommandLog(int maxItems, IEnumerable ignoredLogTypes = null) - { - _logs = new CyclicBuffer(maxItems); - this.ignoredLogTypes = new HashSet( - ignoredLogTypes ?? Enumerable.Empty() - ); - } - - public bool HandleLog(string message, TerminalLogType type, bool includeStackTrace = true) - { - string stackTrace = includeStackTrace ? GetAccurateStackTrace() : string.Empty; - return HandleLog(message, stackTrace, type); - } - - private static string GetAccurateStackTrace() - { - string fullStackTrace = StackTraceUtility.ExtractStackTrace(); - if (string.IsNullOrWhiteSpace(fullStackTrace)) - { - return fullStackTrace; - } - - string[] lines = fullStackTrace.Split(NewlineSeparators, StringSplitOptions.None); - - int startIndex = 1; - while ( - startIndex < lines.Length - && lines[startIndex] - .Contains( - "WallstopStudios.DxCommandTerminal", - StringComparison.OrdinalIgnoreCase - ) - ) - { - ++startIndex; - } - - return lines.Length <= startIndex - ? string.Empty - : string.Join(JoinSeparator, lines, startIndex, lines.Length - startIndex); - } - - public bool HandleLog(string message, string stackTrace, TerminalLogType type) - { - if (ignoredLogTypes.Contains(type)) - { - return false; - } - - _version++; - LogItem log = new(type, message, stackTrace); - _logs.Add(log); - return true; - } - - public int Clear() - { - int logCount = _logs.Count; - _logs.Clear(); - _version++; - return logCount; - } - - public void Resize(int newCapacity) - { - if (newCapacity < _logs.Count) - { - _version++; - } - _logs.Resize(newCapacity); - } - } -} +namespace WallstopStudios.DxCommandTerminal.Backend +{ + using System; + using System.Collections.Concurrent; + using System.Collections.Generic; + using DataStructures; + using UnityEngine; + + public enum TerminalLogType + { + Error = LogType.Error, + Assert = LogType.Assert, + Warning = LogType.Warning, + Message = LogType.Log, + Exception = LogType.Exception, + Input, + ShellMessage, + } + + public readonly struct LogItem + { + public readonly TerminalLogType type; + public readonly string message; + public readonly string stackTrace; + + public LogItem(TerminalLogType type, string message, string stackTrace) + { + this.type = type; + this.message = message ?? string.Empty; + this.stackTrace = stackTrace ?? string.Empty; + } + } + + public sealed class CommandLog + { + private const string InternalNamespace = "WallstopStudios.DxCommandTerminal"; + + public IReadOnlyList Logs => _logs; + public int Capacity => _logs.Capacity; + public long Version => _version; + + public readonly HashSet ignoredLogTypes; + public readonly HashSet allowedLogTypes; + + private readonly CyclicBuffer _logs; + + private long _version; + + private readonly ConcurrentQueue<( + string message, + string stackTrace, + TerminalLogType type, + bool includeStackTrace + )> _pending = new(); + + public CommandLog( + int maxItems, + IEnumerable blockedLogTypes = null, + IEnumerable allowedLogTypes = null + ) + { + _logs = new CyclicBuffer(maxItems); + blockedLogTypes ??= Array.Empty(); + this.ignoredLogTypes = new HashSet(blockedLogTypes); + allowedLogTypes ??= Array.Empty(); + this.allowedLogTypes = new HashSet(allowedLogTypes); + } + + public bool HandleLog(string message, TerminalLogType type, bool includeStackTrace = true) + { + // Main-thread direct path retained for back-compat + string stackTrace = includeStackTrace ? GetAccurateStackTrace() : string.Empty; + return HandleLog(message, stackTrace, type); + } + + private static string GetAccurateStackTrace() + { + string fullStackTrace = StackTraceUtility.ExtractStackTrace(); + if (string.IsNullOrWhiteSpace(fullStackTrace)) + { + return fullStackTrace; + } + int length = fullStackTrace.Length; + int index = 0; + // Skip the first line (StackTraceUtility includes a leading line) + while (index < length && fullStackTrace[index] != '\n' && fullStackTrace[index] != '\r') + { + index++; + } + // Consume newline chars + while ( + index < length && (fullStackTrace[index] == '\n' || fullStackTrace[index] == '\r') + ) + { + index++; + } + + // Skip frames inside our own namespace for clearer logs + while (index < length) + { + int lineStart = index; + // Find end of line + while ( + index < length && fullStackTrace[index] != '\n' && fullStackTrace[index] != '\r' + ) + { + index++; + } + + int lineLen = index - lineStart; + bool isInternal = + fullStackTrace.IndexOf( + InternalNamespace, + lineStart, + lineLen, + StringComparison.OrdinalIgnoreCase + ) >= 0; + if (!isInternal) + { + // Return from this line onward + return fullStackTrace.Substring(lineStart); + } + + // Move to next line start (skip newline chars) + while ( + index < length + && (fullStackTrace[index] == '\n' || fullStackTrace[index] == '\r') + ) + { + index++; + } + } + + return string.Empty; + } + + public bool HandleLog(string message, string stackTrace, TerminalLogType type) + { + if (!IsLogTypePermitted(type)) + { + return false; + } + + _version++; + LogItem log = new(type, message, stackTrace); + _logs.Add(log); + return true; + } + + public void EnqueueMessage(string message, TerminalLogType type, bool includeStackTrace) + { + if (!IsLogTypePermitted(type)) + { + return; + } + _pending.Enqueue((message ?? string.Empty, string.Empty, type, includeStackTrace)); + } + + public void EnqueueUnityLog(string message, string stackTrace, TerminalLogType type) + { + if (!IsLogTypePermitted(type)) + { + return; + } + _pending.Enqueue((message ?? string.Empty, stackTrace ?? string.Empty, type, false)); + } + + public int DrainPending() + { + int added = 0; + while ( + _pending.TryDequeue( + out ( + string message, + string stackTrace, + TerminalLogType type, + bool includeStackTrace + ) item + ) + ) + { + string stack = item.includeStackTrace ? GetAccurateStackTrace() : item.stackTrace; + if (!IsLogTypePermitted(item.type)) + { + continue; + } + _version++; + _logs.Add(new LogItem(item.type, item.message, stack)); + added++; + } + + return added; + } + + public int Clear() + { + int logCount = _logs.Count; + _logs.Clear(); + _version++; + return logCount; + } + + public int RemoveWhere(Func predicate) + { + if (predicate == null) + { + return 0; + } + + int count = _logs.Count; + if (count == 0) + { + return 0; + } + + List retained = new(count); + for (int i = 0; i < count; ++i) + { + LogItem entry = _logs[i]; + if (!predicate(entry)) + { + retained.Add(entry); + } + } + + if (retained.Count == count) + { + return 0; + } + + _logs.Clear(); + for (int i = 0; i < retained.Count; ++i) + { + _logs.Add(retained[i]); + } + _version++; + return count - retained.Count; + } + + public void Resize(int newCapacity) + { + if (newCapacity < _logs.Count) + { + _version++; + } + _logs.Resize(newCapacity); + } + + private bool IsLogTypePermitted(TerminalLogType type) + { + if (ignoredLogTypes.Contains(type)) + { + return false; + } + + if (allowedLogTypes.Count > 0 && !allowedLogTypes.Contains(type)) + { + return false; + } + + return true; + } + } +} diff --git a/Runtime/CommandTerminal/Backend/CommandShell.cs b/Runtime/CommandTerminal/Backend/CommandShell.cs index e81c048..cb1e198 100644 --- a/Runtime/CommandTerminal/Backend/CommandShell.cs +++ b/Runtime/CommandTerminal/Backend/CommandShell.cs @@ -1,569 +1,878 @@ -namespace WallstopStudios.DxCommandTerminal.Backend -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using System.Reflection; - using System.Text; - using Attributes; - using UnityEngine; - - public sealed class CommandShell - { - private static readonly string[] IgnoredTypes = { "JetBrains.Rider" }; - - public static readonly Lazy<( - MethodInfo method, - RegisterCommandAttribute attribute - )[]> RegisteredCommands = new(() => - { - List<(MethodInfo, RegisterCommandAttribute)> commands = new(); - const BindingFlags methodFlags = - BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; - - Assembly[] ourAssembly = { typeof(BuiltInCommands).Assembly }; - foreach ( - Type type in AppDomain - .CurrentDomain.GetAssemblies() - /* - Force our assembly to be processed last so user commands, - if they conflict with in-built ones, are always registered first. - */ - .Except(ourAssembly) - .Concat(ourAssembly) - .SelectMany(assembly => assembly.GetTypes()) - ) - { - try - { - foreach (MethodInfo method in type.GetMethods(methodFlags)) - { - try - { - if ( - Attribute.GetCustomAttribute( - method, - typeof(RegisterCommandAttribute) - ) - is not RegisterCommandAttribute attribute - ) - { - continue; - } - - attribute.NormalizeName(method); - commands.Add((method, attribute)); - } - catch (Exception e) - { - if (ShouldIgnoreExceptionForType(type)) - { - continue; - } - - Debug.LogError( - $"Failed to resolve method {method.Name} of type {type.FullName} with exception {e}" - ); - } - } - } - catch (Exception e) - { - if (ShouldIgnoreExceptionForType(type)) - { - continue; - } - - Debug.LogError( - $"Failed to resolve methods for type {type.FullName} with exception {e}" - ); - } - } - - return commands.ToArray(); - }); - - private readonly List _arguments = new(); // Cache for performance - - private readonly HashSet _autoRegisteredCommands = new( - StringComparer.OrdinalIgnoreCase - ); - - private readonly StringBuilder _commandBuilder = new(); - - private readonly SortedDictionary _commands = new( - StringComparer.OrdinalIgnoreCase - ); - - private readonly Queue _errorMessages = new(); - - private readonly CommandHistory _history; - private readonly HashSet _ignoredCommands = new(StringComparer.OrdinalIgnoreCase); - - private readonly SortedDictionary _rejectedCommands = new( - StringComparer.OrdinalIgnoreCase - ); - - private readonly SortedDictionary _variables = new( - StringComparer.OrdinalIgnoreCase - ); - - public CommandShell(CommandHistory history) - { - _history = history ?? throw new ArgumentNullException(nameof(history)); - } - - public IReadOnlyDictionary Commands => _commands; - public IReadOnlyDictionary Variables => _variables; - - public ImmutableHashSet AutoRegisteredCommands { get; private set; } = - ImmutableHashSet.Empty; - - public ImmutableHashSet IgnoredCommands { get; private set; } = - ImmutableHashSet.Empty; - - public bool IgnoringDefaultCommands { get; private set; } - - public bool HasErrors => 0 < _errorMessages.Count; - - private static bool ShouldIgnoreExceptionForType(Type type) - { - foreach (string ignoredType in IgnoredTypes) - { - if (type.FullName?.IndexOf(ignoredType, StringComparison.OrdinalIgnoreCase) >= 0) - { - return true; - } - } - - return false; - } - - public bool TryConsumeErrorMessage(out string errorMessage) - { - return _errorMessages.TryDequeue(out errorMessage); - } - - public int ClearAllCommands() - { - return ClearAutoRegisteredCommands() + ClearCustomCommands(); - } - - public int ClearCustomCommands() - { - int count = _commands.Count; - _commands.Clear(); - return count; - } - - //public bool RemoveCommand - - public int ClearAutoRegisteredCommands() - { - int count = _autoRegisteredCommands.Count; - foreach (string command in _autoRegisteredCommands) - { - _commands.Remove(command); - } - - _autoRegisteredCommands.Clear(); - AutoRegisteredCommands = ImmutableHashSet.Empty; - return count; - } - - public void InitializeAutoRegisteredCommands( - IEnumerable ignoredCommands = null, - bool ignoreDefaultCommands = false - ) - { - IgnoringDefaultCommands = ignoreDefaultCommands; - ClearAutoRegisteredCommands(); - _ignoredCommands.Clear(); - _ignoredCommands.UnionWith(ignoredCommands ?? Enumerable.Empty()); - foreach (string ignoredCommand in _ignoredCommands) - { - _commands.Remove(ignoredCommand); - } - - IgnoredCommands = _ignoredCommands.ToImmutableHashSet(StringComparer.OrdinalIgnoreCase); - _rejectedCommands.Clear(); - - foreach ( - (MethodInfo method, RegisterCommandAttribute attribute) in RegisteredCommands.Value - ) - { - string commandName = attribute.Name; - if (_ignoredCommands.Contains(commandName)) - { - continue; - } - - if (ignoreDefaultCommands && attribute.Default) - { - continue; - } - - if (attribute.EditorOnly && !Application.isEditor) - { - continue; - } - - if (attribute.DevelopmentOnly && !Application.isEditor && !Debug.isDebugBuild) - { - continue; - } - - ParameterInfo[] methodsParams = method.GetParameters(); - if ( - methodsParams.Length != 1 - || methodsParams[0].ParameterType != typeof(CommandArg[]) - ) - { - _rejectedCommands.TryAdd(commandName, method); - continue; - } - - // Perf boost, much cheaper than running reflection on invoking the method - Action proc = - (Action) - Delegate.CreateDelegate(typeof(Action), method); - bool success = AddCommand( - commandName, - proc, - attribute.MinArgCount, - attribute.MaxArgCount, - attribute.Help, - attribute.Hint - ); - if (success) - { - _autoRegisteredCommands.Add(commandName); - } - } - - AutoRegisteredCommands = _autoRegisteredCommands.ToImmutableHashSet( - StringComparer.OrdinalIgnoreCase - ); - - foreach (KeyValuePair command in _rejectedCommands) - { - IssueErrorMessage( - $"{command.Key} has an invalid signature. " - + $"Expected: {command.Value.Name}(CommandArg[]). " - + $"Found: {command.Value.Name}({string.Join(",", command.Value.GetParameters().Select(p => p.ParameterType.Name))})" - ); - } - } - - /// - /// Parses an input line into a command and runs that command. - /// - public bool RunCommand(string line) - { - string remaining = line; - _arguments.Clear(); - - while (!string.IsNullOrWhiteSpace(remaining)) - { - if (!TryEatArgument(ref remaining, out CommandArg argument)) - { - continue; - } - - string argumentString = argument.contents; - if (argument.endQuote == null) - { - if (string.IsNullOrWhiteSpace(argumentString)) - { - continue; - } - - if (argumentString.StartsWith('$')) - { - string variableName = argumentString[1..]; - - if (_variables.TryGetValue(variableName, out CommandArg variable)) - { - // Replace variable argument if it's defined - argument = variable; - } - } - } - - _arguments.Add(argument); - } - - if (_arguments.Count == 0) - { - _history.Push(line, false, true); - return false; - } - - string commandName = _arguments[0].contents ?? string.Empty; - // Remove command name from arguments - _arguments.RemoveAt(0); - - return RunCommand( - commandName, - _arguments.Count == 0 ? Array.Empty() : _arguments.ToArray() - ); - } - - public bool RunCommand(string commandName, CommandArg[] arguments) - { - _commandBuilder.Clear(); - _commandBuilder.Append(commandName); - if (arguments.Length != 0) - { - _commandBuilder.Append(' '); - } - - for (int i = 0; i < arguments.Length; ++i) - { - CommandArg argument = arguments[i]; - if (argument.startQuote != null) - { - _commandBuilder.Append(argument.startQuote.Value); - } - - _commandBuilder.Append(argument.contents); - if (argument.endQuote != null) - { - _commandBuilder.Append(argument.endQuote.Value); - } - - if (i != arguments.Length - 1) - { - _commandBuilder.Append(' '); - } - } - - string line = _commandBuilder.ToString(); - - if (string.IsNullOrWhiteSpace(commandName)) - { - IssueErrorMessage($"Invalid command name '{commandName}'"); - // Don't log empty commands - return false; - } - - if (commandName.Contains(' ')) - { - commandName = commandName.Replace( - " ", - string.Empty, - StringComparison.OrdinalIgnoreCase - ); - } - - if (!_commands.TryGetValue(commandName, out CommandInfo command)) - { - IssueErrorMessage($"Command {commandName} not found"); - _history.Push(line, false, false); - return false; - } - - int argCount = arguments.Length; - string errorMessage = null; - int requiredArg = 0; - - if (argCount < command.minArgCount) - { - errorMessage = command.minArgCount == command.maxArgCount ? "exactly" : "at least"; - requiredArg = command.minArgCount; - } - else if (0 <= command.maxArgCount && command.maxArgCount < argCount) - { - // Do not check max allowed number of arguments if it is -1 - errorMessage = command.minArgCount == command.maxArgCount ? "exactly" : "at most"; - requiredArg = command.maxArgCount; - } - - if (!string.IsNullOrEmpty(errorMessage)) - { - string pluralFix = requiredArg == 1 ? "" : "s"; - - string invalidMessage = - $"{commandName} requires {errorMessage} {requiredArg} argument{pluralFix}"; - if (!string.IsNullOrWhiteSpace(command.hint)) - { - invalidMessage += $"\n -> Usage: {command.hint}"; - } - - _errorMessages.Enqueue(invalidMessage); - _history.Push(line, false, false); - return false; - } - - int errorCount = _errorMessages.Count; - command.proc?.Invoke(arguments); - _history.Push(line, true, errorCount == _errorMessages.Count); - return true; - } - - // ReSharper disable once MemberCanBePrivate.Global - public bool AddCommand(string name, CommandInfo info) - { - if (string.IsNullOrWhiteSpace(name)) - { - IssueErrorMessage($"Invalid Command Name: {name}"); - return false; - } - - if (name.Contains(' ')) - { - name = name.Replace(" ", string.Empty, StringComparison.OrdinalIgnoreCase); - } - - if (!_commands.TryAdd(name, info)) - { - IssueErrorMessage($"Command {name} is already defined."); - return false; - } - - return true; - } - - // ReSharper disable once MemberCanBePrivate.Global - public bool AddCommand( - string name, - Action proc, - int minArgs = 0, - int maxArgs = -1, - string help = "", - string hint = null - ) - { - CommandInfo info = new(proc, minArgs, maxArgs, help, hint); - return AddCommand(name, info); - } - - public bool SetVariable(string name, string value) - { - value ??= string.Empty; - return SetVariable(name, new CommandArg(value)); - } - - public bool ClearVariable(string name) - { - if (string.IsNullOrWhiteSpace(name)) - { - IssueErrorMessage($"Invalid Variable Name: {name}"); - return false; - } - - if (name.Contains(' ')) - { - name = name.Replace(" ", string.Empty, StringComparison.OrdinalIgnoreCase); - } - - return _variables.Remove(name); - } - - // ReSharper disable once MemberCanBePrivate.Global - public bool SetVariable(string name, CommandArg value) - { - if (string.IsNullOrWhiteSpace(name)) - { - IssueErrorMessage($"Invalid Variable Name: {name}"); - return false; - } - - if (name.Contains(' ')) - { - name = name.Replace(" ", string.Empty, StringComparison.OrdinalIgnoreCase); - } - - _variables[name] = value; - return true; - } - - // ReSharper disable once UnusedMember.Global - public bool TryGetVariable(string name, out CommandArg variable) - { - if (string.IsNullOrWhiteSpace(name)) - { - variable = default; - return false; - } - - if (name.Contains(' ')) - { - name = - name.Replace(" ", string.Empty, StringComparison.OrdinalIgnoreCase) - ?? string.Empty; - } - - return _variables.TryGetValue(name, out variable); - } - - public void IssueErrorMessage(string format, params object[] parameters) - { - string formattedMessage = - (parameters is { Length: > 0 } ? string.Format(format, parameters) : format) - ?? string.Empty; - _errorMessages.Enqueue(formattedMessage); - } - - public static bool TryEatArgument(ref string stringValue, out CommandArg arg) - { - stringValue = stringValue.TrimStart(); - if (stringValue.Length == 0) - { - arg = default; - return false; - } - - char firstChar = stringValue[0]; - if (CommandArg.Quotes.Contains(firstChar)) - { - int closingQuoteIndex = -1; - - // Find the matching closing quote. - for (int i = 1; i < stringValue.Length; ++i) - { - if (stringValue[i] == firstChar) - { - closingQuoteIndex = i; - break; - } - } - - if (closingQuoteIndex < 0) - { - // No closing quote was found; consume the rest of the string (excluding the opening quote). - string input = stringValue.Substring(1); - arg = new CommandArg(input, firstChar); - stringValue = string.Empty; - } - else - { - // Extract the argument inside the quotes. - string input = stringValue.Substring(1, closingQuoteIndex - 1); - arg = new CommandArg(input, firstChar, firstChar); - // Remove the parsed argument (including the quotes) from the input. - stringValue = stringValue.Substring(closingQuoteIndex + 1); - } - } - else - { - // Unquoted argument: find the next space. - int spaceIndex = stringValue.IndexOf(' '); - if (spaceIndex < 0) - { - arg = new CommandArg(stringValue); - stringValue = string.Empty; - } - else - { - string input = stringValue.Substring(0, spaceIndex); - arg = new CommandArg(input); - stringValue = stringValue.Substring(spaceIndex + 1); - } - } - - return true; - } - } -} +namespace WallstopStudios.DxCommandTerminal.Backend +{ + using System; + using System.Collections.Generic; + using System.Collections.Immutable; + using System.Reflection; + using System.Text; + using Attributes; + using UnityEngine; + + public sealed class CommandShell + { + private static readonly string[] IgnoredTypes = { "JetBrains.Rider" }; + + public static readonly Lazy<( + MethodInfo method, + RegisterCommandAttribute attribute + )[]> RegisteredCommands = new(() => + { + List<(MethodInfo, RegisterCommandAttribute)> commands = new(); + const BindingFlags methodFlags = + BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; + + Assembly ourAsm = typeof(BuiltInCommands).Assembly; + Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); + + // First process all but our assembly + for (int ai = 0; ai < assemblies.Length; ++ai) + { + Assembly asm = assemblies[ai]; + if (asm == ourAsm) + { + continue; + } + ProcessAssembly(asm, methodFlags, commands); + } + + // Then process our assembly last + ProcessAssembly(ourAsm, methodFlags, commands); + + return commands.ToArray(); + + static void ProcessAssembly( + Assembly assembly, + BindingFlags methodFlags, + List<(MethodInfo, RegisterCommandAttribute)> commands + ) + { + Type[] types; + try + { + types = assembly.GetTypes(); + } + catch + { + return; + } + + for (int ti = 0; ti < types.Length; ++ti) + { + Type type = types[ti]; + if (type == null) + { + continue; + } + try + { + MethodInfo[] methods = type.GetMethods(methodFlags); + for (int mi = 0; mi < methods.Length; ++mi) + { + MethodInfo method = methods[mi]; + try + { + if ( + Attribute.GetCustomAttribute( + method, + typeof(RegisterCommandAttribute) + ) + is not RegisterCommandAttribute attribute + ) + { + continue; + } + attribute.NormalizeName(method); + commands.Add((method, attribute)); + } + catch (Exception e) + { + if (ShouldIgnoreExceptionForType(type)) + { + continue; + } + Debug.LogError( + $"Failed to resolve method {method.Name} of type {type.FullName} with exception {e}" + ); + } + } + } + catch (Exception e) + { + if (ShouldIgnoreExceptionForType(type)) + { + continue; + } + Debug.LogError( + $"Failed to resolve methods for type {type.FullName} with exception {e}" + ); + } + } + } + }); + + private readonly List _arguments = new(); // Cache for performance + + private readonly HashSet _autoRegisteredCommands = new( + StringComparer.OrdinalIgnoreCase + ); + private readonly HashSet _allowedCommands = new(StringComparer.OrdinalIgnoreCase); + + private readonly StringBuilder _commandBuilder = new(); + + private readonly SortedDictionary _commands = new( + StringComparer.OrdinalIgnoreCase + ); + + private readonly Queue _errorMessages = new(); + + private readonly CommandHistory _history; + private readonly HashSet _ignoredCommands = new(StringComparer.OrdinalIgnoreCase); + + private readonly SortedDictionary _rejectedCommands = new( + StringComparer.OrdinalIgnoreCase + ); + + private readonly SortedDictionary _variables = new( + StringComparer.OrdinalIgnoreCase + ); + + public CommandShell(CommandHistory history) + { + _history = history ?? throw new ArgumentNullException(nameof(history)); + } + + public IReadOnlyDictionary Commands => _commands; + public IReadOnlyDictionary Variables => _variables; + + public ImmutableHashSet AutoRegisteredCommands { get; private set; } = + ImmutableHashSet.Empty; + + public ImmutableHashSet IgnoredCommands { get; private set; } = + ImmutableHashSet.Empty; + public ImmutableHashSet AllowedCommands { get; private set; } = + ImmutableHashSet.Empty; + + public bool IgnoringDefaultCommands { get; private set; } + + public bool HasErrors => 0 < _errorMessages.Count; + + private static bool ShouldIgnoreExceptionForType(Type type) + { + foreach (string ignoredType in IgnoredTypes) + { + if (type.FullName?.IndexOf(ignoredType, StringComparison.OrdinalIgnoreCase) >= 0) + { + return true; + } + } + + return false; + } + + public bool TryConsumeErrorMessage(out string errorMessage) + { + return _errorMessages.TryDequeue(out errorMessage); + } + + public int ClearAllCommands() + { + return ClearAutoRegisteredCommands() + ClearCustomCommands(); + } + + public int ClearCustomCommands() + { + int count = _commands.Count; + _commands.Clear(); + return count; + } + + //public bool RemoveCommand + + public int ClearAutoRegisteredCommands() + { + int count = _autoRegisteredCommands.Count; + foreach (string command in _autoRegisteredCommands) + { + _commands.Remove(command); + } + + _autoRegisteredCommands.Clear(); + AutoRegisteredCommands = ImmutableHashSet.Empty; + return count; + } + + public void InitializeAutoRegisteredCommands( + IEnumerable ignoredCommands = null, + bool ignoreDefaultCommands = false, + IEnumerable allowedCommands = null + ) + { + IgnoringDefaultCommands = ignoreDefaultCommands; + ClearAutoRegisteredCommands(); + _ignoredCommands.Clear(); + if (ignoredCommands != null) + { + _ignoredCommands.UnionWith(ignoredCommands); + } + + foreach (string ignoredCommand in _ignoredCommands) + { + _commands.Remove(ignoredCommand); + } + + IgnoredCommands = _ignoredCommands.ToImmutableHashSet(StringComparer.OrdinalIgnoreCase); + _allowedCommands.Clear(); + if (allowedCommands != null) + { + _allowedCommands.UnionWith(allowedCommands); + } + AllowedCommands = _allowedCommands.ToImmutableHashSet(StringComparer.OrdinalIgnoreCase); + bool hasAllowList = _allowedCommands.Count > 0; + _rejectedCommands.Clear(); + + foreach ( + (MethodInfo method, RegisterCommandAttribute attribute) in RegisteredCommands.Value + ) + { + string commandName = attribute.Name; + if (_ignoredCommands.Contains(commandName)) + { + continue; + } + + if (ignoreDefaultCommands && attribute.Default) + { + continue; + } + + if (attribute.EditorOnly && !Application.isEditor) + { + continue; + } + + if (attribute.DevelopmentOnly && !Application.isEditor && !Debug.isDebugBuild) + { + continue; + } + + ParameterInfo[] methodsParams = method.GetParameters(); + if ( + methodsParams.Length != 1 + || methodsParams[0].ParameterType != typeof(CommandArg[]) + ) + { + _rejectedCommands.TryAdd(commandName, method); + continue; + } + + // Perf boost, much cheaper than running reflection on invoking the method + Action proc = + (Action) + Delegate.CreateDelegate(typeof(Action), method); + // Try resolve optional completer via CommandCompleterAttribute + IArgumentCompleter completer = ResolveCompleter(method); + + if (hasAllowList && !_allowedCommands.Contains(commandName)) + { + continue; + } + + bool success = AddCommand( + commandName, + proc, + attribute.MinArgCount, + attribute.MaxArgCount, + attribute.Help, + attribute.Hint, + completer, + attribute.IncludeInHistory + ); + if (success) + { + _autoRegisteredCommands.Add(commandName); + } + } + + AutoRegisteredCommands = _autoRegisteredCommands.ToImmutableHashSet( + StringComparer.OrdinalIgnoreCase + ); + + foreach (KeyValuePair command in _rejectedCommands) + { + IssueErrorMessage( + $"{command.Key} has an invalid signature. " + + $"Expected: {command.Value.Name}(CommandArg[]). " + + $"Found: {command.Value.Name}(" + + GetParameterTypeNames(command.Value) + + ")" + ); + } + } + + /// + /// Parses an input line into a command and runs that command. + /// + public bool RunCommand(string line) + { + string remaining = line; + _arguments.Clear(); + + while (!string.IsNullOrWhiteSpace(remaining)) + { + if (!TryEatArgument(ref remaining, out CommandArg argument)) + { + continue; + } + + string argumentString = argument.contents; + if (argument.endQuote == null) + { + if (string.IsNullOrWhiteSpace(argumentString)) + { + continue; + } + + if (argumentString.StartsWith('$')) + { + string variableName = argumentString[1..]; + + if (_variables.TryGetValue(variableName, out CommandArg variable)) + { + // Replace variable argument if it's defined + argument = variable; + } + } + } + + _arguments.Add(argument); + } + + if (_arguments.Count == 0) + { + _history.Push(line, false, true); + return false; + } + + string commandName = _arguments[0].contents ?? string.Empty; + int commandSegments = 1; + + if (!TryResolveCommand(commandName, out _, out _)) + { + StringBuilder spacedBuilder = null; + for (int i = 1; i < _arguments.Count; ++i) + { + spacedBuilder ??= new StringBuilder(commandName); + spacedBuilder.Append(' '); + spacedBuilder.Append(_arguments[i].contents); + string candidate = spacedBuilder.ToString(); + + if (TryResolveCommand(candidate, out _, out _)) + { + commandName = candidate; + commandSegments = i + 1; + break; + } + } + } + + _arguments.RemoveRange(0, commandSegments); + + return RunCommand( + commandName, + _arguments.Count == 0 ? Array.Empty() : _arguments.ToArray() + ); + } + + public bool RunCommand(string commandName, CommandArg[] arguments) + { + string originalCommandName = commandName ?? string.Empty; + if (string.IsNullOrWhiteSpace(originalCommandName)) + { + IssueErrorMessage($"Invalid command name '{originalCommandName}'"); + return false; + } + + if ( + !TryResolveCommand( + originalCommandName, + out string canonicalName, + out CommandInfo command + ) + ) + { + string originalLine = BuildCommandLine(originalCommandName, arguments); + IssueErrorMessage($"Command {originalCommandName} not found"); + _history.Push(originalLine, false, false); + return false; + } + + string line = BuildCommandLine(canonicalName, arguments); + + int argCount = arguments.Length; + string errorMessage = null; + int requiredArg = 0; + + if (argCount < command.minArgCount) + { + errorMessage = command.minArgCount == command.maxArgCount ? "exactly" : "at least"; + requiredArg = command.minArgCount; + } + else if (0 <= command.maxArgCount && command.maxArgCount < argCount) + { + // Do not check max allowed number of arguments if it is -1 + errorMessage = command.minArgCount == command.maxArgCount ? "exactly" : "at most"; + requiredArg = command.maxArgCount; + } + + if (!string.IsNullOrEmpty(errorMessage)) + { + string pluralFix = requiredArg == 1 ? "" : "s"; + + string invalidMessage = + $"{canonicalName} requires {errorMessage} {requiredArg} argument{pluralFix}"; + if (!string.IsNullOrWhiteSpace(command.hint)) + { + invalidMessage += $"\n -> Usage: {command.hint}"; + } + + _errorMessages.Enqueue(invalidMessage); + if (command.includeInHistory) + { + _history.Push(line, false, false); + } + return false; + } + + int errorCount = _errorMessages.Count; + command.proc?.Invoke(arguments); + if (command.includeInHistory) + { + _history.Push(line, true, errorCount == _errorMessages.Count); + } + return true; + } + + // ReSharper disable once MemberCanBePrivate.Global + public bool AddCommand(string name, CommandInfo info) + { + if (string.IsNullOrWhiteSpace(name)) + { + IssueErrorMessage($"Invalid Command Name: {name}"); + return false; + } + + if (name.Contains(' ')) + { + name = name.Replace(" ", string.Empty, StringComparison.OrdinalIgnoreCase); + } + + string normalizedCandidate = NormalizeCommandKey(name); + if (string.IsNullOrEmpty(normalizedCandidate)) + { + IssueErrorMessage($"Invalid Command Name: {name}"); + return false; + } + + foreach (string existingName in _commands.Keys) + { + if (string.Equals(existingName, name, StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + if ( + string.Equals( + NormalizeCommandKey(existingName), + normalizedCandidate, + StringComparison.Ordinal + ) + ) + { + IssueErrorMessage( + $"Command {name} conflicts with existing command {existingName}." + ); + return false; + } + } + + if (!_commands.TryAdd(name, info)) + { + IssueErrorMessage($"Command {name} is already defined."); + return false; + } + + return true; + } + + private bool TryResolveCommand( + string inputName, + out string canonicalName, + out CommandInfo command + ) + { + canonicalName = string.Empty; + command = default; + + if (string.IsNullOrWhiteSpace(inputName)) + { + return false; + } + + string candidate = inputName; + if (candidate.Contains(' ')) + { + candidate = candidate.Replace( + " ", + string.Empty, + StringComparison.OrdinalIgnoreCase + ); + } + + string normalizedInput = NormalizeCommandKey(candidate); + + foreach (KeyValuePair entry in _commands) + { + if (string.Equals(entry.Key, candidate, StringComparison.OrdinalIgnoreCase)) + { + canonicalName = entry.Key; + command = entry.Value; + return true; + } + + if ( + string.Equals( + NormalizeCommandKey(entry.Key), + normalizedInput, + StringComparison.Ordinal + ) + ) + { + canonicalName = entry.Key; + command = entry.Value; + return true; + } + } + + return false; + } + + private string BuildCommandLine(string commandName, CommandArg[] arguments) + { + _commandBuilder.Clear(); + _commandBuilder.Append(commandName); + if (arguments.Length != 0) + { + _commandBuilder.Append(' '); + } + + for (int i = 0; i < arguments.Length; ++i) + { + CommandArg argument = arguments[i]; + if (argument.startQuote != null) + { + _commandBuilder.Append(argument.startQuote.Value); + } + + _commandBuilder.Append(argument.contents); + if (argument.endQuote != null) + { + _commandBuilder.Append(argument.endQuote.Value); + } + + if (i != arguments.Length - 1) + { + _commandBuilder.Append(' '); + } + } + + return _commandBuilder.ToString(); + } + + internal static string NormalizeCommandKey(string name) + { + if (string.IsNullOrWhiteSpace(name)) + { + return string.Empty; + } + + StringBuilder builder = null; + for (int i = 0; i < name.Length; ++i) + { + char ch = name[i]; + if (char.IsLetterOrDigit(ch)) + { + builder ??= new StringBuilder(name.Length); + builder.Append(char.ToLowerInvariant(ch)); + } + } + + return builder == null ? string.Empty : builder.ToString(); + } + + // ReSharper disable once MemberCanBePrivate.Global + public bool AddCommand( + string name, + Action proc, + int minArgs = 0, + int maxArgs = -1, + string help = "", + string hint = null, + IArgumentCompleter completer = null, + bool includeInHistory = true + ) + { + CommandInfo info = new(proc, minArgs, maxArgs, help, hint, completer, includeInHistory); + return AddCommand(name, info); + } + + public bool SetVariable(string name, string value) + { + value ??= string.Empty; + return SetVariable(name, new CommandArg(value)); + } + + public bool ClearVariable(string name) + { + if (string.IsNullOrWhiteSpace(name)) + { + IssueErrorMessage($"Invalid Variable Name: {name}"); + return false; + } + + if (name.Contains(' ')) + { + name = name.Replace(" ", string.Empty, StringComparison.OrdinalIgnoreCase); + } + + return _variables.Remove(name); + } + + // ReSharper disable once MemberCanBePrivate.Global + public bool SetVariable(string name, CommandArg value) + { + if (string.IsNullOrWhiteSpace(name)) + { + IssueErrorMessage($"Invalid Variable Name: {name}"); + return false; + } + + if (name.Contains(' ')) + { + name = name.Replace(" ", string.Empty, StringComparison.OrdinalIgnoreCase); + } + + _variables[name] = value; + return true; + } + + internal void CopyCommandNamesTo(List buffer) + { + if (buffer == null) + { + return; + } + buffer.Clear(); + foreach (string key in _commands.Keys) + { + buffer.Add(key); + } + } + + // ReSharper disable once UnusedMember.Global + public bool TryGetVariable(string name, out CommandArg variable) + { + if (string.IsNullOrWhiteSpace(name)) + { + variable = default; + return false; + } + + if (name.Contains(' ')) + { + name = + name.Replace(" ", string.Empty, StringComparison.OrdinalIgnoreCase) + ?? string.Empty; + } + + return _variables.TryGetValue(name, out variable); + } + + public void IssueErrorMessage(string format, params object[] parameters) + { + string formattedMessage = + (parameters is { Length: > 0 } ? string.Format(format, parameters) : format) + ?? string.Empty; + _errorMessages.Enqueue(formattedMessage); + } + + private static string GetParameterTypeNames(MethodInfo method) + { + if (method == null) + { + return string.Empty; + } + + ParameterInfo[] parameters = method.GetParameters(); + if (parameters == null || parameters.Length == 0) + { + return string.Empty; + } + + StringBuilder sb = new(); + for (int i = 0; i < parameters.Length; ++i) + { + if (i > 0) + { + sb.Append(','); + } + ParameterInfo p = parameters[i]; + Type pt = p != null ? p.ParameterType : null; + sb.Append(pt != null ? pt.Name : string.Empty); + } + return sb.ToString(); + } + + public static bool TryEatArgument(ref string stringValue, out CommandArg arg) + { + stringValue = stringValue.TrimStart(); + if (stringValue.Length == 0) + { + arg = default; + return false; + } + + char firstChar = stringValue[0]; + if (CommandArg.Quotes.Contains(firstChar)) + { + int closingQuoteIndex = -1; + + // Find the matching closing quote. + for (int i = 1; i < stringValue.Length; ++i) + { + if (stringValue[i] == firstChar) + { + // Check if this quote is escaped by an odd number of backslashes + int backslashCount = 0; + int j = i - 1; + while (1 <= j && stringValue[j] == '\\') + { + backslashCount++; + j--; + } + if ((backslashCount % 2) == 0) + { + closingQuoteIndex = i; + break; + } + } + } + + if (closingQuoteIndex < 0) + { + // No closing quote was found; consume the rest of the string (excluding the opening quote). + string input = stringValue.Substring(1); + if (firstChar == '\'') + { + input = input.Replace("\\'", "'").Replace("\\\\", "\\"); + } + else if (firstChar == '"') + { + input = input.Replace("\\\"", "\"").Replace("\\\\", "\\"); + } + arg = new CommandArg(input, firstChar); + stringValue = string.Empty; + } + else + { + // Extract the argument inside the quotes. + string input = stringValue.Substring(1, closingQuoteIndex - 1); + // Unescape the matching quote and backslashes + if (firstChar == '\'') + { + input = input.Replace("\\'", "'").Replace("\\\\", "\\"); + } + else if (firstChar == '"') + { + input = input.Replace("\\\"", "\"").Replace("\\\\", "\\"); + } + arg = new CommandArg(input, firstChar, firstChar); + // Remove the parsed argument (including the quotes) from the input. + stringValue = stringValue.Substring(closingQuoteIndex + 1); + } + } + else + { + // Unquoted argument: find the next space. + int spaceIndex = stringValue.IndexOf(' '); + if (spaceIndex < 0) + { + arg = new CommandArg(stringValue); + stringValue = string.Empty; + } + else + { + string input = stringValue.Substring(0, spaceIndex); + arg = new CommandArg(input); + stringValue = stringValue.Substring(spaceIndex + 1); + } + } + + return true; + } + + private static IArgumentCompleter ResolveCompleter(MethodInfo method) + { + try + { + object attr = Attribute.GetCustomAttribute( + method, + typeof(CommandCompleterAttribute) + ); + if (attr is not CommandCompleterAttribute cca) + { + return null; + } + + Type t = cca.CompleterType; + // Prefer a public static Instance property + PropertyInfo instProp = t.GetProperty( + "Instance", + BindingFlags.Public | BindingFlags.Static + ); + if ( + instProp != null + && typeof(IArgumentCompleter).IsAssignableFrom(instProp.PropertyType) + ) + { + return (IArgumentCompleter)instProp.GetValue(null); + } + + // Or a public static Instance field + FieldInfo instField = t.GetField( + "Instance", + BindingFlags.Public | BindingFlags.Static + ); + if ( + instField != null + && typeof(IArgumentCompleter).IsAssignableFrom(instField.FieldType) + ) + { + return (IArgumentCompleter)instField.GetValue(null); + } + + // Else use parameterless constructor + ConstructorInfo ctor = t.GetConstructor(Type.EmptyTypes); + if (ctor != null) + { + return (IArgumentCompleter)Activator.CreateInstance(t); + } + } + catch (Exception) + { + // Swallow and treat as no-completer; errors surface in logs elsewhere + } + + return null; + } + } +} diff --git a/Runtime/CommandTerminal/Backend/Completers.meta b/Runtime/CommandTerminal/Backend/Completers.meta new file mode 100644 index 0000000..c8c73a9 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Completers.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 631b78b1cb8ca704db3d15d7457345ac +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/Completers/FontArgumentCompleter.cs b/Runtime/CommandTerminal/Backend/Completers/FontArgumentCompleter.cs new file mode 100644 index 0000000..aa4320a --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Completers/FontArgumentCompleter.cs @@ -0,0 +1,67 @@ +namespace WallstopStudios.DxCommandTerminal.Backend.Completers +{ + using System; + using System.Collections.Generic; + using UI; + + public sealed class FontArgumentCompleter : IArgumentCompleter + { + public IEnumerable Complete(CommandCompletionContext context) + { + // Only complete the first argument (font name) + if (context.ArgIndex != 0) + { + return Array.Empty(); + } + + TerminalUI terminal = TerminalUI.ActiveTerminal; + if (terminal == null || terminal._fontPack == null) + { + return Array.Empty(); + } + + HashSet set = new(StringComparer.OrdinalIgnoreCase); + List namesList = new(); + List result = new(); + + // Collect unique names (case-insensitive) + foreach (UnityEngine.Font font in terminal._fontPack._fonts) + { + if (font == null) + { + continue; + } + + string name = font.name; + if (string.IsNullOrWhiteSpace(name)) + { + continue; + } + if (set.Add(name)) + { + namesList.Add(name); + } + } + + // Sort deterministically + namesList.Sort(StringComparer.OrdinalIgnoreCase); + + string partial = context.PartialArg ?? string.Empty; + if (string.IsNullOrWhiteSpace(partial)) + { + return namesList; + } + + for (int i = 0; i < namesList.Count; ++i) + { + string n = namesList[i]; + if (n.StartsWith(partial, StringComparison.OrdinalIgnoreCase)) + { + result.Add(n); + } + } + + return result; + } + } +} diff --git a/Runtime/CommandTerminal/Backend/Completers/FontArgumentCompleter.cs.meta b/Runtime/CommandTerminal/Backend/Completers/FontArgumentCompleter.cs.meta new file mode 100644 index 0000000..0b1f466 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Completers/FontArgumentCompleter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 15e03dc2d9132e64b94797812b695fec +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/Completers/ThemeArgumentCompleter.cs b/Runtime/CommandTerminal/Backend/Completers/ThemeArgumentCompleter.cs new file mode 100644 index 0000000..f6e045d --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Completers/ThemeArgumentCompleter.cs @@ -0,0 +1,65 @@ +namespace WallstopStudios.DxCommandTerminal.Backend.Completers +{ + using System; + using System.Collections.Generic; + using Themes; + using UI; + + public sealed class ThemeArgumentCompleter : IArgumentCompleter + { + public IEnumerable Complete(CommandCompletionContext context) + { + // Only complete the first argument (theme) + if (context.ArgIndex != 0) + { + return Array.Empty(); + } + + TerminalUI terminal = TerminalUI.ActiveTerminal; + if (terminal == null || terminal._themePack == null) + { + return Array.Empty(); + } + + HashSet set = new(StringComparer.OrdinalIgnoreCase); + List friendlyList = new(); + List result = new(); + + foreach (string raw in terminal._themePack._themeNames) + { + if (string.IsNullOrWhiteSpace(raw)) + { + continue; + } + string friendly = ThemeNameHelper.GetFriendlyThemeName(raw); + if (string.IsNullOrWhiteSpace(friendly)) + { + continue; + } + if (set.Add(friendly)) + { + friendlyList.Add(friendly); + } + } + + friendlyList.Sort(StringComparer.OrdinalIgnoreCase); + + string partial = context.PartialArg ?? string.Empty; + if (string.IsNullOrWhiteSpace(partial)) + { + return friendlyList; + } + + for (int i = 0; i < friendlyList.Count; ++i) + { + string n = friendlyList[i]; + if (n.StartsWith(partial, StringComparison.OrdinalIgnoreCase)) + { + result.Add(n); + } + } + + return result; + } + } +} diff --git a/Runtime/CommandTerminal/Backend/Completers/ThemeArgumentCompleter.cs.meta b/Runtime/CommandTerminal/Backend/Completers/ThemeArgumentCompleter.cs.meta new file mode 100644 index 0000000..a92ea19 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Completers/ThemeArgumentCompleter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 68121dfae15d1b541bc911995a429ef5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/DefaultTerminalSettingsProvider.cs b/Runtime/CommandTerminal/Backend/DefaultTerminalSettingsProvider.cs new file mode 100644 index 0000000..9390c26 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/DefaultTerminalSettingsProvider.cs @@ -0,0 +1,18 @@ +namespace WallstopStudios.DxCommandTerminal.Backend +{ + internal sealed class DefaultTerminalSettingsProvider : ITerminalSettingsProvider + { + public TerminalRuntimeSettings BuildSettings() + { + return new TerminalRuntimeSettings( + logCapacity: 0, + historyCapacity: 0, + blockedLogTypes: System.Array.Empty(), + allowedLogTypes: System.Array.Empty(), + blockedCommands: System.Array.Empty(), + allowedCommands: System.Array.Empty(), + includeDefaultCommands: true + ); + } + } +} diff --git a/Runtime/CommandTerminal/Backend/DefaultTerminalSettingsProvider.cs.meta b/Runtime/CommandTerminal/Backend/DefaultTerminalSettingsProvider.cs.meta new file mode 100644 index 0000000..5f83dc5 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/DefaultTerminalSettingsProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 983b46dad7ab92b43bbcf7d659cda7aa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/HintDisplayMode.cs b/Runtime/CommandTerminal/Backend/HintDisplayMode.cs index 51e9999..00c51c3 100644 --- a/Runtime/CommandTerminal/Backend/HintDisplayMode.cs +++ b/Runtime/CommandTerminal/Backend/HintDisplayMode.cs @@ -1,13 +1,13 @@ -namespace WallstopStudios.DxCommandTerminal.Backend -{ - using System; - - public enum HintDisplayMode - { - [Obsolete("Use a valid value")] - Unknown = 0, - Always = 1, - AutoCompleteOnly = 2, - Never = 3, - } -} +namespace WallstopStudios.DxCommandTerminal.Backend +{ + using System; + + public enum HintDisplayMode + { + [Obsolete("Use a valid value")] + Unknown = 0, + Always = 1, + AutoCompleteOnly = 2, + Never = 3, + } +} diff --git a/Runtime/CommandTerminal/Backend/IArgumentCompleter.cs b/Runtime/CommandTerminal/Backend/IArgumentCompleter.cs new file mode 100644 index 0000000..231325c --- /dev/null +++ b/Runtime/CommandTerminal/Backend/IArgumentCompleter.cs @@ -0,0 +1,42 @@ +namespace WallstopStudios.DxCommandTerminal.Backend +{ + using System.Collections.Generic; + + /// + /// Context for argument completion queries. + /// + public readonly struct CommandCompletionContext + { + public readonly string FullText; + public readonly string CommandName; + public readonly IReadOnlyList ArgsBeforeCursor; + public readonly string PartialArg; + public readonly int ArgIndex; + public readonly CommandShell Shell; + + public CommandCompletionContext( + string fullText, + string commandName, + IReadOnlyList argsBeforeCursor, + string partialArg, + int argIndex, + CommandShell shell + ) + { + FullText = fullText; + CommandName = commandName; + ArgsBeforeCursor = argsBeforeCursor; + PartialArg = partialArg; + ArgIndex = argIndex; + Shell = shell; + } + } + + /// + /// Implement to provide dynamic, argument-aware completions for a command. + /// + public interface IArgumentCompleter + { + IEnumerable Complete(CommandCompletionContext context); + } +} diff --git a/Runtime/CommandTerminal/Backend/IArgumentCompleter.cs.meta b/Runtime/CommandTerminal/Backend/IArgumentCompleter.cs.meta new file mode 100644 index 0000000..cde02bd --- /dev/null +++ b/Runtime/CommandTerminal/Backend/IArgumentCompleter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ba72911cf45887b4181ca32cded0ddd6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/ITerminalRuntime.cs b/Runtime/CommandTerminal/Backend/ITerminalRuntime.cs new file mode 100644 index 0000000..3111a34 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/ITerminalRuntime.cs @@ -0,0 +1,21 @@ +namespace WallstopStudios.DxCommandTerminal.Backend +{ + /// + /// Represents an isolated runtime backing a terminal instance. Provides access to the + /// command buffer, history, shell, and autocomplete services without relying on static state. + /// + public interface ITerminalRuntime + { + CommandLog Log { get; } + + CommandHistory History { get; } + + CommandShell Shell { get; } + + CommandAutoComplete AutoComplete { get; } + + TerminalRuntimeUpdateResult Configure(in TerminalRuntimeSettings settings, bool forceReset); + + bool LogMessage(TerminalLogType type, string format, params object[] parameters); + } +} diff --git a/Runtime/CommandTerminal/Backend/ITerminalRuntime.cs.meta b/Runtime/CommandTerminal/Backend/ITerminalRuntime.cs.meta new file mode 100644 index 0000000..f06a227 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/ITerminalRuntime.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: feee890667bfd7affb0cae0515944dd0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/ITerminalRuntimeConfiguratorService.cs b/Runtime/CommandTerminal/Backend/ITerminalRuntimeConfiguratorService.cs new file mode 100644 index 0000000..ee8829b --- /dev/null +++ b/Runtime/CommandTerminal/Backend/ITerminalRuntimeConfiguratorService.cs @@ -0,0 +1,21 @@ +namespace WallstopStudios.DxCommandTerminal.Backend +{ + public interface ITerminalRuntimeConfiguratorService + { + TerminalRuntimeModeFlags CurrentMode { get; } + + void SetMode(TerminalRuntimeModeFlags mode); + + bool EditorAutoDiscover { get; set; } + + bool ShouldEnableEditorFeatures(); + + bool ShouldEnableDevelopmentFeatures(); + + bool ShouldEnableProductionFeatures(); + + bool HasFlag(TerminalRuntimeModeFlags value, TerminalRuntimeModeFlags flag); + + int TryAutoDiscoverParsers(); + } +} diff --git a/Runtime/CommandTerminal/Backend/ITerminalRuntimeConfiguratorService.cs.meta b/Runtime/CommandTerminal/Backend/ITerminalRuntimeConfiguratorService.cs.meta new file mode 100644 index 0000000..8b41e2c --- /dev/null +++ b/Runtime/CommandTerminal/Backend/ITerminalRuntimeConfiguratorService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e3c8d0c65b4b4b7c8f1b7a644d8b2699 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/ITerminalRuntimeFactory.cs b/Runtime/CommandTerminal/Backend/ITerminalRuntimeFactory.cs new file mode 100644 index 0000000..ca8fe69 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/ITerminalRuntimeFactory.cs @@ -0,0 +1,10 @@ +namespace WallstopStudios.DxCommandTerminal.Backend +{ + /// + /// Creates instances using provided configuration and services. + /// + public interface ITerminalRuntimeFactory + { + ITerminalRuntime CreateRuntime(ITerminalSettingsProvider settingsProvider); + } +} diff --git a/Runtime/CommandTerminal/Backend/ITerminalRuntimeFactory.cs.meta b/Runtime/CommandTerminal/Backend/ITerminalRuntimeFactory.cs.meta new file mode 100644 index 0000000..b7b73bc --- /dev/null +++ b/Runtime/CommandTerminal/Backend/ITerminalRuntimeFactory.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2625803bb958134499ddfd179074738f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/ITerminalRuntimePool.cs b/Runtime/CommandTerminal/Backend/ITerminalRuntimePool.cs new file mode 100644 index 0000000..5fdba2e --- /dev/null +++ b/Runtime/CommandTerminal/Backend/ITerminalRuntimePool.cs @@ -0,0 +1,11 @@ +namespace WallstopStudios.DxCommandTerminal.Backend +{ + public interface ITerminalRuntimePool + { + bool TryRent(out ITerminalRuntime runtime); + + void Return(ITerminalRuntime runtime); + + void Clear(); + } +} diff --git a/Runtime/CommandTerminal/Backend/ITerminalRuntimePool.cs.meta b/Runtime/CommandTerminal/Backend/ITerminalRuntimePool.cs.meta new file mode 100644 index 0000000..1f2092d --- /dev/null +++ b/Runtime/CommandTerminal/Backend/ITerminalRuntimePool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a8166db28e175b044b24406b49ac243b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/ITerminalRuntimeProvider.cs b/Runtime/CommandTerminal/Backend/ITerminalRuntimeProvider.cs new file mode 100644 index 0000000..fb53f17 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/ITerminalRuntimeProvider.cs @@ -0,0 +1,7 @@ +namespace WallstopStudios.DxCommandTerminal.Backend +{ + public interface ITerminalRuntimeProvider + { + ITerminalRuntime ActiveRuntime { get; } + } +} diff --git a/Runtime/CommandTerminal/Backend/ITerminalRuntimeProvider.cs.meta b/Runtime/CommandTerminal/Backend/ITerminalRuntimeProvider.cs.meta new file mode 100644 index 0000000..fba1e90 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/ITerminalRuntimeProvider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c347807a02f7409b85670697613c2d86 +timeCreated: 1760674723 diff --git a/Runtime/CommandTerminal/Backend/ITerminalRuntimeScope.cs b/Runtime/CommandTerminal/Backend/ITerminalRuntimeScope.cs new file mode 100644 index 0000000..7670b5d --- /dev/null +++ b/Runtime/CommandTerminal/Backend/ITerminalRuntimeScope.cs @@ -0,0 +1,23 @@ +namespace WallstopStudios.DxCommandTerminal.Backend +{ + public interface ITerminalRuntimeScope + { + ITerminalRuntime ActiveRuntime { get; } + + CommandLog Buffer { get; } + + CommandShell Shell { get; } + + CommandHistory History { get; } + + CommandAutoComplete AutoComplete { get; } + + void RegisterRuntime(ITerminalRuntime runtime); + + void UnregisterRuntime(ITerminalRuntime runtime); + + bool Log(TerminalLogType type, string format, params object[] parameters); + + bool Log(string format, params object[] parameters); + } +} diff --git a/Runtime/CommandTerminal/Backend/ITerminalRuntimeScope.cs.meta b/Runtime/CommandTerminal/Backend/ITerminalRuntimeScope.cs.meta new file mode 100644 index 0000000..2ecb980 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/ITerminalRuntimeScope.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 337ea8736c994ae986716b8a998ca5a1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/ITerminalSettingsProvider.cs b/Runtime/CommandTerminal/Backend/ITerminalSettingsProvider.cs new file mode 100644 index 0000000..c75fe44 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/ITerminalSettingsProvider.cs @@ -0,0 +1,10 @@ +namespace WallstopStudios.DxCommandTerminal.Backend +{ + /// + /// Provides runtime settings for a terminal instance. Allows DI-friendly configuration and testing. + /// + public interface ITerminalSettingsProvider + { + TerminalRuntimeSettings BuildSettings(); + } +} diff --git a/Runtime/CommandTerminal/Backend/ITerminalSettingsProvider.cs.meta b/Runtime/CommandTerminal/Backend/ITerminalSettingsProvider.cs.meta new file mode 100644 index 0000000..567ebf9 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/ITerminalSettingsProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 834dbb0fbc5cbb940948c9cbf46b5dd8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/Parsers.meta b/Runtime/CommandTerminal/Backend/Parsers.meta new file mode 100644 index 0000000..4a4922a --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Parsers.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a2e552d3677ee7a4ea4582f6d73a5ff4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/Parsers/BoolArgParser.cs b/Runtime/CommandTerminal/Backend/Parsers/BoolArgParser.cs new file mode 100644 index 0000000..ab58f64 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Parsers/BoolArgParser.cs @@ -0,0 +1,12 @@ +namespace WallstopStudios.DxCommandTerminal.Backend.Parsers +{ + public sealed class BoolArgParser : ArgParser + { + public static readonly BoolArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out bool value) + { + return bool.TryParse(input, out value); + } + } +} diff --git a/Runtime/CommandTerminal/Backend/Parsers/BoolArgParser.cs.meta b/Runtime/CommandTerminal/Backend/Parsers/BoolArgParser.cs.meta new file mode 100644 index 0000000..c159ee9 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Parsers/BoolArgParser.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 35e5bc89a2e639f4d8811660270ce540 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/Parsers/CommandArgParserCommon.cs b/Runtime/CommandTerminal/Backend/Parsers/CommandArgParserCommon.cs new file mode 100644 index 0000000..0dcc80f --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Parsers/CommandArgParserCommon.cs @@ -0,0 +1,407 @@ +namespace WallstopStudios.DxCommandTerminal.Backend.Parsers +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using Backend; + + internal static class CommandArgParserCommon + { + public static bool TryParseFloatList( + ReadOnlySpan input, + out float a, + out float b, + out float c, + out float d, + out int count + ) + { + a = 0f; + b = 0f; + c = 0f; + d = 0f; + count = 0; + int i = 0; + while (i < input.Length) + { + char ch = input[i]; + if (char.IsWhiteSpace(ch) || IsIgnoredChar(ch)) + { + i++; + continue; + } + if (char.IsLetter(ch)) + { + while (i < input.Length && input[i] != ':') + { + i++; + } + if (i < input.Length && input[i] == ':') + { + i++; + } + continue; + } + if (CommandArg.Delimiters.Contains(ch)) + { + i++; + continue; + } + int start = i; + while (i < input.Length) + { + char cch = input[i]; + if ( + char.IsWhiteSpace(cch) + || CommandArg.Delimiters.Contains(cch) + || IsIgnoredChar(cch) + ) + { + break; + } + i++; + } + ReadOnlySpan slice = input.Slice(start, i - start); + if ( + !float.TryParse( + slice, + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float value + ) + ) + { + return false; + } + switch (count) + { + case 0: + a = value; + break; + case 1: + b = value; + break; + case 2: + c = value; + break; + case 3: + d = value; + break; + default: + break; + } + count++; + } + return 0 < count; + } + + public static bool TryParseIntList( + ReadOnlySpan input, + out int a, + out int b, + out int c, + out int d, + out int count + ) + { + a = 0; + b = 0; + c = 0; + d = 0; + count = 0; + int i = 0; + while (i < input.Length) + { + char ch = input[i]; + if (char.IsWhiteSpace(ch) || IsIgnoredChar(ch)) + { + i++; + continue; + } + if (char.IsLetter(ch)) + { + while (i < input.Length && input[i] != ':') + { + i++; + } + if (i < input.Length && input[i] == ':') + { + i++; + } + continue; + } + if (CommandArg.Delimiters.Contains(ch)) + { + i++; + continue; + } + int start = i; + while (i < input.Length) + { + char cch = input[i]; + if ( + char.IsWhiteSpace(cch) + || CommandArg.Delimiters.Contains(cch) + || IsIgnoredChar(cch) + ) + { + break; + } + i++; + } + ReadOnlySpan slice = input.Slice(start, i - start); + if ( + !int.TryParse( + slice, + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out int value + ) + ) + { + return false; + } + switch (count) + { + case 0: + a = value; + break; + case 1: + b = value; + break; + case 2: + c = value; + break; + case 3: + d = value; + break; + default: + break; + } + count++; + } + return 0 < count; + } + + public static string[] StripAndSplit(string input) + { + string strippedInput = input; + foreach (string ignored in CommandArg.IgnoredValuesForComplexTypes) + { + if (!string.IsNullOrEmpty(ignored)) + { + strippedInput = strippedInput.Replace( + ignored, + string.Empty, + StringComparison.OrdinalIgnoreCase + ); + } + } + + foreach (char delimiter in CommandArg.Delimiters) + { + if (strippedInput.Contains(delimiter)) + { + return strippedInput.Split(delimiter); + } + } + + return new[] { strippedInput }; + } + + public static bool IsIgnoredChar(char ch) + { + return ch == '(' + || ch == ')' + || ch == '[' + || ch == ']' + || ch == '\'' + || ch == '`' + || ch == '|' + || ch == '{' + || ch == '}' + || ch == '<' + || ch == '>'; + } + + public static bool TryParseLabeledFloatMap( + ReadOnlySpan input, + out Dictionary values, + out bool malformed + ) + { + values = new Dictionary(StringComparer.OrdinalIgnoreCase); + malformed = false; + int i = 0; + bool foundAnyLabel = false; + while (i < input.Length) + { + char ch = input[i]; + if (char.IsWhiteSpace(ch) || IsIgnoredChar(ch)) + { + i++; + continue; + } + + if (!char.IsLetter(ch)) + { + i++; + continue; + } + + int labelStart = i; + while (i < input.Length && char.IsLetter(input[i])) + { + i++; + } + ReadOnlySpan labelSpan = input.Slice(labelStart, i - labelStart); + // Skip whitespace + while (i < input.Length && char.IsWhiteSpace(input[i])) + { + i++; + } + if (i >= input.Length || input[i] != ':') + { + // Not a label-value pair; continue scanning + continue; + } + i++; // skip colon + foundAnyLabel = true; + + // Skip whitespace and ignored chars after colon + while (i < input.Length && (char.IsWhiteSpace(input[i]) || IsIgnoredChar(input[i]))) + { + i++; + } + + int valueStart = i; + while ( + i < input.Length + && !char.IsWhiteSpace(input[i]) + && !IsIgnoredChar(input[i]) + && !CommandArg.Delimiters.Contains(input[i]) + && !char.IsLetter(input[i]) + ) + { + i++; + } + + ReadOnlySpan valueSpan = input.Slice(valueStart, i - valueStart); + if (valueSpan.Length == 0) + { + malformed = true; + continue; + } + + if ( + float.TryParse( + valueSpan, + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float f + ) + ) + { + string label = labelSpan.ToString(); + values[label] = f; + } + else + { + malformed = true; + } + } + + return foundAnyLabel; + } + + public static bool TryParseLabeledIntMap( + ReadOnlySpan input, + out Dictionary values, + out bool malformed + ) + { + values = new Dictionary(StringComparer.OrdinalIgnoreCase); + malformed = false; + int i = 0; + bool foundAnyLabel = false; + while (i < input.Length) + { + char ch = input[i]; + if (char.IsWhiteSpace(ch) || IsIgnoredChar(ch)) + { + i++; + continue; + } + + if (!char.IsLetter(ch)) + { + i++; + continue; + } + + int labelStart = i; + while (i < input.Length && char.IsLetter(input[i])) + { + i++; + } + ReadOnlySpan labelSpan = input.Slice(labelStart, i - labelStart); + // Skip whitespace + while (i < input.Length && char.IsWhiteSpace(input[i])) + { + i++; + } + if (i >= input.Length || input[i] != ':') + { + // Not a label-value pair; continue scanning + continue; + } + i++; // skip colon + foundAnyLabel = true; + + // Skip whitespace and ignored chars after colon + while (i < input.Length && (char.IsWhiteSpace(input[i]) || IsIgnoredChar(input[i]))) + { + i++; + } + + int valueStart = i; + while ( + i < input.Length + && !char.IsWhiteSpace(input[i]) + && !IsIgnoredChar(input[i]) + && !CommandArg.Delimiters.Contains(input[i]) + && !char.IsLetter(input[i]) + ) + { + i++; + } + + ReadOnlySpan valueSpan = input.Slice(valueStart, i - valueStart); + if (valueSpan.Length == 0) + { + malformed = true; + continue; + } + + if ( + int.TryParse( + valueSpan, + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out int iv + ) + ) + { + string label = labelSpan.ToString(); + values[label] = iv; + } + else + { + malformed = true; + } + } + + return foundAnyLabel; + } + } +} diff --git a/Runtime/CommandTerminal/Backend/Parsers/CommandArgParserCommon.cs.meta b/Runtime/CommandTerminal/Backend/Parsers/CommandArgParserCommon.cs.meta new file mode 100644 index 0000000..ea80b1e --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Parsers/CommandArgParserCommon.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: deab8afadbdbca14bae57fdc529a2455 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/Parsers/EnumArgParser.cs b/Runtime/CommandTerminal/Backend/Parsers/EnumArgParser.cs new file mode 100644 index 0000000..d7da602 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Parsers/EnumArgParser.cs @@ -0,0 +1,58 @@ +namespace WallstopStudios.DxCommandTerminal.Backend.Parsers +{ + using System; + using System.Collections.Generic; + + public static class EnumArgParser + { + private static readonly Dictionary CachedValues = new(); + private static readonly Dictionary> CachedNames = new(); + + public static bool TryParse(Type enumType, string input, out object value) + { + if (!enumType.IsEnum) + { + value = null; + return false; + } + + // Fast name map (case-insensitive) + if (!CachedNames.TryGetValue(enumType, out Dictionary nameMap)) + { + nameMap = new Dictionary(StringComparer.OrdinalIgnoreCase); + string[] names = Enum.GetNames(enumType); + for (int i = 0; i < names.Length; ++i) + { + string n = names[i]; + nameMap[n] = Enum.Parse(enumType, n); + } + CachedNames[enumType] = nameMap; + } + + if (nameMap.TryGetValue(input, out object named)) + { + value = named; + return true; + } + + // Ordinal path + if (int.TryParse(input, out int ordinal)) + { + if (!CachedValues.TryGetValue(enumType, out Array values)) + { + values = Enum.GetValues(enumType); + CachedValues[enumType] = values; + } + + if (0 <= ordinal && ordinal < values.Length) + { + value = values.GetValue(ordinal); + return true; + } + } + + value = null; + return false; + } + } +} diff --git a/Runtime/CommandTerminal/Backend/Parsers/EnumArgParser.cs.meta b/Runtime/CommandTerminal/Backend/Parsers/EnumArgParser.cs.meta new file mode 100644 index 0000000..3cadc86 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Parsers/EnumArgParser.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c4a2ff59c83f1e544a72523ac98430ec +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/Parsers/IArgParser.cs b/Runtime/CommandTerminal/Backend/Parsers/IArgParser.cs new file mode 100644 index 0000000..7d992e7 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Parsers/IArgParser.cs @@ -0,0 +1,29 @@ +namespace WallstopStudios.DxCommandTerminal.Backend.Parsers +{ + using System; + + public interface IArgParser + { + Type TargetType { get; } + bool TryParse(string input, out object value); + } + + public abstract class ArgParser : IArgParser + { + public Type TargetType => typeof(T); + + public bool TryParse(string input, out object value) + { + if (TryParseTyped(input, out T typed)) + { + value = typed; + return true; + } + + value = default; + return false; + } + + protected abstract bool TryParseTyped(string input, out T value); + } +} diff --git a/Runtime/CommandTerminal/Backend/Parsers/IArgParser.cs.meta b/Runtime/CommandTerminal/Backend/Parsers/IArgParser.cs.meta new file mode 100644 index 0000000..241a5f0 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Parsers/IArgParser.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0527b1ce5bbff7941867e7d429ef6607 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/Parsers/MiscArgParsers.cs b/Runtime/CommandTerminal/Backend/Parsers/MiscArgParsers.cs new file mode 100644 index 0000000..ae4388e --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Parsers/MiscArgParsers.cs @@ -0,0 +1,95 @@ +namespace WallstopStudios.DxCommandTerminal.Backend.Parsers +{ + using System; + using System.Globalization; + using System.Net; + + public sealed class GuidArgParser : ArgParser + { + public static readonly GuidArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out Guid value) + { + return Guid.TryParse(input, out value); + } + } + + public sealed class DateTimeArgParser : ArgParser + { + public static readonly DateTimeArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out DateTime value) + { + bool ok = DateTime.TryParse( + input, + CultureInfo.InvariantCulture, + DateTimeStyles.RoundtripKind, + out value + ); + if (!ok) + { + return false; + } + if (value.Kind == DateTimeKind.Utc) + { + value = value.ToLocalTime(); + } + return true; + } + } + + public sealed class DateTimeOffsetArgParser : ArgParser + { + public static readonly DateTimeOffsetArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out DateTimeOffset value) + { + return DateTimeOffset.TryParse( + input, + CultureInfo.InvariantCulture, + DateTimeStyles.RoundtripKind, + out value + ); + } + } + + public sealed class CharArgParser : ArgParser + { + public static readonly CharArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out char value) + { + return char.TryParse(input, out value); + } + } + + public sealed class TimeSpanArgParser : ArgParser + { + public static readonly TimeSpanArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out TimeSpan value) + { + return TimeSpan.TryParse(input, CultureInfo.InvariantCulture, out value); + } + } + + public sealed class VersionArgParser : ArgParser + { + public static readonly VersionArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out Version value) + { + return Version.TryParse(input, out value); + } + } + + public sealed class IPAddressArgParser : ArgParser + { + public static readonly IPAddressArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out IPAddress value) + { + return IPAddress.TryParse(input, out value); + } + } +} diff --git a/Runtime/CommandTerminal/Backend/Parsers/MiscArgParsers.cs.meta b/Runtime/CommandTerminal/Backend/Parsers/MiscArgParsers.cs.meta new file mode 100644 index 0000000..cb86c46 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Parsers/MiscArgParsers.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1705bc034b8860944a1bdf9395fdd88e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/Parsers/NumericArgParsers.cs b/Runtime/CommandTerminal/Backend/Parsers/NumericArgParsers.cs new file mode 100644 index 0000000..0e9bea0 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Parsers/NumericArgParsers.cs @@ -0,0 +1,185 @@ +namespace WallstopStudios.DxCommandTerminal.Backend.Parsers +{ + using System.Globalization; + using System.Numerics; + + public sealed class FloatArgParser : ArgParser + { + public static readonly FloatArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out float value) + { + return float.TryParse( + input, + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out value + ); + } + } + + public sealed class IntArgParser : ArgParser + { + public static readonly IntArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out int value) + { + return int.TryParse( + input, + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out value + ); + } + } + + public sealed class UIntArgParser : ArgParser + { + public static readonly UIntArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out uint value) + { + return uint.TryParse( + input, + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out value + ); + } + } + + public sealed class LongArgParser : ArgParser + { + public static readonly LongArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out long value) + { + return long.TryParse( + input, + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out value + ); + } + } + + public sealed class ULongArgParser : ArgParser + { + public static readonly ULongArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out ulong value) + { + return ulong.TryParse( + input, + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out value + ); + } + } + + public sealed class DoubleArgParser : ArgParser + { + public static readonly DoubleArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out double value) + { + return double.TryParse( + input, + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out value + ); + } + } + + public sealed class ShortArgParser : ArgParser + { + public static readonly ShortArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out short value) + { + return short.TryParse( + input, + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out value + ); + } + } + + public sealed class UShortArgParser : ArgParser + { + public static readonly UShortArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out ushort value) + { + return ushort.TryParse( + input, + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out value + ); + } + } + + public sealed class ByteArgParser : ArgParser + { + public static readonly ByteArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out byte value) + { + return byte.TryParse( + input, + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out value + ); + } + } + + public sealed class SByteArgParser : ArgParser + { + public static readonly SByteArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out sbyte value) + { + return sbyte.TryParse( + input, + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out value + ); + } + } + + public sealed class DecimalArgParser : ArgParser + { + public static readonly DecimalArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out decimal value) + { + return decimal.TryParse( + input, + NumberStyles.Number, + CultureInfo.InvariantCulture, + out value + ); + } + } + + public sealed class BigIntegerArgParser : ArgParser + { + public static readonly BigIntegerArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out BigInteger value) + { + return BigInteger.TryParse( + input, + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out value + ); + } + } +} diff --git a/Runtime/CommandTerminal/Backend/Parsers/NumericArgParsers.cs.meta b/Runtime/CommandTerminal/Backend/Parsers/NumericArgParsers.cs.meta new file mode 100644 index 0000000..6a6dd68 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Parsers/NumericArgParsers.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4d953dfd8daa61a46a629eb8dc7aff24 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/Parsers/StaticMemberParser.cs b/Runtime/CommandTerminal/Backend/Parsers/StaticMemberParser.cs new file mode 100644 index 0000000..1de1430 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Parsers/StaticMemberParser.cs @@ -0,0 +1,68 @@ +namespace WallstopStudios.DxCommandTerminal.Backend.Parsers +{ + using System; + using System.Collections.Generic; + using System.Reflection; + + public static class StaticMemberParser + { + private static bool initialized; + private static Dictionary Properties; + private static Dictionary Fields; + + private static void EnsureInitialized() + { + if (initialized) + { + return; + } + + Type type = typeof(T); + Properties = new Dictionary(StringComparer.OrdinalIgnoreCase); + PropertyInfo[] props = type.GetProperties(BindingFlags.Static | BindingFlags.Public); + for (int i = 0; i < props.Length; ++i) + { + PropertyInfo p = props[i]; + if (p != null && p.PropertyType == type) + { + Properties[p.Name] = p; + } + } + + Fields = new Dictionary(StringComparer.OrdinalIgnoreCase); + FieldInfo[] fields = type.GetFields(BindingFlags.Static | BindingFlags.Public); + for (int i = 0; i < fields.Length; ++i) + { + FieldInfo f = fields[i]; + if (f != null && f.FieldType == type) + { + Fields[f.Name] = f; + } + } + + initialized = true; + } + + public static bool TryParse(string input, out T value) + { + EnsureInitialized(); + + if (Properties.TryGetValue(input, out PropertyInfo property)) + { + object resolved = property.GetValue(null); + value = (T)resolved; + return true; + } + + if (Fields.TryGetValue(input, out FieldInfo field)) + { + object resolved = field.GetValue(null); + value = (T)resolved; + return true; + } + + value = default; + return false; + } + } +} diff --git a/Runtime/CommandTerminal/Backend/Parsers/StaticMemberParser.cs.meta b/Runtime/CommandTerminal/Backend/Parsers/StaticMemberParser.cs.meta new file mode 100644 index 0000000..f34ba06 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Parsers/StaticMemberParser.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2309a9191602a72439c3dbf826dbe177 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/Parsers/UnityArgParsers.cs b/Runtime/CommandTerminal/Backend/Parsers/UnityArgParsers.cs new file mode 100644 index 0000000..9b3d426 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Parsers/UnityArgParsers.cs @@ -0,0 +1,915 @@ +namespace WallstopStudios.DxCommandTerminal.Backend.Parsers +{ + using System; + using System.Globalization; + using UnityEngine; + + public sealed class Vector2ArgParser : ArgParser + { + public static readonly Vector2ArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out Vector2 value) + { + if ( + CommandArgParserCommon.TryParseLabeledFloatMap( + input.AsSpan(), + out System.Collections.Generic.Dictionary labeled, + out bool malformed + ) + ) + { + if (malformed) + { + value = default; + return false; + } + if ( + labeled.TryGetValue("x", out float lx) && labeled.TryGetValue("y", out float ly) + ) + { + value = new Vector2(lx, ly); + return true; + } + value = default; + return false; + } + if ( + CommandArgParserCommon.TryParseFloatList( + input.AsSpan(), + out float f0, + out float f1, + out float f2, + out _, + out int cnt + ) + ) + { + if (cnt == 2) + { + value = new Vector2(f0, f1); + return true; + } + if (cnt == 3) + { + value = (Vector2)new Vector3(f0, f1, f2); + return true; + } + } + + string[] split = CommandArgParserCommon.StripAndSplit(input); + switch (split.Length) + { + case 2 + when float.TryParse( + split[0], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float x + ) + && float.TryParse( + split[1], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float y + ): + value = new Vector2(x, y); + return true; + case 3 + when float.TryParse( + split[0], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float x + ) + && float.TryParse( + split[1], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float y + ) + && float.TryParse( + split[2], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float z + ): + value = (Vector2)new Vector3(x, y, z); + return true; + } + + value = default; + return false; + } + } + + public sealed class Vector3ArgParser : ArgParser + { + public static readonly Vector3ArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out Vector3 value) + { + if ( + CommandArgParserCommon.TryParseLabeledFloatMap( + input.AsSpan(), + out System.Collections.Generic.Dictionary labeled, + out bool malformed + ) + ) + { + if (malformed) + { + value = default; + return false; + } + if ( + labeled.TryGetValue("x", out float lx) + && labeled.TryGetValue("y", out float ly) + && labeled.TryGetValue("z", out float lz) + ) + { + value = new Vector3(lx, ly, lz); + return true; + } + value = default; + return false; + } + if ( + CommandArgParserCommon.TryParseFloatList( + input.AsSpan(), + out float f0, + out float f1, + out float f2, + out _, + out int cnt + ) + ) + { + if (cnt == 2) + { + value = new Vector3(f0, f1); + return true; + } + if (cnt == 3) + { + value = new Vector3(f0, f1, f2); + return true; + } + } + + string[] split = CommandArgParserCommon.StripAndSplit(input); + switch (split.Length) + { + case 2 + when float.TryParse( + split[0], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float x + ) + && float.TryParse( + split[1], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float y + ): + value = new Vector3(x, y); + return true; + case 3 + when float.TryParse( + split[0], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float x + ) + && float.TryParse( + split[1], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float y + ) + && float.TryParse( + split[2], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float z + ): + value = new Vector3(x, y, z); + return true; + } + + value = default; + return false; + } + } + + public sealed class Vector4ArgParser : ArgParser + { + public static readonly Vector4ArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out Vector4 value) + { + if ( + CommandArgParserCommon.TryParseLabeledFloatMap( + input.AsSpan(), + out System.Collections.Generic.Dictionary labeled, + out bool malformed + ) + ) + { + if (malformed) + { + value = default; + return false; + } + if ( + labeled.TryGetValue("x", out float lx) + && labeled.TryGetValue("y", out float ly) + && labeled.TryGetValue("z", out float lz) + && labeled.TryGetValue("w", out float lw) + ) + { + value = new Vector4(lx, ly, lz, lw); + return true; + } + value = default; + return false; + } + if ( + CommandArgParserCommon.TryParseFloatList( + input.AsSpan(), + out float f0, + out float f1, + out float f2, + out float f3, + out int cnt + ) + ) + { + if (cnt == 2) + { + value = new Vector4(f0, f1); + return true; + } + if (cnt == 3) + { + value = new Vector4(f0, f1, f2); + return true; + } + if (cnt == 4) + { + value = new Vector4(f0, f1, f2, f3); + return true; + } + } + + string[] split = CommandArgParserCommon.StripAndSplit(input); + switch (split.Length) + { + case 2 + when float.TryParse( + split[0], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float x + ) + && float.TryParse( + split[1], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float y + ): + value = new Vector4(x, y); + return true; + case 3 + when float.TryParse( + split[0], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float x + ) + && float.TryParse( + split[1], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float y + ) + && float.TryParse( + split[2], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float z + ): + value = new Vector4(x, y, z); + return true; + case 4 + when float.TryParse( + split[0], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float x + ) + && float.TryParse( + split[1], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float y + ) + && float.TryParse( + split[2], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float z + ) + && float.TryParse( + split[3], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float w + ): + value = new Vector4(x, y, z, w); + return true; + } + + value = default; + return false; + } + } + + public sealed class Vector2IntArgParser : ArgParser + { + public static readonly Vector2IntArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out Vector2Int value) + { + if ( + CommandArgParserCommon.TryParseLabeledIntMap( + input.AsSpan(), + out System.Collections.Generic.Dictionary labeled, + out bool malformed + ) + ) + { + if (malformed) + { + value = default; + return false; + } + if (labeled.TryGetValue("x", out int lx) && labeled.TryGetValue("y", out int ly)) + { + value = new Vector2Int(lx, ly); + return true; + } + value = default; + return false; + } + if ( + CommandArgParserCommon.TryParseIntList( + input.AsSpan(), + out int i0, + out int i1, + out int i2, + out _, + out int icnt + ) + ) + { + if (icnt == 2) + { + value = new Vector2Int(i0, i1); + return true; + } + if (icnt == 3) + { + value = (Vector2Int)new Vector3Int(i0, i1, i2); + return true; + } + } + + string[] split = CommandArgParserCommon.StripAndSplit(input); + switch (split.Length) + { + case 2 + when int.TryParse( + split[0], + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out int x + ) + && int.TryParse( + split[1], + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out int y + ): + value = new Vector2Int(x, y); + return true; + case 3 + when int.TryParse( + split[0], + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out int x + ) + && int.TryParse( + split[1], + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out int y + ) + && int.TryParse( + split[2], + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out int z + ): + value = (Vector2Int)new Vector3Int(x, y, z); + return true; + } + + value = default; + return false; + } + } + + public sealed class Vector3IntArgParser : ArgParser + { + public static readonly Vector3IntArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out Vector3Int value) + { + if ( + CommandArgParserCommon.TryParseLabeledIntMap( + input.AsSpan(), + out System.Collections.Generic.Dictionary labeled, + out bool malformed + ) + ) + { + if (malformed) + { + value = default; + return false; + } + if ( + labeled.TryGetValue("x", out int lx) + && labeled.TryGetValue("y", out int ly) + && labeled.TryGetValue("z", out int lz) + ) + { + value = new Vector3Int(lx, ly, lz); + return true; + } + value = default; + return false; + } + if ( + CommandArgParserCommon.TryParseIntList( + input.AsSpan(), + out int i0, + out int i1, + out int i2, + out _, + out int icnt + ) + ) + { + if (icnt == 2) + { + value = new Vector3Int(i0, i1); + return true; + } + if (icnt == 3) + { + value = new Vector3Int(i0, i1, i2); + return true; + } + } + + string[] split = CommandArgParserCommon.StripAndSplit(input); + switch (split.Length) + { + case 2 + when int.TryParse( + split[0], + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out int x + ) + && int.TryParse( + split[1], + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out int y + ): + value = new Vector3Int(x, y); + return true; + case 3 + when int.TryParse( + split[0], + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out int x + ) + && int.TryParse( + split[1], + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out int y + ) + && int.TryParse( + split[2], + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out int z + ): + value = new Vector3Int(x, y, z); + return true; + } + + value = default; + return false; + } + } + + public sealed class ColorArgParser : ArgParser + { + public static readonly ColorArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out Color value) + { + string colorString = input; + if (colorString.StartsWith("RGBA", StringComparison.OrdinalIgnoreCase)) + { + colorString = colorString.Replace( + "RGBA", + string.Empty, + StringComparison.OrdinalIgnoreCase + ); + } + + if ( + CommandArgParserCommon.TryParseLabeledFloatMap( + colorString.AsSpan(), + out System.Collections.Generic.Dictionary labeled, + out bool malformed + ) + ) + { + if (malformed) + { + value = default; + return false; + } + if ( + labeled.TryGetValue("r", out float r) + && labeled.TryGetValue("g", out float g) + && labeled.TryGetValue("b", out float b) + ) + { + float a = labeled.TryGetValue("a", out float la) ? la : 1.0f; + value = new Color(r, g, b, a); + return true; + } + value = default; + return false; + } + + if ( + CommandArgParserCommon.TryParseFloatList( + colorString.AsSpan(), + out float cr, + out float cg, + out float cb, + out float ca, + out int ccnt + ) + ) + { + if (ccnt == 3) + { + value = new Color(cr, cg, cb); + return true; + } + if (ccnt == 4) + { + value = new Color(cr, cg, cb, ca); + return true; + } + } + + string[] split = CommandArgParserCommon.StripAndSplit(colorString); + switch (split.Length) + { + case 3 + when float.TryParse( + split[0], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float r + ) + && float.TryParse( + split[1], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float g + ) + && float.TryParse( + split[2], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float b + ): + value = new Color(r, g, b); + return true; + case 4 + when float.TryParse( + split[0], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float r + ) + && float.TryParse( + split[1], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float g + ) + && float.TryParse( + split[2], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float b + ) + && float.TryParse( + split[3], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float a + ): + value = new Color(r, g, b, a); + return true; + } + + value = default; + return false; + } + } + + public sealed class QuaternionArgParser : ArgParser + { + public static readonly QuaternionArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out Quaternion value) + { + if ( + CommandArgParserCommon.TryParseLabeledFloatMap( + input.AsSpan(), + out System.Collections.Generic.Dictionary labeled, + out bool malformed + ) + ) + { + if (malformed) + { + value = default; + return false; + } + if ( + labeled.TryGetValue("x", out float lx) + && labeled.TryGetValue("y", out float ly) + && labeled.TryGetValue("z", out float lz) + && labeled.TryGetValue("w", out float lw) + ) + { + value = new Quaternion(lx, ly, lz, lw); + return true; + } + value = default; + return false; + } + if ( + CommandArgParserCommon.TryParseFloatList( + input.AsSpan(), + out float qx, + out float qy, + out float qz, + out float qw, + out int qcnt + ) + && qcnt == 4 + ) + { + value = new Quaternion(qx, qy, qz, qw); + return true; + } + + string[] split = CommandArgParserCommon.StripAndSplit(input); + if ( + split.Length == 4 + && float.TryParse( + split[0], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float x + ) + && float.TryParse( + split[1], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float y + ) + && float.TryParse( + split[2], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float z + ) + && float.TryParse( + split[3], + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float w + ) + ) + { + value = new Quaternion(x, y, z, w); + return true; + } + + value = default; + return false; + } + } + + public sealed class RectArgParser : ArgParser + { + public static readonly RectArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out Rect value) + { + if ( + CommandArgParserCommon.TryParseLabeledFloatMap( + input.AsSpan(), + out System.Collections.Generic.Dictionary labeled, + out bool malformed + ) + ) + { + if (malformed) + { + value = default; + return false; + } + if ( + labeled.TryGetValue("x", out float lx) + && labeled.TryGetValue("y", out float ly) + && labeled.TryGetValue("width", out float lw) + && labeled.TryGetValue("height", out float lh) + ) + { + value = new Rect(lx, ly, lw, lh); + return true; + } + value = default; + return false; + } + if ( + CommandArgParserCommon.TryParseFloatList( + input.AsSpan(), + out float rx, + out float ry, + out float rw, + out float rh, + out int rcnt + ) + && rcnt == 4 + ) + { + value = new Rect(rx, ry, rw, rh); + return true; + } + + string[] split = CommandArgParserCommon.StripAndSplit(input); + if ( + split.Length == 4 + && float.TryParse( + split[0].Replace("x:", string.Empty, StringComparison.OrdinalIgnoreCase), + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float x + ) + && float.TryParse( + split[1].Replace("y:", string.Empty, StringComparison.OrdinalIgnoreCase), + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float y + ) + && float.TryParse( + split[2].Replace("width:", string.Empty, StringComparison.OrdinalIgnoreCase), + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float width + ) + && float.TryParse( + split[3].Replace("height:", string.Empty, StringComparison.OrdinalIgnoreCase), + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out float height + ) + ) + { + value = new Rect(x, y, width, height); + return true; + } + + value = default; + return false; + } + } + + public sealed class RectIntArgParser : ArgParser + { + public static readonly RectIntArgParser Instance = new(); + + protected override bool TryParseTyped(string input, out RectInt value) + { + if ( + CommandArgParserCommon.TryParseLabeledIntMap( + input.AsSpan(), + out System.Collections.Generic.Dictionary labeled, + out bool malformed + ) + ) + { + if (malformed) + { + value = default; + return false; + } + if ( + labeled.TryGetValue("x", out int lx) + && labeled.TryGetValue("y", out int ly) + && labeled.TryGetValue("width", out int lw) + && labeled.TryGetValue("height", out int lh) + ) + { + value = new RectInt(lx, ly, lw, lh); + return true; + } + value = default; + return false; + } + if ( + CommandArgParserCommon.TryParseIntList( + input.AsSpan(), + out int rix, + out int riy, + out int riw, + out int rih, + out int ricnt + ) + && ricnt == 4 + ) + { + value = new RectInt(rix, riy, riw, rih); + return true; + } + + string[] split = CommandArgParserCommon.StripAndSplit(input); + if ( + split.Length == 4 + && int.TryParse( + split[0].Replace("x:", string.Empty, StringComparison.OrdinalIgnoreCase), + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out int x + ) + && int.TryParse( + split[1].Replace("y:", string.Empty, StringComparison.OrdinalIgnoreCase), + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out int y + ) + && int.TryParse( + split[2].Replace("width:", string.Empty, StringComparison.OrdinalIgnoreCase), + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out int width + ) + && int.TryParse( + split[3].Replace("height:", string.Empty, StringComparison.OrdinalIgnoreCase), + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out int height + ) + ) + { + value = new RectInt(x, y, width, height); + return true; + } + + value = default; + return false; + } + } +} diff --git a/Runtime/CommandTerminal/Backend/Parsers/UnityArgParsers.cs.meta b/Runtime/CommandTerminal/Backend/Parsers/UnityArgParsers.cs.meta new file mode 100644 index 0000000..6f67e25 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Parsers/UnityArgParsers.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fd6c16f8e8411794eb5502853c0e4720 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/Profiles.meta b/Runtime/CommandTerminal/Backend/Profiles.meta new file mode 100644 index 0000000..b060882 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Profiles.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4fd26bd667daf3205a56f14f306d1666 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/Profiles/RuntimeProfileSettingsProvider.cs b/Runtime/CommandTerminal/Backend/Profiles/RuntimeProfileSettingsProvider.cs new file mode 100644 index 0000000..23f9dfb --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Profiles/RuntimeProfileSettingsProvider.cs @@ -0,0 +1,29 @@ +namespace WallstopStudios.DxCommandTerminal.Backend.Profiles +{ + using Backend; + + internal sealed class RuntimeProfileSettingsProvider : ITerminalSettingsProvider + { + private readonly TerminalRuntimeProfile _profile; + + internal RuntimeProfileSettingsProvider(TerminalRuntimeProfile profile) + { + _profile = profile; + } + + public TerminalRuntimeSettings BuildSettings() + { + return _profile != null + ? _profile.BuildSettings() + : new TerminalRuntimeSettings( + logCapacity: 0, + historyCapacity: 0, + blockedLogTypes: System.Array.Empty(), + allowedLogTypes: System.Array.Empty(), + blockedCommands: System.Array.Empty(), + allowedCommands: System.Array.Empty(), + includeDefaultCommands: true + ); + } + } +} diff --git a/Runtime/CommandTerminal/Backend/Profiles/RuntimeProfileSettingsProvider.cs.meta b/Runtime/CommandTerminal/Backend/Profiles/RuntimeProfileSettingsProvider.cs.meta new file mode 100644 index 0000000..e44b462 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Profiles/RuntimeProfileSettingsProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9bb8ddb3a31b28b41b506300faef9b6e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/Profiles/TerminalConfigurationAsset.cs b/Runtime/CommandTerminal/Backend/Profiles/TerminalConfigurationAsset.cs new file mode 100644 index 0000000..7aa212d --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Profiles/TerminalConfigurationAsset.cs @@ -0,0 +1,35 @@ +namespace WallstopStudios.DxCommandTerminal.Backend.Profiles +{ + using Backend; + using UnityEngine; + + /// + /// Aggregates configuration profiles for terminals to feed the new settings provider/factory pipeline. + /// Backwards-compatible: can wrap existing profile fields used by TerminalUI during migration. + /// + [CreateAssetMenu( + fileName = "TerminalConfigurationAsset", + menuName = "DXCommandTerminal/Terminal Configuration Asset", + order = 400 + )] + public sealed class TerminalConfigurationAsset : ScriptableObject, ITerminalSettingsProvider + { + [Tooltip("Runtime profile providing buffer sizes and command/log filters")] + public TerminalRuntimeProfile runtimeProfile; + + public TerminalRuntimeSettings BuildSettings() + { + return runtimeProfile != null + ? runtimeProfile.BuildSettings() + : new TerminalRuntimeSettings( + logCapacity: 0, + historyCapacity: 0, + blockedLogTypes: System.Array.Empty(), + allowedLogTypes: System.Array.Empty(), + blockedCommands: System.Array.Empty(), + allowedCommands: System.Array.Empty(), + includeDefaultCommands: true + ); + } + } +} diff --git a/Runtime/CommandTerminal/Backend/Profiles/TerminalConfigurationAsset.cs.meta b/Runtime/CommandTerminal/Backend/Profiles/TerminalConfigurationAsset.cs.meta new file mode 100644 index 0000000..adaece5 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Profiles/TerminalConfigurationAsset.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fc166a9df8eccaf46ae80dd64b7d9912 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/Profiles/TerminalFilterConfiguration.cs b/Runtime/CommandTerminal/Backend/Profiles/TerminalFilterConfiguration.cs new file mode 100644 index 0000000..7e0801f --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Profiles/TerminalFilterConfiguration.cs @@ -0,0 +1,50 @@ +namespace WallstopStudios.DxCommandTerminal.Backend.Profiles +{ + using System; + using System.Collections.Generic; + using Backend; + using UnityEngine; + + /// + /// C# serialisable container describing allow/block lists for command registration. + /// + [Serializable] + public sealed class TerminalCommandFilterConfiguration + { + [Tooltip("When true, built-in DX Command Terminal commands are registered automatically.")] + public bool includeDefaultCommands = true; + + [Tooltip( + "Commands that should always be available. Leave empty to allow all registered commands." + )] + public List allowedCommands = new(); + + [Tooltip("Commands that should be removed/disabled after registration.")] + public List blockedCommands = new(); + + internal IReadOnlyList Allowed => allowedCommands; + + internal IReadOnlyList Blocked => blockedCommands; + + internal bool IncludeDefaults => includeDefaultCommands; + } + + /// + /// C# serialisable container describing allow/block lists for log routing into the terminal. + /// + [Serializable] + public sealed class TerminalLogFilterConfiguration + { + [Tooltip( + "Log types that should be routed into the terminal. Leave empty to allow all types." + )] + public List allowedLogTypes = new(); + + [Tooltip("Log types that should be filtered out of the terminal buffer.")] + public List blockedLogTypes = new(); + + internal IReadOnlyList Allowed => allowedLogTypes; + + internal IReadOnlyList Blocked => blockedLogTypes; + } +} diff --git a/Runtime/CommandTerminal/Backend/Profiles/TerminalFilterConfiguration.cs.meta b/Runtime/CommandTerminal/Backend/Profiles/TerminalFilterConfiguration.cs.meta new file mode 100644 index 0000000..24d8d1d --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Profiles/TerminalFilterConfiguration.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bf2624fe444944b189ea6b1d59d2847b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/Profiles/TerminalRuntimeProfile.cs b/Runtime/CommandTerminal/Backend/Profiles/TerminalRuntimeProfile.cs new file mode 100644 index 0000000..bc6149f --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Profiles/TerminalRuntimeProfile.cs @@ -0,0 +1,105 @@ +namespace WallstopStudios.DxCommandTerminal.Backend.Profiles +{ + using System.Collections.Generic; + using Backend; + using UnityEngine; + + [CreateAssetMenu( + fileName = "TerminalRuntimeProfile", + menuName = "DXCommandTerminal/Terminal Runtime Profile", + order = 450 + )] + public sealed class TerminalRuntimeProfile : ScriptableObject + { + [SerializeField] + [Min(0)] + private int _logBufferSize = 256; + + [SerializeField] + [Min(0)] + private int _historyBufferSize = 512; + + [SerializeField] + private TerminalCommandFilterConfiguration _commandFilters = new(); + + [SerializeField] + private TerminalLogFilterConfiguration _logFilters = new(); + + public int LogBufferSize => Mathf.Max(0, _logBufferSize); + + public int HistoryBufferSize => Mathf.Max(0, _historyBufferSize); + + public bool IncludeDefaultCommands => _commandFilters?.IncludeDefaults ?? true; + + public IReadOnlyList AllowedCommands => + _commandFilters?.Allowed ?? System.Array.Empty(); + + public IReadOnlyList BlockedCommands => + _commandFilters?.Blocked ?? System.Array.Empty(); + + public IReadOnlyList AllowedLogTypes => + _logFilters?.Allowed ?? System.Array.Empty(); + + public IReadOnlyList BlockedLogTypes => + _logFilters?.Blocked ?? System.Array.Empty(); + + internal TerminalRuntimeSettings BuildSettings() + { + return new TerminalRuntimeSettings( + LogBufferSize, + HistoryBufferSize, + BlockedLogTypes, + AllowedLogTypes, + BlockedCommands, + AllowedCommands, + IncludeDefaultCommands + ); + } + +#if UNITY_EDITOR + internal void ConfigureForTests( + int logBufferSize, + int historyBufferSize, + bool includeDefaults, + IReadOnlyList allowedLogTypes, + IReadOnlyList blockedLogTypes, + IReadOnlyList allowedCommands, + IReadOnlyList blockedCommands + ) + { + _logBufferSize = logBufferSize; + _historyBufferSize = historyBufferSize; + if (_commandFilters == null) + { + _commandFilters = new TerminalCommandFilterConfiguration(); + } + + if (_logFilters == null) + { + _logFilters = new TerminalLogFilterConfiguration(); + } + + _commandFilters.includeDefaultCommands = includeDefaults; + CopyList(allowedCommands, _commandFilters.allowedCommands); + CopyList(blockedCommands, _commandFilters.blockedCommands); + + CopyList(allowedLogTypes, _logFilters.allowedLogTypes); + CopyList(blockedLogTypes, _logFilters.blockedLogTypes); + } + + private static void CopyList(IReadOnlyList source, List destination) + { + destination.Clear(); + if (source == null) + { + return; + } + + for (int i = 0; i < source.Count; ++i) + { + destination.Add(source[i]); + } + } +#endif + } +} diff --git a/Runtime/CommandTerminal/Backend/Profiles/TerminalRuntimeProfile.cs.meta b/Runtime/CommandTerminal/Backend/Profiles/TerminalRuntimeProfile.cs.meta new file mode 100644 index 0000000..effd482 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/Profiles/TerminalRuntimeProfile.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6be846ab190dfc54081a4fa4397bd422 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/Terminal.cs b/Runtime/CommandTerminal/Backend/Terminal.cs index 2b0ca2d..b11f3e2 100644 --- a/Runtime/CommandTerminal/Backend/Terminal.cs +++ b/Runtime/CommandTerminal/Backend/Terminal.cs @@ -1,33 +1,54 @@ -namespace WallstopStudios.DxCommandTerminal.Backend -{ - using JetBrains.Annotations; - - public static class Terminal - { - public static CommandLog Buffer { get; internal set; } - public static CommandShell Shell { get; internal set; } - public static CommandHistory History { get; internal set; } - public static CommandAutoComplete AutoComplete { get; internal set; } - - [StringFormatMethod("format")] - public static bool Log(string format, params object[] parameters) - { - return Log(TerminalLogType.ShellMessage, format, parameters); - } - - [StringFormatMethod("format")] - public static bool Log(TerminalLogType type, string format, params object[] parameters) - { - CommandLog buffer = Buffer; - if (buffer == null) - { - return false; - } - - string formattedMessage = parameters is { Length: > 0 } - ? string.Format(format, parameters) - : format; - return buffer.HandleLog(formattedMessage, type); - } - } -} +namespace WallstopStudios.DxCommandTerminal.Backend +{ + using JetBrains.Annotations; + + [System.Obsolete( + "Terminal static facade is deprecated. Use ITerminalRuntimeScope via TerminalUI.ServiceLocator.", + false + )] + public static class Terminal + { + private static ITerminalRuntime _activeRuntime; + + public static CommandLog Buffer => _activeRuntime?.Log; + + public static CommandShell Shell => _activeRuntime?.Shell; + + public static CommandHistory History => _activeRuntime?.History; + + public static CommandAutoComplete AutoComplete => _activeRuntime?.AutoComplete; + + internal static ITerminalRuntime ActiveRuntime => _activeRuntime; + + internal static void RegisterRuntime(ITerminalRuntime runtime) + { + _activeRuntime = runtime; + } + + internal static void UnregisterRuntime(ITerminalRuntime runtime) + { + if (_activeRuntime == runtime) + { + _activeRuntime = null; + } + } + + [StringFormatMethod("format")] + public static bool Log(string format, params object[] parameters) + { + return Log(TerminalLogType.ShellMessage, format, parameters); + } + + [StringFormatMethod("format")] + public static bool Log(TerminalLogType type, string format, params object[] parameters) + { + ITerminalRuntime runtime = _activeRuntime; + if (runtime == null) + { + return false; + } + + return runtime.LogMessage(type, format, parameters); + } + } +} diff --git a/Runtime/CommandTerminal/Backend/TerminalCommandProfile.cs b/Runtime/CommandTerminal/Backend/TerminalCommandProfile.cs new file mode 100644 index 0000000..d25db7a --- /dev/null +++ b/Runtime/CommandTerminal/Backend/TerminalCommandProfile.cs @@ -0,0 +1,39 @@ +namespace WallstopStudios.DxCommandTerminal.Backend +{ + using UI; + using UnityEngine; + + [CreateAssetMenu( + fileName = "TerminalCommandProfile", + menuName = "DXCommandTerminal/Terminal Command Profile", + order = 480 + )] + public sealed class TerminalCommandProfile : ScriptableObject + { + [Header("Commands")] + [SerializeField] + private Profiles.TerminalCommandFilterConfiguration _commandFilters = new(); + + [Header("Logs")] + [SerializeField] + private Profiles.TerminalLogFilterConfiguration _logFilters = new(); + + public Profiles.TerminalCommandFilterConfiguration CommandFilters => _commandFilters; + + public Profiles.TerminalLogFilterConfiguration LogFilters => _logFilters; + + public void ApplyTo(TerminalUI terminal) + { + if (terminal == null) + { + return; + } + + terminal.ignoreDefaultCommands = !_commandFilters.IncludeDefaults; + terminal.SetAllowedCommandsForTests(_commandFilters.Allowed); + terminal.SetBlockedCommandsForTests(_commandFilters.Blocked); + terminal.SetAllowedLogTypesForTests(_logFilters.Allowed); + terminal.SetBlockedLogTypesForTests(_logFilters.Blocked); + } + } +} diff --git a/Runtime/CommandTerminal/Backend/TerminalCommandProfile.cs.meta b/Runtime/CommandTerminal/Backend/TerminalCommandProfile.cs.meta new file mode 100644 index 0000000..e8176a8 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/TerminalCommandProfile.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b82e3275e1134903b19f84ce5af6bbf8 +timeCreated: 0 diff --git a/Runtime/CommandTerminal/Backend/TerminalRuntime.cs b/Runtime/CommandTerminal/Backend/TerminalRuntime.cs new file mode 100644 index 0000000..9faf2e3 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/TerminalRuntime.cs @@ -0,0 +1,286 @@ +namespace WallstopStudios.DxCommandTerminal.Backend +{ + using System; + using System.Collections.Generic; + + internal sealed class TerminalRuntime : ITerminalRuntime + { + private readonly HashSet _ignoredLogTypesScratch = new(); + private readonly HashSet _allowedLogTypesScratch = new(); + private readonly HashSet _ignoredCommandScratch = new( + StringComparer.OrdinalIgnoreCase + ); + private readonly HashSet _allowedCommandScratch = new( + StringComparer.OrdinalIgnoreCase + ); + + private CommandLog _log; + private CommandHistory _history; + private CommandShell _shell; + private CommandAutoComplete _autoComplete; + + private bool _appliedIgnoreDefaultCommands; + + public CommandLog Log => _log; + + public CommandHistory History => _history; + + public CommandShell Shell => _shell; + + public CommandAutoComplete AutoComplete => _autoComplete; + + public TerminalRuntimeUpdateResult Configure( + in TerminalRuntimeSettings settings, + bool forceReset + ) + { + bool logRecreated = EnsureLog(settings, forceReset); + bool historyRecreated = EnsureHistory(settings, forceReset); + bool shellRecreated = EnsureShell(settings, forceReset, historyRecreated); + bool autoCompleteRecreated = EnsureAutoComplete( + forceReset || historyRecreated || shellRecreated + ); + bool commandsRefreshed = EnsureShellConfiguration(settings, forceReset, shellRecreated); + + return new TerminalRuntimeUpdateResult( + logRecreated, + historyRecreated, + shellRecreated, + autoCompleteRecreated, + commandsRefreshed + ); + } + + public bool LogMessage(TerminalLogType type, string format, params object[] parameters) + { + CommandLog log = _log; + if (log == null || string.IsNullOrEmpty(format)) + { + return false; + } + + string formattedMessage = parameters is { Length: > 0 } + ? string.Format(format, parameters) + : format; + log.EnqueueMessage(formattedMessage, type, includeStackTrace: true); + return true; + } + + private bool EnsureLog(in TerminalRuntimeSettings settings, bool forceReset) + { + int desiredCapacity = Math.Max(0, settings.LogCapacity); + if (forceReset || _log == null) + { + _log = new CommandLog( + desiredCapacity, + settings.BlockedLogTypes, + settings.AllowedLogTypes + ); + ApplyLogFilters(settings.BlockedLogTypes, settings.AllowedLogTypes); + return true; + } + + if (_log.Capacity != desiredCapacity) + { + _log.Resize(desiredCapacity); + } + + ApplyLogFilters(settings.BlockedLogTypes, settings.AllowedLogTypes); + return false; + } + + private bool EnsureHistory(in TerminalRuntimeSettings settings, bool forceReset) + { + int desiredCapacity = Math.Max(0, settings.HistoryCapacity); + if (forceReset || _history == null) + { + _history = new CommandHistory(desiredCapacity); + return true; + } + + if (_history.Capacity != desiredCapacity) + { + _history.Resize(desiredCapacity); + } + + return false; + } + + private bool EnsureShell( + in TerminalRuntimeSettings settings, + bool forceReset, + bool historyRecreated + ) + { + if (forceReset || _shell == null || historyRecreated) + { + _shell = new CommandShell(_history); + return true; + } + + return false; + } + + private bool EnsureAutoComplete(bool recreate) + { + if (recreate || _autoComplete == null) + { + _autoComplete = new CommandAutoComplete(_history, _shell, _shell.Commands.Keys); + return true; + } + + return false; + } + + private bool EnsureShellConfiguration( + in TerminalRuntimeSettings settings, + bool forceReset, + bool shellRecreated + ) + { + if (_shell == null) + { + return false; + } + + bool shouldRefreshCommands = shellRecreated; + if (!shouldRefreshCommands) + { + if (_shell.Commands.Count <= 0) + { + shouldRefreshCommands = true; + } + else + { + bool ignoreFlagChanged = + _appliedIgnoreDefaultCommands != settings.IgnoreDefaultCommands; + if (ignoreFlagChanged) + { + shouldRefreshCommands = true; + } + else + { + _ignoredCommandScratch.Clear(); + for (int i = 0; i < settings.BlockedCommands.Count; ++i) + { + string command = settings.BlockedCommands[i]; + if (!string.IsNullOrWhiteSpace(command)) + { + _ignoredCommandScratch.Add(command); + } + } + + _allowedCommandScratch.Clear(); + for (int i = 0; i < settings.AllowedCommands.Count; ++i) + { + string command = settings.AllowedCommands[i]; + if (!string.IsNullOrWhiteSpace(command)) + { + _allowedCommandScratch.Add(command); + } + } + + bool blockedChanged = !_shell.IgnoredCommands.SetEquals( + _ignoredCommandScratch + ); + bool allowedChanged = !_shell.AllowedCommands.SetEquals( + _allowedCommandScratch + ); + if (blockedChanged || allowedChanged) + { + shouldRefreshCommands = true; + } + } + } + } + + if (forceReset) + { + shouldRefreshCommands = true; + } + + if (!shouldRefreshCommands) + { + return false; + } + + _shell.ClearAutoRegisteredCommands(); + _ignoredCommandScratch.Clear(); + for (int i = 0; i < settings.BlockedCommands.Count; ++i) + { + string command = settings.BlockedCommands[i]; + if (!string.IsNullOrWhiteSpace(command)) + { + _ignoredCommandScratch.Add(command); + } + } + + _allowedCommandScratch.Clear(); + for (int i = 0; i < settings.AllowedCommands.Count; ++i) + { + string command = settings.AllowedCommands[i]; + if (!string.IsNullOrWhiteSpace(command)) + { + _allowedCommandScratch.Add(command); + } + } + + _shell.InitializeAutoRegisteredCommands( + _ignoredCommandScratch, + ignoreDefaultCommands: settings.IgnoreDefaultCommands, + allowedCommands: _allowedCommandScratch + ); + + _appliedIgnoreDefaultCommands = settings.IgnoreDefaultCommands; + return true; + } + + private void ApplyLogFilters( + IReadOnlyList blockedLogTypes, + IReadOnlyList allowedLogTypes + ) + { + if (_log == null) + { + return; + } + + _ignoredLogTypesScratch.Clear(); + if (blockedLogTypes != null) + { + for (int i = 0; i < blockedLogTypes.Count; ++i) + { + _ignoredLogTypesScratch.Add(blockedLogTypes[i]); + } + } + + _allowedLogTypesScratch.Clear(); + if (allowedLogTypes != null) + { + for (int i = 0; i < allowedLogTypes.Count; ++i) + { + _allowedLogTypesScratch.Add(allowedLogTypes[i]); + } + } + + bool blockedChanged = !_log.ignoredLogTypes.SetEquals(_ignoredLogTypesScratch); + bool allowedChanged = !_log.allowedLogTypes.SetEquals(_allowedLogTypesScratch); + if (!blockedChanged && !allowedChanged) + { + return; + } + + if (blockedChanged) + { + _log.ignoredLogTypes.Clear(); + _log.ignoredLogTypes.UnionWith(_ignoredLogTypesScratch); + } + + if (allowedChanged) + { + _log.allowedLogTypes.Clear(); + _log.allowedLogTypes.UnionWith(_allowedLogTypesScratch); + } + } + } +} diff --git a/Runtime/CommandTerminal/Backend/TerminalRuntime.cs.meta b/Runtime/CommandTerminal/Backend/TerminalRuntime.cs.meta new file mode 100644 index 0000000..cc348d1 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/TerminalRuntime.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1eb6af7b9f0516acc9539d41a5074ca8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/TerminalRuntimeCache.cs b/Runtime/CommandTerminal/Backend/TerminalRuntimeCache.cs new file mode 100644 index 0000000..aa1b532 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/TerminalRuntimeCache.cs @@ -0,0 +1,33 @@ +namespace WallstopStudios.DxCommandTerminal.Backend +{ + [System.Obsolete( + "TerminalRuntimeCache is deprecated. Use ITerminalRuntimePool via TerminalUI.ServiceLocator.RuntimePool.", + false + )] + internal static class TerminalRuntimeCache + { + private static TerminalRuntime _cachedRuntime; + + public static bool TryAcquire(out TerminalRuntime runtime) + { + runtime = _cachedRuntime; + _cachedRuntime = null; + return runtime != null; + } + + public static void Store(TerminalRuntime runtime) + { + if (runtime == null) + { + return; + } + + _cachedRuntime = runtime; + } + + public static void Clear() + { + _cachedRuntime = null; + } + } +} diff --git a/Runtime/CommandTerminal/Backend/TerminalRuntimeCache.cs.meta b/Runtime/CommandTerminal/Backend/TerminalRuntimeCache.cs.meta new file mode 100644 index 0000000..664669c --- /dev/null +++ b/Runtime/CommandTerminal/Backend/TerminalRuntimeCache.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 682d23c38b534ce6a8d5c9a5a71c3892 +timeCreated: 0 diff --git a/Runtime/CommandTerminal/Backend/TerminalRuntimeConfig.cs b/Runtime/CommandTerminal/Backend/TerminalRuntimeConfig.cs new file mode 100644 index 0000000..9c586d1 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/TerminalRuntimeConfig.cs @@ -0,0 +1,137 @@ +namespace WallstopStudios.DxCommandTerminal.Backend +{ + using System; + using Internal; + using UnityEngine; + + [Flags] + public enum TerminalRuntimeModeFlags + { + [Obsolete("None disables all runtime features. Choose explicit modes.")] + None = 0, + Editor = 1 << 0, + Development = 1 << 1, + Production = 1 << 2, + All = Editor | Development | Production, + } + + // Auto-create under Assets/Resources/Wallstop Studios/DxCommandTerminal/TerminalRuntimeConfig.asset + [ScriptableSingletonPath("Wallstop Studios/DxCommandTerminal")] + public sealed class TerminalRuntimeConfig : ScriptableObjectSingleton + { + // Fallbacks ensure static API remains usable even before an asset exists +#pragma warning disable CS0618 // Type or member is obsolete + private static TerminalRuntimeModeFlags _fallbackMode = TerminalRuntimeModeFlags.None; +#pragma warning restore CS0618 // Type or member is obsolete + private static bool _fallbackEditorAutoDiscover; + +#pragma warning disable CS0618 // Type or member is obsolete + [SerializeField] + private TerminalRuntimeModeFlags _mode = TerminalRuntimeModeFlags.None; +#pragma warning restore CS0618 // Type or member is obsolete + + [SerializeField] + private bool _editorAutoDiscover; + + // Instance-level accessors (for inspector/serialization) +#pragma warning disable CS0618 // Type or member is obsolete + public TerminalRuntimeModeFlags Mode + { + get => _mode; + set => _mode = value; + } +#pragma warning restore CS0618 // Type or member is obsolete + + public bool EditorAutoDiscoverInstance + { + get => _editorAutoDiscover; + set => _editorAutoDiscover = value; + } + + // Backwards-compatible static API +#pragma warning disable CS0618 // Type or member is obsolete + public static void SetMode(TerminalRuntimeModeFlags mode) + { + if (Instance != null) + { + Instance._mode = mode; + } + _fallbackMode = mode; + } +#pragma warning restore CS0618 // Type or member is obsolete + + public static bool EditorAutoDiscover + { + get + { + if (Instance != null) + { + return Instance._editorAutoDiscover; + } + return _fallbackEditorAutoDiscover; + } + set + { + if (Instance != null) + { + Instance._editorAutoDiscover = value; + } + _fallbackEditorAutoDiscover = value; + } + } + +#pragma warning disable CS0618 // Type or member is obsolete + private static TerminalRuntimeModeFlags CurrentMode => + Instance != null ? Instance._mode : _fallbackMode; +#pragma warning restore CS0618 // Type or member is obsolete + + public static bool HasFlagNoAlloc( + TerminalRuntimeModeFlags value, + TerminalRuntimeModeFlags flag + ) + { + return ((int)value & (int)flag) == (int)flag; + } + + public static bool ShouldEnableEditorFeatures() + { +#if UNITY_EDITOR + return HasFlagNoAlloc(CurrentMode, TerminalRuntimeModeFlags.Editor); +#else + return false; +#endif + } + + public static bool ShouldEnableDevelopmentFeatures() + { + return HasFlagNoAlloc(CurrentMode, TerminalRuntimeModeFlags.Development) + && Debug.isDebugBuild; + } + + public static bool ShouldEnableProductionFeatures() + { + return HasFlagNoAlloc(CurrentMode, TerminalRuntimeModeFlags.Production) + && !Debug.isDebugBuild; + } + + public static int TryAutoDiscoverParsers() + { + if (ShouldEnableEditorFeatures() && EditorAutoDiscover) + { + return CommandArg.DiscoverAndRegisterParsers(replaceExisting: false); + } + return 0; + } + + internal static TerminalRuntimeModeFlags GetModeForTests() + { +#pragma warning disable CS0618 // Type or member is obsolete + if (Instance != null) + { + return Instance._mode; + } + return _fallbackMode; +#pragma warning restore CS0618 // Type or member is obsolete + } + } +} diff --git a/Runtime/CommandTerminal/Backend/TerminalRuntimeConfig.cs.meta b/Runtime/CommandTerminal/Backend/TerminalRuntimeConfig.cs.meta new file mode 100644 index 0000000..1444045 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/TerminalRuntimeConfig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bb0a29c7af78db448b2e5832e23c79f6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/TerminalRuntimeConfiguratorService.cs b/Runtime/CommandTerminal/Backend/TerminalRuntimeConfiguratorService.cs new file mode 100644 index 0000000..2350ad2 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/TerminalRuntimeConfiguratorService.cs @@ -0,0 +1,48 @@ +namespace WallstopStudios.DxCommandTerminal.Backend +{ + internal sealed class TerminalRuntimeConfiguratorService : ITerminalRuntimeConfiguratorService + { + internal static ITerminalRuntimeConfiguratorService Default { get; } = + new TerminalRuntimeConfiguratorService(); + + private TerminalRuntimeConfiguratorService() { } + + public TerminalRuntimeModeFlags CurrentMode => TerminalRuntimeConfig.GetModeForTests(); + + public void SetMode(TerminalRuntimeModeFlags mode) + { + TerminalRuntimeConfig.SetMode(mode); + } + + public bool EditorAutoDiscover + { + get => TerminalRuntimeConfig.EditorAutoDiscover; + set => TerminalRuntimeConfig.EditorAutoDiscover = value; + } + + public bool ShouldEnableEditorFeatures() + { + return TerminalRuntimeConfig.ShouldEnableEditorFeatures(); + } + + public bool ShouldEnableDevelopmentFeatures() + { + return TerminalRuntimeConfig.ShouldEnableDevelopmentFeatures(); + } + + public bool ShouldEnableProductionFeatures() + { + return TerminalRuntimeConfig.ShouldEnableProductionFeatures(); + } + + public bool HasFlag(TerminalRuntimeModeFlags value, TerminalRuntimeModeFlags flag) + { + return TerminalRuntimeConfig.HasFlagNoAlloc(value, flag); + } + + public int TryAutoDiscoverParsers() + { + return TerminalRuntimeConfig.TryAutoDiscoverParsers(); + } + } +} diff --git a/Runtime/CommandTerminal/Backend/TerminalRuntimeConfiguratorService.cs.meta b/Runtime/CommandTerminal/Backend/TerminalRuntimeConfiguratorService.cs.meta new file mode 100644 index 0000000..4ecb638 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/TerminalRuntimeConfiguratorService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9b4fa40b30c14e9bb25e35bc1a2c3fee +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/TerminalRuntimeFactory.cs b/Runtime/CommandTerminal/Backend/TerminalRuntimeFactory.cs new file mode 100644 index 0000000..fb883df --- /dev/null +++ b/Runtime/CommandTerminal/Backend/TerminalRuntimeFactory.cs @@ -0,0 +1,20 @@ +namespace WallstopStudios.DxCommandTerminal.Backend +{ + /// + /// Default implementation that adapts existing behaviour to the factory abstraction. + /// + public sealed class TerminalRuntimeFactory : ITerminalRuntimeFactory + { + public ITerminalRuntime CreateRuntime(ITerminalSettingsProvider settingsProvider) + { + TerminalRuntime runtime = new(); + if (settingsProvider != null) + { + TerminalRuntimeSettings settings = settingsProvider.BuildSettings(); + _ = runtime.Configure(settings, forceReset: true); + } + + return runtime; + } + } +} diff --git a/Runtime/CommandTerminal/Backend/TerminalRuntimeFactory.cs.meta b/Runtime/CommandTerminal/Backend/TerminalRuntimeFactory.cs.meta new file mode 100644 index 0000000..6878bfd --- /dev/null +++ b/Runtime/CommandTerminal/Backend/TerminalRuntimeFactory.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 11179426fb3cc3940af240266f36556b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/TerminalRuntimePool.cs b/Runtime/CommandTerminal/Backend/TerminalRuntimePool.cs new file mode 100644 index 0000000..2746791 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/TerminalRuntimePool.cs @@ -0,0 +1,36 @@ +namespace WallstopStudios.DxCommandTerminal.Backend +{ + using System.Collections.Generic; + + internal sealed class TerminalRuntimePool : ITerminalRuntimePool + { + private readonly Stack _pool = new(); + + public bool TryRent(out ITerminalRuntime runtime) + { + if (_pool.Count > 0) + { + runtime = _pool.Pop(); + return runtime != null; + } + + runtime = null; + return false; + } + + public void Return(ITerminalRuntime runtime) + { + if (runtime == null) + { + return; + } + + _pool.Push(runtime); + } + + public void Clear() + { + _pool.Clear(); + } + } +} diff --git a/Runtime/CommandTerminal/Backend/TerminalRuntimePool.cs.meta b/Runtime/CommandTerminal/Backend/TerminalRuntimePool.cs.meta new file mode 100644 index 0000000..9f8b877 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/TerminalRuntimePool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 79a64bec583e11447862cebf4e83ede5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/TerminalRuntimeProviderProxy.cs b/Runtime/CommandTerminal/Backend/TerminalRuntimeProviderProxy.cs new file mode 100644 index 0000000..c25da4e --- /dev/null +++ b/Runtime/CommandTerminal/Backend/TerminalRuntimeProviderProxy.cs @@ -0,0 +1,15 @@ +namespace WallstopStudios.DxCommandTerminal.Backend +{ + using UI; + + public sealed class TerminalRuntimeProviderProxy : ITerminalRuntimeProvider + { + internal static ITerminalRuntimeProvider Default { get; } = + new TerminalRuntimeProviderProxy(); + + private TerminalRuntimeProviderProxy() { } + + public ITerminalRuntime ActiveRuntime => + TerminalUI.ServiceLocator?.RuntimeScope?.ActiveRuntime; + } +} diff --git a/Runtime/CommandTerminal/Backend/TerminalRuntimeProviderProxy.cs.meta b/Runtime/CommandTerminal/Backend/TerminalRuntimeProviderProxy.cs.meta new file mode 100644 index 0000000..d7a841c --- /dev/null +++ b/Runtime/CommandTerminal/Backend/TerminalRuntimeProviderProxy.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 664b4508d39246249602eac170c1b985 +timeCreated: 1760674723 diff --git a/Runtime/CommandTerminal/Backend/TerminalRuntimeScope.cs b/Runtime/CommandTerminal/Backend/TerminalRuntimeScope.cs new file mode 100644 index 0000000..3c404e1 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/TerminalRuntimeScope.cs @@ -0,0 +1,53 @@ +namespace WallstopStudios.DxCommandTerminal.Backend +{ + internal sealed class TerminalRuntimeScope : ITerminalRuntimeScope + { + internal static ITerminalRuntimeScope Default { get; } = new TerminalRuntimeScope(); + + private TerminalRuntimeScope() { } + + private ITerminalRuntime _activeRuntime; + + public ITerminalRuntime ActiveRuntime => _activeRuntime; + + public CommandLog Buffer => _activeRuntime?.Log; + + public CommandShell Shell => _activeRuntime?.Shell; + + public CommandHistory History => _activeRuntime?.History; + + public CommandAutoComplete AutoComplete => _activeRuntime?.AutoComplete; + + public void RegisterRuntime(ITerminalRuntime runtime) + { + _activeRuntime = runtime; + Terminal.RegisterRuntime(runtime); + } + + public void UnregisterRuntime(ITerminalRuntime runtime) + { + if (_activeRuntime == runtime) + { + _activeRuntime = null; + } + + Terminal.UnregisterRuntime(runtime); + } + + public bool Log(TerminalLogType type, string format, params object[] parameters) + { + ITerminalRuntime runtime = _activeRuntime; + if (runtime == null) + { + return false; + } + + return runtime.LogMessage(type, format, parameters); + } + + public bool Log(string format, params object[] parameters) + { + return Log(TerminalLogType.ShellMessage, format, parameters); + } + } +} diff --git a/Runtime/CommandTerminal/Backend/TerminalRuntimeScope.cs.meta b/Runtime/CommandTerminal/Backend/TerminalRuntimeScope.cs.meta new file mode 100644 index 0000000..7872b01 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/TerminalRuntimeScope.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2c9941450b52444796364a816af0d497 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/TerminalRuntimeSettings.cs b/Runtime/CommandTerminal/Backend/TerminalRuntimeSettings.cs new file mode 100644 index 0000000..2301c8e --- /dev/null +++ b/Runtime/CommandTerminal/Backend/TerminalRuntimeSettings.cs @@ -0,0 +1,42 @@ +namespace WallstopStudios.DxCommandTerminal.Backend +{ + using System.Collections.Generic; + + public readonly struct TerminalRuntimeSettings + { + public TerminalRuntimeSettings( + int logCapacity, + int historyCapacity, + IReadOnlyList blockedLogTypes, + IReadOnlyList allowedLogTypes, + IReadOnlyList blockedCommands, + IReadOnlyList allowedCommands, + bool includeDefaultCommands + ) + { + LogCapacity = logCapacity; + HistoryCapacity = historyCapacity; + BlockedLogTypes = blockedLogTypes ?? System.Array.Empty(); + AllowedLogTypes = allowedLogTypes ?? System.Array.Empty(); + BlockedCommands = blockedCommands ?? System.Array.Empty(); + AllowedCommands = allowedCommands ?? System.Array.Empty(); + IncludeDefaultCommands = includeDefaultCommands; + } + + public int LogCapacity { get; } + + public int HistoryCapacity { get; } + + public IReadOnlyList BlockedLogTypes { get; } + + public IReadOnlyList AllowedLogTypes { get; } + + public IReadOnlyList BlockedCommands { get; } + + public IReadOnlyList AllowedCommands { get; } + + public bool IncludeDefaultCommands { get; } + + public bool IgnoreDefaultCommands => !IncludeDefaultCommands; + } +} diff --git a/Runtime/CommandTerminal/Backend/TerminalRuntimeSettings.cs.meta b/Runtime/CommandTerminal/Backend/TerminalRuntimeSettings.cs.meta new file mode 100644 index 0000000..639be18 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/TerminalRuntimeSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6498ca131cde2e1e087c401f3d2e7113 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/TerminalRuntimeUpdateResult.cs b/Runtime/CommandTerminal/Backend/TerminalRuntimeUpdateResult.cs new file mode 100644 index 0000000..9375f76 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/TerminalRuntimeUpdateResult.cs @@ -0,0 +1,33 @@ +namespace WallstopStudios.DxCommandTerminal.Backend +{ + public readonly struct TerminalRuntimeUpdateResult + { + public TerminalRuntimeUpdateResult( + bool logRecreated, + bool historyRecreated, + bool shellRecreated, + bool autoCompleteRecreated, + bool commandsRefreshed + ) + { + LogRecreated = logRecreated; + HistoryRecreated = historyRecreated; + ShellRecreated = shellRecreated; + AutoCompleteRecreated = autoCompleteRecreated; + CommandsRefreshed = commandsRefreshed; + } + + public bool LogRecreated { get; } + + public bool HistoryRecreated { get; } + + public bool ShellRecreated { get; } + + public bool AutoCompleteRecreated { get; } + + public bool CommandsRefreshed { get; } + + public bool RuntimeReset => + LogRecreated || HistoryRecreated || ShellRecreated || AutoCompleteRecreated; + } +} diff --git a/Runtime/CommandTerminal/Backend/TerminalRuntimeUpdateResult.cs.meta b/Runtime/CommandTerminal/Backend/TerminalRuntimeUpdateResult.cs.meta new file mode 100644 index 0000000..393fe80 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/TerminalRuntimeUpdateResult.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 02b74b6820582b80bb4aa10e5b47feee +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Backend/TerminalServiceLocator.cs b/Runtime/CommandTerminal/Backend/TerminalServiceLocator.cs new file mode 100644 index 0000000..336a5ec --- /dev/null +++ b/Runtime/CommandTerminal/Backend/TerminalServiceLocator.cs @@ -0,0 +1,97 @@ +namespace WallstopStudios.DxCommandTerminal.Backend +{ + using Input; + using UI; + + public interface ITerminalServiceLocator + { + ITerminalProvider TerminalProvider { get; } + + ITerminalRuntimeConfigurator RuntimeConfigurator { get; } + + ITerminalInputProvider InputProvider { get; } + + ITerminalRuntimeProvider RuntimeProvider { get; } + + ITerminalRuntimeScope RuntimeScope { get; } + + ITerminalRuntimeConfiguratorService RuntimeConfiguratorService { get; } + + ITerminalRuntimePool RuntimePool { get; } + } + + internal sealed class TerminalServiceLocator : ITerminalServiceLocator + { + internal static ITerminalServiceLocator Default { get; } = new TerminalServiceLocator(); + + private readonly ITerminalProvider _terminalProvider; + private readonly ITerminalRuntimeConfigurator _runtimeConfigurator; + private readonly ITerminalInputProvider _inputProvider; + private readonly ITerminalRuntimeProvider _runtimeProvider; + private readonly ITerminalRuntimeScope _runtimeScope; + private readonly ITerminalRuntimeConfiguratorService _runtimeConfiguratorService; + private readonly ITerminalRuntimePool _runtimePool; + + private TerminalServiceLocator() + { + _terminalProvider = TerminalRegistry.Default; + _runtimeConfigurator = TerminalRuntimeConfiguratorProxy.Default; + _inputProvider = TerminalInputProviderProxy.Default; + _runtimeProvider = TerminalRuntimeProviderProxy.Default; + _runtimeScope = TerminalRuntimeScope.Default; + _runtimeConfiguratorService = TerminalRuntimeConfiguratorService.Default; + _runtimePool = new TerminalRuntimePool(); + } + + public ITerminalProvider TerminalProvider => _terminalProvider; + + public ITerminalRuntimeConfigurator RuntimeConfigurator => _runtimeConfigurator; + + public ITerminalInputProvider InputProvider => _inputProvider; + + public ITerminalRuntimeProvider RuntimeProvider => _runtimeProvider; + + public ITerminalRuntimeScope RuntimeScope => _runtimeScope; + + public ITerminalRuntimeConfiguratorService RuntimeConfiguratorService => + _runtimeConfiguratorService; + + public ITerminalRuntimePool RuntimePool => _runtimePool; + } + + internal sealed class MutableTerminalServiceLocator : ITerminalServiceLocator + { + internal MutableTerminalServiceLocator( + ITerminalProvider terminalProvider, + ITerminalRuntimeConfigurator runtimeConfigurator, + ITerminalInputProvider inputProvider, + ITerminalRuntimeProvider runtimeProvider, + ITerminalRuntimeScope runtimeScope, + ITerminalRuntimeConfiguratorService runtimeConfiguratorService, + ITerminalRuntimePool runtimePool + ) + { + TerminalProvider = terminalProvider; + RuntimeConfigurator = runtimeConfigurator; + InputProvider = inputProvider; + RuntimeProvider = runtimeProvider; + RuntimeScope = runtimeScope; + RuntimeConfiguratorService = runtimeConfiguratorService; + RuntimePool = runtimePool; + } + + public ITerminalProvider TerminalProvider { get; set; } + + public ITerminalRuntimeConfigurator RuntimeConfigurator { get; set; } + + public ITerminalInputProvider InputProvider { get; set; } + + public ITerminalRuntimeProvider RuntimeProvider { get; set; } + + public ITerminalRuntimeScope RuntimeScope { get; set; } + + public ITerminalRuntimeConfiguratorService RuntimeConfiguratorService { get; set; } + + public ITerminalRuntimePool RuntimePool { get; set; } + } +} diff --git a/Runtime/CommandTerminal/Backend/TerminalServiceLocator.cs.meta b/Runtime/CommandTerminal/Backend/TerminalServiceLocator.cs.meta new file mode 100644 index 0000000..316d3a6 --- /dev/null +++ b/Runtime/CommandTerminal/Backend/TerminalServiceLocator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f5cfe3a34da34ba9bba31f898a64ac2f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Input/DefaultTerminalInput.cs b/Runtime/CommandTerminal/Input/DefaultTerminalInput.cs index eaf9027..5c1e798 100644 --- a/Runtime/CommandTerminal/Input/DefaultTerminalInput.cs +++ b/Runtime/CommandTerminal/Input/DefaultTerminalInput.cs @@ -1,11 +1,11 @@ -namespace WallstopStudios.DxCommandTerminal.Input -{ - public sealed class DefaultTerminalInput : ITerminalInput - { - public static readonly DefaultTerminalInput Instance = new(); - - public string CommandText { get; set; } = string.Empty; - - private DefaultTerminalInput() { } - } -} +namespace WallstopStudios.DxCommandTerminal.Input +{ + public sealed class DefaultTerminalInput : ITerminalInput + { + public static readonly DefaultTerminalInput Instance = new(); + + public string CommandText { get; set; } = string.Empty; + + private DefaultTerminalInput() { } + } +} diff --git a/Runtime/CommandTerminal/Input/IInputHandler.cs b/Runtime/CommandTerminal/Input/IInputHandler.cs index 33deb93..97af0fe 100644 --- a/Runtime/CommandTerminal/Input/IInputHandler.cs +++ b/Runtime/CommandTerminal/Input/IInputHandler.cs @@ -1,7 +1,7 @@ -namespace WallstopStudios.DxCommandTerminal.Input -{ - public interface IInputHandler - { - bool ShouldHandleInputThisFrame { get; } - } -} +namespace WallstopStudios.DxCommandTerminal.Input +{ + public interface IInputHandler + { + bool ShouldHandleInputThisFrame { get; } + } +} diff --git a/Runtime/CommandTerminal/Input/ITerminalInput.cs b/Runtime/CommandTerminal/Input/ITerminalInput.cs index 8ff85a9..854569b 100644 --- a/Runtime/CommandTerminal/Input/ITerminalInput.cs +++ b/Runtime/CommandTerminal/Input/ITerminalInput.cs @@ -1,7 +1,7 @@ -namespace WallstopStudios.DxCommandTerminal.Input -{ - public interface ITerminalInput - { - string CommandText { get; set; } - } -} +namespace WallstopStudios.DxCommandTerminal.Input +{ + public interface ITerminalInput + { + string CommandText { get; set; } + } +} diff --git a/Runtime/CommandTerminal/Input/ITerminalInputProvider.cs b/Runtime/CommandTerminal/Input/ITerminalInputProvider.cs new file mode 100644 index 0000000..cc258c4 --- /dev/null +++ b/Runtime/CommandTerminal/Input/ITerminalInputProvider.cs @@ -0,0 +1,9 @@ +namespace WallstopStudios.DxCommandTerminal.Input +{ + using UI; + + public interface ITerminalInputProvider + { + ITerminalInput GetInput(TerminalUI terminal); + } +} diff --git a/Runtime/CommandTerminal/Input/ITerminalInputProvider.cs.meta b/Runtime/CommandTerminal/Input/ITerminalInputProvider.cs.meta new file mode 100644 index 0000000..dd073b3 --- /dev/null +++ b/Runtime/CommandTerminal/Input/ITerminalInputProvider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f0300e4969ec4504a158bfafb66f9027 +timeCreated: 1760673634 diff --git a/Runtime/CommandTerminal/Input/ITerminalInputSource.cs b/Runtime/CommandTerminal/Input/ITerminalInputSource.cs new file mode 100644 index 0000000..8027470 --- /dev/null +++ b/Runtime/CommandTerminal/Input/ITerminalInputSource.cs @@ -0,0 +1,9 @@ +namespace WallstopStudios.DxCommandTerminal.Input +{ + internal interface ITerminalInputSource + { + InputMode Mode { get; } + + bool IsKeyPressed(string binding); + } +} diff --git a/Runtime/CommandTerminal/Input/ITerminalInputSource.cs.meta b/Runtime/CommandTerminal/Input/ITerminalInputSource.cs.meta new file mode 100644 index 0000000..bcb2aa4 --- /dev/null +++ b/Runtime/CommandTerminal/Input/ITerminalInputSource.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6aa3c796ccb34215a961de168e2c27e7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Input/ITerminalInputTarget.cs b/Runtime/CommandTerminal/Input/ITerminalInputTarget.cs new file mode 100644 index 0000000..dc438ab --- /dev/null +++ b/Runtime/CommandTerminal/Input/ITerminalInputTarget.cs @@ -0,0 +1,23 @@ +namespace WallstopStudios.DxCommandTerminal.Input +{ + public interface ITerminalInputTarget + { + bool IsClosed { get; } + + void Close(); + + void ToggleSmall(); + + void ToggleFull(); + + void ToggleLauncher(); + + void EnterCommand(); + + void CompleteCommand(bool searchForward); + + void HandlePrevious(); + + void HandleNext(); + } +} diff --git a/Runtime/CommandTerminal/Input/ITerminalInputTarget.cs.meta b/Runtime/CommandTerminal/Input/ITerminalInputTarget.cs.meta new file mode 100644 index 0000000..c30c30c --- /dev/null +++ b/Runtime/CommandTerminal/Input/ITerminalInputTarget.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5e80eb048afb424db4d565879a194ebb +timeCreated: 0 diff --git a/Runtime/CommandTerminal/Input/InputHelpers.cs b/Runtime/CommandTerminal/Input/InputHelpers.cs index 4fe6fce..358de98 100644 --- a/Runtime/CommandTerminal/Input/InputHelpers.cs +++ b/Runtime/CommandTerminal/Input/InputHelpers.cs @@ -1,400 +1,400 @@ -namespace WallstopStudios.DxCommandTerminal.Input -{ - using System; - using System.Collections.Generic; - using Extensions; - using UnityEngine; -#if ENABLE_INPUT_SYSTEM - using UnityEngine.InputSystem; - using UnityEngine.InputSystem.Controls; -#endif - - public static class InputHelpers - { - private static readonly string[] ShiftModifiers = { "shift+", "#" }; - - private static readonly Dictionary CachedSubstrings = new(); - - private static readonly Dictionary KeyCodeMapping = new( - StringComparer.OrdinalIgnoreCase - ) - { - { "~", KeyCode.BackQuote }, - { "`", KeyCode.BackQuote }, - { "!", KeyCode.Alpha1 }, - { "@", KeyCode.Alpha2 }, - { "#", KeyCode.Alpha3 }, - { "$", KeyCode.Alpha4 }, - { "%", KeyCode.Alpha5 }, - { "^", KeyCode.Alpha6 }, - { "&", KeyCode.Alpha7 }, - { "*", KeyCode.Alpha8 }, - { "(", KeyCode.Alpha9 }, - { ")", KeyCode.Alpha0 }, - { "-", KeyCode.Minus }, - { "_", KeyCode.Minus }, - { "=", KeyCode.Equals }, - { "+", KeyCode.Equals }, - { "[", KeyCode.LeftBracket }, - { "{", KeyCode.LeftBracket }, - { "]", KeyCode.RightBracket }, - { "}", KeyCode.RightBracket }, - { "\\", KeyCode.Backslash }, - { "|", KeyCode.Backslash }, - { ";", KeyCode.Semicolon }, - { ":", KeyCode.Semicolon }, - { "'", KeyCode.Quote }, - { "\"", KeyCode.Quote }, - { ",", KeyCode.Comma }, - { "<", KeyCode.Comma }, - { ".", KeyCode.Period }, - { ">", KeyCode.Period }, - { "/", KeyCode.Slash }, - { "?", KeyCode.Slash }, - { "1", KeyCode.Alpha1 }, - { "2", KeyCode.Alpha2 }, - { "3", KeyCode.Alpha3 }, - { "4", KeyCode.Alpha4 }, - { "5", KeyCode.Alpha5 }, - { "6", KeyCode.Alpha6 }, - { "7", KeyCode.Alpha7 }, - { "8", KeyCode.Alpha8 }, - { "9", KeyCode.Alpha9 }, - { "0", KeyCode.Alpha0 }, - { "numpad1", KeyCode.Keypad1 }, - { "keypad1", KeyCode.Keypad1 }, - { "numpad2", KeyCode.Keypad2 }, - { "keypad2", KeyCode.Keypad2 }, - { "numpad3", KeyCode.Keypad3 }, - { "keypad3", KeyCode.Keypad3 }, - { "numpad4", KeyCode.Keypad4 }, - { "keypad4", KeyCode.Keypad4 }, - { "numpad5", KeyCode.Keypad5 }, - { "keypad5", KeyCode.Keypad5 }, - { "numpad6", KeyCode.Keypad6 }, - { "keypad6", KeyCode.Keypad6 }, - { "numpad7", KeyCode.Keypad7 }, - { "keypad7", KeyCode.Keypad7 }, - { "numpad8", KeyCode.Keypad8 }, - { "keypad8", KeyCode.Keypad8 }, - { "numpad9", KeyCode.Keypad9 }, - { "keypad9", KeyCode.Keypad9 }, - { "numpad0", KeyCode.Keypad0 }, - { "keypad0", KeyCode.Keypad0 }, - { "numpadplus", KeyCode.KeypadPlus }, - { "keypadplus", KeyCode.KeypadPlus }, - { "numpad+", KeyCode.KeypadPlus }, - { "numpadminus", KeyCode.KeypadMinus }, - { "keypadminus", KeyCode.KeypadMinus }, - { "numpad-", KeyCode.KeypadMinus }, - { "numpadmultiply", KeyCode.KeypadMultiply }, - { "keypadmultiply", KeyCode.KeypadMultiply }, - { "numpad*", KeyCode.KeypadMultiply }, - { "numpaddivide", KeyCode.KeypadDivide }, - { "keypaddivide", KeyCode.KeypadDivide }, - { "numpad/", KeyCode.KeypadDivide }, - { "numpadenter", KeyCode.KeypadEnter }, - { "keypadenter", KeyCode.KeypadEnter }, - { "numpadperiod", KeyCode.KeypadPeriod }, - { "keypadperiod", KeyCode.KeypadPeriod }, - { "numpad.", KeyCode.KeypadPeriod }, - { "numpaddecimal", KeyCode.KeypadPeriod }, - { "numpadequals", KeyCode.KeypadEquals }, - { "keypadequals", KeyCode.KeypadEquals }, - { "numpad=", KeyCode.KeypadEquals }, - { "esc", KeyCode.Escape }, - { "escape", KeyCode.Escape }, - { "return", KeyCode.Return }, - { "enter", KeyCode.Return }, - { "space", KeyCode.Space }, - { "spacebar", KeyCode.Space }, - { "del", KeyCode.Delete }, - { "delete", KeyCode.Delete }, - { "ins", KeyCode.Insert }, - { "insert", KeyCode.Insert }, - { "pageup", KeyCode.PageUp }, - { "pgup", KeyCode.PageUp }, - { "pagedown", KeyCode.PageDown }, - { "pgdn", KeyCode.PageDown }, - { "pagedn", KeyCode.PageDown }, - { "lshift", KeyCode.LeftShift }, - { "rshift", KeyCode.RightShift }, - { "leftshift", KeyCode.LeftShift }, - { "rightshift", KeyCode.RightShift }, - { "lctrl", KeyCode.LeftControl }, - { "rctrl", KeyCode.RightControl }, - { "lcontrol", KeyCode.LeftControl }, - { "rcontrol", KeyCode.RightControl }, - { "leftctrl", KeyCode.LeftControl }, - { "rightctrl", KeyCode.RightControl }, - { "leftcontrol", KeyCode.LeftControl }, - { "rightcontrol", KeyCode.RightControl }, - { "lalt", KeyCode.LeftAlt }, - { "ralt", KeyCode.RightAlt }, - { "leftalt", KeyCode.LeftAlt }, - { "rightalt", KeyCode.RightAlt }, - { "lcmd", KeyCode.LeftCommand }, - { "rcmd", KeyCode.RightCommand }, - { "lcommand", KeyCode.LeftCommand }, - { "rcommand", KeyCode.RightCommand }, - { "leftcmd", KeyCode.LeftCommand }, - { "rightcmd", KeyCode.RightCommand }, - { "leftcommand", KeyCode.LeftCommand }, - { "rightcommand", KeyCode.RightCommand }, - { "lwin", KeyCode.LeftWindows }, - { "rwin", KeyCode.RightWindows }, - { "leftwindows", KeyCode.LeftWindows }, - { "rightwindows", KeyCode.RightWindows }, - { "leftwin", KeyCode.LeftWindows }, - { "rightwin", KeyCode.RightWindows }, - { "capslock", KeyCode.CapsLock }, - { "numlock", KeyCode.Numlock }, - { "scrolllock", KeyCode.ScrollLock }, - { "prtscn", KeyCode.Print }, - { "printscreen", KeyCode.Print }, - { "pausebreak", KeyCode.Pause }, - { "pause", KeyCode.Pause }, - { "up", KeyCode.UpArrow }, - { "uparrow", KeyCode.UpArrow }, - { "down", KeyCode.DownArrow }, - { "downarrow", KeyCode.DownArrow }, - { "left", KeyCode.LeftArrow }, - { "leftarrow", KeyCode.LeftArrow }, - { "right", KeyCode.RightArrow }, - { "rightarrow", KeyCode.RightArrow }, - { "f1", KeyCode.F1 }, - { "f2", KeyCode.F2 }, - { "f3", KeyCode.F3 }, - { "f4", KeyCode.F4 }, - { "f5", KeyCode.F5 }, - { "f6", KeyCode.F6 }, - { "f7", KeyCode.F7 }, - { "f8", KeyCode.F8 }, - { "f9", KeyCode.F9 }, - { "f10", KeyCode.F10 }, - { "f11", KeyCode.F11 }, - { "f12", KeyCode.F12 }, - { "f13", KeyCode.F13 }, - { "f14", KeyCode.F14 }, - { "f15", KeyCode.F15 }, - { "mouse0", KeyCode.Mouse0 }, - { "leftmouse", KeyCode.Mouse0 }, - { "lmb", KeyCode.Mouse0 }, - { "mouse1", KeyCode.Mouse1 }, - { "rightmouse", KeyCode.Mouse1 }, - { "rmb", KeyCode.Mouse1 }, - { "mouse2", KeyCode.Mouse2 }, - { "middlemouse", KeyCode.Mouse2 }, - { "mmb", KeyCode.Mouse2 }, - { "mouse3", KeyCode.Mouse3 }, - { "mouse4", KeyCode.Mouse4 }, - { "mouse5", KeyCode.Mouse5 }, - { "mouse6", KeyCode.Mouse6 }, - { "none", KeyCode.None }, - }; - - private static readonly Dictionary SpecialKeyCodeMap = new( - StringComparer.OrdinalIgnoreCase - ) - { - { "`", "backquote" }, - { "-", "minus" }, - { "=", "equals" }, - { "[", "leftBracket" }, - { "]", "rightBracket" }, - { ";", "semicolon" }, - { "'", "quote" }, - { "\\", "backslash" }, - { ",", "comma" }, - { ".", "period" }, - { "/", "slash" }, - { "1", "digit1" }, - { "2", "digit2" }, - { "3", "digit3" }, - { "4", "digit4" }, - { "5", "digit5" }, - { "6", "digit6" }, - { "7", "digit7" }, - { "8", "digit8" }, - { "9", "digit9" }, - { "0", "digit0" }, - { "up", "upArrow" }, - { "left", "leftArrow" }, - { "right", "rightArrow" }, - { "down", "downArrow" }, - { " ", "space" }, - }; - - private static readonly Dictionary SpecialShiftedKeyCodeMap = new( - StringComparer.OrdinalIgnoreCase - ) - { - { "~", "backquote" }, - { "!", "digit1" }, - { "@", "digit2" }, - { "#", "digit3" }, - { "$", "digit4" }, - { "^", "digit6" }, - { "%", "digit5" }, - { "&", "digit7" }, - { "*", "digit8" }, - { "(", "digit9" }, - { ")", "digit0" }, - { "_", "minus" }, - { "+", "equals" }, - { "{", "leftBracket" }, - { "}", "rightBracket" }, - { ":", "semicolon" }, - { "\"", "quote" }, - { "|", "backslash" }, - { "<", "comma" }, - { ">", "period" }, - { "?", "slash" }, - }; - - private static readonly Dictionary AlternativeSpecialShiftedKeyCodeMap = - new(StringComparer.OrdinalIgnoreCase) - { - { "!", "1" }, - { "@", "2" }, - { "#", "3" }, - { "$", "4" }, - { "^", "5" }, - { "%", "6" }, - { "&", "7" }, - { "*", "8" }, - { "(", "9" }, - { ")", "0" }, - }; - - public static bool IsKeyPressed(string key, InputMode inputMode) - { -#pragma warning disable CS0612 // Type or member is obsolete - if (inputMode == InputMode.None) -#pragma warning restore CS0612 // Type or member is obsolete - { - return false; - } - - if (string.IsNullOrEmpty(key)) - { - return false; - } - - bool shiftRequired = false; - string keyName = key; - int startIndex = 0; - - foreach (string shiftModifier in ShiftModifiers) - { - if ( - key.StartsWith(shiftModifier, StringComparison.OrdinalIgnoreCase) - && key != shiftModifier - ) - { - shiftRequired = true; - startIndex = shiftModifier.Length; - break; - } - } - - if (!shiftRequired && key.Length == 1) - { - char keyChar = key[0]; - if (char.IsUpper(keyChar) && char.IsLetter(keyChar)) - { - shiftRequired = true; - } - else if ( - AlternativeSpecialShiftedKeyCodeMap.TryGetValue( - key, - out string legacyShiftedKeyName - ) - ) - { - shiftRequired = true; - keyName = legacyShiftedKeyName; - } - } - - if (0 < startIndex) - { - if (!CachedSubstrings.TryGetValue(key, out keyName)) - { - keyName = key[startIndex..]; - if (keyName.NeedsTrim()) - { - keyName = keyName.Trim(); - } - - if (keyName.Length == 1 && keyName.NeedsLowerInvariantConversion()) - { - keyName = keyName.ToLowerInvariant(); - } - - CachedSubstrings[key] = keyName; - } - } - - if (string.IsNullOrWhiteSpace(keyName)) - { - return false; - } -#pragma warning disable CS0612 // Type or member is obsolete - if (inputMode == InputMode.LegacyInputSystem) -#pragma warning restore CS0612 // Type or member is obsolete - { -#if ENABLE_LEGACY_INPUT_MANAGER - if ( - Enum.TryParse(keyName, ignoreCase: true, out KeyCode keyCode) - || KeyCodeMapping.TryGetValue(keyName, out keyCode) - ) - { - return Input.GetKeyDown(keyCode) - && ( - !shiftRequired - || Input.GetKey(KeyCode.LeftShift) - || Input.GetKey(KeyCode.LeftShift) - || Input.GetKey(KeyCode.RightShift) - || Input.GetKey(KeyCode.RightShift) - ); - } -#endif - - return false; - } -#pragma warning disable CS0612 // Type or member is obsolete - if (inputMode == InputMode.NewInputSystem) -#pragma warning restore CS0612 // Type or member is obsolete - { -#if ENABLE_INPUT_SYSTEM - if ( - !shiftRequired - && ( - AlternativeSpecialShiftedKeyCodeMap.TryGetValue( - keyName, - out string shiftedKeyName - ) || SpecialShiftedKeyCodeMap.TryGetValue(keyName, out shiftedKeyName) - ) - ) - { - shiftRequired = true; - keyName = shiftedKeyName; - } - - Keyboard currentKeyboard = Keyboard.current; - return (!shiftRequired || currentKeyboard.shiftKey.isPressed) - && ( - currentKeyboard.TryGetChildControl( - SpecialKeyCodeMap.GetValueOrDefault(keyName, keyName) - ) - is { wasPressedThisFrame: true } - || currentKeyboard.TryGetChildControl(keyName) - is { wasPressedThisFrame: true } - ); -#endif - } - return false; - } - } -} +namespace WallstopStudios.DxCommandTerminal.Input +{ + using System; + using System.Collections.Generic; + using Extensions; + using UnityEngine; +#if ENABLE_INPUT_SYSTEM + using UnityEngine.InputSystem; + using UnityEngine.InputSystem.Controls; +#endif + + public static class InputHelpers + { + private static readonly string[] ShiftModifiers = { "shift+", "#" }; + + private static readonly Dictionary CachedSubstrings = new(); + + private static readonly Dictionary KeyCodeMapping = new( + StringComparer.OrdinalIgnoreCase + ) + { + { "~", KeyCode.BackQuote }, + { "`", KeyCode.BackQuote }, + { "!", KeyCode.Alpha1 }, + { "@", KeyCode.Alpha2 }, + { "#", KeyCode.Alpha3 }, + { "$", KeyCode.Alpha4 }, + { "%", KeyCode.Alpha5 }, + { "^", KeyCode.Alpha6 }, + { "&", KeyCode.Alpha7 }, + { "*", KeyCode.Alpha8 }, + { "(", KeyCode.Alpha9 }, + { ")", KeyCode.Alpha0 }, + { "-", KeyCode.Minus }, + { "_", KeyCode.Minus }, + { "=", KeyCode.Equals }, + { "+", KeyCode.Equals }, + { "[", KeyCode.LeftBracket }, + { "{", KeyCode.LeftBracket }, + { "]", KeyCode.RightBracket }, + { "}", KeyCode.RightBracket }, + { "\\", KeyCode.Backslash }, + { "|", KeyCode.Backslash }, + { ";", KeyCode.Semicolon }, + { ":", KeyCode.Semicolon }, + { "'", KeyCode.Quote }, + { "\"", KeyCode.Quote }, + { ",", KeyCode.Comma }, + { "<", KeyCode.Comma }, + { ".", KeyCode.Period }, + { ">", KeyCode.Period }, + { "/", KeyCode.Slash }, + { "?", KeyCode.Slash }, + { "1", KeyCode.Alpha1 }, + { "2", KeyCode.Alpha2 }, + { "3", KeyCode.Alpha3 }, + { "4", KeyCode.Alpha4 }, + { "5", KeyCode.Alpha5 }, + { "6", KeyCode.Alpha6 }, + { "7", KeyCode.Alpha7 }, + { "8", KeyCode.Alpha8 }, + { "9", KeyCode.Alpha9 }, + { "0", KeyCode.Alpha0 }, + { "numpad1", KeyCode.Keypad1 }, + { "keypad1", KeyCode.Keypad1 }, + { "numpad2", KeyCode.Keypad2 }, + { "keypad2", KeyCode.Keypad2 }, + { "numpad3", KeyCode.Keypad3 }, + { "keypad3", KeyCode.Keypad3 }, + { "numpad4", KeyCode.Keypad4 }, + { "keypad4", KeyCode.Keypad4 }, + { "numpad5", KeyCode.Keypad5 }, + { "keypad5", KeyCode.Keypad5 }, + { "numpad6", KeyCode.Keypad6 }, + { "keypad6", KeyCode.Keypad6 }, + { "numpad7", KeyCode.Keypad7 }, + { "keypad7", KeyCode.Keypad7 }, + { "numpad8", KeyCode.Keypad8 }, + { "keypad8", KeyCode.Keypad8 }, + { "numpad9", KeyCode.Keypad9 }, + { "keypad9", KeyCode.Keypad9 }, + { "numpad0", KeyCode.Keypad0 }, + { "keypad0", KeyCode.Keypad0 }, + { "numpadplus", KeyCode.KeypadPlus }, + { "keypadplus", KeyCode.KeypadPlus }, + { "numpad+", KeyCode.KeypadPlus }, + { "numpadminus", KeyCode.KeypadMinus }, + { "keypadminus", KeyCode.KeypadMinus }, + { "numpad-", KeyCode.KeypadMinus }, + { "numpadmultiply", KeyCode.KeypadMultiply }, + { "keypadmultiply", KeyCode.KeypadMultiply }, + { "numpad*", KeyCode.KeypadMultiply }, + { "numpaddivide", KeyCode.KeypadDivide }, + { "keypaddivide", KeyCode.KeypadDivide }, + { "numpad/", KeyCode.KeypadDivide }, + { "numpadenter", KeyCode.KeypadEnter }, + { "keypadenter", KeyCode.KeypadEnter }, + { "numpadperiod", KeyCode.KeypadPeriod }, + { "keypadperiod", KeyCode.KeypadPeriod }, + { "numpad.", KeyCode.KeypadPeriod }, + { "numpaddecimal", KeyCode.KeypadPeriod }, + { "numpadequals", KeyCode.KeypadEquals }, + { "keypadequals", KeyCode.KeypadEquals }, + { "numpad=", KeyCode.KeypadEquals }, + { "esc", KeyCode.Escape }, + { "escape", KeyCode.Escape }, + { "return", KeyCode.Return }, + { "enter", KeyCode.Return }, + { "space", KeyCode.Space }, + { "spacebar", KeyCode.Space }, + { "del", KeyCode.Delete }, + { "delete", KeyCode.Delete }, + { "ins", KeyCode.Insert }, + { "insert", KeyCode.Insert }, + { "pageup", KeyCode.PageUp }, + { "pgup", KeyCode.PageUp }, + { "pagedown", KeyCode.PageDown }, + { "pgdn", KeyCode.PageDown }, + { "pagedn", KeyCode.PageDown }, + { "lshift", KeyCode.LeftShift }, + { "rshift", KeyCode.RightShift }, + { "leftshift", KeyCode.LeftShift }, + { "rightshift", KeyCode.RightShift }, + { "lctrl", KeyCode.LeftControl }, + { "rctrl", KeyCode.RightControl }, + { "lcontrol", KeyCode.LeftControl }, + { "rcontrol", KeyCode.RightControl }, + { "leftctrl", KeyCode.LeftControl }, + { "rightctrl", KeyCode.RightControl }, + { "leftcontrol", KeyCode.LeftControl }, + { "rightcontrol", KeyCode.RightControl }, + { "lalt", KeyCode.LeftAlt }, + { "ralt", KeyCode.RightAlt }, + { "leftalt", KeyCode.LeftAlt }, + { "rightalt", KeyCode.RightAlt }, + { "lcmd", KeyCode.LeftCommand }, + { "rcmd", KeyCode.RightCommand }, + { "lcommand", KeyCode.LeftCommand }, + { "rcommand", KeyCode.RightCommand }, + { "leftcmd", KeyCode.LeftCommand }, + { "rightcmd", KeyCode.RightCommand }, + { "leftcommand", KeyCode.LeftCommand }, + { "rightcommand", KeyCode.RightCommand }, + { "lwin", KeyCode.LeftWindows }, + { "rwin", KeyCode.RightWindows }, + { "leftwindows", KeyCode.LeftWindows }, + { "rightwindows", KeyCode.RightWindows }, + { "leftwin", KeyCode.LeftWindows }, + { "rightwin", KeyCode.RightWindows }, + { "capslock", KeyCode.CapsLock }, + { "numlock", KeyCode.Numlock }, + { "scrolllock", KeyCode.ScrollLock }, + { "prtscn", KeyCode.Print }, + { "printscreen", KeyCode.Print }, + { "pausebreak", KeyCode.Pause }, + { "pause", KeyCode.Pause }, + { "up", KeyCode.UpArrow }, + { "uparrow", KeyCode.UpArrow }, + { "down", KeyCode.DownArrow }, + { "downarrow", KeyCode.DownArrow }, + { "left", KeyCode.LeftArrow }, + { "leftarrow", KeyCode.LeftArrow }, + { "right", KeyCode.RightArrow }, + { "rightarrow", KeyCode.RightArrow }, + { "f1", KeyCode.F1 }, + { "f2", KeyCode.F2 }, + { "f3", KeyCode.F3 }, + { "f4", KeyCode.F4 }, + { "f5", KeyCode.F5 }, + { "f6", KeyCode.F6 }, + { "f7", KeyCode.F7 }, + { "f8", KeyCode.F8 }, + { "f9", KeyCode.F9 }, + { "f10", KeyCode.F10 }, + { "f11", KeyCode.F11 }, + { "f12", KeyCode.F12 }, + { "f13", KeyCode.F13 }, + { "f14", KeyCode.F14 }, + { "f15", KeyCode.F15 }, + { "mouse0", KeyCode.Mouse0 }, + { "leftmouse", KeyCode.Mouse0 }, + { "lmb", KeyCode.Mouse0 }, + { "mouse1", KeyCode.Mouse1 }, + { "rightmouse", KeyCode.Mouse1 }, + { "rmb", KeyCode.Mouse1 }, + { "mouse2", KeyCode.Mouse2 }, + { "middlemouse", KeyCode.Mouse2 }, + { "mmb", KeyCode.Mouse2 }, + { "mouse3", KeyCode.Mouse3 }, + { "mouse4", KeyCode.Mouse4 }, + { "mouse5", KeyCode.Mouse5 }, + { "mouse6", KeyCode.Mouse6 }, + { "none", KeyCode.None }, + }; + + private static readonly Dictionary SpecialKeyCodeMap = new( + StringComparer.OrdinalIgnoreCase + ) + { + { "`", "backquote" }, + { "-", "minus" }, + { "=", "equals" }, + { "[", "leftBracket" }, + { "]", "rightBracket" }, + { ";", "semicolon" }, + { "'", "quote" }, + { "\\", "backslash" }, + { ",", "comma" }, + { ".", "period" }, + { "/", "slash" }, + { "1", "digit1" }, + { "2", "digit2" }, + { "3", "digit3" }, + { "4", "digit4" }, + { "5", "digit5" }, + { "6", "digit6" }, + { "7", "digit7" }, + { "8", "digit8" }, + { "9", "digit9" }, + { "0", "digit0" }, + { "up", "upArrow" }, + { "left", "leftArrow" }, + { "right", "rightArrow" }, + { "down", "downArrow" }, + { " ", "space" }, + }; + + private static readonly Dictionary SpecialShiftedKeyCodeMap = new( + StringComparer.OrdinalIgnoreCase + ) + { + { "~", "backquote" }, + { "!", "digit1" }, + { "@", "digit2" }, + { "#", "digit3" }, + { "$", "digit4" }, + { "^", "digit6" }, + { "%", "digit5" }, + { "&", "digit7" }, + { "*", "digit8" }, + { "(", "digit9" }, + { ")", "digit0" }, + { "_", "minus" }, + { "+", "equals" }, + { "{", "leftBracket" }, + { "}", "rightBracket" }, + { ":", "semicolon" }, + { "\"", "quote" }, + { "|", "backslash" }, + { "<", "comma" }, + { ">", "period" }, + { "?", "slash" }, + }; + + private static readonly Dictionary AlternativeSpecialShiftedKeyCodeMap = + new(StringComparer.OrdinalIgnoreCase) + { + { "!", "1" }, + { "@", "2" }, + { "#", "3" }, + { "$", "4" }, + { "^", "5" }, + { "%", "6" }, + { "&", "7" }, + { "*", "8" }, + { "(", "9" }, + { ")", "0" }, + }; + + public static bool IsKeyPressed(string key, InputMode inputMode) + { +#pragma warning disable CS0612 // Type or member is obsolete + if (inputMode == InputMode.None) +#pragma warning restore CS0612 // Type or member is obsolete + { + return false; + } + + if (string.IsNullOrEmpty(key)) + { + return false; + } + + bool shiftRequired = false; + string keyName = key; + int startIndex = 0; + + foreach (string shiftModifier in ShiftModifiers) + { + if ( + key.StartsWith(shiftModifier, StringComparison.OrdinalIgnoreCase) + && key != shiftModifier + ) + { + shiftRequired = true; + startIndex = shiftModifier.Length; + break; + } + } + + if (!shiftRequired && key.Length == 1) + { + char keyChar = key[0]; + if (char.IsUpper(keyChar) && char.IsLetter(keyChar)) + { + shiftRequired = true; + } + else if ( + AlternativeSpecialShiftedKeyCodeMap.TryGetValue( + key, + out string legacyShiftedKeyName + ) + ) + { + shiftRequired = true; + keyName = legacyShiftedKeyName; + } + } + + if (0 < startIndex) + { + if (!CachedSubstrings.TryGetValue(key, out keyName)) + { + keyName = key[startIndex..]; + if (keyName.NeedsTrim()) + { + keyName = keyName.Trim(); + } + + if (keyName.Length == 1 && keyName.NeedsLowerInvariantConversion()) + { + keyName = keyName.ToLowerInvariant(); + } + + CachedSubstrings[key] = keyName; + } + } + + if (string.IsNullOrWhiteSpace(keyName)) + { + return false; + } +#pragma warning disable CS0612 // Type or member is obsolete + if (inputMode == InputMode.LegacyInputSystem) +#pragma warning restore CS0612 // Type or member is obsolete + { +#if ENABLE_LEGACY_INPUT_MANAGER + if ( + Enum.TryParse(keyName, ignoreCase: true, out KeyCode keyCode) + || KeyCodeMapping.TryGetValue(keyName, out keyCode) + ) + { + return Input.GetKeyDown(keyCode) + && ( + !shiftRequired + || Input.GetKey(KeyCode.LeftShift) + || Input.GetKey(KeyCode.LeftShift) + || Input.GetKey(KeyCode.RightShift) + || Input.GetKey(KeyCode.RightShift) + ); + } +#endif + + return false; + } +#pragma warning disable CS0612 // Type or member is obsolete + if (inputMode == InputMode.NewInputSystem) +#pragma warning restore CS0612 // Type or member is obsolete + { +#if ENABLE_INPUT_SYSTEM + if ( + !shiftRequired + && ( + AlternativeSpecialShiftedKeyCodeMap.TryGetValue( + keyName, + out string shiftedKeyName + ) || SpecialShiftedKeyCodeMap.TryGetValue(keyName, out shiftedKeyName) + ) + ) + { + shiftRequired = true; + keyName = shiftedKeyName; + } + + Keyboard currentKeyboard = Keyboard.current; + return (!shiftRequired || currentKeyboard.shiftKey.isPressed) + && ( + currentKeyboard.TryGetChildControl( + SpecialKeyCodeMap.GetValueOrDefault(keyName, keyName) + ) + is { wasPressedThisFrame: true } + || currentKeyboard.TryGetChildControl(keyName) + is { wasPressedThisFrame: true } + ); +#endif + } + return false; + } + } +} diff --git a/Runtime/CommandTerminal/Input/InputMode.cs b/Runtime/CommandTerminal/Input/InputMode.cs index e363cca..bedd383 100644 --- a/Runtime/CommandTerminal/Input/InputMode.cs +++ b/Runtime/CommandTerminal/Input/InputMode.cs @@ -1,20 +1,20 @@ -namespace WallstopStudios.DxCommandTerminal.Input -{ - using System; - - public enum InputMode - { - [Obsolete] - None = 0, -#if !ENABLE_LEGACY_INPUT_MANAGER - [Obsolete] -#endif - LegacyInputSystem = 1 << 0 - , -#if !ENABLE_INPUT_SYSTEM - [Obsolete] -#endif - NewInputSystem = 1 << 1 - , - } -} +namespace WallstopStudios.DxCommandTerminal.Input +{ + using System; + + public enum InputMode + { + [Obsolete] + None = 0, +#if !ENABLE_LEGACY_INPUT_MANAGER + [Obsolete] +#endif + LegacyInputSystem = 1 << 0 + , +#if !ENABLE_INPUT_SYSTEM + [Obsolete] +#endif + NewInputSystem = 1 << 1 + , + } +} diff --git a/Runtime/CommandTerminal/Input/TerminalControlTypes.cs b/Runtime/CommandTerminal/Input/TerminalControlTypes.cs index b8835bc..4fdabd2 100644 --- a/Runtime/CommandTerminal/Input/TerminalControlTypes.cs +++ b/Runtime/CommandTerminal/Input/TerminalControlTypes.cs @@ -1,18 +1,19 @@ -namespace WallstopStudios.DxCommandTerminal.Input -{ - using System; - - public enum TerminalControlTypes - { - [Obsolete] - None = 0, - Close = 1, - EnterCommand = 2, - Previous = 3, - Next = 4, - ToggleFull = 5, - ToggleSmall = 6, - CompleteForward = 7, - CompleteBackward = 8, - } -} +namespace WallstopStudios.DxCommandTerminal.Input +{ + using System; + + public enum TerminalControlTypes + { + [Obsolete] + None = 0, + Close = 1, + EnterCommand = 2, + Previous = 3, + Next = 4, + ToggleFull = 5, + ToggleSmall = 6, + CompleteForward = 7, + CompleteBackward = 8, + ToggleLauncher = 9, + } +} diff --git a/Runtime/CommandTerminal/Input/TerminalInputProfile.cs b/Runtime/CommandTerminal/Input/TerminalInputProfile.cs new file mode 100644 index 0000000..7aa39cc --- /dev/null +++ b/Runtime/CommandTerminal/Input/TerminalInputProfile.cs @@ -0,0 +1,61 @@ +namespace WallstopStudios.DxCommandTerminal.Input +{ + using System.Collections.Generic; + using UnityEngine; + + [CreateAssetMenu( + fileName = "TerminalInputProfile", + menuName = "DXCommandTerminal/Terminal Input Profile", + order = 460 + )] + public sealed class TerminalInputProfile : ScriptableObject + { + [Header("System")] + public InputMode inputMode = InputMode.LegacyInputSystem; + + [Header("Hotkeys")] + public string toggleHotkey = "`"; + public string toggleFullHotkey = "#`"; + public string toggleLauncherHotkey = "#space"; + public string completeHotkey = "tab"; + public string reverseCompleteHotkey = "#tab"; + public string previousHotkey = "up"; + public List enterCommandHotkeys = new() { "enter", "return" }; + public string closeHotkey = "escape"; + public string nextHotkey = "down"; + + [Header("Control Order")] + public List controlOrder = new() + { + TerminalControlTypes.Close, + TerminalControlTypes.EnterCommand, + TerminalControlTypes.Previous, + TerminalControlTypes.Next, + TerminalControlTypes.ToggleLauncher, + TerminalControlTypes.ToggleFull, + TerminalControlTypes.ToggleSmall, + TerminalControlTypes.CompleteBackward, + TerminalControlTypes.CompleteForward, + }; + + public void ApplyTo(TerminalKeyboardController controller) + { + if (controller == null) + { + return; + } + + controller.inputMode = inputMode; + controller.toggleHotkey = toggleHotkey; + controller.toggleFullHotkey = toggleFullHotkey; + controller.toggleLauncherHotkey = toggleLauncherHotkey; + controller.completeHotkey = completeHotkey; + controller.reverseCompleteHotkey = reverseCompleteHotkey; + controller.previousHotkey = previousHotkey; + controller._completeCommandHotkeys = new List(enterCommandHotkeys); + controller.closeHotkey = closeHotkey; + controller.nextHotkey = nextHotkey; + controller._controlOrder = new List(controlOrder); + } + } +} diff --git a/Runtime/CommandTerminal/Input/TerminalInputProfile.cs.meta b/Runtime/CommandTerminal/Input/TerminalInputProfile.cs.meta new file mode 100644 index 0000000..0bbf525 --- /dev/null +++ b/Runtime/CommandTerminal/Input/TerminalInputProfile.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7435cc51d78cc51a283e12e69bc2e319 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Input/TerminalInputProviderProxy.cs b/Runtime/CommandTerminal/Input/TerminalInputProviderProxy.cs new file mode 100644 index 0000000..5ef679f --- /dev/null +++ b/Runtime/CommandTerminal/Input/TerminalInputProviderProxy.cs @@ -0,0 +1,16 @@ +namespace WallstopStudios.DxCommandTerminal.Input +{ + using UI; + + internal sealed class TerminalInputProviderProxy : ITerminalInputProvider + { + internal static ITerminalInputProvider Default { get; } = new TerminalInputProviderProxy(); + + private TerminalInputProviderProxy() { } + + public ITerminalInput GetInput(TerminalUI terminal) + { + return DefaultTerminalInput.Instance; + } + } +} diff --git a/Runtime/CommandTerminal/Input/TerminalInputProviderProxy.cs.meta b/Runtime/CommandTerminal/Input/TerminalInputProviderProxy.cs.meta new file mode 100644 index 0000000..c9f9e0b --- /dev/null +++ b/Runtime/CommandTerminal/Input/TerminalInputProviderProxy.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c40b378c0c8b4179b0d2b717c815e2de +timeCreated: 1760673634 diff --git a/Runtime/CommandTerminal/Input/TerminalInputSourceFactory.cs b/Runtime/CommandTerminal/Input/TerminalInputSourceFactory.cs new file mode 100644 index 0000000..c605c3d --- /dev/null +++ b/Runtime/CommandTerminal/Input/TerminalInputSourceFactory.cs @@ -0,0 +1,25 @@ +namespace WallstopStudios.DxCommandTerminal.Input +{ + internal static class TerminalInputSourceFactory + { + private sealed class UnityInputSource : ITerminalInputSource + { + public UnityInputSource(InputMode mode) + { + Mode = mode; + } + + public InputMode Mode { get; } + + public bool IsKeyPressed(string binding) + { + return InputHelpers.IsKeyPressed(binding, Mode); + } + } + + public static ITerminalInputSource Create(InputMode mode) + { + return new UnityInputSource(mode); + } + } +} diff --git a/Runtime/CommandTerminal/Input/TerminalInputSourceFactory.cs.meta b/Runtime/CommandTerminal/Input/TerminalInputSourceFactory.cs.meta new file mode 100644 index 0000000..c3f087c --- /dev/null +++ b/Runtime/CommandTerminal/Input/TerminalInputSourceFactory.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 86c56196e7f09bcbe96a86e9a88763c5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/Input/TerminalKeyboardController.cs b/Runtime/CommandTerminal/Input/TerminalKeyboardController.cs index 6bdd3f3..24428d6 100644 --- a/Runtime/CommandTerminal/Input/TerminalKeyboardController.cs +++ b/Runtime/CommandTerminal/Input/TerminalKeyboardController.cs @@ -1,322 +1,482 @@ -namespace WallstopStudios.DxCommandTerminal.Input -{ - using System; - using System.Collections.Generic; - using System.Linq; - using UI; - using UnityEngine; - - [DisallowMultipleComponent] - public class TerminalKeyboardController : MonoBehaviour, IInputHandler - { - protected static readonly TerminalControlTypes[] ControlTypes = Enum.GetValues( - typeof(TerminalControlTypes) - ) - .OfType() -#pragma warning disable CS0612 // Type or member is obsolete - .Except(new[] { TerminalControlTypes.None }) -#pragma warning restore CS0612 // Type or member is obsolete - .ToArray(); - - public bool ShouldHandleInputThisFrame - { - get - { - foreach (TerminalControlTypes controlType in _controlOrder) - { - if (!_inputChecks.TryGetValue(controlType, out Func inputCheck)) - { - continue; - } - if (inputCheck()) - { - return true; - } - } - - return false; - } - } - - [Header("System")] - public InputMode inputMode = -#if ENABLE_INPUT_SYSTEM - InputMode.NewInputSystem; -#else - InputMode.LegacyInputSystem; -#endif - public TerminalUI terminal; - - [Header("Hotkeys")] - [SerializeField] - public string toggleHotkey = "`"; - - [SerializeField] - public string toggleFullHotkey = "#`"; - - [SerializeField] - public string completeHotkey = "tab"; - - [SerializeField] - public string reverseCompleteHotkey = "#tab"; - - [SerializeField] - public string previousHotkey = "up"; - - [SerializeField] - public List _completeCommandHotkeys = new() { "enter", "return" }; - - [SerializeField] - public string closeHotkey = "escape"; - - [SerializeField] - public string nextHotkey = "down"; - - [SerializeField] - [Tooltip("Re-order these to choose what priority you want input to be checked in")] - protected List _controlOrder = new() - { - TerminalControlTypes.Close, - TerminalControlTypes.EnterCommand, - TerminalControlTypes.Previous, - TerminalControlTypes.Next, - TerminalControlTypes.ToggleFull, - TerminalControlTypes.ToggleSmall, - TerminalControlTypes.CompleteBackward, - TerminalControlTypes.CompleteForward, - }; - - protected readonly Dictionary> _inputChecks = new(); - protected readonly Dictionary _controlHandlerActions = new(); - - public TerminalKeyboardController() - { - _inputChecks.Clear(); - _inputChecks[TerminalControlTypes.Close] = IsClosePressed; - _inputChecks[TerminalControlTypes.EnterCommand] = IsEnterCommandPressed; - _inputChecks[TerminalControlTypes.Previous] = IsPreviousPressed; - _inputChecks[TerminalControlTypes.Next] = IsNextPressed; - _inputChecks[TerminalControlTypes.ToggleFull] = IsToggleFullPressed; - _inputChecks[TerminalControlTypes.ToggleSmall] = IsToggleSmallPressed; - _inputChecks[TerminalControlTypes.CompleteBackward] = IsCompleteBackwardPressed; - _inputChecks[TerminalControlTypes.CompleteForward] = IsCompletePressed; - - _controlHandlerActions.Clear(); - _controlHandlerActions[TerminalControlTypes.Close] = Close; - _controlHandlerActions[TerminalControlTypes.EnterCommand] = EnterCommand; - _controlHandlerActions[TerminalControlTypes.Previous] = Previous; - _controlHandlerActions[TerminalControlTypes.Next] = Next; - _controlHandlerActions[TerminalControlTypes.ToggleFull] = ToggleFull; - _controlHandlerActions[TerminalControlTypes.ToggleSmall] = ToggleSmall; - _controlHandlerActions[TerminalControlTypes.CompleteBackward] = CompleteBackward; - _controlHandlerActions[TerminalControlTypes.CompleteForward] = Complete; - } - - protected virtual void Awake() - { - if (terminal != null) - { - return; - } - - if (!TryGetComponent(out terminal)) - { - Debug.LogError("Failed to find TerminalUI, Input will not work.", this); - } - - if (_controlOrder is not { Count: > 0 }) - { - Debug.LogError("No controls specified, Input will not work.", this); - } - else - { - VerifyControlOrderIntegrity(); - } - } - - protected virtual void OnValidate() - { - if (!Application.isPlaying) - { - VerifyControlOrderIntegrity(); - } - } - - private void VerifyControlOrderIntegrity() - { - if (!_controlOrder.ToHashSet().SetEquals(ControlTypes)) - { - Debug.LogWarning( - $"Control Order is missing the following controls: [{string.Join(", ", ControlTypes.Except(_controlOrder))}]. " - + "Input for these will not be handled. Is this intentional?", - this - ); - } - } - - protected virtual void Update() - { - if (_controlOrder is not { Count: > 0 }) - { - return; - } - - foreach (TerminalControlTypes controlType in _controlOrder) - { - if (!_inputChecks.TryGetValue(controlType, out Func inputCheck)) - { - continue; - } - - if (!inputCheck()) - { - continue; - } - - if (!_controlHandlerActions.TryGetValue(controlType, out Action action)) - { - continue; - } - - action(); - break; - } - } - - #region Commands - - protected virtual void Close() - { - if (terminal == null) - { - return; - } - - terminal.Close(); - } - - protected virtual void EnterCommand() - { - if (terminal == null) - { - return; - } - terminal.EnterCommand(); - } - - protected virtual void Previous() - { - if (terminal == null) - { - return; - } - terminal.HandlePrevious(); - } - - protected virtual void Next() - { - if (terminal == null) - { - return; - } - terminal.HandleNext(); - } - - protected virtual void ToggleFull() - { - if (terminal == null) - { - return; - } - terminal.ToggleFull(); - } - - protected virtual void ToggleSmall() - { - if (terminal == null) - { - return; - } - terminal.ToggleSmall(); - } - - protected virtual void Complete() - { - if (terminal == null) - { - return; - } - - terminal.CompleteCommand(searchForward: true); - } - - protected virtual void CompleteBackward() - { - if (terminal == null) - { - return; - } - terminal.CompleteCommand(searchForward: false); - } - - #endregion - - - #region Control Checks - - protected virtual bool IsClosePressed() - { - return InputHelpers.IsKeyPressed(closeHotkey, inputMode); - } - - protected virtual bool IsPreviousPressed() - { - return InputHelpers.IsKeyPressed(previousHotkey, inputMode); - } - - protected virtual bool IsNextPressed() - { - return InputHelpers.IsKeyPressed(nextHotkey, inputMode); - } - - protected virtual bool IsToggleFullPressed() - { - return InputHelpers.IsKeyPressed(toggleFullHotkey, inputMode); - } - - protected virtual bool IsToggleSmallPressed() - { - return InputHelpers.IsKeyPressed(toggleHotkey, inputMode); - } - - protected virtual bool IsCompleteBackwardPressed() - { - return InputHelpers.IsKeyPressed(reverseCompleteHotkey, inputMode); - } - - protected virtual bool IsCompletePressed() - { - return InputHelpers.IsKeyPressed(completeHotkey, inputMode); - } - - protected virtual bool IsEnterCommandPressed() - { - if (_completeCommandHotkeys is not { Count: > 0 }) - { - return false; - } - - foreach (string command in _completeCommandHotkeys) - { - if (InputHelpers.IsKeyPressed(command, inputMode)) - { - return true; - } - } - - return false; - } - - #endregion - } -} +namespace WallstopStudios.DxCommandTerminal.Input +{ + using System; + using System.Collections.Generic; + using UI; + using UnityEngine; + + [DisallowMultipleComponent] + public class TerminalKeyboardController : MonoBehaviour, IInputHandler + { + protected static readonly TerminalControlTypes[] ControlTypes = BuildControlTypes(); + + protected readonly HashSet _missing = new(); + protected readonly HashSet _terminalControlTypes = new(); + protected ITerminalInputTarget _inputTarget; + private ITerminalInputSource _inputSource; + private bool _missingTargetLogged; + + private static TerminalControlTypes[] BuildControlTypes() + { + Array values = Enum.GetValues(typeof(TerminalControlTypes)); + List list = new(); + for (int i = 0; i < values.Length; ++i) + { + object v = values.GetValue(i); + if (v is TerminalControlTypes t) + { +#pragma warning disable CS0612 // Type or member is obsolete + if (t == TerminalControlTypes.None) + { + continue; + } +#pragma warning restore CS0612 // Type or member is obsolete + list.Add(t); + } + } + return list.ToArray(); + } + + public bool ShouldHandleInputThisFrame + { + get + { + foreach (TerminalControlTypes controlType in _controlOrder) + { + if (IsControlPressed(controlType)) + { + return true; + } + } + + return false; + } + } + + [Header("System")] + public InputMode inputMode = +#if ENABLE_INPUT_SYSTEM + InputMode.NewInputSystem; +#else + InputMode.LegacyInputSystem; +#endif + public TerminalUI terminal; + + [Header("Profiles")] + [SerializeField] + private TerminalInputProfile _inputProfile; + + [Header("Hotkeys")] + public string toggleHotkey = "`"; + public string toggleFullHotkey = "#`"; + public string toggleLauncherHotkey = "#space"; + public string completeHotkey = "tab"; + public string reverseCompleteHotkey = "#tab"; + public string previousHotkey = "up"; + public List _completeCommandHotkeys = new() { "enter", "return" }; + public string closeHotkey = "escape"; + public string nextHotkey = "down"; + + [SerializeField] + [Tooltip("Re-order these to choose what priority you want input to be checked in")] + protected internal List _controlOrder = new() + { + TerminalControlTypes.Close, + TerminalControlTypes.EnterCommand, + TerminalControlTypes.Previous, + TerminalControlTypes.Next, + TerminalControlTypes.ToggleLauncher, + TerminalControlTypes.ToggleFull, + TerminalControlTypes.ToggleSmall, + TerminalControlTypes.CompleteBackward, + TerminalControlTypes.CompleteForward, + }; + + public TerminalKeyboardController() { } + + protected virtual void Awake() + { + ResolveInputTarget(); + ResolveInputSource(); + + ApplyProfileIfAvailable(); + + if (_controlOrder is not { Count: > 0 }) + { + Debug.LogError("No controls specified, Input will not work.", this); + } + else + { + VerifyControlOrderIntegrity(); + } + } + + protected virtual void OnValidate() + { + if (!Application.isPlaying) + { + ResolveInputTarget(); + ApplyProfileIfAvailable(); + VerifyControlOrderIntegrity(); + ResolveInputSource(); + } + } + + private void VerifyControlOrderIntegrity() + { + // Verify set equality without LINQ + _terminalControlTypes.Clear(); + foreach (TerminalControlTypes controlType in _controlOrder) + { + _terminalControlTypes.Add(controlType); + } + bool equal = _terminalControlTypes.Count == ControlTypes.Length; + if (equal) + { + for (int i = 0; i < ControlTypes.Length; ++i) + { + if (!_terminalControlTypes.Contains(ControlTypes[i])) + { + equal = false; + break; + } + } + } + + if (!equal && _controlOrder is { Count: > 0 }) + { + _missing.Clear(); + for (int i = 0; i < ControlTypes.Length; ++i) + { + TerminalControlTypes t = ControlTypes[i]; + if (!_terminalControlTypes.Contains(t)) + { + _missing.Add(t.ToString()); + } + } + + Debug.LogWarning( + $"Control Order is missing the following controls: [{string.Join(", ", _missing)}]. " + + "Input for these will not be handled. Is this intentional?" + + $"\nTerminal Control Types: [{string.Join(", ", ControlTypes)}]" + + $"\nExisting Control Types: [{string.Join(", ", _terminalControlTypes)}]", + this + ); + } + } + + protected virtual void Update() + { + if (_inputTarget == null) + { + ResolveInputTarget(); + if (_inputTarget == null) + { + return; + } + } + + if (_inputSource == null) + { + ResolveInputSource(); + if (_inputSource == null) + { + return; + } + } + + if (_controlOrder is not { Count: > 0 }) + { + return; + } + + foreach (TerminalControlTypes controlType in _controlOrder) + { + if (!IsControlPressed(controlType)) + { + continue; + } + + ExecuteControl(controlType); + break; + } + } + + #region Commands + + protected virtual void Close() + { + if (_inputTarget == null) + { + return; + } + + _inputTarget.Close(); + } + + protected virtual void EnterCommand() + { + if (_inputTarget == null) + { + return; + } + _inputTarget.EnterCommand(); + } + + protected virtual void Previous() + { + if (_inputTarget == null) + { + return; + } + _inputTarget.HandlePrevious(); + } + + protected virtual void Next() + { + if (_inputTarget == null) + { + return; + } + _inputTarget.HandleNext(); + } + + protected virtual void ToggleFull() + { + if (_inputTarget == null) + { + return; + } + _inputTarget.ToggleFull(); + } + + protected virtual void ToggleLauncher() + { + if (_inputTarget == null) + { + return; + } + _inputTarget.ToggleLauncher(); + } + + protected virtual void ToggleSmall() + { + if (_inputTarget == null) + { + return; + } + _inputTarget.ToggleSmall(); + } + + protected virtual void Complete() + { + if (_inputTarget == null) + { + return; + } + + _inputTarget.CompleteCommand(searchForward: true); + } + + protected virtual void CompleteBackward() + { + if (_inputTarget == null) + { + return; + } + _inputTarget.CompleteCommand(searchForward: false); + } + + #endregion + + + #region Control Checks + + protected virtual bool IsClosePressed() + { + return _inputSource != null && _inputSource.IsKeyPressed(closeHotkey); + } + + protected virtual bool IsPreviousPressed() + { + return _inputSource != null && _inputSource.IsKeyPressed(previousHotkey); + } + + protected virtual bool IsNextPressed() + { + return _inputSource != null && _inputSource.IsKeyPressed(nextHotkey); + } + + protected virtual bool IsToggleFullPressed() + { + return _inputSource != null && _inputSource.IsKeyPressed(toggleFullHotkey); + } + + protected virtual bool IsToggleLauncherPressed() + { + return _inputSource != null && _inputSource.IsKeyPressed(toggleLauncherHotkey); + } + + protected virtual bool IsToggleSmallPressed() + { + return _inputSource != null && _inputSource.IsKeyPressed(toggleHotkey); + } + + protected virtual bool IsCompleteBackwardPressed() + { + return _inputSource != null && _inputSource.IsKeyPressed(reverseCompleteHotkey); + } + + protected virtual bool IsCompletePressed() + { + return _inputSource != null && _inputSource.IsKeyPressed(completeHotkey); + } + + protected virtual bool IsEnterCommandPressed() + { + if (_completeCommandHotkeys is not { Count: > 0 }) + { + return false; + } + + foreach (string command in _completeCommandHotkeys) + { + if (_inputSource != null && _inputSource.IsKeyPressed(command)) + { + return true; + } + } + + return false; + } + + #endregion + + private bool IsControlPressed(TerminalControlTypes controlType) + { + switch (controlType) + { + case TerminalControlTypes.Close: + return IsClosePressed(); + case TerminalControlTypes.EnterCommand: + return IsEnterCommandPressed(); + case TerminalControlTypes.Previous: + return IsPreviousPressed(); + case TerminalControlTypes.Next: + return IsNextPressed(); + case TerminalControlTypes.ToggleFull: + return IsToggleFullPressed(); + case TerminalControlTypes.ToggleSmall: + return IsToggleSmallPressed(); + case TerminalControlTypes.ToggleLauncher: + return IsToggleLauncherPressed(); + case TerminalControlTypes.CompleteBackward: + return IsCompleteBackwardPressed(); + case TerminalControlTypes.CompleteForward: + return IsCompletePressed(); + default: + return false; + } + } + + protected virtual void ExecuteControl(TerminalControlTypes controlType) + { + switch (controlType) + { + case TerminalControlTypes.Close: + Close(); + break; + case TerminalControlTypes.EnterCommand: + EnterCommand(); + break; + case TerminalControlTypes.Previous: + Previous(); + break; + case TerminalControlTypes.Next: + Next(); + break; + case TerminalControlTypes.ToggleFull: + ToggleFull(); + break; + case TerminalControlTypes.ToggleSmall: + ToggleSmall(); + break; + case TerminalControlTypes.ToggleLauncher: + ToggleLauncher(); + break; + case TerminalControlTypes.CompleteBackward: + CompleteBackward(); + break; + case TerminalControlTypes.CompleteForward: + Complete(); + break; + } + } + + private void ResolveInputTarget() + { + if (terminal != null) + { + _inputTarget = terminal; + _missingTargetLogged = false; + return; + } + + if (!TryGetComponent(out ITerminalInputTarget resolvedTarget)) + { + MonoBehaviour[] behaviours = GetComponents(); + for (int i = 0; i < behaviours.Length && resolvedTarget == null; ++i) + { + if (behaviours[i] is ITerminalInputTarget candidate) + { + resolvedTarget = candidate; + } + } + } + + if (resolvedTarget != null) + { + _inputTarget = resolvedTarget; + terminal = resolvedTarget as TerminalUI; + _missingTargetLogged = false; + } + else if (!_missingTargetLogged) + { + Debug.LogWarning( + "Failed to locate a terminal input target. Input will not work.", + this + ); + _missingTargetLogged = true; + } + } + +#if UNITY_EDITOR || UNITY_INCLUDE_TESTS + internal void ExecuteControlForTests(TerminalControlTypes controlType) + { + ExecuteControl(controlType); + } + + internal void SetInputProfileForTests(TerminalInputProfile profile) + { + _inputProfile = profile; + ApplyProfileIfAvailable(); + } +#endif + + private void ApplyProfileIfAvailable() + { + if (_inputProfile == null) + { + return; + } + + _inputProfile.ApplyTo(this); + ResolveInputSource(); + } + + private void ResolveInputSource() + { + _inputSource = TerminalInputSourceFactory.Create(inputMode); + } + } +} diff --git a/Runtime/CommandTerminal/Input/TerminalPlayerInputController.cs b/Runtime/CommandTerminal/Input/TerminalPlayerInputController.cs index aa96e1d..44b355c 100644 --- a/Runtime/CommandTerminal/Input/TerminalPlayerInputController.cs +++ b/Runtime/CommandTerminal/Input/TerminalPlayerInputController.cs @@ -1,163 +1,176 @@ -namespace WallstopStudios.DxCommandTerminal.Input -{ -#if ENABLE_INPUT_SYSTEM - using UI; - using UnityEngine; - using UnityEngine.InputSystem; - - [DisallowMultipleComponent] - public class TerminalPlayerInputController : MonoBehaviour - { - [Header("System")] - public bool enableWarnings = true; - - public TerminalUI terminal; - - protected bool _enabled; - - [SerializeField] - protected PlayerInput _serializedPlayerInput; - - protected PlayerInput _playerInput; - - protected virtual void Awake() - { - _playerInput = _serializedPlayerInput; - if (_playerInput == null) - { - if (!TryGetComponent(out _playerInput) && enableWarnings) - { - Debug.LogWarning( - "No PlayerInput attached, events may not work (which is the point of this component).", - this - ); - } - } - - if (terminal != null) - { - return; - } - - if (!TryGetComponent(out terminal)) - { - Debug.LogError("Failed to find TerminalUI, Input will not work.", this); - } - } - - protected virtual void OnEnable() - { - _enabled = true; - } - - protected virtual void OnDisable() - { - _enabled = false; - } - - public virtual void OnHandlePrevious(InputValue inputValue) - { - if (!_enabled) - { - return; - } - if (terminal == null) - { - return; - } - terminal.HandlePrevious(); - } - - public virtual void OnHandleNext(InputValue inputValue) - { - if (!_enabled) - { - return; - } - if (terminal == null) - { - return; - } - terminal.HandleNext(); - } - - public virtual void OnClose(InputValue inputValue) - { - if (!_enabled) - { - return; - } - if (terminal == null) - { - return; - } - terminal.Close(); - } - - public virtual void OnToggleSmall(InputValue inputValue) - { - if (!_enabled) - { - return; - } - if (terminal == null) - { - return; - } - terminal.ToggleSmall(); - } - - public virtual void OnToggleFull(InputValue inputValue) - { - if (!_enabled) - { - return; - } - if (terminal == null) - { - return; - } - terminal.ToggleFull(); - } - - public virtual void OnCompleteCommand(InputValue input) - { - if (!_enabled) - { - return; - } - if (terminal == null) - { - return; - } - terminal.CompleteCommand(searchForward: true); - } - - public virtual void OnReverseCompleteCommand(InputValue input) - { - if (!_enabled) - { - return; - } - if (terminal == null) - { - return; - } - terminal.CompleteCommand(searchForward: false); - } - - public virtual void OnEnterCommand(InputValue inputValue) - { - if (!_enabled) - { - return; - } - if (terminal == null) - { - return; - } - terminal.EnterCommand(); - } - } -#endif -} +namespace WallstopStudios.DxCommandTerminal.Input +{ +#if ENABLE_INPUT_SYSTEM + using UI; + using UnityEngine; + using UnityEngine.InputSystem; + + [DisallowMultipleComponent] + public class TerminalPlayerInputController : MonoBehaviour + { + [Header("System")] + public bool enableWarnings = true; + + public TerminalUI terminal; + + protected bool _enabled; + + [SerializeField] + protected PlayerInput _serializedPlayerInput; + + protected PlayerInput _playerInput; + + protected virtual void Awake() + { + _playerInput = _serializedPlayerInput; + if (_playerInput == null) + { + if (!TryGetComponent(out _playerInput) && enableWarnings) + { + Debug.LogWarning( + "No PlayerInput attached, events may not work (which is the point of this component).", + this + ); + } + } + + if (terminal != null) + { + return; + } + + if (!TryGetComponent(out terminal)) + { + Debug.LogError("Failed to find TerminalUI, Input will not work.", this); + } + } + + protected virtual void OnEnable() + { + _enabled = true; + } + + protected virtual void OnDisable() + { + _enabled = false; + } + + public virtual void OnHandlePrevious(InputValue inputValue) + { + if (!_enabled) + { + return; + } + if (terminal == null) + { + return; + } + terminal.HandlePrevious(); + } + + public virtual void OnHandleNext(InputValue inputValue) + { + if (!_enabled) + { + return; + } + if (terminal == null) + { + return; + } + terminal.HandleNext(); + } + + public virtual void OnClose(InputValue inputValue) + { + if (!_enabled) + { + return; + } + if (terminal == null) + { + return; + } + terminal.Close(); + } + + public virtual void OnToggleSmall(InputValue inputValue) + { + if (!_enabled) + { + return; + } + if (terminal == null) + { + return; + } + terminal.ToggleSmall(); + } + + public virtual void OnToggleFull(InputValue inputValue) + { + if (!_enabled) + { + return; + } + if (terminal == null) + { + return; + } + terminal.ToggleFull(); + } + + public virtual void OnToggleLauncher(InputValue inputValue) + { + if (!_enabled) + { + return; + } + if (terminal == null) + { + return; + } + terminal.ToggleLauncher(); + } + + public virtual void OnCompleteCommand(InputValue input) + { + if (!_enabled) + { + return; + } + if (terminal == null) + { + return; + } + terminal.CompleteCommand(searchForward: true); + } + + public virtual void OnReverseCompleteCommand(InputValue input) + { + if (!_enabled) + { + return; + } + if (terminal == null) + { + return; + } + terminal.CompleteCommand(searchForward: false); + } + + public virtual void OnEnterCommand(InputValue inputValue) + { + if (!_enabled) + { + return; + } + if (terminal == null) + { + return; + } + terminal.EnterCommand(); + } + } +#endif +} diff --git a/Runtime/CommandTerminal/Persistence/TerminalThemeConfiguration.cs b/Runtime/CommandTerminal/Persistence/TerminalThemeConfiguration.cs index ee48b4c..2557583 100644 --- a/Runtime/CommandTerminal/Persistence/TerminalThemeConfiguration.cs +++ b/Runtime/CommandTerminal/Persistence/TerminalThemeConfiguration.cs @@ -1,49 +1,49 @@ -namespace WallstopStudios.DxCommandTerminal.Persistence -{ - using System; - - [Serializable] - public struct TerminalThemeConfiguration : IEquatable - { - public const int HashBase = 397; - - public string terminalId; - public string font; - public string theme; - - public bool Equals(TerminalThemeConfiguration other) - { - return string.Equals(terminalId, other.terminalId, StringComparison.OrdinalIgnoreCase) - && string.Equals(font, other.font, StringComparison.OrdinalIgnoreCase) - && string.Equals(theme, other.theme, StringComparison.OrdinalIgnoreCase); - } - - public override bool Equals(object obj) - { - if (obj is TerminalThemeConfiguration config) - { - return Equals(config); - } - - return false; - } - - public override int GetHashCode() - { - // Auto generated garbage - unchecked - { - int hashCode = ( - terminalId != null ? terminalId.ToLowerInvariant().GetHashCode() : 0 - ); - hashCode = - (hashCode * HashBase) - ^ (font != null ? font.ToLowerInvariant().GetHashCode() : 0); - hashCode = - (hashCode * HashBase) - ^ (theme != null ? theme.ToLowerInvariant().GetHashCode() : 0); - return hashCode; - } - } - } -} +namespace WallstopStudios.DxCommandTerminal.Persistence +{ + using System; + + [Serializable] + public struct TerminalThemeConfiguration : IEquatable + { + public const int HashBase = 397; + + public string terminalId; + public string font; + public string theme; + + public bool Equals(TerminalThemeConfiguration other) + { + return string.Equals(terminalId, other.terminalId, StringComparison.OrdinalIgnoreCase) + && string.Equals(font, other.font, StringComparison.OrdinalIgnoreCase) + && string.Equals(theme, other.theme, StringComparison.OrdinalIgnoreCase); + } + + public override bool Equals(object obj) + { + if (obj is TerminalThemeConfiguration config) + { + return Equals(config); + } + + return false; + } + + public override int GetHashCode() + { + // Auto generated garbage + unchecked + { + int hashCode = ( + terminalId != null ? terminalId.ToLowerInvariant().GetHashCode() : 0 + ); + hashCode = + (hashCode * HashBase) + ^ (font != null ? font.ToLowerInvariant().GetHashCode() : 0); + hashCode = + (hashCode * HashBase) + ^ (theme != null ? theme.ToLowerInvariant().GetHashCode() : 0); + return hashCode; + } + } + } +} diff --git a/Runtime/CommandTerminal/Persistence/TerminalThemeConfigurations.cs b/Runtime/CommandTerminal/Persistence/TerminalThemeConfigurations.cs index 5589008..56bb3c4 100644 --- a/Runtime/CommandTerminal/Persistence/TerminalThemeConfigurations.cs +++ b/Runtime/CommandTerminal/Persistence/TerminalThemeConfigurations.cs @@ -1,81 +1,81 @@ -namespace WallstopStudios.DxCommandTerminal.Persistence -{ - using System; - using System.Collections.Generic; - using UI; - - [Serializable] - public sealed class TerminalThemeConfigurations - { - public List configurations = new(); - - public bool TryGetConfiguration( - TerminalUI terminal, - out TerminalThemeConfiguration configuration - ) - { - int existingIndex = -1; - if (terminal != null) - { - for (int i = 0; i < configurations.Count; ++i) - { - if ( - string.Equals( - terminal.id, - configurations[i].terminalId, - StringComparison.OrdinalIgnoreCase - ) - ) - { - existingIndex = i; - break; - } - } - } - - bool exists = 0 <= existingIndex; - configuration = exists ? configurations[existingIndex] : default; - - return exists; - } - - /// - /// - /// - /// - /// True if a mutation happened, false if it was a no-op - public bool AddOrUpdate(TerminalThemeConfiguration configuration) - { - int existingIndex = -1; - for (int i = 0; i < configurations.Count; ++i) - { - if ( - string.Equals( - configuration.terminalId, - configurations[i].terminalId, - StringComparison.OrdinalIgnoreCase - ) - ) - { - existingIndex = i; - break; - } - } - - if (0 <= existingIndex) - { - if (configurations[existingIndex].Equals(configuration)) - { - return false; - } - configurations[existingIndex] = configuration; - } - else - { - configurations.Add(configuration); - } - - return true; - } - } -} +namespace WallstopStudios.DxCommandTerminal.Persistence +{ + using System; + using System.Collections.Generic; + using UI; + + [Serializable] + public sealed class TerminalThemeConfigurations + { + public List configurations = new(); + + public bool TryGetConfiguration( + TerminalUI terminal, + out TerminalThemeConfiguration configuration + ) + { + int existingIndex = -1; + if (terminal != null) + { + for (int i = 0; i < configurations.Count; ++i) + { + if ( + string.Equals( + terminal.id, + configurations[i].terminalId, + StringComparison.OrdinalIgnoreCase + ) + ) + { + existingIndex = i; + break; + } + } + } + + bool exists = 0 <= existingIndex; + configuration = exists ? configurations[existingIndex] : default; + + return exists; + } + + /// + /// + /// + /// + /// True if a mutation happened, false if it was a no-op + public bool AddOrUpdate(TerminalThemeConfiguration configuration) + { + int existingIndex = -1; + for (int i = 0; i < configurations.Count; ++i) + { + if ( + string.Equals( + configuration.terminalId, + configurations[i].terminalId, + StringComparison.OrdinalIgnoreCase + ) + ) + { + existingIndex = i; + break; + } + } + + if (0 <= existingIndex) + { + if (configurations[existingIndex].Equals(configuration)) + { + return false; + } + configurations[existingIndex] = configuration; + } + else + { + configurations.Add(configuration); + } + + return true; + } + } +} diff --git a/Runtime/CommandTerminal/Persistence/TerminalThemePersistenceProfile.cs b/Runtime/CommandTerminal/Persistence/TerminalThemePersistenceProfile.cs new file mode 100644 index 0000000..d3de2a3 --- /dev/null +++ b/Runtime/CommandTerminal/Persistence/TerminalThemePersistenceProfile.cs @@ -0,0 +1,27 @@ +namespace WallstopStudios.DxCommandTerminal.Persistence +{ + using UnityEngine; + + [CreateAssetMenu( + fileName = "TerminalThemePersistenceProfile", + menuName = "DXCommandTerminal/Terminal Theme Persistence Profile", + order = 490 + )] + public sealed class TerminalThemePersistenceProfile : ScriptableObject + { + [Header("Persistence")] + public bool enablePersistence = true; + + [Tooltip("Automatically hydrate terminal theme/font from storage when enabled.")] + public bool loadOnStart = true; + + [Tooltip("Continuously save terminal state while running.")] + public bool savePeriodically = true; + + [Min(0f)] + public float savePeriod = 1f; + + [Tooltip("Optional file name override for the persisted theme data.")] + public string fileName = "TerminalTheme.json"; + } +} diff --git a/Runtime/CommandTerminal/Persistence/TerminalThemePersistenceProfile.cs.meta b/Runtime/CommandTerminal/Persistence/TerminalThemePersistenceProfile.cs.meta new file mode 100644 index 0000000..70563ba --- /dev/null +++ b/Runtime/CommandTerminal/Persistence/TerminalThemePersistenceProfile.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b30caafa071646c78853ca0b3ae9ab32 +timeCreated: 0 diff --git a/Runtime/CommandTerminal/Persistence/TerminalThemePersister.cs b/Runtime/CommandTerminal/Persistence/TerminalThemePersister.cs index 931bff5..6d7d1ce 100644 --- a/Runtime/CommandTerminal/Persistence/TerminalThemePersister.cs +++ b/Runtime/CommandTerminal/Persistence/TerminalThemePersister.cs @@ -1,308 +1,386 @@ -namespace WallstopStudios.DxCommandTerminal.Persistence -{ - using System; - using System.Collections; - using System.IO; - using System.Threading.Tasks; - using Attributes; - using UI; - using UnityEngine; - - [DisallowMultipleComponent] - public class TerminalThemePersister : MonoBehaviour - { - protected virtual string ThemeFile => - Path.Join(Application.persistentDataPath, "DxCommandTerminal", "TerminalTheme.json"); - - [Header("System")] - public TerminalUI terminal; - - [Header("Config")] - public bool savePeriodically = true; - - [DxShowIf(nameof(savePeriodically))] - public float savePeriod = 1f; - - protected Font _lastSeenFont; - protected string _lastSeenTheme; - protected float? _nextUpdateTime; - - protected bool _persisting; - protected Coroutine _persistence; - - protected virtual void Awake() - { - if (terminal != null) - { - return; - } - - if (!TryGetComponent(out terminal)) - { - Debug.LogError("Failed to find TerminalUI, Theme persistence will not work.", this); - } - } - - protected virtual IEnumerator Start() - { - if (terminal == null) - { - yield break; - } - - string themeFile = ThemeFile; - Debug.Log($"Attempting to initialize from {themeFile}...", this); - yield return CheckAndPersistAnyChanges(hydrate: true); - } - - protected virtual void Update() - { - if (!savePeriodically) - { - return; - } - - if (Time.time <= _nextUpdateTime) - { - return; - } - - if (terminal == null) - { - return; - } - - if ( - _lastSeenFont == terminal.CurrentFont - && string.Equals( - _lastSeenTheme, - terminal.CurrentTheme, - StringComparison.OrdinalIgnoreCase - ) - ) - { - _nextUpdateTime = Time.time + savePeriod; - return; - } - - if (_persisting) - { - return; - } - - if (_persistence != null) - { - return; - } - - _persistence = StartCoroutine(CheckAndPersistAnyChanges(hydrate: false)); - } - - protected virtual IEnumerator CheckAndPersistAnyChanges(bool hydrate) - { - _lastSeenFont = terminal.CurrentFont; - _lastSeenTheme = terminal.CurrentTheme; - _persisting = true; - try - { - if (terminal == null) - { - yield break; - } - - string themeFile = ThemeFile; - string directoryPath = Path.GetDirectoryName(themeFile); - if (!string.IsNullOrWhiteSpace(directoryPath)) - { - Directory.CreateDirectory(directoryPath); - } - - TerminalThemeConfigurations configurations; - if (File.Exists(themeFile)) - { - Task readerTask = File.ReadAllTextAsync(themeFile); - while (!readerTask.IsCompleted) - { - yield return null; - } - - if (!readerTask.IsCompletedSuccessfully) - { - _lastSeenFont = null; - _lastSeenTheme = null; - Debug.LogError( - $"Failed to read theme file {themeFile}: {readerTask.Exception}.", - this - ); - yield break; - } - - string inputJson = readerTask.Result; - try - { - configurations = JsonUtility.FromJson( - inputJson - ); - } - catch (Exception e) - { - Debug.LogError( - $"Failed to parse theme file {themeFile}, defaulting to empty theme file: {e}", - this - ); - configurations = new TerminalThemeConfigurations(); - } - } - else - { - Debug.Log( - $"Creating new theme file {themeFile} for terminal {terminal.id} ...", - this - ); - configurations = new TerminalThemeConfigurations(); - } - - if (hydrate) - { - if ( - configurations.TryGetConfiguration( - terminal, - out TerminalThemeConfiguration existingConfiguration - ) - ) - { - int fontIndex; - if (terminal._fontPack == null) - { - fontIndex = -1; - } - else - { - fontIndex = terminal._fontPack._fonts.FindIndex(font => - string.Equals( - font.name, - existingConfiguration.font, - StringComparison.OrdinalIgnoreCase - ) - ); - } - - if (0 <= fontIndex) - { - _lastSeenFont = terminal._fontPack._fonts[fontIndex]; - terminal.SetFont(_lastSeenFont, persist: true); - } - else - { - Debug.LogWarning( - $"Failed to find persisted font {existingConfiguration.font} for terminal {terminal.id} while hydrating.", - this - ); - } - - int themeIndex; - if (terminal._themePack == null) - { - themeIndex = -1; - } - else - { - themeIndex = terminal._themePack._themeNames.FindIndex(theme => - string.Equals( - theme, - existingConfiguration.theme, - StringComparison.OrdinalIgnoreCase - ) - ); - } - - if (0 <= themeIndex) - { - _lastSeenTheme = terminal._themePack._themeNames[themeIndex]; - terminal.SetTheme(_lastSeenTheme, persist: true); - } - else - { - Debug.LogWarning( - $"Failed to find persisted theme {existingConfiguration.theme} for terminal {terminal.id} while hydrating.", - this - ); - } - - yield break; - } - else - { - Debug.Log( - $"Failed to find persisted configuration for terminal {terminal.id} while hydrating, defaulting to Prefab configuration.", - this - ); - } - } - - TerminalThemeConfiguration? maybeCurrentConfiguration = GetConfiguration(); - if (maybeCurrentConfiguration == null) - { - yield break; - } - - TerminalThemeConfiguration currentConfiguration = maybeCurrentConfiguration.Value; - if (!configurations.AddOrUpdate(currentConfiguration)) - { - yield break; - } - - string outputJson = JsonUtility.ToJson(configurations, prettyPrint: true); - Debug.Log( - $"Writing theme file {themeFile} with contents:{Environment.NewLine}{outputJson}", - this - ); - Task writerTask = File.WriteAllTextAsync(themeFile, outputJson); - while (!writerTask.IsCompleted) - { - yield return null; - } - - if (!writerTask.IsCompletedSuccessfully) - { - _lastSeenFont = null; - _lastSeenTheme = null; - Debug.LogError( - $"Failed to write theme file {themeFile} (terminal {terminal.id}): {writerTask.Exception}", - this - ); - } - else - { - Debug.Log($"Theme file {themeFile} successfully updated.", this); - } - } - finally - { - _nextUpdateTime = Time.time + savePeriod; - _persisting = false; - _persistence = null; - } - } - - public virtual TerminalThemeConfiguration? GetConfiguration() - { - if (terminal == null) - { - return null; - } - - if (string.IsNullOrWhiteSpace(terminal.id)) - { - return null; - } - - return new TerminalThemeConfiguration - { - terminalId = terminal.id, - font = terminal.CurrentFont == null ? string.Empty : terminal.CurrentFont.name, - theme = terminal.CurrentTheme ?? string.Empty, - }; - } - } -} +namespace WallstopStudios.DxCommandTerminal.Persistence +{ + using System; + using System.Collections; + using System.IO; + using System.Threading.Tasks; + using Attributes; + using UI; + using UnityEngine; + + [DisallowMultipleComponent] + public class TerminalThemePersister : MonoBehaviour + { + protected virtual string ThemeFile => + !string.IsNullOrWhiteSpace(_customThemeFile) + ? _customThemeFile + : Path.Join( + Application.persistentDataPath, + "DxCommandTerminal", + "TerminalTheme.json" + ); + + [Header("System")] + public TerminalUI terminal; + + [Header("Config")] + public bool savePeriodically = true; + + [DxShowIf(nameof(savePeriodically))] + public float savePeriod = 1f; + + [Header("Profiles")] + [SerializeField] + private TerminalThemePersistenceProfile _persistenceProfile; + + protected Font _lastSeenFont; + protected string _lastSeenTheme; + protected float? _nextUpdateTime; + + protected bool _persisting; + protected Coroutine _persistence; + private bool _persistenceEnabled = true; + private bool _loadOnStart = true; + private string _customThemeFile; + + protected virtual void Awake() + { + if (terminal != null) + { + ApplyPersistenceProfile(); + return; + } + + if (!TryGetComponent(out terminal)) + { + Debug.LogError("Failed to find TerminalUI, Theme persistence will not work.", this); + return; + } + + ApplyPersistenceProfile(); + } + + protected virtual IEnumerator Start() + { + if (terminal == null || !_persistenceEnabled) + { + yield break; + } + + if (!_loadOnStart) + { + yield break; + } + + string themeFile = ThemeFile; + Debug.Log($"Attempting to initialize from {themeFile}...", this); + yield return CheckAndPersistAnyChanges(hydrate: true); + } + + protected virtual void Update() + { + if (!_persistenceEnabled) + { + return; + } + + if (!savePeriodically) + { + return; + } + + if (Time.time <= _nextUpdateTime) + { + return; + } + + if (terminal == null) + { + return; + } + + if ( + _lastSeenFont == terminal.CurrentFont + && string.Equals( + _lastSeenTheme, + terminal.CurrentTheme, + StringComparison.OrdinalIgnoreCase + ) + ) + { + _nextUpdateTime = Time.time + savePeriod; + return; + } + + if (_persisting) + { + return; + } + + if (_persistence != null) + { + return; + } + + _persistence = StartCoroutine(CheckAndPersistAnyChanges(hydrate: false)); + } + + protected virtual IEnumerator CheckAndPersistAnyChanges(bool hydrate) + { + if (!_persistenceEnabled) + { + yield break; + } + + _lastSeenFont = terminal.CurrentFont; + _lastSeenTheme = terminal.CurrentTheme; + _persisting = true; + try + { + if (terminal == null) + { + yield break; + } + + string themeFile = ThemeFile; + string directoryPath = Path.GetDirectoryName(themeFile); + if (!string.IsNullOrWhiteSpace(directoryPath)) + { + Directory.CreateDirectory(directoryPath); + } + + TerminalThemeConfigurations configurations; + if (File.Exists(themeFile)) + { + Task readerTask = File.ReadAllTextAsync(themeFile); + while (!readerTask.IsCompleted) + { + yield return null; + } + + if (!readerTask.IsCompletedSuccessfully) + { + _lastSeenFont = null; + _lastSeenTheme = null; + Debug.LogError( + $"Failed to read theme file {themeFile}: {readerTask.Exception}.", + this + ); + yield break; + } + + string inputJson = readerTask.Result; + try + { + configurations = JsonUtility.FromJson( + inputJson + ); + } + catch (Exception e) + { + Debug.LogError( + $"Failed to parse theme file {themeFile}, defaulting to empty theme file: {e}", + this + ); + configurations = new TerminalThemeConfigurations(); + } + } + else + { + Debug.Log( + $"Creating new theme file {themeFile} for terminal {terminal.id} ...", + this + ); + configurations = new TerminalThemeConfigurations(); + } + + if (hydrate) + { + if ( + configurations.TryGetConfiguration( + terminal, + out TerminalThemeConfiguration existingConfiguration + ) + ) + { + int fontIndex; + if (terminal._fontPack == null) + { + fontIndex = -1; + } + else + { + fontIndex = terminal._fontPack._fonts.FindIndex(font => + string.Equals( + font.name, + existingConfiguration.font, + StringComparison.OrdinalIgnoreCase + ) + ); + } + + if (0 <= fontIndex) + { + _lastSeenFont = terminal._fontPack._fonts[fontIndex]; + terminal.SetFont(_lastSeenFont, persist: true); + } + else + { + Debug.LogWarning( + $"Failed to find persisted font {existingConfiguration.font} for terminal {terminal.id} while hydrating.", + this + ); + } + + int themeIndex; + if (terminal._themePack == null) + { + themeIndex = -1; + } + else + { + themeIndex = terminal._themePack._themeNames.FindIndex(theme => + string.Equals( + theme, + existingConfiguration.theme, + StringComparison.OrdinalIgnoreCase + ) + ); + } + + if (0 <= themeIndex) + { + _lastSeenTheme = terminal._themePack._themeNames[themeIndex]; + terminal.SetTheme(_lastSeenTheme, persist: true); + } + else + { + Debug.LogWarning( + $"Failed to find persisted theme {existingConfiguration.theme} for terminal {terminal.id} while hydrating.", + this + ); + } + + yield break; + } + else + { + Debug.Log( + $"Failed to find persisted configuration for terminal {terminal.id} while hydrating, defaulting to Prefab configuration.", + this + ); + } + } + + TerminalThemeConfiguration? maybeCurrentConfiguration = GetConfiguration(); + if (maybeCurrentConfiguration == null) + { + yield break; + } + + TerminalThemeConfiguration currentConfiguration = maybeCurrentConfiguration.Value; + if (!configurations.AddOrUpdate(currentConfiguration)) + { + yield break; + } + + string outputJson = JsonUtility.ToJson(configurations, prettyPrint: true); + Debug.Log( + $"Writing theme file {themeFile} with contents:{Environment.NewLine}{outputJson}", + this + ); + Task writerTask = File.WriteAllTextAsync(themeFile, outputJson); + while (!writerTask.IsCompleted) + { + yield return null; + } + + if (!writerTask.IsCompletedSuccessfully) + { + _lastSeenFont = null; + _lastSeenTheme = null; + Debug.LogError( + $"Failed to write theme file {themeFile} (terminal {terminal.id}): {writerTask.Exception}", + this + ); + } + else + { + Debug.Log($"Theme file {themeFile} successfully updated.", this); + } + } + finally + { + _nextUpdateTime = Time.time + savePeriod; + _persisting = false; + _persistence = null; + } + } + + protected virtual void OnValidate() + { + if (!Application.isPlaying) + { + ApplyPersistenceProfile(); + } + } + + public virtual TerminalThemeConfiguration? GetConfiguration() + { + if (terminal == null) + { + return null; + } + + if (string.IsNullOrWhiteSpace(terminal.id)) + { + return null; + } + + return new TerminalThemeConfiguration + { + terminalId = terminal.id, + font = terminal.CurrentFont == null ? string.Empty : terminal.CurrentFont.name, + theme = terminal.CurrentTheme ?? string.Empty, + }; + } + + private void ApplyPersistenceProfile() + { + _persistenceEnabled = true; + _loadOnStart = true; + _customThemeFile = null; + + if (_persistenceProfile == null) + { + return; + } + + _persistenceEnabled = _persistenceProfile.enablePersistence; + _loadOnStart = _persistenceProfile.loadOnStart; + savePeriodically = _persistenceProfile.savePeriodically; + savePeriod = Mathf.Max(0f, _persistenceProfile.savePeriod); + + if (!string.IsNullOrWhiteSpace(_persistenceProfile.fileName)) + { + _customThemeFile = Path.Join( + Application.persistentDataPath, + "DxCommandTerminal", + _persistenceProfile.fileName + ); + } + } + +#if UNITY_EDITOR || UNITY_INCLUDE_TESTS + internal void SetPersistenceProfileForTests(TerminalThemePersistenceProfile profile) + { + _persistenceProfile = profile; + ApplyPersistenceProfile(); + } + + internal bool PersistenceEnabledForTests => _persistenceEnabled; + + internal bool LoadOnStartForTests => _loadOnStart; +#endif + } +} diff --git a/Runtime/CommandTerminal/Service.meta b/Runtime/CommandTerminal/Service.meta new file mode 100644 index 0000000..087f5fd --- /dev/null +++ b/Runtime/CommandTerminal/Service.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 60cd891ba4e34f2d8f8b742def01cb70 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: + diff --git a/Runtime/CommandTerminal/Service/TerminalServiceBindingAsset.cs b/Runtime/CommandTerminal/Service/TerminalServiceBindingAsset.cs new file mode 100644 index 0000000..c388c74 --- /dev/null +++ b/Runtime/CommandTerminal/Service/TerminalServiceBindingAsset.cs @@ -0,0 +1,165 @@ +namespace WallstopStudios.DxCommandTerminal.Service +{ + using System; + using Backend; + using Input; + using UI; + using UnityEngine; + + /// + /// ScriptableObject wrapper that exposes service bindings for Terminal consumers. + /// Can be assigned to to replace global service instances without resorting to static overrides. + /// + [CreateAssetMenu( + fileName = "TerminalServiceBinding", + menuName = "DXCommandTerminal/Terminal Service Binding", + order = 1000 + )] + public sealed class TerminalServiceBindingAsset : ScriptableObject, ITerminalServiceLocator + { + [SerializeField] + [Tooltip( + "Optional override for the terminal registry. Leave empty to use the built-in TerminalRegistry." + )] + private UnityEngine.Object _terminalProviderOverride; + + [SerializeField] + [Tooltip( + "Optional override for runtime configurator (mode flags and autodiscovery). Leave empty for defaults." + )] + private UnityEngine.Object _runtimeConfiguratorOverride; + + [SerializeField] + [Tooltip( + "Optional override for input provider. Leave empty to use the default input source." + )] + private UnityEngine.Object _inputProviderOverride; + + [SerializeField] + [Tooltip( + "Optional override for runtime provider (active runtime accessor). Leave empty to use default proxy." + )] + private UnityEngine.Object _runtimeProviderOverride; + + [SerializeField] + [Tooltip( + "Optional override for runtime scope (registration/log helpers). Leave empty for default scope." + )] + private UnityEngine.Object _runtimeScopeOverride; + + [SerializeField] + [Tooltip( + "Optional override for runtime configurator service (mode evaluation helpers). Leave empty for default service." + )] + private UnityEngine.Object _runtimeConfiguratorServiceOverride; + + [NonSerialized] + private TerminalRuntimePool _runtimePoolInstance; + + public ITerminalProvider TerminalProvider => + Resolve(_terminalProviderOverride, TerminalRegistry.Default); + + public ITerminalRuntimeConfigurator RuntimeConfigurator => + Resolve(_runtimeConfiguratorOverride, TerminalRuntimeConfiguratorProxy.Default); + + public ITerminalInputProvider InputProvider => + Resolve(_inputProviderOverride, TerminalInputProviderProxy.Default); + + public ITerminalRuntimeProvider RuntimeProvider => + Resolve(_runtimeProviderOverride, TerminalRuntimeProviderProxy.Default); + + public ITerminalRuntimeScope RuntimeScope => + Resolve(_runtimeScopeOverride, TerminalRuntimeScope.Default); + + public ITerminalRuntimeConfiguratorService RuntimeConfiguratorService => + Resolve( + _runtimeConfiguratorServiceOverride, + TerminalRuntimeConfiguratorService.Default + ); + + public ITerminalRuntimePool RuntimePool => + _runtimePoolInstance ??= new TerminalRuntimePool(); + + internal void SetTerminalProviderForTests(UnityEngine.Object provider) + { + _terminalProviderOverride = provider; + } + + internal void SetRuntimeConfiguratorForTests(UnityEngine.Object configurator) + { + _runtimeConfiguratorOverride = configurator; + } + + internal void SetInputProviderForTests(UnityEngine.Object provider) + { + _inputProviderOverride = provider; + } + + internal void SetRuntimeProviderForTests(UnityEngine.Object provider) + { + _runtimeProviderOverride = provider; + } + + internal void SetRuntimeScopeForTests(UnityEngine.Object scope) + { + _runtimeScopeOverride = scope; + } + + internal void SetRuntimeConfiguratorServiceForTests(UnityEngine.Object service) + { + _runtimeConfiguratorServiceOverride = service; + } + + private static T Resolve(UnityEngine.Object candidate, T fallback) + where T : class + { + if (candidate == null) + { + return fallback; + } + + if (candidate is T typed) + { + return typed; + } + + return fallback; + } + +#if UNITY_EDITOR + private void OnValidate() + { + EnsureImplements(ref _terminalProviderOverride, typeof(ITerminalProvider)); + EnsureImplements( + ref _runtimeConfiguratorOverride, + typeof(ITerminalRuntimeConfigurator) + ); + EnsureImplements(ref _inputProviderOverride, typeof(ITerminalInputProvider)); + EnsureImplements(ref _runtimeProviderOverride, typeof(ITerminalRuntimeProvider)); + EnsureImplements(ref _runtimeScopeOverride, typeof(ITerminalRuntimeScope)); + EnsureImplements( + ref _runtimeConfiguratorServiceOverride, + typeof(ITerminalRuntimeConfiguratorService) + ); + } + + private void EnsureImplements(ref UnityEngine.Object field, Type expectedInterface) + { + if (field == null) + { + return; + } + + Type fieldType = field.GetType(); + if (!expectedInterface.IsAssignableFrom(fieldType)) + { + Debug.LogWarning( + $"Assigned object '{field.name}' does not implement {expectedInterface.Name} and will be ignored.", + this + ); + field = null; + } + } +#endif + } +} diff --git a/Runtime/CommandTerminal/Service/TerminalServiceBindingAsset.cs.meta b/Runtime/CommandTerminal/Service/TerminalServiceBindingAsset.cs.meta new file mode 100644 index 0000000..4350043 --- /dev/null +++ b/Runtime/CommandTerminal/Service/TerminalServiceBindingAsset.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 304d4afd5b0645789376992a27cce81d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: + diff --git a/Runtime/CommandTerminal/Service/TerminalServiceBindingComponent.cs b/Runtime/CommandTerminal/Service/TerminalServiceBindingComponent.cs new file mode 100644 index 0000000..4524d1b --- /dev/null +++ b/Runtime/CommandTerminal/Service/TerminalServiceBindingComponent.cs @@ -0,0 +1,166 @@ +namespace WallstopStudios.DxCommandTerminal.Service +{ + using System; + using Backend; + using UI; + using UnityEngine; + using WallstopStudios.DxCommandTerminal.Input; + + /// + /// Component-level provider that can supply service overrides to a . + /// + [DisallowMultipleComponent] + public sealed class TerminalServiceBindingComponent : MonoBehaviour, ITerminalServiceLocator + { + [SerializeField] + [Tooltip("Optional binding asset whose values will be used unless overridden locally.")] + private TerminalServiceBindingAsset _bindingAsset; + + [SerializeField] + [Tooltip( + "Override for the terminal provider. Leave empty to use the asset/default implementation." + )] + private UnityEngine.Object _terminalProviderOverride; + + [SerializeField] + [Tooltip("Override for the runtime configurator.")] + private UnityEngine.Object _runtimeConfiguratorOverride; + + [SerializeField] + [Tooltip("Override for the input provider.")] + private UnityEngine.Object _inputProviderOverride; + + [SerializeField] + [Tooltip("Override for the runtime provider (active runtime accessor).")] + private UnityEngine.Object _runtimeProviderOverride; + + [SerializeField] + [Tooltip("Override for runtime scope (registration/logging helpers).")] + private UnityEngine.Object _runtimeScopeOverride; + + [SerializeField] + [Tooltip("Override for runtime configurator service (mode helpers).")] + private UnityEngine.Object _runtimeConfiguratorServiceOverride; + + [SerializeField] + [Tooltip("Override for runtime pool (runtime reuse management).")] + private UnityEngine.Object _runtimePoolOverride; + + [NonSerialized] + private TerminalRuntimePool _localRuntimePool; + + public ITerminalProvider TerminalProvider => + Resolve( + _terminalProviderOverride, + _bindingAsset != null ? _bindingAsset.TerminalProvider : TerminalRegistry.Default + ); + + public ITerminalRuntimeConfigurator RuntimeConfigurator => + Resolve( + _runtimeConfiguratorOverride, + _bindingAsset != null + ? _bindingAsset.RuntimeConfigurator + : TerminalRuntimeConfiguratorProxy.Default + ); + + public ITerminalInputProvider InputProvider => + Resolve( + _inputProviderOverride, + _bindingAsset != null + ? _bindingAsset.InputProvider + : TerminalInputProviderProxy.Default + ); + + public ITerminalRuntimeProvider RuntimeProvider => + Resolve( + _runtimeProviderOverride, + _bindingAsset != null + ? _bindingAsset.RuntimeProvider + : TerminalRuntimeProviderProxy.Default + ); + + public ITerminalRuntimeScope RuntimeScope => + Resolve( + _runtimeScopeOverride, + _bindingAsset != null ? _bindingAsset.RuntimeScope : TerminalRuntimeScope.Default + ); + + public ITerminalRuntimeConfiguratorService RuntimeConfiguratorService => + Resolve( + _runtimeConfiguratorServiceOverride, + _bindingAsset != null + ? _bindingAsset.RuntimeConfiguratorService + : TerminalRuntimeConfiguratorService.Default + ); + + public ITerminalRuntimePool RuntimePool => + Resolve( + _runtimePoolOverride, + _bindingAsset != null + ? _bindingAsset.RuntimePool + : _localRuntimePool ?? (_localRuntimePool = new TerminalRuntimePool()) + ); + + internal void SetBindingAssetForTests(TerminalServiceBindingAsset asset) + { + _bindingAsset = asset; + } + + internal void SetRuntimePoolOverrideForTests(UnityEngine.Object pool) + { + _runtimePoolOverride = pool; + } + + private static T Resolve(UnityEngine.Object candidate, T fallback) + where T : class + { + if (candidate == null) + { + return fallback; + } + + if (candidate is T typed) + { + return typed; + } + + return fallback; + } + +#if UNITY_EDITOR + private void OnValidate() + { + EnsureImplements(ref _terminalProviderOverride, typeof(ITerminalProvider)); + EnsureImplements( + ref _runtimeConfiguratorOverride, + typeof(ITerminalRuntimeConfigurator) + ); + EnsureImplements(ref _inputProviderOverride, typeof(ITerminalInputProvider)); + EnsureImplements(ref _runtimeProviderOverride, typeof(ITerminalRuntimeProvider)); + EnsureImplements(ref _runtimeScopeOverride, typeof(ITerminalRuntimeScope)); + EnsureImplements( + ref _runtimeConfiguratorServiceOverride, + typeof(ITerminalRuntimeConfiguratorService) + ); + EnsureImplements(ref _runtimePoolOverride, typeof(ITerminalRuntimePool)); + } + + private void EnsureImplements(ref UnityEngine.Object field, System.Type expectedType) + { + if (field == null) + { + return; + } + + if (!expectedType.IsAssignableFrom(field.GetType())) + { + Debug.LogWarning( + $"Assigned object '{field.name}' does not implement {expectedType.Name} and will be ignored.", + this + ); + field = null; + } + } +#endif + } +} diff --git a/Runtime/CommandTerminal/Service/TerminalServiceBindingComponent.cs.meta b/Runtime/CommandTerminal/Service/TerminalServiceBindingComponent.cs.meta new file mode 100644 index 0000000..f58e1f6 --- /dev/null +++ b/Runtime/CommandTerminal/Service/TerminalServiceBindingComponent.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 0f5cf2e6182a4c0ab750a6b4413f7686 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: + diff --git a/Runtime/CommandTerminal/Service/TerminalServiceBindingSettings.cs b/Runtime/CommandTerminal/Service/TerminalServiceBindingSettings.cs new file mode 100644 index 0000000..802a95c --- /dev/null +++ b/Runtime/CommandTerminal/Service/TerminalServiceBindingSettings.cs @@ -0,0 +1,58 @@ +namespace WallstopStudios.DxCommandTerminal.Service +{ + using UnityEngine; + using WallstopStudios.DxCommandTerminal.Internal; + + /// + /// Global settings singleton that persists the default service binding asset. + /// + [ScriptableSingletonPath("Assets/Resources/TerminalServiceBindingSettings.asset")] + public sealed class TerminalServiceBindingSettings + : ScriptableObjectSingleton + { + [SerializeField] + [Tooltip( + "Default service binding asset used when a TerminalUI does not specify one explicitly." + )] + private TerminalServiceBindingAsset _defaultBinding; + + private static TerminalServiceBindingAsset _runtimeFallbackBinding; + + public static TerminalServiceBindingAsset DefaultBinding + { + get + { + if (Instance != null && Instance._defaultBinding != null) + { + return Instance._defaultBinding; + } + return _runtimeFallbackBinding; + } + } + + public TerminalServiceBindingAsset GetDefaultBinding() + { + return _defaultBinding; + } + + public void SetDefaultBinding(TerminalServiceBindingAsset binding) + { + _defaultBinding = binding; +#if UNITY_EDITOR + _runtimeFallbackBinding = null; +#endif +#if UNITY_EDITOR + UnityEditor.EditorUtility.SetDirty(this); +#endif + } + + internal static void SetDefaultBindingForTests(TerminalServiceBindingAsset binding) + { + if (Instance != null) + { + Instance._defaultBinding = binding; + } + _runtimeFallbackBinding = binding; + } + } +} diff --git a/Runtime/CommandTerminal/Service/TerminalServiceBindingSettings.cs.meta b/Runtime/CommandTerminal/Service/TerminalServiceBindingSettings.cs.meta new file mode 100644 index 0000000..558241d --- /dev/null +++ b/Runtime/CommandTerminal/Service/TerminalServiceBindingSettings.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 1e3bd0f9c3ff444a8bc0508886d7fd64 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: + diff --git a/Runtime/CommandTerminal/Themes/TerminalFontPack.cs b/Runtime/CommandTerminal/Themes/TerminalFontPack.cs index 2517c07..e1178d0 100644 --- a/Runtime/CommandTerminal/Themes/TerminalFontPack.cs +++ b/Runtime/CommandTerminal/Themes/TerminalFontPack.cs @@ -1,18 +1,18 @@ -namespace WallstopStudios.DxCommandTerminal.Themes -{ - using System.Collections.Generic; - using UnityEngine; - - [CreateAssetMenu( - menuName = "Wallstop Studios/DxCommandTerminal/Font Pack", - fileName = nameof(TerminalFontPack), - order = 1_111_123 - )] - public class TerminalFontPack : ScriptableObject - { - public virtual IReadOnlyList Fonts => _fonts; - - [SerializeField] - protected internal List _fonts = new(); - } -} +namespace WallstopStudios.DxCommandTerminal.Themes +{ + using System.Collections.Generic; + using UnityEngine; + + [CreateAssetMenu( + menuName = "Wallstop Studios/DxCommandTerminal/Font Pack", + fileName = nameof(TerminalFontPack), + order = 1_111_123 + )] + public class TerminalFontPack : ScriptableObject + { + public virtual IReadOnlyList Fonts => _fonts; + + [SerializeField] + protected internal List _fonts = new(); + } +} diff --git a/Runtime/CommandTerminal/Themes/TerminalThemePack.cs b/Runtime/CommandTerminal/Themes/TerminalThemePack.cs index a9356b9..3d9d4e0 100644 --- a/Runtime/CommandTerminal/Themes/TerminalThemePack.cs +++ b/Runtime/CommandTerminal/Themes/TerminalThemePack.cs @@ -1,23 +1,23 @@ -namespace WallstopStudios.DxCommandTerminal.Themes -{ - using System.Collections.Generic; - using UnityEngine; - using UnityEngine.UIElements; - - [CreateAssetMenu( - menuName = "Wallstop Studios/DxCommandTerminal/Theme Pack", - fileName = nameof(TerminalThemePack), - order = 1_111_123 - )] - public class TerminalThemePack : ScriptableObject - { - public virtual IReadOnlyList Themes => _themes; - public virtual IReadOnlyList ThemeNames => _themeNames; - - [SerializeField] - protected internal List _themes = new(); - - [SerializeField] - protected internal List _themeNames = new(); - } -} +namespace WallstopStudios.DxCommandTerminal.Themes +{ + using System.Collections.Generic; + using UnityEngine; + using UnityEngine.UIElements; + + [CreateAssetMenu( + menuName = "Wallstop Studios/DxCommandTerminal/Theme Pack", + fileName = nameof(TerminalThemePack), + order = 1_111_123 + )] + public class TerminalThemePack : ScriptableObject + { + public virtual IReadOnlyList Themes => _themes; + public virtual IReadOnlyList ThemeNames => _themeNames; + + [SerializeField] + protected internal List _themes = new(); + + [SerializeField] + protected internal List _themeNames = new(); + } +} diff --git a/Runtime/CommandTerminal/Themes/ThemeNameHelper.cs b/Runtime/CommandTerminal/Themes/ThemeNameHelper.cs index c65b4ef..81d00fa 100644 --- a/Runtime/CommandTerminal/Themes/ThemeNameHelper.cs +++ b/Runtime/CommandTerminal/Themes/ThemeNameHelper.cs @@ -1,42 +1,42 @@ -namespace WallstopStudios.DxCommandTerminal.Themes -{ - using System; - using System.Collections.Generic; - - public static class ThemeNameHelper - { - public static IEnumerable GetPossibleThemeNames(string theme) - { - theme = GetFriendlyThemeName(theme); - if (string.IsNullOrWhiteSpace(theme)) - { - yield break; - } - - yield return theme + "-theme"; - yield return "theme-" + theme; - } - - public static bool IsThemeName(string theme) - { - if (string.IsNullOrWhiteSpace(theme)) - { - return false; - } - - return theme.Contains("-theme", StringComparison.OrdinalIgnoreCase) - || theme.Contains("theme-", StringComparison.OrdinalIgnoreCase); - } - - public static string GetFriendlyThemeName(string theme) - { - if (string.IsNullOrWhiteSpace(theme)) - { - return theme; - } - return theme - .Replace("theme-", string.Empty, StringComparison.OrdinalIgnoreCase) - .Replace("-theme", string.Empty, StringComparison.OrdinalIgnoreCase); - } - } -} +namespace WallstopStudios.DxCommandTerminal.Themes +{ + using System; + using System.Collections.Generic; + + public static class ThemeNameHelper + { + public static IEnumerable GetPossibleThemeNames(string theme) + { + theme = GetFriendlyThemeName(theme); + if (string.IsNullOrWhiteSpace(theme)) + { + yield break; + } + + yield return theme + "-theme"; + yield return "theme-" + theme; + } + + public static bool IsThemeName(string theme) + { + if (string.IsNullOrWhiteSpace(theme)) + { + return false; + } + + return theme.Contains("-theme", StringComparison.OrdinalIgnoreCase) + || theme.Contains("theme-", StringComparison.OrdinalIgnoreCase); + } + + public static string GetFriendlyThemeName(string theme) + { + if (string.IsNullOrWhiteSpace(theme)) + { + return theme; + } + return theme + .Replace("theme-", string.Empty, StringComparison.OrdinalIgnoreCase) + .Replace("-theme", string.Empty, StringComparison.OrdinalIgnoreCase); + } + } +} diff --git a/Runtime/CommandTerminal/UI/ITerminalProvider.cs b/Runtime/CommandTerminal/UI/ITerminalProvider.cs new file mode 100644 index 0000000..35d7255 --- /dev/null +++ b/Runtime/CommandTerminal/UI/ITerminalProvider.cs @@ -0,0 +1,15 @@ +namespace WallstopStudios.DxCommandTerminal.UI +{ + using System.Collections.Generic; + + public interface ITerminalProvider + { + TerminalUI ActiveTerminal { get; } + + IReadOnlyList ActiveTerminals { get; } + + void Register(TerminalUI terminal); + + void Unregister(TerminalUI terminal); + } +} diff --git a/Runtime/CommandTerminal/UI/ITerminalProvider.cs.meta b/Runtime/CommandTerminal/UI/ITerminalProvider.cs.meta new file mode 100644 index 0000000..35d6e7d --- /dev/null +++ b/Runtime/CommandTerminal/UI/ITerminalProvider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: cc25949e9f624bcb8acc1a2d03f8a564 +timeCreated: 1760666509 diff --git a/Runtime/CommandTerminal/UI/ITerminalRuntimeConfigurator.cs b/Runtime/CommandTerminal/UI/ITerminalRuntimeConfigurator.cs new file mode 100644 index 0000000..ec0d97f --- /dev/null +++ b/Runtime/CommandTerminal/UI/ITerminalRuntimeConfigurator.cs @@ -0,0 +1,13 @@ +namespace WallstopStudios.DxCommandTerminal.UI +{ + using Backend; + + public interface ITerminalRuntimeConfigurator + { + void SetMode(TerminalRuntimeModeFlags modes); + + bool EditorAutoDiscover { get; set; } + + int TryAutoDiscoverParsers(); + } +} diff --git a/Runtime/CommandTerminal/UI/ITerminalRuntimeConfigurator.cs.meta b/Runtime/CommandTerminal/UI/ITerminalRuntimeConfigurator.cs.meta new file mode 100644 index 0000000..ce8b1b7 --- /dev/null +++ b/Runtime/CommandTerminal/UI/ITerminalRuntimeConfigurator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 38b8fade037a4f1f807a5e3b6ce9d17c +timeCreated: 1760671586 diff --git a/Runtime/CommandTerminal/UI/LayoutMeasurementUtility.cs b/Runtime/CommandTerminal/UI/LayoutMeasurementUtility.cs new file mode 100644 index 0000000..a57dadc --- /dev/null +++ b/Runtime/CommandTerminal/UI/LayoutMeasurementUtility.cs @@ -0,0 +1,138 @@ +namespace WallstopStudios.DxCommandTerminal.UI +{ + using UnityEngine; + using UnityEngine.UIElements; + + internal static class LayoutMeasurementUtility + { + internal static float ClampPositive(float value) + { + if (float.IsNaN(value) || value < 0f) + { + return 0f; + } + + return value; + } + + internal static float ClampToRange(float value, float min, float max) + { + if (float.IsNaN(value)) + { + return min; + } + + return Mathf.Clamp(value, min, max); + } + + internal static float ComputeAverageRowHeight( + float totalHeight, + int visibleCount, + float fallback + ) + { + if (visibleCount <= 0) + { + return fallback; + } + + float positiveHeight = ClampPositive(totalHeight); + if (positiveHeight <= 0f) + { + return fallback; + } + + float average = positiveHeight / visibleCount; + if (float.IsNaN(average) || average <= 0f) + { + return fallback; + } + + return average; + } + + internal static float ClampRowHeightEstimate( + float estimate, + float fallback, + float minimum, + float maximum + ) + { + float sanitized = ClampPositive(estimate); + if (sanitized <= 0f) + { + sanitized = fallback; + } + + return Mathf.Clamp(sanitized, minimum, maximum); + } + + internal static float ResolvePadding(float resolved, StyleLength styleValue) + { + float normalizedResolved = ClampPositive(resolved); + if (normalizedResolved > 0f) + { + return normalizedResolved; + } + + if (styleValue.keyword == StyleKeyword.Auto || styleValue.keyword == StyleKeyword.None) + { + return 0f; + } + + return ClampPositive(styleValue.value.value); + } + + internal static float ComputeReservedSuggestionHeight( + bool isLauncherActive, + bool hasSuggestions, + float suggestionsHeight, + float spacingAboveHistory, + float autoCompleteSpacing + ) + { + if (!hasSuggestions) + { + return 0f; + } + + if (isLauncherActive) + { + return suggestionsHeight + spacingAboveHistory; + } + + float standardSpacing = Mathf.Max(2f, autoCompleteSpacing * 0.25f); + return suggestionsHeight + standardSpacing; + } + + internal static float ComputeStandardContainerHeight( + float currentHeight, + float paddingTop, + float paddingBottom + ) + { + float innerHeight = ClampPositive(currentHeight - paddingTop - paddingBottom); + return paddingTop + innerHeight + paddingBottom; + } + + internal static float ClampToHistoryLimit(float value, float historyLimit) + { + return Mathf.Min(historyLimit, ClampPositive(value)); + } + + internal static float ComputeDesiredHistoryHeight( + bool hasHistory, + float fallbackHeight, + float historyLimit + ) + { + if (!hasHistory) + { + return 0f; + } + + float sanitized = ClampPositive(fallbackHeight); + return Mathf.Min(historyLimit, sanitized); + } + } +} diff --git a/Runtime/CommandTerminal/UI/LayoutMeasurementUtility.cs.meta b/Runtime/CommandTerminal/UI/LayoutMeasurementUtility.cs.meta new file mode 100644 index 0000000..230d978 --- /dev/null +++ b/Runtime/CommandTerminal/UI/LayoutMeasurementUtility.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8379c3793d5c4ff88c58fd0aa2c42138 +timeCreated: 1760665120 diff --git a/Runtime/CommandTerminal/UI/TerminalAppearanceProfile.cs b/Runtime/CommandTerminal/UI/TerminalAppearanceProfile.cs new file mode 100644 index 0000000..9705dfe --- /dev/null +++ b/Runtime/CommandTerminal/UI/TerminalAppearanceProfile.cs @@ -0,0 +1,37 @@ +namespace WallstopStudios.DxCommandTerminal.UI +{ + using Backend; + using UnityEngine; + + [CreateAssetMenu( + fileName = "TerminalAppearanceProfile", + menuName = "DXCommandTerminal/Terminal Appearance Profile", + order = 470 + )] + public sealed class TerminalAppearanceProfile : ScriptableObject + { + [Header("Buttons")] + public bool showGUIButtons = true; + public string runButtonText = "run"; + public string closeButtonText = "close"; + public string smallButtonText = "small"; + public string fullButtonText = "full"; + public string launcherButtonText = "launcher"; + + [Header("Hints")] + public HintDisplayMode hintDisplayMode = HintDisplayMode.AutoCompleteOnly; + public bool makeHintsClickable = true; + + [Header("History Fade")] + public TerminalHistoryFadeTargets historyFadeTargets = + TerminalHistoryFadeTargets.SmallTerminal + | TerminalHistoryFadeTargets.FullTerminal + | TerminalHistoryFadeTargets.Launcher; + + [Header("Cursor")] + public int cursorBlinkRateMilliseconds = 666; + + [Header("System")] + public bool logUnityMessages; + } +} diff --git a/Runtime/CommandTerminal/UI/TerminalAppearanceProfile.cs.meta b/Runtime/CommandTerminal/UI/TerminalAppearanceProfile.cs.meta new file mode 100644 index 0000000..5182482 --- /dev/null +++ b/Runtime/CommandTerminal/UI/TerminalAppearanceProfile.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ef93c568d72d487a95944de1bc8f8240 +timeCreated: 0 diff --git a/Runtime/CommandTerminal/UI/TerminalHistoryFadeTargets.cs b/Runtime/CommandTerminal/UI/TerminalHistoryFadeTargets.cs new file mode 100644 index 0000000..36833a5 --- /dev/null +++ b/Runtime/CommandTerminal/UI/TerminalHistoryFadeTargets.cs @@ -0,0 +1,31 @@ +namespace WallstopStudios.DxCommandTerminal.UI +{ + using System; + using System.Runtime.CompilerServices; + + [Flags] + public enum TerminalHistoryFadeTargets + { + None = 0, + SmallTerminal = 1 << 0, + FullTerminal = 1 << 1, + Launcher = 1 << 2, + } + + internal static class TerminalHistoryFadeTargetsExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool HasFlagNoAlloc( + this TerminalHistoryFadeTargets value, + TerminalHistoryFadeTargets flag + ) + { + if (flag == TerminalHistoryFadeTargets.None) + { + return value == TerminalHistoryFadeTargets.None; + } + + return (value & flag) == flag; + } + } +} diff --git a/Runtime/CommandTerminal/UI/TerminalHistoryFadeTargets.cs.meta b/Runtime/CommandTerminal/UI/TerminalHistoryFadeTargets.cs.meta new file mode 100644 index 0000000..d03dea9 --- /dev/null +++ b/Runtime/CommandTerminal/UI/TerminalHistoryFadeTargets.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c5f9d5a43f57480c8e1dc2f985ddc169 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/UI/TerminalLauncherSettings.cs b/Runtime/CommandTerminal/UI/TerminalLauncherSettings.cs new file mode 100644 index 0000000..c4aeaa4 --- /dev/null +++ b/Runtime/CommandTerminal/UI/TerminalLauncherSettings.cs @@ -0,0 +1,216 @@ +namespace WallstopStudios.DxCommandTerminal.UI +{ + using System; + using UnityEngine; + + public enum LauncherSizeMode + { + Pixels = 0, + RelativeToScreen = 1, + RelativeToLauncher = 2, + } + + [Serializable] + public struct LauncherDimension + { + public LauncherSizeMode mode; + + [Min(0f)] + public float value; + + public static LauncherDimension RelativeToScreen(float ratio) + { + return new LauncherDimension + { + mode = LauncherSizeMode.RelativeToScreen, + value = ratio, + }; + } + + public static LauncherDimension RelativeToLauncher(float ratio) + { + return new LauncherDimension + { + mode = LauncherSizeMode.RelativeToLauncher, + value = ratio, + }; + } + + public static LauncherDimension Pixels(float pixels) + { + return new LauncherDimension { mode = LauncherSizeMode.Pixels, value = pixels }; + } + + public float ResolvePixels(int screenLength, float launcherLength = 0f) + { + switch (mode) + { + case LauncherSizeMode.Pixels: + return Mathf.Max(0f, value); + case LauncherSizeMode.RelativeToScreen: + return Mathf.Clamp01(value) * screenLength; + case LauncherSizeMode.RelativeToLauncher: + return Mathf.Clamp01(value) * Mathf.Max(launcherLength, 0f); + default: + return 0f; + } + } + } + + [Serializable] + public sealed class TerminalLauncherSettings + { + private const float MinimumPadding = 0f; + private const float MinimumCorner = 0f; + + [Header("Dimensions")] + public LauncherDimension width = LauncherDimension.RelativeToScreen(0.55f); + + public LauncherDimension height = LauncherDimension.RelativeToScreen(0.33f); + + public LauncherDimension historyHeight = LauncherDimension.RelativeToLauncher(0.45f); + + [Min(220f)] + public float minimumWidth = 380f; + + [Min(72f)] + public float minimumHeight = 110f; + + [Header("Position")] + [Range(0f, 1f)] + public float verticalAnchor = 0.5f; + + [Range(0f, 1f)] + public float horizontalAnchor = 0.5f; + + [Min(MinimumPadding)] + public float screenPadding = 32f; + + [Header("Visuals")] + [Min(MinimumCorner)] + public float cornerRadius = 16f; + + [Min(MinimumPadding)] + public float insetPadding = 14f; + + [Header("History")] + [Min(1)] + public int historyVisibleEntryCount = 6; + + [Range(0.1f, 8f)] + public float historyFadeExponent = 2.3f; + + [Tooltip("Pixels reserved for the input row and autocomplete when sizing history.")] + [Min(48f)] + public float inputReservePixels = 96f; + + [Header("Behaviour")] + public bool snapOpen = true; + + [Min(0f)] + public float animationDuration = 0.14f; + + public LauncherLayoutMetrics ComputeMetrics(int screenWidth, int screenHeight) + { + float safeWidth = Mathf.Max(minimumWidth, width.ResolvePixels(screenWidth)); + float safeHeight = Mathf.Max(minimumHeight, height.ResolvePixels(screenHeight)); + + float horizontalPadding = Mathf.Max(screenPadding, MinimumPadding); + float verticalPadding = Mathf.Max(screenPadding, MinimumPadding); + + float maxWidth = Mathf.Max(minimumWidth, screenWidth - (horizontalPadding * 2f)); + float maxHeight = Mathf.Max(minimumHeight, screenHeight - (verticalPadding * 2f)); + + safeWidth = Mathf.Min(safeWidth, maxWidth); + safeHeight = Mathf.Min(safeHeight, maxHeight); + + float left = Mathf.Clamp( + (screenWidth * horizontalAnchor) - (safeWidth * 0.5f), + horizontalPadding, + Mathf.Max(horizontalPadding, screenWidth - safeWidth - horizontalPadding) + ); + float top = Mathf.Clamp( + (screenHeight * verticalAnchor) - (safeHeight * 0.5f), + verticalPadding, + Mathf.Max(verticalPadding, screenHeight - safeHeight - verticalPadding) + ); + + float maxHistoryHeight = Mathf.Max(0f, safeHeight - inputReservePixels); + float historyPixels = Mathf.Min( + maxHistoryHeight, + historyHeight.ResolvePixels(screenHeight, safeHeight) + ); + + if (historyPixels < 0f) + { + historyPixels = 0f; + } + + return new LauncherLayoutMetrics( + width: safeWidth, + height: safeHeight, + left: left, + top: top, + historyHeight: historyPixels, + cornerRadius: Mathf.Max(cornerRadius, MinimumCorner), + insetPadding: Mathf.Max(insetPadding, MinimumPadding), + historyVisibleEntryCount: Mathf.Max(0, historyVisibleEntryCount), + historyFadeExponent: historyFadeExponent, + snapOpen: snapOpen, + animationDuration: Mathf.Max(0f, animationDuration) + ); + } + } + + public readonly struct LauncherLayoutMetrics + { + public LauncherLayoutMetrics( + float width, + float height, + float left, + float top, + float historyHeight, + float cornerRadius, + float insetPadding, + int historyVisibleEntryCount, + float historyFadeExponent, + bool snapOpen, + float animationDuration + ) + { + Width = width; + Height = height; + Left = left; + Top = top; + HistoryHeight = historyHeight; + CornerRadius = cornerRadius; + InsetPadding = insetPadding; + HistoryVisibleEntryCount = historyVisibleEntryCount; + HistoryFadeExponent = historyFadeExponent; + SnapOpen = snapOpen; + AnimationDuration = animationDuration; + } + + public float Width { get; } + + public float Height { get; } + + public float Left { get; } + + public float Top { get; } + + public float HistoryHeight { get; } + + public float CornerRadius { get; } + + public float InsetPadding { get; } + + public int HistoryVisibleEntryCount { get; } + + public float HistoryFadeExponent { get; } + + public bool SnapOpen { get; } + + public float AnimationDuration { get; } + } +} diff --git a/Runtime/CommandTerminal/UI/TerminalLauncherSettings.cs.meta b/Runtime/CommandTerminal/UI/TerminalLauncherSettings.cs.meta new file mode 100644 index 0000000..0c937bb --- /dev/null +++ b/Runtime/CommandTerminal/UI/TerminalLauncherSettings.cs.meta @@ -0,0 +1,4 @@ +fileFormatVersion: 2 +guid: 2c0e72455786478e8e54b7c74035102a +timeCreated: 1752787200 + diff --git a/Runtime/CommandTerminal/UI/TerminalRegistry.cs b/Runtime/CommandTerminal/UI/TerminalRegistry.cs new file mode 100644 index 0000000..4ebc667 --- /dev/null +++ b/Runtime/CommandTerminal/UI/TerminalRegistry.cs @@ -0,0 +1,42 @@ +namespace WallstopStudios.DxCommandTerminal.UI +{ + using System.Collections.Generic; + + internal sealed class TerminalRegistry : ITerminalProvider + { + private readonly List _terminals = new(); + + internal static ITerminalProvider Default { get; } = new TerminalRegistry(); + + public TerminalUI ActiveTerminal + { + get + { + int count = _terminals.Count; + return count > 0 ? _terminals[count - 1] : null; + } + } + + public IReadOnlyList ActiveTerminals => _terminals; + + public void Register(TerminalUI terminal) + { + if (terminal == null || _terminals.Contains(terminal)) + { + return; + } + + _terminals.Add(terminal); + } + + public void Unregister(TerminalUI terminal) + { + if (terminal == null) + { + return; + } + + _terminals.Remove(terminal); + } + } +} diff --git a/Runtime/CommandTerminal/UI/TerminalRegistry.cs.meta b/Runtime/CommandTerminal/UI/TerminalRegistry.cs.meta new file mode 100644 index 0000000..2346b33 --- /dev/null +++ b/Runtime/CommandTerminal/UI/TerminalRegistry.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 798afa4b136547fb8c67426f55ecafff +timeCreated: 1760666509 diff --git a/Runtime/CommandTerminal/UI/TerminalRuntimeConfiguratorProxy.cs b/Runtime/CommandTerminal/UI/TerminalRuntimeConfiguratorProxy.cs new file mode 100644 index 0000000..bc12eb8 --- /dev/null +++ b/Runtime/CommandTerminal/UI/TerminalRuntimeConfiguratorProxy.cs @@ -0,0 +1,28 @@ +namespace WallstopStudios.DxCommandTerminal.UI +{ + using Backend; + + internal sealed class TerminalRuntimeConfiguratorProxy : ITerminalRuntimeConfigurator + { + internal static ITerminalRuntimeConfigurator Default { get; } = + new TerminalRuntimeConfiguratorProxy(); + + private TerminalRuntimeConfiguratorProxy() { } + + public void SetMode(TerminalRuntimeModeFlags modes) + { + TerminalRuntimeConfig.SetMode(modes); + } + + public bool EditorAutoDiscover + { + get => TerminalRuntimeConfig.EditorAutoDiscover; + set => TerminalRuntimeConfig.EditorAutoDiscover = value; + } + + public int TryAutoDiscoverParsers() + { + return TerminalRuntimeConfig.TryAutoDiscoverParsers(); + } + } +} diff --git a/Runtime/CommandTerminal/UI/TerminalRuntimeConfiguratorProxy.cs.meta b/Runtime/CommandTerminal/UI/TerminalRuntimeConfiguratorProxy.cs.meta new file mode 100644 index 0000000..d397f6d --- /dev/null +++ b/Runtime/CommandTerminal/UI/TerminalRuntimeConfiguratorProxy.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 632ea2d55e594b199f4d2833d236951b +timeCreated: 1760671586 diff --git a/Runtime/CommandTerminal/UI/TerminalState.cs b/Runtime/CommandTerminal/UI/TerminalState.cs index 5c0aea3..cc5657a 100644 --- a/Runtime/CommandTerminal/UI/TerminalState.cs +++ b/Runtime/CommandTerminal/UI/TerminalState.cs @@ -1,13 +1,14 @@ -namespace WallstopStudios.DxCommandTerminal.UI -{ - using System; - - public enum TerminalState - { - [Obsolete("Use a valid value")] - Unknown = 0, - Closed = 1, - OpenSmall = 2, - OpenFull = 3, - } -} +namespace WallstopStudios.DxCommandTerminal.UI +{ + using System; + + public enum TerminalState + { + [Obsolete("Use a valid value")] + Unknown = 0, + Closed = 1, + OpenSmall = 2, + OpenFull = 3, + OpenLauncher = 4, + } +} diff --git a/Runtime/CommandTerminal/UI/TerminalUI.AutoCompleteView.cs b/Runtime/CommandTerminal/UI/TerminalUI.AutoCompleteView.cs new file mode 100644 index 0000000..c5f470a --- /dev/null +++ b/Runtime/CommandTerminal/UI/TerminalUI.AutoCompleteView.cs @@ -0,0 +1,391 @@ +namespace WallstopStudios.DxCommandTerminal.UI +{ + using Backend; + using UnityEngine; + using UnityEngine.UIElements; + + public sealed partial class TerminalUI + { + private void RefreshAutoCompleteHints() + { + bool shouldDisplay = + 0 < _lastCompletionBuffer.Count + && hintDisplayMode is HintDisplayMode.Always or HintDisplayMode.AutoCompleteOnly + && _autoCompleteContainer != null; + + if (!shouldDisplay) + { + if (_autoCompleteContainer != null) + { + _autoCompleteContainer.style.display = DisplayStyle.None; + _autoCompleteContainer.style.height = 0f; + _autoCompleteContainer.style.maxHeight = 0f; + if (0 < _autoCompleteContainer.childCount) + { + _autoCompleteContainer.Clear(); + } + + if (_autoCompleteViewport != null) + { + _autoCompleteViewport.style.height = 0f; + } + } + + _previousLastCompletionIndex = null; + return; + } + + _autoCompleteContainer.style.display = DisplayStyle.Flex; + _autoCompleteContainer.style.height = new StyleLength(StyleKeyword.Null); + _autoCompleteContainer.style.maxHeight = new StyleLength(StyleKeyword.Null); + if (_autoCompleteViewport != null) + { + _autoCompleteViewport.style.height = new StyleLength(StyleKeyword.Null); + } + + int bufferLength = _lastCompletionBuffer.Count; + if (_lastKnownHintsClickable != makeHintsClickable) + { + _autoCompleteContainer.Clear(); + _lastKnownHintsClickable = makeHintsClickable; + } + + int currentChildCount = _autoCompleteContainer.childCount; + + bool dirty = _lastCompletionIndex != _previousLastCompletionIndex; + bool contentsChanged = currentChildCount != bufferLength; + if (contentsChanged) + { + dirty = true; + if (currentChildCount < bufferLength) + { + for (int i = currentChildCount; i < bufferLength; ++i) + { + string hint = _lastCompletionBuffer[i]; + VisualElement hintElement; + + if (makeHintsClickable) + { + int currentIndex = i; + string currentHint = hint; + Button hintButton = new(() => + { + _input.CommandText = BuildCompletionText(currentHint); + _lastCompletionIndex = currentIndex; + _needsFocus = true; + }) + { + text = hint, + }; + hintElement = hintButton; + } + else + { + Label hintText = new(hint); + hintElement = hintText; + } + + hintElement.name = $"SuggestionText{i}"; + _autoCompleteContainer.Add(hintElement); + + bool isSelected = i == _lastCompletionIndex; + hintElement.AddToClassList("terminal-button"); + hintElement.EnableInClassList("autocomplete-item-selected", isSelected); + hintElement.EnableInClassList("autocomplete-item", !isSelected); + } + } + else if (bufferLength < currentChildCount) + { + for (int i = currentChildCount - 1; bufferLength <= i; --i) + { + _autoCompleteContainer.RemoveAt(i); + } + } + } + + bool shouldUpdateCompletionIndex = false; + try + { + shouldUpdateCompletionIndex = _autoCompleteContainer.childCount == bufferLength; + if (shouldUpdateCompletionIndex) + { + UpdateAutoCompleteView(); + } + + if (dirty) + { + for (int i = 0; i < _autoCompleteContainer.childCount && i < bufferLength; ++i) + { + VisualElement hintElement = _autoCompleteContainer[i]; + switch (hintElement) + { + case Button button: + button.text = _lastCompletionBuffer[i]; + break; + case Label label: + label.text = _lastCompletionBuffer[i]; + break; + case TextField textField: + textField.value = _lastCompletionBuffer[i]; + break; + } + + bool isSelected = i == _lastCompletionIndex; + + hintElement.EnableInClassList("autocomplete-item-selected", isSelected); + hintElement.EnableInClassList("autocomplete-item", !isSelected); + } + } + } + finally + { + if (shouldUpdateCompletionIndex) + { + _previousLastCompletionIndex = _lastCompletionIndex; + } + } + } + + private void UpdateAutoCompleteView() + { + if (_autoCompleteContainer == null) + { + return; + } + + int suggestionCount = _autoCompleteContainer.childCount; + if (suggestionCount == 0) + { + _lastCompletionIndex = null; + return; + } + + if (_lastCompletionIndex == null) + { + return; + } + + int clampedIndex = Mathf.Clamp(_lastCompletionIndex.Value, 0, suggestionCount - 1); + if (clampedIndex != _lastCompletionIndex.Value) + { + _lastCompletionIndex = clampedIndex; + } + + VisualElement selectedElement = _autoCompleteContainer[clampedIndex]; + if (selectedElement == null) + { + return; + } + + _autoCompleteContainer.ScrollTo(selectedElement); + } + + private void ResetAutoComplete() + { + _lastKnownCommandText = _input.CommandText ?? string.Empty; + _lastCompletionAnchorText = null; + _lastCompletionAnchorCaretIndex = null; + CommandAutoComplete autoComplete = ActiveAutoComplete; + if (autoComplete == null) + { + _lastCompletionIndex = null; + _previousLastCompletionIndex = null; + _lastCompletionBuffer.Clear(); + _lastCompletionBufferTempCache.Clear(); + _lastCompletionBufferTempSet.Clear(); + return; + } + + if (hintDisplayMode == HintDisplayMode.Always) + { + _lastCompletionBufferTempCache.Clear(); + int caret = + _commandInput != null + ? _commandInput.cursorIndex + : (_lastKnownCommandText?.Length ?? 0); + autoComplete.Complete(_lastKnownCommandText, caret, _lastCompletionBufferTempCache); + bool equivalent = + _lastCompletionBufferTempCache.Count == _lastCompletionBuffer.Count; + if (equivalent) + { + _lastCompletionBufferTempSet.Clear(); + foreach (string completion in _lastCompletionBuffer) + { + _lastCompletionBufferTempSet.Add(completion); + } + + foreach (string completion in _lastCompletionBufferTempCache) + { + if (!_lastCompletionBufferTempSet.Contains(completion)) + { + equivalent = false; + break; + } + } + } + + if (!equivalent) + { + _lastCompletionIndex = null; + _previousLastCompletionIndex = null; + _lastCompletionBuffer.Clear(); + foreach (string completion in _lastCompletionBufferTempCache) + { + _lastCompletionBuffer.Add(completion); + } + } + } + else + { + _lastCompletionIndex = null; + _previousLastCompletionIndex = null; + _lastCompletionBuffer.Clear(); + } + } + + private string BuildCompletionText(string suggestion) + { + if (string.IsNullOrEmpty(suggestion)) + { + return suggestion ?? string.Empty; + } + + CommandAutoComplete autoComplete = ActiveAutoComplete; + if (autoComplete == null || !autoComplete.LastCompletionUsedCompleter) + { + return suggestion; + } + + string prefix = autoComplete.LastCompleterPrefix ?? string.Empty; + return string.Concat(prefix, suggestion); + } + + public void CompleteCommand(bool searchForward = true) + { + if (_state == TerminalState.Closed) + { + return; + } + + try + { + CommandAutoComplete autoComplete = ActiveAutoComplete; + if (autoComplete == null) + { + return; + } + + _lastKnownCommandText = _input.CommandText ?? string.Empty; + _lastCompletionBufferTempCache.Clear(); + int caret = + _commandInput != null + ? _commandInput.cursorIndex + : (_lastKnownCommandText?.Length ?? 0); + + string completionSource = _lastCompletionAnchorText ?? _lastKnownCommandText; + int completionCaret = _lastCompletionAnchorCaretIndex ?? caret; + + autoComplete.Complete( + completionSource, + completionCaret, + _lastCompletionBufferTempCache + ); + + bool equivalentBuffers = true; + try + { + int completionLength = _lastCompletionBufferTempCache.Count; + equivalentBuffers = _lastCompletionBuffer.Count == completionLength; + if (equivalentBuffers) + { + _lastCompletionBufferTempSet.Clear(); + foreach (string item in _lastCompletionBuffer) + { + _lastCompletionBufferTempSet.Add(item); + } + + foreach (string newCompletionItem in _lastCompletionBufferTempCache) + { + if (!_lastCompletionBufferTempSet.Contains(newCompletionItem)) + { + equivalentBuffers = false; + break; + } + } + } + + if (equivalentBuffers) + { + if (completionLength > 0) + { + if (_lastCompletionIndex == null) + { + _lastCompletionIndex = 0; + } + else if (searchForward) + { + _lastCompletionIndex = + (_lastCompletionIndex + 1) % completionLength; + } + else + { + _lastCompletionIndex = + (_lastCompletionIndex - 1 + completionLength) + % completionLength; + } + + string selection = _lastCompletionBuffer[_lastCompletionIndex.Value]; + _input.CommandText = BuildCompletionText(selection); + if (_lastCompletionAnchorText == null) + { + _lastCompletionAnchorText = completionSource; + _lastCompletionAnchorCaretIndex = completionCaret; + } + } + else + { + _lastCompletionIndex = null; + _lastCompletionAnchorText = null; + _lastCompletionAnchorCaretIndex = null; + } + } + else + { + if (completionLength > 0) + { + _lastCompletionIndex = 0; + string selection = _lastCompletionBufferTempCache[0]; + _input.CommandText = BuildCompletionText(selection); + _lastCompletionAnchorText = completionSource; + _lastCompletionAnchorCaretIndex = completionCaret; + } + else + { + _lastCompletionIndex = null; + _lastCompletionAnchorText = null; + _lastCompletionAnchorCaretIndex = null; + } + } + } + finally + { + if (!equivalentBuffers) + { + _lastCompletionBuffer.Clear(); + foreach (string item in _lastCompletionBufferTempCache) + { + _lastCompletionBuffer.Add(item); + } + _previousLastCompletionIndex = null; + } + + _previousLastCompletionIndex ??= _lastCompletionIndex; + } + } + finally + { + _needsFocus = true; + } + } + } +} diff --git a/Runtime/CommandTerminal/UI/TerminalUI.AutoCompleteView.cs.meta b/Runtime/CommandTerminal/UI/TerminalUI.AutoCompleteView.cs.meta new file mode 100644 index 0000000..277a464 --- /dev/null +++ b/Runtime/CommandTerminal/UI/TerminalUI.AutoCompleteView.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a3e1da940123d02be83ec6262bb05f50 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/CommandTerminal/UI/TerminalUI.LauncherViewController.cs b/Runtime/CommandTerminal/UI/TerminalUI.LauncherViewController.cs new file mode 100644 index 0000000..8ac7b14 --- /dev/null +++ b/Runtime/CommandTerminal/UI/TerminalUI.LauncherViewController.cs @@ -0,0 +1,266 @@ +namespace WallstopStudios.DxCommandTerminal.UI +{ + using UnityEngine; + using UnityEngine.UIElements; + + public sealed partial class TerminalUI + { + private sealed class LauncherViewController + { + private readonly TerminalUI _owner; + private const float FadeMomentumWindowSeconds = 0.35f; + private IVisualElementScheduledItem _fadeMomentumSchedule; + private float _fadeMomentumExpiry; + + internal LauncherViewController(TerminalUI owner) + { + _owner = owner; + } + + internal void ConfigureForStandardMode() + { + _owner.SetHistoryJustification(Justify.FlexEnd); + ClearFade(); + } + + internal void ConfigureForLauncherMode() + { + _owner.SetHistoryJustification(Justify.FlexStart); + } + + internal void ClampScroll() + { + ScrollView scrollView = _owner._logScrollView; + if (scrollView == null) + { + return; + } + + Scroller scroller = scrollView.verticalScroller; + if (scroller == null) + { + return; + } + + float clamped = Mathf.Clamp(scroller.value, scroller.lowValue, scroller.highValue); + if (!Mathf.Approximately(clamped, scroller.value)) + { + scroller.value = clamped; + } + } + + internal void UpdateFade() + { + if ( + !_owner.TryGetLauncherFadeContext( + out ScrollView scrollView, + out VisualElement viewport, + out VisualElement historyContent + ) + ) + { + ClearFade(); + return; + } + + Rect viewportWorld = viewport.worldBound; + float viewportHeight = viewportWorld.height; + if (viewportHeight <= 0.01f) + { + return; + } + + Scroller scroller = scrollView.verticalScroller; + float scrollerHigh = scroller != null ? scroller.highValue : 0f; + _owner.LogFadeDiagnostic( + $"UpdateFade viewportHeight={viewportHeight:F3} childCount={historyContent.childCount} scrollerHigh={scrollerHigh:F3}" + ); + _owner.LogLauncherDiagnostic( + $"UpdateFade viewport={viewportHeight:F3} scrollerHigh={scrollerHigh:F3} childCount={historyContent.childCount}" + ); + + float viewportTop = viewportWorld.yMin; + float viewportBottom = viewportWorld.yMax; + + int childCount = historyContent.childCount; + for (int i = 0; i < childCount; ++i) + { + VisualElement entry = historyContent[i]; + if (entry == null || entry.resolvedStyle.display == DisplayStyle.None) + { + continue; + } + + Rect entryBounds = entry.worldBound; + float entryHeight = entryBounds.height; + if (entryHeight <= 0.01f) + { + ApplyOpacity(entry, 1f); + continue; + } + + float overlapHeight = + Mathf.Min(entryBounds.yMax, viewportBottom) + - Mathf.Max(entryBounds.yMin, viewportTop); + if (overlapHeight <= 0f) + { + ApplyOpacity(entry, 1f); + continue; + } + + float entryCenter = (entryBounds.yMin + entryBounds.yMax) * 0.5f; + float clampedCenter = Mathf.Clamp(entryCenter, viewportTop, viewportBottom); + float normalized = Mathf.Clamp01( + (clampedCenter - viewportTop) / viewportHeight + ); + + float opacity = ComputeOpacity(normalized); + + ApplyOpacity(entry, opacity); + } + } + + internal float ComputeOpacityForTests(float normalized) + { + return ComputeOpacity(normalized); + } + + private float ComputeOpacity(float normalized) + { + float rangeFactor = Mathf.Clamp01(_owner.GetHistoryFadeRangeFactor()); + float exponent = Mathf.Max(0.01f, _owner.GetHistoryFadeExponent()); + float minimumOpacity = Mathf.Clamp01(_owner.GetHistoryFadeMinimumOpacity()); + + float adjusted = Mathf.Pow(normalized * rangeFactor, exponent); + return Mathf.Lerp(1f, minimumOpacity, adjusted); + } + + internal void ScheduleFade() + { + ScrollView scrollView = _owner._logScrollView; + if (scrollView == null) + { + return; + } + + _owner.LogLauncherDiagnostic("ScheduleFade requested"); + scrollView.schedule.Execute(UpdateFade).ExecuteLater(0); + StartFadeMomentumMonitor(scrollView); + } + + internal void HandleScrollValueChanged(float value) + { + if ( + !_owner.TryGetLauncherFadeContext( + out ScrollView scrollView, + out VisualElement viewport, + out VisualElement historyContent + ) + ) + { + return; + } + + _owner.LogFadeDiagnostic( + $"HandleScrollValueChanged value={value:F3} viewportHeight={viewport.resolvedStyle.height:F3} contentChildren={historyContent.childCount}" + ); + Scroller scroller = scrollView.verticalScroller; + float scrollerHigh = scroller != null ? scroller.highValue : 0f; + _owner.LogLauncherDiagnostic( + $"HandleScrollValueChanged value={value:F3} scrollerHigh={scrollerHigh:F3}" + ); + + ClampScroll(); + UpdateFade(); + ScheduleFade(); + } + + internal void ClearFade() + { + StopFadeMomentumMonitor(); + + ScrollView scrollView = _owner._logScrollView; + if (scrollView == null) + { + return; + } + + VisualElement historyContent = scrollView.contentContainer; + if (historyContent == null) + { + return; + } + + int childCount = historyContent.childCount; + for (int i = 0; i < childCount; ++i) + { + VisualElement entry = historyContent[i]; + ApplyOpacity(entry, 1f); + } + } + + private void StartFadeMomentumMonitor(ScrollView scrollView) + { + if (scrollView == null) + { + return; + } + + _fadeMomentumExpiry = Time.unscaledTime + FadeMomentumWindowSeconds; + + if (_fadeMomentumSchedule != null) + { + return; + } + + _owner.LogFadeDiagnostic("Starting fade momentum monitor"); + _fadeMomentumSchedule = scrollView.schedule.Execute(FadeMomentumTick).Every(16); + } + + private void StopFadeMomentumMonitor() + { + if (_fadeMomentumSchedule == null) + { + return; + } + + _owner.LogFadeDiagnostic("Stopping fade momentum monitor"); + _fadeMomentumSchedule.Pause(); + _fadeMomentumSchedule = null; + } + + private void FadeMomentumTick() + { + if (_fadeMomentumSchedule == null) + { + return; + } + + if (Time.unscaledTime >= _fadeMomentumExpiry || !_owner.IsLauncherActive) + { + StopFadeMomentumMonitor(); + return; + } + + _owner.LogFadeDiagnostic("Fade momentum tick"); + UpdateFade(); + } + + private static void ApplyOpacity(VisualElement entry, float opacity) + { + if (entry == null) + { + return; + } + + entry.style.opacity = opacity; + + Label label = entry as Label ?? entry.Q