├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ └── build.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src ├── main ├── java │ └── com │ │ └── therandomlabs │ │ └── changeloggenerator │ │ ├── BasicChangelogGenerator.java │ │ ├── ChangelogEntries.java │ │ ├── ChangelogEntry.java │ │ ├── ChangelogGenerator.java │ │ ├── ChangelogGeneratorOptions.java │ │ ├── CommonMarkUtils.java │ │ ├── Main.java │ │ ├── MarkdownChangelogGenerator.java │ │ ├── package-info.java │ │ └── provider │ │ ├── ActuallyAdditionsProvider.java │ │ ├── BiomesOPlentyProvider.java │ │ ├── ChangelogProvider.java │ │ ├── CurseChangelogProvider.java │ │ ├── McJtyProvider.java │ │ ├── MezzProvider.java │ │ └── package-info.java └── resources │ └── log4j2.xml └── test ├── java └── com │ └── therandomlabs │ └── changeloggenerator │ └── ChangelogGeneratorTest.java └── resources └── log4j2-test.xml /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset = utf-8 3 | indent_size = 4 4 | indent_style = tab 5 | insert_final_newline = true 6 | max_line_length = 100 7 | tab_width = 4 8 | trim_trailing_whitespace = true 9 | ij_continuation_indent_size = 8 10 | ij_formatter_off_tag = @formatter:off 11 | ij_formatter_on_tag = @formatter:on 12 | ij_formatter_tags_enabled = false 13 | ij_smart_tabs = false 14 | ij_visual_guides = 100 15 | ij_wrap_on_typing = false 16 | 17 | [*.css] 18 | ij_css_align_closing_brace_with_properties = false 19 | ij_css_blank_lines_around_nested_selector = 1 20 | ij_css_blank_lines_between_blocks = 1 21 | ij_css_brace_placement = end_of_line 22 | ij_css_enforce_quotes_on_format = false 23 | ij_css_hex_color_long_format = true 24 | ij_css_hex_color_lower_case = false 25 | ij_css_hex_color_short_format = false 26 | ij_css_hex_color_upper_case = true 27 | ij_css_keep_blank_lines_in_code = 2 28 | ij_css_keep_indents_on_empty_lines = false 29 | ij_css_keep_single_line_blocks = false 30 | ij_css_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow 31 | ij_css_space_after_colon = true 32 | ij_css_space_before_opening_brace = true 33 | ij_css_use_double_quotes = true 34 | ij_css_value_alignment = do_not_align 35 | 36 | [*.feature] 37 | ij_gherkin_keep_indents_on_empty_lines = false 38 | 39 | [*.gsp] 40 | ij_gsp_keep_indents_on_empty_lines = false 41 | 42 | [*.haml] 43 | ij_haml_keep_indents_on_empty_lines = false 44 | 45 | [*.java] 46 | ij_java_align_consecutive_assignments = false 47 | ij_java_align_consecutive_variable_declarations = false 48 | ij_java_align_group_field_declarations = false 49 | ij_java_align_multiline_annotation_parameters = false 50 | ij_java_align_multiline_array_initializer_expression = false 51 | ij_java_align_multiline_assignment = false 52 | ij_java_align_multiline_binary_operation = false 53 | ij_java_align_multiline_chained_methods = false 54 | ij_java_align_multiline_extends_list = false 55 | ij_java_align_multiline_for = true 56 | ij_java_align_multiline_method_parentheses = false 57 | ij_java_align_multiline_parameters = false 58 | ij_java_align_multiline_parameters_in_calls = false 59 | ij_java_align_multiline_parenthesized_expression = false 60 | ij_java_align_multiline_records = true 61 | ij_java_align_multiline_resources = true 62 | ij_java_align_multiline_ternary_operation = false 63 | ij_java_align_multiline_text_blocks = false 64 | ij_java_align_multiline_throws_list = false 65 | ij_java_align_subsequent_simple_methods = false 66 | ij_java_align_throws_keyword = false 67 | ij_java_annotation_parameter_wrap = normal 68 | ij_java_array_initializer_new_line_after_left_brace = true 69 | ij_java_array_initializer_right_brace_on_new_line = true 70 | ij_java_array_initializer_wrap = normal 71 | ij_java_assert_statement_colon_on_next_line = false 72 | ij_java_assert_statement_wrap = normal 73 | ij_java_assignment_wrap = normal 74 | ij_java_binary_operation_sign_on_next_line = false 75 | ij_java_binary_operation_wrap = normal 76 | ij_java_blank_lines_after_anonymous_class_header = 0 77 | ij_java_blank_lines_after_class_header = 0 78 | ij_java_blank_lines_after_imports = 1 79 | ij_java_blank_lines_after_package = 1 80 | ij_java_blank_lines_around_class = 1 81 | ij_java_blank_lines_around_field = 0 82 | ij_java_blank_lines_around_field_in_interface = 0 83 | ij_java_blank_lines_around_initializer = 1 84 | ij_java_blank_lines_around_method = 1 85 | ij_java_blank_lines_around_method_in_interface = 1 86 | ij_java_blank_lines_before_class_end = 0 87 | ij_java_blank_lines_before_imports = 1 88 | ij_java_blank_lines_before_method_body = 0 89 | ij_java_blank_lines_before_package = 0 90 | ij_java_block_brace_style = end_of_line 91 | ij_java_block_comment_at_first_column = true 92 | ij_java_call_parameters_new_line_after_left_paren = true 93 | ij_java_call_parameters_right_paren_on_new_line = true 94 | ij_java_call_parameters_wrap = normal 95 | ij_java_case_statement_on_separate_line = true 96 | ij_java_catch_on_new_line = false 97 | ij_java_class_annotation_wrap = split_into_lines 98 | ij_java_class_brace_style = end_of_line 99 | ij_java_class_count_to_use_import_on_demand = 999 100 | ij_java_class_names_in_javadoc = 1 101 | ij_java_do_not_indent_top_level_class_members = false 102 | ij_java_do_not_wrap_after_single_annotation = false 103 | ij_java_do_while_brace_force = always 104 | ij_java_doc_add_blank_line_after_description = true 105 | ij_java_doc_add_blank_line_after_param_comments = false 106 | ij_java_doc_add_blank_line_after_return = false 107 | ij_java_doc_add_p_tag_on_empty_lines = true 108 | ij_java_doc_align_exception_comments = false 109 | ij_java_doc_align_param_comments = false 110 | ij_java_doc_do_not_wrap_if_one_line = false 111 | ij_java_doc_enable_formatting = true 112 | ij_java_doc_enable_leading_asterisks = true 113 | ij_java_doc_indent_on_continuation = false 114 | ij_java_doc_keep_empty_lines = true 115 | ij_java_doc_keep_empty_parameter_tag = true 116 | ij_java_doc_keep_empty_return_tag = true 117 | ij_java_doc_keep_empty_throws_tag = true 118 | ij_java_doc_keep_invalid_tags = true 119 | ij_java_doc_param_description_on_new_line = false 120 | ij_java_doc_preserve_line_breaks = false 121 | ij_java_doc_use_throws_not_exception_tag = true 122 | ij_java_else_on_new_line = false 123 | ij_java_entity_dd_suffix = EJB 124 | ij_java_entity_eb_suffix = Bean 125 | ij_java_entity_hi_suffix = Home 126 | ij_java_entity_lhi_prefix = Local 127 | ij_java_entity_lhi_suffix = Home 128 | ij_java_entity_li_prefix = Local 129 | ij_java_entity_pk_class = java.lang.String 130 | ij_java_entity_vo_suffix = VO 131 | ij_java_enum_constants_wrap = split_into_lines 132 | ij_java_extends_keyword_wrap = off 133 | ij_java_extends_list_wrap = normal 134 | ij_java_field_annotation_wrap = split_into_lines 135 | ij_java_finally_on_new_line = false 136 | ij_java_for_brace_force = always 137 | ij_java_for_statement_new_line_after_left_paren = true 138 | ij_java_for_statement_right_paren_on_new_line = true 139 | ij_java_for_statement_wrap = normal 140 | ij_java_generate_final_locals = false 141 | ij_java_generate_final_parameters = false 142 | ij_java_if_brace_force = always 143 | ij_java_imports_layout = $*,|,java.**,javax.**,|,* 144 | ij_java_indent_case_from_switch = false 145 | ij_java_insert_inner_class_imports = false 146 | ij_java_insert_override_annotation = true 147 | ij_java_keep_blank_lines_before_right_brace = 0 148 | ij_java_keep_blank_lines_between_package_declaration_and_header = 1 149 | ij_java_keep_blank_lines_in_code = 1 150 | ij_java_keep_blank_lines_in_declarations = 1 151 | ij_java_keep_control_statement_in_one_line = false 152 | ij_java_keep_first_column_comment = false 153 | ij_java_keep_indents_on_empty_lines = false 154 | ij_java_keep_line_breaks = true 155 | ij_java_keep_multiple_expressions_in_one_line = false 156 | ij_java_keep_simple_blocks_in_one_line = true 157 | ij_java_keep_simple_classes_in_one_line = true 158 | ij_java_keep_simple_lambdas_in_one_line = true 159 | ij_java_keep_simple_methods_in_one_line = true 160 | ij_java_label_indent_absolute = false 161 | ij_java_label_indent_size = 0 162 | ij_java_lambda_brace_style = end_of_line 163 | ij_java_layout_static_imports_separately = true 164 | ij_java_line_comment_add_space = false 165 | ij_java_line_comment_at_first_column = true 166 | ij_java_message_dd_suffix = EJB 167 | ij_java_message_eb_suffix = Bean 168 | ij_java_method_annotation_wrap = split_into_lines 169 | ij_java_method_brace_style = end_of_line 170 | ij_java_method_call_chain_wrap = off 171 | ij_java_method_parameters_new_line_after_left_paren = true 172 | ij_java_method_parameters_right_paren_on_new_line = true 173 | ij_java_method_parameters_wrap = normal 174 | ij_java_modifier_list_wrap = false 175 | ij_java_names_count_to_use_import_on_demand = 999 176 | ij_java_new_line_after_lparen_in_record_header = false 177 | ij_java_parameter_annotation_wrap = normal 178 | ij_java_parentheses_expression_new_line_after_left_paren = false 179 | ij_java_parentheses_expression_right_paren_on_new_line = false 180 | ij_java_place_assignment_sign_on_next_line = false 181 | ij_java_prefer_longer_names = true 182 | ij_java_prefer_parameters_wrap = false 183 | ij_java_record_components_wrap = normal 184 | ij_java_repeat_synchronized = true 185 | ij_java_replace_instanceof_and_cast = false 186 | ij_java_replace_null_check = true 187 | ij_java_replace_sum_lambda_with_method_ref = true 188 | ij_java_resource_list_new_line_after_left_paren = true 189 | ij_java_resource_list_right_paren_on_new_line = true 190 | ij_java_resource_list_wrap = off 191 | ij_java_rparen_on_new_line_in_record_header = false 192 | ij_java_session_dd_suffix = EJB 193 | ij_java_session_eb_suffix = Bean 194 | ij_java_session_hi_suffix = Home 195 | ij_java_session_lhi_prefix = Local 196 | ij_java_session_lhi_suffix = Home 197 | ij_java_session_li_prefix = Local 198 | ij_java_session_si_suffix = Service 199 | ij_java_space_after_closing_angle_bracket_in_type_argument = false 200 | ij_java_space_after_colon = true 201 | ij_java_space_after_comma = true 202 | ij_java_space_after_comma_in_type_arguments = true 203 | ij_java_space_after_for_semicolon = true 204 | ij_java_space_after_quest = true 205 | ij_java_space_after_type_cast = true 206 | ij_java_space_before_annotation_array_initializer_left_brace = false 207 | ij_java_space_before_annotation_parameter_list = false 208 | ij_java_space_before_array_initializer_left_brace = true 209 | ij_java_space_before_catch_keyword = true 210 | ij_java_space_before_catch_left_brace = true 211 | ij_java_space_before_catch_parentheses = true 212 | ij_java_space_before_class_left_brace = true 213 | ij_java_space_before_colon = true 214 | ij_java_space_before_colon_in_foreach = true 215 | ij_java_space_before_comma = false 216 | ij_java_space_before_do_left_brace = true 217 | ij_java_space_before_else_keyword = true 218 | ij_java_space_before_else_left_brace = true 219 | ij_java_space_before_finally_keyword = true 220 | ij_java_space_before_finally_left_brace = true 221 | ij_java_space_before_for_left_brace = true 222 | ij_java_space_before_for_parentheses = true 223 | ij_java_space_before_for_semicolon = false 224 | ij_java_space_before_if_left_brace = true 225 | ij_java_space_before_if_parentheses = true 226 | ij_java_space_before_method_call_parentheses = false 227 | ij_java_space_before_method_left_brace = true 228 | ij_java_space_before_method_parentheses = false 229 | ij_java_space_before_opening_angle_bracket_in_type_parameter = false 230 | ij_java_space_before_quest = true 231 | ij_java_space_before_switch_left_brace = true 232 | ij_java_space_before_switch_parentheses = true 233 | ij_java_space_before_synchronized_left_brace = true 234 | ij_java_space_before_synchronized_parentheses = true 235 | ij_java_space_before_try_left_brace = true 236 | ij_java_space_before_try_parentheses = true 237 | ij_java_space_before_type_parameter_list = false 238 | ij_java_space_before_while_keyword = true 239 | ij_java_space_before_while_left_brace = true 240 | ij_java_space_before_while_parentheses = true 241 | ij_java_space_inside_one_line_enum_braces = false 242 | ij_java_space_within_empty_array_initializer_braces = false 243 | ij_java_space_within_empty_method_call_parentheses = false 244 | ij_java_space_within_empty_method_parentheses = false 245 | ij_java_spaces_around_additive_operators = true 246 | ij_java_spaces_around_assignment_operators = true 247 | ij_java_spaces_around_bitwise_operators = true 248 | ij_java_spaces_around_equality_operators = true 249 | ij_java_spaces_around_lambda_arrow = true 250 | ij_java_spaces_around_logical_operators = true 251 | ij_java_spaces_around_method_ref_dbl_colon = false 252 | ij_java_spaces_around_multiplicative_operators = true 253 | ij_java_spaces_around_relational_operators = true 254 | ij_java_spaces_around_shift_operators = true 255 | ij_java_spaces_around_type_bounds_in_type_parameters = true 256 | ij_java_spaces_around_unary_operator = false 257 | ij_java_spaces_within_angle_brackets = false 258 | ij_java_spaces_within_annotation_parentheses = false 259 | ij_java_spaces_within_array_initializer_braces = false 260 | ij_java_spaces_within_braces = false 261 | ij_java_spaces_within_brackets = false 262 | ij_java_spaces_within_cast_parentheses = false 263 | ij_java_spaces_within_catch_parentheses = false 264 | ij_java_spaces_within_for_parentheses = false 265 | ij_java_spaces_within_if_parentheses = false 266 | ij_java_spaces_within_method_call_parentheses = false 267 | ij_java_spaces_within_method_parentheses = false 268 | ij_java_spaces_within_parentheses = false 269 | ij_java_spaces_within_record_header = false 270 | ij_java_spaces_within_switch_parentheses = false 271 | ij_java_spaces_within_synchronized_parentheses = false 272 | ij_java_spaces_within_try_parentheses = false 273 | ij_java_spaces_within_while_parentheses = false 274 | ij_java_special_else_if_treatment = true 275 | ij_java_subclass_name_suffix = Impl 276 | ij_java_ternary_operation_signs_on_next_line = false 277 | ij_java_ternary_operation_wrap = normal 278 | ij_java_test_name_suffix = Test 279 | ij_java_throws_keyword_wrap = normal 280 | ij_java_throws_list_wrap = off 281 | ij_java_use_external_annotations = false 282 | ij_java_use_fq_class_names = false 283 | ij_java_use_relative_indents = false 284 | ij_java_use_single_class_imports = true 285 | ij_java_variable_annotation_wrap = split_into_lines 286 | ij_java_visibility = public 287 | ij_java_while_brace_force = always 288 | ij_java_while_on_new_line = false 289 | ij_java_wrap_comments = false 290 | ij_java_wrap_first_method_in_call_chain = false 291 | ij_java_wrap_long_lines = false 292 | 293 | [*.less] 294 | ij_less_align_closing_brace_with_properties = false 295 | ij_less_blank_lines_around_nested_selector = 1 296 | ij_less_blank_lines_between_blocks = 1 297 | ij_less_brace_placement = 0 298 | ij_less_enforce_quotes_on_format = false 299 | ij_less_hex_color_long_format = false 300 | ij_less_hex_color_lower_case = false 301 | ij_less_hex_color_short_format = false 302 | ij_less_hex_color_upper_case = false 303 | ij_less_keep_blank_lines_in_code = 2 304 | ij_less_keep_indents_on_empty_lines = false 305 | ij_less_keep_single_line_blocks = false 306 | ij_less_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow 307 | ij_less_space_after_colon = true 308 | ij_less_space_before_opening_brace = true 309 | ij_less_use_double_quotes = true 310 | ij_less_value_alignment = 0 311 | 312 | [*.nbtt] 313 | ij_continuation_indent_size = 4 314 | ij_nbtt_keep_indents_on_empty_lines = false 315 | ij_nbtt_space_after_colon = true 316 | ij_nbtt_space_after_comma = true 317 | ij_nbtt_space_before_colon = true 318 | ij_nbtt_space_before_comma = false 319 | ij_nbtt_spaces_within_brackets = false 320 | ij_nbtt_spaces_within_parentheses = false 321 | 322 | [*.sass] 323 | ij_sass_align_closing_brace_with_properties = false 324 | ij_sass_blank_lines_around_nested_selector = 1 325 | ij_sass_blank_lines_between_blocks = 1 326 | ij_sass_brace_placement = 0 327 | ij_sass_enforce_quotes_on_format = false 328 | ij_sass_hex_color_long_format = false 329 | ij_sass_hex_color_lower_case = false 330 | ij_sass_hex_color_short_format = false 331 | ij_sass_hex_color_upper_case = false 332 | ij_sass_keep_blank_lines_in_code = 2 333 | ij_sass_keep_indents_on_empty_lines = false 334 | ij_sass_keep_single_line_blocks = false 335 | ij_sass_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow 336 | ij_sass_space_after_colon = true 337 | ij_sass_space_before_opening_brace = true 338 | ij_sass_use_double_quotes = true 339 | ij_sass_value_alignment = 0 340 | 341 | [*.scss] 342 | ij_scss_align_closing_brace_with_properties = false 343 | ij_scss_blank_lines_around_nested_selector = 1 344 | ij_scss_blank_lines_between_blocks = 1 345 | ij_scss_brace_placement = 0 346 | ij_scss_enforce_quotes_on_format = false 347 | ij_scss_hex_color_long_format = false 348 | ij_scss_hex_color_lower_case = false 349 | ij_scss_hex_color_short_format = false 350 | ij_scss_hex_color_upper_case = false 351 | ij_scss_keep_blank_lines_in_code = 2 352 | ij_scss_keep_indents_on_empty_lines = false 353 | ij_scss_keep_single_line_blocks = false 354 | ij_scss_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow 355 | ij_scss_space_after_colon = true 356 | ij_scss_space_before_opening_brace = true 357 | ij_scss_use_double_quotes = true 358 | ij_scss_value_alignment = 0 359 | 360 | [*.styl] 361 | ij_stylus_align_closing_brace_with_properties = false 362 | ij_stylus_blank_lines_around_nested_selector = 1 363 | ij_stylus_blank_lines_between_blocks = 1 364 | ij_stylus_brace_placement = 0 365 | ij_stylus_enforce_quotes_on_format = false 366 | ij_stylus_hex_color_long_format = false 367 | ij_stylus_hex_color_lower_case = false 368 | ij_stylus_hex_color_short_format = false 369 | ij_stylus_hex_color_upper_case = false 370 | ij_stylus_keep_blank_lines_in_code = 2 371 | ij_stylus_keep_indents_on_empty_lines = false 372 | ij_stylus_keep_single_line_blocks = false 373 | ij_stylus_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow 374 | ij_stylus_space_after_colon = true 375 | ij_stylus_space_before_opening_brace = true 376 | ij_stylus_use_double_quotes = true 377 | ij_stylus_value_alignment = 0 378 | 379 | [.editorconfig] 380 | ij_editorconfig_align_group_field_declarations = false 381 | ij_editorconfig_space_after_colon = false 382 | ij_editorconfig_space_after_comma = true 383 | ij_editorconfig_space_before_colon = false 384 | ij_editorconfig_space_before_comma = false 385 | ij_editorconfig_spaces_around_assignment_operators = true 386 | 387 | [{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.pom,*.rng,*.tld,*.wadl,*.wsdd,*.wsdl,*.xjb,*.xml,*.xsd,*.xsl,*.xslt,*.xul}] 388 | ij_xml_align_attributes = true 389 | ij_xml_align_text = false 390 | ij_xml_attribute_wrap = normal 391 | ij_xml_block_comment_at_first_column = true 392 | ij_xml_keep_blank_lines = 2 393 | ij_xml_keep_indents_on_empty_lines = false 394 | ij_xml_keep_line_breaks = true 395 | ij_xml_keep_line_breaks_in_text = true 396 | ij_xml_keep_whitespaces = false 397 | ij_xml_keep_whitespaces_around_cdata = preserve 398 | ij_xml_keep_whitespaces_inside_cdata = false 399 | ij_xml_line_comment_at_first_column = true 400 | ij_xml_space_after_tag_name = false 401 | ij_xml_space_around_equals_in_attribute = false 402 | ij_xml_space_inside_empty_tag = false 403 | ij_xml_text_wrap = normal 404 | ij_xml_use_custom_settings = false 405 | 406 | [{*.ats,*.ts}] 407 | ij_typescript_align_imports = false 408 | ij_typescript_align_multiline_array_initializer_expression = false 409 | ij_typescript_align_multiline_binary_operation = false 410 | ij_typescript_align_multiline_chained_methods = false 411 | ij_typescript_align_multiline_extends_list = false 412 | ij_typescript_align_multiline_for = true 413 | ij_typescript_align_multiline_parameters = false 414 | ij_typescript_align_multiline_parameters_in_calls = false 415 | ij_typescript_align_multiline_ternary_operation = false 416 | ij_typescript_align_object_properties = 0 417 | ij_typescript_align_union_types = false 418 | ij_typescript_align_var_statements = 0 419 | ij_typescript_array_initializer_new_line_after_left_brace = true 420 | ij_typescript_array_initializer_right_brace_on_new_line = true 421 | ij_typescript_array_initializer_wrap = normal 422 | ij_typescript_assignment_wrap = normal 423 | ij_typescript_binary_operation_sign_on_next_line = false 424 | ij_typescript_binary_operation_wrap = normal 425 | ij_typescript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/** 426 | ij_typescript_blank_lines_after_imports = 1 427 | ij_typescript_blank_lines_around_class = 1 428 | ij_typescript_blank_lines_around_field = 0 429 | ij_typescript_blank_lines_around_field_in_interface = 0 430 | ij_typescript_blank_lines_around_function = 1 431 | ij_typescript_blank_lines_around_method = 1 432 | ij_typescript_blank_lines_around_method_in_interface = 1 433 | ij_typescript_block_brace_style = end_of_line 434 | ij_typescript_call_parameters_new_line_after_left_paren = true 435 | ij_typescript_call_parameters_right_paren_on_new_line = true 436 | ij_typescript_call_parameters_wrap = normal 437 | ij_typescript_catch_on_new_line = false 438 | ij_typescript_chained_call_dot_on_new_line = true 439 | ij_typescript_class_brace_style = end_of_line 440 | ij_typescript_comma_on_new_line = false 441 | ij_typescript_do_while_brace_force = always 442 | ij_typescript_else_on_new_line = false 443 | ij_typescript_enforce_trailing_comma = keep 444 | ij_typescript_extends_keyword_wrap = off 445 | ij_typescript_extends_list_wrap = normal 446 | ij_typescript_field_prefix = _ 447 | ij_typescript_file_name_style = relaxed 448 | ij_typescript_finally_on_new_line = false 449 | ij_typescript_for_brace_force = always 450 | ij_typescript_for_statement_new_line_after_left_paren = true 451 | ij_typescript_for_statement_right_paren_on_new_line = true 452 | ij_typescript_for_statement_wrap = normal 453 | ij_typescript_force_quote_style = false 454 | ij_typescript_force_semicolon_style = false 455 | ij_typescript_function_expression_brace_style = end_of_line 456 | ij_typescript_if_brace_force = always 457 | ij_typescript_import_merge_members = global 458 | ij_typescript_import_prefer_absolute_path = global 459 | ij_typescript_import_sort_members = true 460 | ij_typescript_import_sort_module_name = false 461 | ij_typescript_import_use_node_resolution = true 462 | ij_typescript_imports_wrap = on_every_item 463 | ij_typescript_indent_case_from_switch = false 464 | ij_typescript_indent_chained_calls = true 465 | ij_typescript_indent_package_children = 0 466 | ij_typescript_jsdoc_include_types = false 467 | ij_typescript_jsx_attribute_value = braces 468 | ij_typescript_keep_blank_lines_in_code = 2 469 | ij_typescript_keep_first_column_comment = false 470 | ij_typescript_keep_indents_on_empty_lines = false 471 | ij_typescript_keep_line_breaks = true 472 | ij_typescript_keep_simple_blocks_in_one_line = true 473 | ij_typescript_keep_simple_methods_in_one_line = true 474 | ij_typescript_line_comment_add_space = true 475 | ij_typescript_line_comment_at_first_column = false 476 | ij_typescript_method_brace_style = end_of_line 477 | ij_typescript_method_call_chain_wrap = off 478 | ij_typescript_method_parameters_new_line_after_left_paren = true 479 | ij_typescript_method_parameters_right_paren_on_new_line = true 480 | ij_typescript_method_parameters_wrap = normal 481 | ij_typescript_object_literal_wrap = on_every_item 482 | ij_typescript_parentheses_expression_new_line_after_left_paren = false 483 | ij_typescript_parentheses_expression_right_paren_on_new_line = false 484 | ij_typescript_place_assignment_sign_on_next_line = false 485 | ij_typescript_prefer_as_type_cast = false 486 | ij_typescript_prefer_explicit_types_function_expression_returns = false 487 | ij_typescript_prefer_explicit_types_function_returns = false 488 | ij_typescript_prefer_explicit_types_vars_fields = false 489 | ij_typescript_prefer_parameters_wrap = false 490 | ij_typescript_reformat_c_style_comments = false 491 | ij_typescript_space_after_colon = true 492 | ij_typescript_space_after_comma = true 493 | ij_typescript_space_after_dots_in_rest_parameter = false 494 | ij_typescript_space_after_generator_mult = true 495 | ij_typescript_space_after_property_colon = true 496 | ij_typescript_space_after_quest = true 497 | ij_typescript_space_after_type_colon = true 498 | ij_typescript_space_after_unary_not = false 499 | ij_typescript_space_before_async_arrow_lparen = true 500 | ij_typescript_space_before_catch_keyword = true 501 | ij_typescript_space_before_catch_left_brace = true 502 | ij_typescript_space_before_catch_parentheses = true 503 | ij_typescript_space_before_class_lbrace = true 504 | ij_typescript_space_before_class_left_brace = true 505 | ij_typescript_space_before_colon = true 506 | ij_typescript_space_before_comma = false 507 | ij_typescript_space_before_do_left_brace = true 508 | ij_typescript_space_before_else_keyword = true 509 | ij_typescript_space_before_else_left_brace = true 510 | ij_typescript_space_before_finally_keyword = true 511 | ij_typescript_space_before_finally_left_brace = true 512 | ij_typescript_space_before_for_left_brace = true 513 | ij_typescript_space_before_for_parentheses = true 514 | ij_typescript_space_before_for_semicolon = false 515 | ij_typescript_space_before_function_left_parenth = true 516 | ij_typescript_space_before_generator_mult = false 517 | ij_typescript_space_before_if_left_brace = true 518 | ij_typescript_space_before_if_parentheses = true 519 | ij_typescript_space_before_method_call_parentheses = false 520 | ij_typescript_space_before_method_left_brace = true 521 | ij_typescript_space_before_method_parentheses = false 522 | ij_typescript_space_before_property_colon = false 523 | ij_typescript_space_before_quest = true 524 | ij_typescript_space_before_switch_left_brace = true 525 | ij_typescript_space_before_switch_parentheses = true 526 | ij_typescript_space_before_try_left_brace = true 527 | ij_typescript_space_before_type_colon = false 528 | ij_typescript_space_before_unary_not = false 529 | ij_typescript_space_before_while_keyword = true 530 | ij_typescript_space_before_while_left_brace = true 531 | ij_typescript_space_before_while_parentheses = true 532 | ij_typescript_spaces_around_additive_operators = true 533 | ij_typescript_spaces_around_arrow_function_operator = true 534 | ij_typescript_spaces_around_assignment_operators = true 535 | ij_typescript_spaces_around_bitwise_operators = true 536 | ij_typescript_spaces_around_equality_operators = true 537 | ij_typescript_spaces_around_logical_operators = true 538 | ij_typescript_spaces_around_multiplicative_operators = true 539 | ij_typescript_spaces_around_relational_operators = true 540 | ij_typescript_spaces_around_shift_operators = true 541 | ij_typescript_spaces_around_unary_operator = false 542 | ij_typescript_spaces_within_array_initializer_brackets = false 543 | ij_typescript_spaces_within_brackets = false 544 | ij_typescript_spaces_within_catch_parentheses = false 545 | ij_typescript_spaces_within_for_parentheses = false 546 | ij_typescript_spaces_within_if_parentheses = false 547 | ij_typescript_spaces_within_imports = false 548 | ij_typescript_spaces_within_interpolation_expressions = false 549 | ij_typescript_spaces_within_method_call_parentheses = false 550 | ij_typescript_spaces_within_method_parentheses = false 551 | ij_typescript_spaces_within_object_literal_braces = false 552 | ij_typescript_spaces_within_object_type_braces = true 553 | ij_typescript_spaces_within_parentheses = false 554 | ij_typescript_spaces_within_switch_parentheses = false 555 | ij_typescript_spaces_within_type_assertion = false 556 | ij_typescript_spaces_within_union_types = true 557 | ij_typescript_spaces_within_while_parentheses = false 558 | ij_typescript_special_else_if_treatment = true 559 | ij_typescript_ternary_operation_signs_on_next_line = false 560 | ij_typescript_ternary_operation_wrap = normal 561 | ij_typescript_union_types_wrap = on_every_item 562 | ij_typescript_use_chained_calls_group_indents = false 563 | ij_typescript_use_double_quotes = true 564 | ij_typescript_use_explicit_js_extension = global 565 | ij_typescript_use_path_mapping = always 566 | ij_typescript_use_public_modifier = false 567 | ij_typescript_use_semicolon_after_statement = true 568 | ij_typescript_var_declaration_wrap = normal 569 | ij_typescript_while_brace_force = always 570 | ij_typescript_while_on_new_line = false 571 | ij_typescript_wrap_comments = false 572 | 573 | [{*.bash,*.sh,*.zsh}] 574 | ij_shell_binary_ops_start_line = false 575 | ij_shell_keep_column_alignment_padding = false 576 | ij_shell_minify_program = false 577 | ij_shell_redirect_followed_by_space = false 578 | ij_shell_switch_cases_indented = false 579 | 580 | [{*.cjs,*.js}] 581 | ij_continuation_indent_size = 4 582 | ij_javascript_align_imports = false 583 | ij_javascript_align_multiline_array_initializer_expression = false 584 | ij_javascript_align_multiline_binary_operation = false 585 | ij_javascript_align_multiline_chained_methods = false 586 | ij_javascript_align_multiline_extends_list = false 587 | ij_javascript_align_multiline_for = true 588 | ij_javascript_align_multiline_parameters = true 589 | ij_javascript_align_multiline_parameters_in_calls = false 590 | ij_javascript_align_multiline_ternary_operation = false 591 | ij_javascript_align_object_properties = 0 592 | ij_javascript_align_union_types = false 593 | ij_javascript_align_var_statements = 0 594 | ij_javascript_array_initializer_new_line_after_left_brace = true 595 | ij_javascript_array_initializer_right_brace_on_new_line = true 596 | ij_javascript_array_initializer_wrap = normal 597 | ij_javascript_assignment_wrap = normal 598 | ij_javascript_binary_operation_sign_on_next_line = false 599 | ij_javascript_binary_operation_wrap = normal 600 | ij_javascript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/** 601 | ij_javascript_blank_lines_after_imports = 1 602 | ij_javascript_blank_lines_around_class = 1 603 | ij_javascript_blank_lines_around_field = 0 604 | ij_javascript_blank_lines_around_function = 1 605 | ij_javascript_blank_lines_around_method = 1 606 | ij_javascript_block_brace_style = end_of_line 607 | ij_javascript_call_parameters_new_line_after_left_paren = true 608 | ij_javascript_call_parameters_right_paren_on_new_line = true 609 | ij_javascript_call_parameters_wrap = normal 610 | ij_javascript_catch_on_new_line = false 611 | ij_javascript_chained_call_dot_on_new_line = false 612 | ij_javascript_class_brace_style = end_of_line 613 | ij_javascript_comma_on_new_line = false 614 | ij_javascript_do_while_brace_force = always 615 | ij_javascript_else_on_new_line = false 616 | ij_javascript_enforce_trailing_comma = remove 617 | ij_javascript_extends_keyword_wrap = normal 618 | ij_javascript_extends_list_wrap = normal 619 | ij_javascript_field_prefix = _ 620 | ij_javascript_file_name_style = relaxed 621 | ij_javascript_finally_on_new_line = false 622 | ij_javascript_for_brace_force = always 623 | ij_javascript_for_statement_new_line_after_left_paren = true 624 | ij_javascript_for_statement_right_paren_on_new_line = true 625 | ij_javascript_for_statement_wrap = normal 626 | ij_javascript_force_quote_style = true 627 | ij_javascript_force_semicolon_style = true 628 | ij_javascript_function_expression_brace_style = end_of_line 629 | ij_javascript_if_brace_force = always 630 | ij_javascript_import_merge_members = global 631 | ij_javascript_import_prefer_absolute_path = global 632 | ij_javascript_import_sort_members = true 633 | ij_javascript_import_sort_module_name = false 634 | ij_javascript_import_use_node_resolution = true 635 | ij_javascript_imports_wrap = on_every_item 636 | ij_javascript_indent_case_from_switch = false 637 | ij_javascript_indent_chained_calls = true 638 | ij_javascript_indent_package_children = 0 639 | ij_javascript_jsx_attribute_value = braces 640 | ij_javascript_keep_blank_lines_in_code = 1 641 | ij_javascript_keep_first_column_comment = true 642 | ij_javascript_keep_indents_on_empty_lines = false 643 | ij_javascript_keep_line_breaks = true 644 | ij_javascript_keep_simple_blocks_in_one_line = false 645 | ij_javascript_keep_simple_methods_in_one_line = false 646 | ij_javascript_line_comment_add_space = true 647 | ij_javascript_line_comment_at_first_column = false 648 | ij_javascript_method_brace_style = end_of_line 649 | ij_javascript_method_call_chain_wrap = normal 650 | ij_javascript_method_parameters_new_line_after_left_paren = true 651 | ij_javascript_method_parameters_right_paren_on_new_line = true 652 | ij_javascript_method_parameters_wrap = normal 653 | ij_javascript_object_literal_wrap = on_every_item 654 | ij_javascript_parentheses_expression_new_line_after_left_paren = false 655 | ij_javascript_parentheses_expression_right_paren_on_new_line = false 656 | ij_javascript_place_assignment_sign_on_next_line = false 657 | ij_javascript_prefer_as_type_cast = false 658 | ij_javascript_prefer_explicit_types_function_expression_returns = false 659 | ij_javascript_prefer_explicit_types_function_returns = false 660 | ij_javascript_prefer_explicit_types_vars_fields = false 661 | ij_javascript_prefer_parameters_wrap = false 662 | ij_javascript_reformat_c_style_comments = false 663 | ij_javascript_space_after_colon = true 664 | ij_javascript_space_after_comma = true 665 | ij_javascript_space_after_dots_in_rest_parameter = false 666 | ij_javascript_space_after_generator_mult = true 667 | ij_javascript_space_after_property_colon = true 668 | ij_javascript_space_after_quest = true 669 | ij_javascript_space_after_type_colon = true 670 | ij_javascript_space_after_unary_not = false 671 | ij_javascript_space_before_async_arrow_lparen = false 672 | ij_javascript_space_before_catch_keyword = true 673 | ij_javascript_space_before_catch_left_brace = true 674 | ij_javascript_space_before_catch_parentheses = true 675 | ij_javascript_space_before_class_lbrace = true 676 | ij_javascript_space_before_class_left_brace = true 677 | ij_javascript_space_before_colon = true 678 | ij_javascript_space_before_comma = false 679 | ij_javascript_space_before_do_left_brace = true 680 | ij_javascript_space_before_else_keyword = true 681 | ij_javascript_space_before_else_left_brace = true 682 | ij_javascript_space_before_finally_keyword = true 683 | ij_javascript_space_before_finally_left_brace = true 684 | ij_javascript_space_before_for_left_brace = true 685 | ij_javascript_space_before_for_parentheses = true 686 | ij_javascript_space_before_for_semicolon = false 687 | ij_javascript_space_before_function_left_parenth = false 688 | ij_javascript_space_before_generator_mult = false 689 | ij_javascript_space_before_if_left_brace = true 690 | ij_javascript_space_before_if_parentheses = true 691 | ij_javascript_space_before_method_call_parentheses = false 692 | ij_javascript_space_before_method_left_brace = true 693 | ij_javascript_space_before_method_parentheses = false 694 | ij_javascript_space_before_property_colon = false 695 | ij_javascript_space_before_quest = true 696 | ij_javascript_space_before_switch_left_brace = true 697 | ij_javascript_space_before_switch_parentheses = true 698 | ij_javascript_space_before_try_left_brace = true 699 | ij_javascript_space_before_type_colon = false 700 | ij_javascript_space_before_unary_not = false 701 | ij_javascript_space_before_while_keyword = true 702 | ij_javascript_space_before_while_left_brace = true 703 | ij_javascript_space_before_while_parentheses = true 704 | ij_javascript_spaces_around_additive_operators = true 705 | ij_javascript_spaces_around_arrow_function_operator = true 706 | ij_javascript_spaces_around_assignment_operators = true 707 | ij_javascript_spaces_around_bitwise_operators = true 708 | ij_javascript_spaces_around_equality_operators = true 709 | ij_javascript_spaces_around_logical_operators = true 710 | ij_javascript_spaces_around_multiplicative_operators = true 711 | ij_javascript_spaces_around_relational_operators = true 712 | ij_javascript_spaces_around_shift_operators = true 713 | ij_javascript_spaces_around_unary_operator = false 714 | ij_javascript_spaces_within_array_initializer_brackets = false 715 | ij_javascript_spaces_within_brackets = false 716 | ij_javascript_spaces_within_catch_parentheses = false 717 | ij_javascript_spaces_within_for_parentheses = false 718 | ij_javascript_spaces_within_if_parentheses = false 719 | ij_javascript_spaces_within_imports = false 720 | ij_javascript_spaces_within_interpolation_expressions = false 721 | ij_javascript_spaces_within_method_call_parentheses = false 722 | ij_javascript_spaces_within_method_parentheses = false 723 | ij_javascript_spaces_within_object_literal_braces = false 724 | ij_javascript_spaces_within_object_type_braces = true 725 | ij_javascript_spaces_within_parentheses = false 726 | ij_javascript_spaces_within_switch_parentheses = false 727 | ij_javascript_spaces_within_type_assertion = false 728 | ij_javascript_spaces_within_union_types = true 729 | ij_javascript_spaces_within_while_parentheses = false 730 | ij_javascript_special_else_if_treatment = true 731 | ij_javascript_ternary_operation_signs_on_next_line = false 732 | ij_javascript_ternary_operation_wrap = normal 733 | ij_javascript_union_types_wrap = on_every_item 734 | ij_javascript_use_chained_calls_group_indents = false 735 | ij_javascript_use_double_quotes = true 736 | ij_javascript_use_explicit_js_extension = global 737 | ij_javascript_use_path_mapping = always 738 | ij_javascript_use_public_modifier = false 739 | ij_javascript_use_semicolon_after_statement = true 740 | ij_javascript_var_declaration_wrap = normal 741 | ij_javascript_while_brace_force = always 742 | ij_javascript_while_on_new_line = false 743 | ij_javascript_wrap_comments = false 744 | 745 | [{*.cjsx,*.coffee}] 746 | ij_continuation_indent_size = 4 747 | ij_coffeescript_align_function_body = false 748 | ij_coffeescript_align_imports = false 749 | ij_coffeescript_align_multiline_array_initializer_expression = true 750 | ij_coffeescript_align_multiline_parameters = true 751 | ij_coffeescript_align_multiline_parameters_in_calls = false 752 | ij_coffeescript_align_object_properties = 0 753 | ij_coffeescript_align_union_types = false 754 | ij_coffeescript_align_var_statements = 0 755 | ij_coffeescript_array_initializer_new_line_after_left_brace = false 756 | ij_coffeescript_array_initializer_right_brace_on_new_line = false 757 | ij_coffeescript_array_initializer_wrap = normal 758 | ij_coffeescript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/** 759 | ij_coffeescript_blank_lines_around_function = 1 760 | ij_coffeescript_call_parameters_new_line_after_left_paren = false 761 | ij_coffeescript_call_parameters_right_paren_on_new_line = false 762 | ij_coffeescript_call_parameters_wrap = normal 763 | ij_coffeescript_chained_call_dot_on_new_line = true 764 | ij_coffeescript_comma_on_new_line = false 765 | ij_coffeescript_enforce_trailing_comma = keep 766 | ij_coffeescript_field_prefix = _ 767 | ij_coffeescript_file_name_style = relaxed 768 | ij_coffeescript_force_quote_style = false 769 | ij_coffeescript_force_semicolon_style = false 770 | ij_coffeescript_function_expression_brace_style = end_of_line 771 | ij_coffeescript_import_merge_members = global 772 | ij_coffeescript_import_prefer_absolute_path = global 773 | ij_coffeescript_import_sort_members = true 774 | ij_coffeescript_import_sort_module_name = false 775 | ij_coffeescript_import_use_node_resolution = true 776 | ij_coffeescript_imports_wrap = on_every_item 777 | ij_coffeescript_indent_chained_calls = true 778 | ij_coffeescript_indent_package_children = 0 779 | ij_coffeescript_jsx_attribute_value = braces 780 | ij_coffeescript_keep_blank_lines_in_code = 2 781 | ij_coffeescript_keep_first_column_comment = true 782 | ij_coffeescript_keep_indents_on_empty_lines = false 783 | ij_coffeescript_keep_line_breaks = true 784 | ij_coffeescript_keep_simple_methods_in_one_line = false 785 | ij_coffeescript_method_parameters_new_line_after_left_paren = false 786 | ij_coffeescript_method_parameters_right_paren_on_new_line = false 787 | ij_coffeescript_method_parameters_wrap = off 788 | ij_coffeescript_object_literal_wrap = on_every_item 789 | ij_coffeescript_prefer_as_type_cast = false 790 | ij_coffeescript_prefer_explicit_types_function_expression_returns = false 791 | ij_coffeescript_prefer_explicit_types_function_returns = false 792 | ij_coffeescript_prefer_explicit_types_vars_fields = false 793 | ij_coffeescript_reformat_c_style_comments = false 794 | ij_coffeescript_space_after_comma = true 795 | ij_coffeescript_space_after_dots_in_rest_parameter = false 796 | ij_coffeescript_space_after_generator_mult = true 797 | ij_coffeescript_space_after_property_colon = true 798 | ij_coffeescript_space_after_type_colon = true 799 | ij_coffeescript_space_after_unary_not = false 800 | ij_coffeescript_space_before_async_arrow_lparen = true 801 | ij_coffeescript_space_before_class_lbrace = true 802 | ij_coffeescript_space_before_comma = false 803 | ij_coffeescript_space_before_function_left_parenth = true 804 | ij_coffeescript_space_before_generator_mult = false 805 | ij_coffeescript_space_before_property_colon = false 806 | ij_coffeescript_space_before_type_colon = false 807 | ij_coffeescript_space_before_unary_not = false 808 | ij_coffeescript_spaces_around_additive_operators = true 809 | ij_coffeescript_spaces_around_arrow_function_operator = true 810 | ij_coffeescript_spaces_around_assignment_operators = true 811 | ij_coffeescript_spaces_around_bitwise_operators = true 812 | ij_coffeescript_spaces_around_equality_operators = true 813 | ij_coffeescript_spaces_around_logical_operators = true 814 | ij_coffeescript_spaces_around_multiplicative_operators = true 815 | ij_coffeescript_spaces_around_relational_operators = true 816 | ij_coffeescript_spaces_around_shift_operators = true 817 | ij_coffeescript_spaces_around_unary_operator = false 818 | ij_coffeescript_spaces_within_array_initializer_braces = false 819 | ij_coffeescript_spaces_within_array_initializer_brackets = false 820 | ij_coffeescript_spaces_within_imports = false 821 | ij_coffeescript_spaces_within_index_brackets = false 822 | ij_coffeescript_spaces_within_interpolation_expressions = false 823 | ij_coffeescript_spaces_within_method_call_parentheses = false 824 | ij_coffeescript_spaces_within_method_parentheses = false 825 | ij_coffeescript_spaces_within_object_braces = false 826 | ij_coffeescript_spaces_within_object_literal_braces = false 827 | ij_coffeescript_spaces_within_object_type_braces = true 828 | ij_coffeescript_spaces_within_range_brackets = false 829 | ij_coffeescript_spaces_within_type_assertion = false 830 | ij_coffeescript_spaces_within_union_types = true 831 | ij_coffeescript_union_types_wrap = on_every_item 832 | ij_coffeescript_use_chained_calls_group_indents = false 833 | ij_coffeescript_use_double_quotes = true 834 | ij_coffeescript_use_explicit_js_extension = global 835 | ij_coffeescript_use_path_mapping = always 836 | ij_coffeescript_use_public_modifier = false 837 | ij_coffeescript_use_semicolon_after_statement = false 838 | ij_coffeescript_var_declaration_wrap = normal 839 | 840 | [{*.ft,*.vm,*.vsl}] 841 | ij_vtl_keep_indents_on_empty_lines = false 842 | 843 | [{*.gant,*.gradle,*.groovy,*.gson,*.gy}] 844 | ij_groovy_align_group_field_declarations = false 845 | ij_groovy_align_multiline_array_initializer_expression = false 846 | ij_groovy_align_multiline_assignment = false 847 | ij_groovy_align_multiline_binary_operation = false 848 | ij_groovy_align_multiline_chained_methods = false 849 | ij_groovy_align_multiline_extends_list = false 850 | ij_groovy_align_multiline_for = true 851 | ij_groovy_align_multiline_list_or_map = false 852 | ij_groovy_align_multiline_method_parentheses = false 853 | ij_groovy_align_multiline_parameters = true 854 | ij_groovy_align_multiline_parameters_in_calls = false 855 | ij_groovy_align_multiline_resources = true 856 | ij_groovy_align_multiline_ternary_operation = false 857 | ij_groovy_align_multiline_throws_list = false 858 | ij_groovy_align_named_args_in_map = false 859 | ij_groovy_align_throws_keyword = false 860 | ij_groovy_array_initializer_new_line_after_left_brace = true 861 | ij_groovy_array_initializer_right_brace_on_new_line = true 862 | ij_groovy_array_initializer_wrap = normal 863 | ij_groovy_assert_statement_wrap = normal 864 | ij_groovy_assignment_wrap = normal 865 | ij_groovy_binary_operation_wrap = normal 866 | ij_groovy_blank_lines_after_class_header = 0 867 | ij_groovy_blank_lines_after_imports = 1 868 | ij_groovy_blank_lines_after_package = 1 869 | ij_groovy_blank_lines_around_class = 1 870 | ij_groovy_blank_lines_around_field = 0 871 | ij_groovy_blank_lines_around_field_in_interface = 0 872 | ij_groovy_blank_lines_around_method = 1 873 | ij_groovy_blank_lines_around_method_in_interface = 1 874 | ij_groovy_blank_lines_before_imports = 1 875 | ij_groovy_blank_lines_before_method_body = 0 876 | ij_groovy_blank_lines_before_package = 0 877 | ij_groovy_block_brace_style = end_of_line 878 | ij_groovy_block_comment_at_first_column = true 879 | ij_groovy_call_parameters_new_line_after_left_paren = true 880 | ij_groovy_call_parameters_right_paren_on_new_line = true 881 | ij_groovy_call_parameters_wrap = normal 882 | ij_groovy_catch_on_new_line = false 883 | ij_groovy_class_annotation_wrap = split_into_lines 884 | ij_groovy_class_brace_style = end_of_line 885 | ij_groovy_class_count_to_use_import_on_demand = 5 886 | ij_groovy_do_while_brace_force = always 887 | ij_groovy_else_on_new_line = false 888 | ij_groovy_enum_constants_wrap = split_into_lines 889 | ij_groovy_extends_keyword_wrap = normal 890 | ij_groovy_extends_list_wrap = normal 891 | ij_groovy_field_annotation_wrap = split_into_lines 892 | ij_groovy_finally_on_new_line = false 893 | ij_groovy_for_brace_force = always 894 | ij_groovy_for_statement_new_line_after_left_paren = true 895 | ij_groovy_for_statement_right_paren_on_new_line = true 896 | ij_groovy_for_statement_wrap = normal 897 | ij_groovy_if_brace_force = always 898 | ij_groovy_import_annotation_wrap = 2 899 | ij_groovy_imports_layout = *,|,javax.**,java.**,|,$* 900 | ij_groovy_indent_case_from_switch = false 901 | ij_groovy_indent_label_blocks = true 902 | ij_groovy_insert_inner_class_imports = false 903 | ij_groovy_keep_blank_lines_before_right_brace = 2 904 | ij_groovy_keep_blank_lines_in_code = 2 905 | ij_groovy_keep_blank_lines_in_declarations = 2 906 | ij_groovy_keep_control_statement_in_one_line = false 907 | ij_groovy_keep_first_column_comment = true 908 | ij_groovy_keep_indents_on_empty_lines = false 909 | ij_groovy_keep_line_breaks = true 910 | ij_groovy_keep_multiple_expressions_in_one_line = false 911 | ij_groovy_keep_simple_blocks_in_one_line = false 912 | ij_groovy_keep_simple_classes_in_one_line = true 913 | ij_groovy_keep_simple_lambdas_in_one_line = false 914 | ij_groovy_keep_simple_methods_in_one_line = true 915 | ij_groovy_label_indent_absolute = false 916 | ij_groovy_label_indent_size = 0 917 | ij_groovy_lambda_brace_style = end_of_line 918 | ij_groovy_layout_static_imports_separately = true 919 | ij_groovy_line_comment_add_space = false 920 | ij_groovy_line_comment_at_first_column = true 921 | ij_groovy_method_annotation_wrap = split_into_lines 922 | ij_groovy_method_brace_style = end_of_line 923 | ij_groovy_method_call_chain_wrap = normal 924 | ij_groovy_method_parameters_new_line_after_left_paren = true 925 | ij_groovy_method_parameters_right_paren_on_new_line = true 926 | ij_groovy_method_parameters_wrap = normal 927 | ij_groovy_modifier_list_wrap = false 928 | ij_groovy_names_count_to_use_import_on_demand = 3 929 | ij_groovy_parameter_annotation_wrap = off 930 | ij_groovy_parentheses_expression_new_line_after_left_paren = false 931 | ij_groovy_parentheses_expression_right_paren_on_new_line = false 932 | ij_groovy_prefer_parameters_wrap = false 933 | ij_groovy_resource_list_new_line_after_left_paren = true 934 | ij_groovy_resource_list_right_paren_on_new_line = true 935 | ij_groovy_resource_list_wrap = normal 936 | ij_groovy_space_after_assert_separator = true 937 | ij_groovy_space_after_colon = true 938 | ij_groovy_space_after_comma = true 939 | ij_groovy_space_after_comma_in_type_arguments = true 940 | ij_groovy_space_after_for_semicolon = true 941 | ij_groovy_space_after_quest = true 942 | ij_groovy_space_after_type_cast = true 943 | ij_groovy_space_before_annotation_parameter_list = false 944 | ij_groovy_space_before_array_initializer_left_brace = true 945 | ij_groovy_space_before_assert_separator = false 946 | ij_groovy_space_before_catch_keyword = true 947 | ij_groovy_space_before_catch_left_brace = true 948 | ij_groovy_space_before_catch_parentheses = true 949 | ij_groovy_space_before_class_left_brace = true 950 | ij_groovy_space_before_closure_left_brace = true 951 | ij_groovy_space_before_colon = true 952 | ij_groovy_space_before_comma = false 953 | ij_groovy_space_before_do_left_brace = true 954 | ij_groovy_space_before_else_keyword = true 955 | ij_groovy_space_before_else_left_brace = true 956 | ij_groovy_space_before_finally_keyword = true 957 | ij_groovy_space_before_finally_left_brace = true 958 | ij_groovy_space_before_for_left_brace = true 959 | ij_groovy_space_before_for_parentheses = true 960 | ij_groovy_space_before_for_semicolon = false 961 | ij_groovy_space_before_if_left_brace = true 962 | ij_groovy_space_before_if_parentheses = true 963 | ij_groovy_space_before_method_call_parentheses = false 964 | ij_groovy_space_before_method_left_brace = true 965 | ij_groovy_space_before_method_parentheses = false 966 | ij_groovy_space_before_quest = true 967 | ij_groovy_space_before_switch_left_brace = true 968 | ij_groovy_space_before_switch_parentheses = true 969 | ij_groovy_space_before_synchronized_left_brace = true 970 | ij_groovy_space_before_synchronized_parentheses = true 971 | ij_groovy_space_before_try_left_brace = true 972 | ij_groovy_space_before_try_parentheses = true 973 | ij_groovy_space_before_while_keyword = true 974 | ij_groovy_space_before_while_left_brace = true 975 | ij_groovy_space_before_while_parentheses = true 976 | ij_groovy_space_in_named_argument = true 977 | ij_groovy_space_in_named_argument_before_colon = false 978 | ij_groovy_space_within_empty_array_initializer_braces = false 979 | ij_groovy_space_within_empty_method_call_parentheses = false 980 | ij_groovy_spaces_around_additive_operators = true 981 | ij_groovy_spaces_around_assignment_operators = true 982 | ij_groovy_spaces_around_bitwise_operators = true 983 | ij_groovy_spaces_around_equality_operators = true 984 | ij_groovy_spaces_around_lambda_arrow = true 985 | ij_groovy_spaces_around_logical_operators = true 986 | ij_groovy_spaces_around_multiplicative_operators = true 987 | ij_groovy_spaces_around_regex_operators = true 988 | ij_groovy_spaces_around_relational_operators = true 989 | ij_groovy_spaces_around_shift_operators = true 990 | ij_groovy_spaces_within_annotation_parentheses = false 991 | ij_groovy_spaces_within_array_initializer_braces = false 992 | ij_groovy_spaces_within_braces = true 993 | ij_groovy_spaces_within_brackets = false 994 | ij_groovy_spaces_within_cast_parentheses = false 995 | ij_groovy_spaces_within_catch_parentheses = false 996 | ij_groovy_spaces_within_for_parentheses = false 997 | ij_groovy_spaces_within_gstring_injection_braces = false 998 | ij_groovy_spaces_within_if_parentheses = false 999 | ij_groovy_spaces_within_list_or_map = false 1000 | ij_groovy_spaces_within_method_call_parentheses = false 1001 | ij_groovy_spaces_within_method_parentheses = false 1002 | ij_groovy_spaces_within_parentheses = false 1003 | ij_groovy_spaces_within_switch_parentheses = false 1004 | ij_groovy_spaces_within_synchronized_parentheses = false 1005 | ij_groovy_spaces_within_try_parentheses = false 1006 | ij_groovy_spaces_within_tuple_expression = false 1007 | ij_groovy_spaces_within_while_parentheses = false 1008 | ij_groovy_special_else_if_treatment = true 1009 | ij_groovy_ternary_operation_wrap = normal 1010 | ij_groovy_throws_keyword_wrap = normal 1011 | ij_groovy_throws_list_wrap = normal 1012 | ij_groovy_use_flying_geese_braces = false 1013 | ij_groovy_use_fq_class_names = false 1014 | ij_groovy_use_fq_class_names_in_javadoc = false 1015 | ij_groovy_use_relative_indents = false 1016 | ij_groovy_use_single_class_imports = true 1017 | ij_groovy_variable_annotation_wrap = off 1018 | ij_groovy_while_brace_force = always 1019 | ij_groovy_while_on_new_line = false 1020 | ij_groovy_wrap_long_lines = false 1021 | 1022 | [{*.gradle.kts,*.kt,*.kts,*.main.kts}] 1023 | ij_kotlin_align_in_columns_case_branch = false 1024 | ij_kotlin_align_multiline_binary_operation = false 1025 | ij_kotlin_align_multiline_extends_list = false 1026 | ij_kotlin_align_multiline_method_parentheses = false 1027 | ij_kotlin_align_multiline_parameters = true 1028 | ij_kotlin_align_multiline_parameters_in_calls = false 1029 | ij_kotlin_allow_trailing_comma = false 1030 | ij_kotlin_allow_trailing_comma_on_call_site = false 1031 | ij_kotlin_assignment_wrap = normal 1032 | ij_kotlin_blank_lines_after_class_header = 0 1033 | ij_kotlin_blank_lines_around_block_when_branches = 0 1034 | ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1 1035 | ij_kotlin_block_comment_at_first_column = true 1036 | ij_kotlin_call_parameters_new_line_after_left_paren = true 1037 | ij_kotlin_call_parameters_right_paren_on_new_line = true 1038 | ij_kotlin_call_parameters_wrap = normal 1039 | ij_kotlin_catch_on_new_line = false 1040 | ij_kotlin_class_annotation_wrap = split_into_lines 1041 | ij_kotlin_continuation_indent_for_chained_calls = true 1042 | ij_kotlin_continuation_indent_for_expression_bodies = true 1043 | ij_kotlin_continuation_indent_in_argument_lists = true 1044 | ij_kotlin_continuation_indent_in_elvis = true 1045 | ij_kotlin_continuation_indent_in_if_conditions = true 1046 | ij_kotlin_continuation_indent_in_parameter_lists = true 1047 | ij_kotlin_continuation_indent_in_supertype_lists = true 1048 | ij_kotlin_else_on_new_line = false 1049 | ij_kotlin_enum_constants_wrap = split_into_lines 1050 | ij_kotlin_extends_list_wrap = normal 1051 | ij_kotlin_field_annotation_wrap = split_into_lines 1052 | ij_kotlin_finally_on_new_line = false 1053 | ij_kotlin_if_rparen_on_new_line = false 1054 | ij_kotlin_import_nested_classes = false 1055 | ij_kotlin_imports_layout = *,java.**,javax.**,kotlin.**,^ 1056 | ij_kotlin_insert_whitespaces_in_simple_one_line_method = true 1057 | ij_kotlin_keep_blank_lines_before_right_brace = 2 1058 | ij_kotlin_keep_blank_lines_in_code = 2 1059 | ij_kotlin_keep_blank_lines_in_declarations = 2 1060 | ij_kotlin_keep_first_column_comment = true 1061 | ij_kotlin_keep_indents_on_empty_lines = false 1062 | ij_kotlin_keep_line_breaks = true 1063 | ij_kotlin_lbrace_on_next_line = false 1064 | ij_kotlin_line_comment_add_space = false 1065 | ij_kotlin_line_comment_at_first_column = true 1066 | ij_kotlin_method_annotation_wrap = split_into_lines 1067 | ij_kotlin_method_call_chain_wrap = normal 1068 | ij_kotlin_method_parameters_new_line_after_left_paren = true 1069 | ij_kotlin_method_parameters_right_paren_on_new_line = true 1070 | ij_kotlin_method_parameters_wrap = normal 1071 | ij_kotlin_name_count_to_use_star_import = 2147483647 1072 | ij_kotlin_name_count_to_use_star_import_for_members = 2147483647 1073 | ij_kotlin_packages_to_use_import_on_demand = java.util.*,kotlinx.android.synthetic.**,io.ktor.** 1074 | ij_kotlin_parameter_annotation_wrap = off 1075 | ij_kotlin_space_after_comma = true 1076 | ij_kotlin_space_after_extend_colon = true 1077 | ij_kotlin_space_after_type_colon = true 1078 | ij_kotlin_space_before_catch_parentheses = true 1079 | ij_kotlin_space_before_comma = false 1080 | ij_kotlin_space_before_extend_colon = true 1081 | ij_kotlin_space_before_for_parentheses = true 1082 | ij_kotlin_space_before_if_parentheses = true 1083 | ij_kotlin_space_before_lambda_arrow = true 1084 | ij_kotlin_space_before_type_colon = false 1085 | ij_kotlin_space_before_when_parentheses = true 1086 | ij_kotlin_space_before_while_parentheses = true 1087 | ij_kotlin_spaces_around_additive_operators = true 1088 | ij_kotlin_spaces_around_assignment_operators = true 1089 | ij_kotlin_spaces_around_equality_operators = true 1090 | ij_kotlin_spaces_around_function_type_arrow = true 1091 | ij_kotlin_spaces_around_logical_operators = true 1092 | ij_kotlin_spaces_around_multiplicative_operators = true 1093 | ij_kotlin_spaces_around_range = false 1094 | ij_kotlin_spaces_around_relational_operators = true 1095 | ij_kotlin_spaces_around_unary_operator = false 1096 | ij_kotlin_spaces_around_when_arrow = true 1097 | ij_kotlin_variable_annotation_wrap = off 1098 | ij_kotlin_while_on_new_line = false 1099 | ij_kotlin_wrap_elvis_expressions = 1 1100 | ij_kotlin_wrap_expression_body_functions = 1 1101 | ij_kotlin_wrap_first_method_in_call_chain = false 1102 | 1103 | [{*.har,*.jsb2,*.jsb3,*.json,*.mcmeta,.babelrc,.eslintrc,.stylelintrc,bowerrc,jest.config,mcmod.info}] 1104 | ij_json_keep_blank_lines_in_code = 0 1105 | ij_json_keep_indents_on_empty_lines = false 1106 | ij_json_keep_line_breaks = true 1107 | ij_json_space_after_colon = true 1108 | ij_json_space_after_comma = true 1109 | ij_json_space_before_colon = true 1110 | ij_json_space_before_comma = false 1111 | ij_json_spaces_within_braces = false 1112 | ij_json_spaces_within_brackets = false 1113 | ij_json_wrap_long_lines = false 1114 | 1115 | [{*.htm,*.html,*.ng,*.sht,*.shtm,*.shtml}] 1116 | ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3 1117 | ij_html_align_attributes = true 1118 | ij_html_align_text = false 1119 | ij_html_attribute_wrap = normal 1120 | ij_html_block_comment_at_first_column = true 1121 | ij_html_do_not_align_children_of_min_lines = 0 1122 | ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p 1123 | ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot 1124 | ij_html_enforce_quotes = true 1125 | ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var 1126 | ij_html_keep_blank_lines = 2 1127 | ij_html_keep_indents_on_empty_lines = false 1128 | ij_html_keep_line_breaks = true 1129 | ij_html_keep_line_breaks_in_text = true 1130 | ij_html_keep_whitespaces = false 1131 | ij_html_keep_whitespaces_inside = span,pre,textarea 1132 | ij_html_line_comment_at_first_column = true 1133 | ij_html_new_line_after_last_attribute = never 1134 | ij_html_new_line_before_first_attribute = never 1135 | ij_html_quote_style = double 1136 | ij_html_remove_new_line_before_tags = br 1137 | ij_html_space_after_tag_name = false 1138 | ij_html_space_around_equality_in_attribute = false 1139 | ij_html_space_inside_empty_tag = false 1140 | ij_html_text_wrap = normal 1141 | ij_html_uniform_ident = false 1142 | 1143 | [{*.jsf,*.jsp,*.jspf,*.tag,*.tagf,*.xjsp}] 1144 | ij_jsp_jsp_prefer_comma_separated_import_list = false 1145 | ij_jsp_keep_indents_on_empty_lines = false 1146 | 1147 | [{*.jspx,*.tagx}] 1148 | ij_jspx_keep_indents_on_empty_lines = false 1149 | 1150 | [{*.properties,spring.handlers,spring.schemas}] 1151 | ij_properties_align_group_field_declarations = false 1152 | ij_properties_keep_blank_lines = false 1153 | ij_properties_key_value_delimiter = equals 1154 | ij_properties_spaces_around_key_value_delimiter = false 1155 | 1156 | [{*.toml,Cargo.lock,Gopkg.lock,Pipfile}] 1157 | ij_toml_keep_indents_on_empty_lines = false 1158 | 1159 | [{*.yaml,*.yml}] 1160 | indent_size = 2 1161 | ij_yaml_keep_indents_on_empty_lines = false 1162 | ij_yaml_keep_line_breaks = true 1163 | ij_yaml_space_before_colon = true 1164 | ij_yaml_spaces_within_braces = true 1165 | ij_yaml_spaces_within_brackets = true 1166 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: therandomlabs 2 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: [push, pull_request] 3 | jobs: 4 | build: 5 | strategy: 6 | matrix: 7 | java: [1.8, 11, 15] 8 | os: [ubuntu-20.04, windows-latest] 9 | runs-on: ${{ matrix.os }} 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: gradle/wrapper-validation-action@v1 13 | - uses: actions/setup-java@v1 14 | with: 15 | java-version: ${{ matrix.java }} 16 | - run: ./gradlew build publishToMavenLocal --parallel --stacktrace 17 | - uses: actions/upload-artifact@v2 18 | with: 19 | name: Artifacts 20 | path: build/libs/ 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | .settings 3 | bin 4 | build 5 | config 6 | .classpath 7 | .project 8 | 9 | # idea 10 | out 11 | *.ipr 12 | *.iws 13 | *.iml 14 | .idea 15 | 16 | # ChangelogGenerator 17 | old.json 18 | new.json 19 | changelog.txt 20 | changelog.md 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019-2021 TheRandomLabs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ChangelogGenerator 2 | 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT) 4 | 5 | ![Build](https://github.com/TheRandomLabs/ChangelogGenerator/workflows/Build/badge.svg?branch=main) 6 | [![Dependabot](https://badgen.net/dependabot/TheRandomLabs/ChangelogGenerator/?icon=dependabot)](https://dependabot.com/) 7 | 8 | [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/TheRandomLabs/ChangelogGenerator.svg)](http://isitmaintained.com/project/TheRandomLabs/ChangelogGenerator "Average time to resolve an issue") 9 | 10 | 13 | 14 | Generates changelogs for CurseForge modpacks. 15 | 16 | All public-facing code is documented with Javadoc and (mostly) tested with JUnit. 17 | 18 | ## Usage 19 | 20 | Run `java -jar ChangelogGenerator-[version].jar --help` for command-line usage instructions. 21 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | group = "com.therandomlabs.changeloggenerator" 2 | version = "2.0.0-pre10" 3 | 4 | ext { 5 | commonGradleBranch = "master" 6 | mainClass = "${group}.Main" 7 | jacocoMinimumInstructionCoverage = 0.7 8 | jacocoExcludes = [ 9 | "com.therandomlabs.changeloggenerator.ChangelogEntry", 10 | "com.therandomlabs.changeloggenerator.ChangelogGeneratorOptions", 11 | "com.therandomlabs.changeloggenerator.Main" 12 | ] 13 | } 14 | 15 | apply from: "https://raw.githubusercontent.com/TheRandomLabs/Common-Gradle/${project.commonGradleBranch}/build.gradle" 16 | 17 | repositories { 18 | maven { 19 | url "https://jitpack.io" 20 | } 21 | } 22 | 23 | dependencies { 24 | api "com.github.TheRandomLabs:CurseAPI:master-SNAPSHOT" 25 | api "com.github.TheRandomLabs:CurseAPI-Minecraft:master-SNAPSHOT" 26 | 27 | implementation "org.commonmark:commonmark:0.17.1" 28 | implementation "com.github.TheRandomLabs:TRLUtils-IO:master-SNAPSHOT" 29 | 30 | implementation "info.picocli:picocli:4.6.1" 31 | annotationProcessor "info.picocli:picocli-codegen:4.6.1" 32 | } 33 | 34 | compileJava { 35 | options.compilerArgs += "-Aproject=${project.group}/${project.name}" 36 | } 37 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheRandomLabs/ChangelogGenerator/deabd425343c50c6022ff697bce86611e38eaf21/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = "ChangelogGenerator" 2 | -------------------------------------------------------------------------------- /src/main/java/com/therandomlabs/changeloggenerator/BasicChangelogGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019-2021 TheRandomLabs 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | package com.therandomlabs.changeloggenerator; 25 | 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | import java.util.Map; 29 | import java.util.Optional; 30 | import java.util.Set; 31 | import java.util.TreeMap; 32 | import java.util.TreeSet; 33 | import java.util.stream.Collectors; 34 | 35 | import com.google.common.base.Splitter; 36 | import com.google.common.collect.Lists; 37 | import com.therandomlabs.curseapi.CurseAPI; 38 | import com.therandomlabs.curseapi.CurseException; 39 | import com.therandomlabs.curseapi.file.BasicCurseFile; 40 | import com.therandomlabs.curseapi.file.CurseFile; 41 | import com.therandomlabs.curseapi.file.CurseFileChange; 42 | import com.therandomlabs.curseapi.file.CurseFiles; 43 | import com.therandomlabs.curseapi.file.CurseFilesComparison; 44 | import com.therandomlabs.curseapi.game.CurseGameVersionGroup; 45 | import com.therandomlabs.curseapi.minecraft.MCVersion; 46 | import com.therandomlabs.curseapi.minecraft.modpack.CurseModpack; 47 | import com.therandomlabs.curseapi.project.CurseProject; 48 | import com.therandomlabs.curseapi.util.JsoupUtils; 49 | 50 | /** 51 | * A basic implementation of {@link ChangelogGenerator} that generates a plaintext changelog. 52 | */ 53 | public class BasicChangelogGenerator extends ChangelogGenerator { 54 | /** 55 | * A {@link Splitter} that works on line separators. 56 | * Empty strings are omitted, and results are trimmed. 57 | */ 58 | protected static final Splitter lineSeparatorSplitter = Splitter.on(System.lineSeparator()). 59 | omitEmptyStrings(). 60 | trimResults(); 61 | 62 | private final ChangelogGeneratorOptions options; 63 | 64 | /** 65 | * Constructs a {@link BasicChangelogGenerator}. 66 | */ 67 | public BasicChangelogGenerator() { 68 | this(new ChangelogGeneratorOptions()); 69 | } 70 | 71 | /** 72 | * Constructs a {@link BasicChangelogGenerator}. 73 | * 74 | * @param options ChangelogGenerator options. 75 | */ 76 | public BasicChangelogGenerator(ChangelogGeneratorOptions options) { 77 | this.options = options; 78 | } 79 | 80 | /** 81 | * {@inheritDoc} 82 | */ 83 | @Override 84 | public String generate(CurseModpack oldModpack, CurseModpack newModpack) throws CurseException { 85 | final CurseFilesComparison comparison = 86 | CurseFilesComparison.of(oldModpack.basicFiles(), newModpack.basicFiles()); 87 | final CurseGameVersionGroup versionGroup = newModpack.mcVersion().versionGroup(); 88 | final StringBuilder builder = new StringBuilder(); 89 | 90 | appendModpackVersions(builder, oldModpack, newModpack); 91 | separateSections(builder); 92 | 93 | if (!comparison.added().isEmpty()) { 94 | appendFiles(builder, "Added", comparison.added()); 95 | separateSections(builder); 96 | } 97 | 98 | if (!comparison.updated().isEmpty()) { 99 | appendChangelogEntries(builder, "Updated", comparison.updated(), versionGroup); 100 | separateSections(builder); 101 | } 102 | 103 | if (!comparison.downgraded().isEmpty()) { 104 | appendChangelogEntries(builder, "Downgraded", comparison.downgraded(), versionGroup); 105 | separateSections(builder); 106 | } 107 | 108 | if (!comparison.removed().isEmpty()) { 109 | appendFiles(builder, "Removed", comparison.removed()); 110 | separateSections(builder); 111 | } 112 | 113 | appendTail(builder); 114 | return builder.toString(); 115 | } 116 | 117 | /** 118 | * Returns ChangelogGenerator options. 119 | * 120 | * @return a {@link ChangelogGeneratorOptions} instance. 121 | */ 122 | protected ChangelogGeneratorOptions getOptions() { 123 | return options; 124 | } 125 | 126 | /** 127 | * Separates two different changelog sections, e.g. "Updated" and "Downgraded". 128 | * By default, this is done by appending {@link System#lineSeparator()} twice. 129 | * 130 | * @param builder a {@link StringBuilder} to append to. 131 | */ 132 | protected void separateSections(StringBuilder builder) { 133 | builder.append(System.lineSeparator()).append(System.lineSeparator()); 134 | } 135 | 136 | /** 137 | * Appends the specified title to the changelog. 138 | * 139 | * @param builder a {@link StringBuilder} to append to. 140 | * @param title a title. 141 | */ 142 | protected void appendTitle(StringBuilder builder, String title) { 143 | builder.append(title).append(':'); 144 | } 145 | 146 | /** 147 | * Appends the old and new modpack versions to the changelog. 148 | * 149 | * @param builder a {@link StringBuilder} to append to. 150 | * @param oldModpack the old version of the modpack. 151 | * @param newModpack the new version of the modpack. 152 | * @throws CurseException if an error occurs. 153 | */ 154 | protected void appendModpackVersions( 155 | StringBuilder builder, CurseModpack oldModpack, CurseModpack newModpack 156 | ) throws CurseException { 157 | final StringBuilder versions = new StringBuilder(); 158 | versions.append(oldModpack.name()).append(' ').append(oldModpack.version()).append(" --> "). 159 | append(newModpack.name()).append(' ').append(newModpack.version()); 160 | 161 | for (int i = 0; i < versions.length(); i++) { 162 | builder.append('='); 163 | } 164 | 165 | builder.append(System.lineSeparator()).append(versions).append(System.lineSeparator()); 166 | 167 | for (int i = 0; i < versions.length(); i++) { 168 | builder.append('='); 169 | } 170 | } 171 | 172 | /** 173 | * Appends the specified files to the modpack under the specified section title. 174 | * By default, the files' project names are listed. 175 | * 176 | * @param builder a {@link StringBuilder} to append to. 177 | * @param title a section title. 178 | * @param files a {@link CurseFiles}. 179 | * @throws CurseException if an error occurs. 180 | */ 181 | protected void appendFiles( 182 | StringBuilder builder, String title, CurseFiles files 183 | ) throws CurseException { 184 | appendTitle(builder, title); 185 | 186 | final Set projectNames = files.parallelMap( 187 | file -> Optional.ofNullable(file.project()). 188 | map(CurseProject::name). 189 | orElse("Deleted project"), 190 | Collectors.toCollection(TreeSet::new) 191 | ); 192 | 193 | for (String projectName : projectNames) { 194 | builder.append(System.lineSeparator()).append("* ").append(projectName); 195 | } 196 | } 197 | 198 | /** 199 | * Appends the changelogs for the specified {@link CurseFileChange}s to the changelog 200 | * under the specified section title. 201 | * 202 | * @param builder a {@link StringBuilder} to append to. 203 | * @param title a section title. 204 | * @param fileChanges a {@link Set} of {@link CurseFileChange}s. 205 | * @param fallbackVersionGroup the {@link CurseGameVersionGroup} to use if a game version group 206 | * is necessary and cannot be determined. 207 | * @throws CurseException if an error occurs. 208 | */ 209 | protected void appendChangelogEntries( 210 | StringBuilder builder, String title, Set> fileChanges, 211 | CurseGameVersionGroup fallbackVersionGroup 212 | ) throws CurseException { 213 | appendTitle(builder, title); 214 | 215 | final Map allEntries = new TreeMap<>(CurseAPI.parallelMap( 216 | fileChanges, 217 | fileChange -> Optional.ofNullable(fileChange.project()). 218 | map(CurseProject::name). 219 | orElseGet(() -> "Deleted project (" + fileChange.projectID() + ")"), 220 | fileChange -> getChangelogEntries(fileChange, fallbackVersionGroup) 221 | )); 222 | 223 | for (Map.Entry changelogEntries : allEntries.entrySet()) { 224 | builder.append(System.lineSeparator()); 225 | appendChangelogEntries(builder, changelogEntries.getKey(), changelogEntries.getValue()); 226 | } 227 | 228 | builder.setLength(builder.length() - System.lineSeparator().length()); 229 | } 230 | 231 | /** 232 | * Appends a {@link ChangelogEntries} to the changelog. 233 | * 234 | * @param builder a {@link StringBuilder} to append to. 235 | * @param projectName a project name. 236 | * @param changelogEntries a {@link ChangelogEntries}. 237 | * @throws CurseException if an error occurs. 238 | */ 239 | @SuppressWarnings("Duplicates") 240 | protected void appendChangelogEntries( 241 | StringBuilder builder, String projectName, ChangelogEntries changelogEntries 242 | ) throws CurseException { 243 | final CurseFileChange fileChange = changelogEntries.fileChange(); 244 | 245 | final CurseFile oldFile = fileChange.oldCurseFile(); 246 | final String oldDisplayName = oldFile == null ? "Archived file" : oldFile.displayName(); 247 | 248 | final CurseFile newFile = fileChange.newCurseFile(); 249 | final String newDisplayName = newFile == null ? "Archived file" : newFile.displayName(); 250 | 251 | builder.append('\t').append( 252 | Optional.ofNullable(fileChange.project()). 253 | map(CurseProject::name). 254 | orElse("Deleted project") 255 | ).append(" (").append(oldDisplayName).append(" --> ").append(newDisplayName).append("):"); 256 | 257 | List entries = new ArrayList<>(changelogEntries.entries()); 258 | 259 | if (entries.isEmpty()) { 260 | return; 261 | } 262 | 263 | final int maxEntries = getOptions().maxEntryCount; 264 | final int extraEntries = maxEntries == 0 ? 0 : entries.size() - maxEntries; 265 | 266 | if (extraEntries > 0) { 267 | entries = entries.subList(0, maxEntries); 268 | } 269 | 270 | for (ChangelogEntry entry : entries) { 271 | builder.append(System.lineSeparator()).append("\t\t").append(entry.title()).append(':'); 272 | 273 | List entryLines; 274 | 275 | if (JsoupUtils.isEmpty(entry.entry())) { 276 | entryLines = Lists.newArrayList("No changelog available."); 277 | } else { 278 | entryLines = Lists.newArrayList( 279 | lineSeparatorSplitter.split(JsoupUtils.getPlainText(entry.entry())) 280 | ); 281 | } 282 | 283 | final int maxLines = getOptions().maxEntryLineCount; 284 | 285 | if (maxLines != 0 && entryLines.size() > maxLines) { 286 | final int extra = entryLines.size() - maxLines; 287 | entryLines.set(maxLines, "(" + extra + " more line" + (extra == 1 ? ")" : "s)")); 288 | entryLines = entryLines.subList(0, maxLines + 1); 289 | } 290 | 291 | for (String line : entryLines) { 292 | builder.append(System.lineSeparator()); 293 | 294 | if (!line.isEmpty()) { 295 | builder.append("\t\t\t"); 296 | 297 | //For consistency, we change "- " and "+ " to "* " at line beginnings. 298 | if ((line.startsWith("- ") || line.startsWith("+ ") || line.startsWith("* ")) && 299 | line.length() > 2) { 300 | builder.append("* ").append(line.substring(2).trim()); 301 | } else { 302 | builder.append(line); 303 | } 304 | } 305 | } 306 | 307 | builder.append(System.lineSeparator()); 308 | } 309 | 310 | if (extraEntries > 0) { 311 | builder.append(System.lineSeparator()).append("\t\t").append(extraEntries). 312 | append(" more entr").append(extraEntries == 1 ? "y" : "ies"). 313 | append(System.lineSeparator()); 314 | } 315 | } 316 | 317 | /** 318 | * Appends a tail message to the changelog. 319 | * 320 | * @param builder a {@link StringBuilder} to append to. 321 | */ 322 | protected void appendTail(StringBuilder builder) { 323 | builder.append("Generated using ChangelogGenerator ").append(VERSION). 324 | append(": https://github.com/TheRandomLabs/ChangelogGenerator"); 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /src/main/java/com/therandomlabs/changeloggenerator/ChangelogEntries.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019-2021 TheRandomLabs 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | package com.therandomlabs.changeloggenerator; 25 | 26 | import java.util.Collection; 27 | import java.util.NavigableSet; 28 | import java.util.SortedSet; 29 | import java.util.TreeSet; 30 | 31 | import com.google.common.base.MoreObjects; 32 | import com.therandomlabs.curseapi.file.BasicCurseFile; 33 | import com.therandomlabs.curseapi.file.CurseFileChange; 34 | 35 | /** 36 | * Represents a set of changelog entries for a {@link CurseFileChange}. 37 | */ 38 | public class ChangelogEntries { 39 | private final CurseFileChange fileChange; 40 | @SuppressWarnings("PMD.LooseCoupling") 41 | private final TreeSet entries; 42 | 43 | /** 44 | * Constructs a {@link ChangelogEntries} instance with the specified {@link CurseFileChange} 45 | * and {@link Collection} of {@link ChangelogEntry} instances. 46 | * 47 | * @param fileChange a {@link CurseFileChange}. 48 | * @param entries a {@link SortedSet} of {@link ChangelogEntry} instances. 49 | */ 50 | @SuppressWarnings("unchecked") 51 | public ChangelogEntries( 52 | CurseFileChange fileChange, 53 | Collection entries 54 | ) { 55 | this.fileChange = (CurseFileChange) fileChange; 56 | this.entries = new TreeSet<>(entries); 57 | } 58 | 59 | /** 60 | * {@inheritDoc} 61 | */ 62 | @Override 63 | public String toString() { 64 | return MoreObjects.toStringHelper(this). 65 | add("fileChange", fileChange). 66 | add("entries", entries). 67 | toString(); 68 | } 69 | 70 | /** 71 | * Returns this {@link ChangelogEntries}' {@link CurseFileChange}. 72 | * 73 | * @return this {@link ChangelogEntries}' {@link CurseFileChange}. 74 | */ 75 | public CurseFileChange fileChange() { 76 | return fileChange; 77 | } 78 | 79 | /** 80 | * Returns this {@link ChangelogEntries}' {@link ChangelogEntry} instances. 81 | * 82 | * @return a mutable {@link NavigableSet} of this {@link ChangelogEntries}' 83 | * {@link ChangelogEntry} instances. 84 | */ 85 | @SuppressWarnings("unchecked") 86 | public NavigableSet entries() { 87 | return (NavigableSet) entries.clone(); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/therandomlabs/changeloggenerator/ChangelogEntry.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019-2021 TheRandomLabs 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | package com.therandomlabs.changeloggenerator; 25 | 26 | import com.google.common.base.MoreObjects; 27 | import okhttp3.HttpUrl; 28 | import org.jsoup.nodes.Element; 29 | 30 | /** 31 | * Represents a changelog entry. 32 | */ 33 | public final class ChangelogEntry implements Comparable { 34 | private final Comparable comparable; 35 | private final String title; 36 | private final HttpUrl url; 37 | private Element entry; 38 | 39 | /** 40 | * Constructs a {@link ChangelogEntry} instance with the specified title, entry and URL. 41 | * 42 | * @param comparable a {@link Comparable} for use in sorting. Changelog entries for newer 43 | * files should be sorted first. 44 | * @param title a title. 45 | * @param url a URL. 46 | * @param entry a changelog entry. 47 | */ 48 | public ChangelogEntry(Comparable comparable, String title, HttpUrl url, Element entry) { 49 | this.comparable = comparable; 50 | this.title = title; 51 | this.url = url; 52 | this.entry = entry; 53 | } 54 | 55 | /** 56 | * {@inheritDoc} 57 | */ 58 | @Override 59 | public int hashCode() { 60 | return comparable.hashCode(); 61 | } 62 | 63 | /** 64 | * {@inheritDoc} 65 | */ 66 | @Override 67 | public boolean equals(Object object) { 68 | return this == object || (object instanceof ChangelogEntry && 69 | comparable.equals(((ChangelogEntry) object).comparable)); 70 | } 71 | 72 | /** 73 | * {@inheritDoc} 74 | */ 75 | @Override 76 | public String toString() { 77 | return MoreObjects.toStringHelper(this). 78 | add("title", title). 79 | add("entry", entry). 80 | toString(); 81 | } 82 | 83 | /** 84 | * {@inheritDoc} 85 | */ 86 | @SuppressWarnings({"rawtypes", "unchecked"}) 87 | @Override 88 | public int compareTo(ChangelogEntry changelogEntry) { 89 | return ((Comparable) comparable).compareTo(changelogEntry.comparable); 90 | } 91 | 92 | /** 93 | * Returns this {@link ChangelogEntry}'s title. 94 | * 95 | * @return this {@link ChangelogEntry}'s title. 96 | */ 97 | public String title() { 98 | return title; 99 | } 100 | 101 | /** 102 | * Returns this {@link ChangelogEntry}'s URL. 103 | * @return this {@link ChangelogEntry}'s URL. 104 | */ 105 | public HttpUrl url() { 106 | return url; 107 | } 108 | 109 | /** 110 | * Returns this {@link ChangelogEntry}'s changelog entry. 111 | * 112 | * @return this {@link ChangelogEntry}'s changelog entry. 113 | */ 114 | public Element entry() { 115 | return entry; 116 | } 117 | 118 | //This is called by ChangelogGenerator#getChangelogEntries(CurseFileChange). 119 | void setEntry(Element entry) { 120 | this.entry = entry; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/com/therandomlabs/changeloggenerator/ChangelogGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019-2021 TheRandomLabs 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | package com.therandomlabs.changeloggenerator; 25 | 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | import java.util.SortedSet; 29 | 30 | import com.google.common.base.Preconditions; 31 | import com.therandomlabs.changeloggenerator.provider.ActuallyAdditionsProvider; 32 | import com.therandomlabs.changeloggenerator.provider.BiomesOPlentyProvider; 33 | import com.therandomlabs.changeloggenerator.provider.ChangelogProvider; 34 | import com.therandomlabs.changeloggenerator.provider.CurseChangelogProvider; 35 | import com.therandomlabs.changeloggenerator.provider.McJtyProvider; 36 | import com.therandomlabs.changeloggenerator.provider.MezzProvider; 37 | import com.therandomlabs.curseapi.CurseException; 38 | import com.therandomlabs.curseapi.file.BasicCurseFile; 39 | import com.therandomlabs.curseapi.file.CurseFileChange; 40 | import com.therandomlabs.curseapi.game.CurseGameVersionGroup; 41 | import com.therandomlabs.curseapi.minecraft.CurseAPIMinecraft; 42 | import com.therandomlabs.curseapi.minecraft.MCVersion; 43 | import com.therandomlabs.curseapi.minecraft.modpack.CurseModpack; 44 | import org.jsoup.nodes.Element; 45 | import org.slf4j.Logger; 46 | import org.slf4j.LoggerFactory; 47 | 48 | /** 49 | * Generates changelogs for CurseForge modpacks. 50 | */ 51 | public abstract class ChangelogGenerator { 52 | /** 53 | * The current version of ChangelogGenerator. 54 | */ 55 | public static final String VERSION = "@VERSION@"; 56 | 57 | private static final Logger logger = LoggerFactory.getLogger(ChangelogGenerator.class); 58 | 59 | private final List providers = new ArrayList<>(); 60 | 61 | static { 62 | CurseAPIMinecraft.initialize(); 63 | } 64 | 65 | /** 66 | * Constructs a {@link ChangelogGenerator} instance with a default set of 67 | * {@link CurseChangelogProvider}s. 68 | */ 69 | protected ChangelogGenerator() { 70 | withProvider(CurseChangelogProvider.instance); 71 | withProvider(ActuallyAdditionsProvider.instance); 72 | withProvider(BiomesOPlentyProvider.instance); 73 | withProvider(McJtyProvider.instance); 74 | withProvider(MezzProvider.instance); 75 | } 76 | 77 | /** 78 | * Returns the {@link ChangelogEntries} for the specified {@link CurseFileChange} by iterating 79 | * through the registered {@link ChangelogProvider}s and calling 80 | * {@link ChangelogProvider#getChangelog(CurseFileChange, CurseGameVersionGroup)}. 81 | * 82 | * @param fileChange a {@link CurseFileChange}. 83 | * @param fallbackVersionGroup the {@link CurseGameVersionGroup} to use if a game version group 84 | * is necessary and cannot be determined. 85 | * @return the {@link ChangelogEntries} for the specified {@link CurseFileChange}. 86 | * @throws CurseException if an error occurs. 87 | * @see #withProvider(ChangelogProvider) 88 | */ 89 | public ChangelogEntries getChangelogEntries( 90 | CurseFileChange fileChange, 91 | CurseGameVersionGroup fallbackVersionGroup 92 | ) throws CurseException { 93 | logger.info("Retrieving changelog entries for file change: {}", fileChange); 94 | 95 | for (ChangelogProvider provider : providers) { 96 | final SortedSet changelog = 97 | provider.getChangelog(fileChange, fallbackVersionGroup); 98 | 99 | if (changelog != null) { 100 | //Call ChangelogProvider#processChangelog(CurseFileChange, Element). 101 | for (ChangelogEntry changelogEntry : changelog) { 102 | Element entry = changelogEntry.entry(); 103 | 104 | for (ChangelogProvider provider2 : providers) { 105 | entry = provider2.processChangelog(fileChange, entry); 106 | } 107 | 108 | changelogEntry.setEntry(entry); 109 | } 110 | 111 | return new ChangelogEntries(fileChange, changelog); 112 | } 113 | } 114 | 115 | throw new CurseException("Could not retrieve changelog entries for: " + fileChange); 116 | } 117 | 118 | /** 119 | * Generates a changelog that details changes between an older and newer version of a 120 | * CurseForge modpack. 121 | * 122 | * @param oldModpack a {@link CurseModpack} instance that represents an older version of a 123 | * CurseForge modpack. 124 | * @param newModpack a {@link CurseModpack} instance that represents a newer version of a 125 | * CurseForge modpack. 126 | * @return the generated changelog. 127 | * @throws CurseException if an error occurs. 128 | */ 129 | public abstract String generate(CurseModpack oldModpack, CurseModpack newModpack) 130 | throws CurseException; 131 | 132 | /** 133 | * Registers the specified {@link ChangelogProvider} to this {@link ChangelogGenerator} 134 | * instance with the highest priority. 135 | * 136 | * @param provider a {@link ChangelogProvider}. 137 | * @return this {@link ChangelogGenerator}. 138 | */ 139 | protected final ChangelogGenerator withProvider(ChangelogProvider provider) { 140 | Preconditions.checkNotNull(provider, "provider should not be null"); 141 | Preconditions.checkArgument( 142 | !providers.contains(provider), "provider should not already be registered" 143 | ); 144 | providers.add(0, provider); 145 | return this; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/main/java/com/therandomlabs/changeloggenerator/ChangelogGeneratorOptions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019-2021 TheRandomLabs 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | package com.therandomlabs.changeloggenerator; 25 | 26 | import java.nio.file.Path; 27 | import java.util.concurrent.Callable; 28 | 29 | import org.checkerframework.checker.nullness.qual.Nullable; 30 | import picocli.CommandLine; 31 | 32 | /** 33 | * Options for ChangelogGenerator. 34 | */ 35 | @CommandLine.Command( 36 | mixinStandardHelpOptions = true, version = ChangelogGenerator.VERSION, 37 | description = "Generates changelogs for CurseForge modpacks." 38 | ) 39 | public final class ChangelogGeneratorOptions implements Callable { 40 | @CommandLine.Option( 41 | names = {"-l", "--lines"}, 42 | description = "The maximum number of lines to display in a changelog entry. " + 43 | "5 by default. 0 removes this limit." 44 | ) 45 | public int maxEntryLineCount = 5; 46 | 47 | @CommandLine.Option( 48 | names = {"-e", "--entries"}, 49 | description = "The maximum number of changelog entries to display per mod. " + 50 | "5 by default. 0 removes this limit." 51 | ) 52 | public int maxEntryCount = 5; 53 | 54 | @CommandLine.Option( 55 | names = {"-m", "--markdown"}, 56 | description = "Generate a Markdown changelog." 57 | ) 58 | boolean markdown; 59 | 60 | @CommandLine.Option( 61 | names = {"-o", "--old"}, 62 | description = "The old modpack or modpack manifest. " + 63 | "\"old.json\" or \"old.zip\" by default." 64 | ) 65 | @Nullable 66 | Path oldModpack; 67 | 68 | @CommandLine.Option( 69 | names = {"-n", "--new"}, 70 | description = "The new modpack or modpack manifest. " + 71 | "\"new.json\" or \"new.zip\" by default." 72 | ) 73 | @Nullable 74 | Path newModpack; 75 | 76 | @CommandLine.Option( 77 | names = {"-O", "--output"}, 78 | description = "The changelog output location. \"changelog.txt\" or " + 79 | "\"changelog.md\" by default." 80 | ) 81 | @Nullable 82 | Path output; 83 | 84 | @Nullable 85 | private final Callable callable; 86 | 87 | /** 88 | * Constructs a {@link ChangelogGeneratorOptions}. 89 | */ 90 | public ChangelogGeneratorOptions() { 91 | this(null); 92 | } 93 | 94 | ChangelogGeneratorOptions(@Nullable Callable callable) { 95 | this.callable = callable; 96 | } 97 | 98 | /** 99 | * This should not be called directly. 100 | * 101 | * @return an exit code. 102 | */ 103 | @Override 104 | public Integer call() throws Exception { 105 | return callable == null ? Integer.valueOf(0) : callable.call(); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/com/therandomlabs/changeloggenerator/CommonMarkUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019-2021 TheRandomLabs 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | package com.therandomlabs.changeloggenerator; 25 | 26 | import com.google.common.base.Preconditions; 27 | import com.therandomlabs.curseapi.util.JsoupUtils; 28 | import org.commonmark.parser.Parser; 29 | import org.commonmark.renderer.html.HtmlRenderer; 30 | import org.jsoup.nodes.Element; 31 | 32 | /** 33 | * Contains utility methods for working with Markdown. 34 | */ 35 | public final class CommonMarkUtils { 36 | private static final Parser parser = Parser.builder().build(); 37 | private static final HtmlRenderer renderer = HtmlRenderer.builder().build(); 38 | 39 | private CommonMarkUtils() {} 40 | 41 | /** 42 | * Parses the specified Markdown string as an HTML {@link Element}. 43 | * 44 | * @param markdown a Markdown string. 45 | * @return an {@link Element} parsed from the specified Markdown string. 46 | */ 47 | public static Element parseElement(String markdown) { 48 | Preconditions.checkNotNull(markdown, "markdown should not be null"); 49 | final Element element = JsoupUtils.parseBody(renderer.render(parser.parse(markdown))); 50 | Preconditions.checkNotNull(element, "element should not be null"); 51 | return element; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/therandomlabs/changeloggenerator/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019-2021 TheRandomLabs 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | package com.therandomlabs.changeloggenerator; 25 | 26 | import java.io.IOException; 27 | import java.nio.file.Files; 28 | import java.nio.file.Path; 29 | import java.nio.file.Paths; 30 | 31 | import com.therandomlabs.curseapi.minecraft.modpack.CurseModpack; 32 | import com.therandomlabs.utils.io.ZipFile; 33 | import okio.BufferedSink; 34 | import okio.Okio; 35 | import org.checkerframework.checker.nullness.qual.Nullable; 36 | import picocli.CommandLine; 37 | 38 | /** 39 | * The main class for the runnable ChangelogGenerator application. 40 | */ 41 | public final class Main { 42 | @Nullable 43 | private static ChangelogGeneratorOptions options; 44 | 45 | @Nullable 46 | private static CommandLine commandLine; 47 | 48 | private Main() {} 49 | 50 | /** 51 | * The main method for the runnable ChangelogGenerator application. 52 | * 53 | * @param args the command-line arguments. 54 | */ 55 | public static void main(String[] args) { 56 | options = new ChangelogGeneratorOptions(Main::run); 57 | commandLine = new CommandLine(options).registerConverter(Path.class, Paths::get); 58 | System.exit(commandLine.execute(args)); 59 | } 60 | 61 | @SuppressWarnings({"ConstantConditions", "NullAway"}) 62 | private static Integer run() throws Exception { 63 | if (options == null) { 64 | throw new IllegalStateException("options should not be null"); 65 | } 66 | 67 | if (commandLine == null) { 68 | throw new IllegalStateException("commandLine should not be null"); 69 | } 70 | 71 | if (options.maxEntryLineCount < 0) { 72 | System.err.println("Cannot display negative number of lines"); 73 | return 1; 74 | } 75 | 76 | if (options.maxEntryCount < 0) { 77 | System.err.println("Cannot display negative number of entries"); 78 | return 1; 79 | } 80 | 81 | if (options.oldModpack == null) { 82 | options.oldModpack = Paths.get("old.json"); 83 | 84 | if (!Files.exists(options.oldModpack)) { 85 | final Path zip = Paths.get("old.zip"); 86 | 87 | if (Files.exists(zip)) { 88 | options.oldModpack = zip; 89 | } 90 | } 91 | } 92 | 93 | if (options.oldModpack.getFileName().toString().endsWith(".zip")) { 94 | try { 95 | options.oldModpack = new ZipFile(options.oldModpack).getEntry("manifest.json"); 96 | } catch (IOException ex) { 97 | System.err.println("Invalid zip file: " + options.oldModpack); 98 | return 1; 99 | } 100 | } 101 | 102 | if (options.newModpack == null) { 103 | options.newModpack = Paths.get("new.json"); 104 | 105 | if (!Files.exists(options.newModpack)) { 106 | final Path zip = Paths.get("new.zip"); 107 | 108 | if (Files.exists(zip)) { 109 | options.newModpack = zip; 110 | } 111 | } 112 | } 113 | 114 | if (options.newModpack.getFileName().toString().endsWith(".zip")) { 115 | try { 116 | options.newModpack = new ZipFile(options.newModpack).getEntry("manifest.json"); 117 | } catch (IOException ex) { 118 | System.err.println("Invalid zip file: " + options.newModpack); 119 | return 1; 120 | } 121 | } 122 | 123 | if (options.output == null) { 124 | options.output = Paths.get(options.markdown ? "changelog.md" : "changelog.txt"); 125 | } 126 | 127 | final CurseModpack oldModpack = CurseModpack.fromJSON(options.oldModpack); 128 | final CurseModpack newModpack = CurseModpack.fromJSON(options.newModpack); 129 | final ChangelogGenerator generator = options.markdown ? 130 | new MarkdownChangelogGenerator(options) : new BasicChangelogGenerator(options); 131 | final String changelog = generator.generate(oldModpack, newModpack); 132 | 133 | try (BufferedSink sink = Okio.buffer(Okio.sink(options.output))) { 134 | sink.writeUtf8(changelog).writeUtf8(System.lineSeparator()); 135 | } 136 | 137 | return 0; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/com/therandomlabs/changeloggenerator/MarkdownChangelogGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019-2021 TheRandomLabs 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | package com.therandomlabs.changeloggenerator; 25 | 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | import java.util.Optional; 29 | import java.util.Set; 30 | import java.util.TreeSet; 31 | import java.util.regex.Pattern; 32 | import java.util.stream.Collectors; 33 | 34 | import com.google.common.collect.Lists; 35 | import com.therandomlabs.curseapi.CurseException; 36 | import com.therandomlabs.curseapi.file.BasicCurseFile; 37 | import com.therandomlabs.curseapi.file.CurseFile; 38 | import com.therandomlabs.curseapi.file.CurseFileChange; 39 | import com.therandomlabs.curseapi.file.CurseFiles; 40 | import com.therandomlabs.curseapi.minecraft.modpack.CurseModpack; 41 | import com.therandomlabs.curseapi.project.CurseProject; 42 | import com.therandomlabs.curseapi.util.JsoupUtils; 43 | 44 | /** 45 | * A variant of {@link BasicChangelogGenerator} that generates a Markdown changelog. 46 | */ 47 | public class MarkdownChangelogGenerator extends BasicChangelogGenerator { 48 | private static final Pattern image = Pattern.compile(".*!\\[.*]\\(.*\\).*"); 49 | 50 | /** 51 | * Constructs a {@link MarkdownChangelogGenerator}. 52 | */ 53 | public MarkdownChangelogGenerator() { 54 | this(new ChangelogGeneratorOptions()); 55 | } 56 | 57 | /** 58 | * Constructs a {@link MarkdownChangelogGenerator}. 59 | * 60 | * @param options ChangelogGenerator options. 61 | */ 62 | public MarkdownChangelogGenerator(ChangelogGeneratorOptions options) { 63 | super(options); 64 | } 65 | 66 | /** 67 | * {@inheritDoc} 68 | */ 69 | @Override 70 | protected void appendTitle(StringBuilder builder, String title) { 71 | builder.append("## ").append(title).append(System.lineSeparator()); 72 | } 73 | 74 | /** 75 | * {@inheritDoc} 76 | */ 77 | @Override 78 | protected void appendModpackVersions( 79 | StringBuilder builder, CurseModpack oldModpack, CurseModpack newModpack 80 | ) throws CurseException { 81 | builder.append("# ").append(oldModpack.name()).append(' ').append(oldModpack.version()). 82 | append('⟶').append(newModpack.name()).append(' ').append(newModpack.version()); 83 | } 84 | 85 | /** 86 | * {@inheritDoc} 87 | */ 88 | @Override 89 | protected void appendFiles( 90 | StringBuilder builder, String title, CurseFiles files 91 | ) throws CurseException { 92 | appendTitle(builder, title); 93 | 94 | final Set projectNames = files.parallelMap( 95 | file -> Optional.ofNullable(file.project()). 96 | map(project -> "[" + project.name() + "](" + project.url() + ")"). 97 | orElse("Deleted project"), 98 | Collectors.toCollection(TreeSet::new) 99 | ); 100 | 101 | for (String projectName : projectNames) { 102 | builder.append(System.lineSeparator()).append("* ").append(projectName); 103 | } 104 | } 105 | 106 | /** 107 | * {@inheritDoc} 108 | */ 109 | @SuppressWarnings("Duplicates") 110 | @Override 111 | protected void appendChangelogEntries( 112 | StringBuilder builder, String projectName, ChangelogEntries changelogEntries 113 | ) throws CurseException { 114 | final CurseFileChange fileChange = changelogEntries.fileChange(); 115 | 116 | final CurseFile oldFile = fileChange.oldCurseFile(); 117 | final String oldDisplayName; 118 | 119 | if (oldFile == null) { 120 | oldDisplayName = "Archived file"; 121 | } else { 122 | oldDisplayName = "[" + oldFile.displayName() + "](" + oldFile.url() + ")"; 123 | } 124 | 125 | final CurseFile newFile = fileChange.newCurseFile(); 126 | final String newDisplayName; 127 | 128 | if (newFile == null) { 129 | newDisplayName = "Archived file"; 130 | } else { 131 | newDisplayName = "[" + newFile.displayName() + "](" + newFile.url() + ")"; 132 | } 133 | 134 | builder.append("### ").append( 135 | Optional.ofNullable(fileChange.project()). 136 | map(project -> "[" + project.name() + "](" + project.url() + ")"). 137 | orElse("Deleted project") 138 | ).append(" (").append(oldDisplayName).append('⟶').append(newDisplayName).append(')'). 139 | append(System.lineSeparator()); 140 | 141 | List entries = new ArrayList<>(changelogEntries.entries()); 142 | 143 | if (entries.isEmpty()) { 144 | return; 145 | } 146 | 147 | final int maxEntries = getOptions().maxEntryCount; 148 | final int extraEntries = maxEntries == 0 ? 0 : entries.size() - maxEntries; 149 | 150 | if (extraEntries > 0) { 151 | entries = entries.subList(0, maxEntries); 152 | } 153 | 154 | for (ChangelogEntry entry : entries) { 155 | builder.append(System.lineSeparator()).append("#### [").append(entry.title()). 156 | append("](").append(entry.url()).append(')').append(System.lineSeparator()); 157 | 158 | List entryLines; 159 | 160 | if (JsoupUtils.isEmpty(entry.entry())) { 161 | entryLines = Lists.newArrayList("No changelog available."); 162 | } else { 163 | entryLines = Lists.newArrayList( 164 | lineSeparatorSplitter.split(JsoupUtils.getPlainText(entry.entry())) 165 | ); 166 | } 167 | 168 | //Remove all lines with images. 169 | entryLines = entryLines.stream(). 170 | filter(line -> !image.matcher(line).matches()). 171 | collect(Collectors.toCollection(ArrayList::new)); 172 | 173 | final int maxLines = getOptions().maxEntryLineCount; 174 | 175 | if (maxLines != 0 && entryLines.size() > maxLines) { 176 | final int extra = entryLines.size() - maxLines; 177 | entryLines.set( 178 | maxLines, 179 | "[(" + extra + " more line" + (extra == 1 ? ")](" : "s)](") + entry.url() + 180 | ')' 181 | ); 182 | entryLines = entryLines.subList(0, maxLines + 1); 183 | } 184 | 185 | boolean wasListItem = false; 186 | 187 | for (int i = 0; i < entryLines.size(); i++) { 188 | final String line = entryLines.get(i); 189 | 190 | builder.append(System.lineSeparator()); 191 | 192 | if (!line.isEmpty()) { 193 | //For consistency, we change "- " and "+ " to "* " at line beginnings. 194 | if ((line.startsWith("- ") || line.startsWith("+ ") || line.startsWith("* ")) && 195 | line.length() > 2) { 196 | builder.append("* ").append(line.substring(2).trim()); 197 | wasListItem = true; 198 | } else { 199 | if (wasListItem) { 200 | builder.append(System.lineSeparator()); 201 | wasListItem = false; 202 | } 203 | 204 | builder.append(line); 205 | 206 | if (i != entryLines.size() - 1) { 207 | builder.append(System.lineSeparator()); 208 | } 209 | } 210 | } 211 | } 212 | 213 | builder.append(System.lineSeparator()); 214 | } 215 | 216 | if (extraEntries > 0) { 217 | //This is guaranteed to be non-null, but we have to appease the static code analysis 218 | //tools. 219 | final CurseProject project = fileChange.project(); 220 | 221 | if (project == null) { 222 | builder.append(System.lineSeparator()).append("#### ").append(extraEntries). 223 | append(" more entr").append(extraEntries == 1 ? "y" : "ies"); 224 | return; 225 | } 226 | 227 | builder.append(System.lineSeparator()).append("#### [").append(extraEntries). 228 | append(" more entr").append(extraEntries == 1 ? "y](" : "ies]("). 229 | append(project.url()).append("/files/all)"). 230 | append(System.lineSeparator()); 231 | } 232 | } 233 | 234 | /** 235 | * {@inheritDoc} 236 | */ 237 | @Override 238 | protected void appendTail(StringBuilder builder) { 239 | builder.append("Generated using [ChangelogGenerator ").append(VERSION). 240 | append("](https://github.com/TheRandomLabs/ChangelogGenerator)."); 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /src/main/java/com/therandomlabs/changeloggenerator/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019-2021 TheRandomLabs 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | /** 25 | * The main ChangelogGenerator package. 26 | */ 27 | package com.therandomlabs.changeloggenerator; 28 | -------------------------------------------------------------------------------- /src/main/java/com/therandomlabs/changeloggenerator/provider/ActuallyAdditionsProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019-2021 TheRandomLabs 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | package com.therandomlabs.changeloggenerator.provider; 25 | 26 | import java.util.SortedSet; 27 | import java.util.TreeSet; 28 | 29 | import com.google.common.base.Splitter; 30 | import com.therandomlabs.changeloggenerator.ChangelogEntry; 31 | import com.therandomlabs.changeloggenerator.CommonMarkUtils; 32 | import com.therandomlabs.curseapi.CurseException; 33 | import com.therandomlabs.curseapi.file.BasicCurseFile; 34 | import com.therandomlabs.curseapi.file.CurseFile; 35 | import com.therandomlabs.curseapi.file.CurseFileChange; 36 | import com.therandomlabs.curseapi.file.CurseFiles; 37 | import com.therandomlabs.curseapi.game.CurseGameVersionGroup; 38 | import com.therandomlabs.curseapi.minecraft.MCVersion; 39 | import com.therandomlabs.curseapi.util.OkHttpUtils; 40 | import okhttp3.HttpUrl; 41 | import org.checkerframework.checker.nullness.qual.Nullable; 42 | import org.jsoup.nodes.Element; 43 | 44 | /** 45 | * An implementation of {@link ChangelogProvider} for Actually Additions. 46 | */ 47 | public final class ActuallyAdditionsProvider implements ChangelogProvider { 48 | /** 49 | * The singleton instance of {@link ActuallyAdditionsProvider}. 50 | */ 51 | public static final ActuallyAdditionsProvider instance = new ActuallyAdditionsProvider(); 52 | 53 | private static final Splitter hyphenSplitter = Splitter.on('-'); 54 | 55 | private static final HttpUrl CHANGELOG_URL = HttpUrl.get( 56 | "https://raw.githubusercontent.com/Ellpeck/ActuallyAdditions/main/update/changelog.md" 57 | ); 58 | 59 | private ActuallyAdditionsProvider() {} 60 | 61 | /** 62 | * {@inheritDoc} 63 | */ 64 | @Nullable 65 | @Override 66 | public SortedSet getChangelog( 67 | CurseFileChange fileChange, 68 | CurseGameVersionGroup fallbackVersionGroup 69 | ) throws CurseException { 70 | if (fileChange.projectID() != 228404) { 71 | return null; 72 | } 73 | 74 | final CurseFiles files = 75 | ChangelogProvider.getFilesBetweenInclusive(fileChange, fallbackVersionGroup); 76 | final int oldVersion = getVersion(files.last()); 77 | final int newVersion = getVersion(files.first()); 78 | 79 | final Element fullChangelog = CommonMarkUtils.parseElement(OkHttpUtils.read(CHANGELOG_URL)); 80 | final SortedSet changelog = new TreeSet<>(); 81 | 82 | boolean newVersionFound = false; 83 | int index = 0; 84 | String version = ""; 85 | 86 | for (Element element : fullChangelog.children()) { 87 | if ("h1".equals(element.tagName())) { 88 | version = element.text(); 89 | 90 | final int currentVersionInt = getVersion(version); 91 | 92 | if (newVersionFound) { 93 | if (currentVersionInt <= oldVersion) { 94 | break; 95 | } 96 | } else if (currentVersionInt <= newVersion) { 97 | newVersionFound = true; 98 | } 99 | 100 | continue; 101 | } 102 | 103 | if (newVersionFound && "ul".equals(element.tagName())) { 104 | changelog.add(new ChangelogEntry( 105 | index++, "Actually Additions " + version, CHANGELOG_URL, element 106 | )); 107 | } 108 | } 109 | 110 | return changelog; 111 | } 112 | 113 | private static int getVersion(CurseFile file) { 114 | return getVersion( 115 | file.displayName().replaceAll("ActuallyAdditions-([\\d.]+-r\\d+).*", "$1") 116 | ); 117 | } 118 | 119 | private static int getVersion(String version) { 120 | return Integer.parseInt(hyphenSplitter.splitToList(version).get(1).substring(1)); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/com/therandomlabs/changeloggenerator/provider/BiomesOPlentyProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019-2021 TheRandomLabs 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | package com.therandomlabs.changeloggenerator.provider; 25 | 26 | import java.util.SortedSet; 27 | import java.util.TreeSet; 28 | 29 | import com.google.common.base.Splitter; 30 | import com.therandomlabs.changeloggenerator.ChangelogEntry; 31 | import com.therandomlabs.curseapi.CurseException; 32 | import com.therandomlabs.curseapi.file.BasicCurseFile; 33 | import com.therandomlabs.curseapi.file.CurseFile; 34 | import com.therandomlabs.curseapi.file.CurseFileChange; 35 | import com.therandomlabs.curseapi.file.CurseFiles; 36 | import com.therandomlabs.curseapi.game.CurseGameVersionGroup; 37 | import com.therandomlabs.curseapi.minecraft.MCVersion; 38 | import org.checkerframework.checker.nullness.qual.Nullable; 39 | import org.jsoup.nodes.Element; 40 | 41 | /** 42 | * An implementation of {@link ChangelogProvider} for Biomes O' Plenty. 43 | */ 44 | public final class BiomesOPlentyProvider implements ChangelogProvider { 45 | /** 46 | * The singleton instance of {@link BiomesOPlentyProvider}. 47 | */ 48 | public static final BiomesOPlentyProvider instance = new BiomesOPlentyProvider(); 49 | 50 | private static final Splitter LINE_SEPARATOR_SPLITTER = Splitter.on(System.lineSeparator()); 51 | private static final Splitter FULL_STOP_SPLITTER = Splitter.on('.'); 52 | 53 | private BiomesOPlentyProvider() {} 54 | 55 | /** 56 | * {@inheritDoc} 57 | */ 58 | @Nullable 59 | @Override 60 | public SortedSet getChangelog( 61 | CurseFileChange fileChange, 62 | CurseGameVersionGroup fallbackVersionGroup 63 | ) throws CurseException { 64 | if (fileChange.projectID() != 220318) { 65 | return null; 66 | } 67 | 68 | final CurseFiles files = 69 | ChangelogProvider.getFilesBetweenInclusive(fileChange, fallbackVersionGroup); 70 | final CurseFile newFile = files.first(); 71 | final String fullChangelog = newFile.changelogPlainText(); 72 | 73 | if (!fullChangelog.startsWith("Build: ")) { 74 | return null; 75 | } 76 | 77 | final int oldVersion = getVersionInt(files.last().displayName()); 78 | 79 | final SortedSet changelog = new TreeSet<>(); 80 | 81 | int index = 0; 82 | String version = ""; 83 | Element entry = new Element("div"); 84 | Element list = null; 85 | 86 | for (String line : LINE_SEPARATOR_SPLITTER.split(fullChangelog)) { 87 | if (line.startsWith("Build: ")) { 88 | version = getVersion(line); 89 | 90 | if (getVersionInt(line) <= oldVersion) { 91 | break; 92 | } 93 | 94 | continue; 95 | } 96 | 97 | if (!line.startsWith("=")) { 98 | if (line.isEmpty()) { 99 | entry.append("
"); 100 | continue; 101 | } 102 | 103 | if (line.endsWith(":")) { 104 | entry.appendText(line); 105 | } else { 106 | if (list == null) { 107 | list = new Element("ul"); 108 | entry.appendChild(list); 109 | } 110 | 111 | list.appendChild(new Element("li").text(line.trim())); 112 | } 113 | 114 | continue; 115 | } 116 | 117 | changelog.add(new ChangelogEntry( 118 | index++, "Biomes O' Plenty " + version, newFile.url(), entry 119 | )); 120 | entry = new Element("div"); 121 | list = null; 122 | } 123 | 124 | return changelog; 125 | } 126 | 127 | private static String getVersion(String string) { 128 | return string.replaceAll(".*((\\d+\\.){2}\\d+-(\\d+\\.){3}\\d+).*", "$1"); 129 | } 130 | 131 | private static int getVersionInt(String string) { 132 | return Integer.parseInt(FULL_STOP_SPLITTER.splitToList(getVersion(string)).get(5)); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/com/therandomlabs/changeloggenerator/provider/ChangelogProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019-2021 TheRandomLabs 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | package com.therandomlabs.changeloggenerator.provider; 25 | 26 | import java.util.Collection; 27 | import java.util.Collections; 28 | import java.util.SortedSet; 29 | 30 | import com.therandomlabs.changeloggenerator.ChangelogEntry; 31 | import com.therandomlabs.curseapi.CurseException; 32 | import com.therandomlabs.curseapi.file.BasicCurseFile; 33 | import com.therandomlabs.curseapi.file.CurseFile; 34 | import com.therandomlabs.curseapi.file.CurseFileChange; 35 | import com.therandomlabs.curseapi.file.CurseFileFilter; 36 | import com.therandomlabs.curseapi.file.CurseFiles; 37 | import com.therandomlabs.curseapi.game.CurseGameVersionGroup; 38 | import com.therandomlabs.curseapi.minecraft.MCVersion; 39 | import org.checkerframework.checker.nullness.qual.Nullable; 40 | import org.jsoup.nodes.Element; 41 | 42 | /** 43 | * Provides changelogs for {@link CurseFileChange}s. 44 | */ 45 | public interface ChangelogProvider { 46 | /** 47 | * Returns the changelog for the specified {@link CurseFileChange}. 48 | * 49 | * @param fileChange a {@link CurseFileChange}. 50 | * @param fallbackVersionGroup the {@link CurseGameVersionGroup} to use if a game version group 51 | * is necessary and cannot be determined. 52 | * @return a {@link SortedSet} of {@link ChangelogEntry} instances that represent 53 | * the changelog for the specified {@link CurseFileChange}. If the {@link CurseFileChange} 54 | * is not supported by this implementation of {@link ChangelogProvider}, 55 | * {@code null} may be returned. 56 | * @throws CurseException if an error occurs. 57 | */ 58 | @Nullable 59 | default SortedSet getChangelog( 60 | CurseFileChange fileChange, 61 | CurseGameVersionGroup fallbackVersionGroup 62 | ) throws CurseException { 63 | return null; 64 | } 65 | 66 | /** 67 | * Performs modifications on a retrieved changelog. 68 | * 69 | * @param fileChange a {@link CurseFileChange}. 70 | * @param changelog an {@link Element} that contains a changelog. 71 | * @return the modified changelog. 72 | * @throws CurseException if an error occurs. 73 | */ 74 | default Element processChangelog( 75 | CurseFileChange fileChange, Element changelog 76 | ) throws CurseException { 77 | return changelog; 78 | } 79 | 80 | /** 81 | * Calls {@link CurseFileChange#filesBetweenInclusive()} on the specified 82 | * {@link CurseFileChange}, then filters the returned {@link CurseFiles} to only 83 | * include files from the relevant game version groups. 84 | * 85 | * @param fileChange a {@link CurseFileChange}. 86 | * @param fallbackVersionGroup the {@link CurseGameVersionGroup} to use if a game version group 87 | * is necessary and cannot be determined. 88 | * @return {@link CurseFileChange#filesBetweenInclusive()} filtered to only include 89 | * files from relevant game version groups. 90 | * @throws CurseException if an error occurs. 91 | */ 92 | @SuppressWarnings("NullAway") 93 | static CurseFiles getFilesBetweenInclusive( 94 | CurseFileChange fileChange, 95 | CurseGameVersionGroup fallbackVersionGroup 96 | ) throws CurseException { 97 | final CurseFiles files = fileChange.filesBetweenInclusive(); 98 | Collection> versionGroup; 99 | 100 | try { 101 | versionGroup = fileChange.get(CurseFile::gameVersionGroups); 102 | } catch (CurseException ex) { 103 | versionGroup = Collections.singleton(fallbackVersionGroup); 104 | } 105 | 106 | new CurseFileFilter().gameVersionGroups(versionGroup).apply(files); 107 | return files; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/com/therandomlabs/changeloggenerator/provider/CurseChangelogProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019-2021 TheRandomLabs 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | package com.therandomlabs.changeloggenerator.provider; 25 | 26 | import java.util.Collection; 27 | import java.util.Collections; 28 | import java.util.SortedSet; 29 | import java.util.TreeSet; 30 | import java.util.stream.Collectors; 31 | 32 | import com.therandomlabs.changeloggenerator.ChangelogEntry; 33 | import com.therandomlabs.curseapi.CurseException; 34 | import com.therandomlabs.curseapi.file.BasicCurseFile; 35 | import com.therandomlabs.curseapi.file.CurseFile; 36 | import com.therandomlabs.curseapi.file.CurseFileChange; 37 | import com.therandomlabs.curseapi.file.CurseFileFilter; 38 | import com.therandomlabs.curseapi.file.CurseFiles; 39 | import com.therandomlabs.curseapi.game.CurseGameVersionGroup; 40 | import com.therandomlabs.curseapi.minecraft.MCVersion; 41 | 42 | /** 43 | * A generic implementation of {@link ChangelogProvider} for all CurseForge files. 44 | */ 45 | public final class CurseChangelogProvider implements ChangelogProvider { 46 | /** 47 | * The singleton instance of {@link CurseChangelogProvider}. 48 | */ 49 | public static final CurseChangelogProvider instance = new CurseChangelogProvider(); 50 | 51 | private CurseChangelogProvider() {} 52 | 53 | /** 54 | * {@inheritDoc} 55 | */ 56 | @SuppressWarnings("NullAway") 57 | @Override 58 | public SortedSet getChangelog( 59 | CurseFileChange fileChange, 60 | CurseGameVersionGroup fallbackVersionGroup 61 | ) throws CurseException { 62 | final CurseFiles files = fileChange.filesBetween(); 63 | 64 | Collection> versionGroup; 65 | 66 | try { 67 | versionGroup = fileChange.get(CurseFile::gameVersionGroups); 68 | } catch (CurseException ex) { 69 | versionGroup = Collections.singleton(fallbackVersionGroup); 70 | } 71 | 72 | new CurseFileFilter(). 73 | gameVersionGroups(versionGroup). 74 | apply(files); 75 | return files.parallelMap( 76 | file -> new ChangelogEntry(file, file.displayName(), file.url(), file.changelog()), 77 | Collectors.toCollection(TreeSet::new) 78 | ); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/therandomlabs/changeloggenerator/provider/McJtyProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019-2021 TheRandomLabs 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | package com.therandomlabs.changeloggenerator.provider; 25 | 26 | import com.therandomlabs.curseapi.CurseException; 27 | import com.therandomlabs.curseapi.file.BasicCurseFile; 28 | import com.therandomlabs.curseapi.file.CurseFileChange; 29 | import com.therandomlabs.curseapi.project.CurseProject; 30 | import org.jsoup.nodes.Element; 31 | 32 | /** 33 | * An implementation of {@link ChangelogProvider} for McJty's mods. 34 | */ 35 | public final class McJtyProvider implements ChangelogProvider { 36 | /** 37 | * The singleton instance of {@link McJtyProvider}. 38 | */ 39 | public static final McJtyProvider instance = new McJtyProvider(); 40 | 41 | private McJtyProvider() {} 42 | 43 | /** 44 | * {@inheritDoc} 45 | */ 46 | @Override 47 | public Element processChangelog( 48 | CurseFileChange fileChange, Element changelog 49 | ) throws CurseException { 50 | final CurseProject project = fileChange.project(); 51 | 52 | if (project == null) { 53 | return changelog; 54 | } 55 | 56 | if (!"McJty".equals(project.author().name())) { 57 | return changelog; 58 | } 59 | 60 | //McJty puts a heading in each of his changelogs with the file version. 61 | changelog.textNodes().get(0).remove(); 62 | 63 | return changelog; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/therandomlabs/changeloggenerator/provider/MezzProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019-2021 TheRandomLabs 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | package com.therandomlabs.changeloggenerator.provider; 25 | 26 | import java.util.List; 27 | import java.util.SortedSet; 28 | import java.util.TreeSet; 29 | import java.util.regex.Pattern; 30 | import java.util.stream.Collectors; 31 | 32 | import com.google.common.base.Splitter; 33 | import com.therandomlabs.changeloggenerator.ChangelogEntry; 34 | import com.therandomlabs.curseapi.CurseException; 35 | import com.therandomlabs.curseapi.file.BasicCurseFile; 36 | import com.therandomlabs.curseapi.file.CurseFile; 37 | import com.therandomlabs.curseapi.file.CurseFileChange; 38 | import com.therandomlabs.curseapi.file.CurseFiles; 39 | import com.therandomlabs.curseapi.game.CurseGameVersionGroup; 40 | import com.therandomlabs.curseapi.minecraft.MCVersion; 41 | import org.checkerframework.checker.nullness.qual.Nullable; 42 | import org.jsoup.nodes.Element; 43 | 44 | /** 45 | * An implementation of {@link ChangelogProvider} for mezz's mods. 46 | */ 47 | public final class MezzProvider implements ChangelogProvider { 48 | /** 49 | * The singleton instance of {@link MezzProvider}. 50 | */ 51 | public static final MezzProvider instance = new MezzProvider(); 52 | 53 | private static final String VERSION_REGEX = "[a-z-_]+[\\d.]+-([\\d]+\\.[\\d]+\\.[\\d]+).*"; 54 | 55 | private static final Splitter lineSeparatorSplitter = Splitter.on(System.lineSeparator()). 56 | omitEmptyStrings(). 57 | trimResults(); 58 | private static final Pattern versionHeading = Pattern.compile("^v[\\d.]+$"); 59 | 60 | private MezzProvider() {} 61 | 62 | /** 63 | * {@inheritDoc} 64 | */ 65 | @Nullable 66 | @Override 67 | public SortedSet getChangelog( 68 | CurseFileChange fileChange, 69 | CurseGameVersionGroup fallbackVersionGroup 70 | ) throws CurseException { 71 | if (fileChange.projectID() != 59751 && fileChange.projectID() != 223525 && 72 | fileChange.projectID() != 238222) { 73 | return null; 74 | } 75 | 76 | final String projectName = fileChange.get(file -> file.project().name()); 77 | final CurseFiles files = 78 | ChangelogProvider.getFilesBetweenInclusive(fileChange, fallbackVersionGroup); 79 | final CurseFile oldFile = files.last(); 80 | final CurseFile newFile = files.first(); 81 | 82 | List oldChangelog = lineSeparatorSplitter.splitToList(oldFile.changelogPlainText()); 83 | final List cleanOldChangelog = oldChangelog.stream(). 84 | map(line -> line.replaceAll("^\\*\\s+", "")). 85 | collect(Collectors.toList()); 86 | 87 | List newChangelog = lineSeparatorSplitter.splitToList(newFile.changelogPlainText()); 88 | //Remove all lines that are in the old changelog. 89 | newChangelog = newChangelog.stream(). 90 | map(line -> line.replaceAll("^\\*\\s+", "")). 91 | filter(line -> !cleanOldChangelog.contains(line)). 92 | collect(Collectors.toList()); 93 | String cleanNewChangelog = String.join(System.lineSeparator(), newChangelog); 94 | 95 | final String oldVersion = oldFile.displayName().replaceAll(VERSION_REGEX, "$1"); 96 | final String newVersion = newFile.displayName().replaceAll(VERSION_REGEX, "$1"); 97 | 98 | final SortedSet changelog = new TreeSet<>(); 99 | 100 | int index = 0; 101 | String version = ""; 102 | Element entry = new Element("div"); 103 | Element list = null; 104 | 105 | for (String line : lineSeparatorSplitter.split(cleanNewChangelog)) { 106 | if (line.startsWith("Current release ")) { 107 | version = newVersion; 108 | continue; 109 | } 110 | 111 | if (!versionHeading.matcher(line).matches()) { 112 | if (version.isEmpty()) { 113 | continue; 114 | } 115 | 116 | if (list == null) { 117 | list = new Element("ul"); 118 | entry.appendChild(list); 119 | } 120 | 121 | list.appendChild(new Element("li").text(line)); 122 | continue; 123 | } 124 | 125 | final String nextVersion = line.substring(1); 126 | 127 | if (version.isEmpty()) { 128 | version = nextVersion; 129 | continue; 130 | } 131 | 132 | if (version.equals(nextVersion)) { 133 | continue; 134 | } 135 | 136 | changelog.add(new ChangelogEntry( 137 | index++, projectName + ' ' + version, newFile.url(), entry 138 | )); 139 | 140 | if (nextVersion.equals(oldVersion) || newVersion.equals(oldVersion)) { 141 | break; 142 | } 143 | 144 | version = nextVersion; 145 | entry = new Element("div"); 146 | list = null; 147 | } 148 | 149 | if (list != null) { 150 | changelog.add(new ChangelogEntry( 151 | index, projectName + ' ' + version, newFile.url(), entry 152 | )); 153 | } 154 | 155 | return changelog; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/com/therandomlabs/changeloggenerator/provider/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019-2021 TheRandomLabs 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | /** 25 | * This package contains {@link com.therandomlabs.changeloggenerator.provider.ChangelogProvider} 26 | * and its implementations. 27 | */ 28 | package com.therandomlabs.changeloggenerator.provider; 29 | -------------------------------------------------------------------------------- /src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/test/java/com/therandomlabs/changeloggenerator/ChangelogGeneratorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019-2021 TheRandomLabs 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | package com.therandomlabs.changeloggenerator; 25 | 26 | import static org.assertj.core.api.Assertions.assertThat; 27 | import static org.assertj.core.api.Assertions.assertThatThrownBy; 28 | 29 | import java.nio.file.Path; 30 | import java.util.Optional; 31 | 32 | import com.therandomlabs.changeloggenerator.provider.CurseChangelogProvider; 33 | import com.therandomlabs.curseapi.CurseAPI; 34 | import com.therandomlabs.curseapi.CurseException; 35 | import com.therandomlabs.curseapi.file.BasicCurseFile; 36 | import com.therandomlabs.curseapi.file.CurseFilesComparison; 37 | import com.therandomlabs.curseapi.minecraft.modpack.CurseModpack; 38 | import com.therandomlabs.utils.io.ZipFile; 39 | import org.junit.jupiter.api.Test; 40 | import org.junit.jupiter.api.io.TempDir; 41 | import org.slf4j.LoggerFactory; 42 | 43 | class ChangelogGeneratorTest { 44 | @Test 45 | void exceptionShouldBeThrownIfDuplicateProviderIsRegistered() { 46 | assertThatThrownBy(() -> new BasicChangelogGenerator().withProvider( 47 | CurseChangelogProvider.instance 48 | )).isInstanceOf(IllegalArgumentException.class). 49 | hasMessageContaining("should not already be registered"); 50 | } 51 | 52 | @Test 53 | void atm4ChangelogIsValid(@TempDir Path tempDirectory) throws Exception { 54 | final Optional optionalOldModpackPath = 55 | CurseAPI.downloadFileToDirectory(316059, 2839369, tempDirectory); 56 | assertThat(optionalOldModpackPath).isPresent(); 57 | 58 | final Path oldModpackPath = optionalOldModpackPath.get(); 59 | assertThat(oldModpackPath).isRegularFile(); 60 | 61 | final Optional optionalNewModpackPath = 62 | CurseAPI.downloadFileToDirectory(316059, 2844397, tempDirectory); 63 | assertThat(optionalNewModpackPath).isPresent(); 64 | 65 | final Path newModpackPath = optionalNewModpackPath.get(); 66 | assertThat(newModpackPath).isRegularFile(); 67 | 68 | try ( 69 | ZipFile oldModpackZipFile = new ZipFile(oldModpackPath); 70 | ZipFile newModpackZipFile = new ZipFile(newModpackPath) 71 | ) { 72 | final CurseModpack oldModpack = 73 | CurseModpack.fromJSON(oldModpackZipFile.getEntry("manifest.json")); 74 | final CurseModpack newModpack = 75 | CurseModpack.fromJSON(newModpackZipFile.getEntry("manifest.json")); 76 | 77 | final String basic = new BasicChangelogGenerator().generate(oldModpack, newModpack); 78 | assertThat(basic).isNotEmpty(); 79 | 80 | final String markdown = 81 | new MarkdownChangelogGenerator().generate(oldModpack, newModpack); 82 | assertThat(markdown).isNotEmpty(); 83 | 84 | final CurseFilesComparison comparison = 85 | CurseFilesComparison.of(oldModpack.files(), newModpack.files()); 86 | final ChangelogEntries entries = new BasicChangelogGenerator().getChangelogEntries( 87 | comparison.updated().iterator().next(), oldModpack.mcVersion().versionGroup() 88 | ); 89 | 90 | assertThat(entries).isNotNull(); 91 | assertThat(entries.toString()).isNotEmpty(); 92 | assertThat(entries.entries()).isNotEmpty(); 93 | assertThat(entries.entries().first().toString()).isNotEmpty(); 94 | 95 | LoggerFactory.getLogger(getClass()).info(basic); 96 | LoggerFactory.getLogger(getClass()).info(markdown); 97 | } 98 | } 99 | 100 | @Test 101 | void actuallyAdditionsChangelogIsValid() throws CurseException { 102 | testProject(228404, 2621643, 2844115); 103 | } 104 | 105 | @Test 106 | void biomesOPlentyChangelogIsValid() throws CurseException { 107 | testProject(220318, 2814404, 2846238); 108 | } 109 | 110 | @Test 111 | void jeiChangelogIsValid() throws CurseException { 112 | testProject(238222, 2822850, 2847274); 113 | testProject(238222, 3136590, 3192904); 114 | } 115 | 116 | @Test 117 | void rfToolsChangelogIsValid() throws CurseException { 118 | testProject(224641, 2672513, 2745848); 119 | } 120 | 121 | private static void testProject(int projectID, int oldFileID, int newFileID) 122 | throws CurseException { 123 | final CurseModpack oldModpack = CurseModpack.createEmpty(); 124 | oldModpack.name("Old Test Modpack"); 125 | oldModpack.basicFiles().add(new BasicCurseFile.Immutable(projectID, oldFileID)); 126 | 127 | final CurseModpack newModpack = CurseModpack.createEmpty(); 128 | newModpack.name("New Test Modpack"); 129 | newModpack.basicFiles().add(new BasicCurseFile.Immutable(projectID, newFileID)); 130 | 131 | final String changelog = new BasicChangelogGenerator().generate(oldModpack, newModpack); 132 | assertThat(changelog).isNotEmpty(); 133 | 134 | LoggerFactory.getLogger(ChangelogGeneratorTest.class).info("\n{}", changelog); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/test/resources/log4j2-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | --------------------------------------------------------------------------------