Repository: gleam-lang/gleam
Branch: main
Commit: 56c224a84420
Files: 4379
Total size: 8.4 MB
Directory structure:
gitextract_2kwl3uaf/
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ ├── actions/
│ │ ├── build-container/
│ │ │ └── action.yml
│ │ └── build-release/
│ │ └── action.yml
│ ├── dependabot.yml
│ ├── pull_request_template.md
│ └── workflows/
│ ├── ci.yaml
│ ├── release-containers.yaml
│ ├── release-nightly.yaml
│ └── release.yaml
├── .gitignore
├── .vscode/
│ └── settings.json
├── .well-known/
│ └── funding-manifest-urls
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Cargo.toml
├── Cross.toml
├── LICENCE
├── Makefile
├── README.md
├── RELEASE.md
├── benchmark/
│ └── list/
│ ├── .gitignore
│ ├── Makefile
│ ├── gleam.toml
│ ├── manifest.toml
│ ├── src/
│ │ └── list.gleam
│ └── test/
│ ├── benchmarks.gleam
│ └── list_test.gleam
├── bin/
│ └── add-nightly-suffix-to-versions.sh
├── changelog/
│ ├── v1.1.md
│ ├── v1.10.md
│ ├── v1.11.md
│ ├── v1.12.md
│ ├── v1.13.md
│ ├── v1.14.md
│ ├── v1.15.md
│ ├── v1.2.md
│ ├── v1.3.md
│ ├── v1.4.md
│ ├── v1.5.md
│ ├── v1.6.md
│ ├── v1.7.md
│ ├── v1.8.md
│ └── v1.9.md
├── compiler-cli/
│ ├── Cargo.toml
│ ├── clippy.toml
│ ├── src/
│ │ ├── add.rs
│ │ ├── beam_compiler.rs
│ │ ├── build.rs
│ │ ├── build_lock.rs
│ │ ├── cli.rs
│ │ ├── compile_package.rs
│ │ ├── config.rs
│ │ ├── dependencies/
│ │ │ ├── dependency_manager.rs
│ │ │ ├── snapshots/
│ │ │ │ ├── gleam_cli__dependencies__tests__pretty_print_major_versions_available.snap
│ │ │ │ └── gleam_cli__dependencies__tests__pretty_print_version_updates.snap
│ │ │ └── tests.rs
│ │ ├── dependencies.rs
│ │ ├── docs.rs
│ │ ├── export.rs
│ │ ├── fix.rs
│ │ ├── format.rs
│ │ ├── fs/
│ │ │ └── tests.rs
│ │ ├── fs.rs
│ │ ├── hex/
│ │ │ └── auth.rs
│ │ ├── hex.rs
│ │ ├── http.rs
│ │ ├── lib.rs
│ │ ├── lsp.rs
│ │ ├── new/
│ │ │ ├── snapshots/
│ │ │ │ ├── gleam_cli__new__tests__new_with_default_template@src__my_project.gleam.snap
│ │ │ │ ├── gleam_cli__new__tests__new_with_default_template@test__my_project_test.gleam.snap
│ │ │ │ ├── gleam_cli__new__tests__new_with_javascript_template@src__my_project.gleam.snap
│ │ │ │ └── gleam_cli__new__tests__new_with_javascript_template@test__my_project_test.gleam.snap
│ │ │ └── tests.rs
│ │ ├── new.rs
│ │ ├── owner.rs
│ │ ├── panic.rs
│ │ ├── publish.rs
│ │ ├── remove.rs
│ │ ├── run.rs
│ │ ├── shell.rs
│ │ └── text_layout.rs
│ ├── templates/
│ │ ├── erlang-shipment-entrypoint.ps1
│ │ ├── erlang-shipment-entrypoint.sh
│ │ └── gleam@@compile.erl
│ └── test/
│ └── hello_world/
│ ├── .gitignore
│ ├── gleam.toml
│ └── manifest.toml
├── compiler-core/
│ ├── Cargo.toml
│ ├── clippy.toml
│ ├── src/
│ │ ├── analyse/
│ │ │ ├── imports.rs
│ │ │ ├── name.rs
│ │ │ └── tests.rs
│ │ ├── analyse.rs
│ │ ├── ast/
│ │ │ ├── constant.rs
│ │ │ ├── tests.rs
│ │ │ ├── typed.rs
│ │ │ ├── untyped.rs
│ │ │ └── visit.rs
│ │ ├── ast.rs
│ │ ├── ast_folder.rs
│ │ ├── bit_array.rs
│ │ ├── build/
│ │ │ ├── elixir_libraries.rs
│ │ │ ├── module_loader/
│ │ │ │ └── tests.rs
│ │ │ ├── module_loader.rs
│ │ │ ├── native_file_copier/
│ │ │ │ └── tests.rs
│ │ │ ├── native_file_copier.rs
│ │ │ ├── package_compiler/
│ │ │ │ ├── snapshots/
│ │ │ │ │ ├── gleam_core__build__package_compiler__tests__different_packages_defining_duplicate_module.snap
│ │ │ │ │ └── gleam_core__build__package_compiler__tests__same_package_defining_duplicate_module.snap
│ │ │ │ └── tests.rs
│ │ │ ├── package_compiler.rs
│ │ │ ├── package_loader/
│ │ │ │ └── tests.rs
│ │ │ ├── package_loader.rs
│ │ │ ├── project_compiler.rs
│ │ │ ├── telemetry.rs
│ │ │ └── tests.rs
│ │ ├── build.rs
│ │ ├── call_graph/
│ │ │ └── into_dependency_order_tests.rs
│ │ ├── call_graph.rs
│ │ ├── codegen.rs
│ │ ├── config/
│ │ │ └── stale_package_remover.rs
│ │ ├── config.rs
│ │ ├── dep_tree.rs
│ │ ├── dependency.rs
│ │ ├── derivation_tree.rs
│ │ ├── diagnostic.rs
│ │ ├── docs/
│ │ │ ├── printer.rs
│ │ │ ├── snapshots/
│ │ │ │ ├── gleam_core__docs__tests__canonical_link.snap
│ │ │ │ ├── gleam_core__docs__tests__constructor_with_long_types_and_many_fields.snap
│ │ │ │ ├── gleam_core__docs__tests__constructor_with_long_types_and_many_fields_that_need_splitting.snap
│ │ │ │ ├── gleam_core__docs__tests__discarded_arguments_are_not_shown.snap
│ │ │ │ ├── gleam_core__docs__tests__docs_of_a_type_constructor_are_not_used_by_the_following_function.snap
│ │ │ │ ├── gleam_core__docs__tests__function_uses_reexport_of_internal_type.snap
│ │ │ │ ├── gleam_core__docs__tests__function_uses_reexport_of_internal_type_in_other_module.snap
│ │ │ │ ├── gleam_core__docs__tests__generated_type_variables.snap
│ │ │ │ ├── gleam_core__docs__tests__generated_type_variables_do_not_take_into_account_other_definitions.snap
│ │ │ │ ├── gleam_core__docs__tests__generated_type_variables_mixed_with_existing_variables.snap
│ │ │ │ ├── gleam_core__docs__tests__generated_type_variables_with_existing_variables_coming_afterwards.snap
│ │ │ │ ├── gleam_core__docs__tests__hello_docs.snap
│ │ │ │ ├── gleam_core__docs__tests__highlight_constant_definition.snap
│ │ │ │ ├── gleam_core__docs__tests__highlight_custom_type.snap
│ │ │ │ ├── gleam_core__docs__tests__highlight_function_definition.snap
│ │ │ │ ├── gleam_core__docs__tests__highlight_opaque_custom_type.snap
│ │ │ │ ├── gleam_core__docs__tests__highlight_type_alias.snap
│ │ │ │ ├── gleam_core__docs__tests__ignored_argument_is_called_arg.snap
│ │ │ │ ├── gleam_core__docs__tests__internal_definitions_are_not_included.snap
│ │ │ │ ├── gleam_core__docs__tests__internal_type_reexport_in_different_module.snap
│ │ │ │ ├── gleam_core__docs__tests__internal_type_reexport_in_same_module.snap
│ │ │ │ ├── gleam_core__docs__tests__internal_type_reexport_in_same_module_as_parameter.snap
│ │ │ │ ├── gleam_core__docs__tests__internal_type_reexport_in_same_module_as_parameter_colours.snap
│ │ │ │ ├── gleam_core__docs__tests__link_to_type_in_different_module.snap
│ │ │ │ ├── gleam_core__docs__tests__link_to_type_in_different_module_from_nested_module.snap
│ │ │ │ ├── gleam_core__docs__tests__link_to_type_in_different_module_from_nested_module_with_shared_path.snap
│ │ │ │ ├── gleam_core__docs__tests__link_to_type_in_different_package.snap
│ │ │ │ ├── gleam_core__docs__tests__link_to_type_in_same_module.snap
│ │ │ │ ├── gleam_core__docs__tests__long_function_with_no_arguments_parentheses_are_not_split.snap
│ │ │ │ ├── gleam_core__docs__tests__long_function_wrapping.snap
│ │ │ │ ├── gleam_core__docs__tests__markdown_code_from_function_comment_is_trimmed.snap
│ │ │ │ ├── gleam_core__docs__tests__markdown_code_from_module_comment_is_trimmed.snap
│ │ │ │ ├── gleam_core__docs__tests__markdown_code_from_standalone_pages_is_not_trimmed.snap
│ │ │ │ ├── gleam_core__docs__tests__no_hex_publish.snap
│ │ │ │ ├── gleam_core__docs__tests__no_link_to_type_in_git_dependency.snap
│ │ │ │ ├── gleam_core__docs__tests__no_link_to_type_in_path_dependency.snap
│ │ │ │ ├── gleam_core__docs__tests__no_links_to_prelude_types.snap
│ │ │ │ ├── gleam_core__docs__tests__output_of_search_data_json.snap
│ │ │ │ ├── gleam_core__docs__tests__print_qualified_names_from_other_modules.snap
│ │ │ │ ├── gleam_core__docs__tests__print_type_variables_in_function_signatures.snap
│ │ │ │ ├── gleam_core__docs__tests__public_type_reexport_in_different_internal_module.snap
│ │ │ │ ├── gleam_core__docs__tests__search_item_for_constant.snap
│ │ │ │ ├── gleam_core__docs__tests__search_item_for_custom_type.snap
│ │ │ │ ├── gleam_core__docs__tests__search_item_for_function.snap
│ │ │ │ ├── gleam_core__docs__tests__search_item_for_type_alias.snap
│ │ │ │ ├── gleam_core__docs__tests__tables.snap
│ │ │ │ └── gleam_core__docs__tests__use_reexport_from_other_package.snap
│ │ │ ├── source_links.rs
│ │ │ └── tests.rs
│ │ ├── docs.rs
│ │ ├── encryption.rs
│ │ ├── erlang/
│ │ │ ├── pattern.rs
│ │ │ ├── snapshots/
│ │ │ │ ├── gleam_core__erlang__tests__allowed_string_escapes.snap
│ │ │ │ ├── gleam_core__erlang__tests__binop_parens.snap
│ │ │ │ ├── gleam_core__erlang__tests__bit_pattern_shadowing.snap
│ │ │ │ ├── gleam_core__erlang__tests__block_assignment.snap
│ │ │ │ ├── gleam_core__erlang__tests__constant_named_module_info.snap
│ │ │ │ ├── gleam_core__erlang__tests__constant_named_module_info_imported.snap
│ │ │ │ ├── gleam_core__erlang__tests__constant_named_module_info_imported_qualified.snap
│ │ │ │ ├── gleam_core__erlang__tests__constant_named_module_info_with_function_inside.snap
│ │ │ │ ├── gleam_core__erlang__tests__constant_named_module_info_with_function_inside_imported.snap
│ │ │ │ ├── gleam_core__erlang__tests__constant_named_module_info_with_function_inside_imported_qualified.snap
│ │ │ │ ├── gleam_core__erlang__tests__discard_in_assert.snap
│ │ │ │ ├── gleam_core__erlang__tests__dynamic.snap
│ │ │ │ ├── gleam_core__erlang__tests__field_access_function_call.snap
│ │ │ │ ├── gleam_core__erlang__tests__field_access_function_call1.snap
│ │ │ │ ├── gleam_core__erlang__tests__float_division_by_literal_non_zero.snap
│ │ │ │ ├── gleam_core__erlang__tests__float_division_by_literal_zero.snap
│ │ │ │ ├── gleam_core__erlang__tests__function_argument_shadowing.snap
│ │ │ │ ├── gleam_core__erlang__tests__function_named_module_info.snap
│ │ │ │ ├── gleam_core__erlang__tests__function_named_module_info_imported.snap
│ │ │ │ ├── gleam_core__erlang__tests__function_named_module_info_imported_qualified.snap
│ │ │ │ ├── gleam_core__erlang__tests__function_named_module_info_in_constant.snap
│ │ │ │ ├── gleam_core__erlang__tests__function_named_module_info_in_constant_imported.snap
│ │ │ │ ├── gleam_core__erlang__tests__function_named_module_info_in_constant_imported_qualified.snap
│ │ │ │ ├── gleam_core__erlang__tests__guard_variable_rewriting.snap
│ │ │ │ ├── gleam_core__erlang__tests__inline_const_pattern_option.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test0_1.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test0_2.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test0_3.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test1.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test10.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test11.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test12.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test13.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test16.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test17.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test18.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test19.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test1_1.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test1_2.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test1_4.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test1_5.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test1_6.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test2.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test20.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test21.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test22.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test23.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test3.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test5.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test6.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test8.snap
│ │ │ │ ├── gleam_core__erlang__tests__integration_test9.snap
│ │ │ │ ├── gleam_core__erlang__tests__keyword_constructors.snap
│ │ │ │ ├── gleam_core__erlang__tests__keyword_constructors1.snap
│ │ │ │ ├── gleam_core__erlang__tests__negation.snap
│ │ │ │ ├── gleam_core__erlang__tests__negation_block.snap
│ │ │ │ ├── gleam_core__erlang__tests__operator_pipe_right_hand_side.snap
│ │ │ │ ├── gleam_core__erlang__tests__positive_zero.snap
│ │ │ │ ├── gleam_core__erlang__tests__recursive_type.snap
│ │ │ │ ├── gleam_core__erlang__tests__scientific_notation.snap
│ │ │ │ ├── gleam_core__erlang__tests__tail_maybe_expr_block.snap
│ │ │ │ ├── gleam_core__erlang__tests__tuple_access_in_guard.snap
│ │ │ │ ├── gleam_core__erlang__tests__type_named_else.snap
│ │ │ │ ├── gleam_core__erlang__tests__type_named_module_info.snap
│ │ │ │ ├── gleam_core__erlang__tests__variable_name_underscores_preserved.snap
│ │ │ │ └── gleam_core__erlang__tests__windows_file_escaping_bug.snap
│ │ │ ├── tests/
│ │ │ │ ├── assert.rs
│ │ │ │ ├── bit_arrays.rs
│ │ │ │ ├── case.rs
│ │ │ │ ├── conditional_compilation.rs
│ │ │ │ ├── consts.rs
│ │ │ │ ├── custom_types.rs
│ │ │ │ ├── documentation.rs
│ │ │ │ ├── echo.rs
│ │ │ │ ├── external_fn.rs
│ │ │ │ ├── functions.rs
│ │ │ │ ├── guards.rs
│ │ │ │ ├── inlining.rs
│ │ │ │ ├── let_assert.rs
│ │ │ │ ├── numbers.rs
│ │ │ │ ├── panic.rs
│ │ │ │ ├── patterns.rs
│ │ │ │ ├── pipes.rs
│ │ │ │ ├── prelude.rs
│ │ │ │ ├── records.rs
│ │ │ │ ├── reserved.rs
│ │ │ │ ├── snapshots/
│ │ │ │ │ ├── gleam_core__erlang__tests__assert__assert_binary_operation.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__assert__assert_binary_operation2.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__assert__assert_binary_operation3.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__assert__assert_binary_operator_with_side_effects.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__assert__assert_binary_operator_with_side_effects2.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__assert__assert_function_call.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__assert__assert_function_call2.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__assert__assert_literal.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__assert__assert_nested_function_call.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__assert__assert_variable.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__assert__assert_with_block_message.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__assert__assert_with_message.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__bit_array.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__bit_array1.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__bit_array2.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__bit_array3.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__bit_array4.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__bit_array5.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__bit_array_declare_and_use_var.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__bit_array_discard.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__bit_array_discard1.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__bit_array_float.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__bit_array_literal_string_constant_is_treated_as_utf8.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__bit_array_literal_string_is_treated_as_utf8.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__bit_array_literal_string_pattern_is_treated_as_utf8.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__block_in_pattern_size.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__discard_utf8_pattern.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__non_byte_aligned_size_calculation.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__operator_in_pattern_size.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__operator_in_pattern_size2.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__operator_in_pattern_size3.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__pattern_match_utf16_codepoint_little_endian.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__pattern_match_utf32_codepoint_little_endian.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__pipe_size_segment.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__unicode_bit_array_1.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__unicode_bit_array_2.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__unicode_character_encoding_in_bit_array_pattern_segment.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__utf16_codepoint_little_endian.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__bit_arrays__utf32_codepoint_little_endian.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__case__aliased_string_prefix_pattern_referenced_in_guard.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__case__alternative_patter_with_string_alias.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__case__alternative_pattern_variable_rewriting.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__case__negative_zero_pattern.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__case__not.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__case__not_two.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__case__positive_zero_pattern.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__case__spread_empty_list.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__case__spread_empty_list_assigning.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__conditional_compilation__excluded_attribute_syntax.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__conditional_compilation__included_attribute_syntax.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__consts__const_generalise.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__consts__const_type_variable.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__consts__list_prepend.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__consts__list_prepend_from_other_module.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__consts__list_prepend_literal.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__consts__pub_const_equal_to_private_function.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__consts__pub_const_equal_to_record_with_nested_private_function_field.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__consts__pub_const_equal_to_record_with_private_function_field.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__consts__record_constructor.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__consts__record_constructor_in_tuple.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__consts__use_private_in_internal.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__consts__use_private_in_list.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__consts__use_private_in_tuple.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__consts__use_qualified_pub_const_equal_to_record_with_private_function_field.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__consts__use_unqualified_pub_const_equal_to_private_function.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__consts__use_unqualified_pub_const_equal_to_record_with_private_function_field.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__custom_types__annotated_external_type.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__custom_types__annotated_external_type_used_in_function.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__custom_types__phantom.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__custom_types__unused_opaque_constructor_is_generated_correctly.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__documentation__backslashes_are_escaped_in_module_comment.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__documentation__backslashes_in_documentation_are_escaped.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__documentation__double_quotes_are_escaped_in_module_comment.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__documentation__function_with_documentation.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__documentation__function_with_multiline_documentation.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__documentation__internal_function_has_no_documentation.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__documentation__multi_line_module_comment.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__documentation__quotes_in_documentation_are_escaped.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__documentation__single_line_module_comment.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__echo__echo_in_a_pipeline.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__echo__echo_in_a_pipeline_with_message.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__echo__echo_with_a_block.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__echo__echo_with_a_case_expression.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__echo__echo_with_a_function_call.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__echo__echo_with_a_function_call_and_a_message.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__echo__echo_with_a_panic.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__echo__echo_with_a_simple_expression.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__echo__echo_with_a_simple_expression_and_a_message.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__echo__echo_with_complex_expression_as_a_message.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__echo__multiple_echos_in_a_pipeline.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__echo__multiple_echos_inside_expression.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__echo__pipeline_printed_by_echo_is_wrapped_in_begin_end_block.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__echo__record_update_printed_by_echo_is_wrapped_in_begin_end_block.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__attribute_erlang.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__attribute_javascript.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__both_externals_no_valid_impl.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__discarded_arg_in_external_are_passed_correctly.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__elixir.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__erlang_and_javascript.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__hole_parameter_erlang.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__hole_parameter_javascript.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__hole_return_erlang.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__hole_return_javascript.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__inlining_external_functions_from_another_module.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__integration_test1_3.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__integration_test7.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__javascript_only.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__javascript_only_indirect.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__multiple_discarded_args_in_external_are_passed_correctly.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__multiple_discarded_args_in_external_are_passed_correctly_2.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__no_body.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__no_body_or_implementation.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__no_gleam_impl_no_annotations_function_fault_tolerance.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__no_target_supported_function_fault_tolerance.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__no_type_annotation_for_parameter.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__no_type_annotation_for_return.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__private.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__private_external_function_calls.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__private_local_function_references.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__public_elixir.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__public_local_function_calls.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__reference_to_imported_elixir_external_fn.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__unqualified_inlining_external_functions_from_another_module.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__external_fn__unqualified_reference_to_imported_elixir_external_fn.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__functions__function_as_value.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__functions__function_called.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__functions__labelled_argument_ordering.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__functions__nested_aliased_imported_function_as_value.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__functions__nested_aliased_imported_function_called.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__functions__nested_imported_function_as_value.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__functions__nested_imported_function_called.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__functions__nested_unqualified_imported_function_as_value.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__functions__nested_unqualified_imported_function_called.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__functions__unused_private_functions.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__clause_guards.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__clause_guards20.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__clause_guards21.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__clause_guards22.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__clause_guards23.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__clause_guards24.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__clause_guards25.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__clause_guards26.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__clause_guards27.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__clause_guards28.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__clause_guards29.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__clause_guards30.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__clause_guards31.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__clause_guards32.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__clause_guards_1.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__clause_guards_10.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__clause_guards_2.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__clause_guards_3.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__clause_guards_4.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__clause_guards_5.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__clause_guards_6.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__clause_guards_7.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__clause_guards_8.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__clause_guards_9.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__constants_in_guards.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__constants_in_guards1.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__field_access.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__module_access.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__module_list_access.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__module_nested_access.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__module_string_access.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__module_tuple_access.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__nested_record_access.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__only_guards.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__only_guards1.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__only_guards2.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__guards__only_guards3.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__inlining__blocks_get_preserved_when_needed.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__inlining__blocks_get_preserved_when_needed2.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__inlining__do_not_inline_parameters_that_have_side_effects.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__inlining__do_not_inline_parameters_used_more_than_once.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__inlining__inline_anonymous_function_call.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__inlining__inline_anonymous_function_in_pipe.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__inlining__inline_function_capture_in_pipe.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__inlining__inline_function_which_calls_other_function.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__inlining__inline_function_with_use.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__inlining__inline_function_with_use_and_anonymous.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__inlining__inline_function_with_use_becomes_tail_recursive.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__inlining__inline_higher_order_function.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__inlining__inline_higher_order_function_anonymous.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__inlining__inline_higher_order_function_with_capture.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__inlining__inline_shadowed_variable.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__inlining__inline_shadowed_variable_nested.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__inlining__inline_variable_shadowed_in_case_pattern.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__inlining__inline_variable_shadowing_case_pattern.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__inlining__inline_variable_shadowing_parameter.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__inlining__inlining_works_properly_with_record_updates.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__inlining__inlining_works_through_blocks.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__inlining__parameters_from_nested_functions_are_correctly_inlined.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__assignment_pattern.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__bit_array_assignment_discard.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__bit_array_assignment_float.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__bit_array_assignment_int.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__bit_array_assignment_string.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__bit_array_pattern.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__constructor_pattern.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__constructor_pattern_with_multiple_variables.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__discard_pattern.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__float_pattern.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__int_pattern.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__just_variable.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__let_assert_at_end_of_block.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__let_assert_should_not_use_redefined_variable.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__list_pattern.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__list_pattern_with_multiple_variables.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__message.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__more_than_one_var.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__one_var.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__pattern_let.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__reference_earlier_segment.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__string_pattern.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__string_prefix_pattern.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__string_prefix_pattern_with_prefix_binding.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__tuple_pattern.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__variable_message.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__let_assert__variable_rewrites.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__numbers__int_negation.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__numbers__numbers_with_scientific_notation.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__numbers__numbers_with_underscores.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__numbers__numbers_with_underscores1.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__numbers__numbers_with_underscores2.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__numbers__repeated_int_negation.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__numbers__zero_b_in_hex.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__panic__panic_as.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__panic__panic_as_function.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__panic__piped.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__panic__piped_chain.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__panic__plain.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__patterns__alternative_patterns.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__patterns__alternative_patterns1.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__patterns__alternative_patterns2.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__patterns__alternative_patterns3.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__patterns__pattern_as.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__patterns__string_prefix_as_pattern_with_assertion.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__patterns__string_prefix_as_pattern_with_list.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__patterns__string_prefix_as_pattern_with_multiple_subjects.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__patterns__string_prefix_as_pattern_with_multiple_subjects_and_guard.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__pipes__block_expr_into_pipe.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__pipes__call_pipeline_result.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__pipes__clever_pipe_rewriting.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__pipes__clever_pipe_rewriting1.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__pipes__multiple_pipes.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__pipes__pipe_in_call.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__pipes__pipe_in_case_subject.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__pipes__pipe_in_eq.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__pipes__pipe_in_list.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__pipes__pipe_in_record_update.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__pipes__pipe_in_tuple.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__basic.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__const_record_update_generic_respecialization.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__constant_record_update_with_unlabelled_fields.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__imported_qualified_constructor_as_fn_name_escape.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__long_definition_formatting.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__module_types.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__nested_record_update.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__nested_record_update_with_blocks.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__pipe_update_subject.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__private_unused_records.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__record_access_block.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__record_accessor_multiple_variants.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__record_accessor_multiple_variants_parameterised_types.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__record_accessor_multiple_variants_positions_other_than_first.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__record_accessor_multiple_with_first_position_different_types.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__record_accessors.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__record_constants.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__record_spread.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__record_spread1.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__record_spread2.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__record_spread3.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__record_update_with_unlabelled_fields.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__record_updates.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__record_updates1.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__record_updates2.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__record_updates3.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__record_updates4.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__reserve_words.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__records__type_vars.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__reserved__build_in_erlang_type_escaping.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__reserved__escape_erlang_reserved_keywords_in_type_names.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__ascii_as_unicode_escape_sequence.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__assert_const_concat.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__assert_const_concat_many_strings.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__assert_const_concat_many_strings_in_list.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__assert_const_concat_other_const_concat.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__assert_string_prefix.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__assert_string_prefix_discar.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__concat.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__concat_3_variables.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__concat_constant.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__concat_constant_fn.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__concat_function_call.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__discard_concat_rest_pattern.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__not_unicode_escape_sequence.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__not_unicode_escape_sequence2.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__pipe_concat.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__rest_variable_rewriting.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__string_of_number_concat.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__string_prefix.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__string_prefix_assignment.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__string_prefix_assignment_not_unicode_escape_sequence.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__string_prefix_assignment_with_escape_sequences.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__string_prefix_assignment_with_guard.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__string_prefix_assignment_with_multiple_subjects.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__string_prefix_not_unicode_escape_sequence.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__string_prefix_shadowing.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__string_prefix_with_escape_sequences.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__unicode1.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__unicode2.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__unicode3.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__unicode_concat1.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__unicode_concat2.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__unicode_concat3.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__strings__unicode_escape_sequence_6_digits.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__todo__named.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__todo__piped.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__todo__plain.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__todo__todo_as.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__todo__todo_as_function.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__type_params__custom_type_named_args_count_once.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__type_params__custom_type_nested_named_args_count_once.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__type_params__custom_type_nested_result_type_count_once.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__type_params__custom_type_tuple_type_params_count_twice.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__type_params__nested_result_type_count_once.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__type_params__result_type_count_once.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__type_params__result_type_inferred_count_once.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__type_params__tuple_type_params_count_twice.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__use___arity_1.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__use___arity_2.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__use___arity_3.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__use___no_callback_body.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__use___pipeline_that_returns_fn.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__variables__anon_external_fun_name_escaping.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__variables__blocks_are_scopes.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__variables__discarded.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__variables__module_const_vars.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__variables__shadow_and_call.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__variables__shadow_let.snap
│ │ │ │ │ ├── gleam_core__erlang__tests__variables__shadow_param.snap
│ │ │ │ │ └── gleam_core__erlang__tests__variables__shadow_pipe.snap
│ │ │ │ ├── strings.rs
│ │ │ │ ├── todo.rs
│ │ │ │ ├── type_params.rs
│ │ │ │ ├── use_.rs
│ │ │ │ └── variables.rs
│ │ │ └── tests.rs
│ │ ├── erlang.rs
│ │ ├── error/
│ │ │ ├── snapshots/
│ │ │ │ ├── gleam_core__error__tests__shell_program_not_found_bun_linux_other.snap
│ │ │ │ ├── gleam_core__error__tests__shell_program_not_found_bun_linux_ubuntu.snap
│ │ │ │ ├── gleam_core__error__tests__shell_program_not_found_bun_macos_other.snap
│ │ │ │ ├── gleam_core__error__tests__shell_program_not_found_deno_linux_other.snap
│ │ │ │ ├── gleam_core__error__tests__shell_program_not_found_deno_linux_ubuntu.snap
│ │ │ │ ├── gleam_core__error__tests__shell_program_not_found_deno_macos_other.snap
│ │ │ │ ├── gleam_core__error__tests__shell_program_not_found_elixir_linux_other.snap
│ │ │ │ ├── gleam_core__error__tests__shell_program_not_found_elixir_linux_ubuntu.snap
│ │ │ │ ├── gleam_core__error__tests__shell_program_not_found_elixir_macos_other.snap
│ │ │ │ ├── gleam_core__error__tests__shell_program_not_found_erlc_linux_other.snap
│ │ │ │ ├── gleam_core__error__tests__shell_program_not_found_erlc_linux_ubuntu.snap
│ │ │ │ ├── gleam_core__error__tests__shell_program_not_found_erlc_macos_other.snap
│ │ │ │ ├── gleam_core__error__tests__shell_program_not_found_git_linux_other.snap
│ │ │ │ ├── gleam_core__error__tests__shell_program_not_found_git_linux_ubuntu.snap
│ │ │ │ ├── gleam_core__error__tests__shell_program_not_found_git_macos_other.snap
│ │ │ │ ├── gleam_core__error__tests__shell_program_not_found_node_linux_other.snap
│ │ │ │ ├── gleam_core__error__tests__shell_program_not_found_node_linux_ubuntu.snap
│ │ │ │ ├── gleam_core__error__tests__shell_program_not_found_node_macos_other.snap
│ │ │ │ ├── gleam_core__error__tests__shell_program_not_found_rebar3_linux_other.snap
│ │ │ │ ├── gleam_core__error__tests__shell_program_not_found_rebar3_linux_ubuntu.snap
│ │ │ │ └── gleam_core__error__tests__shell_program_not_found_rebar3_macos_other.snap
│ │ │ └── tests.rs
│ │ ├── error.rs
│ │ ├── exhaustiveness/
│ │ │ ├── missing_patterns.rs
│ │ │ └── printer.rs
│ │ ├── exhaustiveness.rs
│ │ ├── fix.rs
│ │ ├── format/
│ │ │ ├── tests/
│ │ │ │ ├── asignments.rs
│ │ │ │ ├── binary_operators.rs
│ │ │ │ ├── bit_array.rs
│ │ │ │ ├── blocks.rs
│ │ │ │ ├── cases.rs
│ │ │ │ ├── conditional_compilation.rs
│ │ │ │ ├── constant.rs
│ │ │ │ ├── custom_type.rs
│ │ │ │ ├── echo.rs
│ │ │ │ ├── external_fn.rs
│ │ │ │ ├── external_types.rs
│ │ │ │ ├── function.rs
│ │ │ │ ├── guards.rs
│ │ │ │ ├── imports.rs
│ │ │ │ ├── lists.rs
│ │ │ │ ├── pipeline.rs
│ │ │ │ ├── record_update.rs
│ │ │ │ ├── tuple.rs
│ │ │ │ └── use_.rs
│ │ │ └── tests.rs
│ │ ├── format.rs
│ │ ├── graph.rs
│ │ ├── hex.rs
│ │ ├── inline.rs
│ │ ├── io/
│ │ │ └── memory.rs
│ │ ├── io.rs
│ │ ├── javascript/
│ │ │ ├── decision.rs
│ │ │ ├── expression.rs
│ │ │ ├── import.rs
│ │ │ ├── tests/
│ │ │ │ ├── assert.rs
│ │ │ │ ├── assignments.rs
│ │ │ │ ├── bit_arrays.rs
│ │ │ │ ├── blocks.rs
│ │ │ │ ├── bools.rs
│ │ │ │ ├── case.rs
│ │ │ │ ├── case_clause_guards.rs
│ │ │ │ ├── consts.rs
│ │ │ │ ├── custom_types.rs
│ │ │ │ ├── echo.rs
│ │ │ │ ├── externals.rs
│ │ │ │ ├── functions.rs
│ │ │ │ ├── generics.rs
│ │ │ │ ├── inlining.rs
│ │ │ │ ├── lists.rs
│ │ │ │ ├── modules.rs
│ │ │ │ ├── numbers.rs
│ │ │ │ ├── panic.rs
│ │ │ │ ├── prelude.rs
│ │ │ │ ├── records.rs
│ │ │ │ ├── recursion.rs
│ │ │ │ ├── results.rs
│ │ │ │ ├── snapshots/
│ │ │ │ │ ├── gleam_core__javascript__tests__assert__assert_binary_operation.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assert__assert_binary_operation2.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assert__assert_binary_operation3.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assert__assert_binary_operator_with_side_effects.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assert__assert_binary_operator_with_side_effects2.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assert__assert_function_call.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assert__assert_function_call2.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assert__assert_literal.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assert__assert_nested_function_call.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assert__assert_nil_always_throws.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assert__assert_variable.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assert__assert_with_block_message.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assert__assert_with_case_rhs.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assert__assert_with_logical_and_binary_rhs_1.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assert__assert_with_logical_and_binary_rhs_2.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assert__assert_with_logical_and_binary_rhs_3.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assert__assert_with_message.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assert__assert_with_negated_case_rhs.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assert__assert_with_pipe_on_right.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assert__prova.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__assert.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__assert1.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__assert_that_always_fails.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__assert_that_always_succeeds.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__assert_with_multiple_variants.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__case_message.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__catch_all_assert.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__constant_assignments.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__correct_variable_renaming_in_assigned_functions.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__escaped_variables_in_constants.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__keyword_assignment.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__let_assert_nested_string_prefix.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__let_assert_string_prefix.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__message.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__module_const_var.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__module_const_var1.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__nested_binding.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__rebound_argument.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__rebound_function.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__rebound_function_and_arg.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__returning_literal_subject.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__tuple_matching.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__use_discard_assignment.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__use_matching_assignment.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__variable_message.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__variable_renaming.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__assignments__variable_used_in_pattern_and_assignment.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__alternative_patterns_with_variable_size.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__as_module_const.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__bit_array_assignment_discard.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__bit_array_assignment_float.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__bit_array_assignment_int.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__bit_array_assignment_string.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__bit_array_dynamic_slice.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__bit_array_literal_string_constant_is_treated_as_utf8.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__bit_array_literal_string_is_treated_as_utf8.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__bit_array_literal_string_pattern_is_treated_as_utf8.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__bit_array_pattern_match_all_reachable.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__bit_array_sliced.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__bit_string.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__bit_string_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__bits.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__bits_pattern_requires_v1_9.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__bits_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__block_in_pattern_size.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_bit_array_assignment_discard.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_bit_array_assignment_float.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_bit_array_assignment_int.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_bit_array_assignment_string.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_discard_sized.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_dynamic_size_float_pattern_with_unit.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_dynamic_size_pattern_with_unit.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_empty_match.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_is_byte_aligned.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_binary_size.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_bits_with_size.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_bytes.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_bytes_with_size.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_dynamic_bits_size.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_dynamic_bytes_size.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_dynamic_size.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_dynamic_size_literal_value.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_dynamic_size_shadowed_variable.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_dynamic_size_with_other_segments.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_float.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_float_16_bit.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_float_big_endian.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_float_little_endian.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_float_sized.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_float_sized_big_endian.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_float_sized_little_endian.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_literal_aligned_float.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_literal_float.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_literal_unaligned_float.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_rest.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_rest_bits.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_rest_bits_unaligned.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_rest_bytes.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_signed.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_signed_constant_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_sized.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_sized_big_endian.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_sized_big_endian_constant_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_sized_big_endian_signed.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_sized_big_endian_signed_constant_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_sized_big_endian_unsigned.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_sized_big_endian_unsigned_constant_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_sized_constant_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_sized_little_endian.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_sized_little_endian_constant_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_sized_little_endian_signed.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_sized_little_endian_signed_constant_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_sized_little_endian_unsigned.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_sized_little_endian_unsigned_constant_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_sized_unaligned.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_sized_value.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_sized_value_constant_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_unsigned.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_match_unsigned_constant_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_pattern_with_unit.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_with_remaining_bytes_after_constant_size.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_with_remaining_bytes_after_variable_size.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__case_with_remaining_bytes_after_variable_size_2.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__const_utf16.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__const_utf32.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__discard_sized.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__dynamic_size_pattern_with_unit.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__dynamic_size_with_unit.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__empty.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__empty_match.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__explicit_sized_constant_value.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__explicit_sized_dynamic_value.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__float.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__float_big_endian.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__float_little_endian.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__float_sized.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__float_sized_big_endian.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__float_sized_little_endian.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__integer.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_binary_size.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_bits_with_size.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_bytes.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_bytes_with_size.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_case_utf8.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_case_utf8_with_escape_chars.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_dynamic_bits_size.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_dynamic_bytes_size.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_dynamic_size.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_dynamic_size_literal_value.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_dynamic_size_shadowed_variable.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_dynamic_size_with_other_segments.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_float.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_float_16_bit.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_float_big_endian.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_float_little_endian.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_float_sized.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_float_sized_big_endian.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_float_sized_little_endian.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_literal_aligned_float.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_literal_float.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_literal_unaligned_float.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_rest.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_rest_bits.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_rest_bits_unaligned.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_rest_bytes.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_signed.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_signed_constant_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_sized.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_sized_big_endian.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_sized_big_endian_constant_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_sized_big_endian_signed.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_sized_big_endian_signed_constant_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_sized_big_endian_unsigned.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_sized_big_endian_unsigned_constant_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_sized_constant_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_sized_little_endian.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_sized_little_endian_constant_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_sized_little_endian_signed.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_sized_little_endian_signed_constant_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_sized_little_endian_unsigned.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_sized_little_endian_unsigned_constant_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_sized_unaligned.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_sized_value.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_sized_value_constant_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_unsigned.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_unsigned_constant_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_utf8.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__match_utf8_with_escape_chars.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__multiple_variable_size_segments.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__negative_size_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__negative_size_pattern_2.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__non_byte_aligned_size_calculation.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__one.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__operator_in_pattern_size.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__operator_in_pattern_size2.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__operator_in_pattern_size3.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__operator_in_size_for_bit_array_segment.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__pattern_match_on_negative_size_calculation.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__pattern_match_size_arithmetic.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__pattern_match_unknown_size_and_literal_string.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__pattern_match_utf16.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__pattern_match_utf16_little_endian.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__pattern_match_utf32.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__pattern_match_utf32_little_endian.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__pattern_matching_on_32_float_minus_infinity_still_reachable.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__pattern_matching_on_32_float_minus_infinity_still_reachable_2.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__pattern_matching_on_32_float_nan_still_reachable.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__pattern_matching_on_32_float_nan_still_reachable_2.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__pattern_matching_on_32_float_plus_infinity_still_reachable.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__pattern_matching_on_32_float_plus_infinity_still_reachable_2.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__pattern_matching_on_64_float_float_is_unreachable.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__pattern_matching_on_64_float_int_is_still_reachable.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__pattern_matching_on_64_float_minus_infinity_still_reachable.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__pattern_matching_on_64_float_minus_infinity_still_reachable_2.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__pattern_matching_on_64_float_nan_still_reachable.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__pattern_matching_on_64_float_nan_still_reachable_2.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__pattern_matching_on_64_float_plus_infinity_still_reachable.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__pattern_matching_on_64_float_plus_infinity_still_reachable_2.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__pattern_with_unit.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__segments_shadowing_each_other.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__sized_big_endian_constant_value.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__sized_big_endian_dynamic_value.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__sized_bits_expression_requires_v1_9.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__sized_constant_value.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__sized_constant_value_max_size_for_compile_time_evaluation.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__sized_constant_value_negative_overflow.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__sized_constant_value_positive_overflow.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__sized_dynamic_value.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__sized_little_endian_constant_value.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__sized_little_endian_dynamic_value.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__tuple_bit_array.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__tuple_bit_array_case.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__tuple_multiple_bit_arrays.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__tuple_multiple_bit_arrays_case.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__two.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__unaligned_int_expression_requires_v1_9.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__unaligned_int_pattern_requires_v1_9.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__unit_with_bits_option.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__unit_with_bits_option_constant.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__utf16.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__utf16_codepoint.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__utf16_codepoint_little_endian.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__utf16_little_endian.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__utf32.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__utf32_codepoint.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__utf32_codepoint_little_endian.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__utf32_little_endian.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__utf8.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__utf8_codepoint.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__utf8_codepoint_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__variable.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__variable_sized.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__variable_sized_segment.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bit_arrays__with_unit.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__blocks__assignment_last_in_block.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__blocks__block.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__blocks__block_in_tail_position_is_not_an_iife.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__blocks__block_in_tail_position_shadowing_variables.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__blocks__block_in_tail_position_with_just_an_assignment.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__blocks__block_with_parenthesised_expression_returning_from_function.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__blocks__blocks_returning_functions.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__blocks__blocks_returning_use.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__blocks__blocks_whose_values_are_unused_do_not_generate_assignments.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__blocks__concat_blocks.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__blocks__left_operator_sequence.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__blocks__let_assert_message_no_lifted.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__blocks__let_assert_only_statement_in_block.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__blocks__nested_multiexpr_blocks.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__blocks__nested_multiexpr_blocks_with_case.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__blocks__nested_multiexpr_blocks_with_pipe.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__blocks__nested_multiexpr_non_ending_blocks.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__blocks__nested_simple_blocks.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__blocks__pattern_assignment_last_in_block.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__blocks__right_operator_sequence.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__blocks__sequences.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__blocks__shadowed_variable_in_nested_scope.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bools__assigning.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bools__binop_panic_left.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bools__binop_panic_right.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bools__binop_todo_left.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bools__binop_todo_right.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bools__case.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bools__constants.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bools__constants_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bools__equality.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bools__expressions.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bools__negate_panic.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bools__negate_todo.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bools__negation.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bools__negation_block.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bools__nil_case.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bools__operators.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bools__shadowed_bools_and_nil.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__bools__shadowed_bools_and_nil_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__assignment.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__called_case.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_branches_guards_are_wrapped_in_parentheses.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_building_list_matched_by_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_building_matched_no_variant_record.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_building_matched_no_variant_record_2.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_building_matched_no_variant_record_3.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_building_matched_no_variant_record_4.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_building_matched_string_1.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_building_matched_string_2.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_building_matched_value_alias.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_building_matched_value_alias_2.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_building_matched_value_alias_3.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_building_matched_value_wrapped_in_block.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_building_record_matched_by_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_building_record_with_labels_matched_by_pattern_1.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_building_record_with_labels_matched_by_pattern_2.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_building_record_with_labels_matched_by_pattern_3.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_building_record_with_labels_matched_by_pattern_4.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_building_record_with_labels_matched_by_pattern_5.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_building_record_with_labels_matched_by_pattern_6.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_building_record_with_select_matched_by_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_building_record_with_select_matched_by_pattern_2.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_building_record_with_select_matched_by_pattern_3.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_building_simple_value_matched_by_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_local_var_in_tuple.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_on_error.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_with_multiple_subjects_building_list_matched_by_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_with_multiple_subjects_building_record_matched_by_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_with_multiple_subjects_building_same_value_as_two_subjects_one_is_picked.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__case_with_multiple_subjects_building_simple_value_matched_by_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__deeply_nested_string_prefix_assignment.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__directly_matching_case_subject.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__duplicate_name_for_variables_used_in_guards.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__duplicate_name_for_variables_used_in_guards_shadowing_outer_name.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__following_todo.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__guard_variable_only_brought_into_scope_when_needed.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__guard_variable_only_brought_into_scope_when_needed_1.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__interfering_string_pattern_succeeds_if_succeeding.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__list_with_guard.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__list_with_guard_no_binding.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__multi_subject_catch_all.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__multi_subject_no_catch_all.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__multi_subject_or.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__multi_subject_subject_assignments.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__nested_string_prefix_assignment.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__nested_string_prefix_match.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__nested_string_prefix_match_that_would_crash_on_js.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__pattern_matching_on_aliased_result_constructor.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__pipe.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__pointless.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__preassign_assignment.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__record_update_in_pipeline_in_case_clause.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__result.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__single_clause_variables.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__single_clause_variables_assigned.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__slicing_is_handled_properly_with_multiple_branches.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__string_concatenation_in_clause_guards.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__tuple_and_guard.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case__var_true.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__alternative_patterns.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__alternative_patterns_assignment.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__alternative_patterns_guard.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__alternative_patterns_list.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__bit_array_referencing_shadowed_variable.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__bitarray_with_var.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__constant.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__constructor_function_in_guard.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__custom_type_constructor_imported_and_aliased.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__eq_complex.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__eq_scalar.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__field_access.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__float_division.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__guard_pattern_does_not_shadow_outer_scope.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__imported_aliased_ok.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__imported_ok.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__int_division.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__int_remainder.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__keyword_var.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__module_access.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__module_access_aliased.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__module_access_submodule.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__module_list_access.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__module_nested_access.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__module_string_access.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__module_tuple_access.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__nested_record_access.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__not.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__not_eq_complex.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__not_eq_scalar.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__not_two.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__operator_wrapping_left.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__operator_wrapping_right.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__rebound_var.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__referencing_pattern_var.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__case_clause_guards__tuple_index.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__consts__constant_constructor_gets_pure_annotation.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__consts__constant_list_with_constructors_gets_pure_annotation.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__consts__constant_tuple_with_constructors_gets_pure_annotation.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__consts__constants_get_their_own_jsdoc_comment.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__consts__constructor_function_in_constant.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__consts__custom_type_constructor_imported_and_aliased.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__consts__imported_aliased_ok.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__consts__imported_ok.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__consts__list_prepend.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__consts__list_prepend_from_other_module.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__consts__list_prepend_literal.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__consts__literal_bool_does_not_get_constant_annotation.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__consts__literal_float_does_not_get_constant_annotation.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__consts__literal_int_does_not_get_constant_annotation.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__consts__literal_list_does_not_get_constant_annotation.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__consts__literal_nil_does_not_get_constant_annotation.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__consts__literal_string_does_not_get_constant_annotation.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__consts__literal_tuple_does_not_get_constant_annotation.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__const_imported_ignoring_label.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__const_imported_multiple_fields.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__const_imported_no_label.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__const_imported_using_label.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__const_unqualified_imported_ignoring_label.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__const_unqualified_imported_multiple_fields.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__const_unqualified_imported_no_label.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__const_unqualified_imported_using_label.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__const_with_fields.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__const_with_fields_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__const_zero_arity_imported.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__const_zero_arity_imported_unqualified.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__constructor_as_value.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__constructors_get_their_own_jsdoc.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__custom_type_with_named_fields.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__destructure_custom_type_with_mixed_fields_first_unlabelled.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__destructure_custom_type_with_named_fields.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__equality_with_non_singleton_variant.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__external_annotated_type_used_in_function.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__external_annotation.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__generic_type_parameter_used_in_field.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__guard_equality_with_non_singleton_variant.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__imported_ignoring_label.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__imported_multiple_fields.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__imported_no_label.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__imported_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__imported_using_label.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__keyword_label_name.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__long_name_variant_mixed_labels_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__long_name_variant_without_labels.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__mixed_singleton_and_non_singleton.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__multiple_singleton_constructors.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__nested_pattern_with_labels.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__new_type_import_syntax.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__non_singleton_record_equality.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__opaque_types_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__qualified.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__record_access_in_guard_with_reserved_field_name.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__record_access_in_pattern_with_reserved_field_name.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__record_with_field_named_constructor.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__record_with_field_named_then.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__singleton_in_case_guard.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__singleton_record_equality.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__singleton_record_inequality.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__singleton_record_reverse_order.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__types_must_be_rendered_before_functions.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__unapplied_record_constructors_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__unnamed_fields.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__unnamed_fields_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__unqualified_constructor_as_value.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__unqualified_imported_ignoring_label.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__unqualified_imported_multiple_fields.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__unqualified_imported_no_label.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__unqualified_imported_no_label_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__unqualified_imported_using_label.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__unused_opaque_constructor_is_generated_correctly.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__variant_defined_in_another_module_aliased_clause_guard.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__variant_defined_in_another_module_aliased_expression.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__variant_defined_in_another_module_qualified_clause_guard.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__variant_defined_in_another_module_qualified_expression.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__variant_defined_in_another_module_unqualified_clause_guard.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__variant_defined_in_another_module_unqualified_expression.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__zero_arity_const.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__zero_arity_imported.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__zero_arity_imported_typscript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__zero_arity_imported_unqualified.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__zero_arity_imported_unqualified_aliased.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__zero_arity_imported_unqualified_aliased_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__zero_arity_imported_unqualified_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__custom_types__zero_arity_literal.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__echo__echo_evaluates_printed_value_before_message.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__echo__echo_in_a_pipeline.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__echo__echo_in_a_pipeline_with_message.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__echo__echo_with_a_block.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__echo__echo_with_a_block_as_a_message.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__echo__echo_with_a_case_expression.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__echo__echo_with_a_function_call.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__echo__echo_with_a_function_call_and_a_message.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__echo__echo_with_a_panic.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__echo__echo_with_a_simple_expression.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__echo__echo_with_a_simple_expression_and_a_message.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__echo__echo_with_complex_expression_as_a_message.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__echo__module_named_inspect.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__echo__multiple_echos_in_a_pipeline.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__echo__multiple_echos_inside_expression.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__externals__at_namespace_module.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__externals__attribute_erlang.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__externals__attribute_javascript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__externals__both_externals_no_valid_impl.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__externals__discarded_names_in_external_are_passed_correctly.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__externals__duplicate_import.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__externals__erlang_and_javascript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__externals__erlang_only.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__externals__external_fn_escaping.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__externals__external_type_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__externals__inline_function.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__externals__module_fn.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__externals__name_to_escape.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__externals__no_body.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__externals__no_module.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__externals__pipe_variable_shadow.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__externals__private_attribute_erlang.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__externals__private_attribute_javascript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__externals__private_erlang_and_javascript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__externals__pub_module_fn.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__externals__pub_module_fn_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__externals__same_module_multiple_imports.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__externals__same_name_external.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__externals__tf_type_name_usage.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__externals__type_.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__assert_last.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__bad_comma.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__calling_fn_literal.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__calling_functions.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__case_in_call.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__exported_functions.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__fn_return_fn_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__function_formatting.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__function_formatting1.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__function_formatting2.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__function_formatting3.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__function_formatting_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__function_formatting_typescript1.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__function_literals_get_properly_wrapped_1.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__function_literals_get_properly_wrapped_2.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__function_literals_get_properly_wrapped_3.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__immediately_invoked_function_expressions_include_statement_level.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__internal_function_gets_ignored_jsdoc.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__keyword_in_recursive_function.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__labelled_argument_ordering.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__let_last.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__module_const_fn.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__module_const_fn1.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__multiple_discard.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__no_recur_in_anon_fn.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__pipe_into_block.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__pipe_last.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__pipe_shadow_import.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__pipe_variable_rebinding.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__pipe_with_block_in_the_middle.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__public_function_gets_jsdoc.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__recursion_with_discards.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__reserved_word_argument.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__reserved_word_const.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__reserved_word_fn.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__reserved_word_imported.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__reserved_word_imported_alias.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__reserved_word_in_function_arguments.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__shadowing_current.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__star_slash_in_jsdoc.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__tail_call.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__tail_call_doesnt_clobber_tail_position_tracking.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__two_pipes_in_a_row.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__variable_rewriting_in_anon_fn_with_matching_parameter.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__functions__variable_rewriting_in_anon_fn_with_matching_parameter_in_case.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__generics__fn_generics_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__generics__record_generics_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__generics__result_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__generics__task_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__generics__tuple_generics_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__inlining__blocks_get_preserved_when_needed.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__inlining__blocks_get_preserved_when_needed2.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__inlining__do_not_inline_parameters_that_have_side_effects.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__inlining__do_not_inline_parameters_used_more_than_once.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__inlining__inline_anonymous_function_call.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__inlining__inline_anonymous_function_in_pipe.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__inlining__inline_function_capture_in_pipe.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__inlining__inline_function_which_calls_other_function.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__inlining__inline_function_with_use.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__inlining__inline_function_with_use_and_anonymous.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__inlining__inline_function_with_use_becomes_tail_recursive.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__inlining__inline_higher_order_function.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__inlining__inline_higher_order_function_anonymous.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__inlining__inline_higher_order_function_with_capture.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__inlining__inline_shadowed_variable.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__inlining__inline_shadowed_variable_nested.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__inlining__inline_variable_shadowed_in_case_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__inlining__inline_variable_shadowing_case_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__inlining__inline_variable_shadowing_parameter.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__inlining__inlining_works_properly_with_record_updates.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__inlining__inlining_works_through_blocks.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__inlining__parameters_from_nested_functions_are_correctly_inlined.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__lists__case.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__lists__equality.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__lists__list_constants.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__lists__list_constants_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__lists__list_destructuring.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__lists__list_literals.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__lists__long_list_literals.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__lists__multi_line_list_literals.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__lists__tight_empty_list.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__modules__alias_aliased_constant.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__modules__alias_constant.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__modules__alias_fn_call.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__modules__aliased_unqualified_fn_call.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__modules__constant.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__modules__constant_module_access_with_keyword.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__modules__different_package_import.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__modules__discarded_duplicate_import.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__modules__discarded_duplicate_import_with_unqualified.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__modules__import_with_keyword.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__modules__multiple_unqualified_fn_call.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__modules__nested_fn_call.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__modules__nested_module_constant.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__modules__nested_nested_fn_call.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__modules__nested_same_package.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__modules__renamed_module.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__modules__unqualified_fn_call.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__complex_division_by_non_zero_float.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__complex_division_by_non_zero_int.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__complex_remainder_by_non_zero_int.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__division_by_non_zero_float.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__division_by_non_zero_int.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__division_by_zero_float.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__division_by_zero_int.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__division_inf_by_inf_float.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__float_divide_complex_expr.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__float_equality.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__float_equality1.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__float_literals.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__float_operators.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__float_scientific_literals.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__inf_float_case_statement.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__int_divide_complex_expr.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__int_equality.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__int_equality1.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__int_literals.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__int_mod_complex_expr.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__int_negation.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__int_operators.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__int_patterns.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__many_preceeding_zeros_float.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__many_preceeding_zeros_float_const.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__many_preceeding_zeros_float_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__many_preceeding_zeros_int.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__many_preceeding_zeros_int_const.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__many_preceeding_zeros_int_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__operator_precedence.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__preceeding_zeros_float.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__preceeding_zeros_float_const.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__preceeding_zeros_float_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__preceeding_zeros_int.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__preceeding_zeros_int_const.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__preceeding_zeros_int_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__remainder.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__remainder_by_non_zero_int.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__remainder_by_zero_int.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__repeated_int_negation.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__underscore_after_binary_prefix.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__underscore_after_decimal_point.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__underscore_after_decimal_point_case_statement.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__underscore_after_hexadecimal_prefix.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__underscore_after_octal_prefix.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__underscore_after_zero.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__underscore_after_zero_after_binary_prefix.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__underscore_after_zero_after_hex_prefix.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__underscore_after_zero_after_octal_prefix.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__wide_float_div.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__zero_after_underscore_after_binary_prefix.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__zero_after_underscore_after_hex_prefix.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__numbers__zero_after_underscore_after_octal_prefix.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__panic__as_expression.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__panic__bare.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__panic__bare_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__panic__case.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__panic__case_message.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__panic__panic_as.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__panic__pipe.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__panic__sequence.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__prelude__qualified_error.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__prelude__qualified_nil.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__prelude__qualified_nil_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__prelude__qualified_ok.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__prelude__qualified_ok_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__prelude__qualified_prelude_value_does_not_conflict_with_local_value.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__prelude__qualified_prelude_value_does_not_conflict_with_local_value_constant.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__prelude__qualified_prelude_value_does_not_conflict_with_local_value_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__records__const_record_update_generic_respecialization.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__records__constant_record_update_with_unlabelled_fields.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__records__field_named_constructor_is_escaped.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__records__field_named_prototype_is_escaped.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__records__field_named_then_is_escaped.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__records__field_named_x0.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__records__record_accessor_multiple_variants.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__records__record_accessor_multiple_variants_parameterised_types.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__records__record_accessor_multiple_variants_positions_other_than_first.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__records__record_accessor_multiple_with_first_position_different_types.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__records__record_accessors.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__records__record_update_with_unlabelled_fields.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__recursion__not_tco_due_to_assignment.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__recursion__shadowing_so_not_recursive.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__recursion__tco.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__recursion__tco_case_block.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__results__aliased_error.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__results__aliased_error_fn.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__results__aliased_ok.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__results__aliased_ok_fn.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__results__error.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__results__error_fn.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__results__ok.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__results__ok_fn.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__results__qualified_error.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__results__qualified_error_fn.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__results__qualified_ok.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__results__qualified_ok_fn.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__strings__ascii_as_unicode_escape_sequence.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__strings__case.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__strings__const_concat.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__strings__const_concat_multiple.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__strings__discard_concat_rest_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__strings__equality.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__strings__string_concat.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__strings__string_literals.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__strings__string_patterns.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__strings__string_prefix.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__strings__string_prefix_assignment.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__strings__string_prefix_assignment_with_multiple_subjects.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__strings__string_prefix_assignment_with_utf_escape_sequence.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__strings__string_prefix_shadowing.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__strings__string_prefix_utf16.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__strings__unicode1.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__strings__unicode2.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__strings__unicode_escape_sequence_6_digits.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__todo__as_expression.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__todo__case_message.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__todo__inside_fn.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__todo__with_message.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__todo__with_message_expr.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__todo__without_message.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__todo__without_message_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__tuples__case.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__tuples__constant_tuples.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__tuples__constant_tuples1.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__tuples__nested_pattern.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__tuples__tuple.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__tuples__tuple1.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__tuples__tuple_access.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__tuples__tuple_formatting_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__tuples__tuple_typescript.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__tuples__tuple_with_block_element.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__tuples__tuple_with_block_element1.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__type_alias__import_indirect_type_alias.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__type_alias__private_type_in_opaque_type.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__type_alias__type_alias.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__use___arity_1.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__use___arity_2.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__use___arity_3.snap
│ │ │ │ │ ├── gleam_core__javascript__tests__use___no_callback_body.snap
│ │ │ │ │ └── gleam_core__javascript__tests__use___patterns.snap
│ │ │ │ ├── strings.rs
│ │ │ │ ├── todo.rs
│ │ │ │ ├── tuples.rs
│ │ │ │ ├── type_alias.rs
│ │ │ │ └── use_.rs
│ │ │ ├── tests.rs
│ │ │ └── typescript.rs
│ │ ├── javascript.rs
│ │ ├── lib.rs
│ │ ├── line_numbers.rs
│ │ ├── manifest.rs
│ │ ├── metadata/
│ │ │ └── tests.rs
│ │ ├── metadata.rs
│ │ ├── package_interface/
│ │ │ ├── snapshots/
│ │ │ │ ├── gleam_core__package_interface__tests__constructors_with_documentation.snap
│ │ │ │ ├── gleam_core__package_interface__tests__generic_function.snap
│ │ │ │ ├── gleam_core__package_interface__tests__imported_aliased_type_keeps_original_name.snap
│ │ │ │ ├── gleam_core__package_interface__tests__imported_type.snap
│ │ │ │ ├── gleam_core__package_interface__tests__internal_definitions_are_not_included.snap
│ │ │ │ ├── gleam_core__package_interface__tests__internal_modules_are_not_exported.snap
│ │ │ │ ├── gleam_core__package_interface__tests__labelled_function_parameters.snap
│ │ │ │ ├── gleam_core__package_interface__tests__multiple_type_variables.snap
│ │ │ │ ├── gleam_core__package_interface__tests__opaque_constructors_are_not_exposed.snap
│ │ │ │ ├── gleam_core__package_interface__tests__package_documentation_is_included.snap
│ │ │ │ ├── gleam_core__package_interface__tests__prelude_types.snap
│ │ │ │ ├── gleam_core__package_interface__tests__private_definitions_are_not_included.snap
│ │ │ │ ├── gleam_core__package_interface__tests__type_aliases.snap
│ │ │ │ ├── gleam_core__package_interface__tests__type_constructors.snap
│ │ │ │ └── gleam_core__package_interface__tests__type_definition.snap
│ │ │ └── tests.rs
│ │ ├── package_interface.rs
│ │ ├── parse/
│ │ │ ├── error.rs
│ │ │ ├── extra.rs
│ │ │ ├── lexer.rs
│ │ │ ├── snapshots/
│ │ │ │ ├── gleam_core__parse__tests__append_to_const_list.snap
│ │ │ │ ├── gleam_core__parse__tests__argument_scope.snap
│ │ │ │ ├── gleam_core__parse__tests__arithmetic_in_guards.snap
│ │ │ │ ├── gleam_core__parse__tests__assert_statement.snap
│ │ │ │ ├── gleam_core__parse__tests__assert_statement_followed_by_statement.snap
│ │ │ │ ├── gleam_core__parse__tests__assert_statement_with_message.snap
│ │ │ │ ├── gleam_core__parse__tests__assert_statement_without_expression.snap
│ │ │ │ ├── gleam_core__parse__tests__assign_left_hand_side_of_concat_pattern.snap
│ │ │ │ ├── gleam_core__parse__tests__assignment_pattern_invalid_bit_segment.snap
│ │ │ │ ├── gleam_core__parse__tests__assignment_pattern_invalid_tuple.snap
│ │ │ │ ├── gleam_core__parse__tests__attributes_with_improper_definition.snap
│ │ │ │ ├── gleam_core__parse__tests__attributes_with_no_definition.snap
│ │ │ │ ├── gleam_core__parse__tests__bare_expression.snap
│ │ │ │ ├── gleam_core__parse__tests__bit_array_invalid_segment.snap
│ │ │ │ ├── gleam_core__parse__tests__block_of_one.snap
│ │ │ │ ├── gleam_core__parse__tests__block_of_two.snap
│ │ │ │ ├── gleam_core__parse__tests__byte_order_mark.snap
│ │ │ │ ├── gleam_core__parse__tests__byte_order_mark_module.snap
│ │ │ │ ├── gleam_core__parse__tests__capture_with_name.snap
│ │ │ │ ├── gleam_core__parse__tests__case_alternative_clause_no_subject.snap
│ │ │ │ ├── gleam_core__parse__tests__case_clause_no_subject.snap
│ │ │ │ ├── gleam_core__parse__tests__case_expression_without_body.snap
│ │ │ │ ├── gleam_core__parse__tests__case_guard_with_empty_block.snap
│ │ │ │ ├── gleam_core__parse__tests__case_guard_with_nested_blocks.snap
│ │ │ │ ├── gleam_core__parse__tests__case_invalid_case_pattern.snap
│ │ │ │ ├── gleam_core__parse__tests__case_invalid_expression.snap
│ │ │ │ ├── gleam_core__parse__tests__case_list_pattern_after_spread.snap
│ │ │ │ ├── gleam_core__parse__tests__const_invalid_bit_array_segment.snap
│ │ │ │ ├── gleam_core__parse__tests__const_invalid_list.snap
│ │ │ │ ├── gleam_core__parse__tests__const_invalid_record_constructor.snap
│ │ │ │ ├── gleam_core__parse__tests__const_invalid_tuple.snap
│ │ │ │ ├── gleam_core__parse__tests__const_record_update_all_fields.snap
│ │ │ │ ├── gleam_core__parse__tests__const_record_update_basic.snap
│ │ │ │ ├── gleam_core__parse__tests__const_record_update_only.snap
│ │ │ │ ├── gleam_core__parse__tests__const_record_update_with_module.snap
│ │ │ │ ├── gleam_core__parse__tests__const_string_concat.snap
│ │ │ │ ├── gleam_core__parse__tests__const_string_concat_naked_right.snap
│ │ │ │ ├── gleam_core__parse__tests__const_with_function_call.snap
│ │ │ │ ├── gleam_core__parse__tests__const_with_function_call_with_args.snap
│ │ │ │ ├── gleam_core__parse__tests__constant_inside_function.snap
│ │ │ │ ├── gleam_core__parse__tests__correct_precedence_in_pattern_size.snap
│ │ │ │ ├── gleam_core__parse__tests__deeply_nested_tuples.snap
│ │ │ │ ├── gleam_core__parse__tests__deeply_nested_tuples_no_block.snap
│ │ │ │ ├── gleam_core__parse__tests__deprecation_attribute_on_type_variant.snap
│ │ │ │ ├── gleam_core__parse__tests__deprecation_without_message.snap
│ │ │ │ ├── gleam_core__parse__tests__discard_left_hand_side_of_concat_pattern.snap
│ │ │ │ ├── gleam_core__parse__tests__doesnt_issue_special_error_for_pythonic_import_if_slash.snap
│ │ │ │ ├── gleam_core__parse__tests__dot_access_function_call_in_case_clause_guard.snap
│ │ │ │ ├── gleam_core__parse__tests__echo_at_start_of_pipeline_wraps_the_whole_thing.snap
│ │ │ │ ├── gleam_core__parse__tests__echo_cannot_have_an_expression_in_a_pipeline.snap
│ │ │ │ ├── gleam_core__parse__tests__echo_followed_by_expression_ends_where_expression_ends.snap
│ │ │ │ ├── gleam_core__parse__tests__echo_has_lower_precedence_than_binop.snap
│ │ │ │ ├── gleam_core__parse__tests__echo_has_lower_precedence_than_pipeline.snap
│ │ │ │ ├── gleam_core__parse__tests__echo_in_a_pipeline.snap
│ │ │ │ ├── gleam_core__parse__tests__echo_with_assert_and_message_1.snap
│ │ │ │ ├── gleam_core__parse__tests__echo_with_assert_and_message_2.snap
│ │ │ │ ├── gleam_core__parse__tests__echo_with_assert_and_messages_1.snap
│ │ │ │ ├── gleam_core__parse__tests__echo_with_assert_and_messages_2.snap
│ │ │ │ ├── gleam_core__parse__tests__echo_with_assert_and_messages_3.snap
│ │ │ │ ├── gleam_core__parse__tests__echo_with_block.snap
│ │ │ │ ├── gleam_core__parse__tests__echo_with_complex_expression.snap
│ │ │ │ ├── gleam_core__parse__tests__echo_with_let_assert_and_message.snap
│ │ │ │ ├── gleam_core__parse__tests__echo_with_let_assert_and_messages.snap
│ │ │ │ ├── gleam_core__parse__tests__echo_with_no_expressions_after_it.snap
│ │ │ │ ├── gleam_core__parse__tests__echo_with_no_expressions_after_it_but_a_message.snap
│ │ │ │ ├── gleam_core__parse__tests__echo_with_panic.snap
│ │ │ │ ├── gleam_core__parse__tests__echo_with_panic_and_message.snap
│ │ │ │ ├── gleam_core__parse__tests__echo_with_panic_and_messages.snap
│ │ │ │ ├── gleam_core__parse__tests__echo_with_simple_expression_1.snap
│ │ │ │ ├── gleam_core__parse__tests__echo_with_simple_expression_2.snap
│ │ │ │ ├── gleam_core__parse__tests__error_message_on_variable_starting_with_underscore.snap
│ │ │ │ ├── gleam_core__parse__tests__error_message_on_variable_starting_with_underscore2.snap
│ │ │ │ ├── gleam_core__parse__tests__external_attribute_on_type_variant.snap
│ │ │ │ ├── gleam_core__parse__tests__external_attribute_with_custom_type.snap
│ │ │ │ ├── gleam_core__parse__tests__external_attribute_with_non_fn_definition.snap
│ │ │ │ ├── gleam_core__parse__tests__float_empty_exponent.snap
│ │ │ │ ├── gleam_core__parse__tests__function_call_in_case_clause_guard.snap
│ │ │ │ ├── gleam_core__parse__tests__function_definition_angle_generics_error.snap
│ │ │ │ ├── gleam_core__parse__tests__function_inside_a_type.snap
│ │ │ │ ├── gleam_core__parse__tests__function_invalid_signature.snap
│ │ │ │ ├── gleam_core__parse__tests__function_type_invalid_param_type.snap
│ │ │ │ ├── gleam_core__parse__tests__if_like_expression.snap
│ │ │ │ ├── gleam_core__parse__tests__import_type.snap
│ │ │ │ ├── gleam_core__parse__tests__incomplete_function.snap
│ │ │ │ ├── gleam_core__parse__tests__inner_single_quote_parses.snap
│ │ │ │ ├── gleam_core__parse__tests__internal_attribute_on_type_variant.snap
│ │ │ │ ├── gleam_core__parse__tests__invalid_label_shorthand.snap
│ │ │ │ ├── gleam_core__parse__tests__invalid_label_shorthand_2.snap
│ │ │ │ ├── gleam_core__parse__tests__invalid_label_shorthand_3.snap
│ │ │ │ ├── gleam_core__parse__tests__invalid_label_shorthand_4.snap
│ │ │ │ ├── gleam_core__parse__tests__invalid_label_shorthand_5.snap
│ │ │ │ ├── gleam_core__parse__tests__invalid_left_paren_in_case_clause_guard.snap
│ │ │ │ ├── gleam_core__parse__tests__invalid_pattern_label_shorthand.snap
│ │ │ │ ├── gleam_core__parse__tests__invalid_pattern_label_shorthand_2.snap
│ │ │ │ ├── gleam_core__parse__tests__invalid_pattern_label_shorthand_3.snap
│ │ │ │ ├── gleam_core__parse__tests__invalid_pattern_label_shorthand_4.snap
│ │ │ │ ├── gleam_core__parse__tests__invalid_pattern_label_shorthand_5.snap
│ │ │ │ ├── gleam_core__parse__tests__list_spread_as_first_item_followed_by_other_items.snap
│ │ │ │ ├── gleam_core__parse__tests__list_spread_followed_by_extra_item_and_another_spread.snap
│ │ │ │ ├── gleam_core__parse__tests__list_spread_followed_by_extra_items.snap
│ │ │ │ ├── gleam_core__parse__tests__list_spread_followed_by_other_spread.snap
│ │ │ │ ├── gleam_core__parse__tests__list_spread_with_no_tail_in_the_middle_of_a_list.snap
│ │ │ │ ├── gleam_core__parse__tests__missing_constructor_arguments.snap
│ │ │ │ ├── gleam_core__parse__tests__missing_target.snap
│ │ │ │ ├── gleam_core__parse__tests__missing_target_and_bracket.snap
│ │ │ │ ├── gleam_core__parse__tests__missing_type_constructor_arguments_in_type_definition.snap
│ │ │ │ ├── gleam_core__parse__tests__multiple_deprecation_attribute_on_type_variant.snap
│ │ │ │ ├── gleam_core__parse__tests__multiple_deprecation_attributes.snap
│ │ │ │ ├── gleam_core__parse__tests__multiple_external_for_same_project_erlang.snap
│ │ │ │ ├── gleam_core__parse__tests__multiple_external_for_same_project_javascript.snap
│ │ │ │ ├── gleam_core__parse__tests__multiple_internal_attributes.snap
│ │ │ │ ├── gleam_core__parse__tests__multiple_unsupported_attributes_on_type_variant.snap
│ │ │ │ ├── gleam_core__parse__tests__nested_block.snap
│ │ │ │ ├── gleam_core__parse__tests__nested_tuple_access_after_function.snap
│ │ │ │ ├── gleam_core__parse__tests__nested_tuples.snap
│ │ │ │ ├── gleam_core__parse__tests__nested_tuples_no_block.snap
│ │ │ │ ├── gleam_core__parse__tests__no_eq_after_binding_snapshot_1.snap
│ │ │ │ ├── gleam_core__parse__tests__no_eq_after_binding_snapshot_2.snap
│ │ │ │ ├── gleam_core__parse__tests__no_let_binding_snapshot_1.snap
│ │ │ │ ├── gleam_core__parse__tests__no_let_binding_snapshot_2.snap
│ │ │ │ ├── gleam_core__parse__tests__no_let_binding_snapshot_3.snap
│ │ │ │ ├── gleam_core__parse__tests__non_module_level_function_with_a_name.snap
│ │ │ │ ├── gleam_core__parse__tests__non_module_level_function_with_not_a_name.snap
│ │ │ │ ├── gleam_core__parse__tests__operator_in_pattern_size.snap
│ │ │ │ ├── gleam_core__parse__tests__panic_with_echo.snap
│ │ │ │ ├── gleam_core__parse__tests__panic_with_echo_and_message.snap
│ │ │ │ ├── gleam_core__parse__tests__prepend_no_elements_to_const_list.snap
│ │ │ │ ├── gleam_core__parse__tests__prepend_to_const_list_with_multiple_spreads.snap
│ │ │ │ ├── gleam_core__parse__tests__prepend_to_const_list_with_no_tail.snap
│ │ │ │ ├── gleam_core__parse__tests__prepend_to_const_list_without_comma.snap
│ │ │ │ ├── gleam_core__parse__tests__private_internal_const.snap
│ │ │ │ ├── gleam_core__parse__tests__private_internal_function.snap
│ │ │ │ ├── gleam_core__parse__tests__private_internal_type.snap
│ │ │ │ ├── gleam_core__parse__tests__private_internal_type_alias.snap
│ │ │ │ ├── gleam_core__parse__tests__private_opaque_type_is_parsed.snap
│ │ │ │ ├── gleam_core__parse__tests__pub_function_inside_a_type.snap
│ │ │ │ ├── gleam_core__parse__tests__record_access_no_label.snap
│ │ │ │ ├── gleam_core__parse__tests__repeated_echos.snap
│ │ │ │ ├── gleam_core__parse__tests__reserved_auto.snap
│ │ │ │ ├── gleam_core__parse__tests__reserved_delegate.snap
│ │ │ │ ├── gleam_core__parse__tests__reserved_derive.snap
│ │ │ │ ├── gleam_core__parse__tests__reserved_echo.snap
│ │ │ │ ├── gleam_core__parse__tests__reserved_else.snap
│ │ │ │ ├── gleam_core__parse__tests__reserved_implement.snap
│ │ │ │ ├── gleam_core__parse__tests__reserved_macro.snap
│ │ │ │ ├── gleam_core__parse__tests__reserved_test.snap
│ │ │ │ ├── gleam_core__parse__tests__semicolons.snap
│ │ │ │ ├── gleam_core__parse__tests__special_error_for_pythonic_import.snap
│ │ │ │ ├── gleam_core__parse__tests__special_error_for_pythonic_neste_import.snap
│ │ │ │ ├── gleam_core__parse__tests__string_concatenation_in_case_clause_guard.snap
│ │ │ │ ├── gleam_core__parse__tests__string_single_char_suggestion.snap
│ │ │ │ ├── gleam_core__parse__tests__target_attribute_on_type_variant.snap
│ │ │ │ ├── gleam_core__parse__tests__tuple_invalid_expr.snap
│ │ │ │ ├── gleam_core__parse__tests__tuple_without_hash.snap
│ │ │ │ ├── gleam_core__parse__tests__type_angle_generics_definition_error.snap
│ │ │ │ ├── gleam_core__parse__tests__type_angle_generics_definition_error_fallback.snap
│ │ │ │ ├── gleam_core__parse__tests__type_angle_generics_definition_with_upname_error.snap
│ │ │ │ ├── gleam_core__parse__tests__type_angle_generics_usage_with_module_error.snap
│ │ │ │ ├── gleam_core__parse__tests__type_angle_generics_usage_without_module_error.snap
│ │ │ │ ├── gleam_core__parse__tests__type_invalid_constructor.snap
│ │ │ │ ├── gleam_core__parse__tests__type_invalid_constructor_arg.snap
│ │ │ │ ├── gleam_core__parse__tests__type_invalid_record.snap
│ │ │ │ ├── gleam_core__parse__tests__type_invalid_record_constructor.snap
│ │ │ │ ├── gleam_core__parse__tests__type_invalid_record_constructor_invalid_field_type.snap
│ │ │ │ ├── gleam_core__parse__tests__type_invalid_record_constructor_without_field_type.snap
│ │ │ │ ├── gleam_core__parse__tests__type_invalid_type_name.snap
│ │ │ │ ├── gleam_core__parse__tests__unknown_attribute.snap
│ │ │ │ ├── gleam_core__parse__tests__unknown_external_target.snap
│ │ │ │ ├── gleam_core__parse__tests__unknown_target.snap
│ │ │ │ ├── gleam_core__parse__tests__use_invalid_assignments.snap
│ │ │ │ ├── gleam_core__parse__tests__valueless_list_spread_expression.snap
│ │ │ │ ├── gleam_core__parse__tests__with_let_binding3.snap
│ │ │ │ ├── gleam_core__parse__tests__with_let_binding3_and_annotation.snap
│ │ │ │ ├── gleam_core__parse__tests__wrong_function_return_type_declaration_using_colon_instead_of_right_arrow.snap
│ │ │ │ ├── gleam_core__parse__tests__wrong_record_access_pattern.snap
│ │ │ │ └── gleam_core__parse__tests__wrong_type_of_comments_with_hash.snap
│ │ │ ├── tests.rs
│ │ │ └── token.rs
│ │ ├── parse.rs
│ │ ├── paths.rs
│ │ ├── pretty/
│ │ │ └── tests.rs
│ │ ├── pretty.rs
│ │ ├── reference.rs
│ │ ├── requirement.rs
│ │ ├── snapshots/
│ │ │ ├── gleam_core__config__barebones_package_config_to_json.snap
│ │ │ ├── gleam_core__config__deny_extra_deps_properties.snap
│ │ │ ├── gleam_core__config__name_with_dash.snap
│ │ │ ├── gleam_core__config__name_with_number_start.snap
│ │ │ ├── gleam_core__config__package_config_to_json.snap
│ │ │ ├── gleam_core__dependency__tests__resolution_error_message.snap
│ │ │ ├── gleam_core__docs__barebones_package_config_to_json.snap
│ │ │ ├── gleam_core__docs__package_config_to_json.snap
│ │ │ └── gleam_core__requirement__tests__read_wrong_version.snap
│ │ ├── strings.rs
│ │ ├── type_/
│ │ │ ├── environment.rs
│ │ │ ├── error.rs
│ │ │ ├── expression.rs
│ │ │ ├── fields.rs
│ │ │ ├── hydrator.rs
│ │ │ ├── pattern.rs
│ │ │ ├── pipe.rs
│ │ │ ├── prelude.rs
│ │ │ ├── pretty.rs
│ │ │ ├── printer.rs
│ │ │ ├── snapshots/
│ │ │ │ ├── gleam_core__type___tests__const_record_update_all_fields.snap
│ │ │ │ ├── gleam_core__type___tests__const_record_update_field_type_mismatch_error.snap
│ │ │ │ ├── gleam_core__type___tests__const_record_update_fieldless_warning.snap
│ │ │ │ ├── gleam_core__type___tests__const_record_update_non_record.snap
│ │ │ │ ├── gleam_core__type___tests__const_record_update_nonexistent_field.snap
│ │ │ │ ├── gleam_core__type___tests__const_record_update_type_mismatch_error.snap
│ │ │ │ ├── gleam_core__type___tests__const_record_update_unlabelled_fields.snap
│ │ │ │ ├── gleam_core__type___tests__const_record_update_variant_mismatch_error.snap
│ │ │ │ ├── gleam_core__type___tests__const_record_update_variant_without_args.snap
│ │ │ │ ├── gleam_core__type___tests__correct_type_check_for_multiple_mutually_recursive_functions.snap
│ │ │ │ ├── gleam_core__type___tests__function_parameter_errors_do_not_stop_inference.snap
│ │ │ │ ├── gleam_core__type___tests__generic_unlabelled_field_in_updated_const_record_wrong_type.snap
│ │ │ │ ├── gleam_core__type___tests__pipe_with_annonymous_unannotated_functions_wrong_arity1.snap
│ │ │ │ ├── gleam_core__type___tests__pipe_with_annonymous_unannotated_functions_wrong_arity2.snap
│ │ │ │ ├── gleam_core__type___tests__pipe_with_annonymous_unannotated_functions_wrong_arity3.snap
│ │ │ │ ├── gleam_core__type___tests__prepend_constant_list_wrong_element_type.snap
│ │ │ │ ├── gleam_core__type___tests__prepend_constant_list_wrong_type.snap
│ │ │ │ ├── gleam_core__type___tests__private_types_not_available_in_other_modules.snap
│ │ │ │ ├── gleam_core__type___tests__record_update_variant_inference_fails_for_several_possible_variants.snap
│ │ │ │ ├── gleam_core__type___tests__record_update_variant_inference_fails_for_several_possible_variants_on_subject_variable.snap
│ │ │ │ ├── gleam_core__type___tests__string_concat_ko_1.snap
│ │ │ │ ├── gleam_core__type___tests__string_concat_ko_2.snap
│ │ │ │ ├── gleam_core__type___tests__type_unification_does_not_allow_different_variants_to_be_treated_as_safe.snap
│ │ │ │ ├── gleam_core__type___tests__type_unification_does_not_allow_lowercase_bools_in_match_clause.snap
│ │ │ │ ├── gleam_core__type___tests__type_unification_does_not_cause_false_positives_for_variant_matching.snap
│ │ │ │ ├── gleam_core__type___tests__type_unification_removes_inferred_variant_in_functions.snap
│ │ │ │ ├── gleam_core__type___tests__type_unification_removes_inferred_variant_in_nested_type.snap
│ │ │ │ ├── gleam_core__type___tests__type_unification_removes_inferred_variant_in_tuples.snap
│ │ │ │ ├── gleam_core__type___tests__unlabelled_argument_not_allowed_after_labelled_argument.snap
│ │ │ │ ├── gleam_core__type___tests__variant_inference_does_not_escape_clause_scope.snap
│ │ │ │ └── gleam_core__type___tests__variant_inference_on_literal_record.snap
│ │ │ ├── tests/
│ │ │ │ ├── accessors.rs
│ │ │ │ ├── assert.rs
│ │ │ │ ├── assignments.rs
│ │ │ │ ├── conditional_compilation.rs
│ │ │ │ ├── custom_types.rs
│ │ │ │ ├── dead_code_detection.rs
│ │ │ │ ├── echo.rs
│ │ │ │ ├── errors.rs
│ │ │ │ ├── exhaustiveness.rs
│ │ │ │ ├── externals.rs
│ │ │ │ ├── functions.rs
│ │ │ │ ├── guards.rs
│ │ │ │ ├── imports.rs
│ │ │ │ ├── let_assert.rs
│ │ │ │ ├── pipes.rs
│ │ │ │ ├── pretty.rs
│ │ │ │ ├── snapshots/
│ │ │ │ │ ├── gleam_core__type___tests__assert__bool_literal.snap
│ │ │ │ │ ├── gleam_core__type___tests__assert__comparison_on_literals.snap
│ │ │ │ │ ├── gleam_core__type___tests__assert__equality_check_on_literals.snap
│ │ │ │ │ ├── gleam_core__type___tests__assert__mismatched_types.snap
│ │ │ │ │ ├── gleam_core__type___tests__assert__negation_of_bool_literal.snap
│ │ │ │ │ ├── gleam_core__type___tests__assert__wrong_message_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__custom_types__conflict_with_import.snap
│ │ │ │ │ ├── gleam_core__type___tests__custom_types__depreacted_type_deprecate_varient_err.snap
│ │ │ │ │ ├── gleam_core__type___tests__custom_types__deprecated_all_varients_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__custom_types__deprecated_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__custom_types__deprecated_varients_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__custom_types__duplicate_variable_error_does_not_stop_analysis.snap
│ │ │ │ │ ├── gleam_core__type___tests__custom_types__fault_tolerance.snap
│ │ │ │ │ ├── gleam_core__type___tests__custom_types__pattern_match_correct_labeled_field.snap
│ │ │ │ │ ├── gleam_core__type___tests__custom_types__pattern_match_correct_pos_field.snap
│ │ │ │ │ ├── gleam_core__type___tests__dead_code_detection__constant_only_referenced_by_unused_constant.snap
│ │ │ │ │ ├── gleam_core__type___tests__dead_code_detection__constant_only_referenced_by_unused_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__dead_code_detection__constructor_used_if_type_alias_shadows_it.snap
│ │ │ │ │ ├── gleam_core__type___tests__dead_code_detection__imported_module_alias_only_referenced_by_unused_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__dead_code_detection__imported_module_alias_only_referenced_by_unused_function_with_unqualified.snap
│ │ │ │ │ ├── gleam_core__type___tests__dead_code_detection__imported_module_marked_unused_when_shadowed_in_record_access.snap
│ │ │ │ │ ├── gleam_core__type___tests__dead_code_detection__imported_module_only_referenced_by_unused_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__dead_code_detection__imported_type_and_constructor_with_same_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__dead_code_detection__imported_type_and_constructor_with_same_name2.snap
│ │ │ │ │ ├── gleam_core__type___tests__dead_code_detection__imported_type_only_referenced_by_unused_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__dead_code_detection__imported_value_only_referenced_by_unused_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__dead_code_detection__local_variable_marked_unused_when_shadowed_in_module_access.snap
│ │ │ │ │ ├── gleam_core__type___tests__dead_code_detection__private_type_alias_only_referenced_by_unused_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__dead_code_detection__private_type_alias_underlying_type_referenced_by_public_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__dead_code_detection__shadowed_imported_value_marked_unused.snap
│ │ │ │ │ ├── gleam_core__type___tests__dead_code_detection__type_and_variant_unused.snap
│ │ │ │ │ ├── gleam_core__type___tests__dead_code_detection__type_variant_only_referenced_by_unused_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__dead_code_detection__unused_mutually_recursive_functions.snap
│ │ │ │ │ ├── gleam_core__type___tests__dead_code_detection__unused_recursive_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__dead_code_detection__unused_type_alias.snap
│ │ │ │ │ ├── gleam_core__type___tests__dead_code_detection__used_shadowed_imported_value.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__access_int.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__accessor_multiple_variants_multiple_positions.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__accessor_multiple_variants_multiple_positions2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__add_f_int_float.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__add_int_float.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__add_on_strings.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__ambiguous_import_error_no_unqualified.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__ambiguous_import_error_with_unqualified.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__ambiguous_type_error.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__assigned_function_annotation.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_binary.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_bits_option_in_value.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_float.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_float_size.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_guard.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_invalid_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_conflicting_endianness1.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_conflicting_endianness2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_conflicting_options_bit_array.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_conflicting_options_int.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_conflicting_signedness1.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_conflicting_signedness2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_nosize.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_nosize2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_nosize3.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_size.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_size2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_type_does_not_allow_aliased_variable_string.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_type_does_not_allow_size_utf16.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_type_does_not_allow_size_utf32.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_type_does_not_allow_size_utf8.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_type_does_not_allow_unit_codepoint_utf16.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_type_does_not_allow_unit_codepoint_utf16_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_type_does_not_allow_unit_codepoint_utf32.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_type_does_not_allow_unit_codepoint_utf32_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_type_does_not_allow_unit_codepoint_utf8.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_type_does_not_allow_unit_codepoint_utf8_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_type_does_not_allow_unit_utf16.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_type_does_not_allow_unit_utf32.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_type_does_not_allow_unit_utf8_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_type_does_not_allow_variable_string.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_unit_no_size.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_segment_unit_unit.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_size_not_int.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_size_not_int_variable.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_using_pattern_variables.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_using_pattern_variables_from_other_bit_array.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_utf8_and_size.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_array_utf8_and_unit.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_arrays2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_arrays3.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__bit_arrays4.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case10.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case11.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case12.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case13.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case14.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case15.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case16.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case17.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case18.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case19.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case20.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case3.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case4.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case5.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case6.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case7.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case8.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case9.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case_clause_mismatch.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case_clause_pipe_diagnostic.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case_could_not_unify.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case_int_tuple_guard.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case_list_guard.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case_operator_unify_situation.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case_subject_pattern_unify.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case_subject_pattern_unify_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case_tuple_guard.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__case_tuple_guard_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__const_annotation_wrong.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__const_annotation_wrong_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__const_annotation_wrong_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__const_annotation_wrong_4.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__const_heterogenus_list.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__const_multiple_errors_are_local_with_annotation.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__const_multiple_errors_are_local_with_inferred_value.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__const_multiple_errors_are_local_with_unbound_value.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__const_multiple_errors_invalid_annotation.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__const_multiple_errors_invalid_annotation_and_value.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__const_multiple_errors_invalid_unannotated_value.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__const_multiple_errors_invalid_value.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__const_multiple_errors_mismatched_types.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__const_string_concat_invalid_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__const_usage_wrong.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__constructor_that_does_not_exist_does_not_produce_error_for_labelled_args.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__correct_pipe_arity_error_location.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__custom_type_module_constants.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__do_not_suggest_ignored_variable_outside_of_current_scope.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__double_assignment_in_bit_array.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_alias_names.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_anon_function_arguments.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_const_and_function_names_const_fn.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_const_const.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_const_extfn.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_const_fn.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_const_names.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_constructors.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_constructors2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_constructors3.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_custom_type_names.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_extfn_const.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_extfn_extfn.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_extfn_fn.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_fields_in_record_update_reports_error.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_fn_const.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_fn_extfn.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_fn_fn.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_function_names.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_function_names_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_function_names_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_function_names_4.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_function_names_5.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_label_shorthands_in_record_pattern.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_module_function_arguments.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_var_in_record_pattern.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_vars.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_vars_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__duplicate_vars_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__echo_followed_by_invalid_message.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__echo_followed_by_no_expression.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__echo_followed_by_no_expression_10.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__echo_followed_by_no_expression_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__echo_followed_by_no_expression_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__echo_followed_by_no_expression_4.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__echo_followed_by_no_expression_5.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__echo_followed_by_no_expression_6.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__echo_followed_by_no_expression_7.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__echo_followed_by_no_expression_8.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__echo_followed_by_no_expression_9.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__echo_followed_by_no_expression_and_invalid_message.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__echo_followed_by_no_expression_and_message.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__error_for_missing_type_parameters.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__expression_constructor_update.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__external_annotation_on_custom_type_with_constructors.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__extra_var_inalternative.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__extra_var_inalternative2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__extra_var_inalternative3.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__fault_tolerant_list.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__fault_tolerant_list_tail.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__fault_tolerant_negate_bool.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__fault_tolerant_negate_int.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__fault_tolerant_tuple.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__field_not_in_all_variants.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__field_not_in_any_variant.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__field_type_different_between_variants.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__float_gtf_int.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__float_operator_on_ints.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__float_operator_on_ints_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__float_operator_on_ints_in_case_guard.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__fn0_eq_fn1.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__function_arg_and_return_annotation.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__function_return_annotation.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__function_return_annotation_mismatch_with_pipe.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__function_that_does_not_exist_does_not_produce_error_for_labelled_args.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__functions_called_outside_module.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__generic_unlabelled_field_in_updated_record_wrong_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__guard_float_int_eq_vars.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__guard_if_float.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__guard_int_float_eq_vars.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__guard_record_wrong_arity.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__hint_for_method_call.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__incomplete_pattern_does_not_show_structure_of_internal_type_outside_of_its_module.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__incomplete_pattern_does_not_show_structure_of_internal_type_outside_of_its_module_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__incorrect_arity_error.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__incorrect_arity_error_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__inexhaustive_use_reports_error.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__inferred_variant_record_update_change_type_parameter_different_branches.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__int_eq_float.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__int_float_list.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__int_gt_float.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__int_operator_on_floats.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__int_operator_on_floats_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__int_operator_on_floats_in_case_guard.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_bit_array_pattern_discard_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_bit_array_pattern_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_case_variable_discard_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_case_variable_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_const_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_constructor_arg_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_constructor_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_constructor_pattern_discard_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_constructor_pattern_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_custom_type_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_function_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_function_type_parameter_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_list_pattern_discard_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_list_pattern_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_parameter_discard_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_parameter_discard_name2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_parameter_discard_name3.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_parameter_label.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_parameter_label2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_parameter_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_parameter_name2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_parameter_name3.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_pattern_assignment_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_pattern_label_shorthand.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_string_prefix_pattern_alias.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_string_prefix_pattern_discard_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_string_prefix_pattern_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_tuple_pattern_discard_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_tuple_pattern_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_type_alias_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_type_alias_parameter_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_type_parameter_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_use_discard_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_use_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_variable_discard_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__invalid_variable_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__leak_multiple_private_types.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__let_assert_binding_cannot_be_used_in_panic_message.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__list.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__mismatched_list_tail.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__missing_case_body.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__missing_type_constructor_arguments_in_type_annotation_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__missing_type_constructor_arguments_in_type_annotation_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__missing_variable_in_alternative_pattern.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__module_arity_error.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__module_could_not_unify.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__module_could_not_unify10.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__module_could_not_unify11.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__module_could_not_unify12.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__module_could_not_unify2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__module_could_not_unify3.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__module_could_not_unify4.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__module_could_not_unify5.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__module_could_not_unify6.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__module_could_not_unify7.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__module_could_not_unify8.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__module_could_not_unify9.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__module_non_local_gaurd_var.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__module_private_type_leak_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__module_private_type_leak_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__module_private_type_leak_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__module_private_type_leak_4.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__module_private_type_leak_5.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__module_private_type_leak_6.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__native_endianness_javascript_target.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__negate_boolean_as_integer.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__negate_float_as_integer.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__negate_string.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__negative_out_of_range_erlang_float.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__negative_out_of_range_erlang_float_in_const.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__negative_out_of_range_erlang_float_in_pattern.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__negative_size_pattern.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__no_crash_on_duplicate_definition.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__no_crash_on_duplicate_definition2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__no_crash_on_duplicate_record_fields.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__no_crash_on_record_update_when_constructor_definition_is_invalid.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__no_hint_for_non_method_call.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__no_note_about_reliable_access_if_the_accessed_type_has_a_single_variant.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__non_utf8_string_assignment.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__not_a_constructor_update.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__ok_2_args.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__out_of_range_erlang_float.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__out_of_range_erlang_float_in_const.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__out_of_range_erlang_float_in_pattern.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__pattern_with_incorrect_arity.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__pipe_arity_error.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__pipe_mismatch_error.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__pipe_value_type_mismatch_error.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__positional_argument_after_labelled.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__positional_argument_after_one_using_label_shorthand.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__private_opaque_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__qualified_type_invalid_operands.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__qualified_type_invalid_pipe_argument.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__qualified_type_mismatched_type_error.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__qualified_type_not_a_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__qualified_type_not_a_tuple.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__qualified_type_not_fn_in_use.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__qualified_type_similar_type_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__qualified_type_unification_error.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__qualified_type_unknown_field.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__qualified_type_use_fn_without_callback.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__record_access_on_inferred_variant_when_field_is_in_other_variants.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__record_update_compatible_fields_wrong_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__record_update_compatible_fields_wrong_variant.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__record_update_does_not_stop_at_first_invalid_field_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__record_update_does_not_stop_at_first_invalid_field_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__record_update_does_not_stop_at_first_invalid_field_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__record_update_does_not_stop_at_first_invalid_field_4.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__record_update_does_not_stop_at_first_invalid_field_5.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__record_update_incompatible_but_linked_generics.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__record_update_unknown_variant.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__record_update_wrong_variant.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__record_update_wrong_variant_imported_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__recursive_var.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__remembering_record_field_when_type_checking_fails.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__same_imports_multiple_times.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__same_imports_multiple_times_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__same_imports_multiple_times_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__same_imports_multiple_times_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__same_imports_multiple_times_4.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__same_imports_multiple_times_5.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__same_imports_multiple_times_6.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__same_imports_multiple_times_7.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__shadowed_fn_argument.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__shadowed_function_argument.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__shadowed_let_variable.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__shadowed_pattern_variable.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__show_only_missing_labels.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__src_importing_dev_dependency.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__subject_int_float_guard_tuple.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__suggest_unwrapping_a_result_when_types_match.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__suggest_wrapping_a_function_return_value_in_error.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__suggest_wrapping_a_function_return_value_in_ok.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__suggest_wrapping_a_use_returned_value_in_error.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__suggest_wrapping_a_use_returned_value_in_ok.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__suggest_wrapping_a_value_into_error_if_types_match.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__suggest_wrapping_a_value_into_error_if_types_match_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__suggest_wrapping_a_value_into_ok_if_types_match.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__suggest_wrapping_a_value_into_ok_if_types_match_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__suggest_wrapping_a_value_into_ok_if_types_match_with_block.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__suggest_wrapping_a_value_into_ok_if_types_match_with_multiline_result_in_block.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__suggest_wrapping_a_value_into_ok_with_generic_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__true_fn.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__tuple_2_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__tuple_arity.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__tuple_index_not_a_tuple.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__tuple_index_not_a_tuple_unbound.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__tuple_index_out_of_bounds.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__tuple_int.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__tuple_int_float.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__type_annotations.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__type_holes1.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__type_holes2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__type_holes3.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__type_holes4.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__type_imported_as_value.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__type_used_as_a_constructor_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__type_used_as_a_constructor_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__type_used_as_a_constructor_with_more_arguments.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__type_variables_in_body.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__type_vars_must_be_declared.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unexpected_arg_with_label_shorthand.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unexpected_labelled_arg.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unexpected_labelled_arg_record_constructor.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unknown_accessed_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unknown_constructor_update.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unknown_field.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unknown_field_that_appears_in_a_variant_has_note.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unknown_field_that_appears_in_an_imported_variant_has_note.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unknown_field_that_does_not_appear_in_variant_has_no_note.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unknown_field_update.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unknown_field_update2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unknown_imported_module_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unknown_label.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unknown_label_shorthand.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unknown_module.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unknown_module_suggest_import.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unknown_module_suggest_typo_for_imported_module.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unknown_module_suggest_typo_for_unimported_module.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unknown_record_field.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unknown_record_field_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unknown_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unknown_type_in_alias.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unknown_type_in_alias2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unknown_type_var_in_alias2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unknown_variable.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unknown_variable_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unknown_variable_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unknown_variable_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unknown_variable_update.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__unnecessary_spread_operator.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__update_multi_variant_record.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__utf16_codepoint_javascript_target.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__utf32_codepoint_javascript_target.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__utf8_codepoint_javascript_target.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__value_imported_as_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__wrong_number_of_subjects.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__wrong_number_of_subjects_alternative_patterns.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__wrong_type_arg.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__wrong_type_ret.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__wrong_type_update.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__wrong_type_var.snap
│ │ │ │ │ ├── gleam_core__type___tests__errors__zero_size_pattern.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__bit_array_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__bit_array_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__bit_array_bits_catches_everything.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__bit_array_bytes_needs_catch_all.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__bit_array_overlapping_patterns_are_redundant.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__bit_array_overlapping_redundant_patterns_with_variable_size.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__bit_array_overlapping_redundant_patterns_with_variable_size_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__bool_false.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__bool_true.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__case_error_prints_aliased_unqualified_value.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__case_error_prints_module_alias.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__case_error_prints_module_names.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__case_error_prints_module_when_aliased_and_shadowed.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__case_error_prints_module_when_shadowed.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__case_error_prints_prelude_module_unqualified.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__case_error_prints_prelude_module_when_shadowed.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__case_error_prints_unqualifed_when_aliased.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__case_error_prints_unqualified_value.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__compiler_does_not_crash_when_defining_duplicate_alternative_variables.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__compiler_does_not_crash_when_matching_on_utfcodepoint.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__correct_missing_patterns_for_opaque_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__correct_missing_patterns_for_opaque_type_in_definition_module.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__custom_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__custom_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__discard_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__discard_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__discard_4.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__discard_5.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__discard_6.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__duplicated_alternative_patterns.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__duplicated_pattern_in_alternative.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__duplicated_pattern_with_multiple_alternatives.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__empty_case_of_bool.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__empty_case_of_custom_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__empty_case_of_external.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__empty_case_of_float.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__empty_case_of_generic.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__empty_case_of_int.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__empty_case_of_list.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__empty_case_of_multi_pattern.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__empty_case_of_string.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__float_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__float_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__guard.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__guard_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__inexhaustive_multi_pattern.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__inexhaustive_multi_pattern2.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__inexhaustive_multi_pattern3.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__inexhaustive_multi_pattern4.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__inexhaustive_multi_pattern5.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__int_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__int_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__label_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__let_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__list_bool_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__list_bool_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__list_empty.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__list_non_empty.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__list_one.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__list_one_two.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__list_zero_one_two.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__list_zero_two_any.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__multiple_unreachable_prefix_patterns.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__multiple_unreachable_prefix_patterns_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__nested_type_parameter_usage.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__other_variant_unreachable_when_inferred.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__other_variant_unreachable_when_inferred2.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__redundant_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__redundant_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__redundant_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__redundant_4.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__redundant_5.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__redundant_float_scientific_notation.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__redundant_float_scientific_notation_and_underscore.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__redundant_float_with_different_formatting.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__redundant_float_with_no_trailing_decimal.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__redundant_float_with_underscore.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__redundant_int_with_multiple_underscores.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__redundant_int_with_underscores.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__redundant_missing_patterns.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__reference_absent_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__result_bool_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__result_bool_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__result_bool_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__result_bool_4.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__result_bool_5.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__result_bool_6.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__result_bool_7.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__result_bool_8.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__result_error.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__result_nil_error.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__result_nil_ok.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__result_ok.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__same_catch_all_bytes_are_redundant.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__string_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__string_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__string_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__tuple_0.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__unreachable_alternative_multi_pattern.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__unreachable_multi_pattern.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__unreachable_prefix_pattern_after_prefix.snap
│ │ │ │ │ ├── gleam_core__type___tests__exhaustiveness__unreachable_string_pattern_after_prefix.snap
│ │ │ │ │ ├── gleam_core__type___tests__externals__erlang_only_function_used_by_javascript_module.snap
│ │ │ │ │ ├── gleam_core__type___tests__externals__erlang_only_function_with_erlang_external.snap
│ │ │ │ │ ├── gleam_core__type___tests__externals__erlang_targeted_function_cant_contain_javascript_only_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__externals__imported_javascript_only_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__externals__javascript_only_constant.snap
│ │ │ │ │ ├── gleam_core__type___tests__externals__javascript_only_function_used_by_erlang_module.snap
│ │ │ │ │ ├── gleam_core__type___tests__externals__javascript_only_function_with_javascript_external.snap
│ │ │ │ │ ├── gleam_core__type___tests__externals__javascript_targeted_function_cant_contain_erlang_only_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__externals__public_erlang_external.snap
│ │ │ │ │ ├── gleam_core__type___tests__externals__public_javascript_external.snap
│ │ │ │ │ ├── gleam_core__type___tests__externals__unsupported_target_for_unused_import.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__annotation_mismatch_function_fault_tolerance.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__bad_body_function_fault_tolerance.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__case_clause_guard_fault_tolerance.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__case_clause_pattern_fault_tolerance.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__case_clause_then_fault_tolerance.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__function_call_incorrect_arg_types_fault_tolerance.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__function_call_incorrect_arity_fault_tolerance.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__function_call_incorrect_arity_with_label_shorthand_fault_tolerance.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__function_call_incorrect_arity_with_label_shorthand_fault_tolerance2.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__function_call_incorrect_arity_with_labels_fault_tolerance.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__function_call_incorrect_arity_with_labels_fault_tolerance2.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__invalid_javascript_external_do_not_stop_analysis.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__multiple_bad_statement_assignment_fault_tolerance.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__multiple_bad_statement_assignment_with_annotation_fault_tolerance.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__multiple_bad_statement_assignment_with_annotation_fault_tolerance2.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__multiple_bad_statement_assignment_with_pattern_fault_tolerance2.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__multiple_bad_statement_expression_fault_tolerance.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__no_impl_function_fault_tolerance.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__provide_arg_type_to_fn_arg_infer_error.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__provide_arg_type_to_fn_explicit_error.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__provide_arg_type_to_fn_implicit_error.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__provide_arg_type_to_fn_not_a_tuple.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__provide_one_arg_type_to_two_args_fn.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__provide_two_args_type_to_fn_wrong_types.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__recursive_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__unlabelled_after_labelled.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__unlabelled_after_labelled_external.snap
│ │ │ │ │ ├── gleam_core__type___tests__functions__unlabelled_after_labelled_with_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__guards__string_variable_access.snap
│ │ │ │ │ ├── gleam_core__type___tests__imports__import_errors_do_not_block_analysis.snap
│ │ │ │ │ ├── gleam_core__type___tests__imports__import_type_duplicate.snap
│ │ │ │ │ ├── gleam_core__type___tests__imports__import_type_duplicate_with_as.snap
│ │ │ │ │ ├── gleam_core__type___tests__imports__import_type_duplicate_with_as_multiline.snap
│ │ │ │ │ ├── gleam_core__type___tests__imports__imported_constructor_instead_of_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__imports__module_alias_used_as_a_name.snap
│ │ │ │ │ ├── gleam_core__type___tests__imports__unqualified_import_errors_do_not_block_later_unqualified.snap
│ │ │ │ │ ├── gleam_core__type___tests__imports__unqualified_using_opaque_constructor.snap
│ │ │ │ │ ├── gleam_core__type___tests__imports__unqualified_using_private_constructor.snap
│ │ │ │ │ ├── gleam_core__type___tests__imports__unqualified_using_private_constructor_pattern.snap
│ │ │ │ │ ├── gleam_core__type___tests__imports__unqualified_using_private_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__imports__using_opaque_constructor.snap
│ │ │ │ │ ├── gleam_core__type___tests__imports__using_private_constructor.snap
│ │ │ │ │ ├── gleam_core__type___tests__imports__using_private_constructor_pattern.snap
│ │ │ │ │ ├── gleam_core__type___tests__imports__using_private_custom_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__imports__using_private_external_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__imports__using_private_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__imports__using_private_type_alias.snap
│ │ │ │ │ ├── gleam_core__type___tests__imports__using_private_unqualified_custom_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__imports__using_private_unqualified_external_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__imports__using_private_unqualified_type_alias.snap
│ │ │ │ │ ├── gleam_core__type___tests__let_assert__non_string_message.snap
│ │ │ │ │ ├── gleam_core__type___tests__pipes__pipe_callback_wrong_arity.snap
│ │ │ │ │ ├── gleam_core__type___tests__pretty__prelude_type_clash_custom_first.snap
│ │ │ │ │ ├── gleam_core__type___tests__pretty__prelude_type_clash_prelude_first.snap
│ │ │ │ │ ├── gleam_core__type___tests__pretty__repeated_prelude_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__target_implementations__function_with_no_valid_implementations.snap
│ │ │ │ │ ├── gleam_core__type___tests__type_alias__alias_cycle.snap
│ │ │ │ │ ├── gleam_core__type___tests__type_alias__alias_direct_cycle.snap
│ │ │ │ │ ├── gleam_core__type___tests__type_alias__both_errors_are_shown.snap
│ │ │ │ │ ├── gleam_core__type___tests__type_alias__conflict_with_import.snap
│ │ │ │ │ ├── gleam_core__type___tests__type_alias__duplicate_parameter.snap
│ │ │ │ │ ├── gleam_core__type___tests__type_alias__duplicate_variable_error_does_not_stop_analysis.snap
│ │ │ │ │ ├── gleam_core__type___tests__type_alias__type_alias_error_does_not_stop_analysis.snap
│ │ │ │ │ ├── gleam_core__type___tests__type_alias__unused_parameter.snap
│ │ │ │ │ ├── gleam_core__type___tests__use___invalid_call_is_number.snap
│ │ │ │ │ ├── gleam_core__type___tests__use___invalid_callback_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__use___invalid_callback_type_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__use___invalid_callback_type_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__use___invalid_callback_type_4.snap
│ │ │ │ │ ├── gleam_core__type___tests__use___just_use_in_fn_body.snap
│ │ │ │ │ ├── gleam_core__type___tests__use___multiple_bad_statement_use_fault_tolerance.snap
│ │ │ │ │ ├── gleam_core__type___tests__use___no_callback_body.snap
│ │ │ │ │ ├── gleam_core__type___tests__use___typed_pattern_wrong_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__use___use_with_function_that_doesnt_take_callback_as_last_arg_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__use___use_with_function_that_doesnt_take_callback_as_last_arg_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__use___use_with_function_that_doesnt_take_callback_as_last_arg_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__use___wrong_arity.snap
│ │ │ │ │ ├── gleam_core__type___tests__use___wrong_arity_less_than_required.snap
│ │ │ │ │ ├── gleam_core__type___tests__use___wrong_arity_less_than_required_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__use___wrong_arity_more_than_required.snap
│ │ │ │ │ ├── gleam_core__type___tests__use___wrong_arity_more_than_required_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__use___wrong_callback_arg.snap
│ │ │ │ │ ├── gleam_core__type___tests__use___wrong_callback_arg_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__use___wrong_callback_arg_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__use___wrong_callback_arg_with_wrong_annotation.snap
│ │ │ │ │ ├── gleam_core__type___tests__use___wrong_callback_arity.snap
│ │ │ │ │ ├── gleam_core__type___tests__use___wrong_callback_arity_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__use___wrong_callback_arity_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__aliased_module_used_by_unused_function_is_not_marked_as_unused.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__assert_on_impossible_to_reach_integer_segment.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__assert_on_inferred_variant.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__bit_array_negative_truncated_segment.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__bit_array_negative_truncated_segment_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__bit_array_truncated_segment.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__bit_array_truncated_segment_in_bytes.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__bit_array_truncated_segment_in_bytes_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__bool_assert_requires_v1_11.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__bool_literals_redundant_comparison.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__bool_literals_redundant_comparison_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__const_record_update_requires_v1_14_warning.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__constant_string_concatenation_requires_v1_4.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__constructing_anonymous_function_is_pure.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__deprecated_constant.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__deprecated_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__deprecated_imported_call_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__deprecated_imported_constant.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__deprecated_imported_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__deprecated_imported_unqualified_constant.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__deprecated_imported_unqualified_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__deprecated_list_append_syntax.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__deprecated_list_pattern_syntax.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__deprecated_list_pattern_syntax_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__deprecated_record_pattern_syntax.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__deprecated_record_pattern_syntax_with_label_shorthand.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__deprecated_record_pattern_syntax_with_no_labels.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__deprecated_target_shorthand_erlang.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__deprecated_target_shorthand_javascript.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__deprecated_type_used_as_arg.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__deprecated_type_used_as_case_clause.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__deprecated_type_used_in_alias.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__detached_doc_comment.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__different_records_0_redundant_comparison.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__different_records_1_redundant_comparison.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__different_records_2_redundant_comparison.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__different_records_3_redundant_comparison.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__doesnt_warn_twice_for_unreachable_code_if_has_already_warned_in_a_block_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__doesnt_warn_twice_for_unreachable_code_if_has_already_warned_in_a_block_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__double_unary_bool_literal.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__double_unary_bool_variable.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__double_unary_integer_literal.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__double_unary_integer_variable.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__echo_followed_by_panic.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__echo_followed_by_panicking_expression.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__empty_func_warning_test.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__empty_guard_clause.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__even_number_of_multiple_bool_negations_raise_a_single_warning.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__even_number_of_multiple_integer_negations_raise_a_single_warning.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__expression_in_expression_segment_size_requires_v1_12_warning.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__expression_in_pattern_segment_size_requires_v1_12_warning.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__external_annotation_on_custom_type_requires_v1_14.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__float_divide_in_guards_requires_v1_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__float_literals_redundant_comparison.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__float_literals_redundant_comparison_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__float_literals_redundant_comparison_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__float_literals_redundant_comparison_4.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__float_literals_redundant_comparison_5.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__float_literals_redundant_comparison_6.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__float_literals_redundant_comparison_7.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__float_literals_redundant_comparison_8.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__float_literals_redundant_comparison_different_repr.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__float_literals_redundant_comparison_different_repr_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__float_literals_redundant_comparison_infinity.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__float_literals_redundant_comparison_omitted_zero.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__float_literals_redundant_comparison_precision_loss.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__float_literals_redundant_comparison_signed_zero.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__float_minus_in_guards_requires_v1_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__float_multiplication_in_guards_requires_v1_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__float_plus_in_guards_requires_v1_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__function_is_impure_if_uses_todo.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__function_is_pure_on_erlang_if_external_on_js.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__function_is_pure_on_js_if_external_on_erlang.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__import_module_twice.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__importing_non_direct_dep_package.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__impossible_to_reach_integer_segment.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__impossible_to_reach_integer_segment_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__impossible_to_reach_integer_segment_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__impossible_to_reach_integer_segment_4.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__incomplete_code_block_raises_warning.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__int_divide_in_guards_requires_v1_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__int_literals_redundant_comparison.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__int_literals_redundant_comparison_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__int_literals_redundant_comparison_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__int_literals_redundant_comparison_4.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__int_literals_redundant_comparison_5.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__int_literals_redundant_comparison_6.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__int_literals_redundant_comparison_7.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__int_literals_redundant_comparison_8.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__int_minus_in_guards_requires_v1_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__int_multiplication_in_guards_requires_v1_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__int_plus_in_guards_requires_v1_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__int_remainder_in_guards_requires_v1_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__internal_annotation_on_constant_requires_v1_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__internal_annotation_on_function_requires_v1_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__internal_annotation_on_type_requires_v1_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__javascript_external_module_with_at_requires_v1_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__javascript_unsafe_int_binary.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__javascript_unsafe_int_decimal.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__javascript_unsafe_int_hex.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__javascript_unsafe_int_in_const.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__javascript_unsafe_int_in_const_tuple.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__javascript_unsafe_int_in_pattern.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__javascript_unsafe_int_in_tuple.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__javascript_unsafe_int_octal.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__javascript_unsafe_int_segment_in_bit_array.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__javascript_unsafe_int_segment_in_const_bit_array.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__javascript_unsafe_int_segment_size_in_bit_array.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__javascript_unsafe_int_segment_size_in_const_bit_array.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__javascript_unsafe_int_segment_size_in_pattern.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__javascript_unsafe_int_with_external_function_call.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__label_shorthand_in_call_requires_v1_4.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__label_shorthand_in_constand_requires_v1_4.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__label_shorthand_in_pattern_requires_v1_4.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__let_assert_with_message_requires_v1_7.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__list_literals_redundant_comparison.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__list_literals_redundant_comparison_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__list_literals_redundant_comparison_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__list_literals_redundant_comparison_4.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__list_literals_redundant_comparison_5.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__list_literals_redundant_comparison_7.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__missing_float_option_in_bit_array_constant_segment_requires_v1_10.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__missing_float_option_in_bit_array_pattern_segment_requires_v1_10.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__missing_float_option_in_bit_array_segment_requires_v1_10.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__missing_utf_8_option_in_bit_array_constant_segment_requires_v1_5.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__missing_utf_8_option_in_bit_array_pattern_segment_requires_v1_5.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__missing_utf_8_option_in_bit_array_segment_requires_v1_5.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__module_used_by_unused_function_is_not_marked_as_unused.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__multiple_impossible_to_reach_integer_segments.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__nested_tuple_access_requires_v1_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__odd_number_of_multiple_bool_negations_raise_a_single_warning_that_highlights_the_unnecessary_ones.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__odd_number_of_multiple_integer_negations_raise_a_single_warning_that_highlights_the_unnecessary_ones.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__opaque_external_type_raises_a_warning.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__panic_used_as_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__panic_used_as_function_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__panic_used_as_function_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__panic_used_as_function_inside_pipeline.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__pattern_matching_on_64_float_float_is_unreachable.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__pattern_matching_on_literal_empty_bit_array.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__pattern_matching_on_literal_empty_list.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__pattern_matching_on_literal_empty_tuple.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__pattern_matching_on_literal_float.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__pattern_matching_on_literal_int.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__pattern_matching_on_literal_list.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__pattern_matching_on_literal_list_with_tail.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__pattern_matching_on_literal_record.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__pattern_matching_on_literal_record_with_no_args.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__pattern_matching_on_literal_string.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__pattern_matching_on_literal_tuple.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__pattern_matching_on_multiple_literal_tuples.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__prefer_list_is_empty_over_0_eq_list_length.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__prefer_list_is_empty_over_0_lt_list_length.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__prefer_list_is_empty_over_0_not_eq_list_length.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__prefer_list_is_empty_over_list_length_eq_0.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__prefer_list_is_empty_over_list_length_eq_negative_0.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__prefer_list_is_empty_over_list_length_gt_0.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__prefer_list_is_empty_over_list_length_gt_negative_0.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__prefer_list_is_empty_over_list_length_lt_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__prefer_list_is_empty_over_list_length_lt_eq_0.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__prefer_list_is_empty_over_list_length_not_eq_0.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__prefer_list_is_empty_over_negative_0_eq_list_length.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__prefer_list_is_empty_over_negative_0_lt_list_length.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__pure_pipeline_raises_warning.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__pure_pipeline_with_many_steps_raises_warning.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__pure_standard_library_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__reachable_pattern_after_unreachable_equal_pattern.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__record_access_variant_inference_requires_v1_6.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__record_select_redundant_comparison.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__record_select_redundant_comparison_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__record_update_variant_inference_requires_v1_6.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__record_update_warnings_test2.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__record_update_warnings_test3.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__record_update_with_wrong_types_but_all_fields_produces_warning.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__redundant_function_capture_in_pipe_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__redundant_function_capture_in_pipe_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__redundant_function_capture_in_pipe_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__redundant_function_capture_in_pipe_4.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__redundant_let_assert.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__redundant_let_assert_on_custom_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__result_discard_warning_test.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__result_in_case_discarded.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__shadow_imported_constant.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__shadow_imported_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__string_literals_redundant_comparison.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__string_literals_redundant_comparison_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__todo_used_as_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__todo_used_as_function_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__todo_used_as_function_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__todo_warning_correct_location.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__todo_warning_test.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__todo_with_known_type.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unreachable_code_after_case_subject_panics_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unreachable_code_after_case_subject_panics_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unreachable_code_analysis_treats_anonymous_functions_independently_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unreachable_code_analysis_treats_anonymous_functions_independently_3.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unreachable_code_for_panic_as_first_pipeline_item.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unreachable_function_argument_if_panic_is_argument.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unreachable_function_call_if_panic_is_last_argument_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unreachable_function_call_if_panic_is_last_argument_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unreachable_int_pattern_with_prefix_int.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unreachable_int_pattern_with_string_of_same_value.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unreachable_string_pattern_with_different_encodings.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unreachable_use_after_panic.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unreachable_warning_1.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unreachable_warning_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unreachable_warning_doesnt_escape_out_of_a_block_if_panic_is_not_last.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unreachable_warning_for_panic_as_last_item_of_pipe_on_next_expression.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unreachable_warning_if_all_branches_panic.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unreachable_warning_if_all_branches_panic_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unreachable_warning_on_following_expression_if_panic_is_last_in_a_block.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_alias_for_duplicate_module_no_warning_for_alias_test.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_alias_warning_test.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_binary_operation_raises_a_warning.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_bit_array.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_block_wrapping_pure_expression.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_block_wrapping_pure_expressions.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_bool_negation_raises_a_warning.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_case_expression.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_destructure.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_discard_pattern.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_float.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_fn_function_call.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_function_literal_raises_a_warning.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_imported_module_warnings_test.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_imported_module_with_alias_and_unqualified_name_no_warnings_test.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_imported_module_with_alias_and_unqualified_name_warnings_test.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_imported_module_with_alias_warnings_test.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_int.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_int_negation_raises_a_warning.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_label_shorthand_pattern_arg.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_label_shorthand_pattern_arg_shadowing.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_list.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_module_select_const.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_module_select_constructor.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_module_select_constructor_call.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_module_select_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_module_wuth_alias_warning_test.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_pipeline_ending_with_pure_fn.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_pipeline_ending_with_variant_raises_a_warning.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_pipeline_ending_with_variant_raises_a_warning_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_private_const_warnings_test.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_private_fn_warnings_test.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_private_type_warnings_test.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_private_type_warnings_test3.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_private_type_warnings_test6.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_pure_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_pure_function_that_calls_other_pure_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_record_access_raises_a_warning.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_record_constructor_raises_a_warning.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_record_update_raises_a_warning.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_recursive_function_argument.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_recursive_function_argument_2.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_recursive_function_inside_anonymous_function.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_recursive_function_with_shadowing.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_string.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_tuple.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_tuple_index_raises_a_warning.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_variable_assignment_pattern.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_variable_raises_a_warning.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_variable_shadowing_test.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_variable_string_prefix_pattern.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_variable_string_prefix_pattern2.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_variable_warnings_test.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__unused_variable_warnings_test2.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__use_with_pure_fn_expression_is_marked_as_unused.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__variables_redundant_comparison.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__warning_many_at_same_time.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__warning_private_function_never_used.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__warning_variable_never_used_test.snap
│ │ │ │ │ ├── gleam_core__type___tests__warnings__warnings_for_matches_on_literal_values_that_are_not_like_an_if_1.snap
│ │ │ │ │ └── gleam_core__type___tests__warnings__warnings_for_matches_on_literal_values_that_are_not_like_an_if_2.snap
│ │ │ │ ├── target_implementations.rs
│ │ │ │ ├── type_alias.rs
│ │ │ │ ├── use_.rs
│ │ │ │ ├── version_inference.rs
│ │ │ │ └── warnings.rs
│ │ │ └── tests.rs
│ │ ├── type_.rs
│ │ ├── uid.rs
│ │ ├── version.rs
│ │ └── warning.rs
│ └── templates/
│ ├── docs-css/
│ │ └── index.css
│ ├── docs-js/
│ │ ├── highlightjs-gleam.js
│ │ └── index.js
│ ├── documentation_layout.html
│ ├── documentation_module.html
│ ├── documentation_page.html
│ ├── echo.erl
│ ├── echo.mjs
│ ├── ejected.mk
│ ├── gleam@@main.erl
│ ├── prelude.d.mts
│ └── prelude.mjs
├── compiler-wasm/
│ ├── Cargo.toml
│ ├── README.md
│ └── src/
│ ├── lib.rs
│ ├── tests.rs
│ └── wasm_filesystem.rs
├── containers/
│ ├── elixir-alpine.dockerfile
│ ├── elixir-slim.dockerfile
│ ├── elixir.dockerfile
│ ├── erlang-alpine.dockerfile
│ ├── erlang-slim.dockerfile
│ ├── erlang.dockerfile
│ ├── node-alpine.dockerfile
│ ├── node-slim.dockerfile
│ ├── node.dockerfile
│ └── scratch.dockerfile
├── deny.toml
├── docs/
│ ├── annoyances.md
│ ├── compiler/
│ │ └── README.md
│ ├── runtime-errors.md
│ └── v2.md
├── gleam-bin/
│ ├── .cargo/
│ │ └── config.toml
│ ├── Cargo.toml
│ ├── build.rs
│ └── src/
│ └── main.rs
├── hexpm/
│ ├── .github/
│ │ └── workflows/
│ │ └── ci.yml
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── Cargo.toml
│ ├── LICENCE
│ ├── README.md
│ ├── build.rs
│ ├── proto/
│ │ ├── names.proto
│ │ ├── package.proto
│ │ ├── signed.proto
│ │ └── versions.proto
│ ├── src/
│ │ ├── lib.rs
│ │ ├── proto/
│ │ │ ├── package.rs
│ │ │ ├── signed.rs
│ │ │ └── versions.rs
│ │ ├── proto.rs
│ │ ├── tests.rs
│ │ ├── version/
│ │ │ ├── lexer.rs
│ │ │ ├── parser.rs
│ │ │ └── tests.rs
│ │ └── version.rs
│ ├── test/
│ │ ├── package_exfmt
│ │ ├── public_key
│ │ └── versions
│ └── versions
├── language-server/
│ ├── Cargo.toml
│ └── src/
│ ├── code_action.rs
│ ├── compiler.rs
│ ├── completer.rs
│ ├── edits.rs
│ ├── engine.rs
│ ├── feedback.rs
│ ├── files.rs
│ ├── lib.rs
│ ├── messages.rs
│ ├── progress.rs
│ ├── reference.rs
│ ├── rename.rs
│ ├── router.rs
│ ├── server.rs
│ ├── signature_help.rs
│ ├── tests/
│ │ ├── action.rs
│ │ ├── compilation.rs
│ │ ├── completion.rs
│ │ ├── definition.rs
│ │ ├── document_symbols.rs
│ │ ├── folding_range.rs
│ │ ├── hover.rs
│ │ ├── reference.rs
│ │ ├── rename.rs
│ │ ├── router.rs
│ │ ├── signature_help.rs
│ │ └── snapshots/
│ │ ├── gleam_language_server__tests__action__add_annotation_triggers_on_empty_space_before_function_curly_brace.snap
│ │ ├── gleam_language_server__tests__action__add_annotation_triggers_on_function_curly_brace.snap
│ │ ├── gleam_language_server__tests__action__add_correct_type_annotation_for_non_variable_use.snap
│ │ ├── gleam_language_server__tests__action__add_missing_patterns_adds_a_discard_for_opaque_type.snap
│ │ ├── gleam_language_server__tests__action__add_missing_patterns_adds_a_discard_for_opaque_type_1.snap
│ │ ├── gleam_language_server__tests__action__add_missing_patterns_adds_a_discard_for_opaque_type_2.snap
│ │ ├── gleam_language_server__tests__action__add_missing_patterns_adds_patterns_for_internal_type_inside_same_module_where_it_is_defined.snap
│ │ ├── gleam_language_server__tests__action__add_missing_patterns_bool.snap
│ │ ├── gleam_language_server__tests__action__add_missing_patterns_custom_type.snap
│ │ ├── gleam_language_server__tests__action__add_missing_patterns_infinite.snap
│ │ ├── gleam_language_server__tests__action__add_missing_patterns_inline.snap
│ │ ├── gleam_language_server__tests__action__add_missing_patterns_list.snap
│ │ ├── gleam_language_server__tests__action__add_missing_patterns_multi.snap
│ │ ├── gleam_language_server__tests__action__add_missing_patterns_multibyte_grapheme.snap
│ │ ├── gleam_language_server__tests__action__add_missing_patterns_opaque_type.snap
│ │ ├── gleam_language_server__tests__action__add_missing_patterns_tuple.snap
│ │ ├── gleam_language_server__tests__action__add_missing_patterns_with_labels.snap
│ │ ├── gleam_language_server__tests__action__add_missing_type_parameter_for_single_constructor.snap
│ │ ├── gleam_language_server__tests__action__add_missing_type_parameter_preserves_comments.snap
│ │ ├── gleam_language_server__tests__action__add_missing_type_parameter_sorted_alphabetically.snap
│ │ ├── gleam_language_server__tests__action__add_missing_type_parameter_to_exising_parameter.snap
│ │ ├── gleam_language_server__tests__action__add_multiple_annotations.snap
│ │ ├── gleam_language_server__tests__action__add_omitted_labels_does_not_label_piped_argument.snap
│ │ ├── gleam_language_server__tests__action__add_omitted_labels_does_not_label_use.snap
│ │ ├── gleam_language_server__tests__action__add_omitted_labels_in_function_call.snap
│ │ ├── gleam_language_server__tests__action__add_omitted_labels_in_function_call_uses_shorthand_syntax.snap
│ │ ├── gleam_language_server__tests__action__add_omitted_labels_in_function_call_with_some_labels.snap
│ │ ├── gleam_language_server__tests__action__add_omitted_labels_works_on_call_with_wrongly_placed_labels.snap
│ │ ├── gleam_language_server__tests__action__add_omitted_labels_works_with_constructors_calls.snap
│ │ ├── gleam_language_server__tests__action__add_omitted_labels_works_with_constructors_calls_with_some_labels.snap
│ │ ├── gleam_language_server__tests__action__add_omitted_labels_works_with_constructors_calls_with_some_labels_1.snap
│ │ ├── gleam_language_server__tests__action__add_omitted_labels_works_with_innermost_function_call.snap
│ │ ├── gleam_language_server__tests__action__add_type_annotations_public_alias_to_internal_generic_type.snap
│ │ ├── gleam_language_server__tests__action__add_type_annotations_public_alias_to_internal_type.snap
│ │ ├── gleam_language_server__tests__action__add_type_annotations_public_alias_to_internal_type_aliased_module.snap
│ │ ├── gleam_language_server__tests__action__add_type_annotations_uses_internal_name_for_same_package.snap
│ │ ├── gleam_language_server__tests__action__adding_annotations_correctly_prints_type_variables.snap
│ │ ├── gleam_language_server__tests__action__adding_annotations_prints_contextual_types.snap
│ │ ├── gleam_language_server__tests__action__adding_annotations_prints_contextual_types2.snap
│ │ ├── gleam_language_server__tests__action__adding_annotations_prints_contextual_types3.snap
│ │ ├── gleam_language_server__tests__action__adding_annotations_prints_contextual_types4.snap
│ │ ├── gleam_language_server__tests__action__adding_annotations_prints_contextual_types5.snap
│ │ ├── gleam_language_server__tests__action__adding_annotations_prints_type_variable_names.snap
│ │ ├── gleam_language_server__tests__action__allow_further_pattern_matching_on_asserted_list.snap
│ │ ├── gleam_language_server__tests__action__allow_further_pattern_matching_on_asserted_result.snap
│ │ ├── gleam_language_server__tests__action__allow_further_pattern_matching_on_let_record_destructuring.snap
│ │ ├── gleam_language_server__tests__action__allow_further_pattern_matching_on_let_tuple_destructuring.snap
│ │ ├── gleam_language_server__tests__action__annotate_all_top_level_definitions_constant.snap
│ │ ├── gleam_language_server__tests__action__annotate_all_top_level_definitions_dont_affect_local_vars.snap
│ │ ├── gleam_language_server__tests__action__annotate_all_top_level_definitions_function.snap
│ │ ├── gleam_language_server__tests__action__annotate_all_top_level_definitions_partially_annotated.snap
│ │ ├── gleam_language_server__tests__action__annotate_all_top_level_definitions_with_constant_and_generic_functions.snap
│ │ ├── gleam_language_server__tests__action__annotate_all_top_level_definitions_with_partially_annotated_generic_function.snap
│ │ ├── gleam_language_server__tests__action__annotate_all_top_level_definitions_with_two_generic_functions.snap
│ │ ├── gleam_language_server__tests__action__annotate_anonymous_function.snap
│ │ ├── gleam_language_server__tests__action__annotate_anonymous_function_with_annotated_return_type.snap
│ │ ├── gleam_language_server__tests__action__annotate_anonymous_function_with_partially_annotated_parameters.snap
│ │ ├── gleam_language_server__tests__action__annotate_constant.snap
│ │ ├── gleam_language_server__tests__action__annotate_function.snap
│ │ ├── gleam_language_server__tests__action__annotate_function_with_annotated_return_type.snap
│ │ ├── gleam_language_server__tests__action__annotate_function_with_partially_annotated_parameters.snap
│ │ ├── gleam_language_server__tests__action__annotate_local_variable.snap
│ │ ├── gleam_language_server__tests__action__annotate_local_variable_let_assert.snap
│ │ ├── gleam_language_server__tests__action__annotate_local_variable_with_pattern.snap
│ │ ├── gleam_language_server__tests__action__annotate_local_variable_with_pattern2.snap
│ │ ├── gleam_language_server__tests__action__annotate_nested_local_variable.snap
│ │ ├── gleam_language_server__tests__action__annotate_use.snap
│ │ ├── gleam_language_server__tests__action__annotate_use_with_partially_annotated_parameters.snap
│ │ ├── gleam_language_server__tests__action__assign_unused_result.snap
│ │ ├── gleam_language_server__tests__action__assign_unused_result_in_block.snap
│ │ ├── gleam_language_server__tests__action__assign_unused_result_on_block_end.snap
│ │ ├── gleam_language_server__tests__action__assign_unused_result_on_block_start.snap
│ │ ├── gleam_language_server__tests__action__assign_unused_result_only_first_action.snap
│ │ ├── gleam_language_server__tests__action__collapse_nested_case.snap
│ │ ├── gleam_language_server__tests__action__collapse_nested_case_aliases_variable_if_it_is_used.snap
│ │ ├── gleam_language_server__tests__action__collapse_nested_case_combines_inner_and_outer_guards.snap
│ │ ├── gleam_language_server__tests__action__collapse_nested_case_combines_inner_and_outer_guards_and_adds_parentheses_when_needed.snap
│ │ ├── gleam_language_server__tests__action__collapse_nested_case_combines_list_with_tail.snap
│ │ ├── gleam_language_server__tests__action__collapse_nested_case_combines_list_with_unformatted_tail.snap
│ │ ├── gleam_language_server__tests__action__collapse_nested_case_does_not_ignore_inner_guards.snap
│ │ ├── gleam_language_server__tests__action__collapse_nested_case_does_not_ignore_outer_guards.snap
│ │ ├── gleam_language_server__tests__action__collapse_nested_case_does_not_remove_labels.snap
│ │ ├── gleam_language_server__tests__action__collapse_nested_case_does_not_remove_labels_with_shorthand_syntax.snap
│ │ ├── gleam_language_server__tests__action__collapse_nested_case_works_with_alternative_patterns.snap
│ │ ├── gleam_language_server__tests__action__collapse_nested_case_works_with_blocks.snap
│ │ ├── gleam_language_server__tests__action__collapse_nested_case_works_with_patterns_defining_multiple_variables.snap
│ │ ├── gleam_language_server__tests__action__convert_assert_custom_type_with_label_shorthands_to_case.snap
│ │ ├── gleam_language_server__tests__action__convert_assert_result_to_case.snap
│ │ ├── gleam_language_server__tests__action__convert_from_use_expression_with_empty_parens.snap
│ │ ├── gleam_language_server__tests__action__convert_from_use_expression_with_multiple_patterns.snap
│ │ ├── gleam_language_server__tests__action__convert_from_use_expression_with_no_parens.snap
│ │ ├── gleam_language_server__tests__action__convert_from_use_expression_with_parens_and_other_args.snap
│ │ ├── gleam_language_server__tests__action__convert_from_use_expression_with_single_pattern.snap
│ │ ├── gleam_language_server__tests__action__convert_from_use_expression_with_type_annotations.snap
│ │ ├── gleam_language_server__tests__action__convert_from_use_multiline_with_no_trailing_comma.snap
│ │ ├── gleam_language_server__tests__action__convert_from_use_with_labels.snap
│ │ ├── gleam_language_server__tests__action__convert_from_use_with_labels_2.snap
│ │ ├── gleam_language_server__tests__action__convert_from_use_with_labels_3.snap
│ │ ├── gleam_language_server__tests__action__convert_from_use_with_labels_4.snap
│ │ ├── gleam_language_server__tests__action__convert_from_use_with_trailing_comma.snap
│ │ ├── gleam_language_server__tests__action__convert_from_use_with_trailing_comma_2.snap
│ │ ├── gleam_language_server__tests__action__convert_from_use_with_trailing_comma_and_label.snap
│ │ ├── gleam_language_server__tests__action__convert_inner_let_assert_to_case.snap
│ │ ├── gleam_language_server__tests__action__convert_let_assert_alias_to_case.snap
│ │ ├── gleam_language_server__tests__action__convert_let_assert_bit_array_to_case.snap
│ │ ├── gleam_language_server__tests__action__convert_let_assert_string_prefix_pattern_alias_to_case.snap
│ │ ├── gleam_language_server__tests__action__convert_let_assert_string_prefix_to_case.snap
│ │ ├── gleam_language_server__tests__action__convert_let_assert_to_case_discard.snap
│ │ ├── gleam_language_server__tests__action__convert_let_assert_to_case_indented.snap
│ │ ├── gleam_language_server__tests__action__convert_let_assert_to_case_multi_variables.snap
│ │ ├── gleam_language_server__tests__action__convert_let_assert_to_case_no_variables.snap
│ │ ├── gleam_language_server__tests__action__convert_let_assert_tuple_to_case.snap
│ │ ├── gleam_language_server__tests__action__convert_let_assert_with_message_to_case.snap
│ │ ├── gleam_language_server__tests__action__convert_outer_let_assert_to_case.snap
│ │ ├── gleam_language_server__tests__action__convert_to_function_call_always_inlines_the_first_step.snap
│ │ ├── gleam_language_server__tests__action__convert_to_function_call_works_when_piping_a_module_select.snap
│ │ ├── gleam_language_server__tests__action__convert_to_function_call_works_when_piping_an_invalid_module_select.snap
│ │ ├── gleam_language_server__tests__action__convert_to_function_call_works_with_argument_in_first_position.snap
│ │ ├── gleam_language_server__tests__action__convert_to_function_call_works_with_argument_in_first_position_2.snap
│ │ ├── gleam_language_server__tests__action__convert_to_function_call_works_with_argument_in_first_position_3.snap
│ │ ├── gleam_language_server__tests__action__convert_to_function_call_works_with_argument_in_first_position_4.snap
│ │ ├── gleam_language_server__tests__action__convert_to_function_call_works_with_echo.snap
│ │ ├── gleam_language_server__tests__action__convert_to_function_call_works_with_function_producing_another_function.snap
│ │ ├── gleam_language_server__tests__action__convert_to_function_call_works_with_hole_in_first_position.snap
│ │ ├── gleam_language_server__tests__action__convert_to_function_call_works_with_hole_not_in_first_position.snap
│ │ ├── gleam_language_server__tests__action__convert_to_function_call_works_with_labelled_argument.snap
│ │ ├── gleam_language_server__tests__action__convert_to_function_call_works_with_labelled_argument_2.snap
│ │ ├── gleam_language_server__tests__action__convert_to_pipe_on_first_step_of_pipeline.snap
│ │ ├── gleam_language_server__tests__action__convert_to_pipe_pipes_the_outermost_argument.snap
│ │ ├── gleam_language_server__tests__action__convert_to_pipe_when_first_arg_is_a_pipe_itself.snap
│ │ ├── gleam_language_server__tests__action__convert_to_pipe_with_bool_operator_adds_braces.snap
│ │ ├── gleam_language_server__tests__action__convert_to_pipe_with_comparison_adds_braces.snap
│ │ ├── gleam_language_server__tests__action__convert_to_pipe_with_complex_binop_adds_braces.snap
│ │ ├── gleam_language_server__tests__action__convert_to_pipe_with_function_call_on_first_argument.snap
│ │ ├── gleam_language_server__tests__action__convert_to_pipe_with_function_call_on_function_name_extracts_first_argument.snap
│ │ ├── gleam_language_server__tests__action__convert_to_pipe_with_function_call_on_second_argument.snap
│ │ ├── gleam_language_server__tests__action__convert_to_pipe_with_function_call_with_labelled_arguments_inserts_hole.snap
│ │ ├── gleam_language_server__tests__action__convert_to_pipe_with_function_call_with_labelled_arguments_inserts_hole_2.snap
│ │ ├── gleam_language_server__tests__action__convert_to_pipe_with_function_call_with_shorthand_labelled_argument_inserts_hole.snap
│ │ ├── gleam_language_server__tests__action__convert_to_pipe_with_function_call_with_shorthand_labelled_argument_inserts_hole_2.snap
│ │ ├── gleam_language_server__tests__action__convert_to_pipe_with_function_returning_other_function.snap
│ │ ├── gleam_language_server__tests__action__convert_to_pipe_with_nested_calls_picks_the_innermost_one.snap
│ │ ├── gleam_language_server__tests__action__convert_to_pipe_with_string_concat_adds_braces.snap
│ │ ├── gleam_language_server__tests__action__convert_to_pipe_with_sum_adds_no_braces.snap
│ │ ├── gleam_language_server__tests__action__convert_to_pipe_works_in_anonymous_function_inside_a_pipeline.snap
│ │ ├── gleam_language_server__tests__action__convert_to_pipe_works_in_final_step_of_a_pipeline.snap
│ │ ├── gleam_language_server__tests__action__convert_to_pipe_works_inside_body_of_use.snap
│ │ ├── gleam_language_server__tests__action__desugar_nested_use_expressions_picks_inner_under_cursor.snap
│ │ ├── gleam_language_server__tests__action__desugar_nested_use_expressions_picks_inner_under_cursor_2.snap
│ │ ├── gleam_language_server__tests__action__different_annotations_create_compatible_type_variables.snap
│ │ ├── gleam_language_server__tests__action__expand_function_capture.snap
│ │ ├── gleam_language_server__tests__action__expand_function_capture_2.snap
│ │ ├── gleam_language_server__tests__action__expand_function_capture_does_not_shadow_variables.snap
│ │ ├── gleam_language_server__tests__action__expand_function_capture_picks_a_name_based_on_the_type_of_the_hole.snap
│ │ ├── gleam_language_server__tests__action__extract_anonymous_function_with_variable_capture_1.snap
│ │ ├── gleam_language_server__tests__action__extract_anonymous_function_with_variable_capture_2.snap
│ │ ├── gleam_language_server__tests__action__extract_anonymous_function_without_variable_capture_1.snap
│ │ ├── gleam_language_server__tests__action__extract_anonymous_function_without_variable_capture_2.snap
│ │ ├── gleam_language_server__tests__action__extract_block_tail_position_3.snap
│ │ ├── gleam_language_server__tests__action__extract_block_tail_position_4.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_declaration_with_proper_indentation.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_doesnt_place_constant_below_documentation.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_doesnt_place_constant_below_large_documentation.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_call_argument_with_bit_array.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_call_argument_with_float.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_call_argument_with_int.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_call_argument_with_list.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_call_argument_with_nested_inside.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_call_argument_with_nested_outside.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_call_argument_with_string.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_call_argument_with_tuple.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_declaration_of_bin_op.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_declaration_of_bit_array.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_declaration_of_float.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_declaration_of_int.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_declaration_of_list.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_declaration_of_nested_inside.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_declaration_of_nested_outside.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_declaration_of_string.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_declaration_of_tuple.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_inside_block.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_inside_case.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_inside_use_1.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_inside_use_2.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_list_containing_constant.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_literal_within_list.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_literal_within_tuple.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_nested_inside_in_expr.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_nested_outside_in_expr.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_nil.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_non_record_variant_1.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_non_record_variant_2.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_record_variant_1.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_record_variant_2.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_return_of_float.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_return_of_int.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_return_of_list.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_return_of_nested_outside.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_return_of_string.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_return_of_tuple.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_taken_name_by_constant.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_taken_name_by_function.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_tuple_containing_constant.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_whole_declaration_of_bin_op.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_whole_declaration_of_bit_array.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_whole_declaration_of_float.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_whole_declaration_of_int.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_whole_declaration_of_list.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_whole_declaration_of_nested.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_whole_declaration_of_string.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_from_whole_declaration_of_tuple.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_in_correct_position_1.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_in_correct_position_2.snap
│ │ ├── gleam_language_server__tests__action__extract_constant_in_correct_position_3.snap
│ │ ├── gleam_language_server__tests__action__extract_function.snap
│ │ ├── gleam_language_server__tests__action__extract_function_from_statements.snap
│ │ ├── gleam_language_server__tests__action__extract_function_partially_selected.snap
│ │ ├── gleam_language_server__tests__action__extract_function_when_multiple_names_already_in_scope.snap
│ │ ├── gleam_language_server__tests__action__extract_function_when_name_already_in_scope.snap
│ │ ├── gleam_language_server__tests__action__extract_function_which_use_variables_defined_in_the_extracted_span.snap
│ │ ├── gleam_language_server__tests__action__extract_function_which_use_variables_shadowed_in_an_inner_scope.snap
│ │ ├── gleam_language_server__tests__action__extract_function_which_uses_constant.snap
│ │ ├── gleam_language_server__tests__action__extract_function_which_uses_constant_in_guard.snap
│ │ ├── gleam_language_server__tests__action__extract_function_which_uses_multiple_extracted_variables.snap
│ │ ├── gleam_language_server__tests__action__extract_function_which_uses_no_extracted_variables.snap
│ │ ├── gleam_language_server__tests__action__extract_function_which_uses_variable_in_bit_array_pattern.snap
│ │ ├── gleam_language_server__tests__action__extract_function_which_uses_variable_in_guard.snap
│ │ ├── gleam_language_server__tests__action__extract_statements_in_tail_position.snap
│ │ ├── gleam_language_server__tests__action__extract_unary_anonymous_function_with_variable_capture_1.snap
│ │ ├── gleam_language_server__tests__action__extract_unary_anonymous_function_with_variable_capture_2.snap
│ │ ├── gleam_language_server__tests__action__extract_use_in_tail_position.snap
│ │ ├── gleam_language_server__tests__action__extract_use_in_tail_position_2.snap
│ │ ├── gleam_language_server__tests__action__extract_variable.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_2.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_3.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_after_nested_anonymous_function.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_and_dont_shadow_existing_variable_in_argument.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_and_dont_shadow_existing_variable_in_operator.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_does_not_shadow_name_in_same_block.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_does_not_shadow_name_in_same_branch.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_does_not_shadow_names_in_anonymous_function.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_from_arg_in_nested_function_called_in_pipeline.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_from_arg_in_pipelined_call.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_from_arg_in_pipelined_call_of_function_to_capture.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_from_arg_in_pipelined_call_to_capture.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_from_capture_arguments_2.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_ignores_names_in_anonymous_functions.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_ignores_names_in_other_blocks.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_ignores_names_in_other_branches.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_ignores_names_in_other_branches_2.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_in_anonymous_fn_in_argument.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_in_block.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_in_case_branch.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_in_case_branch_from_second_arg.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_in_case_branch_using_var.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_in_double_nested_anonymous_function.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_in_multiline_case_subject_branch.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_in_multiline_use.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_in_nested_anonymous_function.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_in_use.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_inside_multiline_function_call.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_inside_use_body.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_starting_pipeline_steps.snap
│ │ ├── gleam_language_server__tests__action__extract_variable_with_list_with_plural_name_does_not_add_another_s.snap
│ │ ├── gleam_language_server__tests__action__fill_in_labelled_args_selects_innermost_function.snap
│ │ ├── gleam_language_server__tests__action__fill_in_labelled_args_with_some_arguments_already_supplied.snap
│ │ ├── gleam_language_server__tests__action__fill_in_labelled_args_with_some_arguments_already_supplied_2.snap
│ │ ├── gleam_language_server__tests__action__fill_in_labelled_args_with_some_arguments_already_supplied_3.snap
│ │ ├── gleam_language_server__tests__action__fill_in_labelled_args_works_with_pattern_and_no_parentheses.snap
│ │ ├── gleam_language_server__tests__action__fill_in_labelled_args_works_with_pattern_and_parentheses.snap
│ │ ├── gleam_language_server__tests__action__fill_in_labelled_args_works_with_pattern_and_parentheses_with_spaces.snap
│ │ ├── gleam_language_server__tests__action__fill_in_labelled_args_works_with_pipes.snap
│ │ ├── gleam_language_server__tests__action__fill_in_labelled_args_works_with_pipes_2.snap
│ │ ├── gleam_language_server__tests__action__fill_in_labelled_args_works_with_record_constructor.snap
│ │ ├── gleam_language_server__tests__action__fill_in_labelled_args_works_with_regular_function.snap
│ │ ├── gleam_language_server__tests__action__fill_in_labelled_args_works_with_use.snap
│ │ ├── gleam_language_server__tests__action__fill_in_labelled_args_works_with_use_2.snap
│ │ ├── gleam_language_server__tests__action__fill_in_labelled_args_works_with_use_3.snap
│ │ ├── gleam_language_server__tests__action__fill_labels_all_fields_have_matching_variables.snap
│ │ ├── gleam_language_server__tests__action__fill_labels_falls_back_to_todo_when_type_does_not_match.snap
│ │ ├── gleam_language_server__tests__action__fill_labels_generic_type_matching.snap
│ │ ├── gleam_language_server__tests__action__fill_labels_ignores_underscore_prefixed_variables.snap
│ │ ├── gleam_language_server__tests__action__fill_labels_ignores_variable_defined_after_call.snap
│ │ ├── gleam_language_server__tests__action__fill_labels_inside_anonymous_function.snap
│ │ ├── gleam_language_server__tests__action__fill_labels_inside_assignment_with_same_name_as_field.snap
│ │ ├── gleam_language_server__tests__action__fill_labels_multiple_fields_some_matching.snap
│ │ ├── gleam_language_server__tests__action__fill_labels_nested_pattern_constructor.snap
│ │ ├── gleam_language_server__tests__action__fill_labels_pattern_constructor.snap
│ │ ├── gleam_language_server__tests__action__fill_labels_pattern_constructor_let_assignment.snap
│ │ ├── gleam_language_server__tests__action__fill_labels_pattern_constructor_with_some_labels.snap
│ │ ├── gleam_language_server__tests__action__fill_labels_uses_function_argument_in_scope.snap
│ │ ├── gleam_language_server__tests__action__fill_labels_uses_variable_in_scope_with_matching_type.snap
│ │ ├── gleam_language_server__tests__action__fill_labels_variable_from_outer_scope_not_shadowed.snap
│ │ ├── gleam_language_server__tests__action__fill_labels_variable_in_scope_from_case_pattern.snap
│ │ ├── gleam_language_server__tests__action__fill_labels_variable_out_of_scope_in_block.snap
│ │ ├── gleam_language_server__tests__action__fill_unused_fields_with_all_ignored_fields.snap
│ │ ├── gleam_language_server__tests__action__fill_unused_fields_with_all_positional_fields.snap
│ │ ├── gleam_language_server__tests__action__fill_unused_fields_with_ignored_fields_never_calls_a_positional_arg_as_a_labelled_one.snap
│ │ ├── gleam_language_server__tests__action__fill_unused_fields_with_ignored_labelled_fields.snap
│ │ ├── gleam_language_server__tests__action__fill_unused_fields_with_ignored_mixed_fields.snap
│ │ ├── gleam_language_server__tests__action__fill_unused_fields_with_ignored_positional_fields.snap
│ │ ├── gleam_language_server__tests__action__fix_float_operator_on_ints.snap
│ │ ├── gleam_language_server__tests__action__fix_float_operator_on_ints_2.snap
│ │ ├── gleam_language_server__tests__action__fix_float_operator_on_ints_3.snap
│ │ ├── gleam_language_server__tests__action__fix_int_operator_on_floats.snap
│ │ ├── gleam_language_server__tests__action__fix_int_operator_on_floats_2.snap
│ │ ├── gleam_language_server__tests__action__fix_int_operator_on_floats_3.snap
│ │ ├── gleam_language_server__tests__action__fix_plus_operator_on_strings.snap
│ │ ├── gleam_language_server__tests__action__fix_truncated_segment_1.snap
│ │ ├── gleam_language_server__tests__action__fix_truncated_segment_2.snap
│ │ ├── gleam_language_server__tests__action__generate_dynamic_decoder.snap
│ │ ├── gleam_language_server__tests__action__generate_dynamic_decoder_already_imported_module.snap
│ │ ├── gleam_language_server__tests__action__generate_dynamic_decoder_complex_types.snap
│ │ ├── gleam_language_server__tests__action__generate_dynamic_decoder_does_not_produce_zero_values_for_types_from_other_packages.snap
│ │ ├── gleam_language_server__tests__action__generate_dynamic_decoder_for_multi_variant_type.snap
│ │ ├── gleam_language_server__tests__action__generate_dynamic_decoder_for_multi_variant_type_multi_word_name.snap
│ │ ├── gleam_language_server__tests__action__generate_dynamic_decoder_for_variant_with_no_fields.snap
│ │ ├── gleam_language_server__tests__action__generate_dynamic_decoder_for_variants_with_mixed_fields.snap
│ │ ├── gleam_language_server__tests__action__generate_dynamic_decoder_for_variants_with_no_fields.snap
│ │ ├── gleam_language_server__tests__action__generate_dynamic_decoder_generates_todo_for_zero_value_when_all_constructors_fail.snap
│ │ ├── gleam_language_server__tests__action__generate_dynamic_decoder_produces_zero_values_for_prelude_and_stdlib_types.snap
│ │ ├── gleam_language_server__tests__action__generate_dynamic_decoder_produces_zero_values_for_user_defined_type_in_the_same_package_1.snap
│ │ ├── gleam_language_server__tests__action__generate_dynamic_decoder_produces_zero_values_for_user_defined_type_in_the_same_package_2.snap
│ │ ├── gleam_language_server__tests__action__generate_dynamic_decoder_produces_zero_values_for_user_defined_type_in_the_same_package_3.snap
│ │ ├── gleam_language_server__tests__action__generate_dynamic_decoder_recursive_type.snap
│ │ ├── gleam_language_server__tests__action__generate_dynamic_decoder_skips_over_mutually_recursive_constructors_when_generating_zero_values.snap
│ │ ├── gleam_language_server__tests__action__generate_dynamic_decoder_skips_over_recursive_constructors_when_generating_zero_values.snap
│ │ ├── gleam_language_server__tests__action__generate_dynamic_decoder_tuple.snap
│ │ ├── gleam_language_server__tests__action__generate_dynamic_decoder_uses_decode_success_for_nil.snap
│ │ ├── gleam_language_server__tests__action__generate_dynamic_decoder_uses_smallest_possible_constructor_for_zero_value.snap
│ │ ├── gleam_language_server__tests__action__generate_function_arguments_with_labels_and_variables_uses_different_names.snap
│ │ ├── gleam_language_server__tests__action__generate_function_arguments_with_same_name_get_renamed.snap
│ │ ├── gleam_language_server__tests__action__generate_function_capture.snap
│ │ ├── gleam_language_server__tests__action__generate_function_generates_argument_names_from_labels.snap
│ │ ├── gleam_language_server__tests__action__generate_function_generates_argument_names_from_variables.snap
│ │ ├── gleam_language_server__tests__action__generate_function_in_other_module.snap
│ │ ├── gleam_language_server__tests__action__generate_function_in_other_module_correctly_appends.snap
│ │ ├── gleam_language_server__tests__action__generate_function_labels_and_arguments_can_share_the_same_name.snap
│ │ ├── gleam_language_server__tests__action__generate_function_picks_argument_name_based_on_record_access.snap
│ │ ├── gleam_language_server__tests__action__generate_function_picks_argument_name_based_on_type.snap
│ │ ├── gleam_language_server__tests__action__generate_function_takes_labels_into_account.snap
│ │ ├── gleam_language_server__tests__action__generate_function_wont_generate_two_arguments_with_the_same_name_if_they_have_the_same_type.snap
│ │ ├── gleam_language_server__tests__action__generate_function_works_with_constants.snap
│ │ ├── gleam_language_server__tests__action__generate_function_works_with_constants_2.snap
│ │ ├── gleam_language_server__tests__action__generate_function_works_with_invalid_call.snap
│ │ ├── gleam_language_server__tests__action__generate_function_works_with_pipeline_steps.snap
│ │ ├── gleam_language_server__tests__action__generate_function_works_with_pipeline_steps_1.snap
│ │ ├── gleam_language_server__tests__action__generate_json_encoder.snap
│ │ ├── gleam_language_server__tests__action__generate_json_encoder_already_imported_module.snap
│ │ ├── gleam_language_server__tests__action__generate_json_encoder_complex_types.snap
│ │ ├── gleam_language_server__tests__action__generate_json_encoder_for_multi_variant_type.snap
│ │ ├── gleam_language_server__tests__action__generate_json_encoder_for_multi_variant_type_multi_word_name.snap
│ │ ├── gleam_language_server__tests__action__generate_json_encoder_for_type_with_multiple_variants_with_no_fields.snap
│ │ ├── gleam_language_server__tests__action__generate_json_encoder_for_variant_with_no_fields.snap
│ │ ├── gleam_language_server__tests__action__generate_json_encoder_for_variants_with_mixed_fields.snap
│ │ ├── gleam_language_server__tests__action__generate_json_encoder_list_of_tuples.snap
│ │ ├── gleam_language_server__tests__action__generate_json_encoder_recursive_type.snap
│ │ ├── gleam_language_server__tests__action__generate_json_encoder_tuple.snap
│ │ ├── gleam_language_server__tests__action__generate_qualified_variant_in_other_module.snap
│ │ ├── gleam_language_server__tests__action__generate_to_json_function_ignores_nil_and_nil_tuple_fields_with_underscore.snap
│ │ ├── gleam_language_server__tests__action__generate_to_json_function_uses_json_null_for_nil.snap
│ │ ├── gleam_language_server__tests__action__generate_unqualified_variant_in_other_module.snap
│ │ ├── gleam_language_server__tests__action__generate_variant_from_pattern_with_fields.snap
│ │ ├── gleam_language_server__tests__action__generate_variant_from_pattern_with_labelled_fields.snap
│ │ ├── gleam_language_server__tests__action__generate_variant_from_pattern_with_no_fields.snap
│ │ ├── gleam_language_server__tests__action__generate_variant_with_fields_in_same_module.snap
│ │ ├── gleam_language_server__tests__action__generate_variant_with_labels_in_same_module.snap
│ │ ├── gleam_language_server__tests__action__generate_variant_with_no_fields_in_same_module.snap
│ │ ├── gleam_language_server__tests__action__generated_function_annotations_are_not_affected_by_other_functions.snap
│ │ ├── gleam_language_server__tests__action__generating_function_in_other_module_uses_labels.snap
│ │ ├── gleam_language_server__tests__action__generating_function_in_other_module_uses_local_names.snap
│ │ ├── gleam_language_server__tests__action__import_internal_module_from_same_package.snap
│ │ ├── gleam_language_server__tests__action__import_module_from_constructor.snap
│ │ ├── gleam_language_server__tests__action__import_module_from_function.snap
│ │ ├── gleam_language_server__tests__action__import_module_from_pattern.snap
│ │ ├── gleam_language_server__tests__action__import_module_from_type.snap
│ │ ├── gleam_language_server__tests__action__import_path_module_from_function.snap
│ │ ├── gleam_language_server__tests__action__import_similar_module.snap
│ │ ├── gleam_language_server__tests__action__inexhaustive_let_alias_to_case.snap
│ │ ├── gleam_language_server__tests__action__inexhaustive_let_bit_array_to_case.snap
│ │ ├── gleam_language_server__tests__action__inexhaustive_let_result_to_case.snap
│ │ ├── gleam_language_server__tests__action__inexhaustive_let_string_prefix_pattern_alias_to_case.snap
│ │ ├── gleam_language_server__tests__action__inexhaustive_let_string_prefix_to_case.snap
│ │ ├── gleam_language_server__tests__action__inexhaustive_let_to_case_discard.snap
│ │ ├── gleam_language_server__tests__action__inexhaustive_let_to_case_indented.snap
│ │ ├── gleam_language_server__tests__action__inexhaustive_let_to_case_multi_variables.snap
│ │ ├── gleam_language_server__tests__action__inexhaustive_let_to_case_no_variables.snap
│ │ ├── gleam_language_server__tests__action__inexhaustive_let_tuple_to_case.snap
│ │ ├── gleam_language_server__tests__action__inline_variable.snap
│ │ ├── gleam_language_server__tests__action__inline_variable_from_definition.snap
│ │ ├── gleam_language_server__tests__action__inline_variable_in_case_scope.snap
│ │ ├── gleam_language_server__tests__action__inline_variable_in_nested_scope.snap
│ │ ├── gleam_language_server__tests__action__inline_variable_in_record_update.snap
│ │ ├── gleam_language_server__tests__action__inline_variable_label_shorthand.snap
│ │ ├── gleam_language_server__tests__action__inline_variable_when_over_let_keyword.snap
│ │ ├── gleam_language_server__tests__action__inline_variable_with_record_field.snap
│ │ ├── gleam_language_server__tests__action__inner_inexhaustive_let_to_case.snap
│ │ ├── gleam_language_server__tests__action__interpolate_string_allows_extracting_record_access_syntax.snap
│ │ ├── gleam_language_server__tests__action__interpolate_string_does_not_add_empty_string_right_at_the_end.snap
│ │ ├── gleam_language_server__tests__action__interpolate_string_does_not_add_empty_string_right_at_the_start.snap
│ │ ├── gleam_language_server__tests__action__interpolate_string_inside_string.snap
│ │ ├── gleam_language_server__tests__action__interpolating_string_as_first_pipeline_step_inserts_brackets.snap
│ │ ├── gleam_language_server__tests__action__label_shorthand_action_only_applies_to_selected_args.snap
│ │ ├── gleam_language_server__tests__action__label_shorthand_action_works_on_labelled_call_args.snap
│ │ ├── gleam_language_server__tests__action__label_shorthand_action_works_on_labelled_constructor_call_args.snap
│ │ ├── gleam_language_server__tests__action__label_shorthand_action_works_on_labelled_pattern_call_args.snap
│ │ ├── gleam_language_server__tests__action__label_shorthand_action_works_on_labelled_update_call_args.snap
│ │ ├── gleam_language_server__tests__action__merge_case_branch.snap
│ │ ├── gleam_language_server__tests__action__merge_case_branch_can_merge_branches_defining_the_same_variables.snap
│ │ ├── gleam_language_server__tests__action__merge_case_branch_can_merge_multiple_branches.snap
│ │ ├── gleam_language_server__tests__action__merge_case_branch_with_complex_bodies_1.snap
│ │ ├── gleam_language_server__tests__action__merge_case_branch_with_complex_bodies_2.snap
│ │ ├── gleam_language_server__tests__action__merge_case_branch_with_complex_bodies_3.snap
│ │ ├── gleam_language_server__tests__action__merge_case_branch_with_complex_bodies_4.snap
│ │ ├── gleam_language_server__tests__action__merge_case_branch_with_todo_keeps_the_non_todo_body.snap
│ │ ├── gleam_language_server__tests__action__merge_case_branch_with_todo_keeps_the_non_todo_body_1.snap
│ │ ├── gleam_language_server__tests__action__merge_case_branch_with_todo_keeps_the_non_todo_body_2.snap
│ │ ├── gleam_language_server__tests__action__merge_case_branch_works_with_existing_alternative_patterns.snap
│ │ ├── gleam_language_server__tests__action__outer_inexhaustive_let_to_case.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_argument_adds_patterns_for_internal_type_inside_module_where_it_is_defined.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_argument_available_for_internal_type_defined_in_current_module.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_argument_generates_unique_names_even_with_labels.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_argument_multi_item_tuple.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_argument_nicely_formats_code_when_used_on_function_with_empty_body.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_argument_preserves_indentation_of_statement_following_inserted_let.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_argument_single_item_tuple.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_argument_single_unlabelled_field_is_not_numbered.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_argument_uses_case_with_multiple_constructors.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_argument_uses_label_shorthand_syntax_for_labelled_arguments.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_argument_will_use_aliased_constructor_name.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_argument_will_use_aliased_module_name.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_argument_will_use_qualified_name.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_argument_will_use_unqualified_name.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_argument_with_multiple_constructors_is_nicely_formatted_in_function_with_empty_body.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_argument_with_private_type_from_same_module.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_argument_works_on_fn_arguments.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_argument_works_on_nested_fn_arguments.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_clause_variable.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_clause_variable_nested_pattern.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_clause_variable_with_block_body.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_clause_variable_with_label.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_clause_variable_with_label_shorthand.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_let_assignment.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_let_assignment_with_multiple_constructors.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_list_tail.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_list_tail_used_in_a_branch.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_list_tail_with_shadowed_name.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_list_tail_with_strange_whitespace.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_list_variable.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_use_assignment.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_use_assignment_with_multiple_constructors.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_value_with_private_type_from_same_module.snap
│ │ ├── gleam_language_server__tests__action__pattern_match_on_variable_crashes.snap
│ │ ├── gleam_language_server__tests__action__qualified_aliased_to_unqualified_aliased_type.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_aliased_type.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_aliased_type_with_multiple_imports.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_basic_multiple.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_basic_record_without_argument.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_basic_type_without_argument.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_basic_with_argument.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_below_constructor.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_between_constructors.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_constant_multiple_occurrences.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_constructor_as_argument.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_constructor_complex_pattern.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_constructor_different_module_same_name_inner.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_constructor_different_module_same_name_outer.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_constructor_different_module_same_type_inner.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_constructor_different_module_same_type_outer.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_custom_type_record_declaration.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_different_constructors.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_from_constant_also_updates_functions.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_from_function_also_updates_constants.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_in_case_with_argument.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_in_case_without_argument.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_in_constant_record.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_in_constant_var.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_in_list_and_tuple.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_in_nested_constant.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_in_pattern.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_in_pattern_without_argument.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_multiple_generic_type.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_multiple_imports.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_multiple_line.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_multiple_line_aliased.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_multiple_line_bad_format_multiple_whitespace.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_multiple_line_bad_format_with_trailing_comma.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_multiple_line_bad_format_without_trailing_comma.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_nested_constructor_inner.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_nested_constructor_outer.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_nested_type_inner.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_nested_type_outer.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_type.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_when_unqualified_exists.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_with_alias.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_with_alias_multiple.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_with_comma.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_import_with_comma_pos_not_end.snap
│ │ ├── gleam_language_server__tests__action__qualified_to_unqualified_record_value_constructor_module_name.snap
│ │ ├── gleam_language_server__tests__action__remove_aliased_unused_value.snap
│ │ ├── gleam_language_server__tests__action__remove_block_1.snap
│ │ ├── gleam_language_server__tests__action__remove_block_2.snap
│ │ ├── gleam_language_server__tests__action__remove_block_3.snap
│ │ ├── gleam_language_server__tests__action__remove_block_triggers_on_the_innermost_selected_block.snap
│ │ ├── gleam_language_server__tests__action__remove_block_unwraps_a_single_expression_in_a_binop.snap
│ │ ├── gleam_language_server__tests__action__remove_echo.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_as_function_arg.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_before_pipeline.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_before_pipeline_selecting_step.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_in_pipeline_step.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_in_pipeline_step_with_message.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_in_single_line_pipeline_step.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_in_single_line_pipeline_step_with_message.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_last_in_long_pipeline_step.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_last_in_long_pipeline_step_with_message.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_last_in_short_pipeline_step.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_last_in_short_pipeline_step_with_message.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_removes_all_echos.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_removes_all_echos_1.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_removes_does_not_remove_entire_echo_statement_if_its_the_return.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_removes_does_not_remove_entire_echo_statement_if_its_the_return_of_a_fn.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_removes_entire_echo_statement_used_with_a_var.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_removes_entire_echo_statement_used_with_literals.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_removes_entire_echo_statement_used_with_literals_and_message.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_removes_entire_echo_statement_used_with_literals_in_a_fn.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_removes_multiple_entire_echo_statement_used_with_literals.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_removes_multiple_entire_echo_statement_used_with_literals_but_stops_at_comments.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_removes_multiple_entire_echo_statement_used_with_literals_in_a_fn.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_selecting_expression.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_selecting_message.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_with_message.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_with_message_and_comment.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_with_message_and_comment_2.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_with_message_and_comment_3.snap
│ │ ├── gleam_language_server__tests__action__remove_echo_with_message_removes_does_not_remove_entire_echo_statement_if_its_the_return.snap
│ │ ├── gleam_language_server__tests__action__remove_entire_unused_import.snap
│ │ ├── gleam_language_server__tests__action__remove_multiple_redundant_tuple_with_catch_all_pattern.snap
│ │ ├── gleam_language_server__tests__action__remove_multiple_unused_values.snap
│ │ ├── gleam_language_server__tests__action__remove_multiple_unused_values_2.snap
│ │ ├── gleam_language_server__tests__action__remove_opaque_from_private_type.snap
│ │ ├── gleam_language_server__tests__action__remove_redundant_tuple_in_case_retain_extras.snap
│ │ ├── gleam_language_server__tests__action__remove_redundant_tuple_in_case_subject_nested.snap
│ │ ├── gleam_language_server__tests__action__remove_redundant_tuple_in_case_subject_only_safe_remove.snap
│ │ ├── gleam_language_server__tests__action__remove_redundant_tuple_in_case_subject_simple.snap
│ │ ├── gleam_language_server__tests__action__remove_redundant_tuple_with_catch_all_pattern.snap
│ │ ├── gleam_language_server__tests__action__remove_unreachable_clauses.snap
│ │ ├── gleam_language_server__tests__action__remove_unused_alias.snap
│ │ ├── gleam_language_server__tests__action__remove_unused_simple.snap
│ │ ├── gleam_language_server__tests__action__remove_unused_start_of_file.snap
│ │ ├── gleam_language_server__tests__action__remove_unused_value.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_bit_array_pattern.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_bit_array_pattern_discard.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_case_variable.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_case_variable_discard.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_const.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_constructor.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_constructor_arg.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_constructor_pattern.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_constructor_pattern_discard.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_custom_type.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_function.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_function_type_parameter_name.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_list_pattern.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_list_pattern_discard.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_parameter.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_parameter_discard.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_parameter_discard_name2.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_parameter_discard_name3.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_parameter_label.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_parameter_label2.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_parameter_name2.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_parameter_name3.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_pattern_assignment.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_string_prefix_pattern.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_string_prefix_pattern_alias.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_string_prefix_pattern_discard.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_tuple_pattern.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_tuple_pattern_discard.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_type_alias.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_type_alias_parameter_name.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_type_parameter_name.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_use.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_use_discard.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_variable.snap
│ │ ├── gleam_language_server__tests__action__rename_invalid_variable_discard.snap
│ │ ├── gleam_language_server__tests__action__rename_module_for_imported.snap
│ │ ├── gleam_language_server__tests__action__replace_nested_underscore_in_let_annotation.snap
│ │ ├── gleam_language_server__tests__action__replace_nested_underscore_with_function_return_type.snap
│ │ ├── gleam_language_server__tests__action__replace_nested_underscore_with_generic_type.snap
│ │ ├── gleam_language_server__tests__action__replace_nested_underscore_with_tuple_type.snap
│ │ ├── gleam_language_server__tests__action__replace_underscore_in_fn_expr_argument.snap
│ │ ├── gleam_language_server__tests__action__replace_underscore_in_function_argument.snap
│ │ ├── gleam_language_server__tests__action__replace_underscore_in_let_annotation.snap
│ │ ├── gleam_language_server__tests__action__replace_underscore_with_function_return_type.snap
│ │ ├── gleam_language_server__tests__action__replace_underscore_with_type.snap
│ │ ├── gleam_language_server__tests__action__selected_statements_do_not_select_outer_block.snap
│ │ ├── gleam_language_server__tests__action__split_string.snap
│ │ ├── gleam_language_server__tests__action__splitting_string_as_first_pipeline_step_inserts_brackets.snap
│ │ ├── gleam_language_server__tests__action__turn_call_into_use_starts_from_innermost_function.snap
│ │ ├── gleam_language_server__tests__action__turn_call_into_use_with_another_use_in_the_way.snap
│ │ ├── gleam_language_server__tests__action__turn_call_into_use_with_fn_with_no_args.snap
│ │ ├── gleam_language_server__tests__action__turn_call_into_use_with_last_function_in_a_block.snap
│ │ ├── gleam_language_server__tests__action__turn_call_into_use_with_module_function.snap
│ │ ├── gleam_language_server__tests__action__turn_call_into_use_with_out_of_order_arguments.snap
│ │ ├── gleam_language_server__tests__action__turn_call_into_use_with_single_line_body.snap
│ │ ├── gleam_language_server__tests__action__turn_call_with_fn_with_type_annotations_into_use.snap
│ │ ├── gleam_language_server__tests__action__turn_call_with_multiline_fn_into_use.snap
│ │ ├── gleam_language_server__tests__action__turn_call_with_multiple_arguments_into_use.snap
│ │ ├── gleam_language_server__tests__action__type_variables_are_not_duplicated_when_adding_annotations.snap
│ │ ├── gleam_language_server__tests__action__type_variables_from_other_functions_do_not_change_annotations.snap
│ │ ├── gleam_language_server__tests__action__type_variables_from_other_functions_do_not_change_annotations_constant.snap
│ │ ├── gleam_language_server__tests__action__type_variables_in_let_bindings_are_considered_when_adding_annotations.snap
│ │ ├── gleam_language_server__tests__action__unqualified_to_qualified_import_after_constructor.snap
│ │ ├── gleam_language_server__tests__action__unqualified_to_qualified_import_bad_formatted_comma.snap
│ │ ├── gleam_language_server__tests__action__unqualified_to_qualified_import_bad_formatted_type_constructor.snap
│ │ ├── gleam_language_server__tests__action__unqualified_to_qualified_import_bad_formatted_type_constructor_with_alias.snap
│ │ ├── gleam_language_server__tests__action__unqualified_to_qualified_import_between_constructors.snap
│ │ ├── gleam_language_server__tests__action__unqualified_to_qualified_import_constant.snap
│ │ ├── gleam_language_server__tests__action__unqualified_to_qualified_import_constructor_complex_pattern.snap
│ │ ├── gleam_language_server__tests__action__unqualified_to_qualified_import_function.snap
│ │ ├── gleam_language_server__tests__action__unqualified_to_qualified_import_in_constant_record.snap
│ │ ├── gleam_language_server__tests__action__unqualified_to_qualified_import_in_constant_var.snap
│ │ ├── gleam_language_server__tests__action__unqualified_to_qualified_import_in_list_and_tuple.snap
│ │ ├── gleam_language_server__tests__action__unqualified_to_qualified_import_in_nested_constant.snap
│ │ ├── gleam_language_server__tests__action__unqualified_to_qualified_import_in_pattern_matching.snap
│ │ ├── gleam_language_server__tests__action__unqualified_to_qualified_import_multiple_line_aliased.snap
│ │ ├── gleam_language_server__tests__action__unqualified_to_qualified_import_multiple_line_bad_format_without_trailing_comma.snap
│ │ ├── gleam_language_server__tests__action__unqualified_to_qualified_import_multiple_occurrences.snap
│ │ ├── gleam_language_server__tests__action__unqualified_to_qualified_import_nested_function_call.snap
│ │ ├── gleam_language_server__tests__action__unqualified_to_qualified_import_record_constructor.snap
│ │ ├── gleam_language_server__tests__action__unqualified_to_qualified_import_type_annotation.snap
│ │ ├── gleam_language_server__tests__action__unqualified_to_qualified_import_variable_shadowing.snap
│ │ ├── gleam_language_server__tests__action__unqualified_to_qualified_import_with_alias.snap
│ │ ├── gleam_language_server__tests__action__unqualified_to_qualified_import_with_alias_and_module_alias.snap
│ │ ├── gleam_language_server__tests__action__unqualify_already_imported_type.snap
│ │ ├── gleam_language_server__tests__action__use_label_shorthand_works_for_alternative_patterns.snap
│ │ ├── gleam_language_server__tests__action__use_label_shorthand_works_for_nested_calls.snap
│ │ ├── gleam_language_server__tests__action__use_label_shorthand_works_for_nested_patterns.snap
│ │ ├── gleam_language_server__tests__action__use_label_shorthand_works_for_nested_record_updates.snap
│ │ ├── gleam_language_server__tests__action__wrap_assignment_value_in_block.snap
│ │ ├── gleam_language_server__tests__action__wrap_case_assignment_of_record_access_in_block.snap
│ │ ├── gleam_language_server__tests__action__wrap_case_clause_in_block.snap
│ │ ├── gleam_language_server__tests__action__wrap_case_clause_inside_assignment_in_block.snap
│ │ ├── gleam_language_server__tests__action__wrap_case_clause_with_guard_in_block.snap
│ │ ├── gleam_language_server__tests__action__wrap_case_clause_with_multiple_patterns_in_block.snap
│ │ ├── gleam_language_server__tests__action__wrap_nested_case_clause_in_block.snap
│ │ ├── gleam_language_server__tests__completion__argument_shadowing.snap
│ │ ├── gleam_language_server__tests__completion__argument_variable_shadowing.snap
│ │ ├── gleam_language_server__tests__completion__autocomplete_doesnt_delete_the_piece_of_code_that_comes_after.snap
│ │ ├── gleam_language_server__tests__completion__autocomplete_doesnt_delete_the_piece_of_code_that_comes_after_2.snap
│ │ ├── gleam_language_server__tests__completion__case_subject.snap
│ │ ├── gleam_language_server__tests__completion__complete_echo_keyword.snap
│ │ ├── gleam_language_server__tests__completion__complete_keyword_being_typed.snap
│ │ ├── gleam_language_server__tests__completion__complete_panic_keyword.snap
│ │ ├── gleam_language_server__tests__completion__completion_for_partially_correct_existing_module_select.snap
│ │ ├── gleam_language_server__tests__completion__completion_for_type.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_a_const_annotation.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_a_function_arg_annotation.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_a_function_return_annotation.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_a_var_annotation.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_an_import.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_an_import_from_dependency.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_an_import_from_dependency_with_docs.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_an_import_no_test.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_an_import_not_from_dev_dependency.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_an_import_not_from_dev_dependency_in_dev.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_an_import_not_from_dev_dependency_in_test.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_an_import_not_from_indirect_dependency.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_an_import_preceeding_whitespace.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_an_import_start.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_an_import_while_in_dev.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_an_import_while_in_test.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_an_import_with_docs.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_an_unqualified_import.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_an_unqualified_import_already_imported.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_an_unqualified_import_on_new_line.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_function_labels.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_imported_function_labels.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_imported_record_fields.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_imported_record_labels.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_internal_record_fields_inside_the_same_module.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_labels_in_record_update.snap.new
│ │ ├── gleam_language_server__tests__completion__completions_for_outside_a_function.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_prelude_values.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_private_record_access.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_record_access.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_record_access_known_variant.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_record_access_unknown_variant.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_record_labels.snap
│ │ ├── gleam_language_server__tests__completion__completions_for_type_import_completions_without_brackets.snap
│ │ ├── gleam_language_server__tests__completion__constant.snap
│ │ ├── gleam_language_server__tests__completion__constant_with_many_options.snap
│ │ ├── gleam_language_server__tests__completion__constant_with_module_select.snap
│ │ ├── gleam_language_server__tests__completion__do_not_show_completions_when_typing_a_number.snap
│ │ ├── gleam_language_server__tests__completion__for_custom_type_definition.snap
│ │ ├── gleam_language_server__tests__completion__for_function_arguments.snap
│ │ ├── gleam_language_server__tests__completion__for_type_alias.snap
│ │ ├── gleam_language_server__tests__completion__importable_adds_extra_new_line_if_import_exists_below_other_definitions.snap
│ │ ├── gleam_language_server__tests__completion__importable_adds_extra_new_line_if_no_imports.snap
│ │ ├── gleam_language_server__tests__completion__importable_does_not_add_extra_new_line_if_imports_exist.snap
│ │ ├── gleam_language_server__tests__completion__importable_does_not_add_extra_new_line_if_newline_exists.snap
│ │ ├── gleam_language_server__tests__completion__importable_module_function.snap
│ │ ├── gleam_language_server__tests__completion__importable_module_function_from_deep_module.snap
│ │ ├── gleam_language_server__tests__completion__importable_module_function_with_existing_imports.snap
│ │ ├── gleam_language_server__tests__completion__importable_type.snap
│ │ ├── gleam_language_server__tests__completion__importable_type_from_deep_module.snap
│ │ ├── gleam_language_server__tests__completion__importable_type_with_existing_imports.snap
│ │ ├── gleam_language_server__tests__completion__importable_type_with_existing_imports_at_top.snap
│ │ ├── gleam_language_server__tests__completion__imported_module_function.snap
│ │ ├── gleam_language_server__tests__completion__imported_public_enum.snap
│ │ ├── gleam_language_server__tests__completion__imported_public_record.snap
│ │ ├── gleam_language_server__tests__completion__imported_type.snap
│ │ ├── gleam_language_server__tests__completion__imported_type_cursor_after_dot.snap
│ │ ├── gleam_language_server__tests__completion__imported_type_cursor_after_dot_other_matching_modules.snap
│ │ ├── gleam_language_server__tests__completion__imported_type_cursor_after_dot_other_modules.snap
│ │ ├── gleam_language_server__tests__completion__imported_type_cursor_mid_phrase_other_modules.snap
│ │ ├── gleam_language_server__tests__completion__imported_unqualified_module_function.snap
│ │ ├── gleam_language_server__tests__completion__imported_unqualified_public_enum.snap
│ │ ├── gleam_language_server__tests__completion__imported_unqualified_public_record.snap
│ │ ├── gleam_language_server__tests__completion__in_custom_type_definition.snap
│ │ ├── gleam_language_server__tests__completion__internal_modules_from_same_package_are_included.snap
│ │ ├── gleam_language_server__tests__completion__internal_types_from_a_dependency_are_ignored.snap
│ │ ├── gleam_language_server__tests__completion__internal_types_from_root_package_are_in_the_completions.snap
│ │ ├── gleam_language_server__tests__completion__internal_types_from_the_same_module_are_in_the_completions.snap
│ │ ├── gleam_language_server__tests__completion__internal_values_from_a_dependency_are_ignored.snap
│ │ ├── gleam_language_server__tests__completion__internal_values_from_root_package_are_in_the_completions.snap
│ │ ├── gleam_language_server__tests__completion__internal_values_from_the_same_module_are_in_the_completions.snap
│ │ ├── gleam_language_server__tests__completion__labelled_arguments.snap
│ │ ├── gleam_language_server__tests__completion__labelled_arguments_after_label.snap
│ │ ├── gleam_language_server__tests__completion__labelled_arguments_from_different_module.snap
│ │ ├── gleam_language_server__tests__completion__labelled_arguments_function_call.snap
│ │ ├── gleam_language_server__tests__completion__labelled_arguments_with_existing_label.snap
│ │ ├── gleam_language_server__tests__completion__local_private_type.snap
│ │ ├── gleam_language_server__tests__completion__local_public_enum.snap
│ │ ├── gleam_language_server__tests__completion__local_public_enum_with_documentation.snap
│ │ ├── gleam_language_server__tests__completion__local_public_function.snap
│ │ ├── gleam_language_server__tests__completion__local_public_function_with_documentation.snap
│ │ ├── gleam_language_server__tests__completion__local_public_record.snap
│ │ ├── gleam_language_server__tests__completion__local_public_record_with_documentation.snap
│ │ ├── gleam_language_server__tests__completion__local_variable.snap
│ │ ├── gleam_language_server__tests__completion__local_variable_anonymous_function.snap
│ │ ├── gleam_language_server__tests__completion__local_variable_as.snap
│ │ ├── gleam_language_server__tests__completion__local_variable_bit_array.snap
│ │ ├── gleam_language_server__tests__completion__local_variable_case_expression.snap
│ │ ├── gleam_language_server__tests__completion__local_variable_function_call.snap
│ │ ├── gleam_language_server__tests__completion__local_variable_ignore_anonymous_function_args.snap
│ │ ├── gleam_language_server__tests__completion__local_variable_ignore_anonymous_function_args_nested.snap
│ │ ├── gleam_language_server__tests__completion__local_variable_ignore_anonymous_function_returned.snap
│ │ ├── gleam_language_server__tests__completion__local_variable_ignore_within_function.snap
│ │ ├── gleam_language_server__tests__completion__local_variable_ignored.snap
│ │ ├── gleam_language_server__tests__completion__local_variable_inside_nested_exprs.snap
│ │ ├── gleam_language_server__tests__completion__local_variable_nested_anonymous_function.snap
│ │ ├── gleam_language_server__tests__completion__local_variable_pipe.snap
│ │ ├── gleam_language_server__tests__completion__local_variable_pipe_with_args.snap
│ │ ├── gleam_language_server__tests__completion__local_variable_string.snap
│ │ ├── gleam_language_server__tests__completion__local_variable_tuple.snap
│ │ ├── gleam_language_server__tests__completion__no_completions_for_imported_internal_record_fields.snap
│ │ ├── gleam_language_server__tests__completion__no_label_completions_in_nested_expression.snap
│ │ ├── gleam_language_server__tests__completion__no_variable_completions_after_anonymous_function_scope.snap
│ │ ├── gleam_language_server__tests__completion__no_variable_completions_after_block_scope.snap
│ │ ├── gleam_language_server__tests__completion__no_variable_completions_after_case_clause_scope.snap
│ │ ├── gleam_language_server__tests__completion__no_variable_completions_after_case_scope.snap
│ │ ├── gleam_language_server__tests__completion__no_variable_completions_before_case_clause.snap
│ │ ├── gleam_language_server__tests__completion__no_variable_completions_before_declaration_in_anonymous_function.snap
│ │ ├── gleam_language_server__tests__completion__no_variable_completions_before_declaration_in_block.snap
│ │ ├── gleam_language_server__tests__completion__opaque_type.snap
│ │ ├── gleam_language_server__tests__completion__prefer_function_which_returns_expected_generic_type.snap
│ │ ├── gleam_language_server__tests__completion__prefer_function_which_returns_expected_type.snap
│ │ ├── gleam_language_server__tests__completion__prefer_values_matching_expected_type.snap
│ │ ├── gleam_language_server__tests__completion__private_function.snap
│ │ ├── gleam_language_server__tests__completion__private_function_in_dep.snap
│ │ ├── gleam_language_server__tests__completion__private_type.snap
│ │ ├── gleam_language_server__tests__completion__private_type_in_dep.snap
│ │ ├── gleam_language_server__tests__completion__unqualified_imported_type.snap
│ │ ├── gleam_language_server__tests__completion__variable_shadowing.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_constant.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_constant_imported_record.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_constant_record.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_deep_type_in_module.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_external_module_constants.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_external_module_function_calls.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_external_module_records.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_from_alternative_pattern.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_from_anonymous_function.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_import.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_import_aliased.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_import_unqualified_type.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_import_unqualified_value.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_imported_constant.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_imported_module_constants.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_imported_module_records.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_local_variable.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_module.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_module_function_calls.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_of_external_function_in_same_module.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_of_local_variable_from_guard.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_of_module_select_from_guard.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_of_record_from_guard.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_of_record_module_select_from_guard.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_path_module_function_calls.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_record_update.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_same_module_constants.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_same_module_functions.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_same_module_records.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_type.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_type_in_module.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_type_in_path_dep.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_unqualified_function.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_unqualified_imported_module_constants.snap
│ │ ├── gleam_language_server__tests__definition__goto_definition_unqualified_imported_module_records.snap
│ │ ├── gleam_language_server__tests__definition__goto_type_definition_can_jump_to_all_types_in_a_function_type.snap
│ │ ├── gleam_language_server__tests__definition__goto_type_definition_can_jump_to_all_types_in_a_tuple.snap
│ │ ├── gleam_language_server__tests__definition__goto_type_definition_can_jump_to_multiple_types.snap
│ │ ├── gleam_language_server__tests__definition__goto_type_definition_in_different_file_of_dependency.snap
│ │ ├── gleam_language_server__tests__definition__goto_type_definition_in_different_file_of_same_project.snap
│ │ ├── gleam_language_server__tests__definition__goto_type_definition_in_same_file.snap
│ │ ├── gleam_language_server__tests__document_symbols__doc_symbols_constant.snap
│ │ ├── gleam_language_server__tests__document_symbols__doc_symbols_function.snap
│ │ ├── gleam_language_server__tests__document_symbols__doc_symbols_type_alias.snap
│ │ ├── gleam_language_server__tests__document_symbols__doc_symbols_type_constructor_labeled_args.snap
│ │ ├── gleam_language_server__tests__document_symbols__doc_symbols_type_constructor_no_args.snap
│ │ ├── gleam_language_server__tests__document_symbols__doc_symbols_type_constructor_pos_and_labeled_args.snap
│ │ ├── gleam_language_server__tests__document_symbols__doc_symbols_type_constructor_pos_args.snap
│ │ ├── gleam_language_server__tests__document_symbols__doc_symbols_type_no_constructors.snap
│ │ ├── gleam_language_server__tests__document_symbols__doc_symbols_type_no_constructors_starting_at_documentation.snap
│ │ ├── gleam_language_server__tests__document_symbols__doc_symbols_type_no_constructors_starting_at_empty_doc.snap
│ │ ├── gleam_language_server__tests__folding_range__folds_import_block.snap
│ │ ├── gleam_language_server__tests__folding_range__folds_mixed_definitions_in_source_order.snap
│ │ ├── gleam_language_server__tests__folding_range__folds_multiline_constant.snap
│ │ ├── gleam_language_server__tests__folding_range__folds_multiline_custom_type.snap
│ │ ├── gleam_language_server__tests__folding_range__folds_multiline_function_body.snap
│ │ ├── gleam_language_server__tests__folding_range__folds_multiline_type_alias.snap
│ │ ├── gleam_language_server__tests__folding_range__folds_only_multiline_functions_in_source_order.snap
│ │ ├── gleam_language_server__tests__folding_range__folds_separated_import_blocks.snap
│ │ ├── gleam_language_server__tests__hover__documentation_for_shared_record_field_when_variant_is_known.snap
│ │ ├── gleam_language_server__tests__hover__hover_assignment_annotation.snap
│ │ ├── gleam_language_server__tests__hover__hover_contextual_type.snap
│ │ ├── gleam_language_server__tests__hover__hover_contextual_type_aliased.snap
│ │ ├── gleam_language_server__tests__hover__hover_contextual_type_aliased_module.snap
│ │ ├── gleam_language_server__tests__hover__hover_contextual_type_annotation.snap
│ │ ├── gleam_language_server__tests__hover__hover_contextual_type_annotation_aliased.snap
│ │ ├── gleam_language_server__tests__hover__hover_contextual_type_annotation_aliased_module.snap
│ │ ├── gleam_language_server__tests__hover__hover_contextual_type_annotation_prelude.snap
│ │ ├── gleam_language_server__tests__hover__hover_contextual_type_annotation_unqualified.snap
│ │ ├── gleam_language_server__tests__hover__hover_contextual_type_annotation_unqualified_aliased.snap
│ │ ├── gleam_language_server__tests__hover__hover_contextual_type_arg.snap
│ │ ├── gleam_language_server__tests__hover__hover_contextual_type_expression.snap
│ │ ├── gleam_language_server__tests__hover__hover_contextual_type_function.snap
│ │ ├── gleam_language_server__tests__hover__hover_contextual_type_pattern.snap
│ │ ├── gleam_language_server__tests__hover__hover_contextual_type_pattern_spread.snap
│ │ ├── gleam_language_server__tests__hover__hover_contextual_type_unqualified.snap
│ │ ├── gleam_language_server__tests__hover__hover_contextual_type_unqualified_aliased.snap
│ │ ├── gleam_language_server__tests__hover__hover_contextual_type_unqualified_import.snap
│ │ ├── gleam_language_server__tests__hover__hover_expressions_in_function_body.snap
│ │ ├── gleam_language_server__tests__hover__hover_external_function_with_another_value_same_name.snap
│ │ ├── gleam_language_server__tests__hover__hover_external_imported_constants.snap
│ │ ├── gleam_language_server__tests__hover__hover_external_imported_ffi_renamed_function.snap
│ │ ├── gleam_language_server__tests__hover__hover_external_imported_function.snap
│ │ ├── gleam_language_server__tests__hover__hover_external_imported_function_nested_module.snap
│ │ ├── gleam_language_server__tests__hover__hover_external_imported_function_renamed_module.snap
│ │ ├── gleam_language_server__tests__hover__hover_external_imported_unqualified_constants.snap
│ │ ├── gleam_language_server__tests__hover__hover_external_imported_unqualified_function.snap
│ │ ├── gleam_language_server__tests__hover__hover_external_unqualified_imported_function_renamed_module.snap
│ │ ├── gleam_language_server__tests__hover__hover_external_value_with_two_modules_same_name.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_annotation_in_use.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_anonymous_function_annotation.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_constant_bit_array.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_constant_bit_array_segment.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_constant_bit_array_segment_option.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_constant_float.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_constant_int.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_constant_list.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_constant_list_element.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_constant_other_constant.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_constant_record.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_constant_string.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_constant_string_concatenation.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_constant_string_concatenation_side.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_constant_tuple.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_constant_tuple_element.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_custom_type.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_invalid_record_update_1.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_invalid_record_update_2.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_invalid_record_update_3.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_invalid_record_update_4.snap.new
│ │ ├── gleam_language_server__tests__hover__hover_for_label_in_expression.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_label_in_pattern.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_local_variable_from_guard.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_module_select_from_guard.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_module_select_pattern.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_nested_constant.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_pattern_in_use.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_pattern_spread_ignoring_all_fields.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_pattern_spread_ignoring_all_positional_fields.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_pattern_spread_ignoring_some_fields.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_record_from_guard.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_record_module_select_from_guard.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_string_prefix_pattern.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_string_prefix_pattern_prefix_alias.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_string_prefix_pattern_prefix_alias_alternative_definition.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_string_prefix_pattern_suffix_variable.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_string_prefix_pattern_suffix_variable_alternative_definition.snap
│ │ ├── gleam_language_server__tests__hover__hover_for_string_prefix_pattern_suffix_variable_discard.snap
│ │ ├── gleam_language_server__tests__hover__hover_function_arg_annotation_2.snap
│ │ ├── gleam_language_server__tests__hover__hover_function_arg_annotation_with_documentation.snap
│ │ ├── gleam_language_server__tests__hover__hover_function_argument.snap
│ │ ├── gleam_language_server__tests__hover__hover_function_definition.snap
│ │ ├── gleam_language_server__tests__hover__hover_function_definition_with_docs.snap
│ │ ├── gleam_language_server__tests__hover__hover_function_return_annotation.snap
│ │ ├── gleam_language_server__tests__hover__hover_function_return_annotation_with_tuple.snap
│ │ ├── gleam_language_server__tests__hover__hover_import_unqualified_type.snap
│ │ ├── gleam_language_server__tests__hover__hover_import_unqualified_value.snap
│ │ ├── gleam_language_server__tests__hover__hover_import_unqualified_value_from_hex.snap
│ │ ├── gleam_language_server__tests__hover__hover_imported_function.snap
│ │ ├── gleam_language_server__tests__hover__hover_label_shorthand_in_call_arg.snap
│ │ ├── gleam_language_server__tests__hover__hover_label_shorthand_in_pattern_call_arg.snap
│ │ ├── gleam_language_server__tests__hover__hover_label_shorthand_in_pattern_call_arg_2.snap
│ │ ├── gleam_language_server__tests__hover__hover_local_function.snap
│ │ ├── gleam_language_server__tests__hover__hover_local_function_in_pipe.snap
│ │ ├── gleam_language_server__tests__hover__hover_local_function_in_pipe_1.snap
│ │ ├── gleam_language_server__tests__hover__hover_local_function_in_pipe_2.snap
│ │ ├── gleam_language_server__tests__hover__hover_local_function_in_pipe_3.snap
│ │ ├── gleam_language_server__tests__hover__hover_module_constant.snap
│ │ ├── gleam_language_server__tests__hover__hover_module_constant_annotation.snap
│ │ ├── gleam_language_server__tests__hover__hover_on_pipe_with_invalid_step.snap
│ │ ├── gleam_language_server__tests__hover__hover_on_pipe_with_invalid_step_1.snap
│ │ ├── gleam_language_server__tests__hover__hover_on_pipe_with_invalid_step_2.snap
│ │ ├── gleam_language_server__tests__hover__hover_on_pipe_with_invalid_step_3.snap
│ │ ├── gleam_language_server__tests__hover__hover_on_pipe_with_invalid_step_4.snap
│ │ ├── gleam_language_server__tests__hover__hover_on_pipe_with_invalid_step_5.snap
│ │ ├── gleam_language_server__tests__hover__hover_on_pipe_with_invalid_step_6.snap
│ │ ├── gleam_language_server__tests__hover__hover_on_pipe_with_invalid_step_8.snap
│ │ ├── gleam_language_server__tests__hover__hover_over_block_in_list_spread.snap
│ │ ├── gleam_language_server__tests__hover__hover_over_imported_module.snap
│ │ ├── gleam_language_server__tests__hover__hover_over_module_name.snap
│ │ ├── gleam_language_server__tests__hover__hover_over_module_name_in_annotation.snap
│ │ ├── gleam_language_server__tests__hover__hover_over_module_with_path.snap
│ │ ├── gleam_language_server__tests__hover__hover_prelude_type.snap
│ │ ├── gleam_language_server__tests__hover__hover_print_alias_when_parameters_match.snap
│ │ ├── gleam_language_server__tests__hover__hover_print_aliased_imported_generic_type.snap
│ │ ├── gleam_language_server__tests__hover__hover_print_imported_alias.snap
│ │ ├── gleam_language_server__tests__hover__hover_print_qualified_prelude_type_when_shadowed_by_alias.snap
│ │ ├── gleam_language_server__tests__hover__hover_print_qualified_prelude_type_when_shadowed_by_imported_alias.snap
│ │ ├── gleam_language_server__tests__hover__hover_print_type_variable_names.snap
│ │ ├── gleam_language_server__tests__hover__hover_print_unbound_type_variable_name_without_conflicts.snap
│ │ ├── gleam_language_server__tests__hover__hover_print_unbound_type_variable_names.snap
│ │ ├── gleam_language_server__tests__hover__hover_print_underlying_for_alias_with_parameters.snap
│ │ ├── gleam_language_server__tests__hover__hover_print_underlying_for_imported_alias.snap
│ │ ├── gleam_language_server__tests__hover__hover_shadowed_prelude_type.snap
│ │ ├── gleam_language_server__tests__hover__hover_shadowed_prelude_type_imported.snap
│ │ ├── gleam_language_server__tests__hover__hover_type_alias_annotation.snap
│ │ ├── gleam_language_server__tests__hover__hover_type_constructor_annotation.snap
│ │ ├── gleam_language_server__tests__hover__hover_type_constructor_with_label.snap
│ │ ├── gleam_language_server__tests__hover__hover_type_constructor_with_no_fields.snap
│ │ ├── gleam_language_server__tests__hover__hover_type_constructor_with_no_label.snap
│ │ ├── gleam_language_server__tests__hover__hover_variable_in_use_expression.snap
│ │ ├── gleam_language_server__tests__hover__hover_variable_in_use_expression_1.snap
│ │ ├── gleam_language_server__tests__hover__hover_variable_in_use_expression_2.snap
│ │ ├── gleam_language_server__tests__hover__hover_works_even_for_invalid_code.snap
│ │ ├── gleam_language_server__tests__hover__no_documentation_for_shared_record_field.snap
│ │ ├── gleam_language_server__tests__hover__no_hexdocs_link_when_hovering_over_local_module.snap
│ │ ├── gleam_language_server__tests__hover__record_field_documentation.snap
│ │ ├── gleam_language_server__tests__reference__references_for_aliased_const.snap
│ │ ├── gleam_language_server__tests__reference__references_for_aliased_function.snap
│ │ ├── gleam_language_server__tests__reference__references_for_aliased_type.snap
│ │ ├── gleam_language_server__tests__reference__references_for_aliased_value.snap
│ │ ├── gleam_language_server__tests__reference__references_for_constant_from_qualified_reference.snap
│ │ ├── gleam_language_server__tests__reference__references_for_constant_from_unqualified_reference.snap
│ │ ├── gleam_language_server__tests__reference__references_for_function_from_qualified_reference.snap
│ │ ├── gleam_language_server__tests__reference__references_for_function_from_unqualified_reference.snap
│ │ ├── gleam_language_server__tests__reference__references_for_local_variable.snap
│ │ ├── gleam_language_server__tests__reference__references_for_local_variable_from_definition.snap
│ │ ├── gleam_language_server__tests__reference__references_for_local_variable_from_guard.snap
│ │ ├── gleam_language_server__tests__reference__references_for_module_select_from_guard.snap
│ │ ├── gleam_language_server__tests__reference__references_for_prefix_string_alias_and_suffix_complex_guard.snap
│ │ ├── gleam_language_server__tests__reference__references_for_prefix_string_alias_in_case.snap
│ │ ├── gleam_language_server__tests__reference__references_for_prefix_string_alias_in_case_triggered_from_usage.snap
│ │ ├── gleam_language_server__tests__reference__references_for_prefix_string_alias_in_let_assert.snap
│ │ ├── gleam_language_server__tests__reference__references_for_prefix_string_alias_in_let_assert_triggered_from_usage.snap
│ │ ├── gleam_language_server__tests__reference__references_for_prefix_string_alias_used_in_guard.snap
│ │ ├── gleam_language_server__tests__reference__references_for_prefix_string_alias_with_alternative_definitions_in_case.snap
│ │ ├── gleam_language_server__tests__reference__references_for_prefix_string_alias_with_alternative_definitions_triggered_from_second_pattern.snap
│ │ ├── gleam_language_server__tests__reference__references_for_prefix_string_alias_with_alternative_definitions_triggered_from_usage.snap
│ │ ├── gleam_language_server__tests__reference__references_for_prefix_string_suffix_shadowing_outer_variable.snap
│ │ ├── gleam_language_server__tests__reference__references_for_prefix_string_suffix_used_in_guard.snap
│ │ ├── gleam_language_server__tests__reference__references_for_prefix_string_suffix_variable_in_case.snap
│ │ ├── gleam_language_server__tests__reference__references_for_prefix_string_suffix_variable_in_case_triggered_from_usage.snap
│ │ ├── gleam_language_server__tests__reference__references_for_prefix_string_suffix_variable_in_let_assert.snap
│ │ ├── gleam_language_server__tests__reference__references_for_prefix_string_suffix_variable_in_let_assert_triggered_from_usage.snap
│ │ ├── gleam_language_server__tests__reference__references_for_prefix_string_suffix_variable_nested_in_tuple.snap
│ │ ├── gleam_language_server__tests__reference__references_for_prefix_string_suffix_variable_with_alternative_definition_in_case.snap
│ │ ├── gleam_language_server__tests__reference__references_for_prefix_string_suffix_variable_with_alternative_definition_triggered_from_second_pattern.snap
│ │ ├── gleam_language_server__tests__reference__references_for_prefix_string_suffix_variable_with_alternative_definition_triggered_from_usage.snap
│ │ ├── gleam_language_server__tests__reference__references_for_private_constant.snap
│ │ ├── gleam_language_server__tests__reference__references_for_private_constant_from_reference.snap
│ │ ├── gleam_language_server__tests__reference__references_for_private_function.snap
│ │ ├── gleam_language_server__tests__reference__references_for_private_function_from_reference.snap
│ │ ├── gleam_language_server__tests__reference__references_for_private_type.snap
│ │ ├── gleam_language_server__tests__reference__references_for_private_type_from_reference.snap
│ │ ├── gleam_language_server__tests__reference__references_for_private_type_variant.snap
│ │ ├── gleam_language_server__tests__reference__references_for_private_type_variant_from_reference.snap
│ │ ├── gleam_language_server__tests__reference__references_for_public_constant.snap
│ │ ├── gleam_language_server__tests__reference__references_for_public_function.snap
│ │ ├── gleam_language_server__tests__reference__references_for_public_type.snap
│ │ ├── gleam_language_server__tests__reference__references_for_public_type_variant.snap
│ │ ├── gleam_language_server__tests__reference__references_for_type_from_let_annotation.snap
│ │ ├── gleam_language_server__tests__reference__references_for_type_from_qualified_reference.snap
│ │ ├── gleam_language_server__tests__reference__references_for_type_from_unqualified_reference.snap
│ │ ├── gleam_language_server__tests__reference__references_for_type_variant_from_qualified_reference.snap
│ │ ├── gleam_language_server__tests__reference__references_for_type_variant_from_unqualified_reference.snap
│ │ ├── gleam_language_server__tests__rename__alias_imported_module_from_guard.snap
│ │ ├── gleam_language_server__tests__rename__no_rename_constant_with_invalid_name.snap
│ │ ├── gleam_language_server__tests__rename__no_rename_function_with_invalid_name.snap
│ │ ├── gleam_language_server__tests__rename__no_rename_invalid_name.snap
│ │ ├── gleam_language_server__tests__rename__no_rename_type_variant_with_invalid_name.snap
│ │ ├── gleam_language_server__tests__rename__no_rename_type_with_invalid_name.snap
│ │ ├── gleam_language_server__tests__rename__reanem_module_from_import_with_unqualified_values.snap
│ │ ├── gleam_language_server__tests__rename__rename_aliased_constant.snap
│ │ ├── gleam_language_server__tests__rename__rename_aliased_function.snap
│ │ ├── gleam_language_server__tests__rename__rename_aliased_type.snap
│ │ ├── gleam_language_server__tests__rename__rename_aliased_type_variant.snap
│ │ ├── gleam_language_server__tests__rename__rename_aliased_value.snap
│ │ ├── gleam_language_server__tests__rename__rename_alternative_pattern.snap
│ │ ├── gleam_language_server__tests__rename__rename_alternative_pattern_alias_and_variable_1.snap
│ │ ├── gleam_language_server__tests__rename__rename_alternative_pattern_alias_and_variable_2.snap
│ │ ├── gleam_language_server__tests__rename__rename_alternative_pattern_alias_and_variable_3.snap
│ │ ├── gleam_language_server__tests__rename__rename_alternative_pattern_alias_and_variable_4.snap
│ │ ├── gleam_language_server__tests__rename__rename_alternative_pattern_aliases.snap
│ │ ├── gleam_language_server__tests__rename__rename_alternative_pattern_aliases_from_alternative.snap
│ │ ├── gleam_language_server__tests__rename__rename_alternative_pattern_aliases_from_usage.snap
│ │ ├── gleam_language_server__tests__rename__rename_alternative_pattern_from_usage.snap
│ │ ├── gleam_language_server__tests__rename__rename_constant_from_definition.snap
│ │ ├── gleam_language_server__tests__rename__rename_constant_from_qualified_reference.snap
│ │ ├── gleam_language_server__tests__rename__rename_constant_from_reference.snap
│ │ ├── gleam_language_server__tests__rename__rename_constant_from_unqualified_reference.snap
│ │ ├── gleam_language_server__tests__rename__rename_constant_shadowed_by_field_access.snap
│ │ ├── gleam_language_server__tests__rename__rename_constant_shadowing_module.snap
│ │ ├── gleam_language_server__tests__rename__rename_custom_type_variant_pattern.snap
│ │ ├── gleam_language_server__tests__rename__rename_external_function.snap
│ │ ├── gleam_language_server__tests__rename__rename_external_javascript_function_with_pure_gleam_fallback.snap
│ │ ├── gleam_language_server__tests__rename__rename_function_from_definition.snap
│ │ ├── gleam_language_server__tests__rename__rename_function_from_qualified_reference.snap
│ │ ├── gleam_language_server__tests__rename__rename_function_from_reference.snap
│ │ ├── gleam_language_server__tests__rename__rename_function_from_unqualified_reference.snap
│ │ ├── gleam_language_server__tests__rename__rename_function_shadowed_by_field_access.snap
│ │ ├── gleam_language_server__tests__rename__rename_function_shadowing_module.snap
│ │ ├── gleam_language_server__tests__rename__rename_imported_custom_type_variant_pattern.snap
│ │ ├── gleam_language_server__tests__rename__rename_imported_unqualified_custom_type_variant_pattern.snap
│ │ ├── gleam_language_server__tests__rename__rename_local_variable.snap
│ │ ├── gleam_language_server__tests__rename__rename_local_variable_argument.snap
│ │ ├── gleam_language_server__tests__rename__rename_local_variable_argument_from_definition.snap
│ │ ├── gleam_language_server__tests__rename__rename_local_variable_assignment_pattern.snap
│ │ ├── gleam_language_server__tests__rename__rename_local_variable_from_bit_array_pattern.snap
│ │ ├── gleam_language_server__tests__rename__rename_local_variable_from_definition.snap
│ │ ├── gleam_language_server__tests__rename__rename_local_variable_from_definition_assignment_pattern.snap
│ │ ├── gleam_language_server__tests__rename__rename_local_variable_from_definition_nested_pattern.snap
│ │ ├── gleam_language_server__tests__rename__rename_local_variable_from_guard.snap
│ │ ├── gleam_language_server__tests__rename__rename_local_variable_from_label_shorthand.snap
│ │ ├── gleam_language_server__tests__rename__rename_local_variable_guard_clause.snap
│ │ ├── gleam_language_server__tests__rename__rename_local_variable_in_bit_array_pattern.snap
│ │ ├── gleam_language_server__tests__rename__rename_local_variable_label_shorthand.snap
│ │ ├── gleam_language_server__tests__rename__rename_local_variable_label_shorthand_from_definition.snap
│ │ ├── gleam_language_server__tests__rename__rename_local_variable_record_access.snap
│ │ ├── gleam_language_server__tests__rename__rename_local_variable_with_label_shorthand.snap
│ │ ├── gleam_language_server__tests__rename__rename_module_access_in_clause_guard.snap
│ │ ├── gleam_language_server__tests__rename__rename_module_from_alias_use.snap
│ │ ├── gleam_language_server__tests__rename__rename_module_from_constant_in_clause_guard.snap
│ │ ├── gleam_language_server__tests__rename__rename_module_from_constant_in_const.snap
│ │ ├── gleam_language_server__tests__rename__rename_module_from_constant_in_expression.snap
│ │ ├── gleam_language_server__tests__rename__rename_module_from_function_call.snap
│ │ ├── gleam_language_server__tests__rename__rename_module_from_import.snap
│ │ ├── gleam_language_server__tests__rename__rename_module_from_import_namespaced.snap
│ │ ├── gleam_language_server__tests__rename__rename_module_from_import_namespaced_with_alias.snap
│ │ ├── gleam_language_server__tests__rename__rename_module_from_import_namespaced_with_unqualified_value_and_alias.snap
│ │ ├── gleam_language_server__tests__rename__rename_module_from_import_namespaced_with_unqualified_values.snap
│ │ ├── gleam_language_server__tests__rename__rename_module_from_import_with_alias.snap
│ │ ├── gleam_language_server__tests__rename__rename_module_from_import_with_alias_to_original_name.snap
│ │ ├── gleam_language_server__tests__rename__rename_module_from_import_with_unqualified_value_and_alias.snap
│ │ ├── gleam_language_server__tests__rename__rename_module_from_type_in_annotation.snap
│ │ ├── gleam_language_server__tests__rename__rename_module_from_type_in_custom_type.snap
│ │ ├── gleam_language_server__tests__rename__rename_module_from_type_in_type_alias.snap
│ │ ├── gleam_language_server__tests__rename__rename_module_from_variant_in_clause_guard.snap
│ │ ├── gleam_language_server__tests__rename__rename_module_from_variant_in_const.snap
│ │ ├── gleam_language_server__tests__rename__rename_module_from_variant_in_expression.snap
│ │ ├── gleam_language_server__tests__rename__rename_module_from_variant_in_pattern.snap
│ │ ├── gleam_language_server__tests__rename__rename_module_select_from_guard.snap
│ │ ├── gleam_language_server__tests__rename__rename_nested_aliased_pattern.snap
│ │ ├── gleam_language_server__tests__rename__rename_prefix_string_alias_and_suffix_complex_guard.snap
│ │ ├── gleam_language_server__tests__rename__rename_prefix_string_alias_in_case.snap
│ │ ├── gleam_language_server__tests__rename__rename_prefix_string_alias_in_case_triggered_from_usage.snap
│ │ ├── gleam_language_server__tests__rename__rename_prefix_string_alias_in_let_assert.snap
│ │ ├── gleam_language_server__tests__rename__rename_prefix_string_alias_in_let_assert_triggered_from_usage.snap
│ │ ├── gleam_language_server__tests__rename__rename_prefix_string_alias_used_in_guard.snap
│ │ ├── gleam_language_server__tests__rename__rename_prefix_string_alias_with_alternative_definitions_in_case.snap
│ │ ├── gleam_language_server__tests__rename__rename_prefix_string_alias_with_alternative_definitions_triggered_from_second_pattern.snap
│ │ ├── gleam_language_server__tests__rename__rename_prefix_string_suffix_shadowing_outer_variable.snap
│ │ ├── gleam_language_server__tests__rename__rename_prefix_string_suffix_used_in_guard.snap
│ │ ├── gleam_language_server__tests__rename__rename_prefix_string_suffix_variable_in_case.snap
│ │ ├── gleam_language_server__tests__rename__rename_prefix_string_suffix_variable_in_case_triggered_from_usage.snap
│ │ ├── gleam_language_server__tests__rename__rename_prefix_string_suffix_variable_in_let_assert.snap
│ │ ├── gleam_language_server__tests__rename__rename_prefix_string_suffix_variable_in_let_assert_triggered_from_usage.snap
│ │ ├── gleam_language_server__tests__rename__rename_prefix_string_suffix_variable_nested_in_tuple.snap
│ │ ├── gleam_language_server__tests__rename__rename_prefix_string_suffix_variable_with_alternative_definition_in_case.snap
│ │ ├── gleam_language_server__tests__rename__rename_prefix_string_suffix_variable_with_alternative_definition_triggered_from_second_pattern.snap
│ │ ├── gleam_language_server__tests__rename__rename_prelude_type.snap
│ │ ├── gleam_language_server__tests__rename__rename_prelude_type_with_prelude_value_imported_with_trailing_comma.snap
│ │ ├── gleam_language_server__tests__rename__rename_prelude_value.snap
│ │ ├── gleam_language_server__tests__rename__rename_prelude_value_with_other_module_imported.snap
│ │ ├── gleam_language_server__tests__rename__rename_prelude_value_with_other_prelude_value_imported.snap
│ │ ├── gleam_language_server__tests__rename__rename_prelude_value_with_prelude_already_imported.snap
│ │ ├── gleam_language_server__tests__rename__rename_prelude_value_with_prelude_import_with_empty_braces.snap
│ │ ├── gleam_language_server__tests__rename__rename_shadowed_local_variable.snap
│ │ ├── gleam_language_server__tests__rename__rename_shadowing_local_variable.snap
│ │ ├── gleam_language_server__tests__rename__rename_type_from_definition.snap
│ │ ├── gleam_language_server__tests__rename__rename_type_from_qualified_reference.snap
│ │ ├── gleam_language_server__tests__rename__rename_type_from_reference.snap
│ │ ├── gleam_language_server__tests__rename__rename_type_from_unqualified_reference.snap
│ │ ├── gleam_language_server__tests__rename__rename_type_from_variant_constructor_argument.snap
│ │ ├── gleam_language_server__tests__rename__rename_type_referenced_in_variant_constructor_argument.snap
│ │ ├── gleam_language_server__tests__rename__rename_type_variant_from_definition.snap
│ │ ├── gleam_language_server__tests__rename__rename_type_variant_from_pattern.snap
│ │ ├── gleam_language_server__tests__rename__rename_type_variant_from_qualified_reference.snap
│ │ ├── gleam_language_server__tests__rename__rename_type_variant_from_reference.snap
│ │ ├── gleam_language_server__tests__rename__rename_type_variant_from_unqualified_reference.snap
│ │ ├── gleam_language_server__tests__rename__rename_type_variant_pattern_with_arguments.snap
│ │ ├── gleam_language_server__tests__rename__rename_value_in_aliased_module.snap
│ │ ├── gleam_language_server__tests__rename__rename_value_in_nested_module.snap
│ │ ├── gleam_language_server__tests__rename__rename_variable_used_in_record_update.snap
│ │ ├── gleam_language_server__tests__rename__rename_variable_with_alternative_pattern_with_same_name.snap
│ │ ├── gleam_language_server__tests__rename__rename_works_when_error_is_present.snap
│ │ ├── gleam_language_server__tests__signature_help__help_for_aliased_qualified_call.snap
│ │ ├── gleam_language_server__tests__signature_help__help_for_aliased_unqualified_call.snap
│ │ ├── gleam_language_server__tests__signature_help__help_for_calling_local_variable_first_arg.snap
│ │ ├── gleam_language_server__tests__signature_help__help_for_calling_local_variable_last_arg.snap
│ │ ├── gleam_language_server__tests__signature_help__help_for_calling_local_variable_referencing_constant_referencing_function.snap
│ │ ├── gleam_language_server__tests__signature_help__help_for_calling_local_variable_with_module_function.snap
│ │ ├── gleam_language_server__tests__signature_help__help_for_calling_module_constant_referencing_function.snap
│ │ ├── gleam_language_server__tests__signature_help__help_for_calling_module_function.snap
│ │ ├── gleam_language_server__tests__signature_help__help_for_piped_function_starts_from_second_argument.snap
│ │ ├── gleam_language_server__tests__signature_help__help_for_piped_imported_function_starts_from_second_argument.snap
│ │ ├── gleam_language_server__tests__signature_help__help_for_qualified_call.snap
│ │ ├── gleam_language_server__tests__signature_help__help_for_unqualified_call.snap
│ │ ├── gleam_language_server__tests__signature_help__help_for_use_function_call_starts_from_first_argument.snap
│ │ ├── gleam_language_server__tests__signature_help__help_for_use_function_call_uses_concrete_types_when_late_ubound.snap
│ │ ├── gleam_language_server__tests__signature_help__help_for_use_function_call_uses_concrete_types_when_possible_or_generic_names_when_unbound.snap
│ │ ├── gleam_language_server__tests__signature_help__help_for_use_function_call_uses_generic_names_when_missing_all_arguments.snap
│ │ ├── gleam_language_server__tests__signature_help__help_for_use_function_call_uses_precise_types_when_missing_some_arguments.snap
│ │ ├── gleam_language_server__tests__signature_help__help_for_use_function_shows_next_unlabelled_argument.snap
│ │ ├── gleam_language_server__tests__signature_help__help_shows_documentation_for_imported_function.snap
│ │ ├── gleam_language_server__tests__signature_help__help_shows_documentation_for_local_function.snap
│ │ ├── gleam_language_server__tests__signature_help__help_shows_first_missing_labelled_argument_if_out_of_order.snap
│ │ ├── gleam_language_server__tests__signature_help__help_shows_labelled_argument_after_all_unlabelled.snap
│ │ ├── gleam_language_server__tests__signature_help__help_shows_labels.snap
│ │ ├── gleam_language_server__tests__signature_help__help_still_shows_up_even_if_an_argument_has_the_wrong_type.snap
│ │ └── gleam_language_server__tests__signature_help__help_with_labelled_constructor.snap
│ └── tests.rs
├── test/
│ ├── assert/
│ │ ├── .gitignore
│ │ ├── Makefile
│ │ ├── gleam.toml
│ │ ├── manifest.toml
│ │ ├── run_tests.sh
│ │ └── src/
│ │ ├── failing1.gleam
│ │ ├── failing2.gleam
│ │ ├── failing3.gleam
│ │ ├── failing4.gleam
│ │ └── passing.gleam
│ ├── compile_package0/
│ │ ├── .gitignore
│ │ ├── Makefile
│ │ ├── gleam.toml
│ │ ├── manifest.toml
│ │ ├── src/
│ │ │ ├── one.gleam
│ │ │ └── two.gleam
│ │ └── test/
│ │ └── three.gleam
│ ├── compile_package1/
│ │ ├── .gitignore
│ │ ├── Makefile
│ │ ├── app1/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ ├── one/
│ │ │ │ └── nested.gleam
│ │ │ └── one.gleam
│ │ └── app2/
│ │ ├── gleam.toml
│ │ └── src/
│ │ └── two.gleam
│ ├── erlang_shipment_no_dev_deps/
│ │ ├── .gitignore
│ │ ├── gleam.toml
│ │ ├── manifest.toml
│ │ ├── src/
│ │ │ └── shipment_test.gleam
│ │ └── test.sh
│ ├── errors/
│ │ └── type_unify_int_string/
│ │ ├── .gitignore
│ │ ├── gleam.toml
│ │ └── src/
│ │ └── type_unify_int_string.gleam
│ ├── external_only_erlang/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── gleam.toml
│ │ ├── manifest.toml
│ │ ├── src/
│ │ │ ├── external_only_erlang.gleam
│ │ │ └── external_only_erlang_ffi.erl
│ │ └── test.sh
│ ├── external_only_javascript/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── gleam.toml
│ │ ├── manifest.toml
│ │ ├── src/
│ │ │ ├── external_only_javascript.gleam
│ │ │ └── external_only_javascript_ffi.mjs
│ │ └── test.sh
│ ├── hello_world/
│ │ ├── .gitignore
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── gleam.toml
│ │ ├── manifest.toml
│ │ ├── rebar.config
│ │ ├── src/
│ │ │ ├── hello_world.app.src
│ │ │ ├── hello_world.gleam
│ │ │ ├── nest/
│ │ │ │ ├── bird!.gleam
│ │ │ │ ├── bird.gleam
│ │ │ │ └── bird.js
│ │ │ └── other.gleam
│ │ └── test/
│ │ └── hello_test.gleam
│ ├── hextarball/
│ │ ├── .gitignore
│ │ ├── Makefile
│ │ ├── gleam.toml
│ │ ├── manifest.toml
│ │ └── src/
│ │ └── hextarball.gleam
│ ├── javascript_prelude/
│ │ ├── .gitignore
│ │ ├── Makefile
│ │ └── main.mjs
│ ├── language/
│ │ ├── .gitignore
│ │ ├── Makefile
│ │ ├── gleam.toml
│ │ ├── manifest.toml
│ │ ├── src/
│ │ │ └── language.gleam
│ │ └── test/
│ │ ├── ffi.gleam
│ │ ├── ffi_erlang.erl
│ │ ├── ffi_javascript.mjs
│ │ ├── ffi_typescript.ts
│ │ ├── importable.gleam
│ │ ├── language/
│ │ │ ├── alternative_pattern.gleam
│ │ │ ├── anonymous_function_test.gleam
│ │ │ ├── assertion_test.gleam
│ │ │ ├── bit_array_dynamic_size_test.gleam
│ │ │ ├── bit_array_match_test.gleam
│ │ │ ├── bit_array_test.gleam
│ │ │ ├── block_test.gleam
│ │ │ ├── bool_negation_test.gleam
│ │ │ ├── build_files_test.gleam
│ │ │ ├── clause_guard_test.gleam
│ │ │ ├── constant_test.gleam
│ │ │ ├── directly_matching_case_subject_test.gleam
│ │ │ ├── equality_test.gleam
│ │ │ ├── float_test.gleam
│ │ │ ├── imported_custom_type_test.gleam
│ │ │ ├── importing_test.gleam
│ │ │ ├── int_negation_test.gleam
│ │ │ ├── int_test.gleam
│ │ │ ├── list_prepend_test.gleam
│ │ │ ├── mixed_arg_match_test.gleam
│ │ │ ├── multiple_case_subject_test.gleam
│ │ │ ├── non_utf8_string_bit_array_test.gleam
│ │ │ ├── pipe_test.gleam
│ │ │ ├── precedence_test.gleam
│ │ │ ├── prelude_test.gleam
│ │ │ ├── record_access_test.gleam
│ │ │ ├── record_update_test.gleam
│ │ │ ├── remainder_test.gleam
│ │ │ ├── sized_bit_array_test.gleam
│ │ │ ├── string_pattern_matching_test.gleam
│ │ │ ├── string_test.gleam
│ │ │ ├── tail_call_test.gleam
│ │ │ ├── tuple_access_test.gleam
│ │ │ ├── unaligned_bit_array_expression_test.gleam
│ │ │ └── unaligned_bit_array_pattern_test.gleam
│ │ ├── language_test.gleam
│ │ ├── mod_with_numbers_0123456789.gleam
│ │ ├── port.gleam
│ │ ├── record_update.gleam
│ │ └── shadowed_module.gleam
│ ├── multi_namespace/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── gleam.toml
│ │ ├── manifest.toml
│ │ ├── src/
│ │ │ ├── multi_namespace.gleam
│ │ │ └── second.gleam
│ │ └── test.sh
│ ├── multi_namespace_not_top_level/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── gleam.toml
│ │ ├── src/
│ │ │ ├── module1/
│ │ │ │ └── sub.gleam
│ │ │ └── module2/
│ │ │ └── sub.gleam
│ │ └── test.sh
│ ├── package.jsonc
│ ├── project_deno/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── gleam.toml
│ │ ├── manifest.toml
│ │ ├── src/
│ │ │ ├── project.gleam
│ │ │ └── project_ffi.mjs
│ │ └── test/
│ │ └── project_test.gleam
│ ├── project_erlang/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── gleam.toml
│ │ ├── manifest.toml
│ │ ├── priv/
│ │ │ └── hello.txt
│ │ ├── src/
│ │ │ ├── elixir_file.ex
│ │ │ ├── erlang_file.erl
│ │ │ ├── erlang_header.hrl
│ │ │ └── project.gleam
│ │ └── test/
│ │ ├── elixir_test_file.ex
│ │ ├── erlang_test_file.erl
│ │ ├── erlang_test_header.hrl
│ │ └── project_test.gleam
│ ├── project_erlang_windows/
│ │ ├── .gitignore
│ │ ├── gleam.toml
│ │ ├── manifest.toml
│ │ ├── priv/
│ │ │ └── hello.txt
│ │ ├── src/
│ │ │ ├── elixir_file.ex
│ │ │ ├── erlang_file.erl
│ │ │ ├── erlang_header.hrl
│ │ │ └── project.gleam
│ │ └── test/
│ │ ├── elixir_test_file.ex
│ │ ├── erlang_test_file.erl
│ │ ├── erlang_test_header.hrl
│ │ └── project_test.gleam
│ ├── project_git_deps/
│ │ ├── .gitignore
│ │ ├── gleam.toml
│ │ ├── manifest.toml
│ │ └── src/
│ │ └── git_deps.gleam
│ ├── project_javascript/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── gleam.toml
│ │ ├── manifest.toml
│ │ ├── src/
│ │ │ ├── project.gleam
│ │ │ └── project_ffi.mjs
│ │ └── test/
│ │ └── project_test.gleam
│ ├── project_path_deps/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── project_a/
│ │ │ ├── gleam.toml
│ │ │ ├── manifest.toml
│ │ │ ├── src/
│ │ │ │ └── project_a.gleam
│ │ │ └── test/
│ │ │ └── project_a_test.gleam
│ │ ├── project_b/
│ │ │ ├── gleam.toml
│ │ │ ├── manifest.toml
│ │ │ └── src/
│ │ │ └── project_b.gleam
│ │ ├── project_c/
│ │ │ ├── gleam.toml
│ │ │ ├── manifest.toml
│ │ │ └── src/
│ │ │ └── project_c.gleam
│ │ └── project_d/
│ │ ├── gleam.toml
│ │ ├── manifest.toml
│ │ ├── src/
│ │ │ └── project_d.gleam
│ │ └── test/
│ │ └── project_d_test.gleam
│ ├── publishing_default_main/
│ │ ├── .gitignore
│ │ ├── gleam.toml
│ │ ├── manifest.toml
│ │ ├── src/
│ │ │ ├── default_main/
│ │ │ │ ├── one.gleam
│ │ │ │ └── two.gleam
│ │ │ └── default_main.gleam
│ │ └── test.sh
│ ├── publishing_default_readme/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── gleam.toml
│ │ ├── manifest.toml
│ │ ├── src/
│ │ │ └── default_readme.gleam
│ │ └── test.sh
│ ├── publishing_empty_readme/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── gleam.toml
│ │ ├── manifest.toml
│ │ ├── src/
│ │ │ └── empty_readme.gleam
│ │ └── test.sh
│ ├── publishing_no_readme/
│ │ ├── .gitignore
│ │ ├── gleam.toml
│ │ ├── manifest.toml
│ │ ├── src/
│ │ │ └── no_readme.gleam
│ │ └── test.sh
│ ├── root_package_not_compiled_when_running_dep/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── gleam.toml
│ │ ├── manifest.toml
│ │ ├── src/
│ │ │ └── root_package_not_compiled_when_running_dep.gleam
│ │ └── test.sh
│ ├── running_modules/
│ │ ├── .gitignore
│ │ ├── Makefile
│ │ ├── README.md
│ │ ├── dev/
│ │ │ ├── module_dev.gleam
│ │ │ ├── module_in_dev.gleam
│ │ │ ├── nested/
│ │ │ │ └── module_in_dev.gleam
│ │ │ └── wrong_dev_arity.gleam
│ │ ├── gleam.toml
│ │ ├── manifest.toml
│ │ ├── run_tests.sh
│ │ ├── src/
│ │ │ ├── module/
│ │ │ │ ├── no_main_function.gleam
│ │ │ │ ├── sub_module.gleam
│ │ │ │ └── wrong_arity.gleam
│ │ │ └── module.gleam
│ │ └── test/
│ │ ├── module_in_test.gleam
│ │ ├── module_test.gleam
│ │ ├── nested/
│ │ │ └── module_in_test.gleam
│ │ └── wrong_test_arity.gleam
│ ├── subdir_ffi/
│ │ ├── .gitignore
│ │ ├── Makefile
│ │ ├── README.md
│ │ ├── gleam.toml
│ │ ├── manifest.toml
│ │ ├── src/
│ │ │ ├── headers/
│ │ │ │ └── submodule_ffi_header.hrl
│ │ │ ├── nested/
│ │ │ │ ├── submodule.gleam
│ │ │ │ ├── submodule_ffi.erl
│ │ │ │ ├── submodule_ffi.ex
│ │ │ │ └── submodule_ffi.mjs
│ │ │ ├── project.gleam
│ │ │ ├── project_ffi.erl
│ │ │ └── project_ffi.mjs
│ │ └── test/
│ │ └── subdir_ffi_test.gleam
│ └── unicode_path ⭐/
│ ├── .gitignore
│ ├── Makefile
│ ├── gleam.toml
│ ├── manifest.toml
│ └── src/
│ └── unicode_path.gleam
├── test-community-packages/
│ ├── .gitignore
│ ├── README.md
│ ├── gleam.toml
│ ├── manifest.toml
│ ├── src/
│ │ └── test_community_packages.gleam
│ └── test/
│ └── test_community_packages_test.gleam
├── test-helpers-rs/
│ ├── Cargo.toml
│ └── src/
│ └── lib.rs
├── test-output/
│ ├── Cargo.toml
│ ├── cases/
│ │ ├── .gitignore
│ │ ├── echo_bitarray/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ └── main.gleam
│ │ ├── echo_bool/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ └── main.gleam
│ │ ├── echo_charlist/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ └── main.gleam
│ │ ├── echo_circular_reference/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ ├── main.gleam
│ │ │ └── main_ffi.mjs
│ │ ├── echo_custom_type/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ └── main.gleam
│ │ ├── echo_dict/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ └── main.gleam
│ │ ├── echo_float/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ └── main.gleam
│ │ ├── echo_function/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ └── main.gleam
│ │ ├── echo_importing_module_named_inspect/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ ├── inspect.gleam
│ │ │ └── main.gleam
│ │ ├── echo_int/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ └── main.gleam
│ │ ├── echo_list/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ └── main.gleam
│ │ ├── echo_nil/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ └── main.gleam
│ │ ├── echo_non_record_atom_tag/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ └── main.gleam
│ │ ├── echo_singleton/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ ├── main.gleam
│ │ │ ├── main_ffi.mjs
│ │ │ └── thing.gleam
│ │ ├── echo_string/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ └── main.gleam
│ │ ├── echo_tuple/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ └── main.gleam
│ │ ├── echo_with_message/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ └── main.gleam
│ │ └── linked_process_exit/
│ │ ├── gleam.toml
│ │ └── src/
│ │ ├── main.gleam
│ │ └── main_ffi.erl
│ └── src/
│ ├── lib.rs
│ ├── tests/
│ │ ├── echo.rs
│ │ └── snapshots/
│ │ ├── test_output__tests__echo__echo_bool.snap
│ │ ├── test_output__tests__echo__echo_charlist.snap
│ │ ├── test_output__tests__echo__echo_dict.snap
│ │ ├── test_output__tests__echo__echo_function.snap
│ │ ├── test_output__tests__echo__echo_importing_module_named_inspect.snap
│ │ ├── test_output__tests__echo__echo_int.snap
│ │ ├── test_output__tests__echo__echo_list.snap
│ │ ├── test_output__tests__echo__echo_nil.snap
│ │ ├── test_output__tests__echo__echo_singleton.snap
│ │ ├── test_output__tests__echo__echo_string.snap
│ │ ├── test_output__tests__echo__echo_tuple.snap
│ │ ├── test_output__tests__echo__echo_with_message.snap
│ │ ├── test_output__tests__echo__erlang-echo_bitarray.snap
│ │ ├── test_output__tests__echo__erlang-echo_custom_type.snap
│ │ ├── test_output__tests__echo__erlang-echo_float.snap
│ │ ├── test_output__tests__echo__erlang-echo_non_record_atom_tag.snap
│ │ ├── test_output__tests__echo__erlang-linked_process_exit.snap
│ │ ├── test_output__tests__echo__javascript-echo_bitarray.snap
│ │ ├── test_output__tests__echo__javascript-echo_circular_reference.snap
│ │ ├── test_output__tests__echo__javascript-echo_custom_type.snap
│ │ └── test_output__tests__echo__javascript-echo_float.snap
│ └── tests.rs
├── test-package-compiler/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── build.rs
│ ├── cases/
│ │ ├── alias_unqualified_import/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ ├── one.gleam
│ │ │ └── two.gleam
│ │ ├── dev_importing_test/
│ │ │ ├── dev/
│ │ │ │ └── one.gleam
│ │ │ ├── gleam.toml
│ │ │ └── test/
│ │ │ └── two.gleam
│ │ ├── duplicate_module/
│ │ │ ├── gleam.toml
│ │ │ ├── src/
│ │ │ │ └── main.gleam
│ │ │ └── test/
│ │ │ └── main.gleam
│ │ ├── duplicate_module_dev/
│ │ │ ├── dev/
│ │ │ │ └── main.gleam
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ └── main.gleam
│ │ ├── duplicate_module_test_dev/
│ │ │ ├── dev/
│ │ │ │ └── main.gleam
│ │ │ ├── gleam.toml
│ │ │ └── test/
│ │ │ └── main.gleam
│ │ ├── empty_module_warning/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ ├── empty.gleam
│ │ │ ├── internal.gleam
│ │ │ ├── private.gleam
│ │ │ └── public.gleam
│ │ ├── erlang_app_generation/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ └── main.gleam
│ │ ├── erlang_app_generation_with_argument/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ └── main.gleam
│ │ ├── erlang_bug_752/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ ├── one.gleam
│ │ │ └── two.gleam
│ │ ├── erlang_empty/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ └── empty.gleam
│ │ ├── erlang_escape_names/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ ├── one.gleam
│ │ │ └── two.gleam
│ │ ├── erlang_import/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ ├── one.gleam
│ │ │ └── two.gleam
│ │ ├── erlang_import_shadowing_prelude/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ ├── one.gleam
│ │ │ └── two.gleam
│ │ ├── erlang_nested/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ └── one/
│ │ │ └── two.gleam
│ │ ├── erlang_nested_qualified_constant/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ ├── one/
│ │ │ │ └── two.gleam
│ │ │ └── two.gleam
│ │ ├── hello_joe/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ └── hello_joe.gleam
│ │ ├── import_cycle/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ └── one.gleam
│ │ ├── import_cycle_multi/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ ├── one.gleam
│ │ │ ├── three.gleam
│ │ │ └── two.gleam
│ │ ├── import_shadowed_name_warning/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ ├── one.gleam
│ │ │ └── two.gleam
│ │ ├── imported_constants/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ ├── one.gleam
│ │ │ └── two.gleam
│ │ ├── imported_external_fns/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ ├── one.gleam
│ │ │ ├── three.gleam
│ │ │ └── two.gleam
│ │ ├── imported_record_constructors/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ ├── one/
│ │ │ │ ├── one.gleam
│ │ │ │ └── two.gleam
│ │ │ └── two.gleam
│ │ ├── javascript_d_ts/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ └── hello.gleam
│ │ ├── javascript_empty/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ └── empty.gleam
│ │ ├── javascript_import/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ ├── one/
│ │ │ │ └── two.gleam
│ │ │ └── two.gleam
│ │ ├── not_overwriting_erlang_module/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ └── app/
│ │ │ └── code.gleam
│ │ ├── opaque_type_accessor/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ ├── one.gleam
│ │ │ └── two.gleam
│ │ ├── opaque_type_destructure/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ ├── one.gleam
│ │ │ └── two.gleam
│ │ ├── overwriting_erlang_module/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ └── code.gleam
│ │ ├── src_importing_dev/
│ │ │ ├── dev/
│ │ │ │ └── two.gleam
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ └── one.gleam
│ │ ├── src_importing_test/
│ │ │ ├── gleam.toml
│ │ │ ├── src/
│ │ │ │ └── one.gleam
│ │ │ └── test/
│ │ │ └── two.gleam
│ │ ├── unknown_module_field_in_constant/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ ├── one.gleam
│ │ │ └── two.gleam
│ │ ├── unknown_module_field_in_expression/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ ├── one.gleam
│ │ │ └── two.gleam
│ │ ├── unknown_module_field_in_import/
│ │ │ ├── gleam.toml
│ │ │ └── src/
│ │ │ ├── one.gleam
│ │ │ └── two.gleam
│ │ └── variable_or_module/
│ │ ├── gleam.toml
│ │ └── src/
│ │ ├── main.gleam
│ │ └── power.gleam
│ └── src/
│ ├── generated_tests.rs
│ ├── lib.rs
│ └── snapshots/
│ ├── test_package_compiler__generated_tests__alias_unqualified_import.snap
│ ├── test_package_compiler__generated_tests__dev_importing_test.snap
│ ├── test_package_compiler__generated_tests__duplicate_module.snap
│ ├── test_package_compiler__generated_tests__duplicate_module_dev.snap
│ ├── test_package_compiler__generated_tests__duplicate_module_test_dev.snap
│ ├── test_package_compiler__generated_tests__empty_module_warning.snap
│ ├── test_package_compiler__generated_tests__erlang_app_generation.snap
│ ├── test_package_compiler__generated_tests__erlang_app_generation_with_argument.snap
│ ├── test_package_compiler__generated_tests__erlang_bug_752.snap
│ ├── test_package_compiler__generated_tests__erlang_empty.snap
│ ├── test_package_compiler__generated_tests__erlang_escape_names.snap
│ ├── test_package_compiler__generated_tests__erlang_import.snap
│ ├── test_package_compiler__generated_tests__erlang_import_shadowing_prelude.snap
│ ├── test_package_compiler__generated_tests__erlang_nested.snap
│ ├── test_package_compiler__generated_tests__erlang_nested_qualified_constant.snap
│ ├── test_package_compiler__generated_tests__hello_joe.snap
│ ├── test_package_compiler__generated_tests__import_cycle.snap
│ ├── test_package_compiler__generated_tests__import_cycle_multi.snap
│ ├── test_package_compiler__generated_tests__import_shadowed_name_warning.snap
│ ├── test_package_compiler__generated_tests__imported_constants.snap
│ ├── test_package_compiler__generated_tests__imported_external_fns.snap
│ ├── test_package_compiler__generated_tests__imported_record_constructors.snap
│ ├── test_package_compiler__generated_tests__javascript_d_ts.snap
│ ├── test_package_compiler__generated_tests__javascript_empty.snap
│ ├── test_package_compiler__generated_tests__javascript_import.snap
│ ├── test_package_compiler__generated_tests__not_overwriting_erlang_module.snap
│ ├── test_package_compiler__generated_tests__opaque_type_accessor.snap
│ ├── test_package_compiler__generated_tests__opaque_type_destructure.snap
│ ├── test_package_compiler__generated_tests__overwriting_erlang_module.snap
│ ├── test_package_compiler__generated_tests__src_importing_dev.snap
│ ├── test_package_compiler__generated_tests__src_importing_test.snap
│ ├── test_package_compiler__generated_tests__unknown_module_field_in_constant.snap
│ ├── test_package_compiler__generated_tests__unknown_module_field_in_expression.snap
│ ├── test_package_compiler__generated_tests__unknown_module_field_in_import.snap
│ └── test_package_compiler__generated_tests__variable_or_module.snap
└── test-project-compiler/
├── .gitignore
├── Cargo.toml
├── build.rs
├── cases/
│ ├── with_dep/
│ │ ├── gleam.toml
│ │ └── src/
│ │ └── example.gleam
│ └── with_dev_dep/
│ ├── gleam.toml
│ └── src/
│ └── example.gleam
├── src/
│ ├── generated_tests.rs
│ ├── lib.rs
│ └── snapshots/
│ ├── test_project_compiler__generated_tests__with_dep_dev.snap
│ ├── test_project_compiler__generated_tests__with_dep_lsp.snap
│ ├── test_project_compiler__generated_tests__with_dep_prod.snap
│ ├── test_project_compiler__generated_tests__with_dev_dep_dev.snap
│ ├── test_project_compiler__generated_tests__with_dev_dep_lsp.snap
│ └── test_project_compiler__generated_tests__with_dev_dep_prod.snap
└── support/
└── package_a/
├── gleam.toml
└── src/
└── package_a.gleam
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
# top-most EditorConfig file
root = true
# Matches multiple files with brace expansion notation
# Set default charset
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2
[*.{gleam, erl, hrl, ex, mjs, js, ts, sh}]
max_line_length = 80
[*.{rs, .erl, .hrl}]
indent_size = 4
[Makefile]
indent_style = tab
indent_size = 4
================================================
FILE: .gitattributes
================================================
[attr]generated linguist-generated=true diff=generated
# Tests:
test-package-compiler/src/generated_tests.rs generated
# Lock files:
Cargo.lock generated
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Something isn't working
title: ''
labels: bug
assignees: ''
---
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
================================================
FILE: .github/actions/build-container/action.yml
================================================
name: "Build Gleam"
description: "Build Gleam Container"
inputs:
version:
description: "Build Version"
required: true
release-id:
description: "Release ID"
required: true
runs:
using: composite
steps:
- name: Download Gleam release assets
uses: robinraju/release-downloader@v1
with:
releaseId: "${{ inputs.release-id }}"
fileName: "gleam-${{ inputs.version }}-{x86_64-unknown-linux-musl,aarch64-unknown-linux-musl}.*"
- name: "Unpack release files into correct location"
shell: bash
run: |
declare -A ARCH
ARCH["amd64"]="x86_64-unknown-linux-musl"
ARCH["arm64"]="aarch64-unknown-linux-musl"
for SHORT in "${!ARCH[@]}"; do
LONG="${ARCH[$SHORT]}"
# Unpack Release
tar xf "gleam-$VERSION-$LONG.tar.gz"
# Move files into place
mv gleam "gleam-$SHORT"
# The SBoM is added to the images so that the Docker Scout Scanner is
# able to find the info about the gleam binary since it was not
# installed by the operating system package manager.
mv "gleam-$VERSION-$LONG.tar.gz.sbom.spdx.json" "gleam-$SHORT.sbom.spdx.json"
# Delete Unused Files
rm -rf "gleam-$VERSION-$LONG*"
done
env:
VERSION: "${{ inputs.version }}"
- name: Authenticate with GitHub container registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build version and tags
shell: bash
id: versions
run: |
# Strip `v` prefix from version
BARE_VERSION=$(echo "$VERSION" | sed -e 's|^v/\(.*\)|\1|')
# Build version with platform
PLATFORM_VERSION=$BARE_VERSION-${{ matrix.base-image }}
# Build container tag
TAG=ghcr.io/${{ github.repository }}:$PLATFORM_VERSION
echo "platform-version=$PLATFORM_VERSION" >> $GITHUB_OUTPUT
echo "container-tag=$TAG" >> $GITHUB_OUTPUT
env:
VERSION: "${{ inputs.version }}"
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
file: containers/${{ matrix.base-image }}.dockerfile
push: true
# Enabling `provenance` will cause the action to create SLSA build
# provenance and push it alongside the tagged image. In practical terms,
# we're adding info to the tag that attests to where, when, and how the
# asset and image was built.
#
# For more info on Docker Attestations, see:
# https://docs.docker.com/build/ci/github-actions/attestations/
provenance: true
# Enabling `sbom` will trigger an SBoM Scan using Docker Scout:
# https://docs.docker.com/scout/how-tos/view-create-sboms/
# The scan will detect any operating system packages as well as the Gleam
# Build SBoM added into the Docker Container.
#
# Why is this helpful?
# * If you build services on top of these container images, you can track
# all dependencies that ship with Gleam, plus the rest of your stack in
# the image.
# * This makes it easier to do image-level vulnerability scans and
# compliance checks.
#
# For more info on Docker SBoMs, see:
# https://docs.docker.com/build/metadata/attestations/sbom/
sbom: true
tags: ${{ steps.versions.outputs.container-tag }}
labels: |
org.opencontainers.image.title=gleam
org.opencontainers.image.url=https://gleam.run
org.opencontainers.image.source=https://github.com/gleam-lang/gleam
org.opencontainers.image.version=${{ steps.versions.outputs.platform-version }}
org.opencontainers.image.licenses=Apache-2.0
================================================
FILE: .github/actions/build-release/action.yml
================================================
name: "Build Gleam"
description: "Build Gleam Release"
inputs:
version:
description: "Build Version"
required: true
toolchain:
description: "Cargo Toolchain"
required: true
target:
description: "Cargo Installation Target"
required: true
cargo-tool:
description: "Cargo Tool used for Build (for example, `cross`)"
required: true
expected-binary-architecture:
description: "Expected Binary Architecture"
required: false
default: ""
azure-tenant-id:
description: "Azure Tenant ID for Windows Code Signing"
required: false
azure-subscription-id:
description: "Azure Subscription ID for Windows Code Signing"
required: false
azure-client-id:
description: "Azure Client ID for Windows Code Signing"
required: false
azure-trusted-signing-account-name:
description: "Azure Trusted Signing Account Name for Windows Code Signing"
required: false
azure-certificate-profile-name:
description: "Azure Certificate Profile Name for Windows Code Signing"
required: false
outputs:
archive:
description: "Path to build asset"
value: "${{ steps.build.outputs.archive }}"
files:
description: "Path to all files"
value: |
${{ steps.build.outputs.archive }}
${{ steps.build.outputs.archive }}.sha256
${{ steps.build.outputs.archive }}.sha512
${{ steps.build.outputs.archive }}.sigstore
${{ steps.build.outputs.archive }}.sbom.spdx.json
${{ steps.build.outputs.archive }}.sbom.cyclonedx.json
runs:
using: "composite"
steps:
- name: Install Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: ${{ inputs.toolchain }}
target: ${{ inputs.target }}
cache-key: v1-${{ inputs.target }}
- name: Install Cargo SBoM
shell: bash
# The `cargo-sbom` version is specified in the next line. Change it to
# keep it up-to-date.
run: cargo install cargo-sbom@~0.9.1
- name: Build WASM release binary
if: ${{ inputs.target != 'wasm32-unknown-unknown' }}
uses: clechasseur/rs-cargo@v3
with:
command: build
args: --release --target ${{ inputs.target }}
tool: ${{ inputs.cargo-tool }}
- name: Install wasm-pack
if: ${{ inputs.target == 'wasm32-unknown-unknown' }}
shell: bash
run: curl -sSL https://rustwasm.github.io/wasm-pack/installer/init.sh | sh
- name: Build WASM release binary
if: ${{ inputs.target == 'wasm32-unknown-unknown' }}
shell: bash
run: wasm-pack build --release --target web compiler-wasm
- name: Verify binary architecture
if: ${{ inputs.expected-binary-architecture }}
shell: bash
run: |
BINARY_PATH="target/${{ inputs.target }}/release/gleam"
if [[ "${{ inputs.target }}" == *"windows"* ]]; then
BINARY_PATH="${BINARY_PATH}.exe"
fi
if ! file -b "$BINARY_PATH" | grep -iq "${{ inputs.expected-binary-architecture }}"; then
echo "error: Architecture mismatch"
echo "Expected architecture: '${{ inputs.expected-binary-architecture }}'"
echo "Found binary type: '$(file -b "$BINARY_PATH")'"
exit 1
fi
echo "ok: Architecture match"
# We use Azure Trusted Signing to sign the Windows binaries.
# This is done to ensure that the binaries are trusted and can be
# verified by users and systems that require signed code.
#
# Why is this helpful?
# * It provides a way to verify the authenticity and integrity of the
# binaries distributed by Gleam.
# * It helps prevent tampering with the binaries, ensuring that users
# can trust the code they are running.
#
# For more information, see:
# * https://github.com/Azure/trusted-signing-action
# * https://azure.microsoft.com/en-us/products/trusted-signing
- name: Log in to Azure
if: ${{ contains(inputs.target, '-windows-') && inputs.azure-tenant-id && inputs.azure-subscription-id && inputs.azure-client-id && inputs.azure-trusted-signing-account-name && inputs.azure-certificate-profile-name }}
uses: azure/login@a457da9ea143d694b1b9c7c869ebb04ebe844ef5 # v2.3.0
with:
client-id: ${{ inputs.azure-client-id }}
tenant-id: ${{ inputs.azure-tenant-id }}
subscription-id: ${{ inputs.azure-subscription-id }}
- name: Windows Code Signing
if: ${{ contains(inputs.target, '-windows-') && inputs.azure-tenant-id && inputs.azure-subscription-id && inputs.azure-client-id && inputs.azure-trusted-signing-account-name && inputs.azure-certificate-profile-name }}
uses: azure/trusted-signing-action@0d74250c661747df006298d0fb49944c10f16e03 # v0.5.1
with:
endpoint: https://eus.codesigning.azure.net/
trusted-signing-account-name: ${{ inputs.azure-trusted-signing-account-name }}
certificate-profile-name: ${{ inputs.azure-certificate-profile-name }}
files: ${{ github.workspace }}\target\${{ inputs.target }}\release\gleam.exe
file-digest: SHA256
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
- name: Build archive
id: build
shell: bash
run: |
case "$TARGET" in
*windows*)
ARCHIVE="gleam-$VERSION-$TARGET.zip"
cp "target/$TARGET/release/gleam.exe" "gleam.exe"
7z a "$ARCHIVE" "gleam.exe"
rm gleam.exe
;;
wasm*)
ARCHIVE="gleam-$VERSION-browser.tar.gz"
tar -C compiler-wasm/pkg/ -czvf $ARCHIVE .
rm -rf compiler-wasm/pkg/
;;
*)
ARCHIVE="gleam-$VERSION-$TARGET.tar.gz"
cp "target/$TARGET/release/gleam" "gleam"
tar -czvf "$ARCHIVE" "gleam"
rm gleam
;;
esac
echo "archive=$ARCHIVE" >> $GITHUB_OUTPUT
env:
TARGET: "${{ inputs.target }}"
VERSION: "${{ inputs.version }}"
- name: Ensure binary successfully boots
if: ${{ inputs.expected-binary-architecture }}
shell: bash
run: |
case "$TARGET" in
*windows*)
7z x "$ARCHIVE"
./gleam.exe --version
;;
aarch64*)
echo "We cannot test an ARM binary on a AMD64 runner"
;;
*)
tar -xvzf "$ARCHIVE"
./gleam --version
;;
esac
env:
TARGET: "${{ inputs.target }}"
ARCHIVE: "${{ steps.build.outputs.archive }}"
# By using `cargo-sbom``, we create two formats of Build SBoMs
# (SPDX and CycloneDX) for the gleam build.
# We store those files alongside the build artifacts on the GitHub Release
# page and also use them to create Container SBoMs for Docker images.
#
# Why is this helpful?
# * It gives us and our users complete visibility into which dependencies
# and which versions are present in the build / container image.
# * The SBoM can be fed into vulnerability scanners so that anyone can check
# if any dependencies have known security issues.
- name: Generate Build SBoM
shell: bash
run: |
cargo-sbom \
--output-format spdx_json_2_3 \
> "$ARCHIVE.sbom.spdx.json"
cargo-sbom \
--output-format cyclone_dx_json_1_4 \
> "$ARCHIVE.sbom.cyclonedx.json"
env:
ARCHIVE: "${{ steps.build.outputs.archive }}"
- name: Hash Build Archive
shell: bash
run: |
openssl dgst -r -sha256 -out "$ARCHIVE".sha256 "$ARCHIVE"
openssl dgst -r -sha512 -out "$ARCHIVE".sha512 "$ARCHIVE"
env:
ARCHIVE: "${{ steps.build.outputs.archive }}"
# We provide SLSA Provenance for the distribution build. This attests to
# where, when, and how the asset or image was built.
#
# Why is this helpful?
# * It provides a record of the exact Git commit (git sha) and GitHub
# Actions workflow used to produce a release.
# * Users or automated systems can verify that the artifact you’re
# downloading was indeed built from the official Gleam repo, on a
# particular date, using the correct pipeline and not tampered with later.
# * The attestation is published to a transparency log for extra
# verification: https://github.com/gleam-lang/gleam/attestations/
#
# For more information, see:
# * https://github.com/actions/attest
# * https://github.com/actions/attest-sbom
- name: Attest Distribution Assets with SBoM
id: attest-sbom
uses: actions/attest-sbom@v2
with:
subject-path: |
${{ steps.build.outputs.archive }}
${{ steps.build.outputs.archive }}.sbom.spdx.json
${{ steps.build.outputs.archive }}.sbom.cyclonedx.json
sbom-path: "${{ steps.build.outputs.archive }}.sbom.spdx.json"
# The provenanve information is stored alongside the built artifact with
# the `.sigstore` file extension.
- name: "Copy SBoM provenance"
id: sbom-provenance
shell: bash
run: |
cp "$ATTESTATION" "$ARCHIVE.sigstore"
env:
ARCHIVE: "${{ steps.build.outputs.archive }}"
ATTESTATION: "${{ steps.attest-sbom.outputs.bundle-path }}"
- name: Upload artifact
uses: actions/upload-artifact@v6
with:
name: release-${{ matrix.target }}
path: |
${{ steps.build.outputs.archive }}
${{ steps.build.outputs.archive }}.sha256
${{ steps.build.outputs.archive }}.sha512
${{ steps.build.outputs.archive }}.sigstore
${{ steps.build.outputs.archive }}.sbom.spdx.json
${{ steps.build.outputs.archive }}.sbom.cyclonedx.json
overwrite: true
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: github-actions
directory: "/"
labels: []
schedule:
interval: monthly
open-pull-requests-limit: 10
# Rust:
- package-ecosystem: "cargo"
directory: "/"
labels: []
schedule:
interval: "monthly"
- package-ecosystem: "cargo"
directory: "/compiler-cli"
labels: []
schedule:
interval: "monthly"
- package-ecosystem: "cargo"
directory: "/compiler-core"
labels: []
schedule:
interval: "monthly"
- package-ecosystem: "cargo"
directory: "/compiler-wasm"
labels: []
schedule:
interval: "monthly"
- package-ecosystem: "cargo"
# Does not have any custom deps right now,
# but can add them in the future:
directory: "/test-package-compiler"
labels: []
schedule:
interval: "monthly"
================================================
FILE: .github/pull_request_template.md
================================================
- [ ] The changes in this PR have been discussed beforehand in an issue
- [ ] The issue for this PR has been linked
- [ ] Tests have been added for new behaviour
- [ ] The changelog has been updated for any user-facing changes
================================================
FILE: .github/workflows/ci.yaml
================================================
name: ci
on:
pull_request:
paths-ignore:
- "CHANGELOG.md"
- "docs/**"
push:
branches:
- main
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: "-D warnings"
CARGO_INCREMENTAL: 0
CARGO_PROFILE_DEV_DEBUG: 0
CARGO_PROFILE_TEST_DEBUG: 0
CROSS_CONTAINER_UID: 0
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
test:
name: test
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
toolchain: [stable]
target:
- x86_64-unknown-linux-gnu
- x86_64-unknown-linux-musl
- aarch64-unknown-linux-gnu
- aarch64-unknown-linux-musl
- x86_64-apple-darwin
- x86_64-pc-windows-msvc
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
binary: x86-64
cargo-tool: cargo
run-integration-tests: true
- os: ubuntu-latest
target: x86_64-unknown-linux-musl
binary: x86-64
cargo-tool: cross
run-integration-tests: true
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
binary: aarch64
cargo-tool: cross
run-integration-tests: false # Cannot run aarch64 binaries on x86_64
- os: ubuntu-latest
target: aarch64-unknown-linux-musl
binary: aarch64
cargo-tool: cross
run-integration-tests: false # Cannot run aarch64 binaries on x86_64
- os: macos-15-intel # intel
target: x86_64-apple-darwin
binary: x86_64
cargo-tool: cargo
run-integration-tests: true
- os: macos-latest # aarch64
toolchain: stable
target: aarch64-apple-darwin
binary: arm64
cargo-tool: cargo
run-integration-tests: true
- os: windows-2022
target: x86_64-pc-windows-msvc
binary: x86-64
cargo-tool: cargo
run-integration-tests: true
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Install musl-tools incl. musl-gcc
uses: awalsh128/cache-apt-pkgs-action@v1
with:
# musl-tools provide `musl-gcc` which is required for `ring` which is required for `rustls` et al.
packages: musl-tools
version: 1.1
if: ${{ matrix.target == 'x86_64-unknown-linux-musl'}}
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.toolchain }}
target: ${{ matrix.target }}
- name: Install Erlang
uses: erlef/setup-beam@v1
with:
otp-version: "28.0.1"
elixir-version: "1.18"
rebar3-version: "3"
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: "20"
- name: Setup Deno
uses: denoland/setup-deno@v2
with:
deno-version: "2.2"
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: "1.2"
- name: Handle Rust dependencies caching
uses: Swatinem/rust-cache@v2
with:
key: v1-${{ matrix.target }}
- name: Install Gleam
uses: clechasseur/rs-cargo@v4
with:
command: install
args: "--path gleam-bin --target ${{ matrix.target }} --debug --locked --force"
tool: ${{ matrix.cargo-tool }}
if: ${{ matrix.run-integration-tests }}
- name: Verify binary architecture
shell: bash
run: |
BINARY_PATH="${CARGO_HOME}/bin/gleam"
if [[ "${{ matrix.target }}" == *"windows"* ]]; then
BINARY_PATH="${BINARY_PATH}.exe"
fi
if ! file -b "$BINARY_PATH" | grep -q "${{ matrix.binary }}"; then
echo "error: Architecture mismatch"
echo "Expected architecture: '${{ matrix.binary }}'"
echo "Found binary type: '$(file -b "$BINARY_PATH")'"
exit 1
fi
echo "ok: Architecture match"
if: ${{ matrix.run-integration-tests }}
- name: Run tests
uses: clechasseur/rs-cargo@v4
with:
command: test
# We only want to run the `test-output` when running integration tests.
# There's a caveat though: when `cargo-tool` is `cross` it uses a container
# and would result in these integration tests failing due to not finding
# the escript binary. So, in case `cargo-tool != cargo` we'll skip the
# `test-output` tests as well.
args: >-
--workspace
--target ${{ matrix.target }}
${{ ((matrix.run-integration-tests && matrix.cargo-tool == 'cargo')
&& ' ')
|| '--exclude test-output' }}
tool: ${{ matrix.cargo-tool }}
- name: test/project_erlang (non-windows)
run: |
gleam run && cd src && gleam run && cd ..
gleam check
gleam test && cd src && gleam test && cd ..
gleam docs build
working-directory: ./test/project_erlang
if: ${{ runner.os != 'Windows' && matrix.run-integration-tests }}
- name: test/project_erlang (windows)
run: |
gleam run && cd src && gleam run && cd ..
gleam check
gleam test && cd src && gleam test && cd ..
gleam docs build
working-directory: ./test/project_erlang_windows
if: ${{ runner.os == 'Windows' && matrix.run-integration-tests }}
- name: test/project_erlang export erlang-shipment (non-windows)
run: |
gleam export erlang-shipment
./build/erlang-shipment/entrypoint.sh run
working-directory: ./test/project_erlang
if: ${{ runner.os != 'Windows' && matrix.run-integration-tests }}
- name: test/project_erlang export erlang-shipment (windows)
run: |
gleam export erlang-shipment
.\build\erlang-shipment\entrypoint.ps1 run
working-directory: ./test/project_erlang_windows
if: ${{ runner.os == 'Windows' && matrix.run-integration-tests }}
- name: test/project_erlang export package-interface (non-windows)
run: |
gleam export package-interface --out="interface.json"
cat interface.json
working-directory: ./test/project_erlang
if: ${{ runner.os != 'Windows' && matrix.run-integration-tests }}
- name: test/project_erlang export package-interface (windows)
run: |
gleam export package-interface --out="interface.json"
cat interface.json
working-directory: ./test/project_erlang_windows
if: ${{ runner.os == 'Windows' && matrix.run-integration-tests }}
- name: test/project_erlang export package-information
run: |
gleam export package-information --out="gleam.json"
cat gleam.json
working-directory: ./test/project_erlang
if: ${{ matrix.run-integration-tests }}
- name: test/external_only_javascript
run: ./test.sh
working-directory: ./test/external_only_javascript
if: ${{ matrix.run-integration-tests }}
env:
GLEAM_COMMAND: gleam
- name: test/external_only_erlang
run: ./test.sh
working-directory: ./test/external_only_erlang
if: ${{ matrix.run-integration-tests }}
env:
GLEAM_COMMAND: gleam
- name: test/root_package_not_compiled_when_running_dep
run: ./test.sh
working-directory: ./test/root_package_not_compiled_when_running_dep
if: ${{ matrix.run-integration-tests }}
env:
GLEAM_COMMAND: gleam
- name: test/erlang_shipment_no_dev_deps
run: ./test.sh
working-directory: ./test/erlang_shipment_no_dev_deps
if: ${{ matrix.run-integration-tests }}
env:
GLEAM_COMMAND: gleam
- name: test/project_javascript
run: |
gleam run
gleam check
gleam test
gleam docs build
working-directory: ./test/project_javascript
if: ${{ matrix.run-integration-tests }}
- name: test/project_path_deps
run: |
gleam update
gleam check
working-directory: ./test/project_path_deps/project_a
if: ${{ matrix.run-integration-tests }}
- name: test/project_git_deps
run: |
gleam update
gleam check
working-directory: ./test/project_git_deps
if: ${{ matrix.run-integration-tests }}
- name: Test project generation
run: |
gleam new lib_project
cd lib_project
gleam run
gleam test
# Test adding of deps
gleam add exception # No specifier
gleam add gleam_http@4 # Version specifier
gleam test
# Test documentation generation
gleam docs build
# Assert that module metadata has been written
ls build/dev/erlang/lib_project/_gleam_artefacts/lib_project.cache
# Assert that HTML docs and their assets have been written
ls build/dev/docs/lib_project/index.html
ls build/dev/docs/lib_project/lib_project.html
ls build/dev/docs/lib_project/css/atom-one-light.min.css
ls build/dev/docs/lib_project/css/atom-one-dark.min.css
ls build/dev/docs/lib_project/css/index.css
ls build/dev/docs/lib_project/js/highlight.min.js
ls build/dev/docs/lib_project/js/highlightjs-gleam.js
ls build/dev/docs/lib_project/js/highlightjs-erlang.min.js
ls build/dev/docs/lib_project/js/highlightjs-elixir.min.js
ls build/dev/docs/lib_project/js/highlightjs-javascript.min.js
ls build/dev/docs/lib_project/js/highlightjs-typescript.min.js
ls build/dev/docs/lib_project/js/lunr.min.js
ls build/dev/docs/lib_project/js/index.js
ls build/dev/docs/lib_project/fonts/karla-v23-bold-latin-ext.woff2
ls build/dev/docs/lib_project/fonts/karla-v23-bold-latin.woff2
ls build/dev/docs/lib_project/fonts/karla-v23-regular-latin-ext.woff2
ls build/dev/docs/lib_project/fonts/karla-v23-regular-latin.woff2
ls build/dev/docs/lib_project/fonts/ubuntu-mono-v15-regular-cyrillic-ext.woff2
ls build/dev/docs/lib_project/fonts/ubuntu-mono-v15-regular-cyrillic.woff2
ls build/dev/docs/lib_project/fonts/ubuntu-mono-v15-regular-greek-ext.woff2
ls build/dev/docs/lib_project/fonts/ubuntu-mono-v15-regular-greek.woff2
ls build/dev/docs/lib_project/fonts/ubuntu-mono-v15-regular-latin-ext.woff2
ls build/dev/docs/lib_project/fonts/ubuntu-mono-v15-regular-latin.woff2
if: ${{ matrix.run-integration-tests }}
test-wasm:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
target: wasm32-unknown-unknown
- uses: actions/setup-node@v6
with:
node-version: "20"
- name: Install wasm-pack
run: |
curl -sSL https://rustwasm.github.io/wasm-pack/installer/init.sh | sh
- name: Run wasm tests
run: wasm-pack test --node compiler-wasm
rustfmt:
name: rustfmt
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
components: rustfmt
- run: cargo fmt --all -- --check
validate:
name: validate
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Ensure no merge commits
uses: NexusPHP/no-merge-commits@v2.2.1
if: github.event_name == 'pull_request'
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
- name: Install cargo-deny
run: |
set -e
curl -L https://github.com/EmbarkStudios/cargo-deny/releases/download/0.18.6/cargo-deny-0.18.6-x86_64-unknown-linux-musl.tar.gz | tar xzf -
mv cargo-deny-*-x86_64-unknown-linux-musl/cargo-deny cargo-deny
echo `pwd` >> $GITHUB_PATH
- name: Validate deps
run: cargo deny check
lint-build:
name: lint-build
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
components: clippy
- name: Handle Rust dependencies caching
uses: Swatinem/rust-cache@v2
with:
key: v1-linux-gnu
- name: Run linter
run: cargo clippy --workspace
- run: cargo build
- name: Upload artifact (Ubuntu)
uses: actions/upload-artifact@v7
with:
name: gleam
path: target/debug/gleam
test-projects:
name: test-projects
needs: lint-build
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Install Deno
uses: denoland/setup-deno@v2
with:
deno-version: v2.x
- name: Install Bun
uses: oven-sh/setup-bun@v2
- name: Install Erlang
uses: erlef/setup-beam@v1
with:
otp-version: "26.1"
elixir-version: "1.16.1"
rebar3-version: "3"
- name: Download Gleam binary from previous job
uses: actions/download-artifact@v8
with:
name: gleam
path: ./test
- name: Configure test projects to use Gleam binary
run: |
echo $PWD/ >> $GITHUB_PATH
chmod +x ./gleam
sed -i 's/cargo run --quiet --/gleam/' */Makefile
sed -i 's/cargo run --/gleam/' */Makefile
working-directory: ./test
- name: test/language Erlang
run: make clean erlang
working-directory: ./test/language
- name: test/language JavaScript with NodeJS
run: make clean nodejs
working-directory: ./test/language
- name: test/language JavaScript with Deno
run: make clean deno
working-directory: ./test/language
- name: test/language JavaScript with Bun
run: make clean bun
working-directory: ./test/language
- name: test/compile_package0
run: make
working-directory: ./test/compile_package0
- name: test/compile_package1
run: make
working-directory: ./test/compile_package1
- name: Test JavaScript prelude
run: make
working-directory: ./test/javascript_prelude
- name: Test export of hex tarball
run: make test
working-directory: ./test/hextarball
- name: test/running_modules
run: make test-all
working-directory: ./test/running_modules
- name: test/multi_namespace
run: ./test.sh
working-directory: ./test/multi_namespace
- name: test/multi_namespace_not_top_level
run: ./test.sh
working-directory: ./test/multi_namespace_not_top_level
- name: Test FFI in subdirectories
run: make
working-directory: ./test/subdir_ffi
- name: test/unicode_path
run: make
working-directory: ./test/unicode_path ⭐
- name: test/assert
run: make test-all
working-directory: ./test/assert
- name: Test publishing with default main
run: ./test.sh
working-directory: ./test/publishing_default_main
================================================
FILE: .github/workflows/release-containers.yaml
================================================
name: release-containers
on:
release:
types:
- "published"
permissions:
packages: write
id-token: write
attestations: write
jobs:
publish-container-images:
name: publish-container-images
runs-on: ubuntu-latest
# Covered by `release-nightly.yaml`
if: ${{ github.event.release.tag_name != 'nightly' }}
strategy:
matrix:
base-image:
- scratch
- erlang
- erlang-slim
- erlang-alpine
- elixir
- elixir-slim
- elixir-alpine
- node
- node-slim
- node-alpine
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
sparse-checkout: |
.github/actions
containers
- name: "Build & Push Container"
uses: "./.github/actions/build-container"
with:
release-id: ${{ github.event.release.id }}
version: ${{ github.ref_name }}
================================================
FILE: .github/workflows/release-nightly.yaml
================================================
name: release-nightly
on:
workflow_dispatch:
schedule:
- cron: "45 0 * * *"
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: "-D warnings"
permissions:
contents: write
packages: write
id-token: write
attestations: write
jobs:
# Check if the actions already ran in the last 24 hours
# * If yes: Don't run again
# * If no: Clean out existing release assets
prepare-nightly:
runs-on: ubuntu-latest
name: Prepare Nightly Release
outputs:
should-run: ${{ steps.should-run.outputs.should-run }}
# TODO: Re-add
# if: ${{ github.repository == 'gleam-lang/gleam' }}
steps:
- uses: actions/checkout@v6
- name: print latest_commit
run: echo ${{ github.sha }}
- id: should-run
continue-on-error: true
name: check latest commit is less than a day
if: ${{ github.event_name == 'schedule' }}
run: test -z $(git rev-list --after="24 hours" ${{ github.sha }}) && echo "should-run=false" >> $GITHUB_OUTPUT
- name: Delete old release assets
uses: mknejp/delete-release-assets@v1
if: ${{ steps.should-run != 'false' }}
with:
token: ${{ github.token }}
tag: nightly
fail-if-no-assets: false
fail-if-no-release: false
assets: |
*.zip
*.tar.gz
*.sha256
*.sha512
*.sigstore
*.sbom.*.json
build-release:
name: build-release
runs-on: ${{ matrix.os }}
environment: release
outputs:
release-id: ${{ steps.release.outputs.id }}
strategy:
matrix:
target:
- x86_64-unknown-linux-musl
- aarch64-unknown-linux-musl
- x86_64-apple-darwin
- aarch64-apple-darwin
- x86_64-pc-windows-msvc
- aarch64-pc-windows-msvc
toolchain: [ stable ]
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-musl
expected-binary-architecture: x86-64
cargo-tool: cross
- os: ubuntu-latest
target: aarch64-unknown-linux-musl
expected-binary-architecture: aarch64
cargo-tool: cross
- os: macos-15-intel
target: x86_64-apple-darwin
expected-binary-architecture: x86_64
cargo-tool: cargo
- os: macos-latest
target: aarch64-apple-darwin
expected-binary-architecture: arm64
cargo-tool: cargo
- os: windows-2022
target: x86_64-pc-windows-msvc
expected-binary-architecture: x86-64
cargo-tool: cargo
- os: windows-11-arm
target: aarch64-pc-windows-msvc
expected-binary-architecture: arm64
cargo-tool: cargo
- os: ubuntu-latest
target: wasm32-unknown-unknown
cargo-tool: wasm-pack
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Update versions
shell: bash
run: ./bin/add-nightly-suffix-to-versions.sh
- name: "Build Archive"
id: build
uses: "./.github/actions/build-release"
with:
version: nightly
toolchain: ${{ matrix.toolchain }}
target: ${{ matrix.target }}
cargo-tool: ${{ matrix.cargo-tool }}
expected-binary-architecture: ${{ matrix.expected-binary-architecture }}
azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
azure-subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}
azure-trusted-signing-account-name: ${{ vars.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }}
azure-certificate-profile-name: ${{ vars.AZURE_CERTIFICATE_PROFILE_NAME }}
- name: Upload release archive
id: release
# https://github.com/softprops/action-gh-release/issues/445
# uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@f2352b97da0095b4dbbd885a81023e3deabf4fef
with:
draft: false
prerelease: true
fail_on_unmatched_files: true
files: "${{ steps.build.outputs.files }}"
tag_name: nightly
name: "Nightly"
body: |
A build that runs every night following changes to the main branch. Contains all the latest features and changes, but there is a higher chance of instability, bugs, and unfinished features.
The assets are updated but the tag is not, so if you view the commit or download the source from below you will not get the correct code.
build-container-images:
name: build-container-images
needs: [ prepare-nightly, build-release ]
runs-on: ubuntu-latest
if: ${{ needs.prepare-nightly.outputs.should-run != 'false' }}
strategy:
matrix:
base-image:
- scratch
- erlang
- erlang-slim
- erlang-alpine
- elixir
- elixir-slim
- elixir-alpine
- node
- node-slim
- node-alpine
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
sparse-checkout: |
.github/actions
containers
- name: "Build & Push Container"
uses: "./.github/actions/build-container"
with:
version: nightly
release-id: ${{ needs.build-release.outputs.release-id }}
================================================
FILE: .github/workflows/release.yaml
================================================
name: release
on:
push:
tags:
- "v*"
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: "-D warnings"
permissions:
contents: read
jobs:
build-release:
name: build-release
runs-on: ${{ matrix.os }}
environment: release
permissions:
id-token: write
attestations: write
strategy:
matrix:
target:
- x86_64-unknown-linux-musl
- aarch64-unknown-linux-musl
- x86_64-apple-darwin
- aarch64-apple-darwin
- x86_64-pc-windows-msvc
- aarch64-pc-windows-msvc
toolchain: [ stable ]
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-musl
expected-binary-architecture: x86-64
cargo-tool: cross
- os: ubuntu-latest
target: aarch64-unknown-linux-musl
expected-binary-architecture: aarch64
cargo-tool: cross
- os: macos-15-intel
target: x86_64-apple-darwin
expected-binary-architecture: x86_64
cargo-tool: cargo
- os: macos-latest
target: aarch64-apple-darwin
expected-binary-architecture: arm64
cargo-tool: cargo
- os: windows-2022
target: x86_64-pc-windows-msvc
expected-binary-architecture: x86-64
cargo-tool: cargo
- os: windows-11-arm
target: aarch64-pc-windows-msvc
expected-binary-architecture: arm64
cargo-tool: cargo
- os: ubuntu-latest
target: wasm32-unknown-unknown
cargo-tool: wasm-pack
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: "Build Archive"
id: build
uses: "./.github/actions/build-release"
with:
version: ${{ github.ref_name }}
toolchain: ${{ matrix.toolchain }}
target: ${{ matrix.target }}
cargo-tool: ${{ matrix.cargo-tool }}
expected-binary-architecture: ${{ matrix.expected-binary-architecture }}
azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
azure-subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}
azure-trusted-signing-account-name: ${{ vars.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }}
azure-certificate-profile-name: ${{ vars.AZURE_CERTIFICATE_PROFILE_NAME }}
create-release:
name: create-release
needs: ['build-release']
runs-on: ubuntu-latest
environment: release
permissions:
contents: write
steps:
- name: Download Artifacts
uses: actions/download-artifact@v8
with:
pattern: release-*
merge-multiple: true
- name: Create release
env:
GITHUB_TOKEN: '${{ github.token }}'
REPOSITORY: '${{ github.repository }}'
TITLE: '${{ github.ref_name }}'
TAG_NAME: '${{ github.ref_name }}'
NOTES: '${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/CHANGELOG.md'
run: |
gh release create \
--repo "$REPOSITORY" \
--title "$TITLE" \
--notes "$NOTES" \
--draft \
--verify-tag \
${{ contains(github.ref_name, '-rc') && '--prerelease' || '' }} \
"$TAG_NAME" \
gleam-*
================================================
FILE: .gitignore
================================================
**/*.rs.bk
**/*.beam
/target/
/tmp
.idea/
.idea/*
erl_crash.dump
*.lock
node_modules/
compiler-cli/build/
================================================
FILE: .vscode/settings.json
================================================
{
// Don't show these files in search
"search.exclude": {
// Test snapshots
"**/*.snap": true,
// Generated code
"compiler-core/generated/**": true,
// Images
"images/**": true
},
"cSpell.words": [
"camino",
"cksum",
"codegen",
"ecow",
"flate",
"hardlinking",
"HEXPM",
"insta",
"itertools",
"NOCOLOUR",
"reqwest",
"rustfmt",
"subpackage",
"Telem",
"Unretire",
"Untar",
"walkdir"
],
"cSpell.language": "en,uk,en-GB,en-US"
}
================================================
FILE: .well-known/funding-manifest-urls
================================================
https://gleam.run/funding.json
================================================
FILE: CHANGELOG.md
================================================
# Changelog
## Unreleased
### Compiler
- The compiler now supports list prepending in constants. For example:
```gleam
pub const viviparous_mammals = ["dog", "cat", "human"]
pub const all_mammals = ["platypus", "echidna", ..viviparous_mammals]
```
([Surya Rose](https://github.com/GearsDatapacks))
- The analysis of record update expressions is now fault tolerant, meaning the
compiler will no longer stop reporting errors at the first invalid field it
finds.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
### Build tool
- When publishing, the package manager now uses the full term instead of the
shorthand "MFA" in the prompt and error message.
([Luka Ivanović](https://github.com/luka-hash))
### Language server
- The "extract variable" code action can now pick better names for variables in
case branches and blocks, ignoring unrelated names of variables in other
branches.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server now has a code action to replace a `_` in a type
annotation with the corresponding type. For example:
```gleam
pub fn load_user(id: Int) -> Result(_, Error) {
// ^
// Triggering the code action here
sql.find_by_id(id)
|> result.map_error(CannotLoadUser)
}
```
Triggering the code action over the `_` will result in the following code:
```gleam
pub fn load_user(id: Int) -> Result(User, Error) {
sql.find_by_id(id)
|> result.map_error(CannotLoadUser)
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server now shows completions for the labelled argument of a
record when writing a record update.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
### Formatter
### Bug fixes
- Fixed a bug where the compiler would crash when trying to read the cache for
modules containing large constants.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where `BitArray$BitArray$data` constructed a `DataView` with
incorrect byte length instead of the slice's actual size, causing sliced bit
arrays to include extra bytes from the underlying buffer on JavaScript.
([John Downey](https://github.com/jtdowney))
- The compiler now parses UTF-8 source files with a byte-order mark correctly,
instead of raising an error.
([Lucy McPhail](https://github.com/lucymcphail))
## v1.15.1 - 2026-03-17
### Bug fixes
- Fixed a bug where `BitArray$BitArray$data` constructed a `DataView` with
offset 0 instead of the slice's actual byte offset, causing sliced bit arrays
to read from the wrong position in the underlying buffer on JavaScript.
([John Downey](https://github.com/jtdowney))
- Fixed a bug where the "Add missing type parameter" code action could be
triggered on types that do not exist instead of type variables.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of
any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official email address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
hello@gleam.run.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Gleam
Thanks for contributing to Gleam!
Before continuing please read our [code of conduct][code-of-conduct] which all
contributors are expected to adhere to.
[code-of-conduct]: https://github.com/gleam-lang/gleam/blob/main/CODE_OF_CONDUCT.md
## Contributing bug reports
If you have found a bug in Gleam please check to see if there is an open
ticket for this problem on [our GitHub issue tracker][issues]. If you cannot
find an existing ticket for the bug please open a new one.
[issues]: https://github.com/gleam-lang/gleam/issues
A bug may be a technical problem such as a compiler crash or an incorrect
return value from a library function, or a user experience issue such as
unclear or absent documentation. If you are unsure if your problem is a bug
please open a ticket and we will work it out together.
## Contributing code changes
Before working on code it is suggested that you read the
[docs/compiler/README.md](docs/compiler/README.md) file.
It outlines fundamental components and design of this project.
Code changes to Gleam are welcomed via the process below.
1. Find or open a GitHub issue relevant to the change you wish to make and
comment saying that you wish to work on this issue. If the change
introduces new functionality or behaviour this would be a good time to
discuss the details of the change to ensure we are in agreement as to how
the new functionality should work.
2. Update the [CHANGELOG.md](CHANGELOG.md) file with your changes.
3. Open a GitHub pull request with your changes and ensure the tests and build
pass on CI.
4. A Gleam team member will review the changes and may provide feedback to
work on. Depending on the change there may be multiple rounds of feedback.
5. Once the changes have been approved the code will be rebased into the
`main` branch.
## Local development
To run the compiler tests. This will require a recent stable version of Rust,
Erlang, Elixir, NodeJS, Deno, and Bun to be installed.
If you are using the Nix package manager, there's a [gleam-nix flake](https://github.com/vic/gleam-nix)
you can use for running any Gleam version or quickly obtaining a development
environment for Gleam.
```sh
cargo test
# Or if you have watchexec installed you can run them automatically
# when files change
make test-watch
```
To run the language integration tests. This will require a recent stable
version of Rust, Erlang, and NodeJS to be installed.
```sh
make language-test
```
If you don't have Rust or Cargo installed you can run the above command in a
docker sandbox. Run the command below from this directory.
```sh
docker run -v $(pwd):/opt/app -it -w /opt/app rust:latest bash
```
## Rust development
Here are some tips and guidelines for writing Rust code in the Gleam compiler:
The `GLEAM_LOG` environment variable can be used to cause the compiler to
print more information for debugging and introspection. i.e.
`GLEAM_LOG=trace`.
### Clippy linter
Your PR may fail on CI due to clippy errors. Clippy can be run locally like so:
```sh
cargo clean -p gleam
cargo clippy
```
If you have lint errors on CI but not locally upgrade your Rust version to the
latest stable.
```sh
rustup upgrade stable
```
## Operating system specific code
This project is used on FreeBSD, Linux, MacOS, OpenBSD, Windows, and presumably
other operating systems, so there is some amount of code that needs to be
different depending on which is it running on. So far this is hidden inside
dependencies, with the exception of some code for working with file paths in
tests and for setting file permissions, which is different on Windows. If you
are working in this area then you may get a CI failure relating to this for
your first attempt. If you need help resolving any issues do not hesitate to
ask.
================================================
FILE: Cargo.toml
================================================
[workspace]
resolver = "2"
members = [
"gleam-bin",
"compiler-cli",
"compiler-core",
"compiler-wasm",
"language-server",
"test-helpers-rs",
"test-output",
"test-package-compiler",
"test-project-compiler",
"language-server",
"hexpm",
]
# common dependencies
[workspace.dependencies]
# Immutable data structures
im = { version = "15", features = ["serde"] }
# Extra iter methods
itertools = "0"
# Parsing
regex = "1"
# Colours in terminal
termcolor = "1"
# Data (de)serialisation
serde = { version = "1", features = ["derive", "rc"] }
serde_json = "1"
# toml config file parsing
toml = "0.9"
# Enum trait impl macros
strum = { version = "0", features = ["derive"] }
# Creation of tar file archives
tar = "0"
# gzip compression
flate2 = "1"
# Logging
tracing = "0"
# Macro to work around Rust's traits not working with async. Sigh.
async-trait = "0"
# HTTP types
http = "1"
http-serde = "2.1.1"
# Async combinators for futures
futures = "0"
# Little helper to omit fields that cannot be debug printed
debug-ignore = "1"
# base encoding
base16 = "0"
# Language server protocol server plumbing
lsp-server = "0"
lsp-types = "=0.95.1"
# Compact clone-on-write vector & string type
ecow = "0"
# Drop in replacement for std::path but with only utf-8
camino = "1"
# std::error::Error definition macro
thiserror = "2"
# Test assertion errors with diffs
pretty_assertions = "1"
# Snapshot testing to make test maintenance easier
insta = { version = "1", features = ["glob"] }
# A transitive dependency needed to compile into wasm32-unknown-unknown
# See https://docs.rs/getrandom/latest/getrandom/index.html#webassembly-support
getrandom = { version = "0.2", features = ["js"] }
# Non-empty vectors
vec1 = "1"
# Pubgrub dependency resolution algorithm
pubgrub = "0.3"
# Open the user's web browser
opener = "0"
================================================
FILE: Cross.toml
================================================
[target.x86_64-unknown-linux-musl]
image = "clux/muslrust:stable"
================================================
FILE: LICENCE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2016 - present Louis Pilfold
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: Makefile
================================================
#
# Goals to be specified by user
#
.PHONY: help
help:
@cat $(MAKEFILE_LIST) | grep -E '^[a-zA-Z_-]+:.*?## .*$$' | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
.PHONY: build
build: ## Build the compiler
cargo build --release
.PHONY: install
install: ## Build the Gleam compiler and place it on PATH
cd gleam-bin && cargo install --path . --force --locked
.PHONY: test
test: ## Run the compiler unit tests
cargo test --quiet
cargo clippy
cd test/language && make
cd test/javascript_prelude && make test
cd test/project_erlang && cargo run clean && cargo run check && cargo run test
cd test/project_javascript && cargo run clean && cargo run check && cargo run test
cd test/project_deno && cargo run clean && cargo run check && cargo run test
cd test/hextarball && make test
cd test/running_modules && make test
cd test/subdir_ffi && make
.PHONY: insta-check-unused
insta-check-unused: ## Check for unused cargo insta snapshots
cargo insta test --unreferenced=warn
.PHONY: insta-fix-unused
insta-fix-unused: ## Remove unused cargo insta snapshots
cargo insta test --unreferenced=delete
.PHONY: language-test
language-test: ## Run the language integration tests for all targets
cd test/language && make
.PHONY: language-test-watch
language-test-watch: ## Run the language integration tests for all targets when files change
watchexec "cd test/language && make"
.PHONY: javascript-prelude-test
javascript-prelude-test: ## Run the JavaScript prelude core tests
cd test/javascript_prelude && make test
.PHONY: javascript-prelude-test-watch
javascript-prelude-test-watch: ## Run the JavaScript prelude core tests when files change
watchexec "cd test/javascript_prelude && make test"
.PHONY: test-watch
test-watch: ## Run compiler tests when files change
watchexec -e rs,toml,gleam,html "cargo test --quiet"
.PHONY: export-hex-tarball-test
export-hex-tarball-test: ## Run `gleam export hex-tarball` and verify it is created
cd test/hextarball && make test
.PHONY: benchmark
benchmark: ## Run the benchmarks
cd benchmark/list && make
# Debug print vars with `make print-VAR_NAME`
print-%: ; @echo $*=$($*)
================================================
FILE: README.md
================================================
Gleam is a friendly language for building type-safe systems that scale! For more
information see [the website](https://gleam.run).
## Support Gleam!
Gleam is not owned by a corporation, instead it is kindly supported by its
sponsors. If you like Gleam please consider [sponsoring the project or members
of the core team](https://gleam.run/sponsor).
Thank you so much! 💖
================================================
FILE: RELEASE.md
================================================
# Release checklist
1. Update the version in each `Cargo.toml`.
2. Update versions in `src/new.rs` for stdlib etc if required.
3. Run `make test build`.
4. Git commit, tag, push, push tags.
5. Wait for CI release build to finish.
6. Publish release on GitHub from draft made by CI.
7. Update version in `Cargo.toml` to next-dev.
8. Update clone target version in `getting-started.md` in `website`.
================================================
FILE: benchmark/list/.gitignore
================================================
*.beam
*.ez
/build
erl_crash.dump
================================================
FILE: benchmark/list/Makefile
================================================
.PHONY: build
build: clean erlang nodejs deno bun
.PHONY: clean
clean:
rm -rf build
.PHONY: erlang
erlang:
@echo test/language on Erlang
cargo run --quiet -- test --target erlang
.PHONY: nodejs
nodejs:
@echo test/language on JavaScript with Node
cargo run --quiet -- test --target javascript --runtime nodejs
.PHONY: deno
deno:
@echo test/language on JavaScript with Deno
cargo run --quiet -- test --target javascript --runtime deno
.PHONY: bun
bun:
@echo test/language on JavaScript with Bun
cargo run --quiet -- test --target javascript --runtime bun
================================================
FILE: benchmark/list/gleam.toml
================================================
name = "list"
version = "1.0.0"
description = "Benchmarks for lists"
licenses = ["Apache-2.0"]
[dependencies]
gleam_stdlib = "~> 0.28"
gleamy_bench = ">= 0.6.0 and < 1.0.0"
================================================
FILE: benchmark/list/manifest.toml
================================================
# This file was generated by Gleam
# You typically do not need to edit this file
packages = [
{ name = "gleam_stdlib", version = "0.59.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "F8FEE9B35797301994B81AF75508CF87C328FE1585558B0FFD188DC2B32EAA95" },
{ name = "gleamy_bench", version = "0.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleamy_bench", source = "hex", outer_checksum = "DEF68E4B097A56781282F0F9D48371A0ABBCDDCF89CAD05B28C3BEDD6B2E8DF3" },
]
[requirements]
gleam_stdlib = { version = "~> 0.28" }
gleamy_bench = { version = ">= 0.6.0 and < 1.0.0" }
================================================
FILE: benchmark/list/src/list.gleam
================================================
pub fn main() {
Nil
}
================================================
FILE: benchmark/list/test/benchmarks.gleam
================================================
import gleamy/bench
import gleam/io
import gleam/list
pub fn print_results(results: List(bench.BenchResults)) {
results
|> list.map(fn(result) {
result
|> bench.table([bench.IPS, bench.Min, bench.P(99)])
|> io.println()
})
}
================================================
FILE: benchmark/list/test/list_test.gleam
================================================
import benchmarks.{print_results}
import gleam/list
import gleamy/bench
pub fn main() {
print_results([bench_odd_nums_between(), bench_count_ones(), bench_slice()])
}
fn bench_odd_nums_between() -> bench.BenchResults {
let odd_nums_between = fn(args) {
let #(start, end) = args
odd_nums_between(start, end, [])
}
bench.run(
[
bench.Input("[0, 100)", #(0, 100)),
bench.Input("[0, 1000)", #(0, 1000)),
bench.Input("[0, 10_000)", #(0, 10_000)),
bench.Input("[0, 100_000)", #(0, 100_000)),
bench.Input("[0, 1_000_000)", #(0, 1_000_000)),
],
[bench.Function("odd_nums_between", odd_nums_between)],
[bench.Duration(1000), bench.Warmup(100)],
)
}
/// Returns a list of numbers from `start` up to, but not including, `end`.
fn odd_nums_between(start: Int, end: Int, acc: List(Int)) -> List(Int) {
case start {
start if start >= end -> list.reverse(acc)
_ -> {
let acc = case start {
n if n % 2 == 1 -> [n, ..acc]
_ -> acc
}
odd_nums_between(start + 1, end, acc)
}
}
}
fn bench_count_ones() -> bench.BenchResults {
let create_list = fn(size) {
list.range(0, size)
|> list.map(fn(n) {
case n {
_ if n % 3 == 0 -> 1
_ -> 0
}
})
}
let count_ones = fn(list) { count_ones(list, 0) }
bench.run(
[
bench.Input("100 numbers", create_list(100)),
bench.Input("1000 numbers", create_list(1000)),
bench.Input("10_000 numbers", create_list(10_000)),
bench.Input("100_000 numbers", create_list(100_000)),
bench.Input("1_000_000 numbers", create_list(1_000_000)),
],
[bench.Function("count_ones", count_ones)],
[bench.Duration(1000), bench.Warmup(100)],
)
}
/// Counts the number of ones in a list.
fn count_ones(list: List(Int), count: Int) -> Int {
case list {
[] -> count
[1, ..tail] -> count_ones(tail, count + 1)
[_, ..tail] -> count_ones(tail, count)
}
}
fn bench_slice() -> bench.BenchResults {
let list = list.range(0, 1_000_000)
let slice = fn(args) {
let #(list, start, end) = args
slice(list, start, end, [])
}
bench.run(
[
bench.Input("[0, 1000)", #(list, 0, 1000)),
bench.Input("[0, 10_000)", #(list, 0, 10_000)),
bench.Input("[0, 100_000)", #(list, 0, 100_000)),
bench.Input("[999_000, 1_000_000)", #(list, 999_000, 1_000_000)),
bench.Input("[990_000, 1_000_000)", #(list, 990_000, 1_000_000)),
bench.Input("[900_000, 1_000_000)", #(list, 900_000, 1_000_000)),
bench.Input("[0, 1_000_000)", #(list, 0, 1_000_000)),
],
[bench.Function("slice", slice)],
[bench.Duration(1000), bench.Warmup(100)],
)
}
/// Slices a list.
fn slice(list: List(Int), start: Int, end: Int, acc: List(Int)) -> List(Int) {
case list {
// If the first element is before the slice, skip it.
[_, ..tail] if start > 0 -> {
slice(tail, start - 1, end - 1, acc)
}
// If the first element is within the slice, add it to the accumulator.
[head, ..tail] if end > 0 -> {
slice(tail, start - 1, end - 1, [head, ..acc])
}
// Otherwise, return the accumulator.
_ -> list.reverse(acc)
}
}
================================================
FILE: bin/add-nightly-suffix-to-versions.sh
================================================
#!/bin/sh
#
# Add the `-nightly-YYYYMMDD` suffix the version of all Rust crates in the
# workspace. Used by the nightly build.
#
set -e
find . -name Cargo.toml -exec \
sed -i "" -e "s/^version = \"\([^\"]*\)\"$/version = \"\1-nightly-$(date +%Y%m%d)\"/" {} \;
================================================
FILE: changelog/v1.1.md
================================================
# Changelog
## v1.1.0 - 2024-04-16
### Formatter
- Fixed a bug where the first subject of a case expression clause would be
indented more than necessary.
## v1.1.0-rc3 - 2024-04-12
### Formatter
- Fixed a bug where the `@internal` annotation wouldn't be displayed.
- Fixed a bug where a record update's arguments would be incorrectly split on
multiple lines.
## v1.1.0-rc2 - 2024-04-10
### Compiler
- Fixed a bug on the JavaScript target where variables named `debugger`, which
is a JavaScript keyword, were not being renamed, leading to runtime errors.
### Formatter
- Fixed a bug where comments would be moved out of an empty bit array.
- Fixed a bug where the formatter could add a trailing comma inside empty
bit arrays, generating invalid syntax.
- Revert the warning about internal types being exposed in a package's public
API.
### Build tool
- Revert the change that would make the build tool refuse to publish a package
that exposes an internal type in its public API.
## v1.1.0-rc1 - 2024-04-08
### Compiler
- The `@internal` attribute can now be used to annotate definitions.
This will hide those definitions from the generated documentation,
autocompletions and the exported module interface.
- Prepending to lists in JavaScript (`[x, ..xs]` syntax) has been optimised.
- Function stubs are no longer generated for functions that do not have an
implementation for the current targeting being compiled for.
- Fixed a bug where some functions would not result in a compile error when
compiled for a target that they do not support.
- Fixed a bug where sometimes a warning would not be emitted when a result is
discarded.
- Fixed a bug with JavaScript code generation of pattern matching guards.
- URLs in error messages have been updated for the new language tour.
- Improved error message when erroneously trying to append items to a list using
the spread syntax (like `[..rest, last]`).
- Generate [type references](https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html#-reference-types-)
when compiling to JavaScript with TypeScript definitions enabled.
- Fix a bug where JavaScript code generation would not properly return the
result of nested blocks.
- Fix a bug where JavaScript code generation would not properly handle functions
returned by blocks.
- Fix a bug where Erlang code generation would not properly handle list case
patterns with no head and a spread tail.
- The compiler will now raise a warning if you're pattern matching on tuple
literals and suggest you use multiple subjects instead.
- Fixed a bug where JavaScript code generation would incorrectly parenthesise a
return statement.
- Nested tuple access (`tuple.0.1`) now parses successfully.
- Error messages are more clear about expecting values instead of types.
- Fixed a bug where pattern matching on a string would cause the program to
crash on the JavaScript target.
- A warning is now emitted when defining an opaque external type.
- Improve error message when using incorrect quotes (`'`) to define a string
- Fixed a bug where Erlang string prefix patterns could generate invalid Erlang.
- Fixed string prefix matching producing wrong results on the JavaScript target
when the prefix had a Unicode codepoint escape sequence (`\u{...}`).
- Improved error message for wrong patterns using constructors from other
modules.
- Fixed a bug on the JavaScript target where variables bound by patterns, if
used within a bit array literal inside a `case` clause's guard, would be used
before they were defined, leading to a runtime error when evaluating the
`case` expression.
- Improved error messages when failing to parse a series of things.
- A warning is now raised for unused binary operations, records, record access
and record updates.
- Fixed a bug when using constant as the size option parameter
for `BitArray` caused unknown variable exception.
- Improved recommendations on error messages.
- Improved error message for `BitArray` segment sizes.
- A warning is now raised when an internal type is accidentally exposed in a
package's public API.
- Fixed the error message when using `panic` on the JavaScript target: it now
correctly identifies the error variant as a `panic` instead of a `todo`.
- Fixed a bug on the JavaScript target where empty lists with little space
available could compile to a conversion from the array `[ , ]`, causing them
to wrongly have a length of one (the array has a single `undefined` element).
### Formatter
- The formatting of case expressions with multiple subjects has been improved.
- Fixed a bug where the formatter would move comments from the end of bounded
expressions like lists, tuples, case expressions or function calls.
- Fixed a bug where a record update's arguments would not be indented correctly.
- Fixed a bug where function call arguments, tuple items and list items would be
needlessly indented if preceded by a comment.
- Line endings other than `\n` are now handled by the formatter, preserving
blank lines and converting them to `\n`.
- The formatter can now format groups of imports alphabetically.
- Fixed a bug where comments would be moved out of an empty list.
- Fixed a bug where pipes and binary operations in function calls would be
nested more than necessary.
- Improved formatting of comments in binary operation chains.
### Build tool
- Added support for the [Bun](https://bun.sh/) runtime when compiling to
JavaScript by using `gleam run --target javascript --runtime bun`
- Allow compilation of packages that require `"rebar"` using the rebar3
compiler.
- A warning is now emitted if there is a `.gleam` file with a path that would be
invalid as a module name.
- The `~> x.y` version constraint syntax has been dropped in favour of
`> x.y.z and <= xx.0.0` syntax in `gleam add` and `gleam new`, for clarity.
- New projects are created with the GitHub `actions/checkout` v4 action.
- Fixed a bug where bit arrays would break syntax highlighting in the generated
HTML documentation.
- Dependencies that use Erlang-only bit options can now compile on JavaScript,
though the functions that use them will not be available for use in the root
package.
- The output format of the command line help messages have been changed
slightly.
- The command line help text now lists valid targets and runtimes.
- Generated documentation no longer exposes the constructors of opaque types,
no longer exposes the values of constants, and indicates which types are
opaque.
- Generated HTML documentation now includes a link to the package on Hex.
- Terminal colors can now be forced by setting the `FORCE_COLOR` environment
variable to any non-empty value.
- Rust's Reqwest's `webpki-roots` are now used for TLS verification.
- Update Deno config to allow passing `--location` runtime flag.
- Fixed a bug with dependency resolution of exact versions of RC releases.
- Fixed a bug where the documentation of a labelled record constructor could be
assigned to the wrong definition in the doc site.
- Fixed a bug where the code blocks in the generated documentation's site would
have a wrong indentation.
- Fixed a bug where the compiler would panic when it cannot get current
directory.
- Improved error message for non-UTF-8 paths encountered during Gleam commands.
Now includes the path that caused the error for better diagnostics.
- Fixed a bug where on windows local packages had absolute paths in the manifest
instead of relative.
- The `gleam publish` command now asks for confirmation if the package
repository URL doesn't return a successful status code.
- `gleam publish` can now optionally use a Hex API key to authorise publishing
and retiring packages, set via the environment variable `HEXPM_API_KEY`.
- `gleam publish` will now refuse to publish placeholder packages, to prevent
name squatting which is against the Hex terms of service.
- If a package leaks an internal type in its public API, then the build tool
will now refuse to publish it to Hex.
- Monospaced links in the generated documentation now have the same color as
common links.
- Fixed a bug where the `export package interface` command would always
recompile the project ignoring the cache.
### Language Server
- Update messages from the client are now batched to avoid doing excess
compilation work in the language server, improving performance. This makes a
large difference in low power machines where the language server could
struggle to keep up with the edits from the client.
- The `Compiling Gleam` message is no longer emitted each time code is compiled.
This is to reduce noise in editors that show this message prominently such as
Neovim.
- Fixed a bug where hovering over an expression in the middle of a pipe would
give the wrong node.
- Go to definition now works for values in dependency Gleam modules.
- Completions are now provided for module imports.
## v1.0.0 - 2024-03-04
### Language changes
- Comments have been added to the JavaScript prelude to indicate which members
are in the public API and which are internal.
### Build tool
- Fixed a bug where the exported package interface would not have a module's
documentation.
## v1.0.0-rc2 - 2024-02-14
### Bug fixes
- Fixed a bug where the exhaustiveness checker could crash for some generic
types.
### Formatter
- The format used by the formatter has been improved in some niche cases.
- Improved the formatting of long case guards.
## v1.0.0-rc1 - 2024-02-10
### Language changes
- Using a reserved word is now a compile error, not a warning.
- Inexhaustive matches are now compile errors, not warnings.
- The warning for an unused module alias now shows how to not assign a name to
the module.
- Type aliases with unused type parameters now emit an error.
- Type definitions with duplicate type parameters now emit an error.
### Formatter
- Now the formatter will nest pipelines and binary operators that are used as
function arguments, list items or as tuple items.
- The format function literals used as the last argument in a function call
on long lines has been improved.
### Build tool
- If a package contains a `todo` expression then the build tool will now refuse
to publish it to Hex.
- `gleam export` now takes a `package-interface` option to export a json file
containing metadata about the root package.
- `gleam docs build` now creates a json file containing metadata about the root
package.
- The order of dependencies in `manifest.toml` is now in alphabetical order.
- The search bar in generated docs now has a darker background color.
- The generated docs no longer shows whether an argument is discarded or
not in a function signature.
- It is now possible to use `gleam run -m` to run a dependency module even if
that dependency uses a compile target that your project does not support.
### Bug fixes
- Fixed a bug the build tool could be make to attempt to run a main function
that does not support the current target in some circumstances.
- Fixed a bug where the exhaustiveness checker could crash when checking nested
values inserted into the parent type using type parameters.
- Fixed a bug where `functionname(_name)` would incorrectly parse as a function
capture instead of a syntax error.
- Fixed a bug where external only functions would "successfully" compile for a
target they do not support, leading to a runtime error.
## v0.34.1 - 2023-01-17
### Build tool changes
- Support has been added for using SourceHut as a repository.
### Bug fixes
- Fixed a bug where long function headers with external implementations could
format incorrectly.
- The `@deprecated` attribute can now be used to annotate module constants.
This will cause a warning to be emitted when the constant is used.
## v0.34.0 - 2023-01-16
## v0.34.0-rc3 - 2023-01-12
### Language changes
- "echo" is now a reserved word.
- A warning is no longer emitted when a function has a Gleam implementation as
well as external implementations for both targets. This is because having a
default Gleam implementation means the code is future-proof and continues to
be cross platform even if a new target is added.
### Bug fixes
- Fixed a bug where function heads would go over the line limit in the
formatter.
## v0.34.0-rc2 - 2023-01-11
### Bug fixes
- Fixed a bug where `gleam run` would fail when the current directory is not
the root of the project and using the JavaScript target.
- Fixed a bug where the compiler would in some cases fail to error when an
application uses functions that do not support the current compilation
target.
## v0.34.0-rc1 - 2024-01-07
### Language changes
- Warn about function body not being used, because it already has external
implementations for all targets.
- It's now possible to compile a project with external functions in dependency
packages that are not supported by the compilation target so long as they are
not used on the current target.
- The error message for when one imports a constructor instead of an homonymous
type has been improved.
### Language Server Changes
- Added a `View on HexDocs` link on function hover.
### Formatter
- Fixed some quirk with the formatting of binary operators.
- Fixed a bug where the formatter would move a function call's closed
parentheses on a new line instead of splitting the function's arguments.
- Now the formatter will format tuples as if they were functions, trying to
first split just the last element before splitting the whole tuple.
- Improved the formatting of multiline strings in string concatenation.
### Build tool changes
- The `gleam new` command now accepts any existing path, as long as there are
no conflicts with already existing files. Examples: `gleam new .`, `gleam new
..`, `gleam new ~/projects/test`.
- The format for the README created by `gleam new` has been altered.
- The `gleam.toml` created by `gleam new` now has a link to the full reference
for its available options.
- The `gleam` binary is now statically linked on Windows.
- New projects are created requiring between versions of v0.34.0 inclusive and
exclusive v2.0.0.
- The `repository` section now supports additional VCS types in the form of
codeberg, forgejo and gitea allowing a `user`, `repo` and additionally a
`host` url.
- TypeScript declaration for the prelude exports previously missing functions
and classes. Additionally, swaps interfaces for classes and adds missing
attributes to classes.
- `gleam` commands now look in parent directories for a `gleam.toml` file.
### Bug fixes
- Fixed a bug where `gleam add` would not update `manifest.toml` correctly.
- Fixed a bug where `fn() { Nil }()` could generate invalid JavaScript code.
- Fixed a bug where the build tool would make unnecessary calls to the Hex API
when path dependencies are used.
- Fixed a bug where `gleam new` would generate a gitignore with `build` rather
than `/build`.
- Fixed where the types of generic constants could be incorrectly inferred.
- `Utf8Codepoint` has been renamed to `UtfCodepoint` in `prelude.d.mts`.
- Fixed a bug where `gleam deps list` would look in filesystem root instead of
the current directory.
- Fixed a bug with the `isEqual` function in `prelude.js` where RegExps were
being incorrectly structurally compared and being falsely reported as being
equal.
- JavaScript: export from `prelude.d.mts` in `gleam.d.mts` to fix the error:
"Type 'Result' is not generic".
- Not providing a definition after some attributes is now a parse error.
## v0.33.0 - 2023-12-18
## v0.33.0-rc4 - 2023-12-17
- The deprecated bit array options `binary` and `bit_string` have been removed.
- The deprecated ambiguous type import syntax has been removed.
- The deprecated `BitString` type has been removed.
- The deprecated `inspect` functions and `BitString` type has been removed from
the JavaScript prelude.
## v0.33.0-rc3 - 2023-12-17
### Formatter
- The formatter now tries to split long chains of binary operations around the
operator itself, rather than around other elements like lists or function
calls.
### Bug fixes
- Fixed a bug where string prefix aliases defined in alternative case branches
would all be bound to the same constant.
## v0.33.0-rc2 - 2023-12-07
### Language changes
- The `\e` string escape sequence has been removed. Use `\u{001b}` instead.
- Generated Erlang now disabled redundant case clause warnings as these are now
redundant due to exhaustiveness checking.
### Bug fixes
- Fixed a bug where the `\u` string escape sequence would not work with
on Erlang on the right hand side of a string concatenation.
## v0.33.0-rc1 - 2023-12-06
### Formatter
- The formatter now tries to keep a function body and its arguments on a single
line by first trying to split only its last argument on multiple lines.
- Fixed a bug where the formatter would move comments out of blocks.
- `gleam format` now ignores the Gleam build directory by default, even when not
in a git repository.
### Language changes
- Gleam now has full exhaustiveness checking. Exhaustiveness issues have been
downgraded from errors to warnings so that existing Gleam code can be
upgraded to be exhaustive without breaking existing code. In a future version
they will be upgraded to errors.
- The `!` operator can now be used in clause guards.
- The words `auto`, `delegate`, `derive`, `else`, `implement`, `macro`, and
`test` are now reserved for future use. If used they will emit a warning. In
a future version this may be upgraded to an error.
- The `\u{...}` syntax can be used in strings to specify unicode codepoints via a
hexadecimal number with 1 to 6 digits.
- The `todo as` and `panic as` syntaxes now accept an expression that evaluates
to a string rather than just a string literal.
### Build tool changes
- The `gleam run` and `gleam test` commands gain the `-t` flag, which is an
alias of the `--target` flag.
- The `gleam build`, `gleam check`, `gleam run` and `gleam test` commands now
also accept `js` and `erl` as values for the `--target` flag.
- The `gleam new` command now creates packages at version 1.0.0.
- The `gleam publish` command now asks for confirmation if the package being
published is not yet version 1.0.0.
- The `gleam publish` command now asks for confirmation if the package name is
one that implies the package is maintained by the Gleam core team.
- The error messages shown when dependency resolution fails have been improved.
### Compiler WASM API
- The WASM API for the compiler has been rewritten to be simpler.
- The WASM API for the compiler now exposes warnings.
### HTML documentation generator
- Searching in rendered HTML documentation now also matches words that do not
start with the input but do contain it.
### Bug fixes
- Fixed a bug where the JavaScript code generator could generate invalid code
when pretty printing a zero arity function call when the line is over 80
columns wide.
- Fixed a bug where the build directory could be left in an invalid state if
there is Elixir code to compile and running on Windows without permission to
create symlinks.
- Fixed a bug where numbers with preceding zeros could generate incorrect
JavaScript.
- The Erlang code generated by the `/.` operator no longer generates a warning
for the upcoming negative zero float change in Erlang OTP 27.
- Fixed a bug where using only types from an aliased import, wouldn't stop the
compiler from emitting an unused alias warning for that import.
- Fixed a bug where the formatter would remove the ` as name` from string prefix
patterns.
- Fixed a bug where the formatter would misplace comments at the start of a
block.
- Fixed a bug where using a string prefix pattern in `let assert` would generate
incorrect JavaScript.
## v0.32.4 - 2023-11-09
### Build tool changes
- The build tool now supports rebar3 and mix hex packages where the package name
differs from the otp application name.
### bug fixes
- Fixed a bug where invalid javascript code could be generated when a module
function calls another function that was passed as an argument and the
argument has the same name as the module function.
- Fixed the `target` property of `gleam.toml` being ignored for local path
dependencies by `gleam run -m module/name`
## v0.32.3 - 2023-11-07
### Language changes
- Imported modules can now be discarded by giving them an alias starting with `_`.
### Build tool changes
- New projects are now generated with the call to `gleam format` coming last in
the GitHub Actions workflow. This is so that feedback from tests is presented
even if formatting is incorrect.
- Added Windows support for the `gleam export erlang-shipment` command.
### Bug fixes
- Fixed a bug where some nested pipelines could fail to type check.
## v0.32.2 - 2023-11-03
### Build tool changes
- New Gleam projects are created with `gleam_stdlib` v0.32 and `gleeunit` v1.0.
### Bug fixes
- Fixed a bug where `gleam fix` would not produce correct results for code that
shadowed a prelude name with an import of the same name but a different kind.
- Fixed a bug where documentation would not publish to Hexdocs for packages with
a number in the name.
- Fixed a bug where aliased unqualified types and values of the same name could
produce an incorrect error.
## v0.32.1 - 2023-11-02
### Bug fixes
- Fixed a bug where `gleam fix` would not produce correct results for code that
shadowed a prelude name with an import of the same name but a different kind.
- Fixed a bug where incorrect JavaScript could be generated due to backwards
compatibility with the deprecated import syntax.
## v0.32.0 - 2023-11-01
### Bug fixes
- Fixed a bug where running `gleam fix` multiple times could produce incorrect
results.
## v0.32.0-rc3 - 2023-10-26
### Bug fixes
- Fixed a bug where `gleam fix` would fail to update the deprecated type import
syntax for aliased unqualified types.
## v0.32.0-rc2 - 2023-10-26
### Bug fixes
- Fixed a bug where the backward compatibility for the deprecated import syntax
could result in an import error with some valid imports.
## v0.32.0-rc1 - 2023-10-25
### Language changes
- Using `import module.{TypeName}` to import a type has been deprecated,
replaced by `import module.{type TypeName}`. In a future version of Gleam the
old syntax will only import the value of the same name. Run `gleam fix` to
update your code.
- The `BitString` type has been renamed to `BitArray`. Run `gleam fix` to update
your code.
- The `binary` and `bit_string` bit array modifier have been deprecated in favour
of `bytes` and `bits`.
- The error message for when one element in a list doesn't match the others has
been improved.
- The error message for when the elements of a list's tail don't match the
previous ones has been improved.
- The error message for when one tries to access an unknown field has been
improved.
- The `__gleam_prelude_variant__` property has been removed from the classes
defined in the JavaScript prelude.
- The deprecated `todo("...")` syntax has been removed.
- Module access can now be used in case clause guards.
- The JS target now supports bit syntax for module constants.
- The Erlang compiler will no longer emit a duplicate warning for unused
functions.
- The `@deprecated` attribute can now be used with type definitions.
- A warning is now emitted if a module alias is unused.
### Language server changes
- The language server now has a code action for removing unused items.
- The language server now shows the type of variables defined using `use` on
hover.
### Build tool changes
- The `gleam check` command supports the `target` flag.
- The `gleam fix` command updates code to use `BitArray` rather than `BitString`.
- The `gleam fix` command updates code to use the new import type syntax.
- `gleam fix` sets the `gleam` version constraint in `gleam.toml` to `>= 0.32.0`.
- The `gleam` version constraint field in `gleam.toml` now disregards pre and
build components when checking for compatibility.
- The prelude is no longer rendered once per package when compiling to
JavaScript, instead one copy is rendered for the entire project. If you are
using the `gleam compile-package` API you now need to give a path to the
prelude using the `--javascript-prelude` flag.
- The `gleam export javascript-prelude` and `gleam export typescript-prelude`
commands have been added to export a copy of the prelude. This command may be
useful for build tools that use the compiler via the `gleam compile-package`
API.
- Fixed a bug where some deprecation messages would not be printed.
- The content has been made wider in rendered HTML documentation.
- Dependencies that can be built with both `mix` and `rebar3` are now built
with `mix` if it exists on the system, and with `rebar3` if it doesn't.
### Bug fixes
- "Compiling $package" is now only printed when a package has new changes to
compile.
- The main process started with `gleam run` no longer traps exits on Erlang.
- The formatting of code in rendered HTML documentation has been improved.
- The formatter no longer moves trailing comments out of custom type definitions.
- Fixed a bug where some hexadecimal numbers would generate incorrect Erlang.
- Fixed a bug where markdown tables would not render correctly in HTML
documentation.
- The float 0.0 is now rendered in Erlang as `+0.0` to silence warnings in
Erlang/OTP 27.
## v0.31.0 - 2023-09-25
- New Gleam projects are created with `gleam_stdlib` v0.31, `actions/checkout`
v3.\*, and `erlef/setup-beam` v1.\*.
- A note is included in the generated HTML documentation if a function is
deprecated.
## v0.31.0-rc1 - 2023-09-18
- The `@deprecated("...")` attribute can be used to mark a function as
deprecated. This will cause a warning to be emitted when the function is used.
- A warning is now emitted if a module from a transitive dependency is imported.
- Record access can now be used in case clause guards.
- Fixed a bug where `manifest.toml` could contain absolute paths for path
dependencies.
- The `as` keyword can now be used to assign the literal prefix to a variable
when pattern matching on a string.
- The `if` conditional compilation, `external fn`, and `external type` syntaxes
have been removed.
- The `description` flag for the `gleam new` command has been removed.
- The highlight.js grammar included with generated HTML documentation has been
updated for the latest syntax.
- Packages are no longer precompiled to Erlang when publishing to Hex if the
package target is set to JavaScript.
- An exception is now raised if JavaScript code uses the `BitString` class
constructor and passes in the incorrect argument type.
- Fixed a bug where mutually recursive functions could be incorrectly inferred
as having an overly general type.
- Fixed a bug where recursive type constructors could incorrectly infer a type
error.
- Fixed a bug where some mutually recursive functions would be inferred as
having too general a type.
- Fixed a bug where constants where not being correctly inlined when used in the
size option of a bit string pattern match.
- Fixed a bug where anonymous functions could parse successfully when missing a
body.
- Fixed a bug where incorrect unused variable warnings could be emitted for code
that doesn't type check.
- Fixed a bug where packages defaulting to the JavaScript target could have
modules missing from their HTML documentation when published.
- Corrected some outdated links in error messages.
- Hovering over a function definition will now display the function signature,
or the type of the hovered argument.
- Use `import type` for importing types from typescript declarations.
- Use `.d.mts` extension for typescript declarations to match `.mjs`.
- Prefix module names with dollar sign in typescript to avoid name collisions.
## v0.30.4 - 2023-07-26
- External implementations are always referenced directly in generated code, to
avoid the overhead of an extra function call.
- Fixed a bug where the compiler could infer incorrect generic type parameters
when analysing a module without type annotations with self recursive
functions that reference themselves multiple times.
## v0.30.3 - 2023-07-23
- Fixed a bug where JavaScript module path such as `node:fs` would be rejected.
- New Gleam projects are created with `gleam_stdlib` v0.30, Erlang OTP v26.0.2,
Elixir v1.15.4, actions/checkout v3.5.1, and erlef/setup-beam v1.16.0.
## v0.30.2 - 2023-07-20
- Fixed a bug where the compiler could infer incorrect generic type parameters
when analysing a module without type annotations with self recursive
functions.
- Fixed a bug where the formatter would incorrectly format external functions
by breaking the return annotation instead of the function arguments.
## v0.30.1 - 2023-07-13
- Fixed a bug where the language server could fail to import path dependencies
in monorepos.
## v0.30.0 - 2023-07-12
- A warning is now emitted for the deprecated external fn syntax.
## v0.30.0-rc4 - 2023-07-10
- An error is now emitted for invalid JavaScript external implementations.
- Fixed a bug where Erlang external implementations could generate invalid code.
## v0.30.0-rc3 - 2023-07-05
- Fixed a bug where `gleam fix` would fail to parse command line flags.
## v0.30.0-rc2 - 2023-07-03
- Fixed a bug where `gleam fix` would merge external functions of the same name
but incompatible types.
- Fixed a bug where external function arguments would incorrectly be marked as
unused.
## v0.30.0-rc1 - 2023-06-29
- The new `@target(erlang)` and `@target(javascript)` attribute syntax has been
added for conditional compilation. The existing `if` conditional compilation
syntax has been deprecated. Run `gleam fix` to update your code.
- The new `type TypeName` syntax syntax replaces the `external type TypeName`
syntax. The existing external type syntax has been deprecated. Run `gleam format`
to update your code.
- Adding a new dependency now unlocks the target package. This helps avoid
failing to find a suitable version for the package due to already being
locked.
- A custom message can now be specified for `panic` with `panic as "..."`.
- The syntax for specifying a custom message for `todo` is now `todo as "..."`.
- The Erlang error raised by `let assert` is now tagged `let_assert`.
- Types named `Dynamic` are now called `dynamic_` in Erlang to avoid a clash
with the new Erlang `dynamic` type introduced in OTP26.
- Dependencies can now be loaded from paths using the
`packagename = { path = "..." }` syntax in `gleam.toml`.
- The `javascript.deno.unstable` field in `gleam.toml` can now be used to
enable Deno's unstable APIs when targeting JavaScript.
- Blockquotes are now styled in rendered HTML documentation.
- The `gleam` property can be set in `gleam.toml` can be set to a version
requirement to specify the version of Gleam required to build the project.
- Type aliases can now refer to type aliases defined later in the same module.
- Fixed a bug where unapplied record constructors in constant expressions would
generate invalid Erlang.
- Fixed a bug where the prescedence of `<>` and `|>` would clash.
- Fixed a bug where `gleam docs build` would print an incorrect path upon
completion.
- Warnings from dependency packages are no longer surfaced in the language
server.
- A warning is now emitted when a Gleam file is found with an invalid name.
- A warning is now emitted when using `list.length` to check for the empty list,
which is slow compared to checking for equality or pattern matching (#2180).
- The new `gleam remove ` can be used to remove dependencies
from a Gleam project.
- Fixed a bug where the formatter could crash.
- Fixed a bug where invalid Erlang would be generated when piping into `panic`.
- The `gleam docs build` command gains the `--open` flag to open the docs after
they are generated (#2188).
- Fixed a bug where type annotations for constants could not be written with
type annotations.
- Updated font loading in generated HTML documentation to fix an issue with
fonts not loading properly in some browsers (#2209).
## v0.29.0 - 2023-05-23
- New projects now require `gleam_stdlib` v0.29.
## v0.29.0-rc2 - 2023-05-22
- The `gleam lsp` command is no longer hidden from the help output.
- Fixed a bug where some language server clients would show autocompletion
suggestions too eagerly.
## v0.29.0-rc1 - 2023-05-16
- The language server will now provide autocomplete suggestions for types and
values either imported or defined at the top level of the current module.
- Fixed a bug where record patterns using the spread operator (`..`) to discard
unwanted arguments would not type check correctly when the record had no
labelled fields.
- Add support for using sized binary segments in pattern matches when targeting
JavaScript.
- A warning is now emitted for double unary negation on ints (`--`) and bools
(`!!`) as this does nothing but return the original value.
- Previously the build tool would discard the entire build directory when dependencies
were changed. Now it will only discard the build artefacts for removed
dependencies.
- The errors emitted when a name is reused in a module have been made clearer.
- Fixed an incorrect URL in the error message for failing to parse a let binding
with a type annotation.
- Fixed a bug where shadowing a prelude type name could result in incorrect
errors in exhaustiveness checking.
- Fixed a bug where the language server would in some scenarios not remove an
error diagnostic after it becomes outdated.
- Fixed a bug where the formatter would incorrectly format blocks with a comment
before them that were the only argument to a function call.
- Fixed a bug where the language server would not reset the build directory when
it was created by a different version of Gleam.
- New Gleam projects are created with `erlef/setup-beam@v1.15.4` in their GitHub
actions CI configuration.
- Running a module now uses the dependency's target and runtime in its `gleam.toml`.
## v0.28.3 - 2023-04-17
- Fixed a bug where the language server would show outdated error diagnostics
when a new one was emitted in a different module.
- Fixed a bug where the language server would attempt to analyse Gleam modules
that were outside of the `src` or `test` directories.
- New Gleam projects are created with `actions/checkout@v3.5.1` and
`erlef/setup-beam@1.15.3` in their GitHub actions CI configuration.
## v0.28.2 - 2023-04-10
- Fixed a bug where comments above a `use` expression would be formatted
incorrectly.
- Fixed a bug where the formatter would fail to preserve empty lines after a
block.
- Fixed a bug where the formatter would fail to preserve empty lines after an
anonymous function with a return annotation.
## v0.28.1 - 2023-04-05
- Fixed a bug where the language server would unset too many error diagnostics
when multiple projects are open, more than one have errors, and one of them is
successfully compiled.
- Fixed a bug where the language server would unset error diagnostics when
displaying information on hover.
- Added support for type annotations in use statements.
## v0.28.0 - 2023-04-03
- New projects now require `gleam_stdlib` v0.28.
## v0.28.0-rc3 - 2023-03-31
- Fixed a bug where source links would be incorrect in HTML documentation.
## v0.28.0-rc2 - 2023-03-27
- Fixed a bug where single statement blocks inside binary operators could
generate invalid JavaScript.
- Fixed a bug where the formatter could incorrectly place comments.
- Fixed a bug where the language server would show outdated diagnostics when a
file with an error reverts to the previous valid version, causing the compiler
to use the cached version of the file.
## v0.28.0-rc1 - 2023-03-26
- The language server now analyzes files on edit rather than on save, providing
feedback faster.
- The language server now supports editor sessions that span multiple projects.
This is useful for mono-repos and projects with both a frontend and backend in
Gleam.
- The language server now also shows documentation on hover for expressions.
- The language server now shows types and documentation on hover for patterns.
- Added support for negation of integers with the new `-` unary operator.
- Variable assignments are now only permitted within a function or a block, not
anywhere that an expression is permitted.
- The deprecated `try` expression has been removed.
- The deprecated `assert ... = ...` syntax has been removed.
- Semicolons are no longer whitespace. An error will be emitted if one is
encountered.
- Warnings are now immediately emitted rather than being buffered until the end
of the compilation.
- The `--warnings-as-errors` flag is now supported by `gleam build`.
- Blocks are now preserved by the formatter when they only have a single
expression within them.
- Generated docs now export more meta data to improve the developer experience,
accessibility and search engine discoverability.
- Files are now only recompiled if they have changed since the last compilation,
detected by file hash and modification time. Previously only the modification
time was used.
- Autocompletion of module imports was removed due to a buggy implementation.
- Fixed a bug where the formatter would incorrectly remove `{ ... }` from bit
string segment value expressions.
- Fixed a bug where TypeScript type definitions files could include incorrect
type names.
- Fixed a bug where the compiler used VSCode specific behaviour in the language
server which was incompatible with Helix.
- Fixed a bug where string concatenation patterns on strings with escape
characters would generate javascript code with wrong slice index.
- Fixed a bug where blocks could parse incorrectly.
- Allow modules to be run with the `gleam run --module` command.
## v0.27.0 - 2023-03-01
- Fixed a bug where `panic` could generate incorrect JavaScript code.
- New projects now require `gleam_stdlib` v0.27.
## v0.27.0-rc1 - 2023-02-26
- The new `panic` keyword can be used to crash the program. This may be useful
for situations in which a program has got into an unrecoverable invalid state.
- `try` expressions are now deprecated and will be removed in a future version.
- The new `gleam fix` command can be used to automatically convert `try`
expressions to `use` expressions.
- `let assert ... = ...` is now the syntax for assertion assignments. The
`assert ... = ...` syntax is deprecated and will be removed in a future
version. Run `gleam format` to automatically update your code.
- `gleam export hex-tarball` can be used to create a tarball suitable for
uploading to a Hex compatible package repository.
- The unused private type and constructor detection has been improved.
- The argument `--runtime` now accepts `nodejs` as the name for that runtime.
The previous name `node` is still accepted.
- Patterns can now be used in `use` expressions.
- Fixed a bug where string concatenation patterns could generate javascript
code with wrong slice index due to ut8/ut16 length mismatch.
- The Erlang compiler will no longer emit a duplicate warning for unused
variables.
- Fixed a bug where typescript type definitions for types with unlabelled
arguments where generated with an invalid identifier and unlabelled fields
were generated with a name that didn't match the javascript implementation.
- Fixed a bug in the type inferrer were unannotated functions that were
used before they were defined in a module could in rare cased be inferred
with a more general type than is correct.
- Fixed a bug where the LSP would fail to show type information on hover for
expressions after a use expression.
- Fixed a bug where imported constants could generated incorrect JavaScript
code.
- Fixed a bug where the LSP would perform codegen for dependencies.
- Fixed a bug where the LSP would compile native dependencies needlessly.
- Fixed a bug where integer division with large numbers on JavaScript could
produce incorrect results.
- Fixed a bug where pattern matches on custom types with mixed labelled and
unlabelled arguments could not be compiled when targeting JavaScript.
- Fixed a bug where local variables in case guard constant expressions caused
the compiler to panic.
- The formatter now truncates meaningless zeroes of floats' fractional parts.
- Anonymous functions may now have an empty body. The compiler will emit a
warning for functions without a body, and these functions will crash at
runtime if executed.
- Fixed bug where raised errors on JS would have an extra stack frame recorded
in them.
## v0.26.2 - 2023-02-03
- The formatter now wraps long `|` patterns in case clauses over multiple lines.
- Fixed a bug where unlabelled function arguments could be declared after
labelled ones.
- A broken link was removed from the error messages.
- Fixed a bug where using a qualified imported record constructor function as a
value would produce invalid Erlang code if the name of the record variant was
an Erlang reserved word.
## v0.26.1 - 2023-01-22
- New projects now require `gleeunit` v0.10.
- Rebar3 dependency projects are now compiled in-place. This fixes an issue
where some NIF using projects would fail to boot due to some paths not being
copied to the `build` directory.
- An error is now emitted if a list spread expression is written without a tail
value.
- An error is now emitted when a function is defined with multiple arguments
with the same name.
- The error message emitted when a `let` does not match all possible values has
been improved.
- Fixed a bug where the language server wouldn't analyse test code.
- Fixed a bug where `assert` expressions can generate invalid Erlang.
warning.
- Fixed a bug where arguments would be passed incorrectly to Deno.
- Fixed a bug where defining variables that shadow external functions could
generate invalid JavaScript.
## v0.26.0 - 2023-01-19
[Release blog post](https://gleam.run/news/v0.26-incremental-compilation-and-deno/)
- New projects require `gleam_stdlib` v0.26 and `gleeunit` v0.9.
- Fixed a bug where JavaScript default projects would fail to publish to Hex.
## v0.26.0-rc1 - 2023-01-12
- Added support for Deno runtime for JavaScript target.
- Scientific notation is now available for float literals.
- The compiler now supports incremental compilation at the module level. If a
module or its dependencies have not been changed then it will not be
recompiled.
- The format used by the formatter has been improved.
- 4 digit integers are now always formatted without underscores.
- Running `gleam new` will skip `git init` if the new project directory is
already part of a git work tree.
- Generated HTML documentation now includes all static assets, including web
fonts, so that it can be accessed offline and in future once CDNs would 404.
- Generated HTML documentation now supports TypeScript syntax highlighting.
- New Gleam projects are created using GitHub actions erlef/setup-beam@v1.15.2.
- Some modules can now be hidden from the docs by specifying a list of glob
patterns in `internal_modules` in `gleam.toml`. The default value for this
list is `["$package_name/internal", "$package_name/internal/*"]`.
- The `gleam new` command gains the `--skip-git` flag to skip creation of
`.git/*`, `.gitignore` and `.github/*` files.
- The `gleam new` command gains the `--skip-github` flag to skip creation of
`.github/*` files.
- Fixed a bug where no error would be emitted if a `src` module imported a
`test` module.
- Fixed a bug where comments in list prepending expressions could be formatted
incorrectly.
- Fixed a bug where comments in record update expressions could be formatted
incorrectly.
- Fixed a bug where long `use` expressions could be formatted incorrectly.
- Fixed a bug integer multiplication would overflow large integers when
compiling to JavaScript.
- Fixed `int` and `float` formatting in `const`s and patterns.
- Fixed a bug where piping into a function capture expression with a pipe as one
of the arguments would produce invalid Erlang code.
- Formatter no longer removes new lines in expression blocks within case branches
## v0.25.3 - 2022-12-16
- 4 digit integers are no longer automatically formatted with underscores.
## v0.25.2 - 2022-12-16
- Updated `actions/checkout` from `actions/checkout@v3.0.0` to `@v3.2.0` for
projects created via `gleam new`.
- Fixed a bug where `gleam new` would set a `Rebar3` version to `25.1`
instead of the latest stable `3`.
- Updated following runtime versions set via `gleam new`: `Erlang/OTP`
to `25.2`, and `Elixir` to `1.14.2`.
- The formatter now inserts underscores into larger `Int`s and the larger
integer parts of `Float`s.
- Added support for top level TypeScript file inclusion in builds.
- The build tool will now favour using rebar3 over Mix for packages that support
both. This fixes an issue where some packages could not be compiled without
Elixir installed even though it is not strictly required.
## v0.25.1 - 2022-12-11
- New Gleam projects are now configured to explicitly install rebar3 using
GitHub actions erlef/setup-beam.
- A better error message is now shown when attempting to use a function within a
constant expression.
- Changed float size limit in bit string expressions to 16, 32 or 64, when static.
Also allowed dynamic size.
- New Gleam projects are created using GitHub actions erlef/setup-beam@v1.15.0.
- Fixed a bug where returning an anonymous function from a pipeline and calling
it immediately without assigning it to a variable would produce invalid Erlang
code.
- Fixed a bug where the formatter would remove the braces from negating boolean
expressions.
## v0.25.0 - 2022-11-24
[Release blog post](https://gleam.run/news/v0.25-introducing-use-expressions/)
## v0.25.0-rc2 - 2022-11-23
- Fixed a bug where Gleam dependency packages with a `priv` directory could fail
to build.
- Fixed a regression where Elixir and Erlang Markdown code blocks in generated
documentation would not be highlighted.
## v0.25.0-rc1 - 2022-11-19
- Generated HTML documentation now includes the `theme-color` HTML meta tag.
- The `use` expression has been introduced. This is a new syntactic sugar that
permits callback using code to be written without indentation.
- Nightly builds are now also published as OCI container images hosted on
GitHub.
- Fixed a bug where the build tool would not hook up stdin for Gleam programs it
starts.
- Fixed a bug where using a record constructor as a value could generate a
warning in Erlang.
- Fixed a bug where the build tool would use precompiled code from Hex packages
rather than the latest version, which could result in incorrect external
function usage in some cases.
- Fixed a bug where the warning for `todo` would not print the type of the code
to complete.
- Fixed a bug where `try` expressions inside blocks could generate incorrect
JavaScript.
- Generated HTML documentation now includes all static assets (but the web
fonts), so that it can be accessed offline or in far future once CDNs would 404.
- New Gleam projects are created using GitHub actions erlef/setup-beam@v1.14.0
- The `javascript.typescript_declarations` field in `gleam.toml` now applies to
the entire project rather than just the top level package.
- The formatter now adds a `0` to floats ending with `.` (ie `1.` => `1.0`).
- New projects require `gleam_stdlib` v0.25.
## 0.24.0 - 2022-10-25
[Release blog post](https://gleam.run/news/gleam-v0.24-released/)
## 0.24.0-rc4 - 2022-10-23
- Fixed a bug where the string concatenate operator could produce invalid Erlang
code when working with pipe expressions.
## 0.24.0-rc3 - 2022-10-20
- Fixed a bug where the OOP method call error hint would be shown on too many
errors.
- Fixed a bug where the string concatenate operator could produce invalid Erlang
code when working with constant values.
## 0.24.0-rc2 - 2022-10-18
- Fixed a bug where imported and qualified record constructors used in constant
expressions could fail to resolve.
## 0.24.0-rc1 - 2022-10-15
- Gleam can now compile Elixir files within a project's `src` directory.
- The `<>` operator can now be used for string concatenation and for string
prefix pattern matching.
- Fixed a bug where TypeScript definitions may have incorrect type parameters.
- New projects depend on `gleam_stdlib` v0.24.
- New projects' GitHub Actions config specifies Erlang/OTP 25.1 and suggest
Elixir 1.14.1.
- If you attempt to use the method call syntax (`thing.method()`) on a value
without that field the error message will now include a hint explaining that
Gleam is not object oriented and does not have methods.
- Fixed a bug in the formatter where multiple line documentation comments for
custom type constructor fields could be formatted incorrectly.
- Fixed a bug where tail call optimisation could be incorrectly applied when
compiling to JavaScript in some situations.
- Fixed a bug where the remainder operator would return NaN results when the
right hand side was zero when compiling to JavaScript.
- Fixed a bug where Elixir dependencies would fail to compile on Windows.
- Fixed a bug where images added to HTML documentation via documentation
comments would not have a max width.
## v0.23.0 - 2022-09-15
[Release Blog Post](https://gleam.run/news/gleam-v0.23-released/)
## v0.23.0-rc2 - 2022-09-15
- New Gleam projects are created using GitHub actions erlef/setup-beam@v1.13.0
and actions/checkout@v3.0.0.
- New Gleam projects are created using version v0.23.0 of the stdlib.
- Fixed a bug where LSP hovering would fail to locate the expression.
## v0.23.0-rc1 - 2022-09-01
- Gleam can now build dependency packages that are managed using Mix.
- Compiler performance has been improved by buffering disc writing and by lazily
loading TLS certs. In testing this doubles performance when compiling the
standard library.
- The `gleam publish` command now adds the `priv` directory and any `NOTICE`
file to the tarball.
- The `gleam update` command can now be used to update dependency packages to
their latest versions.
- Module functions with empty bodies are no longer syntax errors.
- The format used by the formatter has been improved.
- OpenSSL swapped out for RustTLS.
- Generated HTML documentation now includes a search bar.
- The LSP will now provide autocompletion for imports.
- A helpful error message is now returned when assignments are missing either a
keyword or a value.
- Qualifiers are now used when multiple types have the same name in an error
message.
- In JavaScript, if an object has defined an `equals` method in its prototype,
Gleam will now use this method when checking for equality.
- Functions can now be defined and referenced in constant expressions.
- An error is now raised if the record update syntax is used with a custom type
that has multiple constructors.
- An error is now raised if a module is imported multiple times.
- Fixed a bug where defining a type named `CustomeType` would product invalid
JavaScript.
- Fixed a bug where defining a variable with the same name as an unqualified
import would produce invalid JavaScript.
- Fixed a bug where piping to `todo` would generate invalid Erlang code.
- Fixed a bug where inspecting a JavaScript object with a null prototype would
crash.
- Fixed a bug where the formatter could crash if source code contained 3 or more
empty lines in a row.
- Fixed a bug where the formatter would remove braces from blocks used as the
subject of a case expression.
- Fixed a bug alternative patterns with a clause containing a pipe with a pipe
after the case expression could render incorrect Erlang.
- Fixed a bug where formatter would strip curly braces around case guards even
when they are required to specify boolean precedence.
- Fixed a bug where `gleam new` would in some situations not validate the
target directory correctly.
- Fixed a bug where pipes inside record update subjects could generate invalid
Erlang.
- Fixed a bug where pipes inside record access could generate invalid Erlang.
## v0.22.1 - 2022-06-27
- The `gleam publish` confirmation prompt now accepts both "Y" and "y".
- Fixed a bug where `todo` would not emit the correct line number to the LSP while.
## v0.22.0 - 2022-06-12
[Release Blog Post](https://gleam.run/news/gleam-v0.22-released/)
- New projects are created with `gleam_stdlib` v0.22.
## v0.22.0-rc1 - 2022-06-12
- Fixed a bug where doc comments would dissociate from their statements when
generating html documentation.
- You are now allowed to use named accessors on types with multiple constructors if the
accessor's name, position and type match (among the constructors) (#1610).
- Added the ability to replace a release up to one hour after it is published
using `gleam publish --replace`.
- `gleam publish`, `gleam docs publish`, `gleam docs remove`, `gleam hex retire`,
and `gleam hex unretire` now have access to environment variables for
username (default key `HEXPM_USER`) and password (default key `HEXPM_PASS`)
- The `gleam publish` command gains the `-y/--yes` flag to disable the "are you
sure" prompt.
- Clear outdated files from the build directory after compilation.
- Fixed a bug where immediately calling the value that a case expression
evaluates to could generate invalid JavaScript.
- Fixed a bug where the default project target is set to JavaScript,
but the project would run on target Erlang instead.
- The compiler is now able to generate TypeScript declaration files on target
JavaScript (#1563). To enable this edit `gleam.toml` like so:
```toml
[javascript]
typescript_declarations = true
```
- Fixed a bug where argument labels were allowed for anonymous functions.
- Fixed a bug where JavaScript code could be invalid if a variable is defined
inside an anonymous function with a parameter with the same name as the
variable.
- Fixed a bug where importing a JavaScript function named "then" could produce
invalid code.
- Fixed a bug where constants that reference locally defined custom types could
render invalid JavaScript.
- The project generator will no longer permit use of the reserved `gleam_`
prefix.
- Generated HTML docs easter egg updated.
- `gleam export erlang-shipment` can be used to create a directory of compiled
Erlang bytecode that can be used as a deployment artefact to get your
application live.
- `gleam format` will now preserve (up to one) empty lines between consecutive
comments, as well as between comments and any following expression
- The deprecated rebar3 integration has been removed.
- Fixed a bug where `gleam format` would output an unwanted newline at the top
of documents that only contain simple `//` comments.
- No longer add `dev_dependencies` to generated `.app` Erlang files unless
we're compiling the root project (#1569).
- Fixed a bug where the formatter could render a syntax error with lists on long
unbreakable lines.
- Fixed a bug where JavaScript variable names could be incorrectly reused.
- Fixed a bug where `gleam format` would remove the braces around a tuple index
access when accessing a field of the returned element.
- Fixed a bug case clause guards could render incorrect JavaScript if a variable
name was rebinded in the clause body.
- The `gleam compile-package` command no longer generates a `.app` file. This
should now be done by the build tool that calls this command as it is
responsible for handling dependencies.
- Fixed a bug where piping a list tail would create invalid Erlang code (#1656).
## v0.21.0 - 2022-04-24
[Release Blog Post](https://gleam.run/news/v0.21-introducing-the-gleam-language-server/)
- New projects are created with `gleam_stdlib` v0.21.
## v0.21.0-rc2 - 2022-04-20
- Added the ability to replace a release up to one hour after it is published
using `gleam publish --replace`.
- The language server will now enter a degraded mode that only performs
formatting if running in a directory that is not a Gleam project with a
`gleam.toml`.
## v0.21.0-rc1 - 2022-04-16
- The Gleam language server is here! This will provide IDE like features for
code editors that support LSP, including but not limited to VSCode, Neovim,
Emacs, Eclipse, Visual Studio, and Atom. This first version includes these
features:
- Project compilation.
- Inline errors and warnings.
- Type information on hover.
- Go-to definition.
- Code formatting.
- Fixed a bug in generated JavaScript code where functions named `then` would
cause errors when dynamically imported.
- Initialize `git` repo when creating a new project.
- Log messages controlled with `GLEAM_LOG` now print to standard error.
- Log message colours can be disabled by setting the `GLEAM_LOG_NOCOLOUR`
environment variable.
- You can now specify multiple packages when using `gleam add`.
- Bools can now be negated with the `!` unary operator.
- If the compiler version changes we now rebuild the project from scratch on
next build command to avoid issues arising from reading metadata in an old
format (#1547).
- Updated the "Unknown label" error message to match other error messages
(#1548).
- Type holes are now permitted in function arguments and return annotations
(#1519).
- Unused module imports now emit a warning (#1553).
- The error message for failing to parse a multiline clauses without curly
braces has been improved with a hint on how to fix the issue (#1555).
- The error messages for when rebar3 or Erlang are missing from the machine has
been improved with a tip on how to install them (#1567).
- Corrected the hint given with certain int and float binary operator type
errors.
- Add support for `int` and `float` bit string type when compiling to JavaScript.
- Add support for specifying size of integers in a bit string. Supports only exact binaries,
i.e. length is a multiple of 8.
- Fixed compilation of rebar3 based dependencies on Windows.
## v0.20.1 - 2022-02-24
- The type checker has been improved to enable use of the record access syntax
(`record.field`) in anonymous functions passed into higher order functions
without additional annotations.
## v0.20.0 - 2022-02-23
[Release Blog Post](https://gleam.run/news/gleam-v0.20-released/)
- New projects are created with `gleam_stdlib` v0.20.
## v0.20.0-rc1 - 2022-02-20
- Type unification errors involving user annotated types now refer to the names
specified by the user instead of internal rigid-type ids.
- The build tool now validates that listed licenses are valid SPDX expressions.
- A WebAssembly version of the compile is now available for use in JavaScript
and other WebAssembly environments.
- New projects include Hex badges and a link to Hexdocs.
- Enhance type mismatch errors in the presence of try.
- Enhance type mismatch error for an inconsistent try.
- Enhance type mismatch error for pipe expressions to show the whole pipeline
and not only its first line.
- Fixed a bug where sometimes type variable could be reused result in incorrect
non-deterministic type errors.
- Built in support for the Mix build tool has been removed. The `mix_gleam`
plugin is to be used instead.
- Introduce a limited form of exhaustiveness checking for pattern matching
of custom types, which only checks that all constructor tags are covered
at the top level of patterns.
- The `ebin` directory is now copied to the build directory for rebar3 managed
dependencies if present before compilation.
- The format used by the formatter has been improved.
- Package names in `gleam.toml` are validated when the config is read.
- The `priv` directory is linked into the build directory for Gleam projects
managed by the build tool.
- Fixed a bug where type errors from pipes could show incorrect information.
- Fixed a bug where types could not be imported if they had the same name as a
value in the prelude.
## v0.19.0 - 2022-01-12
Dedicated to the memory of Muhammad Shaheer, a good and caring man.
[Release Blog Post](https://gleam.run/news/gleam-v0.19-released/)
## v0.19.0-rc4 - 2022-01-10
- New projects are created with `gleam_stdlib` v0.19 and `gleeunit` v0.6.
- Fixed a bug where external functions could be specified with the wrong module
name in generated Erlang when imported from a nested module in another
package.
- Fixed a bug where warnings wouldn't get printed.
## v0.19.0-rc3 - 2022-01-07
- Fixed a bug where precompiled packages would fail to compile due to Erlang
files being compiled twice concurrently.
## v0.19.0-rc2 - 2022-01-06
- Erlang modules are now compiled in a multi-core fashion.
- New projects are created with `erlef/setup-beam` v1.9.0 instead of
`gleam-lang/setup-erlang` and `gleam-lang/setup-gleam`.
- Fixed a bug where tail call optimisation could generate incorrect code when
the function has argument names that are JavaScript keywords.
- Fixed a bug where the build would continue when dependency packages failed to
compile.
- Fixed a bug where `include` directories would not be accessible by the Erlang
compiler during Gleam compilation.
## v0.19.0-rc1 - 2022-01-03
- The build tool now supports the JavaScript target. The target can be specified
in either `gleam.toml` or using the `--target` flag.
- The `gleam check` command has been introduced for rapidly verifying the types
of Gleam code without performing codegen.
- `true` and `false` can no longer be used as pattern matching variables, to
avoid accidental uses of incorrect syntax that is popular in other languages.
An error will hint about using Gleam's `True` and `False` values instead.
- You can now remove build artifacts using the new `gleam clean` command.
- The `compile-package` can now generate `package.app` files and compile source
modules to `.beam` bytecode files.
- The flags that `compile-package` accepts have changed.
- Published Hex packages now include precompiled Erlang files.
- Erlang record headers are now written to the `include` directory within the
package build directory.
- The format used by the formatter has been improved.
- Fixed a bug where tail recursion could sometimes generated incorrect
JavaScript code.
- Performance of code generators has been slightly improved.
- Allow the record in a record expansion to be an expression that returns a
record.
- Fixed a bug where external function module names would not be escaped
correctly if they contained special characters and were assigned to a
variable.
- A helpful error message is shown if Erlang is not installed.
## v0.18.2 - 2021-12-12
- Erlang applications are now automatically started when the VM is started by
`gleam run` and `gleam test`.
## v0.18.1 - 2021-12-12
- Fixed a bug where pipe expressions in record updates and operator expressions
could generate incorrect Erlang code.
- The `priv` directory is now copied to the output directory for rebar3 packages
prior to compilation. This is required for some packages to compile.
- Fixed a bug where deps that fail to compile would be skipped when compilation
would next be attempted, resulting the project being in an invalid state.
## v0.18.0 - 2021-12-06
[Release Blog Post](https://gleam.run/news/gleam-v0.18-released/)
- New projects now include `gleeunit`.
## v0.18.0-rc3 - 2021-12-05
- URL format in gleam.toml is now validated.
- The `gleam deps list` command has been added.
- Fixed a bug where changing requirements in `gleam.toml` would not cause deps
to be re-resolved.
- Fixed a bug where locked deps would cause incompatible package requirements to
be discarded.
- Development dependencies are now included in the applications listed in the
generated OTP `.app` file.
- `gleam.toml` now includes an `erlang.extra_applications` key to specify extra
OTP applications that need to be started.
## v0.18.0-rc2 - 2021-11-26
- Fixed a bug where OTP .app files would be generated with invalid syntax.
- Removed extra whitespace from newly generated projects.
## v0.18.0-rc1 - 2021-11-25
- Gleam can now compile Gleam projects.
- Gleam can now run tests with the `gleam eunit` command.
- Gleam can now run programs with the `gleam run` command.
- Gleam can now run an Erlang shell with the `gleam shell` command.
- Gleam can now resolve package versions for a Gleam project's dependency tree.
- Gleam can now download Hex packages.
- Gleam can now build dependency packages that are managed using Gleam or
rebar3.
- Gleam is now the default build tool for new projects.
- The template names for `gleam new` have been changed.
- Fixed a bug where the error message for a record update with an unknown field
would point to all the fields rather than the unknown one.
- Improved styling for inline code in generated documentation.
- New projects use v0.18 of the stdlib.
## v0.17.0 - 2021-09-20
[Release Blog Post](https://gleam.run/news/gleam-v0.17-released/)
- Functions now get special handling when being printed from JavaScript.
## v0.17.0-rc2 - 2021-09-19
- Errors thrown when no case clause or assignment pattern matches the subject
value now include more debugging information when targeting JavaScript.
- New projects are generated using `gleam_stdlib` v0.17.1.
## v0.17.0-rc1 - 2021-09-11
- Redesigned the Gleam prelude to be a module of core classes when compiling to
JavaScript. This improves the resulting generated code and makes debugging and
interop easier.
- Projects without rebar3 can be generated using the `gleam-lib` template.
- JavaScript modules are imported using a camel case variable name to avoid name
collisions with variables.
- Pipelines now use assignments in the generated code in order to preserve the
order of any side effects.
- Fixed a bug where the compiler would crash rather than raise an error if a
project contained a single module and attempted to import another.
- Special variable naming has been made more consistent in rendered Erlang and
JavaScript.
- Conditional compilation can now be used to have different code within a module
when compiling to a specific target.
- Fixed a bug where `todo` caused values not to be returned in JavaScript.
- Fixed a bug where multiple discarded function arguments generated invalid
JavaScript.
- Fixed a bug where using JavaScript reserved words as function argument names
caused generated invalid JavaScript.
- Fixed a bug where a case expression of just a catch-all pattern generated
invalid JavaScript.
- Fixed a bug where the formatter would incorrectly render extra newlines below
try expressions.
- Fixed a bug where tail recursive functions with arguments with the same name
as JavaScript reserved words generated the wrong JavaScript.
- Fixed a bug where list equality would be incorrectly reported in JavaScript.
- Multiple subjects are now supported for case expressions in JavaScript.
- Fixed a bug where matching using a Bool or Nil literal as the subject for a
case expression would produce invalid code when compiling to JavaScript.
- Unsupported feature error messages now include file path and line numbers for
debugging.
- Bit string literals with no segment options or just the `bit_string`, `utf8`
or `utf8_codepoint` options can be constructed when compiling to JavaScript.
- The format of generated JavaScript has been improved.
- Fixed a bug where rendered JavaScript incorrectly incremented variables when
reassigned in patterns.
- Added `eval` and `arguments` to JavaScript reserved words.
- Support for the deprecated `tuple(x, y, ...)` syntax has been removed in favor
of the more concise (`#(x, y, ...)`). Use `gleam format` with the previous
version of the compiler to auto-migrate.
- New OTP projects are generated using `gleam_otp` v0.1.6.
- Fixed a bug where the equality operators could return the incorrect value for
records when compiling to JavaScript.
- Fixed a bug where `todo` could sometimes render invalid JavaScript when used
as an expression in the generated code.
- An error is now emitted if the list spread syntax is used with no prepended
elements `[..xs]`.
- Fixed a bug where type errors inside piped expressions would be incorrectly be
reported as being an incorrect usage of the pipe operator.
- Gleam modules with no public exports no longer render private members in
Erlang.
- Fixed a bug where discard variables used in assert assignments would generate
invalid Erlang code.
- Fixed a bug where some expressions as case subjects would generate invalid
JavaScript code.
- Fixed a bug where some assignments as the final expression in a function would
not return the correct value in JavaScript.
- Gleam packages imported in JavaScript now have the path prefix
`gleam-packages`. This can be served from your web server or aliased in your
`package.json` for NodeJS projects.
- Fixed a bug where the type checker would fail to generalise some type
variables, causing module metadata writing to fail.
- Fixed a bug where tail call optimisation when compiling to JavaScript could
result in incorrect code.
- Fixed a bug where variable names could be rendered incorrectly in closures.
- An error is now emitted if alternative patterns fail to define all the
variables defined by the first pattern.
- New projects are generated using `gleam_stdlib` v0.17.0.
- New projects are generated using `gleam_otp` v0.2.0.
## v0.16.1 - 2021-06-21
- Values which are being imported more than once in an unqualified fashion now
cause an error to be reported.
- Argument docs for custom type constructors are now rendered in the HTML
documentation.
- Patterns can be used with `try` expressions when compiling to JavaScript.
- Types and record constructors can now be aliased with an uppercase name when
imported. Aliasing them with a lowercase name is no longer permitted.
- Fixed a bug where nested import paths could be rendered incorrectly in
JavaScript.
## v0.16.0 - 2021-06-17
[Release Blog Post](https://gleam.run/news/gleam-v0.16-released/)
## v0.16.0-rc4 - 2021-06-17
- Fixed a bug where if a JavaScript global function was imported as an external
function with the same name the generated code would diverge.
## v0.16.0-rc3 - 2021-06-17
- New projects are generated using `gleam_stdlib` v0.16.0.
## v0.16.0-rc2 - 2021-06-08
- Gleam now supports alternative patterns in case expressions for the JavaScript target.
- The `gleam` prelude module can now be imported when compiling to JavaScript.
- Fixed a bug where the prelude module could not be imported when using the old
build compiler API.
- Fixed a bug where if a JavaScript global function was imported as an external
function with the same name the generated code would diverge.
- Type error messages coming from pipe usage have been improved.
## v0.16.0-rc1 - 2021-06-04
- Gleam can now compile to JavaScript! Specify the `--target javascript` flag to
`gleam compile-package` to use it today.
- A compile time error is now raised when multiple module level constants with
the same name are defined.
- Fixed a bug where declaring a type constructor using reserved erlang keyword
in its fields results in invalid erlang code being generated.
- Fixed a bug where calling a function with discarded labelled arguments
incorrectly results in a compile error.
- Fixed a bug where assert statements return the wrong value.
- The `gleam new` command requires a root folder param, project name is
optional and if not provided the project name will be inferred from
the folder name.
- Generated Erlang record header files now contain Erlang type information.
- New OTP application projects depend on `gleam_otp` v0.1.5.
- The output of the formatter has been improved.
## v0.15.1 - 2021-05-07
- Fixed a bug where blocks that contained try expressions could be formatted
incorrectly.
## v0.15.0 - 2021-05-06
[Release Blog Post](https://gleam.run/news/gleam-v0.15-released/)
## v0.15.0-rc1 - 2021-05-05
- Syntax highlighting of Gleam code in generated HTML documentation has been
improved.
- Fixed a bug where markdown tables in rendered HTML documentation would have
the incorrect background colour on every other row.
- Tuples now have a new, concise syntax variant: `#(x, y, ...)`. Existing code
can be auto-migrated to the new syntax by running `gleam format`.
- Fixed a bug where customt type constructors with Erlang keywords as names
would generate invalid Erlang code.
- Gleam now supports `\e` string escapes.
- Values and types from the prelude can now be used in a qualified fashion by
importing the `gleam` module.
- Empty lists can now be used in constants.
- Compiler performance has been improved when working with lists.
- Compiler performance has been improved when working with sequences of
expressions.
- Assignments using `let` and `assert` are now expressions and no longer require
a following expression in their containing block. They are now themselves
expressions.
- Fixed a bug where tuple indexing could incorrectly claim a tuple is not of
type tuple in some circumstances.
- Glean `new` command now checks if target folder exists, if so it returns
an error.
- A compile time error is now raised if a module is defined with the name `gleam`.
- A compile time error is now raised if a module is defined with the a keyword
in the name.
- New projects are generated using `gleam_stdlib` v0.15.0.
- New projects are generated at v0.1.0.
## v0.14.4 - 2021-03-27
- The Gleam compiler has been updated to compile with the new Rust v1.51.0.
- New project's `gleam.toml` has a comment that shows how to add a
`repository` field.
- New projects no longer include a licence field in `src/$APP.app.src` by
default.
## v0.14.3 - 2021-03-20
- Added an error hint when joining string using the `+` or `+.` operator.
- New projects are created with `setup-erlang` v1.1.2 and Erlang/OTP v23.2.
- Fixed a bug where the compiler would be unable to locate an imported module
if a value from a nested module is used in a qualified fashion.
## v0.14.2 - 2021-03-02
- Project names can now contain numbers.
## v0.14.1 - 2021-02-27
- The error message for binary operators has been given more detail and
hints.
- Fixed a bug where alternative patterns would incorrectly report unused
variables.
- Fixed a bug where private types shadowed shadowed by values would
incorrectly report unused variables.
## v0.14.0 - 2021-02-18
[Release Blog Post](https://gleam.run/news/gleam-v0.14-released/)
## v0.14.0-rc2 - 2021-02-18
- New projects are created with `gleam_stdlib` v0.14.0.
## v0.14.0-rc1 - 2021-02-14
- Gleam now generates Erlang typespecs.
- New projects no longer include a licence file by default.
- New projects can be created using the new `escript` template to generate a
command line tool style program.
- A warning is emitted when a literal value is constructed but not used.
- Automatically generate a link to repository in docs if available.
- Code in HTML documentation is has highlighted syntax.
- Gleam now only supports `\r`, `\n`, `\t`, `\"`, and `\\` string escapes.
- A set of OCI container images are built automatically for each release.
- New compile time checks for invalid bit string literals and patterns have
been added.
- The error messages for syntax errors in names have been improved.
- Fixed a bug where the repo URL would render incorrectly in HTML docs.
- Fixed a bug where piping a block can render invalid Erlang.
- New compile time warnings on unused types, functions and variables.
- The runtime error emitted by the `todo` keyword now carries additional
information.
- The runtime error emitted by the `assert` keyword now carries additional
information.
- Fixed a bug where bit string patterns would not correctly unify with the
subject being pattern matches on.
- Documentation dark mode.
- Fixed a bug where some app.src properties were incorrectly named.
- `--warnings-as-errors` flag added to `gleam build` command.
## v0.13.2 - 2021-01-14
- `ring` dep upgraded to enable compilation on Apple M1 ARM processors.
## v0.13.1 - 2021-01-13
- Fix off-by-one error in message messages.
## v0.13.0 - 2021-01-13
[Release Blog Post](https://gleam.run/news/gleam-v0.13-released/)
- New Gleam projects use stdlib v0.13.0.
## v0.13.0-rc2 - 2021-01-12
- The `version` property in `gleam.toml` is now optional again.
## v0.13.0-rc1 - 2021-01-09
- Variable names now only have 1st letter capitalized when converted to erlang.
- Records defined in other modules can now be used in module constants.
- Documentation can link from functions, types & constants to their source
code definitions on popular project hosting sites.
- Documentation hosted on HexDocs now has a version selector.
- Fixed a bug where the `app` project template rendered invalid code.
- Newly generated projects use stdlib v0.12.0.
- Named subexpressions in patterns now render correct Erlang.
- The anonymous function syntax now successfully parses with whitespace
between `fn` and `(`.
- Fixed a bug where the formatter would incorrectly remove blocks around some
binary operators.
- Constants can now be defined after they are used in functions
- The parser has been rewritten from scratch, dramatically improving error
messages and compilation times.
- `1-1` and `a-1` are now parsed as `1 - 1` and `a - 1`
- Further information has been added to the error messages when a function
returns the wrong type.
- Further information has been added to the error messages when case clauses
return different types.
- Fixed a bug where imported record constructors without labels used as an
anonymous function generates incorrect Erlang.
## v0.12.1 - 2020-11-15
- The compiler can now discriminate between record access and module access
for shadowed names
- The `new` command will no longer permit projects to be made with names that
clash with Erlang standard library modules.
- The formatter now correctly treats lines of only whitespace as empty.
- The styling of tables in rendered HTML documentation has been improved.
- Rendered HTML documentation has regained its max-width styling.
## v0.12.0 - 2020-10-31
[Release Blog Post](https://gleam.run/news/gleam-v0.12-and-gleam-otp-v0.1-released/)
## v0.12.0-rc4 - 2020-10-31
- The rendered module documentation sidebar can now scroll independently to
the page.
- Application projects now have the correct `mod` value in the generated
`.app.src`.
- Records without fields can now be used in module constants.
- New application projects are now created used Gleam's type safe OTP pulled
from Hex.
## v0.12.0-rc3 - 2020-10-24
## v0.12.0-rc2 - 2020-10-24
## v0.12.0-rc1 - 2020-10-24
- The utf8, utf16, and utf32 type specifiers are now only available in bit
string construction, matching must be done with the codepoint versions.
- Functions may now be called before they are defined in a module. This
enabled mutually recursive functions!
- Discarded variable names may now include numbers.
- Fixed a bug where discarded variables might generate incorrect Erlang.
- Added support tuple access in clause guards.
- New projects are created with version 1.0.2 of the setup-gleam GitHub
action.
- New application projects are now created used Gleam's type safe OTP.
- Comments are now correctly handled on platforms that use \r\n line endings,
such as Windows.
## v0.11.2 - 2020-09-01
- Fixed a bug where an imported constructor would emit an unused constructor
warning when only used in pattern matching.
## v0.11.1 - 2020-08-31
- The formatter style has been improved to render function type arguments on
a single line when possible, even if the return type will not fit on a
single line.
- The format for printed types in error messages has been improved.
- Fixed a bug where the formatter would strip a constructor pattern spread
when no fields are given.
- Fixed a bug where assigning the result of a block to a variable would
generate incorrect Erlang.
- The formatter style has been improved for function calls that take a single
block as an argument.
- Reserved words are no longer incorrectly permitted as project names.
## v0.11.0 - 2020-08-28
[Release Blog Post](https://lpil.uk/blog/gleam-v0.11-released/)
## v0.11.0-rc3 - 2020-08-27
- Bit strings now support non-literal strings as segment values.
- Fixed a bug where Erlang variables could be generated with incorrect names
when defining an anonymous function.
## v0.11.0-rc2 - 2020-08-24
- The formatter style has been improved to render some single argument calls
in a more compact style.
## v0.11.0-rc1 - 2020-08-22
- Field access now works before the custom type is defined.
- The error message returned by the compiler when the user tries to use unknown
labelled arguments now handles multiple labels at once, and does not suggest
labels they have already supplied.
- The formatter style has been improved to use a trailing comma on imports
broken over multiple lines.
- The formatter style has been improved to wrap lists and bit strings over as
few lines as possible if the elements are Ints, Floats, or Strings.
- The formatter style has been improved to preserve comments on labelled
call arguments.
- The formatter style has been improved to preserve empty lines in assignments.
- The performance of the formatter has been improved.
- Records can be updated using the spread syntax. A warning is emitted if no
fields are updated when using this syntax.
- Fixed a bug where type parameters can leak between different type
definitions in a module.
- Markdown tables, footnotes, strikethroughs, and tasklists are now supported
in documentation.
- Fixed a bug where generic types may be incorrectly unified.
- Ints and floats can now be written with underscores for clarity.
- The warning for a `todo` now includes the required type of the
not-yet-implemented expression.
- Holes can be used in type annotations to specify part of a type, leaving the
rest for inference.
- The incorrect arity error now prints any missing labelled arguments.
- Fixed a bug where Erlang variables could be generated with incorrect names
when directly calling an anonymous function.
- A warning is emitted when a type is imported or created but not used.
- Fixed a bug where Erlang variables names could clash when rebinding
variables while similarly named variables ending in a number are in scope.
- Fixed a bug in the pretty printer which prevented the formatter from
rendering sub-expressions in a single line when later code would not fit on
the same line.
- The formatter style has been improved to render some single argument calls
in a more compact style.
- Gleam now supports hex, octal, and binary literals.
- Rebar3 hex packages now include `gleam.toml` and `gen`.
- Newly generated projects use stdlib v0.11.0.
## v0.10.1 - 2020-07-15
- Fixed a bug where the compiler failed to return an error when type checking
a tuple with the wrong arity in a pattern.
- The error message for a duplicate module member now shows the location of
both definitions.
- Fix compiler bug where labelled arguments were being reordered incorrectly.
## v0.10.0 - 2020-07-01
[Release Blog Post](https://lpil.uk/blog/gleam-v0.10-released/)
- Newly generated projects use stdlib v0.10.1.
- Fixed a bug where discards inside bit string patterns generated invalid
code.
## v0.10.0-rc2 - 2020-06-30
- Fixed a bug where variables names would be incorrectly generated when using
alternative patterns.
## v0.10.0-rc1 - 2020-06-29
- Single letter module names are now permitted.
- Added support for bit string syntax.
- Support for the deprecated list prepend syntax has been removed.
- Added module level constants that are inlined at compile time.
- Public module level constants generate documentation.
- The formatter style has been improved to wrap and sort imports.
- The formatter now permits comments at the end of module function bodies.
- The formatter now skips files that match patterns defined in ignore files
such as .gitignore and .ignore.
- Error message diagnostic code previews for type errors when using the the
pipe operator have been made more accurate.
- Added support for list literals in clause guards.
- Fixed bug when reassigning a variable inside a case clause with alternative
patterns.
- Todos can now take an optional label.
## v0.9.1 - 2020-06-12
- Fixed a bug where binary operators may lose required `{ }`s when formatted.
## v0.9.0 - 2020-06-01
[Release Blog Post](https://lpil.uk/blog/gleam-v0.9-released/)
- Newly generated projects use stdlib v0.9.0.
- Additional information is printed to the console when generating HTML
documentation from Gleam code.
- Fixed a bug where blocks on either side of a binary operator would be
rendered without `{ }`.
## v0.9.0-rc1 - 2020-05-26
- The formatter style has been improved.
- Numbers are now permitted in module names.
- Emitted Erlang code correctly adds parentheses around binary subexpressions
to preserve precedence.
- Record names and fields are now escaped in `.hrl` files if they conflict
with Erlang reserved words
- Annotations are now supported on `let` and `assert` expressions
- Formatter now accepts comments for the fields of a custom type's constructors
- Added opaque custom types, which have constructors that cannot be accessed
from outside their own modules.
- Additional (arbitrary) markdown documentation pages can now be added and
built with `docs build`.
- Fix code generation when calling functions returned through either record
or tuple access
- Add lookup for Gleam source code in Mix's `deps` directory.
- Newly generated Gleam projects use the GitHub action
`gleam-lang/setup-erlang` v1.1.0.
- Added support for custom type record literals in guards.
- Type variables are now correctly preserved within nested scopes.
## v0.8.1 - 2020-05-19
- The formatter now correctly handles unicode comments.
## v0.8.0 - 2020-05-07
[Release Blog Post](https://lpil.uk/blog/gleam-v0.8-released/)
- The `docs build`, `docs publish`, and `docs remove` commands can be used to
compile HTML documentation locally, publish them to HexDocs, and remove them
from HexDocs respectively.
- Type error reporting has been improved when using the pipe operator.
- Newly generated projects use stdlib v0.8.0.
- The compiler can now emit warnings. Currently there are warnings for using
the old '|' syntax in lists and for todos.
- Will give a clearer error when a function given as an argument to another
function doesn't match the type of the parameter.
- Fixed bug where imported type constructors had the incorrect arity.
- Fixed bug where a doing an unqualified import of a type constructor and
giving it an alias would use the wrong name if it contained any values.
- Fixed a bug trying to access an imported constructor which contained values.
- Fixed a compiler crash that occurred when trying to unify a tuple with something
other than another tuple or a variable.
- Added support for tuple literals in guards.
## v0.8.0-rc1 - 2020-04-28
- Strings are now encoded as utf8 binaries in the generated Erlang.
- HTML documentation can now be generated from Gleam code by running `gleam build --doc`.
- Gleam code can be formatted using the `gleam format` command.
- The pipe operator `|>` will now attempt to insert the left hand side as the
first argument to the right hand side if the right hand side is a call,
removing the need for function capture boilerplate.
- A `record.label` syntax can now be used to access the fields of a custom
type that have a single record variant.
- Anonymous functions can now have return type annotations.
- There is a `todo` keyword for type checking functions that have not yet been
implemented.
- Tuples can be indexed into using the `var.1` syntax.
- `>`, `>=`, `<`, and `<=` operators are now supported in case clause guards
and can be used to check the ordering of integers.
- `>.`, `>=.`, `<.`, and `<=.` operators are now supported in case clause
guards and can be used to check the ordering of floats.
- The list prepend syntax is now `[x, ..y]`. The old `[x | y]` syntax is
deprecated but will continue to work for now. The formatter will rewrite the
old syntax to the new.
- Add new assert syntax for binding variables `assert Ok(x) = result`. In the
future this will allow you to use a pattern that does not match all values.
- Added support for int and float literals in guards.
- Color codes are now only emitted in error output for interactive terminal
sessions.
- Added a new `..` syntax for discarding the remaining fields of a record.
- Using the same variable name multiple times in the same pattern will now
raise an error.
- Discard can now be omitted in list tails in patterns, ie `[x, ..]` is the
same as `[x, .._]`. The former is the preferred version and is emitted by the
formatter.
## v0.7.1 - 2020-03-03
- Projects generated with `gleam new` use `stdlib` version 0.7.0.
## v0.7.0 - 2020-03-01
[Release Blog Post](https://lpil.uk/blog/gleam-v0.7-released/)
## v0.7.0-rc1 - 2020-02-28
- Type aliases can be defined to give concise names to frequently used types.
- Case expression clauses may have guards which can be used to require
equality between specified variables in order for the clause to match.
- Case expression clauses may have alternative patterns, enabling one clause
to match for multiple different possible patterns.
- Types may now be used before they are defined within their defining module.
- Fixed a bug where import paths would not be correctly resolved on Windows.
- Added job to create precompiled binary for 64-bit Windows when releasing.
- `gleam new` now creates a project that uses `actions/checkout@v2.0.0` in its
GitHub actions workflow.
- Labelled argument in functions may now be discarded by prefixing the name
with an underscore, like unlabelled arguments.
- Sub-patterns can have names assigned to them within a pattern using the `as`
keyword.
- The format of compiler error messages printed to the console has been
improved by upgrading to a newer version of the codespan-reporting library.
- Type variables in the given and expected types will now be printed with the
same name in type error messages if they are equivalent.
- A friendly error message is rendered when a case expression clause has the
incorrect number of patterns for the subjects.
- A friendly error message is rendered when a .gleam file cannot be read.
- A friendly error message is rendered when the `gleam new` command fails to
write the new project to the file system.
- A friendly error message is rendered when there is a cycle formed by module
imports.
- Top level types are now printed in error messages for type parameter mismatches.
- The `gen` directory is now deleted before each compilation.
- `gleam new` now includes installation instructions for Hex packages in the
generated README.
- `gleam new` now accepts a `--description` flag for including a description of
the project in the README and `.app.src` file.
- Fixed a bug where variable names would be incorrectly generated in some
situations when variable names are reused during and after a case
expression.
- Performance of the Erlang code generator has been improved by removing some
vector allocations.
- An error is emitted when multiple types with the same name are defined in or
imported into a module.
## v0.6.0 - 2019-12-25 🎄
[Release Blog Post](https://lpil.uk/blog/gleam-v0.6-released/)
- Function capture syntax now supports labelled arguments.
## v0.6.0-rc1 - 2019-12-23
- Syntax for defining structs and enums have been unified into a singular
custom type definition statement. Instances of these custom types are called
records.
- Anonymous structs have been renamed tuples.
- Values and types can be given a new name when imported in the unqualified
fashion using the `import mod.{value as name}` syntax.
- An error will be emitted if multiple values constructors are defined with
the same name in a module.
## v0.5.1 - 2019-12-23
- Fixed a bug where invalid Erlang would be generated when using a local
private function as a value.
## v0.5.0 - 2019-12-16
[Release Blog Post](https://lpil.uk/blog/gleam-v0.5-released/)
- Enum constructor arguments can now be labelled, allowing arguments to be
given by name at the call site.
- An Erlang header file with a record definition is generated for each Gleam
struct defined.
- `gleam new` creates a project at v1.0.0.
- Function calls are now properly escaped when the function name conflicts
with an Erlang keyword.
- References to unqualified imported functions now generate correct Erlang
code.
- Fixed a bug where variable rebinding would generate incorrect code in some
case expressions.
- Fixed a bug where variable rebinding of function arguments would generate
incorrect code.
## v0.5.0-rc1 - 2019-11-26
- Function arguments can be labelled, allowing arguments to be given by name
at the call site.
- `case` expressions now accept multiple subjects, enabling pattern matching
on multiple values simultaneously.
- Values and types can be imported from modules and references in an
unqualified fashion.
- Named structs now have their name as the first element in the generated
Erlang code. This enabled easier use from Erlang by defining records for
them, as well as slightly clearer printf debugging.
- Anonymous structs have been introduced, serving as a quick and generic
alternative to declared structs and as a format for interop with Erlang
tuples.
- `gleam new` now accepts a `--template` flag to generate different styles of
project. An OTP application template has been added alongside the existing
OTP library template.
- `gleam new` now creates configuration for GitHub Actions, making Gleam
projects ready for continuous integration out of the box.
- The syntax for defining enums, case expressions, and blocks has been changed
to a syntax closer to that found in the C family of languages.
- The source code preview for functions that return a type incompatible with
the functions annotations has been improved to be more precise.
- A helpful error message is rendered if an enum field contains a generic type
that has not been declared.
- A bug has been fixed in which type mismatch errors originating from pattern
matching would sometimes display the incorrect expected type.
## v0.4.2 - 2019-10-22
- Fixed a crash when an incorrect number of labelled struct arguments are
given.
- Fixed a struct labelled argument being incorrect reported as already given.
## v0.4.1 - 2019-09-29
- Struct types with parameterised fields are now registered with the correct
number of type parameters.
## v0.4.0 - 2019-09-19
[Release Blog Post](https://lpil.uk/blog/gleam-v0.4-released/)
- The struct data type has be introduced. Structs are pre-declared user
defined data types with named fields and constant access time.
- The map and tuple data types has been removed, replaced by the struct data
type.
- The generated code no longer contains export statements if no functions are
exported from a module.
- Comparison operators have been specialised to operate only on Ints.
- The `>.` `>=.` `<.` and `<=.` comparison operators have been added for
comparing Floats.
- It is now an error to export an enum which has a constructor that takes a
private type as an argument.
- The error messages for defining multiple modules with the same name and for
importing test modules into application code have been improved.
- Numbers are now permitted in type names and constructors.
- The `Nil` constructor will no longer erroneously be of type `Int`.
## v0.3.0 - 2019-08-08
[Release Blog Post](https://lpil.uk/blog/gleam-v0.3-released/)
- New project structure can be generated with the `gleam new` command.
- Functions can be annotated with their argument and return types. This may be
used to restrict the function to a less general type than inferred by the
compiler, or purely for documentation purposes.
- External function names and their target functions are now escaped in the
generated code if they collide with Erlang keywords such as `catch` or `or`.
- Type error arising from the arguments of function calls have more accurate
error diagnostics.
- Precompiled Gleam binaries are now available on the GitHub release page.
- Precompiled Docker images containing the Gleam binary are now available on
DockerHub.
- The formatting of the Erlang code rendered by the compiler has been altered
to improve legibility.
- A helpful error message is now rendered if the shorthand anonymous function
syntax is used with too many underscores.
- A helpful error message is now rendered when attempting to import an unknown
module.
## v0.2.0 - 2019-06-25
- Modules can now live within namespaces such as `my_app/user/profile`.
- The name of the variable created can be specified when importing a module
using the `import my_mod as name` syntax.
- Function names and atoms are now escaped in the generated code if they
collide with Erlang keywords such as `catch` or `or`.
- There is a shorthand syntax for prepending multiple elements to a list.
`[1, 2, 3 | my_list]`
## v0.1.2 - 2019-05-12
- Types containing more than 26 type variables will no longer render with
invalid type variable names.
- Types in error messages no longer have extra indentation that increases as
the type gets larger.
- There is a new type `Nil` which is occupied by a single value (`Nil`). This
type is used to represent the absence of a value and is commonly used with
`Result` to model a value that is either present (`Ok(value)`) or absent
(`Error(Nil)`).
- Zero arity enum constructors now generate the correct Erlang when used in
modules other than the one they are defined in.
## v0.1.1 - 2019-04-28
- Error messages now display the path of the file containing the problem.
- Maps and modules with erroneous extra fields now have a custom error
message.
- Rows with tails that are unbound type variables are now correctly unified in
the type system. This fixes a bug in which maps and modules may sometimes
fail to type check when there is no error.
## v0.1.0 - 2019-04-15
[Release Blog Post](https://lpil.uk/blog/hello-gleam/)
- Initial release!
================================================
FILE: changelog/v1.10.md
================================================
# Changelog
## v1.10.0 - 2025-04-14
### Bug fixes
- Fixed a bug where the code action to unqualify types and values would add an
unqualified import even if it was already imported.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where numbers starting with `0x_`, `0o_` and `0b_` would cause
a syntax error when compiling to JavaScript.
([Surya Rose](https://github.com/GearsDatapacks))
## v1.10.0-rc1 - 2025-04-05
### Compiler
- On the JavaScript target, bit arrays can now use the `unit` option to control
the units of the `size` option.
([Surya Rose](https://github.com/GearsDatapacks))
- The compiler can now tell if string branches are unreachable. For example, the
following code:
```gleam
case a_string {
"Hello, " <> name -> name
"Hello, Jak" -> "Jak"
_ -> "Stranger"
}
```
Will raise the following warning:
```
warning: Unreachable case clause
┌─ /src/greet.gleam:7:5
│
7 │ "Hello, Jak" -> "Jak"
│ ^^^^^^^^^^^^^^^^^^^^^
This case clause cannot be reached as a previous clause matches the same
values.
Hint: It can be safely removed.
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- On the JavaScript target, blocks and various other expressions no longer
compile to immediately invoked function expressions.
([Surya Rose](https://github.com/GearsDatapacks))
- On the JavaScript target, bit arrays can now use 16-bit floats in expressions
and patterns.
([Richard Viney](https://github.com/richard-viney))
- Improved the error message for unknown and missing target names in the
`@target` attribute.
([Alexander Keleschovsky](https://github.com/AlecGhost))
- The compiler now uses a call graph for detecting unused types and values.
This means that among other things, it can now detect unused recursive
functions. For example:
```gleam
// warning: unused
fn some_recursive_function() {
some_recursive_function()
}
```
([Surya Rose](https://github.com/GearsDatapacks))
- The compiler now emits a warning when using `let assert` to assert a value
whose variant has already been inferred. For example:
```gleam
// warning: This will always crash
let assert Ok(_) = Error("Some error")
```
([Surya Rose](https://github.com/GearsDatapacks))
- It is now possible to omit the `:float` option for literal floats used in a
`BitArray` segment.
```gleam
<<1.11>>
```
Is the same as:
```gleam
<<1.11:float>>
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Compilation of binary operators is now fault tolerant and won't stop at the
first type error.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The compiler now provides a better error message when using the wrong operator
to try and join two strings together. For example:
```txt
error: Type mismatch
┌─ /src/wibble.gleam:2:13
│
2 │ "Hello, " + "Lucy"
│ ^ Use <> instead
The + operator can only be used on Ints.
To join two strings together you can use the <> operator.
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The compiler now provides a better error message when using an Int operator on
Float values, suggesting the correct replacement. For example:
```txt
error: Type mismatch
┌─ /Users/giacomocavalieri/Desktop/prova/src/prova.gleam:2:7
│
2 │ 1.0 + 2.0
│ ^ Use +. instead
The + operator can only be used on Ints.
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The compiler now provides a better error message when using a Float operator
on Int values, suggesting the correct replacement. For example:
```txt
error: Type mismatch
┌─ /Users/giacomocavalieri/Desktop/prova/src/prova.gleam:2:5
│
2 │ 1 >. 2
│ ^^ Use > instead
The >. operator can only be used on Floats.
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The compiler no longer shows errors for a function's labels if the called
function itself doesn't exist.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
### Build tool
- Include a type annotation for the `main` function generated by `gleam new`.
([Drew Olson](https://github.com/drewolson))
- Two entry point scripts are now always generated by `gleam export erlang-shipment`:
- `entrypoint.sh` for POSIX Shell
- `entrypoint.ps1` for PowerShell
([Greg Burri](https://github.com/ummon))
- The `gleam export` command now takes a `package-information` option to
export the project's `gleam.toml` as a JSON file.
([Rodrigo Álvarez](https://github.com/Papipo))
- Improved the error message when failing to encrypt or decrypt a local
Hex API key.
([Samuel Cristobal](https://github.com/scristobal))
- The `HEXPM_USER` and `HEXPM_PASS` environment variables when running
`gleam publish` have been deprecated in favour of `HEXPM_API_KEY`.
([Samuel Cristobal](https://github.com/scristobal))
- The "functions" and "constants" sections of generated HTML documentation have
been merged into one "values" section.
([Sam Zanca](https://github.com/metruzanca))
### Language server
- The language server now allows renaming of functions, constants,
custom type variants and custom types across modules. For example:
```gleam
// wibble.gleam
pub fn wibble() {
wibble()
//^ Trigger rename
}
// wobble.gleam
import wibble
pub fn main() {
wibble.wibble()
}
```
Becomes:
```gleam
// wibble.gleam
pub fn wobble() {
wobble()
}
// wobble.gleam
import wibble
pub fn main() {
wibble.wobble()
}
```
([Surya Rose](https://github.com/GearsDatapacks))
- The language server can now offer a code action to replace a `..` in a pattern
with all the fields that are being ignored. For example triggering the code
action on this spread:
```gleam
pub type Pokemon {
Pokemon(id: Int, name: String, moves: List(String))
}
pub fn main() {
let Pokemon(..) = todo
// ^ If you put your cursor here
}
```
Would generate the following code:
```gleam
pub type Pokemon {
Pokemon(id: Int, name: String, moves: List(String))
}
pub fn main() {
let Pokemon(id:, name:, moves:) = todo
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The function generated by the "Generate JSON encoder" code action has been
slightly modified so that it will now fail to compile if the type has new
fields added, ensuring the programmer remembers to re-run the code action.
For example, for this type:
```gleam
type Person {
Person(name: String, age: Int)
}
```
The following code used to be generated:
```gleam
fn encode_person(person: Person) -> json.Json {
json.object([
#("name", json.string(person.name)),
#("age", json.int(person.age)),
])
}
```
But now, this code is generated:
```gleam
fn encode_person(person: Person) -> json.Json {
let Person(name:, age:) = person
json.object([
#("name", json.string(name)),
#("age", json.int(age)),
])
}
```
([Surya Rose](https://github.com/GearsDatapacks))
- The language server now supports finding references to values and types,
both within a module and across multiple modules.
([Surya Rose](https://github.com/GearsDatapacks))
- The language server now offers a code action to remove all `echo`s in a
module. For example:
```gleam
pub fn main() {
[1, 2, 3]
|> echo
// ^^^^ If you put your cursor over here
|> list.filter(int.is_even)
|> echo
}
```
Triggering the code action would remove all the `echo` pipeline steps:
```gleam
pub fn main() {
[1, 2, 3]
|> list.filter(int.is_even)
}
```
This also works with all the `echo`s used before an expression:
```gleam
pub fn main() {
echo 1 + 2
//^^^^^^^^^^ If hovering anywhere over here
}
```
Triggering the code action would remove the `echo`:
```gleam
pub fn main() {
1 + 2
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server now offers a code action to replace a Float operator used
on Int values with the correct operator. For example:
```gleam
pub fn main() {
11 +. 1
//^^^^^^^ When hovering anywhere over here
}
```
Triggering the code action would fix the compilation error by using the
correct Int operator:
```gleam
pub fn main() {
11 + 1
}
```
This also works the other way around:
```gleam
pub fn main() {
1.1 + 10.0
//^^^^^^^^^^ If hovering anywhere over here
}
```
Triggering the code action would replace the wrong operator with the correct
equivalent Float operator:
```gleam
pub fn main() {
1.1 +. 10.0
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- If there's a compilation error because two strings are being joined with the
wrong `+` operator (instead of using `<>`), the language server now offers a
code action to fix the error automatically. For example:
```gleam
pub fn main() {
"Hello, " + "Jak"
//^^^^^^^^^^^^^^^^^ When hovering anywhere over here
}
```
Triggering the code action would fix the compilation error by using the
correct `<>` operator instead of `+`:
```gleam
pub fn main() {
"Hello, " <> "Jak"
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server now offers the option to lift expressions into consts.
For example, in two uses of the code action:
```gleam
pub fn main() {
[#("a", 0), #("b", 1), #("a", 2)]
|> key_filter("a")
}
```
Becomes:
```gleam
const values = [#("a", 0), #("b", 1), #("a", 2)]
const string = "a"
pub fn main() {
values
|> key_filter(string)
}
```
([Matias Carlander](https://github.com/matiascr))
- The language server will now only offer the code action to generate a JSON
encoder if the `gleam_json` package is installed as a dependency.
([Surya Rose](https://github.com/GearsDatapacks))
- The language server will offer to wrap assignment or case clause values in
blocks. Useful when adding more expressions to an existing case clause or
variable assignment.
```gleam
pub fn f(pokemon_type: PokemonType) {
case pokemon_type {
Water -> soak()
// ^^^^^^ selecting the right-hand side of the `->` in a clause
Fire -> burn()
}
}
```
Becomes
```gleam
pub fn f(pokemon_type: PokemonType) {
case pokemon_type {
Water -> {
soak()
}
Fire -> burn()
}
}
```
([Matias Carlander](https://github.com/matiascr))
- The "Generate function" code action now uses labels or variable names to improve
the names of generated arguments. For example:
```gleam
pub fn main() {
let language = English
greet(language, name: "Louis")
}
```
Will generate the following function:
```gleam
pub fn greet(language: String, name name: String) -> a {
todo
}
```
([Surya Rose](https://github.com/GearsDatapacks))
- The "Rewrite from `use`" code action now only triggers if the cursor is on the
first line of the `use` expression to rewrite.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
### Formatter
### Container images
- Container images now contain Software Bill of Materials (SBoM) and SLSA
Provenance information.
([Jonatan Männchen](https://github.com/maennchen))
### Bug fixes
- Fixed a bug where tuples with atoms in the first position could be
incorrectly formatted by `echo`.
([Louis Pilfold](https://github.com/lpil))
- Fixed a bug where unlabelled arguments would be allowed after labelled
arguments in variant constructor definitions.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where using the "Convert to pipe" code action on a function whose
first argument is itself a pipe would result in invalid code.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where using the "Convert to pipe" code action on a function or
record capture produces invalid code.
([Matias Carlander](https://github.com/matiascr))
- Fixed a bug where a temporarily moved or removed file does not get recompiled,
even though its dependencies changed in the meanwhile.
([Sakari Bergen](http://github.com/sbergen))
- Fixed a bug where the "Inline variable" code action would not work properly
if used inside a record update.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where variant inference wouldn't work on `let assert` assignments.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the build tool could fail to lock the build directory but
not report an error.
([Louis Pilfold](https://github.com/lpil))
- Fixed a bug where the language server would be too eager to recompile modules
when it could use the cache from previous compilations.
([Louis Pilfold](https://github.com/lpil))
- Fixed a bug where `let assert` would not assert that the given value matched
the pattern if it was the only expression inside a block.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the code generated for `echo` on JavaScript could have name
collisions if there are functions called `console` or `process`, or custom type
variants called `Object` or `Deno` defined in the module.
([Louis Pilfold](https://github.com/lpil))
- Fixed a bug where the language server would stop working if the build
directory was deleted e.g. as a result of `gleam clean`.
([Sakari Bergen](https://github.com/sbergen))
- Fixed a bug where the "Rewrite to pipe" code action could generate invalid
code when the piped argument was a binary operation.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where prelude types and values would be suggested in autocomplete
when part of a module select.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the check for multiple top-level modules when publishing
would incorrectly print a warning.
([Surya Rose](https://github.com/GearsDatapacks))
## v1.9.1 - 2025-03-10
### Formatter
- Improved the formatting of pipelines printed with `echo`.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
### Bug fixes
- Fixed a bug where `echo` used before a pipeline would generate invalid code
for the Erlang target.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
================================================
FILE: changelog/v1.11.md
================================================
# Changelog
## v1.11.0 - 2025-06-02
### Bug fixes
- Fixed a bug where using a pipe operator on the right-hand side of an `assert`
statement would generate invalid code on the JavaScript target.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the build tool would try to run a module whose main
function is private.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the compiler would sometimes warn that an assertion was
unnecessary because it only asserted literal values, when that was not the case.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where using the "generate function" code action on a function
capture would generate an argument named `_capture`, using the internal
variable names of the compiler.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the language server would provide completions inside a
constant string.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a grammatical error in the bit array truncation warning message.
([Louis Pilfold](https://github.com/lpil))
## v1.11.0-rc2 - 2025-05-29
### Compiler
- The format of the `assert` and `let assert` location information has been
improved, and the file path of the source module has been added.
([Louis Pilfold](https://github.com/lpil))
### Language server
- When using the "remove `echo`" code action, the language server will also
remove any literal expression being printed by `echo` statements. For example
```gleam
pub fn main() {
echo "Before"
do_complex_stuff()
echo "After"
do_something_else()
}
```
Will become:
```gleam
pub fn main() {
do_complex_stuff()
do_something_else()
}
```
Making it easier to get rid of debug printing messages once they're no longer
needed.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
### Bug fixes
- Fixed a bug where type constructors with many fields would not be formatted
properly in the generated documentation.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where fields name `x0` in records could cause invalid code to be
generated on the JavaScript target.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where exceptions on JavaScript could be sometimes missing the
function name metadata.
([Louis Pilfold](https://github.com/lpil))
- Fixed a bug where the missing patterns shown for an inexhaustive `case`
expression would include constructors of opaque types which were not available
in the current module, leading to invalid code.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the language server would offer completions for local
variables where the variables were not in scope, leading to invalid code being
produced if the completion was followed.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the compiler would crash when compiling `let assert`
statements which contained a bit array pattern inside a tuple pattern on the
JavaScript target.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where a zero-length segment of a bit array would never match
in a case expression on the JavaScript target.
([Sakari Bergen](https://github.com/sbergen))
## v1.11.0-rc1 - 2025-05-15
### Compiler
- The compiler can now tell if some branches with bit array patterns are
unreachable. For example, the following code:
```gleam
case payload {
<> -> first_byte
<<1, _:bits>> -> 1
_ -> 0
}
```
Will raise the following warning:
```
warning: Unreachable case clause
┌─ /src/bit_array.gleam:4:5
│
4 │ <<1, _:bits>> -> 1
│ ^^^^^^^^^^^^^^^^^^
This case clause cannot be reached as a previous clause matches the same
values.
Hint: It can be safely removed.
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The code generated for pattern matching on the JavaScript target has been
improved to be more efficient and perform as little checks as possible.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The compiler now raises a warning when it can tell that an int segment
with a literal value is going to be truncated. For example, if you wrote this:
```gleam
<<258>>
```
The compiler will now warn you:
```txt
warning: Truncated bit array segment
┌─ /src/main.gleam:4:5
│
4 │ <<258>>
│ ^^^ You can safely replace this with 2
This segment is 1 byte long, but 258 doesn't fit in that many bytes. It
would be truncated by taking its first byte, resulting in the value 2.
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The compiler will now include labels in the error message when a `case`
expression is inexhaustive. For example, this code:
```gleam
pub type Person {
Person(name: String, age: Int)
}
pub fn classify(person: Person) {
case person {
Person(name: "John", age: 27) -> todo
Person(name: _, age: 42) -> todo
}
}
```
Will produces this error:
```
error: Inexhaustive patterns
┌─ /src/main.gleam:6:3
│
6 │ ╭ case person {
7 │ │ Person(name: "John", age: 27) -> todo
8 │ │ Person(name: _, age: 42) -> todo
9 │ │ }
│ ╰───^
This case expression does not have a pattern for all possible values. If it
is run on one of the values without a pattern then it will crash.
The missing patterns are:
Person(name:, age:)
```
([Surya Rose](https://github.com/GearsDatapacks))
- The analysis of lists, tuples, negation operators, `panic`, `echo` and `todo`
is now fault tolerant, meaning that the compiler will not stop reporting
errors as soon as it finds one.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The error message for types used with the wrong number of arguments has been
improved. For example, this piece of code:
```gleam
type Wibble(a)
type Wobble {
Wobble(Wibble)
}
```
Produces the following error:
```txt
error: Incorrect arity
┌─ /src/one/two.gleam:5:10
│
5 │ Wobble(Wibble)
│ ^^^^^^ Expected 1 type argument, got 0
`Wibble` requires 1 type argument but none where provided.
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- You can now use the `assert` keyword by itself to test a boolean expression.
If the expression evaluates to `False` at runtime, the `assert` statement
will cause the program to panic, with information about the expression that
was asserted.
For example:
```gleam
pub fn ok_error_test() {
assert result.is_ok(Ok(10))
assert result.is_error(Error("Some error"))
assert Ok(1) != Error(1)
assert result.is_error(Ok(42)) // panic: Assertion failed
}
```
A custom panic message can also be provided in order to add extra information:
```gleam
pub fn identity_test() {
assert function.identity(True) as "Identity of True should never be False"
}
```
([Surya Rose](https://github.com/GearsDatapacks))
- The compiler will now emit a warning when the return value of a call to a
function without side effects is unused. For example the following code:
```gleam
pub fn main() {
add(1, 2)
add(3, 4)
}
```
Will produce the following warning:
```
warning: Unused value
┌─ /src/main.gleam:4:3
│
4 │ add(1, 2)
│ ^^^^^^^^^ This value is never used
This expression computes a value without any side effects, but then the
value isn't used at all. You might want to assign it to a variable, or
delete the expression entirely if it's not needed.
```
([Surya Rose](https://github.com/GearsDatapacks))
- The compiler will now generate more efficient code for `let assert` on the
Erlang target.
([Surya Rose](https://github.com/GearsDatapacks))
- The compiler will now reject bit array segment patterns whose constant size is
zero or negative. Previously a zero or negative sized segment would crash the
compiler.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The compiler will not generate needless code for unused pattern variables.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Function parameters are now fault tolerant, meaning that type-checking can
continue to occur if a function references invalid type in its signature.
([Surya Rose](https://github.com/GearsDatapacks))
- The compiler is now fault tolerant when analysing patterns for custom types,
meaning it won't stop at the first error it encounters (for example if a
constructor is wrong).
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- On the JavaScript target, bit arrays now support UTf-16 and UTF-32 string
segments.
([Surya Rose](https://github.com/GearsDatapacks))
- When an import with unqualified types and values is unused the compiler will
now raise a single warning for the entire import rather than warning for each
individual item.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
### Build tool
- The build tool now supports placing modules in a directory called `dev`,
which like `test`, is only for development code.
([Surya Rose](https://github.com/GearsDatapacks))
- There is now a new CLI command, `gleam dev`, which runs the `$PACKAGE_dev`
module, for running development entrypoints.
([Surya Rose](https://github.com/GearsDatapacks))
- Updated the Erlang shipment POSIX entrypoint script to add an exec statement
so the Erlang process replaces the shell's process and can receive signals
when deployed.
([Christopher De Vries](https://github.com/devries))
- The build tool now provides additional information when printing warnings for
deprecated environment variables.
([Surya Rose](https://github.com/GearsDatapacks))
- When generating documentation, the build tool now prints type variable using
the same names as were used in the code for function signatures. For example,
this code:
```gleam
pub fn from_list(entries: List(#(key, value))) -> Dict(key, value) {
...
}
```
Previously would have been printed as:
```gleam
pub fn from_list(entries: List(#(a, b))) -> Dict(a, b)
```
But now is rendered as the following:
```gleam
pub fn from_list(entries: List(#(key, value))) -> Dict(key, value)
```
([Surya Rose](https://github.com/GearsDatapacks))
- When generating documentation, types from other modules are now rendered with
their module qualifiers. Hovering over them shows the module name.
For example, this code:
```gleam
import gleam/dynamic/decode
pub fn something_decoder() -> decode.Decoder(Something) {
...
}
```
Will now generate the following documentation:
```gleam
pub fn something_decoder() -> decode.Decoder(Something)
```
And hovering over the `decode.Decoder` text will show the following:
```txt
gleam/dynamic/decode.{type Decoder}
```
([Surya Rose](https://github.com/GearsDatapacks))
- When generating documentation, types in rendered documentation code will now
link to their corresponding documentation pages.
([Surya Rose](https://github.com/GearsDatapacks))
- `gleam add` will now emit an error if you try to add a package as a dependency
of itself.
([Louis Pilfold](https://github.com/lpil))
- The build tool will now emit an error when compiling a package if the package
has a Gleam and Erlang file which would collide.
([Surya Rose](https://github.com/GearsDatapacks))
### Language server
- The code action to add missing labels to functions now also works in patterns:
```gleam
pub type Person {
Person(name: String, age: Int, job: String)
}
pub fn age(person: Person) {
let Person(age:) = person
age
}
```
Becomes:
```gleam
pub type Person {
Person(name: String, age: Int, job: String)
}
pub fn age(person: Person) {
let Person(age:, name:, job:) = person
age
}
```
([Surya Rose](https://github.com/GearsDatapacks))
- The JSON encoding function that the language server code action generates is
now named `$TYPENAME_to_json` instead of `encode_$TYPENAME`. This is to remove
ambiguity with functions that encode to other formats, and to make the
function easier to discover by searching.
([Louis Pilfold](https://github.com/lpil))
- The code action to add missing patterns to a `case` expression now includes
labels in the generated patterns. For example:
```gleam
pub type Person {
Person(name: String, age: Int)
}
pub fn classify(person: Person) {
case person {
Person(name: "John", age: 27) -> todo
Person(name: _, age: 42) -> todo
}
}
```
Will now become:
```gleam
pub type Person {
Person(name: String, age: Int)
}
pub fn classify(person: Person) {
case person {
Person(name: "John", age: 27) -> todo
Person(name: _, age: 42) -> todo
Person(name:, age:) -> todo
}
}
```
([Surya Rose](https://github.com/GearsDatapacks))
- The language server now provides hover, autocomplete and goto definition
for constant definitions.
([Surya Rose](https://github.com/GearsDatapacks))
- The "generate function" code action can now choose better names based on the
labels and variables used. For example if I write the following code:
```gleam
pub fn main() -> List(Int) {
let list = [1, 2, 3]
let number = 1
remove(each: number, in: list)
//^^^^ This function doesn't exist yet!
}
```
And ask the language server to generate the missing function, the generated
code will now look like this:
```gleam
fn remove(each number: Int, in list: List(Int)) -> List(Int) {
todo
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server now provides autocomplete suggestions for labels after
part of the label has already been typed. For example, in this code:
```gleam
pub type Person {
Person(name: String, number: Int)
}
pub fn main() {
Person(n|)
}
```
The language server will provide `name:` and `number:` as autocomplete
suggestions.
([Surya Rose](https://github.com/GearsDatapacks))
- The language server now provides a code action to automatically generate a new
variant from incorrect code:
```gleam
pub type Msg {
ServerSentResponse(Json)
}
pub fn view() -> Element(Msg) {
div([], [
button([on_click(UserPressedButton)], [text("Press me!")])
// ^^^^^^^^^^^^^^^^^ This doesn't exist yet!
])
}
```
Triggering the code action on the `UserPressedButton` will add it to the `Msg`
type:
```gleam
pub type Msg {
ServerSentResponse(Json)
UserPressedButton
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server can now remove unused imported values and types:
```gleam
import a_module.{type Unused, unused, used}
pub fn main() {
used
}
```
Triggering the code action will remove all unused types and values:
```gleam
import a_module.{used}
pub fn main() {
used
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
### Formatter
- Improved the formatting of `echo` when followed by long binary expressions.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
### Installation
- Windows ARM64 pre-built binaries are now provided.
([Jonatan Männchen](https://github.com/maennchen))
### Bug fixes
- Fixed a bug where `case` expressions in custom panic messages would compile
to invalid syntax on the JavaScript target.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where an underscore after a zero in a number would compile to
invalid syntax on the JavaScript target.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the "generate function" code action could generate invalid
code when the same variable was passed as an argument twice.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where replacing a Hex dependency with a Git dependency of the
same name would cause the build tool to fail.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where updating the remote URL of a Git dependency would fail to
update the remote in the local dependency, causing a caching issue.
([Surya Rose](https://github.com/GearsDatapacks))
- Fix slightly wrong error message for missing main function in test module.
([Samuel Cristobal](https://github.com/scristobal))
- Fixed a bug where the compiler would not properly warn for unreachable
patterns in a `case` expression when the clause matched on multiple
alternative patterns.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the language server would generate invalid code for the
"fill in missing labels" code action.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where referencing an earlier segment of the bit array in a bit
array pattern in a `let assert` assignment would generate invalid code on the
Erlang target.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed instances where the "Extract variable" code action would produce invalid
code, most noticeable in code inside `case` clauses and `use` expressions.
([Matias Carlander](https://github.com/matiascr))
- Fixed a bug where the compiler would crash when type-checking code containing
an assignment pattern inside a bit-array pattern.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where using the pipe operator in the `size` option of a bit array
segment would generate invalid code on the Erlang target.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the language server would generate invalid code for the
"convert to use" code action, when used on a function call with labelled
arguments.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where a reference to an imported external function with a
non-alphanumeric module name would compile to invalid syntax on the Erlang
target.
([Mathieu Darse](https://github.com/mdarse))
- Fixed a bug where the compiler would not correctly check the size of bit
arrays when doing bit array pattern matching on the JavaScript target.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where using the pipe operator inside a record update would cause
the compiler to generate invalid code on the JavaScript target.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where some code actions would produce invalid code when a file
contained characters that were represented with more than one byte in UTF-8.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where LSP ranges would be incorrect when a file contained characters
that were represented with more than one byte in UTF-8.
([Surya Rose](https://github.com/GearsDatapacks))
- Only print the user-friendly LSP message when stdin is a terminal to avoid
emitting redundant logs.
([Ariel Parker](https://github.com/arielherself))
- Fixed a bug where the language server would wrongly override local variables
if they were shadowing an unqualified module function.
([Samuel Cristobal](https://github.com/scristobal))
- Fixed a bug where renaming a local variable which is used in combination with
label shorthand syntax would produce invalid code.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the stack would overflow on Windows when type-checking
multiple nested `use` expressions.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where using a variable from a separate pattern inside a bit array
pattern would be allowed but generate invalid Erlang code.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the compiler would crash if duplicate variables were defined
in alternative patterns.
([Surya Rose](https://github.com/GearsDatapacks))
================================================
FILE: changelog/v1.12.md
================================================
# Changelog
## v1.12.0 - 2025-08-05
### Bug fixes
- Corrected an error message that used incorrect terminology.
([Louis Pilfold](https://github.com/lpil))
## v1.12.0-rc3 - 2025-07-31
### Bug fixes
- Fixed a bug where using `echo` in a module with a function named `process`
would result in a runtime error on JavaScript.
([Peter Saxton](https://github.com/CrowdHailer))
## v1.12.0-rc2 - 2025-07-24
### Formatter
- The formatter now allows more control over how bit arrays are split. By adding
a trailing comma at the end of a bit array that can fit on a single line, the
bit array will be split on multiple lines:
```gleam
pub fn dgram() -> BitArray {
<>
}
```
Will be formatted as:
```gleam
pub fn dgram() -> BitArray {
<<
ip_version:4,
header_length:4,
service_type:8,
>>
}
```
By removing the trailing comma, the formatter will try and fit the bit array
on a single line again:
```gleam
pub fn dgram() -> BitArray {
<<
ip_version:4,
header_length:4,
service_type:8
>>
}
```
Will be formatted back to a single line:
```gleam
pub fn dgram() -> BitArray {
<>
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The formatter now allows more control over how bit arrays are formatted.
If a bit array is split with multiple segments on the same line, removing the
trailing comma will make sure the formatter keeps each segment on its own
line:
```gleam
pub fn dgram() -> BitArray {
<<
"This bit array was formatted", "keeping segments on the same line",
"notice how the formatting changes by removing the trailing comma ->",
>>
}
```
Is formatted as:
```gleam
pub fn dgram() -> BitArray {
<<
"This bit array was formatted",
"keeping segments on the same line",
"notice how the formatting changes by removing the trailing comma ->"
>>
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
### Bug fixes
- Fixed a bug where the formatter would move a comment before `assert` to be
after it.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the message following an `echo`, `panic`, `todo`, `assert`,
or `let assert` would not be formatted properly when preceded by a comment.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the compiler would generate invalid code for an `assert`
using pipes on the JavaScript target.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
## v1.12.0-rc1 - 2025-07-18
### Compiler
- It is now possible to add a custom message to be printed by `echo`, making it
easier to include additional context to be printed at runtime:
```gleam
pub fn main() {
echo 11 as "lucky number"
}
```
Will output to stderr:
```txt
/src/module.gleam:2 lucky number
11
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Generated JavaScript functions, constants, and custom type constructors now
include any doc comment as a JSDoc comment, making it easier to use the
generated code and browse its documentation from JavaScript.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The code generated for a `case` expression on the JavaScript target is now
reduced in size in many cases.
([Surya Rose](https://github.com/GearsDatapacks))
- The code generators now perform usage-based dead code elimination. Unused
definitions are not longer generated.
([Louis Pilfold](https://github.com/lpil))
- `echo` now has better support for character lists, JavaScript errors, and
JavaScript circular references.
([Louis Pilfold](https://github.com/lpil))
- The look of errors and warnings has been improved. Additional labels providing
context for the error message are no longer highlighted with the same style as
the source of the problem.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Gleam will now emit a helpful message when attempting to import modules using
`.` instead of `/`.
```txt
error: Syntax error
┌─ /src/parse/error.gleam:1:11
│
1 │ import one.two.three
│ ^ I was expecting either `/` or `.{` here.
Perhaps you meant one of:
import one/two
import one.{item}
```
([Zij-IT](https://github.com/zij-it))
- The compiler now emits a warning when a top-level constant or function
declaration shadows an imported name in the current module.
([Aayush Tripathi](https://github.com/aayush-tripathi))
- The compiler can now tell when an unknown variable might be referring to an
ignored variable and provide an helpful error message highlighting it. For
example, this piece of code:
```gleam
pub fn go() {
let _x = 1
x + 1
}
```
Now results in the following error:
```
error: Unknown variable
┌─ /src/one/two.gleam:4:3
│
3 │ let _x = 1
│ -- This value is discarded
4 │ x + 1
│ ^ So it is not in scope here.
Hint: Change `_x` to `x` or reference another variable
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The code generated for pattern matching has been optimised on the JavaScript
target to reuse the matched variables when safe to do so. Take the following
snippet of Gleam code:
```gleam
pub fn find_book() {
case ask_for_isbn() {
Ok(isbn) -> load_book(isbn)
Error(Nil) -> Error(Nil)
}
}
```
Notice how in the `Error` case we're returning exactly the same value that is
being matched on! Now the compiler will generate the following JavaScript code
instead of allocating a new `Error` variant entirely:
```js
export function find_book() {
let result = ask_for_isbn();
if (result instanceof Ok) {
let isbn = result[0];
return load_book(isbn);
} else {
// Previously this would have been: `return new Error(undefined);`!
return result;
}
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The compiler now raises a warning when performing a redundant comparison that
it can tell is always going to succeed or fail. For example, this piece of
code:
```gleam
pub fn find_line(lines) {
list.find(lines, fn(x) { x == x })
}
```
Would result in the following warning:
```
warning: Redundant comparison
┌─ /src/warning.gleam:2:17
│
1 │ list.find(lines, fn(x) { x == x })
│ ^^^^^^ This is always `True`
This comparison is redundant since it always succeeds.
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Attempting to use the list prefix syntax with two lists will now
show a helpful error message. For example, this snippet of code:
```gleam
pub fn main() -> Nil {
let xs = [1, 2, 3]
let ys = [5, 6, 7]
[1, ..xs, ..ys]
}
```
Would result in the following error:
```
error: Syntax error
┌─ /src/parse/error.gleam:5:13
│
5 │ [1, ..xs, ..ys]
│ -- ^^ I wasn't expecting a second list here
│ │
│ You're using a list here
Lists are immutable and singly-linked, so to join two or more lists
all the elements of the lists would need to be copied into a new list.
This would be slow, so there is no built-in syntax for it.
```
([Carl Bordum Hansen](https://github.com/carlbordum)) and
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The error message one gets when calling a function with the wrong number of
arguments has been improved and now only suggests the relevant missing labels.
For example, this piece of code:
```gleam
pub type Pokemon {
Pokemon(id: Int, name: String, moves: List(String))
}
pub fn best_pokemon() {
Pokemon(198, name: "murkrow")
}
```
Would result in the following error, suggesting the missing labels:
```txt
error: Incorrect arity
┌─ /src/main.gleam:6:3
│
6 │ Pokemon(198, name: "murkrow")
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Expected 3 arguments, got 2
This call accepts these additional labelled arguments:
- moves
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Code generators now reuse existing variables when possible for the record
update syntax, reducing the size of the generated code and number of
variables defined for both Erlang and JavaScript.
```gleam
pub fn main() -> Nil {
let trainer = Trainer(name: "Ash", badges: 0)
battle(Trainer(..trainer, badges: 1))
}
```
Previously this Gleam code would generate this Erlang code:
```erlang
-spec main() -> nil.
main() ->
Trainer = {trainer, 0, <<"Ash"/utf8>>},
battle(
begin
_record = Trainer,
{trainer, 1, erlang:element(3, _record)}
end
).
```
Now this code will be generated instead:
```erlang
-spec main() -> nil.
main() ->
Trainer = {trainer, 0, <<"Ash"/utf8>>},
battle({trainer, 1, erlang:element(3, Trainer)}).
```
([Louis Pilfold](https://github.com/lpil))
- The compiler now allows using bit array options to specify endianness when
constructing or pattern matching on UTF codepoints in bit arrays.
([Surya Rose](https://github.com/GearsDatapacks))
- Calculations are now allowed in the size options of bit array patterns. For
example, the following code is now valid:
```gleam
let assert <> = some_bit_array
```
([Surya Rose](https://github.com/GearsDatapacks))
- On the Erlang target each generated module enables inlining from the Erlang
compiler.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The code generated for division and modulo operators on the JavaScript target
has been improved to avoid performing needless checks.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
### Build tool
- `gleam update`, `gleam deps update`, and `gleam deps download` will now print
a message when there are new major versions of packages available. For
example:
```txt
$ gleam update
Resolving versions
The following dependencies have new major versions available:
gleam_http 1.7.0 -> 4.0.0
gleam_json 1.0.1 -> 3.0.1
lustre 3.1.4 -> 5.1.1
```
([Amjad Mohamed](https://github.com/andho))
- The documentation generator now strips trailing slashes from Gitea/Forgejo
hosts so sidebar "Repository" and "View Source" links never include `//`, and
single-line "View Source" anchors emit `#Lx` instead of `#Lx-x`.
([Aayush Tripathi](https://github.com/aayush-tripathi))
- The build tool can now compile packages that will have already booted the
Erlang compiler application instead of failing.
([Louis Pilfold](https://github.com/lpil))
- `gleam deps list` now uses a tab rather than a space as a separator.
([Louis Pilfold](https://github.com/lpil))
- The build tool now also supports `.cjs` files placed in the `src`, `dev` or
`test` directories.
([yoshi](https://github.com/yoshi-monster))
- The build tool now produces better error messages when version resolution
fails. For example:
```
$ gleam add wisp@1
Resolving versions
error: Dependency resolution failed
There's no compatible version of `gleam_otp`:
- You require wisp >= 1.0.0 and < 2.0.0
- wisp requires mist >= 1.2.0 and < 5.0.0
- mist requires gleam_otp >= 0.9.0 and < 1.0.0
- You require lustre >= 5.2.1 and < 6.0.0
- lustre requires gleam_otp >= 1.0.0 and < 2.0.0
There's no compatible version of `gleam_json`:
- You require wisp >= 1.0.0 and < 2.0.0
- wisp requires gleam_json >= 3.0.0 and < 4.0.0
- You require gleam_json >= 2.3.0 and < 3.0.0
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The `repository` section in `gleam.toml` now allows specifying the
`tag-prefix` property, which is prepended to the default tag.
This makes it possible to have multiple packages with different versions in
the same repository (together with `path`), without breaking links to source
code in documentation.
([Sakari Bergen](https://github.com/sbergen))
### Language server
- It is now possible to use the "Pattern match on variable" code action on
variables on the left hand side of a `use`. For example:
```gleam
pub type User {
User(id: Int, name: String)
}
pub fn main() {
use user <- result.try(load_user())
// ^^^^ Triggering the code action here
todo
}
```
Would result in the following code:
```gleam
pub type User {
User(id: Int, name: String)
}
pub fn main() {
use user <- result.try(load_user())
let User(id:, name:) = user
todo
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The "generate function" and "generate variant" code actions are now
quickfixes, allowing them to be more easily applied to code which is producing
an error.
([Surya Rose](https://github.com/GearsDatapacks))
- The language server now offers a code action to remove needless blocks
wrapping a single expression. For example, in this code snippet:
```gleam
case greeting {
User(name:) -> { "Hello, " <> name }
// ^^^^^^^^^^^^^^^^^^^^^ Triggering the code action
// with the cursor over this block.
Anonymous -> "Hello, stranger!"
}
```
Would be turned into:
```gleam
case greeting {
User(name:) -> "Hello, " <> name
Anonymous -> "Hello, stranger!"
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- It is now possible to trigger the "Add type annotation" code action anywhere
between the start of a function definition and the start of its body. For
example the action can trigger here while it previously wouldn't:
```gleam
pub fn my_lucky_number() {
// ^^ The action can trigger here as well!
11
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
### Formatter
- The formatter now allows more control over how lists are split. By adding a
trailing comma at the end of a list that can fit on a single line, the list
will be split on multiple lines:
```gleam
pub fn my_favourite_pokemon() -> List(String) {
["natu", "chimecho", "milotic",]
}
```
Will be formatted as:
```gleam
pub fn my_favourite_pokemon() -> List(String) {
[
"natu",
"chimecho",
"milotic",
]
}
```
By removing the trailing comma, the formatter will try and fit the list on a
single line again:
```gleam
pub fn my_favourite_pokemon() -> List(String) {
[
"natu",
"chimecho",
"milotic"
]
}
```
Will be formatted back to a single line:
```gleam
pub fn my_favourite_pokemon() -> List(String) {
["natu", "chimecho", "milotic"]
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The formatter now allows more control over how lists are formatted.
If a list is split with multiple elements on the same line, removing the
trailing comma will make sure the formatter keeps each item on its own line:
```gleam
pub fn my_favourite_pokemon() -> List(String) {
[
"This list was formatted", "keeping multiple elements on the same line",
"notice how the formatting changes by removing the trailing comma ->"
]
}
```
Is formatted as:
```gleam
pub fn my_favourite_pokemon() -> List(String) {
[
"This list was formatted",
"keeping multiple elements on the same line",
"notice how the formatting changes by removing the trailing comma ->",
]
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The formatter no longer removes empty lines between list items. In case an
empty line is added between list items they will all be split on multiple
lines. For example:
```gleam
pub fn main() {
[
"natu", "xatu",
"chimeco"
]
}
```
Is formatted as:
```gleam
pub fn main() {
[
"natu",
"xatu",
"chimeco",
]
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
### Bug fixes
- Fixed a bug where the language server would not show type-related code actions
for record fields in custom type definitions.
([cysabi](https://github.com/cysabi))
- Fixed a bug where the "Inline variable" code action would be offered for
function parameters and other invalid cases.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the "Inline variable" code action would not be applied
correctly to variables using label shorthand syntax.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the compiler would emit the same error twice for patterns
with the wrong number of labels.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the language server would generate invalid code when the
"Extract variable" code action was used on a `use` expression.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the compiler would crash when using the `utf8_codepoint`
bit array segment on the JavaScript target.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where `==` and `!=` would return incorrect output for some
JavaScript objects.
([Louis Pilfold](https://github.com/lpil))
- Fixed a bug where specific combinations of options in bit array segments would
not be allowed on the JavaScript target.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where invalid code would be generated for `let assert` in some
cases on the JavaScript target.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where using the prelude `Ok` and `Error` values in a qualified
fashion could cause a conflict with user-defined `Ok` and `Error` values when
generating code on the JavaScript target.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the "Import module" code action would suggest importing
internal modules from other packages.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the language server would not rename variables defined in
alternative patterns.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where trying to rename a type or value from the Gleam prelude
would result in invalid code.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the generated documentation would not be formatted properly.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the language server wouldn't allow you to jump to the
definition of a record from a record update expression.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the language server would allow using the "extract variable"
code action on variables used in record updates.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where a record pattern with a spread `..` would not be formatted
properly.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where fields of custom types named `prototype` would not be
properly escaped on the JavaScript target.
([Surya Rose](https://github.com/GearsDatapacks))
## v1.11.1 - 2025-06-05
### Compiler
- The displaying of internal types in HTML documentation has been improved.
([Louis Pilfold](https://github.com/lpil))
- A warning is now emitted when the same module is imported
multiple times into the same module with different aliases.
([Louis Pilfold](https://github.com/lpil))
### Bug fixes
- Fixed a bug where a bit array segment matching on a floating point number
would match with `NaN` or `Infinity` on the JavaScript target.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
================================================
FILE: changelog/v1.13.md
================================================
# Changelog
## v1.13.0 - 2025-10-19
## v1.13.0-rc2 - 2025-10-06
### Bug fixes
- Fixed a bug where the "Extract function" code action would not properly
extract a `use` expression.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the "Extract function" code action would generate a function
with the wrong type when used on a use expression.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the error message for inexhaustive patterns could show
incorrect extra patterns in addition to the correct missing patterns.
([Adi Salimgereyev](https://github.com/abs0luty))
- Fixed a bug where triggering the "Generate function" code action to generate
a function in a different module could cause the generated function to appear
in the middle of an existing function, resulting in invalid code.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the "turn into pipe" code action would not trigger inside
the final step of a pipeline.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the "pattern match on variable" code action would crash when
used on a variable followed by a case expression.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
## v1.13.0-rc1 - 2025-09-29
### Compiler
- The compiler now applies an optimisation known as "interference based pruning"
when compiling bit array pattern matching where matches are performed at the
start of bit arrays.
This optimisation drastically reduces compile times, memory usage and the
compiled code size, removing many redundant checks.
It is particularly important for network protocol applications where it is
typical to match on some fixed patterns at the start of the bitarray.
For example:
```gleam
pub fn parser_headers(headers: BitArray, bytes: Int) -> Headers {
case headers {
<<"CONTENT_LENGTH" as header, 0, value:size(bytes), 0, rest:bytes>>
| <<"QUERY_STRING" as header, 0, value:size(bytes), 0, rest:bytes>>
| <<"REQUEST_URI" as header, 0, value:size(bytes), 0, rest:bytes>>
// ...
| <<"REDIRECT_STATUS" as header, 0, value:size(bytes), 0, rest:bytes>>
| <<"SCRIPT_NAME" as header, 0, value:size(bytes), 0, rest:bytes>>
-> [#(header, value), ..parse_headers(rest)]
}
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The compiler now raises a warning for unreachable clauses that are matching
on bit array segments that could never match. Consider this example:
```gleam
pub fn get_payload(packet: BitArray) -> Result(BitArray, Nil) {
case packet {
<<200, payload:bytes>> -> Ok(payload)
<<404, _:bits>> -> Error(Nil)
_ -> Ok(packet)
}
}
```
There's a subtle bug here. The second clause can never match since it's
impossible for the first byte of the bit array to have the value `404`.
The new error explains this nicely:
```txt
warning: Unreachable pattern
┌─ /src.gleam:4:5
│
4 │ <<404, _:bits>> -> Error(Nil)
│ ^^^^^^^^^^^^^^^
│ │
│ A 1 byte unsigned integer will never match this value
This pattern cannot be reached as it contains segments that will never
match.
Hint: It can be safely removed.
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The compiler now raises a warning when a function's argument is only passed
along in a recursive call but not actually used for anything. For example:
```gleam
import gleam/io
pub fn greet(x, times) {
case times {
0 -> Nil
_ -> {
io.println("Hello, Joe!")
greet(x, times - 1)
}
}
}
```
In this piece of code the `x` argument is actually never used, and the
compiler will raise the following warning:
```txt
warning: Unused function argument
┌─ /Users/giacomocavalieri/Desktop/prova/src/prova.gleam:3:14
│
3 │ pub fn greet(x, times) {
│ ^ This argument is unused
This argument is passed to the function when recursing, but it's never used
for anything.
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The compiler now emits a better error message for private types marked as
opaque. For example, the following piece of code:
```gleam
opaque type Wibble {
Wobble
}
```
Would result in the following error:
```
error: Private opaque type
┌─ /src/one/two.gleam:2:1
│
2 │ opaque type Wibble {
│ ^^^^^^ You can safely remove this.
Only a public type can be opaque.
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The parsing of opaque private types is now fault tolerant: having a private
opaque type in a module no longer stops the compiler from highlighting other
errors.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The compiler now emits a single warning for multiple negations in a row, while
previously it would emit multiple comments highlighting increasingly longer
spans.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Redundant `_ as x` patterns are now deprecated in favour of `x`.
([eutampieri](https://github.com/eutampieri))
- The compiler will now raise warning for inefficient use of `list.length()`
when trying to check is list empty via `0 < list.length(list)` or
`list.length(list) > 0` as well as in other cases. For example, the following
code:
```gleam
import gleam/list
pub fn main() {
let numbers = [1, 46]
let _ = 0 < list.length(numbers)
let _ = list.length(numbers) > 0
}
```
Would result in following warnings:
```
warning: Inefficient use of `list.length`
┌─ /data/data/com.termux/files/home/test_gleam/src/test_gleam.gleam:5:13
│
5 │ let _ = 0 < list.length(numbers)
│ ^^^^^^^^^^^^^^^^^^^^^^^^
The `list.length` function has to iterate across the whole
list to calculate the length, which is wasteful if you only
need to know if the list is empty or not.
Hint: You can use `the_list != []` instead.
warning: Inefficient use of `list.length`
┌─ /data/data/com.termux/files/home/test_gleam/src/test_gleam.gleam:6:13
│
6 │ let _ = list.length(numbers) > 0
│ ^^^^^^^^^^^^^^^^^^^^^^^^
The `list.length` function has to iterate across the whole
list to calculate the length, which is wasteful if you only
need to know if the list is empty or not.
Hint: You can use `the_list != []` instead.
```
([Andrey Kozhev](https://github.com/ankddev))
- The compiler now provides an improved error message for when trying to define
a constant inside a function. For example, the following code:
```gleam
pub fn deep_thought() -> Int {
const the_answer = 42
the_answer
}
```
Will produce this error message:
```txt
error: Syntax error
┌─ /src/file.gleam:2:3
│
3 │ const the_answer = 43
│ ^^^^^ Constants are not allowed inside functions
All variables are immutable in Gleam, so constants inside functions are not
necessary.
Hint: Either move this into the global scope or use `let` binding instead.
```
([Surya Rose](https://github.com/GearsDatapacks))
- The code generated for blocks on the JavaScript target has been improved and
is now smaller in certain cases.
([Surya Rose](https://github.com/GearsDatapacks))
- Writing a type name followed by `()` now emits an error during analysis
rather than parsing, so it no longer stops the compiler from reporting errors
further in the code.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The compiler now shows a specific syntax error when trying to use an
angle-bracket syntax for generic types or function definitions:
```txt
error: Syntax error
┌─ /src/parse/error.gleam:2:12
│
2 │ type Either {
│ ^ I was expecting `(` here.
Type parameters use lowercase names and are surrounded by parentheses.
type Either(a, b) {
See: https://tour.gleam.run/data-types/generic-custom-types/
```
([Aaron Christiansen](https://github.com/AaronC81))
- Fault tolerance for analysis of labeled fields in variant patterns has
been improved.
([sobolevn](https://github.com/sobolevn))
- Compiler now adds a hint when `#`-styled comments are used. This code:
```gleam
fn some() {
let a = 1
# let b = 2
}
```
Now produces:
```txt
error: Syntax error
┌─ /src/main.gleam:3:5
│
3 │ # let b = 2
│ ^^^ I was not expecting this
Found the keyword `let`, expected one of:
- `(`
Hint: Maybe you meant to create a comment?
Comments in Gleam start with `//`, not `#`
```
([sobolevn](https://github.com/sobolevn))
- The `erlang.application_start_argument` parameter has been added to
`gleam.toml`. This is a string containing an Erlang term that will be written
into the package's Erlang `.app` file if `erlang.application_start_module`
has been set, replacing the default argument of `[]`.
([Louis Pilfold](https://github.com/lpil))
- Generated code for the JavaScript target now includes a public API which can
be used for FFI interacting with Gleam custom types. For example, if you have
this Gleam code:
```gleam
pub type Person {
Teacher(name: String, subject: String)
Student(name: String, age: Int)
}
```
You can use the new API to use the `Person` type in FFI code:
```javascript
import {...} from "./person.mjs";
// Constructing custom types
let teacher = Person$Teacher("Joe Armstrong", "Computer Science");
let student = Person$Student("Louis Pilfold");
let randomPerson = Math.random() > 0.5 ? teacher : student;
// Checking variants
let randomIsTeacher = Person$isTeacher(randomPerson);
// Getting fields
let teacherSubject = Person$Teacher$subject(teacher);
// The `name` field is shared so can be accessed from either variant
let personName = Person$name(randomPerson);
```
([Surya Rose](https://github.com/GearsDatapacks))
### Build tool
- New projects are generated using OTP28 on GitHub Actions.
([Louis Pilfold](https://github.com/lpil))
- The build tool now has a new `hex owner transfer` subcommand to transfer
ownership of existing packages.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- `gleam add` now adds `dependencies` and `dev_dependencies` as tables instead
of inline tables if they are missing.
([Andrey Kozhev](https://github.com/ankddev))
- After dependency resolution the build tool will now print all packages added
and removed, and any versions changed.
([Louis Pilfold](https://github.com/lpil))
- `gleam publish` now blocks publishing packages that contain the default main
function to prevent accidental publishing of unmodified template code.
([Joohoon Cha](https://github.com/jcha0713))
- When generating documentation, the build tool will now print the names of
public type aliases instead of internal type names when annotating functions
and types. For example, for the following code:
```gleam
import my_package/internal
pub type ExternalAlias = internal.InternalRepresentation
pub fn do_thing() -> ExternalAlias { ... }
```
This is what the build tool used to generate:
```gleam
pub fn do_thing() -> @internal InternalRepresentation
```
Whereas now it will not use the internal name, and instead produce:
```gleam
pub fn do_thing() -> ExternalAlias
```
([Surya Rose](https://github.com/GearsDatapacks))
- Support has been added for using Tangled as a repository.
([Naomi Roberts](https://github.com/naomieow))
### Language server
- The language server now offers a code action to remove all the unreachable
clauses in a case expression. For example:
```gleam
pub fn main() {
case find_user() {
Ok(user) -> todo
Ok(Admin) -> todo
// ^^^^^^^^^ This clause is unreachable
Ok(User) -> todo
// ^^^^^^^^ This clause is unreachable
Error(_) -> todo
}
}
```
Hovering over one of the unreachable clauses and triggering the code action
would remove all the unreachable clauses:
```gleam
pub fn main() {
case find_user() {
Ok(user) -> todo
Error(_) -> todo
}
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The "pattern match on variable" can now be triggered on lists. For example:
```gleam
pub fn is_empty(list: List(a)) -> Bool {
// ^^^^ Triggering the action here
}
```
Triggering the action on the `list` argument would result in the following
code:
```gleam
pub fn is_empty(list: List(a)) -> Bool {
case list {
[] -> todo
[first, ..rest] -> todo
}
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The "pattern match on variable" code action can now be triggered on variables
introduced by other patterns. For example:
```gleam
pub fn main() {
let User(name:, role:) = find_user("lucy")
// ^^^^ Triggering the action here
}
```
Triggering the action on another variable like `role` would result in the
following code:
```gleam
pub fn main() {
let User(name:, role:) = find_user("lucy")
case role {
Admin -> todo
Member -> todo
}
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The "pattern match on variable" code action can now be triggered on variables
in case expressions. For example:
```gleam
pub fn main() {
case find_user() {
Ok(user) -> todo
Error(_) -> todo
}
}
```
Triggering the action on the `user` variable would result in the following
code:
```gleam
pub fn main() {
case find_user() {
Ok(Admin) -> todo
Ok(Member) -> todo
Error(_) -> todo
}
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server now offers a quick fix to remove `opaque` from a private
type:
```gleam
opaque type Wibble {
// ^^^ This is an error!
Wobble
}
```
If you hover over the type and trigger the quick fix, the language server will
automatically remove the `opaque` keyword:
```gleam
type Wibble {
Wobble
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server now offers a code action to add the omitted labels in a
call. For example:
```gleam
pub type User {
User(first_name: String, last_name: String, likes: List(String))
}
pub fn main() {
let first_name = "Giacomo"
User(first_name, "Cavalieri", ["gleam"])
//^^^^ Triggering the code action here
}
```
Triggering the code action on the `User` constructor will result in the
following code:
```gleam
pub type User {
User(first_name: String, last_name: String, likes: List(String))
}
pub fn main() {
let first_name = "Giacomo"
User(first_name:, last_name: "Cavalieri", likes: ["gleam"])
}
```
- The "inline variable" code action is now only suggested when hovering over the
relevant variable.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- When hovering over a record field in a record access expression, the language
sever will now show the documentation for that field, if present.
([Surya Rose](https://github.com/GearsDatapacks))
- Renaming a variable from a label shorthand (`name:`) no longer includes the
colon in the rename dialog (`name:` -> `name`)
([fruno](https://github.com/frunobulax-the-poodle))
- The language server now offers a code action to collapse nested case
expressions. Take this example:
```gleam
case user {
User(role: Admin, name:) ->
// Here the only thing we're doing is pattern matching on the
// `name` variable we've just defined in the outer pattern.
case name {
"Joe" -> "Hello, Joe!"
_ -> "Hello, stranger"
}
_ -> "You're not an admin!"
}
```
We could simplify this case expression and reduce nesting like so:
```gleam
case user {
User(role: Admin, name: "Joe") -> "Hello, Joe!"
User(role: Admin, name: _) -> "Hello, stranger"
_ -> "You're not an admin!"
}
```
Now, if you hover over that pattern, the language server will offer the
"collapse nested case" action that will simplify your code like shown in the
example above.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The "Generate function" code action now allows generating function in other
modules. For example, given the following code:
```gleam
// maths.gleam
pub fn add(a: Int, b: Int) -> Int { a + b }
// main.gleam
import maths
pub fn main() -> Int {
echo maths.add(1, 2)
echo maths.subtract(from: 2, subtract: 1)
// ^ Trigger the "Generate function" code action here
}
```
The language sever will edit the `maths.gleam` file:
```gleam
pub fn add(a: Int, b: Int) -> Int { a + b }
pub fn subtract(from from: Int, subtract subtract: Int) -> Int {
todo
}
```
([Surya Rose](https://github.com/GearsDatapacks))
- The "Add type annotations" and "Generate function" code actions now ignore
type variables defined in other functions, improving the generated code.
For example:
```gleam
fn something(a: a, b: b, c: c) -> d { todo }
fn pair(a, b) { #(a, b) }
```
Previously, when triggering the "Add type annotations" code action on the
`pair` function, the language server would have generated:
```gleam
fn pair(a: e, b: f) -> #(e, f) { #(a, b) }
```
However in 1.13, it will now generate:
```gleam
fn pair(a: a, b: b) -> #(a, b) { #(a, b) }
```
([Surya Rose](https://github.com/GearsDatapacks))
- You can now go to definition, rename, etc. from alternative patterns!
```gleam
case wibble {
Wibble | Wobble -> 0
// ^- Previously you could not trigger actions from here
}
```
([fruno](https://github.com/fruno-bulax))
- When showing types of values on hover, or adding type annotations, the language
server will now prefer public type aliases to internal types. For example, if
the "Add type annotations" code action was triggered on the following code:
```gleam
import lustre/html
import lustre/element
import lustre/attribute
pub fn make_link(attribute, element) {
html.a([attribute], [elements])
}
```
Previously, the following code would have been generated:
```gleam
pub fn make_link(
attribute: vattr.Attribute,
element: vdom.Element(a)
) -> vdom.Element(a) {
html.a([attribute], [elements])
}
```
Which references internal types which should not be imported by the user.
However, now the language server will produce the following:
```gleam
pub fn make_link(
attribute: attribute.Attribute,
element: element.Element(a)
) -> element.Element(a) {
html.a([attribute], [elements])
}
```
([Surya Rose](https://github.com/GearsDatapacks))
- The language server now offers the `convert to case` code action only if a
single `let assert` expression is selected.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server now offers an "Extract function" code action to extract a
selected piece of code into a separate function. For example:
```gleam
const head_byte_count = 256
pub fn get_head_of_file() {
let assert Ok(contents) = read_file()
case contents {
//^ Select from here
<> -> Ok(head)
_ -> Error(Nil)
}
//^ Until here
}
```
Would become:
```gleam
const head_byte_count = 256
pub fn get_head_of_file() {
let assert Ok(contents) = read_file()
function(contents)
}
fn function(contents: BitArray) -> Result(BitArray, Nil) {
case contents {
<> -> Ok(head)
_ -> Error(Nil)
}
}
```
You can then use language server renaming to choose an appropriate name for
the new function.
([Surya Rose](https://github.com/GearsDatapacks))
### Formatter
- The formatter now removes needless multiple negations that are safe to remove.
For example, this snippet of code:
```gleam
pub fn useless_negations() {
let lucky_number = --11
let lucy_is_a_star = !!!False
}
```
Is rewritten as:
```gleam
pub fn useless_negations() {
let lucky_number = 11
let lucy_is_a_star = !False
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Redundant `_ as x` patterns are rewritten to `x`.
([eutampieri](https://github.com/eutampieri))
- The formatter no longer removes blocks from case clause guards.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The formatter now properly formats tuple return annotation with comments.
([Andrey Kozhev](https://github.com/ankddev))
### Bug fixes
- Fixed a bug where literals using `\u{XXXX}` syntax in bit array pattern
segments were not translated to Erlang's `\x{XXXX}` syntax correctly.
([Benjamin Peinhardt](https://github.com/bcpeinhardt))
- Fixed a bug where `echo` could crash on JavaScript if the module contains
record variants with the same name as some built-in JavaScript objects.
([Louis Pilfold](https://github.com/lpil))
- Fixed a bug where the compiler would highlight an entire double negation
expression as safe to remove, instead of just highlighting the double
negation.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the compiler would crash if there was an invalid version
requirement in a project's `gleam.toml`.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the compiler would suggest a discouraged project name as an
alternative to the reserved `gleam` name.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where Forgejo source URLs in the HTML documentation could be
incorrectly structured.
([Louis Pilfold](https://github.com/lpil))
- The compiler now emits an error when a module in the `src` directory imports
a dev dependency, while previously it would incorrectly let these
dependencies to be imported.
([Surya Rose](https://github.com/GearsDatapacks))
- Erroneous extra fields in `gleam.toml` dependency specifications will no
longer be siltently ignored. An error is now returned highlighting the
problem instead.
([Louis Pilfold](https://github.com/lpil))
- Fixed a bug where renaming a constant which is referenced in another module
inside a guard would generate invalid code.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where `echo .. as ..` message will be omitted in browser target.
([Andrey Kozhev](https://github.com/ankddev))
- Fixed a bug where renaming a variable used in a record update would produce
invalid code in certain situations.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where adding `echo` to the subject of a `case` expression would
prevent variant inference from working correctly.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the compiler would suggest to use a discarded value defined
in a different function.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the formatter would format a panic message adding more
nesting than necessary.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the language server wouldn't offer the "unqualify" code
action if used on a type alias.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the language server would fail to rename an external
function with no body.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the compiler allowed to write a guard with an empty clause.
([Tristan-Mihai Radulescu](https://github.com/Courtcircuits))
- Fixed a bug where switching from a hex dependency to a git dependency would
result in an error from the compiler.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the compiler would reference a redeclared variable in a let
assert message, instead of the original variable, on the Erlang target.
([Danielle Maywood](https://github.com/DanielleMaywood))
- Fixed a bug where the compiler would report an imported module as unused if it
were used by private functions only.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the "Extract variable" code action would shadow existing
variables, constants and function names.
([Matias Carlander](https://github.com/matiascr))
- Fixed a bug where the language server would not fill in the missing labels of
a pattern correctly, generating invalid code.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where invalid code was being generated when using the "Extract
variable" code action inside an anonymous function.
([Matias Carlander](https://github.com/matiascr))
- Fixed a bug where running `gleam update` would not properly update git
dependencies unless `gleam clean` was run first.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the compiler would produce wrong JavaScript code for binary
pattern matching expressions using literal strings and byte segments.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the build tool would crash when trying to add transitive dependency.
([Andrey Kozhev](https://github.com/ankddev))
================================================
FILE: changelog/v1.14.md
================================================
# Changelog
## v1.14.0 - 2025-12-25 🎁
### Bug fixes
- Fixed a bug where using bit array segments in guard clauses could cause
incorrect code to be generated on the JavaScript target.
([Surya Rose](https://github.com/GearsDatapacks))
## v1.14.0-rc3 - 2025-12-21
### Bug fixes
- Fixed a bug where checking for equality with a variant with no fields using
qualified syntax would generate invalid code on the JavaScript target.
([Surya Rose](https://github.com/GearsDatapacks))
## v1.14.0-rc2 - 2025-12-19
### Bug fixes
- Fixed a bug where the formatter would remove `@external` attributes from
custom types.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where updating records with unlabelled fields would result in
invalid code.
([Surya Rose](https://github.com/GearsDatapacks))
## v1.14.0-rc1 - 2025-12-15
### Compiler
- The output of `echo` when printing atoms has been updated to use
`atom.create("...")` instead of `atom.create_from_string("...")`.
([Patrick Dewey](https://github.com/ptdewey))
- Patterns aliasing a string prefix have been optimised to generate faster code
on the Erlang target.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Type inference for constants is now fault tolerant, meaning the compiler won't
stop at the first error as it is typing constants.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Analysis is now fault tolerant in the presence of errors in field definitions
of custom type variants.
([Adi Salimgereyev](https://github.com/abs0luty))
- The compiler now emits a warning when a module contains no public definitions
and prevents publishing packages with empty modules to Hex.
([Vitor Souza](https://github.com/vit0rr))
- The `@external` annotation is now supported for external types. It allows
users to point an external type definition to a specific Erlang or TypeScript
type. For example, the `dict.Dict` type from the standard library can now be
written as the following:
```gleam
@external(erlang, "erlang", "map")
@external(javascript, "../dict.d.mts", "Dict")
pub type Dict(key, value)
```
([Surya Rose](https://github.com/GearsDatapacks))
- When matching the wrong number of subjects, the compiler now pinpoints the
error location instead of marking the entire branch.
```gleam
case wibble {
0, _ -> 1
^^^^ Expected 1 pattern, got 2
0 | -> 1
^ I was expecting a pattern after this
}
```
([fruno](https://github.com/fruno-bulax/))
- Missing patterns in error messages and the "Add missing patterns" code action
are no longer sorted lexicographically. Instead, they now consider the order
in which variants were defined. As programmers often group "related" variants
together, this should mean less reshuffling after inserting missing patterns!
([fruno](https://github.com/fruno-bulax/))
- The performance of `==` and `!=` has been improved for fieldless custom type
variants when compiling to JavaScript. This was done by generating comparison
code specific to the custom type rather than using the generic equality check
code.
([Nafi](https://github.com/re-masashi))
- The lowercase bool pattern error is no longer a syntax error, but instead a
part of the analysis step. This allows the entire module to be analyzed,
rather than stopping at the syntax error.
([mxtthias](https://github.com/mxtthias))
- Exhaustiveness checks for ints and floats now correctly handle unreachable
cases in which the numbers contain underscores (i.e. `10` and `1_0`).
Float exhaustiveness checks also now correctly identify unreachable cases
containing scientific notation or trailing zeros (i.e. `100` and `1e2`).
([ptdewey](https://github.com/ptdewey))
- The compiler now emits a warning when a doc comment is not attached to a
definition due to a regular comment in between. For example, in the following
code:
```gleam
/// This documentation is not attached
// This is not a doc comment
/// This is actual documentation
pub fn wibble() {
todo
}
```
Will now produce the following warning:
```txt
warning: Detached doc comment
┌─ src/main.gleam:1:4
│
1 │ /// This documentation is not attached
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This is not attached to a definition
This doc comment is followed by a regular comment so it is not attached to
any definition.
Hint: Move the comment above the doc comment
```
([Surya Rose](https://github.com/GearsDatapacks))
- The [interference-based pruning](https://gleam.run/news/formalising-external-apis/#Improved-bit-array-exhaustiveness-checking)
from 1.13 has been extended to int segments!
Aside from the various performance improvements, this allows the compiler to
mark more branches as unreachable.
```gleam
case bits {
<<"a">> -> 0
<<97>> -> 1
// ^- This branch is unreachable because it's equal to "a".
<<0b1:1, _:1>> -> 2
<<0b11:2>> -> 3
// ^- This branch is unreachable because the branch before it already covers it.
_ -> 99
}
```
([fruno](https://github.com/fruno-bulax/))
- Comparison of record constructors with non-zero arity always produces `False`,
because under the hood during code generation they become anonymous functions:
```gleam
pub type Wibble {
Wobble(String)
}
pub fn main() {
echo Wobble == Wobble // False
}
```
Previously compiler produced false-positive redundant comparison warning, which
is now removed:
([Adi Salimgereyev](https://github.com/abs0luty))
- Record update syntax can now be used in constant definitions. For example:
```gleam
pub const base_http_config = HttpConfig(
host: "0.0.0.0",
port: 8080,
use_tls: False,
log_level: Info,
)
pub const dev_http_config = HttpConfig(
..base_http_config,
port: 4000,
log_level: Debug,
)
pub const prod_http_config = HttpConfig(
..base_http_config,
port: 80,
use_tls: True,
log_level: Warn,
)
```
([Adi Salimgereyev](https://github.com/abs0luty))
### Build tool
- The help text displayed by `gleam dev --help`, `gleam test --help`, and
`gleam run --help` has been improved: now each one states which function it's
going to run.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The `--invert` and `--package` options of `gleam deps tree` are now mutually
exclusive; if both options are given the command will fail. Previously,
`--invert` would be silently ignored if given together with `--package`.
([Evan Silberman](https://github.com/silby))
- Updated to use the latest Elixir API, so a warning would not be shown when
compiling Elixir file in a Gleam project.
([Andrey Kozhev](https://github.com/ankddev))
- The build tool now has a new `gleam deps outdated` command that shows outdated
versions for dependencies. For example:
```sh
$ gleam deps outdated
Package Current Latest
------- ------- ------
wibble 1.4.0 1.4.1
wobble 1.0.1 2.3.0
```
([Vladislav Shakitskiy](https://github.com/vshakitskiy))
- The format used for `gleam deps list` and the notice of available major
version upgrades has been improved.
([Louis Pilfold](https://github.com/lpil))
- `gleam new` now creates the project directory using the confirmed project
name when a suggested rename is accepted.
([Adi Salimgereyev](https://github.com/abs0luty))
- The build tool now provides better error message when trying to build Git
dependencies without Git installed. Previously, it would show this error:
```txt
error: Shell command failure
There was a problem when running the shell command `git`.
The error from the shell command library was:
Could not find the stdio stream
```
Now it will show:
```txt
error: Program not found
The program `git` was not found. Is it installed?
Documentation for installing Git can be viewed here:
https://git-scm.com/book/en/v2/Getting-Started-Installing-Git
```
([Andrey Kozhev](https://github.com/ankddev))
### Language server
- The language server can now offer a code action to merge case clauses with
the same body. For example:
```gleam
case user {
Admin(name:, ..) -> todo
//^^^^^^^^^^^^^^^^^^^^^^^^
Guest(name:, ..) -> todo
//^^^^^^^^^^^^^^^^ Selecting these two branches you can
// trigger the "Merge case branches" code action
_ -> todo
}
```
Triggering the code action would result in the following code:
```gleam
case user {
Admin(name:, ..) | Guest(name:, ..) -> todo
_ -> todo
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The "generate function" code action can now pick better names for arguments
that use the record access syntax. For example:
```gleam
pub type User {
User(id: Int, name: String)
}
pub fn go(user: User) {
authenticate(user.id, user.name)
todo
}
```
Having the language server generate the missing `authenticate` function will
produce the following code:
```gleam
pub fn authenticate(id: Int, name: String) {
todo
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The "inline variable" code action can now trigger when used over the `let`
keyword of a variable to inline.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The "add omitted labels" code action can now be used in function calls where
some of the labels have been provided already.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The "generate function" code action can now trigger when used over constant
values as well.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Grouping of related diagnostics should now work across more editors.
Warnings will display together with their hints and you no longer have
"go to next diagnostic" twice in a row. Zedlings rejoice!
([fruno](https://github.com/fruno-bulax/))
- The "pattern match on variable" code action can now pick better names when
used on tuples.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- When renaming, if the new name is invalid, the language server will produce an
error message instead of silently doing nothing.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- When providing autocomplete suggestions, the language server will now
prioritise values which match the expected type of the value being completed.
([Surya Rose](https://github.com/GearsDatapacks))
- The language server now offers code action to add type annotations to all
functions and constants. For example,
```gleam
pub const answer = 42
pub fn add(x, y) {
x + y
}
pub fn add_one(thing) {
// ^ Triggering "Annotate all top level definitions" code action here
let result = add(thing, 1)
result
}
```
Triggering the "Annotate all top level definitions" code action over
the name of function `add_one` would result in following code:
```gleam
pub const answer: Int = 42
pub fn add(x: Int, y: Int) -> Int {
x + y
}
pub fn add_one(thing: Int) -> Int {
let result = add(thing, 1)
result
}
```
([Andrey Kozhev](https://github.com/ankddev))
- Qualify and unqualify code actions can now trigger when used over constant
values as well.
([Vladislav Shakitskiy](https://github.com/vshakitskiy))
### Formatter
### Bug fixes
- Fixed two bugs that made gleam not update the manifest correctly, causing
it to hit hex for version resolution on every operation and quickly reach
request limits in large projects.
([fruno](https://github.com/fruno-bulax/))
- Fixed a bug where renaming a variable from an alternative pattern would not
rename all its occurrences.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The compiler now reports an error for literal floats that are outside the
floating point representable range on both targets. Previously it would only
do that when compiling on the Erlang target.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a typo in the error message emitted when trying to run a module that
does not have a main function.
([Louis Pilfold](https://github.com/lpil))
- Fixed a bug where the "Generate function" code action would be incorrectly
offered when calling a function unsupported by the current target, leading to
invalid code if the code action was accepted.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the formatter would not remove the right number of double
negations from literal integers.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a typo for the "Invalid number of patterns" error.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a stack overflow when type checking some case expressions with
thousands of branches.
([fruno](https://github.com/fruno-bulax/))
- The "add omitted label" code action no longer adds labels to arguments
being piped in or the callbacks of `use`.
([fruno](https://github.com/fruno-bulax))
- Fixed a bug that caused the compiler to incorrectly optimise away runtime
size checks in bit array patterns on the javascript target if they used
calculations in the size of a segment (`_:size(wibble - wobble)`).
([fruno](https://github.com/fruno-bulax/))
- Add a missing BitArray constructor return type in the prelude's TypeScript
definitions.
([Richard Viney](https://github.com/richard-viney))
- Fixed a bug where the BEAM would be shut down abruptly once the program had
successfully finished running.
([Louis Pilfold](https://github.com/lpil))
- Fixed a bug where the "pattern match on variable" code action would generate
invalid code when applied on a list's tail.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the "pattern match on variable" code action would generate
invalid patterns by repeating a variable name already used in the same
pattern.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the "generate function" code action would pop up for
variants.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where useless comparison warnings for floats compared literal
strings, claiming for example that `1.0 == 1.` was always false.
([fruno](https://github.com/fruno-bulax/))
- Fixed a bug where pattern variables in case clause guards would incorrectly
shadow outer scope variables in other branches when compiling to JavaScript.
([Elias Haider](https://github.com/EliasDerHai))
- Fix invalid TypeScript definition being generated for variant constructors
with long names that take no arguments.
([Richard Viney](https://github.com/richard-viney))
- Fixed a bug where the formatter would remove the `@deprecated` attribute from
constants.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where invalid code would be generated on the JavaScript target in
cases where an underscore followed the decimal point in a float literal.
([Patrick Dewey](https://github.com/ptdewey))
- Typos in the error message shown when trying to install a non-existent package
have been fixed.
([Ioan Clarke](https://github.com/ioanclarke))
- Fixed a bug where the compiler would generate invalid Erlang and TypeScript
code for unused opaque types referencing private types.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the type checker would allow invalid programs when a large
group of functions were all mutually recursive.
([Surya Rose](https://github.com/GearsDatapacks))
- The compiler now provides a clearer error message when a function's return
type is mistakenly declared using `:` instead of `->`.
([Gurvir Singh](https://github.com/baraich))
- Fixed a bug where the data generated for searching documentation was in the
wrong format, preventing it from being used by Hexdocs search.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the "collapse nested case" code action would produce invalid
code on a list tail pattern.
([Matias Carlander](https://github.com/matiascr))
- Fixed two bugs that made gleam not update the manifest correctly, causing
it to hit hex for version resolution on every operation and quickly reach
request limits in large projects.
([fruno](https://github.com/fruno-bulax/))
================================================
FILE: changelog/v1.15.md
================================================
# Changelog
## v1.15.0 - 2026-03-16
- Fixed a bug where the language server wouldn't show the correct hover for some
patterns.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
## v1.15.0-rc2 - 2026-03-16
### Bug fixes
- Marked the `bitSize` and `bitOffset` fields of `BitArray$BitArray` TypeScript
definition as optional.
([acandoo](https://github.com/acandoo))
- Fixed invalid JavaScript code generation when ints or floats had prefixed `0`s
before and after an `_` (e.g. `000_001`).
([Gavin Morrow](https://github.com/gavinmorrow))
## v1.15.0-rc1 - 2026-03-04
### Compiler
- The compiler now reports an error when int and float binary operators are
used incorrectly in case expression guards.
([Adi Salimgereyev](https://github.com/abs0luty))
- The compiler now supports string concatenation in clause guards:
```gleam
case message {
#(version, action) if version <> ":" <> action == "v1:delete" ->
handle_delete()
_ -> ignore()
}
```
([Adi Salimgereyev](https://github.com/abs0luty))
- Improved the code generated on the Erlang target when dividing a `Float`
number by the literal number `0.0`.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The compiler no longer shows the structure of internal types when displaying
an "Inexhaustive patterns" error, making it harder to inadvertently rely on
internal implementation details.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The JavaScript prelude TypeScript API now contains the `BitArray$isBitArray`
and `BitArray$BitArray$data` functions.
([Louis Pilfold](https://github.com/lpil))
- The type-checking JavaScript functions for Gleam data structure now use the
`value is TypeName` TypeScript return type rather than `boolean`.
([Louis Pilfold](https://github.com/lpil))
- The compiler now shows better error message on passing unexpected labeled
arguments by taking into account, whether it's a function or a constructor.
([Andrey Kozhev](https://github.com/ankddev))
### Build tool
- Upgraded `actions/checkout` from v4 to v6 in the GitHub Actions workflow used
by `gleam new`.
([Christian Widlund](https://github.com/chrillep))
- When adding a package that does not exist on Hex, the message is a bit
friendlier.
([Ameen Radwan](https://github.com/Acepie))
- The `gleam.toml` format is now consistent. The two sausage-case fields
(`dev-dependencies` and `tag-prefix`) have been replaced by snake_case
versions. Files using the old names will continue to work.
([Louis Pilfold](https://github.com/lpil))
- The password used for encrypting new local Hex API keys must now be at least
8 characters in length.
([Louis Pilfold](https://github.com/lpil))
- The `gleam help add`, `gleam help deps`, and `gleam help docs` commands have
been improved with much more detailed documentation output.
([Louis Pilfold](https://github.com/lpil))
- When attempting to publish a package on Hex with an already taken name,
the message is clearer.
([vyacheslavhere](https://github.com/vyacheslavhere))
- The build tool will now refuse to publish any package that has the default
README generated by the `gleam new` command.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The build tool now uses OAuth and time-based one-time-passwords for
authentication with Hex, improving security.
Any legacy API tokens previously stored will be revoked after authenticating
using OAuth.
([Louis Pilfold](https://github.com/lpil))
- The build tool will now refuse to publish any package that has no README, or
an empty README.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
### Language server
- The language server now allows extracting the start of a pipeline into a
variable.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server now shows hover information when hovering over custom type
definitions.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server now shows hover information when hovering over a custom
type's constructors.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server is now smarter when producing autocompletions. Imagine
you're updating your code to fully qualify the uses of the `Json` type:
```gleam
pub fn payload() -> js|Json
// ^ typing the module name
```
Accepting the `json.Json` completion will now produce the correct `json.Json`
annotation rather than generating invalid code: `json.JsonJson`.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server now suggests completions for keywords like `echo`,
`panic`, and `todo`.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The "Add missing patterns" code action will insert a catch all pattern for
internal types, making it harder to inadvertently rely on internal
implementation details.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server will no longer show completions for the fields of internal
types outside the module they're defined in, making it harder to inadvertently
rely on internal implementation details.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server now suggests a quick-fix code action for when a custom type
definition uses a type parameter in its variants that have not been declared in
its header.
([Andi Pabst](https://github.com/andipabst))
- The `Extract function` code action now provides more ergonomic/idiomatic
refactorings when used on anonymous functions.
([Hari Mohan](https://github.com/seafoamteal))
- It's now possible to find references and rename variables in string prefix
patterns.
```gleam
case wibble {
"1" as digit <> rest -> digit <> rest
// ^^^^^ ^^^^
// You can now trigger "Find references" and "Rename" from here
}
```
([Igor Castejón](https://github.com/IgorCastejon))
- The language server now offers a rename for module when the cursor is placed
over its import statement or when placed on its name or alias somewhere. For
example,
```gleam
import lustre/element
import lustre/element/html
import lustre/event
fn view(model: Int) -> element.Element(Msg) {
// ^ Renaming module to `el` here
let count = int.to_string(model)
html.div([], [
html.button([event.on_click(Incr)], [element.text(" + ")]),
html.p([], [element.text(count)]),
html.button([event.on_click(Decr)], [element.text(" - ")]),
])
}
```
Using renaming when hovering on module name will would result in the
following code:
```gleam
import lustre/element as el
import lustre/element/html
import lustre/event
fn view(model: Int) -> el.Element(Msg) {
let count = int.to_string(model)
html.div([], [
html.button([event.on_click(Incr)], [el.text(" + ")]),
html.p([], [el.text(count)]),
html.button([event.on_click(Decr)], [el.text(" - ")]),
])
}
```
([Vladislav Shakitskiy](https://github.com/vshakitskiy))
- The "Fill labels" code action now uses variables from scope when they match
the label name and expected type, using shorthand syntax (`name:`) instead of
`name: todo`.
([Vladislav Shakitskiy](https://github.com/vshakitskiy))
- The "Interpolate String" code action now lets the user "cut out" any portion
of a string, regardless of whether it is a valid Gleam identifier or not.
([Hari Mohan](https://github.com/seafoamteal))
- When interpolating an expression at the very start or the very end
of a string, redundant empty strings are no longer added before/after the
interpolated expression.
([Hari Mohan](https://github.com/seafoamteal))
- The language server now performs best-effort zero value generation for
`decode.failure` when using the `Generate Dynamic Decoder` code action.
([Hari Mohan](https://github.com/seafoamteal))
- The `Generate Dynamic Decoder` and `Generate To-JSON Function` code action
now generate a decoder and an encoder, respectively, for `Nil` values.
([Hari Mohan](https://github.com/seafoamteal))
- The language server now supports renaming, go to definition, hover, and
finding references from expressions in case clause guards.
([Surya Rose](https://github.com/GearsDatapacks))
- The language server now supports `textDocument/foldingRange`, enabling
folding for contiguous import blocks and multiline top-level definitions such
as function bodies, custom types, constants, and type aliases.
For example, this import block:
```gleam
import gleam/int
import gleam/list
import gleam/string
```
can now be folded in the editor to:
```gleam
import gleam/int ...
```
([Aayush Tripathi](https://github.com/aayush-tripathi))
- The function signature helper now displays original function definition
generic names when arguments are unbound. For example, in this code:
```gleam
pub fn wibble(x: something, y: fn() -> something, z: anything) { Nil }
pub fn main() {
wibble( )
// ↑
}
```
will show a signature help
```gleam
wibble(something, fn() -> something, anything)
```
instead of
```gleam
wibble(a, fn() -> a, b) -> Nil
```
([Samuel Cristobal](https://github.com/scristobal))
### Formatter
- The formatter no longer wraps multiple tuple or field access into a block.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
### Bug fixes
- The compiler now emits correctly-scoped JavaScript code for `case` expressions
whose subjects directly match one of the branches.
([Justin Lubin](https://github.com/justinlubin))
- Fixed a bug where some bit array patterns would erroneously be marked as
unreachable.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the Gleam standard library's `dict.each` function would
incorrectly be assumed to be pure.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The compiler now correctly tracks the minimum required version for constant
record updates to be `>= 1.14.0`.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The compiler now correctly tracks the minimum required version for
expressions in `BitArray`s' `size` option to be `>= 1.12.0`.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the formatter would not properly format some function calls
if the last argument was followed by a trailing comment.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the compiler would generate invalid code on the JavaScript
target when using a `case` expression as the right hand side of an equality
check in an `assert`.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the compiler would generate invalid code on the JavaScript
target for some `case` expressions using clause guards.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The formatter no longer stack overflows trying to format lists with many
items.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the formatter would not be able to consistently format a
constant list with an `@internal` attribute.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The "convert to pipe" code action now works on nested function calls as well,
rather than always being applied to the outermost one.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the compiler would not detect and reject duplicate modules
in a project's dependencies.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server no longer shows completions when typing a number.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server no longer recommends the deprecated `@target` attribute.
([Hari Mohan](https://github.com/seafoamteal))
- The compiler no longer crashes when trying to pattern match on a
`UtfCodepoint`.
([Hari Mohan](https://github.com/seafoamteal))
- Fixed a bug that would result in not being able to rename an aliased pattern.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed JavaScript codegen bug for `assert` when using `&&` with comparison
operators on the right side.
([vyacheslavhere](https://github.com/vyacheslavhere))
- Added an error message when attempting to update packages that are not
dependencies of the project, instead of failing silently.
([Etienne Boutet](https://github.com/EtienneBoutet),
[Vladislav Shakitskiy](https://github.com/vshakitskiy))
- The build tool now doesn't perform code generation when exporting package
interface.
([Andrey Kozhev](https://github.com/ankddev))
- The "Extract constant" code action now correctly places new constant when
function has documentation. For example,
```gleam
/// Wibble does some wobbling
pub fn wibble() {
let x = "wobble"
// ^ Trigger "Extract constant" here
x
}
```
Previously, it would incorrectly place it below doc comment:
```gleam
/// Wibble does some wobbling
const x = "wobble"
pub fn wibble() {
x
}
```
Now it will correctly place constant above doc comment:
```gleam
const x = "wobble"
/// Wibble does some wobbling
pub fn wibble() {
x
}
```
([Andrey Kozhev](https://github.com/ankddev))
- Fixed a bug where renaming would not work properly if there was an error in
target file.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where generics in custom types would not be properly generated
when emitting TypeScript declarations.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where dev dependencies would be compiled and included in
production builds such as `gleam export erlang-shipment`.
([John Downey](https://github.com/jtdowney))
- Fixed a bug where the package cache would not properly be reset when a version
of a package was replaced on Hex.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a crash (`bad_generator`) that could occur when a linked OTP process
exits with a non-standard exit reason.
([John Downey](https://github.com/jtdowney))
- Fixed a bug where diagnostic about incorrect `size` and `unit` options would
use incorrect error location.
([Andrey Kozhev](https://github.com/ankddev))
- `gleam add` now adds correct constraints for pre-release versions.
([Andrey Kozhev](https://github.com/ankddev))
- Fixed a bug where changes to a path dependency's own dependencies were not
detected when rebuilding the root project, causing the compiler to report
errors about missing modules.
([daniellionel01](https://github.com/daniellionel01))
- Fixed a bug where the compiler would crash when type-checking record
updates if the constructor definition has a positional field defined after
labelled fields.
([Hari Mohan](https://gituhub.com/seafoamteal))
================================================
FILE: changelog/v1.2.md
================================================
# Changelog
## v1.2.0 - 2024-05-27
## v1.2.0-rc2 - 2024-05-27
### Bug fixes
- Fixed a bug where the formatter would incorrectly move comments at the start
of an anonymous function to the end of the arguments.
([Ameen Radwan](https://github.com/Acepie))
- Fixed a bug where the formatter would not indent a multiline function used
in a pipeline.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the compiler would raise a warning for matching on a literal
value if the case expression is used just for its guards.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where not all the analysis errors would be presented to the
programmer. ([Louis Pilfold](https://github.com/lpil))
## v1.2.0-rc1 - 2024-05-23
### Build tool
- A helpful error message is now shown if the `manifest.toml` file has been
edited to be invalid in some way.
```
error: Corrupt manifest.toml
The `manifest.toml` file is corrupt.
Hint: Please run `gleam update` to fix it.
```
([zahash](https://github.com/zahash))
- The error message shown when unable to find package versions that satisfy all
the version constraints specified for a project's dependencies has been
greatly improved.
```
error: Dependency resolution failed
An error occurred while determining what dependency packages and
versions should be downloaded.
The error from the version resolver library was:
Unable to find compatible versions for the version constraints in your
gleam.toml. The conflicting packages are:
- hellogleam
- lustre_dev_tools
- glint
```
([zahash](https://github.com/zahash))
- A link to the package on Hex is no longer auto-added to the HTML documentation
when building them locally. It is still added when publishing to Hex.
([Pi-Cla](https://github.com/Pi-Cla))
- An error is now emitted when compiling to Erlang and there is a Gleam module
that would overwrite a built-in Erlang/OTP module, causing cryptic errors and
crashes.
([Louis Pilfold](https://github.com/lpil))
```
error: Erlang module name collision
The module `src/code.gleam` compiles to an Erlang module named `code`.
By default Erlang includes a module with the same name so if we were to
compile and load your module it would overwrite the Erlang one, potentially
causing confusing errors and crashes.
Hint: Rename this module and try again.
```
- New subcommand `gleam hex revert` added.
- You can specify the options like this:
`gleam hex revert --package gling --version 1.2.3`
- A new package can be reverted or updated within 24 hours of it's initial
publish, a new version of an existing package can be reverted or updated
within one hour.
- You could already update packages even before this release by running:
`gleam publish` again.
([Pi-Cla](https://github.com/Pi-Cla))
- When the user tries to replace a release without the `--replace` flag
the error message now mentions the lack of a `--replace` flag.
```
error: Version already published
Version v1.0.0 has already been published.
This release has been recently published so you can replace it
or you can publish it using a different version number
Hint: Please add the --replace flag if you want to replace the release.
```
([Pi-Cla](https://github.com/Pi-Cla))
### Compiler
- The compiler will now raise a warning for `let assert` assignments where the
assertion is redundant.
```
warning: Redundant assertion
┌─ /home/lucy/src/app/src/app.gleam:4:7
│
4 │ let assert x = get_name()
│ ^^^^^^ You can remove this
This assertion is redundant since the pattern covers all possibilities.
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Empty case expressions are no longer parse errors and will instead be
exhaustiveness errors.
([Race Williams](https://github.com/raquentin))
- Improve error message if importing type using the value import syntax or vice
versa.
```
error: Unknown module field
┌─ /src/one/two.gleam:1:19
│
1 │ import gleam/one.{One}
│ ^^^ Did you mean `type One`?
`One` is only a type, it cannot be imported as a value.
```
```
error: Unknown module type
┌─ /src/one/two.gleam:1:19
│
1 │ import gleam/two.{type Two}
│ ^^^^^^^^ Did you mean `Two`?
`Two` is only a value, it cannot be imported as a type.
```
([Pi-Cla](https://github.com/Pi-Cla/))
- The compiler will now raise a warning when you try to use `todo` or `panic` as
if they were functions: this could previously lead to a confusing behaviour
since one might expect the arguments to be printed in the error message.
The error message now suggests the correct way to add an error message to
`todo` and `panic`.
```
warning: Todo used as a function
┌─ /src/warning/wrn.gleam:2:16
│
2 │ todo(1)
│ ^
`todo` is not a function and will crash before it can do anything with
this argument.
Hint: if you want to display an error message you should write
`todo as "my error message"`
See: https://tour.gleam.run/advanced-features/todo/
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Improve error message when something that is not a function appears on the
right hand side of `<-` in a `use` expression.
```txt
error: Type mismatch
┌─ /src/one/two.gleam:2:8
│
2 │ use <- 123
│ ^^^
In a use expression, there should be a function on the right hand side of
`<-`, but this value has type:
Int
See: https://tour.gleam.run/advanced-features/use/
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Improve error message when a function with the wrong number of arguments
appears on the right hand side of `<-` in a `use` expression.
```txt
error: Incorrect arity
┌─ /src/one/two.gleam:3:8
│
3 │ use <- func
│ ^^^^ Expected no arguments, got 1
The function on the right of `<-` here takes no arguments.
But it has to take at least one argument, a callback function.
See: https://tour.gleam.run/advanced-features/use/
```
```txt
error: Incorrect arity
┌─ /src/one/two.gleam:3:8
│
3 │ use <- f(1, 2)
│ ^^^^^^^ Expected 2 arguments, got 3
The function on the right of `<-` here takes 2 arguments.
All the arguments have already been supplied, so it cannot take the
`use` callback function as a final argument.
See: https://tour.gleam.run/advanced-features/use/
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Improve error message when the callback function of a `use` expression returns
a value with the wrong type.
Now the error will point precisely to the last statement and not complain
about the whole block saying it has the wrong function type.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The compiler will now raise a warning when pattern matching on a literal value
like a list, a tuple, integers, strings, etc.
```
warning: Redundant list
┌─ /src/warning/wrn.gleam:2:14
│
2 │ case [1, 2] {
│ ^^^^^^ You can remove this list wrapper
Instead of building a list and matching on it, you can match on its
contents directly.
A case expression can take multiple subjects separated by commas like this:
case one_subject, another_subject {
_, _ -> todo
}
See: https://tour.gleam.run/flow-control/multiple-subjects/
```
```
warning: Match on a literal value
┌─ /src/warning/wrn.gleam:4:8
│
4 │ case 1 {
│ ^ There's no need to pattern match on this value
Matching on a literal value is redundant since you can already tell which
branch is going to match with this value.
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The compiler will now continue module analysis when there are errors in top
level definitions. This means that when these errors occur the compiler will
continue analysing the rest of the code to find other errors and type
information.
When using the build tool this means that the programmer will be shown
multiple error messages when there are multiple problems in a module.
When using the language server multiple error diagnostics will be shown, and
the compiler will get updated type information about the code even when there
are errors. This should improve the accuracy of feedback and suggestions from
the language server as its information about the code will be more up-to-date.
([Ameen Radwan](https://github.com/Acepie)) and
([Louis Pilfold](https://github.com/Acepie))
- An informative error message is now emitted when attempting to use a function
from another module in a constant expression. Previously this would result in
a cryptic parse error.
```
error: Syntax error
┌─ /src/parse/error.gleam:3:18
│
3 │ const wib: Int = wibble(1, "wobble")
│ ^^^^^^^ Functions can only be called within other functions
```
([Nino Annighoefer](https://github.com/nino))
- The compiler will now provide more helpful error messages when triple equals
are used instead of double equals.
```
error: Syntax error
┌─ /src/parse/error.gleam:4:37
│
4 │ [1,2,3] |> list.filter(fn (a) { a === 3 })
│ ^^^ Did you mean `==`?
Gleam uses `==` to check for equality between two values.
See: https://tour.gleam.run/basics/equality
```
([Rabin Gaire](https://github.com/rabingaire))
- The compiler will now raise a warning for unreachable code that comes after
a panicking expression.
```
pub fn main() {
panic
"unreachable!"
}
```
```
warning: Unreachable code
┌─ /src/warning/wrn.gleam:3:11
│
3 │ "unreachable!"
│ ^^^^^^^^^^^^^^
This code is unreachable because it comes after a `panic`.
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- JavaScript external module names may now include the character `@`.
([Louis Pilfold](https://github.com/lpil))
### Formatter
- Redundant alias names for imported modules are now removed.
```gleam
import gleam/result as result
```
is formatted to
```gleam
import gleam/result
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Comments are no longer moved out of constant lists, constant tuples and empty
tuples. You can now write this:
```gleam
const values = [
// This is a comment!
1, 2, 3
// Another comment...
11,
// And a final one.
]
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Comments at the end of an anonymous function are no longer moved out of it.
You can now write this:
```gleam
fn() {
todo
// A comment here!
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Pipes can now be placed on a single line if they are short enough:
```gleam
[1, 2, 3] |> list.map(int.to_string) |> string.join(with: "\n")
```
In addition you can also force the formatter to break a pipe on multiple lines
by manually breaking it. This:
```gleam
[1, 2, 3]
// By putting a newline here I'm telling the formatter to split the pipeline
|> list.map(int.to_string) |> string.join(with: "\n")
```
Will turn into this:
```gleam
[1, 2, 3]
|> list.map(int.to_string)
|> string.join(with: "\n")
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Comments appearing after arguments are no longer moved to a different place.
You can now write all of those:
```gleam
type Record {
Record(
field: String,
// comment_line_1: String,
// comment_line_2: String,
)
}
```
```gleam
pub fn main() {
fn(
a,
// A comment 2
) {
1
}
}
```
```gleam
fn main() {
let triple = Triple(1, 2, 3)
let Triple(
a,
..,
// comment
) = triple
a
}
```
```gleam
type Record {
Record(
// comment_line_1: String,
// comment_line_2: String,
)
}
```
([Mateusz Ledwoń](https://github.com/Axot017))
### Language Server
- The code action to remove unused imports now removes the entire line is
removed if it would otherwise be left blank.
([Milco Kats](https://github.com/katsmil))
- Hover for type annotations is now separate from the thing being annotated.
([Ameen Radwan](https://github.com/Acepie))
- Go to definition now works for direct type annotations.
([Ameen Radwan](https://github.com/Acepie))
- Go to definition now works for import statements.
([Ameen Radwan](https://github.com/Acepie))
- Hover now works for unqualified imports.
([Ameen Radwan](https://github.com/Acepie))
- The language server now detects when the `gleam.toml` config file has changed
even if the client does not support watching files. This means that changes to
the default target, new dependencies, and other configuration will be
automatically detected.
([Louis Pilfold](https://github.com/lpil))
- Completions are now provided for values and types for use in unqualified
imports.
([Ameen Radwan](https://github.com/Acepie))
- The character `.` is now advertised as a completion trigger character.
([Louis Pilfold](https://github.com/lpil))
- A new code action has been added to remove redundant tuples around case
expression subjects and patterns when possible.
([Nicky Lim](https://github.com/nicklimmm))
```
case #(x, y) {
#(1, 2) -> 0
#(_, _) -> 1
}
```
Is rewritten to:
```
case x, y {
1, 2 -> 0
_, _ -> 1
}
```
- The language server will now register information about code even when there
was a type error or similar. This means that the language server will be able
to produce some up-to-date information about the project, even when errors are
present. This should greatly improve the experience using the language server.
([Louis Pilfold](https://github.com/lpil))
### Bug Fixes
- Fixed [RUSTSEC-2021-0145](https://rustsec.org/advisories/RUSTSEC-2021-0145) by
using Rust's `std::io::IsTerminal` instead of the `atty` library.
([Pi-Cla](https://github.com/Pi-Cla))
- Fixed the generated `mod` property in the Erlang application file when using
the `application_start_module` property in `gleam.toml`.
([Alex Manning](https://github.com/rawhat))
- Fixed a confusing error message when using some reserved keywords.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed variables in constant expressions not being escaped correctly when
exporting to JavaScript.
([PgBiel](https://github.com/PgBiel))
- Fixed a typo when attempting to publish a package with non-Hex dependencies
([inoas](https://github.com/inoas))
- Fixed import completions not appearing in some editors due to the range being
longer than the line.
([Ameen Radwan](https://github.com/Acepie))
- Fixed a bug where TypeScript definitions files would use `null` instead of
`undefined`.
([Louis Pilfold](https://github.com/lpil))
- Fixed a bug where unreachable infinite cases would not be detected when
after a discard or variable pattern.
([Ameen Radwan](https://github.com/Acepie)) and
([Pi-Cla](https://github.com/Pi-Cla))
- Fixed a bug where module imports in guard clauses would not be generated
correctly for js target.
([Ameen Radwan](https://github.com/Acepie))
- Fixed a bug where formatting constant lists of tuples would force the
tuples to be broken across multiple lines, even when they could fit on a
single line.
([Isaac Harris-Holt](https://github.com/isaacharrisholt))
- Fixed a bug where floating points in scientific notation with no trailing
zeros would generate invalid Erlang code.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where having utf8 symbols in `gleam.toml`'s description value
would result in an HTTP 500 error when running `gleam publish`.
([inoas](https://github.com/inoas))
- Unicode `\u{}` syntax in bit_array string segments now produce valid Erlang
unicode characters ([Pi-Cla](https://github.com/Pi-Cla))
- Fixed a bug where using a constant defined in another module that referenced
a private function could generate invalid code on the Erlang target.
([Shayan Javani](https://github.com/massivefermion))
- Fixed a bug where the language server would dynamically request the client to
watch files even when the client has stated it does not support that.
([Louis Pilfold](https://github.com/lpil))
- Fixed a bug where local path dependencies could be mishandled on Windows.
([Francisco Montanez](https://github.com/Francisco-Montanez))
- Fixed a bug where adding a comment to a case clause would cause it to break
on multiple lines.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where pattern matching on a string prefix containing an escape
code could generate incorrect Erlang code.
([Nashwan Azhari](https://github.com/aznashwan))
- Fixed a bug where the formatter would produce uneven indentation within
multi-line comments at the bottom of case blocks.
([Race Williams](https://github.com/raquentin))
================================================
FILE: changelog/v1.3.md
================================================
# Changelog
## v1.3.0 - 2024-07-09
## v1.3.0-rc3 - 2024-07-08
- Fixed a bug where not all pure function calls in constant definitions would be
annotated as `@__PURE__` when compiling to JavaScript.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
## v1.3.0-rc2 - 2024-07-06
### Formatter
- Fixed a bug when multiple subjects in a case would be split even if not
necessary.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
## v1.3.0-rc1 - 2024-06-30
### Build tool
- `gleam remove` will now present an error if a package being removed does not
exist as a dependency in the project.
([Changfeng Lou](https://github.com/hnlcf))
- `gleam add` now takes an optional package version specifier,
separated by a `@`, that resolves as follows:
```sh
gleam add lustre@1.2.3 # "1.2.3"
gleam add lustre@1.2 # ">= 1.2.0 and < 2.0.0"
gleam add lustre@1 # ">= 1.0.0 and < 2.0.0"
```
([Rahul D. Ghosal](https://github.com/rdghosal))
### Compiler
- Added more an informative error message for when attempting to use the `..`
syntax to append to a list rather than prepend.
```
error: Syntax error
┌─ /src/parse/error.gleam:4:14
│
4 │ [..rest, last] -> 1
│ ^^^^^^ I wasn't expecting elements after this
Lists are immutable and singly-linked, so to match on the end
of a list would require the whole list to be traversed. This
would be slow, so there is no built-in syntax for it. Pattern
match on the start of the list instead.
```
([Antonio Iaccarino](https://github.com/eingin))
- The compiler now emits a warning for redundant function captures in a
pipeline:
```
warning: Redundant function capture
┌─ /src/warning/wrn.gleam:5:17
│
5 │ 1 |> wibble(_, 2) |> wibble(2)
│ ^ You can safely remove this
This function capture is redundant since the value is already piped as the
first argument of this call.
See: https://tour.gleam.run/functions/pipelines/
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The syntax `[a..b]` is now deprecated in favour of the `[a, ..b]` syntax.
This was to avoid it being mistaken for a range syntax, which Gleam does
not have.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Functions etc named `maybe` are now escaped in generated Erlang as it is now a
reserved word in Erlang/OTP 27.
([Jake Barszcz](https://github.com/barszcz))
- Functions, types and constructors named `maybe` and `else` are now
escaped in generated Erlang to avoid clashing with Erlang's keywords.
([Jake Barszcz](https://github.com/barszcz)) and
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Non byte aligned arrays that use literals for size are now marked as an
Unsupported feature for Javascript since they would already cause
a runtime error on Javascript.
This means if you compile specifically for Javascript you will now receive
this error:
```
error: Unsupported feature for compilation target
┌─ /src/test/gleam_test.gleam:6:5
│
6 │ <<1:size(5)>>
│ ^^^^^^^^^
Non byte aligned array is not supported for JavaScript compilation.
```
Else any functions which rely on this will not be compiled into Javascript.
([Pi-Cla](https://github.com/Pi-Cla))
- Compilation fault tolerance is now at a statement level instead of a function
level. This means that the compiler will attempt to infer the rest of the
statements in a function when there is an error in a previous one.
([Ameen Radwan](https://github.com/Acepie))
- The JavaScript prelude is no-longer rewritten each time a project is compiled
to JavaScript.
([Ofek Doitch](https://github.com/ofekd))
- Compiler now supports arithmetic operations in guards.
([Danielle Maywood](https://github.com/DanielleMaywood))
- Import cycles now show the location where the import occur.
([Ameen Radwan](https://github.com/Acepie))
- Error messages resulting from unexpected tokens now include information on
the found token's type. This updated message explains how the lexer handled
the token, so as to guide the user towards providing correct syntax.
Following is an example, where the error message indicates that the name of
the provided field conflicts with a keyword:
```
3 │ A(type: String)
│ ^^^^ I was not expecting this
Found the keyword `type`, expected one of:
- `)`
- a constructor argument name
```
([Rahul D. Ghosal](https://github.com/rdghosal))
- When compiling to JavaScript constants will now be annotated as `@__PURE__`.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
### Formatter
### Language Server
- The language server will now suggest the "Remove redundant tuple" action even
if the case expression contains some catch all patterns:
```
case #(a, b) {
#(1, 2) -> todo
_ -> todo
}
```
Becomes:
```
case a, b {
1, 2 -> todo
_, _ -> todo
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- LSP can now suggest completions for values and types from importable modules
and adds the import to the top of the file.
([Ameen Radwan](https://github.com/Acepie))
- Diagnostics with extra labels now show the diagnostic in all locations
including across multiple files.
([Ameen Radwan](https://github.com/Acepie))
- LSP completions now use the "text_edit" language server API resulting in
better/more accurate insertions.
([Ameen Radwan](https://github.com/Acepie))
- Completions are no longer provided inside comments.
([Nicky Lim](https://github.com/nicklimmm))
- The language server will now show all the ignored fields when hovering over
`..` in a record pattern.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
### Bug Fixes
- Fixed a bug where the compiler would output a confusing error message when
trying to use the spread syntax to append to a list.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the formatter would strip empty lines out of the body of an
anonymous function passed as an argument.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the compiler would crash when a type was defined with
the same name as an imported type.
([Gears](https://github.com/gearsdatapacks))
- Fixed a bug where a horizontal scrollbar would appear on code blocks in built
documentation when they contained lines 79 or 80 characters long.
([Richard Viney](https://github.com/richard-viney))
- Fixed a bug where importing a record constructor in an unqualified fashion and
aliasing it and then using it in a constant expression would generate invalid
JavaScript.
([Louis Pilfold](https://github.com/lpil))
- Fixed a bug where the compiler would crash because types weren't registered if
they referenced a non-existent type.
([Gears](https://github.com/gearsdatapacks))
- Fixed a bug where the compiler would generate invalid Erlang when pattern
matching on strings with an `as` pattern.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the compiler would warn that a module alias was unused when
it was only used in case patterns.
([Michael Jones](https://github.com/michaeljones))
## v1.2.1 - 2024-05-30
### Bug Fixes
- Fixed a bug where the compiler could fail to detect modules that would clash
with Erlang modules.
([Louis Pilfold](https://github.com/lpil))
- Fixed a bug where dependency version resolution could crash for certain
release candidate versions.
([Marshall Bowers](https://github.com/maxdeviant))
- Fixed a bug where trailing comments would be moved out of a bit array.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
================================================
FILE: changelog/v1.4.md
================================================
# Changelog
## v1.4.0 - 2024-08-02
### Bug Fixes
- Fixed a bug where pipe function arity errors could have an incorrect error
message.
([sobolevn](https://github.com/sobolevn))
- Fixed a bug where the case of type parameters would not be checked.
([Surya Rose](https://github.com/gearsdatapacks))
- Fixed a bug where the language server would still show completions when inside
a comment.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
## v1.4.0-rc1 - 2024-07-29
### Build tool
- `gleam docs build` now takes an optional `--target` flag to specify the target
platform for the generated documentation.
([Jiangda Wang](https://github.com/frank-iii))
- Warnings are now emitted each time the project is built, even if the module
the warnings originated from were loaded from the cache rather than
recompiling.
([Louis Pilfold](https://github.com/lpil))
### Compiler
- Labelled arguments can now use the label shorthand syntax.
This means that when you're passing a variable as a labelled argument and it
happens to have the same name as the label, you can omit the variable name:
```gleam
pub fn date(day day: Int, month month: Month, year year: Year) -> Date {
todo
}
pub fn main() {
let day = 11
let month = October
let year = 1998
date(year:, month:, day:)
// This is the same as writing
// date(year: year, month: month, day: day)
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Labelled pattern variables can now use the label shorthand syntax.
This means that when you're pattern matching on a record constructor and
binding its labelled fields to variables that happen to have the same name,
you can omit the variable name:
```gleam
pub type Date
Date(day: Int, month: Month, year: Year)
}
pub fn main() {
case Date(11, October, 1998) {
Date(year:, month:, day:) -> todo
// This is the same as writing
// Date(year: year, month: month, day: day) -> todo
}
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The warning for the deprecated `[..]` pattern has been improved.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Record accessors are now fault tolerant. This means an invalid label can be
properly detected and won't invalidate the rest of the expression.
([Ameen Radwan](https://github.com/Acepie))
- Erlang type spec generation has been improved to avoid new warnings emitted in
OTP27.
([Damir Vandic](https://github.com/dvic))
- Error messages for invalid record constructors now contain a restructured
example of what the user likely intended. This is especially helpful for
users coming from other languages, like Rust or Go.
For example, provided a User type:
```gleam
pub type User {
name: String
}
```
The compiler errors with the following message:
```
error: Syntax error
┌─ /src/parse/error.gleam:3:5
│
3 │ name: String,
│ ^^^^ I was not expecting this
Each custom type variant must have a constructor:
pub type User {
User(
name: String,
)
}
```
([Rahul D. Ghosal](https://github.com/rdghosal))
- The `<>` string concatenation operator can now be used in constant
expressions.
([Thomas](https://github.com/DeviousStoat))
- Function calls are now fault tolerant. This means that errors in the function
call arguments won't stop the rest of the call from being analysed.
([Ameen Radwan](https://github.com/Acepie))
- The error message presented when a function is called in a guard has been
improved.
([Thomas](https://github.com/DeviousStoat))
- Case expressions are now fault tolerant. This means an subject, pattern,
guard, or then body can be properly detected and won't invalidate the rest
of the expression.
([Ameen Radwan](https://github.com/Acepie))
- Documentation comments that come before a regular comment are no longer
clumped together with the documentation of the following definition.
Now commenting out a definition won't result in its documentation merging with
the following one's.
```gleam
/// This doc comment will be ignored!
// a commented definition
// fn wibble() {}
/// Wibble's documentation.
fn wibble() { todo }
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The `little` and `big` endianness options, the `signed` and `unsigned` integer
options, and sized floats (32-bit and 64-bit), can now be used in bit array
expressions and patterns on the JavaScript target.
([Richard Viney](https://github.com/richard-viney))
- The `utf8` option can now be used with constant strings in bit array patterns
on the JavaScript target.
([Richard Viney](https://github.com/richard-viney))
### Formatter
- The formatter will no longer move a documentation comment below a regular
comment following it. This snippet of code is left as it is by the formatter:
```gleam
/// This doc comment will be ignored!
// a commented definition
// fn wibble() {}
/// Wibble's documentation.
fn wibble() {
todo
}
```
While previously all documentation comments would be merged together into one,
ignoring the regular comment separating them:
```gleam
// a commented definition
// fn wibble() {}
/// This doc comment will be ignored!
/// Wibble's documentation.
fn wibble() {
todo
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
### Language Server
- The language server can now show completions for fields if a record access is
being attempted.
([Ameen Radwan](https://github.com/Acepie))
- The language server will now insert a blank line before the first statement
when inserting a new import and there are no other imports at the top of the
module.
([Zhomart Mukhamejanov](https://github.com/Zhomart))
- The language server now suggests a code a action to rename variables, types
and functions when they don't match the Gleam naming requirements:
```gleam
let myNumber = 10
```
Becomes:
```gleam
let my_number = 10
```
([Surya Rose](https://github.com/gearsdatapacks))
- The language server can now suggest a code action to convert `let assert` into
a case expression:
```gleam
let assert Ok(value) = get_result()
```
Becomes:
```gleam
let value = case get_result() {
Ok(value) -> value
_ -> panic
}
```
([Surya Rose](https://github.com/gearsdatapacks))
- The language server can now show signature help when writing functions.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server now supports listing document symbols, such as functions
and constants, for the current Gleam file.
([PgBiel](https://github.com/PgBiel))
- The language server can now suggest a code action to automatically use
shorthand labels where possible:
```gleam
case date {
Day(day: day, month: month, year: year) -> todo
}
```
Becomes:
```gleam
case date {
Day(day:, month:, year:) -> todo
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server can now show completions for labels when writing a
function call or record construction.
([Ameen Radwan](https://github.com/Acepie))
- The language server can now suggest a code action to fill in the labels of a
function call:
```gleam
pub type Date {
Date(year: Int, month: Int, day: Int)
}
pub fn main() {
Date()
}
```
Becomes:
```gleam
pub type Date {
Date(year: Int, month: Int, day: Int)
}
pub fn main() {
Date(year: todo, month: todo, day: todo)
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Completions are now sorted by priority based on why the completion is in the
list. This means that more specific completions like labels and local
definitions will be shown before more broad completions like functions from a
not yet imported module.
([Ameen Radwan](https://github.com/Acepie))
### Bug Fixes
- Functions, types and constructors named `module_info` are now escaped
in generated Erlang code to avoid conflicts with the builtin
`module_info/0` and `module_info/1` functions.
([Juraj Petráš](https://github.com/Hackder))
- Fixed formatting of comments at the start of a case branch.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where a private type could be leaked from an internal module.
([Ameen Radwan](https://github.com/Acepie))
- Fixed a bug where certain binops would not wrap their arguments properly
thus generating invalid JavaScript.
([Ameen Radwan](https://github.com/Acepie))
- Fixed formatting of function definitions marked as `@internal`
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where importing a record constructor in an unqualified fashion and
aliasing it and then using it in a case guard expression would generate
invalid JavaScript.
([PgBiel](https://github.com/PgBiel))
## v1.3.2 - 2024-07-11
### Language Server
- The language server no longer shows completions when inside a literal string.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
### Bug Fixes
- Fixed a bug where the compiler would report errors for duplicate `@external`
attributes with inconsistent spans between Erlang and JavaScript.
([Connor Szczepaniak](https://github.com/cszczepaniak))
- Fixed a bug where `gleam add` would fail to parse version specifiers
correctly.
([Louis Pilfold](https://github.com/lpil))
- Fixed a bug where single clause case expressions could generate JavaScript
code with incorrectly rewritten JavaScript variable names.
([Louis Pilfold](https://github.com/lpil))
## v1.3.1 - 2024-07-10
### Bug Fixes
- Fixes a bug with import cycle detection when there is more than 2 imports in
the cycle.
([Ameen Radwan](https://github.com/Acepie))
================================================
FILE: changelog/v1.5.md
================================================
# Changelog
## v1.5.0 - 2024-09-19
## v1.5.0-rc2 - 2024-09-18
### Bug Fixes
- Fixed a bug where the formatter would not format nested tuple access properly.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where multi-variant custom type accessors wouldn't be properly
detected.
([Louis Pilfold](https://github.com/lpil))
## v1.5.0-rc1 - 2024-09-14
### Build tool
- The `--no-print-progress` flag has been added to prevent the build tool from
printing messages as the project is built.
([Ankit Goel](https://github.com/crazymerlyn))
- The compiler is now able to run a dependency's module using `gleam run -m`
even when there's compilation errors in your own project's code.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- HTML docs: make module names in sidebar wrap before a / when possible
([Jiangda Wang](https://github.com/frank-iii))
- The printing of runtime errors has been improved, including those from linked
processes.
([Louis Pilfold](https://github.com/lpil))
- OTP application trees are now shut down gracefully when `main` exits.
([Louis Pilfold](https://github.com/lpil))
- The `gleam fix` command can now update a project's `gleam` version constraint
to make sure it respects the inferred minimum required version.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The build tool now refuses to publish a project where the `gleam` version
constraint would include a compiler version that doesn't support the features
used by the package.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- If a project doesn't specify a `gleam` version constraint, the build tool will
automatically infer it and add it to the project's `gleam.toml` before
publishing it.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
### Compiler
- Compiler progress is now printed to stderr, instead of stdout.
([Victor Kobinski](https://github.com/vkobinski))
- It is now possible to omit the `:utf8` option for literal strings used in a
`BitArray` segment.
```gleam
<<"Hello", " ", "world">>
```
Is the same as:
```gleam
<<"Hello":utf8, " ":utf8, "world":utf8>>
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- In inexhaustive pattern match errors the missing variants are now printed
using the correct syntax for the module the error is emitted in, rather than
the module it was defined in. For example, if you had this code:
```gleam
import gleam/option
pub fn main() {
let an_option = option.Some("wibble!")
case an_option {
option.None -> "missing"
}
}
```
The error message would show the qualified `option.Some(_)` as the missing
pattern:
```txt
error: Inexhaustive patterns
┌─ /Users/giacomocavalieri/Desktop/prova/src/prova.gleam:5:3
│
5 │ ╭ case an_option {
6 │ │ option.None -> "missing"
7 │ │ }
│ ╰───^
This case expression does not have a pattern for all possible values. If it
is run on one of the values without a pattern then it will crash.
The missing patterns are:
option.Some(_)
```
([Surya Rose](https://github.com/gearsdatapacks))
- Anonymous functions that are immediately called with a record or a tuple as an
argument are now inferred correctly without the need to add type annotations.
For example you can now write:
```gleam
fn(x) { x.0 }(#(1, 2))
// ^ you no longer need to annotate this!
```
([sobolevn](https://github.com/sobolevn))
- Anonymous functions that are being piped a record or a tuple as an argument
are now inferred correctly without the need to add type annotations. For
example you can now write:
```gleam
pub type User {
User(name: String)
}
pub fn main() {
User("Giacomo")
|> fn(user) { user.name }
// ^^^^ you no longer need to annotate this!
|> io.debug
}
```
([sobolevn](https://github.com/sobolevn))
- The record pattern matching syntax `Record(a ..)` is now deprecated in favour
of the `Record(a, ..)` syntax.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Adds a better error message when module names are used as values. For example
the following code:
```gleam
import gleam/list
pub fn main() {
list
}
```
Results in the error:
```txt
error: Module `list` used as a value
┌─ /Users/giacomocavalieri/Desktop/prova/src/prova.gleam:4:3
│
4 │ list
│ ^^^^
Modules are not values, so you cannot assign them to variables, pass them to
functions, or anything else that you would do with a value.
```
([sobolevn](https://github.com/sobolevn))
- An helpful error message has been added when the programmer attempts to write
a function within a custom type definition, likely trying to declare an OOP
class. For example:
```gleam
pub type User {
User(name: String)
fn greet(user: User) -> String {
"hello " <> user.name
}
}
```
Now results in the following error:
```txt
error: Syntax error
┌─ /Users/giacomocavalieri/Desktop/prova/src/prova.gleam:8:3
│
8 │ fn greet(user: User) -> String {
│ ^^ I was not expecting this
Found the keyword `fn`, expected one of:
- `}`
- a record constructor
Hint: Gleam is not an object oriented programming language so
functions are declared separately from types.
```
([sobolevn](https://github.com/sobolevn))
- The compiler now gives a hint to import a module when accessing modules that
aren't imported. It only suggests a module if it exports a type/value with
the same name as what the user was trying to access:
```gleam
pub fn main() {
io.println("Hello, world!")
}
```
Produces the following error:
```
error: Unknown module
┌─ /src/file.gleam:2:3
│
2 │ io.println("Hello, world!")
│ ^^
No module has been found with the name `io`.
Hint: Did you mean to import `gleam/io`?
```
This code, however, produces no hint:
```gleam
pub fn main() {
io.non_existent()
}
```
([Surya Rose](https://github.com/gearsdatapacks))
- The compiler now provides improved suggestions in the error for an
inexhaustive case expression. The following code:
```gleam
let a = True
case a {}
```
Now produces this error:
```
error: Inexhaustive patterns
┌─ /src/file.gleam:3:3
│
3 │ case a {}
│ ^^^^^^^^^
This case expression does not have a pattern for all possible values. If it
is run on one of the values without a pattern then it will crash.
The missing patterns are:
False
True
```
Whereas before, it would suggest `_` as the only missing pattern.
([Surya Rose](https://github.com/GearsDatapacks))
- Improve error message for using `@external` with unknown target
([Jiangda Wang](https://github.com/frank-iii))
- Improved error title when using an unknown module value.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The compiler now shows an helpful error message if you try writing an `if`
expression instead of a case. For example, this code:
```gleam
pub fn main() {
let a = if wibble {
1
}
}
```
Results in the following error:
```txt
error: Syntax error
┌─ /src/parse/error.gleam:3:11
│
3 │ let a = if wibble {
│ ^^ Gleam doesn't have if expressions
If you want to write a conditional expression you can use a `case`:
case condition {
True -> todo
False -> todo
}
See: https://tour.gleam.run/flow-control/case-expressions/
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The compiler can now infer the minimum Gleam version needed for your code to
compile and emits a warning if the project's `gleam` version constraint
doesn't include it.
For example, let's say your `gleam.toml` has the constraint
`gleam = ">= 1.1.0"` and your code is using some feature introduced in a later
version:
```gleam
// Concatenating constant strings was introduced in v1.4.0!
pub const greeting = "hello " <> "world!"
```
You would now get the following warning:
```txt
warning: Incompatible gleam version range
┌─ /Users/giacomocavalieri/Desktop/datalog/src/datalog.gleam:1:22
│
1 │ pub const greeting = "hello " <> "world!"
│ ^^^^^^^^^^^^^^^^^^^^ This requires a Gleam version >= 1.4.0
Constant strings concatenation was introduced in version v1.4.0. But the
Gleam version range specified in your `gleam.toml` would allow this code to
run on an earlier version like v1.1.0, resulting in compilation errors!
Hint: Remove the version constraint from your `gleam.toml` or update it to be:
gleam = ">= 1.4.0"
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- On the JavaScript target, non-byte aligned integers in bit array patterns are
now reported as a compile-time error.
([Richard Viney](https://github.com/richard-viney))
### Formatter
- The formatter now adds a `todo` after a `use` expression if it is the last
expression in a block. For example, the following code:
```gleam
pub fn main() {
use user <- result.try(fetch_user())
}
```
Is rewritten as:
```gleam
pub fn main() {
use user <- result.try(fetch_user())
todo
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
### Language Server
- The language server can now show completions for local variables inside a function.
([Ezekiel Grosfeld](https://github.com/ezegros))
- The language server can now suggest a code action to assign an unused value to
`_`.
([Jiangda Wang](https://github.com/frank-iii))
- The language server can now suggest a code action to import modules for
existing code which references unimported modules:
```gleam
pub fn main() {
io.println("Hello, world!")
}
```
Becomes:
```gleam
import gleam/io
pub fn main() {
io.println("Hello, world!")
}
```
([Surya Rose](https://github.com/gearsdatapacks))
- The Language Server can now suggest a code action to fill in the missing
patterns of a case expression:
```gleam
let a = True
case a {}
```
Becomes:
```gleam
let a = True
case a {
False -> todo
True -> todo
}
```
([Surya Rose](https://github.com/GearsDatapacks))
### Bug Fixes
- Fixed a bug where the warnings were printed above the errors without any new
line between them.
([Victor Kobinski](https://github.com/vkobinski))
- Fixed a bug which caused the language server and compiler to crash when two
constructors of the same name were created.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where jumping to the definition of an unqualified function would
produce the correct location, but remain in the same file.
([Surya Rose](https://github.com/gearsdatapacks))
- Fixed a bug where incorrect syntax error message were shown, when using `:` or
`=` in wrong positions in expressions.
([Ankit Goel](https://github.com/crazymerlyn))
- Fixed a bug where the compiler would crash when pattern matching on a type
which had constructors of duplicate names.
([Surya Rose](https://github.com/gearsdatapacks))
- Fixed a bug where referencing record constructors in JavaScript constants but
not calling them could produce invalid code.
([Louis Pilfold](https://github.com/lpil))
- Fixed a bug where source links in HTML documentation would be incorrect for
Codeberg, SourceHut, and Gitea.
([sobolevn](https://github.com/sobolevn))
- Fixed a bug with Erlang code generation for discard utf8 patterns in bit
arrays.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug which affected inference of function calls in pipe expressions.
([sobolevn](https://github.com/sobolevn))
- Improved an error message when using variable names starting with an
underscore in expression like: `let some = _func()` or `case { 1 -> _func() }`
([sobolevn](https://github.com/sobolevn))
- Fixed a bug where the provided `REBAR_BARE_COMPILER_OUTPUT_DIR` env var would
use relative path instead of absolute path causing compilation errors in some
packages.
([Gustavo Inacio](https://github.com/gusinacio))
- Fixed a bug where the compiler would print incorrect missing patterns for
inexhaustive case expressions matching on more than one subject.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the compiler would not check the target support of a
function if it was imported and not used, and generate invalid code.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where an qualified unused constructor wouldn't be reported as
unused.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The Language Server now correctly shows completions for values in the Gleam
prelude.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the language server wouldn't let you jump to the definition
of a function with an external implementation defined in the same module.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
## v1.4.1 - 2024-08-04
### Bug Fixes
- Fix a bug that caused record accessors for private types to not be completed
by the LSP, even when in the same module.
([Ameen Radwan](https://github.com/Acepie))
================================================
FILE: changelog/v1.6.md
================================================
# Changelog
## v1.6.0 - 2024-11-18
### Bug fixes
- Fixed a bug where the language server would delete pieces of code when
applying a suggested autocompletion.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
## v1.6.0-rc2 - 2024-11-14
### Build tool
- The version of Erlang used in the GitHub Actions workflow created by
`gleam new` has been increased from v26.0.2 to v27.1.2.
([Richard Viney](https://github.com/richard-viney))
### Bug fixes
- Fixed a bug where some reserved field names were not properly escaped in
custom types on the JavaScript target.
([yoshi](https://github.com/joshi-monster))
- Fixed a bug where a warning about unsafe integers on the JavaScript target was
emitted when the enclosing function has an external JavaScript implementation.
([Richard Viney](https://github.com/richard-viney))
- Fixed a bug where updating a dependency could break the build cache.
([yoshi](https://github.com/joshi-monster))
- Fixed a bug where if the build directory was not writable then the build tool
would crash when trying to lock the build directory.
([Zak Farmer](https://github.com/ZakFarmer))
## v1.6.0-rc1 - 2024-11-10
### Build tool
- The `--template` flag for `gleam new` takes the values `erlang` and
`javascript` to specify what target to use, with `erlang` being the default.
([Mohammed Khouni](https://github.com/Tar-Tarus))
- The Erlang/Elixir compiler process is now reused for all packages, shaving
off 0.3-0.5s per compiled package.
([yoshi](https://github.com/joshi-monster))
- When a symlink cannot be made on Windows due to lack of permissions the error
now includes information on how to enable Windows' developer mode, enabling
symlinks.
([Louis Pilfold](https://github.com/lpil))
- The cli can now update individual dependencies.
`gleam update` and `gleam deps update` now take an optional list of package
names to update:
```sh
gleam update package_a
gleam deps update package_b package_c
```
This allows for selective updating of dependencies. When package names are
provided, only those packages and their unique dependencies are unlocked and
updated. If no package names are specified, the command behaves as before,
updating all dependencies.
([Jason Sipula](https://github.com/SnakeDoc))
- The `repository` config in `gleam.toml` can now optionally include a `path`
so that source links in generated documentation are correct for packages that
aren't located at the root of their repository:
```toml
[repository]
type = "github"
user = "gleam-lang"
repo = "gleam"
path = "packages/my_package"
```
([Richard Viney](https://github.com/richard-viney))
### Compiler
- The compiler now prints correctly qualified or aliased type names when
printing type errors.
This code:
```gleam
pub type Int
pub fn different_int_types(value: Int) {
value
}
pub fn main() {
different_int_types(20)
}
```
Produces this error:
```
error: Type mismatch
┌─ /src/wibble.gleam:8:23
│
8 │ different_int_types(20)
│ ^^
Expected type:
Int
Found type:
gleam.Int
```
([Surya Rose](https://github.com/GearsDatapacks))
- The compiler can now suggest to pattern match on a `Result(a, b)` if it's
being used where a value of type `a` is expected. For example, this code:
```gleam
import gleam/list
import gleam/int
pub fn main() {
let not_a_number = list.first([1, 2, 3])
int.add(1, not_a_number)
}
```
Results in the following error:
```txt
error: Type mismatch
┌─ /src/one/two.gleam:6:9
│
6 │ int.add(1, not_a_number)
│ ^^^^^^^^^^^^
Expected type:
Int
Found type:
Result(Int, a)
Hint: If you want to get a `Int` out of a `Result(Int, a)` you can pattern
match on it:
case result {
Ok(value) -> todo
Error(error) -> todo
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Improved the error message for unknown record fields, displaying an additional
note on how to have a field accessor only if it makes sense.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The compiler now ignores `optional` dependencies when resolving versions
unless explicitly specified.
([Gustavo Inacio](https://github.com/gusinacio))
- Improved the error message for using `@deprecated` with no deprecation message
([Jiangda Wang](https://github.com/frank-iii))
- Optimised creation of bit arrays on the JavaScript target.
([Richard Viney](https://github.com/richard-viney))
- The compiler can now infer the variant of custom types within expressions that
construct or pattern match on them.
Using this information it can now be more precise with exhaustiveness
checking, identifying patterns for the other variants as unnecessary.
```gleam
pub type Pet {
Dog(name: String, cuteness: Int)
Turtle(name: String, speed: Int, times_renamed: Int)
}
pub fn main() {
// We know `charlie` is a `Dog`...
let charlie = Dog("Charles", 1000)
// ...so you do not need to match on the `Turtle` variant
case charlie {
Dog(..) -> todo
}
}
```
This also means that the record update syntax can be used on multi-variant
custom types, so long as the variant can be inferred from the surrounding
code.
```gleam
pub fn rename(pet: Pet, to name: String) -> Pet {
case pet {
Dog(..) -> Dog(..pet, name:)
Turtle(..) -> Turtle(..pet, name:, times_renamed: pet.times_renamed + 1)
}
}
```
Variant specific fields can also be used with the accessor syntax.
```gleam
pub fn speed(pet: Pet) -> Int {
case pet {
Dog(..) -> 500
Turtle(..) -> pet.speed
}
}
```
([Surya Rose](https://github.com/GearsDatapacks))
- When targeting JavaScript the compiler now emits a warning for integer
literals and constants that lie outside JavaScript's safe integer range:
```txt
warning: Int is outside the safe range on JavaScript
┌─ /Users/richard/Desktop/int_test/src/int_test.gleam:1:15
│
1 │ pub const i = 9_007_199_254_740_992
│ ^^^^^^^^^^^^^^^^^^^^^ This is not a safe integer on JavaScript
This integer value is too large to be represented accurately by
JavaScript's number type. To avoid this warning integer values must be in
the range -(2^53 - 1) - (2^53 - 1).
See JavaScript's Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER
properties for more information.
```
([Richard Viney](https://github.com/richard-viney))
### Formatter
- The formatter no longer removes the first argument from a function
which is part of a pipeline if the first argument is a capture
and it has a label. This snippet of code is left as is by the formatter:
```gleam
pub fn divide(dividend a: Int, divisor b: Int) -> Int {
a / b
}
pub fn main() {
10 |> divide(dividend: _, divisor: 2)
}
```
Whereas previously, the label of the capture variable would be lost:
```gleam
pub fn divide(dividend a: Int, divisor b: Int) -> Int {
a / b
}
pub fn main() {
10 |> divide(divisor: 2)
}
```
([Surya Rose](https://github.com/GearsDatapacks))
### Language Server
- The Language Server now displays correctly qualified or aliased type names
when hovering over a value in a Gleam file:
```gleam
import gleam/option
const value = option.Some(1)
// ^ hovering here shows `option.Option(Int)`
```
```gleam
import gleam/option.{type Option as Maybe}
const value = option.Some(1)
// ^ hovering here shows `Maybe(Int)`
```
([Surya Rose](https://github.com/GearsDatapacks))
- The Language Server now suggests a code action to add type annotations to
local variables, constants and functions:
```gleam
pub fn add_int_to_float(a, b) {
a +. int.to_float(b)
}
```
Becomes:
```gleam
pub fn add_int_to_float(a: Float, b: Int) -> Float {
a +. int.to_float(b)
}
```
([Surya Rose](https://github.com/GearsDatapacks))
- The Language Server now suggests a code action to convert qualified imports to
unqualified imports, which updates all occurrences of the qualified name
throughout the module:
```gleam
import option
pub fn main() {
option.Some(1)
}
```
Becomes:
```gleam
import option.{Some}
pub fn main() {
Some(1)
}
```
([Jiangda Wang](https://github.com/Frank-III))
- The Language Server now suggests a code action to convert unqualified imports
to qualified imports, which updates all occurrences of the unqualified name
throughout the module:
```gleam
import list.{map}
pub fn main() {
map([1, 2, 3], fn(x) { x * 2 })
}
```
Becomes:
```gleam
import list.{}
pub fn main() {
list.map([1, 2, 3], fn(x) { x * 2 })
}
```
([Jiangda Wang](https://github.com/Frank-III))
### Bug Fixes
- Fixed a bug in the compiler where shadowing a sized value in a bit pattern
would cause invalid erlang code to be generated.
([Antonio Iaccarino](https://github.com/eingin))
- Fixed a bug where the formatter would not format strings with big grapheme
clusters properly.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed the `BitArray` constructor not being present in the types for the
JavaScript prelude.
([Richard Viney](https://github.com/richard-viney))
- Fixed a bug where generated TypeScript definitions were invalid for opaque
types that use a private type.
([Richard Viney](https://github.com/richard-viney))
- Fixed the prelude re-export in generated TypeScript definitions.
([Richard Viney](https://github.com/richard-viney))
- Fixed a bug where the compiler would incorrectly type-check and compile
calls to functions with labelled arguments in certain cases.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where importing type aliases that reference unimported modules
would generate invalid TypeScript definitions.
([Richard Viney](https://github.com/richard-viney))
- When splitting a constant list made of records, the formatter will keep each
item on its own line to make things easier to read.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the compiler would crash when pattern matching on a type
which was defined with duplicate fields in one of its variants.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the WASM compiler would return incomplete JavaScript when
unsupported features were used. It now returns a compilation error.
([Richard Viney](https://github.com/richard-viney))
- Fixed a bug where incorrect code would be generated for external function on
the Erlang target if any of their arguments were discarded.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug in the error message when using wrong values in a pipe where the
message would swap the "Expected" and "Found" types.
([Markus Pettersson](https://github.com/MarkusPettersson98/))
- Fixed a bug where the parser would incorrectly parse a record constructor with
no arguments.
([Louis Pilfold](https://github.com/lpil))
- Fixed a bug where the parser would incorrectly parse a generic type
constructor with no arguments.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the parser would incorrectly parse a generic type definition
with no arguments.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the language server wouldn't show hints when hovering over
the tail of a list.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where attempting to jump to the definition of a type from the
annotation of a parameter of an anonymous function would do nothing.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where referencing record constructors in JavaScript guards but
not calling them could produce invalid code.
([PgBiel](https://github.com/PgBiel))
- Fixed a bug where using the label shorthand syntax inside of a record update
wouldn't emit a warning when the minimum specified Gleam version was < 1.4.0.
([yoshi](https://github.com/joshi-monster))
- Fixed a bug where no error would be reported when duplicate labelled
arguments were supplied in a record update.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where an incorrect bit array would be generated on JavaScript for
negative `Int` values when the segment's `size` was wider than 48 bits or when
the `Int` value was less than the minimum representable value for the segment
size.
([Richard Viney](https://github.com/richard-viney))
- Fixed a bug where an incorrect `Int` would be returned when pattern matching
to a negative value wider than 48 bits in a bit array.
([Richard Viney](https://github.com/richard-viney))
- Fixed a bug where unused values coming from other modules wouldn't raise a
warning.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
## v1.5.1 - 2024-09-26
### Bug Fixes
- Fixed a bug where Erlang file paths would not be escaped on Windows.
([Louis Pilfold](https://github.com/lpil))
================================================
FILE: changelog/v1.7.md
================================================
# Changelog
## 1.7.0 - 2025-01-05
Happy birthday Louis! 🎁
## 1.7.0-rc3 - 2025-01-02
### Formatter
- Function captures are now formatted like regular function calls.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Record updates are now formatted like function calls.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
### Bug fixes
- Fixed a bug where the "convert from use" code action would generate invalid
code with labelled arguments.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where private types would be allowed to be used in other modules.
([Surya Rose](https://github.com/GearsDatapacks))
## 1.7.0-rc2 - 2024-12-30
### Bug fixes
- Fixed a bug on JavaScript where a trailing `:bytes` segment would give the
wrong pattern match result for a sliced bit array.
([Richard Viney](https://github.com/richard-viney))
## 1.7.0-rc1 - 2024-12-29
### Compiler
- Removed compiler hint about pattern matching a `Result(a, b)` when being used
where `a` is expected.
([Kieran O'Reilly](https://github.com/SoTeKie))
- Optimised code generated for record updates.
([yoshi](https://github.com/joshi-monster))
- The compiler now allows for record updates to change the generic type
parameters of the record:
```gleam
type Box(value) {
Box(password: String, value: value)
}
fn insert(box: Box(a), value: b) -> Box(b) {
Box(..box, value:)
}
```
([yoshi](https://github.com/joshi-monster))
- It is now allowed to write a block with no expressions. Like an empty function
body, an empty block is considered incomplete as if it contained a `todo`
expression.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The shorthand names for the two targets, `erl` and `js` are now
deprecated in code such as `@target`.
([Surya Rose](https://github.com/GearsDatapacks))
- A custom panic message can now be specified when asserting a value with
`let assert`:
```gleam
let assert Ok(regex) = regex.compile("ab?c+") as "This regex is always valid"
```
([Surya Rose](https://github.com/GearsDatapacks))
- When targeting JavaScript the compiler now generates faster and smaller code
for `Int` values in bit array expressions and patterns by evaluating them at
compile time where possible.
([Richard Viney](https://github.com/richard-viney))
- Qualified records can now be used in clause guards.
([Surya Rose](https://github.com/GearsDatapacks))
- The compiler now allows deprecating specific custom type variants using the
`@deprecated` attribute:
```gleam
pub type HashAlgorithm {
@deprecated("Please upgrade to another algorithm")
Md5
Sha224
Sha512
}
pub fn hash_password(input: String) -> String {
hash(input:, algorithm: Md5) // Warning: Deprecated value used
}
```
([Iesha](https://github.com/wilbert-mad))
- On the JavaScript target, taking byte-aligned slices of bit arrays is now an
O(1) operation instead of O(N), significantly improving performance.
([Richard Viney](https://github.com/richard-viney))
- Better error message for when an existing type constructor is used as a value
constructor.
([Jiangda Wang](https://github.com/Frank-III))
- Print better error messages when shell commands used by compiler cannot be
found.
([wheatfox](https://github.com/enkerewpo))
### Build tool
- Improved the error message you get when trying to add a package that doesn't
exist with `gleam add`.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- External files (such as `.mjs` and `.erl`) are now permitted in subdirectories
of `src/` and `test/`.
([PgBiel](https://github.com/PgBiel))
- `gleam publish` now requires more verbose confirmation for publishing Gleam
team packages and v0 packages.
([Louis Pilfold](https://github.com/lpil))
- `gleam publish` now warns when publishing packages that define multiple
top-level modules, as this can lead to namespace pollution and conflicts for
consumers.
([Aleksei Gurianov](https://github.com/guria))
- New projects now require `gleam_stdlib` v0.44.0.
([Louis Pilfold](https://github.com/lpil))
- `gleam remove` no longer requires a network connection.
([yoshi](https://github.com/joshi-monster))
- Commands that work with the Hex package manager API now create and store an
API key rather than creating a new one each time. This API key is encrypted
with a local password, reducing risk of your Hex password being compromised.
([Louis Pilfold](https://github.com/lpil))
- The build tool now sets the `REBAR_SKIP_PROJECT_PLUGINS` environment variable
when using rebar3 to compile Erlang dependencies. With future versions of
rebar3 this will cause it to skip project plugins, significantly reducing the
amount of code it'll need to download and compile, improving compile times.
([Tristan Sloughter](https://github.com/tsloughter))
### Language server
- The language server now provides type information when hovering over argument
labels.
([Surya Rose](https://github.com/GearsDatapacks))
- The language server now suggests a code action to desugar a use expression
into the equivalent function call. For example, this snippet of code:
```gleam
pub fn main() {
use profile <- result.try(fetch_profile(user))
render_welcome(user, profile)
}
```
Will be turned into:
```gleam
pub fn main() {
result.try(fetch_profile(user), fn(profile) {
render_welcome(user, profile)
})
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server now suggests a code action to turn a function call into
the equivalent use expression. For example, this snippet of code:
```gleam
pub fn main() {
result.try(fetch_profile(user) fn(profile) {
render_welcome(user, profile)
})
}
```
Will be turned into:
```gleam
pub fn main() {
use profile <- result.try(fetch_profile(user))
render_welcome(user, profile)
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server now provides correct information when hovering over
patterns in use expressions.
([Surya Rose](https://github.com/GearsDatapacks))
- The language server now suggests a code action to convert an inexhaustive
`let` assignment into a `case` expression:
```gleam
pub fn unwrap_result(result: Result(a, b)) -> a {
let Ok(inner) = result
inner
}
```
Becomes:
```gleam
pub fn unwrap_result(result: Result(a, b)) -> a {
let inner = case result {
Ok(inner) -> inner
Error(_) -> todo
}
inner
}
```
([Surya Rose](https://github.com/GearsDatapacks))
- The language server now provides an action to extract a value into a variable.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server now suggests a code action to expand a function capture
into the equivalent anonymous function. For example, this snippet of code:
```gleam
pub fn main() {
list.map([1, 2, 3], int.add(_, 11))
}
```
Will be turned into:
```gleam
pub fn main() {
list.map([1, 2, 3], fn(value) { int.add(value, 11) })
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server now suggests a code action to generate a dynamic decoder
for a custom type. For example, this code:
```gleam
pub type Person {
Person(name: String, age: Int)
}
```
Will become:
```gleam
import gleam/dynamic/decode
pub type Person {
Person(name: String, age: Int)
}
fn person_decoder() -> decode.Decoder(Person) {
use name <- decode.field("name", decode.string)
use age <- decode.field("age", decode.int)
decode.success(Person(name:, age:))
}
```
([Surya Rose](https://github.com/GearsDatapacks))
### Formatter
- The formatter now adds a `todo` inside empty blocks.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The formatter now formats lists the same in constants as in expressions.
([Jiangda Wang](https://github.com/Frank-III))
### Documentation
- Canonical links created for documentation pages if published on Hex.
Previously published documentation would need to be updated.
Resolves versioned pages to point to latest page for search engines.
([Dave Lage](https://github.com/rockerBOO))
### Bug fixes
- The compiler now throws an error when a float literal ends with an `e` and
is missing an exponent.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a crash with ENOTEMPTY (os error 39) when building on NTFS partitions.
([Ivan Ermakov](https://github.com/ivanjermakov))
- Fixed a bug where the compiler would crash when pattern matching on multiple
subjects and one of them being a constant record.
([Surya Rose](https://github.com/GearsDatapacks))
- Variant inference on prelude types now works correctly if the variant is
constant.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where patterns in `use` expressions would not be checked to ensure
that they were exhaustive.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where a `module.mjs` file would be overwritten by a `module.gleam`
file of same name without warning. It now produces an error.
([PgBiel](https://github.com/PgBiel))
- Modules depending on removed or renamed modules now get automatically
recompiled.
([Sakari Bergen](https://github.com/sbergen))
- The compiler now raises a warning for unused case expressions, code blocks and
pipelines that would be safe to remove.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where assigning the prefix of a string pattern to a variable
nested inside another pattern would produce invalid code on Javascript.
([yoshi](https://github.com/joshi-monster))
- Fixed a bug where expressions which use an unsafe integer on JavaScript would
not emit a warning if an external function had been referenced.
([Richard Viney](https://github.com/richard-viney))
- Fixed a bug where nested tuple access would not be parsed correctly when
the left-hand side was a function call.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where Gleam would be unable to compile to BEAM bytecode on older
versions of Erlang/OTP.
([yoshi](https://github.com/joshi-monster))
- Fixed a bug where the inferred variant of values was not properly cached,
leading to incorrect errors on incremental builds and in the language server.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where Gleam would be unable to compile to BEAM bytecode if the
project path contains a non-ascii character.
([yoshi](https://github.com/joshi-monster))
- Fixed a bug where the compiler would display incorrect hints about ignoring
unused variables in certain cases.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the completer would not include braces in type import
completions when it should have.
([Jiangda Wang](https://github.com/Frank-III))
- Fixed a bug where `gleam new` would generate the github `test` workflow
configuration with incompatible Erlang OTP and Elixir versions.
([John Strunk](https://github.com/jrstrunk))
## v1.6.1 - 2024-11-19
### Bug fixes
- Fixed a bug where `gleam update` would fail to update versions.
([Jason Sipula](https://github.com/SnakeDoc))
================================================
FILE: changelog/v1.8.md
================================================
# Changelog
Dedicated to the memory of Len Pilfold.
## v1.8.0 - 2025-02-07
## v1.8.0-rc1 - 2025-02-03
### Compiler
- Pipelines are now fault tolerant. A type error in the middle of a pipeline
won't stop the compiler from figuring out the types of the remaining pieces,
enabling the language server to show better suggestions for incomplete pipes.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Improved code generation for blocks in tail position on the Javascript target.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Function documentation comments and module documentation comments are now
included in the generated Erlang code and can be browsed from the Erlang
shell starting from OTP27.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Parsing of `case` expressions is now fault tolerant. If a `case` expressions
is missing its body, the compiler can still perform type inference. This also
allows the Language Server to provide completion hints for `case` subjects.
([Surya Rose](https://github.com/GearsDatapacks))
- The compiler can now suggest to wrap a value in an `Ok` or `Error` if that can
solve a type mismatch error:
```gleam
pub fn greet_logged_user() {
use <- bool.guard(when: !logged_in, return: Error(Nil))
"Hello!"
}
```
Results in the following error:
```txt
error: Type mismatch
┌─ /main.gleam:7:3
│
7 │ "Hello!"
│ ^^^^^^^^ Did you mean to wrap this in an `Ok`?
Expected type:
Result(a, Nil)
Found type:
String
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The compiler now shows an improved error message when using an unknown type as a
variable name
```txt
error: Unknown variable
┌─ /src/one/two.gleam:4:3
│
4 │ X
│ ^
The custom type variant constructor `X` is not in scope here.
```
([Roeeeee](https://github.com/5c077m4n))
- Erlang `file` module attributes now use paths relative to the root.
([Kasim](https://github.com/oneness))
### Build tool
- `gleam new` now has refined project name validation - rather than failing on
invalid project names, it suggests a valid alternative and prompts for
confirmation to use it.
([Diemo Gebhardt](https://github.com/diemogebhardt))
- `gleam docs build` generated documentation site now focuses the search input
when "Cmd/Ctrl + K", "s" or "/" is pressed.
([Sambit Sahoo](https://github.com/soulsam480))
- `gleam deps` now supports `tree` operation that lists the dependency tree.
```markdown
Usage: gleam deps tree [OPTIONS]
Options:
-p, --package Package to be used as the root of the tree
-i, --invert Invert the tree direction and focus on the given package
-h, --help Print help
```
For example, if the root project (`project_a`) depends on `package_b` and
`package_c`, and `package_c` also depends on `package_b`, the output will be:
```markdown
$ gleam deps tree
project_a v1.0.0
├── package_b v0.52.0
└── package_c v1.2.0
└── package_b v0.52.0
$ gleam deps tree --package package_c
package_c v1.2.0
└── package_b v0.52.0
$ gleam deps tree --invert package_b
package_b v0.52.0
├── package_c v1.2.0
│ └── project_a v1.0.0
└── project_a v1.0.0
```
([Ramkarthik Krishnamurthy](https://github.com/ramkarthik))
- The build tool now checks for modules that would collide with the new Erlang
`json` module in addition to the existing Erlang modules it already checked
for.
([Louis Pilfold](https://github.com/lpil))
### Language server
- The language server can now generate the definition of functions that do not
exist in the current file. For example if I write the following piece of code:
```gleam
import gleam/io
pub type Pokemon {
Pokemon(pokedex_number: Int, name: String)
}
pub fn main() {
io.println(to_string(pokemon))
// ^ If you put your cursor over this function that is
// not implemented yet
}
```
Triggering the "generate function" code action, the language server will
generate the following function for you:
```gleam
fn to_string(pokemon: Pokemon) -> String {
todo
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server can now fill in the labels of any function call, even when
only some of the arguments are provided. For example:
```gleam
import gleam/string
pub fn main() {
string.replace("wibble")
}
```
Will be completed to:
```gleam
import gleam/string
pub fn main() {
string.replace("wibble", each: todo, with: todo)
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server now suggests a code action to pattern match on a
function's argument. For example:
```gleam
pub type Pokemon {
Pokemon(pokedex_number: Int, name: String)
}
pub fn to_string(pokemon: Pokemon) {
// ^ If you put your cursor over the argument
todo
}
```
Triggering the code action on the `pokemon` argument will generate the
following code for you:
```gleam
pub type Pokemon {
Pokemon(pokedex_number: Int, name: String)
}
pub fn to_string(pokemon: Pokemon) {
let Pokemon(pokedex_number:, name:) = pokemon
todo
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server now suggests a code action to pattern match on a variable.
For example:
```gleam
pub fn main() {
let result = list.first(a_list)
// ^ If you put your cursor over the variable
todo
}
```
Triggering the code action on the `result` variable will generate the
following code for you:
```gleam
pub fn main() {
let result = list.first(a_list)
case result {
Ok(value) -> todo
Error(value) -> todo
}
todo
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- When generating functions or variables, the language server can now pick
better names using the type of the code it's generating.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The Language Server now provides the ability to rename local variables.
For example:
```gleam
pub fn main() {
let wibble = 10
// ^ If you put your cursor here, and trigger a rename
wibble + 1
}
```
Triggering a rename and entering `my_number` results in this code:
```gleam
pub fn main() {
let my_number = 10
my_number + 1
}
```
([Surya Rose](https://github.com/GearsDatapacks))
- `Unqualify` Action now get triggered when hovering over the module name
for record value constructor.
For example:
```gleam
pub fn main() {
let my_option = option.Some(1)
// ^ would trigger "Unqualify option.Some"
}
```
([Jiangda Wang](https://github.com/Frank-III))
### Formatter
### Bug fixes
- Fixed a bug where the "convert from use" code action would generate invalid
code for use expressions ending with a trailing comma.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where floats outside of Erlang's floating point range were not
causing errors.
([shayan](https://github.com/massivefermion))
- Fixed a bug where build tool could fail to add new dependencies when
dependencies with optional dependencies are present in the manifest.
([Louis Pilfold](https://github.com/lpil))
- Fixed a bug where a block expression containing a singular record update would
produce invalid erlang.
([yoshi](https://github.com/joshi-monster))
- Fixed a typo in the error message when trying to import a test module into an
application module.
([John Strunk](https://github.com/jrstrunk))
- Fixed a bug where the "Extract variable" code action would erroneously extract
a pipeline step as a variable.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where variables bound in `let assert` assignments would be allowed
to be used in the custom panic message.
([Surya Rose](https://github.com/GearsDatapacks))
================================================
FILE: changelog/v1.9.md
================================================
# Changelog
## v1.9.0 - 2025-03-09
## v1.9.0-rc2 - 2025-03-07
### Compiler
- Made runtime warnings regarding the use of deprecated BitArray properties in
JavaScript FFI code more compact. They are now one line instead of three.
([Richard Viney](https://github.com/richard-viney))
### Bug fixes
- Fixed a bug that would result in displaying the wrong name when running
`gleam --version`.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug in the `generate json encoder` and `generate dynamic decoder` that
would result in generating invalid code for variants with no fields.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
## v1.9.0-rc1 - 2025-03-04
### Compiler
- You can now use the `echo` keyword to debug print any value: `echo` can be
followed by any expression and it will print it to stderr alongside the module
it comes from and its line number. This:
```gleam
pub fn main() {
echo [1, 2, 3]
}
```
Will output to stderr:
```txt
/src/module.gleam:2
[1, 2, 3]
```
`echo` can also be used in the middle of a pipeline. This:
```gleam
pub fn main() {
[1, 2, 3]
|> echo
|> list.map(fn(x) { x * 2 })
|> echo
}
```
Will output to stderr:
```txt
/src/module.gleam:3
[1, 2, 3]
/src/module.gleam:5
[2, 4, 6]
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Generated Erlang `.app` files now include external modules written in Elixir
and Erlang.
([LostKobrakai](https://github.com/lostkobrakai))
- On the JavaScript target, bit array expressions and patterns no longer need to
be byte aligned, and the `bits` segment type is now supported in patterns.
([Richard Viney](https://github.com/richard-viney))
- The code generated for list pattern matching on the JavaScript target is now
more efficient. Gleam code that relies heavily on list pattern matching can
now be up to twice as fast.
([yoshi~](https://github.com/yoshi-monster))
- On the JavaScript target, bit array patterns can now match segments of dynamic
size.
([Surya Rose](https://github.com/GearsDatapacks))
### Build tool
- The build tool now supports Git dependencies. For example:
```
[dependencies]
gleam_stdlib = { git = "https://github.com/gleam-lang/stdlib.git", ref = "957b83b" }
```
([Surya Rose](https://github.com/GearsDatapacks))
- The build tool now refuses to publish any incomplete package that has any
`echo` debug printing left.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- HexDocs documentation of Gleam packages now uses the ExDocs search data model,
allowing for global indexing of Gleam packages in HexDocs, and
making Gleam packages discoverable through global search of HexDocs.
([Diemo Gebhardt](https://github.com/diemogebhardt))
- Improved the styling of constructor argument descriptions in the generated
documentation.
([Mikko Ahlroth](https://git.ahlcode.fi/nicd))
- Allow users to set the `GLEAM_CACERTS_PATH` environment variable to specify a
path to a directory containing CA certificates to install Hex packages.
([winstxnhdw](https://github.com/winstxnhdw))
### Language server
- The language server now has the ability to jump to the type definition of any
hovered value.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server now offers a code action to convert the first step of a
pipeline to a regular function call. For example, this code:
```gleam
import gleam/list
pub fn main() {
[1, 2, 3] |> list.map(fn(n) { n * 2 })
}
```
Will be rewritten as:
```gleam
import gleam/list
pub fn main() {
list.map([1, 2, 3], fn(n) { n * 2 })
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server now offers a code action to convert a function call into
a pipeline. For example, this code:
```gleam
import gleam/list
pub fn main() {
list.map([1, 2, 3], fn(n) { n * 2 })
}
```
Will be rewritten as:
```gleam
import gleam/list
pub fn main() {
[1, 2, 3] |> list.map(fn(n) { n * 2 })
}
```
You can also pick which argument is going to be piped. In this case:
```gleam
import gleam/list
pub fn main() {
list.map([1, 2, 3], fn(n) { n * 2 })
// ^ If you put your cursor over here
}
```
The code will be rewritten as:
```gleam
import gleam/list
pub fn main() {
fn(n) { n * 2 } |> list.map([1, 2, 3], _)
}
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server now suggests a code action to generate a function to
encode a custom type as JSON using the `gleam_json` package. For example:
```gleam
pub type Person {
Person(name: String, age: Int)
}
```
Will become:
```gleam
import gleam/json
pub type Person {
Person(name: String, age: Int)
}
fn encode_person(person: Person) -> json.Json {
json.object([
#("name", json.string(person.name)),
#("age", json.int(person.age)),
])
}
```
([Surya Rose](https://github.com/GearsDatapacks))
- The language server now suggests a code action to inline a variable
which is only used once. For example, this code:
```gleam
import gleam/io
pub fn main() {
let greeting = "Hello!"
io.println(greeting)
}
```
Will be rewritten as:
```gleam
import gleam/io
pub fn main() {
io.println("Hello!")
}
```
([Surya Rose](https://github.com/GearsDatapacks))
- The code action to generate a dynamic decoder for a custom type can now
generate decoders for types with multiple variants. For example this code:
```gleam
pub type Person {
Adult(age: Int, job: String)
Child(age: Int, height: Float)
}
```
Becomes:
```gleam
import gleam/dynamic/decode
pub type Person {
Adult(age: Int, job: String)
Child(age: Int, height: Float)
}
fn person_decoder() -> decode.Decoder(Person) {
use variant <- decode.field("type", decode.string)
case variant {
"adult" -> {
use age <- decode.field("age", decode.int)
use job <- decode.field("job", decode.string)
decode.success(Adult(age:, job:))
}
"child" -> {
use age <- decode.field("age", decode.int)
use height <- decode.field("height", decode.float)
decode.success(Child(age:, height:))
}
_ -> decode.failure(todo as "Zero value for Person", "Person")
}
}
```
([Surya Rose](https://github.com/GearsDatapacks))
- The language server now suggests a code action to easily interpolate a value
into a string. If the cursor is inside a literal string the language server
will offer to split it:
```gleam
"wibble | wobble"
// ^ Triggering the action with the cursor
// here will produce this:
"wibble " <> todo <> " wobble"
```
And if the cursor is selecting a valid Gleam name, the language server will
offer to interpolate it as a variable:
```gleam
"wibble wobble woo"
// ^^^^^^ Triggering the code action if you're
// selecting an entire name, will produce this:
"wibble " <> wobble <> " woo"
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- The language server now shows module documentation when hovering over a module
name.
([Surya Rose](https://github.com/GearsDatapacks))
### Formatter
- Redundant function captures that take no additional arguments are now
rewritten to not use the function capture syntax.
```gleam
some_module.some_function(_)
```
This code is reformatted like so:
```gleam
some_module.some_function
```
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
### Bug fixes
- Fixed a bug where division and remainder operators would not work correctly
in guards on the JavaScript target.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where the "Generate function" code action would ignore the
provided labels.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where the "Pattern match on argument" and
"Pattern match on variable" code actions would not allow to pattern match on a
private type used in the same module it's defined in.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
- Fixed a bug where `gleam export package-interface` would not properly generate
the package interface file if some modules were cached.
([Pedro Francisco](https://github.com/mine-tech-oficial)) and
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where pattern matching using a UTF-8 string constant would not
work correctly on the JavaScript target when the string contained escape
characters.
([Richard Viney](https://github.com/richard-viney))
- Fixed a bug where `gleam publish` wouldn't include gitignored or nested native
files.
([PgBiel](https://github.com/PgBiel))
## v1.8.1 - 2025-02-11
### Bug fixes
- Fixed a metadata caching bug where accessors for opaque types could sometimes
be used in other modules.
([Louis Pilfold](https://github.com/lpil))
================================================
FILE: compiler-cli/Cargo.toml
================================================
[package]
name = "gleam-cli"
version = "1.15.1"
authors = ["Louis Pilfold "]
edition = "2024"
license = "Apache-2.0"
[dependencies]
# The pure compiler
gleam-core = { path = "../compiler-core" }
# The language server
gleam-language-server = { path = "../language-server" }
# OS SIGINT and SIGTERM signal handling
ctrlc = { version = "3", features = ["termination"] }
# Command line interface
clap = { version = "4", features = ["derive"] }
# Recursively traversing directories
ignore = "0"
# Allow user to type in sensitive information without showing it in the shell
rpassword = "7"
# Async runtime
tokio = { version = "1", features = ["rt", "rt-multi-thread"] }
# Further file system functions (i.e. copy directory)
fs_extra = "1"
tracing-subscriber = { version = "0.3.20", features = ["fmt", "env-filter"] }
# HTTP client
reqwest = { version = "0", default-features = false, features = ["rustls-tls"] }
# Checksums
sha2 = "0"
# Getting hostname
hostname = "0"
# TOML parser/editor that preserves comments & formatting
toml_edit = "0"
# File locking
fslock = "0"
# Provides a way to determine if two files are the same using filesystem node ids
same-file = "1"
async-trait.workspace = true
base16.workspace = true
camino = { workspace = true, features = ["serde1"] }
debug-ignore.workspace = true
ecow.workspace = true
flate2.workspace = true
futures.workspace = true
hexpm = { path = "../hexpm" }
http.workspace = true
http-serde.workspace = true
im.workspace = true
itertools.workspace = true
lsp-server.workspace = true
lsp-types.workspace = true
opener.workspace = true
regex.workspace = true
serde.workspace = true
serde_json.workspace = true
strum.workspace = true
tar.workspace = true
termcolor.workspace = true
toml.workspace = true
tracing.workspace = true
pubgrub.workspace = true
[dev-dependencies]
# Creation of temporary directories
tempfile = "3"
# Setting file modification times for tests
filetime = "0.2"
pretty_assertions.workspace = true
insta.workspace = true
================================================
FILE: compiler-cli/clippy.toml
================================================
disallowed-methods = [
{ path = "std::path::Path::new", reason = "Manually constructed paths should use camino::Utf8Path" },
{ path = "std::path::PathBuf::new", reason = "Manually constructed pathbufs should use camino::Utf8Path" },
]
================================================
FILE: compiler-cli/src/add.rs
================================================
use camino::{Utf8Path, Utf8PathBuf};
use gleam_core::{
Error, Result,
error::{FileIoAction, FileKind},
paths::ProjectPaths,
};
use hexpm::version::{Identifier, Version};
use crate::{
cli,
dependencies::{self, parse_gleam_add_specifier},
fs,
};
pub fn command(paths: &ProjectPaths, packages_to_add: Vec, dev: bool) -> Result<()> {
let config = crate::config::root_config(paths)?;
if packages_to_add.iter().any(|name| name == &config.name) {
return Err(Error::CannotAddSelfAsDependency {
name: config.name.clone(),
});
}
let mut new_package_requirements = Vec::with_capacity(packages_to_add.len());
for specifier in packages_to_add {
new_package_requirements.push(parse_gleam_add_specifier(&specifier)?);
}
// Insert the new packages into the manifest and perform dependency
// resolution to determine suitable versions
let manifest = dependencies::resolve_and_download(
paths,
cli::Reporter::new(),
Some((new_package_requirements.clone(), dev)),
Vec::new(),
dependencies::DependencyManagerConfig {
use_manifest: dependencies::UseManifest::Yes,
check_major_versions: dependencies::CheckMajorVersions::No,
},
)?;
// Read gleam.toml and manifest.toml so we can insert new deps into it
let mut gleam_toml = read_toml_edit(&paths.root_config())?;
let mut manifest_toml = read_toml_edit(&paths.manifest())?;
// Insert the new deps
for (added_package, _) in new_package_requirements {
let added_package = added_package.to_string();
// Pull the selected version out of the new manifest so we know what it is
let version = &manifest
.packages
.iter()
.find(|package| package.name == *added_package)
.expect("Added package not found in resolved manifest")
.version;
tracing::info!(version=%version, "new_package_version_resolved");
// Produce a version requirement locked to the major version.
// i.e. if 1.2.3 is selected we want >= 1.2.3 and < 2.0.0
let range = format!(
">= {} and < {}.0.0",
version_to_string(version),
version.major + 1
);
// False positive. This package doesn't use the indexing API correctly.
#[allow(clippy::indexing_slicing)]
{
if dev {
let canonical_name = "dev_dependencies";
let deprecated_name = "dev-dependencies";
let has_canonical = gleam_toml.as_table().contains_key(canonical_name);
let has_deprecated = gleam_toml.as_table().contains_key(deprecated_name);
if !has_canonical && !has_deprecated {
gleam_toml["dev_dependencies"] = toml_edit::table();
}
let name = if has_deprecated {
deprecated_name
} else {
canonical_name
};
gleam_toml[name][&added_package] = toml_edit::value(range.clone());
} else {
if !gleam_toml.as_table().contains_key("dependencies") {
gleam_toml["dependencies"] = toml_edit::table();
}
gleam_toml["dependencies"][&added_package] = toml_edit::value(range.clone());
};
manifest_toml["requirements"][&added_package]["version"] = range.into();
}
}
// Write the updated config
fs::write(&paths.root_config(), &gleam_toml.to_string())?;
fs::write(&paths.manifest(), &manifest_toml.to_string())?;
Ok(())
}
fn read_toml_edit(name: &Utf8Path) -> Result {
fs::read(name)?
.parse::()
.map_err(|e| Error::FileIo {
kind: FileKind::File,
action: FileIoAction::Parse,
path: Utf8PathBuf::from("gleam.toml"),
err: Some(e.to_string()),
})
}
fn version_to_string(version: &Version) -> String {
let mut text = String::new();
text.push_str(&format!(
"{}.{}.{}",
version.major, version.minor, version.patch
));
if !version.pre.is_empty() {
text.push('-');
for (i, identifier) in version.pre.iter().enumerate() {
if i != 0 {
text.push('.');
}
match *identifier {
Identifier::Numeric(ref id) => text.push_str(&id.to_string()),
Identifier::AlphaNumeric(ref id) => text.push_str(id),
}
}
}
if let Some(build) = version.build.as_ref() {
text.push_str(&format!("+{build}"));
}
text
}
#[test]
fn displays_simple_version_correctly() {
let version = Version {
major: 1,
minor: 23,
patch: 4,
pre: vec![],
build: None,
};
let displayed = version_to_string(&version);
assert_eq!(displayed, "1.23.4");
}
#[test]
fn displays_full_version_correctly() {
let version = Version {
major: 1,
minor: 23,
patch: 4,
pre: vec![
Identifier::Numeric(123),
Identifier::AlphaNumeric("123abc".to_string()),
],
build: Some("12345abc".to_string()),
};
let displayed = version_to_string(&version);
assert_eq!(displayed, "1.23.4-123.123abc+12345abc");
}
================================================
FILE: compiler-cli/src/beam_compiler.rs
================================================
use gleam_core::{
Result,
error::{Error, ShellCommandFailureReason},
io::{FileSystemWriter, Stdio},
paths,
};
use crate::fs::get_os;
use std::{
collections::HashSet,
io::{self, BufRead, BufReader, Write},
process::{Child, ChildStdin, ChildStdout},
};
use camino::{Utf8Path, Utf8PathBuf};
use itertools::Itertools;
#[derive(Debug)]
struct BeamCompilerInner {
process: Child,
stdin: ChildStdin,
stdout: BufReader,
}
#[derive(Debug, Default)]
pub struct BeamCompiler {
inner: Option,
}
impl BeamCompiler {
pub fn compile(
&mut self,
io: &IO,
out: &Utf8Path,
lib: &Utf8Path,
modules: &HashSet,
stdio: Stdio,
) -> Result, Error> {
let inner = match self.inner {
Some(ref mut inner) => match inner.process.try_wait() {
Ok(None) => inner,
_ => self.inner.insert(self.spawn(io, out)?),
},
None => self.inner.insert(self.spawn(io, out)?),
};
let args = format!(
"{{\"{}\", \"{}\", [\"{}\"]}}",
escape_path(lib),
escape_path(out.join("ebin")),
modules
.iter()
.map(|module| escape_path(out.join(paths::ARTEFACT_DIRECTORY_NAME).join(module)))
.join("\", \"")
);
tracing::debug!(args=?args, "call_beam_compiler");
writeln!(inner.stdin, "{args}.").map_err(|e| Error::ShellCommand {
program: "escript".into(),
reason: ShellCommandFailureReason::IoError(e.kind()),
})?;
let mut buf = String::new();
let mut accumulated_modules: Vec = Vec::new();
while let (Ok(_), Ok(None)) = (inner.stdout.read_line(&mut buf), inner.process.try_wait()) {
match buf.trim() {
"gleam-compile-result-ok" => {
// Return Ok with the accumulated modules
return Ok(accumulated_modules);
}
"gleam-compile-result-error" => {
return Err(Error::ShellCommand {
program: "escript".into(),
reason: ShellCommandFailureReason::Unknown,
});
}
s if s.starts_with("gleam-compile-module:") => {
if let Some(module_content) = s.strip_prefix("gleam-compile-module:") {
accumulated_modules.push(module_content.to_string());
}
}
_ => match stdio {
Stdio::Inherit => print!("{buf}"),
Stdio::Null => {}
},
}
buf.clear()
}
// if we get here, stdout got closed before we got an "ok" or "err".
Err(Error::ShellCommand {
program: "escript".into(),
reason: ShellCommandFailureReason::Unknown,
})
}
fn spawn(
&self,
io: &IO,
out: &Utf8Path,
) -> Result {
let escript_path = out
.join(paths::ARTEFACT_DIRECTORY_NAME)
.join("gleam@@compile.erl");
let escript_source = std::include_str!("../templates/gleam@@compile.erl");
io.write(&escript_path, escript_source)?;
tracing::trace!(escript_path=?escript_path, "spawn_beam_compiler");
let mut process = std::process::Command::new("escript")
.arg(escript_path)
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.spawn()
.map_err(|e| match e.kind() {
io::ErrorKind::NotFound => Error::ShellProgramNotFound {
program: "escript".into(),
os: get_os(),
},
other => Error::ShellCommand {
program: "escript".into(),
reason: ShellCommandFailureReason::IoError(other),
},
})?;
let stdin = process.stdin.take().expect("could not get child stdin");
let stdout = process.stdout.take().expect("could not get child stdout");
Ok(BeamCompilerInner {
process,
stdin,
stdout: BufReader::new(stdout),
})
}
}
impl Drop for BeamCompiler {
fn drop(&mut self) {
if let Some(mut inner) = self.inner.take() {
// closing stdin will cause the erlang process to exit.
drop(inner.stdin);
let _ = inner.process.wait();
}
}
}
fn escape_path>(path: T) -> String {
path.as_ref().replace("\\", "\\\\")
}
================================================
FILE: compiler-cli/src/build.rs
================================================
use std::{rc::Rc, time::Instant};
use gleam_core::{
Result,
build::{Built, Codegen, NullTelemetry, Options, ProjectCompiler, Telemetry},
manifest::Manifest,
paths::ProjectPaths,
warning::WarningEmitterIO,
};
use crate::{
build_lock::BuildLock,
cli, dependencies,
fs::{self, ConsoleWarningEmitter},
};
pub fn download_dependencies(paths: &ProjectPaths, telemetry: impl Telemetry) -> Result {
dependencies::resolve_and_download(
paths,
telemetry,
None,
Vec::new(),
dependencies::DependencyManagerConfig {
use_manifest: dependencies::UseManifest::Yes,
check_major_versions: dependencies::CheckMajorVersions::No,
},
)
}
pub fn main(paths: &ProjectPaths, options: Options, manifest: Manifest) -> Result {
main_with_warnings(paths, options, manifest, Rc::new(ConsoleWarningEmitter))
}
pub(crate) fn main_with_warnings(
paths: &ProjectPaths,
options: Options,
manifest: Manifest,
warnings: Rc,
) -> Result {
let perform_codegen = options.codegen;
let root_config = crate::config::root_config(paths)?;
let telemetry: &'static dyn Telemetry = if options.no_print_progress {
&NullTelemetry
} else {
&cli::Reporter
};
let io = fs::ProjectIO::new();
let start = Instant::now();
let lock = BuildLock::new_target(
paths,
options.mode,
options.target.unwrap_or(root_config.target),
)?;
tracing::info!("Compiling packages");
let result = {
let _guard = lock.lock(telemetry);
let compiler = ProjectCompiler::new(
root_config,
options,
manifest.packages,
telemetry,
warnings,
paths.clone(),
io,
);
compiler.compile()?
};
match perform_codegen {
Codegen::All | Codegen::DepsOnly => telemetry.compiled_package(start.elapsed()),
Codegen::None => telemetry.checked_package(start.elapsed()),
};
Ok(result)
}
================================================
FILE: compiler-cli/src/build_lock.rs
================================================
use camino::Utf8PathBuf;
use gleam_core::{
Error, Result,
build::{Mode, Target, Telemetry},
error::{FileIoAction, FileKind},
paths::ProjectPaths,
};
use strum::IntoEnumIterator;
#[derive(Debug)]
pub(crate) struct BuildLock {
directory: Utf8PathBuf,
filename: String,
}
impl BuildLock {
/// Lock the build directory for the specified mode and target.
pub fn new_target(paths: &ProjectPaths, mode: Mode, target: Target) -> Result {
let directory = paths.build_directory();
crate::fs::mkdir(&directory)?;
let target = match target {
Target::Erlang => "erlang",
Target::JavaScript => "javascript",
};
Ok(Self {
directory,
filename: format!("gleam-{mode}-{target}.lock"),
})
}
/// Lock the packages directory.
pub fn new_packages(paths: &ProjectPaths) -> Result {
let directory = paths.build_packages_directory();
crate::fs::mkdir(&directory)?;
Ok(Self {
directory,
filename: "gleam.lock".to_string(),
})
}
/// Construct the lock file path
pub fn lock_path(&self) -> Utf8PathBuf {
self.directory.join(&self.filename)
}
/// Lock the directory specified by the lock
pub fn lock(&self, telemetry: &Telem) -> Result {
let lock_path = self.lock_path();
tracing::debug!(path=?lock_path, "locking_directory");
crate::fs::mkdir(&self.directory)?;
let mut file = fslock::LockFile::open(lock_path.as_str()).map_err(|e| Error::FileIo {
kind: FileKind::File,
path: lock_path.clone(),
action: FileIoAction::Create,
err: Some(e.to_string()),
})?;
if !file.try_lock_with_pid().expect("Trying directory locking") {
telemetry.waiting_for_build_directory_lock();
file.lock_with_pid().expect("Directory locking")
}
Ok(Guard(file))
}
/// Lock all build directories. Does not lock the packages directory.
pub fn lock_all_build(
paths: &ProjectPaths,
telemetry: &Telem,
) -> Result> {
let mut locks = vec![];
for mode in Mode::iter() {
for target in Target::iter() {
locks.push(Self::new_target(paths, mode, target)?.lock(telemetry)?);
}
}
Ok(locks)
}
}
#[derive(Debug)]
pub(crate) struct Guard(
// False positive. This is used in `drop`. Presumably the lint error is a
// bug in clippy.
#[allow(dead_code)] fslock::LockFile,
);
#[test]
fn locking_global() {
let paths = crate::project_paths_at_current_directory_without_toml();
let lock = BuildLock::new_packages(&paths).expect("make lock");
let _guard1: Guard = lock.lock(&gleam_core::build::NullTelemetry).unwrap();
println!("Locked!")
}
#[test]
fn locking_dev_erlang() {
let paths = crate::project_paths_at_current_directory_without_toml();
let lock = BuildLock::new_target(&paths, Mode::Dev, Target::Erlang).expect("make lock");
let _guard1: Guard = lock.lock(&gleam_core::build::NullTelemetry).unwrap();
println!("Locked!")
}
#[test]
fn locking_prod_erlang() {
let paths = crate::project_paths_at_current_directory_without_toml();
let lock = BuildLock::new_target(&paths, Mode::Prod, Target::Erlang).expect("make lock");
let _guard1: Guard = lock.lock(&gleam_core::build::NullTelemetry).unwrap();
println!("Locked!")
}
#[test]
fn locking_lsp_erlang() {
let paths = crate::project_paths_at_current_directory_without_toml();
let lock = BuildLock::new_target(&paths, Mode::Lsp, Target::Erlang).expect("make lock");
let _guard1: Guard = lock.lock(&gleam_core::build::NullTelemetry).unwrap();
println!("Locked!")
}
#[test]
fn locking_dev_javascript() {
let paths = crate::project_paths_at_current_directory_without_toml();
let lock = BuildLock::new_target(&paths, Mode::Dev, Target::JavaScript).expect("make lock");
let _guard1: Guard = lock.lock(&gleam_core::build::NullTelemetry).unwrap();
println!("Locked!")
}
#[test]
fn locking_prod_javascript() {
let paths = crate::project_paths_at_current_directory_without_toml();
let lock = BuildLock::new_target(&paths, Mode::Prod, Target::JavaScript).expect("make lock");
let _guard1: Guard = lock.lock(&gleam_core::build::NullTelemetry).unwrap();
println!("Locked!")
}
#[test]
fn locking_lsp_javascript() {
let paths = crate::project_paths_at_current_directory_without_toml();
let lock = BuildLock::new_target(&paths, Mode::Lsp, Target::JavaScript).expect("make lock");
let _guard1: Guard = lock.lock(&gleam_core::build::NullTelemetry).unwrap();
println!("Locked!")
}
================================================
FILE: compiler-cli/src/cli.rs
================================================
use ecow::EcoString;
use gleam_core::{
build::Telemetry,
error::{Error, StandardIoAction},
manifest::{Changed, ChangedGit, PackageChanges},
};
use hexpm::version::Version;
use itertools::Itertools as _;
use std::{
io::{IsTerminal, Write},
time::{Duration, Instant},
};
use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor};
#[derive(Debug, Default, Clone)]
pub struct Reporter;
impl Reporter {
pub fn new() -> Self {
Self
}
}
impl Telemetry for Reporter {
fn compiled_package(&self, duration: Duration) {
print_compiled(duration);
}
fn compiling_package(&self, name: &str) {
print_compiling(name);
}
fn checked_package(&self, duration: Duration) {
print_checked(duration);
}
fn checking_package(&self, name: &str) {
print_checking(name);
}
fn downloading_package(&self, name: &str) {
print_downloading(name)
}
fn packages_downloaded(&self, start: Instant, count: usize) {
print_packages_downloaded(start, count)
}
fn resolving_package_versions(&self) {
print_resolving_versions()
}
fn running(&self, name: &str) {
print_running(name);
}
fn waiting_for_build_directory_lock(&self) {
print_waiting_for_build_directory_lock()
}
fn resolved_package_versions(&self, changes: &PackageChanges) {
print_package_changes(changes)
}
}
pub fn ask(question: &str) -> Result {
print!("{question}: ");
std::io::stdout().flush().expect("ask stdout flush");
let mut answer = String::new();
let _ = std::io::stdin()
.read_line(&mut answer)
.map_err(|e| Error::StandardIo {
action: StandardIoAction::Read,
err: Some(e.kind()),
})?;
Ok(answer.trim().to_string())
}
pub fn confirm(question: &str) -> Result {
let answer = ask(&format!("{question} [y/n]"))?;
match answer.as_str() {
"y" | "yes" | "Y" | "YES" => Ok(true),
_ => Ok(false),
}
}
pub fn confirm_with_text(response: &str) -> Result {
let answer = ask(&format!("Type '{response}' to continue"))?;
Ok(response == answer)
}
pub fn ask_password(question: &str) -> Result {
let prompt = format!("{question} (will not be printed as you type): ");
rpassword::prompt_password(prompt)
.map_err(|e| Error::StandardIo {
action: StandardIoAction::Read,
err: Some(e.kind()),
})
.map(|s| EcoString::from(s.trim()))
}
pub fn print_publishing(name: &str, version: &Version) {
print_colourful_prefix("Publishing", &format!("{name} v{version}"))
}
pub fn print_published(detail: &str) {
print_colourful_prefix("Published", detail)
}
pub fn print_retired(package: &str, version: &str) {
print_colourful_prefix("Retired", &format!("{package} {version}"))
}
pub fn print_unretired(package: &str, version: &str) {
print_colourful_prefix("Unretired", &format!("{package} {version}"))
}
pub fn print_publishing_documentation() {
print_colourful_prefix("Publishing", "documentation");
}
fn print_downloading(text: &str) {
print_colourful_prefix("Downloading", text)
}
fn print_waiting_for_build_directory_lock() {
print_colourful_prefix("Waiting", "for build directory lock")
}
fn print_resolving_versions() {
print_colourful_prefix("Resolving", "versions")
}
fn print_compiling(text: &str) {
print_colourful_prefix("Compiling", text)
}
pub(crate) fn print_exported(text: &str) {
print_colourful_prefix("Exported", text)
}
pub(crate) fn print_checking(text: &str) {
print_colourful_prefix("Checking", text)
}
pub(crate) fn print_compiled(duration: Duration) {
print_colourful_prefix("Compiled", &format!("in {}", seconds(duration)))
}
pub(crate) fn print_checked(duration: Duration) {
print_colourful_prefix("Checked", &format!("in {}", seconds(duration)))
}
pub(crate) fn print_running(text: &str) {
print_colourful_prefix("Running", text)
}
pub(crate) fn print_package_changes(changes: &PackageChanges) {
for (name, version) in changes.added.iter().sorted() {
print_added(&format!("{name} v{version}"));
}
for Changed { name, old, new } in changes.changed.iter().sorted_by_key(|p| &p.name) {
print_changed(&format!("{name} v{old} -> v{new}"));
}
for ChangedGit {
name,
old_hash,
new_hash,
} in changes.changed_git.iter().sorted_by_key(|p| &p.name)
{
print_changed(&format!("{name} {old_hash} -> {new_hash}"));
}
for name in changes.removed.iter().sorted() {
print_removed(name);
}
}
fn print_added(text: &str) {
print_colourful_prefix("Added", text)
}
fn print_changed(text: &str) {
print_colourful_prefix("Changed", text)
}
fn print_removed(text: &str) {
print_colourful_prefix("Removed", text)
}
pub(crate) fn print_generating_documentation() {
print_colourful_prefix("Generating", "documentation")
}
pub(crate) fn print_transferring_ownership() {
print_colourful_prefix("Transferring", "ownership");
}
pub(crate) fn print_transferred_ownership() {
print_colourful_prefix("Transferred", "ownership");
}
fn print_packages_downloaded(start: Instant, count: usize) {
let elapsed = seconds(start.elapsed());
let msg = match count {
1 => format!("1 package in {elapsed}"),
_ => format!("{count} packages in {elapsed}"),
};
print_colourful_prefix("Downloaded", &msg)
}
pub fn seconds(duration: Duration) -> String {
format!("{:.2}s", duration.as_millis() as f32 / 1000.)
}
pub fn print_colourful_prefix(prefix: &str, text: &str) {
let buffer_writer = stderr_buffer_writer();
let mut buffer = buffer_writer.buffer();
buffer
.set_color(
ColorSpec::new()
.set_intense(true)
.set_fg(Some(Color::Magenta)),
)
.expect("print_green_prefix");
write!(buffer, "{prefix: >11}").expect("print_green_prefix");
buffer
.set_color(&ColorSpec::new())
.expect("print_green_prefix");
writeln!(buffer, " {text}").expect("print_green_prefix");
buffer_writer.print(&buffer).expect("print_green_prefix");
}
pub fn stderr_buffer_writer() -> BufferWriter {
// Don't add color codes to the output if standard error isn't connected to a terminal
BufferWriter::stderr(color_choice())
}
fn colour_forced() -> bool {
if let Ok(force) = std::env::var("FORCE_COLOR") {
!force.is_empty()
} else {
false
}
}
fn color_choice() -> ColorChoice {
if colour_forced() {
ColorChoice::Always
} else if std::io::stderr().is_terminal() {
ColorChoice::Auto
} else {
ColorChoice::Never
}
}
================================================
FILE: compiler-cli/src/compile_package.rs
================================================
use crate::{
CompilePackage, config,
fs::{self, ConsoleWarningEmitter, ProjectIO},
};
use camino::Utf8Path;
use ecow::EcoString;
use gleam_core::{
Error, Result,
build::{
Mode, NullTelemetry, PackageCompiler, StaleTracker, Target, TargetCodegenConfiguration,
},
error::{FileIoAction, FileKind},
metadata,
paths::{self, ProjectPaths},
type_::ModuleInterface,
uid::UniqueIdGenerator,
warning::WarningEmitter,
};
use std::{collections::HashSet, rc::Rc};
pub fn command(options: CompilePackage) -> Result<()> {
let ids = UniqueIdGenerator::new();
let mut type_manifests = load_libraries(&ids, &options.libraries_directory)?;
let mut defined_modules = im::HashMap::new();
let warnings = WarningEmitter::new(Rc::new(ConsoleWarningEmitter));
let paths = ProjectPaths::new(options.package_directory.clone());
let config = config::read(paths.root_config())?;
let target = match options.target {
Target::Erlang => TargetCodegenConfiguration::Erlang { app_file: None },
Target::JavaScript => TargetCodegenConfiguration::JavaScript {
emit_typescript_definitions: false,
prelude_location: options
.javascript_prelude
.ok_or_else(|| Error::JavaScriptPreludeRequired)?,
},
};
tracing::info!("Compiling package");
let mut compiler = PackageCompiler::new(
&config,
Mode::Dev,
&options.package_directory,
&options.output_directory,
&options.libraries_directory,
&target,
ids,
ProjectIO::new(),
);
compiler.write_entrypoint = false;
compiler.write_metadata = true;
compiler.compile_beam_bytecode = !options.skip_beam_compilation;
compiler
.compile(
&warnings,
&mut type_manifests,
&mut defined_modules,
&mut StaleTracker::default(),
&mut HashSet::new(),
&NullTelemetry,
)
.into_result()
.map(|_| ())
}
fn load_libraries(
ids: &UniqueIdGenerator,
lib: &Utf8Path,
) -> Result> {
tracing::info!("Reading precompiled module metadata files");
let mut manifests = im::HashMap::new();
for lib in fs::read_dir(lib)?.filter_map(Result::ok) {
let path = lib.path().join(paths::ARTEFACT_DIRECTORY_NAME);
if !path.is_dir() {
continue;
}
for module in fs::module_caches_paths(path)? {
let bytes = fs::read_bytes(module.clone())?;
let module = match metadata::decode(&bytes, ids.clone()) {
Ok(module) => module,
Err(e) => {
return Err(Error::FileIo {
kind: FileKind::File,
action: FileIoAction::Parse,
path: module,
err: Some(e.to_string()),
});
}
};
let _ = manifests.insert(module.name.clone(), module);
}
}
Ok(manifests)
}
================================================
FILE: compiler-cli/src/config.rs
================================================
use camino::Utf8PathBuf;
use gleam_core::{
config::PackageConfig,
error::{Error, FileIoAction, FileKind},
manifest::{Manifest, ManifestPackage, ManifestPackageSource},
paths::ProjectPaths,
};
#[derive(Debug, Clone, Copy)]
pub enum PackageKind {
Dependency,
Root,
}
/// Get the config for a dependency module. Return the config for the current
/// project if a dependency doesn't have a config file.
pub fn find_package_config_for_module(
mod_path: &str,
manifest: &Manifest,
project_paths: &ProjectPaths,
) -> Result<(PackageConfig, PackageKind), Error> {
for package in &manifest.packages {
// Not a Gleam package
if !package.build_tools.contains(&"gleam".into()) {
continue;
}
let root = package_root(package, project_paths);
let mut module_path = root.join("src").join(mod_path);
_ = module_path.set_extension("gleam");
// This package doesn't have the module we're looking for
if !module_path.is_file() {
continue;
}
let configuration = read(root.join("gleam.toml"))?;
return Ok((configuration, PackageKind::Dependency));
}
Ok((root_config(project_paths)?, PackageKind::Root))
}
fn package_root(package: &ManifestPackage, project_paths: &ProjectPaths) -> Utf8PathBuf {
match &package.source {
ManifestPackageSource::Local { path } => project_paths.root().join(path),
ManifestPackageSource::Hex { .. } | ManifestPackageSource::Git { .. } => {
project_paths.build_packages_package(&package.name)
}
}
}
pub fn root_config(paths: &ProjectPaths) -> Result {
read(paths.root_config())
}
pub fn read(config_path: Utf8PathBuf) -> Result {
let toml = crate::fs::read(&config_path)?;
let config: PackageConfig = toml::from_str(&toml).map_err(|e| Error::FileIo {
action: FileIoAction::Parse,
kind: FileKind::File,
path: config_path,
err: Some(e.to_string()),
})?;
config.check_gleam_compatibility()?;
Ok(config)
}
pub fn ensure_config_exists(paths: &ProjectPaths) -> Result<(), Error> {
let path = paths.root_config();
if !path.is_file() {
return Err(Error::FileIo {
action: FileIoAction::Read,
kind: FileKind::File,
path,
err: Some("File not found".into()),
});
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use gleam_core::manifest::Base16Checksum;
#[test]
fn package_root_hex() {
let paths = ProjectPaths::new(Utf8PathBuf::from("/app"));
let package = ManifestPackage {
name: "the_package".into(),
version: hexpm::version::Version::new(1, 0, 0),
build_tools: vec!["gleam".into()],
otp_app: None,
requirements: vec![],
source: ManifestPackageSource::Hex {
outer_checksum: Base16Checksum(vec![]),
},
};
assert_eq!(
package_root(&package, &paths),
Utf8PathBuf::from("/app/build/packages/the_package")
);
}
#[test]
fn package_root_git() {
let paths = ProjectPaths::new(Utf8PathBuf::from("/app"));
let package = ManifestPackage {
name: "the_package".into(),
version: hexpm::version::Version::new(1, 0, 0),
build_tools: vec!["gleam".into()],
otp_app: None,
requirements: vec![],
source: ManifestPackageSource::Git {
repo: "repo".into(),
commit: "commit".into(),
},
};
assert_eq!(
package_root(&package, &paths),
Utf8PathBuf::from("/app/build/packages/the_package")
);
}
#[test]
fn package_root_local() {
let paths = ProjectPaths::new(Utf8PathBuf::from("/app"));
let package = ManifestPackage {
name: "the_package".into(),
version: hexpm::version::Version::new(1, 0, 0),
build_tools: vec!["gleam".into()],
otp_app: None,
requirements: vec![],
source: ManifestPackageSource::Local {
path: Utf8PathBuf::from("../wibble"),
},
};
assert_eq!(
package_root(&package, &paths),
Utf8PathBuf::from("/app/../wibble")
);
}
}
================================================
FILE: compiler-cli/src/dependencies/dependency_manager.rs
================================================
use ecow::EcoString;
use futures::future;
use gleam_core::{
Error, Result,
build::{Mode, Telemetry},
config::PackageConfig,
dependency,
manifest::{Manifest, ManifestPackageSource, PackageChanges, Resolved},
paths::ProjectPaths,
requirement::Requirement,
};
use std::collections::HashMap;
use crate::{
build_lock::BuildLock,
dependencies::{pretty_print_major_versions_available, write_manifest_to_disc},
fs::ProjectIO,
};
use super::{
CheckMajorVersions, LocalPackages, UseManifest, add_missing_packages, is_same_requirements,
lookup_package, path_dependency_configs_unchanged, provide_git_package, provide_local_package,
read_manifest_from_disc, remove_extra_packages, unlock_packages,
};
/// Verifies that all specified packages exist in the manifest.
pub fn ensure_packages_exist_locally(manifest: &Manifest, packages: &[EcoString]) -> Result<()> {
let missing_packages: Vec = packages
.iter()
.filter(|package_name| !manifest.packages.iter().any(|p| &p.name == *package_name))
.cloned()
.collect();
if !missing_packages.is_empty() {
return Err(Error::PackagesToUpdateNotExist {
packages: missing_packages,
});
}
Ok(())
}
pub struct DependencyManagerConfig {
// If `Yes` we read the manifest from disc. If not set then we ignore any
// manifest which will result in the latest versions of the dependency
// packages being resolved (not the locked ones).
pub use_manifest: UseManifest,
/// When set to `Yes`, the cli will check for major version updates of direct dependencies and
/// print them to the console if the major versions are not upgradeable due to constraints.
pub check_major_versions: CheckMajorVersions,
}
impl DependencyManagerConfig {
pub fn into_dependency_manager(
self,
runtime: tokio::runtime::Handle,
package_fetcher: P,
telemetry: Telem,
mode: Mode,
) -> DependencyManager {
DependencyManager {
runtime,
package_fetcher,
telemetry,
mode,
use_manifest: self.use_manifest,
check_major_versions: self.check_major_versions,
}
}
}
pub struct DependencyManager {
runtime: tokio::runtime::Handle,
package_fetcher: P,
mode: Mode,
use_manifest: UseManifest,
telemetry: Telem,
check_major_versions: CheckMajorVersions,
}
impl DependencyManager
where
P: dependency::PackageFetcher,
Telem: Telemetry,
{
/// Resolve the dependency versions used by a package.
///
/// If the `use_manifest` configuration was set to `false` then it'll always resolve all the
/// versions, even if there are already versions locked in the manifest.
pub fn resolve_versions(
&self,
paths: &ProjectPaths,
config: &PackageConfig,
packages_to_update: Vec,
) -> Result {
// If there's no manifest then the only thing we can do is attempt to update the versions.
if !paths.manifest().exists() {
tracing::debug!("manifest_not_present");
let manifest = self.perform_version_resolution(paths, config, None, Vec::new())?;
ensure_packages_exist_locally(&manifest, &packages_to_update)?;
return Ok(Resolved::all_added(manifest));
}
let existing_manifest = read_manifest_from_disc(paths)?;
ensure_packages_exist_locally(&existing_manifest, &packages_to_update)?;
// If we have been asked not to use the manifest then
let (requirements_changed, manifest_for_resolver) = match self.use_manifest {
UseManifest::No => (true, None),
UseManifest::Yes => {
let config_dependencies = config.all_direct_dependencies()?;
let same_requirements = is_same_requirements(
&existing_manifest.requirements,
&config_dependencies,
paths.root(),
)?;
// If the manifest is to be used and the requirements have not changed then there's
// no point in performing resolution, it'll always result in the same versions
// already specified in the manifest.
if packages_to_update.is_empty()
&& same_requirements
&& path_dependency_configs_unchanged(&config_dependencies, paths)?
{
return Ok(Resolved::no_change(existing_manifest));
}
// Otherwise, use the manifest to inform resolution.
(!same_requirements, Some(&existing_manifest))
}
};
tracing::debug!("manifest_outdated");
let new_manifest = self.perform_version_resolution(
paths,
config,
manifest_for_resolver,
packages_to_update,
)?;
let resolved = Resolved {
package_changes: PackageChanges::between_manifests(&existing_manifest, &new_manifest),
manifest: new_manifest,
requirements_changed,
};
Ok(resolved)
}
pub fn resolve_and_download_versions(
&self,
paths: &ProjectPaths,
new_package: Option<(Vec<(EcoString, Requirement)>, bool)>,
packages_to_update: Vec,
) -> Result {
let span = tracing::info_span!("download_deps");
let _enter = span.enter();
// We do this before acquiring the build lock so that we don't create the
// build directory if there is no gleam.toml
crate::config::ensure_config_exists(paths)?;
let lock = BuildLock::new_packages(paths)?;
let _guard = lock.lock(&self.telemetry);
let fs = ProjectIO::boxed();
// Read the project config
let mut config = crate::config::read(paths.root_config())?;
let project_name = config.name.clone();
// Insert the new packages to add, if it exists
if let Some((packages, dev)) = new_package {
for (package, requirement) in packages {
if dev {
_ = config.dev_dependencies.insert(package, requirement);
} else {
_ = config.dependencies.insert(package, requirement);
};
}
}
// Determine what versions we need
let resolved = self.resolve_versions(paths, &config, packages_to_update)?;
let local = LocalPackages::read_from_disc(paths)?;
// Remove any packages that are no longer required due to gleam.toml changes
remove_extra_packages(paths, &local, &resolved.manifest, &self.telemetry)?;
// Download them from Hex to the local cache
self.runtime.block_on(add_missing_packages(
paths,
fs,
&resolved.manifest,
&local,
project_name,
&self.telemetry,
))?;
if resolved.any_changes() {
// Record new state of the packages directory
// TODO: test
tracing::debug!("writing_manifest_toml");
write_manifest_to_disc(paths, &resolved.manifest)?;
}
LocalPackages::from_manifest(&resolved.manifest).write_to_disc(paths)?;
// Display the changes in versions to the user.
self.telemetry
.resolved_package_versions(&resolved.package_changes);
// If requested to do so, check if there are major upgrades that could be performed with
// more relaxed version requirements, and inform the user if so.
if let CheckMajorVersions::Yes = self.check_major_versions {
let major_versions_available = dependency::check_for_major_version_updates(
&resolved.manifest,
&self.package_fetcher,
);
if !major_versions_available.is_empty() {
eprintln!(
"{}",
pretty_print_major_versions_available(major_versions_available)
);
}
}
Ok(resolved.manifest)
}
fn perform_version_resolution(
&self,
project_paths: &ProjectPaths,
config: &PackageConfig,
manifest: Option<&Manifest>,
packages_to_update: Vec,
) -> Result {
self.telemetry.resolving_package_versions();
let dependencies = config.dependencies_for(self.mode)?;
let mut locked = config.locked(manifest)?;
if !packages_to_update.is_empty() {
unlock_packages(&mut locked, &packages_to_update, manifest)?;
}
// Packages which are provided directly instead of downloaded from hex
let mut provided_packages = HashMap::new();
// The version requires of the current project
let mut root_requirements = HashMap::new();
// Populate the provided_packages and root_requirements maps
for (name, requirement) in dependencies.into_iter() {
let version = match requirement {
Requirement::Hex { version } => version,
Requirement::Path { path } => provide_local_package(
name.clone(),
&path,
project_paths.root(),
project_paths,
&mut provided_packages,
&mut vec![],
)?,
Requirement::Git { git, ref_ } => {
// If this package is locked and we already resolved a commit
// hash for it, we want to use that hash rather than pulling
// the latest commit.
let ref_to_use = if locked.contains_key(&name)
&& let Some(manifest) = manifest
&& let Some(package) = manifest
.packages
.iter()
.find(|package| package.name == name)
&& let ManifestPackageSource::Git { commit, .. } = &package.source
{
commit
} else {
// If the package is unlocked or we haven't resolved a version yet, we use
// the ref specified in `gleam.toml`.
&ref_
};
provide_git_package(
name.clone(),
&git,
ref_to_use,
project_paths,
&mut provided_packages,
&mut Vec::new(),
)?
}
};
let _ = root_requirements.insert(name, version);
}
// Convert provided packages into hex packages for pub-grub resolve
let provided_hex_packages = provided_packages
.iter()
.map(|(name, package)| (name.clone(), package.to_hex_package(name)))
.collect();
let resolved = dependency::resolve_versions(
&self.package_fetcher,
provided_hex_packages,
config.name.clone(),
root_requirements.into_iter(),
&locked,
)?;
// Convert the hex packages and local packages into manifest packages
let manifest_packages = self.runtime.block_on(future::try_join_all(
resolved
.into_iter()
.map(|(name, version)| lookup_package(name, version, &provided_packages)),
))?;
let manifest = Manifest {
packages: manifest_packages,
requirements: config.all_direct_dependencies()?,
};
Ok(manifest)
}
}
================================================
FILE: compiler-cli/src/dependencies/snapshots/gleam_cli__dependencies__tests__pretty_print_major_versions_available.snap
================================================
---
source: compiler-cli/src/dependencies/tests.rs
assertion_line: 1349
expression: output
snapshot_kind: text
---
The following dependencies have new major versions available:
Package Current Latest
------- ------- ------
gleam_stdlib 0.45.0 1.0.0
short_name 1.0.0 2.0.0
very_long_package_name 18.382.43 19.0.38
================================================
FILE: compiler-cli/src/dependencies/snapshots/gleam_cli__dependencies__tests__pretty_print_version_updates.snap
================================================
---
source: compiler-cli/src/dependencies/tests.rs
assertion_line: 1373
expression: output
snapshot_kind: text
---
Package Current Latest
------- ------- ------
gleam_stdlib 0.45.0 0.46.0
very_long_package_name 12.12.12 120.12.12
wisp 2.1.0 2.1.1
================================================
FILE: compiler-cli/src/dependencies/tests.rs
================================================
use std::collections::HashMap;
use camino::{Utf8Path, Utf8PathBuf};
use ecow::EcoString;
use hexpm::version::Version;
use pretty_assertions::assert_eq;
use gleam_core::{
Error,
build::Runtime,
config::{DenoConfig, DenoFlag, Docs, ErlangConfig, JavaScriptConfig},
manifest::{Base16Checksum, Manifest, ManifestPackage, ManifestPackageSource},
paths::ProjectPaths,
requirement::Requirement,
};
use crate::dependencies::*;
#[test]
fn list_manifest_format() {
let mut buffer = vec![];
let manifest = Manifest {
requirements: HashMap::new(),
packages: vec![
ManifestPackage {
name: "root".into(),
version: Version::parse("1.0.0").unwrap(),
build_tools: ["gleam".into()].into(),
otp_app: None,
requirements: vec![],
source: ManifestPackageSource::Hex {
outer_checksum: Base16Checksum(vec![1, 2, 3, 4]),
},
},
ManifestPackage {
name: "aaa".into(),
version: Version::new(0, 4, 2),
build_tools: ["rebar3".into(), "make".into()].into(),
otp_app: Some("aaa_app".into()),
requirements: vec!["zzz".into(), "gleam_stdlib".into()],
source: ManifestPackageSource::Hex {
outer_checksum: Base16Checksum(vec![3, 22]),
},
},
ManifestPackage {
name: "zzz".into(),
version: Version::new(0, 4, 0),
build_tools: ["mix".into()].into(),
otp_app: None,
requirements: vec![],
source: ManifestPackageSource::Hex {
outer_checksum: Base16Checksum(vec![3, 22]),
},
},
],
};
list_manifest_packages(&mut buffer, manifest).unwrap();
assert_eq!(
std::str::from_utf8(&buffer).unwrap(),
"Package Version
------- -------
root 1.0.0
aaa 0.4.2
zzz 0.4.0
"
)
}
#[test]
fn tree_format() {
let mut buffer = vec![];
let manifest = Manifest {
requirements: HashMap::new(),
packages: vec![
ManifestPackage {
name: "deps_proj".into(),
version: Version::parse("1.0.0").unwrap(),
build_tools: [].into(),
otp_app: None,
requirements: vec!["gleam_regexp".into(), "gleam_stdlib".into()],
source: ManifestPackageSource::Hex {
outer_checksum: Base16Checksum(vec![1, 2, 3, 4]),
},
},
ManifestPackage {
name: "gleam_stdlib".into(),
version: Version::new(0, 52, 0),
build_tools: ["rebar3".into(), "make".into()].into(),
otp_app: Some("aaa_app".into()),
requirements: vec![],
source: ManifestPackageSource::Hex {
outer_checksum: Base16Checksum(vec![3, 22]),
},
},
ManifestPackage {
name: "gleam_regexp".into(),
version: Version::new(1, 0, 0),
build_tools: ["mix".into()].into(),
otp_app: None,
requirements: vec!["gleam_stdlib".into()],
source: ManifestPackageSource::Hex {
outer_checksum: Base16Checksum(vec![3, 22]),
},
},
],
};
let options = TreeOptions {
package: None,
invert: None,
};
let root_package_name = EcoString::from("deps_proj");
list_package_and_dependencies_tree(
&mut buffer,
options,
manifest.packages.clone(),
root_package_name,
)
.unwrap();
assert_eq!(
std::str::from_utf8(&buffer).unwrap(),
r#"deps_proj v1.0.0
├── gleam_regexp v1.0.0
│ └── gleam_stdlib v0.52.0
└── gleam_stdlib v0.52.0
"#
)
}
#[test]
fn tree_package_format() {
let mut buffer = vec![];
let manifest = Manifest {
requirements: HashMap::new(),
packages: vec![
ManifestPackage {
name: "gleam_stdlib".into(),
version: Version::new(0, 52, 0),
build_tools: ["rebar3".into(), "make".into()].into(),
otp_app: Some("aaa_app".into()),
requirements: vec![],
source: ManifestPackageSource::Hex {
outer_checksum: Base16Checksum(vec![3, 22]),
},
},
ManifestPackage {
name: "deps_proj".into(),
version: Version::parse("1.0.0").unwrap(),
build_tools: [].into(),
otp_app: None,
requirements: vec!["gleam_stdlib".into(), "gleam_regexp".into()],
source: ManifestPackageSource::Hex {
outer_checksum: Base16Checksum(vec![1, 2, 3, 4]),
},
},
ManifestPackage {
name: "gleam_regexp".into(),
version: Version::new(1, 0, 0),
build_tools: ["mix".into()].into(),
otp_app: None,
requirements: vec!["gleam_stdlib".into()],
source: ManifestPackageSource::Hex {
outer_checksum: Base16Checksum(vec![3, 22]),
},
},
],
};
let options = TreeOptions {
package: Some("gleam_regexp".to_string()),
invert: None,
};
let root_package_name = EcoString::from("deps_proj");
list_package_and_dependencies_tree(
&mut buffer,
options,
manifest.packages.clone(),
root_package_name,
)
.unwrap();
assert_eq!(
std::str::from_utf8(&buffer).unwrap(),
r#"gleam_regexp v1.0.0
└── gleam_stdlib v0.52.0
"#
)
}
#[test]
fn tree_invert_format() {
let mut buffer = vec![];
let manifest = Manifest {
requirements: HashMap::new(),
packages: vec![
ManifestPackage {
name: "gleam_stdlib".into(),
version: Version::new(0, 52, 0),
build_tools: ["rebar3".into(), "make".into()].into(),
otp_app: Some("aaa_app".into()),
requirements: vec![],
source: ManifestPackageSource::Hex {
outer_checksum: Base16Checksum(vec![3, 22]),
},
},
ManifestPackage {
name: "deps_proj".into(),
version: Version::parse("1.0.0").unwrap(),
build_tools: [].into(),
otp_app: None,
requirements: vec!["gleam_stdlib".into(), "gleam_regexp".into()],
source: ManifestPackageSource::Hex {
outer_checksum: Base16Checksum(vec![1, 2, 3, 4]),
},
},
ManifestPackage {
name: "gleam_regexp".into(),
version: Version::new(1, 0, 0),
build_tools: ["mix".into()].into(),
otp_app: None,
requirements: vec!["gleam_stdlib".into()],
source: ManifestPackageSource::Hex {
outer_checksum: Base16Checksum(vec![3, 22]),
},
},
],
};
let options = TreeOptions {
package: None,
invert: Some("gleam_stdlib".to_string()),
};
let root_package_name = EcoString::from("deps_proj");
list_package_and_dependencies_tree(
&mut buffer,
options,
manifest.packages.clone(),
root_package_name,
)
.unwrap();
assert_eq!(
std::str::from_utf8(&buffer).unwrap(),
r#"gleam_stdlib v0.52.0
├── deps_proj v1.0.0
└── gleam_regexp v1.0.0
└── deps_proj v1.0.0
"#
)
}
#[test]
fn list_tree_invalid_package_format() {
let mut buffer = vec![];
let manifest = Manifest {
requirements: HashMap::new(),
packages: vec![
ManifestPackage {
name: "gleam_stdlib".into(),
version: Version::new(0, 52, 0),
build_tools: ["rebar3".into(), "make".into()].into(),
otp_app: Some("aaa_app".into()),
requirements: vec![],
source: ManifestPackageSource::Hex {
outer_checksum: Base16Checksum(vec![3, 22]),
},
},
ManifestPackage {
name: "gleam_regexp".into(),
version: Version::new(1, 0, 0),
build_tools: ["mix".into()].into(),
otp_app: None,
requirements: vec!["gleam_stdlib".into()],
source: ManifestPackageSource::Hex {
outer_checksum: Base16Checksum(vec![3, 22]),
},
},
ManifestPackage {
name: "root".into(),
version: Version::parse("1.0.0").unwrap(),
build_tools: [].into(),
otp_app: None,
requirements: vec!["gleam_regexp".into(), "gleam_stdlib".into()],
source: ManifestPackageSource::Hex {
outer_checksum: Base16Checksum(vec![1, 2, 3, 4]),
},
},
],
};
let options = TreeOptions {
package: Some("zzzzzz".to_string()),
invert: None,
};
let root_package_name = EcoString::from("deps_proj");
list_package_and_dependencies_tree(
&mut buffer,
options,
manifest.packages.clone(),
root_package_name,
)
.unwrap();
assert_eq!(
std::str::from_utf8(&buffer).unwrap(),
r#"Package not found. Please check the package name.
"#
)
}
#[test]
fn parse_gleam_add_specifier_invalid_semver() {
assert!(parse_gleam_add_specifier("some_package@1.2.3.4").is_err());
}
#[test]
fn parse_gleam_add_specifier_non_numeric_version() {
assert!(parse_gleam_add_specifier("some_package@not_a_version").is_err());
}
#[test]
fn parse_gleam_add_specifier_default() {
let provided = "some_package";
let expected = Requirement::hex(">= 0.0.0").unwrap();
let (package, version) = parse_gleam_add_specifier(provided).unwrap();
assert_eq!(version, expected);
assert_eq!("some_package", package);
}
#[test]
fn parse_gleam_add_specifier_major_only() {
let provided = "wobble@1";
let expected = Requirement::hex(">= 1.0.0 and < 2.0.0").unwrap();
let (package, version) = parse_gleam_add_specifier(provided).unwrap();
assert_eq!(version, expected);
assert_eq!("wobble", package);
}
#[test]
fn parse_gleam_add_specifier_major_and_minor() {
let provided = "wibble@1.2";
let expected = Requirement::hex(">= 1.2.0 and < 2.0.0").unwrap();
let (package, version) = parse_gleam_add_specifier(provided).unwrap();
assert_eq!(version, expected);
assert_eq!("wibble", package);
}
#[test]
fn parse_gleam_add_specifier_major_minor_and_patch() {
let provided = "bobble@1.2.3";
let expected = Requirement::hex("1.2.3").unwrap();
let (package, version) = parse_gleam_add_specifier(provided).unwrap();
assert_eq!(version, expected);
assert_eq!("bobble", package);
}
#[test]
fn missing_local_packages() {
let manifest = Manifest {
requirements: HashMap::new(),
packages: vec![
ManifestPackage {
name: "root".into(),
version: Version::parse("1.0.0").unwrap(),
build_tools: ["gleam".into()].into(),
otp_app: None,
requirements: vec![],
source: ManifestPackageSource::Hex {
outer_checksum: Base16Checksum(vec![1, 2, 3, 4]),
},
},
ManifestPackage {
name: "local1".into(),
version: Version::parse("1.0.0").unwrap(),
build_tools: ["gleam".into()].into(),
otp_app: None,
requirements: vec![],
source: ManifestPackageSource::Hex {
outer_checksum: Base16Checksum(vec![1, 2, 3, 4, 5]),
},
},
ManifestPackage {
name: "local2".into(),
version: Version::parse("3.0.0").unwrap(),
build_tools: ["gleam".into()].into(),
otp_app: None,
requirements: vec![],
source: ManifestPackageSource::Hex {
outer_checksum: Base16Checksum(vec![1, 2, 3, 4, 5]),
},
},
],
};
let mut extra = LocalPackages {
packages: [
("local2".into(), Version::parse("2.0.0").unwrap()),
("local3".into(), Version::parse("3.0.0").unwrap()),
]
.into(),
}
.missing_local_packages(&manifest, "root");
extra.sort();
assert_eq!(
extra,
[
&ManifestPackage {
name: "local1".into(),
version: Version::parse("1.0.0").unwrap(),
build_tools: ["gleam".into()].into(),
otp_app: None,
requirements: vec![],
source: ManifestPackageSource::Hex {
outer_checksum: Base16Checksum(vec![1, 2, 3, 4, 5]),
},
},
&ManifestPackage {
name: "local2".into(),
version: Version::parse("3.0.0").unwrap(),
build_tools: ["gleam".into()].into(),
otp_app: None,
requirements: vec![],
source: ManifestPackageSource::Hex {
outer_checksum: Base16Checksum(vec![1, 2, 3, 4, 5]),
},
},
]
)
}
#[test]
fn extra_local_packages() {
let mut extra = LocalPackages {
packages: [
("local1".into(), Version::parse("1.0.0").unwrap()),
("local2".into(), Version::parse("2.0.0").unwrap()),
("local3".into(), Version::parse("3.0.0").unwrap()),
]
.into(),
}
.extra_local_packages(&Manifest {
requirements: HashMap::new(),
packages: vec![
ManifestPackage {
name: "local1".into(),
version: Version::parse("1.0.0").unwrap(),
build_tools: ["gleam".into()].into(),
otp_app: None,
requirements: vec![],
source: ManifestPackageSource::Hex {
outer_checksum: Base16Checksum(vec![1, 2, 3, 4, 5]),
},
},
ManifestPackage {
name: "local2".into(),
version: Version::parse("3.0.0").unwrap(),
build_tools: ["gleam".into()].into(),
otp_app: None,
requirements: vec![],
source: ManifestPackageSource::Hex {
outer_checksum: Base16Checksum(vec![4, 5]),
},
},
],
});
extra.sort();
assert_eq!(
extra,
[
("local2".into(), Version::new(2, 0, 0)),
("local3".into(), Version::new(3, 0, 0)),
]
)
}
#[test]
fn provide_wrong_package() {
let mut provided = HashMap::new();
let project_paths = crate::project_paths_at_current_directory_without_toml();
let result = provide_local_package(
"wrong_name".into(),
Utf8Path::new("./test/hello_world"),
Utf8Path::new("./"),
&project_paths,
&mut provided,
&mut vec!["root".into(), "subpackage".into()],
);
match result {
Err(Error::WrongDependencyProvided {
expected, found, ..
}) => {
assert_eq!(expected, "wrong_name");
assert_eq!(found, "hello_world");
}
_ => {
panic!("Expected WrongDependencyProvided error")
}
}
}
#[test]
fn provide_existing_package() {
let mut provided = HashMap::new();
let project_paths = crate::project_paths_at_current_directory_without_toml();
let result = provide_local_package(
"hello_world".into(),
Utf8Path::new("./test/hello_world"),
Utf8Path::new("./"),
&project_paths,
&mut provided,
&mut vec!["root".into(), "subpackage".into()],
);
assert_eq!(
result,
Ok(hexpm::version::Range::new("== 0.1.0".into()).unwrap())
);
let result = provide_local_package(
"hello_world".into(),
Utf8Path::new("./test/hello_world"),
Utf8Path::new("./"),
&project_paths,
&mut provided,
&mut vec!["root".into(), "subpackage".into()],
);
assert_eq!(
result,
Ok(hexpm::version::Range::new("== 0.1.0".into()).unwrap())
);
}
#[test]
fn provide_conflicting_package() {
let mut provided = HashMap::new();
let project_paths = crate::project_paths_at_current_directory_without_toml();
let result = provide_local_package(
"hello_world".into(),
Utf8Path::new("./test/hello_world"),
Utf8Path::new("./"),
&project_paths,
&mut provided,
&mut vec!["root".into(), "subpackage".into()],
);
assert_eq!(
result,
Ok(hexpm::version::Range::new("== 0.1.0".into()).unwrap())
);
let result = provide_package(
"hello_world".into(),
Utf8PathBuf::from("./test/other"),
ProvidedPackageSource::Local {
path: Utf8Path::new("./test/other").to_path_buf(),
},
&project_paths,
&mut provided,
&mut vec!["root".into(), "subpackage".into()],
);
match result {
Err(Error::ProvidedDependencyConflict { package, .. }) => {
assert_eq!(package, "hello_world");
}
_ => {
panic!("Expected ProvidedDependencyConflict error")
}
}
}
#[test]
fn provided_is_absolute() {
let mut provided = HashMap::new();
let project_paths = crate::project_paths_at_current_directory_without_toml();
let result = provide_local_package(
"hello_world".into(),
Utf8Path::new("./test/hello_world"),
Utf8Path::new("./"),
&project_paths,
&mut provided,
&mut vec!["root".into(), "subpackage".into()],
);
assert_eq!(
result,
Ok(hexpm::version::Range::new("== 0.1.0".into()).unwrap())
);
let package = provided.get("hello_world").unwrap().clone();
match package.source {
ProvidedPackageSource::Local { path } => {
assert!(path.is_absolute())
}
_ => {
panic!("Provide_local_package provided a package that is not local!")
}
}
}
#[test]
fn provided_recursive() {
let mut provided = HashMap::new();
let project_paths = crate::project_paths_at_current_directory_without_toml();
let result = provide_local_package(
"hello_world".into(),
Utf8Path::new("./test/hello_world"),
Utf8Path::new("./"),
&project_paths,
&mut provided,
&mut vec!["root".into(), "hello_world".into(), "subpackage".into()],
);
assert_eq!(
result,
Err(Error::PackageCycle {
packages: vec!["subpackage".into(), "hello_world".into()],
})
)
}
#[test]
fn provided_local_to_hex() {
let provided_package = ProvidedPackage {
version: Version::new(1, 0, 0),
source: ProvidedPackageSource::Local {
path: "canonical/path/to/package".into(),
},
requirements: [
(
"req_1".into(),
hexpm::version::Range::new("~> 1.0.0".into()).unwrap(),
),
(
"req_2".into(),
hexpm::version::Range::new("== 1.0.0".into()).unwrap(),
),
]
.into(),
};
let hex_package = hexpm::Package {
name: "package".into(),
repository: "local".into(),
releases: vec![hexpm::Release {
version: Version::new(1, 0, 0),
retirement_status: None,
outer_checksum: vec![],
meta: (),
requirements: [
(
"req_1".into(),
hexpm::Dependency {
requirement: hexpm::version::Range::new("~> 1.0.0".into()).unwrap(),
optional: false,
app: None,
repository: None,
},
),
(
"req_2".into(),
hexpm::Dependency {
requirement: hexpm::version::Range::new("== 1.0.0".into()).unwrap(),
optional: false,
app: None,
repository: None,
},
),
]
.into(),
}],
};
assert_eq!(
provided_package.to_hex_package(&"package".into()),
hex_package
);
}
#[test]
fn provided_git_to_hex() {
let provided_package = ProvidedPackage {
version: Version::new(1, 0, 0),
source: ProvidedPackageSource::Git {
repo: "https://github.com/gleam-lang/gleam.git".into(),
commit: "bd9fe02f72250e6a136967917bcb1bdccaffa3c8".into(),
},
requirements: [
(
"req_1".into(),
hexpm::version::Range::new("~> 1.0.0".into()).unwrap(),
),
(
"req_2".into(),
hexpm::version::Range::new("== 1.0.0".into()).unwrap(),
),
]
.into(),
};
let hex_package = hexpm::Package {
name: "package".into(),
repository: "local".into(),
releases: vec![hexpm::Release {
version: Version::new(1, 0, 0),
retirement_status: None,
outer_checksum: vec![],
meta: (),
requirements: [
(
"req_1".into(),
hexpm::Dependency {
requirement: hexpm::version::Range::new("~> 1.0.0".into()).unwrap(),
optional: false,
app: None,
repository: None,
},
),
(
"req_2".into(),
hexpm::Dependency {
requirement: hexpm::version::Range::new("== 1.0.0".into()).unwrap(),
optional: false,
app: None,
repository: None,
},
),
]
.into(),
}],
};
assert_eq!(
provided_package.to_hex_package(&"package".into()),
hex_package
);
}
#[test]
fn provided_local_to_manifest() {
let provided_package = ProvidedPackage {
version: Version::new(1, 0, 0),
source: ProvidedPackageSource::Local {
path: "canonical/path/to/package".into(),
},
requirements: [
(
"req_1".into(),
hexpm::version::Range::new("~> 1.0.0".into()).unwrap(),
),
(
"req_2".into(),
hexpm::version::Range::new("== 1.0.0".into()).unwrap(),
),
]
.into(),
};
let manifest_package = ManifestPackage {
name: "package".into(),
version: Version::new(1, 0, 0),
otp_app: None,
build_tools: vec!["gleam".into()],
requirements: vec!["req_1".into(), "req_2".into()],
source: ManifestPackageSource::Local {
path: "canonical/path/to/package".into(),
},
};
assert_eq!(
provided_package.to_manifest_package("package"),
manifest_package
);
}
#[test]
fn provided_git_to_manifest() {
let provided_package = ProvidedPackage {
version: Version::new(1, 0, 0),
source: ProvidedPackageSource::Git {
repo: "https://github.com/gleam-lang/gleam.git".into(),
commit: "bd9fe02f72250e6a136967917bcb1bdccaffa3c8".into(),
},
requirements: [
(
"req_1".into(),
hexpm::version::Range::new("~> 1.0.0".into()).unwrap(),
),
(
"req_2".into(),
hexpm::version::Range::new("== 1.0.0".into()).unwrap(),
),
]
.into(),
};
let manifest_package = ManifestPackage {
name: "package".into(),
version: Version::new(1, 0, 0),
otp_app: None,
build_tools: vec!["gleam".into()],
requirements: vec!["req_1".into(), "req_2".into()],
source: ManifestPackageSource::Git {
repo: "https://github.com/gleam-lang/gleam.git".into(),
commit: "bd9fe02f72250e6a136967917bcb1bdccaffa3c8".into(),
},
};
assert_eq!(
provided_package.to_manifest_package("package"),
manifest_package
);
}
#[test]
fn verified_requirements_equality_with_canonicalized_paths() {
let temp_dir = tempfile::tempdir().expect("Failed to create a temp directory");
let temp_path = Utf8PathBuf::from_path_buf(temp_dir.path().to_path_buf())
.expect("Path should be valid UTF-8");
let sub_dir = temp_path.join("subdir");
std::fs::create_dir(&sub_dir).expect("Failed to create a subdir");
let file_path = sub_dir.join("file.txt");
fs::write(&file_path, "content").expect("Failed to write to file");
let canonical_path = std::fs::canonicalize(&file_path).expect("Failed to canonicalize path");
let relative_path = temp_path.join("./subdir/../subdir/./file.txt");
let requirements1 = HashMap::from([(
EcoString::from("dep1"),
Requirement::Path {
path: Utf8PathBuf::from(canonical_path.to_str().expect("Path should be valid UTF-8")),
},
)]);
let requirements2 = HashMap::from([(
EcoString::from("dep1"),
Requirement::Path {
path: Utf8PathBuf::from(relative_path.to_string()),
},
)]);
assert!(
is_same_requirements(&requirements1, &requirements2, &temp_path)
.expect("Requirements should be the same")
);
}
#[test]
fn test_path_dependency_config_updates() {
let temp_dir = tempfile::tempdir().expect("Failed to create a temp directory");
let root_path = Utf8PathBuf::from_path_buf(temp_dir.path().to_path_buf())
.expect("Path should be valid UTF-8");
let paths = ProjectPaths::new(root_path.clone());
let dep_path = root_path.join("dep");
std::fs::create_dir_all(&dep_path).expect("Failed to create dependency directory");
let build_packages_dir = root_path.join("build").join("packages");
std::fs::create_dir_all(&build_packages_dir)
.expect("Failed to create build/packages directory");
let config = "name = \"dep\"
version = \"1.0.0\"
[dependencies]
";
let dep_config_path = dep_path.join("gleam.toml");
fs::write(&dep_config_path, config).expect("Failed to write to manifest file");
let requirements = HashMap::from([(
EcoString::from("dep"),
Requirement::Path {
path: Utf8PathBuf::from("dep"),
},
)]);
// Initial check testing
let unchanged = path_dependency_configs_unchanged(&requirements, &paths).unwrap();
assert!(!unchanged, "fresh always needs resolution");
let fingerprint_path = build_packages_dir.join("dep.config_fingerprint");
assert!(fingerprint_path.exists(), "fingerprint must exist");
let unchanged = path_dependency_configs_unchanged(&requirements, &paths).unwrap();
assert!(unchanged, "is unchanged");
// Set fingerprint mtime to some time in the past. This causes the mtime check to fail,
// so it moves on to checking the fingerprint itself.
let past = std::time::SystemTime::now() - std::time::Duration::from_secs(10);
let past = filetime::FileTime::from_system_time(past);
filetime::set_file_mtime(&fingerprint_path, past).unwrap();
let unchanged = path_dependency_configs_unchanged(&requirements, &paths).unwrap();
assert!(unchanged, "mtime is outdated, but content has not changed");
// Writing new content means the mtime and the fingerprint checks will fail.
let config = "name = \"dep\"
version = \"1.0.0\"
[dependencies]
blah = \">= 1.0.0\"
";
fs::write(&dep_config_path, config).unwrap();
let unchanged = path_dependency_configs_unchanged(&requirements, &paths).unwrap();
assert!(!unchanged, "content has changed");
// Run again, to ensure that the fingerprint has been updated.
let unchanged = path_dependency_configs_unchanged(&requirements, &paths).unwrap();
assert!(unchanged, "no changes since last run");
// Test that mtime is checked first, and that content is not checked when the mtime
// is still valid. We do this by having having content that would fail the fingerprint
// check, but having a fresh mtime so it never gets checked.
// This can never happen in reality as you can't update the content without updating
// the mtime, but we create the situation in this test to verify the short-circuiting
// behaviour.
let config = "name = \"dep\"
version = \"1.0.0\"
[dependencies]
blah = \">= 1.0.0\"
wub = \">= 1.0.0\"
";
fs::write(&dep_config_path, config).unwrap();
let future = std::time::SystemTime::now() + std::time::Duration::from_secs(10);
let future = filetime::FileTime::from_system_time(future);
filetime::set_file_mtime(&fingerprint_path, future).unwrap();
let unchanged = path_dependency_configs_unchanged(&requirements, &paths).unwrap();
assert!(unchanged, "fingerprint is outdated, but mtime is not");
}
fn create_testable_unlock_manifest(
packages: Vec<(EcoString, Version, Vec)>,
requirements: Vec<(EcoString, EcoString)>,
) -> Manifest {
let manifest_packages = packages
.into_iter()
.map(|(name, version, requirements)| ManifestPackage {
name,
version,
build_tools: vec!["gleam".into()],
otp_app: None,
requirements,
source: ManifestPackageSource::Hex {
outer_checksum: Base16Checksum(vec![]),
},
})
.collect();
let root_requirements = requirements
.into_iter()
.map(|(name, range)| {
(
name,
Requirement::Hex {
version: hexpm::version::Range::new(range.into()).unwrap(),
},
)
})
.collect();
Manifest {
packages: manifest_packages,
requirements: root_requirements,
}
}
#[test]
fn test_unlock_package() {
let mut locked = HashMap::from([
("package_a".into(), Version::new(1, 0, 0)),
("package_b".into(), Version::new(2, 0, 0)),
("package_c".into(), Version::new(3, 0, 0)),
("package_d".into(), Version::new(4, 0, 0)),
]);
let packages = vec![
(
"package_a".into(),
Version::new(1, 0, 0),
vec!["package_b".into()],
),
(
"package_b".into(),
Version::new(2, 0, 0),
vec!["package_c".into()],
),
("package_c".into(), Version::new(3, 0, 0), vec![]),
("package_d".into(), Version::new(4, 0, 0), vec![]),
];
let manifest = create_testable_unlock_manifest(packages, Vec::new());
let packages_to_unlock = vec!["package_a".into()];
unlock_packages(&mut locked, &packages_to_unlock, Some(&manifest)).unwrap();
assert!(!locked.contains_key("package_a"));
assert!(!locked.contains_key("package_b"));
assert!(!locked.contains_key("package_c"));
assert!(locked.contains_key("package_d"));
}
#[test]
fn test_unlock_package_without_manifest() {
let mut locked = HashMap::from([
("package_a".into(), Version::new(1, 0, 0)),
("package_b".into(), Version::new(2, 0, 0)),
("package_c".into(), Version::new(3, 0, 0)),
]);
let packages_to_unlock = vec!["package_a".into()];
unlock_packages(&mut locked, &packages_to_unlock, None).unwrap();
assert!(!locked.contains_key("package_a"));
assert!(locked.contains_key("package_b"));
assert!(locked.contains_key("package_c"));
}
#[test]
fn test_unlock_nonexistent_package() {
let initial_locked = HashMap::from([
("package_a".into(), Version::new(1, 0, 0)),
("package_b".into(), Version::new(2, 0, 0)),
]);
let packages = vec![
(
"package_a".into(),
Version::new(1, 0, 0),
vec!["package_b".into()],
),
("package_b".into(), Version::new(2, 0, 0), vec![]),
];
let manifest = create_testable_unlock_manifest(packages, Vec::new());
let packages_to_unlock = vec!["nonexistent_package".into()];
let mut locked = initial_locked.clone();
unlock_packages(&mut locked, &packages_to_unlock, Some(&manifest)).unwrap();
assert_eq!(
initial_locked, locked,
"Locked packages should remain unchanged"
);
}
#[test]
fn test_unlock_multiple_packages() {
let mut locked = HashMap::from([
("package_a".into(), Version::new(1, 0, 0)),
("package_b".into(), Version::new(2, 0, 0)),
("package_c".into(), Version::new(3, 0, 0)),
("package_d".into(), Version::new(4, 0, 0)),
("package_e".into(), Version::new(5, 0, 0)),
]);
let packages = vec![
(
"package_a".into(),
Version::new(1, 0, 0),
vec!["package_b".into()],
),
(
"package_b".into(),
Version::new(2, 0, 0),
vec!["package_c".into()],
),
("package_c".into(), Version::new(3, 0, 0), vec![]),
(
"package_d".into(),
Version::new(4, 0, 0),
vec!["package_e".into()],
),
("package_e".into(), Version::new(5, 0, 0), vec![]),
];
let manifest = create_testable_unlock_manifest(packages, Vec::new());
let packages_to_unlock = vec!["package_a".into(), "package_d".into()];
unlock_packages(&mut locked, &packages_to_unlock, Some(&manifest)).unwrap();
assert!(!locked.contains_key("package_a"));
assert!(!locked.contains_key("package_b"));
assert!(!locked.contains_key("package_c"));
assert!(!locked.contains_key("package_d"));
assert!(!locked.contains_key("package_e"));
}
#[test]
fn test_unlock_packages_empty_input() {
let initial_locked = HashMap::from([
("package_a".into(), Version::new(1, 0, 0)),
("package_b".into(), Version::new(2, 0, 0)),
]);
let packages = vec![
(
"package_a".into(),
Version::new(1, 0, 0),
vec!["package_b".into()],
),
("package_b".into(), Version::new(2, 0, 0), vec![]),
];
let manifest = create_testable_unlock_manifest(packages, Vec::new());
let packages_to_unlock: Vec = vec![];
let mut locked = initial_locked.clone();
unlock_packages(&mut locked, &packages_to_unlock, Some(&manifest)).unwrap();
assert_eq!(
initial_locked, locked,
"Locked packages should remain unchanged when no packages are specified to unlock"
);
}
#[test]
fn test_unlock_package_preserve_shared_deps() {
let mut locked = HashMap::from([
("package_a".into(), Version::new(1, 0, 0)),
("package_b".into(), Version::new(2, 0, 0)),
("package_c".into(), Version::new(3, 0, 0)),
]);
let packages = vec![
(
"package_a".into(),
Version::new(1, 0, 0),
vec!["package_c".into()],
),
(
"package_b".into(),
Version::new(2, 0, 0),
vec!["package_c".into()],
),
("package_c".into(), Version::new(3, 0, 0), vec![]),
];
let manifest = create_testable_unlock_manifest(packages, Vec::new());
let packages_to_unlock: Vec = vec!["package_a".into()];
unlock_packages(&mut locked, &packages_to_unlock, Some(&manifest)).unwrap();
assert!(!locked.contains_key("package_a"));
assert!(locked.contains_key("package_b"));
assert!(locked.contains_key("package_c"));
}
#[test]
fn test_unlock_package_with_root_dep() {
let mut locked = HashMap::from([
("package_a".into(), Version::new(1, 0, 0)),
("package_b".into(), Version::new(2, 0, 0)),
("package_c".into(), Version::new(3, 0, 0)),
]);
let packages = vec![
(
"package_a".into(),
Version::new(1, 0, 0),
vec!["package_b".into()],
),
(
"package_b".into(),
Version::new(2, 0, 0),
vec!["package_c".into()],
),
("package_c".into(), Version::new(3, 0, 0), vec![]),
];
let requirements = vec![("package_b".into(), ">= 2.0.0".into())];
let manifest = create_testable_unlock_manifest(packages, requirements);
let packages_to_unlock: Vec = vec!["package_a".into()];
unlock_packages(&mut locked, &packages_to_unlock, Some(&manifest)).unwrap();
assert!(!locked.contains_key("package_a"));
assert!(locked.contains_key("package_b"));
assert!(locked.contains_key("package_c"));
}
#[test]
fn test_unlock_root_dep_package() {
let mut locked = HashMap::from([
("package_a".into(), Version::new(1, 0, 0)),
("package_b".into(), Version::new(2, 0, 0)),
("package_c".into(), Version::new(3, 0, 0)),
]);
let packages = vec![
(
"package_a".into(),
Version::new(1, 0, 0),
vec!["package_b".into()],
),
("package_b".into(), Version::new(2, 0, 0), vec![]),
("package_c".into(), Version::new(3, 0, 0), vec![]),
];
let requirements = vec![("package_a".into(), ">= 1.0.0".into())];
let manifest = create_testable_unlock_manifest(packages, requirements);
let packages_to_unlock: Vec = vec!["package_a".into()];
unlock_packages(&mut locked, &packages_to_unlock, Some(&manifest)).unwrap();
assert!(!locked.contains_key("package_a"));
assert!(!locked.contains_key("package_b"));
assert!(locked.contains_key("package_c"));
}
#[test]
fn test_unlock_package_with_and_without_root_dep() {
let mut locked = HashMap::from([
("package_a".into(), Version::new(1, 0, 0)),
("package_b".into(), Version::new(2, 0, 0)),
("package_c".into(), Version::new(3, 0, 0)),
]);
let packages = vec![
(
"package_a".into(),
Version::new(1, 0, 0),
vec!["package_b".into(), "package_c".into()],
),
("package_b".into(), Version::new(2, 0, 0), vec![]),
("package_c".into(), Version::new(3, 0, 0), vec![]),
];
let requirements = vec![("package_b".into(), ">= 2.0.0".into())];
let manifest = create_testable_unlock_manifest(packages, requirements);
let packages_to_unlock: Vec = vec!["package_a".into()];
unlock_packages(&mut locked, &packages_to_unlock, Some(&manifest)).unwrap();
assert!(!locked.contains_key("package_a"));
assert!(locked.contains_key("package_b"));
assert!(!locked.contains_key("package_c"));
}
fn manifest_package(name: &str, version: &str, requirements: Vec) -> ManifestPackage {
ManifestPackage {
name: name.into(),
version: Version::parse(version).unwrap(),
build_tools: ["gleam".into()].into(),
otp_app: None,
requirements,
source: ManifestPackageSource::Hex {
outer_checksum: Base16Checksum(vec![1, 2, 3, 4]),
},
}
}
fn package_config(
dependencies: HashMap,
dev_dependencies: HashMap,
) -> PackageConfig {
PackageConfig {
name: "the_package".into(),
version: Version::parse("1.0.0").unwrap(),
gleam_version: None,
licences: vec![],
description: "".into(),
documentation: Docs { pages: vec![] },
dependencies,
dev_dependencies,
repository: None,
links: vec![],
erlang: ErlangConfig {
application_start_module: None,
application_start_argument: None,
extra_applications: vec![],
},
javascript: JavaScriptConfig {
typescript_declarations: false,
runtime: Runtime::NodeJs,
deno: DenoConfig {
allow_env: DenoFlag::AllowAll,
allow_sys: true,
allow_hrtime: true,
allow_net: DenoFlag::AllowAll,
allow_ffi: true,
allow_read: DenoFlag::AllowAll,
allow_run: DenoFlag::AllowAll,
allow_write: DenoFlag::AllowAll,
allow_all: true,
unstable: true,
location: None,
},
},
target: Target::Erlang,
internal_modules: None,
}
}
#[test]
fn test_remove_do_nothing() {
let config = package_config(
HashMap::from([("a".into(), Requirement::hex("~>1.0").unwrap())]),
HashMap::from([("b".into(), Requirement::hex("~>2.0").unwrap())]),
);
let mut manifest = Manifest {
requirements: HashMap::from([
("a".into(), Requirement::hex("~>1.0").unwrap()),
("b".into(), Requirement::hex("~>2.0").unwrap()),
]),
packages: vec![
manifest_package("a", "1.0.0", vec![]),
manifest_package("b", "2.0.8", vec![]),
],
};
let manifest_copy = manifest.clone();
remove_extra_requirements(&config, &mut manifest).unwrap();
assert_eq!(manifest.requirements, manifest_copy.requirements);
assert_eq!(manifest.packages, manifest_copy.packages);
}
#[test]
fn test_remove_simple() {
let config = package_config(HashMap::new(), HashMap::new());
let mut manifest = Manifest {
requirements: HashMap::from([("a".into(), Requirement::hex("~>1.0").unwrap())]),
packages: vec![manifest_package("a", "1.0.0", vec![])],
};
remove_extra_requirements(&config, &mut manifest).unwrap();
assert_eq!(manifest.requirements, config.dependencies);
assert_eq!(manifest.packages, vec![]);
}
#[test]
fn test_remove_package_with_transitive_dependencies() {
let config = package_config(HashMap::new(), HashMap::new());
let mut manifest = Manifest {
requirements: HashMap::from([("a".into(), Requirement::hex("~>1.0").unwrap())]),
packages: vec![
manifest_package("a", "1.0.0", vec!["b".into()]),
manifest_package("b", "1.2.3", vec!["c".into()]),
manifest_package("c", "2.0.0", vec![]),
],
};
remove_extra_requirements(&config, &mut manifest).unwrap();
assert_eq!(manifest.requirements, config.dependencies);
assert_eq!(manifest.packages, vec![]);
}
#[test]
fn test_remove_package_with_shared_transitive_dependencies() {
let config = package_config(
HashMap::from([("a".into(), Requirement::hex("~>1.0").unwrap())]),
HashMap::new(),
);
let mut manifest = Manifest {
requirements: HashMap::from([
("a".into(), Requirement::hex("~>1.0").unwrap()),
("b".into(), Requirement::hex("~>1.0").unwrap()),
]),
packages: vec![
manifest_package("a", "1.0.0", vec!["c".into()]),
manifest_package("b", "1.2.3", vec!["c".into(), "d".into()]),
manifest_package("c", "2.0.0", vec![]),
manifest_package("d", "0.1.0", vec![]),
],
};
remove_extra_requirements(&config, &mut manifest).unwrap();
assert_eq!(manifest.requirements, config.dependencies);
assert_eq!(
manifest.packages,
vec![
manifest_package("a", "1.0.0", vec!["c".into()]),
manifest_package("c", "2.0.0", vec![]),
]
);
}
#[test]
fn test_remove_package_that_is_also_a_transitive_dependency() {
let config = package_config(
HashMap::from([("a".into(), Requirement::hex("~>1.0").unwrap())]),
HashMap::new(),
);
let mut manifest = Manifest {
requirements: HashMap::from([
("a".into(), Requirement::hex("~>1.0").unwrap()),
("b".into(), Requirement::hex("~>1.0").unwrap()),
]),
packages: vec![
manifest_package("a", "1.0.0", vec!["b".into(), "c".into()]),
manifest_package("b", "1.2.3", vec!["c".into(), "d".into()]),
manifest_package("c", "2.0.0", vec![]),
manifest_package("d", "0.1.0", vec![]),
],
};
let manifest_copy = manifest.clone();
remove_extra_requirements(&config, &mut manifest).unwrap();
assert_eq!(manifest.requirements, config.dependencies);
assert_eq!(manifest.packages, manifest_copy.packages);
}
#[test]
fn test_pretty_print_major_versions_available() {
let versions = vec![
(
"very_long_package_name".to_string(),
(Version::new(18, 382, 43), Version::new(19, 0, 38)),
),
(
"gleam_stdlib".to_string(),
(Version::new(0, 45, 0), Version::new(1, 0, 0)),
),
(
"short_name".to_string(),
(Version::new(1, 0, 0), Version::new(2, 0, 0)),
),
]
.into_iter()
.collect();
let output = pretty_print_major_versions_available(versions);
insta::assert_snapshot!(output);
}
#[test]
fn test_pretty_print_version_updates() {
let versions = vec![
(
"gleam_stdlib".to_string(),
(Version::new(0, 45, 0), Version::new(0, 46, 0)),
),
(
"wisp".to_string(),
(Version::new(2, 1, 0), Version::new(2, 1, 1)),
),
(
"very_long_package_name".to_string(),
(Version::new(12, 12, 12), Version::new(120, 12, 12)),
),
]
.into_iter()
.collect();
let output = pretty_print_version_updates(versions);
insta::assert_snapshot!(output);
}
#[test]
fn test_ensure_packages_exist_locally_all_present() {
let manifest = Manifest {
requirements: HashMap::new(),
packages: vec![
manifest_package("package_a", "1.0.0", vec![]),
manifest_package("package_b", "2.0.0", vec![]),
manifest_package("package_c", "3.0.0", vec![]),
],
};
let packages_to_check = vec!["package_a".into(), "package_b".into()];
let result = dependency_manager::ensure_packages_exist_locally(&manifest, &packages_to_check);
assert!(result.is_ok(), "All packages exist, should return Ok");
}
#[test]
fn test_ensure_packages_exist_locally_some_missing() {
let manifest = Manifest {
requirements: HashMap::new(),
packages: vec![
manifest_package("package_a", "1.0.0", vec![]),
manifest_package("package_b", "2.0.0", vec![]),
],
};
let packages_to_check = vec![
"package_a".into(),
"package_b".into(),
"missing_package".into(),
"another_missing".into(),
];
let result = dependency_manager::ensure_packages_exist_locally(&manifest, &packages_to_check);
match result {
Err(Error::PackagesToUpdateNotExist { packages }) => {
assert_eq!(packages.len(), 2);
assert!(packages.contains(&"missing_package".into()));
assert!(packages.contains(&"another_missing".into()));
}
_ => panic!("Expected PackagesToUpdateNotExist error"),
}
}
================================================
FILE: compiler-cli/src/dependencies.rs
================================================
mod dependency_manager;
use std::{
cell::RefCell,
collections::{HashMap, HashSet},
io::ErrorKind,
process::Command,
rc::Rc,
time::Instant,
};
use camino::{Utf8Path, Utf8PathBuf};
use ecow::{EcoString, eco_format};
use flate2::read::GzDecoder;
use gleam_core::{
Error, Result,
build::{Mode, SourceFingerprint, Target, Telemetry},
config::PackageConfig,
dependency::{self, PackageFetchError},
error::{FileIoAction, FileKind, ShellCommandFailureReason, StandardIoAction},
hex::{self, HEXPM_PUBLIC_KEY},
io::{HttpClient as _, TarUnpacker, WrappedReader},
manifest::{Base16Checksum, Manifest, ManifestPackage, ManifestPackageSource, PackageChanges},
paths::ProjectPaths,
requirement::Requirement,
};
use hexpm::version::Version;
use itertools::Itertools;
use same_file::is_same_file;
use strum::IntoEnumIterator;
pub use dependency_manager::DependencyManagerConfig;
#[cfg(test)]
mod tests;
use crate::{
TreeOptions,
build_lock::{BuildLock, Guard},
cli,
fs::{self, ProjectIO},
http::HttpClient,
text_layout::space_table,
};
struct Symbols {
down: &'static str,
tee: &'static str,
ell: &'static str,
right: &'static str,
}
static UTF8_SYMBOLS: Symbols = Symbols {
down: "│",
tee: "├",
ell: "└",
right: "─",
};
/// When set to `Yes`, the cli will check for major version updates of direct dependencies and
/// print them to the console if the major versions are not upgradeable due to constraints.
#[derive(Debug, Clone, Copy)]
pub enum CheckMajorVersions {
Yes,
No,
}
pub fn list(paths: &ProjectPaths) -> Result<()> {
let (_, manifest) = get_manifest_details(paths)?;
list_manifest_packages(std::io::stdout(), manifest)
}
pub fn tree(paths: &ProjectPaths, options: TreeOptions) -> Result<()> {
let (config, manifest) = get_manifest_details(paths)?;
// Initialize the root package since it is not part of the manifest
let root_package = ManifestPackage {
build_tools: vec![],
name: config.name.clone(),
requirements: config.all_direct_dependencies()?.keys().cloned().collect(),
version: config.version.clone(),
source: ManifestPackageSource::Local {
path: paths.root().to_path_buf(),
},
otp_app: None,
};
// Get the manifest packages and add the root package to the vec
let mut packages = manifest.packages.iter().cloned().collect_vec();
packages.append(&mut vec![root_package.clone()]);
list_package_and_dependencies_tree(std::io::stdout(), options, packages.clone(), config.name)
}
fn get_manifest_details(paths: &ProjectPaths) -> Result<(PackageConfig, Manifest)> {
let runtime = tokio::runtime::Runtime::new().expect("Unable to start Tokio async runtime");
let config = crate::config::root_config(paths)?;
let package_fetcher = PackageFetcher::new(runtime.handle().clone());
let dependency_manager = DependencyManagerConfig {
use_manifest: UseManifest::Yes,
check_major_versions: CheckMajorVersions::No,
}
.into_dependency_manager(
runtime.handle().clone(),
package_fetcher,
cli::Reporter::new(),
Mode::Dev,
);
let manifest = dependency_manager
.resolve_versions(paths, &config, Vec::new())?
.manifest;
Ok((config, manifest))
}
fn list_manifest_packages(mut buffer: W, manifest: Manifest) -> Result<()> {
let packages = manifest
.packages
.into_iter()
.map(|package| vec![package.name.to_string(), package.version.to_string()])
.collect_vec();
let out = space_table(&["Package", "Version"], packages);
write!(buffer, "{out}").map_err(|e| Error::StandardIo {
action: StandardIoAction::Write,
err: Some(e.kind()),
})
}
fn list_package_and_dependencies_tree(
mut buffer: W,
options: TreeOptions,
packages: Vec,
root_package_name: EcoString,
) -> Result<()> {
let mut invert = false;
let package: Option<&ManifestPackage> = if let Some(input_package_name) = options.package {
packages.iter().find(|p| p.name == input_package_name)
} else if let Some(input_package_name) = options.invert {
invert = true;
packages.iter().find(|p| p.name == input_package_name)
} else {
packages.iter().find(|p| p.name == root_package_name)
};
if let Some(package) = package {
let tree = Vec::from([eco_format!("{0} v{1}", package.name, package.version)]);
let tree = list_dependencies_tree(
tree.clone(),
package.clone(),
packages,
EcoString::new(),
invert,
);
tree.iter()
.try_for_each(|line| writeln!(buffer, "{line}"))
.map_err(|e| Error::StandardIo {
action: StandardIoAction::Write,
err: Some(e.kind()),
})
} else {
writeln!(buffer, "Package not found. Please check the package name.").map_err(|e| {
Error::StandardIo {
action: StandardIoAction::Write,
err: Some(e.kind()),
}
})
}
}
fn list_dependencies_tree(
mut tree: Vec,
package: ManifestPackage,
packages: Vec,
accum: EcoString,
invert: bool,
) -> Vec {
let dependencies = packages
.iter()
.filter(|p| {
(invert && p.requirements.contains(&package.name))
|| (!invert && package.requirements.contains(&p.name))
})
.cloned()
.collect_vec();
let dependencies = dependencies.iter().sorted().enumerate();
let deps_length = dependencies.len();
for (index, dependency) in dependencies {
let is_last = index == deps_length - 1;
let prefix = if is_last {
UTF8_SYMBOLS.ell
} else {
UTF8_SYMBOLS.tee
};
tree.push(eco_format!(
"{0}{1}{2}{2} {3} v{4}",
accum.clone(),
prefix,
UTF8_SYMBOLS.right,
dependency.name,
dependency.version
));
let accum = accum.clone() + (if !is_last { UTF8_SYMBOLS.down } else { " " }) + " ";
tree = list_dependencies_tree(
tree.clone(),
dependency.clone(),
packages.clone(),
accum.clone(),
invert,
);
}
tree
}
pub fn outdated(paths: &ProjectPaths) -> Result<()> {
let (_, manifest) = get_manifest_details(paths)?;
let runtime = tokio::runtime::Runtime::new().expect("Unable to start Tokio async runtime");
let package_fetcher = PackageFetcher::new(runtime.handle().clone());
let version_updates = dependency::check_for_version_updates(&manifest, &package_fetcher);
if !version_updates.is_empty() {
print!("{}", pretty_print_version_updates(version_updates));
}
Ok(())
}
#[derive(Debug, Clone, Copy)]
pub enum UseManifest {
Yes,
No,
}
pub fn update(paths: &ProjectPaths, packages: Vec) -> Result<()> {
let use_manifest = if packages.is_empty() {
UseManifest::No
} else {
UseManifest::Yes
};
// Update specific packages
_ = resolve_and_download(
paths,
cli::Reporter::new(),
None,
packages.into_iter().map(EcoString::from).collect(),
DependencyManagerConfig {
use_manifest,
check_major_versions: CheckMajorVersions::Yes,
},
)?;
Ok(())
}
/// Edit the manifest.toml file in this proejct, removing all extra requirements and packages
/// that are no longer present in the gleam.toml config.
pub fn cleanup(paths: &ProjectPaths, telemetry: Telem) -> Result {
let span = tracing::info_span!("remove_deps");
let _enter = span.enter();
// We do this before acquiring the build lock so that we don't create the
// build directory if there is no gleam.toml
crate::config::ensure_config_exists(paths)?;
let lock = BuildLock::new_packages(paths)?;
let _guard: Guard = lock.lock(&telemetry)?;
// Read the project config
let config = crate::root_config(paths)?;
let old_manifest = read_manifest_from_disc(paths)?;
let mut manifest = old_manifest.clone();
remove_extra_requirements(&config, &mut manifest)?;
// Remove any packages that are no longer required due to manifest changes
let local = LocalPackages::read_from_disc(paths)?;
remove_extra_packages(paths, &local, &manifest, &telemetry)?;
// Record new state of the packages directory
tracing::debug!("writing_manifest_toml");
write_manifest_to_disc(paths, &manifest)?;
LocalPackages::from_manifest(&manifest).write_to_disc(paths)?;
let changes = PackageChanges::between_manifests(&old_manifest, &manifest);
telemetry.resolved_package_versions(&changes);
Ok(manifest)
}
/// Remove requirements and unneeded packages from manifest that are no longer present in config.
fn remove_extra_requirements(config: &PackageConfig, manifest: &mut Manifest) -> Result<()> {
// "extra requirements" are all packages that are requirements in the manifest, but no longer
// part of the gleam.toml config.
let is_extra_requirement = |name: &EcoString| {
!config.dev_dependencies.contains_key(name) && !config.dependencies.contains_key(name)
};
// If a requirement is also used as a dependency, we do not want to force-unlock it.
// If the dependents get deleted as well, this transitive dependency will be dropped.
let is_unlockable_requirement = |name: &EcoString| {
manifest
.packages
.iter()
.all(|p| !p.requirements.contains(name))
};
let extra_requirements = manifest
.requirements
.keys()
.filter(|&name| is_extra_requirement(name) && is_unlockable_requirement(name))
.cloned()
.collect::>();
manifest
.requirements
.retain(|name, _| !is_extra_requirement(name));
// Unlock all packages that we we want to remove - this removes them and all unneeded
// dependencies from `locked`.
let mut locked = config.locked(Some(manifest))?;
unlock_packages(&mut locked, extra_requirements.as_slice(), Some(manifest))?;
// Remove all unlocked packages from the manifest - these are truly no longer needed.
manifest
.packages
.retain(|package| locked.contains_key(&package.name));
Ok(())
}
pub fn parse_gleam_add_specifier(package: &str) -> Result<(EcoString, Requirement)> {
let Some((package, version)) = package.split_once('@') else {
// Default to the latest version available.
return Ok((
package.into(),
Requirement::hex(">= 0.0.0").expect("'>= 0.0.0' should be a valid pubgrub range"),
));
};
// Parse the major and minor from the provided semantic version.
let parts = version.split('.').collect::>();
let major = match parts.first() {
Some(major) => Ok(major),
None => Err(Error::InvalidVersionFormat {
input: package.to_string(),
error: "Failed to parse semantic major version".to_string(),
}),
}?;
let minor = match parts.get(1) {
Some(minor) => minor,
None => "0",
};
// Using the major version specifier, calculate the maximum
// allowable version (i.e., the next major version).
let Ok(num) = major.parse::() else {
return Err(Error::InvalidVersionFormat {
input: version.to_string(),
error: "Failed to parse semantic major version as integer".to_string(),
});
};
let max_ver = [&(num + 1).to_string(), "0", "0"].join(".");
// Pad the provided version specifier with zeros map to a Hex version.
let requirement = match parts.len() {
1 | 2 => {
let min_ver = [major, minor, "0"].join(".");
Requirement::hex(&[">=", &min_ver, "and", "<", &max_ver].join(" "))
}
3 => Requirement::hex(version),
n => {
return Err(Error::InvalidVersionFormat {
input: version.to_string(),
error: format!(
"Expected up to 3 numbers in version specifier (MAJOR.MINOR.PATCH), found {n}"
),
});
}
}?;
Ok((package.into(), requirement))
}
pub fn resolve_and_download(
paths: &ProjectPaths,
telemetry: Telem,
new_package: Option<(Vec<(EcoString, Requirement)>, bool)>,
packages_to_update: Vec,
config: DependencyManagerConfig,
) -> Result {
// Start event loop so we can run async functions to call the Hex API
let runtime = tokio::runtime::Runtime::new().expect("Unable to start Tokio async runtime");
let package_fetcher = PackageFetcher::new(runtime.handle().clone());
let dependency_manager = config.into_dependency_manager(
runtime.handle().clone(),
package_fetcher,
telemetry,
Mode::Dev,
);
dependency_manager.resolve_and_download_versions(paths, new_package, packages_to_update)
}
fn format_versions_and_extract_longest_parts(
versions: dependency::PackageVersionDiffs,
) -> Vec> {
versions
.iter()
.map(|(name, (v1, v2))| vec![name.to_string(), v1.to_string(), v2.to_string()])
.sorted()
.collect_vec()
}
fn pretty_print_major_versions_available(versions: dependency::PackageVersionDiffs) -> String {
let versions = format_versions_and_extract_longest_parts(versions);
format!(
"\nThe following dependencies have new major versions available:\n\n{}",
space_table(&["Package", "Current", "Latest"], &versions)
)
}
fn pretty_print_version_updates(versions: dependency::PackageVersionDiffs) -> EcoString {
let versions = format_versions_and_extract_longest_parts(versions);
space_table(&["Package", "Current", "Latest"], &versions)
}
async fn add_missing_packages(
paths: &ProjectPaths,
fs: Box,
manifest: &Manifest,
local: &LocalPackages,
project_name: EcoString,
telemetry: &Telem,
) -> Result<(), Error> {
let missing_packages = local.missing_local_packages(manifest, &project_name);
let mut num_to_download = 0;
let missing_git_packages = missing_packages
.iter()
.copied()
.filter(|package| package.is_git())
.inspect(|_| {
num_to_download += 1;
})
.collect_vec();
let mut missing_hex_packages = missing_packages
.iter()
.copied()
.filter(|package| package.is_hex())
.inspect(|_| {
num_to_download += 1;
})
.peekable();
// If we need to download at-least one package
if missing_hex_packages.peek().is_some() || !missing_git_packages.is_empty() {
let http = HttpClient::boxed();
let downloader = hex::Downloader::new(fs.clone(), fs, http, Untar::boxed(), paths.clone());
let start = Instant::now();
telemetry.downloading_package("packages");
downloader
.download_hex_packages(missing_hex_packages, &project_name)
.await?;
for package in missing_git_packages {
let ManifestPackageSource::Git { repo, commit } = &package.source else {
continue;
};
let _ = download_git_package(&package.name, repo, commit, paths)?;
}
telemetry.packages_downloaded(start, num_to_download);
}
Ok(())
}
fn remove_extra_packages(
paths: &ProjectPaths,
local: &LocalPackages,
manifest: &Manifest,
telemetry: &Telem,
) -> Result<()> {
let _guard = BuildLock::lock_all_build(paths, telemetry)?;
for (package_name, version) in local.extra_local_packages(manifest) {
// TODO: test
// Delete the package source
let path = paths.build_packages_package(&package_name);
if path.exists() {
tracing::debug!(package=%package_name, version=%version, "removing_unneeded_package");
fs::delete_directory(&path)?;
}
// TODO: test
// Delete any build artefacts for the package
for mode in Mode::iter() {
for target in Target::iter() {
let name = manifest
.packages
.iter()
.find(|p| p.name == package_name)
.map(|p| p.application_name().as_str())
.unwrap_or(package_name.as_str());
let path = paths.build_directory_for_package(mode, target, name);
if path.exists() {
tracing::debug!(package=%package_name, version=%version, "deleting_build_cache");
fs::delete_directory(&path)?;
}
}
}
}
Ok(())
}
fn read_manifest_from_disc(paths: &ProjectPaths) -> Result {
tracing::debug!("reading_manifest_toml");
let manifest_path = paths.manifest();
let toml = fs::read(&manifest_path)?;
let manifest = toml::from_str(&toml).map_err(|e| Error::FileIo {
action: FileIoAction::Parse,
kind: FileKind::File,
path: manifest_path.clone(),
err: Some(e.to_string()),
})?;
Ok(manifest)
}
fn write_manifest_to_disc(paths: &ProjectPaths, manifest: &Manifest) -> Result<()> {
let path = paths.manifest();
fs::write(&path, &manifest.to_toml(paths.root()))
}
// This is the container for locally pinned packages, representing the current contents of
// the `project/build/packages` directory.
// For descriptions of packages provided by paths and git deps, see the ProvidedPackage struct.
// The same package may appear in both at different times.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct LocalPackages {
packages: HashMap,
}
impl LocalPackages {
pub fn extra_local_packages(&self, manifest: &Manifest) -> Vec<(String, Version)> {
let manifest_packages: HashSet<_> = manifest
.packages
.iter()
.map(|p| (&p.name, &p.version))
.collect();
self.packages
.iter()
.filter(|(n, v)| !manifest_packages.contains(&(&EcoString::from(*n), v)))
.map(|(n, v)| (n.clone(), v.clone()))
.collect()
}
pub fn missing_local_packages<'a>(
&self,
manifest: &'a Manifest,
root: &str,
) -> Vec<&'a ManifestPackage> {
manifest
.packages
.iter()
// We don't need to download the root package
.filter(|p| p.name != root)
// We don't need to download local packages because we use the linked source directly
.filter(|p| !p.is_local())
// We don't need to download packages which we have the correct version of
.filter(|p| self.packages.get(p.name.as_str()) != Some(&p.version))
.collect()
}
pub fn read_from_disc(paths: &ProjectPaths) -> Result {
let path = paths.build_packages_toml();
if !path.exists() {
return Ok(Self {
packages: HashMap::new(),
});
}
let toml = fs::read(&path)?;
toml::from_str(&toml).map_err(|e| Error::FileIo {
action: FileIoAction::Parse,
kind: FileKind::File,
path: path.clone(),
err: Some(e.to_string()),
})
}
pub fn write_to_disc(&self, paths: &ProjectPaths) -> Result<()> {
let path = paths.build_packages_toml();
let toml = toml::to_string(&self).expect("packages.toml serialization");
fs::write(&path, &toml)
}
pub fn from_manifest(manifest: &Manifest) -> Self {
Self {
packages: manifest
.packages
.iter()
.map(|p| (p.name.to_string(), p.version.clone()))
.collect(),
}
}
}
fn is_same_requirements(
requirements1: &HashMap,
requirements2: &HashMap,
root_path: &Utf8Path,
) -> Result {
if requirements1.len() != requirements2.len() {
return Ok(false);
}
for (key, requirement1) in requirements1 {
if !same_requirements(requirement1, requirements2.get(key), root_path)? {
return Ok(false);
}
}
Ok(true)
}
/// Returns true if all path dependency configs are unchanged since last build.
///
/// If any of the path dependency configs have changed that means that we need to
/// re-perform dependency resolution, as their dependencies could have changed
/// themselves.
///
/// We use gleam.toml rather than manifest.toml as:
///
/// 1. The dependency requirements could have changed but resolution not have been
/// run in that package yet, so the manifest would be the same, resulting in us
/// failing to detect that resolution is required.
///
/// 2. Dependency manifests are not used in any way, so a change in the manifest
/// may not have any impact on this package.
///
/// This does mean that changes unrelated to the path dependency's dependencies
/// will trigger resolution, but gleam.toml is edited rarely, and no-change
/// resolution is fast enough, so that's OK.
///
/// Note: This does not check path dependencies of path dependencies! Changes to
/// their configs will fail to be picked up. To resolve this we would need to keep
/// a list of all the path dependencies in the project, instead of only the direct
/// path dependencies.
///
fn path_dependency_configs_unchanged(
requirements: &HashMap,
paths: &ProjectPaths,
) -> Result {
for (name, requirement) in requirements {
let Requirement::Path { path } = requirement else {
continue;
};
let config_path = paths.path_dependency_gleam_toml_path(path);
let fingerprint_path = paths.dependency_gleam_toml_fingerprint_path(name.as_str());
// Check mtimes before hashing, to avoid extra work
if fingerprint_path.exists() {
let config_time = fs::modification_time(&config_path)?;
let fingerprint_time = fs::modification_time(&fingerprint_path)?;
if config_time <= fingerprint_time {
continue;
}
};
let config_text = fs::read(&config_path)?;
let current_fingerprint = SourceFingerprint::new(&config_text).to_numerical_string();
// If cached hash file doesn't exist, this is the first time we're checking this dependency
if !fingerprint_path.exists() {
// Save the current hash for future comparisons
fs::write(&fingerprint_path, ¤t_fingerprint)?;
return Ok(false);
}
let previous_fingerprint = fs::read(&fingerprint_path)?;
if previous_fingerprint != current_fingerprint {
tracing::debug!("path_dependency_config_changed_forcing_rebuild");
fs::write(&fingerprint_path, ¤t_fingerprint)?;
return Ok(false);
}
}
Ok(true)
}
fn same_requirements(
requirement1: &Requirement,
requirement2: Option<&Requirement>,
root_path: &Utf8Path,
) -> Result {
let (left, right) = match (requirement1, requirement2) {
(Requirement::Path { path: path1 }, Some(Requirement::Path { path: path2 })) => {
let left = fs::canonicalise(&root_path.join(path1))?;
let right = fs::canonicalise(&root_path.join(path2))?;
(left, right)
}
(_, Some(requirement2)) => return Ok(requirement1 == requirement2),
(_, None) => return Ok(false),
};
Ok(left == right)
}
#[derive(Clone, Eq, PartialEq, Debug)]
struct ProvidedPackage {
version: Version,
source: ProvidedPackageSource,
requirements: HashMap,
}
#[derive(Clone, Eq, Debug)]
enum ProvidedPackageSource {
Git { repo: EcoString, commit: EcoString },
Local { path: Utf8PathBuf },
}
impl ProvidedPackage {
fn to_hex_package(&self, name: &EcoString) -> hexpm::Package {
let requirements = self
.requirements
.iter()
.map(|(name, version)| {
(
name.as_str().into(),
hexpm::Dependency {
requirement: version.clone(),
optional: false,
app: None,
repository: None,
},
)
})
.collect();
let release = hexpm::Release {
version: self.version.clone(),
requirements,
retirement_status: None,
outer_checksum: vec![],
meta: (),
};
hexpm::Package {
name: name.as_str().into(),
repository: "local".into(),
releases: vec![release],
}
}
fn to_manifest_package(&self, name: &str) -> ManifestPackage {
let mut package = ManifestPackage {
name: name.into(),
version: self.version.clone(),
otp_app: None, // Note, this will probably need to be set to something eventually
build_tools: vec!["gleam".into()],
requirements: self.requirements.keys().cloned().collect(),
source: self.source.to_manifest_package_source(),
};
package.requirements.sort();
package
}
}
impl ProvidedPackageSource {
fn to_manifest_package_source(&self) -> ManifestPackageSource {
match self {
Self::Git { repo, commit } => ManifestPackageSource::Git {
repo: repo.clone(),
commit: commit.clone(),
},
Self::Local { path } => ManifestPackageSource::Local { path: path.clone() },
}
}
fn to_toml(&self) -> String {
match self {
Self::Git { repo, commit } => {
format!(r#"{{ repo: "{repo}", commit: "{commit}" }}"#)
}
Self::Local { path } => {
format!(r#"{{ path: "{path}" }}"#)
}
}
}
}
impl PartialEq for ProvidedPackageSource {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Local { path: own_path }, Self::Local { path: other_path }) => {
is_same_file(own_path, other_path).unwrap_or(false)
}
(
Self::Git {
repo: own_repo,
commit: own_commit,
},
Self::Git {
repo: other_repo,
commit: other_commit,
},
) => own_repo == other_repo && own_commit == other_commit,
(Self::Git { .. }, Self::Local { .. }) | (Self::Local { .. }, Self::Git { .. }) => {
false
}
}
}
}
/// Provide a package from a local project
fn provide_local_package(
package_name: EcoString,
package_path: &Utf8Path,
parent_path: &Utf8Path,
project_paths: &ProjectPaths,
provided: &mut HashMap,
parents: &mut Vec,
) -> Result {
let package_path = if package_path.is_absolute() {
package_path.to_path_buf()
} else {
fs::canonicalise(&parent_path.join(package_path))?
};
let package_source = ProvidedPackageSource::Local {
path: package_path.clone(),
};
provide_package(
package_name,
package_path,
package_source,
project_paths,
provided,
parents,
)
}
fn execute_command(command: &mut Command) -> Result {
let result = command.output();
match result {
Ok(output) if output.status.success() => Ok(output),
Ok(output) => {
let reason = match String::from_utf8(output.stderr) {
Ok(stderr) => ShellCommandFailureReason::ShellCommandError(stderr),
Err(_) => ShellCommandFailureReason::Unknown,
};
Err(Error::ShellCommand {
program: "git".into(),
reason,
})
}
Err(error) => Err(match error.kind() {
ErrorKind::NotFound => Error::ShellProgramNotFound {
program: "git".into(),
os: fs::get_os(),
},
other => Error::ShellCommand {
program: "git".into(),
reason: ShellCommandFailureReason::IoError(other),
},
}),
}
}
/// Downloads a git package from a remote repository. The commands that are run
/// looks like this:
///
/// ```sh
/// git init
/// git remote remove origin
/// git remote add origin
/// git fetch origin
/// git checkout [
/// git rev-parse HEAD
/// ```
///
/// This is somewhat inefficient as we have to fetch the entire git history before
/// switching to the exact commit we want. There a few alternatives to this:
///
/// - `git clone --depth 1 --branch="]["` This works, but only allows us to use
/// branch names as refs, however we want to allow commit hashes as well.
/// - `git fetch --depth 1 origin ][` Similarly, this imposes an unwanted
/// restriction. `git fetch` only allows branch names or full commit hashes,
/// but we want to allow partial hashes as well.
///
/// Since Git dependencies will be used quite rarely, this option was settled upon
/// because it allows branch names, full and partial commit hashes as refs.
///
/// In the future we can optimise this more, for example first checking if we
/// are already checked out to the commit stored in the manifest, or by only
/// fetching the history without the objects to resolve partial commit hashes.
/// For now though this is good enough until it become an actual performance
/// problem.
///
fn download_git_package(
package_name: &str,
repo: &str,
ref_: &str,
project_paths: &ProjectPaths,
) -> Result {
let package_path = project_paths.build_packages_package(package_name);
// If the package path exists but is not inside a git work tree, we need to
// remove the directory because running `git init` in a non-empty directory
// followed by `git checkout ...` is an error. See
// https://github.com/gleam-lang/gleam/issues/4488 for details.
if !fs::is_git_work_tree_root(&package_path) {
fs::delete_directory(&package_path)?;
}
fs::mkdir(&package_path)?;
let _ = execute_command(Command::new("git").arg("init").current_dir(&package_path))?;
// If this directory already exists, but the remote URL has been edited in
// `gleam.toml` without a `gleam clean`, `git remote add` will fail, causing
// the remote to be stuck as the original value. Here we remove the remote
// first, which ensures that `git remote add` properly add the remote each
// time. If this fails, that means we haven't set the remote in the first
// place, so we can safely ignore the error.
let _ = Command::new("git")
.arg("remote")
.arg("remove")
.arg("origin")
.current_dir(&package_path)
.output();
let _ = execute_command(
Command::new("git")
.arg("remote")
.arg("add")
.arg("origin")
.arg(repo)
.current_dir(&package_path),
)?;
let _ = execute_command(
Command::new("git")
.arg("fetch")
.arg("origin")
.current_dir(&package_path),
)?;
let _ = execute_command(
Command::new("git")
.arg("checkout")
.arg(ref_)
.current_dir(&package_path),
)?;
let output = execute_command(
Command::new("git")
.arg("rev-parse")
.arg("HEAD")
.current_dir(&package_path),
)?;
let commit = String::from_utf8(output.stdout)
.expect("Output should be UTF-8")
.trim()
.into();
Ok(commit)
}
/// Provide a package from a git repository
fn provide_git_package(
package_name: EcoString,
repo: &str,
// A git ref, such as a branch name, commit hash or tag name
ref_: &str,
project_paths: &ProjectPaths,
provided: &mut HashMap,
parents: &mut Vec,
) -> Result {
let commit = download_git_package(&package_name, repo, ref_, project_paths)?;
let package_source = ProvidedPackageSource::Git {
repo: repo.into(),
commit,
};
let package_path = fs::canonicalise(&project_paths.build_packages_package(&package_name))?;
provide_package(
package_name,
package_path,
package_source,
project_paths,
provided,
parents,
)
}
/// Adds a gleam project located at a specific path to the list of "provided packages"
fn provide_package(
package_name: EcoString,
package_path: Utf8PathBuf,
package_source: ProvidedPackageSource,
project_paths: &ProjectPaths,
provided: &mut HashMap,
parents: &mut Vec,
) -> Result {
// Return early if a package cycle is detected
if parents.contains(&package_name) {
let mut last_cycle = parents
.split(|p| p == &package_name)
.next_back()
.unwrap_or_default()
.to_vec();
last_cycle.push(package_name);
return Err(Error::PackageCycle {
packages: last_cycle,
});
}
// Check that we do not have a cached version of this package already
match provided.get(&package_name) {
Some(package) if package.source == package_source => {
// This package has already been provided from this source, return the version
let version = hexpm::version::Range::new(format!("== {}", &package.version))
.expect("== {version} should be a valid range");
return Ok(version);
}
Some(package) => {
// This package has already been provided from a different source which conflicts
return Err(Error::ProvidedDependencyConflict {
package: package_name.into(),
source_1: package_source.to_toml(),
source_2: package.source.to_toml(),
});
}
None => (),
}
// Load the package
let config = crate::config::read(package_path.join("gleam.toml"))?;
// Check that we are loading the correct project
if config.name != package_name {
return Err(Error::WrongDependencyProvided {
expected: package_name.into(),
path: package_path.to_path_buf(),
found: config.name.into(),
});
};
// Walk the requirements of the package
let mut requirements = HashMap::new();
parents.push(package_name);
for (name, requirement) in config.dependencies.into_iter() {
let version = match requirement {
Requirement::Hex { version } => version,
Requirement::Path { path } => {
// Recursively walk local packages
provide_local_package(
name.clone(),
&path,
&package_path,
project_paths,
provided,
parents,
)?
}
Requirement::Git { git, ref_ } => {
provide_git_package(name.clone(), &git, &ref_, project_paths, provided, parents)?
}
};
let _ = requirements.insert(name, version);
}
let _ = parents.pop();
// Add the package to the provided packages dictionary
let version = hexpm::version::Range::new(format!("== {}", &config.version))
.expect("== {version} should be a valid range");
let _ = provided.insert(
config.name,
ProvidedPackage {
version: config.version,
source: package_source,
requirements,
},
);
// Return the version
Ok(version)
}
/// Unlocks specified packages and their unique dependencies.
///
/// If a manifest is provided, it also unlocks indirect dependencies that are
/// not required by any other package or the root project.
pub fn unlock_packages(
locked: &mut HashMap,
packages_to_unlock: &[EcoString],
manifest: Option<&Manifest>,
) -> Result<()> {
if let Some(manifest) = manifest {
let mut packages_to_unlock: Vec = packages_to_unlock.to_vec();
while let Some(package_name) = packages_to_unlock.pop() {
if locked.remove(&package_name).is_some()
&& let Some(package) = manifest.packages.iter().find(|p| p.name == package_name)
{
let deps_to_unlock = find_deps_to_unlock(package, locked, manifest);
packages_to_unlock.extend(deps_to_unlock);
}
}
} else {
for package_name in packages_to_unlock {
let _ = locked.remove(package_name);
}
}
Ok(())
}
/// Identifies which dependencies of a package should be unlocked.
///
/// A dependency is eligible for unlocking if it is currently locked,
/// is not a root dependency, and is not required by any locked package.
fn find_deps_to_unlock(
package: &ManifestPackage,
locked: &HashMap,
manifest: &Manifest,
) -> Vec {
package
.requirements
.iter()
.filter(|&dep| {
locked.contains_key(dep)
&& !manifest.requirements.contains_key(dep)
&& manifest
.packages
.iter()
.all(|p| !locked.contains_key(&p.name) || !p.requirements.contains(dep))
})
.cloned()
.collect()
}
/// Determine the information to add to the manifest for a specific package
async fn lookup_package(
name: String,
version: Version,
provided: &HashMap,
) -> Result {
match provided.get(name.as_str()) {
Some(provided_package) => Ok(provided_package.to_manifest_package(name.as_str())),
None => {
let config = hexpm::Config::new();
let release =
hex::get_package_release(&name, &version, &config, &HttpClient::new()).await?;
let build_tools = release
.meta
.build_tools
.iter()
.map(|s| EcoString::from(s.as_str()))
.collect_vec();
let requirements = release
.requirements
.keys()
.map(|s| EcoString::from(s.as_str()))
.collect_vec();
Ok(ManifestPackage {
name: name.into(),
version,
otp_app: Some(release.meta.app.into()),
build_tools,
requirements,
source: ManifestPackageSource::Hex {
outer_checksum: Base16Checksum(release.outer_checksum),
},
})
}
}
}
struct PackageFetcher {
runtime_cache: RefCell>>,
runtime: tokio::runtime::Handle,
http: HttpClient,
}
impl PackageFetcher {
pub fn new(runtime: tokio::runtime::Handle) -> Self {
Self {
runtime_cache: RefCell::new(HashMap::new()),
runtime,
http: HttpClient::new(),
}
}
/// Caches the result of `get_dependencies` so that we don't need to make a network request.
/// Currently dependencies are fetched during initial version resolution, and then during check
/// for major version availability.
fn cache_package(&self, package: &str, result: Rc) {
let mut runtime_cache = self.runtime_cache.borrow_mut();
let _ = runtime_cache.insert(package.to_string(), result);
}
}
#[derive(Debug)]
pub struct Untar;
impl Untar {
pub fn boxed() -> Box {
Box::new(Self)
}
}
impl TarUnpacker for Untar {
fn io_result_entries<'a>(
&self,
archive: &'a mut tar::Archive,
) -> std::io::Result> {
archive.entries()
}
fn io_result_unpack(
&self,
path: &Utf8Path,
mut archive: tar::Archive>>,
) -> std::io::Result<()> {
archive.unpack(path)
}
}
impl dependency::PackageFetcher for PackageFetcher {
fn get_dependencies(&self, package: &str) -> Result, PackageFetchError> {
{
let runtime_cache = self.runtime_cache.borrow();
let result = runtime_cache.get(package);
if let Some(result) = result {
return Ok(result.clone());
}
}
tracing::debug!(package = package, "looking_up_hex_package");
let config = hexpm::Config::new();
let request = hexpm::repository_v2_get_package_request(package, None, &config);
let response = self
.runtime
.block_on(self.http.send(request))
.map_err(PackageFetchError::fetch_error)?;
let pkg = hexpm::repository_v2_get_package_response(response, HEXPM_PUBLIC_KEY)
.map_err(|e| PackageFetchError::from_api_error(e, package))?;
let pkg = Rc::new(pkg);
let pkg_ref = Rc::clone(&pkg);
self.cache_package(package, pkg);
Ok(pkg_ref)
}
}
================================================
FILE: compiler-cli/src/docs.rs
================================================
use std::{collections::HashMap, time::SystemTime};
use camino::{Utf8Path, Utf8PathBuf};
use ecow::EcoString;
use crate::{cli, fs::ProjectIO, http::HttpClient};
use gleam_core::{
Result,
analyse::TargetSupport,
build::{Codegen, Compile, Mode, Options, Package, Target},
config::{DocsPage, PackageConfig},
docs::{Dependency, DependencyKind, DocContext},
error::Error,
hex,
io::HttpClient as _,
manifest::ManifestPackageSource,
paths::ProjectPaths,
type_,
};
pub fn remove(package: String, version: String) -> Result<()> {
let runtime = tokio::runtime::Runtime::new().expect("Unable to start Tokio async runtime");
let http = HttpClient::new();
let hex_config = hexpm::Config::new();
let credentials = crate::hex::HexAuthentication::new(&runtime, &http, hex_config.clone())
.get_or_create_api_credentials()?;
// Remove docs from API
let request = hexpm::api_remove_docs_request(
&package,
&version,
&crate::hex::write_credentials(&credentials)?,
&hex_config,
)
.map_err(Error::hex)?;
let response = runtime.block_on(http.send(request))?;
hexpm::api_remove_docs_response(response).map_err(Error::hex)?;
// Done!
println!("The docs for {package} {version} have been removed from HexDocs");
Ok(())
}
#[derive(Debug)]
pub struct BuildOptions {
/// Whether to open the docs after building.
pub open: bool,
pub target: Option,
}
pub fn build(paths: &ProjectPaths, options: BuildOptions) -> Result<()> {
let config = crate::config::root_config(paths)?;
// Reset the build directory so we know the state of the project
crate::fs::delete_directory(&paths.build_directory_for_target(Mode::Prod, config.target))?;
let out = paths.build_documentation_directory(&config.name);
let manifest = crate::build::download_dependencies(paths, cli::Reporter::new())?;
let dependencies = manifest
.packages
.iter()
.map(|package| {
(
package.name.clone(),
Dependency {
version: package.version.clone(),
kind: match &package.source {
ManifestPackageSource::Hex { .. } => DependencyKind::Hex,
ManifestPackageSource::Git { .. } => DependencyKind::Git,
ManifestPackageSource::Local { .. } => DependencyKind::Path,
},
},
)
})
.collect();
let mut built = crate::build::main(
paths,
Options {
mode: Mode::Prod,
target: options.target,
codegen: Codegen::All,
compile: Compile::All,
warnings_as_errors: false,
root_target_support: TargetSupport::Enforced,
no_print_progress: false,
},
manifest,
)?;
let outputs = build_documentation(
paths,
&config,
dependencies,
&mut built.root_package,
DocContext::Build,
&built.module_interfaces,
)?;
// Write
crate::fs::delete_directory(&out)?;
crate::fs::write_outputs_under(&outputs, &out)?;
let index_html = out.join("index.html");
println!(
"\nThe documentation for {package} has been rendered to \n{index_html}",
package = config.name,
index_html = index_html
);
if options.open {
open_docs(&index_html)?;
}
// We're done!
Ok(())
}
/// Opens the indicated path in the default program configured by the system.
///
/// For the docs this will generally be a browser (unless some other program is
/// configured as the default for `.html` files).
fn open_docs(path: &Utf8Path) -> Result<()> {
opener::open(path).map_err(|error| Error::FailedToOpenDocs {
path: path.to_path_buf(),
error: error.to_string(),
})?;
Ok(())
}
pub(crate) fn build_documentation(
paths: &ProjectPaths,
config: &PackageConfig,
dependencies: HashMap,
compiled: &mut Package,
is_hex_publish: DocContext,
cached_modules: &im::HashMap,
) -> Result, Error> {
compiled.attach_doc_and_module_comments();
cli::print_generating_documentation();
let mut pages = vec![DocsPage {
title: "README".into(),
path: "index.html".into(),
source: paths.readme(), // TODO: support non markdown READMEs. Or a default if there is none.
}];
pages.extend(config.documentation.pages.iter().cloned());
let mut outputs = gleam_core::docs::generate_html(
paths,
gleam_core::docs::DocumentationConfig {
package_config: config,
dependencies,
analysed: compiled.modules.as_slice(),
docs_pages: &pages,
rendering_timestamp: SystemTime::now(),
context: is_hex_publish,
},
ProjectIO::new(),
);
outputs.push(gleam_core::docs::generate_json_package_interface(
Utf8PathBuf::from("package-interface.json"),
compiled,
cached_modules,
));
Ok(outputs)
}
pub fn publish(paths: &ProjectPaths) -> Result<()> {
let config = crate::config::root_config(paths)?;
let http = HttpClient::new();
let runtime = tokio::runtime::Runtime::new().expect("Unable to start Tokio async runtime");
let hex_config = hexpm::Config::new();
let credentials = crate::hex::HexAuthentication::new(&runtime, &http, hex_config.clone())
.get_or_create_api_credentials()?;
// Reset the build directory so we know the state of the project
crate::fs::delete_directory(&paths.build_directory_for_target(Mode::Prod, config.target))?;
let manifest = crate::build::download_dependencies(paths, cli::Reporter::new())?;
let dependencies = manifest
.packages
.iter()
.map(|package| {
(
package.name.clone(),
Dependency {
version: package.version.clone(),
kind: match &package.source {
ManifestPackageSource::Hex { .. } => DependencyKind::Hex,
ManifestPackageSource::Git { .. } => DependencyKind::Git,
ManifestPackageSource::Local { .. } => DependencyKind::Path,
},
},
)
})
.collect();
let mut built = crate::build::main(
paths,
Options {
root_target_support: TargetSupport::Enforced,
warnings_as_errors: false,
codegen: Codegen::All,
compile: Compile::All,
mode: Mode::Prod,
target: None,
no_print_progress: false,
},
manifest,
)?;
let outputs = build_documentation(
paths,
&config,
dependencies,
&mut built.root_package,
DocContext::HexPublish,
&built.module_interfaces,
)?;
let archive = crate::fs::create_tar_archive(outputs)?;
cli::print_publishing_documentation();
runtime.block_on(hex::publish_documentation(
&config.name,
&config.version,
archive,
&crate::hex::write_credentials(&credentials)?,
&hex_config,
&http,
))?;
cli::print_published("documentation");
Ok(())
}
================================================
FILE: compiler-cli/src/export.rs
================================================
use camino::Utf8PathBuf;
use gleam_core::{
Result,
analyse::TargetSupport,
build::{Codegen, Compile, Mode, Options, Target},
paths::ProjectPaths,
};
static ENTRYPOINT_FILENAME_POWERSHELL: &str = "entrypoint.ps1";
static ENTRYPOINT_FILENAME_POSIX_SHELL: &str = "entrypoint.sh";
static ENTRYPOINT_TEMPLATE_POWERSHELL: &str =
include_str!("../templates/erlang-shipment-entrypoint.ps1");
static ENTRYPOINT_TEMPLATE_POSIX_SHELL: &str =
include_str!("../templates/erlang-shipment-entrypoint.sh");
// TODO: start in embedded mode
// TODO: test
/// Generate a directory of precompiled Erlang along with a start script.
/// Suitable for deployment to a server.
///
/// For each Erlang application (aka package) directory these directories are
/// copied across:
/// - ebin
/// - include
/// - priv
pub(crate) fn erlang_shipment(paths: &ProjectPaths) -> Result<()> {
let target = Target::Erlang;
let mode = Mode::Prod;
let build = paths.build_directory_for_target(mode, target);
let out = paths.erlang_shipment_directory();
crate::fs::mkdir(&out)?;
// Reset the directories to ensure we have a clean slate and no old code
crate::fs::delete_directory(&build)?;
crate::fs::delete_directory(&out)?;
// Build project in production mode
let built = crate::build::main(
paths,
Options {
root_target_support: TargetSupport::Enforced,
warnings_as_errors: false,
codegen: Codegen::All,
compile: Compile::All,
mode,
target: Some(target),
no_print_progress: false,
},
crate::build::download_dependencies(paths, crate::cli::Reporter::new())?,
)?;
for entry in crate::fs::read_dir(&build)?.filter_map(Result::ok) {
let path = entry.path();
// We are only interested in package directories
if !path.is_dir() {
continue;
}
let name = path.file_name().expect("Directory name");
let build = build.join(name);
let out = out.join(name);
crate::fs::mkdir(&out)?;
// Copy desired package subdirectories
for subdirectory in ["ebin", "priv", "include"] {
let source = build.join(subdirectory);
if source.is_dir() {
let source = crate::fs::canonicalise(&source)?;
let out = out.join(subdirectory);
crate::fs::copy_dir(source, &out)?;
}
}
}
// PowerShell entry point script.
write_entrypoint_script(
&out.join(ENTRYPOINT_FILENAME_POWERSHELL),
ENTRYPOINT_TEMPLATE_POWERSHELL,
&built.root_package.config.name,
)?;
// POSIX Shell entry point script.
write_entrypoint_script(
&out.join(ENTRYPOINT_FILENAME_POSIX_SHELL),
ENTRYPOINT_TEMPLATE_POSIX_SHELL,
&built.root_package.config.name,
)?;
crate::cli::print_exported(&built.root_package.config.name);
println!(
"
Your Erlang shipment has been generated to {out}.
It can be copied to a compatible server with Erlang installed and run with
one of the following scripts:
- {ENTRYPOINT_FILENAME_POWERSHELL} (PowerShell script)
- {ENTRYPOINT_FILENAME_POSIX_SHELL} (POSIX Shell script)
",
);
Ok(())
}
fn write_entrypoint_script(
entrypoint_output_path: &Utf8PathBuf,
entrypoint_template_path: &str,
package_name: &str,
) -> Result<()> {
let text = entrypoint_template_path.replace("$PACKAGE_NAME_FROM_GLEAM", package_name);
crate::fs::write(entrypoint_output_path, &text)?;
crate::fs::make_executable(entrypoint_output_path)?;
Ok(())
}
pub fn hex_tarball(paths: &ProjectPaths) -> Result<()> {
let mut config = crate::config::root_config(paths)?;
let data: Vec = crate::publish::build_hex_tarball(paths, &mut config)?;
let path = paths.build_export_hex_tarball(&config.name, &config.version.to_string());
crate::fs::write_bytes(&path, &data)?;
println!(
"
Your hex tarball has been generated in {}.
",
&path
);
Ok(())
}
pub fn javascript_prelude() -> Result<()> {
print!("{}", gleam_core::javascript::PRELUDE);
Ok(())
}
pub fn typescript_prelude() -> Result<()> {
print!("{}", gleam_core::javascript::PRELUDE_TS_DEF);
Ok(())
}
pub fn package_interface(paths: &ProjectPaths, out: Utf8PathBuf) -> Result<()> {
// Build the project
let mut built = crate::build::main(
paths,
Options {
mode: Mode::Prod,
target: None,
codegen: Codegen::None,
compile: Compile::All,
warnings_as_errors: false,
root_target_support: TargetSupport::Enforced,
no_print_progress: false,
},
crate::build::download_dependencies(paths, crate::cli::Reporter::new())?,
)?;
built.root_package.attach_doc_and_module_comments();
let out = gleam_core::docs::generate_json_package_interface(
out,
&built.root_package,
&built.module_interfaces,
);
crate::fs::write_outputs_under(&[out], paths.root())?;
Ok(())
}
pub fn package_information(paths: &ProjectPaths, out: Utf8PathBuf) -> Result<()> {
let config = crate::config::root_config(paths)?;
let out = gleam_core::docs::generate_json_package_information(out, config);
crate::fs::write_outputs_under(&[out], paths.root())?;
Ok(())
}
================================================
FILE: compiler-cli/src/fix.rs
================================================
use std::rc::Rc;
use gleam_core::{
Error, Result, Warning,
analyse::TargetSupport,
build::{Codegen, Compile, Mode, Options},
error::{FileIoAction, FileKind},
paths::ProjectPaths,
type_,
warning::VectorWarningEmitterIO,
};
use hexpm::version::Version;
use crate::{build, cli};
pub fn run(paths: &ProjectPaths) -> Result<()> {
// When running gleam fix we want all the compilation warnings to be hidden,
// at the same time we need to access those to apply the fixes: so we
// accumulate those into a vector.
let warnings = Rc::new(VectorWarningEmitterIO::new());
let _built = build::main_with_warnings(
paths,
Options {
root_target_support: TargetSupport::Enforced,
warnings_as_errors: false,
codegen: Codegen::DepsOnly,
compile: Compile::All,
mode: Mode::Dev,
target: None,
no_print_progress: false,
},
build::download_dependencies(paths, cli::Reporter::new())?,
warnings.clone(),
)?;
let warnings = warnings.take();
fix_minimum_required_version(paths, warnings)?;
println!("Done!");
Ok(())
}
fn fix_minimum_required_version(paths: &ProjectPaths, warnings: Vec) -> Result<()> {
let Some(minimum_required_version) = minimum_required_version_from_warnings(warnings) else {
return Ok(());
};
// Set the version requirement in gleam.toml
let root_config = paths.root_config();
let mut toml = crate::fs::read(&root_config)?
.parse::()
.map_err(|e| Error::FileIo {
kind: FileKind::File,
action: FileIoAction::Parse,
path: root_config.to_path_buf(),
err: Some(e.to_string()),
})?;
#[allow(clippy::indexing_slicing)]
{
toml["gleam"] = toml_edit::value(format!(">= {minimum_required_version}"));
}
// Write the updated config
crate::fs::write(root_config.as_path(), &toml.to_string())?;
println!("- Set required Gleam version to \">= {minimum_required_version}\"");
Ok(())
}
/// Returns the highest minimum required version among all warnings requiring a
/// specific Gleam version that is not allowed by the `gleam` version contraint
/// in the `gleam.toml`.
fn minimum_required_version_from_warnings(warnings: Vec) -> Option {
warnings
.iter()
.filter_map(|warning| match warning {
Warning::Type {
warning:
type_::Warning::FeatureRequiresHigherGleamVersion {
minimum_required_version,
..
},
..
} => Some(minimum_required_version),
_ => None,
})
.reduce(std::cmp::max)
.cloned()
}
================================================
FILE: compiler-cli/src/format.rs
================================================
use gleam_core::{
error::{Error, FileIoAction, FileKind, Result, StandardIoAction, Unformatted},
io::Content,
io::OutputFile,
};
use std::{io::Read, str::FromStr};
use camino::{Utf8Path, Utf8PathBuf};
pub fn run(stdin: bool, check: bool, files: Vec) -> Result<()> {
if stdin {
process_stdin(check)
} else {
process_files(check, files)
}
}
fn process_stdin(check: bool) -> Result<()> {
let src = read_stdin()?.into();
let mut out = String::new();
gleam_core::format::pretty(&mut out, &src, Utf8Path::new(""))?;
if !check {
print!("{out}");
return Ok(());
}
if src != out {
return Err(Error::Format {
problem_files: vec![Unformatted {
source: Utf8PathBuf::from(""),
destination: Utf8PathBuf::from(""),
input: src,
output: out,
}],
});
}
Ok(())
}
fn process_files(check: bool, files: Vec) -> Result<()> {
if check {
check_files(files)
} else {
format_files(files)
}
}
fn check_files(files: Vec) -> Result<()> {
let problem_files = unformatted_files(files)?;
if problem_files.is_empty() {
Ok(())
} else {
Err(Error::Format { problem_files })
}
}
fn format_files(files: Vec) -> Result<()> {
for file in unformatted_files(files)? {
crate::fs::write_output(&OutputFile {
path: file.destination,
content: Content::Text(file.output),
})?;
}
Ok(())
}
pub fn unformatted_files(files: Vec) -> Result> {
let mut problem_files = Vec::with_capacity(files.len());
for file_path in files {
let path = Utf8PathBuf::from_str(&file_path).map_err(|e| Error::FileIo {
action: FileIoAction::Open,
kind: FileKind::File,
path: Utf8PathBuf::from(file_path),
err: Some(e.to_string()),
})?;
if path.is_dir() {
for path in crate::fs::gleam_files(&path) {
format_file(&mut problem_files, path)?;
}
} else {
format_file(&mut problem_files, path)?;
}
}
Ok(problem_files)
}
fn format_file(problem_files: &mut Vec, path: Utf8PathBuf) -> Result<()> {
let src = crate::fs::read(&path)?.into();
let mut output = String::new();
gleam_core::format::pretty(&mut output, &src, &path)?;
if src != output {
problem_files.push(Unformatted {
source: path.clone(),
destination: path,
input: src,
output,
});
}
Ok(())
}
pub fn read_stdin() -> Result {
let mut src = String::new();
let _ = std::io::stdin()
.read_to_string(&mut src)
.map_err(|e| Error::StandardIo {
action: StandardIoAction::Read,
err: Some(e.kind()),
})?;
Ok(src)
}
================================================
FILE: compiler-cli/src/fs/tests.rs
================================================
use camino::Utf8Path;
use itertools::Itertools;
#[test]
fn is_inside_git_work_tree_ok() {
let tmp_dir = tempfile::tempdir().unwrap();
let path = Utf8Path::from_path(tmp_dir.path()).expect("Non Utf-8 Path");
assert!(!super::is_inside_git_work_tree(path).unwrap());
assert_eq!(super::git_init(path), Ok(()));
assert!(super::is_inside_git_work_tree(path).unwrap())
}
#[test]
fn git_init_success() {
let tmp_dir = tempfile::tempdir().unwrap();
let path = Utf8Path::from_path(tmp_dir.path()).expect("Non Utf-8 Path");
let git = path.join(".git");
assert!(!git.exists());
assert_eq!(super::git_init(path), Ok(()));
assert!(git.exists());
}
#[test]
fn git_init_already_in_git() {
let tmp_dir = tempfile::tempdir().unwrap();
let git = Utf8Path::from_path(tmp_dir.path())
.expect("Non Utf-8 Path")
.join(".git");
assert!(!git.exists());
assert_eq!(
super::git_init(Utf8Path::from_path(tmp_dir.path()).expect("Non Utf-8 Path")),
Ok(())
);
assert!(git.exists());
let sub = Utf8Path::from_path(tmp_dir.path())
.expect("Non Utf-8 Path")
.join("subproject");
let git = sub.join(".git");
crate::fs::mkdir(&sub).unwrap();
assert!(!git.exists());
assert_eq!(super::git_init(&sub), Ok(()));
assert!(!git.exists());
}
#[test]
fn exclude_build_dir() {
/*
a
|-- gleam.toml
|-- build
| |-- f.gleam # do not count as gleam file
b
|-- build
| |-- f.gleam # count as gleam file
*/
let tmp_dir = tempfile::tempdir().unwrap();
let path = Utf8Path::from_path(tmp_dir.path()).expect("Non Utf-8 Path");
// excluded gleam file
{
let gleam_toml = path.join("a/gleam.toml").to_path_buf();
super::write(&gleam_toml, "").unwrap();
let gleam_file = path.join("a/build/f.gleam").to_path_buf();
super::write(&gleam_file, "").unwrap();
};
// included gleam file
let gleam_file = path.join("b/build/f.gleam").to_path_buf();
super::write(&gleam_file, "").unwrap();
let files = super::gleam_files(path).collect::>();
assert_eq!(files, vec![gleam_file]);
}
#[test]
fn erlang_files_include_gitignored_files() {
let tmp_dir = tempfile::tempdir().unwrap();
let path = Utf8Path::from_path(tmp_dir.path()).expect("Non Utf-8 Path");
let included_files = &[
".hidden.erl",
"abc.erl",
"abc.hrl",
"build/include/abc.erl",
"build/include/abc.hrl",
"ignored.erl",
"ignored.hrl",
];
let excluded_files = &[
".gitignore",
"abc.gleam",
"abc.js",
"build/abc.gleam",
"build/abc.js",
];
let gitignore = "build/
ignored.*";
for &file in included_files.iter().chain(excluded_files) {
let contents = match file {
".gitignore" => gitignore,
_ => "",
};
super::write(&path.join(file), contents).unwrap();
}
let mut chosen_files = super::erlang_files(path).collect_vec();
chosen_files.sort_unstable();
let expected_files = included_files.iter().map(|s| path.join(s)).collect_vec();
assert_eq!(expected_files, chosen_files);
}
#[test]
fn is_gleam_path_test() {
assert!(super::is_gleam_path(
Utf8Path::new("/some-prefix/a.gleam"),
Utf8Path::new("/some-prefix/")
));
assert!(super::is_gleam_path(
Utf8Path::new("/some-prefix/one_two/a.gleam"),
Utf8Path::new("/some-prefix/")
));
assert!(super::is_gleam_path(
Utf8Path::new("/some-prefix/one_two/a123.gleam"),
Utf8Path::new("/some-prefix/")
));
assert!(super::is_gleam_path(
Utf8Path::new("/some-prefix/one_2/a123.gleam"),
Utf8Path::new("/some-prefix/")
));
}
#[test]
fn extract_distro_id_test() {
let os_release = "
PRETTY_NAME=\"Debian GNU/Linux 12 (bookworm)\"
NAME=\"Debian GNU/Linux\"
VERSION_ID=\"12\"
VERSION=\"12 (bookworm)\"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL=\"https://www.debian.org/\"
";
assert_eq!(super::extract_distro_id(os_release.to_string()), "debian");
let os_release = "
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL=\"https://www.ubuntu.com/\"
";
assert_eq!(super::extract_distro_id(os_release.to_string()), "ubuntu");
assert_eq!(super::extract_distro_id("".to_string()), "");
assert_eq!(super::extract_distro_id("\n".to_string()), "");
assert_eq!(super::extract_distro_id("ID=".to_string()), "");
assert_eq!(super::extract_distro_id("ID= ".to_string()), " ");
assert_eq!(
super::extract_distro_id("ID= space test ".to_string()),
" space test "
);
assert_eq!(super::extract_distro_id("id=ubuntu".to_string()), "");
assert_eq!(
super::extract_distro_id("NAME=\"Debian\"\nID=debian".to_string()),
"debian"
);
assert_eq!(
super::extract_distro_id("\n\nNAME=\n\n\nID=test123\n".to_string()),
"test123"
);
assert_eq!(
super::extract_distro_id("\nID=\"id first\"\nID=another_id".to_string()),
"id first"
);
}
================================================
FILE: compiler-cli/src/fs.rs
================================================
use gleam_core::{
Result, Warning,
build::{NullTelemetry, Target},
error::{Error, FileIoAction, FileKind, OS, ShellCommandFailureReason, parse_os},
io::{
BeamCompiler, Command, CommandExecutor, Content, DirEntry, FileSystemReader,
FileSystemWriter, OutputFile, ReadDir, Stdio, WrappedReader, is_native_file_extension,
},
manifest::Manifest,
paths::ProjectPaths,
warning::WarningEmitterIO,
};
use gleam_language_server::{DownloadDependencies, Locker, MakeLocker};
use std::{
collections::HashSet,
fmt::Debug,
fs::{File, exists},
io::{self, BufRead, BufReader, Write},
sync::{Arc, Mutex, OnceLock},
time::SystemTime,
};
use camino::{ReadDirUtf8, Utf8Path, Utf8PathBuf};
use crate::{dependencies, lsp::LspLocker};
#[cfg(test)]
mod tests;
/// Return the current directory as a UTF-8 Path
pub fn get_current_directory() -> Result {
let curr_dir = std::env::current_dir().map_err(|e| Error::FileIo {
kind: FileKind::Directory,
action: FileIoAction::Open,
path: ".".into(),
err: Some(e.to_string()),
})?;
Utf8PathBuf::from_path_buf(curr_dir.clone()).map_err(|_| Error::NonUtf8Path { path: curr_dir })
}
// Return the first directory with a gleam.toml as a UTF-8 Path
pub fn get_project_root(path: Utf8PathBuf) -> Result {
fn walk(dir: Utf8PathBuf) -> Option {
match dir.join("gleam.toml").is_file() {
true => Some(dir),
false => match dir.parent() {
Some(p) => walk(p.into()),
None => None,
},
}
}
walk(path.clone()).ok_or(Error::UnableToFindProjectRoot {
path: path.to_string(),
})
}
pub fn get_os() -> OS {
parse_os(std::env::consts::OS, get_distro_str().as_str())
}
// try to extract the distro id from /etc/os-release
pub fn extract_distro_id(os_release: String) -> String {
let distro = os_release.lines().find(|line| line.starts_with("ID="));
if let Some(distro) = distro {
let id = distro.split('=').nth(1).unwrap_or("").replace("\"", "");
return id;
}
"".to_string()
}
pub fn get_distro_str() -> String {
let path = Utf8Path::new("/etc/os-release");
if std::env::consts::OS != "linux" || !path.exists() {
return "other".to_string();
}
let os_release = read(path);
match os_release {
Ok(os_release) => extract_distro_id(os_release),
Err(_) => "other".to_string(),
}
}
/// A `FileWriter` implementation that writes to the file system.
#[derive(Debug, Clone, Default)]
pub struct ProjectIO {
beam_compiler: Arc>,
}
impl ProjectIO {
pub fn new() -> Self {
Self {
beam_compiler: Default::default(),
}
}
pub fn boxed() -> Box {
Box::new(Self::new())
}
}
impl FileSystemReader for ProjectIO {
fn read(&self, path: &Utf8Path) -> Result {
read(path)
}
fn read_bytes(&self, path: &Utf8Path) -> Result, Error> {
read_bytes(path)
}
fn is_file(&self, path: &Utf8Path) -> bool {
path.is_file()
}
fn is_directory(&self, path: &Utf8Path) -> bool {
path.is_dir()
}
fn reader(&self, path: &Utf8Path) -> Result {
reader(path)
}
fn read_dir(&self, path: &Utf8Path) -> Result {
read_dir(path).map(|entries| {
entries
.map(|result| result.map(|entry| DirEntry::from_path(entry.path())))
.collect()
})
}
fn modification_time(&self, path: &Utf8Path) -> Result {
modification_time(path)
}
fn canonicalise(&self, path: &Utf8Path) -> Result {
canonicalise(path)
}
}
pub fn modification_time(path: &Utf8Path) -> std::result::Result {
path.metadata()
.map(|m| m.modified().unwrap_or_else(|_| SystemTime::now()))
.map_err(|e| Error::FileIo {
action: FileIoAction::ReadMetadata,
kind: FileKind::File,
path: path.to_path_buf(),
err: Some(e.to_string()),
})
}
impl FileSystemWriter for ProjectIO {
fn delete_directory(&self, path: &Utf8Path) -> Result<()> {
delete_directory(path)
}
fn copy(&self, from: &Utf8Path, to: &Utf8Path) -> Result<()> {
copy(from, to)
}
fn copy_dir(&self, from: &Utf8Path, to: &Utf8Path) -> Result<()> {
copy_dir(from, to)
}
fn mkdir(&self, path: &Utf8Path) -> Result<(), Error> {
mkdir(path)
}
fn hardlink(&self, from: &Utf8Path, to: &Utf8Path) -> Result<(), Error> {
hardlink(from, to)
}
fn symlink_dir(&self, from: &Utf8Path, to: &Utf8Path) -> Result<(), Error> {
symlink_dir(from, to)
}
fn delete_file(&self, path: &Utf8Path) -> Result<()> {
delete_file(path)
}
fn write(&self, path: &Utf8Path, content: &str) -> Result<(), Error> {
write(path, content)
}
fn write_bytes(&self, path: &Utf8Path, content: &[u8]) -> Result<(), Error> {
write_bytes(path, content)
}
fn exists(&self, path: &Utf8Path) -> bool {
path.exists()
}
}
impl CommandExecutor for ProjectIO {
fn exec(&self, command: Command) -> Result {
let Command {
program,
args,
env,
cwd,
stdio,
} = command;
tracing::debug!(program=program, args=?args.join(" "), env=?env, cwd=?cwd, "command_exec");
let result = std::process::Command::new(&program)
.args(args)
.stdin(stdio.get_process_stdio())
.stdout(stdio.get_process_stdio())
.envs(env.iter().map(|pair| (&pair.0, &pair.1)))
.current_dir(cwd.unwrap_or_else(|| Utf8Path::new("./").to_path_buf()))
.status();
match result {
Ok(status) => Ok(status.code().unwrap_or_default()),
Err(error) => Err(match error.kind() {
io::ErrorKind::NotFound => Error::ShellProgramNotFound {
program,
os: get_os(),
},
other => Error::ShellCommand {
program,
reason: ShellCommandFailureReason::IoError(other),
},
}),
}
}
}
impl BeamCompiler for ProjectIO {
fn compile_beam(
&self,
out: &Utf8Path,
lib: &Utf8Path,
modules: &HashSet,
stdio: Stdio,
) -> Result, Error> {
self.beam_compiler
.lock()
.as_mut()
.expect("could not get beam_compiler")
.compile(self, out, lib, modules, stdio)
}
}
impl MakeLocker for ProjectIO {
fn make_locker(&self, paths: &ProjectPaths, target: Target) -> Result> {
let locker = LspLocker::new(paths, target)?;
Ok(Box::new(locker))
}
}
impl DownloadDependencies for ProjectIO {
fn download_dependencies(&self, paths: &ProjectPaths) -> Result {
dependencies::resolve_and_download(
paths,
NullTelemetry,
None,
Vec::new(),
dependencies::DependencyManagerConfig {
use_manifest: dependencies::UseManifest::Yes,
check_major_versions: dependencies::CheckMajorVersions::No,
},
)
}
}
pub fn delete_directory(dir: &Utf8Path) -> Result<(), Error> {
tracing::debug!(path=?dir, "deleting_directory");
if dir.exists() {
std::fs::remove_dir_all(dir).map_err(|e| Error::FileIo {
action: FileIoAction::Delete,
kind: FileKind::Directory,
path: dir.to_path_buf(),
err: Some(e.to_string()),
})?;
} else {
tracing::debug!(path=?dir, "directory_did_not_exist_for_deletion");
}
Ok(())
}
pub fn delete_file(file: &Utf8Path) -> Result<(), Error> {
tracing::debug!("Deleting file {:?}", file);
if file.exists() {
std::fs::remove_file(file).map_err(|e| Error::FileIo {
action: FileIoAction::Delete,
kind: FileKind::File,
path: file.to_path_buf(),
err: Some(e.to_string()),
})?;
} else {
tracing::debug!("Did not exist for deletion: {:?}", file);
}
Ok(())
}
pub fn write_outputs_under(outputs: &[OutputFile], base: &Utf8Path) -> Result<(), Error> {
for file in outputs {
let path = base.join(&file.path);
match &file.content {
Content::Binary(buffer) => write_bytes(&path, buffer),
Content::Text(buffer) => write(&path, buffer),
}?;
}
Ok(())
}
pub fn write_output(file: &OutputFile) -> Result<(), Error> {
let OutputFile { path, content } = file;
match content {
Content::Binary(buffer) => write_bytes(path, buffer),
Content::Text(buffer) => write(path, buffer),
}
}
pub fn write(path: &Utf8Path, text: &str) -> Result<(), Error> {
write_bytes(path, text.as_bytes())
}
#[cfg(target_family = "unix")]
pub fn make_executable(path: impl AsRef) -> Result<(), Error> {
use std::os::unix::fs::PermissionsExt;
tracing::debug!(path = ?path.as_ref(), "setting_permissions");
std::fs::set_permissions(path.as_ref(), std::fs::Permissions::from_mode(0o755)).map_err(
|e| Error::FileIo {
action: FileIoAction::UpdatePermissions,
kind: FileKind::File,
path: path.as_ref().to_path_buf(),
err: Some(e.to_string()),
},
)?;
Ok(())
}
#[cfg(not(target_family = "unix"))]
pub fn make_executable(_path: impl AsRef) -> Result<(), Error> {
Ok(())
}
pub fn write_bytes(path: &Utf8Path, bytes: &[u8]) -> Result<(), Error> {
tracing::debug!(path=?path, "writing_file");
let dir_path = path.parent().ok_or_else(|| Error::FileIo {
action: FileIoAction::FindParent,
kind: FileKind::Directory,
path: path.to_path_buf(),
err: None,
})?;
std::fs::create_dir_all(dir_path).map_err(|e| Error::FileIo {
action: FileIoAction::Create,
kind: FileKind::Directory,
path: dir_path.to_path_buf(),
err: Some(e.to_string()),
})?;
let mut f = File::create(path).map_err(|e| Error::FileIo {
action: FileIoAction::Create,
kind: FileKind::File,
path: path.to_path_buf(),
err: Some(e.to_string()),
})?;
f.write_all(bytes).map_err(|e| Error::FileIo {
action: FileIoAction::WriteTo,
kind: FileKind::File,
path: path.to_path_buf(),
err: Some(e.to_string()),
})?;
Ok(())
}
fn is_gleam_path(path: &Utf8Path, dir: impl AsRef) -> bool {
use regex::Regex;
static RE: OnceLock = OnceLock::new();
RE.get_or_init(|| {
Regex::new(&format!(
"^({module}{slash})*{module}\\.gleam$",
module = "[a-z][_a-z0-9]*",
slash = "(/|\\\\)",
))
.expect("is_gleam_path() RE regex")
})
.is_match(
path.strip_prefix(dir.as_ref())
.expect("is_gleam_path(): strip_prefix")
.as_str(),
)
}
fn is_gleam_build_dir(e: &ignore::DirEntry) -> bool {
if !e.path().is_dir() || !e.path().ends_with("build") {
return false;
}
let Some(parent_path) = e.path().parent() else {
return false;
};
parent_path.join("gleam.toml").exists()
}
/// Walks through all Gleam module files in the directory, even if ignored,
/// except for those in the `build/` directory. Excludes any Gleam files within
/// invalid module paths, for example if they or a folder they're in contain a
/// dot or a hyphen within their names.
pub fn gleam_files(dir: &Utf8Path) -> impl Iterator]- + '_ {
ignore::WalkBuilder::new(dir)
.follow_links(true)
.standard_filters(false)
.filter_entry(|entry| !is_gleam_build_dir(entry))
.build()
.filter_map(Result::ok)
.filter(|entry| {
entry
.file_type()
.map(|type_| type_.is_file())
.unwrap_or(false)
})
.map(ignore::DirEntry::into_path)
.map(|path| Utf8PathBuf::from_path_buf(path).expect("Non Utf-8 Path"))
.filter(move |d| is_gleam_path(d, dir))
}
/// Walks through all native files in the directory, such as `.mjs` and `.erl`,
/// even if ignored.
pub fn native_files(dir: &Utf8Path) -> impl Iterator
- + '_ {
ignore::WalkBuilder::new(dir)
.follow_links(true)
.standard_filters(false)
.filter_entry(|entry| !is_gleam_build_dir(entry))
.build()
.filter_map(Result::ok)
.filter(|entry| {
entry
.file_type()
.map(|type_| type_.is_file())
.unwrap_or(false)
})
.map(ignore::DirEntry::into_path)
.map(|path| Utf8PathBuf::from_path_buf(path).expect("Non Utf-8 Path"))
.filter(|path| {
let extension = path.extension().unwrap_or_default();
is_native_file_extension(extension)
})
}
/// Walks through all files in the directory, even if ignored.
pub fn private_files(dir: &Utf8Path) -> impl Iterator
- + '_ {
ignore::WalkBuilder::new(dir)
.follow_links(true)
.standard_filters(false)
.build()
.filter_map(Result::ok)
.filter(|entry| {
entry
.file_type()
.map(|type_| type_.is_file())
.unwrap_or(false)
})
.map(ignore::DirEntry::into_path)
.map(|path| Utf8PathBuf::from_path_buf(path).expect("Non Utf-8 Path"))
}
/// Walks through all `.erl` and `.hrl` files in the directory, even if ignored.
pub fn erlang_files(dir: &Utf8Path) -> impl Iterator
- + '_ {
ignore::WalkBuilder::new(dir)
.follow_links(true)
.standard_filters(false)
.build()
.filter_map(Result::ok)
.filter(|entry| {
entry
.file_type()
.map(|type_| type_.is_file())
.unwrap_or(false)
})
.map(ignore::DirEntry::into_path)
.map(|path| Utf8PathBuf::from_path_buf(path).expect("Non Utf-8 Path"))
.filter(|path| {
let extension = path.extension().unwrap_or_default();
extension == "erl" || extension == "hrl"
})
}
pub fn create_tar_archive(outputs: Vec) -> Result, Error> {
tracing::debug!("creating_tar_archive");
let encoder = flate2::write::GzEncoder::new(vec![], flate2::Compression::default());
let mut builder = tar::Builder::new(encoder);
for file in outputs {
let mut header = tar::Header::new_gnu();
header.set_path(&file.path).map_err(|e| Error::AddTar {
path: file.path.clone(),
err: e.to_string(),
})?;
header.set_size(file.content.as_bytes().len() as u64);
header.set_cksum();
builder
.append(&header, file.content.as_bytes())
.map_err(|e| Error::AddTar {
path: file.path.clone(),
err: e.to_string(),
})?;
}
builder
.into_inner()
.map_err(|e| Error::TarFinish(e.to_string()))?
.finish()
.map_err(|e| Error::Gzip(e.to_string()))
}
pub fn mkdir(path: impl AsRef + Debug) -> Result<(), Error> {
if path.as_ref().exists() {
return Ok(());
}
tracing::debug!(path=?path, "creating_directory");
std::fs::create_dir_all(path.as_ref()).map_err(|err| Error::FileIo {
kind: FileKind::Directory,
path: Utf8PathBuf::from(path.as_ref()),
action: FileIoAction::Create,
err: Some(err.to_string()),
})
}
pub fn read_dir(path: impl AsRef + Debug) -> Result {
tracing::debug!(path=?path,"reading_directory");
Utf8Path::read_dir_utf8(path.as_ref()).map_err(|e| Error::FileIo {
action: FileIoAction::Read,
kind: FileKind::Directory,
path: Utf8PathBuf::from(path.as_ref()),
err: Some(e.to_string()),
})
}
pub fn module_caches_paths(
path: impl AsRef + Debug,
) -> Result, Error> {
Ok(read_dir(path)?
.filter_map(Result::ok)
.map(|f| f.into_path())
.filter(|p| p.extension() == Some("cache")))
}
pub fn read(path: impl AsRef + Debug) -> Result {
tracing::debug!(path=?path,"reading_file");
std::fs::read_to_string(path.as_ref()).map_err(|err| Error::FileIo {
action: FileIoAction::Read,
kind: FileKind::File,
path: Utf8PathBuf::from(path.as_ref()),
err: Some(err.to_string()),
})
}
pub fn read_bytes(path: impl AsRef + Debug) -> Result, Error> {
tracing::debug!(path=?path,"reading_file");
std::fs::read(path.as_ref()).map_err(|err| Error::FileIo {
action: FileIoAction::Read,
kind: FileKind::File,
path: Utf8PathBuf::from(path.as_ref()),
err: Some(err.to_string()),
})
}
pub fn reader(path: impl AsRef + Debug) -> Result {
tracing::debug!(path=?path,"opening_file_reader");
let reader = File::open(path.as_ref()).map_err(|err| Error::FileIo {
action: FileIoAction::Open,
kind: FileKind::File,
path: Utf8PathBuf::from(path.as_ref()),
err: Some(err.to_string()),
})?;
Ok(WrappedReader::new(path.as_ref(), Box::new(reader)))
}
pub fn buffered_reader + Debug>(path: P) -> Result {
tracing::debug!(path=?path,"opening_file_buffered_reader");
let reader = File::open(path.as_ref()).map_err(|err| Error::FileIo {
action: FileIoAction::Open,
kind: FileKind::File,
path: Utf8PathBuf::from(path.as_ref()),
err: Some(err.to_string()),
})?;
Ok(BufReader::new(reader))
}
pub fn copy(
path: impl AsRef + Debug,
to: impl AsRef + Debug,
) -> Result<(), Error> {
tracing::debug!(from=?path, to=?to, "copying_file");
// TODO: include the destination in the error message
std::fs::copy(path.as_ref(), to.as_ref())
.map_err(|err| Error::FileIo {
action: FileIoAction::Copy,
kind: FileKind::File,
path: Utf8PathBuf::from(path.as_ref()),
err: Some(err.to_string()),
})
.map(|_| ())
}
// pub fn rename(path: impl AsRef + Debug, to: impl AsRef + Debug) -> Result<(), Error> {
// tracing::debug!(from=?path, to=?to, "renaming_file");
// // TODO: include the destination in the error message
// std::fs::rename(&path, &to)
// .map_err(|err| Error::FileIo {
// action: FileIoAction::Rename,
// kind: FileKind::File,
// path: Utf8PathBuf::from(path.as_ref()),
// err: Some(err.to_string()),
// })
// .map(|_| ())
// }
pub fn copy_dir(
path: impl AsRef + Debug,
to: impl AsRef + Debug,
) -> Result<(), Error> {
tracing::debug!(from=?path, to=?to, "copying_directory");
// TODO: include the destination in the error message
fs_extra::dir::copy(
path.as_ref(),
to.as_ref(),
&fs_extra::dir::CopyOptions::new()
.copy_inside(false)
.content_only(true),
)
.map_err(|err| Error::FileIo {
action: FileIoAction::Copy,
kind: FileKind::Directory,
path: Utf8PathBuf::from(path.as_ref()),
err: Some(err.to_string()),
})
.map(|_| ())
}
pub fn symlink_dir(
src: impl AsRef + Debug,
dest: impl AsRef + Debug,
) -> Result<(), Error> {
tracing::debug!(src=?src, dest=?dest, "symlinking");
let src = canonicalise(src.as_ref())?;
#[cfg(target_family = "windows")]
let result = std::os::windows::fs::symlink_dir(src, dest.as_ref());
#[cfg(not(target_family = "windows"))]
let result = std::os::unix::fs::symlink(src, dest.as_ref());
result.map_err(|err| Error::FileIo {
action: FileIoAction::Link,
kind: FileKind::File,
path: Utf8PathBuf::from(dest.as_ref()),
err: Some(err.to_string()),
})?;
Ok(())
}
pub fn hardlink(
from: impl AsRef + Debug,
to: impl AsRef + Debug,
) -> Result<(), Error> {
tracing::debug!(from=?from, to=?to, "hardlinking");
std::fs::hard_link(from.as_ref(), to.as_ref())
.map_err(|err| Error::FileIo {
action: FileIoAction::Link,
kind: FileKind::File,
path: Utf8PathBuf::from(from.as_ref()),
err: Some(err.to_string()),
})
.map(|_| ())
}
/// Check if the given path is inside a git work tree.
/// This is done by running `git rev-parse --is-inside-work-tree --quiet` in the
/// given path. If git is not installed then we assume we're not in a git work
/// tree.
///
fn is_inside_git_work_tree(path: &Utf8Path) -> Result {
tracing::debug!(path=?path, "checking_for_git_repo");
let args: Vec<&str> = vec!["rev-parse", "--is-inside-work-tree", "--quiet"];
// Ignore all output, rely on the exit code instead.
// git will display a fatal error on stderr if rev-parse isn't run inside of a git work tree,
// so send stderr to /dev/null
let result = std::process::Command::new("git")
.args(args)
.stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.current_dir(path)
.status();
match result {
Ok(status) => Ok(status.success()),
Err(error) => match error.kind() {
io::ErrorKind::NotFound => Ok(false),
other => Err(Error::ShellCommand {
program: "git".into(),
reason: ShellCommandFailureReason::IoError(other),
}),
},
}
}
pub(crate) fn is_git_work_tree_root(path: &Utf8Path) -> bool {
tracing::debug!(path=?path, "checking_for_git_repo_root");
exists(path.join(".git")).unwrap_or(false)
}
/// Run `git init` in the given path.
/// If git is not installed then we do nothing.
pub fn git_init(path: &Utf8Path) -> Result<(), Error> {
tracing::debug!(path=?path, "initializing git");
if is_inside_git_work_tree(path)? {
tracing::debug!(path=?path, "git_repo_already_exists");
return Ok(());
}
let args = vec!["init".into(), "--quiet".into(), path.to_string()];
let command = Command {
program: "git".to_string(),
args,
env: vec![],
cwd: None,
stdio: Stdio::Inherit,
};
match ProjectIO::new().exec(command) {
Ok(_) => Ok(()),
Err(err) => match err {
Error::ShellProgramNotFound { .. } => Ok(()),
_ => Err(Error::GitInitialization {
error: err.to_string(),
}),
},
}
}
pub fn canonicalise(path: &Utf8Path) -> Result {
std::fs::canonicalize(path)
.map_err(|err| Error::FileIo {
action: FileIoAction::Canonicalise,
kind: FileKind::File,
path: Utf8PathBuf::from(path),
err: Some(err.to_string()),
})
.map(|pb| Utf8PathBuf::from_path_buf(pb).expect("Non Utf8 Path"))
}
#[derive(Debug, Clone, Copy)]
pub struct ConsoleWarningEmitter;
impl WarningEmitterIO for ConsoleWarningEmitter {
fn emit_warning(&self, warning: Warning) {
let buffer_writer = crate::cli::stderr_buffer_writer();
let mut buffer = buffer_writer.buffer();
warning.pretty(&mut buffer);
buffer_writer
.print(&buffer)
.expect("Writing warning to stderr");
}
}
================================================
FILE: compiler-cli/src/hex/auth.rs
================================================
use crate::{cli, http::HttpClient};
use ecow::EcoString;
use gleam_core::{
Error, Result, encryption,
error::{FileIoAction, FileKind},
io::HttpClient as _,
paths,
};
use hexpm::OAuthTokens;
use sha2::Digest as _;
pub const HEX_OAUTH_CLIENT_ID: &str = "877731e8-cb88-45e1-9b84-9214de7da421";
pub const LOCAL_PASS_PROMPT: &str = "Local password";
pub const API_ENV_NAME: &str = "HEXPM_API_KEY";
#[derive(Debug)]
pub struct EncryptedLegacyApiKey {
pub name: String,
}
pub struct HexAuthentication<'runtime> {
runtime: &'runtime tokio::runtime::Runtime,
http: &'runtime HttpClient,
local_password: Option,
hex_config: hexpm::Config,
}
impl<'runtime> HexAuthentication<'runtime> {
/// Reads the stored API key from disc, if it exists.
///
pub fn new(
runtime: &'runtime tokio::runtime::Runtime,
http: &'runtime HttpClient,
hex_config: hexpm::Config,
) -> Self {
Self {
runtime,
http,
local_password: None,
hex_config,
}
}
/// Create new OAuth tokens by sending the user through the OAuth flow.
///
/// Any already-existing tokens stored on the file system are revoked.
///
pub fn create_and_store_new_credentials_via_oauth(&mut self) -> Result {
let previous_oauth_token = self.read_stored_encrypted_oauth_refresh_token()?;
let previous_legacy_key = self.read_stored_legacy_api_key()?;
// Create a device authorisation with Hex, starting the oauth flow.
let mut device_authorisation = self.create_oauth_device_authorisation()?;
// Show the user their code, and send them to Hex to log in.
send_user_to_oauth_verification_url(&device_authorisation);
// The user has been sent to the Hex website to authenticate.
// Poll the Hex API until they accept or reject the request, or it times out.
let tokens = loop {
let next = self.poll_for_oauth_next_step(&mut device_authorisation)?;
match next {
hexpm::PollStep::Done(tokens) => break tokens,
hexpm::PollStep::SleepThenPollAgain(duration) => std::thread::sleep(duration),
}
};
// We are creating a new API key, so we need a new local password
// to encrypt it with.
self.ask_for_new_local_password()?;
self.encrypt_and_store_oauth_refresh_token(&tokens)?;
let credentials = tokens.as_credentials();
// Dispose of old tokens, if there was any.
self.revoke_old_tokens(&credentials, previous_oauth_token, previous_legacy_key)?;
self.delete_legacy_api_key_from_filesystem()?;
Ok(credentials)
}
fn poll_for_oauth_next_step(
&mut self,
device_authorisation: &mut hexpm::OAuthDeviceAuthorisation,
) -> Result {
let request = device_authorisation.poll_token_request(&self.hex_config);
let response = self.runtime.block_on(self.http.send(request))?;
let next = device_authorisation
.poll_token_response(response)
.map_err(Error::hex)?;
Ok(next)
}
fn create_oauth_device_authorisation(
&mut self,
) -> Result {
// Create a recognisable name for the client, so folks can more easily understand which
// session is which in the Hex console.
// It is expected that we can always get the hostname.
let hostname = hostname::get().expect("hostname");
let client_name = format!("Gleam ({})", hostname.to_string_lossy());
let request = hexpm::oauth_device_authorisation_request(
HEX_OAUTH_CLIENT_ID,
&client_name,
&self.hex_config,
);
let response = self.runtime.block_on(self.http.send(request))?;
hexpm::oauth_device_authorisation_response(HEX_OAUTH_CLIENT_ID.to_string(), response)
.map_err(Error::hex)
}
fn encrypt_and_store_oauth_refresh_token(&mut self, tokens: &OAuthTokens) -> Result<(), Error> {
let path = paths::global_hexpm_oauth_credentials_path();
let local_password = self.get_local_password()?;
let encrypted_refresh_token =
encryption::encrypt_with_passphrase(tokens.refresh_token.as_bytes(), &local_password)
.map_err(|e| Error::FailedToEncryptLocalHexApiKey {
detail: e.to_string(),
})?;
let credentials = StoredOAuthCredentials {
hexpm: StoredOAuthRepoCredentials {
api: self.hex_config.api_base.clone(),
repository: self.hex_config.repository_base.clone(),
refresh_token: encrypted_refresh_token,
refresh_token_hash: {
let mut hasher = sha2::Sha256::new();
hasher.update(tokens.refresh_token.as_bytes());
base16::encode_lower(&hasher.finalize())
},
},
};
let toml = toml::to_string(&credentials).expect("OAuth credentials TOML encoding");
crate::fs::write(&path, &toml)?;
Ok(())
}
/// Create a new local password.
///
/// The password must be long enough.
///
/// The old password will be discarded, and the new one will be both
/// returned and stored in `self.local_password`
///
fn ask_for_new_local_password(&mut self) -> Result<()> {
let required_length = 8;
self.local_password = None;
println!(
"Please enter a new unique password, at least {required_length} characters long.
It will be used to locally encrypt your Hex API tokens.
"
);
loop {
let password = cli::ask_password(LOCAL_PASS_PROMPT)?;
if password.chars().count() < required_length {
println!("\nPlease use a password at least {required_length} characters long.\n")
} else {
self.local_password = Some(password.clone());
return Ok(());
}
}
}
fn get_local_password(&mut self) -> Result {
if let Some(password) = self.local_password.as_ref() {
return Ok(password.clone());
}
let password = cli::ask_password(LOCAL_PASS_PROMPT)?;
self.local_password = Some(password.clone());
Ok(password)
}
/// Get a token that can be used to authenticate with the Hex API.
/// In order, it will try these sources:
///
/// 1. An API key from the HEXPM_API_KEY environment variable.
/// 2. An OAuth refresh token from the file system, which is then exchanged for
/// an access token.
/// 3. The OAuth flow.
pub fn get_or_create_api_credentials(&mut self) -> Result {
if let Some(key) = Self::read_env_api_key()? {
return Ok(key);
}
if let Some(tokens) = self.read_and_decrypt_and_refresh_stored_tokens()? {
return Ok(tokens.as_credentials());
}
self.create_and_store_new_credentials_via_oauth()
}
fn read_env_api_key() -> Result