diff --git a/.github/workflows/macos_x86_64.yml b/.github/workflows/macos_x86_64.yml index fc3527fa29..127a6eb000 100644 --- a/.github/workflows/macos_x86_64.yml +++ b/.github/workflows/macos_x86_64.yml @@ -24,7 +24,4 @@ jobs: - name: regular rust tests run: cargo test --locked --release -- --skip opaque_wrap_function --skip gen_list::bool_list_literal --skip platform_switching_swift --skip swift_ui --skip gen_str::str_append_scalar --skip gen_tags::phantom_polymorphic_record && sccache --show-stats # swift tests are skipped because of "Could not find or use auto-linked library 'swiftCompatibilityConcurrency'" on macos-11 x86_64 CI machine - # this issue may be caused by using older versions of XCode - - - name: test launching the editor - run: cargo test --release --locked editor_launch_test::launch -- --ignored # `--ignored` to run this test that is ignored for "normal" runs + # this issue may be caused by using older versions of XCode \ No newline at end of file diff --git a/.github/workflows/nightly_linux_x86_64.yml b/.github/workflows/nightly_linux_x86_64.yml index a71cb5a385..711ca23d68 100644 --- a/.github/workflows/nightly_linux_x86_64.yml +++ b/.github/workflows/nightly_linux_x86_64.yml @@ -17,7 +17,7 @@ jobs: run: ./ci/write_version.sh - name: build release with lto - run: RUSTFLAGS="-C target-cpu=x86-64" cargo build --profile=release-with-lto --locked --features "editor" --bin roc + run: RUSTFLAGS="-C target-cpu=x86-64" cargo build --profile=release-with-lto --locked --bin roc # target-cpu=x86-64 -> For maximal compatibility for all CPU's. This was also faster in our tests: https://roc.zulipchat.com/#narrow/stream/231635-compiler-development/topic/.2Ecargo.2Fconfig.2Etoml/near/325726299 - name: get commit SHA diff --git a/.github/workflows/nightly_macos_apple_silicon.yml b/.github/workflows/nightly_macos_apple_silicon.yml index 3c0e70fd23..8d3e9f8c55 100644 --- a/.github/workflows/nightly_macos_apple_silicon.yml +++ b/.github/workflows/nightly_macos_apple_silicon.yml @@ -36,7 +36,7 @@ jobs: run: ./ci/write_version.sh - name: build nightly release - run: cargo build --locked --profile=release-with-lto --features "editor" --bin roc + run: cargo build --locked --profile=release-with-lto --bin roc # this makes the roc binary a lot smaller - name: strip debug info diff --git a/.github/workflows/nightly_macos_x86_64.yml b/.github/workflows/nightly_macos_x86_64.yml index 43e5ce868f..77c7f3e200 100644 --- a/.github/workflows/nightly_macos_x86_64.yml +++ b/.github/workflows/nightly_macos_x86_64.yml @@ -22,8 +22,8 @@ jobs: # this issue may be caused by using older versions of XCode - name: build release - run: RUSTFLAGS="-C target-cpu=x86-64" cargo build --profile=release-with-lto --locked --features "editor" --bin roc - # target-cpu=x86-64 -> For maximal compatibility for all CPU's. Note that this setting will likely make the compiler slower. + run: RUSTFLAGS="-C target-cpu=x86-64" cargo build --profile=release-with-lto --locked --bin roc + # target-cpu=x86-64 -> For maximal compatibility for all CPU's. - name: get commit SHA run: echo "SHA=$(git rev-parse --short "$GITHUB_SHA")" >> $GITHUB_ENV diff --git a/.github/workflows/nix_macos_apple_silicon.yml b/.github/workflows/nix_macos_apple_silicon.yml index 065456ad7d..95db6c76ff 100644 --- a/.github/workflows/nix_macos_apple_silicon.yml +++ b/.github/workflows/nix_macos_apple_silicon.yml @@ -29,6 +29,9 @@ jobs: - name: check code style with clippy --release run: cargo clippy --workspace --tests --release -- --deny warnings + - name: test building default.nix + run: nix-build + - name: execute tests with --release run: nix develop -c cargo test --locked --release @@ -37,10 +40,7 @@ jobs: - name: check that the platform`s produced dylib is loadable run: cd examples/platform-switching/rust-platform && nix develop -c cargo test --release --locked - - - name: test launching the editor - run: nix develop -c cargo test --release --locked editor_launch_test::launch -- --ignored # `--ignored` to run this test that is ignored for "normal" runs - + # we run the llvm wasm tests only on this machine because it is fast and wasm should be cross-target - name: execute llvm wasm tests with --release run: nix develop -c cargo test-gen-llvm-wasm --locked --release diff --git a/.github/workflows/nix_macos_x86_64.yml b/.github/workflows/nix_macos_x86_64.yml index 7c9e43aa28..0aef9e78c1 100644 --- a/.github/workflows/nix_macos_x86_64.yml +++ b/.github/workflows/nix_macos_x86_64.yml @@ -22,6 +22,9 @@ jobs: - uses: cachix/install-nix-action@v22 + - name: test building default.nix + run: nix-build + - name: execute cli_run tests only, the full tests take too long but are run nightly run: nix develop -c cargo test --locked --release -p roc_cli diff --git a/.github/workflows/ubuntu_x86_64.yml b/.github/workflows/ubuntu_x86_64.yml index 86a6eb8040..bf754c42eb 100644 --- a/.github/workflows/ubuntu_x86_64.yml +++ b/.github/workflows/ubuntu_x86_64.yml @@ -39,10 +39,7 @@ jobs: - name: check that the platform`s produced dylib is loadable run: cd examples/platform-switching/rust-platform && LD_LIBRARY_PATH=. cargo test --release --locked - - - name: test launching the editor - run: cargo test --release --locked editor_launch_test::launch -- --ignored # `--ignored` to run this test that is ignored for "normal" runs - + - name: test the dev backend # these tests require an explicit feature flag run: cargo test --locked --release --package test_gen --no-default-features --features gen-dev && sccache --show-stats diff --git a/.github/workflows/windows_tests.yml b/.github/workflows/windows_tests.yml index 545be60e78..a5e59699dd 100644 --- a/.github/workflows/windows_tests.yml +++ b/.github/workflows/windows_tests.yml @@ -50,11 +50,11 @@ jobs: # Why are these tests not build with previous command? => fingerprint error. Use `CARGO_LOG=cargo::core::compiler::fingerprint=info` to investigate - name: Build specific tests without running. Twice for zig lld-link error. - run: cargo test --locked --release --no-run -p roc_ident -p roc_region -p roc_collections -p roc_can -p roc_types -p roc_solve -p roc_mono -p roc_gen_dev -p roc_gen_wasm -p roc_serialize -p roc_editor -p roc_linker -p roc_cli -p test_gen || cargo test --locked --release --no-run -p roc_ident -p roc_region -p roc_collections -p roc_can -p roc_types -p roc_solve -p roc_mono -p roc_gen_dev -p roc_gen_wasm -p roc_serialize -p roc_editor -p roc_linker -p roc_cli -p test_gen + run: cargo test --locked --release --no-run -p roc_ident -p roc_region -p roc_collections -p roc_can -p roc_types -p roc_solve -p roc_mono -p roc_gen_dev -p roc_gen_wasm -p roc_serialize -p roc_linker -p roc_cli -p test_gen || cargo test --locked --release --no-run -p roc_ident -p roc_region -p roc_collections -p roc_can -p roc_types -p roc_solve -p roc_mono -p roc_gen_dev -p roc_gen_wasm -p roc_serialize -p roc_linker -p roc_cli -p test_gen - name: Test setjmp/longjmp logic run: cargo test-gen-dev --locked --release nat_alias && cargo test-gen-dev --locked --release a_crash - name: Actually run the tests. - run: cargo test --locked --release -p roc_ident -p roc_region -p roc_collections -p roc_can -p roc_types -p roc_solve -p roc_mono -p roc_gen_dev -p roc_gen_wasm -p roc_serialize -p roc_editor -p roc_linker -p roc_cli + run: cargo test --locked --release -p roc_ident -p roc_region -p roc_collections -p roc_can -p roc_types -p roc_solve -p roc_mono -p roc_gen_dev -p roc_gen_wasm -p roc_serialize -p roc_linker -p roc_cli diff --git a/.gitignore b/.gitignore index 76e064d8c3..304a09f535 100644 --- a/.gitignore +++ b/.gitignore @@ -38,18 +38,6 @@ metadata .vimrc .nvimrc -#files too big to track in git -editor/benches/resources/100000_lines.roc -editor/benches/resources/10000_lines.roc -editor/benches/resources/1000_lines.roc -editor/benches/resources/100_lines.roc -editor/benches/resources/25000000_lines.roc -editor/benches/resources/50000_lines.roc -editor/benches/resources/500_lines.roc - -# file editor creates when no arg is passed -roc-projects - # rust cache (sccache folder) sccache_dir diff --git a/BUILDING_FROM_SOURCE.md b/BUILDING_FROM_SOURCE.md index b94f842c56..86150e5b89 100644 --- a/BUILDING_FROM_SOURCE.md +++ b/BUILDING_FROM_SOURCE.md @@ -57,17 +57,6 @@ Read the instructions [here](devtools/README.md) to make nix work well with your If you want to load all dependencies automatically whenever you `cd` into `roc`, check out [direnv](https://direnv.net/). Then you will no longer need to execute `nix develop` first. -### Editor - -The editor is a :construction:WIP:construction: and not ready yet to replace your favorite editor, although if you want to try it out on nix, read on. -`cargo run edit` should work on NixOS and MacOS. If you use Linux x86_64, follow the instructions below. - -If you're not already in a nix shell, execute `nix develop` at the the root of the repo folder and then execute: - -```sh -nixVulkanIntel cargo run edit -``` - ## Troubleshooting Create an issue if you run into problems not listed here. @@ -190,7 +179,7 @@ export CPPFLAGS="-I/usr/local/opt/llvm/include" ### LLVM installation on Windows -**Warning** While `cargo build` works on windows, linking roc programs does not yet, see issue #2608. This also means the repl, the editor and many tests will not work on windows. +**Warning** While `cargo build` works on windows, linking roc programs does not yet, see issue #2608. This also means the repl, and many tests will not work on windows. The official LLVM pre-built binaries for Windows lack features that roc needs. Instead: 1. Download the custom LLVM 7z archive [here](https://github.com/roc-lang/llvm-package-windows/releases/download/v13.0.1/LLVM-13.0.1-win64.7z). diff --git a/Cargo.lock b/Cargo.lock index bdd7d5a49f..a9da27e797 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,22 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "ab_glyph" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5110f1c78cf582855d895ecd0746b653db010cec6d9f5575293f27934d980a39" -dependencies = [ - "ab_glyph_rasterizer", - "owned_ttf_parser", -] - -[[package]] -name = "ab_glyph_rasterizer" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" - [[package]] name = "addr2line" version = "0.19.0" @@ -33,24 +17,13 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - [[package]] name = "ahash" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "once_cell", "version_check", ] @@ -152,24 +125,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "approx" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f2a05fd1bd10b2527e20a2cd32d8873d115b8b39fe219ee25f42a8aca6ba278" -dependencies = [ - "num-traits", -] - -[[package]] -name = "approx" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" -dependencies = [ - "num-traits", -] - [[package]] name = "arena-pool" version = "0.0.1" @@ -204,15 +159,6 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "ash" -version = "0.34.0+1.2.203" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0f780da53d0063880d45554306489f09dd8d1bda47688b4a57bc579119356df" -dependencies = [ - "libloading", -] - [[package]] name = "atty" version = "0.2.14" @@ -238,7 +184,7 @@ checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" dependencies = [ "addr2line", "cc", - "cfg-if 1.0.0", + "cfg-if", "libc", "miniz_oxide 0.6.2", "object", @@ -326,17 +272,11 @@ dependencies = [ "arrayref", "arrayvec 0.7.2", "cc", - "cfg-if 1.0.0", + "cfg-if", "constant_time_eq", "digest", ] -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - [[package]] name = "block-buffer" version = "0.10.4" @@ -383,26 +323,6 @@ dependencies = [ "utf8-width", ] -[[package]] -name = "bytemuck" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" -dependencies = [ - "bytemuck_derive", -] - -[[package]] -name = "bytemuck_derive" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdde5c9cd29ebd706ce1b35600920a33550e402fc998a2e53ad3b42c3c47a192" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.16", -] - [[package]] name = "byteorder" version = "1.4.3" @@ -415,16 +335,6 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" -[[package]] -name = "calloop" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf2eec61efe56aa1e813f5126959296933cf0700030e4314786c48779a66ab82" -dependencies = [ - "log", - "nix 0.22.3", -] - [[package]] name = "capstone" version = "0.11.0" @@ -460,34 +370,12 @@ version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - -[[package]] -name = "cgmath" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a98d30140e3296250832bbaaff83b27dcd6fa3cc70fb6f1f3e5c9c0023b5317" -dependencies = [ - "approx 0.4.0", - "num-traits", -] - [[package]] name = "chrono" version = "0.4.26" @@ -573,16 +461,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "clipboard-win" -version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fdf5e01086b6be750428ba4a40619f847eb2e95756eee84b18e06e5f0b50342" -dependencies = [ - "lazy-bytes-cast", - "winapi", -] - [[package]] name = "clipboard-win" version = "4.5.0" @@ -594,64 +472,12 @@ dependencies = [ "winapi", ] -[[package]] -name = "cocoa" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" -dependencies = [ - "bitflags", - "block", - "cocoa-foundation", - "core-foundation 0.9.3", - "core-graphics 0.22.3", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "cocoa-foundation" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "931d3837c286f56e3c58423ce4eba12d08db2374461a785c86f672b08b5650d6" -dependencies = [ - "bitflags", - "block", - "core-foundation 0.9.3", - "core-graphics-types", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - [[package]] name = "colorchoice" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" -[[package]] -name = "colored" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" -dependencies = [ - "atty", - "lazy_static", - "winapi", -] - [[package]] name = "console" version = "0.15.5" @@ -670,7 +496,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "wasm-bindgen", ] @@ -700,124 +526,19 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b" -[[package]] -name = "copyless" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2df960f5d869b2dd8532793fde43eb5427cceb126c929747a26823ab0eeb536" - -[[package]] -name = "copypasta" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "133fc8675ee3a4ec9aa513584deda9aa0faeda3586b87f7f0f2ba082c66fb172" -dependencies = [ - "clipboard-win 3.1.1", - "objc", - "objc-foundation", - "objc_id", - "smithay-clipboard", - "x11-clipboard", -] - -[[package]] -name = "core-foundation" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" -dependencies = [ - "core-foundation-sys 0.7.0", - "libc", -] - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys 0.8.4", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" - [[package]] name = "core-foundation-sys" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" -[[package]] -name = "core-graphics" -version = "0.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3889374e6ea6ab25dba90bb5d96202f61108058361f6dc72e8b03e6f8bbe923" -dependencies = [ - "bitflags", - "core-foundation 0.7.0", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" -dependencies = [ - "bitflags", - "core-foundation 0.9.3", - "core-graphics-types", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" -dependencies = [ - "bitflags", - "core-foundation 0.9.3", - "foreign-types", - "libc", -] - -[[package]] -name = "core-video-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ecad23610ad9757664d644e369246edde1803fcb43ed72876565098a5d3828" -dependencies = [ - "cfg-if 0.1.10", - "core-foundation-sys 0.7.0", - "core-graphics 0.19.2", - "libc", - "objc", -] - -[[package]] -name = "cpufeatures" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" -dependencies = [ - "libc", -] - [[package]] name = "crc32fast" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -860,7 +581,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crossbeam-channel", "crossbeam-deque", "crossbeam-epoch", @@ -874,7 +595,7 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crossbeam-utils", ] @@ -884,7 +605,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] @@ -896,7 +617,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" dependencies = [ "autocfg", - "cfg-if 1.0.0", + "cfg-if", "crossbeam-utils", "memoffset 0.8.0", "scopeguard", @@ -908,7 +629,7 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crossbeam-utils", ] @@ -918,7 +639,7 @@ version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -962,65 +683,13 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "cty" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" - [[package]] name = "cvt" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2ae9bf77fbf2d39ef573205d554d87e86c12f1994e9ea335b0651b9b278bcf1" dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "d3d12" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2daefd788d1e96e0a9d66dee4b828b883509bc3ea9ce30665f04c3246372690c" -dependencies = [ - "bitflags", - "libloading", - "winapi", -] - -[[package]] -name = "darling" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 1.0.109", -] - -[[package]] -name = "darling_macro" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" -dependencies = [ - "darling_core", - "quote", - "syn 1.0.109", + "cfg-if", ] [[package]] @@ -1029,11 +698,11 @@ version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "hashbrown 0.12.3", "lock_api", "once_cell", - "parking_lot_core 0.9.7", + "parking_lot_core", ] [[package]] @@ -1070,7 +739,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "dirs-sys-next", ] @@ -1085,39 +754,18 @@ dependencies = [ "winapi", ] -[[package]] -name = "dispatch" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" - [[package]] name = "distance" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d9d8664cf849d7d0f3114a3a387d2f5e4303176d746d5a951aaddc66dfe9240" -[[package]] -name = "dlib" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" -dependencies = [ - "libloading", -] - [[package]] name = "doc-comment" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" -[[package]] -name = "downcast-rs" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" - [[package]] name = "dunce" version = "1.0.4" @@ -1154,7 +802,7 @@ version = "0.8.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -1210,7 +858,7 @@ version = "3.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ae6b3d9530211fb3b12a95374b8b0823be812f53d09e18c5675c0146b09642" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "rustix", "windows-sys 0.48.0", ] @@ -1221,21 +869,12 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "redox_syscall", "windows-sys 0.48.0", ] -[[package]] -name = "find-crate" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a98bbaacea1c0eb6a0876280051b892eb73594fd90cf3b20e9c817029c57d2" -dependencies = [ - "toml", -] - [[package]] name = "flate2" version = "1.0.26" @@ -1252,21 +891,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.1.0" @@ -1283,7 +907,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0504bab20f4487fdf1c20ed48e3e32c7951827a778cd3dfded1768f90b6abb0a" dependencies = [ "aligned", - "cfg-if 1.0.0", + "cfg-if", "cvt", "libc", "nix 0.26.2", @@ -1392,15 +1016,6 @@ dependencies = [ "slab", ] -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -1411,23 +1026,13 @@ dependencies = [ "version_check", ] -[[package]] -name = "gethostname" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "getrandom" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", @@ -1440,95 +1045,6 @@ version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" -[[package]] -name = "glow" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8bd5877156a19b8ac83a29b2306fe20537429d318f3ff0a1a2119f8d9c61919" -dependencies = [ - "js-sys", - "slotmap", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "glyph_brush" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4edefd123f28a0b1d41ec4a489c2b43020b369180800977801611084f342978d" -dependencies = [ - "glyph_brush_draw_cache", - "glyph_brush_layout", - "ordered-float", - "rustc-hash", - "twox-hash", -] - -[[package]] -name = "glyph_brush_draw_cache" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6010675390f6889e09a21e2c8b575b3ee25667ea8237a8d59423f73cb8c28610" -dependencies = [ - "ab_glyph", - "crossbeam-channel", - "crossbeam-deque", - "linked-hash-map", - "rayon", - "rustc-hash", -] - -[[package]] -name = "glyph_brush_layout" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc32c2334f00ca5ac3695c5009ae35da21da8c62d255b5b96d56e2597a637a38" -dependencies = [ - "ab_glyph", - "approx 0.5.1", - "xi-unicode", -] - -[[package]] -name = "gpu-alloc" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22beaafc29b38204457ea030f6fb7a84c9e4dd1b86e311ba0542533453d87f62" -dependencies = [ - "bitflags", - "gpu-alloc-types", -] - -[[package]] -name = "gpu-alloc-types" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54804d0d6bc9d7f26db4eaec1ad10def69b599315f487d32c334a80d1efe67a5" -dependencies = [ - "bitflags", -] - -[[package]] -name = "gpu-descriptor" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b0c02e1ba0bdb14e965058ca34e09c020f8e507a760df1121728e0aef68d57a" -dependencies = [ - "bitflags", - "gpu-descriptor-types", - "hashbrown 0.12.3", -] - -[[package]] -name = "gpu-descriptor-types" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "363e3677e55ad168fef68cf9de3a4a310b53124c5e784c53a1d70e92d23f2126" -dependencies = [ - "bitflags", -] - [[package]] name = "h2" version = "0.3.19" @@ -1559,9 +1075,6 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.6", -] [[package]] name = "hashbrown" @@ -1569,7 +1082,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.3", + "ahash", "bumpalo", ] @@ -1603,12 +1116,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" -[[package]] -name = "hexf-parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" - [[package]] name = "html-escape" version = "0.2.13" @@ -1696,7 +1203,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" dependencies = [ "android_system_properties", - "core-foundation-sys 0.8.4", + "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", @@ -1721,12 +1228,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - [[package]] name = "idna" version = "0.3.0" @@ -1791,7 +1292,7 @@ dependencies = [ "libc", "llvm-sys", "once_cell", - "parking_lot 0.12.1", + "parking_lot", ] [[package]] @@ -1804,12 +1305,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "inplace_it" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e567468c50f3d4bc7397702e09b380139f9b9288b4e909b070571007f8b5bf78" - [[package]] name = "insta" version = "1.29.0" @@ -1823,18 +1318,6 @@ dependencies = [ "yaml-rust", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if 1.0.0", - "js-sys", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "io-lifetimes" version = "1.0.10" @@ -1888,12 +1371,6 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" -[[package]] -name = "jni-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" - [[package]] name = "js-sys" version = "0.3.62" @@ -1913,22 +1390,6 @@ dependencies = [ "rayon", ] -[[package]] -name = "khronos-egl" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c2352bd1d0bceb871cb9d40f24360c8133c11d7486b68b5381c1dd1a32015e3" -dependencies = [ - "libc", - "libloading", -] - -[[package]] -name = "lazy-bytes-cast" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10257499f089cd156ad82d0a9cd57d9501fa2c989068992a97eb3c27836f206b" - [[package]] name = "lazy_static" version = "1.4.0" @@ -1947,7 +1408,7 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "winapi", ] @@ -2019,7 +1480,7 @@ version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -2038,15 +1499,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - [[package]] name = "maplit" version = "1.0.2" @@ -2068,15 +1520,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" -[[package]] -name = "memmap2" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b6c2ebff6180198788f5db08d7ce3bc1d0b617176678831a7510825973e357" -dependencies = [ - "libc", -] - [[package]] name = "memmap2" version = "0.5.10" @@ -2104,20 +1547,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "metal" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0514f491f4cc03632ab399ee01e2c1c1b12d3e1cf2d667c1ff5f87d6dcd2084" -dependencies = [ - "bitflags", - "block", - "core-graphics-types", - "foreign-types", - "log", - "objc", -] - [[package]] name = "mimalloc" version = "0.1.37" @@ -2133,12 +1562,6 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "miniz_oxide" version = "0.6.2" @@ -2180,77 +1603,6 @@ dependencies = [ "typed-arena", ] -[[package]] -name = "naga" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3012f2dbcc79e8e0b5825a4836a7106a75dd9b2fe42c528163be0f572538c705" -dependencies = [ - "bit-set", - "bitflags", - "codespan-reporting", - "hexf-parse", - "indexmap", - "log", - "num-traits", - "rustc-hash", - "spirv", - "thiserror", -] - -[[package]] -name = "ndk" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d868f654c72e75f8687572699cdabe755f03effbb62542768e995d5b8d699d" -dependencies = [ - "bitflags", - "jni-sys", - "ndk-sys", - "num_enum", - "thiserror", -] - -[[package]] -name = "ndk-context" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" - -[[package]] -name = "ndk-glue" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71bee8ea72d685477e28bd004cfe1bf99c754d688cd78cad139eae4089484d4" -dependencies = [ - "lazy_static", - "libc", - "log", - "ndk", - "ndk-context", - "ndk-macro", - "ndk-sys", -] - -[[package]] -name = "ndk-macro" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" -dependencies = [ - "darling", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ndk-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121" - [[package]] name = "nibble_vec" version = "0.1.0" @@ -2260,19 +1612,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "nix" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf" -dependencies = [ - "bitflags", - "cc", - "cfg-if 1.0.0", - "libc", - "memoffset 0.6.5", -] - [[package]] name = "nix" version = "0.23.2" @@ -2281,19 +1620,7 @@ checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" dependencies = [ "bitflags", "cc", - "cfg-if 1.0.0", - "libc", - "memoffset 0.6.5", -] - -[[package]] -name = "nix" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" -dependencies = [ - "bitflags", - "cfg-if 1.0.0", + "cfg-if", "libc", "memoffset 0.6.5", ] @@ -2305,27 +1632,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" dependencies = [ "bitflags", - "cfg-if 1.0.0", + "cfg-if", "libc", "static_assertions", ] -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "nonempty" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aeaf4ad7403de93e699c191202f017118df734d3850b01e13a3a8b2e6953d3c9" - [[package]] name = "normpath" version = "1.1.1" @@ -2365,66 +1676,6 @@ dependencies = [ "libc", ] -[[package]] -name = "num_enum" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", - "objc_exception", -] - -[[package]] -name = "objc-foundation" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" -dependencies = [ - "block", - "objc", - "objc_id", -] - -[[package]] -name = "objc_exception" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" -dependencies = [ - "cc", -] - -[[package]] -name = "objc_id" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" -dependencies = [ - "objc", -] - [[package]] name = "object" version = "0.30.3" @@ -2450,15 +1701,6 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" -[[package]] -name = "ordered-float" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fc2dbde8f8a79f2102cc474ceb0ad68e3b80b85289ea62389b60e66777e4213" -dependencies = [ - "num-traits", -] - [[package]] name = "output_vt100" version = "0.1.3" @@ -2474,15 +1716,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" -[[package]] -name = "owned_ttf_parser" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "706de7e2214113d63a8238d1910463cfce781129a6f263d13fdb09ff64355ba4" -dependencies = [ - "ttf-parser", -] - [[package]] name = "packed_struct" version = "0.10.1" @@ -2505,51 +1738,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "page_size" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b7663cbd190cfd818d08efa8497f6cd383076688c49a391ef7c0d03cd12b561" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "palette" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f9cd68f7112581033f157e56c77ac4a5538ec5836a2e39284e65bd7d7275e49" -dependencies = [ - "approx 0.5.1", - "num-traits", - "palette_derive", - "phf", -] - -[[package]] -name = "palette_derive" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05eedf46a8e7c27f74af0c9cfcdb004ceca158cb1b918c6f68f8d7a549b3e427" -dependencies = [ - "find-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - [[package]] name = "parking_lot" version = "0.12.1" @@ -2557,21 +1745,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.7", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if 1.0.0", - "instant", - "libc", - "redox_syscall", - "smallvec", - "winapi", + "parking_lot_core", ] [[package]] @@ -2580,7 +1754,7 @@ version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "redox_syscall", "smallvec", @@ -2630,82 +1804,6 @@ dependencies = [ "ucd-trie", ] -[[package]] -name = "pest_derive" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b79d4c71c865a25a4322296122e3924d30bc8ee0834c8bfc8b95f7f054afbfb" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c435bf1076437b851ebc8edc3a18442796b30f1728ffea6262d59bbe28b077e" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn 2.0.16", -] - -[[package]] -name = "pest_meta" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "745a452f8eb71e39ffd8ee32b3c5f51d03845f99786fa9b68db6ff509c505411" -dependencies = [ - "once_cell", - "pest", - "sha2", -] - -[[package]] -name = "phf" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c" -dependencies = [ - "phf_macros", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf" -dependencies = [ - "phf_shared", - "rand", -] - -[[package]] -name = "phf_macros" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92aacdc5f16768709a569e913f7451034034178b05bdc8acda226659a3dccc66" -dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "phf_shared" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676" -dependencies = [ - "siphasher", -] - [[package]] name = "pin-project-lite" version = "0.2.9" @@ -2718,12 +1816,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkg-config" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" - [[package]] name = "plotters" version = "0.3.1" @@ -2769,16 +1861,6 @@ dependencies = [ "yansi", ] -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit", -] - [[package]] name = "proc-macro2" version = "1.0.57" @@ -2788,12 +1870,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "profiling" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "332cd62e95873ea4f41f3dfd6bbbfc5b52aec892d7e8d534197c4720a0bbbab2" - [[package]] name = "proptest" version = "1.1.0" @@ -2933,21 +2009,6 @@ dependencies = [ "rand_core", ] -[[package]] -name = "range-alloc" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab" - -[[package]] -name = "raw-window-handle" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b800beb9b6e7d2df1fe337c9e3d04e3af22a124460fb4c30fcc22c9117cefb41" -dependencies = [ - "cty", -] - [[package]] name = "rayon" version = "1.7.0" @@ -3038,7 +2099,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23895cfadc1917fed9c6ed76a8c2903615fa3704f7493ff82b364c6540acc02b" dependencies = [ "aligned", - "cfg-if 1.0.0", + "cfg-if", "cvt", "fs_at", "lazy_static", @@ -3047,12 +2108,6 @@ dependencies = [ "windows-sys 0.45.0", ] -[[package]] -name = "renderdoc-sys" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157" - [[package]] name = "repl_test" version = "0.0.1" @@ -3143,37 +2198,6 @@ dependencies = [ "roc_mono", ] -[[package]] -name = "roc_ast" -version = "0.0.1" -dependencies = [ - "arrayvec 0.7.2", - "bumpalo", - "indoc", - "libc", - "page_size", - "roc_builtins", - "roc_can", - "roc_checkmate", - "roc_collections", - "roc_error_macros", - "roc_load", - "roc_module", - "roc_packaging", - "roc_parse", - "roc_problem", - "roc_region", - "roc_reporting", - "roc_solve", - "roc_solve_schema", - "roc_target", - "roc_types", - "roc_unify", - "snafu", - "ven_graph", - "winapi", -] - [[package]] name = "roc_bitcode" version = "0.0.1" @@ -3301,7 +2325,7 @@ dependencies = [ "libc", "libloading", "mimalloc", - "parking_lot 0.12.1", + "parking_lot", "pretty_assertions", "roc_build", "roc_builtins", @@ -3309,7 +2333,6 @@ dependencies = [ "roc_collections", "roc_command_utils", "roc_docs", - "roc_editor", "roc_error_macros", "roc_fmt", "roc_gen_dev", @@ -3337,19 +2360,6 @@ dependencies = [ "ven_pretty", ] -[[package]] -name = "roc_code_markup" -version = "0.0.1" -dependencies = [ - "bumpalo", - "palette", - "roc_ast", - "roc_error_utils", - "roc_module", - "serde", - "snafu", -] - [[package]] name = "roc_collections" version = "0.0.1" @@ -3423,10 +2433,8 @@ dependencies = [ "peg", "pretty_assertions", "pulldown-cmark", - "roc_ast", "roc_builtins", "roc_can", - "roc_code_markup", "roc_collections", "roc_highlight", "roc_load", @@ -3450,54 +2458,6 @@ dependencies = [ "roc_docs", ] -[[package]] -name = "roc_editor" -version = "0.0.1" -dependencies = [ - "arrayvec 0.7.2", - "bumpalo", - "bytemuck", - "cgmath", - "colored", - "copypasta", - "fs_extra", - "futures", - "glyph_brush", - "libc", - "log", - "nonempty", - "page_size", - "palette", - "pest", - "pest_derive", - "rand", - "roc_ast", - "roc_builtins", - "roc_can", - "roc_code_markup", - "roc_collections", - "roc_command_utils", - "roc_load", - "roc_module", - "roc_packaging", - "roc_parse", - "roc_problem", - "roc_region", - "roc_reporting", - "roc_solve", - "roc_types", - "roc_unify", - "serde", - "snafu", - "tempfile", - "threadpool", - "uuid", - "ven_graph", - "wgpu", - "wgpu_glyph", - "winit", -] - [[package]] name = "roc_error_macros" version = "0.0.1" @@ -3669,7 +2629,7 @@ dependencies = [ "indoc", "libc", "mach_object", - "memmap2 0.5.10", + "memmap2", "object", "roc_collections", "roc_error_macros", @@ -3712,7 +2672,7 @@ dependencies = [ "crossbeam", "indoc", "maplit", - "parking_lot 0.12.1", + "parking_lot", "pretty_assertions", "roc_builtins", "roc_can", @@ -3763,7 +2723,7 @@ dependencies = [ "bitvec", "bumpalo", "hashbrown 0.13.2", - "parking_lot 0.12.1", + "parking_lot", "roc_builtins", "roc_can", "roc_collections", @@ -4146,12 +3106,6 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - [[package]] name = "rustc_version" version = "0.4.0" @@ -4220,8 +3174,8 @@ version = "9.1.1" source = "git+https://github.com/roc-lang/rustyline?rev=e74333c#e74333c0d618896b88175bf06645108f996fe6d0" dependencies = [ "bitflags", - "cfg-if 1.0.0", - "clipboard-win 4.5.0", + "cfg-if", + "clipboard-win", "dirs-next", "fd-lock", "libc", @@ -4285,12 +3239,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.1.0" @@ -4417,7 +3365,7 @@ dependencies = [ "futures", "lazy_static", "log", - "parking_lot 0.12.1", + "parking_lot", "serial_test_derive", ] @@ -4432,17 +3380,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "sha2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" -dependencies = [ - "cfg-if 1.0.0", - "cpufeatures", - "digest", -] - [[package]] name = "sharded-slab" version = "0.1.4" @@ -4477,12 +3414,6 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf" -[[package]] -name = "siphasher" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" - [[package]] name = "sized-chunks" version = "0.6.5" @@ -4502,15 +3433,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "slotmap" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" -dependencies = [ - "version_check", -] - [[package]] name = "smallvec" version = "1.10.0" @@ -4528,53 +3450,6 @@ dependencies = [ "syn 2.0.16", ] -[[package]] -name = "smithay-client-toolkit" -version = "0.15.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a28f16a97fa0e8ce563b2774d1e732dd5d4025d2772c5dba0a41a0f90a29da3" -dependencies = [ - "bitflags", - "calloop", - "dlib", - "lazy_static", - "log", - "memmap2 0.3.1", - "nix 0.22.3", - "pkg-config", - "wayland-client", - "wayland-cursor", - "wayland-protocols", -] - -[[package]] -name = "smithay-client-toolkit" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f307c47d32d2715eb2e0ece5589057820e0e5e70d07c247d1063e844e107f454" -dependencies = [ - "bitflags", - "dlib", - "lazy_static", - "log", - "memmap2 0.5.10", - "nix 0.24.3", - "pkg-config", - "wayland-client", - "wayland-cursor", - "wayland-protocols", -] - -[[package]] -name = "smithay-clipboard" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a345c870a1fae0b1b779085e81b51e614767c239e93503588e54c5b17f4b0e8" -dependencies = [ - "smithay-client-toolkit 0.16.0", - "wayland-client", -] - [[package]] name = "snafu" version = "0.7.4" @@ -4614,16 +3489,6 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" -[[package]] -name = "spirv" -version = "0.2.0+1.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "246bfa38fe3db3f1dfc8ca5a2cdeb7348c78be2112740cc0ec8ef18b6d94f830" -dependencies = [ - "bitflags", - "num-traits", -] - [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -4736,7 +3601,7 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "rand", "redox_syscall", @@ -4924,7 +3789,7 @@ version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "once_cell", ] @@ -5041,32 +3906,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_datetime" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" - -[[package]] -name = "toml_edit" -version = "0.19.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" -dependencies = [ - "indexmap", - "toml_datetime", - "winnow", -] - [[package]] name = "tower-service" version = "0.3.2" @@ -5079,7 +3918,7 @@ version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -5152,23 +3991,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" -[[package]] -name = "ttf-parser" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44dcf002ae3b32cd25400d6df128c5babec3927cd1eb7ce813cfff20eb6c3746" - -[[package]] -name = "twox-hash" -version = "1.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" -dependencies = [ - "cfg-if 1.0.0", - "rand", - "static_assertions", -] - [[package]] name = "typed-arena" version = "2.0.2" @@ -5302,9 +4124,6 @@ name = "uuid" version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2" -dependencies = [ - "getrandom", -] [[package]] name = "valgrind" @@ -5427,7 +4246,7 @@ version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b6cb788c4e39112fbe1822277ef6fb3c55cd86b95cb3d3c4c1c9597e4ac74b4" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "wasm-bindgen-macro", ] @@ -5452,7 +4271,7 @@ version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "083abe15c5d88556b77bdf7aef403625be9e327ad37c62c4e4129af740168163" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "js-sys", "wasm-bindgen", "web-sys", @@ -5487,79 +4306,6 @@ version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a901d592cafaa4d711bc324edfaff879ac700b19c3dfd60058d2b445be2691eb" -[[package]] -name = "wayland-client" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" -dependencies = [ - "bitflags", - "downcast-rs", - "libc", - "nix 0.24.3", - "scoped-tls", - "wayland-commons", - "wayland-scanner", - "wayland-sys", -] - -[[package]] -name = "wayland-commons" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" -dependencies = [ - "nix 0.24.3", - "once_cell", - "smallvec", - "wayland-sys", -] - -[[package]] -name = "wayland-cursor" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" -dependencies = [ - "nix 0.24.3", - "wayland-client", - "xcursor", -] - -[[package]] -name = "wayland-protocols" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" -dependencies = [ - "bitflags", - "wayland-client", - "wayland-commons", - "wayland-scanner", -] - -[[package]] -name = "wayland-scanner" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" -dependencies = [ - "proc-macro2", - "quote", - "xml-rs", -] - -[[package]] -name = "wayland-sys" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" -dependencies = [ - "dlib", - "lazy_static", - "pkg-config", -] - [[package]] name = "web-sys" version = "0.3.57" @@ -5589,109 +4335,6 @@ dependencies = [ "webpki", ] -[[package]] -name = "wgpu" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97cd781ff044d6d697b632a2e212032c2e957d1afaa21dbf58069cbb8f78567" -dependencies = [ - "arrayvec 0.7.2", - "js-sys", - "log", - "naga", - "parking_lot 0.11.2", - "raw-window-handle", - "smallvec", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "wgpu-core", - "wgpu-hal", - "wgpu-types", -] - -[[package]] -name = "wgpu-core" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4688c000eb841ca55f7b35db659b78d6e1cd77d7caf8fb929f4e181f754047d" -dependencies = [ - "arrayvec 0.7.2", - "bitflags", - "cfg_aliases", - "codespan-reporting", - "copyless", - "fxhash", - "log", - "naga", - "parking_lot 0.11.2", - "profiling", - "raw-window-handle", - "smallvec", - "thiserror", - "wgpu-hal", - "wgpu-types", -] - -[[package]] -name = "wgpu-hal" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d684ea6a34974a2fc19f1dfd183d11a62e22d75c4f187a574bb1224df8e056c2" -dependencies = [ - "arrayvec 0.7.2", - "ash", - "bit-set", - "bitflags", - "block", - "core-graphics-types", - "d3d12", - "foreign-types", - "fxhash", - "glow", - "gpu-alloc", - "gpu-descriptor", - "inplace_it", - "js-sys", - "khronos-egl", - "libloading", - "log", - "metal", - "naga", - "objc", - "parking_lot 0.11.2", - "profiling", - "range-alloc", - "raw-window-handle", - "renderdoc-sys", - "thiserror", - "wasm-bindgen", - "web-sys", - "wgpu-types", - "winapi", -] - -[[package]] -name = "wgpu-types" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "549533d9e1cdd4b4cda7718d33ff500fc4c34b5467b71d76b547ae0324f3b2a2" -dependencies = [ - "bitflags", -] - -[[package]] -name = "wgpu_glyph" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8134edb15ae465caf308125646c9e98bdef7398cdefc69227ac77a5eb795e7fe" -dependencies = [ - "bytemuck", - "glyph_brush", - "log", - "wgpu", -] - [[package]] name = "winapi" version = "0.3.9" @@ -5717,15 +4360,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "winapi-wsapoll" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -5888,48 +4522,6 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" -[[package]] -name = "winit" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b43cc931d58b99461188607efd7acb2a093e65fc621f54cad78517a6063e73a" -dependencies = [ - "bitflags", - "cocoa", - "core-foundation 0.9.3", - "core-graphics 0.22.3", - "core-video-sys", - "dispatch", - "instant", - "lazy_static", - "libc", - "log", - "mio", - "ndk", - "ndk-glue", - "ndk-sys", - "objc", - "parking_lot 0.11.2", - "percent-encoding", - "raw-window-handle", - "smithay-client-toolkit 0.15.4", - "wasm-bindgen", - "wayland-client", - "wayland-protocols", - "web-sys", - "winapi", - "x11-dl", -] - -[[package]] -name = "winnow" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" -dependencies = [ - "memchr", -] - [[package]] name = "winreg" version = "0.10.1" @@ -5957,48 +4549,6 @@ dependencies = [ "tap", ] -[[package]] -name = "x11-clipboard" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "980b9aa9226c3b7de8e2adb11bf20124327c054e0e5812d2aac0b5b5a87e7464" -dependencies = [ - "x11rb", -] - -[[package]] -name = "x11-dl" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" -dependencies = [ - "libc", - "once_cell", - "pkg-config", -] - -[[package]] -name = "x11rb" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "592b4883219f345e712b3209c62654ebda0bb50887f330cbd018d0f654bfd507" -dependencies = [ - "gethostname", - "nix 0.24.3", - "winapi", - "winapi-wsapoll", - "x11rb-protocol", -] - -[[package]] -name = "x11rb-protocol" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56b245751c0ac9db0e006dc812031482784e434630205a93c73cfefcaabeac67" -dependencies = [ - "nix 0.24.3", -] - [[package]] name = "xattr" version = "0.2.3" @@ -6008,21 +4558,6 @@ dependencies = [ "libc", ] -[[package]] -name = "xcursor" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" -dependencies = [ - "nom", -] - -[[package]] -name = "xi-unicode" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a" - [[package]] name = "xml-rs" version = "0.8.16" diff --git a/Cargo.toml b/Cargo.toml index 76e0f62e83..cc46326f11 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,11 +3,8 @@ members = [ "crates/compiler/*", "crates/vendor/*", "crates/glue", - "crates/editor", - "crates/ast", "crates/cli", "crates/cli_utils", - "crates/code_markup", "crates/highlight", "crates/error_macros", "crates/reporting", diff --git a/LEGAL_DETAILS b/LEGAL_DETAILS index f5b1e21a11..86d8beb786 100644 --- a/LEGAL_DETAILS +++ b/LEGAL_DETAILS @@ -52,7 +52,7 @@ limitations under the License. * Elm - https://github.com/elm/compiler -This source code can be found in editor/src/lang/solve.rs and compiler/src/solve.rs, and is licensed under the following terms: +This source code can be found in compiler/src/solve.rs, and is licensed under the following terms: Copyright 2012-present Evan Czaplicki @@ -119,23 +119,6 @@ limitations under the License. =========================================================== -* learn-wgpu - https://github.com/sotrh/learn-wgpu - -This source code can be found in editor/src/graphics/lowlevel/buffer.rs, editor/src/graphics/primitives/text.rs, and editor/src/graphics/primitives/lowlevel/vertex.rs, and is licensed under the following terms: - - -MIT License - -Copyright (c) 2020 Benjamin Hansen - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -=========================================================== - * pretty - https://github.com/Marwes/pretty.rs This source code can be found in vendor/pretty/ and is licensed under the following terms: @@ -186,21 +169,6 @@ limitations under the License. =========================================================== -* Ropey - https://github.com/cessen/ropey - -This source code can be found in editor/src/ui/text/lines.rs and is licensed under the following terms: - - -Copyright (c) 2017 Nathan Vegdahl - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -=========================================================== - * Zig - https://ziglang.org This source code can be found in compiler/builtins/bitcode/src/hash.zig, highlight/tests/peg_grammar.rs and highlight/src/highlight_parser.rs and is licensed under the following terms: diff --git a/crates/README.md b/crates/README.md index db2dc86d80..319bac9dec 100644 --- a/crates/README.md +++ b/crates/README.md @@ -8,10 +8,6 @@ You can use `cargo doc` to generate docs for a specific package; e.g. cargo doc --package roc_ast --open ``` -## `ast/` - `roc_ast` - -Code to represent the [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) as used by the editor. In contrast to the compiler, the types in this AST do not keep track of the location of the matching code in the source file. - ## `cli/` - `roc_cli` The `roc` binary that brings together all functionality in the Roc toolset. @@ -20,10 +16,6 @@ The `roc` binary that brings together all functionality in the Roc toolset. Provides shared code for cli tests and benchmarks. -## `code_markup/` - `roc_code_markup` - -A [markup language](https://en.wikipedia.org/wiki/Markup_language) to display Roc code in the editor. - ## `compiler/` Compiles `.roc` files and combines them with their platform into an executable binary. See [compiler/README.md](./compiler/README.md) for more information. @@ -76,10 +68,6 @@ Used for [roc-lang.org/builtins/Num](https://www.roc-lang.org/builtins/Num). Provides a binary that is only used for static build servers. -## `editor/` - `roc_editor` - -Roc's editor. See [README.md](./editor/README.md) for more information. - ## `error_macros/` - `roc_error_macros` Provides macros for consistent reporting of errors in Roc's rust code. @@ -90,7 +78,7 @@ The `roc_glue` crate generates code needed for platform hosts to communicate wit ## `highlight/` - `roc_highlight` -Provides syntax highlighting for the editor by transforming a string to markup nodes. +Provides syntax highlighting for the static site gen platform which is used by the tutorial. ## `linker/` - `roc_linker` diff --git a/crates/ast/Cargo.toml b/crates/ast/Cargo.toml deleted file mode 100644 index 56d27ae3ec..0000000000 --- a/crates/ast/Cargo.toml +++ /dev/null @@ -1,41 +0,0 @@ -[package] -name = "roc_ast" -description = "AST as used by the editor and (soon) docs. In contrast to the compiler, these types do not keep track of a location in a file." - -authors.workspace = true -edition.workspace = true -license.workspace = true -version.workspace = true - -[dependencies] -roc_builtins = { path = "../compiler/builtins" } -roc_can = { path = "../compiler/can" } -roc_checkmate = { path = "../compiler/checkmate" } -roc_collections = { path = "../compiler/collections" } -roc_error_macros = { path = "../error_macros" } -roc_load = { path = "../compiler/load" } -roc_module = { path = "../compiler/module" } -roc_packaging = { path = "../packaging" } -roc_parse = { path = "../compiler/parse" } -roc_problem = { path = "../compiler/problem" } -roc_region = { path = "../compiler/region" } -roc_reporting = { path = "../reporting" } -roc_solve = { path = "../compiler/solve" } -roc_solve_schema = { path = "../compiler/solve_schema" } -roc_target = { path = "../compiler/roc_target" } -roc_types = { path = "../compiler/types" } -roc_unify = { path = "../compiler/unify" } - -ven_graph = { path = "../vendor/pathfinding" } - -arrayvec.workspace = true -bumpalo.workspace = true -libc.workspace = true -page_size.workspace = true -snafu.workspace = true - -[dev-dependencies] -indoc.workspace = true - -[target.'cfg(windows)'.dependencies] -winapi.workspace = true diff --git a/crates/ast/src/ast_error.rs b/crates/ast/src/ast_error.rs deleted file mode 100644 index dd7dfe19a9..0000000000 --- a/crates/ast/src/ast_error.rs +++ /dev/null @@ -1,73 +0,0 @@ -use roc_module::{ident::Ident, module_err::ModuleError}; -use roc_parse::parser::SyntaxError; -use roc_region::all::{Loc, Region}; -use snafu::{Backtrace, Snafu}; - -use crate::lang::core::ast::ASTNodeId; - -#[derive(Debug, Snafu)] -#[snafu(visibility(pub))] -pub enum ASTError { - #[snafu(display( - "ASTNodeIdWithoutExprId: The expr_id_opt in ASTNode({:?}) was `None` but I was expecting `Some(ExprId)` .", - ast_node_id - ))] - ASTNodeIdWithoutExprId { - ast_node_id: ASTNodeId, - backtrace: Backtrace, - }, - #[snafu(display( - "UnexpectedASTNode: required a {} at this position, node was a {}.", - required_node_type, - encountered_node_type - ))] - UnexpectedASTNode { - required_node_type: String, - encountered_node_type: String, - backtrace: Backtrace, - }, - #[snafu(display( - "UnexpectedPattern2Variant: required a {} at this position, Pattern2 was a {}.", - required_pattern2, - encountered_pattern2, - ))] - UnexpectedPattern2Variant { - required_pattern2: String, - encountered_pattern2: String, - backtrace: Backtrace, - }, - #[snafu(display("IdentExistsError: {}", msg))] - IdentExistsError { msg: String }, - - WrapModuleError { - #[snafu(backtrace)] - source: ModuleError, - }, - - #[snafu(display("SyntaxError: {}", msg))] - SyntaxErrorNoBacktrace { msg: String }, -} - -pub type ASTResult = std::result::Result; - -impl From for ASTError { - fn from(module_err: ModuleError) -> Self { - Self::WrapModuleError { source: module_err } - } -} - -impl From<(Region, Loc)> for ASTError { - fn from(ident_exists_err: (Region, Loc)) -> Self { - Self::IdentExistsError { - msg: format!("{ident_exists_err:?}"), - } - } -} - -impl<'a> From> for ASTError { - fn from(syntax_err: SyntaxError) -> Self { - Self::SyntaxErrorNoBacktrace { - msg: format!("{syntax_err:?}"), - } - } -} diff --git a/crates/ast/src/builtin_aliases.rs b/crates/ast/src/builtin_aliases.rs deleted file mode 100644 index 1d70b867f8..0000000000 --- a/crates/ast/src/builtin_aliases.rs +++ /dev/null @@ -1,840 +0,0 @@ -use roc_collections::all::{default_hasher, ImMap, MutMap}; -use roc_module::ident::{Lowercase, TagName}; -use roc_module::symbol::Symbol; -use roc_region::all::{Loc, Region}; -use roc_types::subs::{VarId, Variable}; -use roc_types::types::{AliasKind, RecordField}; -use std::collections::HashMap; - -#[derive(Debug, Clone)] -pub struct SolvedLambdaSet(pub SolvedType); - -/// This is a fully solved type, with no Variables remaining in it. -#[derive(Debug, Clone)] -pub enum SolvedType { - /// A function. The types of its arguments, then the type of its return value. - #[allow(unused)] - Func(Vec, Box, Box), - /// Applying a type to some arguments (e.g. Map.Map String Int) - #[allow(unused)] - Apply(Symbol, Vec), - /// A bound type variable, e.g. `a` in `(a -> a)` - #[allow(unused)] - Rigid(Lowercase), - Flex(VarId), - #[allow(unused)] - Wildcard, - /// Inline type alias, e.g. `as List a` in `[Cons a (List a), Nil] as List a` - #[allow(unused)] - Record { - fields: Vec<(Lowercase, RecordField)>, - /// The row type variable in an open record, e.g. the `r` in `{ name: Str }r`. - /// This is None if it's a closed record annotation like `{ name: Str }`. - ext: Box, - }, - #[allow(unused)] - EmptyRecord, - TagUnion(Vec<(TagName, Vec)>, Box), - #[allow(unused)] - LambdaTag(Symbol, Vec), - #[allow(unused)] - FunctionOrTagUnion(TagName, Symbol, Box), - #[allow(unused)] - RecursiveTagUnion(VarId, Vec<(TagName, Vec)>, Box), - EmptyTagUnion, - /// A type from an Invalid module - #[allow(unused)] - Erroneous, - - Alias( - Symbol, - Vec, - Vec, - Box, - AliasKind, - ), - - #[allow(unused)] - HostExposedAlias { - name: Symbol, - arguments: Vec, - lambda_set_variables: Vec, - actual_var: VarId, - actual: Box, - }, - - /// A type error - #[allow(unused)] - Error, -} - -#[derive(Clone, Debug)] -pub struct BuiltinAlias { - pub region: Region, - pub vars: Vec>, - pub typ: SolvedType, - pub kind: AliasKind, -} - -#[derive(Debug, Clone, Default)] -pub struct FreeVars { - pub named_vars: ImMap, - pub unnamed_vars: ImMap, - pub wildcards: Vec, -} - -const NUM_BUILTIN_IMPORTS: usize = 8; - -/// These can be shared between definitions, they will get instantiated when converted to Type -const TVAR1: VarId = VarId::from_u32(1); - -pub fn aliases() -> MutMap { - let mut aliases = HashMap::with_capacity_and_hasher(NUM_BUILTIN_IMPORTS, default_hasher()); - - let mut add_alias = |symbol, alias| { - debug_assert!( - !aliases.contains_key(&symbol), - "Duplicate alias definition for {symbol:?}" - ); - - // TODO instead of using Region::zero for all of these, - // instead use the Region where they were defined in their - // source .roc files! This can give nicer error messages. - aliases.insert(symbol, alias); - }; - - // Int range : Num (Integer range) - add_alias( - Symbol::NUM_INT, - BuiltinAlias { - region: Region::zero(), - vars: vec![Loc::at(Region::zero(), "range".into())], - typ: int_alias_content(flex(TVAR1)), - kind: AliasKind::Structural, - }, - ); - - // Frac range : Num (FloatingPoint range) - add_alias( - Symbol::NUM_FRAC, - BuiltinAlias { - region: Region::zero(), - vars: vec![Loc::at(Region::zero(), "range".into())], - typ: frac_alias_content(flex(TVAR1)), - kind: AliasKind::Structural, - }, - ); - - // Num range := range - add_alias( - Symbol::NUM_NUM, - BuiltinAlias { - region: Region::zero(), - vars: vec![Loc::at(Region::zero(), "range".into())], - typ: num_alias_content(flex(TVAR1)), - kind: AliasKind::Opaque, - }, - ); - - // Integer range := range - add_alias( - Symbol::NUM_INTEGER, - BuiltinAlias { - region: Region::zero(), - vars: vec![Loc::at(Region::zero(), "range".into())], - typ: integer_alias_content(flex(TVAR1)), - kind: AliasKind::Opaque, - }, - ); - - // Natural := [] - add_alias( - Symbol::NUM_NATURAL, - BuiltinAlias { - region: Region::zero(), - vars: vec![], - typ: natural_alias_content(), - kind: AliasKind::Opaque, - }, - ); - - // Nat : Int Natural - add_alias( - Symbol::NUM_NAT, - BuiltinAlias { - region: Region::zero(), - vars: Vec::new(), - typ: nat_alias_content(), - kind: AliasKind::Structural, - }, - ); - - // Signed128 := [] - add_alias( - Symbol::NUM_SIGNED128, - BuiltinAlias { - region: Region::zero(), - vars: vec![], - typ: signed128_alias_content(), - kind: AliasKind::Opaque, - }, - ); - - // I128 : Int Signed128 - add_alias( - Symbol::NUM_I128, - BuiltinAlias { - region: Region::zero(), - vars: Vec::new(), - typ: i128_alias_content(), - kind: AliasKind::Structural, - }, - ); - - // U128 : Int Unsigned128 - add_alias( - Symbol::NUM_U128, - BuiltinAlias { - region: Region::zero(), - vars: Vec::new(), - typ: u128_alias_content(), - kind: AliasKind::Structural, - }, - ); - - // Signed64 := [] - add_alias( - Symbol::NUM_SIGNED64, - BuiltinAlias { - region: Region::zero(), - vars: vec![], - typ: signed64_alias_content(), - kind: AliasKind::Opaque, - }, - ); - - // I64 : Int Signed64 - add_alias( - Symbol::NUM_I64, - BuiltinAlias { - region: Region::zero(), - vars: Vec::new(), - typ: i64_alias_content(), - kind: AliasKind::Structural, - }, - ); - - // U64 : Int Unsigned64 - add_alias( - Symbol::NUM_U64, - BuiltinAlias { - region: Region::zero(), - vars: Vec::new(), - typ: u64_alias_content(), - kind: AliasKind::Structural, - }, - ); - - // Signed32 := [] - add_alias( - Symbol::NUM_SIGNED32, - BuiltinAlias { - region: Region::zero(), - vars: vec![], - typ: signed32_alias_content(), - kind: AliasKind::Opaque, - }, - ); - - // I32 : Int Signed32 - add_alias( - Symbol::NUM_I32, - BuiltinAlias { - region: Region::zero(), - vars: Vec::new(), - typ: i32_alias_content(), - kind: AliasKind::Structural, - }, - ); - - // U32 : Int Unsigned32 - add_alias( - Symbol::NUM_U32, - BuiltinAlias { - region: Region::zero(), - vars: Vec::new(), - typ: u32_alias_content(), - kind: AliasKind::Structural, - }, - ); - - // Signed16 := [] - add_alias( - Symbol::NUM_SIGNED16, - BuiltinAlias { - region: Region::zero(), - vars: vec![], - typ: signed16_alias_content(), - kind: AliasKind::Opaque, - }, - ); - - // I16 : Int Signed16 - add_alias( - Symbol::NUM_I16, - BuiltinAlias { - region: Region::zero(), - vars: Vec::new(), - typ: i16_alias_content(), - kind: AliasKind::Structural, - }, - ); - - // U16 : Int Unsigned16 - add_alias( - Symbol::NUM_U16, - BuiltinAlias { - region: Region::zero(), - vars: Vec::new(), - typ: u16_alias_content(), - kind: AliasKind::Structural, - }, - ); - - // Signed8 := [] - add_alias( - Symbol::NUM_SIGNED8, - BuiltinAlias { - region: Region::zero(), - vars: vec![], - typ: signed8_alias_content(), - kind: AliasKind::Opaque, - }, - ); - - // I8 : Int Signed8 - add_alias( - Symbol::NUM_I8, - BuiltinAlias { - region: Region::zero(), - vars: Vec::new(), - typ: i8_alias_content(), - kind: AliasKind::Structural, - }, - ); - - // U8 : Int Unsigned8 - add_alias( - Symbol::NUM_U8, - BuiltinAlias { - region: Region::zero(), - vars: Vec::new(), - typ: u8_alias_content(), - kind: AliasKind::Structural, - }, - ); - - // Decimal := [] - add_alias( - Symbol::NUM_DECIMAL, - BuiltinAlias { - region: Region::zero(), - vars: vec![], - typ: decimal_alias_content(), - kind: AliasKind::Opaque, - }, - ); - - // Binary64 := [] - add_alias( - Symbol::NUM_BINARY64, - BuiltinAlias { - region: Region::zero(), - vars: vec![], - typ: binary64_alias_content(), - kind: AliasKind::Opaque, - }, - ); - - // Binary32 := [] - add_alias( - Symbol::NUM_BINARY32, - BuiltinAlias { - region: Region::zero(), - vars: vec![], - typ: binary32_alias_content(), - kind: AliasKind::Opaque, - }, - ); - - // FloatingPoint range := range - add_alias( - Symbol::NUM_FLOATINGPOINT, - BuiltinAlias { - region: Region::zero(), - vars: vec![Loc::at(Region::zero(), "range".into())], - typ: floatingpoint_alias_content(flex(TVAR1)), - kind: AliasKind::Opaque, - }, - ); - - // Dec : Frac Decimal - add_alias( - Symbol::NUM_DEC, - BuiltinAlias { - region: Region::zero(), - vars: Vec::new(), - typ: dec_alias_content(), - kind: AliasKind::Structural, - }, - ); - - // F64 : Frac Binary64 - add_alias( - Symbol::NUM_F64, - BuiltinAlias { - region: Region::zero(), - vars: Vec::new(), - typ: f64_alias_content(), - kind: AliasKind::Structural, - }, - ); - - // F32 : Frac Binary32 - add_alias( - Symbol::NUM_F32, - BuiltinAlias { - region: Region::zero(), - vars: Vec::new(), - typ: f32_alias_content(), - kind: AliasKind::Structural, - }, - ); - - // Bool : [True, False] - add_alias( - Symbol::BOOL_BOOL, - BuiltinAlias { - region: Region::zero(), - vars: Vec::new(), - typ: bool_alias_content(), - kind: AliasKind::Structural, - }, - ); - - // Utf8ByteProblem : [InvalidStartByte, UnexpectedEndOfSequence, ExpectedContinuation, OverlongEncoding, CodepointTooLarge, EncodesSurrogateHalf] - add_alias( - Symbol::STR_UT8_BYTE_PROBLEM, - BuiltinAlias { - region: Region::zero(), - vars: Vec::new(), - typ: str_utf8_byte_problem_alias_content(), - kind: AliasKind::Structural, - }, - ); - - // Utf8Problem : { byteIndex : Nat, problem : Utf8ByteProblem } - add_alias( - Symbol::STR_UT8_PROBLEM, - BuiltinAlias { - region: Region::zero(), - vars: Vec::new(), - typ: str_utf8_byte_problem_alias_content(), - kind: AliasKind::Structural, - }, - ); - - aliases -} - -#[inline(always)] -pub fn flex(tvar: VarId) -> SolvedType { - SolvedType::Flex(tvar) -} - -#[inline(always)] -pub fn num_type(range: SolvedType) -> SolvedType { - SolvedType::Alias( - Symbol::NUM_NUM, - vec![(range.clone())], - vec![], - Box::new(num_alias_content(range)), - AliasKind::Opaque, - ) -} - -#[inline(always)] -fn num_alias_content(range: SolvedType) -> SolvedType { - range -} - -// FLOATING POINT - -#[inline(always)] -pub fn floatingpoint_type(range: SolvedType) -> SolvedType { - SolvedType::Alias( - Symbol::NUM_FLOATINGPOINT, - vec![(range.clone())], - vec![], - Box::new(floatingpoint_alias_content(range)), - AliasKind::Opaque, - ) -} - -#[inline(always)] -fn floatingpoint_alias_content(range: SolvedType) -> SolvedType { - range -} - -#[inline(always)] -fn frac_alias_content(range: SolvedType) -> SolvedType { - num_type(floatingpoint_type(range)) -} - -#[inline(always)] -fn f64_alias_content() -> SolvedType { - frac_alias_content(binary64_type()) -} - -#[inline(always)] -fn f32_alias_content() -> SolvedType { - frac_alias_content(binary32_type()) -} - -#[inline(always)] -fn nat_alias_content() -> SolvedType { - int_alias_content(natural_type()) -} - -#[inline(always)] -fn i128_alias_content() -> SolvedType { - int_alias_content(signed128_type()) -} - -#[inline(always)] -fn u128_alias_content() -> SolvedType { - int_alias_content(unsigned128_type()) -} - -#[inline(always)] -fn u64_alias_content() -> SolvedType { - int_alias_content(unsigned64_type()) -} - -#[inline(always)] -fn i64_alias_content() -> SolvedType { - int_alias_content(signed64_type()) -} - -#[inline(always)] -fn u32_alias_content() -> SolvedType { - int_alias_content(unsigned32_type()) -} - -#[inline(always)] -fn i32_alias_content() -> SolvedType { - int_alias_content(signed32_type()) -} - -#[inline(always)] -fn u16_alias_content() -> SolvedType { - int_alias_content(unsigned16_type()) -} - -#[inline(always)] -fn i16_alias_content() -> SolvedType { - int_alias_content(signed16_type()) -} - -#[inline(always)] -fn u8_alias_content() -> SolvedType { - int_alias_content(unsigned8_type()) -} - -// I8 - -#[inline(always)] -fn i8_alias_content() -> SolvedType { - int_alias_content(signed8_type()) -} - -#[inline(always)] -fn int_alias_content(range: SolvedType) -> SolvedType { - num_type(integer_type(range)) -} - -// INTEGER - -#[inline(always)] -pub fn integer_type(range: SolvedType) -> SolvedType { - SolvedType::Alias( - Symbol::NUM_INTEGER, - vec![(range.clone())], - vec![], - Box::new(integer_alias_content(range)), - AliasKind::Opaque, - ) -} - -#[inline(always)] -fn integer_alias_content(range: SolvedType) -> SolvedType { - range -} - -#[inline(always)] -pub fn binary64_type() -> SolvedType { - SolvedType::Alias( - Symbol::NUM_BINARY64, - vec![], - vec![], - Box::new(binary64_alias_content()), - AliasKind::Structural, - ) -} - -#[inline(always)] -pub fn binary64_alias_content() -> SolvedType { - SolvedType::EmptyTagUnion -} - -#[inline(always)] -pub fn binary32_type() -> SolvedType { - SolvedType::Alias( - Symbol::NUM_BINARY32, - vec![], - vec![], - Box::new(binary32_alias_content()), - AliasKind::Structural, - ) -} - -#[inline(always)] -fn binary32_alias_content() -> SolvedType { - SolvedType::EmptyTagUnion -} - -#[inline(always)] -pub fn natural_type() -> SolvedType { - SolvedType::Alias( - Symbol::NUM_NATURAL, - vec![], - vec![], - Box::new(natural_alias_content()), - AliasKind::Structural, - ) -} - -#[inline(always)] -fn natural_alias_content() -> SolvedType { - SolvedType::EmptyTagUnion -} - -#[inline(always)] -pub fn signed128_type() -> SolvedType { - SolvedType::Alias( - Symbol::NUM_SIGNED128, - vec![], - vec![], - Box::new(signed128_alias_content()), - AliasKind::Structural, - ) -} - -#[inline(always)] -fn signed128_alias_content() -> SolvedType { - SolvedType::EmptyTagUnion -} - -#[inline(always)] -pub fn signed64_type() -> SolvedType { - SolvedType::Alias( - Symbol::NUM_SIGNED64, - vec![], - vec![], - Box::new(signed64_alias_content()), - AliasKind::Structural, - ) -} - -#[inline(always)] -fn signed64_alias_content() -> SolvedType { - SolvedType::EmptyTagUnion -} - -#[inline(always)] -pub fn signed32_type() -> SolvedType { - SolvedType::Alias( - Symbol::NUM_SIGNED32, - vec![], - vec![], - Box::new(signed32_alias_content()), - AliasKind::Structural, - ) -} - -#[inline(always)] -fn signed32_alias_content() -> SolvedType { - SolvedType::EmptyTagUnion -} - -#[inline(always)] -pub fn signed16_type() -> SolvedType { - SolvedType::Alias( - Symbol::NUM_SIGNED16, - vec![], - vec![], - Box::new(signed16_alias_content()), - AliasKind::Structural, - ) -} - -#[inline(always)] -fn signed16_alias_content() -> SolvedType { - SolvedType::EmptyTagUnion -} - -#[inline(always)] -pub fn signed8_type() -> SolvedType { - SolvedType::Alias( - Symbol::NUM_SIGNED8, - vec![], - vec![], - Box::new(signed8_alias_content()), - AliasKind::Structural, - ) -} - -#[inline(always)] -fn signed8_alias_content() -> SolvedType { - SolvedType::EmptyTagUnion -} - -#[inline(always)] -pub fn unsigned128_type() -> SolvedType { - SolvedType::Alias( - Symbol::NUM_UNSIGNED128, - vec![], - vec![], - Box::new(unsigned128_alias_content()), - AliasKind::Structural, - ) -} - -#[inline(always)] -fn unsigned128_alias_content() -> SolvedType { - SolvedType::EmptyTagUnion -} - -#[inline(always)] -pub fn unsigned64_type() -> SolvedType { - SolvedType::Alias( - Symbol::NUM_UNSIGNED64, - vec![], - vec![], - Box::new(unsigned64_alias_content()), - AliasKind::Structural, - ) -} - -#[inline(always)] -fn unsigned64_alias_content() -> SolvedType { - SolvedType::EmptyTagUnion -} - -#[inline(always)] -pub fn unsigned32_type() -> SolvedType { - SolvedType::Alias( - Symbol::NUM_UNSIGNED32, - vec![], - vec![], - Box::new(unsigned32_alias_content()), - AliasKind::Structural, - ) -} - -#[inline(always)] -fn unsigned32_alias_content() -> SolvedType { - SolvedType::EmptyTagUnion -} - -#[inline(always)] -pub fn unsigned16_type() -> SolvedType { - SolvedType::Alias( - Symbol::NUM_UNSIGNED16, - vec![], - vec![], - Box::new(unsigned16_alias_content()), - AliasKind::Structural, - ) -} - -#[inline(always)] -fn unsigned16_alias_content() -> SolvedType { - SolvedType::EmptyTagUnion -} - -#[inline(always)] -pub fn unsigned8_type() -> SolvedType { - SolvedType::Alias( - Symbol::NUM_UNSIGNED8, - vec![], - vec![], - Box::new(unsigned8_alias_content()), - AliasKind::Structural, - ) -} - -#[inline(always)] -fn unsigned8_alias_content() -> SolvedType { - SolvedType::EmptyTagUnion -} - -#[inline(always)] -fn decimal_alias_content() -> SolvedType { - SolvedType::EmptyTagUnion -} - -#[inline(always)] -fn dec_alias_content() -> SolvedType { - frac_alias_content(decimal_type()) -} - -#[inline(always)] -pub fn decimal_type() -> SolvedType { - SolvedType::Alias( - Symbol::NUM_DECIMAL, - vec![], - vec![], - Box::new(decimal_alias_content()), - AliasKind::Structural, - ) -} - -fn bool_alias_content() -> SolvedType { - SolvedType::TagUnion( - vec![ - (TagName("False".into()), vec![]), - (TagName("True".into()), vec![]), - ], - Box::new(SolvedType::EmptyTagUnion), - ) -} - -#[inline(always)] -pub fn str_utf8_byte_problem_alias_content() -> SolvedType { - // 1. This must have the same values as the Zig struct Utf8ByteProblem in src/str.zig - // 2. This must be in alphabetical order - // - // [CodepointTooLarge, EncodesSurrogateHalf, OverlongEncoding, InvalidStartByte, UnexpectedEndOfSequence, ExpectedContinuation] - SolvedType::TagUnion( - vec![ - (TagName("CodepointTooLarge".into()), vec![]), - (TagName("EncodesSurrogateHalf".into()), vec![]), - (TagName("ExpectedContinuation".into()), vec![]), - (TagName("InvalidStartByte".into()), vec![]), - (TagName("OverlongEncoding".into()), vec![]), - (TagName("UnexpectedEndOfSequence".into()), vec![]), - ], - Box::new(SolvedType::EmptyTagUnion), - ) -} diff --git a/crates/ast/src/canonicalization/canonicalize.rs b/crates/ast/src/canonicalization/canonicalize.rs deleted file mode 100644 index 434676c915..0000000000 --- a/crates/ast/src/canonicalization/canonicalize.rs +++ /dev/null @@ -1,316 +0,0 @@ -use roc_collections::all::MutMap; -use roc_problem::can::Problem; -use roc_region::all::{Loc, Region}; -use roc_types::subs::Variable; - -use crate::{ - lang::{ - core::{ - def::def::References, - expr::{ - expr2::{Expr2, ExprId, WhenBranch}, - expr_to_expr2::expr_to_expr2, - output::Output, - record_field::RecordField, - }, - pattern::to_pattern2, - }, - env::Env, - scope::Scope, - }, - mem_pool::{pool_str::PoolStr, pool_vec::PoolVec, shallow_clone::ShallowClone}, -}; - -pub(crate) enum CanonicalizeRecordProblem { - #[allow(dead_code)] - InvalidOptionalValue { - field_name: PoolStr, - field_region: Region, - record_region: Region, - }, -} - -enum FieldVar { - VarAndExprId(Variable, ExprId), - OnlyVar(Variable), -} - -pub(crate) fn canonicalize_fields<'a>( - env: &mut Env<'a>, - scope: &mut Scope, - fields: &'a [Loc>>], -) -> Result<(PoolVec, Output), CanonicalizeRecordProblem> { - let mut can_fields: MutMap<&'a str, FieldVar> = MutMap::default(); - let mut output = Output::default(); - - for loc_field in fields.iter() { - match canonicalize_field(env, scope, &loc_field.value) { - Ok(can_field) => { - match can_field { - CanonicalField::LabelAndValue { - label, - value_expr, - value_output, - var, - } => { - let expr_id = env.pool.add(value_expr); - - let replaced = - can_fields.insert(label, FieldVar::VarAndExprId(var, expr_id)); - - if let Some(_old) = replaced { - // env.problems.push(Problem::DuplicateRecordFieldValue { - // field_name: label, - // field_region: loc_field.region, - // record_region: region, - // replaced_region: old.region, - // }); - todo!() - } - - output.references.union_mut(value_output.references); - } - CanonicalField::InvalidLabelOnly { label, var } => { - let replaced = can_fields.insert(label, FieldVar::OnlyVar(var)); - - if let Some(_old) = replaced { - todo!() - } - } - } - } - - Err(CanonicalizeFieldProblem::InvalidOptionalValue { - field_name: _, - field_region: _, - }) => { - // env.problem(Problem::InvalidOptionalValue { - // field_name: field_name.clone(), - // field_region, - // record_region: region, - // }); - // return Err(CanonicalizeRecordProblem::InvalidOptionalValue { - // field_name, - // field_region, - // record_region: region, - // }); - todo!() - } - } - } - - let pool_vec = PoolVec::with_capacity(can_fields.len() as u32, env.pool); - - for (node_id, (string, field_var)) in pool_vec.iter_node_ids().zip(can_fields.into_iter()) { - let name = PoolStr::new(string, env.pool); - - match field_var { - FieldVar::VarAndExprId(var, expr_id) => { - env.pool[node_id] = RecordField::LabeledValue(name, var, expr_id); - } - FieldVar::OnlyVar(var) => { - env.pool[node_id] = RecordField::InvalidLabelOnly(name, var); - } // TODO RecordField::LabelOnly - } - } - - Ok((pool_vec, output)) -} - -#[allow(dead_code)] -enum CanonicalizeFieldProblem { - InvalidOptionalValue { - field_name: PoolStr, - field_region: Region, - }, -} - -// TODO: the `value_output: Output` field takes _a lot_ of space! -#[allow(clippy::large_enum_variant)] -enum CanonicalField<'a> { - LabelAndValue { - label: &'a str, - value_expr: Expr2, - value_output: Output, - var: Variable, - }, - InvalidLabelOnly { - label: &'a str, - var: Variable, - }, // TODO make ValidLabelOnly -} - -fn canonicalize_field<'a>( - env: &mut Env<'a>, - scope: &mut Scope, - field: &'a roc_parse::ast::AssignedField<'a, roc_parse::ast::Expr<'a>>, -) -> Result, CanonicalizeFieldProblem> { - use roc_parse::ast::AssignedField::*; - - match field { - // Both a label and a value, e.g. `{ name: "blah" }` - RequiredValue(label, _, loc_expr) => { - let field_var = env.var_store.fresh(); - let (loc_can_expr, output) = - expr_to_expr2(env, scope, &loc_expr.value, loc_expr.region); - - match loc_can_expr { - Expr2::RuntimeError() => Ok(CanonicalField::InvalidLabelOnly { - label: label.value, - var: field_var, - }), - _ => Ok(CanonicalField::LabelAndValue { - label: label.value, - value_expr: loc_can_expr, - value_output: output, - var: field_var, - }), - } - } - - OptionalValue(label, _, loc_expr) => Err(CanonicalizeFieldProblem::InvalidOptionalValue { - field_name: PoolStr::new(label.value, env.pool), - field_region: Region::span_across(&label.region, &loc_expr.region), - }), - - // A label with no value, e.g. `{ name }` (this is sugar for { name: name }) - LabelOnly(label) => { - let field_var = env.var_store.fresh(); - // TODO return ValidLabel if label points to in scope variable - Ok(CanonicalField::InvalidLabelOnly { - label: label.value, - var: field_var, - }) - } - - SpaceBefore(sub_field, _) | SpaceAfter(sub_field, _) => { - canonicalize_field(env, scope, sub_field) - } - - Malformed(_string) => { - panic!("TODO canonicalize malformed record field"); - } - } -} - -#[inline(always)] -pub(crate) fn canonicalize_when_branch<'a>( - env: &mut Env<'a>, - scope: &mut Scope, - branch: &'a roc_parse::ast::WhenBranch<'a>, - output: &mut Output, -) -> (WhenBranch, References) { - let patterns = PoolVec::with_capacity(branch.patterns.len() as u32, env.pool); - - let original_scope = scope; - let mut scope = original_scope.shallow_clone(); - - // TODO report symbols not bound in all patterns - for (node_id, loc_pattern) in patterns.iter_node_ids().zip(branch.patterns.iter()) { - let (new_output, can_pattern) = to_pattern2( - env, - &mut scope, - roc_parse::pattern::PatternType::WhenBranch, - &loc_pattern.value, - loc_pattern.region, - ); - - output.union(new_output); - - env.set_region(node_id, loc_pattern.region); - env.pool[node_id] = can_pattern; - } - - let (value, mut branch_output) = - expr_to_expr2(env, &mut scope, &branch.value.value, branch.value.region); - let value_id = env.pool.add(value); - env.set_region(value_id, branch.value.region); - - let guard = match &branch.guard { - None => None, - Some(loc_expr) => { - let (can_guard, guard_branch_output) = - expr_to_expr2(env, &mut scope, &loc_expr.value, loc_expr.region); - - let expr_id = env.pool.add(can_guard); - env.set_region(expr_id, loc_expr.region); - - branch_output.union(guard_branch_output); - Some(expr_id) - } - }; - - // Now that we've collected all the references for this branch, check to see if - // any of the new idents it defined were unused. If any were, report it. - for (symbol, region) in scope.symbols() { - let symbol = symbol; - - if !output.references.has_lookup(symbol) - && !branch_output.references.has_lookup(symbol) - && !original_scope.contains_symbol(symbol) - { - env.problem(Problem::UnusedDef(symbol, region)); - } - } - - let references = branch_output.references.clone(); - output.union(branch_output); - - ( - WhenBranch { - patterns, - body: value_id, - guard, - }, - references, - ) -} - -pub(crate) fn canonicalize_lookup( - env: &mut Env<'_>, - scope: &mut Scope, - module_name: &str, - ident: &str, - region: Region, -) -> (Expr2, Output) { - use Expr2::*; - - let mut output = Output::default(); - let can_expr = if module_name.is_empty() { - // Since module_name was empty, this is an unqualified var. - // Look it up in scope! - match scope.lookup(&(*ident).into(), region) { - Ok(symbol) => { - output.references.lookups.insert(symbol); - - Var(symbol) - } - Err(problem) => { - env.problem(Problem::RuntimeError(problem)); - - RuntimeError() - } - } - } else { - // Since module_name was nonempty, this is a qualified var. - // Look it up in the env! - match env.qualified_lookup(module_name, ident, region) { - Ok(symbol) => { - output.references.lookups.insert(symbol); - - Var(symbol) - } - Err(problem) => { - // Either the module wasn't imported, or - // it was imported but it doesn't expose this ident. - env.problem(Problem::RuntimeError(problem)); - - RuntimeError() - } - } - }; - - // If it's valid, this ident should be in scope already. - - (can_expr, output) -} diff --git a/crates/ast/src/canonicalization/mod.rs b/crates/ast/src/canonicalization/mod.rs deleted file mode 100644 index fdbc754fc0..0000000000 --- a/crates/ast/src/canonicalization/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod canonicalize; -pub mod module; diff --git a/crates/ast/src/canonicalization/module.rs b/crates/ast/src/canonicalization/module.rs deleted file mode 100644 index ac11c1b50c..0000000000 --- a/crates/ast/src/canonicalization/module.rs +++ /dev/null @@ -1,41 +0,0 @@ -#![allow(clippy::all)] -#![allow(dead_code)] -#![allow(unused_imports)] -#![allow(unused_variables)] -use bumpalo::Bump; -use roc_collections::all::{default_hasher, ImMap, ImSet, MutMap, MutSet, SendMap}; -use roc_module::ident::Ident; -use roc_module::ident::Lowercase; -use roc_module::symbol::IdentIdsByModule; -use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol}; -use roc_parse::ast; -use roc_parse::pattern::PatternType; -use roc_problem::can::{Problem, RuntimeError}; -use roc_region::all::{Loc, Region}; -use roc_types::subs::{VarStore, Variable}; - -use crate::lang::core::def::def::canonicalize_defs; -use crate::lang::core::def::def::Def; -use crate::lang::core::def::def::{sort_can_defs, Declaration}; -use crate::lang::core::expr::expr2::Expr2; -use crate::lang::core::expr::output::Output; -use crate::lang::core::pattern::Pattern2; -use crate::lang::core::types::Alias; -use crate::lang::core::val_def::ValueDef; -use crate::lang::env::Env; -use crate::lang::scope::Scope; -use crate::mem_pool::pool::NodeId; -use crate::mem_pool::pool::Pool; -use crate::mem_pool::pool_vec::PoolVec; -use crate::mem_pool::shallow_clone::ShallowClone; - -pub struct ModuleOutput { - pub aliases: MutMap>, - pub rigid_variables: MutMap, - pub declarations: Vec, - pub exposed_imports: MutMap, - pub lookups: Vec<(Symbol, Variable, Region)>, - pub problems: Vec, - pub ident_ids: IdentIds, - pub references: MutSet, -} diff --git a/crates/ast/src/constrain.rs b/crates/ast/src/constrain.rs deleted file mode 100644 index c7467723bb..0000000000 --- a/crates/ast/src/constrain.rs +++ /dev/null @@ -1,2814 +0,0 @@ -use bumpalo::{collections::Vec as BumpVec, Bump}; - -use roc_can::expected::{Expected, PExpected}; -use roc_collections::all::{BumpMap, BumpMapDefault, HumanIndex, SendMap}; -use roc_module::{ - ident::{Lowercase, TagName}, - symbol::Symbol, -}; -use roc_region::all::Region; -use roc_types::{ - subs::Variable, - types::{self, AnnotationSource, IndexOrField, PReason, PatternCategory}, - types::{Category, Reason}, -}; - -use crate::{ - lang::{ - core::{ - expr::{ - expr2::{ClosureExtra, Expr2, ExprId, WhenBranch}, - record_field::RecordField, - }, - fun_def::FunctionDef, - pattern::{DestructType, Pattern2, PatternId, PatternState2, RecordDestruct}, - types::{Type2, TypeId}, - val_def::ValueDef, - }, - env::Env, - }, - mem_pool::{pool::Pool, pool_vec::PoolVec, shallow_clone::ShallowClone}, -}; - -/// A presence constraint is an additive constraint that defines the lower bound -/// of a type. For example, `Present(t1, IncludesTag(A, []))` means that the -/// type `t1` must contain at least the tag `A`. The additive nature of these -/// constraints makes them behaviorally different from unification-based constraints. -#[derive(Debug)] -pub enum PresenceConstraint<'a> { - IncludesTag(TagName, BumpVec<'a, Type2>), - IsOpen, - Pattern(Region, PatternCategory, PExpected), -} - -#[derive(Debug)] -pub enum Constraint<'a> { - Eq(Type2, Expected, Category, Region), - // Store(Type, Variable, &'static str, u32), - Lookup(Symbol, Expected, Region), - Pattern(Region, PatternCategory, Type2, PExpected), - And(BumpVec<'a, Constraint<'a>>), - Let(&'a LetConstraint<'a>), - // SaveTheEnvironment, - True, // Used for things that always unify, e.g. blanks and runtime errors - Present(Type2, PresenceConstraint<'a>), -} - -#[derive(Debug)] -pub struct LetConstraint<'a> { - pub rigid_vars: BumpVec<'a, Variable>, - pub flex_vars: BumpVec<'a, Variable>, - pub def_types: BumpMap, - pub defs_constraint: Constraint<'a>, - pub ret_constraint: Constraint<'a>, -} - -pub fn constrain_expr<'a>( - arena: &'a Bump, - env: &mut Env, - expr: &Expr2, - expected: Expected, - region: Region, -) -> Constraint<'a> { - use Constraint::*; - - match expr { - Expr2::Blank | Expr2::RuntimeError() | Expr2::InvalidLookup(_) => True, - Expr2::Str(_) => Eq(str_type(env.pool), expected, Category::Str, region), - Expr2::SmallStr(_) => Eq(str_type(env.pool), expected, Category::Str, region), - Expr2::Var(symbol) => Lookup(*symbol, expected, region), - Expr2::EmptyRecord => constrain_empty_record(expected, region), - Expr2::SmallInt { var, .. } | Expr2::I128 { var, .. } | Expr2::U128 { var, .. } => { - let mut flex_vars = BumpVec::with_capacity_in(1, arena); - - flex_vars.push(*var); - - let precision_var = env.var_store.fresh(); - - let range_type = Type2::Variable(precision_var); - - let range_type_id = env.pool.add(range_type); - - exists( - arena, - flex_vars, - Eq( - num_num(env.pool, range_type_id), - expected, - Category::Num, - region, - ), - ) - } - Expr2::Float { var, .. } => { - let mut flex_vars = BumpVec::with_capacity_in(1, arena); - - let mut and_constraints = BumpVec::with_capacity_in(2, arena); - - let num_type = Type2::Variable(*var); - - flex_vars.push(*var); - - let precision_var = env.var_store.fresh(); - - let range_type = Type2::Variable(precision_var); - - let range_type_id = env.pool.add(range_type); - - and_constraints.push(Eq( - num_type.shallow_clone(), - Expected::ForReason( - Reason::FloatLiteral, - num_float(env.pool, range_type_id), - region, - ), - Category::Int, - region, - )); - - and_constraints.push(Eq(num_type, expected, Category::Frac, region)); - - let defs_constraint = And(and_constraints); - - exists(arena, flex_vars, defs_constraint) - } - Expr2::List { - elem_var, elems, .. - } => { - let mut flex_vars = BumpVec::with_capacity_in(1, arena); - - flex_vars.push(*elem_var); - - if elems.is_empty() { - exists( - arena, - flex_vars, - Eq( - empty_list_type(env.pool, *elem_var), - expected, - Category::List, - region, - ), - ) - } else { - let mut constraints = BumpVec::with_capacity_in(1 + elems.len(), arena); - - let list_elem_type = Type2::Variable(*elem_var); - - let indexed_node_ids: Vec<(usize, ExprId)> = - elems.iter(env.pool).copied().enumerate().collect(); - - for (index, elem_node_id) in indexed_node_ids { - let elem_expr = env.pool.get(elem_node_id); - - let elem_expected = Expected::ForReason( - Reason::ElemInList { - index: HumanIndex::zero_based(index), - }, - list_elem_type.shallow_clone(), - region, - ); - - let constraint = constrain_expr(arena, env, elem_expr, elem_expected, region); - - constraints.push(constraint); - } - - constraints.push(Eq( - list_type(env.pool, list_elem_type), - expected, - Category::List, - region, - )); - - exists(arena, flex_vars, And(constraints)) - } - } - Expr2::Record { fields, record_var } => { - if fields.is_empty() { - constrain_empty_record(expected, region) - } else { - let field_types = PoolVec::with_capacity(fields.len() as u32, env.pool); - - let mut field_vars = BumpVec::with_capacity_in(fields.len(), arena); - - // Constraints need capacity for each field - // + 1 for the record itself + 1 for record var - let mut constraints = BumpVec::with_capacity_in(2 + fields.len(), arena); - - for (record_field_node_id, field_type_node_id) in - fields.iter_node_ids().zip(field_types.iter_node_ids()) - { - let record_field = env.pool.get(record_field_node_id); - - match record_field { - RecordField::LabeledValue(pool_str, var, node_id) => { - let expr = env.pool.get(*node_id); - - let (field_type, field_con) = constrain_field(arena, env, *var, expr); - - field_vars.push(*var); - - let field_type_id = env.pool.add(field_type); - - env.pool[field_type_node_id] = - (*pool_str, types::RecordField::Required(field_type_id)); - - constraints.push(field_con); - } - e => todo!("{:?}", e), - } - } - - let record_type = Type2::Record(field_types, env.pool.add(Type2::EmptyRec)); - - let record_con = Eq( - record_type, - expected.shallow_clone(), - Category::Record, - region, - ); - - constraints.push(record_con); - - // variable to store in the AST - let stored_con = Eq( - Type2::Variable(*record_var), - expected, - Category::Storage(std::file!(), std::line!()), - region, - ); - - field_vars.push(*record_var); - constraints.push(stored_con); - - exists(arena, field_vars, And(constraints)) - } - } - Expr2::Tag { - variant_var, - ext_var, - name, - arguments, - } => { - let tag_name = TagName(name.as_str(env.pool).into()); - - constrain_tag( - arena, - env, - expected, - region, - tag_name, - arguments, - *ext_var, - *variant_var, - ) - } - Expr2::Call { - args, - expr_var, - expr_id: expr_node_id, - closure_var, - fn_var, - called_via, - } => { - // The expression that evaluates to the function being called, e.g. `foo` in - // (foo) bar baz - let call_expr = env.pool.get(*expr_node_id); - - let opt_symbol = if let Expr2::Var(symbol) = call_expr { - Some(*symbol) - } else { - None - }; - - let fn_type = Type2::Variable(*fn_var); - let fn_region = region; - let fn_expected = Expected::NoExpectation(fn_type.shallow_clone()); - - let fn_reason = Reason::FnCall { - name: opt_symbol, - arity: args.len() as u8, - called_via: *called_via, - }; - - let fn_con = constrain_expr(arena, env, call_expr, fn_expected, region); - - // The function's return type - // TODO: don't use expr_var? - let ret_type = Type2::Variable(*expr_var); - - // type of values captured in the closure - let closure_type = Type2::Variable(*closure_var); - - // This will be used in the occurs check - let mut vars = BumpVec::with_capacity_in(2 + args.len(), arena); - - vars.push(*fn_var); - // TODO: don't use expr_var? - vars.push(*expr_var); - vars.push(*closure_var); - - let mut arg_types = BumpVec::with_capacity_in(args.len(), arena); - let mut arg_cons = BumpVec::with_capacity_in(args.len(), arena); - - for (index, arg_node_id) in args.iter_node_ids().enumerate() { - let (arg_var, arg) = env.pool.get(arg_node_id); - let arg_expr = env.pool.get(*arg); - - let region = region; - let arg_type = Type2::Variable(*arg_var); - - let reason = Reason::FnArg { - name: opt_symbol, - arg_index: HumanIndex::zero_based(index), - }; - - let expected_arg = Expected::ForReason(reason, arg_type.shallow_clone(), region); - - let arg_con = constrain_expr(arena, env, arg_expr, expected_arg, region); - - vars.push(*arg_var); - arg_types.push(arg_type); - arg_cons.push(arg_con); - } - - let expected_fn_type = Expected::ForReason( - fn_reason, - Type2::Function( - PoolVec::new(arg_types.into_iter(), env.pool), - env.pool.add(closure_type), - env.pool.add(ret_type.shallow_clone()), - ), - region, - ); - - let category = Category::CallResult(opt_symbol, *called_via); - - let mut and_constraints = BumpVec::with_capacity_in(4, arena); - - and_constraints.push(fn_con); - and_constraints.push(Eq(fn_type, expected_fn_type, category.clone(), fn_region)); - and_constraints.push(And(arg_cons)); - and_constraints.push(Eq(ret_type, expected, category, region)); - - exists(arena, vars, And(and_constraints)) - } - Expr2::Accessor { - function_var, - closure_var, - field, - record_var, - ext_var, - field_var, - } => { - let ext_var = *ext_var; - let ext_type = Type2::Variable(ext_var); - - let field_var = *field_var; - let field_type = Type2::Variable(field_var); - - let record_field = - types::RecordField::Demanded(env.pool.add(field_type.shallow_clone())); - - let record_type = Type2::Record( - PoolVec::new(vec![(*field, record_field)].into_iter(), env.pool), - env.pool.add(ext_type), - ); - - let category = Category::Accessor(IndexOrField::Field(field.as_str(env.pool).into())); - - let record_expected = Expected::NoExpectation(record_type.shallow_clone()); - let record_con = Eq( - Type2::Variable(*record_var), - record_expected, - category.clone(), - region, - ); - - let function_type = Type2::Function( - PoolVec::new(vec![record_type].into_iter(), env.pool), - env.pool.add(Type2::Variable(*closure_var)), - env.pool.add(field_type), - ); - - let mut flex_vars = BumpVec::with_capacity_in(5, arena); - - flex_vars.push(*record_var); - flex_vars.push(*function_var); - flex_vars.push(*closure_var); - flex_vars.push(field_var); - flex_vars.push(ext_var); - - let mut and_constraints = BumpVec::with_capacity_in(3, arena); - - and_constraints.push(Eq( - function_type.shallow_clone(), - expected, - category.clone(), - region, - )); - - and_constraints.push(Eq( - function_type, - Expected::NoExpectation(Type2::Variable(*function_var)), - category, - region, - )); - - and_constraints.push(record_con); - - exists(arena, flex_vars, And(and_constraints)) - } - Expr2::Access { - expr: expr_id, - field, - field_var, - record_var, - ext_var, - } => { - let ext_type = Type2::Variable(*ext_var); - - let field_type = Type2::Variable(*field_var); - - let record_field = - types::RecordField::Demanded(env.pool.add(field_type.shallow_clone())); - - let record_type = Type2::Record( - PoolVec::new(vec![(*field, record_field)].into_iter(), env.pool), - env.pool.add(ext_type), - ); - - let record_expected = Expected::NoExpectation(record_type); - - let category = Category::RecordAccess(field.as_str(env.pool).into()); - - let record_con = Eq( - Type2::Variable(*record_var), - record_expected.shallow_clone(), - category.clone(), - region, - ); - - let access_expr = env.pool.get(*expr_id); - - let constraint = constrain_expr(arena, env, access_expr, record_expected, region); - - let mut flex_vars = BumpVec::with_capacity_in(3, arena); - - flex_vars.push(*record_var); - flex_vars.push(*field_var); - flex_vars.push(*ext_var); - - let mut and_constraints = BumpVec::with_capacity_in(3, arena); - - and_constraints.push(constraint); - and_constraints.push(Eq(field_type, expected, category, region)); - and_constraints.push(record_con); - - exists(arena, flex_vars, And(and_constraints)) - } - Expr2::If { - cond_var, - expr_var, - branches, - final_else, - } => { - let expect_bool = |region| { - let bool_type = Type2::Variable(Variable::BOOL); - Expected::ForReason(Reason::IfCondition, bool_type, region) - }; - - let mut branch_cons = BumpVec::with_capacity_in(2 * branches.len() + 3, arena); - - // TODO why does this cond var exist? is it for error messages? - // let first_cond_region = branches[0].0.region; - let cond_var_is_bool_con = Eq( - Type2::Variable(*cond_var), - expect_bool(region), - Category::If, - region, - ); - - branch_cons.push(cond_var_is_bool_con); - - let final_else_expr = env.pool.get(*final_else); - - let mut flex_vars = BumpVec::with_capacity_in(2, arena); - - flex_vars.push(*cond_var); - flex_vars.push(*expr_var); - - match expected { - Expected::FromAnnotation(name, arity, ann_source, tipe) => { - let num_branches = branches.len() + 1; - - for (index, branch_id) in branches.iter_node_ids().enumerate() { - let (cond_id, body_id) = env.pool.get(branch_id); - - let cond = env.pool.get(*cond_id); - let body = env.pool.get(*body_id); - - let cond_con = - constrain_expr(arena, env, cond, expect_bool(region), region); - - let then_con = constrain_expr( - arena, - env, - body, - Expected::FromAnnotation( - name.clone(), - arity, - AnnotationSource::TypedIfBranch { - index: HumanIndex::zero_based(index), - num_branches, - region: ann_source.region(), - }, - tipe.shallow_clone(), - ), - region, - ); - - branch_cons.push(cond_con); - branch_cons.push(then_con); - } - - let else_con = constrain_expr( - arena, - env, - final_else_expr, - Expected::FromAnnotation( - name, - arity, - AnnotationSource::TypedIfBranch { - index: HumanIndex::zero_based(branches.len()), - num_branches, - region: ann_source.region(), - }, - tipe.shallow_clone(), - ), - region, - ); - - let ast_con = Eq( - Type2::Variable(*expr_var), - Expected::NoExpectation(tipe), - Category::Storage(std::file!(), std::line!()), - region, - ); - - branch_cons.push(ast_con); - branch_cons.push(else_con); - - exists(arena, flex_vars, And(branch_cons)) - } - _ => { - for (index, branch_id) in branches.iter_node_ids().enumerate() { - let (cond_id, body_id) = env.pool.get(branch_id); - - let cond = env.pool.get(*cond_id); - let body = env.pool.get(*body_id); - - let cond_con = - constrain_expr(arena, env, cond, expect_bool(region), region); - - let then_con = constrain_expr( - arena, - env, - body, - Expected::ForReason( - Reason::IfBranch { - index: HumanIndex::zero_based(index), - total_branches: branches.len(), - }, - Type2::Variable(*expr_var), - // should be from body - region, - ), - region, - ); - - branch_cons.push(cond_con); - branch_cons.push(then_con); - } - - let else_con = constrain_expr( - arena, - env, - final_else_expr, - Expected::ForReason( - Reason::IfBranch { - index: HumanIndex::zero_based(branches.len()), - total_branches: branches.len() + 1, - }, - Type2::Variable(*expr_var), - // should come from final_else - region, - ), - region, - ); - - branch_cons.push(Eq( - Type2::Variable(*expr_var), - expected, - Category::Storage(std::file!(), std::line!()), - region, - )); - - branch_cons.push(else_con); - - exists(arena, flex_vars, And(branch_cons)) - } - } - } - Expr2::When { - cond_var, - expr_var, - cond: cond_id, - branches, - } => { - // Infer the condition expression's type. - let cond_type = Type2::Variable(*cond_var); - - let cond = env.pool.get(*cond_id); - - let expr_con = constrain_expr( - arena, - env, - cond, - Expected::NoExpectation(cond_type.shallow_clone()), - region, - ); - - let mut constraints = BumpVec::with_capacity_in(branches.len() + 1, arena); - - constraints.push(expr_con); - - let mut flex_vars = BumpVec::with_capacity_in(2, arena); - - flex_vars.push(*cond_var); - flex_vars.push(*expr_var); - - match &expected { - Expected::FromAnnotation(name, arity, ann_source, _typ) => { - // NOTE deviation from elm. - // - // in elm, `_typ` is used, but because we have this `expr_var` too - // and need to constrain it, this is what works and gives better error messages - let typ = Type2::Variable(*expr_var); - - for (index, when_branch_id) in branches.iter_node_ids().enumerate() { - let when_branch = env.pool.get(when_branch_id); - - // let pattern_region = Region::across_all( - // when_branch.patterns.iter(env.pool).map(|v| &v.region), - // ); - - let pattern_expected = |sub_pattern, sub_region| { - PExpected::ForReason( - PReason::WhenMatch { - index: HumanIndex::zero_based(index), - sub_pattern, - }, - cond_type.shallow_clone(), - sub_region, - ) - }; - - let branch_con = constrain_when_branch( - arena, - env, - // TODO: when_branch.value.region, - region, - when_branch, - pattern_expected, - Expected::FromAnnotation( - name.clone(), - *arity, - AnnotationSource::TypedWhenBranch { - index: HumanIndex::zero_based(index), - region: ann_source.region(), - }, - typ.shallow_clone(), - ), - ); - - constraints.push(branch_con); - } - - constraints.push(Eq(typ, expected, Category::When, region)); - - return exists(arena, flex_vars, And(constraints)); - } - - _ => { - let branch_type = Type2::Variable(*expr_var); - let mut branch_cons = BumpVec::with_capacity_in(branches.len(), arena); - - for (index, when_branch_id) in branches.iter_node_ids().enumerate() { - let when_branch = env.pool.get(when_branch_id); - - // let pattern_region = - // Region::across_all(when_branch.patterns.iter().map(|v| &v.region)); - - let pattern_expected = |sub_pattern, sub_region| { - PExpected::ForReason( - PReason::WhenMatch { - index: HumanIndex::zero_based(index), - sub_pattern, - }, - cond_type.shallow_clone(), - sub_region, - ) - }; - - let branch_con = constrain_when_branch( - arena, - env, - region, - when_branch, - pattern_expected, - Expected::ForReason( - Reason::WhenBranch { - index: HumanIndex::zero_based(index), - }, - branch_type.shallow_clone(), - // TODO: when_branch.value.region, - region, - ), - ); - - branch_cons.push(branch_con); - } - - let mut and_constraints = BumpVec::with_capacity_in(2, arena); - - and_constraints.push(And(branch_cons)); - and_constraints.push(Eq(branch_type, expected, Category::When, region)); - - constraints.push(And(and_constraints)); - } - } - - // exhautiveness checking happens when converting to mono::Expr - exists(arena, flex_vars, And(constraints)) - } - Expr2::LetValue { - def_id, - body_id, - body_var, - } => { - let value_def = env.pool.get(*def_id); - let body = env.pool.get(*body_id); - - let body_con = constrain_expr(arena, env, body, expected.shallow_clone(), region); - - match value_def { - ValueDef::WithAnnotation { .. } => todo!("implement {:?}", value_def), - ValueDef::NoAnnotation { - pattern_id, - expr_id, - expr_var, - } => { - let pattern = env.pool.get(*pattern_id); - - let mut flex_vars = BumpVec::with_capacity_in(1, arena); - flex_vars.push(*body_var); - - let expr_type = Type2::Variable(*expr_var); - - let pattern_expected = PExpected::NoExpectation(expr_type.shallow_clone()); - let mut state = PatternState2 { - headers: BumpMap::new_in(arena), - vars: BumpVec::with_capacity_in(1, arena), - constraints: BumpVec::with_capacity_in(1, arena), - }; - - constrain_pattern( - arena, - env, - pattern, - region, - pattern_expected, - &mut state, - false, - ); - state.vars.push(*expr_var); - - let def_expr = env.pool.get(*expr_id); - - let constrained_def = Let(arena.alloc(LetConstraint { - rigid_vars: BumpVec::new_in(arena), - flex_vars: state.vars, - def_types: state.headers, - defs_constraint: Let(arena.alloc(LetConstraint { - rigid_vars: BumpVec::new_in(arena), // always empty - flex_vars: BumpVec::new_in(arena), // empty, because our functions have no arguments - def_types: BumpMap::new_in(arena), // empty, because our functions have no arguments! - defs_constraint: And(state.constraints), - ret_constraint: constrain_expr( - arena, - env, - def_expr, - Expected::NoExpectation(expr_type), - region, - ), - })), - ret_constraint: body_con, - })); - - let mut and_constraints = BumpVec::with_capacity_in(2, arena); - - and_constraints.push(constrained_def); - and_constraints.push(Eq( - Type2::Variable(*body_var), - expected, - Category::Storage(std::file!(), std::line!()), - // TODO: needs to be ret region - region, - )); - - exists(arena, flex_vars, And(and_constraints)) - } - } - } - // In an expression like - // id = \x -> x - // - // id 1 - // The `def_id` refers to the definition `id = \x -> x`, - // and the body refers to `id 1`. - Expr2::LetFunction { - def_id, - body_id, - body_var: _, - } => { - let body = env.pool.get(*body_id); - let body_con = constrain_expr(arena, env, body, expected.shallow_clone(), region); - - let function_def = env.pool.get(*def_id); - - let (name, arguments, body_id, rigid_vars, args_constrs) = match function_def { - FunctionDef::WithAnnotation { - name, - arguments, - body_id, - rigids, - return_type: _, - } => { - // The annotation gives us arguments with proper Type2s, but the constraints we - // generate below args bound to type variables. Create fresh ones and bind them - // to the types we already know. - let mut args_constrs = BumpVec::with_capacity_in(arguments.len(), arena); - let args_vars = PoolVec::with_capacity(arguments.len() as u32, env.pool); - for (arg_ty_node_id, arg_var_node_id) in - arguments.iter_node_ids().zip(args_vars.iter_node_ids()) - { - let (ty, pattern) = env.pool.get(arg_ty_node_id); - let arg_var = env.var_store.fresh(); - let ty = env.pool.get(*ty); - args_constrs.push(Eq( - Type2::Variable(arg_var), - Expected::NoExpectation(ty.shallow_clone()), - Category::Storage(std::file!(), std::line!()), - // TODO: should be the actual region of the argument - region, - )); - env.pool[arg_var_node_id] = (arg_var, *pattern); - } - - let rigids = env.pool.get(*rigids); - let rigid_vars: BumpVec = - BumpVec::from_iter_in(rigids.names.iter(env.pool).map(|&(_, v)| v), arena); - - (name, args_vars, body_id, rigid_vars, args_constrs) - } - FunctionDef::NoAnnotation { - name, - arguments, - body_id, - return_var: _, - } => { - ( - name, - arguments.shallow_clone(), - body_id, - BumpVec::new_in(arena), // The function is unannotated, so there are no rigid type vars - BumpVec::new_in(arena), // No extra constraints to generate for arguments - ) - } - }; - - // A function definition is equivalent to a named value definition, where the - // value is a closure. So, we create a closure definition in correspondence - // with the function definition, generate type constraints for it, and demand - // that type of the function is just the type of the resolved closure. - let fn_var = env.var_store.fresh(); - let fn_ty = Type2::Variable(fn_var); - - let extra = ClosureExtra { - return_type: env.var_store.fresh(), - captured_symbols: PoolVec::empty(env.pool), - closure_type: env.var_store.fresh(), - closure_ext_var: env.var_store.fresh(), - }; - let clos = Expr2::Closure { - args: arguments.shallow_clone(), - uniq_symbol: *name, - body_id: *body_id, - function_type: env.var_store.fresh(), - extra: env.pool.add(extra), - recursive: roc_can::expr::Recursive::Recursive, - }; - let clos_con = constrain_expr( - arena, - env, - &clos, - Expected::NoExpectation(fn_ty.shallow_clone()), - region, - ); - - // This is the `foo` part in `foo = \...`. We want to bind the name of the - // function with its type, whose constraints we generated above. - let mut def_pattern_state = PatternState2 { - headers: BumpMap::new_in(arena), - vars: BumpVec::new_in(arena), - constraints: args_constrs, - }; - def_pattern_state.headers.insert(*name, fn_ty); - def_pattern_state.vars.push(fn_var); - - Let(arena.alloc(LetConstraint { - rigid_vars, - flex_vars: def_pattern_state.vars, - def_types: def_pattern_state.headers, // Binding function name -> its type - defs_constraint: Let(arena.alloc(LetConstraint { - rigid_vars: BumpVec::new_in(arena), // always empty - flex_vars: BumpVec::new_in(arena), // empty, because our functions have no arguments - def_types: BumpMap::new_in(arena), // empty, because our functions have no arguments - defs_constraint: And(def_pattern_state.constraints), - ret_constraint: clos_con, - })), - ret_constraint: body_con, - })) - } - Expr2::Update { - symbol, - updates, - ext_var, - record_var, - } => { - let field_types = PoolVec::with_capacity(updates.len() as u32, env.pool); - let mut flex_vars = BumpVec::with_capacity_in(updates.len() + 2, arena); - let mut cons = BumpVec::with_capacity_in(updates.len() + 1, arena); - let mut record_key_updates = SendMap::default(); - - for (record_field_id, field_type_node_id) in - updates.iter_node_ids().zip(field_types.iter_node_ids()) - { - let record_field = env.pool.get(record_field_id); - - match record_field { - RecordField::LabeledValue(pool_str, var, node_id) => { - let expr = env.pool.get(*node_id); - - let (field_type, field_con) = constrain_field_update( - arena, - env, - *var, - pool_str.as_str(env.pool).into(), - expr, - ); - - let field_type_id = env.pool.add(field_type); - - env.pool[field_type_node_id] = - (*pool_str, types::RecordField::Required(field_type_id)); - - record_key_updates.insert(pool_str.as_str(env.pool).into(), Region::zero()); - - flex_vars.push(*var); - cons.push(field_con); - } - e => todo!("{:?}", e), - } - } - - let fields_type = Type2::Record(field_types, env.pool.add(Type2::Variable(*ext_var))); - let record_type = Type2::Variable(*record_var); - - // NOTE from elm compiler: fields_type is separate so that Error propagates better - let fields_con = Eq( - record_type.shallow_clone(), - Expected::NoExpectation(fields_type), - Category::Record, - region, - ); - let record_con = Eq( - record_type.shallow_clone(), - expected, - Category::Record, - region, - ); - - flex_vars.push(*record_var); - flex_vars.push(*ext_var); - - let con = Lookup( - *symbol, - Expected::ForReason( - Reason::RecordUpdateKeys(*symbol, record_key_updates), - record_type, - region, - ), - region, - ); - - // ensure constraints are solved in this order, gives better errors - cons.insert(0, fields_con); - cons.insert(1, con); - cons.insert(2, record_con); - - exists(arena, flex_vars, And(cons)) - } - - Expr2::RunLowLevel { op, args, ret_var } => { - // This is a modified version of what we do for function calls. - - // The operation's return type - let ret_type = Type2::Variable(*ret_var); - - // This will be used in the occurs check - let mut vars = BumpVec::with_capacity_in(1 + args.len(), arena); - - vars.push(*ret_var); - - let mut arg_types = BumpVec::with_capacity_in(args.len(), arena); - let mut arg_cons = BumpVec::with_capacity_in(args.len(), arena); - - for (index, node_id) in args.iter_node_ids().enumerate() { - let (arg_var, arg_id) = env.pool.get(node_id); - - vars.push(*arg_var); - - let arg_type = Type2::Variable(*arg_var); - - let reason = Reason::LowLevelOpArg { - op: *op, - arg_index: HumanIndex::zero_based(index), - }; - let expected_arg = - Expected::ForReason(reason, arg_type.shallow_clone(), Region::zero()); - let arg = env.pool.get(*arg_id); - - let arg_con = constrain_expr(arena, env, arg, expected_arg, Region::zero()); - - arg_types.push(arg_type); - arg_cons.push(arg_con); - } - - let category = Category::LowLevelOpResult(*op); - - let mut and_constraints = BumpVec::with_capacity_in(2, arena); - - and_constraints.push(And(arg_cons)); - and_constraints.push(Eq(ret_type, expected, category, region)); - - exists(arena, vars, And(and_constraints)) - } - Expr2::Closure { - args, - uniq_symbol, - body_id, - function_type: fn_var, - extra, - .. - } => { - // NOTE defs are treated somewhere else! - let body = env.pool.get(*body_id); - - let ClosureExtra { - captured_symbols, - return_type: ret_var, - closure_type: closure_var, - closure_ext_var, - } = env.pool.get(*extra); - - let closure_type = Type2::Variable(*closure_var); - let return_type = Type2::Variable(*ret_var); - - let (mut vars, pattern_state, function_type) = - constrain_untyped_args(arena, env, args, closure_type, return_type.shallow_clone()); - - vars.push(*ret_var); - vars.push(*closure_var); - vars.push(*closure_ext_var); - vars.push(*fn_var); - - let expected_body_type = Expected::NoExpectation(return_type); - // Region here should come from body expr - let ret_constraint = constrain_expr(arena, env, body, expected_body_type, region); - - let captured_symbols_as_vec = captured_symbols - .iter(env.pool) - .copied() - .collect::>(); - - // make sure the captured symbols are sorted! - debug_assert_eq!(captured_symbols_as_vec, { - let mut copy: Vec<(Symbol, Variable)> = captured_symbols_as_vec.clone(); - - copy.sort(); - - copy - }); - - let closure_constraint = constrain_closure_size( - arena, - env, - *uniq_symbol, - region, - captured_symbols, - *closure_var, - *closure_ext_var, - &mut vars, - ); - - let mut and_constraints = BumpVec::with_capacity_in(4, arena); - - and_constraints.push(Let(arena.alloc(LetConstraint { - rigid_vars: BumpVec::new_in(arena), - flex_vars: pattern_state.vars, - def_types: pattern_state.headers, - defs_constraint: And(pattern_state.constraints), - ret_constraint, - }))); - - // "the closure's type is equal to expected type" - and_constraints.push(Eq( - function_type.shallow_clone(), - expected, - Category::Lambda, - region, - )); - - // "fn_var is equal to the closure's type" - fn_var is used in code gen - and_constraints.push(Eq( - Type2::Variable(*fn_var), - Expected::NoExpectation(function_type), - Category::Storage(std::file!(), std::line!()), - region, - )); - - and_constraints.push(closure_constraint); - - exists(arena, vars, And(and_constraints)) - } - Expr2::LetRec { .. } => todo!(), - } -} - -fn exists<'a>( - arena: &'a Bump, - flex_vars: BumpVec<'a, Variable>, - defs_constraint: Constraint<'a>, -) -> Constraint<'a> { - Constraint::Let(arena.alloc(LetConstraint { - rigid_vars: BumpVec::new_in(arena), - flex_vars, - def_types: BumpMap::new_in(arena), - defs_constraint, - ret_constraint: Constraint::True, - })) -} - -#[allow(clippy::too_many_arguments)] -fn constrain_tag<'a>( - arena: &'a Bump, - env: &mut Env, - expected: Expected, - region: Region, - tag_name: TagName, - arguments: &PoolVec<(Variable, ExprId)>, - ext_var: Variable, - variant_var: Variable, -) -> Constraint<'a> { - use Constraint::*; - - let mut flex_vars = BumpVec::with_capacity_in(arguments.len(), arena); - let types = PoolVec::with_capacity(arguments.len() as u32, env.pool); - let mut arg_cons = BumpVec::with_capacity_in(arguments.len(), arena); - - for (argument_node_id, type_node_id) in arguments.iter_node_ids().zip(types.iter_node_ids()) { - let (var, expr_node_id) = env.pool.get(argument_node_id); - - let argument_expr = env.pool.get(*expr_node_id); - - let arg_con = constrain_expr( - arena, - env, - argument_expr, - Expected::NoExpectation(Type2::Variable(*var)), - region, - ); - - arg_cons.push(arg_con); - flex_vars.push(*var); - - env.pool[type_node_id] = Type2::Variable(*var); - } - - let union_con = Eq( - Type2::TagUnion( - PoolVec::new(std::iter::once((tag_name.clone(), types)), env.pool), - env.pool.add(Type2::Variable(ext_var)), - ), - expected.shallow_clone(), - Category::TagApply { - tag_name, - args_count: arguments.len(), - }, - region, - ); - - let ast_con = Eq( - Type2::Variable(variant_var), - expected, - Category::Storage(std::file!(), std::line!()), - region, - ); - - flex_vars.push(variant_var); - flex_vars.push(ext_var); - - arg_cons.push(union_con); - arg_cons.push(ast_con); - - exists(arena, flex_vars, And(arg_cons)) -} - -fn constrain_field<'a>( - arena: &'a Bump, - env: &mut Env, - field_var: Variable, - expr: &Expr2, -) -> (Type2, Constraint<'a>) { - let field_type = Type2::Variable(field_var); - let field_expected = Expected::NoExpectation(field_type.shallow_clone()); - let constraint = constrain_expr(arena, env, expr, field_expected, Region::zero()); - - (field_type, constraint) -} - -#[inline(always)] -fn constrain_field_update<'a>( - arena: &'a Bump, - env: &mut Env, - field_var: Variable, - field: Lowercase, - expr: &Expr2, -) -> (Type2, Constraint<'a>) { - let field_type = Type2::Variable(field_var); - let reason = Reason::RecordUpdateValue(field); - let field_expected = Expected::ForReason(reason, field_type.shallow_clone(), Region::zero()); - let con = constrain_expr(arena, env, expr, field_expected, Region::zero()); - - (field_type, con) -} - -fn constrain_empty_record<'a>(expected: Expected, region: Region) -> Constraint<'a> { - Constraint::Eq(Type2::EmptyRec, expected, Category::Record, region) -} - -#[inline(always)] -fn constrain_when_branch<'a>( - arena: &'a Bump, - env: &mut Env, - region: Region, - when_branch: &WhenBranch, - pattern_expected: impl Fn(HumanIndex, Region) -> PExpected, - expr_expected: Expected, -) -> Constraint<'a> { - let when_expr = env.pool.get(when_branch.body); - - let ret_constraint = constrain_expr(arena, env, when_expr, expr_expected, region); - - let mut state = PatternState2 { - headers: BumpMap::new_in(arena), - vars: BumpVec::with_capacity_in(1, arena), - constraints: BumpVec::with_capacity_in(1, arena), - }; - - // TODO investigate for error messages, is it better to unify all branches with a variable, - // then unify that variable with the expectation? - for (sub_pattern, pattern_id) in when_branch.patterns.iter_node_ids().enumerate() { - let pattern = env.pool.get(pattern_id); - - let pattern_expected = pattern_expected( - HumanIndex::zero_based(sub_pattern), - // TODO: use the proper subpattern region. Not available to us right now. - region, - ); - - constrain_pattern( - arena, - env, - pattern, - // loc_pattern.region, - region, - pattern_expected, - &mut state, - true, - ); - } - - if let Some(guard_id) = &when_branch.guard { - let guard = env.pool.get(*guard_id); - - let guard_constraint = constrain_expr( - arena, - env, - guard, - Expected::ForReason( - Reason::WhenGuard, - Type2::Variable(Variable::BOOL), - // TODO: loc_guard.region, - region, - ), - region, - ); - - // must introduce the headers from the pattern before constraining the guard - Constraint::Let(arena.alloc(LetConstraint { - rigid_vars: BumpVec::new_in(arena), - flex_vars: state.vars, - def_types: state.headers, - defs_constraint: Constraint::And(state.constraints), - ret_constraint: Constraint::Let(arena.alloc(LetConstraint { - rigid_vars: BumpVec::new_in(arena), - flex_vars: BumpVec::new_in(arena), - def_types: BumpMap::new_in(arena), - defs_constraint: guard_constraint, - ret_constraint, - })), - })) - } else { - Constraint::Let(arena.alloc(LetConstraint { - rigid_vars: BumpVec::new_in(arena), - flex_vars: state.vars, - def_types: state.headers, - defs_constraint: Constraint::And(state.constraints), - ret_constraint, - })) - } -} - -fn make_pattern_constraint( - region: Region, - category: PatternCategory, - actual: Type2, - expected: PExpected, - presence_con: bool, -) -> Constraint<'static> { - if presence_con { - Constraint::Present( - actual, - PresenceConstraint::Pattern(region, category, expected), - ) - } else { - Constraint::Pattern(region, category, actual, expected) - } -} - -/// This accepts PatternState (rather than returning it) so that the caller can -/// initialize the Vecs in PatternState using with_capacity -/// based on its knowledge of their lengths. -pub fn constrain_pattern<'a>( - arena: &'a Bump, - env: &mut Env, - pattern: &Pattern2, - region: Region, - expected: PExpected, - state: &mut PatternState2<'a>, - destruct_position: bool, -) { - use Pattern2::*; - - match pattern { - Underscore if destruct_position => { - // This is an underscore in a position where we destruct a variable, - // like a when expression: - // when x is - // A -> "" - // _ -> "" - // so, we know that "x" (in this case, a tag union) must be open. - state.constraints.push(Constraint::Present( - expected.get_type(), - PresenceConstraint::IsOpen, - )); - } - - Underscore | UnsupportedPattern(_) | MalformedPattern(_, _) | Shadowed { .. } => { - // Neither the _ pattern nor erroneous ones add any constraints. - } - - Identifier(symbol) => { - if destruct_position { - state.constraints.push(Constraint::Present( - expected.get_type_ref().shallow_clone(), - PresenceConstraint::IsOpen, - )); - } - state.headers.insert(*symbol, expected.get_type()); - } - - NumLiteral(var, _) => { - state.vars.push(*var); - - let type_id = env.pool.add(Type2::Variable(*var)); - - state.constraints.push(Constraint::Pattern( - region, - PatternCategory::Num, - num_num(env.pool, type_id), - expected, - )); - } - - IntLiteral(_int_val) => { - let precision_var = env.var_store.fresh(); - - let range = env.add(Type2::Variable(precision_var), region); - - state.constraints.push(Constraint::Pattern( - region, - PatternCategory::Int, - num_int(env.pool, range), - expected, - )); - } - - FloatLiteral(_float_val) => { - let precision_var = env.var_store.fresh(); - - let range = env.add(Type2::Variable(precision_var), region); - - state.constraints.push(Constraint::Pattern( - region, - PatternCategory::Float, - num_float(env.pool, range), - expected, - )); - } - - StrLiteral(_) => { - state.constraints.push(Constraint::Pattern( - region, - PatternCategory::Str, - str_type(env.pool), - expected, - )); - } - - CharacterLiteral(_) => { - state.constraints.push(Constraint::Pattern( - region, - PatternCategory::Character, - num_unsigned32(env.pool), - expected, - )); - } - - RecordDestructure { - whole_var, - ext_var, - destructs, - } => { - state.vars.push(*whole_var); - state.vars.push(*ext_var); - let ext_type = Type2::Variable(*ext_var); - - let mut field_types = Vec::new(); - - for destruct_id in destructs.iter_node_ids() { - let RecordDestruct { - var, - label, - symbol, - typ, - } = env.pool.get(destruct_id); - - let pat_type = Type2::Variable(*var); - let expected = PExpected::NoExpectation(pat_type.shallow_clone()); - - if !state.headers.contains_key(symbol) { - state.headers.insert(*symbol, pat_type.shallow_clone()); - } - - let destruct_type = env.pool.get(*typ); - - let field_type = match destruct_type { - DestructType::Guard(guard_var, guard_id) => { - state.constraints.push(make_pattern_constraint( - region, - PatternCategory::PatternGuard, - Type2::Variable(*guard_var), - PExpected::ForReason( - PReason::PatternGuard, - pat_type.shallow_clone(), - // TODO: region should be from guard_id - region, - ), - destruct_position, - )); - - state.vars.push(*guard_var); - - let guard = env.pool.get(*guard_id); - - // TODO: region should be from guard_id - constrain_pattern( - arena, - env, - guard, - region, - expected, - state, - destruct_position, - ); - - types::RecordField::Demanded(env.pool.add(pat_type)) - } - DestructType::Optional(expr_var, expr_id) => { - state.constraints.push(make_pattern_constraint( - region, - PatternCategory::PatternDefault, - Type2::Variable(*expr_var), - PExpected::ForReason( - PReason::OptionalField, - pat_type.shallow_clone(), - // TODO: region should be from expr_id - region, - ), - destruct_position, - )); - - state.vars.push(*expr_var); - - let expr_expected = Expected::ForReason( - Reason::RecordDefaultField(label.as_str(env.pool).into()), - pat_type.shallow_clone(), - // TODO: region should be from expr_id - region, - ); - - let expr = env.pool.get(*expr_id); - - // TODO: region should be from expr_id - let expr_con = constrain_expr(arena, env, expr, expr_expected, region); - - state.constraints.push(expr_con); - - types::RecordField::Optional(env.pool.add(pat_type)) - } - DestructType::Required => { - // No extra constraints necessary. - types::RecordField::Demanded(env.pool.add(pat_type)) - } - }; - - field_types.push((*label, field_type)); - - state.vars.push(*var); - } - - let record_type = Type2::Record( - PoolVec::new(field_types.into_iter(), env.pool), - env.pool.add(ext_type), - ); - - let whole_con = Constraint::Eq( - Type2::Variable(*whole_var), - Expected::NoExpectation(record_type), - Category::Storage(std::file!(), std::line!()), - region, - ); - - let record_con = make_pattern_constraint( - region, - PatternCategory::Record, - Type2::Variable(*whole_var), - expected, - destruct_position, - ); - - state.constraints.push(whole_con); - state.constraints.push(record_con); - } - Tag { - whole_var, - ext_var, - tag_name: name, - arguments, - } => { - let tag_name = TagName(name.as_str(env.pool).into()); - - constrain_tag_pattern( - arena, - env, - region, - expected, - state, - *whole_var, - *ext_var, - arguments, - tag_name, - destruct_position, - ); - } - } -} - -#[allow(clippy::too_many_arguments)] -fn constrain_tag_pattern<'a>( - arena: &'a Bump, - env: &mut Env, - region: Region, - expected: PExpected, - state: &mut PatternState2<'a>, - whole_var: Variable, - ext_var: Variable, - arguments: &PoolVec<(Variable, PatternId)>, - tag_name: TagName, - destruct_position: bool, -) { - let mut argument_types = Vec::with_capacity(arguments.len()); - - for (index, arg_id) in arguments.iter_node_ids().enumerate() { - let (pattern_var, pattern_id) = env.pool.get(arg_id); - let pattern = env.pool.get(*pattern_id); - - state.vars.push(*pattern_var); - - let pattern_type = Type2::Variable(*pattern_var); - argument_types.push(pattern_type.shallow_clone()); - - let expected = PExpected::ForReason( - PReason::TagArg { - tag_name: tag_name.clone(), - index: HumanIndex::zero_based(index), - }, - pattern_type, - region, - ); - - // TODO region should come from pattern - constrain_pattern(arena, env, pattern, region, expected, state, false); - } - - let whole_con = if destruct_position { - Constraint::Present( - expected.get_type_ref().shallow_clone(), - PresenceConstraint::IncludesTag( - tag_name.clone(), - BumpVec::from_iter_in(argument_types.into_iter(), arena), - ), - ) - } else { - Constraint::Eq( - Type2::Variable(whole_var), - Expected::NoExpectation(Type2::TagUnion( - PoolVec::new( - vec![( - tag_name.clone(), - PoolVec::new(argument_types.into_iter(), env.pool), - )] - .into_iter(), - env.pool, - ), - env.pool.add(Type2::Variable(ext_var)), - )), - Category::Storage(std::file!(), std::line!()), - region, - ) - }; - - let tag_con = make_pattern_constraint( - region, - PatternCategory::Ctor(tag_name), - Type2::Variable(whole_var), - expected, - destruct_position, - ); - - state.vars.push(whole_var); - state.vars.push(ext_var); - state.constraints.push(whole_con); - state.constraints.push(tag_con); -} - -fn constrain_untyped_args<'a>( - arena: &'a Bump, - env: &mut Env, - arguments: &PoolVec<(Variable, PatternId)>, - closure_type: Type2, - return_type: Type2, -) -> (BumpVec<'a, Variable>, PatternState2<'a>, Type2) { - let mut vars = BumpVec::with_capacity_in(arguments.len(), arena); - - let pattern_types = PoolVec::with_capacity(arguments.len() as u32, env.pool); - - let mut pattern_state = PatternState2 { - headers: BumpMap::new_in(arena), - vars: BumpVec::with_capacity_in(1, arena), - constraints: BumpVec::with_capacity_in(1, arena), - }; - - for (arg_node_id, pattern_type_id) in - arguments.iter_node_ids().zip(pattern_types.iter_node_ids()) - { - let (pattern_var, pattern_id) = env.pool.get(arg_node_id); - let pattern = env.pool.get(*pattern_id); - - let pattern_type = Type2::Variable(*pattern_var); - let pattern_expected = PExpected::NoExpectation(pattern_type.shallow_clone()); - - env.pool[pattern_type_id] = pattern_type; - - constrain_pattern( - arena, - env, - pattern, - // TODO needs to come from pattern - Region::zero(), - pattern_expected, - &mut pattern_state, - false, - ); - - vars.push(*pattern_var); - } - - let function_type = Type2::Function( - pattern_types, - env.pool.add(closure_type), - env.pool.add(return_type), - ); - - (vars, pattern_state, function_type) -} - -#[allow(clippy::too_many_arguments)] -fn constrain_closure_size<'a>( - arena: &'a Bump, - env: &mut Env, - _name: Symbol, - region: Region, - captured_symbols: &PoolVec<(Symbol, Variable)>, - closure_var: Variable, - closure_ext_var: Variable, - variables: &mut BumpVec<'a, Variable>, -) -> Constraint<'a> { - use Constraint::*; - - debug_assert!(variables.iter().any(|s| *s == closure_var)); - debug_assert!(variables.iter().any(|s| *s == closure_ext_var)); - - let tag_arguments = PoolVec::with_capacity(captured_symbols.len() as u32, env.pool); - let mut captured_symbols_constraints = BumpVec::with_capacity_in(captured_symbols.len(), arena); - - for (captured_symbol_id, tag_arg_id) in captured_symbols - .iter_node_ids() - .zip(tag_arguments.iter_node_ids()) - { - let (symbol, var) = env.pool.get(captured_symbol_id); - - // make sure the variable is registered - variables.push(*var); - - let tag_arg_type = Type2::Variable(*var); - - // this symbol is captured, so it must be part of the closure type - env.pool[tag_arg_id] = tag_arg_type.shallow_clone(); - - // make the variable equal to the looked-up type of symbol - captured_symbols_constraints.push(Lookup( - *symbol, - Expected::NoExpectation(tag_arg_type), - Region::zero(), - )); - } - - // This is incorrect, but the editor will be using the Can AST soon, so disregarding for now. - let tag_name = TagName("FAKE CLOSURE".into()); - let closure_type = Type2::TagUnion( - PoolVec::new(vec![(tag_name, tag_arguments)].into_iter(), env.pool), - env.pool.add(Type2::Variable(closure_ext_var)), - ); - - let finalizer = Eq( - Type2::Variable(closure_var), - Expected::NoExpectation(closure_type), - Category::ClosureSize, - region, - ); - - captured_symbols_constraints.push(finalizer); - - And(captured_symbols_constraints) -} - -#[inline(always)] -fn builtin_type(symbol: Symbol, args: PoolVec) -> Type2 { - Type2::Apply(symbol, args) -} - -#[inline(always)] -fn str_type(pool: &mut Pool) -> Type2 { - builtin_type(Symbol::STR_STR, PoolVec::empty(pool)) -} - -#[inline(always)] -fn empty_list_type(pool: &mut Pool, var: Variable) -> Type2 { - list_type(pool, Type2::Variable(var)) -} - -#[inline(always)] -fn list_type(pool: &mut Pool, typ: Type2) -> Type2 { - builtin_type(Symbol::LIST_LIST, PoolVec::new(vec![typ].into_iter(), pool)) -} - -#[inline(always)] -fn num_float(pool: &mut Pool, range: TypeId) -> Type2 { - let num_floatingpoint_type = num_floatingpoint(pool, range); - let num_floatingpoint_id = pool.add(num_floatingpoint_type); - - let num_num_type = num_num(pool, num_floatingpoint_id); - let num_num_id = pool.add(num_num_type); - - Type2::Alias( - Symbol::NUM_FRAC, - PoolVec::new(vec![range].into_iter(), pool), - num_num_id, - ) -} - -#[inline(always)] -fn num_floatingpoint(pool: &mut Pool, range: TypeId) -> Type2 { - let range_type = pool.get(range); - - let alias_content = range_type.shallow_clone(); - - Type2::Opaque( - Symbol::NUM_FLOATINGPOINT, - PoolVec::new(vec![range].into_iter(), pool), - pool.add(alias_content), - ) -} - -#[inline(always)] -fn num_int(pool: &mut Pool, range: TypeId) -> Type2 { - let num_integer_type = _num_integer(pool, range); - let num_integer_id = pool.add(num_integer_type); - - let num_num_type = num_num(pool, num_integer_id); - let num_num_id = pool.add(num_num_type); - - Type2::Alias( - Symbol::NUM_INT, - PoolVec::new(vec![range].into_iter(), pool), - num_num_id, - ) -} - -#[inline(always)] -fn _num_signed64(pool: &mut Pool) -> Type2 { - Type2::Alias( - Symbol::NUM_SIGNED64, - PoolVec::empty(pool), - pool.add(Type2::EmptyTagUnion), - ) -} - -#[inline(always)] -fn num_unsigned32(pool: &mut Pool) -> Type2 { - let alias_content = Type2::EmptyTagUnion; - - Type2::Alias( - Symbol::NUM_UNSIGNED32, - PoolVec::empty(pool), - pool.add(alias_content), - ) -} - -#[inline(always)] -fn _num_integer(pool: &mut Pool, range: TypeId) -> Type2 { - let range_type = pool.get(range); - - let alias_content = range_type.shallow_clone(); - - Type2::Opaque( - Symbol::NUM_INTEGER, - PoolVec::new(vec![range].into_iter(), pool), - pool.add(alias_content), - ) -} - -#[inline(always)] -fn num_num(pool: &mut Pool, type_id: TypeId) -> Type2 { - let range_type = pool.get(type_id); - - let alias_content = range_type.shallow_clone(); - - Type2::Opaque( - Symbol::NUM_NUM, - PoolVec::new(vec![type_id].into_iter(), pool), - pool.add(alias_content), - ) -} - -#[cfg(test)] -pub mod test_constrain { - use bumpalo::Bump; - use roc_can::expected::Expected; - use roc_collections::all::MutMap; - use roc_module::{ - ident::Lowercase, - symbol::{IdentIds, Interns, ModuleIds, Symbol}, - }; - use roc_parse::parser::{SourceError, SyntaxError}; - use roc_region::all::Region; - use roc_solve::module::Solved; - use roc_types::{ - pretty_print::{name_and_print_var, DebugPrint}, - subs::{Subs, VarStore, Variable}, - }; - - use super::Constraint; - use crate::{ - constrain::constrain_expr, - lang::{ - core::{ - expr::{expr2::Expr2, expr_to_expr2::loc_expr_to_expr2, output::Output}, - types::Type2, - }, - env::Env, - scope::Scope, - }, - mem_pool::pool::Pool, - solve_type, - }; - use indoc::indoc; - - fn run_solve( - arena: &Bump, - mempool: &mut Pool, - aliases: MutMap, - rigid_variables: MutMap, - constraint: Constraint, - var_store: VarStore, - ) -> (Solved, solve_type::Env, Vec) { - let env = solve_type::Env { - vars_by_symbol: MutMap::default(), - aliases, - }; - - let mut subs = Subs::new_from_varstore(var_store); - - for (var, name) in rigid_variables { - subs.rigid_var(var, name); - } - - // Now that the module is parsed, canonicalized, and constrained, - // we need to type check it. - let mut problems = Vec::new(); - - // Run the solver to populate Subs. - let (solved_subs, solved_env) = - solve_type::run(arena, mempool, &env, &mut problems, subs, &constraint); - - (solved_subs, solved_env, problems) - } - - fn infer_eq(actual: &str, expected_str: &str) { - let mut env_pool = Pool::with_capacity(1024); - let env_arena = Bump::new(); - let code_arena = Bump::new(); - - let mut var_store = VarStore::default(); - let var = var_store.fresh(); - let dep_idents = IdentIds::exposed_builtins(8); - let exposed_ident_ids = IdentIds::default(); - let mut module_ids = ModuleIds::default(); - let mod_id = module_ids.get_or_insert(&"ModId123".into()); - - let mut env = Env::new( - mod_id, - &env_arena, - &mut env_pool, - &mut var_store, - dep_idents, - &module_ids, - exposed_ident_ids, - ); - - let mut scope = Scope::new(env.home, env.pool, env.var_store); - - let region = Region::zero(); - - let expr2_result = str_to_expr2(&code_arena, actual, &mut env, &mut scope, region); - - match expr2_result { - Ok((expr, output)) => { - let constraint = constrain_expr( - &code_arena, - &mut env, - &expr, - Expected::NoExpectation(Type2::Variable(var)), - Region::zero(), - ); - - let Env { - pool, - var_store: ref_var_store, - mut dep_idents, - .. - } = env; - - // extract the var_store out of the env again - let mut var_store = VarStore::default(); - std::mem::swap(ref_var_store, &mut var_store); - - let rigids = output.introduced_variables.name_by_var; - - let (mut solved, _, _) = run_solve( - &code_arena, - pool, - Default::default(), - rigids, - constraint, - var_store, - ); - - let subs = solved.inner_mut(); - - // Connect the ModuleId to it's IdentIds - dep_idents.insert(mod_id, env.ident_ids); - - let interns = Interns { - module_ids: env.module_ids.clone(), - all_ident_ids: dep_idents, - }; - - let actual_str = - name_and_print_var(var, subs, mod_id, &interns, DebugPrint::NOTHING); - - assert_eq!(actual_str, expected_str); - } - Err(e) => panic!("syntax error {e:?}"), - } - } - - pub fn str_to_expr2<'a>( - arena: &'a Bump, - input: &'a str, - env: &mut Env<'a>, - scope: &mut Scope, - region: Region, - ) -> Result<(Expr2, Output), SourceError<'a, SyntaxError<'a>>> { - match roc_parse::test_helpers::parse_loc_with(arena, input.trim()) { - Ok(loc_expr) => Ok(loc_expr_to_expr2(arena, loc_expr, env, scope, region)), - Err(fail) => Err(fail), - } - } - - #[test] - fn constrain_str() { - infer_eq( - indoc!( - r#" - "type inference!" - "# - ), - "Str", - ) - } - - // This will be more useful once we actually map - // strings less than 15 chars to SmallStr - #[test] - fn constrain_small_str() { - infer_eq( - indoc!( - r#" - "a" - "# - ), - "Str", - ) - } - - #[test] - fn constrain_empty_record() { - infer_eq( - indoc!( - r#" - {} - "# - ), - "{}", - ) - } - - #[test] - fn constrain_small_int() { - infer_eq( - indoc!( - r#" - 12 - "# - ), - "Num *", - ) - } - - #[test] - fn constrain_float() { - infer_eq( - indoc!( - r#" - 3.14 - "# - ), - "Float *", - ) - } - - #[test] - fn constrain_record() { - infer_eq( - indoc!( - r#" - { x : 1, y : "hi" } - "# - ), - "{ x : Num *, y : Str }", - ) - } - - #[test] - fn constrain_empty_list() { - infer_eq( - indoc!( - r#" - [] - "# - ), - "List *", - ) - } - - #[test] - fn constrain_list() { - infer_eq( - indoc!( - r#" - [1, 2] - "# - ), - "List (Num *)", - ) - } - - #[test] - fn constrain_list_of_records() { - infer_eq( - indoc!( - r#" - [{ x: 1 }, { x: 3 }] - "# - ), - "List { x : Num * }", - ) - } - - #[test] - fn constrain_tag() { - infer_eq( - indoc!( - r#" - Foo - "# - ), - "[Foo]", - ) - } - - #[test] - fn constrain_call_and_accessor() { - infer_eq( - indoc!( - r#" - .foo { foo: "bar" } - "# - ), - "Str", - ) - } - - #[test] - fn constrain_access() { - infer_eq( - indoc!( - r#" - { foo: "bar" }.foo - "# - ), - "Str", - ) - } - - #[test] - fn constrain_if() { - infer_eq( - indoc!( - r#" - if True then Green else Red - "# - ), - "[Green, Red]", - ) - } - - #[test] - fn constrain_when() { - infer_eq( - indoc!( - r#" - when if True then Green else Red is - Green -> Blue - Red -> Purple - "# - ), - "[Blue, Purple]", - ) - } - - #[test] - fn constrain_let_value() { - infer_eq( - indoc!( - r#" - person = { name: "roc" } - - person - "# - ), - "{ name : Str }", - ) - } - - #[test] - fn constrain_update() { - infer_eq( - indoc!( - r#" - person = { name: "roc" } - - { person & name: "bird" } - "# - ), - "{ name : Str }", - ) - } - - #[ignore = "TODO: implement builtins in the editor"] - #[test] - fn constrain_run_low_level() { - infer_eq( - indoc!( - r#" - List.map [{ name: "roc" }, { name: "bird" }] .name - "# - ), - "List Str", - ) - } - - #[test] - fn dual_arity_lambda() { - infer_eq( - indoc!( - r#" - \a, b -> Pair a b - "# - ), - "a, b -> [Pair a b]", - ); - } - - #[test] - fn anonymous_identity() { - infer_eq( - indoc!( - r#" - (\a -> a) 3.14 - "# - ), - "Float *", - ); - } - - #[test] - fn identity_of_identity() { - infer_eq( - indoc!( - r#" - (\val -> val) (\val -> val) - "# - ), - "a -> a", - ); - } - - #[test] - fn identity_function() { - infer_eq( - indoc!( - r#" - \val -> val - "# - ), - "a -> a", - ); - } - - #[test] - fn apply_function() { - infer_eq( - indoc!( - r#" - \f, x -> f x - "# - ), - "(a -> b), a -> b", - ); - } - - #[test] - fn flip_function() { - infer_eq( - indoc!( - r#" - \f -> (\a, b -> f b a) - "# - ), - "(a, b -> d) -> (b, a -> d)", - ); - } - - #[test] - fn always_function() { - infer_eq( - indoc!( - r#" - \val -> \_ -> val - "# - ), - "a -> (* -> a)", - ); - } - - #[test] - fn pass_a_function() { - infer_eq( - indoc!( - r#" - \f -> f {} - "# - ), - "({} -> a) -> a", - ); - } - - #[test] - fn constrain_closure() { - infer_eq( - indoc!( - r#" - x = 1 - - \{} -> x - "# - ), - "{}* -> Num a", - ) - } - - #[test] - fn recursive_identity() { - infer_eq( - indoc!( - r#" - identity = \val -> val - - identity - "# - ), - "a -> a", - ); - } - - #[test] - fn use_apply() { - infer_eq( - indoc!( - r#" - identity = \a -> a - apply = \f, x -> f x - - apply identity 5 - "# - ), - "Num *", - ); - } - - #[test] - fn nested_let_function() { - infer_eq( - indoc!( - r#" - curryPair = \a -> - getB = \b -> Pair a b - getB - - curryPair - "# - ), - "a -> (b -> [Pair a b])", - ); - } - - #[test] - fn record_with_bound_var() { - infer_eq( - indoc!( - r#" - fn = \rec -> - x = rec.x - - rec - - fn - "# - ), - "{ x : a }b -> { x : a }b", - ); - } - - #[test] - fn using_type_signature() { - infer_eq( - indoc!( - r#" - bar : custom -> custom - bar = \x -> x - - bar - "# - ), - "custom -> custom", - ); - } - - #[ignore = "Currently panics at 'Invalid Cycle', ast/src/lang/core/def/def.rs:1212:21"] - #[test] - fn using_type_signature2() { - infer_eq( - indoc!( - r#" - id1 : tya -> tya - id1 = \x -> x - - id2 : tyb -> tyb - id2 = id1 - - id2 - "# - ), - "tyb -> tyb", - ); - } - - #[ignore = "Implement annotation-only decls"] - #[test] - fn type_signature_without_body() { - infer_eq( - indoc!( - r#" - foo: Str -> {} - - foo "hi" - "# - ), - "{}", - ); - } - - #[ignore = "Implement annotation-only decls"] - #[test] - fn type_signature_without_body_rigid() { - infer_eq( - indoc!( - r#" - foo : Num * -> custom - - foo 2 - "# - ), - "custom", - ); - } - - #[test] - fn inference_var_inside_arrow() { - infer_eq( - indoc!( - r#" - id : _ -> _ - id = \x -> x - id - "# - ), - "a -> a", - ) - } - - #[test] - #[ignore = "TODO: Type2::substitute"] - fn inference_var_inside_ctor() { - infer_eq( - indoc!( - r#" - canIGo : _ -> Result _ _ - canIGo = \color -> - when color is - "green" -> Ok "go!" - "yellow" -> Err (SlowIt "whoa, let's slow down!") - "red" -> Err (StopIt "absolutely not") - _ -> Err (UnknownColor "this is a weird stoplight") - canIGo - "# - ), - "Str -> Result Str [SlowIt Str, StopIt Str, UnknownColor Str]*", - ) - } - - #[test] - #[ignore = "TODO: Gives { x : *, y : * } -> { x : *, y : * }. This is a bug in typechecking defs with annotations."] - fn inference_var_inside_ctor_linked() { - infer_eq( - indoc!( - r#" - swapRcd: {x: _, y: _} -> {x: _, y: _} - swapRcd = \{x, y} -> {x: y, y: x} - swapRcd - "# - ), - "{ x : a, y : b } -> { x : b, y : a }", - ) - } - - #[test] - fn inference_var_link_with_rigid() { - infer_eq( - indoc!( - r#" - swapRcd: {x: tx, y: ty} -> {x: _, y: _} - swapRcd = \{x, y} -> {x: y, y: x} - swapRcd - "# - ), - "{ x : tx, y : ty } -> { x : ty, y : tx }", - ) - } - - #[test] - #[ignore = "TODO: Type2::substitute"] - fn inference_var_inside_tag_ctor() { - infer_eq( - indoc!( - r#" - badComics: Bool -> [CowTools _, Thagomizer _] - badComics = \c -> - when c is - True -> CowTools "The Far Side" - False -> Thagomizer "The Far Side" - badComics - "# - ), - "Bool -> [CowTools Str, Thagomizer Str]", - ) - } - - #[test] - fn inference_var_tag_union_ext() { - // TODO: we should really be inferring [Blue, Orange]a -> [Lavender, Peach]a here. - // See https://github.com/roc-lang/roc/issues/2053 - infer_eq( - indoc!( - r#" - pastelize: _ -> [Lavender, Peach]_ - pastelize = \color -> - when color is - Blue -> Lavender - Orange -> Peach - col -> col - pastelize - "# - ), - "[Blue, Lavender, Orange, Peach]a -> [Blue, Lavender, Orange, Peach]a", - ) - } - - #[test] - #[ignore = "TODO: gives { email : a, name : b }c -> { email : a, name : b }c. This is a bug in typechecking defs with annotations."] - fn inference_var_rcd_union_ext() { - infer_eq( - indoc!( - r#" - setRocEmail : _ -> { name: Str, email: Str }_ - setRocEmail = \person -> - { person & email: "\(person.name)@roclang.com" } - setRocEmail - "# - ), - "{ email : Str, name : Str }a -> { email : Str, name : Str }a", - ) - } - - #[test] - fn infer_union_input_position1() { - infer_eq( - indoc!( - r#" - \tag -> - when tag is - A -> X - B -> Y - "# - ), - "[A, B] -> [X, Y]", - ) - } - - #[test] - fn infer_union_input_position2() { - infer_eq( - indoc!( - r#" - \tag -> - when tag is - A -> X - B -> Y - _ -> Z - "# - ), - "[A, B]* -> [X, Y, Z]", - ) - } - - #[test] - fn infer_union_input_position3() { - infer_eq( - indoc!( - r#" - \tag -> - when tag is - A M -> X - A N -> Y - "# - ), - "[A [M, N]] -> [X, Y]", - ) - } - - #[test] - fn infer_union_input_position4() { - infer_eq( - indoc!( - r#" - \tag -> - when tag is - A M -> X - A N -> Y - A _ -> Z - "# - ), - "[A [M, N]] -> [X, Y, Z]", - ) - } - - #[test] - #[ignore = "TODO: currently [A [M [J]*, N [K]*]] -> [X]*"] - fn infer_union_input_position5() { - infer_eq( - indoc!( - r#" - \tag -> - when tag is - A (M J) -> X - A (N K) -> X - "# - ), - "[A [M [J], N [K]]] -> [X]*", - ) - } - - #[test] - fn infer_union_input_position6() { - infer_eq( - indoc!( - r#" - \tag -> - when tag is - A M -> X - B -> X - A N -> X - "# - ), - "[A [M, N], B] -> [X]", - ) - } - - #[test] - #[ignore = "TODO: currently [A]* -> [A, X]*"] - fn infer_union_input_position7() { - infer_eq( - indoc!( - r#" - \tag -> - when tag is - A -> X - t -> t - "# - ), - // TODO: we could be a bit smarter by subtracting "A" as a possible - // tag in the union known by t, which would yield the principal type - // [A,]a -> [X]a - "[A, X]a -> [A, X]a", - ) - } - - #[test] - fn infer_union_input_position8() { - infer_eq( - indoc!( - r#" - \opt -> - when opt is - Some ({tag: A}) -> 1 - Some ({tag: B}) -> 1 - None -> 0 - "# - ), - "[None, Some { tag : [A, B] }*] -> Num *", - ) - } - - #[test] - #[ignore = "TODO: panicked at 'Invalid Cycle', ast/src/lang/core/def/def.rs:1208:21"] - fn infer_union_input_position9() { - infer_eq( - indoc!( - r#" - opt : [Some Str, None] - opt = Some "" - rcd = { opt } - - when rcd is - { opt: Some s } -> s - { opt: None } -> "?" - "# - ), - "Str", - ) - } - - #[test] - #[ignore = "TODO: currently -> Num a"] - fn infer_union_input_position10() { - infer_eq( - indoc!( - r#" - \r -> - when r is - { x: Blue, y ? 3 } -> y - { x: Red, y ? 5 } -> y - "# - ), - "{ x : [Blue, Red], y ? Num a }* -> Num a", - ) - } -} diff --git a/crates/ast/src/lang/core/ast.rs b/crates/ast/src/lang/core/ast.rs deleted file mode 100644 index c86df4d571..0000000000 --- a/crates/ast/src/lang/core/ast.rs +++ /dev/null @@ -1,63 +0,0 @@ -use crate::{ - ast_error::{ASTNodeIdWithoutExprIdSnafu, ASTResult}, - mem_pool::pool::Pool, -}; - -use super::{ - def::def2::{def2_to_string, DefId}, - expr::{expr2::ExprId, expr2_to_string::expr2_to_string}, - header::AppHeader, -}; - -#[derive(Debug)] -pub struct AST { - pub header: AppHeader, - pub def_ids: Vec, -} - -impl AST { - pub fn insert_def_at_index(&mut self, new_def_id: DefId, index: usize) { - self.def_ids.insert(index, new_def_id); - } - - // TODO print in tree shape, similar to linux tree command - pub fn ast_to_string(&self, pool: &Pool) -> String { - let mut full_ast_string = String::new(); - - for def_id in self.def_ids.iter() { - full_ast_string.push_str(&def2_to_string(*def_id, pool)); - full_ast_string.push_str("\n\n"); - } - - full_ast_string - } -} - -#[derive(Debug, PartialEq, Copy, Clone)] -pub enum ASTNodeId { - ADefId(DefId), - AExprId(ExprId), -} - -impl ASTNodeId { - pub fn to_expr_id(&self) -> ASTResult { - match self { - ASTNodeId::AExprId(expr_id) => Ok(*expr_id), - _ => ASTNodeIdWithoutExprIdSnafu { ast_node_id: *self }.fail()?, - } - } - - pub fn to_def_id(&self) -> ASTResult { - match self { - ASTNodeId::ADefId(def_id) => Ok(*def_id), - _ => ASTNodeIdWithoutExprIdSnafu { ast_node_id: *self }.fail()?, - } - } -} - -pub fn ast_node_to_string(node_id: ASTNodeId, pool: &Pool) -> String { - match node_id { - ASTNodeId::ADefId(def_id) => def2_to_string(def_id, pool), - ASTNodeId::AExprId(expr_id) => expr2_to_string(expr_id, pool), - } -} diff --git a/crates/ast/src/lang/core/declaration.rs b/crates/ast/src/lang/core/declaration.rs deleted file mode 100644 index e45668b484..0000000000 --- a/crates/ast/src/lang/core/declaration.rs +++ /dev/null @@ -1,70 +0,0 @@ -use roc_types::subs::VarStore; - -use crate::{ - lang::core::{def::def::Def, expr::expr2::Expr2}, - mem_pool::{pool::Pool, pool_vec::PoolVec}, -}; - -use super::def::def::Declaration; - -pub(crate) fn decl_to_let( - pool: &mut Pool, - var_store: &mut VarStore, - decl: Declaration, - ret: Expr2, -) -> Expr2 { - match decl { - Declaration::Declare(def) => match def { - Def::AnnotationOnly { .. } => todo!(), - Def::Value(value_def) => { - let def_id = pool.add(value_def); - - let body_id = pool.add(ret); - - Expr2::LetValue { - def_id, - body_id, - body_var: var_store.fresh(), - } - } - Def::Function(function_def) => { - let def_id = pool.add(function_def); - let body_id = pool.add(ret); - - Expr2::LetFunction { - def_id, - body_id, - body_var: var_store.fresh(), - } - } - }, - Declaration::DeclareRec(defs) => { - let mut function_defs = vec![]; - - for def in defs { - match def { - Def::AnnotationOnly { .. } => todo!(), - Def::Function(function_def) => function_defs.push(function_def), - Def::Value(_) => unreachable!(), - } - } - - let body_id = pool.add(ret); - - Expr2::LetRec { - defs: PoolVec::new(function_defs.into_iter(), pool), - body_var: var_store.fresh(), - body_id, - } - } - Declaration::InvalidCycle(_entries, _) => { - // TODO: replace with something from Expr2 - // Expr::RuntimeError(RuntimeError::CircularDef(entries)) - todo!() - } - Declaration::Builtin(_) => { - // Builtins should only be added to top-level decls, not to let-exprs! - unreachable!() - } - } -} diff --git a/crates/ast/src/lang/core/def/def.rs b/crates/ast/src/lang/core/def/def.rs deleted file mode 100644 index 1510f6b704..0000000000 --- a/crates/ast/src/lang/core/def/def.rs +++ /dev/null @@ -1,1491 +0,0 @@ -#![allow(clippy::all)] -#![allow(dead_code)] -#![allow(unused_imports)] -#![allow(unused_variables)] -// use crate::annotation::canonicalize_annotation; -// use crate::annotation::IntroducedVariables; -// use crate::env::Env; -// use crate::expr::Expr::{self, *}; -// use crate::expr::{ -// canonicalize_expr, local_successors, references_from_call, references_from_local, Output, -// Recursive, -// }; -// use crate::pattern::{bindings_from_patterns, canonicalize_pattern, Pattern}; -// use crate::procedure::References; -use roc_collections::all::{default_hasher, ImMap, MutMap, MutSet, SendMap}; -use roc_error_macros::{internal_error, todo_abilities}; -use roc_module::ident::Lowercase; -use roc_module::symbol::Symbol; -use roc_parse::ast::{self, CommentOrNewline, Defs, TypeDef, TypeHeader, ValueDef as AstValueDef}; -use roc_parse::pattern::PatternType; -use roc_problem::can::{Problem, RuntimeError, ShadowKind}; -use roc_region::all::{Loc, Region}; -use roc_types::subs::{VarStore, Variable}; -use roc_types::types::AliasKind; -use std::collections::HashMap; -use std::fmt::Debug; -use ven_graph::{strongly_connected_components, topological_sort_into_groups}; - -use crate::{ - lang::{ - core::{ - expr::{expr2::Expr2, expr_to_expr2::expr_to_expr2, output::Output}, - fun_def::FunctionDef, - pattern::{self, symbols_from_pattern, to_pattern_id, Pattern2, PatternId}, - types::{to_annotation2, Alias, Annotation2, Signature, Type2, TypeId}, - val_def::ValueDef, - }, - env::Env, - rigids::Rigids, - scope::Scope, - }, - mem_pool::{ - pool::{NodeId, Pool}, - pool_vec::PoolVec, - shallow_clone::ShallowClone, - }, -}; - -#[derive(Debug)] -pub enum Def { - AnnotationOnly { rigids: Rigids, annotation: TypeId }, - Value(ValueDef), - Function(FunctionDef), -} - -impl Def { - pub fn symbols(&self, pool: &Pool) -> MutSet { - let mut output = MutSet::default(); - - match self { - Def::AnnotationOnly { .. } => todo!("lost pattern information here ... "), - Def::Value(value_def) => match value_def { - ValueDef::WithAnnotation { pattern_id, .. } - | ValueDef::NoAnnotation { pattern_id, .. } => { - let pattern2 = &pool[*pattern_id]; - output.extend(symbols_from_pattern(pool, pattern2)); - } - }, - Def::Function(function_def) => match function_def { - FunctionDef::NoAnnotation { name, .. } - | FunctionDef::WithAnnotation { name, .. } => { - output.insert(*name); - } - }, - } - - output - } -} - -impl ShallowClone for Def { - fn shallow_clone(&self) -> Self { - match self { - Self::AnnotationOnly { rigids, annotation } => Self::AnnotationOnly { - rigids: rigids.shallow_clone(), - annotation: *annotation, - }, - Self::Value(def) => Self::Value(def.shallow_clone()), - Self::Function(def) => Self::Function(def.shallow_clone()), - } - } -} - -/// A Def that has had patterns and type annnotations canonicalized, -/// but no Expr canonicalization has happened yet. Also, it has had spaces -/// and nesting resolved, and knows whether annotations are standalone or not. -#[derive(Debug)] -pub enum PendingDef<'a> { - /// A standalone annotation with no body - AnnotationOnly( - &'a Loc>, - PatternId, - &'a Loc>, - ), - /// A body with no type annotation - Body(&'a Loc>, PatternId, &'a Loc>), - /// A body with a type annotation - TypedBody( - &'a Loc>, - PatternId, - &'a Loc>, - &'a Loc>, - ), - - /// A type alias, e.g. `Ints : List Int` - Alias { - name: Loc, - vars: Vec>, - ann: &'a Loc>, - }, - - /// An invalid alias, that is ignored in the rest of the pipeline - /// e.g. a shadowed alias, or a definition like `MyAlias 1 : Int` - /// with an incorrect pattern - InvalidAlias, -} - -pub enum AstDef<'a> { - Type(TypeDef<'a>), - Value(AstValueDef<'a>), - - // Blank Space (e.g. comments, spaces, newlines) before or after a def. - // We preserve this for the formatter; canonicalization ignores it. - SpaceBefore(&'a AstDef<'a>, &'a [CommentOrNewline<'a>]), - SpaceAfter(&'a AstDef<'a>, &'a [CommentOrNewline<'a>]), - - NotYetImplemented(&'static str), -} - -fn to_pending_def<'a>( - env: &mut Env<'a>, - def: &'a AstDef<'a>, - scope: &mut Scope, - pattern_type: PatternType, -) -> Option<(Output, PendingDef<'a>)> { - use AstDef::*; - - match def { - Value(AstValueDef::Annotation(loc_pattern, loc_ann)) => { - // This takes care of checking for shadowing and adding idents to scope. - let (output, loc_can_pattern) = pattern::to_pattern_id( - env, - scope, - pattern_type, - &loc_pattern.value, - loc_pattern.region, - ); - - Some(( - output, - PendingDef::AnnotationOnly(loc_pattern, loc_can_pattern, loc_ann), - )) - } - Value(AstValueDef::Body(loc_pattern, loc_expr)) => { - // This takes care of checking for shadowing and adding idents to scope. - let (output, loc_can_pattern) = pattern::to_pattern_id( - env, - scope, - pattern_type, - &loc_pattern.value, - loc_pattern.region, - ); - - Some(( - output, - PendingDef::Body(loc_pattern, loc_can_pattern, loc_expr), - )) - } - - Value(AstValueDef::AnnotatedBody { - ann_pattern, - ann_type, - comment: _, - body_pattern, - body_expr, - }) => { - if ann_pattern.value.equivalent(&body_pattern.value) { - // NOTE: Pick the body pattern, picking the annotation one is - // incorrect in the presence of optional record fields! - // - // { x, y } : { x : Int, y ? Bool }* - // { x, y ? False } = rec - Some(pending_typed_body( - env, - body_pattern, - ann_type, - body_expr, - scope, - pattern_type, - )) - } else { - // the pattern of the annotation does not match the pattern of the body direc - env.problem(Problem::SignatureDefMismatch { - annotation_pattern: ann_pattern.region, - def_pattern: body_pattern.region, - }); - - // TODO: Should we instead build some PendingDef::InvalidAnnotatedBody ? This would - // remove the `Option` on this function (and be probably more reliable for further - // problem/error reporting) - None - } - } - - Type(TypeDef::Alias { - header: TypeHeader { name, vars }, - ann, - }) => { - let region = Region::span_across(&name.region, &ann.region); - - match scope.introduce( - name.value.into(), - &env.exposed_ident_ids, - &mut env.ident_ids, - region, - ) { - Ok(symbol) => { - let mut can_rigids: Vec> = Vec::with_capacity(vars.len()); - - for loc_var in vars.iter() { - match loc_var.value { - ast::Pattern::Identifier(name) - if name.chars().next().unwrap().is_lowercase() => - { - let lowercase = Lowercase::from(name); - can_rigids.push(Loc { - value: lowercase, - region: loc_var.region, - }); - } - _ => { - // any other pattern in this position is a syntax error. - env.problem(Problem::InvalidAliasRigid { - alias_name: symbol, - region: loc_var.region, - }); - - return Some((Output::default(), PendingDef::InvalidAlias)); - } - } - } - - Some(( - Output::default(), - PendingDef::Alias { - name: Loc { - region: name.region, - value: symbol, - }, - vars: can_rigids, - ann, - }, - )) - } - - Err((original_region, loc_shadowed_symbol)) => { - env.problem(Problem::Shadowing { - original_region, - shadow: loc_shadowed_symbol, - kind: ShadowKind::Variable, - }); - - Some((Output::default(), PendingDef::InvalidAlias)) - } - } - } - - Type(TypeDef::Opaque { .. }) => internal_error!("opaques not implemented"), - Type(TypeDef::Ability { .. }) => todo_abilities!(), - - Value(AstValueDef::Dbg { .. }) => todo!(), - Value(AstValueDef::Expect { .. }) => todo!(), - Value(AstValueDef::ExpectFx { .. }) => todo!(), - - SpaceBefore(sub_def, _) | SpaceAfter(sub_def, _) => { - to_pending_def(env, sub_def, scope, pattern_type) - } - - NotYetImplemented(s) => todo!("{}", s), - } -} - -fn pending_typed_body<'a>( - env: &mut Env<'a>, - loc_pattern: &'a Loc>, - loc_ann: &'a Loc>, - loc_expr: &'a Loc>, - scope: &mut Scope, - pattern_type: PatternType, -) -> (Output, PendingDef<'a>) { - // This takes care of checking for shadowing and adding idents to scope. - let (output, loc_can_pattern) = to_pattern_id( - env, - scope, - pattern_type, - &loc_pattern.value, - loc_pattern.region, - ); - - ( - output, - PendingDef::TypedBody(loc_pattern, loc_can_pattern, loc_ann, loc_expr), - ) -} - -fn from_pending_alias<'a>( - env: &mut Env<'a>, - scope: &mut Scope, - name: Loc, - vars: Vec>, - ann: &'a Loc>, - mut output: Output, -) -> Output { - let symbol = name.value; - - match to_annotation2(env, scope, &ann.value, ann.region) { - Annotation2::Erroneous => todo!(), - Annotation2::Annotation { - named_rigids, - unnamed_rigids, - symbols, - signature, - } => { - // Record all the annotation's references in output.references.lookups - - for symbol in symbols { - output.references.lookups.insert(symbol); - output.references.referenced_aliases.insert(symbol); - } - - for loc_lowercase in vars { - if !named_rigids.contains_key(&loc_lowercase.value) { - env.problem(Problem::PhantomTypeArgument { - typ: symbol, - variable_region: loc_lowercase.region, - variable_name: loc_lowercase.value.clone(), - alias_kind: AliasKind::Structural, - }); - } - } - - let rigids = Rigids::new(named_rigids, unnamed_rigids, env.pool); - - let annotation = match signature { - Signature::Value { annotation } => annotation, - Signature::Function { - arguments, - closure_type_id, - return_type_id, - } => Type2::Function(arguments, closure_type_id, return_type_id), - Signature::FunctionWithAliases { annotation, .. } => annotation, - }; - - if annotation.contains_symbol(env.pool, symbol) { - // the alias is recursive. If it's a tag union, we attempt to fix this - if let Type2::TagUnion(tags, ext) = annotation { - // re-canonicalize the alias with the alias already in scope - let rec_var = env.var_store.fresh(); - let rec_type_union = Type2::RecursiveTagUnion(rec_var, tags, ext); - - // NOTE this only looks at the symbol, and just assumes that the - // recursion is not polymorphic - rec_type_union.substitute_alias(env.pool, symbol, Type2::Variable(rec_var)); - - let annotation_id = env.add(rec_type_union, ann.region); - let named = rigids.named(env.pool); - - scope.add_alias(env.pool, symbol, named, annotation_id); - } else { - env.problem(Problem::CyclicAlias( - symbol, - name.region, - vec![], - AliasKind::Structural, - )); - return output; - } - } else { - let annotation_id = env.add(annotation, ann.region); - let named = rigids.named(env.pool); - - scope.add_alias(env.pool, symbol, named, annotation_id); - } - - output - } - } -} - -// TODO trim down these arguments! -#[allow(clippy::too_many_arguments)] -#[allow(clippy::cognitive_complexity)] -fn canonicalize_pending_def<'a>( - env: &mut Env<'a>, - pending_def: PendingDef<'a>, - mut output: Output, - scope: &mut Scope, - can_defs_by_symbol: &mut MutMap, - refs_by_symbol: &mut MutMap, - aliases: &mut MutMap, -) -> Output { - use PendingDef::*; - - // Make types for the body expr, even if we won't end up having a body. - let expr_var = env.var_store.fresh(); - - match pending_def { - AnnotationOnly(_, loc_can_pattern, loc_ann) => { - // annotation sans body cannot introduce new rigids that are visible in other annotations - // but the rigids can show up in type error messages, so still register them - - match to_annotation2(env, scope, &loc_ann.value, loc_ann.region) { - Annotation2::Erroneous => todo!(), - Annotation2::Annotation { - named_rigids, - unnamed_rigids, - symbols, - signature, - } => { - // Record all the annotation's references in output.references.lookups - - for symbol in symbols { - output.references.lookups.insert(symbol); - output.references.referenced_aliases.insert(symbol); - } - - let rigids = Rigids::new(named_rigids, unnamed_rigids, env.pool); - - let annotation = match signature { - Signature::Value { annotation } => annotation, - Signature::Function { - arguments, - closure_type_id, - return_type_id, - } => Type2::Function(arguments, closure_type_id, return_type_id), - Signature::FunctionWithAliases { annotation, .. } => annotation, - }; - let annotation = env.add(annotation, loc_ann.region); - - let def = Def::AnnotationOnly { rigids, annotation }; - - for symbol in symbols_from_pattern(env.pool, env.pool.get(loc_can_pattern)) { - can_defs_by_symbol.insert(symbol, def.shallow_clone()); - } - - output - } - } - } - - PendingDef::Alias { name, ann, vars } => { - from_pending_alias(env, scope, name, vars, ann, output) - } - - InvalidAlias => { - // invalid aliases (shadowed, incorrect patterns ) - todo!() - } - - TypedBody(loc_pattern, loc_can_pattern, loc_ann, loc_expr) => { - match to_annotation2(env, scope, &loc_ann.value, loc_ann.region) { - Annotation2::Erroneous => todo!(), - Annotation2::Annotation { - named_rigids, - unnamed_rigids, - symbols, - signature, - } => { - // Record all the annotation's references in output.references.lookups - - for symbol in symbols { - output.references.lookups.insert(symbol); - output.references.referenced_aliases.insert(symbol); - } - - // Ensure rigid type vars and their names are known in the output. - for (name, &var) in named_rigids.iter() { - output.introduced_variables.insert_named(name.clone(), var); - } - let rigids = Rigids::new(named_rigids, unnamed_rigids, env.pool); - - // bookkeeping for tail-call detection. If we're assigning to an - // identifier (e.g. `f = \x -> ...`), then this symbol can be tail-called. - let outer_identifier = env.tailcallable_symbol; - - if let Pattern2::Identifier(ref defined_symbol) = env.pool[loc_can_pattern] { - env.tailcallable_symbol = Some(*defined_symbol); - }; - - // register the name of this closure, to make sure the closure won't capture it's own name - if let (Pattern2::Identifier(ref defined_symbol), &ast::Expr::Closure(_, _)) = - (&env.pool[loc_can_pattern], &loc_expr.value) - { - env.closure_name_symbol = Some(*defined_symbol); - }; - - let (loc_can_expr, can_output) = - expr_to_expr2(env, scope, &loc_expr.value, loc_expr.region); - - output.references.union_mut(can_output.references.clone()); - - // reset the tailcallable_symbol - env.tailcallable_symbol = outer_identifier; - - // First, make sure we are actually assigning an identifier instead of (for example) a tag. - // - // If we're assigning (UserId userId) = ... then this is certainly not a closure declaration, - // which also implies it's not a self tail call! - // - // Only defs of the form (foo = ...) can be closure declarations or self tail calls. - match loc_can_expr { - Expr2::Closure { - args: closure_args, - body_id, - extra, - uniq_symbol: closure_symbol, - .. - } => { - let symbol = match env.pool[loc_can_pattern] { - Pattern2::Identifier(ref s) => *s, - _ => todo!( - r"this is an error; functions must be bound with an identifier pattern!" - ), - }; - - // Since everywhere in the code it'll be referred to by its defined name, - // remove its generated name from the closure map. - let references = - env.closures.remove(&closure_symbol).unwrap_or_else(|| { - panic!( r"Tried to remove symbol {:?} from procedures, but it was not found: {:?}", closure_symbol, env.closures) - }); - - // TODO should we re-insert this function into env.closures? - env.closures.insert(symbol, references); - - // Recursion doesn't count as referencing. (If it did, all recursive functions - // would result in circular def errors!) - refs_by_symbol.entry(symbol).and_modify(|(_, refs)| { - refs.lookups.remove(&symbol); - }); - - // Functions' references don't count in defs. - // See 3d5a2560057d7f25813112dfa5309956c0f9e6a9 and its - // parent commit for the bug this fixed! - let refs = References::new(); - - let arguments: PoolVec<(NodeId, PatternId)> = - PoolVec::with_capacity(closure_args.len() as u32, env.pool); - - let return_type: TypeId; - - let annotation = match signature { - Signature::Value { .. } => { - todo!("type annotation says 0 arguments, but it's a function") - } - Signature::Function { - arguments: type_arguments, - closure_type_id, - return_type_id, - } - | Signature::FunctionWithAliases { - arguments: type_arguments, - closure_type_id, - return_type_id, - .. - } => { - if arguments.len() != type_arguments.len() { - panic!("argument number mismatch"); - } - - let it: Vec<_> = closure_args - .iter(env.pool) - .map(|(x, y)| (*x, *y)) - .zip( - type_arguments - .iter(env.pool) - .map(|t| t.shallow_clone()), - ) - .collect(); - - for (node_id, ((_, pattern_id), typ)) in - arguments.iter_node_ids().zip(it.into_iter()) - { - let typ = env.pool.add(typ); - env.pool[node_id] = (typ, pattern_id); - } - - return_type = return_type_id; - } - }; - - let function_def = FunctionDef::WithAnnotation { - name: symbol, - arguments, - rigids: env.pool.add(rigids), - return_type, - body_id, - }; - - let def = Def::Function(function_def); - - can_defs_by_symbol.insert(symbol, def); - - output - } - - _ => { - let annotation = match signature { - Signature::Value { annotation } => annotation, - Signature::Function { - arguments, - closure_type_id, - return_type_id, - } => Type2::Function(arguments, closure_type_id, return_type_id), - Signature::FunctionWithAliases { annotation, .. } => annotation, - }; - let annotation = env.add(annotation, loc_ann.region); - - let value_def = ValueDef::WithAnnotation { - pattern_id: loc_can_pattern, - expr_id: env.pool.add(loc_can_expr), - type_id: annotation, - rigids, - expr_var: env.var_store.fresh(), - }; - - let def = Def::Value(value_def); - - for symbol in - symbols_from_pattern(env.pool, env.pool.get(loc_can_pattern)) - { - can_defs_by_symbol.insert(symbol, def.shallow_clone()); - } - - for (_, (symbol, region)) in scope.idents() { - // if !vars_by_symbol.contains_key(&symbol) { - // continue; - // } - let refs = can_output.references.clone(); - refs_by_symbol.insert(*symbol, (*region, refs)); - } - - output - } - } - } - } - } - - Body(loc_pattern, loc_can_pattern, loc_expr) => { - // bookkeeping for tail-call detection. If we're assigning to an - // identifier (e.g. `f = \x -> ...`), then this symbol can be tail-called. - let outer_identifier = env.tailcallable_symbol; - - if let Pattern2::Identifier(ref defined_symbol) = env.pool[loc_can_pattern] { - env.tailcallable_symbol = Some(*defined_symbol); - }; - - // register the name of this closure, to make sure the closure won't capture it's own name - if let (Pattern2::Identifier(ref defined_symbol), &ast::Expr::Closure(_, _)) = - (&env.pool[loc_can_pattern], &loc_expr.value) - { - env.closure_name_symbol = Some(*defined_symbol); - }; - - let (loc_can_expr, can_output) = - expr_to_expr2(env, scope, &loc_expr.value, loc_expr.region); - - output.references.union_mut(can_output.references.clone()); - - // reset the tailcallable_symbol - env.tailcallable_symbol = outer_identifier; - - // First, make sure we are actually assigning an identifier instead of (for example) a tag. - // - // If we're assigning (UserId userId) = ... then this is certainly not a closure declaration, - // which also implies it's not a self tail call! - // - // Only defs of the form (foo = ...) can be closure declarations or self tail calls. - match loc_can_expr { - Expr2::Closure { - args: closure_args, - body_id, - extra, - uniq_symbol: closure_symbol, - .. - } => { - let symbol = match env.pool[loc_can_pattern] { - Pattern2::Identifier(ref s) => *s, - _ => todo!( - r"this is an error; functions must be bound with an identifier pattern!" - ), - }; - - // Since everywhere in the code it'll be referred to by its defined name, - // remove its generated name from the closure map. - let references = - env.closures.remove(&closure_symbol).unwrap_or_else(|| { - panic!( r"Tried to remove symbol {:?} from procedures, but it was not found: {:?}", closure_symbol, env.closures) - }); - - // TODO should we re-insert this function into env.closures? - env.closures.insert(symbol, references); - - // Recursion doesn't count as referencing. (If it did, all recursive functions - // would result in circular def errors!) - refs_by_symbol.entry(symbol).and_modify(|(_, refs)| { - refs.lookups.remove(&symbol); - }); - - // Functions' references don't count in defs. - // See 3d5a2560057d7f25813112dfa5309956c0f9e6a9 and its - // parent commit for the bug this fixed! - let refs = References::new(); - - let arguments: PoolVec<(Variable, PatternId)> = - PoolVec::with_capacity(closure_args.len() as u32, env.pool); - - let it: Vec<_> = closure_args.iter(env.pool).map(|(x, y)| (*x, *y)).collect(); - - for (node_id, (_, pattern_id)) in arguments.iter_node_ids().zip(it.into_iter()) - { - env.pool[node_id] = (env.var_store.fresh(), pattern_id); - } - - let function_def = FunctionDef::NoAnnotation { - name: symbol, - arguments, - return_var: env.var_store.fresh(), - body_id, - }; - - let def = Def::Function(function_def); - - can_defs_by_symbol.insert(symbol, def); - - output - } - - _ => { - let value_def = ValueDef::NoAnnotation { - pattern_id: loc_can_pattern, - expr_id: env.pool.add(loc_can_expr), - expr_var: env.var_store.fresh(), - }; - - let def = Def::Value(value_def); - - for symbol in symbols_from_pattern(env.pool, env.pool.get(loc_can_pattern)) { - can_defs_by_symbol.insert(symbol, def.shallow_clone()); - } - - for (_, (symbol, region)) in scope.idents() { - // if !vars_by_symbol.contains_key(&symbol) { - // continue; - // } - let refs = can_output.references.clone(); - refs_by_symbol.insert(*symbol, (*region, refs)); - } - - output - } - } - } - } -} - -#[derive(Clone, Debug, Default, PartialEq)] -pub struct References { - pub bound_symbols: MutSet, - pub lookups: MutSet, - pub referenced_aliases: MutSet, - pub calls: MutSet, -} - -impl References { - pub fn new() -> References { - Self::default() - } - - pub fn union_mut(&mut self, other: References) { - self.lookups.extend(other.lookups); - self.calls.extend(other.calls); - self.bound_symbols.extend(other.bound_symbols); - self.referenced_aliases.extend(other.referenced_aliases); - } - - pub fn has_lookup(&self, symbol: Symbol) -> bool { - self.lookups.contains(&symbol) - } -} - -#[derive(Debug)] -pub struct CanDefs { - pub refs_by_symbol: MutMap, - pub can_defs_by_symbol: MutMap, - pub aliases: MutMap, -} - -#[inline(always)] -pub fn canonicalize_defs<'a>( - env: &mut Env<'a>, - mut output: Output, - original_scope: &Scope, - loc_defs: &'a Defs<'a>, - pattern_type: PatternType, -) -> (CanDefs, Scope, Output, MutMap) { - // Canonicalizing defs while detecting shadowing involves a multi-step process: - // - // 1. Go through each of the patterns. - // 2. For each identifier pattern, get the scope.symbol() for the ident. (That symbol will use the home module for its module.) - // 3. If that symbol is already in scope, then we're about to shadow it. Error! - // 4. Otherwise, add it to the scope immediately, so we can detect shadowing within the same - // pattern (e.g. (Foo a a) = ...) - // 5. Add this canonicalized pattern and its corresponding ast::Expr to pending_exprs. - // 5. Once every pattern has been processed and added to scope, go back and canonicalize the exprs from - // pending_exprs, this time building up a canonical def for each one. - // - // This way, whenever any expr is doing lookups, it knows everything that's in scope - - // even defs that appear after it in the source. - // - // This naturally handles recursion too, because a given expr which refers - // to itself won't be processed until after its def has been added to scope. - - // Record both the original and final idents from the scope, - // so we can diff them while detecting unused defs. - let mut scope = original_scope.shallow_clone(); - let num_defs = loc_defs.len(); - let mut refs_by_symbol = MutMap::default(); - let mut can_defs_by_symbol = HashMap::with_capacity_and_hasher(num_defs, default_hasher()); - let mut pending = Vec::with_capacity(num_defs); // TODO bump allocate this! - - // Canonicalize all the patterns, record shadowing problems, and store - // the ast::Expr values in pending_exprs for further canonicalization - // once we've finished assembling the entire scope. - for loc_def in loc_defs.defs() { - let def = match loc_def { - Ok(type_def) => AstDef::Type(type_def.clone()), - Err(value_def) => AstDef::Value(value_def.clone()), - }; - - match to_pending_def(env, env.arena.alloc(def), &mut scope, pattern_type) { - None => (), - Some((new_output, pending_def)) => { - // store the top-level defs, used to ensure that closures won't capture them - if let PatternType::TopLevelDef = pattern_type { - match &pending_def { - PendingDef::AnnotationOnly(_, loc_can_pattern, _) - | PendingDef::Body(_, loc_can_pattern, _) - | PendingDef::TypedBody(_, loc_can_pattern, _, _) => { - env.top_level_symbols.extend(symbols_from_pattern( - env.pool, - env.pool.get(*loc_can_pattern), - )) - } - PendingDef::Alias { .. } | PendingDef::InvalidAlias => {} - } - } - // Record the ast::Expr for later. We'll do another pass through these - // once we have the entire scope assembled. If we were to canonicalize - // the exprs right now, they wouldn't have symbols in scope from defs - // that get would have gotten added later in the defs list! - pending.push(pending_def); - output.union(new_output); - } - } - } - - if cfg!(debug_assertions) { - env.home.register_debug_idents(&env.ident_ids); - } - - // TODO what to do here? aliases are already in the scope! - let mut aliases = MutMap::default(); - let mut value_defs = Vec::new(); - - for pending_def in pending.into_iter() { - match pending_def { - PendingDef::Alias { name, vars, ann } => { - output = from_pending_alias(env, &mut scope, name, vars, ann, output); - } - other => value_defs.push(other), - } - } - - // TODO - // correct_mutual_recursive_type_alias(env, &mut aliases, var_store); - - // Now that we have the scope completely assembled, and shadowing resolved, - // we're ready to canonicalize any body exprs. - for pending_def in value_defs.into_iter() { - output = canonicalize_pending_def( - env, - pending_def, - output, - &mut scope, - &mut can_defs_by_symbol, - &mut refs_by_symbol, - &mut aliases, - ); - - // TODO we should do something with these references; they include - // things like type annotations. - } - - // Determine which idents we introduced in the course of this process. - let mut symbols_introduced = MutMap::default(); - - for (symbol, region) in scope.symbols() { - if !original_scope.contains_symbol(symbol) { - symbols_introduced.insert(symbol, region); - } - } - - // This returns both the defs info as well as the new scope. - // - // We have to return the new scope because we added defs to it - // (and those lookups shouldn't fail later, e.g. when canonicalizing - // the return expr), but we didn't want to mutate the original scope - // directly because we wanted to keep a clone of it around to diff - // when looking for unused idents. - // - // We have to return the scope separately from the defs, because the - // defs need to get moved later. - ( - CanDefs { - refs_by_symbol, - can_defs_by_symbol, - aliases, - }, - scope, - output, - symbols_introduced, - ) -} - -// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. -#[derive(Debug)] -#[allow(clippy::large_enum_variant)] -pub enum Declaration { - Declare(Def), - DeclareRec(Vec), - Builtin(Def), - InvalidCycle(Vec, Vec<(Region /* pattern */, Region /* expr */)>), -} - -impl Declaration { - pub fn def_count(&self) -> usize { - use Declaration::*; - match self { - Declare(_) => 1, - DeclareRec(defs) => defs.len(), - InvalidCycle(_, _) => 0, - Builtin(_) => 0, - } - } -} - -#[inline(always)] -pub fn sort_can_defs( - env: &mut Env<'_>, - defs: CanDefs, - mut output: Output, -) -> (Result, RuntimeError>, Output) { - let CanDefs { - refs_by_symbol, - can_defs_by_symbol, - aliases, - } = defs; - - // for (symbol, alias) in aliases.into_iter() { - // output.aliases.insert(symbol, alias); - // } - - // Determine the full set of references by traversing the graph. - let mut visited_symbols = MutSet::default(); - let returned_lookups = MutSet::clone(&output.references.lookups); - - // Start with the return expression's referenced locals. They're the only ones that count! - // - // If I have two defs which reference each other, but neither of them is referenced - // in the return expression, I don't want either of them (or their references) to end up - // in the final output.references. They were unused, and so were their references! - // - // The reason we need a graph here is so we don't overlook transitive dependencies. - // For example, if I have `a = b + 1` and the def returns `a + 1`, then the - // def as a whole references both `a` *and* `b`, even though it doesn't - // directly mention `b` - because `a` depends on `b`. If we didn't traverse a graph here, - // we'd erroneously give a warning that `b` was unused since it wasn't directly referenced. - for symbol in returned_lookups.into_iter() { - // We only care about local symbols in this analysis. - if symbol.module_id() == env.home { - // Traverse the graph and look up *all* the references for this local symbol. - let refs = - references_from_local(symbol, &mut visited_symbols, &refs_by_symbol, &env.closures); - - output.references.union_mut(refs); - } - } - - for symbol in output.references.calls.clone() { - // Traverse the graph and look up *all* the references for this call. - // Reuse the same visited_symbols as before; if we already visited it, - // we won't learn anything new from visiting it again! - let refs = - references_from_call(symbol, &mut visited_symbols, &refs_by_symbol, &env.closures); - - output.references.union_mut(refs); - } - - let mut defined_symbols: Vec = Vec::new(); - let mut defined_symbols_set: MutSet = MutSet::default(); - - for symbol in can_defs_by_symbol.keys().into_iter() { - defined_symbols.push(*symbol); - defined_symbols_set.insert(*symbol); - } - - // Use topological sort to reorder the defs based on their dependencies to one another. - // This way, during code gen, no def will refer to a value that hasn't been initialized yet. - // As a bonus, the topological sort also reveals any cycles between the defs, allowing - // us to give a CircularAssignment error for invalid (mutual) recursion, and a `DeclareRec` for mutually - // recursive definitions. - - // All successors that occur in the body of a symbol. - let all_successors_without_self = |symbol: &Symbol| -> MutSet { - // This may not be in refs_by_symbol. For example, the `f` in `f x` here: - // - // f = \z -> z - // - // (\x -> - // a = f x - // x - // ) - // - // It's not part of the current defs (the one with `a = f x`); rather, - // it's in the enclosing scope. It's still referenced though, so successors - // will receive it as an argument! - match refs_by_symbol.get(symbol) { - Some((_, references)) => { - // We can only sort the symbols at the current level. That is safe because - // symbols defined at higher levels cannot refer to symbols at lower levels. - // Therefore they can never form a cycle! - // - // In the above example, `f` cannot reference `a`, and in the closure - // a call to `f` cannot cycle back to `a`. - let mut loc_succ = local_successors(&references, &env.closures); - - // if the current symbol is a closure, peek into its body - if let Some(References { lookups, .. }) = env.closures.get(symbol) { - let home = env.home; - - for lookup in lookups { - if lookup != symbol && lookup.module_id() == home { - // DO NOT register a self-call behind a lambda! - // - // We allow `boom = \_ -> boom {}`, but not `x = x` - loc_succ.insert(*lookup); - } - } - } - - // remove anything that is not defined in the current block - loc_succ.retain(|key| defined_symbols_set.contains(key)); - - loc_succ - } - None => MutSet::default(), - } - }; - - // All successors that occur in the body of a symbol, including the symbol itself - // This is required to determine whether a symbol is recursive. Recursive symbols - // (that are not faulty) always need a DeclareRec, even if there is just one symbol in the - // group - let mut all_successors_with_self = |symbol: &Symbol| -> MutSet { - // This may not be in refs_by_symbol. For example, the `f` in `f x` here: - // - // f = \z -> z - // - // (\x -> - // a = f x - // x - // ) - // - // It's not part of the current defs (the one with `a = f x`); rather, - // it's in the enclosing scope. It's still referenced though, so successors - // will receive it as an argument! - match refs_by_symbol.get(symbol) { - Some((_, references)) => { - // We can only sort the symbols at the current level. That is safe because - // symbols defined at higher levels cannot refer to symbols at lower levels. - // Therefore they can never form a cycle! - // - // In the above example, `f` cannot reference `a`, and in the closure - // a call to `f` cannot cycle back to `a`. - let mut loc_succ = local_successors(&references, &env.closures); - - // if the current symbol is a closure, peek into its body - if let Some(References { lookups, .. }) = env.closures.get(symbol) { - for lookup in lookups { - loc_succ.insert(*lookup); - } - } - - // remove anything that is not defined in the current block - loc_succ.retain(|key| defined_symbols_set.contains(key)); - - loc_succ - } - None => MutSet::default(), - } - }; - - // If a symbol is a direct successor of itself, there is an invalid cycle. - // The difference with the function above is that this one does not look behind lambdas, - // but does consider direct self-recursion. - let direct_successors = |symbol: &Symbol| -> MutSet { - match refs_by_symbol.get(symbol) { - Some((_, references)) => { - let mut loc_succ = local_successors(&references, &env.closures); - - // NOTE: if the symbol is a closure we DONT look into its body - - // remove anything that is not defined in the current block - loc_succ.retain(|key| defined_symbols_set.contains(key)); - - // NOTE: direct recursion does matter here: `x = x` is invalid recursion! - - loc_succ - } - None => MutSet::default(), - } - }; - - // TODO also do the same `addDirects` check elm/compiler does, so we can - // report an error if a recursive definition can't possibly terminate! - match ven_graph::topological_sort_into_groups( - defined_symbols.as_slice(), - all_successors_without_self, - ) { - Ok(groups) => { - let mut declarations = Vec::new(); - - // groups are in reversed order - let mut can_defs_by_symbol = can_defs_by_symbol; - let cdbs = &mut can_defs_by_symbol; - for group in groups.into_iter().rev() { - group_to_declaration( - &group, - &env.closures, - &mut all_successors_with_self, - cdbs, - &mut declarations, - ); - } - - (Ok(declarations), output) - } - Err((mut groups, nodes_in_cycle)) => { - let mut declarations = Vec::new(); - let problems = Vec::new(); - - // nodes_in_cycle are symbols that form a syntactic cycle. That isn't always a problem, - // and in general it's impossible to decide whether it is. So we use a crude heuristic: - // - // Definitions where the cycle occurs behind a lambda are OK - // - // boom = \_ -> boom {} - // - // But otherwise we report an error, e.g. - // - // foo = if b then foo else bar - - for cycle in strongly_connected_components(&nodes_in_cycle, all_successors_without_self) - { - // check whether the cycle is faulty, which is when it has - // a direct successor in the current cycle. This catches things like: - // - // x = x - // - // or - // - // p = q - // q = p - let is_invalid_cycle = match cycle.get(0) { - Some(symbol) => { - let mut succs = direct_successors(symbol); - - succs.retain(|key| cycle.contains(key)); - - !succs.is_empty() - } - None => false, - }; - - if is_invalid_cycle { - // We want to show the entire cycle in the error message, so expand it out. - let mut loc_symbols = Vec::new(); - - for symbol in cycle { - match refs_by_symbol.get(&symbol) { - None => unreachable!( - r#"Symbol `{:?}` not found in refs_by_symbol! refs_by_symbol was: {:?}"#, - symbol, refs_by_symbol - ), - Some((region, _)) => { - loc_symbols.push(Loc::at(*region, symbol)); - } - } - } - - // TODO we don't store those regions any more! - // let regions = Vec::with_capacity(can_defs_by_symbol.len()); - // for def in can_defs_by_symbol.values() { - // regions.push((def.loc_pattern.region, def.loc_expr.region)); - // } - // - // // Sort them by line number to make the report more helpful. - // loc_symbols.sort(); - // regions.sort(); - - // let symbols_in_cycle: Vec = - // loc_symbols.into_iter().map(|s| s.value).collect(); - // - // problems.push(Problem::RuntimeError(RuntimeError::CircularDef( - // symbols_in_cycle.clone(), - // regions.clone(), - // ))); - // - // declarations.push(Declaration::InvalidCycle(symbols_in_cycle, regions)); - panic!("Invalid Cycle"); - } else { - // slightly inefficient, because we know this becomes exactly one DeclareRec already - groups.push(cycle); - } - } - - // now we have a collection of groups whose dependencies are not cyclic. - // They are however not yet topologically sorted. Here we have to get a bit - // creative to get all the definitions in the correct sorted order. - - let mut group_ids = Vec::with_capacity(groups.len()); - let mut symbol_to_group_index = MutMap::default(); - for (i, group) in groups.iter().enumerate() { - for symbol in group { - symbol_to_group_index.insert(*symbol, i); - } - - group_ids.push(i); - } - - let successors_of_group = |group_id: &usize| { - let mut result = MutSet::default(); - - // for each symbol in this group - for symbol in &groups[*group_id] { - // find its successors - for succ in all_successors_without_self(symbol) { - // and add its group to the result - result.insert(symbol_to_group_index[&succ]); - } - } - - // don't introduce any cycles to self - result.remove(group_id); - - result - }; - - match ven_graph::topological_sort_into_groups(&group_ids, successors_of_group) { - Ok(sorted_group_ids) => { - let mut can_defs_by_symbol = can_defs_by_symbol; - let cdbs = &mut can_defs_by_symbol; - for sorted_group in sorted_group_ids.iter().rev() { - for group_id in sorted_group.iter().rev() { - let group = &groups[*group_id]; - - group_to_declaration( - group, - &env.closures, - &mut all_successors_with_self, - cdbs, - &mut declarations, - ); - } - } - } - Err(_) => unreachable!("there should be no cycles now!"), - } - - for problem in problems { - env.problem(problem); - } - - (Ok(declarations), output) - } - } -} - -pub fn references_from_local<'a, T>( - defined_symbol: Symbol, - visited: &'a mut MutSet, - refs_by_def: &'a MutMap, - closures: &'a MutMap, -) -> References -where - T: Debug, -{ - let mut answer: References = References::new(); - - match refs_by_def.get(&defined_symbol) { - Some((_, refs)) => { - visited.insert(defined_symbol); - - for local in refs.lookups.iter() { - if !visited.contains(&local) { - let other_refs: References = - references_from_local(*local, visited, refs_by_def, closures); - - answer.union_mut(other_refs); - } - - answer.lookups.insert(*local); - } - - for call in refs.calls.iter() { - if !visited.contains(&call) { - let other_refs = references_from_call(*call, visited, refs_by_def, closures); - - answer.union_mut(other_refs); - } - - answer.calls.insert(*call); - } - - answer - } - None => answer, - } -} - -pub fn references_from_call<'a, T>( - call_symbol: Symbol, - visited: &'a mut MutSet, - refs_by_def: &'a MutMap, - closures: &'a MutMap, -) -> References -where - T: Debug, -{ - match closures.get(&call_symbol) { - Some(references) => { - let mut answer = references.clone(); - - visited.insert(call_symbol); - - for closed_over_local in references.lookups.iter() { - if !visited.contains(&closed_over_local) { - let other_refs = - references_from_local(*closed_over_local, visited, refs_by_def, closures); - - answer.union_mut(other_refs); - } - - answer.lookups.insert(*closed_over_local); - } - - for call in references.calls.iter() { - if !visited.contains(&call) { - let other_refs = references_from_call(*call, visited, refs_by_def, closures); - - answer.union_mut(other_refs); - } - - answer.calls.insert(*call); - } - - answer - } - None => { - // If the call symbol was not in the closure map, that means we're calling a non-function and - // will get a type mismatch later. For now, assume no references as a result of the "call." - References::new() - } - } -} - -fn local_successors( - references: &References, - closures: &MutMap, -) -> MutSet { - let mut answer = references.lookups.clone(); - - for call_symbol in references.calls.iter() { - answer.extend(call_successors(*call_symbol, closures)); - } - - answer -} - -fn call_successors<'a>( - call_symbol: Symbol, - closures: &'a MutMap, -) -> MutSet { - let mut answer = MutSet::default(); - let mut seen = MutSet::default(); - let mut queue = vec![call_symbol]; - - while let Some(symbol) = queue.pop() { - if seen.contains(&symbol) { - continue; - } - - if let Some(references) = closures.get(&symbol) { - answer.extend(references.lookups.iter().copied()); - queue.extend(references.calls.iter().copied()); - - seen.insert(symbol); - } - } - - answer -} - -fn group_to_declaration( - group: &[Symbol], - closures: &MutMap, - successors: &mut dyn FnMut(&Symbol) -> MutSet, - can_defs_by_symbol: &mut MutMap, - declarations: &mut Vec, -) { - use Declaration::*; - - // We want only successors in the current group, otherwise definitions get duplicated - let filtered_successors = |symbol: &Symbol| -> MutSet { - let mut result = successors(symbol); - - result.retain(|key| group.contains(key)); - result - }; - - // TODO fix this - // Patterns like - // - // { x, y } = someDef - // - // Can bind multiple symbols. When not incorrectly recursive (which is guaranteed in this function), - // normally `someDef` would be inserted twice. We use the region of the pattern as a unique key - // for a definition, so every definition is only inserted (thus typechecked and emitted) once - // let mut seen_pattern_regions: MutSet = MutSet::default(); - - for cycle in strongly_connected_components(&group, filtered_successors) { - if cycle.len() == 1 { - let symbol = &cycle[0]; - - if let Some(can_def) = can_defs_by_symbol.remove(&symbol) { - // Determine recursivity of closures that are not tail-recursive - - let is_recursive = successors(&symbol).contains(&symbol); - - if is_recursive { - declarations.push(DeclareRec(vec![can_def])); - } else { - declarations.push(Declare(can_def)); - } - } - } else { - let mut can_defs = Vec::new(); - - // Topological sort gives us the reverse of the sorting we want! - for symbol in cycle.into_iter().rev() { - if let Some(can_def) = can_defs_by_symbol.remove(&symbol) { - can_defs.push(can_def); - } - } - - declarations.push(DeclareRec(can_defs)); - } - } -} diff --git a/crates/ast/src/lang/core/def/def2.rs b/crates/ast/src/lang/core/def/def2.rs deleted file mode 100644 index 634f7d6718..0000000000 --- a/crates/ast/src/lang/core/def/def2.rs +++ /dev/null @@ -1,60 +0,0 @@ -use roc_module::symbol::IdentId; -use std::fmt::Write as _; // import without risk of name clashing - -use crate::{ - lang::core::expr::{expr2::Expr2, expr2_to_string::expr2_to_string}, - mem_pool::pool::{NodeId, Pool}, -}; - -// A top level definition, not inside a function. For example: `main = "Hello, world!"` -#[derive(Debug)] -pub enum Def2 { - // ValueDef example: `main = "Hello, world!"`. identifier -> `main`, expr -> "Hello, world!" - ValueDef { - identifier_id: IdentId, - expr_id: NodeId, - }, - Blank, - CommentsBefore { - comments: String, - def_id: DefId, - }, - CommentsAfter { - comments: String, - def_id: DefId, - }, -} - -pub type DefId = NodeId; - -pub fn def2_to_string(node_id: DefId, pool: &Pool) -> String { - let mut full_string = String::new(); - let def2 = pool.get(node_id); - - match def2 { - Def2::ValueDef { - identifier_id, - expr_id, - } => { - let _ = write!( - full_string, - "Def2::ValueDef(identifier_id: >>{:?}), expr_id: >>{:?})", - identifier_id, - expr2_to_string(*expr_id, pool) - ); - } - Def2::Blank => { - full_string.push_str("Def2::Blank"); - } - Def2::CommentsBefore { - comments, - def_id: _, - } => full_string.push_str(comments), - Def2::CommentsAfter { - comments, - def_id: _, - } => full_string.push_str(comments), - } - - full_string -} diff --git a/crates/ast/src/lang/core/def/def_to_def2.rs b/crates/ast/src/lang/core/def/def_to_def2.rs deleted file mode 100644 index 957ff28552..0000000000 --- a/crates/ast/src/lang/core/def/def_to_def2.rs +++ /dev/null @@ -1,109 +0,0 @@ -use bumpalo::Bump; -use roc_parse::{ast::CommentOrNewline, parser::SyntaxError}; -use roc_region::all::Region; - -use crate::lang::{core::expr::expr_to_expr2::loc_expr_to_expr2, env::Env, scope::Scope}; - -use super::def2::Def2; - -fn spaces_to_comments(spaces: &[CommentOrNewline]) -> Option { - if !spaces.is_empty() && !all_newlines(spaces) { - let mut all_comments_str = String::new(); - - for comment in spaces.iter().filter(|c_or_nl| !c_or_nl.is_newline()) { - all_comments_str.push_str(&comment.to_string_repr()); - } - - Some(all_comments_str) - } else { - None - } -} - -pub fn toplevel_defs_to_defs2<'a>( - arena: &'a Bump, - env: &mut Env<'a>, - scope: &mut Scope, - parsed_defs: roc_parse::ast::Defs<'a>, - region: Region, -) -> Vec { - let mut result = Vec::with_capacity(parsed_defs.tags.len()); - - for (index, def) in parsed_defs.defs().enumerate() { - let mut def = match def { - Err(roc_parse::ast::ValueDef::Body(&loc_pattern, &loc_expr)) => { - let expr2 = loc_expr_to_expr2(arena, loc_expr, env, scope, region).0; - let expr_id = env.pool.add(expr2); - - use roc_parse::ast::Pattern::*; - - match loc_pattern.value { - Identifier(id_str) => { - let identifier_id = env.ident_ids.get_or_insert(id_str); - - // TODO support with annotation - Def2::ValueDef { - identifier_id, - expr_id, - } - } - other => { - unimplemented!( - "I don't yet know how to convert the pattern {:?} into an expr2", - other - ) - } - } - } - - other => { - unimplemented!( - "I don't know how to make an expr2 from this def yet: {:?}", - other - ) - } - }; - - let spaces_before = &parsed_defs.spaces[parsed_defs.space_before[index].indices()]; - let spaces_after = &parsed_defs.spaces[parsed_defs.space_after[index].indices()]; - - if let Some(comments) = spaces_to_comments(spaces_before) { - let inner_def_id = env.pool.add(def); - def = Def2::CommentsBefore { - comments, - def_id: inner_def_id, - }; - } - - if let Some(comments) = spaces_to_comments(spaces_after) { - let inner_def_id = env.pool.add(def); - def = Def2::CommentsAfter { - comments, - def_id: inner_def_id, - }; - } - - result.push(def) - } - - result -} - -fn all_newlines(comments: &[CommentOrNewline]) -> bool { - comments - .iter() - .all(|com_or_newline| com_or_newline.is_newline()) -} - -pub fn str_to_def2<'a>( - arena: &'a Bump, - input: &'a str, - env: &mut Env<'a>, - scope: &mut Scope, - region: Region, -) -> Result, SyntaxError<'a>> { - match roc_parse::test_helpers::parse_defs_with(arena, input.trim()) { - Ok(defs) => Ok(toplevel_defs_to_defs2(arena, env, scope, defs, region)), - Err(fail) => Err(fail), - } -} diff --git a/crates/ast/src/lang/core/def/mod.rs b/crates/ast/src/lang/core/def/mod.rs deleted file mode 100644 index 7ab541b811..0000000000 --- a/crates/ast/src/lang/core/def/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod def; -pub mod def2; -pub mod def_to_def2; diff --git a/crates/ast/src/lang/core/expr/expr2.rs b/crates/ast/src/lang/core/expr/expr2.rs deleted file mode 100644 index c6e5068b26..0000000000 --- a/crates/ast/src/lang/core/expr/expr2.rs +++ /dev/null @@ -1,228 +0,0 @@ -use arrayvec::ArrayString; -use roc_types::subs::Variable; - -use crate::{ - lang::core::{fun_def::FunctionDef, pattern::Pattern2, val_def::ValueDef}, - mem_pool::{pool::NodeId, pool_str::PoolStr, pool_vec::PoolVec}, -}; -use roc_can::expr::Recursive; -use roc_module::called_via::CalledVia; -use roc_module::low_level::LowLevel; -use roc_module::symbol::Symbol; - -use super::record_field::RecordField; - -pub const ARR_STRING_CAPACITY: usize = 24; -pub type ArrString = ArrayString; - -// TODO make the inner types private? -pub type ExprId = NodeId; - -/// An Expr that fits in 32B. -/// It has a 1B discriminant and variants which hold payloads of at most 31B. -#[derive(Debug)] -pub enum Expr2 { - /// A negative number literal without a dot - SmallInt { - number: IntVal, // 16B - var: Variable, // 4B - style: IntStyle, // 1B - text: PoolStr, // 8B - }, - // TODO(rvcas): rename this eventually - /// A large (over 64-bit) negative number literal without a dot. - /// This variant can't use IntVal because if IntVal stored 128-bit - /// integers, it would be 32B on its own because of alignment. - I128 { - number: i128, // 16B - var: Variable, // 4B - style: IntStyle, // 1B - text: PoolStr, // 8B - }, - // TODO(rvcas): rename this eventually - /// A large (over 64-bit) nonnegative number literal without a dot - /// This variant can't use IntVal because if IntVal stored 128-bit - /// integers, it would be 32B on its own because of alignment. - U128 { - number: u128, // 16B - var: Variable, // 4B - style: IntStyle, // 1B - text: PoolStr, // 8B - }, - /// A floating-point literal (with a dot) - Float { - number: FloatVal, // 16B - var: Variable, // 4B - text: PoolStr, // 8B - }, - /// string literals of length up to 30B - SmallStr(ArrString), // 31B - /// string literals of length 31B or more - Str(PoolStr), // 8B - // Lookups - Var(Symbol), // 8B - InvalidLookup(PoolStr), // 8B - - List { - elem_var: Variable, // 4B - elems: PoolVec, // 8B - }, - If { - cond_var: Variable, // 4B - expr_var: Variable, // 4B - branches: PoolVec<(ExprId, ExprId)>, // 8B - final_else: ExprId, // 4B - }, - When { - cond_var: Variable, // 4B - expr_var: Variable, // 4B - branches: PoolVec, // 8B - cond: ExprId, // 4B - }, - LetRec { - defs: PoolVec, // 8B - body_var: Variable, // 8B - body_id: ExprId, // 4B - }, - LetFunction { - def_id: NodeId, // 4B - body_var: Variable, // 8B - body_id: ExprId, // 4B - }, - LetValue { - def_id: NodeId, // 4B - body_id: ExprId, // 4B - body_var: Variable, // 4B - }, - Call { - args: PoolVec<(Variable, ExprId)>, // 8B - expr_id: ExprId, // 4B - expr_var: Variable, // 4B - fn_var: Variable, // 4B - closure_var: Variable, // 4B - called_via: CalledVia, // 2B - }, - RunLowLevel { - op: LowLevel, // 1B - args: PoolVec<(Variable, ExprId)>, // 8B - ret_var: Variable, // 4B - }, - Closure { - args: PoolVec<(Variable, NodeId)>, // 8B - uniq_symbol: Symbol, // 8B This is a globally unique symbol for the closure - body_id: ExprId, // 4B - function_type: Variable, // 4B - recursive: Recursive, // 1B - extra: NodeId, // 4B - }, - // Product Types - Record { - record_var: Variable, // 4B - fields: PoolVec, // 8B - }, - /// Empty record constant - EmptyRecord, - /// Look up exactly one field on a record, e.g. (expr).foo. - Access { - field: PoolStr, // 4B - expr: ExprId, // 4B - record_var: Variable, // 4B - ext_var: Variable, // 4B - field_var: Variable, // 4B - }, - - /// field accessor as a function, e.g. (.foo) expr - Accessor { - function_var: Variable, // 4B - closure_var: Variable, // 4B - field: PoolStr, // 4B - record_var: Variable, // 4B - ext_var: Variable, // 4B - field_var: Variable, // 4B - }, - Update { - symbol: Symbol, // 8B - updates: PoolVec, // 8B - record_var: Variable, // 4B - ext_var: Variable, // 4B - }, - - // Sum Types - Tag { - name: PoolStr, // 4B - variant_var: Variable, // 4B - ext_var: Variable, // 4B - arguments: PoolVec<(Variable, ExprId)>, // 8B - }, - Blank, // Rendered as empty box in editor - - // Compiles, but will crash if reached - RuntimeError(/* TODO make a version of RuntimeError that fits in 15B */), -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub enum Problem { - RanOutOfNodeIds, -} - -pub type Res = Result; - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum IntStyle { - Decimal, - Octal, - Hex, - Binary, -} - -impl IntStyle { - pub fn from_base(base: roc_parse::ast::Base) -> Self { - use roc_parse::ast::Base; - match base { - Base::Decimal => Self::Decimal, - Base::Octal => Self::Octal, - Base::Hex => Self::Hex, - Base::Binary => Self::Binary, - } - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum IntVal { - I64(i64), - U64(u64), - I32(i32), - U32(u32), - I16(i16), - U16(u16), - I8(i8), - U8(u8), -} - -#[test] -fn size_of_intval() { - assert_eq!(std::mem::size_of::(), 16); -} - -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum FloatVal { - F64(f64), - F32(f32), -} - -#[derive(Debug)] -pub struct WhenBranch { - pub patterns: PoolVec, // 4B - pub body: ExprId, // 3B - pub guard: Option, // 4B -} - -/// This is overflow data from a Closure variant, which needs to store -/// more than 32B of total data -#[derive(Debug)] -pub struct ClosureExtra { - pub return_type: Variable, // 4B - pub captured_symbols: PoolVec<(Symbol, Variable)>, // 8B - pub closure_type: Variable, // 4B - pub closure_ext_var: Variable, // 4B -} diff --git a/crates/ast/src/lang/core/expr/expr2_to_string.rs b/crates/ast/src/lang/core/expr/expr2_to_string.rs deleted file mode 100644 index cb94e8af71..0000000000 --- a/crates/ast/src/lang/core/expr/expr2_to_string.rs +++ /dev/null @@ -1,164 +0,0 @@ -use super::expr2::{Expr2, ExprId}; -use crate::{ - lang::core::{expr::record_field::RecordField, val_def::value_def_to_string}, - mem_pool::pool::Pool, -}; -use roc_types::subs::Variable; -use std::fmt::Write as _; // import without risk of name clashing - -pub fn expr2_to_string(node_id: ExprId, pool: &Pool) -> String { - let mut full_string = String::new(); - let expr2 = pool.get(node_id); - - expr2_to_string_helper(expr2, 0, pool, &mut full_string); - - full_string -} - -fn get_spacing(indent_level: usize) -> String { - std::iter::repeat(" ") - .take(indent_level) - .collect::>() - .join("") -} - -fn expr2_to_string_helper( - expr2: &Expr2, - indent_level: usize, - pool: &Pool, - out_string: &mut String, -) { - out_string.push_str(&get_spacing(indent_level)); - - match expr2 { - Expr2::SmallStr(arr_string) => { - let _ = write!(out_string, "SmallStr(\"{}\")", arr_string.as_str()); - } - Expr2::Str(pool_str) => { - let _ = write!(out_string, "Str(\"{}\")", pool_str.as_str(pool)); - } - Expr2::Blank => out_string.push_str("Blank"), - Expr2::EmptyRecord => out_string.push_str("EmptyRecord"), - Expr2::Record { record_var, fields } => { - out_string.push_str("Record:\n"); - out_string.push_str(&var_to_string(record_var, indent_level + 1)); - - let _ = writeln!(out_string, "{}fields: [", get_spacing(indent_level + 1)); - - let mut first_child = true; - - for field in fields.iter(pool) { - if !first_child { - out_string.push_str(", ") - } else { - first_child = false; - } - - match field { - RecordField::InvalidLabelOnly(pool_str, var) => { - let _ = write!( - out_string, - "{}({}, Var({:?})", - get_spacing(indent_level + 2), - pool_str.as_str(pool), - var, - ); - } - RecordField::LabelOnly(pool_str, var, symbol) => { - let _ = write!( - out_string, - "{}({}, Var({:?}), Symbol({:?})", - get_spacing(indent_level + 2), - pool_str.as_str(pool), - var, - symbol - ); - } - RecordField::LabeledValue(pool_str, var, val_node_id) => { - let _ = writeln!( - out_string, - "{}({}, Var({:?}), Expr2(", - get_spacing(indent_level + 2), - pool_str.as_str(pool), - var, - ); - - let val_expr2 = pool.get(*val_node_id); - expr2_to_string_helper(val_expr2, indent_level + 3, pool, out_string); - let _ = writeln!(out_string, "{})", get_spacing(indent_level + 2)); - } - } - } - - let _ = writeln!(out_string, "{}]", get_spacing(indent_level + 1)); - } - Expr2::List { elem_var, elems } => { - out_string.push_str("List:\n"); - out_string.push_str(&var_to_string(elem_var, indent_level + 1)); - let _ = writeln!(out_string, "{}elems: [\n", get_spacing(indent_level + 1)); - - let mut first_elt = true; - - for elem_expr2_id in elems.iter(pool) { - if !first_elt { - out_string.push_str(", ") - } else { - first_elt = false; - } - - let elem_expr2 = pool.get(*elem_expr2_id); - - expr2_to_string_helper(elem_expr2, indent_level + 2, pool, out_string) - } - - let _ = writeln!(out_string, "{}]", get_spacing(indent_level + 1)); - } - Expr2::InvalidLookup(pool_str) => { - let _ = write!(out_string, "InvalidLookup({})", pool_str.as_str(pool)); - } - Expr2::SmallInt { text, .. } => { - let _ = write!(out_string, "SmallInt({})", text.as_str(pool)); - } - Expr2::LetValue { - def_id, body_id, .. - } => { - let _ = write!( - out_string, - "LetValue(def_id: >>{:?}), body_id: >>{:?})", - value_def_to_string(pool.get(*def_id), pool), - pool.get(*body_id) - ); - } - Expr2::Call { .. } => { - let _ = write!(out_string, "Call({expr2:?})"); - } - Expr2::Closure { args, .. } => { - out_string.push_str("Closure:\n"); - let _ = writeln!(out_string, "{}args: [", get_spacing(indent_level + 1)); - - for (_, pattern_id) in args.iter(pool) { - let arg_pattern2 = pool.get(*pattern_id); - - let _ = writeln!( - out_string, - "{}{:?}", - get_spacing(indent_level + 2), - arg_pattern2 - ); - } - } - &Expr2::Var { .. } => { - let _ = write!(out_string, "{expr2:?}"); - } - Expr2::RuntimeError { .. } => { - out_string.push_str("RuntimeError\n"); - } - other => todo!("Implement for {:?}", other), - } - - out_string.push('\n'); -} - -fn var_to_string(some_var: &Variable, indent_level: usize) -> String { - format!("{}Var({:?})\n", get_spacing(indent_level + 1), some_var) -} diff --git a/crates/ast/src/lang/core/expr/expr_to_expr2.rs b/crates/ast/src/lang/core/expr/expr_to_expr2.rs deleted file mode 100644 index b494abfc7e..0000000000 --- a/crates/ast/src/lang/core/expr/expr_to_expr2.rs +++ /dev/null @@ -1,702 +0,0 @@ -use bumpalo::Bump; -use roc_can::expr::{IntValue, Recursive}; -use roc_can::num::{ - finish_parsing_base, finish_parsing_float, finish_parsing_num, ParsedNumResult, -}; -use roc_can::operator::desugar_expr; -use roc_collections::all::MutSet; -use roc_module::symbol::Symbol; -use roc_parse::ident::Accessor; -use roc_parse::{ast::Expr, pattern::PatternType}; -use roc_problem::can::{Problem, RuntimeError}; -use roc_region::all::{Loc, Region}; - -use super::{expr2::Expr2, output::Output}; -use crate::canonicalization::canonicalize::{ - canonicalize_fields, canonicalize_lookup, canonicalize_when_branch, CanonicalizeRecordProblem, -}; -use crate::lang::core::declaration::decl_to_let; -use crate::lang::core::def::def::{canonicalize_defs, sort_can_defs}; -use crate::lang::core::expr::expr2::ClosureExtra; -use crate::lang::core::pattern::to_pattern2; -use crate::lang::core::str::flatten_str_literal; -use crate::mem_pool::shallow_clone::ShallowClone; -use crate::{ - lang::{ - core::expr::expr2::{ExprId, FloatVal, IntStyle, IntVal}, - env::Env, - scope::Scope, - }, - mem_pool::{pool_str::PoolStr, pool_vec::PoolVec}, -}; - -pub fn loc_expr_to_expr2<'a>( - arena: &'a Bump, - loc_expr: Loc>, - env: &mut Env<'a>, - scope: &mut Scope, - region: Region, -) -> (Expr2, Output) { - let desugared_loc_expr = desugar_expr(arena, arena.alloc(loc_expr)); - - expr_to_expr2(env, scope, arena.alloc(desugared_loc_expr.value), region) -} - -const ZERO: Region = Region::zero(); - -pub fn expr_to_expr2<'a>( - env: &mut Env<'a>, - scope: &mut Scope, - parse_expr: &'a roc_parse::ast::Expr<'a>, - region: Region, -) -> (Expr2, self::Output) { - use roc_parse::ast::Expr::*; - //dbg!("{:?}", parse_expr); - - match parse_expr { - Float(string) => { - match finish_parsing_float(string) { - Ok((string_without_suffix, float, _bound)) => { - let expr = Expr2::Float { - number: FloatVal::F64(float), - var: env.var_store.fresh(), - text: PoolStr::new(string_without_suffix, env.pool), - }; - - (expr, Output::default()) - } - Err((raw, error)) => { - // emit runtime error - let runtime_error = RuntimeError::InvalidFloat(error, ZERO, raw.into()); - - env.problem(Problem::RuntimeError(runtime_error)); - // - // Expr::RuntimeError(runtime_error) - todo!() - } - } - } - Num(string) => { - match finish_parsing_num(string) { - Ok(( - parsed, - ParsedNumResult::UnknownNum(int, _) | ParsedNumResult::Int(int, _), - )) => { - let expr = Expr2::SmallInt { - number: IntVal::I64(match int { - IntValue::U128(_) => todo!(), - IntValue::I128(n) => i128::from_ne_bytes(n) as i64, // FIXME - }), - var: env.var_store.fresh(), - // TODO non-hardcode - style: IntStyle::Decimal, - text: PoolStr::new(parsed, env.pool), - }; - - (expr, Output::default()) - } - Ok((parsed, ParsedNumResult::Float(float, _))) => { - let expr = Expr2::Float { - number: FloatVal::F64(float), - var: env.var_store.fresh(), - text: PoolStr::new(parsed, env.pool), - }; - - (expr, Output::default()) - } - Err((raw, error)) => { - // emit runtime error - let runtime_error = RuntimeError::InvalidInt( - error, - roc_parse::ast::Base::Decimal, - ZERO, - raw.into(), - ); - - env.problem(Problem::RuntimeError(runtime_error)); - // - // Expr::RuntimeError(runtime_error) - todo!() - } - } - } - NonBase10Int { - string, - base, - is_negative, - } => { - match finish_parsing_base(string, *base, *is_negative) { - Ok((int, _bound)) => { - let expr = Expr2::SmallInt { - number: IntVal::I64(match int { - IntValue::U128(_) => todo!(), - IntValue::I128(n) => i128::from_ne_bytes(n) as i64, // FIXME - }), - var: env.var_store.fresh(), - // TODO non-hardcode - style: IntStyle::from_base(*base), - text: PoolStr::new(string, env.pool), - }; - - (expr, Output::default()) - } - Err((raw, error)) => { - // emit runtime error - let runtime_error = RuntimeError::InvalidInt(error, *base, ZERO, raw.into()); - - env.problem(Problem::RuntimeError(runtime_error)); - // - // Expr::RuntimeError(runtime_error) - todo!() - } - } - } - - Str(literal) => flatten_str_literal(env, scope, literal), - - List(items) => { - let mut output = Output::default(); - let output_ref = &mut output; - - let elems: PoolVec = PoolVec::with_capacity(items.len() as u32, env.pool); - - for (node_id, item) in elems.iter_node_ids().zip(items.iter()) { - let (expr, sub_output) = expr_to_expr2(env, scope, &item.value, item.region); - - output_ref.union(sub_output); - - let expr_id = env.pool.add(expr); - env.pool[node_id] = expr_id; - } - - let expr = Expr2::List { - elem_var: env.var_store.fresh(), - elems, - }; - - (expr, output) - } - - Tag(tag) => { - // a tag without any arguments - ( - Expr2::Tag { - name: PoolStr::new(tag, env.pool), - variant_var: env.var_store.fresh(), - ext_var: env.var_store.fresh(), - arguments: PoolVec::empty(env.pool), - }, - Output::default(), - ) - } - - RecordUpdate { - fields, - update: loc_update, - } => { - let (can_update, update_out) = - expr_to_expr2(env, scope, &loc_update.value, loc_update.region); - - if let Expr2::Var(symbol) = &can_update { - match canonicalize_fields(env, scope, fields.items) { - Ok((can_fields, mut output)) => { - output.references.union_mut(update_out.references); - - let answer = Expr2::Update { - record_var: env.var_store.fresh(), - ext_var: env.var_store.fresh(), - symbol: *symbol, - updates: can_fields, - }; - - (answer, output) - } - Err(CanonicalizeRecordProblem::InvalidOptionalValue { - field_name: _, - field_region: _, - record_region: _, - }) => { - // let runtime_error = roc_problem::can::RuntimeError::InvalidOptionalValue { - // field_name, - // field_region, - // record_region, - // }; - // - // env.problem(Problem::RuntimeError(runtime_error)); - - todo!() - } - } - } else { - // only (optionally qualified) variables can be updated, not arbitrary expressions - - // let error = roc_problem::can::RuntimeError::InvalidRecordUpdate { - // region: can_update.region, - // }; - // - // let answer = Expr::RuntimeError(error.clone()); - // - // env.problems.push(Problem::RuntimeError(error)); - // - // (answer, Output::default()) - todo!("{:?}", &can_update) - } - } - - Record(fields) => { - if fields.is_empty() { - (Expr2::EmptyRecord, Output::default()) - } else { - match canonicalize_fields(env, scope, fields.items) { - Ok((can_fields, output)) => ( - Expr2::Record { - record_var: env.var_store.fresh(), - fields: can_fields, - }, - output, - ), - Err(CanonicalizeRecordProblem::InvalidOptionalValue { - field_name: _, - field_region: _, - record_region: _, - }) => { - // let runtime_error = RuntimeError::InvalidOptionalValue { - // field_name, - // field_region, - // record_region, - // }; - // - // env.problem(runtime_error); - // ( - // Expr::RuntimeError( - // ), - // Output::default(), - // - // ) - todo!() - } - } - } - } - - RecordAccess(record_expr, field) => { - // TODO - let region = ZERO; - let (record_expr_id, output) = to_expr_id(env, scope, record_expr, region); - - ( - Expr2::Access { - record_var: env.var_store.fresh(), - field_var: env.var_store.fresh(), - ext_var: env.var_store.fresh(), - expr: record_expr_id, - field: PoolStr::new(field, env.pool), - }, - output, - ) - } - - AccessorFunction(Accessor::RecordField(field)) => ( - Expr2::Accessor { - function_var: env.var_store.fresh(), - record_var: env.var_store.fresh(), - ext_var: env.var_store.fresh(), - closure_var: env.var_store.fresh(), - field_var: env.var_store.fresh(), - field: PoolStr::new(field, env.pool), - }, - Output::default(), - ), - - If(branches, final_else) => { - let mut new_branches = Vec::with_capacity(branches.len()); - let mut output = Output::default(); - - for (condition, then_branch) in branches.iter() { - let (cond, cond_output) = - expr_to_expr2(env, scope, &condition.value, condition.region); - - let (then_expr, then_output) = - expr_to_expr2(env, scope, &then_branch.value, then_branch.region); - - output.references.union_mut(cond_output.references); - output.references.union_mut(then_output.references); - - new_branches.push((env.pool.add(cond), env.pool.add(then_expr))); - } - - let (else_expr, else_output) = - expr_to_expr2(env, scope, &final_else.value, final_else.region); - - output.references.union_mut(else_output.references); - - let expr = Expr2::If { - cond_var: env.var_store.fresh(), - expr_var: env.var_store.fresh(), - branches: PoolVec::new(new_branches.into_iter(), env.pool), - final_else: env.pool.add(else_expr), - }; - - (expr, output) - } - - When(loc_cond, branches) => { - // Infer the condition expression's type. - let cond_var = env.var_store.fresh(); - let (can_cond, mut output) = - expr_to_expr2(env, scope, &loc_cond.value, loc_cond.region); - - // the condition can never be a tail-call - output.tail_call = None; - - let can_branches = PoolVec::with_capacity(branches.len() as u32, env.pool); - - for (node_id, branch) in can_branches.iter_node_ids().zip(branches.iter()) { - let (can_when_branch, branch_references) = - canonicalize_when_branch(env, scope, branch, &mut output); - - output.references.union_mut(branch_references); - - env.pool[node_id] = can_when_branch; - } - - // A "when" with no branches is a runtime error, but it will mess things up - // if code gen mistakenly thinks this is a tail call just because its condition - // happened to be one. (The condition gave us our initial output value.) - if branches.is_empty() { - output.tail_call = None; - } - - // Incorporate all three expressions into a combined Output value. - let expr = Expr2::When { - expr_var: env.var_store.fresh(), - cond_var, - cond: env.pool.add(can_cond), - branches: can_branches, - }; - - (expr, output) - } - - Closure(loc_arg_patterns, loc_body_expr) => { - // The globally unique symbol that will refer to this closure once it gets converted - // into a top-level procedure for code gen. - // - // In the Foo module, this will look something like Foo.$1 or Foo.$2. - let (symbol, is_anonymous) = match env.closure_name_symbol { - Some(symbol) => (symbol, false), - None => (env.gen_unique_symbol(), true), - }; - env.closure_name_symbol = None; - - // The body expression gets a new scope for canonicalization. - // Shadow `scope` to make sure we don't accidentally use the original one for the - // rest of this block, but keep the original around for later diffing. - let original_scope = scope; - let mut scope = original_scope.shallow_clone(); - let can_args = PoolVec::with_capacity(loc_arg_patterns.len() as u32, env.pool); - let mut output = Output::default(); - - let mut bound_by_argument_patterns = MutSet::default(); - - for (node_id, loc_pattern) in can_args.iter_node_ids().zip(loc_arg_patterns.iter()) { - let (new_output, can_arg) = to_pattern2( - env, - &mut scope, - roc_parse::pattern::PatternType::FunctionArg, - &loc_pattern.value, - loc_pattern.region, - ); - - bound_by_argument_patterns - .extend(new_output.references.bound_symbols.iter().copied()); - - output.union(new_output); - - let pattern_id = env.add(can_arg, loc_pattern.region); - env.pool[node_id] = (env.var_store.fresh(), pattern_id); - } - - let (body_expr, new_output) = - expr_to_expr2(env, &mut scope, &loc_body_expr.value, loc_body_expr.region); - - let mut captured_symbols: MutSet = - new_output.references.lookups.iter().copied().collect(); - - // filter out the closure's name itself - captured_symbols.remove(&symbol); - - // symbols bound either in this pattern or deeper down are not captured! - captured_symbols.retain(|s| !new_output.references.bound_symbols.contains(s)); - captured_symbols.retain(|s| !bound_by_argument_patterns.contains(s)); - - // filter out top-level symbols - // those will be globally available, and don't need to be captured - captured_symbols.retain(|s| !env.top_level_symbols.contains(s)); - - // filter out imported symbols - // those will be globally available, and don't need to be captured - captured_symbols.retain(|s| s.module_id() == env.home); - - // TODO any Closure that has an empty `captured_symbols` list could be excluded! - - output.union(new_output); - - // filter out aliases - captured_symbols.retain(|s| !output.references.referenced_aliases.contains(s)); - - // filter out functions that don't close over anything - captured_symbols.retain(|s| !output.non_closures.contains(s)); - - // Now that we've collected all the references, check to see if any of the args we defined - // went unreferenced. If any did, report them as unused arguments. - for (sub_symbol, region) in scope.symbols() { - if !original_scope.contains_symbol(sub_symbol) { - if !output.references.has_lookup(sub_symbol) { - // The body never referenced this argument we declared. It's an unused argument! - env.problem(Problem::UnusedArgument( - symbol, - is_anonymous, - sub_symbol, - region, - )); - } - - // We shouldn't ultimately count arguments as referenced locals. Otherwise, - // we end up with weird conclusions like the expression (\x -> x + 1) - // references the (nonexistent) local variable x! - output.references.lookups.remove(&sub_symbol); - } - } - - env.register_closure(symbol, output.references.clone()); - - let mut captured_symbols: Vec<_> = captured_symbols - .into_iter() - .map(|s| (s, env.var_store.fresh())) - .collect(); - - // sort symbols, so we know the order in which they're stored in the closure record - captured_symbols.sort(); - - // store that this function doesn't capture anything. It will be promoted to a - // top-level function, and does not need to be captured by other surrounding functions. - if captured_symbols.is_empty() { - output.non_closures.insert(symbol); - } - - let captured_symbols = PoolVec::new(captured_symbols.into_iter(), env.pool); - - let extra = ClosureExtra { - return_type: env.var_store.fresh(), // 4B - captured_symbols, // 8B - closure_type: env.var_store.fresh(), // 4B - closure_ext_var: env.var_store.fresh(), // 4B - }; - - ( - Expr2::Closure { - function_type: env.var_store.fresh(), - uniq_symbol: symbol, - recursive: Recursive::NotRecursive, - args: can_args, - body_id: env.add(body_expr, loc_body_expr.region), - extra: env.pool.add(extra), - }, - output, - ) - } - - Apply(loc_fn, loc_args, application_style) => { - // The expression that evaluates to the function being called, e.g. `foo` in - // (foo) bar baz - let fn_region = loc_fn.region; - - // Canonicalize the function expression and its arguments - let (fn_expr, mut output) = expr_to_expr2(env, scope, &loc_fn.value, fn_region); - // The function's return type - let args = PoolVec::with_capacity(loc_args.len() as u32, env.pool); - - for (node_id, loc_arg) in args.iter_node_ids().zip(loc_args.iter()) { - let (arg_expr_id, arg_out) = to_expr_id(env, scope, &loc_arg.value, loc_arg.region); - - env.pool[node_id] = (env.var_store.fresh(), arg_expr_id); - - output.references.union_mut(arg_out.references); - } - - // Default: We're not tail-calling a symbol (by name), we're tail-calling a function value. - output.tail_call = None; - - let expr = match fn_expr { - Expr2::Var(ref symbol) => { - output.references.calls.insert(*symbol); - - // we're tail-calling a symbol by name, check if it's the tail-callable symbol - output.tail_call = match &env.tailcallable_symbol { - Some(tc_sym) if *tc_sym == *symbol => Some(*symbol), - Some(_) | None => None, - }; - - // IDEA: Expr2::CallByName? - let fn_expr_id = env.add(fn_expr, fn_region); - Expr2::Call { - args, - expr_id: fn_expr_id, - expr_var: env.var_store.fresh(), - fn_var: env.var_store.fresh(), - closure_var: env.var_store.fresh(), - called_via: *application_style, - } - } - Expr2::RuntimeError() => { - // We can't call a runtime error; bail out by propagating it! - return (fn_expr, output); - } - Expr2::Tag { - variant_var, - ext_var, - name, - .. - } => Expr2::Tag { - variant_var, - ext_var, - name, - arguments: args, - }, - _ => { - // This could be something like ((if True then fn1 else fn2) arg1 arg2). - let fn_expr_id = env.add(fn_expr, fn_region); - Expr2::Call { - args, - expr_id: fn_expr_id, - expr_var: env.var_store.fresh(), - fn_var: env.var_store.fresh(), - closure_var: env.var_store.fresh(), - called_via: *application_style, - } - } - }; - - (expr, output) - } - - Defs(loc_defs, loc_ret) => { - let (unsorted, mut scope, defs_output, symbols_introduced) = canonicalize_defs( - env, - Output::default(), - scope, - loc_defs, - PatternType::DefExpr, - ); - - // The def as a whole is a tail call iff its return expression is a tail call. - // Use its output as a starting point because its tail_call already has the right answer! - let (ret_expr, mut output) = - expr_to_expr2(env, &mut scope, &loc_ret.value, loc_ret.region); - - output - .introduced_variables - .union(&defs_output.introduced_variables); - - output.references.union_mut(defs_output.references); - - // Now that we've collected all the references, check to see if any of the new idents - // we defined went unused by the return expression. If any were unused, report it. - for (symbol, region) in symbols_introduced { - if !output.references.has_lookup(symbol) { - env.problem(Problem::UnusedDef(symbol, region)); - } - } - - let (can_defs, output) = sort_can_defs(env, unsorted, output); - - match can_defs { - Ok(decls) => { - let mut expr = ret_expr; - - for declaration in decls.into_iter().rev() { - expr = decl_to_let(env.pool, env.var_store, declaration, expr); - } - - (expr, output) - } - Err(_err) => { - // TODO: fix this to be something from Expr2 - // (RuntimeError(err), output) - todo!() - } - } - } - - PrecedenceConflict { .. } => { - // use roc_problem::can::RuntimeError::*; - // - // let problem = PrecedenceProblem::BothNonAssociative( - // *whole_region, - // binop1.clone(), - // binop2.clone(), - // ); - // - // env.problem(Problem::PrecedenceProblem(problem.clone())); - // - // ( - // RuntimeError(InvalidPrecedence(problem, region)), - // Output::default(), - // ) - todo!() - } - MalformedClosure => { - // use roc_problem::can::RuntimeError::*; - // (RuntimeError(MalformedClosure(region)), Output::default()) - todo!() - } - MalformedIdent(_name, _problem) => { - // use roc_problem::can::RuntimeError::*; - // - // let problem = MalformedIdentifier((*name).into(), region); - // env.problem(Problem::RuntimeError(problem.clone())); - // - // (RuntimeError(problem), Output::default()) - todo!() - } - Var { - module_name, // module_name will only be filled if the original Roc code stated something like `5 + SomeModule.myVar`, module_name will be blank if it was `5 + myVar` - ident, - } => canonicalize_lookup(env, scope, module_name, ident, region), - - ParensAround(sub_expr) => expr_to_expr2(env, scope, sub_expr, region), - - // Below this point, we shouln't see any of these nodes anymore because - // operator desugaring should have removed them! - bad_expr @ SpaceBefore(_, _) => { - panic!( - "A SpaceBefore did not get removed during operator desugaring somehow: {bad_expr:#?}" - ); - } - bad_expr @ SpaceAfter(_, _) => { - panic!( - "A SpaceAfter did not get removed during operator desugaring somehow: {bad_expr:#?}" - ); - } - bad_expr @ BinOps { .. } => { - panic!("A binary operator chain did not get desugared somehow: {bad_expr:#?}"); - } - bad_expr @ UnaryOp(_, _) => { - panic!("A unary operator did not get desugared somehow: {bad_expr:#?}"); - } - - rest => todo!("not yet implemented {:?}", rest), - } -} - -pub fn to_expr_id<'a>( - env: &mut Env<'a>, - scope: &mut Scope, - parse_expr: &'a roc_parse::ast::Expr<'a>, - region: Region, -) -> (ExprId, Output) { - let (expr, output) = expr_to_expr2(env, scope, parse_expr, region); - - (env.add(expr, region), output) -} diff --git a/crates/ast/src/lang/core/expr/introduced_vars.rs b/crates/ast/src/lang/core/expr/introduced_vars.rs deleted file mode 100644 index 91d5b7cdbe..0000000000 --- a/crates/ast/src/lang/core/expr/introduced_vars.rs +++ /dev/null @@ -1,51 +0,0 @@ -use roc_collections::all::MutMap; -use roc_module::ident::Lowercase; -use roc_module::symbol::Symbol; -use roc_types::subs::Variable; - -#[derive(Clone, Debug, PartialEq, Eq, Default)] -pub struct IntroducedVariables { - // Rigids must be unique within a type annotation. - // E.g. in `identity : a -> a`, there should only be one - // variable (a rigid one, with name "a"). - // Hence `rigids : Map` - // - // But then between annotations, the same name can occur multiple times, - // but a variable can only have one name. Therefore - // `ftv : Map`. - pub wildcards: Vec, - pub var_by_name: MutMap, - pub name_by_var: MutMap, - pub host_exposed_aliases: MutMap, -} - -impl IntroducedVariables { - pub fn insert_named(&mut self, name: Lowercase, var: Variable) { - self.var_by_name.insert(name.clone(), var); - self.name_by_var.insert(var, name); - } - - pub fn insert_wildcard(&mut self, var: Variable) { - self.wildcards.push(var); - } - - pub fn insert_host_exposed_alias(&mut self, symbol: Symbol, var: Variable) { - self.host_exposed_aliases.insert(symbol, var); - } - - pub fn union(&mut self, other: &Self) { - self.wildcards.extend(other.wildcards.iter().cloned()); - self.var_by_name.extend(other.var_by_name.clone()); - self.name_by_var.extend(other.name_by_var.clone()); - self.host_exposed_aliases - .extend(other.host_exposed_aliases.clone()); - } - - pub fn var_by_name(&self, name: &Lowercase) -> Option<&Variable> { - self.var_by_name.get(name) - } - - pub fn name_by_var(&self, var: Variable) -> Option<&Lowercase> { - self.name_by_var.get(&var) - } -} diff --git a/crates/ast/src/lang/core/expr/mod.rs b/crates/ast/src/lang/core/expr/mod.rs deleted file mode 100644 index bb27a4ad9b..0000000000 --- a/crates/ast/src/lang/core/expr/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod expr2; -pub mod expr2_to_string; -pub mod expr_to_expr2; -mod introduced_vars; -pub(crate) mod output; -pub mod record_field; diff --git a/crates/ast/src/lang/core/expr/output.rs b/crates/ast/src/lang/core/expr/output.rs deleted file mode 100644 index 4287e3f72f..0000000000 --- a/crates/ast/src/lang/core/expr/output.rs +++ /dev/null @@ -1,30 +0,0 @@ -use crate::{ - lang::core::{def::def::References, types::Alias}, - mem_pool::pool::NodeId, -}; -use roc_collections::all::{MutMap, MutSet}; -use roc_module::symbol::Symbol; - -use super::introduced_vars::IntroducedVariables; - -#[derive(Clone, Default, Debug, PartialEq)] -pub struct Output { - pub references: References, - pub tail_call: Option, - pub introduced_variables: IntroducedVariables, - pub aliases: MutMap>, - pub non_closures: MutSet, -} - -impl Output { - pub fn union(&mut self, other: Self) { - self.references.union_mut(other.references); - - if let (None, Some(later)) = (self.tail_call, other.tail_call) { - self.tail_call = Some(later); - } - - self.aliases.extend(other.aliases); - self.non_closures.extend(other.non_closures); - } -} diff --git a/crates/ast/src/lang/core/expr/record_field.rs b/crates/ast/src/lang/core/expr/record_field.rs deleted file mode 100644 index aaf464799f..0000000000 --- a/crates/ast/src/lang/core/expr/record_field.rs +++ /dev/null @@ -1,49 +0,0 @@ -use roc_types::subs::Variable; - -use crate::mem_pool::pool_str::PoolStr; -use roc_module::symbol::Symbol; - -use super::expr2::ExprId; - -#[derive(Debug)] -pub enum RecordField { - InvalidLabelOnly(PoolStr, Variable), - LabelOnly(PoolStr, Variable, Symbol), - LabeledValue(PoolStr, Variable, ExprId), -} - -use RecordField::*; - -impl RecordField { - pub fn get_record_field_var(&self) -> &Variable { - match self { - InvalidLabelOnly(_, var) => var, - LabelOnly(_, var, _) => var, - LabeledValue(_, var, _) => var, - } - } - - pub fn get_record_field_pool_str(&self) -> &PoolStr { - match self { - InvalidLabelOnly(pool_str, _) => pool_str, - LabelOnly(pool_str, _, _) => pool_str, - LabeledValue(pool_str, _, _) => pool_str, - } - } - - pub fn get_record_field_pool_str_mut(&mut self) -> &mut PoolStr { - match self { - InvalidLabelOnly(pool_str, _) => pool_str, - LabelOnly(pool_str, _, _) => pool_str, - LabeledValue(pool_str, _, _) => pool_str, - } - } - - pub fn get_record_field_val_node_id(&self) -> Option { - match self { - InvalidLabelOnly(_, _) => None, - LabelOnly(_, _, _) => None, - LabeledValue(_, _, field_val_id) => Some(*field_val_id), - } - } -} diff --git a/crates/ast/src/lang/core/fun_def.rs b/crates/ast/src/lang/core/fun_def.rs deleted file mode 100644 index e8aa642a86..0000000000 --- a/crates/ast/src/lang/core/fun_def.rs +++ /dev/null @@ -1,61 +0,0 @@ -use crate::{ - lang::rigids::Rigids, - mem_pool::{pool::NodeId, pool_vec::PoolVec, shallow_clone::ShallowClone}, -}; -use roc_module::symbol::Symbol; -use roc_types::subs::Variable; - -use super::{ - expr::expr2::ExprId, - pattern::PatternId, - types::{Type2, TypeId}, -}; - -#[derive(Debug)] -pub enum FunctionDef { - WithAnnotation { - name: Symbol, // 8B - arguments: PoolVec<(NodeId, PatternId)>, // 8B - rigids: NodeId, // 4B - return_type: TypeId, // 4B - body_id: ExprId, // 4B - }, - NoAnnotation { - name: Symbol, // 8B - arguments: PoolVec<(Variable, PatternId)>, // 8B - return_var: Variable, // 4B - body_id: ExprId, // 4B - }, -} - -impl ShallowClone for FunctionDef { - fn shallow_clone(&self) -> Self { - match self { - Self::WithAnnotation { - name, - arguments, - rigids, - return_type, - body_id, - } => Self::WithAnnotation { - name: *name, - arguments: arguments.shallow_clone(), - rigids: *rigids, - return_type: *return_type, - body_id: *body_id, - }, - - Self::NoAnnotation { - name, - arguments, - return_var, - body_id, - } => Self::NoAnnotation { - name: *name, - arguments: arguments.shallow_clone(), - return_var: *return_var, - body_id: *body_id, - }, - } - } -} diff --git a/crates/ast/src/lang/core/header.rs b/crates/ast/src/lang/core/header.rs deleted file mode 100644 index 4b10dad537..0000000000 --- a/crates/ast/src/lang/core/header.rs +++ /dev/null @@ -1,10 +0,0 @@ -use super::expr::expr2::ExprId; - -#[derive(Debug)] -pub struct AppHeader { - pub app_name: String, - pub packages_base: String, - pub imports: Vec, - pub provides: Vec, - pub ast_node_id: ExprId, // TODO probably want to create and use HeaderId -} diff --git a/crates/ast/src/lang/core/mod.rs b/crates/ast/src/lang/core/mod.rs deleted file mode 100644 index 801f6afa18..0000000000 --- a/crates/ast/src/lang/core/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub mod ast; -mod declaration; -pub mod def; -pub mod expr; -pub mod fun_def; -pub mod header; -pub mod pattern; -pub mod str; -pub mod types; -pub mod val_def; diff --git a/crates/ast/src/lang/core/pattern.rs b/crates/ast/src/lang/core/pattern.rs deleted file mode 100644 index 3399bc97ec..0000000000 --- a/crates/ast/src/lang/core/pattern.rs +++ /dev/null @@ -1,643 +0,0 @@ -#![allow(clippy::all)] -#![allow(dead_code)] -#![allow(unused_imports)] - -use bumpalo::collections::Vec as BumpVec; -use roc_can::expr::IntValue; -use roc_can::num::{ - finish_parsing_base, finish_parsing_float, finish_parsing_num, ParsedNumResult, -}; -use roc_collections::all::BumpMap; -use roc_error_macros::internal_error; -use roc_module::symbol::{Interns, Symbol}; -use roc_parse::ast::{StrLiteral, StrSegment}; -use roc_parse::pattern::PatternType; -use roc_problem::can::{MalformedPatternProblem, Problem, RuntimeError, ShadowKind}; -use roc_region::all::Region; -use roc_types::subs::Variable; - -use crate::ast_error::{ASTResult, UnexpectedPattern2VariantSnafu}; -use crate::constrain::Constraint; -use crate::lang::core::expr::expr_to_expr2::to_expr_id; -use crate::lang::env::Env; -use crate::lang::scope::Scope; -use crate::mem_pool::pool::{NodeId, Pool}; -use crate::mem_pool::pool_str::PoolStr; -use crate::mem_pool::pool_vec::PoolVec; -use crate::mem_pool::shallow_clone::ShallowClone; - -use super::expr::expr2::{ExprId, FloatVal, IntVal}; -use super::expr::output::Output; -use super::types::Type2; - -pub type PatternId = NodeId; - -#[derive(Debug)] -pub enum Pattern2 { - Identifier(Symbol), // 8B - NumLiteral(Variable, i64), // 4B + 8B - IntLiteral(IntVal), // 16B - FloatLiteral(FloatVal), // 16B - StrLiteral(PoolStr), // 8B - CharacterLiteral(char), // 4B - Underscore, // 0B - Tag { - whole_var: Variable, // 4B - ext_var: Variable, // 4B - tag_name: PoolStr, // 8B - arguments: PoolVec<(Variable, PatternId)>, // 8B - }, - RecordDestructure { - whole_var: Variable, // 4B - ext_var: Variable, // 4B - destructs: PoolVec, // 8B - }, - - // Runtime Exceptions - // TODO: figure out how to better handle regions - // to keep this member under 32. With 2 Regions - // it ends up at size 40 - Shadowed { - shadowed_ident: PoolStr, - // definition: Region, - // shadowed_at: Region, - }, - - /// Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments! - UnsupportedPattern(Region), - // parse error patterns - MalformedPattern(MalformedPatternProblem, Region), -} - -impl ShallowClone for Pattern2 { - fn shallow_clone(&self) -> Self { - todo!() - } -} - -#[derive(Debug)] -pub struct PatternState2<'a> { - pub headers: BumpMap, - pub vars: BumpVec<'a, Variable>, - pub constraints: BumpVec<'a, Constraint<'a>>, -} - -#[derive(Debug)] -pub struct RecordDestruct { - pub var: Variable, // 4B - pub label: PoolStr, // 8B - pub symbol: Symbol, // 8B - pub typ: NodeId, // 4B -} - -#[derive(Clone, Debug)] -pub enum DestructType { - Required, - Optional(Variable, ExprId), // 4B + 4B - Guard(Variable, PatternId), // 4B + 4B -} - -pub fn as_pattern_id<'a>( - env: &mut Env<'a>, - scope: &mut Scope, - pattern_id: PatternId, - pattern_type: PatternType, - pattern: &roc_parse::ast::Pattern<'a>, - region: Region, -) -> Output { - let (output, can_pattern) = to_pattern2(env, scope, pattern_type, pattern, region); - - env.pool[pattern_id] = can_pattern; - env.set_region(pattern_id, region); - - output -} - -pub fn to_pattern_id<'a>( - env: &mut Env<'a>, - scope: &mut Scope, - pattern_type: PatternType, - pattern: &roc_parse::ast::Pattern<'a>, - region: Region, -) -> (Output, PatternId) { - let (output, can_pattern) = to_pattern2(env, scope, pattern_type, pattern, region); - - let pattern_id = env.pool.add(can_pattern); - env.set_region(pattern_id, region); - - (output, pattern_id) -} - -pub fn to_pattern2<'a>( - env: &mut Env<'a>, - scope: &mut Scope, - pattern_type: PatternType, - pattern: &roc_parse::ast::Pattern<'a>, - region: Region, -) -> (Output, Pattern2) { - use roc_parse::ast::Pattern::*; - use PatternType::*; - - let mut output = Output::default(); - let can_pattern = match pattern { - Identifier(name) => match scope.introduce( - (*name).into(), - &env.exposed_ident_ids, - &mut env.ident_ids, - region, - ) { - Ok(symbol) => { - output.references.bound_symbols.insert(symbol); - - Pattern2::Identifier(symbol) - } - Err((original_region, shadow)) => { - env.problem(Problem::RuntimeError(RuntimeError::Shadowing { - original_region, - shadow: shadow.clone(), - kind: ShadowKind::Variable, - })); - - let name: &str = shadow.value.as_ref(); - - Pattern2::Shadowed { - shadowed_ident: PoolStr::new(name, env.pool), - } - } - }, - - QualifiedIdentifier { .. } => { - let problem = MalformedPatternProblem::QualifiedIdentifier; - malformed_pattern(env, problem, region) - } - - Underscore(_) => Pattern2::Underscore, - - FloatLiteral(ref string) => match pattern_type { - WhenBranch => match finish_parsing_float(string) { - Err(_error) => { - let problem = MalformedPatternProblem::MalformedFloat; - malformed_pattern(env, problem, region) - } - Ok((_, float, _bound)) => Pattern2::FloatLiteral(FloatVal::F64(float)), - }, - ptype => unsupported_pattern(env, ptype, region), - }, - - NumLiteral(string) => match pattern_type { - WhenBranch => match finish_parsing_num(string) { - Err(_error) => { - let problem = MalformedPatternProblem::MalformedInt; - malformed_pattern(env, problem, region) - } - Ok((_, ParsedNumResult::UnknownNum(int, _bound))) => { - Pattern2::NumLiteral( - env.var_store.fresh(), - match int { - IntValue::U128(_) => todo!(), - IntValue::I128(n) => i128::from_ne_bytes(n) as i64, // FIXME - }, - ) - } - Ok((_, ParsedNumResult::Int(int, _bound))) => { - Pattern2::IntLiteral(IntVal::I64(match int { - IntValue::U128(_) => todo!(), - IntValue::I128(n) => i128::from_ne_bytes(n) as i64, // FIXME - })) - } - Ok((_, ParsedNumResult::Float(int, _bound))) => { - Pattern2::FloatLiteral(FloatVal::F64(int)) - } - }, - ptype => unsupported_pattern(env, ptype, region), - }, - - NonBase10Literal { - string, - base, - is_negative, - } => match pattern_type { - WhenBranch => match finish_parsing_base(string, *base, *is_negative) { - Err(_error) => { - let problem = MalformedPatternProblem::MalformedBase(*base); - malformed_pattern(env, problem, region) - } - Ok((int, _bound)) => { - let int = match int { - IntValue::U128(_) => todo!(), - IntValue::I128(n) => i128::from_ne_bytes(n) as i64, // FIXME - }; - if *is_negative { - Pattern2::IntLiteral(IntVal::I64(-int)) - } else { - Pattern2::IntLiteral(IntVal::I64(int)) - } - } - }, - ptype => unsupported_pattern(env, ptype, region), - }, - - StrLiteral(literal) => match pattern_type { - WhenBranch => flatten_str_literal(env.pool, literal), - ptype => unsupported_pattern(env, ptype, region), - }, - - SingleQuote(string) => match pattern_type { - WhenBranch => { - let mut it = string.chars().peekable(); - if let Some(char) = it.next() { - if it.peek().is_none() { - Pattern2::CharacterLiteral(char) - } else { - // multiple chars is found - let problem = MalformedPatternProblem::MultipleCharsInSingleQuote; - malformed_pattern(env, problem, region) - } - } else { - // no characters found - let problem = MalformedPatternProblem::EmptySingleQuote; - malformed_pattern(env, problem, region) - } - } - ptype => unsupported_pattern(env, ptype, region), - }, - - Tag(name) => { - // Canonicalize the tag's name. - Pattern2::Tag { - whole_var: env.var_store.fresh(), - ext_var: env.var_store.fresh(), - tag_name: PoolStr::new(name, env.pool), - arguments: PoolVec::empty(env.pool), - } - } - - OpaqueRef(..) => internal_error!("opaques not implemented"), - - Apply(tag, patterns) => { - let can_patterns = PoolVec::with_capacity(patterns.len() as u32, env.pool); - for (loc_pattern, node_id) in (*patterns).iter().zip(can_patterns.iter_node_ids()) { - let (new_output, can_pattern) = to_pattern2( - env, - scope, - pattern_type, - &loc_pattern.value, - loc_pattern.region, - ); - - output.union(new_output); - - let can_pattern_id = env.pool.add(can_pattern); - - env.pool[node_id] = (env.var_store.fresh(), can_pattern_id); - } - - match tag.value { - Tag(name) => Pattern2::Tag { - whole_var: env.var_store.fresh(), - ext_var: env.var_store.fresh(), - tag_name: PoolStr::new(name, env.pool), - arguments: can_patterns, - }, - _ => unreachable!("Other patterns cannot be applied"), - } - } - - RecordDestructure(patterns) => { - let ext_var = env.var_store.fresh(); - let whole_var = env.var_store.fresh(); - let destructs = PoolVec::with_capacity(patterns.len() as u32, env.pool); - let opt_erroneous = None; - - for (node_id, loc_pattern) in destructs.iter_node_ids().zip((*patterns).iter()) { - match loc_pattern.value { - Identifier(label) => { - match scope.introduce( - label.into(), - &env.exposed_ident_ids, - &mut env.ident_ids, - region, - ) { - Ok(symbol) => { - output.references.bound_symbols.insert(symbol); - - let destruct = RecordDestruct { - var: env.var_store.fresh(), - label: PoolStr::new(label, env.pool), - symbol, - typ: env.pool.add(DestructType::Required), - }; - - env.pool[node_id] = destruct; - env.set_region(node_id, loc_pattern.region); - } - Err((original_region, shadow)) => { - env.problem(Problem::RuntimeError(RuntimeError::Shadowing { - original_region, - shadow: shadow.clone(), - kind: ShadowKind::Variable, - })); - - // let shadowed = Pattern2::Shadowed { - // definition: original_region, - // shadowed_at: loc_pattern.region, - // shadowed_ident: shadow.value, - // }; - - // No matter what the other patterns - // are, we're definitely shadowed and will - // get a runtime exception as soon as we - // encounter the first bad pattern. - // opt_erroneous = Some(); - // env.pool[node_id] = sha; - // env.set_region(node_id, loc_pattern.region); - todo!("we must both report/store the problem, but also not lose any information") - } - }; - } - - RequiredField(label, loc_guard) => { - // a guard does not introduce the label into scope! - let symbol = scope.ignore(label.into(), &mut env.ident_ids); - let (new_output, can_guard) = to_pattern_id( - env, - scope, - pattern_type, - &loc_guard.value, - loc_guard.region, - ); - - let destruct = RecordDestruct { - var: env.var_store.fresh(), - label: PoolStr::new(label, env.pool), - symbol, - typ: env - .pool - .add(DestructType::Guard(env.var_store.fresh(), can_guard)), - }; - - output.union(new_output); - - env.pool[node_id] = destruct; - env.set_region(node_id, loc_pattern.region); - } - OptionalField(label, loc_default) => { - // an optional DOES introduce the label into scope! - match scope.introduce( - label.into(), - &env.exposed_ident_ids, - &mut env.ident_ids, - region, - ) { - Ok(symbol) => { - let (can_default, expr_output) = - to_expr_id(env, scope, &loc_default.value, loc_default.region); - - // an optional field binds the symbol! - output.references.bound_symbols.insert(symbol); - - output.union(expr_output); - - let destruct = RecordDestruct { - var: env.var_store.fresh(), - label: PoolStr::new(label, env.pool), - symbol, - typ: env.pool.add(DestructType::Optional( - env.var_store.fresh(), - can_default, - )), - }; - - env.pool[node_id] = destruct; - env.set_region(node_id, loc_pattern.region); - } - Err((original_region, shadow)) => { - env.problem(Problem::RuntimeError(RuntimeError::Shadowing { - original_region, - shadow: shadow.clone(), - kind: ShadowKind::Variable, - })); - - // No matter what the other patterns - // are, we're definitely shadowed and will - // get a runtime exception as soon as we - // encounter the first bad pattern. - // opt_erroneous = Some(Pattern::Shadowed(original_region, shadow)); - todo!("must report problem but also not loose any information") - } - }; - } - _ => unreachable!("Any other pattern should have given a parse error"), - } - } - - // If we encountered an erroneous pattern (e.g. one with shadowing), - // use the resulting RuntimeError. Otherwise, return a successful record destructure. - opt_erroneous.unwrap_or(Pattern2::RecordDestructure { - whole_var, - ext_var, - destructs, - }) - } - - RequiredField(_name, _loc_pattern) => { - unreachable!("should have been handled in RecordDestructure"); - } - OptionalField(_name, _loc_pattern) => { - unreachable!("should have been handled in RecordDestructure"); - } - - Tuple(..) => todo!(), - List(..) => todo!(), - ListRest(_) => todo!(), - As(_, _) => todo!(), - - Malformed(_str) => { - let problem = MalformedPatternProblem::Unknown; - malformed_pattern(env, problem, region) - } - - MalformedIdent(_str, bad_ident) => { - let problem = MalformedPatternProblem::BadIdent(*bad_ident); - malformed_pattern(env, problem, region) - } - - SpaceBefore(sub_pattern, _) | SpaceAfter(sub_pattern, _) => { - return to_pattern2(env, scope, pattern_type, sub_pattern, region) - } - }; - - (output, can_pattern) -} - -pub fn symbols_from_pattern(pool: &Pool, initial: &Pattern2) -> Vec { - use Pattern2::*; - let mut symbols = Vec::new(); - let mut stack = vec![initial]; - - while let Some(pattern) = stack.pop() { - match pattern { - Identifier(symbol) => { - symbols.push(*symbol); - } - - Tag { arguments, .. } => { - for (_, pat_id) in arguments.iter(pool) { - let pat = pool.get(*pat_id); - stack.push(pat); - } - } - - RecordDestructure { destructs, .. } => { - for destruct in destructs.iter(pool) { - let destruct_type = pool.get(destruct.typ); - - if let DestructType::Guard(_, subpattern_id) = &destruct_type { - let subpattern = pool.get(*subpattern_id); - stack.push(subpattern); - } else { - symbols.push(destruct.symbol); - } - } - } - - NumLiteral(_, _) - | IntLiteral(_) - | FloatLiteral(_) - | StrLiteral(_) - | CharacterLiteral(_) - | Underscore - | MalformedPattern(_, _) - | Shadowed { .. } - | UnsupportedPattern(_) => {} - } - } - - symbols -} - -pub fn get_identifier_string(pattern: &Pattern2, interns: &Interns) -> ASTResult { - match pattern { - Pattern2::Identifier(symbol) => Ok(symbol.as_str(interns).to_string()), - other => UnexpectedPattern2VariantSnafu { - required_pattern2: "Identifier".to_string(), - encountered_pattern2: format!("{:?}", other), - } - .fail()?, - } -} - -pub fn symbols_and_variables_from_pattern( - pool: &Pool, - initial: &Pattern2, - initial_var: Variable, -) -> Vec<(Symbol, Variable)> { - use Pattern2::*; - let mut symbols = Vec::new(); - let mut stack = vec![(initial_var, initial)]; - - while let Some((variable, pattern)) = stack.pop() { - match pattern { - Identifier(symbol) => { - symbols.push((*symbol, variable)); - } - - Tag { arguments, .. } => { - for (var, pat_id) in arguments.iter(pool) { - let pat = pool.get(*pat_id); - stack.push((*var, pat)); - } - } - - RecordDestructure { destructs, .. } => { - for destruct in destructs.iter(pool) { - let destruct_type = pool.get(destruct.typ); - - if let DestructType::Guard(_, subpattern_id) = &destruct_type { - let subpattern = pool.get(*subpattern_id); - stack.push((destruct.var, subpattern)); - } else { - symbols.push((destruct.symbol, destruct.var)); - } - } - } - - NumLiteral(_, _) - | IntLiteral(_) - | FloatLiteral(_) - | StrLiteral(_) - | CharacterLiteral(_) - | Underscore - | MalformedPattern(_, _) - | Shadowed { .. } - | UnsupportedPattern(_) => {} - } - } - - symbols -} - -/// When we detect an unsupported pattern type (e.g. 5 = 1 + 2 is unsupported because you can't -/// assign to Int patterns), report it to Env and return an UnsupportedPattern runtime error pattern. -fn unsupported_pattern<'a>( - env: &mut Env<'a>, - pattern_type: PatternType, - region: Region, -) -> Pattern2 { - use roc_problem::can::BadPattern; - env.problem(Problem::UnsupportedPattern( - BadPattern::Unsupported(pattern_type), - region, - )); - - Pattern2::UnsupportedPattern(region) -} - -pub(crate) fn flatten_str_literal(pool: &mut Pool, literal: &StrLiteral<'_>) -> Pattern2 { - use roc_parse::ast::StrLiteral::*; - - match literal { - PlainLine(str_slice) => Pattern2::StrLiteral(PoolStr::new(str_slice, pool)), - Line(segments) => flatten_str_lines(pool, &[segments]), - Block(lines) => flatten_str_lines(pool, lines), - } -} - -pub(crate) fn flatten_str_lines(pool: &mut Pool, lines: &[&[StrSegment<'_>]]) -> Pattern2 { - use StrSegment::*; - - let mut buf = String::new(); - - for line in lines { - for segment in line.iter() { - match segment { - Plaintext(string) => { - buf.push_str(string); - } - Unicode(loc_digits) => { - todo!("parse unicode digits {:?}", loc_digits); - } - Interpolated(loc_expr) => { - return Pattern2::UnsupportedPattern(loc_expr.region); - } - EscapedChar(escaped) => buf.push(escaped.unescape()), - } - } - } - - Pattern2::StrLiteral(PoolStr::new(&buf, pool)) -} - -/// When we detect a malformed pattern like `3.X` or `0b5`, -/// report it to Env and return an UnsupportedPattern runtime error pattern. -fn malformed_pattern<'a>( - env: &mut Env<'a>, - problem: MalformedPatternProblem, - region: Region, -) -> Pattern2 { - env.problem(Problem::RuntimeError(RuntimeError::MalformedPattern( - problem, region, - ))); - - Pattern2::MalformedPattern(problem, region) -} diff --git a/crates/ast/src/lang/core/str.rs b/crates/ast/src/lang/core/str.rs deleted file mode 100644 index c5e53b0691..0000000000 --- a/crates/ast/src/lang/core/str.rs +++ /dev/null @@ -1,252 +0,0 @@ -use roc_error_macros::internal_error; -use roc_module::{called_via::CalledVia, symbol::Symbol}; -use roc_parse::ast::StrLiteral; - -use crate::{ - ast_error::{ASTResult, UnexpectedASTNodeSnafu}, - lang::{ - core::expr::{ - expr2::{ArrString, ARR_STRING_CAPACITY}, - expr_to_expr2::expr_to_expr2, - }, - env::Env, - scope::Scope, - }, - mem_pool::{pool::Pool, pool_str::PoolStr, pool_vec::PoolVec}, -}; - -use super::expr::{ - expr2::{Expr2, ExprId}, - output::Output, -}; - -pub(crate) fn flatten_str_literal<'a>( - env: &mut Env<'a>, - scope: &mut Scope, - literal: &StrLiteral<'a>, -) -> (Expr2, Output) { - use roc_parse::ast::StrLiteral::*; - - match literal { - PlainLine(str_slice) => { - // TODO use smallstr - let expr = Expr2::Str(PoolStr::new(str_slice, env.pool)); - - (expr, Output::default()) - } - Line(segments) => flatten_str_lines(env, scope, &[segments]), - Block(lines) => flatten_str_lines(env, scope, lines), - } -} - -enum StrSegment { - Interpolation(Expr2), - Plaintext(PoolStr), -} - -fn flatten_str_lines<'a>( - env: &mut Env<'a>, - scope: &mut Scope, - lines: &[&[roc_parse::ast::StrSegment<'a>]], -) -> (Expr2, Output) { - use roc_parse::ast::StrSegment::*; - - let mut buf = String::new(); - let mut segments = Vec::new(); - let mut output = Output::default(); - - for line in lines { - for segment in line.iter() { - match segment { - Plaintext(string) => { - buf.push_str(string); - } - Unicode(loc_hex_digits) => match u32::from_str_radix(loc_hex_digits.value, 16) { - Ok(code_pt) => match std::char::from_u32(code_pt) { - Some(ch) => { - buf.push(ch); - } - None => { - // env.problem(Problem::InvalidUnicodeCodePt(loc_hex_digits.region)); - // - // return ( - // Expr::RuntimeError(RuntimeError::InvalidUnicodeCodePt( - // loc_hex_digits.region, - // )), - // output, - // ); - todo!() - } - }, - Err(_) => { - // env.problem(Problem::InvalidHexadecimal(loc_hex_digits.region)); - // - // return ( - // Expr::RuntimeError(RuntimeError::InvalidHexadecimal( - // loc_hex_digits.region, - // )), - // output, - // ); - todo!() - } - }, - Interpolated(loc_expr) => { - if roc_can::expr::is_valid_interpolation(loc_expr.value) { - // Interpolations desugar to Str.concat calls - output.references.calls.insert(Symbol::STR_CONCAT); - - if !buf.is_empty() { - segments.push(StrSegment::Plaintext(PoolStr::new(&buf, env.pool))); - - buf = String::new(); - } - - let (loc_expr, new_output) = - expr_to_expr2(env, scope, loc_expr.value, loc_expr.region); - - output.union(new_output); - - segments.push(StrSegment::Interpolation(loc_expr)); - } else { - // env.problem(Problem::InvalidInterpolation(loc_expr.region)); - // - // return ( - // Expr::RuntimeError(RuntimeError::InvalidInterpolation(loc_expr.region)), - // output, - // ); - todo!() - } - } - EscapedChar(escaped) => buf.push(escaped.unescape()), - } - } - } - - if !buf.is_empty() { - segments.push(StrSegment::Plaintext(PoolStr::new(&buf, env.pool))); - } - - (desugar_str_segments(env, segments), output) -} - -/// Resolve string interpolations by desugaring a sequence of StrSegments -/// into nested calls to Str.concat -fn desugar_str_segments(env: &mut Env, segments: Vec) -> Expr2 { - use StrSegment::*; - - let pool = &mut env.pool; - let var_store = &mut env.var_store; - - let mut iter = segments.into_iter().rev(); - let mut expr = match iter.next() { - Some(Plaintext(pool_str)) => Expr2::Str(pool_str), - Some(Interpolation(expr_id)) => expr_id, - None => { - // No segments? Empty string! - - let pool_str = PoolStr::new("", pool); - Expr2::Str(pool_str) - } - }; - - for seg in iter { - let new_expr = match seg { - Plaintext(string) => Expr2::Str(string), - Interpolation(expr_id) => expr_id, - }; - - let concat_expr_id = pool.add(Expr2::Var(Symbol::STR_CONCAT)); - - let args = vec![ - (var_store.fresh(), pool.add(new_expr)), - (var_store.fresh(), pool.add(expr)), - ]; - let args = PoolVec::new(args.into_iter(), pool); - - let new_call = Expr2::Call { - args, - expr_id: concat_expr_id, - expr_var: var_store.fresh(), - fn_var: var_store.fresh(), - closure_var: var_store.fresh(), - called_via: CalledVia::Space, - }; - - expr = new_call - } - - expr -} - -pub fn update_str_expr( - node_id: ExprId, - new_char: char, - insert_index: usize, - pool: &mut Pool, -) -> ASTResult<()> { - let str_expr = pool.get_mut(node_id); - - enum Either { - MyArrString(ArrString), - OldPoolStr(PoolStr), - NewPoolStr(PoolStr), - } - - let insert_either = match str_expr { - Expr2::SmallStr(arr_string) => { - if arr_string.len() < arr_string.capacity() { - let mut new_bytes: [u8; ARR_STRING_CAPACITY] = Default::default(); - let arr_bytes = arr_string.as_str().as_bytes(); - new_bytes[..insert_index].copy_from_slice(&arr_bytes[..insert_index]); - new_bytes[insert_index] = new_char as u8; - new_bytes[insert_index + 1..arr_bytes.len() + 1] - .copy_from_slice(&arr_bytes[insert_index..]); - - let new_str = unsafe { - // all old characters have been checked on file load, new_char has been checked inside editor/src/editor/mvc/ed_update.rs - std::str::from_utf8_unchecked(&new_bytes[..arr_bytes.len() + 1]) - }; - - let new_arr_string = match ArrString::from(new_str) { - Ok(arr_string) => arr_string, - Err(e) => { - internal_error!("Failed to build valid ArrayString from str: {:?}", e) - } - }; - - Either::MyArrString(new_arr_string) - } else { - let mut new_string = arr_string.as_str().to_owned(); - - new_string.insert(insert_index, new_char); - - let new_pool_str = PoolStr::new(&new_string, pool); - Either::NewPoolStr(new_pool_str) - } - } - Expr2::Str(old_pool_str) => Either::OldPoolStr(*old_pool_str), - other => UnexpectedASTNodeSnafu { - required_node_type: "SmallStr or Str", - encountered_node_type: format!("{other:?}"), - } - .fail()?, - }; - - match insert_either { - Either::MyArrString(arr_string) => { - pool.set(node_id, Expr2::SmallStr(arr_string)); - } - Either::OldPoolStr(old_pool_str) => { - let mut new_string = old_pool_str.as_str(pool).to_owned(); - - new_string.insert(insert_index, new_char); - - let new_pool_str = PoolStr::new(&new_string, pool); - - pool.set(node_id, Expr2::Str(new_pool_str)) - } - Either::NewPoolStr(new_pool_str) => pool.set(node_id, Expr2::Str(new_pool_str)), - } - - Ok(()) -} diff --git a/crates/ast/src/lang/core/types.rs b/crates/ast/src/lang/core/types.rs deleted file mode 100644 index 75ace432c8..0000000000 --- a/crates/ast/src/lang/core/types.rs +++ /dev/null @@ -1,855 +0,0 @@ -#![allow(clippy::all)] -#![allow(dead_code)] -#![allow(unused_imports)] -// use roc_can::expr::Output; -use roc_collections::all::{MutMap, MutSet}; -use roc_error_macros::todo_abilities; -use roc_module::ident::{Ident, Lowercase, TagName, Uppercase}; -use roc_module::symbol::Symbol; -use roc_region::all::{Loc, Region}; -use roc_types::types::{AliasKind, RecordField}; -use roc_types::{subs::Variable, types::ErrorType}; - -use crate::lang::env::Env; -use crate::lang::scope::Scope; -use crate::mem_pool::pool::{NodeId, Pool}; -use crate::mem_pool::pool_str::PoolStr; -use crate::mem_pool::pool_vec::PoolVec; -use crate::mem_pool::shallow_clone::ShallowClone; - -pub type TypeId = NodeId; - -const TYPE2_SIZE: () = assert!(std::mem::size_of::() == 3 * 8 + 4); - -#[derive(Debug)] -pub enum Type2 { - Variable(Variable), // 4B - - Alias(Symbol, PoolVec, TypeId), // 24B = 8B + 8B + 4B + pad - Opaque(Symbol, PoolVec, TypeId), // 24B = 8B + 8B + 4B + pad - AsAlias(Symbol, PoolVec<(PoolStr, TypeId)>, TypeId), // 24B = 8B + 8B + 4B + pad - - // 24B - HostExposedAlias { - name: Symbol, // 8B - arguments: PoolVec<(PoolStr, TypeId)>, // 8B - actual_var: Variable, // 4B - actual: TypeId, // 4B - }, - EmptyTagUnion, - TagUnion(PoolVec<(TagName, PoolVec)>, TypeId), // 12B = 8B + 4B - RecursiveTagUnion(Variable, PoolVec<(TagName, PoolVec)>, TypeId), // 16B = 4B + 8B + 4B - - EmptyRec, - Record(PoolVec<(PoolStr, RecordField)>, TypeId), // 12B = 8B + 4B - - Function(PoolVec, TypeId, TypeId), // 16B = 8B + 4B + 4B - Apply(Symbol, PoolVec), // 16B = 8B + 8B - - Erroneous(Problem2), // 24B -} - -#[derive(Debug)] -pub enum Problem2 { - CanonicalizationProblem, - CircularType(Symbol, NodeId), // 12B = 8B + 4B - CyclicAlias(Symbol, PoolVec), // 20B = 8B + 12B - UnrecognizedIdent(PoolStr), // 8B - Shadowed(Loc), - BadTypeArguments { - symbol: Symbol, // 8B - type_got: u8, // 1B - alias_needs: u8, // 1B - }, - InvalidModule, - SolvedTypeError, -} - -impl ShallowClone for Type2 { - fn shallow_clone(&self) -> Self { - match self { - Self::Variable(var) => Self::Variable(*var), - Self::Alias(symbol, args, alias_type_id) => { - Self::Alias(*symbol, args.shallow_clone(), alias_type_id.clone()) - } - Self::Opaque(symbol, args, alias_type_id) => { - Self::Opaque(*symbol, args.shallow_clone(), alias_type_id.clone()) - } - Self::Record(fields, ext_id) => Self::Record(fields.shallow_clone(), ext_id.clone()), - Self::Function(args, closure_type_id, ret_type_id) => Self::Function( - args.shallow_clone(), - closure_type_id.clone(), - ret_type_id.clone(), - ), - rest => todo!("{:?}", rest), - } - } -} - -impl Type2 { - fn substitute(_pool: &mut Pool, _subs: &MutMap, _type_id: TypeId) { - todo!() - } - - pub fn variables(&self, pool: &mut Pool) -> MutSet { - use Type2::*; - - let mut stack = vec![self]; - let mut result = MutSet::default(); - - while let Some(this) = stack.pop() { - match this { - Variable(v) => { - result.insert(*v); - } - Alias(_, _, actual) | AsAlias(_, _, actual) | Opaque(_, _, actual) => { - stack.push(pool.get(*actual)); - } - HostExposedAlias { - actual_var, actual, .. - } => { - result.insert(*actual_var); - stack.push(pool.get(*actual)); - } - EmptyTagUnion | EmptyRec | Erroneous(_) => {} - TagUnion(tags, ext) => { - for (_, args) in tags.iter(pool) { - stack.extend(args.iter(pool)); - } - stack.push(pool.get(*ext)); - } - RecursiveTagUnion(rec, tags, ext) => { - for (_, args) in tags.iter(pool) { - stack.extend(args.iter(pool)); - } - stack.push(pool.get(*ext)); - result.insert(*rec); - } - Record(fields, ext) => { - for (_, field) in fields.iter(pool) { - stack.push(pool.get(*field.as_inner())); - } - stack.push(pool.get(*ext)); - } - Function(args, closure, result) => { - stack.extend(args.iter(pool)); - stack.push(pool.get(*closure)); - stack.push(pool.get(*result)); - } - Apply(_, args) => { - stack.extend(args.iter(pool)); - } - } - } - - result - } - - pub fn contains_symbol(&self, _pool: &mut Pool, _needle: Symbol) -> bool { - todo!() - } - - pub fn substitute_alias(&self, _pool: &mut Pool, _needle: Symbol, _actual: Self) { - todo!() - } -} - -impl NodeId { - pub fn variables(&self, _pool: &mut Pool) -> MutSet { - todo!() - } -} - -/// A temporary data structure to return a bunch of values to Def construction -pub enum Signature { - FunctionWithAliases { - annotation: Type2, - arguments: PoolVec, - closure_type_id: TypeId, - return_type_id: TypeId, - }, - Function { - arguments: PoolVec, - closure_type_id: TypeId, - return_type_id: TypeId, - }, - Value { - annotation: Type2, - }, -} - -pub enum Annotation2 { - Annotation { - named_rigids: MutMap, - unnamed_rigids: MutSet, - symbols: MutSet, - signature: Signature, - }, - Erroneous, -} - -pub fn to_annotation2<'a>( - env: &mut Env, - scope: &mut Scope, - annotation: &'a roc_parse::ast::TypeAnnotation<'a>, - region: Region, -) -> Annotation2 { - let mut references = References::default(); - - let annotation = to_type2(env, scope, &mut references, annotation, region); - - // we dealias until we hit a non-alias, then we either hit a function type (and produce a - // function annotation) or anything else (and produce a value annotation) - match annotation { - Type2::Function(arguments, closure_type_id, return_type_id) => { - let References { - named, - unnamed, - symbols, - .. - } = references; - - let signature = Signature::Function { - arguments, - closure_type_id, - return_type_id, - }; - - Annotation2::Annotation { - named_rigids: named, - unnamed_rigids: unnamed, - symbols, - signature, - } - } - Type2::Alias(_, _, _) => { - // most of the time, the annotation is not an alias, so this case is comparatively - // less efficient - shallow_dealias(env, references, annotation) - } - _ => { - let References { - named, - unnamed, - symbols, - .. - } = references; - - let signature = Signature::Value { annotation }; - - Annotation2::Annotation { - named_rigids: named, - unnamed_rigids: unnamed, - symbols, - signature, - } - } - } -} - -fn shallow_dealias<'a>(env: &mut Env, references: References, annotation: Type2) -> Annotation2 { - let References { - named, - unnamed, - symbols, - .. - } = references; - let mut inner = &annotation; - - loop { - match inner { - Type2::Alias(_, _, actual) => { - inner = env.pool.get(*actual); - } - Type2::Function(arguments, closure_type_id, return_type_id) => { - let signature = Signature::FunctionWithAliases { - arguments: arguments.shallow_clone(), - closure_type_id: *closure_type_id, - return_type_id: *return_type_id, - annotation, - }; - - return Annotation2::Annotation { - named_rigids: named, - unnamed_rigids: unnamed, - symbols, - signature, - }; - } - _ => { - let signature = Signature::Value { annotation }; - - return Annotation2::Annotation { - named_rigids: named, - unnamed_rigids: unnamed, - symbols, - signature, - }; - } - } - } -} - -#[derive(Default)] -pub struct References { - named: MutMap, - unnamed: MutSet, - hidden: MutSet, - symbols: MutSet, -} - -pub fn to_type_id<'a>( - env: &mut Env, - scope: &mut Scope, - rigids: &mut References, - annotation: &roc_parse::ast::TypeAnnotation<'a>, - region: Region, -) -> TypeId { - let type2 = to_type2(env, scope, rigids, annotation, region); - - env.add(type2, region) -} - -pub fn as_type_id<'a>( - env: &mut Env, - scope: &mut Scope, - rigids: &mut References, - type_id: TypeId, - annotation: &roc_parse::ast::TypeAnnotation<'a>, - region: Region, -) { - let type2 = to_type2(env, scope, rigids, annotation, region); - - env.pool[type_id] = type2; - env.set_region(type_id, region); -} - -pub fn to_type2<'a>( - env: &mut Env, - scope: &mut Scope, - references: &mut References, - annotation: &roc_parse::ast::TypeAnnotation<'a>, - region: Region, -) -> Type2 { - use roc_parse::ast::Pattern; - use roc_parse::ast::TypeAnnotation::*; - use roc_parse::ast::TypeHeader; - - match annotation { - Apply(module_name, ident, targs) => { - match to_type_apply(env, scope, references, module_name, ident, targs, region) { - TypeApply::Apply(symbol, args) => { - references.symbols.insert(symbol); - Type2::Apply(symbol, args) - } - TypeApply::Alias(symbol, args, actual) => { - references.symbols.insert(symbol); - Type2::Alias(symbol, args, actual) - } - TypeApply::Erroneous => { - // Type2::Erroneous - todo!() - } - } - } - Function(argument_types, return_type) => { - let arguments = PoolVec::with_capacity(argument_types.len() as u32, env.pool); - - for (type_id, loc_arg) in arguments.iter_node_ids().zip(argument_types.iter()) { - as_type_id( - env, - scope, - references, - type_id, - &loc_arg.value, - loc_arg.region, - ); - } - - let return_type_id = to_type_id( - env, - scope, - references, - &return_type.value, - return_type.region, - ); - - let closure_type = Type2::Variable(env.var_store.fresh()); - let closure_type_id = env.pool.add(closure_type); - - Type2::Function(arguments, closure_type_id, return_type_id) - } - BoundVariable(v) => { - // A rigid type variable. The parser should have already ensured that the name is indeed a lowercase. - let v = Lowercase::from(*v); - match references.named.get(&v) { - Some(var) => Type2::Variable(*var), - None => { - let var = env.var_store.fresh(); - - references.named.insert(v, var); - - Type2::Variable(var) - } - } - } - Inferred => { - let var = env.var_store.fresh(); - - Type2::Variable(var) - } - Wildcard | Malformed(_) => { - let var = env.var_store.fresh(); - - references.unnamed.insert(var); - - Type2::Variable(var) - } - Tuple { elems: _, ext: _ } => { - todo!("tuple type"); - } - Record { fields, ext, .. } => { - let field_types_map = - can_assigned_fields(env, scope, references, &fields.items, region); - - let field_types = PoolVec::with_capacity(field_types_map.len() as u32, env.pool); - - for (node_id, (label, field)) in field_types.iter_node_ids().zip(field_types_map) { - let poolstr = PoolStr::new(label.as_str(), env.pool); - - let rec_field = field.map_owned(|field| env.pool.add(field)); - env.pool[node_id] = (poolstr, rec_field); - } - - let ext_type = match ext { - Some(loc_ann) => to_type_id(env, scope, references, &loc_ann.value, region), - None => env.add(Type2::EmptyRec, region), - }; - - Type2::Record(field_types, ext_type) - } - TagUnion { tags, ext, .. } => { - let tag_types_vec = can_tags(env, scope, references, tags.items, region); - - let tag_types = PoolVec::with_capacity(tag_types_vec.len() as u32, env.pool); - - for (node_id, (tag_name, field)) in tag_types.iter_node_ids().zip(tag_types_vec) { - env.pool[node_id] = (tag_name, field); - } - - let ext_type = match ext { - Some(loc_ann) => to_type_id(env, scope, references, &loc_ann.value, region), - None => env.add(Type2::EmptyTagUnion, region), - }; - - Type2::TagUnion(tag_types, ext_type) - } - As( - loc_inner, - _spaces, - TypeHeader { - name, - vars: loc_vars, - }, - ) => { - // e.g. `{ x : Int, y : Int } as Point` - let symbol = match scope.introduce( - name.value.into(), - &env.exposed_ident_ids, - &mut env.ident_ids, - region, - ) { - Ok(symbol) => symbol, - - Err((_original_region, _shadow)) => { - // let problem = Problem2::Shadowed(original_region, shadow.clone()); - - // env.problem(roc_problem::can::Problem::ShadowingInAnnotation { - // original_region, - // shadow, - // }); - - // return Type2::Erroneous(problem); - todo!(); - } - }; - - let inner_type = to_type2(env, scope, references, &loc_inner.value, region); - let vars = PoolVec::with_capacity(loc_vars.len() as u32, env.pool); - - let lowercase_vars = PoolVec::with_capacity(loc_vars.len() as u32, env.pool); - - for ((loc_var, named_id), var_id) in loc_vars - .iter() - .zip(lowercase_vars.iter_node_ids()) - .zip(vars.iter_node_ids()) - { - let var = match loc_var.value { - Pattern::Identifier(name) if name.chars().next().unwrap().is_lowercase() => { - name - } - _ => unreachable!("I thought this was validated during parsing"), - }; - let var_name = Lowercase::from(var); - - if let Some(var) = references.named.get(&var_name) { - let poolstr = PoolStr::new(var_name.as_str(), env.pool); - - let type_id = env.pool.add(Type2::Variable(*var)); - env.pool[var_id] = (poolstr.shallow_clone(), type_id); - - env.pool[named_id] = (poolstr, *var); - env.set_region(named_id, loc_var.region); - } else { - let var = env.var_store.fresh(); - - references.named.insert(var_name.clone(), var); - let poolstr = PoolStr::new(var_name.as_str(), env.pool); - - let type_id = env.pool.add(Type2::Variable(var)); - env.pool[var_id] = (poolstr.shallow_clone(), type_id); - - env.pool[named_id] = (poolstr, var); - env.set_region(named_id, loc_var.region); - } - } - - let alias_actual = inner_type; - // TODO instantiate recursive tag union - // let alias_actual = if let Type2::TagUnion(tags, ext) = inner_type { - // let rec_var = env.var_store.fresh(); - // - // let mut new_tags = Vec::with_capacity(tags.len()); - // for (tag_name, args) in tags { - // let mut new_args = Vec::with_capacity(args.len()); - // for arg in args { - // let mut new_arg = arg.clone(); - // new_arg.substitute_alias(symbol, &Type2::Variable(rec_var)); - // new_args.push(new_arg); - // } - // new_tags.push((tag_name.clone(), new_args)); - // } - // Type2::RecursiveTagUnion(rec_var, new_tags, ext) - // } else { - // inner_type - // }; - - let mut hidden_variables = MutSet::default(); - hidden_variables.extend(alias_actual.variables(env.pool)); - - for (_, var) in lowercase_vars.iter(env.pool) { - hidden_variables.remove(var); - } - - let alias_actual_id = env.pool.add(alias_actual); - scope.add_alias(env.pool, symbol, lowercase_vars, alias_actual_id); - - let alias = scope.lookup_alias(symbol).unwrap(); - // local_aliases.insert(symbol, alias.clone()); - - // TODO host-exposed - // if vars.is_empty() && env.home == symbol.module_id() { - // let actual_var = env.var_store.fresh(); - // rigids.host_exposed.insert(symbol, actual_var); - // Type::HostExposedAlias { - // name: symbol, - // arguments: vars, - // actual: Box::new(alias.typ.clone()), - // actual_var, - // } - // } else { - // Type::Alias(symbol, vars, Box::new(alias.typ.clone())) - // } - Type2::AsAlias(symbol, vars, alias.actual) - } - Where { .. } => todo_abilities!(), - SpaceBefore(nested, _) | SpaceAfter(nested, _) => { - to_type2(env, scope, references, nested, region) - } - } -} - -// TODO trim down these arguments! -#[allow(clippy::too_many_arguments)] -fn can_assigned_fields<'a>( - env: &mut Env, - scope: &mut Scope, - rigids: &mut References, - fields: &&[Loc>>], - region: Region, -) -> MutMap> { - use roc_parse::ast::AssignedField::*; - use roc_types::types::RecordField::*; - - // SendMap doesn't have a `with_capacity` - let mut field_types = MutMap::default(); - - // field names we've seen so far in this record - let mut seen = std::collections::HashMap::with_capacity(fields.len()); - - 'outer: for loc_field in fields.iter() { - let mut field = &loc_field.value; - - // use this inner loop to unwrap the SpaceAfter/SpaceBefore - // when we find the name of this field, break out of the loop - // with that value, so we can check whether the field name is - // a duplicate - let new_name = 'inner: loop { - match field { - RequiredValue(field_name, _, annotation) => { - let field_type = - to_type2(env, scope, rigids, &annotation.value, annotation.region); - - let label = Lowercase::from(field_name.value); - field_types.insert(label.clone(), Required(field_type)); - - break 'inner label; - } - OptionalValue(field_name, _, annotation) => { - let field_type = - to_type2(env, scope, rigids, &annotation.value, annotation.region); - - let label = Lowercase::from(field_name.value); - field_types.insert(label.clone(), Optional(field_type)); - - break 'inner label; - } - LabelOnly(loc_field_name) => { - // Interpret { a, b } as { a : a, b : b } - let field_name = Lowercase::from(loc_field_name.value); - let field_type = { - if let Some(var) = rigids.named.get(&field_name) { - Type2::Variable(*var) - } else { - let field_var = env.var_store.fresh(); - rigids.named.insert(field_name.clone(), field_var); - Type2::Variable(field_var) - } - }; - - field_types.insert(field_name.clone(), Required(field_type)); - - break 'inner field_name; - } - SpaceBefore(nested, _) | SpaceAfter(nested, _) => { - // check the nested field instead - field = nested; - continue 'inner; - } - Malformed(_) => { - // TODO report this? - // completely skip this element, advance to the next tag - continue 'outer; - } - } - }; - - // ensure that the new name is not already in this record: - // note that the right-most tag wins when there are two with the same name - if let Some(replaced_region) = seen.insert(new_name.clone(), loc_field.region) { - env.problem(roc_problem::can::Problem::DuplicateRecordFieldType { - field_name: new_name.into(), - record_region: region, - field_region: loc_field.region, - replaced_region, - }); - } - } - - field_types -} - -fn can_tags<'a>( - env: &mut Env, - scope: &mut Scope, - rigids: &mut References, - tags: &'a [Loc>], - region: Region, -) -> Vec<(TagName, PoolVec)> { - use roc_parse::ast::Tag; - let mut tag_types = Vec::with_capacity(tags.len()); - - // tag names we've seen so far in this tag union - let mut seen = std::collections::HashMap::with_capacity(tags.len()); - - 'outer: for loc_tag in tags.iter() { - let mut tag = &loc_tag.value; - - // use this inner loop to unwrap the SpaceAfter/SpaceBefore - // when we find the name of this tag, break out of the loop - // with that value, so we can check whether the tag name is - // a duplicate - let new_name = 'inner: loop { - match tag { - Tag::Apply { name, args } => { - let arg_types = PoolVec::with_capacity(args.len() as u32, env.pool); - - for (type_id, loc_arg) in arg_types.iter_node_ids().zip(args.iter()) { - as_type_id(env, scope, rigids, type_id, &loc_arg.value, loc_arg.region); - } - - let tag_name = TagName(name.value.into()); - tag_types.push((tag_name.clone(), arg_types)); - - break 'inner tag_name; - } - Tag::SpaceBefore(nested, _) | Tag::SpaceAfter(nested, _) => { - // check the nested tag instead - tag = nested; - continue 'inner; - } - Tag::Malformed(_) => { - // TODO report this? - // completely skip this element, advance to the next tag - continue 'outer; - } - } - }; - - // ensure that the new name is not already in this tag union: - // note that the right-most tag wins when there are two with the same name - if let Some(replaced_region) = seen.insert(new_name.clone(), loc_tag.region) { - env.problem(roc_problem::can::Problem::DuplicateTag { - tag_region: loc_tag.region, - tag_union_region: region, - replaced_region, - tag_name: new_name, - }); - } - } - - tag_types -} - -enum TypeApply { - Apply(Symbol, PoolVec), - Alias(Symbol, PoolVec, TypeId), - Erroneous, -} - -#[inline(always)] -fn to_type_apply<'a>( - env: &mut Env, - scope: &mut Scope, - rigids: &mut References, - module_name: &str, - ident: &str, - type_arguments: &[Loc>], - region: Region, -) -> TypeApply { - let symbol = if module_name.is_empty() { - // Since module_name was empty, this is an unqualified type. - // Look it up in scope! - let ident: Ident = (*ident).into(); - - match scope.lookup(&ident, region) { - Ok(symbol) => symbol, - Err(problem) => { - env.problem(roc_problem::can::Problem::RuntimeError(problem)); - - return TypeApply::Erroneous; - } - } - } else { - match env.qualified_lookup(module_name, ident, region) { - Ok(symbol) => symbol, - Err(problem) => { - // Either the module wasn't imported, or - // it was imported but it doesn't expose this ident. - env.problem(roc_problem::can::Problem::RuntimeError(problem)); - - return TypeApply::Erroneous; - } - } - }; - - let argument_type_ids = PoolVec::with_capacity(type_arguments.len() as u32, env.pool); - - for (type_id, loc_arg) in argument_type_ids.iter_node_ids().zip(type_arguments.iter()) { - as_type_id(env, scope, rigids, type_id, &loc_arg.value, loc_arg.region); - } - - let args = type_arguments; - let opt_alias = scope.lookup_alias(symbol); - match opt_alias { - Some(ref alias) => { - // use a known alias - let actual = alias.actual; - let mut substitutions: MutMap = MutMap::default(); - - if alias.targs.len() != args.len() { - return TypeApply::Erroneous; - } - - let arguments = PoolVec::with_capacity(type_arguments.len() as u32, env.pool); - - let it = arguments.iter_node_ids().zip( - argument_type_ids - .iter_node_ids() - .zip(alias.targs.iter_node_ids()), - ); - - for (node_id, (type_id, loc_var_id)) in it { - let loc_var = &env.pool[loc_var_id]; - let name = loc_var.0.shallow_clone(); - let var = loc_var.1; - - env.pool[node_id] = (name, type_id); - - substitutions.insert(var, type_id); - } - - // make sure the recursion variable is freshly instantiated - // have to allocate these outside of the if for lifetime reasons... - let new = env.var_store.fresh(); - let fresh = env.pool.add(Type2::Variable(new)); - if let Type2::RecursiveTagUnion(rvar, ref tags, ext) = &mut env.pool[actual] { - substitutions.insert(*rvar, fresh); - - env.pool[actual] = Type2::RecursiveTagUnion(new, tags.shallow_clone(), *ext); - } - - // make sure hidden variables are freshly instantiated - for var_id in alias.hidden_variables.iter_node_ids() { - let var = env.pool[var_id]; - let fresh = env.pool.add(Type2::Variable(env.var_store.fresh())); - substitutions.insert(var, fresh); - } - - // instantiate variables - Type2::substitute(env.pool, &substitutions, actual); - - let type_arguments = PoolVec::with_capacity(arguments.len() as u32, env.pool); - - for (node_id, type_id) in arguments - .iter_node_ids() - .zip(type_arguments.iter_node_ids()) - { - let typ = env.pool[node_id].1; - env.pool[type_id] = typ; - } - - TypeApply::Alias(symbol, type_arguments, actual) - } - None => TypeApply::Apply(symbol, argument_type_ids), - } -} - -#[derive(Debug)] -pub struct Alias { - pub targs: PoolVec<(PoolStr, Variable)>, - pub actual: TypeId, - - /// hidden type variables, like the closure variable in `a -> b` - pub hidden_variables: PoolVec, -} - -impl ShallowClone for Alias { - fn shallow_clone(&self) -> Self { - Self { - targs: self.targs.shallow_clone(), - hidden_variables: self.hidden_variables.shallow_clone(), - actual: self.actual, - } - } -} diff --git a/crates/ast/src/lang/core/val_def.rs b/crates/ast/src/lang/core/val_def.rs deleted file mode 100644 index b30bfb3ace..0000000000 --- a/crates/ast/src/lang/core/val_def.rs +++ /dev/null @@ -1,101 +0,0 @@ -use crate::{ - lang::{core::expr::expr2_to_string::expr2_to_string, rigids::Rigids}, - mem_pool::{ - pool::{NodeId, Pool}, - shallow_clone::ShallowClone, - }, -}; -use roc_types::subs::Variable; - -use super::{ - expr::expr2::ExprId, - pattern::{Pattern2, PatternId}, - types::TypeId, -}; - -#[derive(Debug)] -pub enum ValueDef { - WithAnnotation { - pattern_id: PatternId, // 4B - expr_id: ExprId, // 4B - type_id: TypeId, - rigids: Rigids, - expr_var: Variable, // 4B - }, - NoAnnotation { - pattern_id: PatternId, // 4B - expr_id: ExprId, // 4B - expr_var: Variable, // 4B - }, -} - -impl ShallowClone for ValueDef { - fn shallow_clone(&self) -> Self { - match self { - Self::WithAnnotation { - pattern_id, - expr_id, - type_id, - rigids, - expr_var, - } => Self::WithAnnotation { - pattern_id: *pattern_id, - expr_id: *expr_id, - type_id: *type_id, - rigids: rigids.shallow_clone(), - expr_var: *expr_var, - }, - Self::NoAnnotation { - pattern_id, - expr_id, - expr_var, - } => Self::NoAnnotation { - pattern_id: *pattern_id, - expr_id: *expr_id, - expr_var: *expr_var, - }, - } - } -} - -impl ValueDef { - pub fn get_expr_id(&self) -> ExprId { - match self { - ValueDef::WithAnnotation { expr_id, .. } => *expr_id, - ValueDef::NoAnnotation { expr_id, .. } => *expr_id, - } - } - - pub fn get_pattern_id(&self) -> NodeId { - match self { - ValueDef::WithAnnotation { pattern_id, .. } => *pattern_id, - ValueDef::NoAnnotation { pattern_id, .. } => *pattern_id, - } - } -} - -pub fn value_def_to_string(val_def: &ValueDef, pool: &Pool) -> String { - match val_def { - ValueDef::WithAnnotation { - pattern_id, - expr_id, - type_id, - rigids, - expr_var, - } => { - format!("WithAnnotation {{ pattern_id: {:?}, expr_id: {:?}, type_id: {:?}, rigids: {:?}, expr_var: {:?}}}", pool.get(*pattern_id), expr2_to_string(*expr_id, pool), pool.get(*type_id), rigids, expr_var) - } - ValueDef::NoAnnotation { - pattern_id, - expr_id, - expr_var, - } => { - format!( - "NoAnnotation {{ pattern_id: {:?}, expr_id: {:?}, expr_var: {:?}}}", - pool.get(*pattern_id), - expr2_to_string(*expr_id, pool), - expr_var - ) - } - } -} diff --git a/crates/ast/src/lang/env.rs b/crates/ast/src/lang/env.rs deleted file mode 100644 index 8a40df3a7e..0000000000 --- a/crates/ast/src/lang/env.rs +++ /dev/null @@ -1,188 +0,0 @@ -use crate::mem_pool::pool::{NodeId, Pool}; -use bumpalo::{collections::Vec as BumpVec, Bump}; -use roc_collections::all::{MutMap, MutSet}; -use roc_module::ident::{Ident, Lowercase, ModuleName}; -use roc_module::symbol::{IdentIds, IdentIdsByModule, ModuleId, ModuleIds, Symbol}; -use roc_problem::can::{Problem, RuntimeError}; -use roc_region::all::{Loc, Region}; -use roc_types::subs::VarStore; - -use super::core::def::def::References; - -/// TODO document -#[derive(Debug)] -pub struct Env<'a> { - pub home: ModuleId, - pub var_store: &'a mut VarStore, - pub pool: &'a mut Pool, - pub arena: &'a Bump, - - pub problems: BumpVec<'a, Problem>, - - pub dep_idents: IdentIdsByModule, - pub module_ids: &'a ModuleIds, - pub ident_ids: IdentIds, - pub exposed_ident_ids: IdentIds, - - pub closures: MutMap, - /// Symbols which were referenced by qualified lookups. - pub qualified_lookups: MutSet, - - pub top_level_symbols: MutSet, - - pub closure_name_symbol: Option, - pub tailcallable_symbol: Option, -} - -impl<'a> Env<'a> { - #[allow(clippy::too_many_arguments)] - pub fn new( - home: ModuleId, - arena: &'a Bump, - pool: &'a mut Pool, - var_store: &'a mut VarStore, - dep_idents: IdentIdsByModule, - module_ids: &'a ModuleIds, - exposed_ident_ids: IdentIds, - ) -> Env<'a> { - Env { - home, - arena, - pool, - problems: BumpVec::new_in(arena), - var_store, - dep_idents, - module_ids, - ident_ids: exposed_ident_ids.clone(), // we start with these, but will add more later using Scope.introduce - exposed_ident_ids, - closures: MutMap::default(), - qualified_lookups: MutSet::default(), - tailcallable_symbol: None, - closure_name_symbol: None, - top_level_symbols: MutSet::default(), - } - } - - pub fn add(&mut self, item: T, region: Region) -> NodeId { - let id = self.pool.add(item); - self.set_region(id, region); - - id - } - - pub fn problem(&mut self, problem: Problem) { - self.problems.push(problem); - } - - pub fn set_region(&mut self, _node_id: NodeId, _region: Region) { - dbg!("Don't Forget to set the region eventually"); - } - - pub fn register_closure(&mut self, symbol: Symbol, references: References) { - self.closures.insert(symbol, references); - } - - /// Generates a unique, new symbol like "$1" or "$5", - /// using the home module as the module_id. - /// - /// This is used, for example, during canonicalization of an Expr::Closure - /// to generate a unique symbol to refer to that closure. - pub fn gen_unique_symbol(&mut self) -> Symbol { - let ident_id = self.ident_ids.gen_unique(); - - Symbol::new(self.home, ident_id) - } - - /// Returns Err if the symbol resolved, but it was not exposed by the given module - pub fn qualified_lookup( - &mut self, - module_name: &str, - ident: &str, - region: Region, - ) -> Result { - debug_assert!( - !module_name.is_empty(), - "Called env.qualified_lookup with an unqualified ident: {ident:?}" - ); - - let module_name: ModuleName = module_name.into(); - - match self.module_ids.get_id(&module_name) { - Some(module_id) => { - // You can do qualified lookups on your own module, e.g. - // if I'm in the Foo module, I can do a `Foo.bar` lookup. - if module_id == self.home { - match self.ident_ids.get_id(ident) { - Some(ident_id) => { - let symbol = Symbol::new(module_id, ident_id); - - self.qualified_lookups.insert(symbol); - - Ok(symbol) - } - None => Err(RuntimeError::LookupNotInScope { - loc_name: Loc { - value: Ident::from(ident), - region, - }, - suggestion_options: self - .ident_ids - .ident_strs() - .map(|(_, string)| string.into()) - .collect(), - underscored_suggestion_region: None, - }), - } - } else { - match self.dep_idents.get(&module_id) { - Some(exposed_ids) => match exposed_ids.get_id(ident) { - Some(ident_id) => { - let symbol = Symbol::new(module_id, ident_id); - - self.qualified_lookups.insert(symbol); - - Ok(symbol) - } - None => { - let exposed_values = exposed_ids - .ident_strs() - .filter(|(_, ident)| { - ident.starts_with(|c: char| c.is_lowercase()) - }) - .map(|(_, ident)| Lowercase::from(ident)) - .collect(); - Err(RuntimeError::ValueNotExposed { - module_name, - ident: Ident::from(ident), - region, - exposed_values, - }) - } - }, - None => Err(RuntimeError::ModuleNotImported { - module_name, - imported_modules: self - .dep_idents - .keys() - .filter_map(|module_id| self.module_ids.get_name(*module_id)) - .map(|module_name| module_name.as_ref().into()) - .collect(), - region, - module_exists: true, - }), - } - } - } - None => Err(RuntimeError::ModuleNotImported { - module_name, - imported_modules: self - .module_ids - .available_modules() - .map(|string| string.as_ref().into()) - .collect(), - region, - module_exists: false, - }), - } - } -} diff --git a/crates/ast/src/lang/mod.rs b/crates/ast/src/lang/mod.rs deleted file mode 100644 index fa21ea740d..0000000000 --- a/crates/ast/src/lang/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod core; -pub mod env; -mod rigids; -pub mod scope; diff --git a/crates/ast/src/lang/rigids.rs b/crates/ast/src/lang/rigids.rs deleted file mode 100644 index d86aa5ec88..0000000000 --- a/crates/ast/src/lang/rigids.rs +++ /dev/null @@ -1,83 +0,0 @@ -use std::{ - collections::{HashMap, HashSet}, - hash::BuildHasherDefault, -}; - -use crate::mem_pool::{ - pool::Pool, pool_str::PoolStr, pool_vec::PoolVec, shallow_clone::ShallowClone, -}; -use roc_collections::all::WyHash; -use roc_module::ident::Lowercase; -use roc_types::subs::Variable; - -#[derive(Debug)] -pub struct Rigids { - // Rigid type variable = type variable where type is specified by the programmer - pub names: PoolVec<(Option, Variable)>, // 8B - padding: [u8; 1], -} - -#[allow(clippy::needless_collect)] -impl Rigids { - pub fn new( - named: HashMap>, - unnamed: HashSet>, - pool: &mut Pool, - ) -> Self { - let names = PoolVec::with_capacity((named.len() + unnamed.len()) as u32, pool); - - let mut temp_names = Vec::new(); - - temp_names.extend(named.iter().map(|(name, var)| (Some(name.as_str()), *var))); - - temp_names.extend(unnamed.iter().map(|var| (None, *var))); - - for (node_id, (opt_name, variable)) in names.iter_node_ids().zip(temp_names) { - let poolstr = opt_name.map(|name| PoolStr::new(name, pool)); - - pool[node_id] = (poolstr, variable); - } - - Self { - names, - padding: Default::default(), - } - } - - pub fn named(&self, pool: &mut Pool) -> PoolVec<(PoolStr, Variable)> { - let named = self - .names - .iter(pool) - .filter_map(|(opt_pool_str, var)| { - opt_pool_str.as_ref().map(|pool_str| (*pool_str, *var)) - }) - .collect::>(); - - PoolVec::new(named.into_iter(), pool) - } - - pub fn unnamed(&self, pool: &mut Pool) -> PoolVec { - let unnamed = self - .names - .iter(pool) - .filter_map(|(opt_pool_str, var)| { - if opt_pool_str.is_none() { - Some(*var) - } else { - None - } - }) - .collect::>(); - - PoolVec::new(unnamed.into_iter(), pool) - } -} - -impl ShallowClone for Rigids { - fn shallow_clone(&self) -> Self { - Self { - names: self.names.shallow_clone(), - padding: self.padding, - } - } -} diff --git a/crates/ast/src/lang/scope.rs b/crates/ast/src/lang/scope.rs deleted file mode 100644 index db2c23f3f5..0000000000 --- a/crates/ast/src/lang/scope.rs +++ /dev/null @@ -1,351 +0,0 @@ -#![allow(clippy::all)] -#![allow(dead_code)] -#![allow(unused_imports)] - -use std::fmt; - -use crate::ast_error::ASTResult; -use crate::builtin_aliases::{self, BuiltinAlias, FreeVars, SolvedType}; -use crate::mem_pool::pool::Pool; -use crate::mem_pool::pool_str::PoolStr; -use crate::mem_pool::pool_vec::PoolVec; -use crate::mem_pool::shallow_clone::ShallowClone; -use roc_collections::all::{MutMap, MutSet}; -use roc_module::ident::{Ident, Lowercase}; -use roc_module::symbol::{ - get_module_ident_ids, get_module_ident_ids_mut, IdentIds, IdentIdsByModule, Interns, ModuleId, - Symbol, -}; -use roc_problem::can::RuntimeError; -use roc_region::all::{Loc, Region}; -use roc_types::subs::{VarId, VarStore, Variable}; - -use super::core::types::{Alias, Type2, TypeId}; -use super::env::Env; - -fn solved_type_to_type_id( - pool: &mut Pool, - solved_type: &SolvedType, - free_vars: &mut FreeVars, - var_store: &mut VarStore, -) -> TypeId { - let typ2 = to_type2(pool, solved_type, free_vars, var_store); - - pool.add(typ2) -} - -fn to_type2( - pool: &mut Pool, - solved_type: &SolvedType, - free_vars: &mut FreeVars, - var_store: &mut VarStore, -) -> Type2 { - match solved_type { - // TODO(opaques): take opaques into account - SolvedType::Alias(symbol, solved_type_variables, _todo, solved_actual, _kind) => { - let type_variables = PoolVec::with_capacity(solved_type_variables.len() as u32, pool); - - for (type_variable_node_id, solved_arg) in type_variables - .iter_node_ids() - .zip(solved_type_variables.iter()) - { - let typ2 = to_type2(pool, solved_arg, free_vars, var_store); - - let node = pool.add(typ2); - - pool[type_variable_node_id] = node; - } - - let actual_typ2 = to_type2(pool, solved_actual, free_vars, var_store); - - let actual = pool.add(actual_typ2); - - let typ2 = Type2::Alias(*symbol, type_variables, actual); - - typ2 - } - SolvedType::TagUnion(tags, ext) => { - let new_tags = PoolVec::with_capacity(tags.len() as u32, pool); - - for (tag_node_id, (tag_name, args)) in new_tags.iter_node_ids().zip(tags.iter()) { - let new_args: PoolVec = PoolVec::with_capacity(args.len() as u32, pool); - - for (arg_node_id, arg) in new_args.iter_node_ids().zip(args.iter()) { - let node = to_type2(pool, arg, free_vars, var_store); - - pool[arg_node_id] = node; - } - - pool[tag_node_id] = (tag_name.clone(), new_args); - } - - let actual_typ2 = to_type2(pool, ext, free_vars, var_store); - - let actual = pool.add(actual_typ2); - - let typ2 = Type2::TagUnion(new_tags, actual); - - typ2 - } - SolvedType::Flex(var_id) => { - Type2::Variable(var_id_to_flex_var(*var_id, free_vars, var_store)) - } - SolvedType::EmptyTagUnion => Type2::EmptyTagUnion, - rest => todo!("{:?}", rest), - } -} - -fn var_id_to_flex_var( - var_id: VarId, - free_vars: &mut FreeVars, - var_store: &mut VarStore, -) -> Variable { - if let Some(var) = free_vars.unnamed_vars.get(&var_id) { - *var - } else { - let var = var_store.fresh(); - free_vars.unnamed_vars.insert(var_id, var); - - var - } -} - -#[derive(Debug)] -pub struct Scope { - /// All the identifiers in scope, mapped to were they were defined and - /// the Symbol they resolve to. - idents: MutMap, - - /// A cache of all the symbols in scope. This makes lookups much - /// faster when checking for unused defs and unused arguments. - symbols: MutMap, - - /// The type aliases currently in scope - aliases: MutMap, - - /// The current module being processed. This will be used to turn - /// unqualified idents into Symbols. - home: ModuleId, -} - -impl Scope { - pub fn new(home: ModuleId, pool: &mut Pool, var_store: &mut VarStore) -> Scope { - let solved_aliases = builtin_aliases::aliases(); - let mut aliases = MutMap::default(); - - for (symbol, builtin_alias) in solved_aliases { - // let BuiltinAlias { region, vars, typ } = builtin_alias; - let BuiltinAlias { vars, typ, .. } = builtin_alias; - - let mut free_vars = FreeVars::default(); - - // roc_types::solved_types::to_type(&typ, &mut free_vars, var_store); - let actual = solved_type_to_type_id(pool, &typ, &mut free_vars, var_store); - - // make sure to sort these variables to make them line up with the type arguments - let mut type_variables: Vec<_> = free_vars.unnamed_vars.into_iter().collect(); - type_variables.sort(); - - debug_assert_eq!(vars.len(), type_variables.len()); - let variables = PoolVec::with_capacity(vars.len() as u32, pool); - - let it = variables - .iter_node_ids() - .zip(vars.iter()) - .zip(type_variables); - for ((node_id, loc_name), (_, var)) in it { - // TODO region is ignored, but "fake" anyway. How to resolve? - let name = PoolStr::new(loc_name.value.as_str(), pool); - pool[node_id] = (name, var); - } - - let alias = Alias { - actual, - /// We know that builtin aliases have no hidden variables (e.g. in closures) - hidden_variables: PoolVec::empty(pool), - targs: variables, - }; - - aliases.insert(symbol, alias); - } - - let idents = Symbol::apply_types_in_scope(); - let idents: MutMap<_, _> = idents.into_iter().collect(); - - Scope { - home, - idents, - symbols: MutMap::default(), - aliases, - } - } - - pub fn idents(&self) -> impl Iterator { - self.idents.iter() - } - - pub fn symbols(&self) -> impl Iterator + '_ { - self.symbols.iter().map(|(x, y)| (*x, *y)) - } - - pub fn contains_ident(&self, ident: &Ident) -> bool { - self.idents.contains_key(ident) - } - - pub fn contains_symbol(&self, symbol: Symbol) -> bool { - self.symbols.contains_key(&symbol) - } - - pub fn num_idents(&self) -> usize { - self.idents.len() - } - - pub fn lookup(&mut self, ident: &Ident, region: Region) -> Result { - match self.idents.get(ident) { - Some((symbol, _)) => Ok(*symbol), - None => Err(RuntimeError::LookupNotInScope { - loc_name: Loc { - region, - value: ident.clone().into(), - }, - suggestion_options: self.idents.keys().map(|v| v.as_ref().into()).collect(), - underscored_suggestion_region: None, - }), - } - } - - pub fn lookup_alias(&self, symbol: Symbol) -> Option<&Alias> { - self.aliases.get(&symbol) - } - - /// Introduce a new ident to scope. - /// - /// Returns Err if this would shadow an existing ident, including the - /// Symbol and Region of the ident we already had in scope under that name. - pub fn introduce( - &mut self, - ident: Ident, - exposed_ident_ids: &IdentIds, - all_ident_ids: &mut IdentIds, - region: Region, - ) -> Result)> { - match self.idents.get(&ident) { - Some((_, original_region)) => { - let shadow = Loc { - value: ident, - region, - }; - - Err((*original_region, shadow)) - } - None => { - // If this IdentId was already added previously - // when the value was exposed in the module header, - // use that existing IdentId. Otherwise, create a fresh one. - let ident_id = match exposed_ident_ids.get_id(ident.as_str()) { - Some(ident_id) => ident_id, - None => all_ident_ids.add_str(ident.as_str()), - }; - - let symbol = Symbol::new(self.home, ident_id); - - self.symbols.insert(symbol, region); - self.idents.insert(ident, (symbol, region)); - - Ok(symbol) - } - } - } - - /// Ignore an identifier. - /// - /// Used for record guards like { x: Just _ } - pub fn ignore(&mut self, ident: Ident, all_ident_ids: &mut IdentIds) -> Symbol { - let ident_id = all_ident_ids.add_str(ident.as_str()); - Symbol::new(self.home, ident_id) - } - - /// Import a Symbol from another module into this module's top-level scope. - /// - /// Returns Err if this would shadow an existing ident, including the - /// Symbol and Region of the ident we already had in scope under that name. - pub fn import( - &mut self, - ident: Ident, - symbol: Symbol, - region: Region, - ) -> Result<(), (Symbol, Region)> { - match self.idents.get(&ident) { - Some(shadowed) => Err(*shadowed), - None => { - self.symbols.insert(symbol, region); - self.idents.insert(ident, (symbol, region)); - - Ok(()) - } - } - } - - pub fn add_alias( - &mut self, - pool: &mut Pool, - name: Symbol, - vars: PoolVec<(PoolStr, Variable)>, - typ: TypeId, - ) { - let mut hidden_variables = MutSet::default(); - hidden_variables.extend(typ.variables(pool)); - - for loc_var in vars.iter(pool) { - hidden_variables.remove(&loc_var.1); - } - - let hidden_variables_vec = PoolVec::with_capacity(hidden_variables.len() as u32, pool); - - for (node_id, var) in hidden_variables_vec.iter_node_ids().zip(hidden_variables) { - pool[node_id] = var; - } - - let alias = Alias { - targs: vars, - hidden_variables: hidden_variables_vec, - actual: typ, - }; - - self.aliases.insert(name, alias); - } - - pub fn contains_alias(&mut self, name: Symbol) -> bool { - self.aliases.contains_key(&name) - } - - pub fn fill_scope(&mut self, env: &Env, all_ident_ids: &mut IdentIdsByModule) -> ASTResult<()> { - let ident_ids = get_module_ident_ids(all_ident_ids, &env.home)?.clone(); - - for (_, ident_ref) in ident_ids.ident_strs() { - self.introduce( - ident_ref.into(), - &env.exposed_ident_ids, - get_module_ident_ids_mut(all_ident_ids, &env.home)?, - Region::zero(), - )?; - } - - Ok(()) - } -} - -impl ShallowClone for Scope { - fn shallow_clone(&self) -> Self { - Self { - idents: self.idents.clone(), - symbols: self.symbols.clone(), - aliases: self - .aliases - .iter() - .map(|(s, a)| (*s, a.shallow_clone())) - .collect(), - home: self.home, - } - } -} diff --git a/crates/ast/src/lib.rs b/crates/ast/src/lib.rs deleted file mode 100644 index 03b303688f..0000000000 --- a/crates/ast/src/lib.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! Library for the Roc AST -//! -//! Code to represent the [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) -//! as used by the editor. In contrast to the compiler, the types in this AST do -//! not keep track of the location of the matching code in the source file. -pub mod ast_error; -mod builtin_aliases; -mod canonicalization; -pub mod constrain; -pub mod lang; -pub mod mem_pool; -pub mod module; -pub mod parse; -pub mod solve_type; diff --git a/crates/ast/src/mem_pool/mod.rs b/crates/ast/src/mem_pool/mod.rs deleted file mode 100644 index b6c8c83b8f..0000000000 --- a/crates/ast/src/mem_pool/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod pool; -pub mod pool_str; -pub mod pool_vec; -pub mod shallow_clone; diff --git a/crates/ast/src/mem_pool/pool.rs b/crates/ast/src/mem_pool/pool.rs deleted file mode 100644 index 36a534699e..0000000000 --- a/crates/ast/src/mem_pool/pool.rs +++ /dev/null @@ -1,269 +0,0 @@ -/// A memory pool of 32-byte nodes. The node value 0 is reserved for the pool's -/// use, and valid nodes may never have that value. -/// -/// Internally, the pool is divided into pages of 4096 bytes. It stores nodes -/// into one page at a time, and when it runs out, it uses mmap to reserve an -/// anonymous memory page in which to store nodes. -/// -/// Since nodes are 32 bytes, one page can store 128 nodes; you can access a -/// particular node by its NodeId, which is an opaque wrapper around a pointer. -/// -/// Pages also use the node value 0 (all 0 bits) to mark nodes as unoccupied. -/// This is important for performance. -use std::any::type_name; -use std::ffi::c_void; -use std::marker::PhantomData; -use std::mem::{align_of, size_of, MaybeUninit}; - -pub const NODE_BYTES: usize = 32; - -// Each page has 128 slots. Each slot holds one 32B node -// This means each page is 4096B, which is the size of a memory page -// on typical systems where the compiler will be run. -// -// Nice things about this system include: -// * Allocating a new page is as simple as asking the OS for a memory page. -// * Since each node is 32B, each node's memory address will be a multiple of 16. -// * Thanks to the free lists and our consistent chunk sizes, we should -// end up with very little fragmentation. -// * Finding a slot for a given node should be very fast: see if the relevant -// free list has any openings; if not, try the next size up. -// -// Less nice things include: -// * This system makes it very hard to ever give a page back to the OS. -// We could try doing the Mesh Allocator strategy: whenever we allocate -// something, assign it to a random slot in the page, and then periodically -// try to merge two pages into one (by locking and remapping them in the OS) -// and then returning the redundant physical page back to the OS. This should -// work in theory, but is pretty complicated, and we'd need to schedule it. -// Keep in mind that we can't use the Mesh Allocator itself because it returns -// usize pointers, which would be too big for us to have 16B nodes. -// On the plus side, we could be okay with higher memory usage early on, -// and then later use the Mesh strategy to reduce long-running memory usage. -// -// With this system, we can allocate up to 4B nodes. If we wanted to keep -// a generational index in there, like https://crates.io/crates/sharded-slab -// does, we could use some of the 32 bits for that. For example, if we wanted -// to have a 5-bit generational index (supporting up to 32 generations), then -// we would have 27 bits remaining, meaning we could only support at most -// 134M nodes. Since the editor has a separate Pool for each module, is that -// enough for any single module we'll encounter in practice? Probably, and -// especially if we allocate super large collection literals on the heap instead -// of in the pool. -// -// Another possible design is to try to catch reuse bugs using an "ASan" like -// approach: in development builds, whenever we "free" a particular slot, we -// can add it to a dev-build-only "freed nodes" list and don't hand it back -// out (so, we leak the memory.) Then we can (again, in development builds only) -// check to see if we're about to store something in zeroed-out memory; if so, check -// to see if it was - -#[derive(Debug, Eq)] -pub struct NodeId { - pub(super) index: u32, - pub(super) _phantom: PhantomData, -} - -impl Clone for NodeId { - fn clone(&self) -> Self { - NodeId { - index: self.index, - _phantom: PhantomData, - } - } -} - -impl PartialEq for NodeId { - fn eq(&self, other: &Self) -> bool { - self.index == other.index - } -} - -impl Copy for NodeId {} - -#[derive(Debug)] -pub struct Pool { - pub(super) nodes: *mut [MaybeUninit; NODE_BYTES], - num_nodes: u32, - capacity: u32, - // free_1node_slots: Vec>, -} - -impl Pool { - pub fn with_capacity(nodes: u32) -> Self { - // round up number of nodes requested to nearest page size in bytes - let bytes_per_page = page_size::get(); - let node_bytes = NODE_BYTES * nodes as usize; - let leftover = node_bytes % bytes_per_page; - let bytes_to_mmap = if leftover == 0 { - node_bytes - } else { - node_bytes + bytes_per_page - leftover - }; - - let nodes = unsafe { - // mmap anonymous memory pages - that is, contiguous virtual memory - // addresses from the OS which will be lazily translated into - // physical memory one 4096-byte page at a time, once we actually - // try to read or write in that page's address range. - #[cfg(unix)] - { - use libc::{MAP_ANONYMOUS, MAP_PRIVATE, PROT_READ, PROT_WRITE}; - - libc::mmap( - std::ptr::null_mut(), - bytes_to_mmap, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, - 0, - 0, - ) - } - #[cfg(windows)] - { - use winapi::um::memoryapi::VirtualAlloc; - use winapi::um::winnt::PAGE_READWRITE; - use winapi::um::winnt::{MEM_COMMIT, MEM_RESERVE}; - - VirtualAlloc( - std::ptr::null_mut(), - bytes_to_mmap, - MEM_COMMIT | MEM_RESERVE, - PAGE_READWRITE, - ) - } - } as *mut [MaybeUninit; NODE_BYTES]; - - // This is our actual capacity, in nodes. - // It might be higher than the requested capacity due to rounding up - // to nearest page size. - let capacity = (bytes_to_mmap / NODE_BYTES) as u32; - - Pool { - nodes, - num_nodes: 0, - capacity, - } - } - - pub fn add(&mut self, node: T) -> NodeId { - // It's only safe to store this if T fits in S. - debug_assert!( - size_of::() <= NODE_BYTES, - "{} has a size of {}, but it needs to be at most {}", - type_name::(), - size_of::(), - NODE_BYTES - ); - - let node_id = self.reserve(1); - - let node_ptr = self.get_ptr(node_id); - - unsafe { node_ptr.write(MaybeUninit::new(node)) }; - - node_id - } - - /// Reserves the given number of contiguous node slots, and returns - /// the NodeId of the first one. We only allow reserving 2^32 in a row. - pub(super) fn reserve(&mut self, nodes: u32) -> NodeId { - // TODO once we have a free list, look in there for an open slot first! - let index = self.num_nodes; - - if index < self.capacity { - self.num_nodes = index + nodes; - - NodeId { - index, - _phantom: PhantomData, - } - } else { - todo!("pool ran out of capacity. TODO reallocate the nodes pointer to map to a bigger space. Can use mremap on Linux, but must memcpy lots of bytes on macOS and Windows."); - } - } - - pub fn get<'b, T>(&self, node_id: NodeId) -> &'b T { - unsafe { - let node_ptr = self.get_ptr(node_id) as *const T; - - &*node_ptr - } - } - - pub fn get_mut(&mut self, node_id: NodeId) -> &mut T { - unsafe { - let node_ptr = self.get_ptr(node_id) as *mut T; - - &mut *node_ptr - } - } - - pub fn set(&mut self, node_id: NodeId, element: T) { - unsafe { - let node_ptr = self.get_ptr(node_id); - - node_ptr.write(MaybeUninit::new(element)); - } - } - - fn get_ptr(&self, node_id: NodeId) -> *mut MaybeUninit { - let node_offset = unsafe { self.nodes.offset(node_id.index as isize) }; - - // This checks if the node_offset is aligned to T - assert!(0 == (node_offset as usize) & (align_of::() - 1)); - - node_offset as *mut MaybeUninit - } - - // A node is available iff its bytes are all zeroes - #[allow(dead_code)] - fn is_available(&self, node_id: NodeId) -> bool { - debug_assert_eq!(size_of::(), NODE_BYTES); - - unsafe { - let node_ptr = self.nodes.offset(node_id.index as isize) as *const [u8; NODE_BYTES]; - - *node_ptr == [0; NODE_BYTES] - } - } -} - -impl std::ops::Index> for Pool { - type Output = T; - - fn index(&self, node_id: NodeId) -> &Self::Output { - self.get(node_id) - } -} - -impl std::ops::IndexMut> for Pool { - fn index_mut(&mut self, node_id: NodeId) -> &mut Self::Output { - self.get_mut(node_id) - } -} - -impl Drop for Pool { - fn drop(&mut self) { - unsafe { - #[cfg(unix)] - { - libc::munmap( - self.nodes as *mut c_void, - NODE_BYTES * self.capacity as usize, - ); - } - #[cfg(windows)] - { - use winapi::um::memoryapi::VirtualFree; - use winapi::um::winnt::MEM_RELEASE; - - VirtualFree( - self.nodes as *mut c_void, - NODE_BYTES * self.capacity as usize, - MEM_RELEASE, - ); - } - } - } -} diff --git a/crates/ast/src/mem_pool/pool_str.rs b/crates/ast/src/mem_pool/pool_str.rs deleted file mode 100644 index 9bfb327365..0000000000 --- a/crates/ast/src/mem_pool/pool_str.rs +++ /dev/null @@ -1,86 +0,0 @@ -use super::pool::{NodeId, Pool, NODE_BYTES}; -use super::shallow_clone::ShallowClone; -use std::ffi::c_void; -use std::marker::PhantomData; -use std::mem::size_of; - -/// A string containing at most 2^32 pool-allocated bytes. -#[derive(Debug, Copy, Clone)] -pub struct PoolStr { - first_node_id: NodeId<()>, - len: u32, -} - -#[test] -fn pool_str_size() { - assert_eq!(size_of::(), 8); -} - -impl PoolStr { - pub fn new(string: &str, pool: &mut Pool) -> Self { - debug_assert!(string.len() <= u32::MAX as usize); - - let chars_per_node = NODE_BYTES / size_of::(); - - let number_of_nodes = f64::ceil(string.len() as f64 / chars_per_node as f64) as u32; - - if number_of_nodes > 0 { - let first_node_id = pool.reserve(number_of_nodes); - let index = first_node_id.index as isize; - let next_node_ptr = unsafe { pool.nodes.offset(index) } as *mut c_void; - - unsafe { - libc::memcpy( - next_node_ptr, - string.as_ptr() as *const c_void, - string.len(), - ); - } - - PoolStr { - first_node_id, - len: string.len() as u32, - } - } else { - PoolStr { - first_node_id: NodeId { - index: 0, - _phantom: PhantomData, - }, - len: 0, - } - } - } - - pub fn as_str(&self, pool: &Pool) -> &str { - unsafe { - let node_ptr = pool.nodes.offset(self.first_node_id.index as isize) as *const u8; - - let node_slice: &[u8] = std::slice::from_raw_parts(node_ptr, self.len as usize); - - std::str::from_utf8_unchecked(&node_slice[0..self.len as usize]) - } - } - - #[allow(clippy::len_without_is_empty)] - pub fn len(&self, pool: &Pool) -> usize { - let contents = self.as_str(pool); - - contents.len() - } - - pub fn is_empty(&self, pool: &Pool) -> bool { - self.len(pool) == 0 - } -} - -impl ShallowClone for PoolStr { - fn shallow_clone(&self) -> Self { - // Question: should this fully clone, or is a shallow copy - // (and the aliasing it entails) OK? - Self { - first_node_id: self.first_node_id, - len: self.len, - } - } -} diff --git a/crates/ast/src/mem_pool/pool_vec.rs b/crates/ast/src/mem_pool/pool_vec.rs deleted file mode 100644 index 63b3e51d3a..0000000000 --- a/crates/ast/src/mem_pool/pool_vec.rs +++ /dev/null @@ -1,323 +0,0 @@ -use super::pool::{NodeId, Pool, NODE_BYTES}; -use super::shallow_clone::ShallowClone; -use std::any::type_name; -use std::cmp::Ordering; -use std::ffi::c_void; -use std::marker::PhantomData; -use std::mem::size_of; - -/// An array of at most 2^32 pool-allocated nodes. -#[derive(Debug)] -pub struct PoolVec { - first_node_id: NodeId, - len: u32, -} - -#[test] -fn pool_vec_size() { - assert_eq!(size_of::>(), 8); -} - -impl<'a, T: 'a + Sized> PoolVec { - pub fn empty(pool: &mut Pool) -> Self { - Self::new(std::iter::empty(), pool) - } - - pub fn with_capacity(len: u32, pool: &mut Pool) -> Self { - debug_assert!( - size_of::() <= NODE_BYTES, - "{} has a size of {}", - type_name::(), - size_of::() - ); - - if len == 0 { - Self::empty(pool) - } else { - let first_node_id = pool.reserve(len); - - PoolVec { first_node_id, len } - } - } - - pub fn len(&self) -> usize { - self.len as usize - } - - pub fn is_empty(&self) -> bool { - self.len == 0 - } - - pub fn new>(nodes: I, pool: &mut Pool) -> Self { - debug_assert!(nodes.len() <= u32::MAX as usize); - debug_assert!(size_of::() <= NODE_BYTES); - - let len = nodes.len() as u32; - - if len > 0 { - let first_node_id = pool.reserve(len); - let index = first_node_id.index as isize; - let mut next_node_ptr = unsafe { pool.nodes.offset(index) } as *mut T; - - for (indx_inc, node) in nodes.enumerate() { - unsafe { - *next_node_ptr = node; - - next_node_ptr = pool.nodes.offset(index + (indx_inc as isize) + 1) as *mut T; - } - } - - PoolVec { first_node_id, len } - } else { - PoolVec { - first_node_id: NodeId { - index: 0, - _phantom: PhantomData, - }, - len: 0, - } - } - } - - pub fn iter(&self, pool: &'a Pool) -> impl ExactSizeIterator { - self.pool_list_iter(pool) - } - - pub fn iter_mut(&self, pool: &'a mut Pool) -> impl ExactSizeIterator { - self.pool_list_iter_mut(pool) - } - - pub fn iter_node_ids(&self) -> impl ExactSizeIterator> { - self.pool_list_iter_node_ids() - } - - /// Private version of into_iter which exposes the implementation detail - /// of PoolVecIter. We don't want that struct to be public, but we - /// actually do want to have this separate function for code reuse - /// in the iterator's next() method. - #[inline(always)] - fn pool_list_iter(&self, pool: &'a Pool) -> PoolVecIter<'a, T> { - PoolVecIter { - pool, - current_node_id: self.first_node_id, - len_remaining: self.len, - } - } - - #[inline(always)] - fn pool_list_iter_mut(&self, pool: &'a Pool) -> PoolVecIterMut<'a, T> { - PoolVecIterMut { - pool, - current_node_id: self.first_node_id, - len_remaining: self.len, - } - } - - #[inline(always)] - fn pool_list_iter_node_ids(&self) -> PoolVecIterNodeIds { - PoolVecIterNodeIds { - current_node_id: self.first_node_id, - len_remaining: self.len, - } - } - - pub fn free(self, pool: &'a mut Pool) { - // zero out the memory - unsafe { - let index = self.first_node_id.index as isize; - let node_ptr = pool.nodes.offset(index) as *mut c_void; - let bytes = self.len as usize * NODE_BYTES; - - libc::memset(node_ptr, 0, bytes); - } - - // TODO insert it into the pool's free list - } -} - -impl ShallowClone for PoolVec { - fn shallow_clone(&self) -> Self { - // Question: should this fully clone, or is a shallow copy - // (and the aliasing it entails) OK? - Self { - first_node_id: self.first_node_id, - len: self.len, - } - } -} - -struct PoolVecIter<'a, T> { - pool: &'a Pool, - current_node_id: NodeId, - len_remaining: u32, -} - -impl<'a, T> ExactSizeIterator for PoolVecIter<'a, T> -where - T: 'a, -{ - fn len(&self) -> usize { - self.len_remaining as usize - } -} - -impl<'a, T> Iterator for PoolVecIter<'a, T> -where - T: 'a, -{ - type Item = &'a T; - - fn next(&mut self) -> Option { - let len_remaining = self.len_remaining; - - match len_remaining.cmp(&1) { - Ordering::Greater => { - // Get the current node - let index = self.current_node_id.index; - let node_ptr = unsafe { self.pool.nodes.offset(index as isize) } as *const T; - - // Advance the node pointer to the next node in the current page - self.current_node_id = NodeId { - index: index + 1, - _phantom: PhantomData, - }; - self.len_remaining = len_remaining - 1; - - Some(unsafe { &*node_ptr }) - } - Ordering::Equal => { - self.len_remaining = 0; - - // Don't advance the node pointer's node, because that might - // advance past the end of the page! - - let index = self.current_node_id.index; - let node_ptr = unsafe { self.pool.nodes.offset(index as isize) } as *const T; - - Some(unsafe { &*node_ptr }) - } - Ordering::Less => { - // len_remaining was 0 - None - } - } - } -} - -struct PoolVecIterMut<'a, T> { - pool: &'a Pool, - current_node_id: NodeId, - len_remaining: u32, -} - -impl<'a, T> ExactSizeIterator for PoolVecIterMut<'a, T> -where - T: 'a, -{ - fn len(&self) -> usize { - self.len_remaining as usize - } -} - -impl<'a, T> Iterator for PoolVecIterMut<'a, T> -where - T: 'a, -{ - type Item = &'a mut T; - - fn next(&mut self) -> Option { - let len_remaining = self.len_remaining; - - match len_remaining.cmp(&1) { - Ordering::Greater => { - // Get the current node - let index = self.current_node_id.index; - let node_ptr = unsafe { self.pool.nodes.offset(index as isize) } as *mut T; - - // Advance the node pointer to the next node in the current page - self.current_node_id = NodeId { - index: index + 1, - _phantom: PhantomData, - }; - self.len_remaining = len_remaining - 1; - - Some(unsafe { &mut *node_ptr }) - } - Ordering::Equal => { - self.len_remaining = 0; - - // Don't advance the node pointer's node, because that might - // advance past the end of the page! - - let index = self.current_node_id.index; - let node_ptr = unsafe { self.pool.nodes.offset(index as isize) } as *mut T; - - Some(unsafe { &mut *node_ptr }) - } - Ordering::Less => { - // len_remaining was 0 - None - } - } - } -} - -struct PoolVecIterNodeIds { - current_node_id: NodeId, - len_remaining: u32, -} - -impl ExactSizeIterator for PoolVecIterNodeIds { - fn len(&self) -> usize { - self.len_remaining as usize - } -} - -impl Iterator for PoolVecIterNodeIds { - type Item = NodeId; - - fn next(&mut self) -> Option { - let len_remaining = self.len_remaining; - - match len_remaining.cmp(&1) { - Ordering::Greater => { - // Get the current node - let current = self.current_node_id; - let index = current.index; - - // Advance the node pointer to the next node in the current page - self.current_node_id = NodeId { - index: index + 1, - _phantom: PhantomData, - }; - self.len_remaining = len_remaining - 1; - - Some(current) - } - Ordering::Equal => { - self.len_remaining = 0; - - // Don't advance the node pointer's node, because that might - // advance past the end of the page! - - Some(self.current_node_id) - } - Ordering::Less => { - // len_remaining was 0 - None - } - } - } -} - -#[test] -fn pool_vec_iter_test() { - let expected_vec: Vec = vec![2, 4, 8, 16]; - - let mut test_pool = Pool::with_capacity(1024); - let pool_vec = PoolVec::new(expected_vec.clone().into_iter(), &mut test_pool); - - let current_vec: Vec = pool_vec.iter(&test_pool).copied().collect(); - - assert_eq!(current_vec, expected_vec); -} diff --git a/crates/ast/src/mem_pool/shallow_clone.rs b/crates/ast/src/mem_pool/shallow_clone.rs deleted file mode 100644 index 7bcd6b46e3..0000000000 --- a/crates/ast/src/mem_pool/shallow_clone.rs +++ /dev/null @@ -1,35 +0,0 @@ -use roc_can::expected::Expected; -use roc_can::expected::PExpected; - -/// Clones the outer node, but does not clone any nodeids -pub trait ShallowClone { - fn shallow_clone(&self) -> Self; -} - -impl ShallowClone for Expected -where - T: ShallowClone, -{ - fn shallow_clone(&self) -> Self { - use Expected::*; - - match self { - NoExpectation(t) => NoExpectation(t.shallow_clone()), - ForReason(reason, t, region) => ForReason(reason.clone(), t.shallow_clone(), *region), - FromAnnotation(loc_pat, n, source, t) => { - FromAnnotation(loc_pat.clone(), *n, *source, t.shallow_clone()) - } - } - } -} - -impl ShallowClone for PExpected { - fn shallow_clone(&self) -> Self { - use PExpected::*; - - match self { - NoExpectation(t) => NoExpectation(t.shallow_clone()), - ForReason(reason, t, region) => ForReason(reason.clone(), t.shallow_clone(), *region), - } - } -} diff --git a/crates/ast/src/module.rs b/crates/ast/src/module.rs deleted file mode 100644 index 942e6d4d5b..0000000000 --- a/crates/ast/src/module.rs +++ /dev/null @@ -1,33 +0,0 @@ -use bumpalo::Bump; -use roc_load::{ExecutionMode, LoadConfig, LoadedModule, Threading}; -use roc_packaging::cache::RocCacheDir; -use roc_reporting::report::DEFAULT_PALETTE; -use roc_target::TargetInfo; -use std::path::Path; - -pub fn load_module( - src_file: &Path, - roc_cache_dir: RocCacheDir<'_>, - threading: Threading, -) -> LoadedModule { - let load_config = LoadConfig { - target_info: TargetInfo::default_x86_64(), // editor only needs type info, so this is unused - function_kind: roc_solve::FunctionKind::LambdaSet, // TODO the editor may need to dynamically change this - render: roc_reporting::report::RenderTarget::ColorTerminal, - palette: DEFAULT_PALETTE, - threading, - exec_mode: ExecutionMode::Check, - }; - - let arena = Bump::new(); - let loaded = - roc_load::load_and_typecheck(&arena, src_file.to_path_buf(), roc_cache_dir, load_config); - - match loaded { - Ok(x) => x, - Err(roc_load::LoadingProblem::FormattedReport(report)) => { - panic!("Failed to load module from src_file: {src_file:?}. Report: {report}"); - } - Err(e) => panic!("Failed to load module from src_file {src_file:?}: {e:?}"), - } -} diff --git a/crates/ast/src/parse/mod.rs b/crates/ast/src/parse/mod.rs deleted file mode 100644 index 31bf78fd5e..0000000000 --- a/crates/ast/src/parse/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod parse_ast; -pub mod parse_header; diff --git a/crates/ast/src/parse/parse_ast.rs b/crates/ast/src/parse/parse_ast.rs deleted file mode 100644 index 378b8838e2..0000000000 --- a/crates/ast/src/parse/parse_ast.rs +++ /dev/null @@ -1,54 +0,0 @@ -use bumpalo::Bump; -use roc_module::symbol::Interns; -use roc_region::all::Region; - -use crate::{ - ast_error::ASTResult, - lang::{ - core::{ - ast::AST, - def::{def2::DefId, def_to_def2::str_to_def2}, - expr::expr2::Expr2, - }, - env::Env, - scope::Scope, - }, -}; - -use super::parse_header; - -pub fn parse_from_string<'a>( - code_str: &'a str, - env: &mut Env<'a>, - ast_arena: &'a Bump, - interns: &mut Interns, -) -> ASTResult { - let blank_line_indx = code_str - .find("\n\n") - .expect("I was expecting two newline chars to split header and rest of code."); - - let header_str = &code_str[0..blank_line_indx]; - let tail_str = &code_str[blank_line_indx..]; - - let mut scope = Scope::new(env.home, env.pool, env.var_store); - scope.fill_scope(env, &mut interns.all_ident_ids)?; - - let region = Region::zero(); - - let mut def_ids = Vec::::new(); - - let def2_vec = str_to_def2(ast_arena, tail_str, env, &mut scope, region)?; - - for def2 in def2_vec { - let def_id = env.pool.add(def2); - - def_ids.push(def_id); - } - - let ast_node_id = env.pool.add(Expr2::Blank); - - Ok(AST { - header: parse_header::parse_from_string(header_str, ast_node_id), - def_ids, - }) -} diff --git a/crates/ast/src/parse/parse_header.rs b/crates/ast/src/parse/parse_header.rs deleted file mode 100644 index 21991ba842..0000000000 --- a/crates/ast/src/parse/parse_header.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::lang::core::{expr::expr2::ExprId, header::AppHeader}; - -// TODO don't use mock struct and actually parse string -pub fn parse_from_string(_header_str: &str, ast_node_id: ExprId) -> AppHeader { - AppHeader { - app_name: "\"untitled-app\"".to_owned(), - packages_base: "\"rust-platform/main.roc\"".to_owned(), - imports: vec![], - provides: vec!["main".to_owned()], - ast_node_id, - } -} diff --git a/crates/ast/src/solve_type.rs b/crates/ast/src/solve_type.rs deleted file mode 100644 index eca1326b11..0000000000 --- a/crates/ast/src/solve_type.rs +++ /dev/null @@ -1,2027 +0,0 @@ -#![allow(clippy::all)] -#![allow(dead_code)] -use bumpalo::Bump; -use roc_can::expected::{Expected, PExpected}; -use roc_checkmate::with_checkmate; -use roc_collections::all::{BumpMap, BumpMapDefault, MutMap}; -use roc_error_macros::internal_error; -use roc_module::ident::TagName; -use roc_module::symbol::Symbol; -use roc_region::all::{Loc, Region}; -use roc_solve::module::Solved; -use roc_solve_schema::UnificationMode; -use roc_types::subs::{ - self, AliasVariables, Content, Descriptor, FlatType, Mark, OptVariable, Rank, RecordFields, - Subs, SubsSlice, TagExt, UnionLambdas, UnionTags, Variable, VariableSubsSlice, -}; -use roc_types::types::{ - gather_fields_unsorted_iter, Alias, AliasKind, Category, ErrorType, PatternCategory, Polarity, - RecordField, -}; -use roc_unify::unify::unify; -use roc_unify::unify::Unified::*; -use roc_unify::Env as UEnv; - -use crate::constrain::{Constraint, PresenceConstraint}; -use crate::lang::core::types::Type2; -use crate::mem_pool::pool::Pool; -use crate::mem_pool::pool_vec::PoolVec; -use crate::mem_pool::shallow_clone::ShallowClone; - -// Type checking system adapted from Elm by Evan Czaplicki, BSD-3-Clause Licensed -// https://github.com/elm/compiler -// Thank you, Evan! - -// A lot of energy was put into making type inference fast. That means it's pretty intimidating. -// -// Fundamentally, type inference assigns very general types based on syntax, and then tries to -// make all the pieces fit together. For instance when writing -// -// > f x -// -// We know that `f` is a function, and thus must have some type `a -> b`. -// `x` is just a variable, that gets the type `c` -// -// Next comes constraint generation. For `f x` to be well-typed, -// it must be the case that `c = a`, So a constraint `Eq(c, a)` is generated. -// But `Eq` is a bit special: `c` does not need to equal `a` exactly, but they need to be equivalent. -// This allows for instance the use of aliases. `c` could be an alias, and so looks different from -// `a`, but they still represent the same type. -// -// Then we get to solving, which happens in this file. -// -// When we hit an `Eq` constraint, then we check whether the two involved types are in fact -// equivalent using unification, and when they are, we can substitute one for the other. -// -// When all constraints are processed, and no unification errors have occurred, then the program -// is type-correct. Otherwise the errors are reported. -// -// Now, coming back to efficiency, this type checker uses *ranks* to optimize -// The rank tracks the number of let-bindings a variable is "under". Top-level definitions -// have rank 1. A let in a top-level definition gets rank 2, and so on. -// -// This has to do with generalization of type variables. This is described here -// -// http://okmij.org/ftp/ML/generalization.html#levels -// -// The problem is that when doing inference naively, this program would fail to typecheck -// -// f = -// id = \x -> x -// -// { a: id 1, b: id "foo" } -// -// Because `id` is applied to an integer, the type `Int -> Int` is inferred, which then gives a -// type error for `id "foo"`. -// -// Thus instead the inferred type for `id` is generalized (see the `generalize` function) to `a -> a`. -// Ranks are used to limit the number of type variables considered for generalization. Only those inside -// of the let (so those used in inferring the type of `\x -> x`) are considered. - -#[derive(Debug, Clone)] -pub enum TypeError { - BadExpr(Region, Category, ErrorType, Expected), - BadPattern(Region, PatternCategory, ErrorType, PExpected), - CircularType(Region, Symbol, ErrorType), - UnexposedLookup(Symbol), -} - -#[derive(Clone, Debug, Default)] -pub struct Env { - pub vars_by_symbol: MutMap, - pub aliases: MutMap, -} - -const DEFAULT_POOLS: usize = 8; - -#[derive(Clone, Debug)] -struct Pools(Vec>); - -impl Default for Pools { - fn default() -> Self { - Pools::new(DEFAULT_POOLS) - } -} - -impl Pools { - pub fn new(num_pools: usize) -> Self { - Pools(vec![Vec::new(); num_pools]) - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn get_mut(&mut self, rank: Rank) -> &mut Vec { - self.0 - .get_mut(rank.into_usize()) - .unwrap_or_else(|| panic!("Compiler bug: could not find pool at rank {}", rank)) - } - - pub fn get(&self, rank: Rank) -> &Vec { - self.0 - .get(rank.into_usize()) - .unwrap_or_else(|| panic!("Compiler bug: could not find pool at rank {}", rank)) - } - - pub fn iter(&self) -> std::slice::Iter<'_, Vec> { - self.0.iter() - } - - pub fn split_last(&self) -> (&Vec, &[Vec]) { - self.0 - .split_last() - .unwrap_or_else(|| panic!("Attempted to split_last() on non-empty Pools")) - } - - pub fn extend_to(&mut self, n: usize) { - for _ in self.len()..n { - self.0.push(Vec::new()); - } - } -} - -#[derive(Clone)] -struct State { - env: Env, - mark: Mark, -} - -pub fn run<'a>( - arena: &'a Bump, - mempool: &mut Pool, - env: &Env, - problems: &mut Vec, - mut subs: Subs, - constraint: &Constraint, -) -> (Solved, Env) { - let env = run_in_place(arena, mempool, env, problems, &mut subs, constraint); - - (Solved(subs), env) -} - -/// Modify an existing subs in-place instead -pub fn run_in_place<'a>( - arena: &'a Bump, - mempool: &mut Pool, - env: &Env, - problems: &mut Vec, - subs: &mut Subs, - constraint: &Constraint, -) -> Env { - let mut pools = Pools::default(); - let state = State { - env: env.clone(), - mark: Mark::NONE.next(), - }; - let rank = Rank::toplevel(); - let state = solve( - arena, - mempool, - env, - state, - rank, - &mut pools, - problems, - &mut MutMap::default(), - subs, - constraint, - ); - - state.env -} - -#[allow(clippy::too_many_arguments)] -fn solve<'a>( - arena: &'a Bump, - mempool: &mut Pool, - env: &Env, - state: State, - rank: Rank, - pools: &mut Pools, - problems: &mut Vec, - cached_aliases: &mut MutMap, - subs: &mut Subs, - constraint: &Constraint, -) -> State { - use crate::solve_type::Constraint::*; - - match constraint { - True => state, - // SaveTheEnvironment => { - // // NOTE deviation: elm only copies the env into the state on SaveTheEnvironment - // let mut copy = state; - // - // copy.env = env.clone(); - // - // copy - // } - Eq(typ, expectation, category, region) => { - let actual = type_to_var(arena, mempool, subs, rank, pools, cached_aliases, typ); - let expected = type_to_var( - arena, - mempool, - subs, - rank, - pools, - cached_aliases, - expectation.get_type_ref(), - ); - - match unify( - &mut with_checkmate!({ - on => UEnv::new(subs, None), - off => UEnv::new(subs), - }), - actual, - expected, - UnificationMode::EQ, - Polarity::OF_VALUE, - ) { - Success { - vars, - must_implement_ability: _, - lambda_sets_to_specialize: _, // TODO ignored - extra_metadata: _, - } => { - // TODO(abilities) record deferred ability checks - introduce(subs, rank, pools, &vars); - - state - } - Failure(vars, actual_type, expected_type, _bad_impl) => { - introduce(subs, rank, pools, &vars); - - let problem = TypeError::BadExpr( - *region, - category.clone(), - actual_type, - expectation.replace_ref(expected_type), - ); - - problems.push(problem); - - state - } - } - } - // Store(source, target, _filename, _linenr) => { - // // a special version of Eq that is used to store types in the AST. - // // IT DOES NOT REPORT ERRORS! - // let actual = type_to_var(subs, rank, pools, cached_aliases, source); - // let target = *target; - // - // match unify(subs, actual, target) { - // Success(vars) => { - // introduce(subs, rank, pools, &vars); - // - // state - // } - // Failure(vars, _actual_type, _expected_type, _bad_impl) => { - // introduce(subs, rank, pools, &vars); - // - // // ERROR NOT REPORTED - // - // state - // } - // BadType(vars, _problem) => { - // introduce(subs, rank, pools, &vars); - // - // // ERROR NOT REPORTED - // - // state - // } - // } - // } - Lookup(symbol, expectation, region) => { - match env.vars_by_symbol.get(&symbol) { - Some(var) => { - // Deep copy the vars associated with this symbol before unifying them. - // Otherwise, suppose we have this: - // - // identity = \a -> a - // - // x = identity 5 - // - // When we call (identity 5), it's important that we not unify - // on identity's original vars. If we do, the type of `identity` will be - // mutated to be `Int -> Int` instead of `a -> `, which would be incorrect; - // the type of `identity` is more general than that! - // - // Instead, we want to unify on a *copy* of its vars. If the copy unifies - // successfully (in this case, to `Int -> Int`), we can use that to - // infer the type of this lookup (in this case, `Int`) without ever - // having mutated the original. - // - // If this Lookup is targeting a value in another module, - // then we copy from that module's Subs into our own. If the value - // is being looked up in this module, then we use our Subs as both - // the source and destination. - let actual = deep_copy_var(subs, rank, pools, *var); - - let expected = type_to_var( - arena, - mempool, - subs, - rank, - pools, - cached_aliases, - expectation.get_type_ref(), - ); - - match unify( - &mut with_checkmate!({ - on => UEnv::new(subs, None), - off => UEnv::new(subs), - }), - actual, - expected, - UnificationMode::EQ, - Polarity::OF_VALUE, - ) { - Success { - vars, - must_implement_ability: _, - lambda_sets_to_specialize: _, // TODO ignored - extra_metadata: _, - } => { - // TODO(abilities) record deferred ability checks - introduce(subs, rank, pools, &vars); - - state - } - - Failure(vars, actual_type, expected_type, _bad_impl) => { - introduce(subs, rank, pools, &vars); - - let problem = TypeError::BadExpr( - *region, - Category::Lookup(*symbol), - actual_type, - expectation.shallow_clone().replace(expected_type), - ); - - problems.push(problem); - - state - } - } - } - None => { - problems.push(TypeError::UnexposedLookup(*symbol)); - - state - } - } - } - And(sub_constraints) => { - let mut state = state; - - for sub_constraint in sub_constraints.iter() { - state = solve( - arena, - mempool, - env, - state, - rank, - pools, - problems, - cached_aliases, - subs, - sub_constraint, - ); - } - - state - } - Pattern(region, category, typ, expectation) - | Present(typ, PresenceConstraint::Pattern(region, category, expectation)) => { - let actual = type_to_var(arena, mempool, subs, rank, pools, cached_aliases, typ); - let expected = type_to_var( - arena, - mempool, - subs, - rank, - pools, - cached_aliases, - expectation.get_type_ref(), - ); - - // TODO(ayazhafiz): presence constraints for Expr2/Type2 - match unify( - &mut with_checkmate!({ - on => UEnv::new(subs, None), - off => UEnv::new(subs), - }), - actual, - expected, - UnificationMode::EQ, - Polarity::OF_PATTERN, - ) { - Success { - vars, - must_implement_ability: _, - lambda_sets_to_specialize: _, // TODO ignored - extra_metadata: _, - } => { - // TODO(abilities) record deferred ability checks - introduce(subs, rank, pools, &vars); - - state - } - Failure(vars, actual_type, expected_type, _bad_impl) => { - introduce(subs, rank, pools, &vars); - - let problem = TypeError::BadPattern( - *region, - category.clone(), - actual_type, - expectation.shallow_clone().replace(expected_type), - ); - - problems.push(problem); - - state - } - } - } - Let(let_con) => { - match &let_con.ret_constraint { - True if let_con.rigid_vars.is_empty() => { - introduce(subs, rank, pools, &let_con.flex_vars); - - // If the return expression is guaranteed to solve, - // solve the assignments themselves and move on. - solve( - arena, - mempool, - &env, - state, - rank, - pools, - problems, - cached_aliases, - subs, - &let_con.defs_constraint, - ) - } - ret_con if let_con.rigid_vars.is_empty() && let_con.flex_vars.is_empty() => { - let state = solve( - arena, - mempool, - env, - state, - rank, - pools, - problems, - cached_aliases, - subs, - &let_con.defs_constraint, - ); - - // Add a variable for each def to new_vars_by_env. - let mut local_def_vars = BumpMap::new_in(arena); - - for (symbol, typ) in let_con.def_types.iter() { - let var = - type_to_var(arena, mempool, subs, rank, pools, cached_aliases, typ); - - // TODO: region should come from typ - local_def_vars.insert( - *symbol, - Loc { - value: var, - region: Region::zero(), - }, - ); - } - - let mut new_env = env.clone(); - for (symbol, loc_var) in local_def_vars.iter() { - if !new_env.vars_by_symbol.contains_key(&symbol) { - new_env.vars_by_symbol.insert(*symbol, loc_var.value); - } - } - - let new_state = solve( - arena, - mempool, - &new_env, - state, - rank, - pools, - problems, - cached_aliases, - subs, - ret_con, - ); - - for (symbol, loc_var) in local_def_vars { - check_for_infinite_type(subs, problems, symbol, loc_var); - } - - new_state - } - ret_con => { - let rigid_vars = &let_con.rigid_vars; - let flex_vars = &let_con.flex_vars; - - // work in the next pool to localize header - let next_rank = rank.next(); - - // introduce variables - for &var in rigid_vars.iter().chain(flex_vars.iter()) { - subs.set_rank(var, next_rank); - } - - // determine the next pool - let next_pools; - if next_rank.into_usize() < pools.len() { - next_pools = pools - } else { - // we should be off by one at this point - debug_assert_eq!(next_rank.into_usize(), 1 + pools.len()); - pools.extend_to(next_rank.into_usize()); - next_pools = pools; - } - - let pool: &mut Vec = next_pools.get_mut(next_rank); - - // Replace the contents of this pool with rigid_vars and flex_vars - pool.clear(); - pool.reserve(rigid_vars.len() + flex_vars.len()); - pool.extend(rigid_vars.iter()); - pool.extend(flex_vars.iter()); - - // run solver in next pool - - // Add a variable for each def to local_def_vars. - let mut local_def_vars = BumpMap::new_in(arena); - - for (symbol, typ) in let_con.def_types.iter() { - let var = type_to_var( - arena, - mempool, - subs, - next_rank, - next_pools, - cached_aliases, - typ, - ); - - // TODO: region should come from type - local_def_vars.insert( - *symbol, - Loc { - value: var, - region: Region::zero(), - }, - ); - } - - // Solve the assignments' constraints first. - let State { - env: saved_env, - mark, - } = solve( - arena, - mempool, - &env, - state, - next_rank, - next_pools, - problems, - cached_aliases, - subs, - &let_con.defs_constraint, - ); - - let young_mark = mark; - let visit_mark = young_mark.next(); - let final_mark = visit_mark.next(); - - debug_assert_eq!( - { - let offenders = next_pools - .get(next_rank) - .iter() - .filter(|var| { - let current_rank = - subs.get_rank(roc_types::subs::Variable::clone(var)); - - current_rank.into_usize() > next_rank.into_usize() - }) - .collect::>(); - - let result = offenders.len(); - - if result > 0 { - eprintln!( - "subs: {:?}\n\noffenders: {:?}\n\nlet_con.def_types: {:?}\n", - &subs, &offenders, &let_con.def_types - ); - } - - result - }, - 0 - ); - - // pop pool - generalize(subs, young_mark, visit_mark, next_rank, next_pools); - - next_pools.get_mut(next_rank).clear(); - - // check that things went well - debug_assert!({ - // NOTE the `subs.redundant` check is added for the uniqueness - // inference, and does not come from elm. It's unclear whether this is - // a bug with uniqueness inference (something is redundant that - // shouldn't be) or that it just never came up in elm. - let failing: Vec<_> = rigid_vars - .iter() - .filter(|&var| { - !subs.redundant(*var) && subs.get_rank(*var) != Rank::GENERALIZED - }) - .collect(); - - if !failing.is_empty() { - println!("Rigids {:?}", &rigid_vars); - println!("Failing {:?}", failing); - } - - failing.is_empty() - }); - - let mut new_env = env.clone(); - for (symbol, loc_var) in local_def_vars.iter() { - // when there are duplicates, keep the one from `env` - if !new_env.vars_by_symbol.contains_key(&symbol) { - new_env.vars_by_symbol.insert(*symbol, loc_var.value); - } - } - - // Note that this vars_by_symbol is the one returned by the - // previous call to solve() - let temp_state = State { - env: saved_env, - mark: final_mark, - }; - - // Now solve the body, using the new vars_by_symbol which includes - // the assignments' name-to-variable mappings. - let new_state = solve( - arena, - mempool, - &new_env, - temp_state, - rank, - next_pools, - problems, - cached_aliases, - subs, - &ret_con, - ); - - for (symbol, loc_var) in local_def_vars { - check_for_infinite_type(subs, problems, symbol, loc_var); - } - - new_state - } - } - } - Present(typ, PresenceConstraint::IsOpen) => { - let actual = type_to_var(arena, mempool, subs, rank, pools, cached_aliases, typ); - let mut new_desc = subs.get(actual); - match new_desc.content { - Content::Structure(FlatType::TagUnion(tags, _)) => { - let new_ext = TagExt::Any(subs.fresh_unnamed_flex_var()); - let new_union = Content::Structure(FlatType::TagUnion(tags, new_ext)); - new_desc.content = new_union; - subs.set(actual, new_desc); - state - } - _ => { - // Today, an "open" constraint doesn't affect any types - // other than tag unions. Recursive tag unions are constructed - // at a later time (during occurs checks after tag unions are - // resolved), so that's not handled here either. - // NB: Handle record types here if we add presence constraints - // to their type inference as well. - state - } - } - } - Present(typ, PresenceConstraint::IncludesTag(tag_name, tys)) => { - let actual = type_to_var(arena, mempool, subs, rank, pools, cached_aliases, typ); - let tag_ty = Type2::TagUnion( - PoolVec::new( - std::iter::once(( - tag_name.clone(), - PoolVec::new(tys.into_iter().map(ShallowClone::shallow_clone), mempool), - )), - mempool, - ), - mempool.add(Type2::EmptyTagUnion), - ); - let includes = type_to_var(arena, mempool, subs, rank, pools, cached_aliases, &tag_ty); - - match unify( - &mut with_checkmate!({ - on => UEnv::new(subs, None), - off => UEnv::new(subs), - }), - actual, - includes, - UnificationMode::PRESENT, - Polarity::OF_PATTERN, - ) { - Success { - vars, - must_implement_ability: _, - lambda_sets_to_specialize: _, // TODO ignored - extra_metadata: _, - } => { - // TODO(abilities) record deferred ability checks - introduce(subs, rank, pools, &vars); - - state - } - Failure(vars, actual_type, expected_type, _bad_impl) => { - introduce(subs, rank, pools, &vars); - - // TODO: do we need a better error type here? - let problem = TypeError::BadExpr( - Region::zero(), - Category::When, - actual_type, - Expected::NoExpectation(expected_type), - ); - - problems.push(problem); - - state - } - } - } - } -} - -fn type_to_var<'a>( - arena: &'a Bump, - mempool: &mut Pool, - subs: &mut Subs, - rank: Rank, - pools: &mut Pools, - cached: &mut MutMap, - typ: &Type2, -) -> Variable { - type_to_variable(arena, mempool, subs, rank, pools, cached, typ) -} - -/// Abusing existing functions for our purposes -/// this is to put a solved type back into subs -pub fn insert_type_into_subs<'a>( - arena: &'a Bump, - mempool: &mut Pool, - subs: &mut Subs, - typ: &Type2, -) -> Variable { - let rank = Rank::GENERALIZED; - let mut pools = Pools::default(); - let mut cached = MutMap::default(); - - type_to_variable(arena, mempool, subs, rank, &mut pools, &mut cached, typ) -} - -fn type_to_variable<'a>( - arena: &'a Bump, - mempool: &Pool, - subs: &mut Subs, - rank: Rank, - pools: &mut Pools, - cached: &mut MutMap, - typ: &Type2, -) -> Variable { - use Type2::*; - - match typ { - Variable(var) => *var, - Apply(symbol, args) => { - let mut arg_vars = Vec::with_capacity(args.len()); - for var_id in args.iter_node_ids() { - let arg = mempool.get(var_id); - arg_vars.push(type_to_variable( - arena, mempool, subs, rank, pools, cached, arg, - )) - } - - let arg_vars = VariableSubsSlice::insert_into_subs(subs, arg_vars); - let flat_type = FlatType::Apply(*symbol, arg_vars); - let content = Content::Structure(flat_type); - - register(subs, rank, pools, content) - } - - EmptyRec => roc_types::subs::Variable::EMPTY_RECORD, - EmptyTagUnion => roc_types::subs::Variable::EMPTY_TAG_UNION, - - Record(fields, ext_id) => { - let mut field_vars = Vec::new(); - - for node_id in fields.iter_node_ids() { - use RecordField::*; - - let (field, field_type) = mempool.get(node_id); - - let field_var = match field_type { - Required(type_id) => Required(type_to_variable( - arena, - mempool, - subs, - rank, - pools, - cached, - mempool.get(*type_id), - )), - RigidRequired(type_id) => RigidRequired(type_to_variable( - arena, - mempool, - subs, - rank, - pools, - cached, - mempool.get(*type_id), - )), - Optional(type_id) => Optional(type_to_variable( - arena, - mempool, - subs, - rank, - pools, - cached, - mempool.get(*type_id), - )), - RigidOptional(type_id) => RigidOptional(type_to_variable( - arena, - mempool, - subs, - rank, - pools, - cached, - mempool.get(*type_id), - )), - Demanded(type_id) => Demanded(type_to_variable( - arena, - mempool, - subs, - rank, - pools, - cached, - mempool.get(*type_id), - )), - }; - - field_vars.push((field.as_str(mempool).into(), field_var)); - } - - let ext = mempool.get(*ext_id); - let temp_ext_var = type_to_variable(arena, mempool, subs, rank, pools, cached, ext); - - let (it, new_ext_var) = - gather_fields_unsorted_iter(subs, RecordFields::empty(), temp_ext_var) - .expect("Something ended up weird in this record type"); - - let it = it - .into_iter() - .map(|(field, field_type)| (field.clone(), field_type)); - - field_vars.extend(it); - field_vars.sort_unstable_by(RecordFields::compare); - - let record_fields = RecordFields::insert_into_subs(subs, field_vars); - - let content = Content::Structure(FlatType::Record(record_fields, new_ext_var)); - - register(subs, rank, pools, content) - } - - Alias(symbol, args, alias_type_id) | Opaque(symbol, args, alias_type_id) => { - // TODO cache in uniqueness inference gives problems! all Int's get the same uniqueness var! - // Cache aliases without type arguments. Commonly used aliases like `Int` would otherwise get O(n) - // different variables (once for each occurrence). The recursion restriction is required - // for uniqueness types only: recursive aliases "introduce" an unbound uniqueness - // attribute in the body, when - // - // Peano : [S Peano, Z] - // - // becomes - // - // Peano : [S (Attr u Peano), Z] - // - // This `u` variable can be different between lists, so giving just one variable to - // this type is incorrect. - // TODO does caching work at all with uniqueness types? even Int then hides a uniqueness variable - - let alias_type = mempool.get(*alias_type_id); - let is_recursive = false; // alias_type.is_recursive(); - let no_args = args.is_empty(); - /* - if no_args && !is_recursive { - if let Some(var) = cached.get(symbol) { - return *var; - } - } - */ - - let mut arg_vars = Vec::with_capacity(args.len()); - - for arg_type_id in args.iter(mempool) { - let arg_type = mempool.get(*arg_type_id); - - let arg_var = type_to_variable(arena, mempool, subs, rank, pools, cached, arg_type); - - arg_vars.push(arg_var); - } - - let arg_vars = AliasVariables::insert_into_subs(subs, arg_vars, [], []); - - let alias_var = type_to_variable(arena, mempool, subs, rank, pools, cached, alias_type); - - let kind = match typ { - Alias(..) => AliasKind::Structural, - Opaque(..) => AliasKind::Opaque, - _ => internal_error!(), - }; - let content = Content::Alias(*symbol, arg_vars, alias_var, kind); - - let result = register(subs, rank, pools, content); - - if no_args && !is_recursive { - // cached.insert(*symbol, result); - } - - result - } - TagUnion(tags, ext_id) => { - let ext = mempool.get(*ext_id); - - let (union_tags, ext) = - type_to_union_tags(arena, mempool, subs, rank, pools, cached, tags, ext); - let content = Content::Structure(FlatType::TagUnion(union_tags, TagExt::Any(ext))); - - register(subs, rank, pools, content) - } - // This case is important for the rank of boolean variables - Function(arg_vars, closure_type_id, ret_type_id) => { - let closure_type = mempool.get(*closure_type_id); - let ret_type = mempool.get(*ret_type_id); - - let mut new_arg_vars = Vec::with_capacity(arg_vars.len()); - - for var_id in arg_vars.iter_node_ids() { - let arg = mempool.get(var_id); - let var = type_to_variable(arena, mempool, subs, rank, pools, cached, arg); - new_arg_vars.push(var) - } - - let arg_vars = VariableSubsSlice::insert_into_subs(subs, new_arg_vars); - - let ret_var = type_to_variable(arena, mempool, subs, rank, pools, cached, ret_type); - let closure_var = - type_to_variable(arena, mempool, subs, rank, pools, cached, closure_type); - - let content = Content::Structure(FlatType::Func(arg_vars, closure_var, ret_var)); - - register(subs, rank, pools, content) - } - other => todo!("not implemented {:?}", &other), - // RecursiveTagUnion(rec_var, tags, ext) => { - // let mut tag_vars = MutMap::default(); - // - // for (tag, tag_argument_types) in tags { - // let mut tag_argument_vars = Vec::with_capacity(tag_argument_types.len()); - // - // for arg_type in tag_argument_types { - // tag_argument_vars.push(type_to_variable(subs, rank, pools, cached, arg_type)); - // } - // - // tag_vars.insert(tag.clone(), tag_argument_vars); - // } - // - // let temp_ext_var = type_to_variable(subs, rank, pools, cached, ext); - // let mut ext_tag_vec = Vec::new(); - // let new_ext_var = match roc_types::pretty_print::chase_ext_tag_union( - // subs, - // temp_ext_var, - // &mut ext_tag_vec, - // ) { - // Ok(()) => Variable::EMPTY_TAG_UNION, - // Err((new, _)) => new, - // }; - // tag_vars.extend(ext_tag_vec.into_iter()); - // - // let content = - // Content::Structure(FlatType::RecursiveTagUnion(*rec_var, tag_vars, new_ext_var)); - // - // let tag_union_var = register(subs, rank, pools, content); - // - // subs.set_content( - // *rec_var, - // Content::RecursionVar { - // opt_name: None, - // structure: tag_union_var, - // }, - // ); - // - // tag_union_var - // } - // HostExposedAlias { - // name: symbol, - // arguments: args, - // actual: alias_type, - // actual_var, - // .. - // } => { - // let mut arg_vars = Vec::with_capacity(args.len()); - // let mut new_aliases = ImMap::default(); - // - // for (arg, arg_type) in args { - // let arg_var = type_to_variable(subs, rank, pools, cached, arg_type); - // - // arg_vars.push((arg.clone(), arg_var)); - // new_aliases.insert(arg.clone(), arg_var); - // } - // - // let alias_var = type_to_variable(subs, rank, pools, cached, alias_type); - // - // // unify the actual_var with the result var - // // this can be used to access the type of the actual_var - // // to determine its layout later - // // subs.set_content(*actual_var, descriptor.content); - // - // //subs.set(*actual_var, descriptor.clone()); - // let content = Content::Alias(*symbol, arg_vars, alias_var); - // - // let result = register(subs, rank, pools, content); - // - // // We only want to unify the actual_var with the alias once - // // if it's already redirected (and therefore, redundant) - // // don't do it again - // if !subs.redundant(*actual_var) { - // let descriptor = subs.get(result); - // subs.union(result, *actual_var, descriptor); - // } - // - // result - // } - // Erroneous(problem) => { - // let content = Content::Structure(FlatType::Erroneous(problem.clone())); - // - // register(subs, rank, pools, content) - // } - } -} - -fn type_to_union_tags<'a>( - arena: &'a Bump, - mempool: &Pool, - subs: &mut Subs, - rank: Rank, - pools: &mut Pools, - cached: &mut MutMap, - tags: &PoolVec<(TagName, PoolVec)>, - ext: &Type2, -) -> (UnionTags, Variable) { - let mut tag_vars = Vec::with_capacity(tags.len()); - - let mut tag_argument_vars = Vec::new(); - for id in tags.iter_node_ids() { - let (tag, tag_argument_types) = mempool.get(id); - for arg_id in tag_argument_types.iter_node_ids() { - let arg_type = mempool.get(arg_id); - let new_var = type_to_variable(arena, mempool, subs, rank, pools, cached, arg_type); - tag_argument_vars.push(new_var); - } - - let new_slice = VariableSubsSlice::insert_into_subs(subs, tag_argument_vars.drain(..)); - - tag_vars.push((tag.clone(), new_slice)); - } - - let temp_ext_var = type_to_variable(arena, mempool, subs, rank, pools, cached, ext); - - let ext = { - let (it, ext) = roc_types::types::gather_tags_unsorted_iter( - subs, - UnionTags::default(), - TagExt::Any(temp_ext_var), - ) - .expect("not a tag union"); - - tag_vars.extend(it.map(|(n, v)| (n.clone(), v))); - tag_vars.sort_unstable_by(|(a, _), (b, _)| a.cmp(b)); - - // deduplicate, keeping the right-most occurrence of a tag name - let mut i = 0; - - while i < tag_vars.len() { - match (tag_vars.get(i), tag_vars.get(i + 1)) { - (Some((t1, _)), Some((t2, _))) => { - if t1 == t2 { - tag_vars.remove(i); - } else { - i += 1; - } - } - _ => break, - } - } - - ext - }; - - ( - UnionTags::insert_slices_into_subs(subs, tag_vars), - ext.var(), - ) -} - -fn check_for_infinite_type( - subs: &mut Subs, - problems: &mut Vec, - symbol: Symbol, - loc_var: Loc, -) { - let var = loc_var.value; - - while let Err((recursive, _chain)) = subs.occurs(var) { - let description = subs.get(recursive); - let content = description.content; - - // try to make a tag union recursive, see if that helps - match content { - Content::Structure(FlatType::TagUnion(tags, ext_var)) => { - let rec_var = subs.fresh_unnamed_flex_var(); - subs.set_rank(rec_var, description.rank); - subs.set_content( - rec_var, - Content::RecursionVar { - opt_name: None, - structure: recursive, - }, - ); - - let mut new_tags = Vec::with_capacity(tags.len()); - - for (name_index, slice_index) in tags.iter_all() { - let slice = subs[slice_index]; - - let mut new_vars = Vec::new(); - for var_index in slice { - let var = subs[var_index]; - new_vars.push(subs.explicit_substitute(recursive, rec_var, var)); - } - - new_tags.push((subs[name_index].clone(), new_vars)); - } - - let new_ext_var = ext_var.map(|v| subs.explicit_substitute(recursive, rec_var, v)); - - let new_tags = UnionTags::insert_into_subs(subs, new_tags); - - let flat_type = FlatType::RecursiveTagUnion(rec_var, new_tags, new_ext_var); - - subs.set_content(recursive, Content::Structure(flat_type)); - } - - _other => circular_error(subs, problems, symbol, &loc_var), - } - } -} - -fn circular_error( - subs: &mut Subs, - problems: &mut Vec, - symbol: Symbol, - loc_var: &Loc, -) { - let var = loc_var.value; - let error_type = subs.var_to_error_type(var, Polarity::Pos); - let problem = TypeError::CircularType(loc_var.region, symbol, error_type); - - subs.set_content(var, Content::Error); - - problems.push(problem); -} - -fn generalize( - subs: &mut Subs, - young_mark: Mark, - visit_mark: Mark, - young_rank: Rank, - pools: &mut Pools, -) { - let young_vars = pools.get(young_rank); - let rank_table = pool_to_rank_table(subs, young_mark, young_rank, young_vars); - - // Get the ranks right for each entry. - // Start at low ranks so we only have to pass over the information once. - for (index, table) in rank_table.iter().enumerate() { - for &var in table.iter() { - adjust_rank(subs, young_mark, visit_mark, Rank::from(index), var); - } - } - - let (last_pool, all_but_last_pool) = rank_table.split_last(); - - // For variables that have rank lowerer than young_rank, register them in - // the appropriate old pool if they are not redundant. - for vars in all_but_last_pool { - for &var in vars { - if !subs.redundant(var) { - let rank = subs.get_rank(var); - - pools.get_mut(rank).push(var); - } - } - } - - // For variables with rank young_rank, if rank < young_rank: register in old pool, - // otherwise generalize - for &var in last_pool { - if !subs.redundant(var) { - let desc_rank = subs.get_rank(var); - - if desc_rank < young_rank { - pools.get_mut(desc_rank).push(var); - } else { - subs.set_rank(var, Rank::GENERALIZED); - } - } - } -} - -fn pool_to_rank_table( - subs: &mut Subs, - young_mark: Mark, - young_rank: Rank, - young_vars: &[Variable], -) -> Pools { - let mut pools = Pools::new(young_rank.into_usize() + 1); - - // Sort the variables into buckets by rank. - for &var in young_vars.iter() { - let rank = subs.get_rank(var); - subs.set_mark(var, young_mark); - - debug_assert!(rank.into_usize() < young_rank.into_usize() + 1); - pools.get_mut(rank).push(var); - } - - pools -} - -/// Adjust variable ranks such that ranks never increase as you move deeper. -/// This way the outermost rank is representative of the entire structure. -fn adjust_rank( - subs: &mut Subs, - young_mark: Mark, - visit_mark: Mark, - group_rank: Rank, - var: Variable, -) -> Rank { - let desc = subs.get(var); - - if desc.mark == young_mark { - let Descriptor { - content, - rank: _, - mark: _, - copy, - } = desc; - - // Mark the variable as visited before adjusting content, as it may be cyclic. - subs.set_mark(var, visit_mark); - - let max_rank = adjust_rank_content(subs, young_mark, visit_mark, group_rank, &content); - - subs.set( - var, - Descriptor { - content, - rank: max_rank, - mark: visit_mark, - copy, - }, - ); - - max_rank - } else if desc.mark == visit_mark { - // nothing changes - desc.rank - } else { - let mut desc = desc; - - let min_rank = group_rank.min(desc.rank); - - // TODO from elm-compiler: how can min_rank ever be group_rank? - desc.rank = min_rank; - desc.mark = visit_mark; - - subs.set(var, desc); - - min_rank - } -} - -fn adjust_rank_content( - subs: &mut Subs, - young_mark: Mark, - visit_mark: Mark, - group_rank: Rank, - content: &Content, -) -> Rank { - use roc_types::subs::Content::*; - use roc_types::subs::FlatType::*; - - match content { - FlexVar(_) | RigidVar(_) | FlexAbleVar(..) | RigidAbleVar(..) | Error => group_rank, - - RecursionVar { .. } => group_rank, - - Structure(flat_type) => { - match flat_type { - Apply(_, args) => { - let mut rank = Rank::toplevel(); - - for var_index in args.into_iter() { - let var = subs[var_index]; - rank = rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, var)); - } - - rank - } - - Func(arg_vars, closure_var, ret_var) => { - let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, *ret_var); - - // TODO investigate further. - // - // My theory is that because the closure_var contains variables already - // contained in the signature only, it does not need to be part of the rank - // calculuation - if true { - rank = rank.max(adjust_rank( - subs, - young_mark, - visit_mark, - group_rank, - *closure_var, - )); - } - - for index in arg_vars.into_iter() { - let var = subs[index]; - rank = rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, var)); - } - - rank - } - - EmptyRecord | EmptyTuple => { - // from elm-compiler: THEORY: an empty record never needs to get generalized - Rank::toplevel() - } - - EmptyTagUnion => Rank::toplevel(), - - Tuple(..) => { - todo!() - } - Record(fields, ext_var) => { - let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, *ext_var); - - for index in fields.iter_variables() { - let var = subs[index]; - rank = rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, var)); - } - - rank - } - - TagUnion(tags, ext_var) => { - let mut rank = - adjust_rank(subs, young_mark, visit_mark, group_rank, ext_var.var()); - - for (_, index) in tags.iter_all() { - let slice = subs[index]; - for var_index in slice { - let var = subs[var_index]; - rank = rank - .max(adjust_rank(subs, young_mark, visit_mark, group_rank, var)); - } - } - - rank - } - - FunctionOrTagUnion(_, _, ext_var) => { - adjust_rank(subs, young_mark, visit_mark, group_rank, ext_var.var()) - } - - RecursiveTagUnion(rec_var, tags, ext_var) => { - let mut rank = - adjust_rank(subs, young_mark, visit_mark, group_rank, ext_var.var()); - - for (_, index) in tags.iter_all() { - let slice = subs[index]; - for var_index in slice { - let var = subs[var_index]; - rank = rank - .max(adjust_rank(subs, young_mark, visit_mark, group_rank, var)); - } - } - - // THEORY: the recursion var has the same rank as the tag union itself - // all types it uses are also in the tags already, so it cannot influence the - // rank - debug_assert!( - rank >= adjust_rank(subs, young_mark, visit_mark, group_rank, *rec_var) - ); - - rank - } - } - } - - LambdaSet(subs::LambdaSet { - solved, - recursion_var, - // TODO: handle unspecialized - unspecialized: _, - ambient_function: _, - }) => { - let mut rank = group_rank; - - for (_, index) in solved.iter_all() { - let slice = subs[index]; - for var_index in slice { - let var = subs[var_index]; - rank = rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, var)); - } - } - - if let Some(rec_var) = recursion_var.into_variable() { - // THEORY: the recursion var has the same rank as the tag union itself - // all types it uses are also in the tags already, so it cannot influence the - // rank - debug_assert!( - rank >= adjust_rank(subs, young_mark, visit_mark, group_rank, rec_var) - ); - } - - rank - } - - Alias(_, args, real_var, _) => { - let mut rank = Rank::toplevel(); - - for var_index in args.all_variables() { - let var = subs[var_index]; - rank = rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, var)); - } - - // from elm-compiler: THEORY: anything in the real_var would be Rank::toplevel() - // this theory is not true in Roc! aliases of function types capture the closure var - rank = rank.max(adjust_rank( - subs, young_mark, visit_mark, group_rank, *real_var, - )); - - rank - } - - ErasedLambda => group_rank, - - RangedNumber(_vars) => group_rank, - } -} - -/// Introduce some variables to Pools at the given rank. -/// Also, set each of their ranks in Subs to be the given rank. -fn introduce(subs: &mut Subs, rank: Rank, pools: &mut Pools, vars: &[Variable]) { - let pool: &mut Vec = pools.get_mut(rank); - - for &var in vars.iter() { - subs.set_rank(var, rank); - } - - pool.extend(vars); -} - -/// Function that converts rigids variables to flex variables -/// this is used during the monomorphization process -pub fn instantiate_rigids(subs: &mut Subs, var: Variable) { - let rank = Rank::GENERALIZED; - let mut pools = Pools::default(); - - instantiate_rigids_help(subs, rank, &mut pools, var); -} - -fn instantiate_rigids_help( - subs: &mut Subs, - max_rank: Rank, - pools: &mut Pools, - var: Variable, -) -> Variable { - use roc_types::subs::Content::*; - use roc_types::subs::FlatType::*; - - let desc = subs.get_without_compacting(var); - - if let Some(copy) = desc.copy.into_variable() { - return copy; - } - - let make_descriptor = |content| Descriptor { - content, - rank: max_rank, - mark: Mark::NONE, - copy: OptVariable::NONE, - }; - - let content = desc.content; - let copy = var; - - pools.get_mut(max_rank).push(copy); - - // Link the original variable to the new variable. This lets us - // avoid making multiple copies of the variable we are instantiating. - // - // Need to do this before recursively copying to avoid looping. - subs.set( - var, - Descriptor { - content: content.clone(), - rank: desc.rank, - mark: Mark::NONE, - copy: copy.into(), - }, - ); - - // Now we recursively copy the content of the variable. - // We have already marked the variable as copied, so we - // will not repeat this work or crawl this variable again. - match content { - Structure(flat_type) => { - match flat_type { - Apply(_, args) => { - for var_index in args.into_iter() { - let var = subs[var_index]; - instantiate_rigids_help(subs, max_rank, pools, var); - } - } - - Func(arg_vars, closure_var, ret_var) => { - instantiate_rigids_help(subs, max_rank, pools, ret_var); - instantiate_rigids_help(subs, max_rank, pools, closure_var); - - for index in arg_vars.into_iter() { - let var = subs[index]; - instantiate_rigids_help(subs, max_rank, pools, var); - } - } - - EmptyRecord | EmptyTuple | EmptyTagUnion => {} - - Tuple(..) => { - todo!() - } - Record(fields, ext_var) => { - for index in fields.iter_variables() { - let var = subs[index]; - instantiate_rigids_help(subs, max_rank, pools, var); - } - - instantiate_rigids_help(subs, max_rank, pools, ext_var); - } - - TagUnion(tags, ext_var) => { - for (_, index) in tags.iter_all() { - let slice = subs[index]; - for var_index in slice { - let var = subs[var_index]; - instantiate_rigids_help(subs, max_rank, pools, var); - } - } - - instantiate_rigids_help(subs, max_rank, pools, ext_var.var()); - } - - FunctionOrTagUnion(_tag_name, _symbol, ext_var) => { - instantiate_rigids_help(subs, max_rank, pools, ext_var.var()); - } - - RecursiveTagUnion(rec_var, tags, ext_var) => { - instantiate_rigids_help(subs, max_rank, pools, rec_var); - - for (_, index) in tags.iter_all() { - let slice = subs[index]; - for var_index in slice { - let var = subs[var_index]; - instantiate_rigids_help(subs, max_rank, pools, var); - } - } - - instantiate_rigids_help(subs, max_rank, pools, ext_var.var()); - } - }; - } - - FlexVar(_) | FlexAbleVar(_, _) | Error => {} - - RecursionVar { structure, .. } => { - instantiate_rigids_help(subs, max_rank, pools, structure); - } - - RigidVar(name) => { - // what it's all about: convert the rigid var into a flex var - subs.set(copy, make_descriptor(FlexVar(Some(name)))); - } - - RigidAbleVar(name, ability) => { - // what it's all about: convert the rigid var into a flex var - subs.set(copy, make_descriptor(FlexAbleVar(Some(name), ability))); - } - - Alias(_, args, real_type_var, _) => { - for var_index in args.all_variables() { - let var = subs[var_index]; - instantiate_rigids_help(subs, max_rank, pools, var); - } - - instantiate_rigids_help(subs, max_rank, pools, real_type_var); - } - - LambdaSet(subs::LambdaSet { - solved, - recursion_var, - // TODO: handle unspecialized - unspecialized: _, - ambient_function: _, - }) => { - if let Some(rec_var) = recursion_var.into_variable() { - instantiate_rigids_help(subs, max_rank, pools, rec_var); - } - - for (_, index) in solved.iter_all() { - let slice = subs[index]; - for var_index in slice { - let var = subs[var_index]; - instantiate_rigids_help(subs, max_rank, pools, var); - } - } - } - - ErasedLambda => {} - RangedNumber(_vars) => {} - } - - var -} - -fn deep_copy_var(subs: &mut Subs, rank: Rank, pools: &mut Pools, var: Variable) -> Variable { - let copy = deep_copy_var_help(subs, rank, pools, var); - - copy -} - -fn deep_copy_var_help( - subs: &mut Subs, - max_rank: Rank, - pools: &mut Pools, - var: Variable, -) -> Variable { - use roc_types::subs::Content::*; - use roc_types::subs::FlatType::*; - - let desc = subs.get_without_compacting(var); - - if let Some(copy) = desc.copy.into_variable() { - return copy; - } else if desc.rank != Rank::GENERALIZED { - return var; - } - - let make_descriptor = |content| Descriptor { - content, - rank: max_rank, - mark: Mark::NONE, - copy: OptVariable::NONE, - }; - - let content = desc.content; - let copy = subs.fresh(make_descriptor(content.clone())); - - pools.get_mut(max_rank).push(copy); - - // Link the original variable to the new variable. This lets us - // avoid making multiple copies of the variable we are instantiating. - // - // Need to do this before recursively copying to avoid looping. - subs.set( - var, - Descriptor { - content: content.clone(), - rank: desc.rank, - mark: Mark::NONE, - copy: copy.into(), - }, - ); - - // Now we recursively copy the content of the variable. - // We have already marked the variable as copied, so we - // will not repeat this work or crawl this variable again. - match content { - Structure(flat_type) => { - let new_flat_type = match flat_type { - Apply(symbol, args) => { - let mut new_arg_vars = Vec::with_capacity(args.len()); - - for index in args.into_iter() { - let var = subs[index]; - let copy_var = deep_copy_var_help(subs, max_rank, pools, var); - new_arg_vars.push(copy_var); - } - - let arg_vars = VariableSubsSlice::insert_into_subs(subs, new_arg_vars); - - Apply(symbol, arg_vars) - } - - Func(arg_vars, closure_var, ret_var) => { - let new_ret_var = deep_copy_var_help(subs, max_rank, pools, ret_var); - let new_closure_var = deep_copy_var_help(subs, max_rank, pools, closure_var); - - let mut new_arg_vars = Vec::with_capacity(arg_vars.len()); - - for index in arg_vars.into_iter() { - let var = subs[index]; - let copy_var = deep_copy_var_help(subs, max_rank, pools, var); - new_arg_vars.push(copy_var); - } - - let arg_vars = VariableSubsSlice::insert_into_subs(subs, new_arg_vars); - - Func(arg_vars, new_closure_var, new_ret_var) - } - - same @ EmptyRecord | same @ EmptyTuple | same @ EmptyTagUnion => same, - - Tuple(..) => { - todo!() - } - Record(fields, ext_var) => { - let record_fields = { - let mut new_vars = Vec::with_capacity(fields.len()); - - for index in fields.iter_variables() { - let var = subs[index]; - let copy_var = deep_copy_var_help(subs, max_rank, pools, var); - - new_vars.push(copy_var); - } - - let field_names_start = subs.field_names.len() as u32; - let variables_start = subs.variables.len() as u32; - let field_types_start = subs.record_fields.len() as u32; - - let mut length = 0; - - for ((i1, _, i3), var) in fields.iter_all().zip(new_vars) { - let record_field = subs[i3].map(|_| var); - - subs.field_names.push(subs[i1].clone()); - subs.record_fields.push(record_field.map(|_| ())); - subs.variables.push(*record_field.as_inner()); - - length += 1; - } - - RecordFields { - length, - field_names_start, - variables_start, - field_types_start, - } - }; - - Record( - record_fields, - deep_copy_var_help(subs, max_rank, pools, ext_var), - ) - } - - TagUnion(tags, ext_var) => { - let mut new_variable_slices = Vec::with_capacity(tags.len()); - - let mut new_variables = Vec::new(); - for index in tags.variables() { - let slice = subs[index]; - for var_index in slice { - let var = subs[var_index]; - let new_var = deep_copy_var_help(subs, max_rank, pools, var); - new_variables.push(new_var); - } - - let new_slice = - VariableSubsSlice::insert_into_subs(subs, new_variables.drain(..)); - - new_variable_slices.push(new_slice); - } - - let new_variables = { - let start = subs.variable_slices.len() as u32; - let length = new_variable_slices.len() as u16; - subs.variable_slices.extend(new_variable_slices); - - SubsSlice::new(start, length) - }; - - let union_tags = UnionTags::from_slices(tags.labels(), new_variables); - - let new_ext = ext_var.map(|v| deep_copy_var_help(subs, max_rank, pools, v)); - TagUnion(union_tags, new_ext) - } - - FunctionOrTagUnion(tag_name, symbol, ext_var) => FunctionOrTagUnion( - tag_name, - symbol, - ext_var.map(|v| deep_copy_var_help(subs, max_rank, pools, v)), - ), - - RecursiveTagUnion(rec_var, tags, ext_var) => { - let mut new_variable_slices = Vec::with_capacity(tags.len()); - - let mut new_variables = Vec::new(); - for index in tags.variables() { - let slice = subs[index]; - for var_index in slice { - let var = subs[var_index]; - let new_var = deep_copy_var_help(subs, max_rank, pools, var); - new_variables.push(new_var); - } - - let new_slice = - VariableSubsSlice::insert_into_subs(subs, new_variables.drain(..)); - - new_variable_slices.push(new_slice); - } - - let new_variables = { - let start = subs.variable_slices.len() as u32; - let length = new_variable_slices.len() as u16; - subs.variable_slices.extend(new_variable_slices); - - SubsSlice::new(start, length) - }; - - let union_tags = UnionTags::from_slices(tags.labels(), new_variables); - - let new_ext = ext_var.map(|v| deep_copy_var_help(subs, max_rank, pools, v)); - let new_rec_var = deep_copy_var_help(subs, max_rank, pools, rec_var); - FlatType::RecursiveTagUnion(new_rec_var, union_tags, new_ext) - } - }; - - subs.set(copy, make_descriptor(Structure(new_flat_type))); - - copy - } - - FlexVar(_) | FlexAbleVar(_, _) | Error => copy, - - RecursionVar { - opt_name, - structure, - } => { - let new_structure = deep_copy_var_help(subs, max_rank, pools, structure); - - subs.set( - copy, - make_descriptor(RecursionVar { - opt_name, - structure: new_structure, - }), - ); - - copy - } - - RigidVar(name) => { - subs.set(copy, make_descriptor(FlexVar(Some(name)))); - - copy - } - - RigidAbleVar(name, ability) => { - subs.set(copy, make_descriptor(FlexAbleVar(Some(name), ability))); - - copy - } - - Alias(symbol, mut args, real_type_var, kind) => { - let mut new_args = Vec::with_capacity(args.all_variables().len()); - - for var_index in args.all_variables() { - let var = subs[var_index]; - let new_var = deep_copy_var_help(subs, max_rank, pools, var); - new_args.push(new_var); - } - - args.replace_variables(subs, new_args); - - let new_real_type_var = deep_copy_var_help(subs, max_rank, pools, real_type_var); - let new_content = Alias(symbol, args, new_real_type_var, kind); - - subs.set(copy, make_descriptor(new_content)); - - copy - } - - LambdaSet(subs::LambdaSet { - solved, - recursion_var, - unspecialized, - ambient_function, - }) => { - let mut new_variable_slices = Vec::with_capacity(solved.len()); - - let mut new_variables = Vec::new(); - for index in solved.variables() { - let slice = subs[index]; - for var_index in slice { - let var = subs[var_index]; - let new_var = deep_copy_var_help(subs, max_rank, pools, var); - new_variables.push(new_var); - } - - let new_slice = VariableSubsSlice::insert_into_subs(subs, new_variables.drain(..)); - - new_variable_slices.push(new_slice); - } - - let new_variables = { - let start = subs.variable_slices.len() as u32; - let length = new_variable_slices.len() as u16; - subs.variable_slices.extend(new_variable_slices); - - SubsSlice::new(start, length) - }; - - let new_solved = UnionLambdas::from_slices(solved.labels(), new_variables); - let new_rec_var = - recursion_var.map(|rec_var| deep_copy_var_help(subs, max_rank, pools, rec_var)); - - let new_content = LambdaSet(subs::LambdaSet { - solved: new_solved, - recursion_var: new_rec_var, - // TODO: actually copy - unspecialized, - ambient_function, - }); - - subs.set(copy, make_descriptor(new_content)); - - copy - } - - ErasedLambda => { - subs.set(copy, make_descriptor(ErasedLambda)); - - copy - } - - RangedNumber(vars) => { - let new_content = RangedNumber(vars); - - subs.set(copy, make_descriptor(new_content)); - - copy - } - } -} - -fn register(subs: &mut Subs, rank: Rank, pools: &mut Pools, content: Content) -> Variable { - let var = subs.fresh(Descriptor { - content, - rank, - mark: Mark::NONE, - copy: OptVariable::NONE, - }); - - pools.get_mut(rank).push(var); - - var -} diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 771b3e1ebd..6424b0e247 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -21,8 +21,6 @@ default = ["target-aarch64", "target-x86_64", "target-wasm32"] i386-cli-run = ["target-x86"] wasm32-cli-run = ["target-wasm32", "run-wasm32"] -editor = ["roc_editor"] - run-wasm32 = ["roc_wasm_interp"] # Compiling for a different target than the current machine can cause linker errors. @@ -43,7 +41,6 @@ roc_builtins = { path = "../compiler/builtins" } roc_can = { path = "../compiler/can" } roc_collections = { path = "../compiler/collections" } roc_docs = { path = "../docs" } -roc_editor = { path = "../editor", optional = true } roc_error_macros = { path = "../error_macros" } roc_fmt = { path = "../compiler/fmt" } roc_gen_llvm = { path = "../compiler/gen_llvm" } diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index 6a7cbc5484..20935c0f8b 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -41,7 +41,6 @@ pub const CMD_BUILD: &str = "build"; pub const CMD_RUN: &str = "run"; pub const CMD_DEV: &str = "dev"; pub const CMD_REPL: &str = "repl"; -pub const CMD_EDIT: &str = "edit"; pub const CMD_DOCS: &str = "docs"; pub const CMD_CHECK: &str = "check"; pub const CMD_VERSION: &str = "version"; @@ -142,7 +141,8 @@ pub fn build_app() -> Command { let build_target_values_parser = PossibleValuesParser::new(Target::iter().map(Into::<&'static str>::into)); - let app = Command::new("roc") + + Command::new("roc") .version(concatcp!(VERSION, "\n")) .about("Run the given .roc file, if there are no compilation errors.\nYou can use one of the SUBCOMMANDS below to do something else!") .args_conflicts_with_subcommands(true) @@ -332,23 +332,7 @@ pub fn build_app() -> Command { .arg(flag_linker) .arg(flag_prebuilt) .arg(roc_file_to_run) - .arg(args_for_app.trailing_var_arg(true)); - - if cfg!(feature = "editor") { - app.subcommand( - Command::new(CMD_EDIT) - .about("Launch the Roc editor (Work In Progress)") - .arg( - Arg::new(DIRECTORY_OR_FILES) - .num_args(0..) - .required(false) - .value_parser(value_parser!(OsString)) - .help("(optional) The directory or files to open on launch"), - ), - ) - } else { - app - } + .arg(args_for_app.trailing_var_arg(true)) } #[derive(Debug, PartialEq, Eq)] diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 705be3ef98..e0ced7b2c0 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -3,7 +3,7 @@ use roc_build::link::LinkType; use roc_build::program::{check_file, CodeGenBackend}; use roc_cli::{ build_app, format, test, BuildConfig, FormatMode, CMD_BUILD, CMD_CHECK, CMD_DEV, CMD_DOCS, - CMD_EDIT, CMD_FORMAT, CMD_GEN_STUB_LIB, CMD_GLUE, CMD_REPL, CMD_RUN, CMD_TEST, CMD_VERSION, + CMD_FORMAT, CMD_GEN_STUB_LIB, CMD_GLUE, CMD_REPL, CMD_RUN, CMD_TEST, CMD_VERSION, DIRECTORY_OR_FILES, FLAG_CHECK, FLAG_DEV, FLAG_LIB, FLAG_NO_LINK, FLAG_TARGET, FLAG_TIME, GLUE_DIR, GLUE_SPEC, ROC_FILE, }; @@ -52,9 +52,7 @@ fn main() -> io::Result<()> { LinkType::Executable, ) } else { - launch_editor(None)?; - - Ok(0) + Ok(1) } } Some((CMD_RUN, matches)) => { @@ -213,22 +211,6 @@ fn main() -> io::Result<()> { } } Some((CMD_REPL, _)) => Ok(roc_repl_cli::main()), - Some((CMD_EDIT, matches)) => { - match matches - .get_many::(DIRECTORY_OR_FILES) - .map(|mut values| values.next()) - { - Some(Some(os_string)) => { - launch_editor(Some(Path::new(os_string)))?; - } - _ => { - launch_editor(None)?; - } - } - - // Exit 0 if the editor exited normally - Ok(0) - } Some((CMD_DOCS, matches)) => { let root_path = matches.get_one::(ROC_FILE).unwrap(); @@ -333,13 +315,3 @@ fn roc_files_recursive>( Ok(()) } - -#[cfg(feature = "editor")] -fn launch_editor(project_dir_path: Option<&Path>) -> io::Result<()> { - roc_editor::launch(project_dir_path) -} - -#[cfg(not(feature = "editor"))] -fn launch_editor(_project_dir_path: Option<&Path>) -> io::Result<()> { - panic!("Cannot launch the editor because this build of roc did not include `feature = \"editor\"`!"); -} diff --git a/crates/cli/tests/editor.rs b/crates/cli/tests/editor.rs deleted file mode 100644 index 8680e5de6a..0000000000 --- a/crates/cli/tests/editor.rs +++ /dev/null @@ -1,70 +0,0 @@ -#[cfg(test)] -mod editor_launch_test { - - use core::time; - use std::{ - env, - process::{Command, Stdio}, - thread, - }; - - use cli_utils::helpers::build_roc_bin; - use roc_cli::CMD_EDIT; - use roc_command_utils::root_dir; - use std::io::Read; - - #[ignore = "We don't want to bring up the editor window during regular tests, only on specific CI machines."] - #[test] - fn launch_test() { - launch(None); - - // with a file arg - launch(Some("roc-projects/new-roc-project-1/main.roc")); - - // with a folder arg - launch(Some("roc-projects/new-roc-project-1")); - } - - fn launch(arg_path_str_opt: Option<&str>) { - let root_dir = root_dir(); - - // The editor expects to be run from the root of the repo, so it can find the cli-platform to init a new project folder. - env::set_current_dir(&root_dir) - .unwrap_or_else(|_| panic!("Failed to set current dir to {root_dir:?}")); - - let roc_binary_path = build_roc_bin(&["--features", "editor"]); - - let mut cmd_args = vec![CMD_EDIT]; - - if let Some(arg_path_str) = arg_path_str_opt { - cmd_args.push(arg_path_str) - } - - let mut roc_process = Command::new(roc_binary_path) - .args(cmd_args) - .stdout(Stdio::piped()) - .spawn() - .expect("Failed to start editor from cli."); - - // wait for editor to show - thread::sleep(time::Duration::from_millis(2000)); - - // We extract 12 bytes from the logs for verification - let mut stdout_buffer = [0; 12]; - let mut stdout = roc_process.stdout.take().unwrap(); - stdout.read_exact(&mut stdout_buffer).unwrap(); - - match roc_process.try_wait() { - Ok(Some(status)) => panic!( - "The editor exited with status \"{status}\" but I expected it to still be running." - ), - Ok(None) => { - // The editor is still running as desired, we check if logs are as expected: - assert_eq!("Loading file", std::str::from_utf8(&stdout_buffer).unwrap()); - // Kill the editor, we don't want it to stay open forever. - roc_process.kill().unwrap(); - } - Err(e) => panic!("Failed to wait launch editor cli command: {e}"), - } - } -} diff --git a/crates/code_markup/Cargo.toml b/crates/code_markup/Cargo.toml deleted file mode 100644 index 4a784234b1..0000000000 --- a/crates/code_markup/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "roc_code_markup" -description = "Our own markup language for Roc code. Used by the editor and the docs." - -authors.workspace = true -edition.workspace = true -license.workspace = true -version.workspace = true - -[dependencies] -roc_ast = { path = "../ast" } -roc_module = { path = "../compiler/module" } -roc_error_utils = { path = "../utils/error" } - -palette.workspace = true - -bumpalo.workspace = true -serde.workspace = true -snafu.workspace = true diff --git a/crates/code_markup/src/colors.rs b/crates/code_markup/src/colors.rs deleted file mode 100644 index bf64fd7b95..0000000000 --- a/crates/code_markup/src/colors.rs +++ /dev/null @@ -1,22 +0,0 @@ -use palette::{FromColor, Hsv, LinSrgb, Srgb}; - -pub type RgbaTup = (f32, f32, f32, f32); -pub const WHITE: RgbaTup = (1.0, 1.0, 1.0, 1.0); - -pub fn to_slice((r, g, b, a): RgbaTup) -> [f32; 4] { - [r, g, b, a] -} - -pub fn from_hsb(hue: usize, saturation: usize, brightness: usize) -> RgbaTup { - from_hsba(hue, saturation, brightness, 1.0) -} - -pub fn from_hsba(hue: usize, saturation: usize, brightness: usize, alpha: f32) -> RgbaTup { - let rgb = LinSrgb::from(Srgb::from_color(Hsv::new( - hue as f32, - (saturation as f32) / 100.0, - (brightness as f32) / 100.0, - ))); - - (rgb.red, rgb.green, rgb.blue, alpha) -} diff --git a/crates/code_markup/src/lib.rs b/crates/code_markup/src/lib.rs deleted file mode 100644 index 56891ef552..0000000000 --- a/crates/code_markup/src/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -//! A [markup language](https://en.wikipedia.org/wiki/Markup_language) to display Roc code in the editor. -pub mod colors; -pub mod markup; -pub mod markup_error; -pub mod slow_pool; -pub mod syntax_highlight; -pub mod underline_style; diff --git a/crates/code_markup/src/markup/attribute.rs b/crates/code_markup/src/markup/attribute.rs deleted file mode 100644 index f0fd611997..0000000000 --- a/crates/code_markup/src/markup/attribute.rs +++ /dev/null @@ -1,124 +0,0 @@ -#![allow(dead_code)] -use snafu::ensure; - -use crate::markup_error::{CaretNotFoundSnafu, MarkResult}; - -#[derive(Debug, Copy, Clone)] -pub struct Caret { - pub offset_col: usize, -} - -impl Caret { - pub fn new_attr(offset_col: usize) -> Attribute { - Attribute::Caret { - caret: Caret { offset_col }, - } - } -} -#[derive(Debug)] -pub struct SelectionStart { - offset_col: usize, -} -#[derive(Debug)] -pub struct SelectionEnd { - offset_col: usize, -} - -// Highlight is used for example when searching for a specific string to highlight all search results in the module -#[derive(Debug)] -pub struct HighlightStart { - offset_col: usize, -} -#[derive(Debug)] -pub struct HighlightEnd { - offset_col: usize, -} - -// Underline is used for warnings and errors -#[derive(Debug)] -pub struct UnderlineStart { - offset_col: usize, -} -#[derive(Debug)] -pub struct UnderlineEnd { - offset_col: usize, -} - -#[derive(Debug)] -pub enum Attribute { - // Rust does not yet support types for enum variants so we have to do it like this - Caret { caret: Caret }, - - SelectionStart { selection_start: SelectionStart }, - SelectionEnd { selection_end: SelectionEnd }, - - HighlightStart { highlight_start: HighlightStart }, - HighlightEnd { highlight_end: HighlightEnd }, - - Underline { underline_spec: UnderlineSpec }, -} - -#[derive(Debug)] -pub enum UnderlineSpec { - Partial { start: usize, end: usize }, - Full, -} - -#[derive(Debug, Default)] -pub struct Attributes { - pub all: Vec, -} - -impl Attributes { - pub fn add(&mut self, attr: Attribute) { - self.all.push(attr); - } - - pub fn add_caret(&mut self, offset_col: usize) { - self.all.push(Attribute::Caret { - caret: Caret { offset_col }, - }); - } - - pub fn get_mut_carets(&mut self) -> Vec<&mut Caret> { - let mut carets = Vec::new(); - - for attr in self.all.iter_mut() { - if let Attribute::Caret { caret } = attr { - carets.push(caret) - } - } - - carets - } - - pub fn get_carets(&self) -> Vec { - let mut carets = Vec::new(); - - for attr in self.all.iter() { - if let Attribute::Caret { caret } = attr { - carets.push(*caret) - } - } - - carets - } - - pub fn delete_caret(&mut self, offset_col: usize, node_id: usize) -> MarkResult<()> { - let old_len = self.all.len(); - - self.all.retain(|attr| { - if let Attribute::Caret { caret } = attr { - caret.offset_col != offset_col - } else { - true - } - }); - - let new_len = self.all.len(); - - ensure!(old_len != new_len, CaretNotFoundSnafu { node_id }); - - Ok(()) - } -} diff --git a/crates/code_markup/src/markup/common_nodes.rs b/crates/code_markup/src/markup/common_nodes.rs deleted file mode 100644 index ac2add6693..0000000000 --- a/crates/code_markup/src/markup/common_nodes.rs +++ /dev/null @@ -1,167 +0,0 @@ -use crate::{ - slow_pool::{MarkNodeId, SlowPool}, - syntax_highlight::HighlightStyle, -}; - -use super::{ - attribute::Attributes, - nodes::MarkupNode, - nodes::{self, make_nested_mn}, -}; - -pub fn new_equals_mn() -> MarkupNode { - common_text_node(nodes::EQUALS.to_owned(), HighlightStyle::Operator, 0) -} - -pub fn new_comma_mn() -> MarkupNode { - common_text_node(nodes::COMMA.to_owned(), HighlightStyle::Operator, 0) -} - -pub fn new_dot_mn() -> MarkupNode { - common_text_node(nodes::DOT.to_owned(), HighlightStyle::Operator, 0) -} - -pub fn new_blank_mn() -> MarkupNode { - MarkupNode::Blank { - attributes: Attributes::default(), - parent_id_opt: None, - newlines_at_end: 0, - } -} - -pub fn new_blank_mn_w_nls(nr_of_newlines: usize) -> MarkupNode { - MarkupNode::Blank { - attributes: Attributes::default(), - parent_id_opt: None, - newlines_at_end: nr_of_newlines, - } -} - -pub fn new_colon_mn() -> MarkupNode { - new_operator_mn(nodes::COLON.to_owned()) -} - -pub fn new_operator_mn(content: String) -> MarkupNode { - common_text_node(content, HighlightStyle::Operator, 0) -} - -pub fn new_left_accolade_mn() -> MarkupNode { - common_text_node(nodes::LEFT_ACCOLADE.to_owned(), HighlightStyle::Bracket, 0) -} - -pub fn new_right_accolade_mn() -> MarkupNode { - common_text_node(nodes::RIGHT_ACCOLADE.to_owned(), HighlightStyle::Bracket, 0) -} - -pub fn new_left_square_mn() -> MarkupNode { - common_text_node(nodes::LEFT_SQUARE_BR.to_owned(), HighlightStyle::Bracket, 0) -} - -pub fn new_right_square_mn() -> MarkupNode { - common_text_node( - nodes::RIGHT_SQUARE_BR.to_owned(), - HighlightStyle::Bracket, - 0, - ) -} - -pub fn new_func_name_mn(content: String) -> MarkupNode { - common_text_node(content, HighlightStyle::FunctionName, 0) -} - -pub fn new_arg_name_mn(content: String) -> MarkupNode { - common_text_node(content, HighlightStyle::FunctionArgName, 0) -} - -pub fn new_arrow_mn(newlines_at_end: usize) -> MarkupNode { - common_text_node( - nodes::ARROW.to_owned(), - HighlightStyle::Operator, - newlines_at_end, - ) -} - -pub fn new_comments_mn(comment: String, newlines_at_end: usize) -> MarkupNode { - common_text_node(comment, HighlightStyle::Comment, newlines_at_end) -} - -fn common_text_node( - content: String, - highlight_style: HighlightStyle, - newlines_at_end: usize, -) -> MarkupNode { - MarkupNode::Text { - content, - syn_high_style: highlight_style, - attributes: Attributes::default(), - parent_id_opt: None, - newlines_at_end, - } -} - -pub const NEW_LINES_AFTER_DEF: usize = 2; - -pub fn new_assign_mn( - val_name_mn_id: MarkNodeId, - equals_mn_id: MarkNodeId, - expr_mark_node_id: MarkNodeId, -) -> MarkupNode { - make_nested_mn( - vec![val_name_mn_id, equals_mn_id, expr_mark_node_id], - NEW_LINES_AFTER_DEF, - ) -} - -pub fn new_module_name_mn_id(mn_ids: Vec, mark_node_pool: &mut SlowPool) -> MarkNodeId { - if mn_ids.len() == 1 { - *mn_ids.first().unwrap() // safe because we checked the length before - } else { - let nested_node = make_nested_mn(mn_ids, 0); - mark_node_pool.add(nested_node) - } -} - -pub fn new_module_var_mn( - module_name_id: MarkNodeId, - dot_id: MarkNodeId, - ident_id: MarkNodeId, -) -> MarkupNode { - make_nested_mn(vec![module_name_id, dot_id, ident_id], 0) -} - -pub fn if_mn() -> MarkupNode { - keyword_mn("if ") -} - -pub fn then_mn() -> MarkupNode { - keyword_mn(" then ") -} - -pub fn else_mn() -> MarkupNode { - keyword_mn(" else ") -} - -fn keyword_mn(keyword: &str) -> MarkupNode { - common_text_node(keyword.to_owned(), HighlightStyle::Keyword, 0) -} - -pub fn new_if_expr_mn( - if_mn_id: MarkNodeId, - cond_expr_mn_id: MarkNodeId, - then_mn_id: MarkNodeId, - then_expr_mn_id: MarkNodeId, - else_mn_id: MarkNodeId, - else_expr_mn_id: MarkNodeId, -) -> MarkupNode { - make_nested_mn( - vec![ - if_mn_id, - cond_expr_mn_id, - then_mn_id, - then_expr_mn_id, - else_mn_id, - else_expr_mn_id, - ], - 1, - ) -} diff --git a/crates/code_markup/src/markup/convert/from_ast.rs b/crates/code_markup/src/markup/convert/from_ast.rs deleted file mode 100644 index 18f3f14532..0000000000 --- a/crates/code_markup/src/markup/convert/from_ast.rs +++ /dev/null @@ -1,50 +0,0 @@ -use roc_ast::{ - ast_error::ASTResult, - lang::{core::ast::AST, env::Env}, -}; -use roc_module::symbol::Interns; - -use crate::{ - markup::{ - convert::{from_def2::def2_to_markup, from_header::header_to_markup}, - mark_id_ast_id_map::MarkIdAstIdMap, - nodes::set_parent_for_all, - }, - slow_pool::{MarkNodeId, SlowPool}, -}; - -pub fn ast_to_mark_nodes( - env: &mut Env<'_>, - ast: &AST, - mark_node_pool: &mut SlowPool, - interns: &Interns, -) -> ASTResult<(Vec, MarkIdAstIdMap)> { - let mut mark_id_ast_id_map = MarkIdAstIdMap::default(); - let mut all_mark_node_ids = vec![header_to_markup( - &ast.header, - mark_node_pool, - &mut mark_id_ast_id_map, - )]; - - for &def_id in ast.def_ids.iter() { - // for debugging - //println!("{}", def2_to_string(def_id, env.pool)); - - let def2 = env.pool.get(def_id); - - let expr2_markup_id = def2_to_markup( - env, - def2, - def_id, - mark_node_pool, - &mut mark_id_ast_id_map, - interns, - )?; - - set_parent_for_all(expr2_markup_id, mark_node_pool); - - all_mark_node_ids.push(expr2_markup_id); - } - - Ok((all_mark_node_ids, mark_id_ast_id_map)) -} diff --git a/crates/code_markup/src/markup/convert/from_def2.rs b/crates/code_markup/src/markup/convert/from_def2.rs deleted file mode 100644 index c1d84a0d34..0000000000 --- a/crates/code_markup/src/markup/convert/from_def2.rs +++ /dev/null @@ -1,137 +0,0 @@ -use crate::{ - markup::{ - common_nodes::new_blank_mn_w_nls, - mark_id_ast_id_map::MarkIdAstIdMap, - nodes::MarkupNode, - top_level_def::{assignment_mark_node, tld_w_comments_mark_node}, - }, - slow_pool::{MarkNodeId, SlowPool}, -}; - -use super::from_expr2::expr2_to_markup; - -use roc_ast::{ - ast_error::ASTResult, - lang::{ - core::{ - ast::ASTNodeId, - def::def2::{Def2, DefId}, - }, - env::Env, - }, -}; -use roc_module::symbol::Interns; - -pub fn add_node( - mark_node: MarkupNode, - ast_node_id: ASTNodeId, - mark_node_pool: &mut SlowPool, - mark_id_ast_id_map: &mut MarkIdAstIdMap, -) -> MarkNodeId { - let mark_node_id = mark_node_pool.add(mark_node); - - mark_id_ast_id_map.insert(mark_node_id, ast_node_id); - - mark_node_id -} - -pub fn def2_to_markup( - env: &mut Env<'_>, - def2: &Def2, - def2_node_id: DefId, - mark_node_pool: &mut SlowPool, - mark_id_ast_id_map: &mut MarkIdAstIdMap, - interns: &Interns, -) -> ASTResult { - let ast_node_id = ASTNodeId::ADefId(def2_node_id); - - let mark_node_id = match def2 { - Def2::ValueDef { - identifier_id, - expr_id, - } => { - let expr_mn_id = expr2_to_markup( - env, - env.pool.get(*expr_id), - *expr_id, - mark_node_pool, - mark_id_ast_id_map, - interns, - 0, - )?; - - let tld_mn = assignment_mark_node( - *identifier_id, - expr_mn_id, - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - env, - )?; - - add_node(tld_mn, ast_node_id, mark_node_pool, mark_id_ast_id_map) - } - Def2::Blank => add_node( - new_blank_mn_w_nls(2), - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ), - Def2::CommentsBefore { comments, def_id } => { - let inner_def = env.pool.get(*def_id); - let inner_def_mark_node_id = def2_to_markup( - env, - inner_def, - *def_id, - mark_node_pool, - mark_id_ast_id_map, - interns, - )?; - - let full_mark_node = tld_w_comments_mark_node( - comments.clone(), - inner_def_mark_node_id, - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - true, - )?; - - add_node( - full_mark_node, - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ) - } - Def2::CommentsAfter { def_id, comments } => { - let inner_def = env.pool.get(*def_id); - let inner_def_mark_node_id = def2_to_markup( - env, - inner_def, - *def_id, - mark_node_pool, - mark_id_ast_id_map, - interns, - )?; - - let full_mark_node = tld_w_comments_mark_node( - comments.clone(), - inner_def_mark_node_id, - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - false, - )?; - - add_node( - full_mark_node, - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ) - } - }; - - Ok(mark_node_id) -} diff --git a/crates/code_markup/src/markup/convert/from_expr2.rs b/crates/code_markup/src/markup/convert/from_expr2.rs deleted file mode 100644 index 04e5ee5d22..0000000000 --- a/crates/code_markup/src/markup/convert/from_expr2.rs +++ /dev/null @@ -1,496 +0,0 @@ -use crate::{ - markup::{ - attribute::Attributes, - common_nodes::{ - new_arg_name_mn, new_arrow_mn, new_blank_mn, new_colon_mn, new_comma_mn, new_equals_mn, - new_left_accolade_mn, new_left_square_mn, new_operator_mn, new_right_accolade_mn, - new_right_square_mn, - }, - mark_id_ast_id_map::MarkIdAstIdMap, - nodes::{ - get_string, join_mark_nodes_commas, join_mark_nodes_spaces, new_markup_node, MarkupNode, - }, - }, - slow_pool::{MarkNodeId, SlowPool}, - syntax_highlight::HighlightStyle, -}; - -use roc_ast::{ - ast_error::ASTResult, - lang::{ - core::{ - ast::ASTNodeId, - expr::{ - expr2::{Expr2, ExprId}, - record_field::RecordField, - }, - pattern::{get_identifier_string, Pattern2}, - val_def::ValueDef, - }, - env::Env, - }, -}; -use roc_module::{module_err::ModuleResult, symbol::Interns}; - -use super::from_def2::add_node; - -// make Markup Nodes: generate String representation, assign Highlighting Style -pub fn expr2_to_markup( - env: &Env<'_>, - expr2: &Expr2, - expr2_node_id: ExprId, - mark_node_pool: &mut SlowPool, - mark_id_ast_id_map: &mut MarkIdAstIdMap, - interns: &Interns, - indent_level: usize, -) -> ASTResult { - let ast_node_id = ASTNodeId::AExprId(expr2_node_id); - - // for debugging - //println!("EXPR2 {:?}", expr2); - - let mark_node_id = match expr2 { - Expr2::SmallInt { text, .. } - | Expr2::I128 { text, .. } - | Expr2::U128 { text, .. } - | Expr2::Float { text, .. } => { - let num_str = get_string(env, text); - - new_markup_node( - with_indent(indent_level, &num_str), - ast_node_id, - HighlightStyle::Number, - mark_node_pool, - mark_id_ast_id_map, - indent_level, - ) - } - Expr2::Str(text) => { - let content = format!("\"{}\"", text.as_str(env.pool)); - - string_mark_node( - &content, - indent_level, - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ) - } - Expr2::SmallStr(array_str) => { - let content = format!("\"{}\"", array_str.as_str()); - - string_mark_node( - &content, - indent_level, - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ) - } - Expr2::Tag { name, .. } => new_markup_node( - with_indent(indent_level, &get_string(env, name)), - ast_node_id, - HighlightStyle::Type, - mark_node_pool, - mark_id_ast_id_map, - indent_level, - ), - Expr2::Call { args, expr_id, .. } => { - let expr = env.pool.get(*expr_id); - let fun_call_mark_id = expr2_to_markup( - env, - expr, - *expr_id, - mark_node_pool, - mark_id_ast_id_map, - interns, - indent_level, - )?; - - let arg_expr_ids: Vec = - args.iter(env.pool).map(|(_, arg_id)| *arg_id).collect(); - - let arg_call_mark_ids: Vec = arg_expr_ids - .iter() - .map(|arg_id| { - let arg_expr = env.pool.get(*arg_id); - - expr2_to_markup( - env, - arg_expr, - *arg_id, - mark_node_pool, - mark_id_ast_id_map, - interns, - 0, - ) - }) - .collect::>>()?; - - let mut args_with_sapces = - join_mark_nodes_spaces(arg_call_mark_ids, true, mark_node_pool); - - let mut children_ids = vec![fun_call_mark_id]; - children_ids.append(&mut args_with_sapces); - - let call_node = MarkupNode::Nested { - children_ids, - parent_id_opt: None, - newlines_at_end: 0, - }; - - add_node(call_node, ast_node_id, mark_node_pool, mark_id_ast_id_map) - } - Expr2::Var(symbol) => { - let text = symbol.fully_qualified(interns, env.home); - - new_markup_node( - text.to_string(), - ast_node_id, - HighlightStyle::Value, - mark_node_pool, - mark_id_ast_id_map, - indent_level, - ) - } - Expr2::List { elems, .. } => { - let mut children_ids = vec![add_node( - new_left_square_mn(), - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - )]; - - let indexed_node_ids: Vec<(usize, ExprId)> = - elems.iter(env.pool).copied().enumerate().collect(); - - for (idx, node_id) in indexed_node_ids.iter() { - let sub_expr2 = env.pool.get(*node_id); - - children_ids.push(expr2_to_markup( - env, - sub_expr2, - *node_id, - mark_node_pool, - mark_id_ast_id_map, - interns, - indent_level, - )?); - - if idx + 1 < elems.len() { - children_ids.push(add_node( - new_comma_mn(), - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - )); - } - } - children_ids.push(add_node( - new_right_square_mn(), - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - )); - - let list_mn = MarkupNode::Nested { - children_ids, - parent_id_opt: None, - newlines_at_end: 0, - }; - - add_node(list_mn, ast_node_id, mark_node_pool, mark_id_ast_id_map) - } - Expr2::EmptyRecord => { - let children_ids = vec![ - add_node( - new_left_accolade_mn(), - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ), - add_node( - new_right_accolade_mn(), - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ), - ]; - - let record_mn = MarkupNode::Nested { - children_ids, - parent_id_opt: None, - newlines_at_end: 0, - }; - - add_node(record_mn, ast_node_id, mark_node_pool, mark_id_ast_id_map) - } - Expr2::Record { fields, .. } => { - let mut children_ids = vec![add_node( - new_left_accolade_mn(), - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - )]; - - for (idx, field_node_id) in fields.iter_node_ids().enumerate() { - let record_field = env.pool.get(field_node_id); - - let field_name = record_field.get_record_field_pool_str(); - - children_ids.push(new_markup_node( - field_name.as_str(env.pool).to_owned(), - ast_node_id, - HighlightStyle::RecordField, - mark_node_pool, - mark_id_ast_id_map, - indent_level, - )); - - match record_field { - RecordField::InvalidLabelOnly(_, _) => (), - RecordField::LabelOnly(_, _, _) => (), - RecordField::LabeledValue(_, _, sub_expr2_node_id) => { - children_ids.push(add_node( - new_colon_mn(), - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - )); - - let sub_expr2 = env.pool.get(*sub_expr2_node_id); - children_ids.push(expr2_to_markup( - env, - sub_expr2, - *sub_expr2_node_id, - mark_node_pool, - mark_id_ast_id_map, - interns, - indent_level, - )?); - } - } - - if idx + 1 < fields.len() { - children_ids.push(add_node( - new_comma_mn(), - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - )); - } - } - - children_ids.push(add_node( - new_right_accolade_mn(), - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - )); - - let record_mn = MarkupNode::Nested { - children_ids, - parent_id_opt: None, - newlines_at_end: 0, - }; - - add_node(record_mn, ast_node_id, mark_node_pool, mark_id_ast_id_map) - } - Expr2::Blank => add_node( - new_blank_mn(), - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ), - Expr2::LetValue { - def_id, - body_id: _, - body_var: _, - } => { - let pattern_id = env.pool.get(*def_id).get_pattern_id(); - - let pattern2 = env.pool.get(pattern_id); - - let val_name = get_identifier_string(pattern2, interns)?; - - let val_name_mn = MarkupNode::Text { - content: val_name, - syn_high_style: HighlightStyle::Value, - attributes: Attributes::default(), - parent_id_opt: None, - newlines_at_end: 0, - }; - - let val_name_mn_id = - add_node(val_name_mn, ast_node_id, mark_node_pool, mark_id_ast_id_map); - - let equals_mn_id = add_node( - new_equals_mn(), - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ); - - let value_def = env.pool.get(*def_id); - - match value_def { - ValueDef::NoAnnotation { - pattern_id: _, - expr_id, - expr_var: _, - } => { - let body_mn_id = expr2_to_markup( - env, - env.pool.get(*expr_id), - *expr_id, - mark_node_pool, - mark_id_ast_id_map, - interns, - indent_level, - )?; - - let body_mn = mark_node_pool.get_mut(body_mn_id); - body_mn.add_newline_at_end(); - - let full_let_mn = MarkupNode::Nested { - children_ids: vec![val_name_mn_id, equals_mn_id, body_mn_id], - parent_id_opt: None, - newlines_at_end: 1, - }; - - add_node(full_let_mn, ast_node_id, mark_node_pool, mark_id_ast_id_map) - } - other => { - unimplemented!( - "I don't know how to convert {:?} into a MarkupNode yet.", - other - ) - } - } - } - Expr2::Closure { - function_type: _, - uniq_symbol: _, - recursive: _, - args, - body_id, - extra: _, - } => { - let backslash_mn = new_operator_mn("\\".to_string()); - let backslash_mn_id = add_node( - backslash_mn, - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ); - - let arg_names: Vec<&str> = args - .iter(env.pool) - .map(|(_, arg_node_id)| { - let arg_pattern2 = env.pool.get(*arg_node_id); - - match arg_pattern2 { - Pattern2::Identifier(id_symbol) => { - let ident_id = id_symbol.ident_id(); - - env.ident_ids.get_name_str_res(ident_id) - } - Pattern2::Shadowed { shadowed_ident } => { - Ok(shadowed_ident.as_str(env.pool)) - } - other => { - todo!( - "TODO: support the following pattern2 as function arg: {:?}", - other - ); - } - } - }) - .collect::>>()?; - - let arg_mark_nodes: Vec<_> = arg_names - .iter() - .map(|arg_name| new_arg_name_mn(arg_name.to_string())) - .collect(); - - let args_with_commas: Vec = join_mark_nodes_commas(arg_mark_nodes); - - let mut args_with_commas_ids: Vec = args_with_commas - .into_iter() - .map(|mark_node| { - add_node(mark_node, ast_node_id, mark_node_pool, mark_id_ast_id_map) - }) - .collect(); - - let arrow_mn = new_arrow_mn(1); - let arrow_mn_id = add_node(arrow_mn, ast_node_id, mark_node_pool, mark_id_ast_id_map); - - let mut children_ids = vec![backslash_mn_id]; - children_ids.append(&mut args_with_commas_ids); - children_ids.push(arrow_mn_id); - - let args_mn = MarkupNode::Nested { - children_ids, - parent_id_opt: None, - newlines_at_end: 0, - }; - let args_mn_id = add_node(args_mn, ast_node_id, mark_node_pool, mark_id_ast_id_map); - - let body_expr = env.pool.get(*body_id); - let body_mn_id = expr2_to_markup( - env, - body_expr, - *body_id, - mark_node_pool, - mark_id_ast_id_map, - interns, - indent_level + 1, - )?; - - let function_mn = MarkupNode::Nested { - children_ids: vec![args_mn_id, body_mn_id], - parent_id_opt: None, - newlines_at_end: 0, - }; - - add_node(function_mn, ast_node_id, mark_node_pool, mark_id_ast_id_map) - } - Expr2::RuntimeError() => new_markup_node( - "RunTimeError".to_string(), - ast_node_id, - HighlightStyle::Blank, - mark_node_pool, - mark_id_ast_id_map, - indent_level, - ), - rest => todo!("implement expr2_to_markup for {:?}", rest), - }; - - Ok(mark_node_id) -} - -fn with_indent(indent_level: usize, some_str: &str) -> String { - let full_indent = std::iter::repeat(" ").take(indent_level * 4); - let mut full_string: String = full_indent.collect(); - - full_string.push_str(some_str); - - full_string -} - -fn string_mark_node( - content: &str, - indent_level: usize, - ast_node_id: ASTNodeId, - mark_node_pool: &mut SlowPool, - mark_id_ast_id_map: &mut MarkIdAstIdMap, -) -> MarkNodeId { - new_markup_node( - with_indent(indent_level, content), - ast_node_id, - HighlightStyle::String, - mark_node_pool, - mark_id_ast_id_map, - indent_level, - ) -} diff --git a/crates/code_markup/src/markup/convert/from_header.rs b/crates/code_markup/src/markup/convert/from_header.rs deleted file mode 100644 index 0c35b3b75c..0000000000 --- a/crates/code_markup/src/markup/convert/from_header.rs +++ /dev/null @@ -1,305 +0,0 @@ -use roc_ast::lang::core::{ast::ASTNodeId, header::AppHeader}; - -use crate::{ - markup::{ - attribute::Attributes, - common_nodes::{ - new_comma_mn, new_left_accolade_mn, new_left_square_mn, new_right_accolade_mn, - new_right_square_mn, - }, - mark_id_ast_id_map::MarkIdAstIdMap, - nodes::{set_parent_for_all, MarkupNode}, - }, - slow_pool::{MarkNodeId, SlowPool}, - syntax_highlight::HighlightStyle, -}; - -use super::from_def2::add_node; - -pub fn header_to_markup( - app_header: &AppHeader, - mark_node_pool: &mut SlowPool, - mark_id_ast_id_map: &mut MarkIdAstIdMap, -) -> MarkNodeId { - let expr_id = app_header.ast_node_id; - let ast_node_id = ASTNodeId::AExprId(expr_id); - - let app_node_id = header_mn( - "app ".to_owned(), - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ); - - let app_name_node_id = header_val_mn( - app_header.app_name.clone(), - ast_node_id, - HighlightStyle::String, - mark_node_pool, - mark_id_ast_id_map, - ); - - let full_app_node = MarkupNode::Nested { - children_ids: vec![app_node_id, app_name_node_id], - parent_id_opt: None, - newlines_at_end: 1, - }; - - let packages_node_id = header_mn( - " packages ".to_owned(), - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ); - - let pack_left_acc_node_id = add_node( - new_left_accolade_mn(), - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ); - - let pack_base_node_id = header_val_mn( - "pf: ".to_owned(), - ast_node_id, - HighlightStyle::RecordField, - mark_node_pool, - mark_id_ast_id_map, - ); - - let pack_val_node_id = header_val_mn( - app_header.packages_base.clone(), - ast_node_id, - HighlightStyle::String, - mark_node_pool, - mark_id_ast_id_map, - ); - - let pack_right_acc_node_id = add_node( - new_right_accolade_mn(), - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ); - - let full_packages_node = MarkupNode::Nested { - children_ids: vec![ - packages_node_id, - pack_left_acc_node_id, - pack_base_node_id, - pack_val_node_id, - pack_right_acc_node_id, - ], - parent_id_opt: None, - newlines_at_end: 1, - }; - - let imports_node_id = header_mn( - " imports ".to_owned(), - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ); - - let imports_left_square_node_id = add_node( - new_left_square_mn(), - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ); - - let mut import_child_ids: Vec = add_header_mn_list( - &app_header.imports, - ast_node_id, - HighlightStyle::Import, - mark_node_pool, - mark_id_ast_id_map, - ); - - let imports_right_square_node_id = add_node( - new_right_square_mn(), - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ); - - let mut full_import_children = vec![imports_node_id, imports_left_square_node_id]; - - full_import_children.append(&mut import_child_ids); - full_import_children.push(imports_right_square_node_id); - - let full_import_node = MarkupNode::Nested { - children_ids: full_import_children, - parent_id_opt: None, - newlines_at_end: 1, - }; - - let provides_node_id = header_mn( - " provides ".to_owned(), - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ); - - let provides_left_square_node_id = add_node( - new_left_square_mn(), - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ); - - let mut provides_val_node_ids: Vec = add_header_mn_list( - &app_header.provides, - ast_node_id, - HighlightStyle::Provides, - mark_node_pool, - mark_id_ast_id_map, - ); - - let provides_right_square_node_id = add_node( - new_right_square_mn(), - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ); - - let provides_end_node_id = header_mn( - " to pf".to_owned(), - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ); - - let mut full_provides_children = vec![provides_node_id, provides_left_square_node_id]; - - full_provides_children.append(&mut provides_val_node_ids); - full_provides_children.push(provides_right_square_node_id); - full_provides_children.push(provides_end_node_id); - - let full_provides_node = MarkupNode::Nested { - children_ids: full_provides_children, - parent_id_opt: None, - newlines_at_end: 1, - }; - - let full_app_node_id = add_node( - full_app_node, - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ); - let full_packages_node = add_node( - full_packages_node, - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ); - let full_import_node_id = add_node( - full_import_node, - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ); - let full_provides_node_id = add_node( - full_provides_node, - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ); - - let header_mark_node = MarkupNode::Nested { - children_ids: vec![ - full_app_node_id, - full_packages_node, - full_import_node_id, - full_provides_node_id, - ], - parent_id_opt: None, - newlines_at_end: 1, - }; - - let header_mn_id = add_node( - header_mark_node, - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ); - - set_parent_for_all(header_mn_id, mark_node_pool); - - header_mn_id -} - -// Used for provides and imports -fn add_header_mn_list( - str_vec: &[String], - ast_node_id: ASTNodeId, - highlight_style: HighlightStyle, - mark_node_pool: &mut SlowPool, - mark_id_ast_id_map: &mut MarkIdAstIdMap, -) -> Vec { - let nr_of_elts = str_vec.len(); - - str_vec - .iter() - .enumerate() - .flat_map(|(indx, provide_str)| { - let provide_str = header_val_mn( - provide_str.to_owned(), - ast_node_id, - highlight_style, - mark_node_pool, - mark_id_ast_id_map, - ); - - if indx != nr_of_elts - 1 { - vec![ - provide_str, - add_node( - new_comma_mn(), - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ), - ] - } else { - vec![provide_str] - } - }) - .collect() -} - -fn header_mn( - content: String, - ast_node_id: ASTNodeId, - mark_node_pool: &mut SlowPool, - mark_id_ast_id_map: &mut MarkIdAstIdMap, -) -> MarkNodeId { - let mark_node = MarkupNode::Text { - content, - syn_high_style: HighlightStyle::PackageRelated, - attributes: Attributes::default(), - parent_id_opt: None, - newlines_at_end: 0, - }; - - add_node(mark_node, ast_node_id, mark_node_pool, mark_id_ast_id_map) -} - -fn header_val_mn( - content: String, - ast_node_id: ASTNodeId, - highlight_style: HighlightStyle, - mark_node_pool: &mut SlowPool, - mark_id_ast_id_map: &mut MarkIdAstIdMap, -) -> MarkNodeId { - let mark_node = MarkupNode::Text { - content, - syn_high_style: highlight_style, - attributes: Attributes::default(), - parent_id_opt: None, - newlines_at_end: 0, - }; - - add_node(mark_node, ast_node_id, mark_node_pool, mark_id_ast_id_map) -} diff --git a/crates/code_markup/src/markup/convert/mod.rs b/crates/code_markup/src/markup/convert/mod.rs deleted file mode 100644 index 57b87c5918..0000000000 --- a/crates/code_markup/src/markup/convert/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod from_ast; -pub mod from_def2; -pub mod from_expr2; -pub mod from_header; diff --git a/crates/code_markup/src/markup/mark_id_ast_id_map.rs b/crates/code_markup/src/markup/mark_id_ast_id_map.rs deleted file mode 100644 index 42724f3c7d..0000000000 --- a/crates/code_markup/src/markup/mark_id_ast_id_map.rs +++ /dev/null @@ -1,29 +0,0 @@ -use std::collections::HashMap; - -use roc_ast::lang::core::ast::ASTNodeId; - -use crate::markup_error::MarkNodeIdWithoutCorrespondingASTNodeIdSnafu; -use crate::{markup_error::MarkResult, slow_pool::MarkNodeId}; - -/// A hashmap is wrapped to allow for an easy swap out with more performant alternatives -#[derive(Debug, Default)] -pub struct MarkIdAstIdMap { - map: HashMap, -} - -impl MarkIdAstIdMap { - pub fn insert(&mut self, mn_id: MarkNodeId, ast_id: ASTNodeId) { - self.map.insert(mn_id, ast_id); - } - - pub fn get(&self, mn_id: MarkNodeId) -> MarkResult { - match self.map.get(&mn_id) { - Some(ast_node_id) => Ok(*ast_node_id), - None => MarkNodeIdWithoutCorrespondingASTNodeIdSnafu { - node_id: mn_id, - keys_str: format!("{:?}", self.map.keys()), - } - .fail(), - } - } -} diff --git a/crates/code_markup/src/markup/mod.rs b/crates/code_markup/src/markup/mod.rs deleted file mode 100644 index f9a4f0f98e..0000000000 --- a/crates/code_markup/src/markup/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod attribute; -pub mod common_nodes; -pub mod convert; -pub mod mark_id_ast_id_map; -pub mod nodes; -pub mod top_level_def; diff --git a/crates/code_markup/src/markup/nodes.rs b/crates/code_markup/src/markup/nodes.rs deleted file mode 100644 index 638f4c4d9e..0000000000 --- a/crates/code_markup/src/markup/nodes.rs +++ /dev/null @@ -1,533 +0,0 @@ -use crate::{ - markup_error::MarkResult, - slow_pool::{MarkNodeId, SlowPool}, - syntax_highlight::HighlightStyle, -}; - -use super::{ - attribute::Attributes, common_nodes::new_comma_mn, convert::from_def2::add_node, - mark_id_ast_id_map::MarkIdAstIdMap, -}; - -use crate::markup_error::{ - ExpectedTextNodeSnafu, NestedNodeMissingChildSnafu, NestedNodeRequiredSnafu, -}; -use roc_ast::{ - lang::{core::ast::ASTNodeId, env::Env}, - mem_pool::pool_str::PoolStr, -}; -use roc_error_utils::{index_of, slice_get}; -use std::fmt; -use std::fmt::Write; - -#[derive(Debug)] -pub enum MarkupNode { - Nested { - children_ids: Vec, - parent_id_opt: Option, - newlines_at_end: usize, - }, - Text { - content: String, - syn_high_style: HighlightStyle, - attributes: Attributes, - parent_id_opt: Option, - newlines_at_end: usize, - }, - Blank { - attributes: Attributes, - parent_id_opt: Option, - newlines_at_end: usize, - }, - Indent { - indent_level: usize, - parent_id_opt: Option, - }, -} - -impl MarkupNode { - pub fn get_parent_id_opt(&self) -> Option { - match self { - MarkupNode::Nested { parent_id_opt, .. } => *parent_id_opt, - MarkupNode::Text { parent_id_opt, .. } => *parent_id_opt, - MarkupNode::Blank { parent_id_opt, .. } => *parent_id_opt, - MarkupNode::Indent { parent_id_opt, .. } => *parent_id_opt, - } - } - - pub fn get_children_ids(&self) -> Vec { - match self { - MarkupNode::Nested { children_ids, .. } => children_ids.to_vec(), - MarkupNode::Text { .. } => vec![], - MarkupNode::Blank { .. } => vec![], - MarkupNode::Indent { .. } => vec![], - } - } - - pub fn get_sibling_ids(&self, mark_node_pool: &SlowPool) -> Vec { - if let Some(parent_id) = self.get_parent_id_opt() { - let parent_node = mark_node_pool.get(parent_id); - - parent_node.get_children_ids() - } else { - vec![] - } - } - - // return (index of child in list of children, closest ast index of child corresponding to ast node) - pub fn get_child_indices( - &self, - mark_node_id: MarkNodeId, - ast_node_id: ASTNodeId, - mark_id_ast_id_map: &MarkIdAstIdMap, - ) -> MarkResult<(usize, usize)> { - match self { - MarkupNode::Nested { children_ids, .. } => { - let mut mark_child_index_opt: Option = None; - let mut child_ids_with_ast: Vec = Vec::new(); - - for (indx, &mark_child_id) in children_ids.iter().enumerate() { - if mark_child_id == mark_node_id { - mark_child_index_opt = Some(indx); - } - - let child_ast_node_id = mark_id_ast_id_map.get(mark_child_id)?; - // a node that points to the same ast_node as the parent is a ',', '[', ']' - // those are not "real" ast children - if child_ast_node_id != ast_node_id { - child_ids_with_ast.push(mark_child_id) - } - } - - if let Some(child_index) = mark_child_index_opt { - if child_index == (children_ids.len() - 1) { - let ast_child_index = child_ids_with_ast.len(); - - Ok((child_index, ast_child_index)) - } else { - // we want to find the index of the closest ast mark node to child_index - let mut indices_in_mark = vec![]; - - for &c_id in child_ids_with_ast.iter() { - indices_in_mark.push(index_of(c_id, children_ids)?); - } - - let mut last_diff = usize::MAX; - let mut best_index = 0; - - for index in indices_in_mark.iter() { - let curr_diff = - isize::abs((*index as isize) - (child_index as isize)) as usize; - - if curr_diff >= last_diff { - break; - } else { - last_diff = curr_diff; - best_index = *index; - } - } - - let closest_ast_child = slice_get(best_index, children_ids)?; - - let closest_ast_child_index = - index_of(*closest_ast_child, &child_ids_with_ast)?; - - // +1 because we want to insert after ast_child - Ok((child_index, closest_ast_child_index + 1)) - } - } else { - NestedNodeMissingChildSnafu { - node_id: mark_node_id, - children_ids: children_ids.clone(), - } - .fail() - } - } - _ => NestedNodeRequiredSnafu { - node_type: self.node_type_as_string(), - } - .fail(), - } - } - - pub fn get_content(&self) -> String { - match self { - MarkupNode::Nested { .. } => "".to_owned(), - MarkupNode::Text { content, .. } => content.clone(), - MarkupNode::Blank { .. } => BLANK_PLACEHOLDER.to_owned(), - MarkupNode::Indent { indent_level, .. } => SINGLE_INDENT.repeat(*indent_level), - } - } - - // gets content and adds newline from newline_at_end - pub fn get_full_content(&self) -> String { - let mut full_content = self.get_content(); - - for _ in 0..self.get_newlines_at_end() { - full_content.push('\n') - } - - full_content - } - - pub fn get_content_mut(&mut self) -> MarkResult<&mut String> { - match self { - MarkupNode::Text { content, .. } => Ok(content), - _ => ExpectedTextNodeSnafu { - function_name: "set_content".to_owned(), - node_type: self.node_type_as_string(), - } - .fail(), - } - } - - pub fn is_all_alphanumeric(&self) -> bool { - self.get_content() - .chars() - .all(|chr| chr.is_ascii_alphanumeric()) - } - - pub fn add_child_at_index(&mut self, index: usize, child_id: MarkNodeId) -> MarkResult<()> { - if let MarkupNode::Nested { children_ids, .. } = self { - children_ids.splice(index..index, vec![child_id]); - } else { - NestedNodeRequiredSnafu { - node_type: self.node_type_as_string(), - } - .fail()?; - } - - Ok(()) - } - - pub fn node_type_as_string(&self) -> String { - let type_str = match self { - MarkupNode::Nested { .. } => "Nested", - MarkupNode::Text { .. } => "Text", - MarkupNode::Blank { .. } => "Blank", - MarkupNode::Indent { .. } => "Indent", - }; - - type_str.to_owned() - } - - pub fn is_blank(&self) -> bool { - matches!(self, MarkupNode::Blank { .. }) - } - - pub fn is_nested(&self) -> bool { - matches!(self, MarkupNode::Nested { .. }) - } - - pub fn get_newlines_at_end(&self) -> usize { - match self { - MarkupNode::Nested { - newlines_at_end, .. - } => *newlines_at_end, - MarkupNode::Text { - newlines_at_end, .. - } => *newlines_at_end, - MarkupNode::Blank { - newlines_at_end, .. - } => *newlines_at_end, - MarkupNode::Indent { .. } => 0, - } - } - - pub fn add_newline_at_end(&mut self) { - match self { - MarkupNode::Nested { - newlines_at_end, .. - } => *newlines_at_end += 1, - MarkupNode::Text { - newlines_at_end, .. - } => *newlines_at_end += 1, - MarkupNode::Blank { - newlines_at_end, .. - } => *newlines_at_end += 1, - _ => {} - } - } -} - -pub fn make_nested_mn(children_ids: Vec, newlines_at_end: usize) -> MarkupNode { - MarkupNode::Nested { - children_ids, - parent_id_opt: None, - newlines_at_end, - } -} - -pub fn get_string(env: &Env<'_>, pool_str: &PoolStr) -> String { - pool_str.as_str(env.pool).to_owned() -} - -pub const BLANK_PLACEHOLDER: &str = " "; -pub const LEFT_ACCOLADE: &str = "{ "; -pub const RIGHT_ACCOLADE: &str = " }"; -pub const LEFT_SQUARE_BR: &str = "[ "; -pub const RIGHT_SQUARE_BR: &str = " ]"; -pub const COLON: &str = ": "; -pub const COMMA: &str = ", "; -pub const DOT: &str = "."; -pub const STRING_QUOTES: &str = "\"\""; -pub const EQUALS: &str = " = "; -pub const ARROW: &str = " -> "; -pub const SINGLE_INDENT: &str = " "; // 4 spaces - -pub fn new_markup_node( - text: String, - node_id: ASTNodeId, - highlight_style: HighlightStyle, - mark_node_pool: &mut SlowPool, - mark_id_ast_id_map: &mut MarkIdAstIdMap, - indent_level: usize, -) -> MarkNodeId { - let content_node = MarkupNode::Text { - content: text, - syn_high_style: highlight_style, - attributes: Attributes::default(), - parent_id_opt: None, - newlines_at_end: 0, - }; - - let content_node_id = add_node(content_node, node_id, mark_node_pool, mark_id_ast_id_map); - - if indent_level > 0 { - let indent_node = MarkupNode::Indent { - indent_level, - parent_id_opt: None, - }; - - let indent_node_id = add_node(indent_node, node_id, mark_node_pool, mark_id_ast_id_map); - - let nested_node = MarkupNode::Nested { - children_ids: vec![indent_node_id, content_node_id], - parent_id_opt: None, - newlines_at_end: 0, - }; - - add_node(nested_node, node_id, mark_node_pool, mark_id_ast_id_map) - } else { - content_node_id - } -} - -pub fn set_parent_for_all(markup_node_id: MarkNodeId, mark_node_pool: &mut SlowPool) { - let node = mark_node_pool.get(markup_node_id); - - if let MarkupNode::Nested { - children_ids, - parent_id_opt: _, - newlines_at_end: _, - } = node - { - // need to clone because of borrowing issues - let children_ids_clone = children_ids.clone(); - - for child_id in children_ids_clone { - set_parent_for_all_helper(child_id, markup_node_id, mark_node_pool); - } - } -} - -pub fn set_parent_for_all_helper( - markup_node_id: MarkNodeId, - parent_node_id: MarkNodeId, - mark_node_pool: &mut SlowPool, -) { - let node = mark_node_pool.get_mut(markup_node_id); - - match node { - MarkupNode::Nested { - children_ids, - parent_id_opt, - .. - } => { - *parent_id_opt = Some(parent_node_id); - - // need to clone because of borrowing issues - let children_ids_clone = children_ids.clone(); - - for child_id in children_ids_clone { - set_parent_for_all_helper(child_id, markup_node_id, mark_node_pool); - } - } - MarkupNode::Text { parent_id_opt, .. } => *parent_id_opt = Some(parent_node_id), - MarkupNode::Blank { parent_id_opt, .. } => *parent_id_opt = Some(parent_node_id), - MarkupNode::Indent { parent_id_opt, .. } => *parent_id_opt = Some(parent_node_id), - } -} - -impl fmt::Display for MarkupNode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{} ({}, {})", - self.node_type_as_string(), - self.get_content(), - self.get_newlines_at_end() - ) - } -} - -pub fn tree_as_string(root_node_id: MarkNodeId, mark_node_pool: &SlowPool) -> String { - let mut full_string = "\n(mark_node_tree)\n".to_owned(); - - let node = mark_node_pool.get(root_node_id); - - writeln!(full_string, "{node} mn_id {root_node_id}\n").unwrap(); - - tree_as_string_helper(node, 1, &mut full_string, mark_node_pool); - - full_string -} - -fn tree_as_string_helper( - node: &MarkupNode, - level: usize, - tree_string: &mut String, - mark_node_pool: &SlowPool, -) { - for child_id in node.get_children_ids() { - let child = mark_node_pool.get(child_id); - let child_str = format!("{}", mark_node_pool.get(child_id)).replace('\n', "\\n"); - - let mut full_str = std::iter::repeat("|--- ") - .take(level) - .collect::>() - .join("") - .to_owned(); - - writeln!(full_str, "{child_str} mn_id {child_id}").unwrap(); - - tree_string.push_str(&full_str); - - tree_as_string_helper(child, level + 1, tree_string, mark_node_pool); - } -} - -// return to the the root parent_id of the current node -pub fn get_root_mark_node_id(mark_node_id: MarkNodeId, mark_node_pool: &SlowPool) -> MarkNodeId { - let mut curr_mark_node_id = mark_node_id; - let mut curr_parent_id_opt = mark_node_pool.get(curr_mark_node_id).get_parent_id_opt(); - - while let Some(curr_parent_id) = curr_parent_id_opt { - curr_mark_node_id = curr_parent_id; - curr_parent_id_opt = mark_node_pool.get(curr_mark_node_id).get_parent_id_opt(); - } - - curr_mark_node_id -} - -// put space mark nodes between each node in mark_nodes -pub fn join_mark_nodes_spaces( - mark_nodes_ids: Vec, - with_prepend: bool, - mark_node_pool: &mut SlowPool, -) -> Vec { - let space_range_max = if with_prepend { - mark_nodes_ids.len() - } else { - mark_nodes_ids.len() - 1 - }; - - let join_nodes: Vec = (0..space_range_max) - .map(|_| { - let space_node = MarkupNode::Text { - content: " ".to_string(), - syn_high_style: HighlightStyle::Blank, - attributes: Attributes::default(), - parent_id_opt: None, - newlines_at_end: 0, - }; - - mark_node_pool.add(space_node) - }) - .collect(); - - if with_prepend { - interleave(join_nodes.into_iter(), mark_nodes_ids) - } else { - interleave(mark_nodes_ids, join_nodes.into_iter()) - } -} - -// put comma mark nodes between each node in mark_nodes -pub fn join_mark_nodes_commas(mark_nodes: Vec) -> Vec { - let join_nodes: Vec = (0..(mark_nodes.len() - 1)) - .map(|_| new_comma_mn()) - .collect(); - - interleave(mark_nodes.into_iter(), join_nodes) -} - -pub fn mark_nodes_to_string(markup_node_ids: &[MarkNodeId], mark_node_pool: &SlowPool) -> String { - let mut all_code_string = String::new(); - - for mark_node_id in markup_node_ids.iter() { - node_to_string_w_children(*mark_node_id, &mut all_code_string, mark_node_pool) - } - - all_code_string -} - -pub fn node_to_string_w_children( - node_id: MarkNodeId, - str_buffer: &mut String, - mark_node_pool: &SlowPool, -) { - let node = mark_node_pool.get(node_id); - - if node.is_nested() { - for child_id in node.get_children_ids() { - node_to_string_w_children(child_id, str_buffer, mark_node_pool); - } - for _ in 0..node.get_newlines_at_end() { - str_buffer.push('\n') - } - } else { - let node_content_str = node.get_full_content(); - - str_buffer.push_str(&node_content_str); - } -} - -fn interleave(i: I, j: J) -> Vec -where - I: IntoIterator, - J: IntoIterator, -{ - let mut output = Vec::new(); - - let mut flag = false; - - let mut i = i.into_iter(); - let mut j = j.into_iter(); - - loop { - flag = !flag; - - if flag { - match i.next() { - None => { - output.extend(j); - break output; - } - Some(v) => { - output.push(v); - } - } - } else { - match j.next() { - None => { - output.extend(i); - break output; - } - Some(v) => { - output.push(v); - } - } - } - } -} diff --git a/crates/code_markup/src/markup/top_level_def.rs b/crates/code_markup/src/markup/top_level_def.rs deleted file mode 100644 index 69119f750f..0000000000 --- a/crates/code_markup/src/markup/top_level_def.rs +++ /dev/null @@ -1,84 +0,0 @@ -use roc_ast::{ - ast_error::ASTResult, - lang::{core::ast::ASTNodeId, env::Env}, -}; -use roc_module::symbol::IdentId; - -use crate::{ - markup::{ - attribute::Attributes, - common_nodes::{new_comments_mn, new_equals_mn}, - nodes::MarkupNode, - }, - slow_pool::{MarkNodeId, SlowPool}, - syntax_highlight::HighlightStyle, -}; - -use super::{ - common_nodes::new_assign_mn, convert::from_def2::add_node, mark_id_ast_id_map::MarkIdAstIdMap, -}; - -// represents for example: `main = "Hello, World!"` -pub fn assignment_mark_node( - identifier_id: IdentId, - expr_mark_node_id: MarkNodeId, - ast_node_id: ASTNodeId, - mark_node_pool: &mut SlowPool, - mark_id_ast_id_map: &mut MarkIdAstIdMap, - env: &Env<'_>, -) -> ASTResult { - let val_name = env.ident_ids.get_name_str_res(identifier_id)?; - - let val_name_mn = MarkupNode::Text { - content: val_name.to_owned(), - syn_high_style: HighlightStyle::Value, - attributes: Attributes::default(), - parent_id_opt: None, - newlines_at_end: 0, - }; - - let val_name_mn_id = add_node(val_name_mn, ast_node_id, mark_node_pool, mark_id_ast_id_map); - - let equals_mn_id = add_node( - new_equals_mn(), - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ); - - Ok(new_assign_mn( - val_name_mn_id, - equals_mn_id, - expr_mark_node_id, - )) -} - -pub fn tld_w_comments_mark_node( - comments: String, - def_mark_node_id: MarkNodeId, - ast_node_id: ASTNodeId, - mark_node_pool: &mut SlowPool, - mark_id_ast_id_map: &mut MarkIdAstIdMap, - comments_before: bool, -) -> ASTResult { - let comment_mn_id = add_node( - new_comments_mn(comments, 1), - ast_node_id, - mark_node_pool, - mark_id_ast_id_map, - ); - - let children_ids = if comments_before { - vec![comment_mn_id, def_mark_node_id] - } else { - vec![def_mark_node_id, comment_mn_id] - }; - - let tld_w_comment_node = MarkupNode::Nested { - children_ids, - parent_id_opt: None, - newlines_at_end: 2, - }; - - Ok(tld_w_comment_node) -} diff --git a/crates/code_markup/src/markup_error.rs b/crates/code_markup/src/markup_error.rs deleted file mode 100644 index 80abf3d004..0000000000 --- a/crates/code_markup/src/markup_error.rs +++ /dev/null @@ -1,67 +0,0 @@ -use roc_error_utils::UtilError; -use snafu::{Backtrace, NoneError, ResultExt, Snafu}; - -use crate::slow_pool::MarkNodeId; - -#[derive(Debug, Snafu)] -#[snafu(visibility(pub))] -pub enum MarkError { - #[snafu(display( - "CaretNotFound: No carets were found in the expected node with id {}", - node_id - ))] - CaretNotFound { - node_id: MarkNodeId, - backtrace: Backtrace, - }, - #[snafu(display( - "ExpectedTextNode: the function {} expected a Text node, got {} instead.", - function_name, - node_type - ))] - ExpectedTextNode { - function_name: String, - node_type: String, - backtrace: Backtrace, - }, - #[snafu(display( - "MarkNodeIdWithoutCorrespondingASTNodeId: MarkupNode with id {} was not found in MarkIdAstIdMap, available keys are: {}.", - node_id, - keys_str - ))] - MarkNodeIdWithoutCorrespondingASTNodeId { - node_id: MarkNodeId, - keys_str: String, - backtrace: Backtrace, - }, - #[snafu(display("NestedNodeMissingChild: expected to find child with id {} in Nested MarkupNode, but it was missing. Id's of the children are {:?}.", node_id, children_ids))] - NestedNodeMissingChild { - node_id: MarkNodeId, - children_ids: Vec, - backtrace: Backtrace, - }, - #[snafu(display( - "NestedNodeRequired: required a Nested node at this position, node was a {}.", - node_type - ))] - NestedNodeRequired { - node_type: String, - backtrace: Backtrace, - }, - #[snafu(display("UtilError: {}", msg))] - UtilErrorBacktrace { msg: String, backtrace: Backtrace }, -} - -pub type MarkResult = std::result::Result; - -impl From for MarkError { - fn from(util_err: UtilError) -> Self { - let msg = format!("{util_err}"); - - // hack to handle MarkError derive - let dummy_res: Result<(), NoneError> = Err(NoneError {}); - dummy_res - .context(UtilErrorBacktraceSnafu { msg }) - .unwrap_err() - } -} diff --git a/crates/code_markup/src/slow_pool.rs b/crates/code_markup/src/slow_pool.rs deleted file mode 100644 index a5526eca13..0000000000 --- a/crates/code_markup/src/slow_pool.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::markup::{mark_id_ast_id_map::MarkIdAstIdMap, nodes::MarkupNode}; -use std::fmt::Write; - -pub type MarkNodeId = usize; - -#[derive(Debug, Default)] -pub struct SlowPool { - nodes: Vec, -} - -impl SlowPool { - pub fn add(&mut self, node: MarkupNode) -> MarkNodeId { - let id = self.nodes.len(); - - self.nodes.push(node); - - id - } - - pub fn get(&self, node_id: MarkNodeId) -> &MarkupNode { - // unwrap because Pool doesn't return Result either - self.nodes.get(node_id).unwrap() - } - - pub fn get_mut(&mut self, node_id: MarkNodeId) -> &mut MarkupNode { - // unwrap because Pool doesn't return Result either - self.nodes.get_mut(node_id).unwrap() - } - - pub fn replace_node(&mut self, node_id: MarkNodeId, new_node: MarkupNode) { - self.nodes[node_id] = new_node; - - // TODO delete children of old node, this requires SlowPool to be changed to - // make sure the indexes still make sense after removal/compaction - } - - pub fn debug_string(&self, mark_id_ast_id_map: &MarkIdAstIdMap) -> String { - let mut ret_str = String::new(); - - for (mark_node_id, node) in self.nodes.iter().enumerate() { - let ast_node_id_str = match mark_id_ast_id_map.get(mark_node_id) { - Ok(ast_id) => format!("{ast_id:?}"), - Err(err) => format!("{err:?}"), - }; - let ast_node_id: String = ast_node_id_str - .chars() - .filter(|c| c.is_ascii_digit()) - .collect(); - - let mut child_str = String::new(); - - let node_children = node.get_children_ids(); - - if !node_children.is_empty() { - child_str = format!("children: {node_children:?}"); - } - - write!( - ret_str, - "{}: {} ({}) ast_id {:?} {}", - mark_node_id, - node.node_type_as_string(), - node.get_content(), - ast_node_id.parse::().unwrap(), - child_str - ) - .unwrap(); - } - - ret_str - } -} diff --git a/crates/code_markup/src/syntax_highlight.rs b/crates/code_markup/src/syntax_highlight.rs deleted file mode 100644 index 9f728a7913..0000000000 --- a/crates/code_markup/src/syntax_highlight.rs +++ /dev/null @@ -1,60 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -use crate::colors::{from_hsb, RgbaTup}; - -#[derive(Hash, Eq, PartialEq, Copy, Clone, Debug, Deserialize, Serialize)] -pub enum HighlightStyle { - Operator, // =+-<>... - String, - FunctionName, - FunctionArgName, - Type, - Bracket, - Number, - PackageRelated, // app, packages, imports, exposes, provides... - Value, - RecordField, - Import, - Provides, - Blank, - Comment, - DocsComment, - UppercaseIdent, - LowercaseIdent, // TODO we probably don't want all lowercase identifiers to have the same color? - Keyword, // if, else, when... -} - -pub fn default_highlight_map() -> HashMap { - use HighlightStyle::*; - - let almost_white = from_hsb(258, 5, 95); - - let mut highlight_map = HashMap::new(); - [ - (Operator, from_hsb(185, 50, 75)), - (String, from_hsb(346, 65, 97)), - (FunctionName, almost_white), - (FunctionArgName, from_hsb(225, 50, 100)), - (Type, almost_white), - (Bracket, from_hsb(347, 80, 100)), - (Number, from_hsb(225, 50, 100)), - (PackageRelated, almost_white), - (Value, almost_white), - (RecordField, from_hsb(258, 50, 90)), - (Import, from_hsb(225, 50, 100)), - (Provides, from_hsb(225, 50, 100)), - (Blank, from_hsb(258, 50, 90)), - (Comment, from_hsb(258, 50, 90)), // TODO check color - (DocsComment, from_hsb(258, 50, 90)), // TODO check color - (UppercaseIdent, almost_white), - (LowercaseIdent, from_hsb(225, 50, 100)), - (Keyword, almost_white), - ] - .iter() - .for_each(|tup| { - highlight_map.insert(tup.0, tup.1); - }); - - highlight_map -} diff --git a/crates/code_markup/src/underline_style.rs b/crates/code_markup/src/underline_style.rs deleted file mode 100644 index 4eb81ee73c..0000000000 --- a/crates/code_markup/src/underline_style.rs +++ /dev/null @@ -1,20 +0,0 @@ -use std::collections::HashMap; - -use serde::{Deserialize, Serialize}; - -use crate::colors::{from_hsb, RgbaTup}; - -#[derive(Hash, Eq, PartialEq, Copy, Clone, Debug, Deserialize, Serialize)] -pub enum UnderlineStyle { - Error, - Warning, -} - -pub fn default_underline_color_map() -> HashMap { - let mut underline_colors = HashMap::new(); - - underline_colors.insert(UnderlineStyle::Error, from_hsb(0, 50, 75)); - underline_colors.insert(UnderlineStyle::Warning, from_hsb(60, 50, 75)); - - underline_colors -} diff --git a/crates/docs/Cargo.toml b/crates/docs/Cargo.toml index 85721ed2cc..06e3fc69d2 100644 --- a/crates/docs/Cargo.toml +++ b/crates/docs/Cargo.toml @@ -8,10 +8,8 @@ license.workspace = true version.workspace = true [dependencies] -roc_ast = { path = "../ast" } roc_builtins = { path = "../compiler/builtins" } roc_can = { path = "../compiler/can" } -roc_code_markup = { path = "../code_markup" } roc_collections = { path = "../compiler/collections" } roc_highlight = { path = "../highlight" } roc_load = { path = "../compiler/load" } diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml deleted file mode 100644 index b4fc930952..0000000000 --- a/crates/editor/Cargo.toml +++ /dev/null @@ -1,58 +0,0 @@ -[package] -name = "roc_editor" -description = "An editor for Roc" - -authors.workspace = true -edition.workspace = true -license.workspace = true -version.workspace = true - -[features] -default = [] - -[dependencies] -roc_ast = { path = "../ast" } -roc_builtins = { path = "../compiler/builtins" } -roc_can = { path = "../compiler/can" } -roc_code_markup = { path = "../code_markup" } -roc_collections = { path = "../compiler/collections" } -roc_command_utils = { path = "../utils/command" } -roc_load = { path = "../compiler/load" } -roc_module = { path = "../compiler/module" } -roc_packaging = { path = "../packaging" } -roc_parse = { path = "../compiler/parse" } -roc_problem = { path = "../compiler/problem" } -roc_region = { path = "../compiler/region" } -roc_reporting = { path = "../reporting" } -roc_solve = { path = "../compiler/solve" } -roc_types = { path = "../compiler/types" } -roc_unify = { path = "../compiler/unify" } -ven_graph = { path = "../vendor/pathfinding" } - -arrayvec.workspace = true -bumpalo.workspace = true -bytemuck.workspace = true -cgmath.workspace = true -colored.workspace = true -copypasta.workspace = true -fs_extra.workspace = true -futures.workspace = true -glyph_brush.workspace = true -libc.workspace = true -log.workspace = true -nonempty.workspace = true -page_size.workspace = true -palette.workspace = true -pest.workspace = true -pest_derive.workspace = true -serde.workspace = true -snafu.workspace = true -threadpool.workspace = true -wgpu.workspace = true -wgpu_glyph.workspace = true -winit.workspace = true - -[dev-dependencies] -rand.workspace = true -tempfile.workspace = true -uuid.workspace = true diff --git a/crates/editor/README.md b/crates/editor/README.md deleted file mode 100644 index 58ce9aeab0..0000000000 --- a/crates/editor/README.md +++ /dev/null @@ -1,78 +0,0 @@ -# :construction: Work In Progress :construction: - -The editor is a work in progress, only a limited subset of Roc expressions are currently supported. - -Unlike most editors, we use projectional or structural editing to edit the [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) directly. This will allow for cool features like excellent auto-complete, refactoring and never needing to format your code. - -## Getting started - -- Install the compiler, see [here](../../BUILDING_FROM_SOURCE.md). -- Run the following from the roc folder: - -```sh -cargo run edit -``` - -## Troubleshooting - -If you encounter problems with integrated graphics hardware, install `mesa-vulkan-drivers` and `vulkan-tools`. - -If you encounter an error like `gfx_backend_vulkan ... Failed to detect any valid GPUs in the current config ...` make sure the correct graphics card drivers are installed. On ubuntu `sudo ubuntu-drivers autoinstall` can resolve the problem. -If the error persists, take a look [here](https://www.techpowerup.com/gpu-specs/) to see if your GPU supports vulkan. -Use of OpenGL instead of vulkan should be available in several months. - -Make sure to [create an issue](https://github.com/roc-lang/roc/issues/new/choose) if you encounter any problems not listed above. - -## Inspiration - -We thank the following open source projects in particular for inspiring us when designing the Roc editor: - -- [learn-wgpu](https://github.com/sotrh/learn-wgpu) -- [rgx](https://github.com/cloudhead/rgx) -- [elm-editor](https://github.com/jxxcarlson/elm-editor) -- [iced](https://github.com/hecrj/iced) - -## How does it work? - -To take a look behind the scenes, open the editor with `./roc edit` or `cargo run edit` and press F11. -This debug view shows important data structures that can be found in `editor/src/editor/mvc/ed_model.rs`. -Add or delete some code to see how these data structures are updated. - -From roc to render: - -- `./roc edit` or `cargo run edit` is first handled in `cli/src/main.rs`, from there the editor's launch function is called (`editor/src/editor/main.rs`). -- In `run_event_loop` we initialize the winit window, wgpu, the editor's model(`EdModel`) and start the rendering loop. -- The `ed_model` is filled in part with data obtained by loading and typechecking the roc file with the same function (`load_and_typecheck`) that is used by the compiler. -- `ed_model` also contains an `EdModule`, which holds the parsed abstract syntax tree (AST). -- In the `init_model` function: - - The AST is converted into a tree of `MarkupNode`. The different types of `MarkupNode` are similar to the elements/nodes in HTML. A line of roc code is represented as a nested `MarkupNode` containing mostly text `MarkupNode`s. The line `foo = "bar"` is represented as - three text `MarkupNode`; representing `foo`, ` = ` and `bar`. Multiple lines of roc code are represented as nested `MarkupNode` that contain other nested `MarkupNode`. - - `CodeLines` holds a `Vec` of `String`, each line of code is a `String`. When saving the file, the content of `CodeLines` is written to disk. - - `GridNodeMap` maps every position of a char of roc code to a `MarkNodeId`, for easy interaction with the caret. -- Back in `editor/src/editor/main.rs` we convert the `EdModel` to `RenderedWgpu` by calling `model_to_wgpu`. -- The `RenderedWgpu` is passed to the `glyph_brush` to draw the characters(glyphs) on the screen. - -### Important files - -To understand how the editor works it is useful to know the most important files: - -- editor/src/editor/main.rs -- editor/src/editor/mvc/ed_update.rs -- editor/src/editor/mvc/ed_model.rs -- editor/src/editor/mvc/ed_view.rs -- editor/src/editor/render_ast.rs -- editor/src/editor/render_debug.rs - -Important folders/files outside the editor folder: - -- code_markup/src/markup/convert -- code_markup/src/markup/nodes.rs -- ast/src/lang/core/def -- ast/src/lang/core/expr -- ast/src/lang/core/ast.rs -- ast/src/lang/env.rs - -## Contributing - -We welcome new contributors :heart: and are happy to help you get started. -Check [CONTRIBUTING.md](../../CONTRIBUTING.md) for more info. diff --git a/crates/editor/creature.png b/crates/editor/creature.png deleted file mode 100644 index f0fd87e6b0..0000000000 Binary files a/crates/editor/creature.png and /dev/null differ diff --git a/crates/editor/snippet-ideas.md b/crates/editor/snippet-ideas.md deleted file mode 100644 index 044ab36c2d..0000000000 --- a/crates/editor/snippet-ideas.md +++ /dev/null @@ -1,115 +0,0 @@ -# Snippet ideas - -I think snippet insertion would make for an awesome demo that shows off the potential of the editor and a basic version would not be that difficult to implement. -With snippet insertion I mean the following: - -say you have a list of Person records, people in scope -you press some keyboard shortcut -a text field pops up -you enter "sort" -we show autocomplete options like sort people by firstName, sort people by lastName and sort people by age. The recommendations can be that good because we know people is the only list in scope and we know which fields are in the Person record. -you navigate to sort people by age, press enter and we show in the autocomplete popup: sort people by age descending and sort people by age ascending. -you navigate to sort people by age ascending and press Enter -The correct Roc code is inserted -This is most useful for beginning Roc programmers, but I could see it saving time for experts as well. -Novice to expert programmers who are new to Roc can also perfectly describe what they want to happen but may not know the correct syntax, names of builtin functions... -Other useful snippet commands for beginning Roc programmers might be empty dict, lambda function, split strVal into chars... -Some more advanced snippets: post jsonVal to urlVal, connect to amazon S3, insert function of common algorithm like: sieve of erathostenes, greatest common divider... - -This could remove the need for a lot of googling/stackoverflow, creating a delightful experience that sets us apart from other editors. -And contrary to stackoverflow/github copilot, snippets will be written by Roc experts or be easily editable by us. They can also be guaranteed to work for a specific Roc and library version because we update, version, and test them. - -A nice goal to aim for is that the user never needs/wants to leave the editor to look things up. -We have way more context inside the editor so we should be able to do better than any general-purpose search engine. - -I think the snippet insertion commands also set us up for quality interaction with users using voice input. - -The CC0 license seems like a good fit for the snippets. - -Fuzzy matching should be done to suggest a closest fuzzy match, so if the user types the snippet command `empty Map`, we should suggest `empty Dict`. - -## Pure Text Snippets - -Pure text snippets are not templates and do not contain typed holes. -Fish hooks are used when subvariants should be created e.g.: means this pure text snippets should be created for all Roc collections such as Dict, Set, List... - -- command: empty - - example: empty dict >> `{::}` -- command: - - example: sieve of erathostenes >> `inserts function for sieve of erathostenes` - - common algorithms: sieve of erathostenes, greatest common divisor, prime factorisation, A* path finding, Dijkstra's algorithm, Breadth First Search... -- command: current date/datetime - - example: current datetime >> `now <- Time.now\n` -- command: list range 1 to 5 - - example: [1, 2, 3, 4, 5] -- command: use commandline args -- command: post/get/put request -- command: extract float(s)/number/emal addresses from string. regex match float/number/email address/... -- command: execute (bash) command/script -- command: cast/convert/parse list of x to list of y -- command: pattern match/ match/ switch/ case - -## AST aware snippets - -Snippets are inserted based on type of value on which the cursor is located. - -- command: - - example: - - We have the cursor like this `people|` - - User presses snippet shortcut or dot key - - We show a list with all builtin functions for the List type - - User chooses contains - - We change code to `List.contains people |Blank` -- command: Str to chars/charlist - -## Snippets with Typed Holes - -- command: sort ^List *^ (by ^Record Field^) {ascending/descending} - - example: sort people by age descending >> ... -- command: escape url - - example: >> `percEncodedString = Url.percentEncode ^String^` -- command: list files in directory - - example: >> - - ``` - path <- File.pathFromStr ^String^ - dirContents <- File.enumerateDir path - ``` - -- command: remove/create file -- command: read/write from file -- command: concatenate strings -- command: trim (newlines) at end/start/right/left -- command: evaluate predicate for all in slice/list/array -- command: get element at index -- command: get char at index -- command: reverse stirng -- command: lambda/anonymous function -- we should auto create type hole commands for all builtins. - - example: List has builtins reverse, repeat, len... generated snippet commands should be: - - reverse list > List.reverse ^List *^ - - repeat list > List.repeat ^elem^ ^Nat^ - - len list (fuzzy matches should be length of list) -- append element to list - -## fuzzy matching - - some pairs for fuzzy matching unit tests: - -- hashmap > Dict -- map > map (function), Dict -- for > map, mapWithIndex, walk, walkBackwards, zip -- apply/for yield > map -- fold > walk, walkBackwards -- foldl > walkBackwards -- foldr > walk -- head > takeFirst -- filter > keepIf - -## Inspiration - -- [grepper](https://www.codegrepper.com/) snippet collection that embeds in google search results. See also this [collection of common questions](https://www.codegrepper.com/code-examples/rust). -- [github copilot](https://copilot.github.com/) snippet generation with machine learning -- [stackoverflow](https://stackoverflow.com) -- [rosetta code](http://www.rosettacode.org/wiki/Rosetta_Code) snippets in many different programming languages. Many [snippets](https://www.rosettacode.org/wiki/Category:Programming_Tasks) are programming contest style problems, but there also problems that demonstrate the use of JSON, SHA-256, read a file line by line... -- check docs of popular languages to cross reference function/snippet names for fuzzy matching diff --git a/crates/editor/src/editor/code_lines.rs b/crates/editor/src/editor/code_lines.rs deleted file mode 100644 index 38e6aac66d..0000000000 --- a/crates/editor/src/editor/code_lines.rs +++ /dev/null @@ -1,82 +0,0 @@ -use crate::ui::text::lines::Lines; -use crate::ui::text::text_pos::TextPos; -use crate::ui::ui_error::UIResult; -use crate::ui::util::slice_get; -use std::fmt; - -#[derive(Debug, Default)] -pub struct CodeLines { - pub lines: Vec, - pub nr_of_chars: usize, -} - -impl CodeLines { - pub fn from_str(code_str: &str) -> CodeLines { - CodeLines { - lines: code_str.split('\n').map(|s| s.to_owned()).collect(), - nr_of_chars: code_str.len(), - } - } - - // last column of last line - pub fn end_txt_pos(&self) -> TextPos { - let last_line_nr = self.nr_of_lines() - 1; - - TextPos { - line: last_line_nr, - column: self.line_len(last_line_nr).unwrap(), // safe because we just calculated last_line - } - } -} - -impl Lines for CodeLines { - fn get_line_ref(&self, line_nr: usize) -> UIResult<&str> { - let line_string = slice_get(line_nr, &self.lines)?; - - Ok(line_string) - } - - fn line_len(&self, line_nr: usize) -> UIResult { - self.get_line_ref(line_nr).map(|line| line.len()) - } - - fn nr_of_lines(&self) -> usize { - self.lines.len() - } - - fn nr_of_chars(&self) -> usize { - self.nr_of_chars - } - - fn all_lines_as_string(&self) -> String { - self.lines.join("\n") - } - - fn is_last_line(&self, line_nr: usize) -> bool { - line_nr == self.nr_of_lines() - 1 - } - - fn last_char(&self, line_nr: usize) -> UIResult> { - Ok(self.get_line_ref(line_nr)?.chars().last()) - } -} - -impl fmt::Display for CodeLines { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for row in &self.lines { - let row_str = row - .chars() - .map(|code_char| format!("{code_char}")) - .collect::>() - .join(" "); - - let escaped_row_str = row_str.replace('\n', "\\n"); - - write!(f, "\n{escaped_row_str}")?; - } - - writeln!(f, " (code_lines, {:?} lines)", self.lines.len())?; - - Ok(()) - } -} diff --git a/crates/editor/src/editor/config.rs b/crates/editor/src/editor/config.rs deleted file mode 100644 index 8a16e3e70e..0000000000 --- a/crates/editor/src/editor/config.rs +++ /dev/null @@ -1,31 +0,0 @@ -use serde::{Deserialize, Serialize}; - -use crate::editor::theme::EdTheme; - -use super::resources::strings::START_TIP; - -#[derive(Serialize, Deserialize)] -pub struct Config { - pub code_font_size: f32, - pub debug_font_size: f32, - pub ed_theme: EdTheme, -} - -impl Default for Config { - fn default() -> Self { - Self { - code_font_size: 30.0, - debug_font_size: 20.0, - ed_theme: EdTheme::default(), - } - } -} - -impl Config { - pub fn make_code_txt_xy(&self) -> (f32, f32) { - ( - self.code_font_size, - (START_TIP.matches('\n').count() as f32 + 2.0) * self.code_font_size, - ) - } -} diff --git a/crates/editor/src/editor/ed_error.rs b/crates/editor/src/editor/ed_error.rs deleted file mode 100644 index 732b7873fc..0000000000 --- a/crates/editor/src/editor/ed_error.rs +++ /dev/null @@ -1,365 +0,0 @@ -use crate::ui::text::text_pos::TextPos; -use colored::*; -use roc_ast::ast_error::ASTError; -use roc_ast::lang::core::ast::ASTNodeId; -use roc_code_markup::markup_error::MarkError; -use roc_code_markup::slow_pool::MarkNodeId; -use roc_module::module_err::ModuleError; -use snafu::{Backtrace, ErrorCompat, Snafu}; - -//import errors as follows: -// `use crate::error::OutOfBounds;` -// *not* `use crate::error::EdError::OutOfBounds;` -// see https://github.com/shepmaster/snafu/issues/211 - -#[derive(Debug, Snafu)] -#[snafu(visibility(pub))] -pub enum EdError { - #[snafu(display( - "ASTNodeIdWithoutDefId: The expr_id_opt in ASTNode({:?}) was `None` but I was expecting `Some(DefId)` .", - ast_node_id - ))] - ASTNodeIdWithoutDefId { - ast_node_id: ASTNodeId, - backtrace: Backtrace, - }, - - #[snafu(display( - "ASTNodeIdWithoutExprId: The expr_id_opt in ASTNode({:?}) was `None` but I was expecting `Some(ExprId)` .", - ast_node_id - ))] - ASTNodeIdWithoutExprId { - ast_node_id: ASTNodeId, - backtrace: Backtrace, - }, - - #[snafu(display( - "CaretNotFound: No carets were found in the expected node with id {}", - node_id - ))] - CaretNotFound { - node_id: MarkNodeId, - backtrace: Backtrace, - }, - - #[snafu(display("ClipboardReadFailed: could not get clipboard contents: {}", err_msg))] - ClipboardReadFailed { - err_msg: String, - }, - - #[snafu(display("ClipboardWriteFailed: could not set clipboard contents: {}", err_msg))] - ClipboardWriteFailed { - err_msg: String, - }, - - #[snafu(display( - "ClipboardInitFailed: could not initialize ClipboardContext: {}.", - err_msg - ))] - ClipboardInitFailed { - err_msg: String, - }, - - #[snafu(display( - "ExpectedTextNode: the function {} expected a Text node, got {} instead.", - function_name, - node_type - ))] - ExpectedTextNode { - function_name: String, - node_type: String, - backtrace: Backtrace, - }, - - #[snafu(display( - "EmptyCodeString: I need to have a code string (code_str) that contains either an app, interface, package, or platform header. The code string was empty.", - ))] - EmptyCodeString { - backtrace: Backtrace, - }, - - #[snafu(display("FailedToUpdateIdentIdName: {}", err_str))] - FailedToUpdateIdentIdName { - err_str: String, - backtrace: Backtrace, - }, - - #[snafu(display("GetContentOnNestedNode: tried to get string content from Nested MarkupNode. Can only get content from Text or Blank nodes."))] - GetContentOnNestedNode { - backtrace: Backtrace, - }, - - #[snafu(display( - "IndexOfFailed: Element {} was not found in collection {}.", - elt_str, - collection_str - ))] - IndexOfFailed { - elt_str: String, - collection_str: String, - backtrace: Backtrace, - }, - - #[snafu(display("KeyNotFound: key {} was not found in HashMap.", key_str,))] - KeyNotFound { - key_str: String, - backtrace: Backtrace, - }, - - #[snafu(display( - "MissingParent: MarkupNode with id {} should have a parent but there was none.", - node_id - ))] - MissingParent { - node_id: MarkNodeId, - backtrace: Backtrace, - }, - - #[snafu(display( - "MissingSelection: ed_model.selected_expr2_id was Some(ExprId) but ed_model.caret_w_sel_vec did not contain any Some(Selection)." - ))] - MissingSelection { - backtrace: Backtrace, - }, - - #[snafu(display("NestedNodeMissingChild: expected to find child with id {} in Nested MarkupNode, but it was missing. Id's of the children are {:?}.", node_id, children_ids))] - NestedNodeMissingChild { - node_id: MarkNodeId, - children_ids: Vec, - backtrace: Backtrace, - }, - - #[snafu(display( - "NestedNodeRequired: required a Nested node at this position, node was a {}.", - node_type - ))] - NestedNodeRequired { - node_type: String, - backtrace: Backtrace, - }, - - #[snafu(display("NestedNodeWithoutChildren: tried to retrieve child from Nested MarkupNode with id {} but it had no children.", node_id))] - NestedNodeWithoutChildren { - node_id: MarkNodeId, - backtrace: Backtrace, - }, - - #[snafu(display("NoDefMarkNodeBeforeLineNr: I could not find a MarkupNode whose root parent points to a DefId located before the given line number: {}.", line_nr))] - NoDefMarkNodeBeforeLineNr { - line_nr: usize, - backtrace: Backtrace, - }, - - #[snafu(display("NodeWithoutAttributes: expected to have a node with attributes. This is a Nested MarkupNode, only Text and Blank nodes have attributes."))] - NodeWithoutAttributes { - backtrace: Backtrace, - }, - - #[snafu(display( - "NodeIdNotInGridNodeMap: MarkNodeId {} was not found in ed_model.grid_node_map.", - node_id - ))] - NodeIdNotInGridNodeMap { - node_id: MarkNodeId, - backtrace: Backtrace, - }, - - #[snafu(display( - "NoNodeAtCaretPosition: there was no node at the current caret position {:?}.", - caret_pos, - ))] - NoNodeAtCaretPosition { - caret_pos: TextPos, - backtrace: Backtrace, - }, - - #[snafu(display( - "OutOfBounds: index {} was out of bounds for {} with length {}.", - index, - collection_name, - len - ))] - OutOfBounds { - index: usize, - collection_name: String, - len: usize, - backtrace: Backtrace, - }, - - #[snafu(display("RecordWithoutFields: expected record to have at least one field because it is not an EmptyRecord."))] - RecordWithoutFields { - backtrace: Backtrace, - }, - - #[snafu(display( - "RocCheckFailed: `cargo run check`/`roc check` detected errors(see terminal)." - ))] - RocCheckFailed, - - #[snafu(display("ParseError: Failed to parse AST: SyntaxError: {}.", syntax_err))] - SrcParseError { - syntax_err: String, - backtrace: Backtrace, - }, - - #[snafu(display("StringParseError: {}", msg))] - StringParseError { - msg: String, - backtrace: Backtrace, - }, - - #[snafu(display( - "UnexpectedASTNode: required a {} at this position, node was a {}.", - required_node_type, - encountered_node_type - ))] - UnexpectedASTNode { - required_node_type: String, - encountered_node_type: String, - backtrace: Backtrace, - }, - - #[snafu(display( - "UnexpectedEmptyPoolVec: expected PoolVec {} to have at least one element.", - descriptive_vec_name - ))] - UnexpectedEmptyPoolVec { - descriptive_vec_name: String, - backtrace: Backtrace, - }, - - #[snafu(display( - "UnexpectedPattern2Variant: required a {} at this position, Pattern2 was a {}.", - required_pattern2, - encountered_pattern2, - ))] - UnexpectedPattern2Variant { - required_pattern2: String, - encountered_pattern2: String, - backtrace: Backtrace, - }, - - #[snafu(display("ASTError: {}", msg))] - ASTErrorBacktrace { - msg: String, - backtrace: Backtrace, - }, - #[snafu(display("UIError: {}", msg))] - UIErrorBacktrace { - msg: String, - backtrace: Backtrace, - }, - #[snafu(display("MarkError: {}", msg))] - MarkErrorBacktrace { - msg: String, - backtrace: Backtrace, - }, - WrapASTError { - #[snafu(backtrace)] - source: ASTError, - }, - WrapUIError { - #[snafu(backtrace)] - source: UIError, - }, - WrapMarkError { - #[snafu(backtrace)] - source: MarkError, - }, - WrapModuleError { - #[snafu(backtrace)] - source: ModuleError, - }, - WrapIoError { - source: std::io::Error, - }, -} - -pub type EdResult = std::result::Result; - -pub fn print_err(err: &EdError) { - eprintln!("{}", format!("{err}").truecolor(255, 0, 0)); - - if let Some(backtrace) = ErrorCompat::backtrace(err) { - eprintln!("{}", color_backtrace(backtrace)); - } -} - -fn color_backtrace(backtrace: &snafu::Backtrace) -> String { - let backtrace_str = format!("{backtrace}"); - let backtrace_split = backtrace_str.split('\n'); - let irrelevant_src = vec![".cargo", "registry", ".rustup", "rustc"]; - - let mut ret_str = String::new(); - let mut prev_line_opt: Option = None; - - for line in backtrace_split { - let new_line = if line.contains("src") { - if !contains_one_of(line, &irrelevant_src) { - if let Some(prev_line) = prev_line_opt { - prev_line_opt = Some(format!("{}", prev_line.truecolor(255, 30, 30))); - } - format!("{}\n", line.truecolor(255, 100, 100)) - } else { - format!("{line}\n") - } - } else { - format!("{line}\n") - }; - - if let Some(prev_line) = prev_line_opt { - ret_str.push_str(&prev_line); - } - prev_line_opt = Some(new_line); - } - - ret_str -} - -fn contains_one_of(main_str: &str, contain_slice: &[&str]) -> bool { - for contain_str in contain_slice { - if main_str.contains(contain_str) { - return true; - } - } - - false -} - -impl From for String { - fn from(ed_error: EdError) -> Self { - format!("{ed_error}") - } -} - -use crate::ui::ui_error::UIError; - -impl From for EdError { - fn from(ui_err: UIError) -> Self { - Self::WrapUIError { source: ui_err } - } -} - -impl From for EdError { - fn from(mark_err: MarkError) -> Self { - Self::WrapMarkError { source: mark_err } - } -} - -impl From for EdError { - fn from(ast_err: ASTError) -> Self { - Self::WrapASTError { source: ast_err } - } -} - -impl From for EdError { - fn from(module_err: ModuleError) -> Self { - Self::WrapModuleError { source: module_err } - } -} - -impl From for EdError { - fn from(io_err: std::io::Error) -> Self { - Self::WrapIoError { source: io_err } - } -} diff --git a/crates/editor/src/editor/grid_node_map.rs b/crates/editor/src/editor/grid_node_map.rs deleted file mode 100644 index 87907276dc..0000000000 --- a/crates/editor/src/editor/grid_node_map.rs +++ /dev/null @@ -1,443 +0,0 @@ -use crate::editor::ed_error::EdResult; -use crate::editor::ed_error::NestedNodeWithoutChildrenSnafu; -use crate::editor::ed_error::{NoDefMarkNodeBeforeLineNrSnafu, NodeIdNotInGridNodeMapSnafu}; -use crate::editor::mvc::ed_model::EdModel; -use crate::editor::util::first_last_index_of; -use crate::editor::util::index_of; -use crate::ui::text::selection::Selection; -use crate::ui::text::text_pos::TextPos; -use crate::ui::ui_error::{LineInsertionFailedSnafu, OutOfBoundsSnafu, UIResult}; -use crate::ui::util::{slice_get, slice_get_mut}; -use roc_ast::lang::core::ast::ASTNodeId; -use roc_code_markup::markup::mark_id_ast_id_map::MarkIdAstIdMap; -use roc_code_markup::markup::nodes::get_root_mark_node_id; -use roc_code_markup::slow_pool::MarkNodeId; -use roc_code_markup::slow_pool::SlowPool; -use snafu::OptionExt; -use std::cmp::Ordering; -use std::fmt; - -#[derive(Debug)] -pub struct GridNodeMap { - pub lines: Vec>, -} - -impl GridNodeMap { - pub fn insert_between_line( - &mut self, - line_nr: usize, - index: usize, - len: usize, - node_id: MarkNodeId, - ) -> UIResult<()> { - let nr_of_lines = self.lines.len(); - - if line_nr < nr_of_lines { - let line_ref = slice_get_mut(line_nr, &mut self.lines)?; - let new_cols_vec: Vec = std::iter::repeat(node_id).take(len).collect(); - - line_ref.splice(index..index, new_cols_vec); - } else if line_nr >= nr_of_lines { - for _ in 0..((line_nr - nr_of_lines) + 1) { - self.push_empty_line(); - } - - self.insert_between_line(line_nr, index, len, node_id)?; - } else { - LineInsertionFailedSnafu { - line_nr, - nr_of_lines, - } - .fail()?; - } - - Ok(()) - } - - pub fn insert_empty_line(&mut self, line_nr: usize) -> UIResult<()> { - if line_nr <= self.lines.len() { - self.lines.insert(line_nr, Vec::new()); - - Ok(()) - } else { - OutOfBoundsSnafu { - index: line_nr, - collection_name: "code_lines.lines".to_owned(), - len: self.lines.len(), - } - .fail() - } - } - - pub fn push_empty_line(&mut self) { - self.lines.push(vec![]); - } - - pub fn break_line(&mut self, line_nr: usize, col_nr: usize) -> UIResult<()> { - // clippy prefers this over if-else - match line_nr.cmp(&self.lines.len()) { - Ordering::Less => { - self.insert_empty_line(line_nr + 1)?; - - let line_ref = self.lines.get_mut(line_nr).unwrap(); // safe because we checked line_nr - - if col_nr < line_ref.len() { - let next_line_str: Vec = line_ref.drain(col_nr..).collect(); - - let next_line_ref = self.lines.get_mut(line_nr + 1).unwrap(); // safe because we just added the line - - *next_line_ref = next_line_str; - } - - Ok(()) - } - Ordering::Equal => self.insert_empty_line(line_nr + 1), - Ordering::Greater => OutOfBoundsSnafu { - index: line_nr, - collection_name: "grid_node_map.lines".to_owned(), - len: self.lines.len(), - } - .fail(), - } - } - - pub fn clear_line(&mut self, line_nr: usize) -> UIResult<()> { - let line_ref = slice_get_mut(line_nr, &mut self.lines)?; - - *line_ref = vec![]; - - Ok(()) - } - - pub fn del_line(&mut self, line_nr: usize) { - self.lines.remove(line_nr); - } - - pub fn del_at_line(&mut self, line_nr: usize, column: usize) -> UIResult<()> { - let line_ref = slice_get_mut(line_nr, &mut self.lines)?; - - line_ref.remove(column); - - Ok(()) - } - - pub fn del_range_at_line( - &mut self, - line_nr: usize, - col_range: std::ops::Range, - ) -> UIResult<()> { - let line_ref = slice_get_mut(line_nr, &mut self.lines)?; - - line_ref.drain(col_range); - - Ok(()) - } - - pub fn del_selection(&mut self, selection: Selection) -> UIResult<()> { - if selection.is_on_same_line() { - let line_ref = slice_get_mut(selection.start_pos.line, &mut self.lines)?; - - line_ref.drain(selection.start_pos.column..selection.end_pos.column); - } else { - unimplemented!("TODO support deleting multiline selection") - } - - Ok(()) - } - - pub fn get_id_at_row_col(&self, caret_pos: TextPos) -> UIResult { - let line = slice_get(caret_pos.line, &self.lines)?; - let node_id = slice_get(caret_pos.column, line)?; - - Ok(*node_id) - } - - pub fn get_offset_to_node_id( - &self, - caret_pos: TextPos, - node_id: MarkNodeId, - ) -> EdResult { - let line = slice_get(caret_pos.line, &self.lines)?; - - let first_node_index = index_of(node_id, line)?; - - Ok(caret_pos.column - first_node_index) - } - - pub fn node_exists_at_pos(&self, pos: TextPos) -> bool { - if pos.line < self.lines.len() { - // safe unwrap because we checked the length - let line = self.lines.get(pos.line).unwrap(); - - pos.column < line.len() - } else { - false - } - } - - // get position of first occurrence of node_id if get_first_pos, else get the last occurrence - pub fn get_node_position(&self, node_id: MarkNodeId, get_first_pos: bool) -> EdResult { - let mut last_pos_opt = None; - - for (line_index, line) in self.lines.iter().enumerate() { - for (col_index, iter_node_id) in line.iter().enumerate() { - if node_id == *iter_node_id && get_first_pos { - return Ok(TextPos { - line: line_index, - column: col_index, - }); - } else if node_id == *iter_node_id { - last_pos_opt = Some(TextPos { - line: line_index, - column: col_index, - }) - } else if let Some(last_pos) = last_pos_opt { - return Ok(last_pos); - } - } - } - - if let Some(last_pos) = last_pos_opt { - Ok(last_pos) - } else { - NodeIdNotInGridNodeMapSnafu { node_id }.fail() - } - } - - // returns start and end pos of Expr2/Def2, relevant AST node and MarkNodeId of the corresponding MarkupNode - pub fn get_block_start_end_pos( - &self, - caret_pos: TextPos, - ed_model: &EdModel, - ) -> EdResult<(TextPos, TextPos, ASTNodeId, MarkNodeId)> { - let line = slice_get(caret_pos.line, &self.lines)?; - let node_id = *slice_get(caret_pos.column, line)?; - let node = ed_model.mark_node_pool.get(node_id); - - if node.is_nested() { - let (start_pos, end_pos) = self.get_nested_start_end_pos(node_id, ed_model)?; - - Ok(( - start_pos, - end_pos, - ed_model.mark_id_ast_id_map.get(node_id)?, - node_id, - )) - } else { - let (first_node_index, last_node_index) = first_last_index_of(node_id, line)?; - - let curr_node_id = *slice_get(first_node_index, line)?; - let curr_ast_node_id = ed_model.mark_id_ast_id_map.get(curr_node_id)?; - - let mut expr_start_index = first_node_index; - let mut expr_end_index = last_node_index; - - // we may encounter ast id's of children of the current node - let mut pos_extra_subtract = 0; - - for i in (0..first_node_index).rev() { - let prev_pos_node_id = *slice_get(i, line)?; - let prev_ast_node_id = ed_model.mark_id_ast_id_map.get(prev_pos_node_id)?; - - if prev_ast_node_id == curr_ast_node_id { - if pos_extra_subtract > 0 { - expr_start_index -= pos_extra_subtract + 1; - pos_extra_subtract = 0; - } else { - expr_start_index -= 1; - } - } else { - pos_extra_subtract += 1; - } - } - - // we may encounter ast id's of children of the current node - let mut pos_extra_add = 0; - - for i in last_node_index..line.len() { - let next_pos_node_id = slice_get(i, line)?; - let next_ast_node_id = ed_model.mark_id_ast_id_map.get(*next_pos_node_id)?; - - if next_ast_node_id == curr_ast_node_id { - if pos_extra_add > 0 { - expr_end_index += pos_extra_add + 1; - pos_extra_add = 0; - } else { - expr_end_index += 1; - } - } else { - pos_extra_add += 1; - } - } - - let correct_mark_node_id = GridNodeMap::get_top_node_with_expr_id( - curr_node_id, - &ed_model.mark_node_pool, - &ed_model.mark_id_ast_id_map, - )?; - - Ok(( - TextPos { - line: caret_pos.line, - column: expr_start_index, - }, - TextPos { - line: caret_pos.line, - column: expr_end_index, - }, - curr_ast_node_id, - correct_mark_node_id, - )) - } - } - - // A markup node may refer to a bracket `{`, in that case we want the parent, a Nested MarkNode. - // `{` is not the entire Expr2 - fn get_top_node_with_expr_id( - curr_node_id: MarkNodeId, - mark_node_pool: &SlowPool, - mark_id_ast_id_map: &MarkIdAstIdMap, - ) -> EdResult { - let curr_node = mark_node_pool.get(curr_node_id); - - if let Some(parent_id) = curr_node.get_parent_id_opt() { - if mark_id_ast_id_map.get(parent_id)? == mark_id_ast_id_map.get(curr_node_id)? { - Ok(parent_id) - } else { - Ok(curr_node_id) - } - } else { - Ok(curr_node_id) - } - } - - pub fn get_nested_start_end_pos( - &self, - nested_node_id: MarkNodeId, - ed_model: &EdModel, - ) -> EdResult<(TextPos, TextPos)> { - let left_most_leaf = self.get_leftmost_leaf(nested_node_id, ed_model)?; - - let right_most_leaf = self.get_rightmost_leaf(nested_node_id, ed_model)?; - - let expr_start_pos = ed_model - .grid_node_map - .get_node_position(left_most_leaf, true)?; - let expr_end_pos = ed_model - .grid_node_map - .get_node_position(right_most_leaf, false)? - .increment_col(); - - Ok((expr_start_pos, expr_end_pos)) - } - - fn get_leftmost_leaf( - &self, - nested_node_id: MarkNodeId, - ed_model: &EdModel, - ) -> EdResult { - let mut children_ids = ed_model - .mark_node_pool - .get(nested_node_id) - .get_children_ids(); - let mut first_child_id = 0; - - while !children_ids.is_empty() { - first_child_id = - *children_ids - .first() - .with_context(|| NestedNodeWithoutChildrenSnafu { - node_id: nested_node_id, - })?; - - children_ids = ed_model - .mark_node_pool - .get(first_child_id) - .get_children_ids(); - } - - Ok(first_child_id) - } - - fn get_rightmost_leaf( - &self, - nested_node_id: MarkNodeId, - ed_model: &EdModel, - ) -> EdResult { - let mut children_ids = ed_model - .mark_node_pool - .get(nested_node_id) - .get_children_ids(); - let mut last_child_id = 0; - - while !children_ids.is_empty() { - last_child_id = - *children_ids - .last() - .with_context(|| NestedNodeWithoutChildrenSnafu { - node_id: nested_node_id, - })?; - - children_ids = ed_model - .mark_node_pool - .get(last_child_id) - .get_children_ids(); - } - - Ok(last_child_id) - } - - // get id of root mark_node whose ast_node_id points to a DefId - pub fn get_def_mark_node_id_before_line( - &self, - line_nr: usize, - mark_node_pool: &SlowPool, - mark_id_ast_id_map: &MarkIdAstIdMap, - ) -> EdResult { - for curr_line_nr in (0..line_nr).rev() { - let first_col_pos = TextPos { - line: curr_line_nr, - column: 0, - }; - - if self.node_exists_at_pos(first_col_pos) { - let mark_node_id = self.get_id_at_row_col(first_col_pos)?; - let root_mark_node_id = get_root_mark_node_id(mark_node_id, mark_node_pool); - - let ast_node_id = mark_id_ast_id_map.get(root_mark_node_id)?; - - if let ASTNodeId::ADefId(_) = ast_node_id { - return Ok(root_mark_node_id); - } - } - } - - NoDefMarkNodeBeforeLineNrSnafu { line_nr }.fail() - } -} - -impl Default for GridNodeMap { - fn default() -> Self { - GridNodeMap { - lines: vec![Vec::new()], - } - } -} - -impl fmt::Display for GridNodeMap { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for row in &self.lines { - let row_str = row - .iter() - .map(|mark_node_id| format!(" {mark_node_id} ")) - .collect::>() - .join(", "); - - writeln!(f, "{row_str}")?; - } - - writeln!(f, "(grid_node_map, {:?} lines)", self.lines.len())?; - - Ok(()) - } -} diff --git a/crates/editor/src/editor/keyboard_input.rs b/crates/editor/src/editor/keyboard_input.rs deleted file mode 100644 index f1dad9a2af..0000000000 --- a/crates/editor/src/editor/keyboard_input.rs +++ /dev/null @@ -1,177 +0,0 @@ -use crate::editor::ed_error::EdResult; -use crate::editor::mvc::app_model::AppModel; -use crate::editor::mvc::app_update::{ - handle_copy, handle_cut, handle_paste, pass_keydown_to_focused, -}; -use crate::window::keyboard_input::from_winit; -use winit::event::VirtualKeyCode::*; -use winit::event::{ElementState, ModifiersState, VirtualKeyCode}; - -pub fn handle_keydown( - elem_state: ElementState, - virtual_keycode: VirtualKeyCode, - modifiers_winit: ModifiersState, - app_model: &mut AppModel, -) -> EdResult<()> { - if let ElementState::Released = elem_state { - return Ok(()); - } - - let modifiers = from_winit(&modifiers_winit); - - match virtual_keycode { - Left | Up | Right | Down => { - pass_keydown_to_focused(&modifiers, virtual_keycode, app_model)? - } - - Copy => handle_copy(app_model)?, - Paste => handle_paste(app_model)?, - Cut => handle_cut(app_model)?, - C => { - if modifiers.cmd_or_ctrl() { - handle_copy(app_model)? - } - } - V => { - if modifiers.cmd_or_ctrl() { - handle_paste(app_model)? - } - } - X => { - if modifiers.cmd_or_ctrl() { - handle_cut(app_model)? - } - } - - _ => pass_keydown_to_focused(&modifiers, virtual_keycode, app_model)?, - } - - Ok(()) -} - -// pub fn handle_text_input( -// text_state: &mut String, -// elem_state: ElementState, -// virtual_keycode: VirtualKeyCode, -// _modifiers: ModifiersState, -// ) { -// use winit::event::VirtualKeyCode::*; - -// if let ElementState::Released = elem_state { -// return; -// } - -// match virtual_keycode { -// Key1 | Numpad1 => text_state.push('1'), -// Key2 | Numpad2 => text_state.push('2'), -// Key3 | Numpad3 => text_state.push('3'), -// Key4 | Numpad4 => text_state.push('4'), -// Key5 | Numpad5 => text_state.push('5'), -// Key6 | Numpad6 => text_state.push('6'), -// Key7 | Numpad7 => text_state.push('7'), -// Key8 | Numpad8 => text_state.push('8'), -// Key9 | Numpad9 => text_state.push('9'), -// Key0 | Numpad0 => text_state.push('0'), -// A => text_state.push('a'), -// B => text_state.push('b'), -// C => text_state.push('c'), -// D => text_state.push('d'), -// E => text_state.push('e'), -// F => text_state.push('f'), -// G => text_state.push('g'), -// H => text_state.push('h'), -// I => text_state.push('i'), -// J => text_state.push('j'), -// K => text_state.push('k'), -// L => text_state.push('l'), -// M => text_state.push('m'), -// N => text_state.push('n'), -// O => text_state.push('o'), -// P => text_state.push('p'), -// Q => text_state.push('q'), -// R => text_state.push('r'), -// S => text_state.push('s'), -// T => text_state.push('t'), -// U => text_state.push('u'), -// V => text_state.push('v'), -// W => text_state.push('w'), -// X => text_state.push('x'), -// Y => text_state.push('y'), -// Z => text_state.push('z'), -// Escape | F1 | F2 | F3 | F4 | F5 | F6 | F7 | F8 | F9 | F10 | F11 | F12 | F13 | F14 | F15 -// | F16 | F17 | F18 | F19 | F20 | F21 | F22 | F23 | F24 | Snapshot | Scroll | Pause -// | Insert | Home | Delete | End | PageDown | PageUp | Left | Up | Right | Down | Compose -// | caret | Numlock | AbntC1 | AbntC2 | Ax | Calculator | Capital | Convert | Kana -// | Kanji | LAlt | LBracket | LControl | LShift | LWin | Mail | MediaSelect | PlayPause -// | Power | PrevTrack | MediaStop | Mute | MyComputer | NavigateForward -// | NavigateBackward | NextTrack | NoConvert | OEM102 | RAlt | Sysrq | RBracket -// | RControl | RShift | RWin | Sleep | Stop | Unlabeled | VolumeDown | VolumeUp | Wake -// | WebBack | WebFavorites | WebForward | WebHome | WebRefresh | WebSearch | Apps | Tab -// | WebStop => { -// // TODO handle -// } -// Back => { -// text_state.pop(); -// } -// Return | NumpadEnter => { -// text_state.push('\n'); -// } -// Space => { -// text_state.push(' '); -// } -// Comma | NumpadComma => { -// text_state.push(','); -// } -// Add => { -// text_state.push('+'); -// } -// Apostrophe => { -// text_state.push('\''); -// } -// At => { -// text_state.push('@'); -// } -// Backslash => { -// text_state.push('\\'); -// } -// Colon => { -// text_state.push(':'); -// } -// Period | Decimal => { -// text_state.push('.'); -// } -// Equals | NumpadEquals => { -// text_state.push('='); -// } -// Grave => { -// text_state.push('`'); -// } -// Minus | Subtract => { -// text_state.push('-'); -// } -// Multiply => { -// text_state.push('*'); -// } -// Semicolon => { -// text_state.push(';'); -// } -// Slash | Divide => { -// text_state.push('/'); -// } -// Underline => { -// text_state.push('_'); -// } -// Yen => { -// text_state.push('¥'); -// } -// Copy => { -// todo!("copy"); -// } -// Paste => { -// todo!("paste"); -// } -// Cut => { -// todo!("cut"); -// } -// } -// } diff --git a/crates/editor/src/editor/main.rs b/crates/editor/src/editor/main.rs deleted file mode 100644 index 9ac83cd0f2..0000000000 --- a/crates/editor/src/editor/main.rs +++ /dev/null @@ -1,635 +0,0 @@ -use super::keyboard_input; -use super::resources::strings::PLATFORM_DIR_NAME; -use crate::editor::mvc::ed_view; -use crate::editor::mvc::ed_view::RenderedWgpu; -use crate::editor::resources::strings::{HELLO_WORLD, NOTHING_OPENED}; -use crate::editor::{ - config::Config, - ed_error::print_err, - mvc::{app_model::AppModel, app_update, app_update::InputOutcome, ed_model}, - theme::EdTheme, -}; -use crate::graphics::{ - colors::to_wgpu_color, - lowlevel::buffer::create_rect_buffers, - lowlevel::ortho::update_ortho_buffer, - lowlevel::pipelines, - primitives::rect::Rect, - primitives::text::{build_glyph_brush, example_code_glyph_rect, queue_text_draw, Text}, -}; -use crate::ui::text::caret_w_select::CaretPos; -use bumpalo::Bump; -use cgmath::Vector2; -use fs_extra::dir::{copy, ls, CopyOptions, DirEntryAttr, DirEntryValue}; -use futures::TryFutureExt; -use pipelines::RectResources; -use roc_ast::lang::env::Env; -use roc_ast::mem_pool::pool::Pool; -use roc_ast::module::load_module; -use roc_load::Threading; -use roc_module::symbol::IdentIds; -use roc_packaging::cache::{self, RocCacheDir}; -use roc_types::subs::VarStore; -use std::collections::HashSet; -use std::env; -use std::fs::{self, metadata, File}; -use std::io::Write; -use std::path::PathBuf; -use std::{error::Error, io, path::Path}; -use wgpu::{CommandEncoder, LoadOp, RenderPass, TextureView}; -use wgpu_glyph::GlyphBrush; -use winit::{ - dpi::PhysicalSize, - event, - event::{Event, ModifiersState}, - event_loop::ControlFlow, - platform::run_return::EventLoopExtRunReturn, -}; - -// Inspired by: -// https://github.com/sotrh/learn-wgpu by Benjamin Hansen, which is licensed under the MIT license -// https://github.com/cloudhead/rgx by Alexis Sellier, which is licensed under the MIT license -// -// See this link to learn wgpu: https://sotrh.github.io/learn-wgpu/ - -/// The editor is actually launched from the CLI if you pass it zero arguments, -/// or if you provide it 1 or more files or directories to open on launch. -pub fn launch(project_path_opt: Option<&Path>) -> io::Result<()> { - run_event_loop(project_path_opt).expect("Error running event loop"); - - Ok(()) -} - -fn run_event_loop(project_path_opt: Option<&Path>) -> Result<(), Box> { - // Open window and create a surface - let mut event_loop = winit::event_loop::EventLoop::new(); - - let window = winit::window::WindowBuilder::new() - .with_inner_size(PhysicalSize::new(1900.0, 1000.0)) - .with_title("The Roc Editor - Work In Progress") - .build(&event_loop) - .unwrap(); - - let instance = wgpu::Instance::new(wgpu::Backends::all()); - - let surface = unsafe { instance.create_surface(&window) }; - - // Initialize GPU - let (gpu_device, cmd_queue, color_format) = futures::executor::block_on(async { - create_device( - &instance, - &surface, - wgpu::PowerPreference::HighPerformance, - false, - ) - .or_else(|_| create_device(&instance, &surface, wgpu::PowerPreference::LowPower, false)) - .or_else(|_| { - create_device( - &instance, - &surface, - wgpu::PowerPreference::HighPerformance, - true, - ) - }) - .unwrap_or_else(|err| { - panic!("Failed to request device: `{err}`"); - }) - .await - }); - - // Create staging belt and a local pool - let mut staging_belt = wgpu::util::StagingBelt::new(1024); - let mut local_pool = futures::executor::LocalPool::new(); - let local_spawner = local_pool.spawner(); - - let mut size = window.inner_size(); - - let surface_config = wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - format: color_format, - width: size.width, - height: size.height, - present_mode: wgpu::PresentMode::Mailbox, - }; - - surface.configure(&gpu_device, &surface_config); - - let rect_resources = pipelines::make_rect_pipeline(&gpu_device, &surface_config); - - let mut glyph_brush = build_glyph_brush(&gpu_device, color_format)?; - - let is_animating = true; - - let mut env_pool = Pool::with_capacity(1024); - let env_arena = Bump::new(); - let code_arena = Bump::new(); - - let (file_path_buf, code_str) = read_main_roc_file(project_path_opt); - println!("Loading file {file_path_buf:?}..."); - - let file_path = Path::new(&file_path_buf); - let loaded_module = load_module( - file_path, - RocCacheDir::Persistent(cache::roc_cache_dir().as_path()), - Threading::AllAvailable, - ); - - let mut var_store = VarStore::default(); - let dep_idents = IdentIds::exposed_builtins(8); - let exposed_ident_ids = IdentIds::default(); - let module_ids = loaded_module.interns.module_ids.clone(); - - let env = Env::new( - loaded_module.module_id, - &env_arena, - &mut env_pool, - &mut var_store, - dep_idents, - &module_ids, - exposed_ident_ids, - ); - - let config: Config = Config::default(); //confy::load("roc_editor", None)?; - let ed_model_opt = { - let ed_model_res = ed_model::init_model( - &code_str, - file_path, - env, - loaded_module, - &code_arena, - CaretPos::End, - ); - - match ed_model_res { - Ok(mut ed_model) => { - ed_model.glyph_dim_rect_opt = Some(example_code_glyph_rect( - &mut glyph_brush, - config.code_font_size, - )); - - Some(ed_model) - } - Err(e) => { - print_err(&e); - None - } - } - }; - - let mut rendered_wgpu_opt: Option = None; - - let mut app_model = AppModel::init(ed_model_opt); - - let mut keyboard_modifiers = ModifiersState::empty(); - let ed_theme = EdTheme::default(); - - // Render loop - window.request_redraw(); - - event_loop.run_return(|event, _, control_flow| { - // TODO dynamically switch this on/off depending on whether any - // animations are running. Should conserve CPU usage and battery life! - if is_animating { - *control_flow = ControlFlow::Poll; - } else { - *control_flow = ControlFlow::Wait; - } - - match event { - //Close - Event::WindowEvent { - event: event::WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, - //Resize - Event::WindowEvent { - event: event::WindowEvent::Resized(new_size), - .. - } => { - size = new_size; - - surface.configure( - &gpu_device, - &wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - format: color_format, - width: size.width, - height: size.height, - present_mode: wgpu::PresentMode::Mailbox, - }, - ); - - update_ortho_buffer( - size.width, - size.height, - &gpu_device, - &rect_resources.ortho.buffer, - &cmd_queue, - ); - } - //Received Character - Event::WindowEvent { - event: event::WindowEvent::ReceivedCharacter(ch), - .. - } => { - let input_outcome_res = - app_update::handle_new_char(&ch, &mut app_model, keyboard_modifiers); - if let Err(e) = input_outcome_res { - print_err(&e) - } else if let Ok(InputOutcome::Ignored) = input_outcome_res { - println!("\nInput '{ch}' ignored!"); - } else { - window.request_redraw() - } - } - //Keyboard Input - Event::WindowEvent { - event: event::WindowEvent::KeyboardInput { input, .. }, - .. - } => { - if let Some(virtual_keycode) = input.virtual_keycode { - if let Some(ref mut ed_model) = app_model.ed_model_opt { - if ed_model.has_focus { - let keydown_res = keyboard_input::handle_keydown( - input.state, - virtual_keycode, - keyboard_modifiers, - &mut app_model, - ); - - if let Err(e) = keydown_res { - print_err(&e) - } - - window.request_redraw() - } - } - } - } - //Modifiers Changed - Event::WindowEvent { - event: event::WindowEvent::ModifiersChanged(modifiers), - .. - } => { - keyboard_modifiers = modifiers; - } - Event::RedrawRequested { .. } => { - // Get a command encoder for the current frame - let mut encoder = - gpu_device.create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("Redraw"), - }); - - let surface_texture = surface - .get_current_texture() - .expect("Failed to acquire next SwapChainTexture"); - - let view = surface_texture - .texture - .create_view(&wgpu::TextureViewDescriptor::default()); - - if let Some(ref mut ed_model) = app_model.ed_model_opt { - if rendered_wgpu_opt.is_none() || ed_model.dirty { - let rendered_wgpu_res = ed_view::model_to_wgpu( - ed_model, - &size, - config.make_code_txt_xy().into(), - &config, - ); - - match rendered_wgpu_res { - Ok(rendered_wgpu) => rendered_wgpu_opt = Some(rendered_wgpu), - Err(e) => print_err(&e), - } - - ed_model.dirty = false; - } - - if let Some(ref rendered_wgpu) = rendered_wgpu_opt { - draw_rects( - &rendered_wgpu.rects_behind, - &mut encoder, - &view, - &gpu_device, - &rect_resources, - wgpu::LoadOp::Clear(to_wgpu_color(ed_theme.background)), - ); - - for text_section in &rendered_wgpu.text_sections_behind { - let borrowed_text = text_section.to_borrowed(); - - glyph_brush.queue(borrowed_text); - } - - // draw first layer of text - glyph_brush - .draw_queued( - &gpu_device, - &mut staging_belt, - &mut encoder, - &view, - size.width, - size.height, - ) - .expect("Failed to draw first layer of text."); - - // draw rects on top of first text layer - draw_rects( - &rendered_wgpu.rects_front, - &mut encoder, - &view, - &gpu_device, - &rect_resources, - wgpu::LoadOp::Load, - ); - - for text_section in &rendered_wgpu.text_sections_front { - let borrowed_text = text_section.to_borrowed(); - - glyph_brush.queue(borrowed_text); - } - } - } else { - begin_render_pass( - &mut encoder, - &view, - wgpu::LoadOp::Clear(to_wgpu_color(ed_theme.background)), - ); - - queue_no_file_text( - &size, - NOTHING_OPENED, - config.make_code_txt_xy().into(), - &config, - &mut glyph_brush, - ); - } - - // draw text - glyph_brush - .draw_queued( - &gpu_device, - &mut staging_belt, - &mut encoder, - &view, - size.width, - size.height, - ) - .expect("Failed to draw queued text."); - - staging_belt.finish(); - cmd_queue.submit(Some(encoder.finish())); - surface_texture.present(); - - // Recall unused staging buffers - use futures::task::SpawnExt; - - local_spawner - .spawn(staging_belt.recall()) - .expect("Recall staging belt"); - - local_pool.run_until_stalled(); - } - _ => { - *control_flow = winit::event_loop::ControlFlow::Wait; - } - } - }); - - Ok(()) -} - -async fn create_device( - instance: &wgpu::Instance, - surface: &wgpu::Surface, - power_preference: wgpu::PowerPreference, - force_fallback_adapter: bool, -) -> Result<(wgpu::Device, wgpu::Queue, wgpu::TextureFormat), wgpu::RequestDeviceError> { - if force_fallback_adapter { - log::error!("Falling back to software renderer. GPU acceleration has been disabled."); - } - - let adapter = instance - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference, - compatible_surface: Some(surface), - force_fallback_adapter, - }) - .await - .expect(r#"Request adapter - If you're running this from inside nix, follow the instructions here to resolve this: https://github.com/roc-lang/roc/blob/main/BUILDING_FROM_SOURCE.md#editor - "#); - - let color_format = surface.get_preferred_format(&adapter).unwrap(); - - if color_format != wgpu::TextureFormat::Bgra8UnormSrgb { - log::warn!("Your preferred TextureFormat {:?} is different than expected. Colors may look different, please report this issue on github and tag @Anton-4.", color_format); - } - - let request_res = adapter - .request_device( - &wgpu::DeviceDescriptor { - label: None, - features: wgpu::Features::empty(), - limits: wgpu::Limits::default(), - }, - None, - ) - .await; - - match request_res { - Ok((device, queue)) => Ok((device, queue, color_format)), - Err(err) => Err(err), - } -} - -fn draw_rects( - all_rects: &[Rect], - encoder: &mut CommandEncoder, - texture_view: &TextureView, - gpu_device: &wgpu::Device, - rect_resources: &RectResources, - load_op: LoadOp, -) { - let rect_buffers = create_rect_buffers(gpu_device, encoder, all_rects); - - let mut render_pass = begin_render_pass(encoder, texture_view, load_op); - - render_pass.set_pipeline(&rect_resources.pipeline); - render_pass.set_bind_group(0, &rect_resources.ortho.bind_group, &[]); - render_pass.set_vertex_buffer(0, rect_buffers.vertex_buffer.slice(..)); - render_pass.set_index_buffer( - rect_buffers.index_buffer.slice(..), - wgpu::IndexFormat::Uint32, - ); - render_pass.draw_indexed(0..rect_buffers.num_rects, 0, 0..1); -} - -fn begin_render_pass<'a>( - encoder: &'a mut CommandEncoder, - texture_view: &'a TextureView, - load_op: LoadOp, -) -> RenderPass<'a> { - encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - color_attachments: &[wgpu::RenderPassColorAttachment { - view: texture_view, - resolve_target: None, - ops: wgpu::Operations { - load: load_op, - store: true, - }, - }], - depth_stencil_attachment: None, - label: None, - }) -} - -const ROC_PROJECTS_FOLDER: &str = "roc-projects"; -const ROC_NEW_PROJECT_FOLDER: &str = "new-roc-project-1"; - -fn read_main_roc_file(project_path_opt: Option<&Path>) -> (PathBuf, String) { - if let Some(project_path) = project_path_opt { - let path_metadata = metadata(project_path).unwrap_or_else(|err| panic!("You provided the path {:?}, but I could not read the metadata for the provided path; error: {:?}", &project_path, err)); - - if path_metadata.is_file() { - let file_content_as_str = std::fs::read_to_string(project_path).unwrap_or_else(|err| { - panic!( - "You provided the file {:?}, but I could not read it; error: {}", - &project_path, err - ) - }); - - return (project_path.to_path_buf(), file_content_as_str); - } - - let mut ls_config = HashSet::new(); - ls_config.insert(DirEntryAttr::FullName); - - let dir_items = ls(project_path, &ls_config) - .unwrap_or_else(|err| { - panic!("Failed to list items in project directory; error: {err:?}") - }) - .items; - - let file_names = dir_items.iter().flat_map(|info_hash_map| { - info_hash_map - .values() - .filter_map(|dir_entry_value| { - if let DirEntryValue::String(file_name) = dir_entry_value { - Some(file_name) - } else { - None - } - }) - .collect::>() - }); - - let roc_file_names: Vec<&String> = file_names - .filter(|file_name| file_name.contains(".roc")) - .collect(); - - if let Some(&roc_file_name) = roc_file_names.first() { - let full_roc_file_path = project_path.join(roc_file_name); - let file_content_as_str = std::fs::read_to_string(Path::new(&full_roc_file_path)) - .unwrap_or_else(|err| panic!("In the provided project {:?}, I found the roc file {:?}, but I failed to read it: {}", &project_path, full_roc_file_path, err)); - - (full_roc_file_path, file_content_as_str) - } else { - init_new_roc_project(project_path) - } - } else { - init_new_roc_project(&Path::new(ROC_PROJECTS_FOLDER).join(ROC_NEW_PROJECT_FOLDER)) - } -} - -// returns path and content of app file -fn init_new_roc_project(project_dir_path: &Path) -> (PathBuf, String) { - let orig_platform_path = Path::new("examples") - .join("platform-switching") - .join(PLATFORM_DIR_NAME); - - let roc_file_path = Path::new(project_dir_path).join("main.roc"); - - let project_platform_path = project_dir_path.join(PLATFORM_DIR_NAME); - - if !project_dir_path.exists() { - fs::create_dir_all(project_dir_path).expect("Failed to create dir for roc project."); - } - - copy_roc_platform_if_not_exists( - &orig_platform_path, - &project_platform_path, - project_dir_path, - ); - - let code_str = create_roc_file_if_not_exists(project_dir_path, &roc_file_path); - - (roc_file_path, code_str) -} - -// returns contents of file -fn create_roc_file_if_not_exists(project_dir_path: &Path, roc_file_path: &Path) -> String { - if !roc_file_path.exists() { - let mut roc_file = File::create(roc_file_path).unwrap_or_else(|err| { - panic!("No roc file path was passed to the editor, so I wanted to create a new roc project with the file {roc_file_path:?}, but it failed: {err}") - }); - - write!(roc_file, "{HELLO_WORLD}").unwrap_or_else(|err| { - panic!( - r#"No roc file path was passed to the editor, so I created a new roc project with the file {roc_file_path:?} - I wanted to write roc hello world to that file, but it failed: {err:?}"# - ) - }); - - HELLO_WORLD.to_string() - } else { - std::fs::read_to_string(roc_file_path).unwrap_or_else(|err| { - panic!( - "I detected an existing {roc_file_path:?} inside {project_dir_path:?}, but I failed to read from it: {err}" - ) - }) - } -} - -fn copy_roc_platform_if_not_exists( - orig_platform_path: &Path, - project_platform_path: &Path, - project_dir_path: &Path, -) { - if !orig_platform_path.exists() && !project_platform_path.exists() { - panic!( - r#"No roc file path was passed to the editor, so I wanted to create a new roc project but I could not find the platform at {:?}. - Are you at the root of the roc repository? - My current directory is: {:?}"#, - orig_platform_path, - env::current_dir() - ); - } else if !project_platform_path.exists() { - copy(orig_platform_path, project_dir_path, &CopyOptions::new()).unwrap_or_else(|err|{ - panic!(r#"No roc file path was passed to the editor, so I wanted to create a new roc project and roc projects require a platform, - I tried to copy the platform at {orig_platform_path:?} to {project_platform_path:?} but it failed: {err}"# - ) - }); - } -} - -fn queue_no_file_text( - size: &PhysicalSize, - text: &str, - text_coords: Vector2, - config: &Config, - glyph_brush: &mut GlyphBrush<()>, -) { - let area_bounds = (size.width as f32, size.height as f32).into(); - - let code_text = Text { - position: text_coords, - area_bounds, - color: config.ed_theme.ui_theme.text, - text, - size: config.code_font_size, - ..Default::default() - }; - - queue_text_draw(&code_text, glyph_brush); -} diff --git a/crates/editor/src/editor/mod.rs b/crates/editor/src/editor/mod.rs deleted file mode 100644 index 5012aa0b4d..0000000000 --- a/crates/editor/src/editor/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -mod code_lines; -mod config; -pub mod ed_error; -mod grid_node_map; -mod keyboard_input; -pub mod main; -mod mvc; -mod render_ast; -mod render_debug; -mod resources; -mod theme; -mod util; diff --git a/crates/editor/src/editor/mvc/app_model.rs b/crates/editor/src/editor/mvc/app_model.rs deleted file mode 100644 index 357ab617de..0000000000 --- a/crates/editor/src/editor/mvc/app_model.rs +++ /dev/null @@ -1,108 +0,0 @@ -#![allow(dead_code)] - -use super::ed_model::EdModel; -use crate::editor::ed_error::{ - print_err, - EdError::{ClipboardInitFailed, ClipboardReadFailed, ClipboardWriteFailed}, - EdResult, -}; -use copypasta::{ClipboardContext, ClipboardProvider}; -use std::fmt; -use threadpool::ThreadPool; - -pub struct AppModel<'a> { - pub ed_model_opt: Option>, - pub clipboard_opt: Option, - pub sound_thread_pool: ThreadPool, // thread is blocked while sound is played, hence the threadpool -} - -impl<'a> AppModel<'a> { - pub fn init(ed_model_opt: Option>) -> AppModel { - AppModel { - ed_model_opt, - clipboard_opt: AppModel::init_clipboard_opt(), - sound_thread_pool: ThreadPool::new(7), // can play up to 7 sounds simultaneously - } - } - - pub fn init_clipboard_opt() -> Option { - let clipboard_res = Clipboard::init(); - - match clipboard_res { - Ok(clipboard) => Some(clipboard), - Err(e) => { - print_err(&e); - None - } - } - } -} - -pub struct Clipboard { - context: ClipboardContext, -} - -impl Clipboard { - pub fn init() -> EdResult { - let context_res = ClipboardContext::new(); - - match context_res { - Ok(context) => Ok(Clipboard { context }), - Err(e) => Err(ClipboardInitFailed { - err_msg: e.to_string(), - }), - } - } - - // clipboard crate needs this to be mutable - pub fn get_content(&mut self) -> EdResult { - let content_res = self.context.get_contents(); - - match content_res { - Ok(content_str) => Ok(content_str), - Err(e) => Err(ClipboardReadFailed { - err_msg: e.to_string(), - }), - } - } - - pub fn set_content(&mut self, copy_str: String) -> EdResult<()> { - let content_set_res = self.context.set_contents(copy_str); - - match content_set_res { - Ok(_) => Ok(()), - Err(e) => Err(ClipboardWriteFailed { - err_msg: e.to_string(), - }), - } - } -} - -pub fn set_clipboard_txt(clipboard_opt: &mut Option, txt: &str) -> EdResult<()> { - if let Some(ref mut clipboard) = clipboard_opt { - clipboard.set_content(txt.to_owned())?; - } else { - return Err(ClipboardWriteFailed { - err_msg: "Clipboard was never initialized successfully.".to_owned(), - }); - } - - Ok(()) -} - -pub fn get_clipboard_txt(clipboard_opt: &mut Option) -> EdResult { - if let Some(ref mut clipboard) = clipboard_opt { - clipboard.get_content() - } else { - Err(ClipboardReadFailed { - err_msg: "Clipboard was never initialized successfully.".to_owned(), - }) - } -} - -impl fmt::Debug for Clipboard { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // showing the clipboard would require a mut ref which is not possible - f.debug_struct("Clipboard (can't show)").finish() - } -} diff --git a/crates/editor/src/editor/mvc/app_update.rs b/crates/editor/src/editor/mvc/app_update.rs deleted file mode 100644 index 37715440ad..0000000000 --- a/crates/editor/src/editor/mvc/app_update.rs +++ /dev/null @@ -1,229 +0,0 @@ -use super::app_model::AppModel; -use super::ed_update; -use crate::window::keyboard_input::Modifiers; -use crate::{editor::ed_error::EdResult, window::keyboard_input::from_winit}; -use winit::event::{ModifiersState, VirtualKeyCode}; - -pub fn handle_copy(app_model: &mut AppModel) -> EdResult<()> { - if let Some(ref mut ed_model) = app_model.ed_model_opt { - if ed_model.has_focus { - unimplemented!("TODO"); - } - } - - Ok(()) -} - -pub fn handle_paste(app_model: &mut AppModel) -> EdResult<()> { - if let Some(ref mut ed_model) = app_model.ed_model_opt { - if ed_model.has_focus { - unimplemented!("TODO"); - } - } - - Ok(()) -} - -pub fn handle_cut(app_model: &mut AppModel) -> EdResult<()> { - if let Some(ref mut ed_model) = app_model.ed_model_opt { - if ed_model.has_focus { - unimplemented!("TODO"); - } - } - - Ok(()) -} - -pub fn pass_keydown_to_focused( - modifiers: &Modifiers, - virtual_keycode: VirtualKeyCode, - app_model: &mut AppModel, -) -> EdResult<()> { - if let Some(ref mut ed_model) = app_model.ed_model_opt { - if ed_model.has_focus { - ed_model.ed_handle_key_down( - modifiers, - virtual_keycode, - &mut app_model.sound_thread_pool, - )?; - } - } - - Ok(()) -} - -#[derive(Debug)] -pub enum InputOutcome { - Accepted, - Ignored, - SilentIgnored, -} - -pub fn handle_new_char( - received_char: &char, - app_model: &mut AppModel, - modifiers_winit: ModifiersState, -) -> EdResult { - if let Some(ref mut ed_model) = app_model.ed_model_opt { - if ed_model.has_focus { - let modifiers = from_winit(&modifiers_winit); - - if modifiers.new_char_modifiers() { - // shortcuts with modifiers are handled by ed_handle_key_down - return ed_update::handle_new_char(received_char, ed_model); - } - } - } - - Ok(InputOutcome::SilentIgnored) -} - -/* -#[cfg(test)] -pub mod test_app_update { - use crate::editor::mvc::app_model; - use crate::editor::mvc::app_model::{AppModel, Clipboard}; - use crate::editor::mvc::app_update::{handle_copy, handle_cut, handle_paste}; - use crate::editor::mvc::ed_model::EdModel; - use crate::ui::text::{ - big_selectable_text::test_big_sel_text::{ - all_lines_vec, convert_selection_to_dsl, gen_big_text, - }, - big_selectable_text::BigSelectableText, - }; - - pub fn mock_app_model( - big_sel_text: BigSelectableText, - clipboard_opt: Option, - ) -> AppModel { - AppModel { - ed_model_opt: Some(EdModel { - text: big_sel_text, - glyph_dim_rect_opt: None, - has_focus: true, - }), - clipboard_opt, - } - } - - fn assert_copy( - pre_lines_str: &[&str], - expected_clipboard_content: &str, - clipboard_opt: Option, - ) -> Result, String> { - let pre_text_buf = gen_big_text(pre_lines_str)?; - - let mut app_model = mock_app_model(pre_text_buf, clipboard_opt); - - handle_copy(&mut app_model)?; - - let clipboard_content = app_model::get_clipboard_txt(&mut app_model.clipboard_opt)?; - - assert_eq!(clipboard_content, expected_clipboard_content); - - Ok(app_model.clipboard_opt) - } - - fn assert_paste( - pre_lines_str: &[&str], - clipboard_content: &str, - expected_post_lines_str: &[&str], - clipboard_opt: Option, - ) -> Result, String> { - let pre_big_text = gen_big_text(pre_lines_str)?; - - let mut app_model = mock_app_model(pre_big_text, clipboard_opt); - - app_model::set_clipboard_txt(&mut app_model.clipboard_opt, clipboard_content)?; - - handle_paste(&mut app_model)?; - - let ed_model = app_model.ed_model_opt.unwrap(); - let mut text_lines = all_lines_vec(&ed_model.text); - let post_lines_str = - convert_selection_to_dsl(ed_model.text.caret_w_select, &mut text_lines)?; - - assert_eq!(post_lines_str, expected_post_lines_str); - - Ok(app_model.clipboard_opt) - } - - fn assert_cut( - pre_lines_str: &[&str], - expected_clipboard_content: &str, - expected_post_lines_str: &[&str], - clipboard_opt: Option, - ) -> Result, String> { - let pre_big_text = gen_big_text(pre_lines_str)?; - - let mut app_model = mock_app_model(pre_big_text, clipboard_opt); - - handle_cut(&mut app_model)?; - - let clipboard_content = app_model::get_clipboard_txt(&mut app_model.clipboard_opt)?; - - assert_eq!(clipboard_content, expected_clipboard_content); - - let ed_model = app_model.ed_model_opt.unwrap(); - let mut text_lines = all_lines_vec(&ed_model.text); - let post_lines_str = - convert_selection_to_dsl(ed_model.text.caret_w_select, &mut text_lines)?; - - assert_eq!(post_lines_str, expected_post_lines_str); - - Ok(app_model.clipboard_opt) - } - - #[test] - #[ignore] // ignored because of clipboard problems on ci - fn copy_paste_cut() -> Result<(), String> { - // can only init clipboard once - let mut clipboard_opt = AppModel::init_clipboard_opt(); - - // copy - clipboard_opt = assert_copy(&["[a]|"], "a", clipboard_opt)?; - clipboard_opt = assert_copy(&["|[b]"], "b", clipboard_opt)?; - clipboard_opt = assert_copy(&["a[ ]|"], " ", clipboard_opt)?; - clipboard_opt = assert_copy(&["[ ]|b"], " ", clipboard_opt)?; - clipboard_opt = assert_copy(&["a\n", "[b\n", "]|"], "b\n", clipboard_opt)?; - clipboard_opt = assert_copy(&["[a\n", " b\n", "]|"], "a\n b\n", clipboard_opt)?; - clipboard_opt = assert_copy( - &["abc\n", "d[ef\n", "ghi]|\n", "jkl"], - "ef\nghi", - clipboard_opt, - )?; - - // paste - - clipboard_opt = assert_paste(&["|"], "", &["|"], clipboard_opt)?; - clipboard_opt = assert_paste(&["|"], "a", &["a|"], clipboard_opt)?; - clipboard_opt = assert_paste(&["a|"], "b", &["ab|"], clipboard_opt)?; - clipboard_opt = assert_paste(&["|a"], "b", &["b|a"], clipboard_opt)?; - clipboard_opt = assert_paste(&["[a]|"], "c", &["c|"], clipboard_opt)?; - clipboard_opt = assert_paste(&["[ab]|"], "d", &["d|"], clipboard_opt)?; - clipboard_opt = assert_paste(&["a[b]|c"], "e", &["ae|c"], clipboard_opt)?; - clipboard_opt = assert_paste(&["a\n", "[b\n", "]|"], "f", &["a\n", "f|"], clipboard_opt)?; - clipboard_opt = assert_paste( - &["abc\n", "d[ef\n", "ghi]|\n", "jkl"], - "ef\nghi", - &["abc\n", "def\n", "ghi|\n", "jkl"], - clipboard_opt, - )?; - - // cut - clipboard_opt = assert_cut(&["[a]|"], "a", &["|"], clipboard_opt)?; - clipboard_opt = assert_cut(&["|[b]"], "b", &["|"], clipboard_opt)?; - clipboard_opt = assert_cut(&["a[ ]|"], " ", &["a|"], clipboard_opt)?; - clipboard_opt = assert_cut(&["[ ]|b"], " ", &["|b"], clipboard_opt)?; - clipboard_opt = assert_cut(&["a\n", "[b\n", "]|"], "b\n", &["a\n", "|"], clipboard_opt)?; - clipboard_opt = assert_cut(&["[a\n", " b\n", "]|"], "a\n b\n", &["|"], clipboard_opt)?; - assert_cut( - &["abc\n", "d[ef\n", "ghi]|\n", "jkl"], - "ef\nghi", - &["abc\n", "d|\n", "jkl"], - clipboard_opt, - )?; - - Ok(()) - } -}*/ diff --git a/crates/editor/src/editor/mvc/break_line.rs b/crates/editor/src/editor/mvc/break_line.rs deleted file mode 100644 index c5264d7a9e..0000000000 --- a/crates/editor/src/editor/mvc/break_line.rs +++ /dev/null @@ -1,63 +0,0 @@ -use roc_ast::lang::core::def::def2::Def2; -use roc_code_markup::markup::common_nodes::NEW_LINES_AFTER_DEF; - -use crate::editor::ed_error::EdResult; -use crate::editor::mvc::app_update::InputOutcome; -use crate::editor::mvc::ed_model::EdModel; -use crate::editor::util::index_of; -use crate::ui::text::lines::Lines; -use crate::ui::text::text_pos::TextPos; - -// put everything after caret on new line, create a Def2::Blank if there was nothing after the caret. -pub fn break_line(ed_model: &mut EdModel) -> EdResult { - let carets = ed_model.get_carets(); - - for caret_pos in carets.iter() { - let caret_line_nr = caret_pos.line; - - // don't allow adding new lines on empty line - if caret_pos.column > 0 - && ed_model.grid_node_map.node_exists_at_pos(TextPos { - line: caret_line_nr, - column: caret_pos.column - 1, - }) - { - let new_blank_line_nr = caret_line_nr + NEW_LINES_AFTER_DEF; - // if there already is a blank line at new_blank_line_nr just move the caret there, don't add extra lines - // safe unwrap, we already checked the nr_of_lines - if !(ed_model.code_lines.nr_of_lines() >= new_blank_line_nr - && ed_model.code_lines.line_len(new_blank_line_nr).unwrap() == 0) - { - for i in 1..=NEW_LINES_AFTER_DEF { - EdModel::insert_empty_line(caret_line_nr + i, &mut ed_model.grid_node_map)?; - } - - insert_new_blank(ed_model, caret_pos.line + NEW_LINES_AFTER_DEF + 1)?; - } - } - } - - ed_model.simple_move_carets_down(NEW_LINES_AFTER_DEF); // one blank lines between top level definitions - - Ok(InputOutcome::Accepted) -} - -pub fn insert_new_blank(ed_model: &mut EdModel, insert_on_line_nr: usize) -> EdResult<()> { - // find position of the previous ASTNode to figure out where to add this new Blank ASTNode - let def_mark_node_id = ed_model.grid_node_map.get_def_mark_node_id_before_line( - insert_on_line_nr, - &ed_model.mark_node_pool, - &ed_model.mark_id_ast_id_map, - )?; - - let new_line_blank = Def2::Blank; - let new_line_blank_id = ed_model.module.env.pool.add(new_line_blank); - - let insertion_index = index_of(def_mark_node_id, &ed_model.markup_ids)?; - ed_model - .module - .ast - .insert_def_at_index(new_line_blank_id, insertion_index); - - Ok(()) -} diff --git a/crates/editor/src/editor/mvc/ed_model.rs b/crates/editor/src/editor/mvc/ed_model.rs deleted file mode 100644 index ab7f131aea..0000000000 --- a/crates/editor/src/editor/mvc/ed_model.rs +++ /dev/null @@ -1,362 +0,0 @@ -use crate::editor::code_lines::CodeLines; -use crate::editor::grid_node_map::GridNodeMap; -use crate::editor::{ - ed_error::SrcParseSnafu, - ed_error::{EdResult, EmptyCodeStringSnafu, MissingParentSnafu, NoNodeAtCaretPositionSnafu}, -}; -use crate::graphics::primitives::rect::Rect; -use crate::ui::text::caret_w_select::{CaretPos, CaretWSelect}; -use crate::ui::text::lines::SelectableLines; -use crate::ui::text::text_pos::TextPos; -use crate::ui::ui_error::UIResult; -use bumpalo::Bump; -use nonempty::NonEmpty; -use roc_ast::lang::core::ast::{ASTNodeId, AST}; -use roc_ast::lang::env::Env; -use roc_ast::mem_pool::pool_str::PoolStr; -use roc_ast::parse::parse_ast; -use roc_code_markup::markup::convert::from_ast::ast_to_mark_nodes; -use roc_code_markup::markup::mark_id_ast_id_map::MarkIdAstIdMap; -use roc_code_markup::markup::nodes; -use roc_code_markup::slow_pool::{MarkNodeId, SlowPool}; -use roc_load::LoadedModule; -use roc_module::symbol::Interns; -use std::path::Path; - -/// Contains nearly all state related to a single roc file in the editor. -#[derive(Debug)] -pub struct EdModel<'a> { - pub module: EdModule<'a>, // contains Abstract Syntax Tree of code - pub file_path: &'a Path, - pub code_lines: CodeLines, // Vec of all code, this Vec is written to disk when saving a file. - pub grid_node_map: GridNodeMap, // allows us to map window coordinates to MarkNodeId's - pub markup_ids: Vec, // one root node for every top level definition - pub mark_node_pool: SlowPool, // all MarkupNodes for this file are saved into this pool and can be retrieved using their MarkNodeId - pub mark_id_ast_id_map: MarkIdAstIdMap, // To find the ASTNode that is represented by a MarkNode - pub glyph_dim_rect_opt: Option, // represents the width and height of single monospace glyph(char) - pub has_focus: bool, - pub caret_w_select_vec: NonEmpty<(CaretWSelect, Option)>, // the editor supports multiple carets/cursors and multiple selections - pub selected_block_opt: Option, // a selected AST node, the roc type of this node is shown in the editor on ctrl+shift+"up arrow" - pub loaded_module: LoadedModule, // contains all roc symbols, exposed values, exposed aliases, solved types... in the file(=module) - pub show_debug_view: bool, // see render_debug.rs for the debug view - pub dirty: bool, // EdModel is dirty if it has changed since the previous render. -} - -// a selected AST node, the roc type of this node is shown in the editor on ctrl+shift+"up arrow" -#[derive(Debug, Copy, Clone)] -pub struct SelectedBlock { - pub ast_node_id: ASTNodeId, - pub mark_node_id: MarkNodeId, - pub type_str: PoolStr, -} - -pub fn init_model<'a>( - code_str: &'a str, // entire roc file as one str - file_path: &'a Path, - env: Env<'a>, // contains all variables, identifiers, closures, top level symbols... - loaded_module: LoadedModule, // contains all roc symbols, exposed values, exposed aliases, solved types... in the file(=module) - code_arena: &'a Bump, // bump allocation arena, used for fast memory allocation - caret_pos: CaretPos, // to set caret position when the file is displayed -) -> EdResult> { - // for debugging - //println!("{}", code_str); - let mut owned_loaded_module = loaded_module; - - let mut module = EdModule::new(code_str, env, &mut owned_loaded_module.interns, code_arena)?; - - let mut mark_node_pool = SlowPool::default(); - - let (markup_ids, mark_id_ast_id_map) = if code_str.is_empty() { - EmptyCodeStringSnafu {}.fail() - } else { - Ok(ast_to_mark_nodes( - &mut module.env, - &module.ast, - &mut mark_node_pool, - &owned_loaded_module.interns, - )?) - }?; - - let code_lines = - CodeLines::from_str(&nodes::mark_nodes_to_string(&markup_ids, &mark_node_pool)); - let mut grid_node_map = GridNodeMap::default(); - - let mut line_nr = 0; - let mut col_nr = 0; - - for mark_node_id in &markup_ids { - // for debugging: - //println!("{}", tree_as_string(*mark_node_id, &mark_node_pool)); - EdModel::insert_mark_node_between_line( - &mut line_nr, - &mut col_nr, - *mark_node_id, - &mut grid_node_map, - &mark_node_pool, - )? - } - - let caret = match caret_pos { - CaretPos::Start => CaretWSelect::default(), - CaretPos::Exact(txt_pos) => CaretWSelect::new(txt_pos, None), - CaretPos::End => CaretWSelect::new(code_lines.end_txt_pos(), None), - }; - - Ok(EdModel { - module, - file_path, - code_lines, - grid_node_map, - markup_ids, - mark_node_pool, - mark_id_ast_id_map, - glyph_dim_rect_opt: None, - has_focus: true, - caret_w_select_vec: NonEmpty::new((caret, None)), - selected_block_opt: None, - loaded_module: owned_loaded_module, - show_debug_view: false, - dirty: true, - }) -} - -impl<'a> EdModel<'a> { - pub fn get_carets(&self) -> Vec { - self.caret_w_select_vec - .iter() - .map(|tup| tup.0.caret_pos) - .collect() - } - - pub fn get_curr_mark_node_id(&self) -> UIResult { - let caret_pos = self.get_caret(); - self.grid_node_map.get_id_at_row_col(caret_pos) - } - - // get id of MarkNode that is located before the caret - pub fn get_prev_mark_node_id(&self) -> UIResult> { - let caret_pos = self.get_caret(); - - let prev_id_opt = if caret_pos.column > 0 { - let prev_mark_node_id = self.grid_node_map.get_id_at_row_col(TextPos { - line: caret_pos.line, - column: caret_pos.column - 1, - })?; - - Some(prev_mark_node_id) - } else { - None - }; - - Ok(prev_id_opt) - } - - pub fn node_exists_at_caret(&self) -> bool { - self.grid_node_map.node_exists_at_pos(self.get_caret()) - } - - // return (index of child in list of children, closest ast index of child corresponding to ast node) of MarkupNode at current caret position - pub fn get_curr_child_indices(&self) -> EdResult<(usize, usize)> { - if self.node_exists_at_caret() { - let curr_mark_node_id = self.get_curr_mark_node_id()?; - let curr_mark_node = self.mark_node_pool.get(curr_mark_node_id); - - if let Some(parent_id) = curr_mark_node.get_parent_id_opt() { - let parent = self.mark_node_pool.get(parent_id); - let ast_node_id = self.mark_id_ast_id_map.get(curr_mark_node_id)?; - Ok(parent.get_child_indices( - curr_mark_node_id, - ast_node_id, - &self.mark_id_ast_id_map, - )?) - } else { - MissingParentSnafu { - node_id: curr_mark_node_id, - } - .fail() - } - } else { - NoNodeAtCaretPositionSnafu { - caret_pos: self.get_caret(), - } - .fail() - } - } -} - -#[derive(Debug)] -pub struct EdModule<'a> { - pub env: Env<'a>, - pub ast: AST, -} - -// for debugging -//use crate::lang::ast::expr2_to_string; - -impl<'a> EdModule<'a> { - pub fn new( - code_str: &'a str, - mut env: Env<'a>, - interns: &mut Interns, // contains ids of all identifiers in this roc file - ast_arena: &'a Bump, - ) -> EdResult> { - if !code_str.is_empty() { - let parse_res = parse_ast::parse_from_string(code_str, &mut env, ast_arena, interns); - - match parse_res { - Ok(ast) => Ok(EdModule { env, ast }), - Err(err) => SrcParseSnafu { - syntax_err: format!("{err:?}"), - } - .fail(), - } - } else { - EmptyCodeStringSnafu {}.fail() - } - } -} - -#[cfg(test)] -pub mod test_ed_model { - use crate::editor::ed_error::EdResult; - use crate::editor::mvc::ed_model; - use crate::editor::resources::strings::{ - nr_hello_world_lines, HELLO_WORLD, PLATFORM_DIR_NAME, PLATFORM_STR, - }; - use crate::ui::text::caret_w_select::test_caret_w_select::convert_dsl_to_selection; - use crate::ui::text::caret_w_select::test_caret_w_select::convert_selection_to_dsl; - use crate::ui::text::caret_w_select::CaretPos; - use crate::ui::text::lines::SelectableLines; - use crate::ui::text::text_pos::TextPos; - use crate::ui::ui_error::UIResult; - use bumpalo::Bump; - use ed_model::EdModel; - use roc_ast::lang::env::Env; - use roc_ast::mem_pool::pool::Pool; - use roc_ast::module::load_module; - use roc_load::{LoadedModule, Threading}; - use roc_module::symbol::IdentIds; - use roc_module::symbol::ModuleIds; - use roc_packaging::cache::RocCacheDir; - use roc_types::subs::VarStore; - use std::fs; - use std::fs::File; - use std::io::Write; - use std::path::Path; - use std::path::PathBuf; - use tempfile::tempdir; - use uuid::Uuid; - - pub fn init_dummy_model<'a>( - code_str: &'a str, - loaded_module: LoadedModule, - module_ids: &'a ModuleIds, - ed_model_refs: &'a mut EdModelRefs, - code_arena: &'a Bump, - ) -> EdResult> { - let file_path = Path::new(""); - - let dep_idents = IdentIds::exposed_builtins(8); - let exposed_ident_ids = IdentIds::default(); - - let env = Env::new( - loaded_module.module_id, - &ed_model_refs.env_arena, - &mut ed_model_refs.env_pool, - &mut ed_model_refs.var_store, - dep_idents, - module_ids, - exposed_ident_ids, - ); - - ed_model::init_model( - code_str, - file_path, - env, - loaded_module, - code_arena, - CaretPos::End, - ) - } - - pub struct EdModelRefs { - env_arena: Bump, - env_pool: Pool, - var_store: VarStore, - } - - pub fn init_model_refs() -> EdModelRefs { - EdModelRefs { - env_arena: Bump::new(), - env_pool: Pool::with_capacity(1024), - var_store: VarStore::default(), - } - } - - // We use a Domain Specific Language to clearly represent some of the editor's state. - // Here we convert that DSL to an EdModel. - // Example of dsl: "val = ┃❮5❯", 5 is selected and the caret is located before the 5. - pub fn ed_model_from_dsl<'a>( - clean_code_str: &'a mut String, - code_lines: Vec, - ed_model_refs: &'a mut EdModelRefs, - module_ids: &'a ModuleIds, - code_arena: &'a Bump, - ) -> Result, String> { - // to be able to load the code as a LoadedModule we add a roc app header and a main function - *clean_code_str = vec![HELLO_WORLD, clean_code_str.as_str()].join(""); - // for debugging - //println!("{}", clean_code_str); - - let temp_dir = tempdir().expect("Failed to create temporary directory for test."); - - let platform_dir = temp_dir.path().join(PLATFORM_DIR_NAME); - fs::create_dir(platform_dir.clone()).expect("Failed to create platform directory"); - let platform_module_path = platform_dir.join("main.roc"); - let mut platform_module_file = - File::create(platform_module_path).expect("Failed to create main.roc"); - writeln!(platform_module_file, "{PLATFORM_STR}").expect("Failed to write to main.roc"); - - let temp_file_path_buf = - PathBuf::from([Uuid::new_v4().to_string(), ".roc".to_string()].join("")); - let temp_file_full_path = temp_dir.path().join(temp_file_path_buf); - - let mut file = File::create(temp_file_full_path.clone()).unwrap_or_else(|_| { - panic!("Failed to create temporary file for path {temp_file_full_path:?}") - }); - writeln!(file, "{clean_code_str}") - .unwrap_or_else(|_| panic!("Failed to write {clean_code_str:?} to file: {file:?}")); - - let loaded_module = load_module( - &temp_file_full_path, - RocCacheDir::Disallowed, - Threading::AllAvailable, - ); - - let mut ed_model = init_dummy_model( - clean_code_str, - loaded_module, - module_ids, - ed_model_refs, - code_arena, - )?; - - // adjust caret for header and main function - let caret_w_select = convert_dsl_to_selection(&code_lines)?; - let adjusted_caret_pos = TextPos { - line: caret_w_select.caret_pos.line + nr_hello_world_lines(), - column: caret_w_select.caret_pos.column, - }; - - ed_model.set_caret(adjusted_caret_pos); - - Ok(ed_model) - } - - pub fn ed_model_to_dsl(ed_model: &EdModel) -> UIResult> { - let caret_w_select = ed_model.caret_w_select_vec.first().0; - let code_lines = ed_model.code_lines.lines.clone(); - - convert_selection_to_dsl(caret_w_select, code_lines) - } -} diff --git a/crates/editor/src/editor/mvc/ed_update.rs b/crates/editor/src/editor/mvc/ed_update.rs deleted file mode 100644 index ceb789a51d..0000000000 --- a/crates/editor/src/editor/mvc/ed_update.rs +++ /dev/null @@ -1,3470 +0,0 @@ -#![allow(dead_code)] - -use std::process::Stdio; - -use crate::editor::code_lines::CodeLines; -use crate::editor::ed_error::EdResult; -use crate::editor::ed_error::{MissingSelectionSnafu, RocCheckFailedSnafu}; -use crate::editor::grid_node_map::GridNodeMap; -use crate::editor::mvc::app_update::InputOutcome; -use crate::editor::mvc::ed_model::EdModel; -use crate::editor::mvc::ed_model::SelectedBlock; -use crate::editor::mvc::int_update::start_new_int; -use crate::editor::mvc::int_update::update_int; -use crate::editor::mvc::list_update::{add_blank_child, start_new_list}; -use crate::editor::mvc::lookup_update::update_invalid_lookup; -use crate::editor::mvc::record_update::start_new_record; -use crate::editor::mvc::record_update::update_empty_record; -use crate::editor::mvc::record_update::update_record_colon; -use crate::editor::mvc::record_update::update_record_field; -use crate::editor::mvc::string_update::start_new_string; -use crate::editor::mvc::string_update::update_small_string; -use crate::editor::mvc::string_update::update_string; -use crate::editor::mvc::tld_value_update::{start_new_tld_value, update_tld_val_name}; -use crate::ui::text::caret_w_select::CaretWSelect; -use crate::ui::text::lines::MoveCaretFun; -use crate::ui::text::selection::validate_raw_sel; -use crate::ui::text::selection::RawSelection; -use crate::ui::text::selection::Selection; -use crate::ui::text::text_pos::TextPos; -use crate::ui::text::{lines, lines::Lines, lines::SelectableLines}; -use crate::ui::ui_error::UIResult; -use crate::ui::util::path_to_string; -use crate::ui::util::write_to_file; -use crate::window::keyboard_input::Modifiers; -use bumpalo::Bump; -use roc_ast::constrain::constrain_expr; -use roc_ast::constrain::Constraint; -use roc_ast::lang::core::ast::ASTNodeId; -use roc_ast::lang::core::def::def2::Def2; -use roc_ast::lang::core::def::def2::DefId; -use roc_ast::lang::core::expr::expr2::Expr2; -use roc_ast::lang::core::expr::expr2::ExprId; -use roc_ast::lang::core::types::Type2; -use roc_ast::mem_pool::pool::Pool; -use roc_ast::mem_pool::pool_str::PoolStr; -use roc_ast::solve_type; -use roc_can::expected::Expected; -use roc_code_markup::markup::attribute::Attributes; -use roc_code_markup::markup::convert::from_ast::ast_to_mark_nodes; -use roc_code_markup::markup::nodes; -use roc_code_markup::markup::nodes::MarkupNode; -use roc_code_markup::markup::nodes::EQUALS; -use roc_code_markup::slow_pool::MarkNodeId; -use roc_code_markup::slow_pool::SlowPool; -use roc_collections::all::MutMap; -use roc_command_utils::cargo; -use roc_module::ident::Lowercase; -use roc_module::symbol::Symbol; -use roc_region::all::Region; -use roc_solve::module::Solved; -use roc_types::pretty_print::name_and_print_var; -use roc_types::pretty_print::DebugPrint; -use roc_types::subs::{Subs, VarStore, Variable}; -use snafu::OptionExt; -use threadpool::ThreadPool; -use winit::event::VirtualKeyCode; -use VirtualKeyCode::*; - -use super::break_line::break_line; -use super::break_line::insert_new_blank; -use super::let_update::start_new_let_value; - -/// ed_update.rs contains all functions that change the ed_model. -/// Additions and deletions of new characters to the editor are handled here. -/// A large percentage of the editor's tests are at the end of this file. -impl<'a> EdModel<'a> { - pub fn move_caret( - &mut self, - move_fun: MoveCaretFun, - modifiers: &Modifiers, - ) -> UIResult<()> { - self.dirty = true; - - for caret_tup in self.caret_w_select_vec.iter_mut() { - caret_tup.0 = move_fun(&self.code_lines, caret_tup.0, modifiers)?; - caret_tup.1 = None; - } - self.selected_block_opt = None; - - Ok(()) - } - - // disregards EdModel.code_lines because the caller knows the resulting caret position will be valid. - // allows us to prevent multiple updates to EdModel.code_lines - pub fn simple_move_carets_right(&mut self, repeat: usize) { - for caret_tup in self.caret_w_select_vec.iter_mut() { - caret_tup.0.caret_pos.column += repeat; - caret_tup.1 = None; - } - } - - // disregards EdModel.code_lines because the caller knows the resulting caret position will be valid. - // allows us to prevent multiple updates to EdModel.code_lines - // TODO error if no match was found for old_caret_pos - pub fn simple_move_caret_right(&mut self, old_caret_pos: TextPos, repeat: usize) { - for caret_tup in self.caret_w_select_vec.iter_mut() { - if caret_tup.0.caret_pos == old_caret_pos { - caret_tup.0.caret_pos.column += repeat; - caret_tup.1 = None; - } - } - } - - // disregards EdModel.code_lines because the caller knows the resulting caret position will be valid. - // allows us to prevent multiple updates to EdModel.code_lines - pub fn simple_move_carets_left(&mut self, repeat: usize) { - for caret_tup in self.caret_w_select_vec.iter_mut() { - caret_tup.0.caret_pos.column -= repeat; - caret_tup.1 = None; - } - } - - // disregards EdModel.code_lines because the caller knows the resulting caret position will be valid. - // allows us to prevent multiple updates to EdModel.code_lines - pub fn simple_move_carets_down(&mut self, repeat: usize) { - for caret_tup in self.caret_w_select_vec.iter_mut() { - caret_tup.0.caret_pos.column = 0; - caret_tup.0.caret_pos.line += repeat; - caret_tup.1 = None; - } - } - - // disregards EdModel.code_lines because the caller knows the resulting caret position will be valid. - // allows us to prevent multiple updates to EdModel.code_lines - // TODO error if no match was found for old_caret_pos - pub fn simple_move_caret_down(&mut self, old_caret_pos: TextPos, repeat: usize) { - for caret_tup in self.caret_w_select_vec.iter_mut() { - if caret_tup.0.caret_pos == old_caret_pos { - caret_tup.0.caret_pos.column = 0; - caret_tup.0.caret_pos.line += repeat; - caret_tup.1 = None; - } - } - } - - // disregards EdModel.code_lines because the caller knows the resulting caret position will be valid. - // allows us to prevent multiple updates to EdModel.code_lines - pub fn simple_move_carets_up(&mut self, repeat: usize) { - for caret_tup in self.caret_w_select_vec.iter_mut() { - caret_tup.0.caret_pos.line -= repeat; - caret_tup.1 = None; - } - } - - pub fn add_mark_node(&mut self, node: MarkupNode) -> MarkNodeId { - self.mark_node_pool.add(node) - } - - fn build_markup_string( - node_id: MarkNodeId, - all_code_string: &mut String, - mark_node_pool: &SlowPool, - ) -> EdResult<()> { - let node = mark_node_pool.get(node_id); - - if node.is_nested() { - for child_id in node.get_children_ids() { - EdModel::build_markup_string(child_id, all_code_string, mark_node_pool)?; - } - } else { - let node_content_str = node.get_content(); - - all_code_string.push_str(&node_content_str); - } - - for _ in 0..node.get_newlines_at_end() { - all_code_string.push('\n'); - } - - Ok(()) - } - - // updates grid_node_map and code_lines but nothing else. - pub fn insert_between_line( - line_nr: usize, - index: usize, - new_str: &str, - node_id: MarkNodeId, - grid_node_map: &mut GridNodeMap, - ) -> UIResult<()> { - grid_node_map.insert_between_line(line_nr, index, new_str.len(), node_id) - } - - pub fn insert_all_between_line( - &mut self, - line_nr: usize, - index: usize, - leaf_node_ids: &[MarkNodeId], - ) -> UIResult<()> { - let mut col_nr = index; - let mut curr_line_nr = line_nr; - - for &node_id in leaf_node_ids { - let mark_node = self.mark_node_pool.get(node_id); - let node_full_content = mark_node.get_full_content(); - - if node_full_content.contains('\n') { - //insert separate lines separately - let split_lines = node_full_content.split('\n'); - - for line in split_lines { - self.grid_node_map.insert_between_line( - curr_line_nr, - col_nr, - line.len(), - node_id, - )?; - - curr_line_nr += 1; - col_nr = 0; - } - } else { - let node_content = mark_node.get_content(); - - self.grid_node_map.insert_between_line( - line_nr, - col_nr, - node_content.len(), - node_id, - )?; - - col_nr += node_content.len(); - } - } - - Ok(()) - } - - pub fn insert_mark_node_between_line( - line_nr: &mut usize, - col_nr: &mut usize, - mark_node_id: MarkNodeId, - grid_node_map: &mut GridNodeMap, - mark_node_pool: &SlowPool, - ) -> UIResult<()> { - let mark_node = mark_node_pool.get(mark_node_id); - - let node_newlines = mark_node.get_newlines_at_end(); - - if mark_node.is_nested() { - let children_ids = mark_node.get_children_ids(); - - for child_id in children_ids { - EdModel::insert_mark_node_between_line( - line_nr, - col_nr, - child_id, - grid_node_map, - mark_node_pool, - )?; - } - } else { - let node_content = mark_node.get_content(); - - EdModel::insert_between_line( - *line_nr, - *col_nr, - &node_content, - mark_node_id, - grid_node_map, - )?; - - *col_nr += node_content.len(); - } - - if node_newlines > 0 { - EdModel::break_line(*line_nr, *col_nr, grid_node_map)?; - - *line_nr += 1; - *col_nr = 0; - - for _ in 1..node_newlines { - EdModel::insert_empty_line(*line_nr, grid_node_map)?; - - *line_nr += 1; - *col_nr = 0; - } - } - - Ok(()) - } - - // break(split) line at col_nr and move everything after col_nr to the next line - pub fn break_line( - line_nr: usize, - col_nr: usize, - grid_node_map: &mut GridNodeMap, - ) -> UIResult<()> { - grid_node_map.break_line(line_nr, col_nr) - } - - pub fn insert_empty_line(line_nr: usize, grid_node_map: &mut GridNodeMap) -> UIResult<()> { - grid_node_map.insert_empty_line(line_nr) - } - - pub fn push_empty_line(grid_node_map: &mut GridNodeMap) { - grid_node_map.push_empty_line(); - } - - pub fn clear_line(&mut self, line_nr: usize) -> UIResult<()> { - self.grid_node_map.clear_line(line_nr) - } - - pub fn del_line(&mut self, line_nr: usize) { - self.grid_node_map.del_line(line_nr) - } - - pub fn del_at_line(&mut self, line_nr: usize, index: usize) -> UIResult<()> { - self.grid_node_map.del_at_line(line_nr, index) - } - - // updates grid_node_map and code_lines but nothing else. - pub fn del_range_at_line( - &mut self, - line_nr: usize, - col_range: std::ops::Range, - ) -> UIResult<()> { - self.grid_node_map.del_range_at_line(line_nr, col_range) - } - - pub fn del_blank_expr_node(&mut self, txt_pos: TextPos) -> UIResult<()> { - self.del_at_line(txt_pos.line, txt_pos.column) - } - - pub fn set_selected_expr( - &mut self, - expr_start_pos: TextPos, - expr_end_pos: TextPos, - ast_node_id: ASTNodeId, - mark_node_id: MarkNodeId, - ) -> EdResult<()> { - self.set_raw_sel(RawSelection { - start_pos: expr_start_pos, - end_pos: expr_end_pos, - })?; - - self.set_caret(expr_start_pos); - - let type_str = match ast_node_id { - ASTNodeId::ADefId(def_id) => { - if let Some(expr_id) = self.extract_expr_from_def(def_id) { - self.expr2_to_type(expr_id) - } else { - PoolStr::new(" * ", self.module.env.pool) - } - } - - ASTNodeId::AExprId(expr_id) => self.expr2_to_type(expr_id), - }; - - self.selected_block_opt = Some(SelectedBlock { - ast_node_id, - mark_node_id, - type_str, - }); - - self.dirty = true; - - Ok(()) - } - - // select all MarkupNodes that refer to specific ast node and its children. - pub fn select_expr(&mut self) -> EdResult<()> { - // include parent in selection if an `Expr2` was already selected - if let Some(selected_block) = &self.selected_block_opt { - let expr2_level_mark_node = self.mark_node_pool.get(selected_block.mark_node_id); - - if let Some(parent_id) = expr2_level_mark_node.get_parent_id_opt() { - let ast_node_id = self.mark_id_ast_id_map.get(parent_id)?; - - let (expr_start_pos, expr_end_pos) = self - .grid_node_map - .get_nested_start_end_pos(parent_id, self)?; - - self.set_selected_expr(expr_start_pos, expr_end_pos, ast_node_id, parent_id)?; - } - } else { - // select `Expr2` in which caret is currently positioned - let caret_pos = self.get_caret(); - if self.grid_node_map.node_exists_at_pos(caret_pos) { - let (expr_start_pos, expr_end_pos, ast_node_id, mark_node_id) = self - .grid_node_map - .get_block_start_end_pos(self.get_caret(), self)?; - - self.set_selected_expr(expr_start_pos, expr_end_pos, ast_node_id, mark_node_id)?; - } else if self - .grid_node_map - .node_exists_at_pos(caret_pos.decrement_col()) - { - let (expr_start_pos, expr_end_pos, ast_node_id, mark_node_id) = self - .grid_node_map - .get_block_start_end_pos(self.get_caret().decrement_col(), self)?; - - self.set_selected_expr(expr_start_pos, expr_end_pos, ast_node_id, mark_node_id)?; - } - } - - Ok(()) - } - - fn extract_expr_from_def(&self, def_id: DefId) -> Option { - let def = self.module.env.pool.get(def_id); - - match def { - Def2::ValueDef { - identifier_id: _, - expr_id, - } => Some(*expr_id), - Def2::Blank => None, - Def2::CommentsBefore { - comments: _, - def_id, - } => self.extract_expr_from_def(*def_id), - Def2::CommentsAfter { - comments: _, - def_id, - } => self.extract_expr_from_def(*def_id), - } - } - - fn expr2_to_type(&mut self, expr2_id: ExprId) -> PoolStr { - let var = self.module.env.var_store.fresh(); - let expr = self.module.env.pool.get(expr2_id); - let arena = Bump::new(); - - let constrained = constrain_expr( - &arena, - &mut self.module.env, - expr, - Expected::NoExpectation(Type2::Variable(var)), - Region::zero(), - ); - - // extract the var_store out of the env - let mut var_store = VarStore::default(); - std::mem::swap(self.module.env.var_store, &mut var_store); - - let (mut solved, _, _) = EdModel::run_solve( - self.module.env.pool, - Default::default(), - Default::default(), - constrained, - var_store, - ); - - // put the updated var_store back in env - std::mem::swap( - &mut VarStore::new_from_subs(solved.inner()), - self.module.env.var_store, - ); - - let subs = solved.inner_mut(); - - let pretty_var = name_and_print_var( - var, - subs, - self.module.env.home, - &self.loaded_module.interns, - DebugPrint::NOTHING, - ); - - PoolStr::new(&pretty_var, self.module.env.pool) - } - - fn run_solve( - mempool: &mut Pool, - aliases: MutMap, - rigid_variables: MutMap, - constraint: Constraint, - var_store: VarStore, - ) -> (Solved, solve_type::Env, Vec) { - let env = solve_type::Env { - vars_by_symbol: MutMap::default(), - aliases, - }; - let arena = Bump::new(); - - let mut subs = Subs::new_from_varstore(var_store); - - for (var, name) in rigid_variables { - subs.rigid_var(var, name); - } - - // Now that the module is parsed, canonicalized, and constrained, - // we need to type check it. - let mut problems = Vec::new(); - - // Run the solver to populate Subs. - let (solved_subs, solved_env) = - solve_type::run(&arena, mempool, &env, &mut problems, subs, &constraint); - - (solved_subs, solved_env, problems) - } - - pub fn ed_handle_key_down( - &mut self, - modifiers: &Modifiers, - virtual_keycode: VirtualKeyCode, - _sound_thread_pool: &mut ThreadPool, - ) -> EdResult<()> { - match virtual_keycode { - Left => self.move_caret_left(modifiers)?, - Up => { - if modifiers.cmd_or_ctrl() && modifiers.shift { - self.select_expr()? - } else { - self.move_caret_up(modifiers)? - } - } - Right => self.move_caret_right(modifiers)?, - Down => self.move_caret_down(modifiers)?, - - A => { - if modifiers.cmd_or_ctrl() { - self.select_all()? - } - } - S => { - if modifiers.cmd_or_ctrl() { - self.save_file()? - } - } - R => { - if modifiers.cmd_or_ctrl() { - self.check_file()?; - self.run_file()? - } - } - - Home => self.move_caret_home(modifiers)?, - End => self.move_caret_end(modifiers)?, - - F11 => { - self.show_debug_view = !self.show_debug_view; - self.dirty = true; - } - _ => (), - } - - Ok(()) - } - - // Replaces selected expression with blank. - // If no expression is selected, this function will select one to guide the user to using backspace in a projectional editing way - fn backspace(&mut self) -> EdResult<()> { - if let Some(sel_block) = &self.selected_block_opt { - let expr2_level_mark_node = self.mark_node_pool.get(sel_block.mark_node_id); - let newlines_at_end = expr2_level_mark_node.get_newlines_at_end(); - - let blank_replacement = MarkupNode::Blank { - attributes: Attributes::default(), - parent_id_opt: expr2_level_mark_node.get_parent_id_opt(), - newlines_at_end, - }; - - self.mark_node_pool - .replace_node(sel_block.mark_node_id, blank_replacement); - - let active_selection = self.get_selection().context(MissingSelectionSnafu {})?; - - self.grid_node_map.del_selection(active_selection)?; - - match sel_block.ast_node_id { - ASTNodeId::ADefId(def_id) => { - self.module.env.pool.set(def_id, Def2::Blank); - } - ASTNodeId::AExprId(expr_id) => { - self.module.env.pool.set(expr_id, Expr2::Blank); - } - } - - let expr_mark_node_id = sel_block.mark_node_id; - - let caret_pos = self.get_caret(); - - EdModel::insert_between_line( - caret_pos.line, - caret_pos.column, - nodes::BLANK_PLACEHOLDER, - expr_mark_node_id, - &mut self.grid_node_map, - )?; - - self.set_sel_none(); - } else { - self.select_expr()?; - }; - - Ok(()) - } - - fn save_file(&mut self) -> UIResult<()> { - let all_lines_str = self.code_lines.all_lines_as_string(); - - write_to_file(self.file_path, &all_lines_str)?; - - println!("\nsave successful!"); - - Ok(()) - } - - fn check_file(&mut self) -> EdResult<()> { - println!("\nChecking file (cargo run check )..."); - - let roc_file_str = path_to_string(self.file_path); - - let cmd_out = cargo() - .arg("run") - .arg("--release") - .arg("check") - .arg(roc_file_str) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) - .output()?; - - if !cmd_out.status.success() { - RocCheckFailedSnafu.fail()? - } - - Ok(()) - } - - fn run_file(&mut self) -> EdResult<()> { - println!("\nExecuting file (cargo run --release )..."); - - let roc_file_str = path_to_string(self.file_path); - - cargo() - .arg("run") - .arg("--release") - .arg(roc_file_str) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) - .output()?; - - Ok(()) - } - - /// update MarkupNode's, grid_node_map, code_lines after the AST has been updated - fn post_process_ast_update(&mut self) -> EdResult<()> { - //dbg!("{}",self.module.ast.ast_to_string(self.module.env.pool)); - - let markup_ids_tup = ast_to_mark_nodes( - &mut self.module.env, - &self.module.ast, - &mut self.mark_node_pool, - &self.loaded_module.interns, - )?; - - self.markup_ids = markup_ids_tup.0; - self.mark_id_ast_id_map = markup_ids_tup.1; - - self.code_lines = CodeLines::from_str(&nodes::mark_nodes_to_string( - &self.markup_ids, - &self.mark_node_pool, - )); - self.grid_node_map = GridNodeMap::default(); - - let mut line_nr = 0; - let mut col_nr = 0; - - for mark_node_id in &self.markup_ids { - // for debugging: - //println!("{}", tree_as_string(*mark_node_id, &mark_node_pool)); - EdModel::insert_mark_node_between_line( - &mut line_nr, - &mut col_nr, - *mark_node_id, - &mut self.grid_node_map, - &self.mark_node_pool, - )? - } - - Ok(()) - } -} - -impl<'a> SelectableLines for EdModel<'a> { - fn get_caret(&self) -> TextPos { - self.caret_w_select_vec.first().0.caret_pos - } - - // keeps active selection - fn set_caret(&mut self, caret_pos: TextPos) { - let caret_tup = self.caret_w_select_vec.first_mut(); - caret_tup.0.caret_pos = caret_pos; - caret_tup.1 = None; - } - - fn move_caret_left(&mut self, modifiers: &Modifiers) -> UIResult<()> { - let move_fun: MoveCaretFun = lines::move_caret_left; - EdModel::move_caret(self, move_fun, modifiers)?; - - Ok(()) - } - - fn move_caret_right(&mut self, modifiers: &Modifiers) -> UIResult<()> { - let move_fun: MoveCaretFun = lines::move_caret_right; - EdModel::move_caret(self, move_fun, modifiers)?; - - Ok(()) - } - - fn move_caret_up(&mut self, modifiers: &Modifiers) -> UIResult<()> { - let move_fun: MoveCaretFun = lines::move_caret_up; - EdModel::move_caret(self, move_fun, modifiers)?; - - Ok(()) - } - - fn move_caret_down(&mut self, modifiers: &Modifiers) -> UIResult<()> { - let move_fun: MoveCaretFun = lines::move_caret_down; - EdModel::move_caret(self, move_fun, modifiers)?; - - Ok(()) - } - - fn move_caret_home(&mut self, modifiers: &Modifiers) -> UIResult<()> { - let move_fun: MoveCaretFun = lines::move_caret_home; - EdModel::move_caret(self, move_fun, modifiers)?; - - Ok(()) - } - - fn move_caret_end(&mut self, modifiers: &Modifiers) -> UIResult<()> { - let move_fun: MoveCaretFun = lines::move_caret_end; - EdModel::move_caret(self, move_fun, modifiers)?; - - Ok(()) - } - - fn get_selection(&self) -> Option { - self.caret_w_select_vec.first().0.selection_opt - } - - fn is_selection_active(&self) -> bool { - self.get_selection().is_some() - } - - fn get_selected_str(&self) -> UIResult> { - if let Some(selection) = self.get_selection() { - let start_line_index = selection.start_pos.line; - let start_col = selection.start_pos.column; - let end_line_index = selection.end_pos.line; - let end_col = selection.end_pos.column; - - if start_line_index == end_line_index { - let line_ref = self.code_lines.get_line_ref(start_line_index)?; - - Ok(Some(line_ref[start_col..end_col].to_string())) - } else { - let full_str = String::new(); - - // TODO - Ok(Some(full_str)) - } - } else { - Ok(None) - } - } - - fn set_raw_sel(&mut self, raw_sel: RawSelection) -> UIResult<()> { - self.caret_w_select_vec.first_mut().0.selection_opt = Some(validate_raw_sel(raw_sel)?); - - Ok(()) - } - - fn set_sel_none(&mut self) { - self.caret_w_select_vec.first_mut().0.selection_opt = None; - self.selected_block_opt = None; - } - - fn set_caret_w_sel(&mut self, caret_w_sel: CaretWSelect) { - self.caret_w_select_vec.first_mut().0 = caret_w_sel; - } - - fn select_all(&mut self) -> UIResult<()> { - if self.code_lines.nr_of_chars() > 0 { - let last_pos = self.last_text_pos()?; - - self.set_raw_sel(RawSelection { - start_pos: TextPos { line: 0, column: 0 }, - end_pos: last_pos, - })?; - - self.set_caret(last_pos); - } - - Ok(()) - } - - fn last_text_pos(&self) -> UIResult { - let nr_of_lines = self.code_lines.lines.len(); - let last_line_index = nr_of_lines - 1; - let last_line = self.code_lines.get_line_ref(last_line_index)?; - - Ok(TextPos { - line: self.code_lines.lines.len() - 1, - column: last_line.len(), - }) - } - - fn handle_key_down( - &mut self, - _modifiers: &Modifiers, - _virtual_keycode: VirtualKeyCode, - ) -> UIResult<()> { - unreachable!("Use EdModel::ed_handle_key_down instead.") - } -} - -pub struct NodeContext<'a> { - pub old_caret_pos: TextPos, - pub curr_mark_node_id: MarkNodeId, - pub curr_mark_node: &'a MarkupNode, - pub parent_id_opt: Option, - pub ast_node_id: ASTNodeId, -} - -pub fn get_node_context<'a>(ed_model: &'a EdModel) -> EdResult> { - let old_caret_pos = ed_model.get_caret(); - let curr_mark_node_id = ed_model - .grid_node_map - .get_id_at_row_col(ed_model.get_caret())?; - let curr_mark_node = ed_model.mark_node_pool.get(curr_mark_node_id); - let parent_id_opt = curr_mark_node.get_parent_id_opt(); - let ast_node_id = ed_model.mark_id_ast_id_map.get(curr_mark_node_id)?; - - Ok(NodeContext { - old_caret_pos, - curr_mark_node_id, - curr_mark_node, - parent_id_opt, - ast_node_id, - }) -} - -fn if_modifiers(modifiers: &Modifiers, shortcut_result: UIResult<()>) -> EdResult<()> { - if modifiers.cmd_or_ctrl() { - Ok(shortcut_result?) - } else { - Ok(()) - } -} - -// handle new char when current(=caret is here) MarkupNode corresponds to a Def2 in the AST -pub fn handle_new_char_def( - received_char: &char, - def_id: DefId, - ed_model: &mut EdModel, -) -> EdResult { - let def_ref = ed_model.module.env.pool.get(def_id); - let ch = received_char; - - let NodeContext { - old_caret_pos, - curr_mark_node_id, - curr_mark_node, - parent_id_opt: _, - ast_node_id: _, - } = get_node_context(ed_model)?; - - let outcome = match def_ref { - Def2::Blank { .. } => match ch { - 'a'..='z' => start_new_tld_value(ed_model, ch)?, - _ => InputOutcome::Ignored, - }, - Def2::ValueDef { .. } => { - if curr_mark_node.get_content() == EQUALS { - let node_caret_offset = ed_model - .grid_node_map - .get_offset_to_node_id(old_caret_pos, curr_mark_node_id)?; - - if node_caret_offset == 0 || node_caret_offset == EQUALS.len() { - let prev_mark_node_id_opt = ed_model.get_prev_mark_node_id()?; - - if let Some(prev_mark_node_id) = prev_mark_node_id_opt { - update_tld_val_name( - prev_mark_node_id, - ed_model.get_caret(), // TODO update for multiple carets - ed_model, - ch, - )? - } else { - unreachable!() - } - } else { - InputOutcome::Ignored - } - } else { - update_tld_val_name( - curr_mark_node_id, - ed_model.get_caret(), // TODO update for multiple carets - ed_model, - ch, - )? - } - } - Def2::CommentsBefore { .. } => { - todo!() - } - Def2::CommentsAfter { .. } => { - todo!() - } - }; - - Ok(outcome) -} - -// handle new char when the current(caret is here) MarkupNode corresponds to an Expr2 in the AST -pub fn handle_new_char_expr( - received_char: &char, - expr_id: ExprId, - ed_model: &mut EdModel, -) -> EdResult { - let expr_ref = ed_model.module.env.pool.get(expr_id); - let ch = received_char; - - let NodeContext { - old_caret_pos: _, - curr_mark_node_id, - curr_mark_node, - parent_id_opt: _, - ast_node_id: _, - } = get_node_context(ed_model)?; - - let prev_mark_node_id_opt = ed_model.get_prev_mark_node_id()?; - - let outcome = if let Expr2::Blank { .. } = expr_ref { - match ch { - 'a'..='z' => start_new_let_value(ed_model, ch)?, - '"' => start_new_string(ed_model)?, - '{' => start_new_record(ed_model)?, - '0'..='9' => start_new_int(ed_model, ch)?, - '[' => { - // this can also be a tag union or become a set, assuming list for now - start_new_list(ed_model)? - } - '\r' => { - println!("For convenience and consistency there is only one way to format Roc, you can't add extra blank lines."); - InputOutcome::Ignored - } - _ => InputOutcome::Ignored, - } - } else if let Some(prev_mark_node_id) = prev_mark_node_id_opt { - if prev_mark_node_id == curr_mark_node_id { - match expr_ref { - Expr2::SmallInt { .. } => update_int(ed_model, curr_mark_node_id, ch)?, - Expr2::SmallStr(old_arr_str) => update_small_string(ch, old_arr_str, ed_model)?, - Expr2::Str(..) => update_string(*ch, ed_model)?, - Expr2::InvalidLookup(old_pool_str) => update_invalid_lookup( - &ch.to_string(), - old_pool_str, - curr_mark_node_id, - expr_id, - ed_model, - )?, - Expr2::EmptyRecord => { - // prev_mark_node_id and curr_mark_node_id should be different to allow creating field at current caret position - InputOutcome::Ignored - } - Expr2::Record { - record_var: _, - fields, - } => { - if curr_mark_node - .get_content() - .chars() - .all(|chr| chr.is_ascii_alphanumeric()) - { - update_record_field( - &ch.to_string(), - ed_model.get_caret(), - curr_mark_node_id, - fields, - ed_model, - )? - } else { - InputOutcome::Ignored - } - } - _ => InputOutcome::Ignored, - } - } else if ch.is_ascii_alphanumeric() { - // prev_mark_node_id != curr_mark_node_id - - match expr_ref { - Expr2::SmallInt { .. } => update_int(ed_model, curr_mark_node_id, ch)?, - _ => { - let prev_ast_node_id = ed_model.mark_id_ast_id_map.get(prev_mark_node_id)?; - - match prev_ast_node_id { - ASTNodeId::ADefId(_) => InputOutcome::Ignored, - ASTNodeId::AExprId(prev_expr_id) => { - handle_new_char_diff_mark_nodes_prev_is_expr( - ch, - prev_expr_id, - expr_id, - prev_mark_node_id, - curr_mark_node_id, - ed_model, - )? - } - } - } - } - } else if *ch == ':' { - let mark_parent_id_opt = curr_mark_node.get_parent_id_opt(); - - if let Some(mark_parent_id) = mark_parent_id_opt { - let parent_ast_id = ed_model.mark_id_ast_id_map.get(mark_parent_id)?; - - match parent_ast_id { - ASTNodeId::ADefId(_) => InputOutcome::Ignored, - ASTNodeId::AExprId(parent_expr_id) => { - update_record_colon(ed_model, parent_expr_id)? - } - } - } else { - InputOutcome::Ignored - } - } else if *ch == ',' { - if curr_mark_node.get_content() == nodes::LEFT_SQUARE_BR { - InputOutcome::Ignored - } else { - let mark_parent_id_opt = curr_mark_node.get_parent_id_opt(); - - if let Some(mark_parent_id) = mark_parent_id_opt { - let parent_ast_id = ed_model.mark_id_ast_id_map.get(mark_parent_id)?; - - match parent_ast_id { - ASTNodeId::ADefId(_) => InputOutcome::Ignored, - ASTNodeId::AExprId(parent_expr_id) => { - let parent_expr2 = ed_model.module.env.pool.get(parent_expr_id); - - match parent_expr2 { - Expr2::List { - elem_var: _, - elems: _, - } => { - let (new_child_index, new_ast_child_index) = - ed_model.get_curr_child_indices()?; - // insert a Blank first, this results in cleaner code - add_blank_child(new_child_index, new_ast_child_index, ed_model)? - } - Expr2::Record { - record_var: _, - fields: _, - } => { - todo!("multiple record fields") - } - _ => InputOutcome::Ignored, - } - } - } - } else { - InputOutcome::Ignored - } - } - } else if "\"{[".contains(*ch) { - let prev_mark_node = ed_model.mark_node_pool.get(prev_mark_node_id); - - if prev_mark_node.get_content() == nodes::LEFT_SQUARE_BR - && curr_mark_node.get_content() == nodes::RIGHT_SQUARE_BR - { - let (new_child_index, new_ast_child_index) = ed_model.get_curr_child_indices()?; - // insert a Blank first, this results in cleaner code - add_blank_child(new_child_index, new_ast_child_index, ed_model)?; - ed_model.post_process_ast_update()?; - handle_new_char(received_char, ed_model)? - } else { - InputOutcome::Ignored - } - } else { - InputOutcome::Ignored - } - } else { - InputOutcome::Ignored - }; - - Ok(outcome) -} - -// handle new char when prev_mark_node != curr_mark_node and prev_mark_node's AST node is an Expr2 -pub fn handle_new_char_diff_mark_nodes_prev_is_expr( - received_char: &char, - prev_expr_id: ExprId, - curr_expr_id: ExprId, - prev_mark_node_id: MarkNodeId, - curr_mark_node_id: MarkNodeId, - ed_model: &mut EdModel, -) -> EdResult { - let prev_expr_ref = ed_model.module.env.pool.get(prev_expr_id); - let curr_expr_ref = ed_model.module.env.pool.get(curr_expr_id); - let ch = received_char; - let curr_mark_node = ed_model.mark_node_pool.get(curr_mark_node_id); - - let outcome = match prev_expr_ref { - Expr2::SmallInt { .. } => update_int(ed_model, prev_mark_node_id, ch)?, - Expr2::InvalidLookup(old_pool_str) => update_invalid_lookup( - &ch.to_string(), - old_pool_str, - prev_mark_node_id, - prev_expr_id, - ed_model, - )?, - Expr2::Record { - record_var: _, - fields, - } => { - let prev_mark_node = ed_model.mark_node_pool.get(prev_mark_node_id); - - if (curr_mark_node.get_content() == nodes::RIGHT_ACCOLADE - || curr_mark_node.get_content() == nodes::COLON) - && prev_mark_node.is_all_alphanumeric() - { - update_record_field( - &ch.to_string(), - ed_model.get_caret(), - prev_mark_node_id, - fields, - ed_model, - )? - } else if prev_mark_node.get_content() == nodes::LEFT_ACCOLADE - && curr_mark_node.is_all_alphanumeric() - { - update_record_field( - &ch.to_string(), - ed_model.get_caret(), - curr_mark_node_id, - fields, - ed_model, - )? - } else { - InputOutcome::Ignored - } - } - Expr2::List { - elem_var: _, - elems: _, - } => { - let prev_mark_node = ed_model.mark_node_pool.get(prev_mark_node_id); - - if prev_mark_node.get_content() == nodes::LEFT_SQUARE_BR - && curr_mark_node.get_content() == nodes::RIGHT_SQUARE_BR - { - // based on if, we are at the start of the list - let new_child_index = 1; - let new_ast_child_index = 0; - // insert a Blank first, this results in cleaner code - add_blank_child(new_child_index, new_ast_child_index, ed_model)?; - ed_model.post_process_ast_update()?; - handle_new_char(received_char, ed_model)? - } else { - InputOutcome::Ignored - } - } - _ => match curr_expr_ref { - Expr2::EmptyRecord => { - let sibling_ids = curr_mark_node.get_sibling_ids(&ed_model.mark_node_pool); - - update_empty_record(&ch.to_string(), prev_mark_node_id, sibling_ids, ed_model)? - } - _ => InputOutcome::Ignored, - }, - }; - - Ok(outcome) -} - -// updates the ed_model based on the char the user just typed if the result would be syntactically correct. -pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult { - //dbg!("{}", ed_model.module.ast.ast_to_string(ed_model.module.env.pool)); - - let input_outcome = match received_char { - '\u{e000}'..='\u{f8ff}' // http://www.unicode.org/faq/private_use.html - | '\u{f0000}'..='\u{ffffd}' // ^ - | '\u{100000}'..='\u{10fffd}' // ^ - => { - InputOutcome::Ignored - } - '\u{8}' | '\u{7f}' => { - // On Linux, '\u{8}' is backspace, - // on macOS '\u{7f}'. - - ed_model.backspace()?; - - InputOutcome::Accepted - } - ch => { - let outcome = - if ed_model.node_exists_at_caret() { - let curr_mark_node_id = ed_model.get_curr_mark_node_id()?; - let ast_node_id = ed_model.mark_id_ast_id_map.get(curr_mark_node_id)?; - - match ast_node_id { - ASTNodeId::ADefId(def_id) => { - handle_new_char_def(received_char, def_id, ed_model)? - }, - ASTNodeId::AExprId(expr_id) => { - handle_new_char_expr(received_char, expr_id, ed_model)? - } - } - - } else { //no MarkupNode at the current position - if *received_char == '\r' { - break_line(ed_model)? - } else { - let prev_mark_node_id_opt = ed_model.get_prev_mark_node_id()?; - if let Some(prev_mark_node_id) = prev_mark_node_id_opt { - - let prev_ast_node_id = ed_model.mark_id_ast_id_map.get(prev_mark_node_id)?.to_expr_id()?; - let prev_ast_node = ed_model.module.env.pool.get(prev_ast_node_id); - - match prev_ast_node { - Expr2::SmallInt{ .. } => { - update_int(ed_model, prev_mark_node_id, ch)? - }, - _ => { - InputOutcome::Ignored - } - } - } else { - match ch { - 'a'..='z' => { - for caret_pos in ed_model.get_carets() { - - if caret_pos.line > 0 { - insert_new_blank(ed_model, caret_pos.line)?; - ed_model.post_process_ast_update()?; - } - } - handle_new_char(received_char, ed_model)? - } - _ => { - InputOutcome::Ignored - } - } - } - } - }; - - - if let InputOutcome::Accepted = outcome { - ed_model.set_sel_none(); - } - - outcome - } - }; - - if let InputOutcome::Accepted = input_outcome { - ed_model.post_process_ast_update()?; - ed_model.dirty = true; - } - - Ok(input_outcome) -} - -#[cfg(test)] -pub mod test_ed_update { - use std::iter; - - use crate::editor::ed_error::print_err; - use crate::editor::mvc::ed_model::test_ed_model::ed_model_from_dsl; - use crate::editor::mvc::ed_model::test_ed_model::ed_model_to_dsl; - use crate::editor::mvc::ed_model::test_ed_model::init_model_refs; - use crate::editor::mvc::ed_update::handle_new_char; - use crate::editor::mvc::ed_update::EdModel; - use crate::editor::mvc::ed_update::EdResult; - use crate::editor::resources::strings::nr_hello_world_lines; - use crate::ui::text::lines::SelectableLines; - use crate::ui::ui_error::UIResult; - use crate::window::keyboard_input::no_mods; - use crate::window::keyboard_input::test_modifiers::ctrl_cmd_shift; - use crate::window::keyboard_input::Modifiers; - use bumpalo::Bump; - use roc_code_markup::markup::common_nodes::NEW_LINES_AFTER_DEF; - use roc_module::symbol::ModuleIds; - use threadpool::ThreadPool; - use winit::event::VirtualKeyCode::*; - - fn ed_res_to_res(ed_res: EdResult) -> Result { - match ed_res { - Ok(t) => Ok(t), - Err(e) => { - print_err(&e); - Err(e.to_string()) - } - } - } - - fn ui_res_to_res(ed_res: UIResult) -> Result { - match ed_res { - Ok(t) => Ok(t), - Err(e) => Err(e.to_string()), - } - } - - // Create ed_model from pre_lines DSL, do handle_new_char() with new_char, check if modified ed_model has expected - // string representation of code, caret position and active selection. - pub fn assert_insert( - pre_lines: Vec, - expected_post_lines: Vec, - new_char: char, - ) -> Result<(), String> { - assert_insert_seq(pre_lines, expected_post_lines, &new_char.to_string()) - } - - pub fn assert_insert_nls( - pre_lines: Vec, - expected_post_lines: Vec, - new_char: char, - ) -> Result<(), String> { - assert_insert(pre_lines, add_nls(expected_post_lines), new_char) - } - - pub fn assert_insert_no_pre( - expected_post_lines: Vec, - new_char: char, - ) -> Result<(), String> { - assert_insert_seq_no_pre(expected_post_lines, &new_char.to_string()) - } - - pub fn assert_insert_seq_no_pre( - expected_post_lines: Vec, - new_char_seq: &str, - ) -> Result<(), String> { - assert_insert_seq(vec!["┃".to_owned()], expected_post_lines, new_char_seq) - } - - // pre-insert `val = ` - pub fn assert_insert_in_def( - expected_post_lines: Vec, - new_char: char, - ) -> Result<(), String> { - assert_insert_seq_in_def(expected_post_lines, &new_char.to_string()) - } - - // pre-insert `val = ` - pub fn assert_insert_seq_in_def( - expected_post_lines: Vec, - new_char_seq: &str, - ) -> Result<(), String> { - let prefix = "val🡲🡲🡲"; - - let full_input = merge_strings(vec![prefix, new_char_seq]); - - let mut expected_post_lines_vec = expected_post_lines.to_vec(); - - let first_line_opt = expected_post_lines_vec.first(); - let val_str = "val = "; - - if let Some(first_line) = first_line_opt { - expected_post_lines_vec[0] = merge_strings(vec![val_str, first_line]); - } else { - expected_post_lines_vec = vec![val_str.to_owned()]; - } - - assert_insert_seq_no_pre(expected_post_lines_vec, &full_input) - } - - pub fn assert_insert_in_def_nls( - expected_post_lines: Vec, - new_char: char, - ) -> Result<(), String> { - assert_insert_seq_in_def(add_nls(expected_post_lines), &new_char.to_string()) - } - - // Create ed_model from pre_lines DSL, do handle_new_char() for every char in new_char_seq, check if modified ed_model has expected - // string representation of code, caret position and active selection. - pub fn assert_insert_seq( - pre_lines: Vec, - expected_post_lines: Vec, - new_char_seq: &str, - ) -> Result<(), String> { - let mut code_str = pre_lines.join("\n").replace('┃', ""); - - let mut model_refs = init_model_refs(); - let code_arena = Bump::new(); - let module_ids = ModuleIds::default(); - - let mut ed_model = ed_model_from_dsl( - &mut code_str, - pre_lines, - &mut model_refs, - &module_ids, - &code_arena, - )?; - - for input_char in new_char_seq.chars() { - if input_char == '🡲' { - ed_model.simple_move_carets_right(1); - } else if input_char == '🡰' { - ed_model.simple_move_carets_left(1); - } else if input_char == '🡱' { - ed_model.simple_move_carets_up(1); - } else { - //dbg!(input_char); - ed_res_to_res(handle_new_char(&input_char, &mut ed_model))?; - } - } - - let mut post_lines = ui_res_to_res(ed_model_to_dsl(&ed_model))?; - strip_header(&mut post_lines); // remove header for clean tests - - assert_eq!(post_lines, expected_post_lines); - - Ok(()) - } - - fn strip_header(lines: &mut Vec) { - lines.drain(0..nr_hello_world_lines()); - } - - pub fn assert_insert_seq_nls( - pre_lines: Vec, - expected_post_lines: Vec, - new_char_seq: &str, - ) -> Result<(), String> { - assert_insert_seq(pre_lines, add_nls(expected_post_lines), new_char_seq) - } - - pub fn assert_insert_seq_ignore(lines: Vec, new_char_seq: &str) -> Result<(), String> { - assert_insert_seq(lines.clone(), lines, new_char_seq) - } - - pub fn assert_insert_seq_ignore_nls( - lines: Vec, - new_char_seq: &str, - ) -> Result<(), String> { - assert_insert_seq_ignore(add_nls(lines), new_char_seq) - } - - pub fn assert_insert_ignore(lines: Vec, new_char: char) -> Result<(), String> { - assert_insert_seq_ignore(lines, &new_char.to_string()) - } - - pub fn assert_insert_ignore_nls(lines: Vec, new_char: char) -> Result<(), String> { - assert_insert_seq_ignore(add_nls(lines), &new_char.to_string()) - } - - // to create Vec from list of &str - macro_rules! ovec { - ( $( $x:expr ),* ) => { - { - vec![ - $( - $x.to_owned(), - )* - ] - } - }; - } - - #[test] - fn test_ignore_basic() -> Result<(), String> { - assert_insert_no_pre(ovec!["┃"], ';')?; - assert_insert_no_pre(ovec!["┃"], '-')?; - assert_insert_no_pre(ovec!["┃"], '_')?; - // extra space because of Expr2::Blank placeholder - assert_insert_in_def_nls(ovec!["┃ "], ';')?; - assert_insert_in_def_nls(ovec!["┃ "], '-')?; - assert_insert_in_def_nls(ovec!["┃ "], '_')?; - - Ok(()) - } - - // add newlines like the editor's formatting would add them - fn add_nls(lines: Vec) -> Vec { - let mut new_lines = lines; - //line(s) between TLD's, extra newline so the user can go to last line add new def there - let mut extra_empty_lines = iter::repeat("".to_owned()) - .take(NEW_LINES_AFTER_DEF) - .collect(); - new_lines.append(&mut extra_empty_lines); - - new_lines - } - - //TODO test_int arch bit limit - #[test] - fn test_int() -> Result<(), String> { - assert_insert_in_def_nls(ovec!["0┃"], '0')?; - assert_insert_in_def_nls(ovec!["1┃"], '1')?; - assert_insert_in_def_nls(ovec!["2┃"], '2')?; - assert_insert_in_def_nls(ovec!["3┃"], '3')?; - assert_insert_in_def_nls(ovec!["4┃"], '4')?; - assert_insert_in_def_nls(ovec!["5┃"], '5')?; - assert_insert_in_def_nls(ovec!["6┃"], '6')?; - assert_insert_in_def_nls(ovec!["7┃"], '7')?; - assert_insert_in_def_nls(ovec!["8┃"], '8')?; - assert_insert_in_def_nls(ovec!["9┃"], '9')?; - - assert_insert(ovec!["val = 1┃"], add_nls(ovec!["val = 19┃"]), '9')?; - assert_insert(ovec!["val = 9876┃"], add_nls(ovec!["val = 98769┃"]), '9')?; - assert_insert(ovec!["val = 10┃"], add_nls(ovec!["val = 103┃"]), '3')?; - assert_insert(ovec!["val = ┃0"], add_nls(ovec!["val = 1┃0"]), '1')?; - assert_insert(ovec!["val = 10000┃"], add_nls(ovec!["val = 100000┃"]), '0')?; - - assert_insert(ovec!["val = ┃1234"], add_nls(ovec!["val = 5┃1234"]), '5')?; - assert_insert(ovec!["val = 1┃234"], add_nls(ovec!["val = 10┃234"]), '0')?; - assert_insert(ovec!["val = 12┃34"], add_nls(ovec!["val = 121┃34"]), '1')?; - assert_insert(ovec!["val = 123┃4"], add_nls(ovec!["val = 1232┃4"]), '2')?; - - Ok(()) - } - - fn merge_strings(strings: Vec<&str>) -> String { - strings - .iter() - .map(|&some_str| some_str.to_owned()) - .collect::>() - .join("") - } - - const IGNORE_CHARS: &str = "{}()[]-><-_\"azAZ:@09"; - const IGNORE_CHARS_NO_NUM: &str = ",{}()[]-><-_\"azAZ:@"; - const IGNORE_NO_LTR: &str = "{\"5"; - const IGNORE_NO_NUM: &str = "a{\""; - - #[test] - fn test_ignore_int() -> Result<(), String> { - assert_insert_seq_ignore_nls(ovec!["vec = ┃0"], IGNORE_CHARS_NO_NUM)?; - assert_insert_seq_ignore_nls(ovec!["vec = ┃7"], IGNORE_CHARS_NO_NUM)?; - - assert_insert_seq_ignore_nls(ovec!["vec = 0┃"], IGNORE_CHARS_NO_NUM)?; - assert_insert_seq_ignore_nls(ovec!["vec = 8┃"], IGNORE_CHARS_NO_NUM)?; - assert_insert_seq_ignore_nls(ovec!["vec = 20┃"], IGNORE_CHARS_NO_NUM)?; - assert_insert_seq_ignore_nls(ovec!["vec = 83┃"], IGNORE_CHARS_NO_NUM)?; - - assert_insert_seq_ignore_nls(ovec!["vec = 1┃0"], IGNORE_CHARS_NO_NUM)?; - assert_insert_seq_ignore_nls(ovec!["vec = 8┃4"], IGNORE_CHARS_NO_NUM)?; - - assert_insert_seq_ignore_nls(ovec!["vec = ┃10"], IGNORE_CHARS_NO_NUM)?; - assert_insert_seq_ignore_nls(ovec!["vec = ┃84"], IGNORE_CHARS_NO_NUM)?; - - assert_insert_seq_ignore_nls(ovec!["vec = 129┃96"], IGNORE_CHARS_NO_NUM)?; - assert_insert_seq_ignore_nls(ovec!["vec = 97┃684"], IGNORE_CHARS_NO_NUM)?; - - assert_insert_ignore_nls(ovec!["vec = 0┃"], '0')?; - assert_insert_ignore_nls(ovec!["vec = 0┃"], '9')?; - assert_insert_ignore_nls(ovec!["vec = ┃0"], '0')?; - assert_insert_ignore_nls(ovec!["vec = ┃1234"], '0')?; - assert_insert_ignore_nls(ovec!["vec = ┃100"], '0')?; - - Ok(()) - } - - #[test] - fn test_string() -> Result<(), String> { - assert_insert_in_def_nls(ovec!["\"┃\""], '"')?; - assert_insert(ovec!["val = \"┃\""], add_nls(ovec!["val = \"a┃\""]), 'a')?; - assert_insert(ovec!["val = \"┃\""], add_nls(ovec!["val = \"{┃\""]), '{')?; - assert_insert(ovec!["val = \"┃\""], add_nls(ovec!["val = \"}┃\""]), '}')?; - assert_insert(ovec!["val = \"┃\""], add_nls(ovec!["val = \"[┃\""]), '[')?; - assert_insert(ovec!["val = \"┃\""], add_nls(ovec!["val = \"]┃\""]), ']')?; - assert_insert(ovec!["val = \"┃\""], add_nls(ovec!["val = \"-┃\""]), '-')?; - assert_insert(ovec!["val = \"┃-\""], add_nls(ovec!["val = \"<┃-\""]), '<')?; - assert_insert(ovec!["val = \"-┃\""], add_nls(ovec!["val = \"->┃\""]), '>')?; - - assert_insert(ovec!["val = \"a┃\""], add_nls(ovec!["val = \"ab┃\""]), 'b')?; - assert_insert( - ovec!["val = \"ab┃\""], - add_nls(ovec!["val = \"abc┃\""]), - 'c', - )?; - assert_insert(ovec!["val = \"┃a\""], add_nls(ovec!["val = \"z┃a\""]), 'z')?; - assert_insert(ovec!["val = \"┃a\""], add_nls(ovec!["val = \" ┃a\""]), ' ')?; - assert_insert( - ovec!["val = \"a┃b\""], - add_nls(ovec!["val = \"az┃b\""]), - 'z', - )?; - assert_insert( - ovec!["val = \"a┃b\""], - add_nls(ovec!["val = \"a ┃b\""]), - ' ', - )?; - - assert_insert( - ovec!["val = \"ab ┃\""], - add_nls(ovec!["val = \"ab {┃\""]), - '{', - )?; - assert_insert( - ovec!["val = \"ab ┃\""], - add_nls(ovec!["val = \"ab }┃\""]), - '}', - )?; - assert_insert( - ovec!["val = \"{ str: 4┃}\""], - add_nls(ovec!["val = \"{ str: 44┃}\""]), - '4', - )?; - assert_insert( - ovec!["val = \"┃ello, hello, hello\""], - add_nls(ovec!["val = \"h┃ello, hello, hello\""]), - 'h', - )?; - assert_insert( - ovec!["val = \"hello┃ hello, hello\""], - add_nls(ovec!["val = \"hello,┃ hello, hello\""]), - ',', - )?; - assert_insert( - ovec!["val = \"hello, hello, hello┃\""], - add_nls(ovec!["val = \"hello, hello, hello.┃\""]), - '.', - )?; - - Ok(()) - } - - #[test] - fn test_ignore_string() -> Result<(), String> { - assert_insert_ignore(add_nls(ovec!["val = ┃\"\""]), 'a')?; - assert_insert_ignore(add_nls(ovec!["val = ┃\"\""]), 'A')?; - assert_insert_ignore(add_nls(ovec!["val = ┃\"\""]), '"')?; - assert_insert_ignore(add_nls(ovec!["val = ┃\"\""]), '{')?; - assert_insert_ignore(add_nls(ovec!["val = ┃\"\""]), '[')?; - assert_insert_ignore(add_nls(ovec!["val = ┃\"\""]), '}')?; - assert_insert_ignore(add_nls(ovec!["val = ┃\"\""]), ']')?; - assert_insert_ignore(add_nls(ovec!["val = ┃\"\""]), '-')?; - - assert_insert_ignore(add_nls(ovec!["val = \"\"┃"]), 'a')?; - assert_insert_ignore(add_nls(ovec!["val = \"\"┃"]), 'A')?; - assert_insert_ignore(add_nls(ovec!["val = \"\"┃"]), '"')?; - assert_insert_ignore(add_nls(ovec!["val = \"\"┃"]), '{')?; - assert_insert_ignore(add_nls(ovec!["val = \"\"┃"]), '[')?; - assert_insert_ignore(add_nls(ovec!["val = \"\"┃"]), '}')?; - assert_insert_ignore(add_nls(ovec!["val = \"\"┃"]), ']')?; - assert_insert_ignore(add_nls(ovec!["val = \"\"┃"]), '-')?; - - assert_insert_ignore(add_nls(ovec!["val = ┃\"a\""]), 'a')?; - assert_insert_ignore(add_nls(ovec!["val = ┃\"a\""]), 'A')?; - assert_insert_ignore(add_nls(ovec!["val = ┃\"a\""]), '"')?; - assert_insert_ignore(add_nls(ovec!["val = ┃\"a\""]), '{')?; - assert_insert_ignore(add_nls(ovec!["val = ┃\"a\""]), '[')?; - assert_insert_ignore(add_nls(ovec!["val = ┃\"a\""]), '}')?; - assert_insert_ignore(add_nls(ovec!["val = ┃\"a\""]), ']')?; - assert_insert_ignore(add_nls(ovec!["val = ┃\"a\""]), '-')?; - - assert_insert_ignore(add_nls(ovec!["val = \"a\"┃"]), 'a')?; - assert_insert_ignore(add_nls(ovec!["val = \"a\"┃"]), 'A')?; - assert_insert_ignore(add_nls(ovec!["val = \"a\"┃"]), '"')?; - assert_insert_ignore(add_nls(ovec!["val = \"a\"┃"]), '{')?; - assert_insert_ignore(add_nls(ovec!["val = \"a\"┃"]), '[')?; - assert_insert_ignore(add_nls(ovec!["val = \"a\"┃"]), '}')?; - assert_insert_ignore(add_nls(ovec!["val = \"a\"┃"]), ']')?; - assert_insert_ignore(add_nls(ovec!["val = \"a\"┃"]), '-')?; - - assert_insert_ignore(add_nls(ovec!["val = ┃\"{ }\""]), 'a')?; - assert_insert_ignore(add_nls(ovec!["val = ┃\"{ }\""]), 'A')?; - assert_insert_ignore(add_nls(ovec!["val = ┃\"{ }\""]), '"')?; - assert_insert_ignore(add_nls(ovec!["val = ┃\"{ }\""]), '{')?; - assert_insert_ignore(add_nls(ovec!["val = ┃\"{ }\""]), '[')?; - assert_insert_ignore(add_nls(ovec!["val = ┃\"{ }\""]), '}')?; - assert_insert_ignore(add_nls(ovec!["val = ┃\"{ }\""]), ']')?; - assert_insert_ignore(add_nls(ovec!["val = ┃\"{ }\""]), '-')?; - - assert_insert_ignore(add_nls(ovec!["val = \"{ }\"┃"]), 'A')?; - assert_insert_ignore(add_nls(ovec!["val = \"{ }\"┃"]), 'a')?; - assert_insert_ignore(add_nls(ovec!["val = \"{ }\"┃"]), '"')?; - assert_insert_ignore(add_nls(ovec!["val = \"{ }\"┃"]), '{')?; - assert_insert_ignore(add_nls(ovec!["val = \"{ }\"┃"]), '[')?; - assert_insert_ignore(add_nls(ovec!["val = \"{ }\"┃"]), '}')?; - assert_insert_ignore(add_nls(ovec!["val = \"{ }\"┃"]), ']')?; - assert_insert_ignore(add_nls(ovec!["val = \"{ }\"┃"]), '-')?; - - assert_insert_ignore(add_nls(ovec!["val = \"[ 1, 2, 3 ]\"┃"]), '{')?; - assert_insert_ignore(add_nls(ovec!["val = ┃\"[ 1, 2, 3 ]\""]), '{')?; - assert_insert_ignore(add_nls(ovec!["val = \"hello, hello, hello\"┃"]), '.')?; - assert_insert_ignore(add_nls(ovec!["val = ┃\"hello, hello, hello\""]), '.')?; - - Ok(()) - } - - #[test] - fn test_record() -> Result<(), String> { - assert_insert_in_def_nls(ovec!["{ ┃ }"], '{')?; - assert_insert_nls(ovec!["val = { ┃ }"], ovec!["val = { a┃ }"], 'a')?; - assert_insert_nls(ovec!["val = { a┃ }"], ovec!["val = { ab┃ }"], 'b')?; - assert_insert_nls(ovec!["val = { a┃ }"], ovec!["val = { a1┃ }"], '1')?; - assert_insert_nls(ovec!["val = { a1┃ }"], ovec!["val = { a1z┃ }"], 'z')?; - assert_insert_nls(ovec!["val = { a1┃ }"], ovec!["val = { a15┃ }"], '5')?; - assert_insert_nls(ovec!["val = { ab┃ }"], ovec!["val = { abc┃ }"], 'c')?; - assert_insert_nls(ovec!["val = { ┃abc }"], ovec!["val = { z┃abc }"], 'z')?; - assert_insert_nls(ovec!["val = { a┃b }"], ovec!["val = { az┃b }"], 'z')?; - assert_insert_nls(ovec!["val = { a┃b }"], ovec!["val = { a9┃b }"], '9')?; - - assert_insert_nls(ovec!["val = { a┃ }"], ovec!["val = { a: ┃ }"], ':')?; - assert_insert_nls(ovec!["val = { abc┃ }"], ovec!["val = { abc: ┃ }"], ':')?; - assert_insert_nls(ovec!["val = { aBc┃ }"], ovec!["val = { aBc: ┃ }"], ':')?; - - assert_insert_seq_nls(ovec!["val = { a┃ }"], ovec!["val = { a: \"┃\" }"], ":\"")?; - assert_insert_seq_nls( - ovec!["val = { abc┃ }"], - ovec!["val = { abc: \"┃\" }"], - ":\"", - )?; - - assert_insert_seq_nls(ovec!["val = { a┃ }"], ovec!["val = { a: 0┃ }"], ":0")?; - assert_insert_seq_nls(ovec!["val = { abc┃ }"], ovec!["val = { abc: 9┃ }"], ":9")?; - assert_insert_seq_nls(ovec!["val = { a┃ }"], ovec!["val = { a: 1000┃ }"], ":1000")?; - assert_insert_seq_nls( - ovec!["val = { abc┃ }"], - ovec!["val = { abc: 98761┃ }"], - ":98761", - )?; - - assert_insert_nls( - ovec!["val = { a: \"┃\" }"], - ovec!["val = { a: \"a┃\" }"], - 'a', - )?; - assert_insert_nls( - ovec!["val = { a: \"a┃\" }"], - ovec!["val = { a: \"ab┃\" }"], - 'b', - )?; - assert_insert_nls( - ovec!["val = { a: \"a┃b\" }"], - ovec!["val = { a: \"az┃b\" }"], - 'z', - )?; - assert_insert_nls( - ovec!["val = { a: \"┃ab\" }"], - ovec!["val = { a: \"z┃ab\" }"], - 'z', - )?; - - assert_insert_nls(ovec!["val = { a: 1┃ }"], ovec!["val = { a: 10┃ }"], '0')?; - assert_insert_nls(ovec!["val = { a: 100┃ }"], ovec!["val = { a: 1004┃ }"], '4')?; - assert_insert_nls(ovec!["val = { a: 9┃76 }"], ovec!["val = { a: 98┃76 }"], '8')?; - assert_insert_nls( - ovec!["val = { a: 4┃691 }"], - ovec!["val = { a: 40┃691 }"], - '0', - )?; - assert_insert_nls( - ovec!["val = { a: 469┃1 }"], - ovec!["val = { a: 4699┃1 }"], - '9', - )?; - - assert_insert_nls( - ovec!["val = { camelCase: \"┃\" }"], - ovec!["val = { camelCase: \"a┃\" }"], - 'a', - )?; - assert_insert_nls( - ovec!["val = { camelCase: \"a┃\" }"], - ovec!["val = { camelCase: \"ab┃\" }"], - 'b', - )?; - - assert_insert_nls( - ovec!["val = { camelCase: 3┃ }"], - ovec!["val = { camelCase: 35┃ }"], - '5', - )?; - assert_insert_nls( - ovec!["val = { camelCase: ┃2 }"], - ovec!["val = { camelCase: 5┃2 }"], - '5', - )?; - assert_insert_nls( - ovec!["val = { camelCase: 10┃2 }"], - ovec!["val = { camelCase: 106┃2 }"], - '6', - )?; - - assert_insert_nls( - ovec!["val = { a┃: \"\" }"], - ovec!["val = { ab┃: \"\" }"], - 'b', - )?; - assert_insert_nls( - ovec!["val = { ┃a: \"\" }"], - ovec!["val = { z┃a: \"\" }"], - 'z', - )?; - assert_insert_nls( - ovec!["val = { ab┃: \"\" }"], - ovec!["val = { abc┃: \"\" }"], - 'c', - )?; - assert_insert_nls( - ovec!["val = { ┃ab: \"\" }"], - ovec!["val = { z┃ab: \"\" }"], - 'z', - )?; - assert_insert_nls( - ovec!["val = { camelCase┃: \"hello\" }"], - ovec!["val = { camelCaseB┃: \"hello\" }"], - 'B', - )?; - assert_insert_nls( - ovec!["val = { camel┃Case: \"hello\" }"], - ovec!["val = { camelZ┃Case: \"hello\" }"], - 'Z', - )?; - assert_insert_nls( - ovec!["val = { ┃camelCase: \"hello\" }"], - ovec!["val = { z┃camelCase: \"hello\" }"], - 'z', - )?; - - assert_insert_nls(ovec!["val = { a┃: 0 }"], ovec!["val = { ab┃: 0 }"], 'b')?; - assert_insert_nls( - ovec!["val = { ┃a: 2100 }"], - ovec!["val = { z┃a: 2100 }"], - 'z', - )?; - assert_insert_nls( - ovec!["val = { ab┃: 9876 }"], - ovec!["val = { abc┃: 9876 }"], - 'c', - )?; - assert_insert_nls( - ovec!["val = { ┃ab: 102 }"], - ovec!["val = { z┃ab: 102 }"], - 'z', - )?; - assert_insert_nls( - ovec!["val = { camelCase┃: 99999 }"], - ovec!["val = { camelCaseB┃: 99999 }"], - 'B', - )?; - assert_insert_nls( - ovec!["val = { camel┃Case: 88156 }"], - ovec!["val = { camelZ┃Case: 88156 }"], - 'Z', - )?; - assert_insert_nls( - ovec!["val = { ┃camelCase: 1 }"], - ovec!["val = { z┃camelCase: 1 }"], - 'z', - )?; - - assert_insert_seq_nls( - ovec!["val = { ┃ }"], - ovec!["val = { camelCase: \"hello┃\" }"], - "camelCase:\"hello", - )?; - assert_insert_seq_nls( - ovec!["val = { ┃ }"], - ovec!["val = { camelCase: 10009┃ }"], - "camelCase:10009", - )?; - - Ok(()) - } - - #[test] - fn test_nested_record() -> Result<(), String> { - assert_insert_seq_nls(ovec!["val = { a┃ }"], ovec!["val = { a: { ┃ } }"], ":{")?; - assert_insert_seq_nls(ovec!["val = { abc┃ }"], ovec!["val = { abc: { ┃ } }"], ":{")?; - assert_insert_seq_nls( - ovec!["val = { camelCase┃ }"], - ovec!["val = { camelCase: { ┃ } }"], - ":{", - )?; - - assert_insert_seq_nls( - ovec!["val = { a: { ┃ } }"], - ovec!["val = { a: { zulu┃ } }"], - "zulu", - )?; - assert_insert_seq_nls( - ovec!["val = { abc: { ┃ } }"], - ovec!["val = { abc: { camelCase┃ } }"], - "camelCase", - )?; - assert_insert_seq_nls( - ovec!["val = { camelCase: { ┃ } }"], - ovec!["val = { camelCase: { z┃ } }"], - "z", - )?; - - assert_insert_seq_nls( - ovec!["val = { a: { zulu┃ } }"], - ovec!["val = { a: { zulu: ┃ } }"], - ":", - )?; - assert_insert_seq_nls( - ovec!["val = { abc: { camelCase┃ } }"], - ovec!["val = { abc: { camelCase: ┃ } }"], - ":", - )?; - assert_insert_seq_nls( - ovec!["val = { camelCase: { z┃ } }"], - ovec!["val = { camelCase: { z: ┃ } }"], - ":", - )?; - - assert_insert_seq_nls( - ovec!["val = { a┃: { zulu } }"], - ovec!["val = { a0┃: { zulu } }"], - "0", - )?; - assert_insert_seq_nls( - ovec!["val = { ab┃c: { camelCase } }"], - ovec!["val = { abz┃c: { camelCase } }"], - "z", - )?; - assert_insert_seq_nls( - ovec!["val = { ┃camelCase: { z } }"], - ovec!["val = { x┃camelCase: { z } }"], - "x", - )?; - - assert_insert_seq_nls( - ovec!["val = { a: { zulu┃ } }"], - ovec!["val = { a: { zulu: \"┃\" } }"], - ":\"", - )?; - assert_insert_seq_nls( - ovec!["val = { abc: { camelCase┃ } }"], - ovec!["val = { abc: { camelCase: \"┃\" } }"], - ":\"", - )?; - assert_insert_seq_nls( - ovec!["val = { camelCase: { z┃ } }"], - ovec!["val = { camelCase: { z: \"┃\" } }"], - ":\"", - )?; - - assert_insert_seq_nls( - ovec!["val = { a: { zulu: \"┃\" } }"], - ovec!["val = { a: { zulu: \"azula┃\" } }"], - "azula", - )?; - assert_insert_seq_nls( - ovec!["val = { a: { zulu: \"az┃a\" } }"], - ovec!["val = { a: { zulu: \"azul┃a\" } }"], - "ul", - )?; - - assert_insert_seq_nls( - ovec!["val = { a: { zulu┃ } }"], - ovec!["val = { a: { zulu: 1┃ } }"], - ":1", - )?; - assert_insert_seq_nls( - ovec!["val = { abc: { camelCase┃ } }"], - ovec!["val = { abc: { camelCase: 0┃ } }"], - ":0", - )?; - assert_insert_seq_nls( - ovec!["val = { camelCase: { z┃ } }"], - ovec!["val = { camelCase: { z: 45┃ } }"], - ":45", - )?; - - assert_insert_seq_nls( - ovec!["val = { a: { zulu: ┃0 } }"], - ovec!["val = { a: { zulu: 4┃0 } }"], - "4", - )?; - assert_insert_seq_nls( - ovec!["val = { a: { zulu: 10┃98 } }"], - ovec!["val = { a: { zulu: 1077┃98 } }"], - "77", - )?; - - assert_insert_seq_nls( - ovec!["val = { a: { zulu┃ } }"], - ovec!["val = { a: { zulu: { ┃ } } }"], - ":{", - )?; - assert_insert_seq_nls( - ovec!["val = { abc: { camelCase┃ } }"], - ovec!["val = { abc: { camelCase: { ┃ } } }"], - ":{", - )?; - assert_insert_seq_nls( - ovec!["val = { camelCase: { z┃ } }"], - ovec!["val = { camelCase: { z: { ┃ } } }"], - ":{", - )?; - - assert_insert_seq_nls( - ovec!["val = { a: { zulu: { ┃ } } }"], - ovec!["val = { a: { zulu: { he┃ } } }"], - "he", - )?; - assert_insert_seq_nls( - ovec!["val = { a: { ┃zulu: { } } }"], - ovec!["val = { a: { x┃zulu: { } } }"], - "x", - )?; - assert_insert_seq_nls( - ovec!["val = { a: { z┃ulu: { } } }"], - ovec!["val = { a: { z9┃ulu: { } } }"], - "9", - )?; - assert_insert_seq_nls( - ovec!["val = { a: { zulu┃: { } } }"], - ovec!["val = { a: { zulu7┃: { } } }"], - "7", - )?; - - assert_insert_seq_nls( - ovec!["val = { a┃: { bcD: { eFgHij: { k15 } } } }"], - ovec!["val = { a4┃: { bcD: { eFgHij: { k15 } } } }"], - "4", - )?; - assert_insert_seq_nls( - ovec!["val = { ┃a: { bcD: { eFgHij: { k15 } } } }"], - ovec!["val = { y┃a: { bcD: { eFgHij: { k15 } } } }"], - "y", - )?; - assert_insert_seq_nls( - ovec!["val = { a: { bcD: { eF┃gHij: { k15 } } } }"], - ovec!["val = { a: { bcD: { eFxyz┃gHij: { k15 } } } }"], - "xyz", - )?; - - assert_insert_seq_nls( - ovec!["val = { ┃ }"], - ovec!["val = { g: { oi: { ng: { d: { e: { e: { p: { camelCase┃ } } } } } } } }"], - "g:{oi:{ng:{d:{e:{e:{p:{camelCase", - )?; - - Ok(()) - } - - fn concat_strings(str_a: &str, str_b: &str) -> String { - let mut string_a = str_a.to_owned(); - - string_a.push_str(str_b); - - string_a - } - - #[test] - fn test_ignore_record() -> Result<(), String> { - assert_insert_seq_ignore_nls(ovec!["val = ┃{ }"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = { }┃"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = {┃ }"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = { ┃}"], IGNORE_CHARS)?; - - assert_insert_seq_ignore_nls(ovec!["val = { ┃ }"], IGNORE_NO_LTR)?; - assert_insert_seq_ignore_nls(ovec!["val = { ┃a }"], IGNORE_NO_LTR)?; - assert_insert_seq_ignore_nls(ovec!["val = { ┃abc }"], IGNORE_NO_LTR)?; - - assert_insert_seq_ignore_nls(ovec!["val = ┃{ a }"], IGNORE_CHARS)?; - assert_insert_seq_nls( - ovec!["val = { a┃ }"], - ovec!["val = { a:┃ }"], - &concat_strings(":🡰", IGNORE_CHARS), - )?; - assert_insert_seq_nls( - ovec!["val = { a┃ }"], - ovec!["val = { a: ┃ }"], - &concat_strings(":🡲", IGNORE_CHARS), - )?; - assert_insert_seq_ignore_nls(ovec!["val = {┃ a }"], IGNORE_CHARS)?; - - assert_insert_seq_ignore_nls(ovec!["val = ┃{ a15 }"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = {┃ a15 }"], IGNORE_CHARS)?; - - assert_insert_seq_ignore_nls(ovec!["val = ┃{ camelCase }"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = {┃ camelCase }"], IGNORE_CHARS)?; - assert_insert_seq_nls( - ovec!["val = { camelCase┃ }"], - ovec!["val = { camelCase:┃ }"], - &concat_strings(":🡰", IGNORE_CHARS), - )?; - assert_insert_seq_nls( - ovec!["val = { camelCase┃ }"], - ovec!["val = { camelCase: ┃ }"], - &concat_strings(":🡲", IGNORE_CHARS), - )?; - - assert_insert_seq_ignore_nls(ovec!["val = ┃{ a: \"\" }"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = {┃ a: \"\" }"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = { a: ┃\"\" }"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = { a: \"\"┃ }"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = { a: \"\" }┃"], IGNORE_CHARS)?; - - assert_insert_seq_ignore_nls(ovec!["val = ┃{ a: 1 }"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = {┃ a: 2 }"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = { a: ┃6 }"], IGNORE_NO_NUM)?; - assert_insert_seq_ignore_nls(ovec!["val = { a: 8┃ }"], IGNORE_NO_NUM)?; - assert_insert_seq_ignore_nls(ovec!["val = { a: 0 }┃"], IGNORE_CHARS)?; - - assert_insert_seq_ignore_nls(ovec!["val = ┃{ camelCase: 1 }"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = {┃ camelCase: 7 }"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = { camelCase: ┃2 }"], IGNORE_NO_NUM)?; - assert_insert_seq_ignore_nls(ovec!["val = { camelCase: 4┃ }"], IGNORE_NO_NUM)?; - assert_insert_seq_ignore_nls(ovec!["val = { camelCase: 9 }┃"], IGNORE_CHARS)?; - - assert_insert_seq_ignore_nls(ovec!["val = ┃{ camelCase: \"\" }"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = {┃ camelCase: \"\" }"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = { camelCase: ┃\"\" }"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = { camelCase: \"\"┃ }"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = { camelCase: \"\" }┃"], IGNORE_CHARS)?; - - assert_insert_seq_ignore_nls(ovec!["val = ┃{ a: \"z\" }"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = {┃ a: \"z\" }"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = { a: ┃\"z\" }"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = { a: \"z\"┃ }"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = { a: \"z\" }┃"], IGNORE_CHARS)?; - - assert_insert_seq_ignore_nls( - ovec!["val = ┃{ a: \"hello, hello.0123456789ZXY{}[]-><-\" }"], - IGNORE_CHARS, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = {┃ a: \"hello, hello.0123456789ZXY{}[]-><-\" }"], - IGNORE_CHARS, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { a: ┃\"hello, hello.0123456789ZXY{}[]-><-\" }"], - IGNORE_CHARS, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { a: \"hello, hello.0123456789ZXY{}[]-><-\"┃ }"], - IGNORE_CHARS, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { a: \"hello, hello.0123456789ZXY{}[]-><-\" }┃"], - IGNORE_CHARS, - )?; - - assert_insert_seq_ignore_nls(ovec!["val = ┃{ a: 915480 }"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = {┃ a: 915480 }"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = { a: ┃915480 }"], IGNORE_NO_NUM)?; - assert_insert_seq_ignore_nls(ovec!["val = { a: 915480┃ }"], IGNORE_NO_NUM)?; - assert_insert_seq_ignore_nls(ovec!["val = { a: 915480 }┃"], IGNORE_CHARS)?; - - Ok(()) - } - - #[test] - fn test_ignore_nested_record() -> Result<(), String> { - assert_insert_seq_ignore_nls(ovec!["val = { a: { ┃ } }"], IGNORE_NO_LTR)?; - assert_insert_seq_ignore_nls(ovec!["val = { a: ┃{ } }"], IGNORE_NO_LTR)?; - assert_insert_seq_ignore_nls(ovec!["val = { a: {┃ } }"], IGNORE_NO_LTR)?; - assert_insert_seq_ignore_nls(ovec!["val = { a: { }┃ }"], IGNORE_NO_LTR)?; - assert_insert_seq_ignore_nls(ovec!["val = { a: { } ┃}"], IGNORE_NO_LTR)?; - assert_insert_seq_ignore_nls(ovec!["val = { a: { } }┃"], IGNORE_NO_LTR)?; - assert_insert_seq_ignore_nls(ovec!["val = { a:┃ { } }"], IGNORE_NO_LTR)?; - assert_insert_seq_ignore_nls(ovec!["val = {┃ a: { } }"], IGNORE_NO_LTR)?; - assert_insert_seq_ignore_nls(ovec!["val = ┃{ a: { } }"], IGNORE_NO_LTR)?; - assert_insert_seq_ignore_nls(ovec!["val = { ┃a: { } }"], "1")?; - - assert_insert_seq_ignore_nls(ovec!["val = { camelCaseB1: {┃ z15a } }"], IGNORE_NO_LTR)?; - assert_insert_seq_ignore_nls(ovec!["val = { camelCaseB1: ┃{ z15a } }"], IGNORE_NO_LTR)?; - assert_insert_seq_nls( - ovec!["val = { camelCaseB1: { z15a┃ } }"], - ovec!["val = { camelCaseB1: { z15a:┃ } }"], - &concat_strings(":🡰", IGNORE_CHARS), - )?; - assert_insert_seq_nls( - ovec!["val = { camelCaseB1: { z15a┃ } }"], - ovec!["val = { camelCaseB1: { z15a: ┃ } }"], - &concat_strings(":🡲", IGNORE_CHARS), - )?; - assert_insert_seq_ignore_nls(ovec!["val = { camelCaseB1:┃ { z15a } }"], IGNORE_NO_LTR)?; - assert_insert_seq_ignore_nls(ovec!["val = {┃ camelCaseB1: { z15a } }"], IGNORE_NO_LTR)?; - assert_insert_seq_ignore_nls(ovec!["val = ┃{ camelCaseB1: { z15a } }"], IGNORE_NO_LTR)?; - assert_insert_seq_ignore_nls(ovec!["val = { ┃camelCaseB1: { z15a } }"], "1")?; - assert_insert_seq_ignore_nls(ovec!["val = { camelCaseB1: { ┃z15a } }"], "1")?; - - assert_insert_seq_ignore_nls( - ovec!["val = { camelCaseB1: { z15a: \"\"┃ } }"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { camelCaseB1: { z15a: ┃\"\" } }"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { camelCaseB1: { z15a:┃ \"\" } }"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { camelCaseB1: { z15a: \"\" ┃} }"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { camelCaseB1: {┃ z15a: \"\" } }"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { camelCaseB1: ┃{ z15a: \"\" } }"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { camelCaseB1: { z15a: \"\" }┃ }"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { camelCaseB1: { z15a: \"\" } ┃}"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { camelCaseB1: { z15a: \"\" } }┃"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { camelCaseB1:┃ { z15a: \"\" } }"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = {┃ camelCaseB1: { z15a: \"\" } }"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = ┃{ camelCaseB1: { z15a: \"\" } }"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls(ovec!["val = { ┃camelCaseB1: { z15a: \"\" } }"], "1")?; - assert_insert_seq_ignore_nls(ovec!["val = { camelCaseB1: { ┃z15a: \"\" } }"], "1")?; - - assert_insert_seq_ignore_nls(ovec!["val = { camelCaseB1: { z15a: 0┃ } }"], IGNORE_NO_NUM)?; - assert_insert_seq_ignore_nls( - ovec!["val = { camelCaseB1: { z15a: ┃123 } }"], - IGNORE_NO_NUM, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { camelCaseB1: { z15a:┃ 999 } }"], - IGNORE_NO_NUM, - )?; - assert_insert_seq_ignore_nls(ovec!["val = { camelCaseB1: { z15a: 80 ┃} }"], IGNORE_NO_NUM)?; - assert_insert_seq_ignore_nls( - ovec!["val = { camelCaseB1: {┃ z15a: 99000 } }"], - IGNORE_NO_NUM, - )?; - assert_insert_seq_ignore_nls(ovec!["val = { camelCaseB1: ┃{ z15a: 12 } }"], IGNORE_NO_NUM)?; - assert_insert_seq_ignore_nls(ovec!["val = { camelCaseB1: { z15a: 7 }┃ }"], IGNORE_NO_NUM)?; - assert_insert_seq_ignore_nls(ovec!["val = { camelCaseB1: { z15a: 98 } ┃}"], IGNORE_NO_NUM)?; - assert_insert_seq_ignore_nls( - ovec!["val = { camelCaseB1: { z15a: 4582 } }┃"], - IGNORE_NO_NUM, - )?; - assert_insert_seq_ignore_nls(ovec!["val = { camelCaseB1:┃ { z15a: 0 } }"], IGNORE_NO_NUM)?; - assert_insert_seq_ignore_nls(ovec!["val = {┃ camelCaseB1: { z15a: 44 } }"], IGNORE_NO_NUM)?; - assert_insert_seq_ignore_nls( - ovec!["val = ┃{ camelCaseB1: { z15a: 100123 } }"], - IGNORE_NO_NUM, - )?; - assert_insert_seq_ignore_nls(ovec!["val = { ┃camelCaseB1: { z15a: 5 } }"], "1")?; - assert_insert_seq_ignore_nls(ovec!["val = { camelCaseB1: { ┃z15a: 6 } }"], "1")?; - - assert_insert_seq_ignore_nls( - ovec!["val = { camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\"┃ } }"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { camelCaseB1: { z15a: ┃\"hello, hello.0123456789ZXY{}[]-><-\" } }"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { camelCaseB1: { z15a:┃ \"hello, hello.0123456789ZXY{}[]-><-\" } }"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" ┃} }"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { camelCaseB1: {┃ z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { camelCaseB1: ┃{ z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" }┃ }"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } ┃}"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }┃"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { camelCaseB1:┃ { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = {┃ camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = ┃{ camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { ┃camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }"], - "1", - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { camelCaseB1: { ┃z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }"], - "1", - )?; - - assert_insert_seq_ignore_nls( - ovec!["val = { g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } } } }┃"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { g: { oi: { ng: { d: { e: {┃ e: { p: { camelCase } } } } } } } }"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { g: { oi: { ng: { d: { e: { e:┃ { p: { camelCase } } } } } } } }"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = {┃ g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } } } }"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = ┃{ g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } } } }"], - IGNORE_NO_LTR, - )?; - assert_insert_seq_ignore_nls( - ovec!["val = { ┃g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } } } }"], - "2", - )?; - Ok(()) - } - - #[test] - fn test_single_elt_list() -> Result<(), String> { - assert_insert_in_def_nls(ovec!["[ ┃ ]"], '[')?; - - assert_insert_nls(ovec!["val = [ ┃ ]"], ovec!["val = [ 0┃ ]"], '0')?; - assert_insert_nls(ovec!["val = [ ┃ ]"], ovec!["val = [ 1┃ ]"], '1')?; - assert_insert_nls(ovec!["val = [ ┃ ]"], ovec!["val = [ 9┃ ]"], '9')?; - - assert_insert_nls(ovec!["val = [ ┃ ]"], ovec!["val = [ \"┃\" ]"], '\"')?; - assert_insert_seq_nls( - ovec!["val = [ ┃ ]"], - ovec!["val = [ \"hello, hello.0123456789ZXY{}[]-><-┃\" ]"], - "\"hello, hello.0123456789ZXY{}[]-><-", - )?; - - assert_insert_nls(ovec!["val = [ ┃ ]"], ovec!["val = [ { ┃ } ]"], '{')?; - assert_insert_seq_nls(ovec!["val = [ ┃ ]"], ovec!["val = [ { a┃ } ]"], "{a")?; - assert_insert_seq_nls( - ovec!["val = [ ┃ ]"], - ovec!["val = [ { camelCase: { zulu: \"nested┃\" } } ]"], - "{camelCase:{zulu:\"nested", - )?; - - assert_insert_nls(ovec!["val = [ ┃ ]"], ovec!["val = [ [ ┃ ] ]"], '[')?; - assert_insert_seq_nls(ovec!["val = [ ┃ ]"], ovec!["val = [ [ [ ┃ ] ] ]"], "[[")?; - assert_insert_seq_nls(ovec!["val = [ ┃ ]"], ovec!["val = [ [ 0┃ ] ]"], "[0")?; - assert_insert_seq_nls( - ovec!["val = [ ┃ ]"], - ovec!["val = [ [ \"abc┃\" ] ]"], - "[\"abc", - )?; - assert_insert_seq_nls( - ovec!["val = [ ┃ ]"], - ovec!["val = [ [ { camelCase: { a: 79000┃ } } ] ]"], - "[{camelCase:{a:79000", - )?; - - Ok(()) - } - - #[test] - fn test_ignore_single_elt_list() -> Result<(), String> { - assert_insert_seq_ignore_nls(ovec!["val = ┃[ ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ ]┃"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [┃ ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ ┃]"], IGNORE_CHARS)?; - - assert_insert_seq_ignore_nls(ovec!["val = ┃[ 0 ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ 0 ]┃"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [┃ 0 ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ 0 ┃]"], IGNORE_CHARS)?; - - assert_insert_seq_ignore_nls(ovec!["val = ┃[ 137 ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ 137 ]┃"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [┃ 137 ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ 137 ┃]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ ┃137 ]"], IGNORE_NO_NUM)?; - assert_insert_seq_ignore_nls(ovec!["val = [ 137┃ ]"], IGNORE_NO_NUM)?; - - assert_insert_seq_ignore_nls(ovec!["val = ┃[ \"teststring\" ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ \"teststring\" ]┃"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [┃ \"teststring\" ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ \"teststring\" ┃]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ ┃\"teststring\" ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ \"teststring\"┃ ]"], IGNORE_CHARS)?; - - assert_insert_seq_ignore_nls(ovec!["val = ┃[ { a: 1 } ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ { a: 1 } ]┃"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [┃ { a: 1 } ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ { a: 1 } ┃]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ ┃{ a: 1 } ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ {┃ a: 1 } ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ { a:┃ 1 } ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ { a: 1 ┃} ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ { a: 1 }┃ ]"], IGNORE_CHARS)?; - - assert_insert_seq_ignore_nls(ovec!["val = ┃[ [ ] ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ [ ] ]┃"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [┃ [ ] ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ [ ] ┃]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ ┃[ ] ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ [ ]┃ ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ [┃ ] ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ [ ┃] ]"], IGNORE_CHARS)?; - - Ok(()) - } - - #[test] - fn test_multi_elt_list() -> Result<(), String> { - assert_insert_seq_nls(ovec!["val = [ ┃ ]"], ovec!["val = [ 0, 1┃ ]"], "0,1")?; - assert_insert_seq_nls( - ovec!["val = [ ┃ ]"], - ovec!["val = [ 987, 6543, 210┃ ]"], - "987,6543,210", - )?; - - assert_insert_seq_nls( - ovec!["val = [ ┃ ]"], - ovec!["val = [ \"a\", \"bcd\", \"EFGH┃\" ]"], - "\"a🡲,\"bcd🡲,\"EFGH", - )?; - - assert_insert_seq_nls( - ovec!["val = [ ┃ ]"], - ovec!["val = [ { a: 1 }, { b: 23 }, { c: 456┃ } ]"], - "{a:1🡲🡲,{b:23🡲🡲,{c:456", - )?; - - assert_insert_seq_nls( - ovec!["val = [ ┃ ]"], - ovec!["val = [ [ 1 ], [ 23 ], [ 456┃ ] ]"], - "[1🡲🡲,[23🡲🡲,[456", - )?; - - // insert element in between - assert_insert_seq_nls( - ovec!["val = [ ┃ ]"], - ovec!["val = [ 0, 2┃, 1 ]"], - "0,1🡰🡰🡰,2", - )?; - assert_insert_seq_nls( - ovec!["val = [ ┃ ]"], - ovec!["val = [ 0, 2, 3┃, 1 ]"], - "0,1🡰🡰🡰,2,3", - )?; - assert_insert_seq_nls( - ovec!["val = [ ┃ ]"], - ovec!["val = [ 0, 3┃, 2, 1 ]"], - "0,1🡰🡰🡰,2🡰🡰🡰,3", - )?; - - assert_insert_seq_nls( - ovec!["val = [ ┃ ]"], - ovec!["val = [ \"abc\", \"f┃\", \"de\" ]"], - "\"abc🡲,\"de🡰🡰🡰🡰🡰,\"f", - )?; - - assert_insert_seq_nls( - ovec!["val = [ ┃ ]"], - ovec!["val = [ [ 0 ], [ 2┃ ], [ 1 ] ]"], - "[0🡲🡲,[1🡰🡰🡰🡰🡰,[2", - )?; - - assert_insert_seq_nls( - ovec!["val = [ ┃ ]"], - ovec!["val = [ { a: 0 }, { a: 2┃ }, { a: 1 } ]"], - "{a:0🡲🡲,{a:1🡰🡰🡰🡰🡰🡰🡰🡰,{a:2", - )?; - - Ok(()) - } - - #[test] - fn test_ignore_multi_elt_list() -> Result<(), String> { - assert_insert_seq_ignore_nls(ovec!["val = ┃[ 0, 1 ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ 0, 1 ]┃"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [┃ 0, 1 ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ 0, 1 ┃]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ 0,┃ 1 ]"], IGNORE_CHARS)?; - - assert_insert_seq_ignore_nls(ovec!["val = ┃[ 123, 56, 7 ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ 123, 56, 7 ]┃"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [┃ 123, 56, 7 ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ 123, 56, 7 ┃]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ 123,┃ 56, 7 ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ 123, 56,┃ 7 ]"], IGNORE_CHARS)?; - - assert_insert_seq_ignore_nls(ovec!["val = ┃[ \"123\", \"56\", \"7\" ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ \"123\", \"56\", \"7\" ]┃"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [┃ \"123\", \"56\", \"7\" ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ \"123\", \"56\", \"7\" ┃]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ \"123\",┃ \"56\", \"7\" ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ \"123\", \"56\",┃ \"7\" ]"], IGNORE_CHARS)?; - - assert_insert_seq_ignore_nls(ovec!["val = ┃[ { a: 0 }, { a: 1 } ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ { a: 0 }, { a: 1 } ]┃"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [┃ { a: 0 }, { a: 1 } ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ { a: 0 }, { a: 1 } ┃]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ { a: 0 },┃ { a: 1 } ]"], IGNORE_CHARS)?; - - assert_insert_seq_ignore_nls(ovec!["val = ┃[ [ 0 ], [ 1 ] ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ [ 0 ], [ 1 ] ]┃"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [┃ [ 0 ], [ 1 ] ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ [ 0 ], [ 1 ] ┃]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ [ 0 ],┃ [ 1 ] ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ ┃[ 0 ], [ 1 ] ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ [ 0 ]┃, [ 1 ] ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ [┃ 0 ], [ 1 ] ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ [ 0 ┃], [ 1 ] ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ [ 0 ], ┃[ 1 ] ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ [ 0 ], [┃ 1 ] ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ [ 0 ], [ 1 ]┃ ]"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["val = [ [ 0 ], [ 1 ┃] ]"], IGNORE_CHARS)?; - - Ok(()) - } - - #[test] - fn test_tld_value() -> Result<(), String> { - assert_insert_nls(ovec!["┃"], ovec!["a┃ = "], 'a')?; - assert_insert_nls(ovec!["┃"], ovec!["m┃ = "], 'm')?; - assert_insert_nls(ovec!["┃"], ovec!["z┃ = "], 'z')?; - - assert_insert_seq_nls(ovec!["┃"], ovec!["ab┃ = "], "ab")?; - // TODO see issue #2548 - //assert_insert_seq_nls(ovec!["┃"], ovec!["mainVal┃ = "], "mainVal")?; - assert_insert_seq_nls(ovec!["┃"], ovec!["camelCase123┃ = "], "camelCase123")?; - assert_insert_seq_nls(ovec!["┃"], ovec!["c137┃ = "], "c137")?; - assert_insert_seq_nls(ovec!["┃"], ovec!["c137Bb┃ = "], "c137Bb")?; - assert_insert_seq_nls(ovec!["┃"], ovec!["bBbb┃ = "], "bBbb")?; - assert_insert_seq_nls(ovec!["┃"], ovec!["cC0Z┃ = "], "cC0Z")?; - - Ok(()) - } - - #[test] - fn test_ignore_tld_value() -> Result<(), String> { - assert_insert_seq_ignore_nls(ovec!["a ┃= 0"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["a =┃ 0"], IGNORE_CHARS)?; - - assert_insert_seq_ignore_nls(ovec!["aBC ┃= 0"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["aBC =┃ 0"], IGNORE_CHARS)?; - - assert_insert_seq_ignore_nls(ovec!["camelCase123 ┃= 0"], IGNORE_CHARS)?; - assert_insert_seq_ignore_nls(ovec!["camelCase123 =┃ 0"], IGNORE_CHARS)?; - - Ok(()) - } - - #[test] - fn test_enter() -> Result<(), String> { - assert_insert_seq( - ovec!["┃"], - add_nls(ovec!["ab = 5", "", "cd = \"good┃\""]), - "ab🡲🡲🡲5\rcd🡲🡲🡲\"good", - )?; - - Ok(()) - } - - // Create ed_model from pre_lines DSL, do handle_new_char for every char in input_seq, do ctrl+shift+up as many times as repeat. - // check if modified ed_model has expected string representation of code, caret position and active selection. - pub fn assert_ctrl_shift_up_repeat( - pre_lines: Vec, - expected_post_lines: Vec, - input_seq: &str, - repeats: usize, - ) -> Result<(), String> { - let mut code_str = pre_lines.join("").replace('┃', ""); - - let mut model_refs = init_model_refs(); - let code_arena = Bump::new(); - let module_ids = ModuleIds::default(); - - let mut ed_model = ed_model_from_dsl( - &mut code_str, - pre_lines, - &mut model_refs, - &module_ids, - &code_arena, - )?; - - for input_char in input_seq.chars() { - if input_char == '🡲' { - ed_model.simple_move_carets_right(1); - } else if input_char == '🡰' { - ed_model.simple_move_carets_left(1); - } else if input_char == '🡱' { - ed_model.simple_move_carets_up(1); - } else { - //dbg!(input_char); - ed_res_to_res(handle_new_char(&input_char, &mut ed_model))?; - } - } - - for _ in 0..repeats { - ed_model.ed_handle_key_down(&ctrl_cmd_shift(), Up, &mut ThreadPool::new(1))?; - } - - let mut post_lines = ui_res_to_res(ed_model_to_dsl(&ed_model))?; - strip_header(&mut post_lines); // remove header for clean tests - - assert_eq!(post_lines, add_nls(expected_post_lines)); - - Ok(()) - } - - pub fn assert_ctrl_shift_up_no_inp( - pre_lines: Vec, - expected_post_lines: Vec, - ) -> Result<(), String> { - assert_ctrl_shift_up_repeat(pre_lines, expected_post_lines, "", 1) - } - - pub fn assert_ctrl_shift_up_repeat_no_inp( - pre_lines: Vec, - expected_post_lines: Vec, - repeats: usize, - ) -> Result<(), String> { - assert_ctrl_shift_up_repeat(pre_lines, expected_post_lines, "", repeats) - } - - #[test] - fn test_ctrl_shift_up_blank() -> Result<(), String> { - // Blank is auto-inserted when creating top level def - assert_ctrl_shift_up_repeat(ovec!["┃"], ovec!["val = ┃❮ ❯"], "val=🡲🡲🡲", 1)?; - assert_ctrl_shift_up_repeat(ovec!["┃"], ovec!["┃❮val = ❯"], "val=🡲🡲🡲", 4)?; - - Ok(()) - } - - #[test] - fn test_ctrl_shift_up_int() -> Result<(), String> { - assert_ctrl_shift_up_no_inp(ovec!["val = 5┃"], ovec!["val = ┃❮5❯"])?; - assert_ctrl_shift_up_repeat_no_inp(ovec!["val = 0┃"], ovec!["┃❮val = 0❯"], 4)?; - assert_ctrl_shift_up_no_inp(ovec!["val = 12345┃"], ovec!["val = ┃❮12345❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = ┃12345"], ovec!["val = ┃❮12345❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = 1┃2345"], ovec!["val = ┃❮12345❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = 12┃345"], ovec!["val = ┃❮12345❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = 123┃45"], ovec!["val = ┃❮12345❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = 1234┃5"], ovec!["val = ┃❮12345❯"])?; - - Ok(()) - } - - #[test] - fn test_ctrl_shift_up_string() -> Result<(), String> { - assert_ctrl_shift_up_no_inp(ovec!["val = \"┃\""], ovec!["val = ┃❮\"\"❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = ┃\"\""], ovec!["val = ┃❮\"\"❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = \"┃0\""], ovec!["val = ┃❮\"0\"❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = \"0┃\""], ovec!["val = ┃❮\"0\"❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = \"abc┃\""], ovec!["val = ┃❮\"abc\"❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = \"ab┃c\""], ovec!["val = ┃❮\"abc\"❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = \"┃abc\""], ovec!["val = ┃❮\"abc\"❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = ┃\"abc\""], ovec!["val = ┃❮\"abc\"❯"])?; - assert_ctrl_shift_up_repeat_no_inp(ovec!["val = \"abc┃\""], ovec!["┃❮val = \"abc\"❯"], 4)?; - assert_ctrl_shift_up_no_inp( - ovec!["val = \"hello, hello.0123456789ZXY{}[]-><-┃\""], - ovec!["val = ┃❮\"hello, hello.0123456789ZXY{}[]-><-\"❯"], - )?; - - assert_ctrl_shift_up_no_inp(ovec!["val = \"\"┃"], ovec!["val = ┃❮\"\"❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = \"abc\"┃"], ovec!["val = ┃❮\"abc\"❯"])?; - - Ok(()) - } - - #[test] - fn test_ctrl_shift_up_record() -> Result<(), String> { - assert_ctrl_shift_up_no_inp(ovec!["val = { ┃ }"], ovec!["val = ┃❮{ }❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = {┃ }"], ovec!["val = ┃❮{ }❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = ┃{ }"], ovec!["val = ┃❮{ }❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = { ┃}"], ovec!["val = ┃❮{ }❯"])?; - assert_ctrl_shift_up_repeat_no_inp(ovec!["val = { ┃ }"], ovec!["┃❮val = { }❯"], 4)?; - assert_ctrl_shift_up_no_inp(ovec!["val = { }┃"], ovec!["val = ┃❮{ }❯"])?; - // TODO uncomment tests once #1649 is fixed - /*assert_ctrl_shift_up_no_inp(ovec!["val = { pear┃ }"], ovec!["val = ┃❮{ pear }❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = { pea┃r }"], ovec!["val = ┃❮{ pear }❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = { p┃ear }"], ovec!["val = ┃❮{ pear }❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = { ┃pear }"], ovec!["val = ┃❮{ pear }❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = {┃ pear }"], ovec!["val = ┃❮{ pear }❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = ┃{ pear }"], ovec!["val = ┃❮{ pear }❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = { pear ┃}"], ovec!["val = ┃❮{ pear }❯"])?; - assert_ctrl_shift_up_repeat(ovec!["val = { pear┃ }"], ovec!["val = ┃❮{ pear }❯"], 3)?; - assert_ctrl_shift_up_no_inp(ovec!["val = { pear }┃"], ovec!["val = ┃❮{ pear }❯"])?; - - assert_ctrl_shift_up_no_inp(ovec!["val = { camelCase123┃ }"], ovec!["val = ┃❮{ camelCase123 }❯"])?;*/ - - assert_ctrl_shift_up_no_inp(ovec!["val = { a: \"┃\" }"], ovec!["val = { a: ┃❮\"\"❯ }"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = { a: ┃\"\" }"], ovec!["val = { a: ┃❮\"\"❯ }"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = { a: \"\"┃ }"], ovec!["val = ┃❮{ a: \"\" }❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = { a: \"\" ┃}"], ovec!["val = ┃❮{ a: \"\" }❯"])?; - assert_ctrl_shift_up_repeat_no_inp( - ovec!["val = { a: \"\" ┃}"], - ovec!["┃❮val = { a: \"\" }❯"], - 3, - )?; - assert_ctrl_shift_up_no_inp(ovec!["val = { a: \"\" }┃"], ovec!["val = ┃❮{ a: \"\" }❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = { a:┃ \"\" }"], ovec!["val = ┃❮{ a: \"\" }❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = { a┃: \"\" }"], ovec!["val = ┃❮{ a: \"\" }❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = { ┃a: \"\" }"], ovec!["val = ┃❮{ a: \"\" }❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = {┃ a: \"\" }"], ovec!["val = ┃❮{ a: \"\" }❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = ┃{ a: \"\" }"], ovec!["val = ┃❮{ a: \"\" }❯"])?; - assert_ctrl_shift_up_repeat_no_inp( - ovec!["val = { a: \"┃\" }"], - ovec!["val = ┃❮{ a: \"\" }❯"], - 2, - )?; - assert_ctrl_shift_up_repeat_no_inp( - ovec!["val = { a: \"┃\" }"], - ovec!["┃❮val = { a: \"\" }❯"], - 4, - )?; - - assert_ctrl_shift_up_no_inp(ovec!["val = { a: 1┃0 }"], ovec!["val = { a: ┃❮10❯ }"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = { a: ┃9 }"], ovec!["val = { a: ┃❮9❯ }"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = { a: 98┃89 }"], ovec!["val = { a: ┃❮9889❯ }"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = { a: 44┃ }"], ovec!["val = ┃❮{ a: 44 }❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = { a: 0 ┃}"], ovec!["val = ┃❮{ a: 0 }❯"])?; - assert_ctrl_shift_up_repeat_no_inp( - ovec!["val = { a: 123 ┃}"], - ovec!["┃❮val = { a: 123 }❯"], - 3, - )?; - assert_ctrl_shift_up_no_inp(ovec!["val = { a: 96 }┃"], ovec!["val = ┃❮{ a: 96 }❯"])?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { a:┃ 985600 }"], - ovec!["val = ┃❮{ a: 985600 }❯"], - )?; - assert_ctrl_shift_up_no_inp(ovec!["val = { a┃: 5648 }"], ovec!["val = ┃❮{ a: 5648 }❯"])?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { ┃a: 1000000 }"], - ovec!["val = ┃❮{ a: 1000000 }❯"], - )?; - assert_ctrl_shift_up_no_inp(ovec!["val = {┃ a: 1 }"], ovec!["val = ┃❮{ a: 1 }❯"])?; - assert_ctrl_shift_up_no_inp( - ovec!["val = ┃{ a: 900600 }"], - ovec!["val = ┃❮{ a: 900600 }❯"], - )?; - assert_ctrl_shift_up_repeat_no_inp( - ovec!["val = { a: 10┃000 }"], - ovec!["val = ┃❮{ a: 10000 }❯"], - 2, - )?; - assert_ctrl_shift_up_repeat_no_inp( - ovec!["val = { a: ┃45 }"], - ovec!["┃❮val = { a: 45 }❯"], - 4, - )?; - - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: \"de┃\" }"], - ovec!["val = { abc: ┃❮\"de\"❯ }"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: \"d┃e\" }"], - ovec!["val = { abc: ┃❮\"de\"❯ }"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: \"┃de\" }"], - ovec!["val = { abc: ┃❮\"de\"❯ }"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: ┃\"de\" }"], - ovec!["val = { abc: ┃❮\"de\"❯ }"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: \"de\"┃ }"], - ovec!["val = ┃❮{ abc: \"de\" }❯"], - )?; - assert_ctrl_shift_up_repeat_no_inp( - ovec!["val = { abc: \"d┃e\" }"], - ovec!["val = ┃❮{ abc: \"de\" }❯"], - 2, - )?; - assert_ctrl_shift_up_repeat_no_inp( - ovec!["val = { abc: \"d┃e\" }"], - ovec!["┃❮val = { abc: \"de\" }❯"], - 3, - )?; - - assert_ctrl_shift_up_no_inp( - ovec!["val = { camelCase123: \"hello, hello.012┃3456789ZXY{}[]-><-\" }"], - ovec!["val = { camelCase123: ┃❮\"hello, hello.0123456789ZXY{}[]-><-\"❯ }"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { camel┃Case123: \"hello, hello.0123456789ZXY{}[]-><-\" }"], - ovec!["val = ┃❮{ camelCase123: \"hello, hello.0123456789ZXY{}[]-><-\" }❯"], - )?; - assert_ctrl_shift_up_repeat_no_inp( - ovec!["val = { camelCase123: \"hello, hello┃.0123456789ZXY{}[]-><-\" }"], - ovec!["val = ┃❮{ camelCase123: \"hello, hello.0123456789ZXY{}[]-><-\" }❯"], - 2, - )?; - - Ok(()) - } - - #[test] - fn test_ctrl_shift_up_nested_record() -> Result<(), String> { - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: { ┃ } }"], - ovec!["val = { abc: ┃❮{ }❯ }"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: {┃ } }"], - ovec!["val = { abc: ┃❮{ }❯ }"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: ┃{ } }"], - ovec!["val = { abc: ┃❮{ }❯ }"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: { ┃} }"], - ovec!["val = { abc: ┃❮{ }❯ }"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: { }┃ }"], - ovec!["val = ┃❮{ abc: { } }❯"], - )?; - - // TODO uncomment tests once #1649 is fixed - /*assert_ctrl_shift_up_no_inp(ovec!["val = { abc: { ┃d } }"], ovec!["val = { abc: ┃❮{ d }❯ }"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = { abc: {┃ d } }"], ovec!["val = { abc: ┃❮{ d }❯ }"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = { abc: ┃{ d } }"], ovec!["val = { abc: ┃❮{ d }❯ }"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = { abc: { d ┃} }"], ovec!["val = { abc: ┃❮{ d }❯ }"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = { abc: { d┃e } }"], ovec!["val = { abc: ┃❮{ de }❯ }"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = { abc: { d }┃ }"], ovec!["val = ┃❮{ abc: { d } }❯"])?; - assert_ctrl_shift_up_no_inp(ovec!["val = ┃{ abc: { d } }"], ovec!["val = ┃❮{ abc: { d } }❯"])?;*/ - - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: { de: { ┃ } } }"], - ovec!["val = { abc: { de: ┃❮{ }❯ } }"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: { de: ┃{ } } }"], - ovec!["val = { abc: { de: ┃❮{ }❯ } }"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: { de: { }┃ } }"], - ovec!["val = { abc: ┃❮{ de: { } }❯ }"], - )?; - - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: { de: \"┃\" } }"], - ovec!["val = { abc: { de: ┃❮\"\"❯ } }"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: { de: ┃\"\" } }"], - ovec!["val = { abc: { de: ┃❮\"\"❯ } }"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: { de: \"\"┃ } }"], - ovec!["val = { abc: ┃❮{ de: \"\" }❯ }"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: { de: \"f g┃\" } }"], - ovec!["val = { abc: { de: ┃❮\"f g\"❯ } }"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: { de┃: \"f g\" } }"], - ovec!["val = { abc: ┃❮{ de: \"f g\" }❯ }"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: {┃ de: \"f g\" } }"], - ovec!["val = { abc: ┃❮{ de: \"f g\" }❯ }"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: { de: \"f g\" ┃} }"], - ovec!["val = { abc: ┃❮{ de: \"f g\" }❯ }"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: { de: \"f g\" }┃ }"], - ovec!["val = ┃❮{ abc: { de: \"f g\" } }❯"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = ┃{ abc: { de: \"f g\" } }"], - ovec!["val = ┃❮{ abc: { de: \"f g\" } }❯"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: { de: \"f g\" } }┃"], - ovec!["val = ┃❮{ abc: { de: \"f g\" } }❯"], - )?; - - assert_ctrl_shift_up_repeat_no_inp( - ovec!["val = { abc: { de: \"f g┃\" } }"], - ovec!["val = { abc: ┃❮{ de: \"f g\" }❯ }"], - 2, - )?; - assert_ctrl_shift_up_repeat_no_inp( - ovec!["val = { abc: { de: ┃\"f g\" } }"], - ovec!["val = ┃❮{ abc: { de: \"f g\" } }❯"], - 3, - )?; - assert_ctrl_shift_up_repeat_no_inp( - ovec!["val = { abc: { de: ┃\"f g\" } }"], - ovec!["┃❮val = { abc: { de: \"f g\" } }❯"], - 4, - )?; - - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: { de: ┃951 } }"], - ovec!["val = { abc: { de: ┃❮951❯ } }"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: { de: 11┃0 } }"], - ovec!["val = { abc: { de: ┃❮110❯ } }"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: { de: 444┃ } }"], - ovec!["val = { abc: ┃❮{ de: 444 }❯ }"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: { de┃: 99 } }"], - ovec!["val = { abc: ┃❮{ de: 99 }❯ }"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: {┃ de: 0 } }"], - ovec!["val = { abc: ┃❮{ de: 0 }❯ }"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: { de: 230 ┃} }"], - ovec!["val = { abc: ┃❮{ de: 230 }❯ }"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: { de: 7 }┃ }"], - ovec!["val = ┃❮{ abc: { de: 7 } }❯"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = ┃{ abc: { de: 1 } }"], - ovec!["val = ┃❮{ abc: { de: 1 } }❯"], - )?; - assert_ctrl_shift_up_no_inp( - ovec!["val = { abc: { de: 111111 } }┃"], - ovec!["val = ┃❮{ abc: { de: 111111 } }❯"], - )?; - - assert_ctrl_shift_up_repeat_no_inp( - ovec!["val = { abc: { de: 1┃5 } }"], - ovec!["val = { abc: ┃❮{ de: 15 }❯ }"], - 2, - )?; - assert_ctrl_shift_up_repeat_no_inp( - ovec!["val = { abc: { de: ┃55 } }"], - ovec!["val = ┃❮{ abc: { de: 55 } }❯"], - 3, - )?; - assert_ctrl_shift_up_repeat_no_inp( - ovec!["val = { abc: { de: ┃400 } }"], - ovec!["┃❮val = { abc: { de: 400 } }❯"], - 5, - )?; - - // TODO uncomment tests once #1649 is fixed - /*assert_ctrl_shift_up_repeat_no_inp( - ovec!["val = { g: { oi: { ng: { d: { e: { e: { p: { camelCase┃ } } } } } } } }"], - ovec!["val = { g: { oi: { ng: { d: ┃❮{ e: { e: { p: { camelCase } } } }❯ } } } }"], - 4, - )?; - assert_ctrl_shift_up_repeat_no_inp( - ovec!["val = { g: { oi: { ng: { d: { e: { e: { p: { camelCase┃ } } } } } } } }"], - ovec!["val = { g: ┃❮{ oi: { ng: { d: { e: { e: { p: { camelCase } } } } } } }❯ }"], - 7, - )?; - assert_ctrl_shift_up_repeat_no_inp( - ovec!["val = { g: { oi: { ng: { d: { e: { e: { p: { camelCase┃ } } } } } } } }"], - ovec!["val = ┃❮{ g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } } } }❯"], - 9, - )?;*/ - - Ok(()) - } - - // Create ed_model from pre_lines DSL, do handle_new_char() with new_char_seq, select current Expr2, - // check if generated tooltips match expected_tooltips. - pub fn assert_type_tooltips_seq( - pre_lines: Vec, - expected_tooltips: Vec, - new_char_seq: &str, - ) -> Result<(), String> { - let mut code_str = pre_lines.join("").replace('┃', ""); - - let mut model_refs = init_model_refs(); - let code_arena = Bump::new(); - let module_ids = ModuleIds::default(); - - let mut ed_model = ed_model_from_dsl( - &mut code_str, - pre_lines, - &mut model_refs, - &module_ids, - &code_arena, - )?; - - for input_char in new_char_seq.chars() { - if input_char == '🡲' { - ed_model.simple_move_carets_right(1); - } else { - ed_res_to_res(handle_new_char(&input_char, &mut ed_model))?; - } - } - - for expected_tooltip in expected_tooltips.iter() { - ed_model.select_expr()?; - - let created_tooltip = ed_model.selected_block_opt.unwrap().type_str; - - assert_eq!( - created_tooltip.as_str(ed_model.module.env.pool), - *expected_tooltip - ); - } - - Ok(()) - } - - // Create ed_model from pre_lines DSL, do handle_new_char() with new_char, select current Expr2, - // check if generated tooltip matches expected_tooltip. - pub fn assert_type_tooltip( - pre_lines: Vec, - expected_tooltip: &str, - new_char: char, - ) -> Result<(), String> { - assert_type_tooltips_seq(pre_lines, ovec![expected_tooltip], &new_char.to_string()) - } - - pub fn assert_type_tooltip_clean( - lines: Vec, - expected_tooltip: &str, - ) -> Result<(), String> { - assert_type_tooltips_seq(lines, ovec![expected_tooltip], "") - } - - // When doing ctrl+shift+up multiple times we select the surrounding expression every time, - // every new selection should have the correct tooltip - pub fn assert_type_tooltips_clean( - lines: Vec, - expected_tooltips: Vec, - ) -> Result<(), String> { - assert_type_tooltips_seq(lines, expected_tooltips, "") - } - - #[test] - fn test_type_tooltip() -> Result<(), String> { - assert_type_tooltip_clean(ovec!["val = ┃5"], "Num *")?; - assert_type_tooltip_clean(ovec!["val = 42┃"], "Num *")?; - assert_type_tooltip_clean(ovec!["val = 13┃7"], "Num *")?; - - assert_type_tooltip_clean(ovec!["val = \"┃abc\""], "Str")?; - assert_type_tooltip_clean(ovec!["val = ┃\"abc\""], "Str")?; - assert_type_tooltip_clean(ovec!["val = \"abc\"┃"], "Str")?; - - assert_type_tooltip_clean(ovec!["val = { ┃ }"], "{}")?; - assert_type_tooltip_clean(ovec!["val = { a: \"abc\" }┃"], "{ a : Str }")?; - assert_type_tooltip_clean(ovec!["val = { ┃a: 0 }"], "{ a : Num * }")?; - assert_type_tooltip_clean(ovec!["val = { ┃z: { } }"], "{ z : {} }")?; - assert_type_tooltip_clean(ovec!["val = { camelCase: ┃0 }"], "Num *")?; - - assert_type_tooltips_seq(ovec!["┃"], ovec!["*"], "val=🡲🡲🡲")?; - assert_type_tooltips_seq( - ovec!["┃"], - ovec!["*", "{ a : * }", "{ a : * }"], - "val=🡲🡲🡲{a:", - )?; - - assert_type_tooltips_clean( - ovec!["val = { camelCase: ┃0 }"], - ovec!["Num *", "{ camelCase : Num * }"], - )?; - assert_type_tooltips_clean( - ovec!["val = { a: { b: { c: \"hello┃, hello.0123456789ZXY{}[]-><-\" } } }"], - ovec![ - "Str", - "{ c : Str }", - "{ b : { c : Str } }", - "{ a : { b : { c : Str } } }" - ], - )?; - - Ok(()) - } - - #[test] - fn test_type_tooltip_list() -> Result<(), String> { - assert_type_tooltip_clean(ovec!["val = [ ┃ ]"], "List *")?; - assert_type_tooltips_clean(ovec!["val = [ ┃0 ]"], ovec!["Num *", "List (Num *)"])?; - assert_type_tooltips_clean( - ovec!["val = [ [ ┃0 ] ]"], - ovec!["Num *", "List (Num *)", "List (List (Num *))"], - )?; - - assert_type_tooltips_clean( - ovec!["val = [ [ [ \"ab┃c\" ] ] ]"], - ovec![ - "Str", - "List Str", - "List (List Str)", - "List (List (List Str))" - ], - )?; - assert_type_tooltips_clean( - ovec!["val = [ [ { a┃: 1 } ] ]"], - ovec![ - "{ a : Num * }", - "List { a : Num * }", - "List (List { a : Num * })" - ], - )?; - - // multi element lists - assert_type_tooltips_clean(ovec!["val = [ ┃1, 2, 3 ]"], ovec!["Num *", "List (Num *)"])?; - assert_type_tooltips_clean( - ovec!["val = [ \"┃abc\", \"de\", \"f\" ]"], - ovec!["Str", "List Str"], - )?; - assert_type_tooltips_clean( - ovec!["val = [ { a:┃ 1 }, { a: 12 }, { a: 444 } ]"], - ovec!["{ a : Num * }", "List { a : Num * }"], - )?; - - Ok(()) - } - - #[test] - fn test_type_tooltip_mismatch() -> Result<(), String> { - assert_type_tooltips_clean( - ovec!["val = [ 1, \"ab┃c\" ]"], - ovec!["Str", "List "], - )?; - assert_type_tooltips_clean( - ovec!["val = [ \"abc\", 5┃0 ]"], - ovec!["Num *", "List "], - )?; - - assert_type_tooltips_clean( - ovec!["val = [ { a: 0 }, { a: \"0┃\" } ]"], - ovec!["Str", "{ a : Str }", "List "], - )?; - - assert_type_tooltips_clean( - ovec!["val = [ [ 0, 1, \"2\" ], [ 3, 4, 5 ┃] ]"], - ovec!["List (Num *)", "List (List )"], - )?; - - Ok(()) - } - - type ModelMoveCaretFun = fn(&mut EdModel<'_>, &Modifiers) -> UIResult<()>; - - // Create ed_model from pre_lines DSL, do ctrl+shift+up as many times as repeat. Then move the caret by executing - // move_caret_fun. Next check if modified ed_model has expected string representation of code, caret position and - // active selection. - fn assert_ctrl_shift_up_move( - pre_lines: Vec, - expected_post_lines: Vec, - repeats: usize, - move_caret_fun: ModelMoveCaretFun, - ) -> Result<(), String> { - let mut code_str = pre_lines.join("").replace('┃', ""); - - let mut model_refs = init_model_refs(); - let code_arena = Bump::new(); - let module_ids = ModuleIds::default(); - - let mut ed_model = ed_model_from_dsl( - &mut code_str, - pre_lines, - &mut model_refs, - &module_ids, - &code_arena, - )?; - - for _ in 0..repeats { - ed_model.ed_handle_key_down(&ctrl_cmd_shift(), Up, &mut ThreadPool::new(1))?; - } - - move_caret_fun(&mut ed_model, &no_mods())?; - - let mut post_lines = ui_res_to_res(ed_model_to_dsl(&ed_model))?; - strip_header(&mut post_lines); // remove header for clean tests - - assert_eq!(post_lines, expected_post_lines); - - Ok(()) - } - - fn assert_ctrl_shift_up_move_nls( - pre_lines: Vec, - expected_post_lines: Vec, - repeats: usize, - move_caret_fun: ModelMoveCaretFun, - ) -> Result<(), String> { - assert_ctrl_shift_up_move( - pre_lines, - add_nls(expected_post_lines), - repeats, - move_caret_fun, - ) - } - - fn assert_ctrl_shift_single_up_move( - pre_lines: Vec, - expected_post_lines: Vec, - move_caret_fun: ModelMoveCaretFun, - ) -> Result<(), String> { - assert_ctrl_shift_up_move(pre_lines, expected_post_lines, 1, move_caret_fun) - } - - fn assert_ctrl_shift_single_up_move_nls( - pre_lines: Vec, - expected_post_lines: Vec, - move_caret_fun: ModelMoveCaretFun, - ) -> Result<(), String> { - assert_ctrl_shift_single_up_move(pre_lines, add_nls(expected_post_lines), move_caret_fun) - } - - // because complex lifetime stuff - macro_rules! move_up { - () => { - |ed_model, modifiers| EdModel::move_caret_up(ed_model, modifiers) - }; - } - macro_rules! move_down { - () => { - |ed_model, modifiers| EdModel::move_caret_down(ed_model, modifiers) - }; - } - macro_rules! move_home { - () => { - |ed_model, modifiers| EdModel::move_caret_home(ed_model, modifiers) - }; - } - macro_rules! move_end { - () => { - |ed_model, modifiers| EdModel::move_caret_end(ed_model, modifiers) - }; - } - - #[test] - fn test_ctrl_shift_up_move_int() -> Result<(), String> { - assert_ctrl_shift_single_up_move_nls(ovec!["val = ┃0"], ovec!["val = 0┃"], move_down!())?; - assert_ctrl_shift_single_up_move_nls( - ovec!["val = ┃9654"], - ovec!["val = ┃9654"], - move_up!(), - )?; - assert_ctrl_shift_single_up_move_nls( - ovec!["val = ┃100546"], - ovec!["val = 100546┃"], - move_end!(), - )?; - - Ok(()) - } - - #[test] - fn test_ctrl_shift_up_move_string() -> Result<(), String> { - assert_ctrl_shift_single_up_move_nls( - ovec!["val = ┃\"\""], - ovec!["val = \"\"┃"], - move_down!(), - )?; - assert_ctrl_shift_single_up_move_nls( - ovec!["val = ┃\"abc\""], - ovec!["val = ┃\"abc\""], - move_up!(), - )?; - assert_ctrl_shift_single_up_move_nls( - ovec!["val = ┃\"hello, hello.0123456789ZXY{}[]-><-\""], - ovec!["val = \"hello, hello.0123456789ZXY{}[]-><-\"┃"], - move_end!(), - )?; - - Ok(()) - } - - #[test] - fn test_ctrl_shift_up_move_record() -> Result<(), String> { - assert_ctrl_shift_single_up_move_nls( - ovec!["val = ┃{ }"], - ovec!["┃val = { }"], - move_home!(), - )?; - // TODO uncomment tests once #1649 is fixed. - //assert_ctrl_shift_single_up_move(ovec!["┃{ a }"], ovec!["{ a }┃"], move_down!())?; - //assert_ctrl_shift_single_up_move(ovec!["┃{ a: { b } }"], ovec!["{ a: { b } }┃"], move_right!())?; - assert_ctrl_shift_single_up_move_nls( - ovec!["val = { a: { ┃ } }"], - ovec!["val = { a: { } }┃"], - move_end!(), - )?; - assert_ctrl_shift_up_move_nls( - ovec!["val = { a: { b: { ┃ } } }"], - ovec!["val = { a: ┃{ b: { } } }"], - 2, - move_up!(), - )?; - assert_ctrl_shift_up_move_nls( - ovec!["val = { camelCase: { cC123: \"hello┃, hello.0123456789ZXY{}[]-><-\" } }"], - ovec!["val = { camelCase: { cC123: \"hello, hello.0123456789ZXY{}[]-><-\" }┃ }"], - 2, - move_down!(), - )?; - - assert_ctrl_shift_up_move_nls( - ovec!["val = { camelCase: { cC123: 9┃5 } }"], - ovec!["val = { camelCase: { cC123: 95 }┃ }"], - 2, - move_down!(), - )?; - - Ok(()) - } - - // Create ed_model from pre_lines DSL, do ctrl+shift+up as many times as repeat. Then do backspace. - // Next check if modified ed_model has expected string representation of code, caret position and - // active selection. - fn assert_ctrl_shift_up_backspace( - pre_lines: Vec, - expected_post_lines: Vec, - repeats: usize, - ) -> Result<(), String> { - let mut code_str = pre_lines.join("").replace('┃', ""); - - let mut model_refs = init_model_refs(); - let code_arena = Bump::new(); - let module_ids = ModuleIds::default(); - - let mut ed_model = ed_model_from_dsl( - &mut code_str, - pre_lines, - &mut model_refs, - &module_ids, - &code_arena, - )?; - - for _ in 0..repeats { - ed_model.ed_handle_key_down(&ctrl_cmd_shift(), Up, &mut ThreadPool::new(1))?; - } - - handle_new_char(&'\u{8}', &mut ed_model)?; // \u{8} is the char for backspace on linux - - let mut post_lines = ui_res_to_res(ed_model_to_dsl(&ed_model))?; - strip_header(&mut post_lines); - - assert_eq!(post_lines, expected_post_lines); - - Ok(()) - } - fn assert_ctrl_shift_up_backspace_nls( - pre_lines: Vec, - expected_post_lines: Vec, - repeats: usize, - ) -> Result<(), String> { - assert_ctrl_shift_up_backspace(pre_lines, add_nls(expected_post_lines), repeats) - } - - fn assert_ctrl_shift_single_up_backspace( - pre_lines: Vec, - expected_post_lines: Vec, - ) -> Result<(), String> { - assert_ctrl_shift_up_backspace(pre_lines, expected_post_lines, 1) - } - - fn assert_ctrl_shift_single_up_backspace_nls( - pre_lines: Vec, - expected_post_lines: Vec, - ) -> Result<(), String> { - assert_ctrl_shift_single_up_backspace(pre_lines, add_nls(expected_post_lines)) - } - - #[test] - fn test_ctrl_shift_up_backspace_int() -> Result<(), String> { - // Blank is inserted when root is deleted - assert_ctrl_shift_single_up_backspace_nls(ovec!["val = 95┃21"], ovec!["val = ┃ "])?; - assert_ctrl_shift_single_up_backspace_nls(ovec!["val = 0┃"], ovec!["val = ┃ "])?; - assert_ctrl_shift_single_up_backspace_nls(ovec!["val = ┃10000"], ovec!["val = ┃ "])?; - - Ok(()) - } - - #[test] - fn test_ctrl_shift_up_backspace_string() -> Result<(), String> { - // Blank is inserted when root is deleted - assert_ctrl_shift_single_up_backspace_nls(ovec!["val = \"┃\""], ovec!["val = ┃ "])?; - assert_ctrl_shift_single_up_backspace_nls(ovec!["val = \"\"┃"], ovec!["val = ┃ "])?; - assert_ctrl_shift_single_up_backspace_nls(ovec!["val = ┃\"abc\""], ovec!["val = ┃ "])?; - assert_ctrl_shift_single_up_backspace_nls( - ovec!["val = \"hello┃, hello.0123456789ZXY{}[]-><-\""], - ovec!["val = ┃ "], - )?; - - Ok(()) - } - - #[test] - fn test_ctrl_shift_up_backspace_record() -> Result<(), String> { - // Blank is inserted when root of Expr2 is deleted - assert_ctrl_shift_single_up_backspace_nls(ovec!["val = {┃ }"], ovec!["val = ┃ "])?; - - // TODO: uncomment tests, once issue #1649 is fixed - //assert_ctrl_shift_single_up_backspace(ovec!["{ a┃ }"], ovec!["┃ "])?; - //assert_ctrl_shift_single_up_backspace(ovec!["{ a: { b }┃ }"], ovec!["┃ "])?; - assert_ctrl_shift_single_up_backspace_nls( - ovec!["val = { a: \"b cd\"┃ }"], - ovec!["val = ┃ "], - )?; - - //assert_ctrl_shift_single_up_backspace(ovec!["{ a: ┃{ b } }"], ovec!["{ a: ┃ }"])?; - assert_ctrl_shift_single_up_backspace_nls( - ovec!["val = { a: \"┃b cd\" }"], - ovec!["val = { a: ┃ }"], - )?; - assert_ctrl_shift_single_up_backspace_nls( - ovec!["val = { a: ┃12 }"], - ovec!["val = { a: ┃ }"], - )?; - /*assert_ctrl_shift_single_up_backspace( - ovec!["{ g: { oi: { ng: { d: { ┃e: { e: { p: { camelCase } } } } } } } }"], - ovec!["{ g: { oi: { ng: { d: ┃ } } } }"], - )?;*/ - - assert_ctrl_shift_up_backspace_nls( - ovec!["val = { a: { b: { c: \"abc┃ \" } } }"], - ovec!["val = { a: { b: ┃ } }"], - 2, - )?; - assert_ctrl_shift_up_backspace_nls( - ovec!["val = { a: { b: { c: 100┃000 } } }"], - ovec!["val = { a: { b: ┃ } }"], - 2, - )?; - assert_ctrl_shift_up_backspace_nls( - ovec!["val = { a: { b: { c: {┃ } } } }"], - ovec!["val = { a: { b: ┃ } }"], - 2, - )?; - /*assert_ctrl_shift_up_backspace( - ovec!["{ g: { oi: { ng: { d: { e: { e: { p┃: { camelCase } } } } } } } }"], - ovec!["{ g: ┃ }"], - 6, - )?;*/ - - Ok(()) - } -} diff --git a/crates/editor/src/editor/mvc/ed_view.rs b/crates/editor/src/editor/mvc/ed_view.rs deleted file mode 100644 index 7998439d03..0000000000 --- a/crates/editor/src/editor/mvc/ed_view.rs +++ /dev/null @@ -1,199 +0,0 @@ -use super::ed_model::EdModel; -use crate::editor::config::Config; -use crate::editor::ed_error::EdResult; -use crate::editor::mvc::ed_model::SelectedBlock; -use crate::editor::render_ast::build_code_graphics; -use crate::editor::render_debug::build_debug_graphics; -use crate::editor::resources::strings::START_TIP; -use crate::graphics::primitives::rect::Rect; -use crate::graphics::primitives::text::{owned_section_from_text, Text}; -use crate::ui::text::caret_w_select::make_caret_rect; -use crate::ui::text::caret_w_select::make_selection_rect; -use crate::ui::text::caret_w_select::CaretWSelect; -use crate::ui::text::selection::Selection; -use crate::ui::tooltip::ToolTip; -use crate::ui::ui_error::MissingGlyphDimsSnafu; -use cgmath::Vector2; -use roc_ast::mem_pool::pool::Pool; -use snafu::OptionExt; -use winit::dpi::PhysicalSize; - -#[derive(Debug)] -pub struct RenderedWgpu { - pub text_sections_behind: Vec, // displayed in front of rect_behind, behind everything else - pub text_sections_front: Vec, // displayed in front of everything - pub rects_behind: Vec, // displayed at lowest depth - pub rects_front: Vec, // displayed in front of text_sections_behind, behind text_sections_front -} - -impl RenderedWgpu { - pub fn new() -> Self { - Self { - text_sections_behind: Vec::new(), - text_sections_front: Vec::new(), - rects_behind: Vec::new(), - rects_front: Vec::new(), - } - } - - pub fn add_text_behind(&mut self, new_text_section: glyph_brush::OwnedSection) { - self.text_sections_behind.push(new_text_section); - } - - pub fn add_text_front(&mut self, new_text_section: glyph_brush::OwnedSection) { - self.text_sections_front.push(new_text_section); - } - - pub fn add_rect_behind(&mut self, new_rect: Rect) { - self.rects_behind.push(new_rect); - } - - pub fn add_rects_behind(&mut self, new_rects: Vec) { - self.rects_behind.extend(new_rects); - } - - pub fn add_rect_front(&mut self, new_rect: Rect) { - self.rects_front.push(new_rect); - } - - pub fn extend(&mut self, rendered_wgpu: RenderedWgpu) { - self.text_sections_behind - .extend(rendered_wgpu.text_sections_behind); - self.text_sections_front - .extend(rendered_wgpu.text_sections_front); - self.rects_behind.extend(rendered_wgpu.rects_behind); - self.rects_front.extend(rendered_wgpu.rects_front); - } -} - -// create text and rectangles based on EdModel's markup_root -pub fn model_to_wgpu( - ed_model: &mut EdModel, - size: &PhysicalSize, - txt_coords: Vector2, - config: &Config, -) -> EdResult { - let glyph_dim_rect = ed_model - .glyph_dim_rect_opt - .context(MissingGlyphDimsSnafu {})?; - - let mut all_rendered = RenderedWgpu::new(); - - let tip_txt_coords = ( - txt_coords.x, - txt_coords.y - (START_TIP.matches('\n').count() as f32 + 1.0) * config.code_font_size, - ); - - let start_tip_text = owned_section_from_text(&Text { - position: tip_txt_coords.into(), - area_bounds: (size.width as f32, size.height as f32).into(), - color: config.ed_theme.subtle_text, - text: START_TIP, - size: config.code_font_size, - ..Default::default() - }); - - all_rendered.add_text_behind(start_tip_text); - - let rendered_code_graphics = build_code_graphics( - &ed_model.markup_ids, - size, - txt_coords, - config, - glyph_dim_rect, - &ed_model.mark_node_pool, - )?; - - all_rendered.extend(rendered_code_graphics); - - let caret_w_sel_vec = ed_model - .caret_w_select_vec - .iter() - .map(|(caret_w_sel, _)| *caret_w_sel) - .collect(); - - let rendered_selection = build_selection_graphics( - caret_w_sel_vec, - &ed_model.selected_block_opt, - txt_coords, - config, - glyph_dim_rect, - ed_model.module.env.pool, - )?; - - all_rendered.extend(rendered_selection); - - if ed_model.show_debug_view { - all_rendered.add_text_behind(build_debug_graphics(size, txt_coords, config, ed_model)?); - } - - Ok(all_rendered) -} - -pub fn build_selection_graphics( - caret_w_select_vec: Vec, - selected_expr_opt: &Option, - txt_coords: Vector2, - config: &Config, - glyph_dim_rect: Rect, - pool: &Pool, -) -> EdResult { - let mut all_rendered = RenderedWgpu::new(); - let char_width = glyph_dim_rect.width; - let char_height = glyph_dim_rect.height; - - let y_offset = 0.1 * char_height; - - for caret_w_sel in caret_w_select_vec { - let caret_row = caret_w_sel.caret_pos.line as f32; - let caret_col = caret_w_sel.caret_pos.column as f32; - - let top_left_x = txt_coords.x + caret_col * char_width; - let top_left_y = txt_coords.y + caret_row * char_height + y_offset; - - if let Some(selection) = caret_w_sel.selection_opt { - let Selection { start_pos, end_pos } = selection; - - let sel_rect_x = txt_coords.x + ((start_pos.column as f32) * char_width); - let sel_rect_y = txt_coords.y + char_height * (start_pos.line as f32) + y_offset; - - let width = - ((end_pos.column as f32) * char_width) - ((start_pos.column as f32) * char_width); - - all_rendered.add_rect_behind(make_selection_rect( - sel_rect_x, - sel_rect_y, - width, - &glyph_dim_rect, - &config.ed_theme.ui_theme, - )); - - // render tooltip showing type - if let Some(selected_expr) = selected_expr_opt { - let tooltip = ToolTip { - position_x: sel_rect_x, - position_y: sel_rect_y - glyph_dim_rect.height, - text: selected_expr.type_str.as_str(pool), - }; - - let (tip_rect, tip_text) = tooltip.render_tooltip( - &glyph_dim_rect, - &config.ed_theme.ui_theme, - config.code_font_size, - ); - - all_rendered.add_rect_front(tip_rect); - all_rendered.add_text_front(tip_text); - } - } - - all_rendered.add_rect_front(make_caret_rect( - top_left_x, - top_left_y, - &glyph_dim_rect, - &config.ed_theme.ui_theme, - )); - } - - Ok(all_rendered) -} diff --git a/crates/editor/src/editor/mvc/int_update.rs b/crates/editor/src/editor/mvc/int_update.rs deleted file mode 100644 index 7796d237e5..0000000000 --- a/crates/editor/src/editor/mvc/int_update.rs +++ /dev/null @@ -1,133 +0,0 @@ -use roc_ast::lang::core::expr::expr2::Expr2::SmallInt; -use roc_ast::lang::core::expr::expr2::IntStyle; -use roc_ast::lang::core::expr::expr2::IntVal; -use roc_ast::mem_pool::pool_str::PoolStr; -use roc_code_markup::slow_pool::MarkNodeId; - -use crate::editor::ed_error::EdResult; -use crate::editor::ed_error::StringParseSnafu; -use crate::editor::mvc::app_update::InputOutcome; -use crate::editor::mvc::ed_model::EdModel; -use crate::editor::mvc::ed_update::get_node_context; -use crate::editor::mvc::ed_update::NodeContext; -use crate::ui::text::lines::SelectableLines; - -// digit_char should be verified to be a digit before calling this function -pub fn start_new_int(ed_model: &mut EdModel, digit_char: &char) -> EdResult { - let NodeContext { - old_caret_pos: _, - curr_mark_node_id: _, - curr_mark_node, - parent_id_opt: _, - ast_node_id, - } = get_node_context(ed_model)?; - - let is_blank_node = curr_mark_node.is_blank(); - - let int_var = ed_model.module.env.var_store.fresh(); - - let digit_string = digit_char.to_string(); - - let expr2_node = SmallInt { - number: IntVal::U64(*digit_char as u64), // TODO determine if u64 on wordlength of current arch, perhaps introduce Unknown(i64) - var: int_var, - style: IntStyle::Decimal, - text: PoolStr::new(&digit_string, ed_model.module.env.pool), - }; - - ed_model - .module - .env - .pool - .set(ast_node_id.to_expr_id()?, expr2_node); - - if is_blank_node { - let char_len = 1; - ed_model.simple_move_carets_right(char_len); - - Ok(InputOutcome::Accepted) - } else { - Ok(InputOutcome::Ignored) - } -} - -// TODO check if new int needs more than e.g. 64 bits -pub fn update_int( - ed_model: &mut EdModel, - int_mark_node_id: MarkNodeId, - ch: &char, -) -> EdResult { - if ch.is_ascii_digit() { - let old_caret_pos = ed_model.get_caret(); - - let node_caret_offset = ed_model - .grid_node_map - .get_offset_to_node_id(old_caret_pos, int_mark_node_id)?; - - let int_mark_node = ed_model.mark_node_pool.get_mut(int_mark_node_id); - let int_ast_node_id = ed_model.mark_id_ast_id_map.get(int_mark_node_id)?; - - let content_str_mut = int_mark_node.get_content_mut()?; - - // 00, 01 are not valid ints - if (content_str_mut == "0" && (node_caret_offset == 1 || *ch == '0')) - || (*ch == '0' && node_caret_offset == 0) - { - Ok(InputOutcome::Ignored) - } else { - content_str_mut.insert(node_caret_offset, *ch); - - let content_str = int_mark_node.get_content(); - - // update ast - let new_pool_str = PoolStr::new(&content_str, ed_model.module.env.pool); - let int_ast_node = ed_model - .module - .env - .pool - .get_mut(int_ast_node_id.to_expr_id()?); - match int_ast_node { - SmallInt { number, text, .. } => { - update_small_int_num(number, &content_str)?; - - *text = new_pool_str; - } - _ => unimplemented!("TODO implement updating this type of Number"), - } - - // update caret - ed_model.simple_move_carets_right(1); - - Ok(InputOutcome::Accepted) - } - } else { - Ok(InputOutcome::Ignored) - } -} - -fn update_small_int_num(number: &mut IntVal, updated_str: &str) -> EdResult<()> { - use IntVal::*; - - *number = match number { - I64(_) => I64(check_parse_res(updated_str.parse::())?), - U64(_) => U64(check_parse_res(updated_str.parse::())?), - I32(_) => I32(check_parse_res(updated_str.parse::())?), - U32(_) => U32(check_parse_res(updated_str.parse::())?), - I16(_) => I16(check_parse_res(updated_str.parse::())?), - U16(_) => U16(check_parse_res(updated_str.parse::())?), - I8(_) => I8(check_parse_res(updated_str.parse::())?), - U8(_) => U8(check_parse_res(updated_str.parse::())?), - }; - - Ok(()) -} - -fn check_parse_res(parse_res: Result) -> EdResult { - match parse_res { - Ok(some_type) => Ok(some_type), - Err(parse_err) => StringParseSnafu { - msg: format!("{parse_err:?}"), - } - .fail(), - } -} diff --git a/crates/editor/src/editor/mvc/let_update.rs b/crates/editor/src/editor/mvc/let_update.rs deleted file mode 100644 index 482b5d4c9a..0000000000 --- a/crates/editor/src/editor/mvc/let_update.rs +++ /dev/null @@ -1,135 +0,0 @@ -use roc_ast::lang::core::expr::expr2::Expr2; -use roc_ast::lang::core::pattern::Pattern2; -use roc_ast::lang::core::val_def::ValueDef; -use roc_module::symbol::Symbol; - -use crate::editor::ed_error::EdResult; -use crate::editor::mvc::app_update::InputOutcome; -use crate::editor::mvc::ed_model::EdModel; -use crate::editor::mvc::ed_update::get_node_context; -use crate::editor::mvc::ed_update::NodeContext; - -pub fn start_new_let_value(ed_model: &mut EdModel, new_char: &char) -> EdResult { - let NodeContext { - old_caret_pos: _, - curr_mark_node_id: _, - curr_mark_node, - parent_id_opt: _, - ast_node_id, - } = get_node_context(ed_model)?; - - let is_blank_node = curr_mark_node.is_blank(); - - let val_name_string = new_char.to_string(); - // safe unwrap because our ArrString has a 30B capacity - let val_expr2_node = Expr2::Blank; - let val_expr_id = ed_model.module.env.pool.add(val_expr2_node); - - let ident_id = ed_model.module.env.ident_ids.add_str(&val_name_string); - let var_symbol = Symbol::new(ed_model.module.env.home, ident_id); - let body = Expr2::Var(var_symbol); - let body_id = ed_model.module.env.pool.add(body); - - let pattern = Pattern2::Identifier(var_symbol); - let pattern_id = ed_model.module.env.pool.add(pattern); - - let value_def = ValueDef::NoAnnotation { - pattern_id, - expr_id: val_expr_id, - expr_var: ed_model.module.env.var_store.fresh(), - }; - let def_id = ed_model.module.env.pool.add(value_def); - - let expr2_node = Expr2::LetValue { - def_id, - body_id, - body_var: ed_model.module.env.var_store.fresh(), - }; - - ed_model - .module - .env - .pool - .set(ast_node_id.to_expr_id()?, expr2_node); - - if is_blank_node { - let char_len = 1; - ed_model.simple_move_carets_right(char_len); - - Ok(InputOutcome::Accepted) - } else { - Ok(InputOutcome::Ignored) - } -} - -// TODO reenable this for updating non-top level value defs -/* -pub fn update_let_value( - val_name_mn_id: MarkNodeId, - def_id: NodeId, - body_id: NodeId, - ed_model: &mut EdModel, - new_char: &char, -) -> EdResult { - if new_char.is_ascii_alphanumeric() { - let old_caret_pos = ed_model.get_caret(); - - // update markup - let val_name_mn_mut = ed_model.mark_node_pool.get_mut(val_name_mn_id); - let content_str_mut = val_name_mn_mut.get_content_mut()?; - - let old_val_name = content_str_mut.clone(); - - let node_caret_offset = ed_model - .grid_node_map - .get_offset_to_node_id(old_caret_pos, val_name_mn_id)?; - - if node_caret_offset <= content_str_mut.len() { - content_str_mut.insert(node_caret_offset, *new_char); - - // update ast - let value_def = ed_model.module.env.pool.get(def_id); - let value_ident_pattern_id = value_def.get_pattern_id(); - - // TODO no unwrap - let ident_id = ed_model - .module - .env - .ident_ids - .update_key(&old_val_name, content_str_mut) - .unwrap(); - - let new_var_symbol = Symbol::new(ed_model.module.env.home, ident_id); - - ed_model - .module - .env - .pool - .set(value_ident_pattern_id, Pattern2::Identifier(new_var_symbol)); - - ed_model - .module - .env - .pool - .set(body_id, Expr2::Var(new_var_symbol)); - - // update GridNodeMap and CodeLines - ed_model.insert_between_line( - old_caret_pos.line, - old_caret_pos.column, - &new_char.to_string(), - val_name_mn_id, - )?; - - // update caret - ed_model.simple_move_carets_right(1); - - Ok(InputOutcome::Accepted) - } else { - Ok(InputOutcome::Ignored) - } - } else { - Ok(InputOutcome::Ignored) - } -} -*/ diff --git a/crates/editor/src/editor/mvc/list_update.rs b/crates/editor/src/editor/mvc/list_update.rs deleted file mode 100644 index 92e11b9275..0000000000 --- a/crates/editor/src/editor/mvc/list_update.rs +++ /dev/null @@ -1,123 +0,0 @@ -use roc_ast::lang::core::ast::ast_node_to_string; -use roc_ast::lang::core::expr::expr2::{Expr2, ExprId}; -use roc_ast::mem_pool::pool_vec::PoolVec; -use roc_code_markup::markup::nodes::{self}; -use roc_code_markup::slow_pool::MarkNodeId; - -use crate::editor::ed_error::EdResult; -use crate::editor::ed_error::{MissingParentSnafu, UnexpectedASTNodeSnafu}; -use crate::editor::mvc::app_update::InputOutcome; -use crate::editor::mvc::ed_model::EdModel; -use crate::editor::mvc::ed_update::get_node_context; -use crate::editor::mvc::ed_update::NodeContext; - -pub fn start_new_list(ed_model: &mut EdModel) -> EdResult { - let NodeContext { - old_caret_pos: _, - curr_mark_node_id: _, - curr_mark_node, - parent_id_opt: _, - ast_node_id, - } = get_node_context(ed_model)?; - - let is_blank_node = curr_mark_node.is_blank(); - - let expr2_node = Expr2::List { - elem_var: ed_model.module.env.var_store.fresh(), - elems: PoolVec::empty(ed_model.module.env.pool), - }; - - ed_model - .module - .env - .pool - .set(ast_node_id.to_expr_id()?, expr2_node); - - if is_blank_node { - ed_model.simple_move_carets_right(nodes::LEFT_SQUARE_BR.len()); - - Ok(InputOutcome::Accepted) - } else { - Ok(InputOutcome::Ignored) - } -} - -// insert Blank at current position for easy code reuse -pub fn add_blank_child( - new_child_index: usize, - new_ast_child_index: usize, - ed_model: &mut EdModel, -) -> EdResult { - let NodeContext { - old_caret_pos: _, - curr_mark_node_id, - curr_mark_node: _, - parent_id_opt, - ast_node_id, - } = get_node_context(ed_model)?; - - let trip_result: EdResult<(ExprId, ExprId, MarkNodeId)> = if let Some(parent_id) = parent_id_opt - { - let list_ast_node_id = ed_model.mark_id_ast_id_map.get(parent_id)?; - let list_ast_node = ed_model.module.env.pool.get(list_ast_node_id.to_expr_id()?); - - match list_ast_node { - Expr2::List { - elem_var: _, - elems: _, - } => { - let blank_elt = Expr2::Blank; - let blank_elt_id = ed_model.module.env.pool.add(blank_elt); - - Ok((blank_elt_id, list_ast_node_id.to_expr_id()?, parent_id)) - } - _ => UnexpectedASTNodeSnafu { - required_node_type: "List".to_string(), - encountered_node_type: ast_node_to_string(ast_node_id, ed_model.module.env.pool), - } - .fail(), - } - } else { - MissingParentSnafu { - node_id: curr_mark_node_id, - } - .fail() - }; - - let (blank_elt_id, list_ast_node_id, _parent_id) = trip_result?; - - let list_ast_node = ed_model.module.env.pool.get(list_ast_node_id); - - match list_ast_node { - Expr2::List { elem_var, elems } => { - let mut new_elems: Vec = - elems.iter(ed_model.module.env.pool).copied().collect(); - - new_elems.insert(new_ast_child_index, blank_elt_id); - - let new_list_node = Expr2::List { - elem_var: *elem_var, - elems: PoolVec::new(new_elems.into_iter(), ed_model.module.env.pool), - }; - - ed_model - .module - .env - .pool - .set(list_ast_node_id, new_list_node); - - Ok(()) - } - _ => UnexpectedASTNodeSnafu { - required_node_type: "List".to_string(), - encountered_node_type: ast_node_to_string(ast_node_id, ed_model.module.env.pool), - } - .fail(), - }?; - - if new_child_index > 1 { - ed_model.simple_move_carets_right(nodes::COMMA.len()); - } - - Ok(InputOutcome::Accepted) -} diff --git a/crates/editor/src/editor/mvc/lookup_update.rs b/crates/editor/src/editor/mvc/lookup_update.rs deleted file mode 100644 index cf08166243..0000000000 --- a/crates/editor/src/editor/mvc/lookup_update.rs +++ /dev/null @@ -1,44 +0,0 @@ -use roc_ast::lang::core::expr::expr2::{Expr2, ExprId}; -use roc_ast::mem_pool::pool_str::PoolStr; -use roc_code_markup::slow_pool::MarkNodeId; - -use crate::editor::ed_error::EdResult; -use crate::editor::mvc::app_update::InputOutcome; -use crate::editor::mvc::ed_model::EdModel; -use crate::ui::text::lines::SelectableLines; - -pub fn update_invalid_lookup( - input_str: &str, - old_pool_str: &PoolStr, - curr_mark_node_id: MarkNodeId, - expr_id: ExprId, - ed_model: &mut EdModel, -) -> EdResult { - if input_str.chars().all(|ch| ch.is_ascii_alphanumeric()) { - let mut new_lookup_str = String::new(); - - new_lookup_str.push_str(old_pool_str.as_str(ed_model.module.env.pool)); - - let caret_offset = ed_model - .grid_node_map - .get_offset_to_node_id(ed_model.get_caret(), curr_mark_node_id)?; - - new_lookup_str.insert_str(caret_offset, input_str); - - let new_pool_str = PoolStr::new(&new_lookup_str, ed_model.module.env.pool); - - // update AST - ed_model - .module - .env - .pool - .set(expr_id, Expr2::InvalidLookup(new_pool_str)); - - // update caret - ed_model.simple_move_carets_right(input_str.len()); - - Ok(InputOutcome::Accepted) - } else { - Ok(InputOutcome::Ignored) - } -} diff --git a/crates/editor/src/editor/mvc/mod.rs b/crates/editor/src/editor/mvc/mod.rs deleted file mode 100644 index 5b4a11ad9a..0000000000 --- a/crates/editor/src/editor/mvc/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -pub mod app_model; -pub mod app_update; -mod break_line; -pub mod ed_model; -pub mod ed_update; -pub mod ed_view; -mod int_update; -mod let_update; -mod list_update; -mod lookup_update; -mod record_update; -mod string_update; -pub mod tld_value_update; diff --git a/crates/editor/src/editor/mvc/record_update.rs b/crates/editor/src/editor/mvc/record_update.rs deleted file mode 100644 index 33ba51ba64..0000000000 --- a/crates/editor/src/editor/mvc/record_update.rs +++ /dev/null @@ -1,295 +0,0 @@ -use crate::editor::ed_error::EdResult; -use crate::editor::ed_error::MissingParentSnafu; -use crate::editor::ed_error::RecordWithoutFieldsSnafu; -use crate::editor::mvc::app_update::InputOutcome; -use crate::editor::mvc::ed_model::EdModel; -use crate::editor::mvc::ed_update::get_node_context; -use crate::editor::mvc::ed_update::NodeContext; -use crate::editor::util::index_of; -use crate::ui::text::text_pos::TextPos; -use roc_ast::lang::core::ast::ASTNodeId; -use roc_ast::lang::core::expr::expr2::Expr2; -use roc_ast::lang::core::expr::expr2::ExprId; -use roc_ast::lang::core::expr::record_field::RecordField; -use roc_ast::mem_pool::pool_str::PoolStr; -use roc_ast::mem_pool::pool_vec::PoolVec; -use roc_code_markup::markup::attribute::Attributes; -use roc_code_markup::markup::nodes; -use roc_code_markup::markup::nodes::MarkupNode; -use roc_code_markup::markup::nodes::COLON; -use roc_code_markup::slow_pool::MarkNodeId; -use roc_code_markup::syntax_highlight::HighlightStyle; -use snafu::OptionExt; - -pub fn start_new_record(ed_model: &mut EdModel) -> EdResult { - let NodeContext { - old_caret_pos: _, - curr_mark_node_id: _, - curr_mark_node, - parent_id_opt: _, - ast_node_id, - } = get_node_context(ed_model)?; - - let is_blank_node = curr_mark_node.is_blank(); - - let ast_pool = &mut ed_model.module.env.pool; - let expr2_node = Expr2::EmptyRecord; - - ast_pool.set(ast_node_id.to_expr_id()?, expr2_node); - - if is_blank_node { - ed_model.simple_move_carets_right(nodes::LEFT_ACCOLADE.len()); - - Ok(InputOutcome::Accepted) - } else { - Ok(InputOutcome::Ignored) - } -} - -pub fn update_empty_record( - new_input: &str, - prev_mark_node_id: MarkNodeId, - sibling_ids: Vec, - ed_model: &mut EdModel, -) -> EdResult { - let mut input_chars = new_input.chars(); - - if input_chars.all(|ch| ch.is_ascii_alphabetic()) - && input_chars.all(|ch| ch.is_ascii_lowercase()) - { - let prev_mark_node = ed_model.mark_node_pool.get(prev_mark_node_id); - - let NodeContext { - old_caret_pos, - curr_mark_node_id, - curr_mark_node, - parent_id_opt, - ast_node_id, - } = get_node_context(ed_model)?; - - if prev_mark_node.get_content() == nodes::LEFT_ACCOLADE - && curr_mark_node.get_content() == nodes::RIGHT_ACCOLADE - { - // update AST - let record_var = ed_model.module.env.var_store.fresh(); - let field_name = PoolStr::new(new_input, ed_model.module.env.pool); - let field_var = ed_model.module.env.var_store.fresh(); - //TODO actually check if field_str belongs to a previously defined variable - let first_field = RecordField::InvalidLabelOnly(field_name, field_var); - - let fields = PoolVec::new(vec![first_field].into_iter(), ed_model.module.env.pool); - - let new_ast_node = Expr2::Record { record_var, fields }; - - ed_model - .module - .env - .pool - .set(ast_node_id.to_expr_id()?, new_ast_node); - - // update Markup - - let record_field_node = MarkupNode::Text { - content: new_input.to_owned(), - syn_high_style: HighlightStyle::RecordField, - attributes: Attributes::default(), - parent_id_opt, - newlines_at_end: 0, - }; - - let record_field_node_id = ed_model.add_mark_node(record_field_node); - - if let Some(parent_id) = parent_id_opt { - let parent = ed_model.mark_node_pool.get_mut(parent_id); - - let new_child_index = index_of(curr_mark_node_id, &sibling_ids)?; - - parent.add_child_at_index(new_child_index, record_field_node_id)?; - } else { - MissingParentSnafu { - node_id: curr_mark_node_id, - } - .fail()? - } - - // update caret - ed_model.simple_move_carets_right(1); - - // update GridNodeMap and CodeLines - EdModel::insert_between_line( - old_caret_pos.line, - old_caret_pos.column, - new_input, - record_field_node_id, - &mut ed_model.grid_node_map, - )?; - - Ok(InputOutcome::Accepted) - } else { - Ok(InputOutcome::Ignored) - } - } else { - Ok(InputOutcome::Ignored) - } -} - -pub fn update_record_colon( - ed_model: &mut EdModel, - record_ast_node_id: ExprId, -) -> EdResult { - let NodeContext { - old_caret_pos, - curr_mark_node_id: _, - curr_mark_node: _, - parent_id_opt: _, - ast_node_id, - } = get_node_context(ed_model)?; - let curr_ast_node = ed_model.module.env.pool.get(ast_node_id.to_expr_id()?); - - let prev_mark_node_id_opt = ed_model.get_prev_mark_node_id()?; - if let Some(prev_mark_node_id) = prev_mark_node_id_opt { - let prev_mn_ast_node_id = ed_model.mark_id_ast_id_map.get(prev_mark_node_id)?; - - match prev_mn_ast_node_id { - ASTNodeId::ADefId(_) => Ok(InputOutcome::Ignored), - ASTNodeId::AExprId(prev_expr_id) => { - let prev_expr = ed_model.module.env.pool.get(prev_expr_id); - - // current and prev node should always point to record when in valid position to add ':' - if matches!(prev_expr, Expr2::Record { .. }) - && matches!(curr_ast_node, Expr2::Record { .. }) - { - let ast_node_ref = ed_model.module.env.pool.get(record_ast_node_id); - - match ast_node_ref { - Expr2::Record { - record_var: _, - fields, - } => { - if ed_model.node_exists_at_caret() { - let next_mark_node_id = - ed_model.grid_node_map.get_id_at_row_col(old_caret_pos)?; - let next_mark_node = ed_model.mark_node_pool.get(next_mark_node_id); - if next_mark_node.get_content() == nodes::RIGHT_ACCOLADE { - // update AST node - let new_field_val = Expr2::Blank; - let new_field_val_id = - ed_model.module.env.pool.add(new_field_val); - - let first_field_mut = fields - .iter_mut(ed_model.module.env.pool) - .next() - .with_context(|| RecordWithoutFieldsSnafu {})?; - - *first_field_mut = RecordField::LabeledValue( - *first_field_mut.get_record_field_pool_str(), - *first_field_mut.get_record_field_var(), - new_field_val_id, - ); - - // update caret - ed_model.simple_move_carets_right(COLON.len()); - - Ok(InputOutcome::Accepted) - } else { - Ok(InputOutcome::Ignored) - } - } else { - Ok(InputOutcome::Ignored) - } - } - _ => Ok(InputOutcome::Ignored), - } - } else { - Ok(InputOutcome::Ignored) - } - } - } - } else { - Ok(InputOutcome::Ignored) - } -} - -pub fn update_record_field( - new_input: &str, - old_caret_pos: TextPos, - curr_mark_node_id: MarkNodeId, - record_fields: &PoolVec, - ed_model: &mut EdModel, -) -> EdResult { - // update MarkupNode - let curr_mark_node_mut = ed_model.mark_node_pool.get_mut(curr_mark_node_id); - let content_str_mut = curr_mark_node_mut.get_content_mut()?; - let node_caret_offset = ed_model - .grid_node_map - .get_offset_to_node_id(old_caret_pos, curr_mark_node_id)?; - - if node_caret_offset == 0 { - let first_char_opt = new_input.chars().next(); - let first_char_is_num = first_char_opt.unwrap_or('0').is_ascii_digit(); - - // variable name can't start with number - if first_char_is_num { - return Ok(InputOutcome::Ignored); - } - } - - content_str_mut.insert_str(node_caret_offset, new_input); - - // update caret - ed_model.simple_move_carets_right(new_input.len()); - - // update AST Node - let first_field = record_fields - .iter(ed_model.module.env.pool) - .next() - .with_context(|| RecordWithoutFieldsSnafu {})?; - - let field_pool_str = first_field - .get_record_field_pool_str() - .as_str(ed_model.module.env.pool); - - let mut new_field_name = String::new(); - - // -push old field name - new_field_name.push_str(field_pool_str); - new_field_name.insert_str(node_caret_offset, new_input); - let new_field_pool_str = PoolStr::new(&new_field_name, ed_model.module.env.pool); - - let first_field_mut = record_fields - .iter_mut(ed_model.module.env.pool) - .next() - .with_context(|| RecordWithoutFieldsSnafu {})?; - - let field_pool_str_mut = first_field_mut.get_record_field_pool_str_mut(); - *field_pool_str_mut = new_field_pool_str; - - // because borrow issues - let first_field_b = record_fields - .iter(ed_model.module.env.pool) - .next() - .with_context(|| RecordWithoutFieldsSnafu {})?; - - match first_field_b { - RecordField::InvalidLabelOnly(_, _) => { - // TODO check if label is now valid. If it is, return LabelOnly - } - RecordField::LabelOnly(_, _, _symbol) => { - // TODO check if symbol is still valid. If not, return InvalidLabelOnly - } - RecordField::LabeledValue(_, _, field_val_id_ref) => { - let field_val_id = *field_val_id_ref; - let sub_expr2 = ed_model.module.env.pool.get(field_val_id); - - if let Expr2::InvalidLookup(_) = sub_expr2 { - ed_model - .module - .env - .pool - .set(field_val_id, Expr2::InvalidLookup(new_field_pool_str)); - } - } - } - - Ok(InputOutcome::Accepted) -} diff --git a/crates/editor/src/editor/mvc/string_update.rs b/crates/editor/src/editor/mvc/string_update.rs deleted file mode 100644 index dc0152aa40..0000000000 --- a/crates/editor/src/editor/mvc/string_update.rs +++ /dev/null @@ -1,120 +0,0 @@ -use roc_ast::lang::core::expr::expr2::ArrString; -use roc_ast::lang::core::expr::expr2::Expr2; -use roc_ast::lang::core::str::update_str_expr; -use roc_ast::mem_pool::pool_str::PoolStr; - -use crate::editor::ed_error::EdResult; -use crate::editor::mvc::app_update::InputOutcome; -use crate::editor::mvc::ed_model::EdModel; -use crate::editor::mvc::ed_update::get_node_context; -use crate::editor::mvc::ed_update::NodeContext; - -pub fn update_small_string( - new_char: &char, - old_array_str: &ArrString, - ed_model: &mut EdModel, -) -> EdResult { - let NodeContext { - old_caret_pos, - curr_mark_node_id, - curr_mark_node, - parent_id_opt: _, - ast_node_id, - } = get_node_context(ed_model)?; - - let new_input = &new_char.to_string(); - - let content_str = curr_mark_node.get_content(); - let node_caret_offset = ed_model - .grid_node_map - .get_offset_to_node_id(old_caret_pos, curr_mark_node_id)?; - - if node_caret_offset != 0 && node_caret_offset < content_str.len() { - if old_array_str.len() < old_array_str.capacity() { - if let Expr2::SmallStr(ref mut mut_array_str) = - ed_model.module.env.pool.get_mut(ast_node_id.to_expr_id()?) - { - // safe because we checked the length - mut_array_str.push(*new_char); - } else { - unreachable!() - } - } else { - let mut new_str = old_array_str.as_str().to_owned(); - new_str.push(*new_char); - - let new_ast_node = Expr2::Str(PoolStr::new(&new_str, ed_model.module.env.pool)); - - ed_model - .module - .env - .pool - .set(ast_node_id.to_expr_id()?, new_ast_node); - } - - // update caret - ed_model.simple_move_carets_right(new_input.len()); - - Ok(InputOutcome::Accepted) - } else { - Ok(InputOutcome::Ignored) - } -} - -pub fn update_string(new_char: char, ed_model: &mut EdModel) -> EdResult { - let NodeContext { - old_caret_pos, - curr_mark_node_id, - curr_mark_node, - parent_id_opt: _, - ast_node_id, - } = get_node_context(ed_model)?; - - let content_str = curr_mark_node.get_content(); - let node_caret_offset = ed_model - .grid_node_map - .get_offset_to_node_id(old_caret_pos, curr_mark_node_id)?; - - if node_caret_offset != 0 && node_caret_offset < content_str.len() { - // update ast - update_str_expr( - ast_node_id.to_expr_id()?, - new_char, - node_caret_offset - 1, // -1 because offset was calculated with quotes - ed_model.module.env.pool, - )?; - - // update caret - ed_model.simple_move_carets_right(1); - - Ok(InputOutcome::Accepted) - } else { - Ok(InputOutcome::Ignored) - } -} - -pub fn start_new_string(ed_model: &mut EdModel) -> EdResult { - let NodeContext { - old_caret_pos: _, - curr_mark_node_id: _, - curr_mark_node, - parent_id_opt: _, - ast_node_id, - } = get_node_context(ed_model)?; - - if curr_mark_node.is_blank() { - let new_expr2_node = Expr2::SmallStr(arrayvec::ArrayString::new()); - - ed_model - .module - .env - .pool - .set(ast_node_id.to_expr_id()?, new_expr2_node); - - ed_model.simple_move_carets_right(1); - - Ok(InputOutcome::Accepted) - } else { - Ok(InputOutcome::Ignored) - } -} diff --git a/crates/editor/src/editor/mvc/tld_value_update.rs b/crates/editor/src/editor/mvc/tld_value_update.rs deleted file mode 100644 index 33f459ec94..0000000000 --- a/crates/editor/src/editor/mvc/tld_value_update.rs +++ /dev/null @@ -1,104 +0,0 @@ -use roc_ast::lang::core::{def::def2::Def2, expr::expr2::Expr2}; -use roc_code_markup::slow_pool::MarkNodeId; - -use crate::{ - editor::ed_error::{EdResult, FailedToUpdateIdentIdNameSnafu, KeyNotFoundSnafu}, - ui::text::text_pos::TextPos, -}; - -use super::{ - app_update::InputOutcome, - ed_model::EdModel, - ed_update::{get_node_context, NodeContext}, -}; - -// Top Level Defined Value. example: `main = "Hello, World!"` -pub fn start_new_tld_value(ed_model: &mut EdModel, new_char: &char) -> EdResult { - let NodeContext { - old_caret_pos: _, - curr_mark_node_id: _, - curr_mark_node: _, - parent_id_opt: _, - ast_node_id, - } = get_node_context(ed_model)?; - - // create new blank >> m = Blank - let val_expr_node = Expr2::Blank; - let val_expr_id = ed_model.module.env.pool.add(val_expr_node); - - let ident_str = new_char.to_string(); - let ident_id = ed_model.module.env.ident_ids.add_str(&ident_str); - - let module_ident_ids_opt = ed_model - .loaded_module - .interns - .all_ident_ids - .get_mut(&ed_model.module.env.home); - - if let Some(module_ident_ids_ref) = module_ident_ids_opt { - // this might create different IdentId for interns and env.ident_ids which may be a problem - module_ident_ids_ref.add_str(&ident_str); - } else { - KeyNotFoundSnafu { - key_str: format!("{:?}", ed_model.module.env.home), - } - .fail()? - } - - let new_ast_node = Def2::ValueDef { - identifier_id: ident_id, - expr_id: val_expr_id, - }; - - ed_model - .module - .env - .pool - .set(ast_node_id.to_def_id()?, new_ast_node); - - let char_len = 1; - ed_model.simple_move_carets_right(char_len); - - Ok(InputOutcome::Accepted) -} - -pub fn update_tld_val_name( - val_name_mn_id: MarkNodeId, - old_caret_pos: TextPos, - ed_model: &mut EdModel, - new_char: &char, -) -> EdResult { - if new_char.is_ascii_alphanumeric() { - // update markup - let val_name_mn = ed_model.mark_node_pool.get(val_name_mn_id); - let mut val_name_str = val_name_mn.get_content(); - - let old_val_name = val_name_str.clone(); - - let node_caret_offset = ed_model - .grid_node_map - .get_offset_to_node_id(old_caret_pos, val_name_mn_id)?; - - if node_caret_offset <= val_name_str.len() { - val_name_str.insert(node_caret_offset, *new_char); - - let update_val_name_res = ed_model - .module - .env - .ident_ids - .update_key(&old_val_name, &val_name_str); - - if let Err(err_str) = update_val_name_res { - FailedToUpdateIdentIdNameSnafu { err_str }.fail()?; - } - - ed_model.simple_move_caret_right(old_caret_pos, 1); - - Ok(InputOutcome::Accepted) - } else { - Ok(InputOutcome::Ignored) - } - } else { - Ok(InputOutcome::Ignored) - } -} diff --git a/crates/editor/src/editor/render_ast.rs b/crates/editor/src/editor/render_ast.rs deleted file mode 100644 index ae687de017..0000000000 --- a/crates/editor/src/editor/render_ast.rs +++ /dev/null @@ -1,237 +0,0 @@ -use crate::editor::mvc::ed_view::RenderedWgpu; -use crate::editor::{ed_error::EdResult, theme::EdTheme, util::map_get}; -use crate::graphics::primitives::rect::Rect; -use crate::graphics::primitives::text as gr_text; -use cgmath::Vector2; -use roc_code_markup::{ - markup::{ - attribute::Attribute, - nodes::{MarkupNode, BLANK_PLACEHOLDER}, - }, - slow_pool::{MarkNodeId, SlowPool}, - syntax_highlight::HighlightStyle, - underline_style::UnderlineStyle, -}; -use winit::dpi::PhysicalSize; - -use crate::{editor::config::Config, graphics::colors}; - -pub fn build_code_graphics( - markup_ids: &[MarkNodeId], - size: &PhysicalSize, - txt_coords: Vector2, - config: &Config, - glyph_dim_rect: Rect, - mark_node_pool: &SlowPool, -) -> EdResult { - let area_bounds = (size.width as f32, size.height as f32); - let layout = wgpu_glyph::Layout::default().h_align(wgpu_glyph::HorizontalAlign::Left); - let mut rendered_wgpu = RenderedWgpu::new(); - - let mut all_glyph_text_vec = vec![]; - let mut all_rects = vec![]; - let mut txt_row_col = (0, 0); - - for markup_id in markup_ids.iter() { - let mark_node = mark_node_pool.get(*markup_id); - - let (mut glyph_text_vec, mut rects) = markup_to_wgpu( - mark_node, - &CodeStyle { - ed_theme: &config.ed_theme, - font_size: config.code_font_size, - txt_coords, - glyph_dim_rect, - }, - &mut txt_row_col, - mark_node_pool, - )?; - - all_glyph_text_vec.append(&mut glyph_text_vec); - all_rects.append(&mut rects) - } - - let section = gr_text::owned_section_from_glyph_texts( - all_glyph_text_vec, - txt_coords.into(), - area_bounds, - layout, - ); - - rendered_wgpu.add_rects_behind(all_rects); // currently only rects for Blank - rendered_wgpu.add_text_behind(section); - - Ok(rendered_wgpu) -} - -struct CodeStyle<'a> { - ed_theme: &'a EdTheme, - font_size: f32, - txt_coords: Vector2, - glyph_dim_rect: Rect, -} - -fn markup_to_wgpu<'a>( - markup_node: &'a MarkupNode, - code_style: &CodeStyle, - txt_row_col: &mut (usize, usize), - mark_node_pool: &'a SlowPool, -) -> EdResult<(Vec, Vec)> { - let mut wgpu_texts: Vec = Vec::new(); - let mut rects: Vec = Vec::new(); - - markup_to_wgpu_helper( - markup_node, - &mut wgpu_texts, - &mut rects, - code_style, - txt_row_col, - mark_node_pool, - )?; - - Ok((wgpu_texts, rects)) -} - -fn markup_to_wgpu_helper<'a>( - markup_node: &'a MarkupNode, - wgpu_texts: &mut Vec, - rects: &mut Vec, - code_style: &CodeStyle, - txt_row_col: &mut (usize, usize), - mark_node_pool: &'a SlowPool, -) -> EdResult<()> { - let char_width = code_style.glyph_dim_rect.width; - let char_height = code_style.glyph_dim_rect.height; - - match markup_node { - MarkupNode::Nested { - children_ids, - parent_id_opt: _, - newlines_at_end, - } => { - for child_id in children_ids.iter() { - let child = mark_node_pool.get(*child_id); - markup_to_wgpu_helper( - child, - wgpu_texts, - rects, - code_style, - txt_row_col, - mark_node_pool, - )?; - } - - for _ in 0..*newlines_at_end { - wgpu_texts.push(newline(code_style.font_size)); - - txt_row_col.0 += 1; - txt_row_col.1 = 0; - } - } - MarkupNode::Text { - content, - syn_high_style, - attributes, - parent_id_opt: _, - newlines_at_end, - } => { - let highlight_color = map_get(&code_style.ed_theme.syntax_high_map, syn_high_style)?; - - let full_content = markup_node.get_full_content().replace('\n', "\\n"); // any \n left here should be escaped so that it can be shown as \n - - let glyph_text = glyph_brush::OwnedText::new(&full_content) - .with_color(colors::to_slice(*highlight_color)) - .with_scale(code_style.font_size); - - for attribute in &attributes.all { - match attribute { - Attribute::Underline { underline_spec: _ } => { - // TODO use underline_spec - let top_left_coords = ( - code_style.txt_coords.x + (txt_row_col.1 as f32) * char_width, - code_style.txt_coords.y - + (txt_row_col.0 as f32) * char_height - + 1.0 * char_height, - ); - - let underline_rect = Rect { - top_left_coords: top_left_coords.into(), - width: char_width * (full_content.len() as f32), - height: 5.0, - color: *code_style - .ed_theme - .underline_color_map - .get(&UnderlineStyle::Error) - .unwrap(), - }; - - rects.push(underline_rect); - } - rest => todo!("handle Attribute: {:?}", rest), - } - } - - txt_row_col.1 += content.len(); - - for _ in 0..*newlines_at_end { - txt_row_col.0 += 1; - txt_row_col.1 = 0; - } - - wgpu_texts.push(glyph_text); - } - MarkupNode::Blank { - attributes: _, - parent_id_opt: _, - newlines_at_end, - } => { - let full_content = markup_node.get_full_content(); - - let glyph_text = glyph_brush::OwnedText::new(full_content) - .with_color(colors::to_slice(colors::WHITE)) - .with_scale(code_style.font_size); - - let highlight_color = - map_get(&code_style.ed_theme.syntax_high_map, &HighlightStyle::Blank)?; - - let blank_rect = Rect { - top_left_coords: ( - code_style.txt_coords.x + (txt_row_col.1 as f32) * char_width, - code_style.txt_coords.y - + (txt_row_col.0 as f32) * char_height - + 0.1 * char_height, - ) - .into(), - width: char_width, - height: char_height, - color: *highlight_color, - }; - rects.push(blank_rect); - - txt_row_col.1 += BLANK_PLACEHOLDER.len(); - wgpu_texts.push(glyph_text); - - for _ in 0..*newlines_at_end { - txt_row_col.0 += 1; - txt_row_col.1 = 0; - } - } - MarkupNode::Indent { .. } => { - let full_content: String = markup_node.get_content(); - - txt_row_col.1 += full_content.len(); - - let glyph_text = glyph_brush::OwnedText::new(full_content) - .with_color(colors::to_slice(colors::WHITE)) - .with_scale(code_style.font_size); - - wgpu_texts.push(glyph_text); - } - }; - - Ok(()) -} - -fn newline(font_size: f32) -> glyph_brush::OwnedText { - glyph_brush::OwnedText::new("\n").with_scale(font_size) -} diff --git a/crates/editor/src/editor/render_debug.rs b/crates/editor/src/editor/render_debug.rs deleted file mode 100644 index dec6950f60..0000000000 --- a/crates/editor/src/editor/render_debug.rs +++ /dev/null @@ -1,81 +0,0 @@ -use crate::editor::ed_error::EdResult; -use crate::editor::mvc::ed_model::EdModel; -use crate::graphics::colors; -use crate::graphics::colors::from_hsb; -use crate::graphics::primitives::text as gr_text; -use cgmath::Vector2; -use roc_ast::lang::core::def::def2::def2_to_string; -use roc_code_markup::markup::nodes::tree_as_string; -use winit::dpi::PhysicalSize; - -use crate::editor::config::Config; - -pub fn build_debug_graphics( - size: &PhysicalSize, - txt_coords: Vector2, - config: &Config, - ed_model: &EdModel, -) -> EdResult { - let area_bounds = (size.width as f32, size.height as f32); - let layout = wgpu_glyph::Layout::default().h_align(wgpu_glyph::HorizontalAlign::Left); - - let debug_txt_coords: Vector2 = (txt_coords.x * 20.0, txt_coords.y).into(); - - let carets_text = - glyph_brush::OwnedText::new(format!("carets: {:?}\n\n", ed_model.get_carets())) - .with_color(colors::to_slice(from_hsb(0, 0, 100))) - .with_scale(config.debug_font_size); - - let grid_node_map_text = glyph_brush::OwnedText::new(format!("{}", ed_model.grid_node_map)) - .with_color(colors::to_slice(from_hsb(20, 41, 100))) - .with_scale(config.debug_font_size); - - let code_lines_text = glyph_brush::OwnedText::new(format!("{}", ed_model.code_lines)) - .with_color(colors::to_slice(from_hsb(0, 49, 96))) - .with_scale(config.debug_font_size); - - let mut mark_node_trees_string = "\nmark node trees:".to_owned(); - - for mark_id in ed_model.markup_ids[1..].iter() { - // 1.. -> skip header - mark_node_trees_string.push_str(&tree_as_string(*mark_id, &ed_model.mark_node_pool)); - } - - let mark_node_tree_text = glyph_brush::OwnedText::new(mark_node_trees_string) - .with_color(colors::to_slice(from_hsb(266, 31, 96))) - .with_scale(config.debug_font_size); - - let mark_node_pool_text = glyph_brush::OwnedText::new( - ed_model - .mark_node_pool - .debug_string(&ed_model.mark_id_ast_id_map), - ) - .with_color(colors::to_slice(from_hsb(110, 45, 82))) - .with_scale(config.debug_font_size); - - let mut ast_node_text_str = "AST:\n".to_owned(); - - for def_id in ed_model.module.ast.def_ids.iter() { - ast_node_text_str.push_str(&def2_to_string(*def_id, ed_model.module.env.pool)) - } - - let ast_node_text = glyph_brush::OwnedText::new(ast_node_text_str) - .with_color(colors::to_slice(from_hsb(211, 80, 100))) - .with_scale(config.debug_font_size); - - let section = gr_text::owned_section_from_glyph_texts( - vec![ - carets_text, - grid_node_map_text, - code_lines_text, - mark_node_tree_text, - mark_node_pool_text, - ast_node_text, - ], - debug_txt_coords.into(), - area_bounds, - layout, - ); - - Ok(section) -} diff --git a/crates/editor/src/editor/resources/mod.rs b/crates/editor/src/editor/resources/mod.rs deleted file mode 100644 index e8dfd788ff..0000000000 --- a/crates/editor/src/editor/resources/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod strings; diff --git a/crates/editor/src/editor/resources/sounds/bell_sound.mp3 b/crates/editor/src/editor/resources/sounds/bell_sound.mp3 deleted file mode 100644 index c18b130858..0000000000 Binary files a/crates/editor/src/editor/resources/sounds/bell_sound.mp3 and /dev/null differ diff --git a/crates/editor/src/editor/resources/strings.rs b/crates/editor/src/editor/resources/strings.rs deleted file mode 100644 index cb604d99ed..0000000000 --- a/crates/editor/src/editor/resources/strings.rs +++ /dev/null @@ -1,45 +0,0 @@ -#![allow(dead_code)] - -pub const NOTHING_OPENED: &str = - "Execute `cargo run edit` from the root folder of the repo to try the editor."; - -pub const START_TIP: &str = r#"Currently supported: lists, records, string, numbers and value definitions. - -Use `Ctrl+Shift+Up` or `Cmd+Shift+Up` to select surrounding expression. -Use backspace after `Ctrl+Shift+Up` to delete the selected expression. - -`Ctrl+S` or `Cmd+S` to save. -`Ctrl+R` to run. - -Input chars that would create parse errors or change formatting will be ignored. -For convenience and consistency, there is only one way to format roc. -"#; - -pub const HELLO_WORLD: &str = r#" -app "untitled-app" - packages { pf: "rust-platform/main.roc" } - imports [] - provides [main] to pf - -main = "Hello, world!" - -"#; - -pub fn nr_hello_world_lines() -> usize { - HELLO_WORLD.matches('\n').count() - 1 -} - -pub const PLATFORM_DIR_NAME: &str = "rust-platform"; -pub const PLATFORM_FILE_NAME: &str = "main.roc"; - -pub const PLATFORM_STR: &str = r#" -platform "test-platform" - requires {} { main : Str } - exposes [] - packages {} - imports [] - provides [mainForHost] - -mainForHost : Str -mainForHost = main -"#; diff --git a/crates/editor/src/editor/theme.rs b/crates/editor/src/editor/theme.rs deleted file mode 100644 index d3482f2cbe..0000000000 --- a/crates/editor/src/editor/theme.rs +++ /dev/null @@ -1,31 +0,0 @@ -use gr_colors::{from_hsb, RgbaTup}; -use roc_code_markup::{ - syntax_highlight::{default_highlight_map, HighlightStyle}, - underline_style::{default_underline_color_map, UnderlineStyle}, -}; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -use crate::graphics::colors as gr_colors; -use crate::ui::theme::UITheme; - -#[derive(Serialize, Deserialize)] -pub struct EdTheme { - pub background: RgbaTup, - pub subtle_text: RgbaTup, - pub syntax_high_map: HashMap, - pub ui_theme: UITheme, - pub underline_color_map: HashMap, -} - -impl Default for EdTheme { - fn default() -> Self { - Self { - background: from_hsb(240, 10, 19), // #2C2C35 - subtle_text: from_hsb(240, 5, 60), - syntax_high_map: default_highlight_map(), - ui_theme: UITheme::default(), - underline_color_map: default_underline_color_map(), - } - } -} diff --git a/crates/editor/src/editor/util.rs b/crates/editor/src/editor/util.rs deleted file mode 100644 index b5266c0ced..0000000000 --- a/crates/editor/src/editor/util.rs +++ /dev/null @@ -1,68 +0,0 @@ -use super::ed_error::{EdResult, KeyNotFoundSnafu}; -use crate::editor::ed_error::IndexOfFailedSnafu; -use snafu::OptionExt; -use std::collections::HashMap; - -// replace HashMap method that returns Option with one that returns Result and proper Error -pub fn map_get<'a, K: ::std::fmt::Debug + std::hash::Hash + std::cmp::Eq, V>( - hash_map: &'a HashMap, - key: &K, -) -> EdResult<&'a V> { - let value = hash_map.get(key).context(KeyNotFoundSnafu { - key_str: format!("{key:?}"), - })?; - - Ok(value) -} - -pub fn index_of(elt: T, slice: &[T]) -> EdResult { - let index = slice - .iter() - .position(|slice_elt| *slice_elt == elt) - .with_context(|| { - let elt_str = format!("{elt:?}"); - let collection_str = format!("{slice:?}"); - - IndexOfFailedSnafu { - elt_str, - collection_str, - } - })?; - - Ok(index) -} - -// returns the index of the first occurrence of element and index of the last occurrence -pub fn first_last_index_of( - elt: T, - slice: &[T], -) -> EdResult<(usize, usize)> { - let mut first_index_opt = None; - let mut last_index_opt = None; - - for (index, list_elt) in slice.iter().enumerate() { - if *list_elt == elt { - if first_index_opt.is_none() { - first_index_opt = Some(index); - last_index_opt = Some(index); - } else { - last_index_opt = Some(index) - } - } else if last_index_opt.is_some() { - break; - } - } - - if let (Some(first_index), Some(last_index)) = (first_index_opt, last_index_opt) { - Ok((first_index, last_index)) - } else { - let elt_str = format!("{elt:?}"); - let collection_str = format!("{slice:?}"); - - IndexOfFailedSnafu { - elt_str, - collection_str, - } - .fail() - } -} diff --git a/crates/editor/src/graphics/colors.rs b/crates/editor/src/graphics/colors.rs deleted file mode 100644 index 67dc2801a8..0000000000 --- a/crates/editor/src/graphics/colors.rs +++ /dev/null @@ -1,31 +0,0 @@ -use palette::{FromColor, Hsv, LinSrgb, Srgb}; - -pub type RgbaTup = (f32, f32, f32, f32); -pub const WHITE: RgbaTup = (1.0, 1.0, 1.0, 1.0); - -pub fn to_wgpu_color((r, g, b, a): RgbaTup) -> wgpu::Color { - wgpu::Color { - r: r as f64, - g: g as f64, - b: b as f64, - a: a as f64, - } -} - -pub fn to_slice((r, g, b, a): RgbaTup) -> [f32; 4] { - [r, g, b, a] -} - -pub fn from_hsb(hue: usize, saturation: usize, brightness: usize) -> RgbaTup { - from_hsba(hue, saturation, brightness, 1.0) -} - -pub fn from_hsba(hue: usize, saturation: usize, brightness: usize, alpha: f32) -> RgbaTup { - let rgb = LinSrgb::from(Srgb::from_color(Hsv::new( - hue as f32, - (saturation as f32) / 100.0, - (brightness as f32) / 100.0, - ))); - - (rgb.red, rgb.green, rgb.blue, alpha) -} diff --git a/crates/editor/src/graphics/lowlevel/buffer.rs b/crates/editor/src/graphics/lowlevel/buffer.rs deleted file mode 100644 index 8439b735fa..0000000000 --- a/crates/editor/src/graphics/lowlevel/buffer.rs +++ /dev/null @@ -1,168 +0,0 @@ -// Adapted from https://github.com/sotrh/learn-wgpu -// by Benjamin Hansen - license information can be found in the LEGAL_DETAILS -// file in the root directory of this distribution. -// -// Thank you, Benjamin! -use super::vertex::Vertex; -use crate::graphics::colors::to_slice; -use crate::graphics::primitives::rect::Rect; -use wgpu::util::{BufferInitDescriptor, DeviceExt}; - -pub struct QuadBufferBuilder { - vertex_data: Vec, - index_data: Vec, - current_quad: u32, -} - -impl QuadBufferBuilder { - pub fn new() -> Self { - Self { - vertex_data: Vec::new(), - index_data: Vec::new(), - current_quad: 0, - } - } - - pub fn push_rect(self, rect: &Rect) -> Self { - let coords = rect.top_left_coords; - self.push_quad( - coords.x, - coords.y, - coords.x + rect.width, - coords.y + rect.height, - to_slice(rect.color), - ) - } - - pub fn push_quad( - mut self, - min_x: f32, - min_y: f32, - max_x: f32, - max_y: f32, - color: [f32; 4], - ) -> Self { - self.vertex_data.extend([ - Vertex { - position: (min_x, min_y).into(), - color, - }, - Vertex { - position: (max_x, min_y).into(), - color, - }, - Vertex { - position: (max_x, max_y).into(), - color, - }, - Vertex { - position: (min_x, max_y).into(), - color, - }, - ]); - self.index_data.extend([ - self.current_quad * 4, - self.current_quad * 4 + 1, - self.current_quad * 4 + 2, - self.current_quad * 4, - self.current_quad * 4 + 2, - self.current_quad * 4 + 3, - ]); - self.current_quad += 1; - self - } - - pub fn build(self, device: &wgpu::Device) -> (StagingBuffer, StagingBuffer, u32) { - ( - StagingBuffer::new(device, &self.vertex_data), - StagingBuffer::new(device, &self.index_data), - self.index_data.len() as u32, - ) - } -} - -impl Default for QuadBufferBuilder { - fn default() -> Self { - Self::new() - } -} - -pub struct RectBuffers { - pub vertex_buffer: wgpu::Buffer, - pub index_buffer: wgpu::Buffer, - pub num_rects: u32, -} - -pub fn create_rect_buffers( - gpu_device: &wgpu::Device, - encoder: &mut wgpu::CommandEncoder, - rects: &[Rect], -) -> RectBuffers { - let nr_of_rects = rects.len() as u64; - - let vertex_buffer = gpu_device.create_buffer(&wgpu::BufferDescriptor { - label: None, - size: Vertex::SIZE * 4 * nr_of_rects, - usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, - mapped_at_creation: false, - }); - - let u32_size = std::mem::size_of::() as wgpu::BufferAddress; - - let index_buffer = gpu_device.create_buffer(&wgpu::BufferDescriptor { - label: None, - size: u32_size * 6 * nr_of_rects, - usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST, - mapped_at_creation: false, - }); - - let num_rects = { - let mut quad_buffer_builder = QuadBufferBuilder::new(); - for rect in rects { - quad_buffer_builder = quad_buffer_builder.push_rect(rect); - } - - let (stg_vertex, stg_index, num_indices) = quad_buffer_builder.build(gpu_device); - - stg_vertex.copy_to_buffer(encoder, &vertex_buffer); - stg_index.copy_to_buffer(encoder, &index_buffer); - num_indices - }; - - RectBuffers { - vertex_buffer, - index_buffer, - num_rects, - } -} - -pub struct StagingBuffer { - buffer: wgpu::Buffer, - size: wgpu::BufferAddress, -} - -impl StagingBuffer { - pub fn new(device: &wgpu::Device, data: &[T]) -> StagingBuffer { - StagingBuffer { - buffer: device.create_buffer_init(&BufferInitDescriptor { - contents: bytemuck::cast_slice(data), - usage: wgpu::BufferUsages::COPY_SRC, - label: Some("Staging Buffer"), - }), - size: size_of_slice(data) as wgpu::BufferAddress, - } - } - - pub fn copy_to_buffer(&self, encoder: &mut wgpu::CommandEncoder, other: &wgpu::Buffer) { - encoder.copy_buffer_to_buffer(&self.buffer, 0, other, 0, self.size) - } -} - -// Taken from https://github.com/sotrh/learn-wgpu -// by Benjamin Hansen - license information can be found in the LEGAL_DETAILS -// file in the root directory of this distribution. -// -// Thank you, Benjamin! -pub fn size_of_slice(slice: &[T]) -> usize { - std::mem::size_of_val(slice) -} diff --git a/crates/editor/src/graphics/lowlevel/mod.rs b/crates/editor/src/graphics/lowlevel/mod.rs deleted file mode 100644 index 71f3ecc015..0000000000 --- a/crates/editor/src/graphics/lowlevel/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod buffer; -pub mod ortho; -pub mod pipelines; -pub mod vertex; diff --git a/crates/editor/src/graphics/lowlevel/ortho.rs b/crates/editor/src/graphics/lowlevel/ortho.rs deleted file mode 100644 index 9e4348e809..0000000000 --- a/crates/editor/src/graphics/lowlevel/ortho.rs +++ /dev/null @@ -1,117 +0,0 @@ -use cgmath::{Matrix4, Ortho}; -use wgpu::util::DeviceExt; -use wgpu::{ - BindGroup, BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, Buffer, - ShaderStages, -}; - -// orthographic projection is used to transform pixel coords to the coordinate system used by wgpu - -#[repr(C)] -#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] -struct Uniforms { - // We can't use cgmath with bytemuck directly so we'll have - // to convert the Matrix4 into a 4x4 f32 array - ortho: [[f32; 4]; 4], -} - -impl Uniforms { - fn new(w: u32, h: u32) -> Self { - let ortho: Matrix4 = Ortho:: { - left: 0.0, - right: w as f32, - bottom: h as f32, - top: 0.0, - near: -1.0, - far: 1.0, - } - .into(); - Self { - ortho: ortho.into(), - } - } -} - -// update orthographic buffer according to new window size -pub fn update_ortho_buffer( - inner_width: u32, - inner_height: u32, - gpu_device: &wgpu::Device, - ortho_buffer: &Buffer, - cmd_queue: &wgpu::Queue, -) { - let new_uniforms = Uniforms::new(inner_width, inner_height); - - let new_ortho_buffer = gpu_device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Ortho uniform buffer"), - contents: bytemuck::cast_slice(&[new_uniforms]), - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_SRC, - }); - - // get a command encoder for the current frame - let mut encoder = gpu_device.create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("Resize"), - }); - - // overwrite the new buffer over the old one - encoder.copy_buffer_to_buffer( - &new_ortho_buffer, - 0, - ortho_buffer, - 0, - std::mem::size_of_val(vec![new_uniforms].as_slice()) as wgpu::BufferAddress, - ); - - cmd_queue.submit(Some(encoder.finish())); -} - -#[derive(Debug)] -pub struct OrthoResources { - pub buffer: Buffer, - pub bind_group_layout: BindGroupLayout, - pub bind_group: BindGroup, -} - -pub fn init_ortho( - inner_width: u32, - inner_height: u32, - gpu_device: &wgpu::Device, -) -> OrthoResources { - let uniforms = Uniforms::new(inner_width, inner_height); - - let ortho_buffer = gpu_device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Ortho uniform buffer"), - contents: bytemuck::cast_slice(&[uniforms]), - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - }); - - // bind groups consist of extra resources that are provided to the shaders - let ortho_bind_group_layout = gpu_device.create_bind_group_layout(&BindGroupLayoutDescriptor { - entries: &[BindGroupLayoutEntry { - binding: 0, - visibility: ShaderStages::VERTEX, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }], - label: Some("Ortho bind group layout"), - }); - - let ortho_bind_group = gpu_device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &ortho_bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: ortho_buffer.as_entire_binding(), - }], - label: Some("Ortho bind group"), - }); - - OrthoResources { - buffer: ortho_buffer, - bind_group_layout: ortho_bind_group_layout, - bind_group: ortho_bind_group, - } -} diff --git a/crates/editor/src/graphics/lowlevel/pipelines.rs b/crates/editor/src/graphics/lowlevel/pipelines.rs deleted file mode 100644 index 1063fcf551..0000000000 --- a/crates/editor/src/graphics/lowlevel/pipelines.rs +++ /dev/null @@ -1,71 +0,0 @@ -use super::ortho::{init_ortho, OrthoResources}; -use super::vertex::Vertex; -use std::borrow::Cow; - -pub struct RectResources { - pub pipeline: wgpu::RenderPipeline, - pub ortho: OrthoResources, -} - -pub fn make_rect_pipeline( - gpu_device: &wgpu::Device, - surface_config: &wgpu::SurfaceConfiguration, -) -> RectResources { - let ortho = init_ortho(surface_config.width, surface_config.height, gpu_device); - - let pipeline_layout = gpu_device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - bind_group_layouts: &[&ortho.bind_group_layout], - push_constant_ranges: &[], - label: Some("Rectangle pipeline layout"), - }); - let pipeline = create_render_pipeline( - gpu_device, - &pipeline_layout, - surface_config.format, - &wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("../shaders/shader.wgsl"))), - }, - ); - - RectResources { pipeline, ortho } -} - -pub fn create_render_pipeline( - device: &wgpu::Device, - layout: &wgpu::PipelineLayout, - color_format: wgpu::TextureFormat, - shader_module_desc: &wgpu::ShaderModuleDescriptor, -) -> wgpu::RenderPipeline { - let shader = device.create_shader_module(shader_module_desc); - - device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Render pipeline"), - layout: Some(layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "vs_main", - buffers: &[Vertex::DESC], - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: "fs_main", - targets: &[wgpu::ColorTargetState { - format: color_format, - blend: Some(wgpu::BlendState { - color: wgpu::BlendComponent { - operation: wgpu::BlendOperation::Add, - src_factor: wgpu::BlendFactor::SrcAlpha, - dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, - }, - alpha: wgpu::BlendComponent::REPLACE, - }), - write_mask: wgpu::ColorWrites::ALL, - }], - }), - primitive: wgpu::PrimitiveState::default(), - depth_stencil: None, - multisample: wgpu::MultisampleState::default(), - multiview: None, - }) -} diff --git a/crates/editor/src/graphics/lowlevel/vertex.rs b/crates/editor/src/graphics/lowlevel/vertex.rs deleted file mode 100644 index 08a91c2d18..0000000000 --- a/crates/editor/src/graphics/lowlevel/vertex.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Taken from https://github.com/sotrh/learn-wgpu -// by Benjamin Hansen - license information can be found in the LEGAL_DETAILS -// file in the root directory of this distribution. -// -// Thank you, Benjamin! -use cgmath::Vector2; - -#[derive(Copy, Clone)] -#[repr(C)] -pub struct Vertex { - pub position: Vector2, - pub color: [f32; 4], -} - -// Safety: As defined, there is no padding -// the type is repr(C), and Vector2 is repr(C) -unsafe impl bytemuck::Pod for Vertex {} -unsafe impl bytemuck::Zeroable for Vertex {} - -impl Vertex { - pub const SIZE: wgpu::BufferAddress = std::mem::size_of::() as wgpu::BufferAddress; - pub const DESC: wgpu::VertexBufferLayout<'static> = wgpu::VertexBufferLayout { - array_stride: Self::SIZE, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &[ - // position - wgpu::VertexAttribute { - offset: 0, - shader_location: 0, - format: wgpu::VertexFormat::Float32x2, - }, - // color - wgpu::VertexAttribute { - offset: std::mem::size_of::<[f32; 2]>() as wgpu::BufferAddress, - shader_location: 1, - format: wgpu::VertexFormat::Float32x4, - }, - ], - }; -} diff --git a/crates/editor/src/graphics/mod.rs b/crates/editor/src/graphics/mod.rs deleted file mode 100644 index 0eb7fcd6da..0000000000 --- a/crates/editor/src/graphics/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod colors; -pub mod lowlevel; -pub mod primitives; -pub mod style; diff --git a/crates/editor/src/graphics/primitives/mod.rs b/crates/editor/src/graphics/primitives/mod.rs deleted file mode 100644 index a9adb18862..0000000000 --- a/crates/editor/src/graphics/primitives/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod rect; -pub mod text; diff --git a/crates/editor/src/graphics/primitives/rect.rs b/crates/editor/src/graphics/primitives/rect.rs deleted file mode 100644 index ddf26f1199..0000000000 --- a/crates/editor/src/graphics/primitives/rect.rs +++ /dev/null @@ -1,19 +0,0 @@ -use cgmath::Vector2; - -/// These fields are ordered this way because in Roc, the corresponding stuct is: -/// -/// { top : F32, left : F32, width : F32, height : F32 } -/// -/// alphabetically, that's { height, left, top, width } - which works out to the same as: -/// -/// height: f32, pos: Vector2, width: f32 -/// -/// ...because Vector2 is a repr(C) struct of { x: f32, y: f32 } -#[derive(Debug, Copy, Clone)] -#[repr(C)] -pub struct Rect { - pub color: (f32, f32, f32, f32), - pub height: f32, - pub top_left_coords: Vector2, - pub width: f32, -} diff --git a/crates/editor/src/graphics/primitives/text.rs b/crates/editor/src/graphics/primitives/text.rs deleted file mode 100644 index 57cd6d2ef9..0000000000 --- a/crates/editor/src/graphics/primitives/text.rs +++ /dev/null @@ -1,160 +0,0 @@ -// Adapted from https://github.com/sotrh/learn-wgpu -// by Benjamin Hansen - license information can be found in the LEGAL_DETAILS -// file in the root directory of this distribution. -// -// Thank you, Benjamin! - -use super::rect::Rect; -use crate::graphics::colors; -use crate::graphics::colors::RgbaTup; -use crate::graphics::style::DEFAULT_FONT_SIZE; -use ab_glyph::{FontArc, Glyph, InvalidFont}; -use cgmath::{Vector2, Vector4}; -use glyph_brush::OwnedSection; -use wgpu_glyph::{ab_glyph, GlyphBrush, GlyphBrushBuilder, GlyphCruncher, Section}; - -#[derive(Debug)] -pub struct Text<'a> { - pub position: Vector2, - pub area_bounds: Vector2, - pub color: RgbaTup, - pub text: &'a str, - pub size: f32, - pub visible: bool, - pub centered: bool, -} - -impl<'a> Default for Text<'a> { - fn default() -> Self { - Self { - position: (0.0, 0.0).into(), - area_bounds: (std::f32::INFINITY, std::f32::INFINITY).into(), - color: colors::WHITE, - text: "", - size: DEFAULT_FONT_SIZE, - visible: true, - centered: false, - } - } -} - -// necessary to get dimensions for caret -pub fn example_code_glyph_rect(glyph_brush: &mut GlyphBrush<()>, font_size: f32) -> Rect { - let code_text = Text { - position: (0.0, 0.0).into(), - area_bounds: (std::f32::INFINITY, std::f32::INFINITY).into(), - color: colors::WHITE, - text: "a", - size: font_size, - ..Default::default() - }; - - let layout = layout_from_text(&code_text); - - let section = section_from_text(&code_text, layout); - - let mut glyph_section_iter = glyph_brush.glyphs_custom_layout(section, &layout); - - if let Some(glyph) = glyph_section_iter.next() { - glyph_to_rect(glyph) - } else { - unreachable!(); - } -} - -pub fn layout_from_text(text: &Text) -> wgpu_glyph::Layout { - wgpu_glyph::Layout::default().h_align(if text.centered { - wgpu_glyph::HorizontalAlign::Center - } else { - wgpu_glyph::HorizontalAlign::Left - }) -} - -fn section_from_text<'a>( - text: &'a Text, - layout: wgpu_glyph::Layout, -) -> wgpu_glyph::Section<'a> { - Section { - screen_position: text.position.into(), - bounds: text.area_bounds.into(), - layout, - ..Section::default() - } - .add_text( - wgpu_glyph::Text::new(text.text) - .with_color(Vector4::from(text.color)) - .with_scale(text.size), - ) -} - -pub fn owned_section_from_text(text: &Text) -> OwnedSection { - let layout = layout_from_text(text); - - OwnedSection { - screen_position: text.position.into(), - bounds: text.area_bounds.into(), - layout, - ..OwnedSection::default() - } - .add_text( - glyph_brush::OwnedText::new(text.text) - .with_color(Vector4::from(text.color)) - .with_scale(text.size), - ) -} - -pub fn owned_section_from_glyph_texts( - text: Vec, - screen_position: (f32, f32), - area_bounds: (f32, f32), - layout: wgpu_glyph::Layout, -) -> glyph_brush::OwnedSection { - glyph_brush::OwnedSection { - screen_position, - bounds: area_bounds, - layout, - text, - } -} - -pub fn queue_text_draw(text: &Text, glyph_brush: &mut GlyphBrush<()>) { - let layout = layout_from_text(text); - - let section = section_from_text(text, layout); - - glyph_brush.queue(section.clone()); -} - -fn glyph_to_rect(glyph: &wgpu_glyph::SectionGlyph) -> Rect { - let position = glyph.glyph.position; - let px_scale = glyph.glyph.scale; - let width = glyph_width(&glyph.glyph); - let height = px_scale.y; - let top_y = glyph_top_y(&glyph.glyph); - - Rect { - top_left_coords: [position.x, top_y].into(), - width, - height, - color: colors::WHITE, - } -} - -pub fn glyph_top_y(glyph: &Glyph) -> f32 { - let height = glyph.scale.y; - - glyph.position.y - height * 0.75 -} - -pub fn glyph_width(glyph: &Glyph) -> f32 { - glyph.scale.x * 0.4765 -} - -pub fn build_glyph_brush( - gpu_device: &wgpu::Device, - render_format: wgpu::TextureFormat, -) -> Result, InvalidFont> { - let inconsolata = FontArc::try_from_slice(include_bytes!("../../../Inconsolata-Regular.ttf"))?; - - Ok(GlyphBrushBuilder::using_font(inconsolata).build(gpu_device, render_format)) -} diff --git a/crates/editor/src/graphics/shaders/shader.wgsl b/crates/editor/src/graphics/shaders/shader.wgsl deleted file mode 100644 index 660833662d..0000000000 --- a/crates/editor/src/graphics/shaders/shader.wgsl +++ /dev/null @@ -1,29 +0,0 @@ -struct VertexOutput { - [[location(0)]] color: vec4; - [[builtin(position)]] position: vec4; -}; - -struct Globals { - ortho: mat4x4; -}; - -[[group(0), binding(0)]] -var u_globals: Globals; - -[[stage(vertex)]] -fn vs_main( - [[location(0)]] in_position: vec2, - [[location(1)]] in_color: vec4, -) -> VertexOutput { - var out: VertexOutput; - - out.position = u_globals.ortho * vec4(in_position, 0.0, 1.0); - out.color = in_color; - - return out; -} - -[[stage(fragment)]] -fn fs_main(in: VertexOutput) -> [[location(0)]] vec4 { - return in.color; -} \ No newline at end of file diff --git a/crates/editor/src/graphics/style.rs b/crates/editor/src/graphics/style.rs deleted file mode 100644 index 11e609075b..0000000000 --- a/crates/editor/src/graphics/style.rs +++ /dev/null @@ -1 +0,0 @@ -pub const DEFAULT_FONT_SIZE: f32 = 30.0; diff --git a/crates/editor/src/lib.rs b/crates/editor/src/lib.rs deleted file mode 100644 index 2b11b3e6ae..0000000000 --- a/crates/editor/src/lib.rs +++ /dev/null @@ -1,21 +0,0 @@ -//! Roc's editor. Work In Progress. -#![warn(clippy::dbg_macro)] -// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. -#![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)] - -#[cfg_attr(test, macro_use)] -extern crate pest; -#[cfg_attr(test, macro_use)] -extern crate pest_derive; - -mod editor; -mod graphics; -mod ui; -mod window; - -use std::io; -use std::path::Path; - -pub fn launch(project_dir_path_opt: Option<&Path>) -> io::Result<()> { - editor::main::launch(project_dir_path_opt) -} diff --git a/crates/editor/src/ui/mod.rs b/crates/editor/src/ui/mod.rs deleted file mode 100644 index 308df14af0..0000000000 --- a/crates/editor/src/ui/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod text; -pub mod theme; -pub mod tooltip; -pub mod ui_error; -pub mod util; diff --git a/crates/editor/src/ui/text/big_text_area.rs b/crates/editor/src/ui/text/big_text_area.rs deleted file mode 100644 index 178882376a..0000000000 --- a/crates/editor/src/ui/text/big_text_area.rs +++ /dev/null @@ -1,3411 +0,0 @@ -// Adapted from https://github.com/cessen/ropey by Nathan Vegdahl, licensed under the MIT license - -#![allow(dead_code)] - -use crate::ui::text::{ - caret_w_select::CaretWSelect, - lines, - lines::{Lines, MutSelectableLines, SelectableLines}, - selection::{validate_raw_sel, RawSelection, Selection}, - text_pos::TextPos, -}; -use crate::ui::ui_error::{OutOfBoundsSnafu, UIResult}; -use crate::ui::util::is_newline; -use crate::window::keyboard_input::{no_mods, Modifiers}; -use bumpalo::Bump; -use snafu::ensure; -use std::{fmt, path::Path}; -use winit::event::{VirtualKeyCode, VirtualKeyCode::*}; - -use super::text_buffer::TextBuffer; - -pub struct BigTextArea { - pub caret_w_select: CaretWSelect, - text_buffer: TextBuffer, - pub path_str: String, - arena: Bump, -} - -impl BigTextArea { - fn check_bounds(&self, char_indx: usize) -> UIResult<()> { - ensure!( - char_indx <= self.text_buffer.nr_of_chars(), - OutOfBoundsSnafu { - index: char_indx, - collection_name: "TextBuffer", - len: self.text_buffer.nr_of_chars() - } - ); - - Ok(()) - } -} - -impl Lines for BigTextArea { - fn get_line_ref(&self, line_nr: usize) -> UIResult<&str> { - self.text_buffer.get_line_ref(line_nr) - } - - fn line_len(&self, line_nr: usize) -> UIResult { - self.get_line_ref(line_nr).map(|line| line.len()) - } - - fn nr_of_lines(&self) -> usize { - self.text_buffer.nr_of_lines() - } - - fn nr_of_chars(&self) -> usize { - self.text_buffer.nr_of_chars() - } - - fn all_lines_as_string(&self) -> String { - self.text_buffer.all_lines_ref().join("\n") - } - - fn is_last_line(&self, line_nr: usize) -> bool { - line_nr == self.nr_of_lines() - 1 - } - - fn last_char(&self, line_nr: usize) -> UIResult> { - Ok(self.get_line_ref(line_nr)?.chars().last()) - } -} - -impl SelectableLines for BigTextArea { - fn get_caret(&self) -> TextPos { - self.caret_w_select.caret_pos - } - - fn set_caret(&mut self, caret_pos: TextPos) { - self.caret_w_select.caret_pos = caret_pos; - } - - fn move_caret_left(&mut self, modifiers: &Modifiers) -> UIResult<()> { - self.caret_w_select = lines::move_caret_left(self, self.caret_w_select, modifiers)?; - - Ok(()) - } - - fn move_caret_right(&mut self, modifiers: &Modifiers) -> UIResult<()> { - self.caret_w_select = lines::move_caret_right(self, self.caret_w_select, modifiers)?; - - Ok(()) - } - - fn move_caret_up(&mut self, modifiers: &Modifiers) -> UIResult<()> { - self.caret_w_select = lines::move_caret_up(self, self.caret_w_select, modifiers)?; - - Ok(()) - } - - fn move_caret_down(&mut self, modifiers: &Modifiers) -> UIResult<()> { - self.caret_w_select = lines::move_caret_down(self, self.caret_w_select, modifiers)?; - - Ok(()) - } - - fn move_caret_home(&mut self, modifiers: &Modifiers) -> UIResult<()> { - self.caret_w_select = lines::move_caret_home(self, self.caret_w_select, modifiers)?; - - Ok(()) - } - - fn move_caret_end(&mut self, modifiers: &Modifiers) -> UIResult<()> { - self.caret_w_select = lines::move_caret_end(self, self.caret_w_select, modifiers)?; - - Ok(()) - } - - fn get_selection(&self) -> Option { - self.caret_w_select.selection_opt - } - - fn is_selection_active(&self) -> bool { - self.get_selection().is_some() - } - - fn get_selected_str(&self) -> UIResult> { - if let Some(val_sel) = self.caret_w_select.selection_opt { - Ok(Some(self.text_buffer.get_selected_str(val_sel)?)) - } else { - Ok(None) - } - } - - fn set_raw_sel(&mut self, raw_sel: RawSelection) -> UIResult<()> { - self.caret_w_select.selection_opt = Some(validate_raw_sel(raw_sel)?); - - Ok(()) - } - - fn set_caret_w_sel(&mut self, caret_w_sel: CaretWSelect) { - self.caret_w_select = caret_w_sel; - } - - fn set_sel_none(&mut self) { - self.caret_w_select.selection_opt = None; - } - - fn select_all(&mut self) -> UIResult<()> { - if self.nr_of_chars() > 0 { - let last_pos = self.last_text_pos()?; - - self.set_raw_sel(RawSelection { - start_pos: TextPos { line: 0, column: 0 }, - end_pos: last_pos, - })?; - - self.set_caret(last_pos); - } - - Ok(()) - } - - fn last_text_pos(&self) -> UIResult { - let line_nr = self.nr_of_lines() - 1; - let last_col = self.line_len(line_nr)?; - - Ok(TextPos { - line: line_nr, - column: last_col, - }) - } - - fn handle_key_down( - &mut self, - modifiers: &Modifiers, - virtual_keycode: VirtualKeyCode, - ) -> UIResult<()> { - match virtual_keycode { - Left => self.move_caret_left(modifiers), - Up => self.move_caret_up(modifiers), - Right => self.move_caret_right(modifiers), - Down => self.move_caret_down(modifiers), - - A => { - if modifiers.cmd_or_ctrl() { - self.select_all() - } else { - Ok(()) - } - } - Home => self.move_caret_home(modifiers), - End => self.move_caret_end(modifiers), - _ => Ok(()), - } - } -} - -impl MutSelectableLines for BigTextArea { - fn insert_char(&mut self, new_char: &char) -> UIResult<()> { - if self.is_selection_active() { - self.del_selection()?; - } - - self.insert_str(&new_char.to_string())?; - - if is_newline(new_char) { - self.set_caret(TextPos { - line: self.caret_w_select.caret_pos.line + 1, - column: 0, - }); - } else { - self.move_caret_right(&no_mods())?; - } - - self.set_sel_none(); - - Ok(()) - } - - fn handle_new_char(&mut self, received_char: &char) -> UIResult<()> { - match received_char { - '\u{8}' | '\u{7f}' => { - // On Linux, '\u{8}' is backspace, - // on macOS '\u{7f}'. - - self.backspace()? - } - - '\u{1}' // Ctrl + A - | '\u{3}' // Ctrl + C - | '\u{16}' // Ctrl + V - | '\u{18}' // Ctrl + X - | '\u{e000}'..='\u{f8ff}' // http://www.unicode.org/faq/private_use.html - | '\u{f0000}'..='\u{ffffd}' // ^ - | '\u{100000}'..='\u{10fffd}' // ^ - => { - // chars that can be ignored - } - - _ => { - self.insert_char(received_char)?; - } - } - - Ok(()) - } - - fn insert_str(&mut self, new_str: &str) -> UIResult<()> { - let caret_pos = self.caret_w_select.caret_pos; - - self.text_buffer.insert_str(caret_pos, new_str)?; - - Ok(()) - } - - fn backspace(&mut self) -> UIResult<()> { - if self.is_selection_active() { - self.del_selection()?; - } else { - let old_caret_pos = self.caret_w_select.caret_pos; - - self.move_caret_left(&no_mods())?; - - self.text_buffer.backspace_char(old_caret_pos)?; - } - - Ok(()) - } - - fn del_selection(&mut self) -> UIResult<()> { - if let Some(selection) = self.caret_w_select.selection_opt { - self.text_buffer.del_selection(selection)?; - - self.set_caret(selection.start_pos); - - self.set_sel_none(); - } - - Ok(()) - } -} - -impl Default for BigTextArea { - fn default() -> Self { - let caret_w_select = CaretWSelect::default(); - let text_buffer = TextBuffer { lines: Vec::new() }; - let path_str = "".to_owned(); - let arena = Bump::new(); - - Self { - caret_w_select, - text_buffer, - path_str, - arena, - } - } -} - -pub fn from_path(path: &Path) -> UIResult { - let text_buffer = TextBuffer::from_path(path)?; - let path_str = path_to_string(path); - - Ok(BigTextArea { - text_buffer, - path_str, - ..Default::default() - }) -} - -#[allow(dead_code)] -// used by tests but will also be used in the future -pub fn from_str_vec(lines: Vec) -> BigTextArea { - BigTextArea { - text_buffer: TextBuffer { lines }, - ..Default::default() - } -} - -fn path_to_string(path: &Path) -> String { - let mut path_str = String::new(); - path_str.push_str(&path.to_string_lossy()); - - path_str -} - -// need to explicitly omit arena -impl fmt::Debug for BigTextArea { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("BigTextArea") - .field("caret_w_select", &self.caret_w_select) - .field("text_buffer", &self.text_buffer) - .field("path_str", &self.path_str) - .finish() - } -} - -#[cfg(test)] -pub mod test_big_sel_text { - use crate::ui::text::caret_w_select::test_caret_w_select::convert_dsl_to_selection; - use crate::ui::text::caret_w_select::test_caret_w_select::convert_selection_to_dsl; - use crate::ui::text::{ - big_text_area::BigTextArea, - lines::{Lines, MutSelectableLines, SelectableLines}, - text_pos::TextPos, - }; - use crate::ui::ui_error::{OutOfBoundsSnafu, UIResult}; - use crate::window::keyboard_input::{no_mods, Modifiers}; - use snafu::OptionExt; - use std::slice::SliceIndex; - - use super::from_str_vec; - - fn shift_pressed() -> Modifiers { - Modifiers { - shift: true, - ..Default::default() - } - } - - fn insert_at_pos(lines: &mut [String], pos: TextPos, insert_char: char) -> UIResult<()> { - let line = get_mut_res(pos.line, lines)?; - line.insert(pos.column, insert_char); - - Ok(()) - } - - // It's much nicer to have get_mut return a Result with clear error than an Option - fn get_mut_res( - index: usize, - vec: &mut [T], - ) -> UIResult<&mut >::Output> { - let vec_len = vec.len(); - - let elt_ref = vec.get_mut(index).context(OutOfBoundsSnafu { - index, - collection_name: "Slice", - len: vec_len, - })?; - - Ok(elt_ref) - } - - pub fn big_text_from_dsl_str(lines: &[String]) -> BigTextArea { - from_str_vec( - lines - .iter() - .map(|line| line.replace(&['❮', '❯', '┃'][..], "")) - .collect::>(), - ) - } - - pub fn all_lines_vec(big_sel_text: &BigTextArea) -> Vec { - let mut lines: Vec = Vec::new(); - - for i in 0..big_sel_text.nr_of_lines() { - lines.push(big_sel_text.get_line_ref(i).unwrap().to_string()); - } - - lines - } - - pub fn gen_big_text(lines: &[&str]) -> Result { - let lines_string_slice: Vec = lines.iter().map(|l| l.to_string()).collect(); - let mut big_text = big_text_from_dsl_str(&lines_string_slice); - let caret_w_select = convert_dsl_to_selection(&lines_string_slice).unwrap(); - - big_text.caret_w_select = caret_w_select; - - Ok(big_text) - } - - fn assert_insert( - pre_lines_str: &[&str], - expected_post_lines_str: &[&str], - new_char: char, - ) -> Result<(), String> { - let mut big_text = gen_big_text(pre_lines_str)?; - - if let Err(e) = big_text.handle_new_char(&new_char) { - return Err(e.to_string()); - } - - let actual_lines = all_lines_vec(&big_text); - let dsl_slice = convert_selection_to_dsl(big_text.caret_w_select, actual_lines).unwrap(); - assert_eq!(dsl_slice, expected_post_lines_str); - - Ok(()) - } - - #[test] - fn insert_new_char_simple() -> Result<(), String> { - assert_insert(&["┃"], &["a┃"], 'a')?; - assert_insert(&["┃"], &[" ┃"], ' ')?; - assert_insert(&["a┃"], &["aa┃"], 'a')?; - assert_insert(&["a┃"], &["a ┃"], ' ')?; - assert_insert(&["a┃", ""], &["ab┃", ""], 'b')?; - assert_insert(&["a", "┃"], &["a", "b┃"], 'b')?; - assert_insert(&["a", "b", "c┃"], &["a", "b", "cd┃"], 'd')?; - - Ok(()) - } - - #[test] - fn insert_new_char_mid() -> Result<(), String> { - assert_insert(&["ab┃d"], &["abc┃d"], 'c')?; - assert_insert(&["a┃cd"], &["ab┃cd"], 'b')?; - assert_insert(&["abc", "┃e"], &["abc", "d┃e"], 'd')?; - assert_insert(&["abc", "def", "┃ "], &["abc", "def", "g┃ "], 'g')?; - assert_insert(&["abc", "def", "┃ "], &["abc", "def", " ┃ "], ' ')?; - - Ok(()) - } - - #[test] - fn simple_backspace() -> Result<(), String> { - assert_insert(&["┃"], &["┃"], '\u{8}')?; - assert_insert(&[" ┃"], &["┃"], '\u{8}')?; - assert_insert(&["a┃"], &["┃"], '\u{8}')?; - assert_insert(&["ab┃"], &["a┃"], '\u{8}')?; - assert_insert(&["a┃", ""], &["┃", ""], '\u{8}')?; - assert_insert(&["ab┃", ""], &["a┃", ""], '\u{8}')?; - assert_insert(&["a", "┃"], &["a┃"], '\u{8}')?; - assert_insert(&["a", "b", "c┃"], &["a", "b", "┃"], '\u{8}')?; - assert_insert(&["a", "b", "┃"], &["a", "b┃"], '\u{8}')?; - - Ok(()) - } - - #[test] - fn selection_backspace() -> Result<(), String> { - assert_insert(&["❮a❯┃"], &["┃"], '\u{8}')?; - assert_insert(&["a❮a❯┃"], &["a┃"], '\u{8}')?; - assert_insert(&["❮aa❯┃"], &["┃"], '\u{8}')?; - assert_insert(&["a❮b c❯┃"], &["a┃"], '\u{8}')?; - assert_insert(&["❮abc❯┃", ""], &["┃", ""], '\u{8}')?; - assert_insert(&["a", "❮abc❯┃"], &["a", "┃"], '\u{8}')?; - assert_insert(&["❮a", "abc❯┃"], &["┃"], '\u{8}')?; - assert_insert(&["a❮b", "cdef ghij❯┃"], &["a┃"], '\u{8}')?; - assert_insert(&["❮a", "b", "c❯┃"], &["┃"], '\u{8}')?; - assert_insert(&["a", "❮b", "❯┃"], &["a", "┃"], '\u{8}')?; - assert_insert( - &["abc", "d❮ef", "ghi❯┃", "jkl"], - &["abc", "d┃", "jkl"], - '\u{8}', - )?; - assert_insert( - &["abc", "❮def", "ghi❯┃", "jkl"], - &["abc", "┃", "jkl"], - '\u{8}', - )?; - assert_insert( - &["abc", "", "❮def", "ghi❯┃", "jkl"], - &["abc", "", "┃", "jkl"], - '\u{8}', - )?; - assert_insert(&["❮abc", "", "def", "ghi", "jkl❯┃"], &["┃"], '\u{8}')?; - - Ok(()) - } - - #[test] - fn insert_with_selection() -> Result<(), String> { - assert_insert(&["❮a❯┃"], &["z┃"], 'z')?; - assert_insert(&["a❮a❯┃"], &["az┃"], 'z')?; - assert_insert(&["❮aa❯┃"], &["z┃"], 'z')?; - assert_insert(&["a❮b c❯┃"], &["az┃"], 'z')?; - assert_insert(&["❮abc❯┃", ""], &["z┃", ""], 'z')?; - assert_insert(&["a", "❮abc❯┃"], &["a", "z┃"], 'z')?; - assert_insert(&["❮a", "abc❯┃"], &["z┃"], 'z')?; - assert_insert(&["a❮b", "cdef ghij❯┃"], &["az┃"], 'z')?; - assert_insert(&["❮a", "b", "c❯┃"], &["z┃"], 'z')?; - assert_insert(&["a", "❮b", "❯┃"], &["a", "z┃"], 'z')?; - assert_insert( - &["abc", "d❮ef", "ghi❯┃", "jkl"], - &["abc", "dz┃", "jkl"], - 'z', - )?; - assert_insert(&["abc", "❮def", "ghi❯┃", "jkl"], &["abc", "z┃", "jkl"], 'z')?; - assert_insert( - &["abc", "", "❮def", "ghi❯┃", "jkl"], - &["abc", "", "z┃", "jkl"], - 'z', - )?; - assert_insert(&["❮abc", "", "def", "ghi", "jkl❯┃"], &["z┃"], 'z')?; - - Ok(()) - } - - fn assert_select_all( - pre_lines_str: &[&str], - expected_post_lines_str: &[&str], - ) -> Result<(), String> { - let mut big_text = gen_big_text(pre_lines_str)?; - - big_text.select_all().unwrap(); - - let big_text_lines = all_lines_vec(&big_text); - let post_lines_str = convert_selection_to_dsl(big_text.caret_w_select, big_text_lines)?; - - assert_eq!(post_lines_str, expected_post_lines_str); - - Ok(()) - } - - #[test] - fn select_all() -> Result<(), String> { - assert_select_all(&["┃"], &["┃"])?; - assert_select_all(&["┃a"], &["❮a❯┃"])?; - assert_select_all(&["a┃"], &["❮a❯┃"])?; - assert_select_all(&["abc d┃ef ghi"], &["❮abc def ghi❯┃"])?; - assert_select_all(&["❮a❯┃"], &["❮a❯┃"])?; - assert_select_all(&["┃❮a❯"], &["❮a❯┃"])?; - assert_select_all(&["┃❮abc def ghi❯"], &["❮abc def ghi❯┃"])?; - assert_select_all(&["a", "❮b", "❯┃"], &["❮a", "b", "❯┃"])?; - assert_select_all(&["a", "❮b❯┃", ""], &["❮a", "b", "❯┃"])?; - assert_select_all(&["a", "┃❮b", "❯"], &["❮a", "b", "❯┃"])?; - assert_select_all( - &["abc", "def", "gh┃i", "jkl"], - &["❮abc", "def", "ghi", "jkl❯┃"], - )?; - assert_select_all( - &["┃❮abc", "def", "ghi", "jkl❯"], - &["❮abc", "def", "ghi", "jkl❯┃"], - )?; - - Ok(()) - } - - type MoveCaretFun = fn(&mut BigTextArea, &Modifiers) -> UIResult<()>; - - // Convert nice string representations and compare results - fn assert_move( - pre_lines_str: &[&str], - expected_post_lines_str: &[&str], - modifiers: &Modifiers, - move_fun: MoveCaretFun, - ) -> Result<(), String> { - let expected_post_lines: Vec = expected_post_lines_str - .iter() - .map(|l| l.to_string()) - .collect(); - - let mut big_text = gen_big_text(pre_lines_str)?; - - move_fun(&mut big_text, modifiers)?; - - let lines_vec = all_lines_vec(&big_text) - .iter() - .map(|l| l.replace('\n', "")) - .collect(); - let post_lines_res = convert_selection_to_dsl(big_text.caret_w_select, lines_vec); - - match post_lines_res { - Ok(post_lines) => { - assert_eq!(expected_post_lines, post_lines); - Ok(()) - } - Err(e) => Err(format!("{e:?}")), - } - } - - #[test] - fn move_right() -> Result<(), String> { - let move_caret_right = SelectableLines::move_caret_right; - - assert_move(&["┃"], &["┃"], &no_mods(), move_caret_right)?; - assert_move(&["a┃"], &["a┃"], &no_mods(), move_caret_right)?; - assert_move(&["┃A"], &["A┃"], &no_mods(), move_caret_right)?; - assert_move(&["┃abc"], &["a┃bc"], &no_mods(), move_caret_right)?; - assert_move(&["a┃bc"], &["ab┃c"], &no_mods(), move_caret_right)?; - assert_move(&["abc┃"], &["abc┃"], &no_mods(), move_caret_right)?; - assert_move(&["┃ abc"], &[" ┃abc"], &no_mods(), move_caret_right)?; - assert_move(&["abc┃ "], &["abc ┃"], &no_mods(), move_caret_right)?; - assert_move(&["abc┃", "d"], &["abc", "┃d"], &no_mods(), move_caret_right)?; - assert_move(&["abc┃", ""], &["abc", "┃"], &no_mods(), move_caret_right)?; - assert_move( - &["abc", "┃def"], - &["abc", "d┃ef"], - &no_mods(), - move_caret_right, - )?; - assert_move( - &["abc", "def┃ "], - &["abc", "def ┃"], - &no_mods(), - move_caret_right, - )?; - assert_move( - &["abc", "def ┃", "ghi"], - &["abc", "def ", "┃ghi"], - &no_mods(), - move_caret_right, - )?; - assert_move( - &["abc", "def┃", ""], - &["abc", "def", "┃"], - &no_mods(), - move_caret_right, - )?; - assert_move( - &["abc", "def", "ghi┃", "jkl"], - &["abc", "def", "ghi", "┃jkl"], - &no_mods(), - move_caret_right, - )?; - assert_move( - &["abc", "def", "┃ghi", "jkl"], - &["abc", "def", "g┃hi", "jkl"], - &no_mods(), - move_caret_right, - )?; - assert_move( - &["abc", "def", "g┃hi", "jkl"], - &["abc", "def", "gh┃i", "jkl"], - &no_mods(), - move_caret_right, - )?; - - Ok(()) - } - - #[test] - fn move_left() -> Result<(), String> { - let move_caret_left = SelectableLines::move_caret_left; - - assert_move(&["┃"], &["┃"], &no_mods(), move_caret_left)?; - assert_move(&["┃a"], &["┃a"], &no_mods(), move_caret_left)?; - assert_move(&["┃A"], &["┃A"], &no_mods(), move_caret_left)?; - assert_move(&["a┃bc"], &["┃abc"], &no_mods(), move_caret_left)?; - assert_move(&["ab┃c"], &["a┃bc"], &no_mods(), move_caret_left)?; - assert_move(&["abc┃"], &["ab┃c"], &no_mods(), move_caret_left)?; - assert_move(&[" ┃abc"], &["┃ abc"], &no_mods(), move_caret_left)?; - assert_move(&["abc ┃"], &["abc┃ "], &no_mods(), move_caret_left)?; - assert_move(&["abc", "┃d"], &["abc┃", "d"], &no_mods(), move_caret_left)?; - assert_move(&["abc", "┃"], &["abc┃", ""], &no_mods(), move_caret_left)?; - assert_move( - &["abc", "d┃ef"], - &["abc", "┃def"], - &no_mods(), - move_caret_left, - )?; - assert_move( - &["abc", "def ┃"], - &["abc", "def┃ "], - &no_mods(), - move_caret_left, - )?; - assert_move( - &["abc", "def ", "┃ghi"], - &["abc", "def ┃", "ghi"], - &no_mods(), - move_caret_left, - )?; - assert_move( - &["abc", "def", "┃"], - &["abc", "def┃", ""], - &no_mods(), - move_caret_left, - )?; - assert_move( - &["abc", "def", "ghi", "┃jkl"], - &["abc", "def", "ghi┃", "jkl"], - &no_mods(), - move_caret_left, - )?; - assert_move( - &["abc", "def", "g┃hi", "jkl"], - &["abc", "def", "┃ghi", "jkl"], - &no_mods(), - move_caret_left, - )?; - assert_move( - &["abc", "def", "gh┃i", "jkl"], - &["abc", "def", "g┃hi", "jkl"], - &no_mods(), - move_caret_left, - )?; - - Ok(()) - } - - #[test] - fn move_up() -> Result<(), String> { - let move_caret_up = SelectableLines::move_caret_up; - - assert_move(&["┃"], &["┃"], &no_mods(), move_caret_up)?; - assert_move(&["┃a"], &["┃a"], &no_mods(), move_caret_up)?; - assert_move(&["A┃"], &["┃A"], &no_mods(), move_caret_up)?; - assert_move(&["a┃bc"], &["┃abc"], &no_mods(), move_caret_up)?; - assert_move(&["ab┃c"], &["┃abc"], &no_mods(), move_caret_up)?; - assert_move(&["abc┃"], &["┃abc"], &no_mods(), move_caret_up)?; - assert_move( - &["┃abc", "def"], - &["┃abc", "def"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abc", "┃def"], - &["┃abc", "def"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abc", "d┃ef"], - &["a┃bc", "def"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abc", "de┃f"], - &["ab┃c", "def"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abc", "def┃"], - &["abc┃", "def"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abc", "def ", "┃ghi"], - &["abc", "┃def ", "ghi"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abc", "def ", "g┃hi"], - &["abc", "d┃ef ", "ghi"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abc", "def ", "gh┃i"], - &["abc", "de┃f ", "ghi"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abc", "def ", "ghi┃"], - &["abc", "def┃ ", "ghi"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abc", "de", "ghi┃"], - &["abc", "de┃", "ghi"], - &no_mods(), - move_caret_up, - )?; - assert_move(&["abc", "de┃"], &["ab┃c", "de"], &no_mods(), move_caret_up)?; - assert_move(&["abc", "d┃e"], &["a┃bc", "de"], &no_mods(), move_caret_up)?; - assert_move(&["abc", "┃de"], &["┃abc", "de"], &no_mods(), move_caret_up)?; - assert_move( - &["ab", "cdef", "ghijkl", "mnopqrst┃"], - &["ab", "cdef", "ghijkl┃", "mnopqrst"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["ab", "cdef", "ghijkl┃", "mnopqrst"], - &["ab", "cdef┃", "ghijkl", "mnopqrst"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["ab", "cdef", "ghijkl", "┃mnopqrst"], - &["ab", "cdef", "┃ghijkl", "mnopqrst"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &[" ab", " ┃cdef", "ghijkl", "mnopqrst"], - &[" ┃ab", " cdef", "ghijkl", "mnopqrst"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["ab", "cdef", "ghijkl", "mnopqr┃st"], - &["ab", "cdef", "ghijkl┃", "mnopqrst"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["ab", "cde┃f", "ghijkl", "mnopqrst"], - &["ab┃", "cdef", "ghijkl", "mnopqrst"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abcdefgh", "ijklmn", "opqr", "st┃"], - &["abcdefgh", "ijklmn", "op┃qr", "st"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abcdefgh", "ijklmn", "opqr┃", "st"], - &["abcdefgh", "ijkl┃mn", "opqr", "st"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abcdefgh", "ijklmn┃", "opqr", "st"], - &["abcdef┃gh", "ijklmn", "opqr", "st"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abcdefgh┃", "ijklmn", "opqr", "st"], - &["┃abcdefgh", "ijklmn", "opqr", "st"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abcdefg┃h", "ijklmn", "opqr", "st"], - &["┃abcdefgh", "ijklmn", "opqr", "st"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["a┃bcdefgh", "ijklmn", "opqr", "st"], - &["┃abcdefgh", "ijklmn", "opqr", "st"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["┃abcdefgh", "ijklmn", "opqr", "st"], - &["┃abcdefgh", "ijklmn", "opqr", "st"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abc def gh ┃"], - &["┃abc def gh "], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abc de┃f gh "], - &["┃abc def gh "], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["ab┃c def gh "], - &["┃abc def gh "], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["a┃bc def gh "], - &["┃abc def gh "], - &no_mods(), - move_caret_up, - )?; - - Ok(()) - } - - #[test] - fn move_down() -> Result<(), String> { - let move_caret_down = SelectableLines::move_caret_down; - - assert_move(&["┃"], &["┃"], &no_mods(), move_caret_down)?; - assert_move(&["┃a"], &["a┃"], &no_mods(), move_caret_down)?; - assert_move(&["A┃"], &["A┃"], &no_mods(), move_caret_down)?; - assert_move(&["a┃bc"], &["abc┃"], &no_mods(), move_caret_down)?; - assert_move(&["ab┃c"], &["abc┃"], &no_mods(), move_caret_down)?; - assert_move(&["abc┃"], &["abc┃"], &no_mods(), move_caret_down)?; - assert_move(&["abc┃ "], &["abc ┃"], &no_mods(), move_caret_down)?; - assert_move( - &["abc", "┃def"], - &["abc", "def┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abc", "d┃ef"], - &["abc", "def┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abc", "de┃f"], - &["abc", "def┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abc", "def┃"], - &["abc", "def┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["┃abc", "def"], - &["abc", "┃def"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["a┃bc", "def"], - &["abc", "d┃ef"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["ab┃c", "def"], - &["abc", "de┃f"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abc┃", "def"], - &["abc", "def┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abc", "┃def ", "ghi"], - &["abc", "def ", "┃ghi"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abc", "d┃ef ", "ghi"], - &["abc", "def ", "g┃hi"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abc", "de┃f ", "ghi"], - &["abc", "def ", "gh┃i"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abc", "def┃ ", "ghi"], - &["abc", "def ", "ghi┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abc", "def ┃", "ghi"], - &["abc", "def ", "ghi┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abc", "de┃", "ghi"], - &["abc", "de", "gh┃i"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abc┃", "de"], - &["abc", "de┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["ab┃c", "de"], - &["abc", "de┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["a┃bc", "de"], - &["abc", "d┃e"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["┃abc", "de"], - &["abc", "┃de"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["ab┃", "cdef", "ghijkl", "mnopqrst"], - &["ab", "cd┃ef", "ghijkl", "mnopqrst"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["ab", "cdef┃", "ghijkl", "mnopqrst"], - &["ab", "cdef", "ghij┃kl", "mnopqrst"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["ab", "cdef", "ghijkl┃", "mnopqrst"], - &["ab", "cdef", "ghijkl", "mnopqr┃st"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &[" ┃ab", " cdef", "ghijkl", "mnopqrst"], - &[" ab", " ┃cdef", "ghijkl", "mnopqrst"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["ab", "┃cdef", "ghijkl", "mnopqrst"], - &["ab", "cdef", "┃ghijkl", "mnopqrst"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["ab", "cdef", "┃ghijkl", "mnopqrst"], - &["ab", "cdef", "ghijkl", "┃mnopqrst"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abcdefgh┃", "ijklmn", "opqr", "st"], - &["abcdefgh", "ijklmn┃", "opqr", "st"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abcdefgh", "ijklmn┃", "opqr", "st"], - &["abcdefgh", "ijklmn", "opqr┃", "st"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abcdefgh", "ijklmn", "opqr┃", "st"], - &["abcdefgh", "ijklmn", "opqr", "st┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abcdefgh", "ijklmn", "opqr", "┃st"], - &["abcdefgh", "ijklmn", "opqr", "st┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abc def gh ┃"], - &["abc def gh ┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abc de┃f gh "], - &["abc def gh ┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["ab┃c def gh "], - &["abc def gh ┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["a┃bc def gh "], - &["abc def gh ┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["┃abc def gh "], - &["abc def gh ┃"], - &no_mods(), - move_caret_down, - )?; - - Ok(()) - } - - #[test] - fn move_home() -> Result<(), String> { - let move_caret_home = BigTextArea::move_caret_home; - assert_move(&["┃"], &["┃"], &no_mods(), move_caret_home)?; - assert_move(&["a┃"], &["┃a"], &no_mods(), move_caret_home)?; - assert_move(&["┃a"], &["┃a"], &no_mods(), move_caret_home)?; - assert_move(&[" ┃a"], &["┃ a"], &no_mods(), move_caret_home)?; - assert_move(&["┃ a"], &[" ┃a"], &no_mods(), move_caret_home)?; - assert_move(&[" a┃"], &[" ┃a"], &no_mods(), move_caret_home)?; - assert_move(&[" abc ┃"], &[" ┃abc "], &no_mods(), move_caret_home)?; - assert_move(&["\tabc ┃"], &["\t┃abc "], &no_mods(), move_caret_home)?; - assert_move(&["\t┃abc "], &["┃\tabc "], &no_mods(), move_caret_home)?; - assert_move(&["┃\tabc "], &["\t┃abc "], &no_mods(), move_caret_home)?; - assert_move( - &[" abc def\tghi┃"], - &[" ┃abc def\tghi"], - &no_mods(), - move_caret_home, - )?; - assert_move( - &[" ┃abc def\tghi"], - &["┃ abc def\tghi"], - &no_mods(), - move_caret_home, - )?; - assert_move( - &["┃ abc def\tghi"], - &[" ┃abc def\tghi"], - &no_mods(), - move_caret_home, - )?; - - assert_move( - &["abc", "de┃", "ghi"], - &["abc", "┃de", "ghi"], - &no_mods(), - move_caret_home, - )?; - assert_move( - &["abc", " d┃e", "ghi"], - &["abc", " ┃de", "ghi"], - &no_mods(), - move_caret_home, - )?; - assert_move( - &["abc", "┃ de", "ghi"], - &["abc", " ┃de", "ghi"], - &no_mods(), - move_caret_home, - )?; - assert_move( - &["abc", " ┃de", "ghi"], - &["abc", "┃ de", "ghi"], - &no_mods(), - move_caret_home, - )?; - assert_move( - &["abc┃", "de", "ghi"], - &["┃abc", "de", "ghi"], - &no_mods(), - move_caret_home, - )?; - assert_move( - &[" ┃abc", "de", "ghi"], - &["┃ abc", "de", "ghi"], - &no_mods(), - move_caret_home, - )?; - assert_move( - &["┃ abc", "de", "ghi"], - &[" ┃abc", "de", "ghi"], - &no_mods(), - move_caret_home, - )?; - assert_move( - &["abc", "de", "ghi┃"], - &["abc", "de", "┃ghi"], - &no_mods(), - move_caret_home, - )?; - assert_move( - &["abc", "de", " ┃ghi"], - &["abc", "de", "┃ ghi"], - &no_mods(), - move_caret_home, - )?; - assert_move( - &["abc", "de", "┃ ghi"], - &["abc", "de", " ┃ghi"], - &no_mods(), - move_caret_home, - )?; - assert_move( - &["abc ", "de ", "┃ghi "], - &["abc ", "de ", "┃ghi "], - &no_mods(), - move_caret_home, - )?; - assert_move( - &["abc ", "┃de ", "ghi "], - &["abc ", "┃de ", "ghi "], - &no_mods(), - move_caret_home, - )?; - assert_move( - &["┃abc ", "de ", "ghi "], - &["┃abc ", "de ", "ghi "], - &no_mods(), - move_caret_home, - )?; - - Ok(()) - } - - #[test] - fn move_end() -> Result<(), String> { - let move_caret_end = BigTextArea::move_caret_end; - assert_move(&["┃"], &["┃"], &no_mods(), move_caret_end)?; - assert_move(&["┃a"], &["a┃"], &no_mods(), move_caret_end)?; - assert_move(&["a┃"], &["a┃"], &no_mods(), move_caret_end)?; - assert_move(&[" a┃ "], &[" a ┃"], &no_mods(), move_caret_end)?; - assert_move(&["┃ abc "], &[" abc ┃"], &no_mods(), move_caret_end)?; - assert_move(&["┃\tabc "], &["\tabc ┃"], &no_mods(), move_caret_end)?; - assert_move( - &[" abc d┃ef\tghi"], - &[" abc def\tghi┃"], - &no_mods(), - move_caret_end, - )?; - - assert_move( - &["abc", "┃de", "ghi"], - &["abc", "de┃", "ghi"], - &no_mods(), - move_caret_end, - )?; - assert_move( - &["abc", " d┃e", "ghi"], - &["abc", " de┃", "ghi"], - &no_mods(), - move_caret_end, - )?; - assert_move( - &["┃abc", "de", "ghi"], - &["abc┃", "de", "ghi"], - &no_mods(), - move_caret_end, - )?; - assert_move( - &["abc", "de", "g┃hi"], - &["abc", "de", "ghi┃"], - &no_mods(), - move_caret_end, - )?; - assert_move( - &["abc ", "de ", "ghi┃ "], - &["abc ", "de ", "ghi ┃"], - &no_mods(), - move_caret_end, - )?; - assert_move( - &["abc ", "┃de ", "ghi "], - &["abc ", "de ┃", "ghi "], - &no_mods(), - move_caret_end, - )?; - assert_move( - &["abc ┃", "de ", "ghi "], - &["abc ┃", "de ", "ghi "], - &no_mods(), - move_caret_end, - )?; - assert_move( - &["abc ", "de ┃", "ghi "], - &["abc ", "de ┃", "ghi "], - &no_mods(), - move_caret_end, - )?; - assert_move( - &["abc ", "de ", "ghi ┃"], - &["abc ", "de ", "ghi ┃"], - &no_mods(), - move_caret_end, - )?; - - Ok(()) - } - - #[test] - fn start_selection_right() -> Result<(), String> { - let move_caret_right = SelectableLines::move_caret_right; - - assert_move(&["┃"], &["┃"], &shift_pressed(), move_caret_right)?; - assert_move(&["a┃"], &["a┃"], &shift_pressed(), move_caret_right)?; - assert_move(&["┃A"], &["❮A❯┃"], &shift_pressed(), move_caret_right)?; - assert_move(&["┃abc"], &["❮a❯┃bc"], &shift_pressed(), move_caret_right)?; - assert_move(&["a┃bc"], &["a❮b❯┃c"], &shift_pressed(), move_caret_right)?; - assert_move(&["abc┃"], &["abc┃"], &shift_pressed(), move_caret_right)?; - assert_move(&["┃ abc"], &["❮ ❯┃abc"], &shift_pressed(), move_caret_right)?; - assert_move(&["abc┃ "], &["abc❮ ❯┃"], &shift_pressed(), move_caret_right)?; - assert_move( - &["abc┃", "d"], - &["abc❮", "❯┃d"], - &shift_pressed(), - move_caret_right, - )?; - assert_move( - &["abc┃", ""], - &["abc❮", "❯┃"], - &shift_pressed(), - move_caret_right, - )?; - assert_move( - &["abc", "┃def"], - &["abc", "❮d❯┃ef"], - &shift_pressed(), - move_caret_right, - )?; - assert_move( - &["abc", "def┃ "], - &["abc", "def❮ ❯┃"], - &shift_pressed(), - move_caret_right, - )?; - assert_move( - &["abc", "def ┃", "ghi"], - &["abc", "def ❮", "❯┃ghi"], - &shift_pressed(), - move_caret_right, - )?; - assert_move( - &["abc", "def┃", ""], - &["abc", "def❮", "❯┃"], - &shift_pressed(), - move_caret_right, - )?; - assert_move( - &["abc", "def", "ghi┃", "jkl"], - &["abc", "def", "ghi❮", "❯┃jkl"], - &shift_pressed(), - move_caret_right, - )?; - assert_move( - &["abc", "def", "┃ghi", "jkl"], - &["abc", "def", "❮g❯┃hi", "jkl"], - &shift_pressed(), - move_caret_right, - )?; - assert_move( - &["abc", "def", "g┃hi", "jkl"], - &["abc", "def", "g❮h❯┃i", "jkl"], - &shift_pressed(), - move_caret_right, - )?; - - Ok(()) - } - - #[test] - fn start_selection_left() -> Result<(), String> { - let move_caret_left = SelectableLines::move_caret_left; - - assert_move(&["┃"], &["┃"], &shift_pressed(), move_caret_left)?; - assert_move(&["a┃"], &["┃❮a❯"], &shift_pressed(), move_caret_left)?; - assert_move(&["┃A"], &["┃A"], &shift_pressed(), move_caret_left)?; - assert_move(&["┃abc"], &["┃abc"], &shift_pressed(), move_caret_left)?; - assert_move(&["a┃bc"], &["┃❮a❯bc"], &shift_pressed(), move_caret_left)?; - assert_move(&["abc┃"], &["ab┃❮c❯"], &shift_pressed(), move_caret_left)?; - assert_move(&[" ┃abc"], &["┃❮ ❯abc"], &shift_pressed(), move_caret_left)?; - assert_move(&["abc ┃"], &["abc┃❮ ❯"], &shift_pressed(), move_caret_left)?; - assert_move( - &["abc┃", "d"], - &["ab┃❮c❯", "d"], - &shift_pressed(), - move_caret_left, - )?; - assert_move( - &["abc", "┃d"], - &["abc┃❮", "❯d"], - &shift_pressed(), - move_caret_left, - )?; - assert_move( - &["abc", "┃"], - &["abc┃❮", "❯"], - &shift_pressed(), - move_caret_left, - )?; - assert_move( - &["abc", " ┃def"], - &["abc", "┃❮ ❯def"], - &shift_pressed(), - move_caret_left, - )?; - assert_move( - &["abc", "d┃ef"], - &["abc", "┃❮d❯ef"], - &shift_pressed(), - move_caret_left, - )?; - assert_move( - &["abc", "de┃f "], - &["abc", "d┃❮e❯f "], - &shift_pressed(), - move_caret_left, - )?; - assert_move( - &["abc", "def", "┃"], - &["abc", "def┃❮", "❯"], - &shift_pressed(), - move_caret_left, - )?; - assert_move( - &["abc", "def", "┃ghi", "jkl"], - &["abc", "def┃❮", "❯ghi", "jkl"], - &shift_pressed(), - move_caret_left, - )?; - assert_move( - &["abc", "def", "g┃hi", "jkl"], - &["abc", "def", "┃❮g❯hi", "jkl"], - &shift_pressed(), - move_caret_left, - )?; - assert_move( - &["abc", "def", "gh┃i", "jkl"], - &["abc", "def", "g┃❮h❯i", "jkl"], - &shift_pressed(), - move_caret_left, - )?; - assert_move( - &["abc", "def", "ghi┃", "jkl"], - &["abc", "def", "gh┃❮i❯", "jkl"], - &shift_pressed(), - move_caret_left, - )?; - - Ok(()) - } - - #[test] - fn start_selection_down() -> Result<(), String> { - let move_caret_down = SelectableLines::move_caret_down; - - assert_move(&["┃"], &["┃"], &shift_pressed(), move_caret_down)?; - assert_move(&["┃a"], &["❮a❯┃"], &shift_pressed(), move_caret_down)?; - assert_move(&["A┃"], &["A┃"], &shift_pressed(), move_caret_down)?; - assert_move(&["a┃bc"], &["a❮bc❯┃"], &shift_pressed(), move_caret_down)?; - assert_move(&["ab┃c"], &["ab❮c❯┃"], &shift_pressed(), move_caret_down)?; - assert_move(&["abc┃"], &["abc┃"], &shift_pressed(), move_caret_down)?; - assert_move(&["abc┃ "], &["abc❮ ❯┃"], &shift_pressed(), move_caret_down)?; - assert_move( - &["abc", "┃def"], - &["abc", "❮def❯┃"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["abc", "d┃ef"], - &["abc", "d❮ef❯┃"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["abc", "de┃f"], - &["abc", "de❮f❯┃"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["abc", "def┃"], - &["abc", "def┃"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["┃abc", "def"], - &["❮abc", "❯┃def"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["a┃bc", "def"], - &["a❮bc", "d❯┃ef"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["ab┃c", "def"], - &["ab❮c", "de❯┃f"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["abc┃", "def"], - &["abc❮", "def❯┃"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["abc", "┃def ", "ghi"], - &["abc", "❮def ", "❯┃ghi"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["abc", "d┃ef ", "ghi"], - &["abc", "d❮ef ", "g❯┃hi"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["abc", "de┃f ", "ghi"], - &["abc", "de❮f ", "gh❯┃i"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["abc", "def┃ ", "ghi"], - &["abc", "def❮ ", "ghi❯┃"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["abc", "def ┃", "ghi"], - &["abc", "def ❮", "ghi❯┃"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["abc", "de┃", "ghi"], - &["abc", "de❮", "gh❯┃i"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["abc┃", "de"], - &["abc❮", "de❯┃"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["ab┃c", "de"], - &["ab❮c", "de❯┃"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["a┃bc", "de"], - &["a❮bc", "d❯┃e"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["┃abc", "de"], - &["❮abc", "❯┃de"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["ab┃", "cdef", "ghijkl", "mnopqrst"], - &["ab❮", "cd❯┃ef", "ghijkl", "mnopqrst"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["ab", "cdef┃", "ghijkl", "mnopqrst"], - &["ab", "cdef❮", "ghij❯┃kl", "mnopqrst"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["ab", "cdef", "ghijkl┃", "mnopqrst"], - &["ab", "cdef", "ghijkl❮", "mnopqr❯┃st"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &[" ┃ab", " cdef", "ghijkl", "mnopqrst"], - &[" ❮ab", " ❯┃cdef", "ghijkl", "mnopqrst"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["ab", "┃cdef", "ghijkl", "mnopqrst"], - &["ab", "❮cdef", "❯┃ghijkl", "mnopqrst"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["ab", "cdef", "┃ghijkl", "mnopqrst"], - &["ab", "cdef", "❮ghijkl", "❯┃mnopqrst"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["abcdefgh┃", "ijklmn", "opqr", "st"], - &["abcdefgh❮", "ijklmn❯┃", "opqr", "st"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["abcdefgh", "ijklmn┃", "opqr", "st"], - &["abcdefgh", "ijklmn❮", "opqr❯┃", "st"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["abcdefgh", "ijklmn", "opqr┃", "st"], - &["abcdefgh", "ijklmn", "opqr❮", "st❯┃"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["abcdefgh", "ijklmn", "opqr", "┃st"], - &["abcdefgh", "ijklmn", "opqr", "❮st❯┃"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["abc def gh ┃"], - &["abc def gh ┃"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["abc de┃f gh "], - &["abc de❮f gh ❯┃"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["ab┃c def gh "], - &["ab❮c def gh ❯┃"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["a┃bc def gh "], - &["a❮bc def gh ❯┃"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["┃abc def gh "], - &["❮abc def gh ❯┃"], - &shift_pressed(), - move_caret_down, - )?; - - Ok(()) - } - - #[test] - fn start_selection_up() -> Result<(), String> { - let move_caret_up = SelectableLines::move_caret_up; - - assert_move(&["┃"], &["┃"], &shift_pressed(), move_caret_up)?; - assert_move(&["┃a"], &["┃a"], &shift_pressed(), move_caret_up)?; - assert_move(&["A┃"], &["┃❮A❯"], &shift_pressed(), move_caret_up)?; - assert_move(&["a┃bc"], &["┃❮a❯bc"], &shift_pressed(), move_caret_up)?; - assert_move(&["ab┃c"], &["┃❮ab❯c"], &shift_pressed(), move_caret_up)?; - assert_move(&["abc┃"], &["┃❮abc❯"], &shift_pressed(), move_caret_up)?; - assert_move( - &["┃abc", "def"], - &["┃abc", "def"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["abc", "┃def"], - &["┃❮abc", "❯def"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["abc", "d┃ef"], - &["a┃❮bc", "d❯ef"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["abc", "de┃f"], - &["ab┃❮c", "de❯f"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["abc", "def┃"], - &["abc┃❮", "def❯"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["abc", "def ", "┃ghi"], - &["abc", "┃❮def ", "❯ghi"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["abc", "def ", "g┃hi"], - &["abc", "d┃❮ef ", "g❯hi"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["abc", "def ", "gh┃i"], - &["abc", "de┃❮f ", "gh❯i"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["abc", "def ", "ghi┃"], - &["abc", "def┃❮ ", "ghi❯"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["abc", "de", "ghi┃"], - &["abc", "de┃❮", "ghi❯"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["abc", "de┃"], - &["ab┃❮c", "de❯"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["abc", "d┃e"], - &["a┃❮bc", "d❯e"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["abc", "┃de"], - &["┃❮abc", "❯de"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["ab", "cdef", "ghijkl", "mnopqrst┃"], - &["ab", "cdef", "ghijkl┃❮", "mnopqrst❯"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["ab", "cdef", "ghijkl┃", "mnopqrst"], - &["ab", "cdef┃❮", "ghijkl❯", "mnopqrst"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["ab", "cdef", "ghijkl", "┃mnopqrst"], - &["ab", "cdef", "┃❮ghijkl", "❯mnopqrst"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &[" ab", " ┃cdef", "ghijkl", "mnopqrst"], - &[" ┃❮ab", " ❯cdef", "ghijkl", "mnopqrst"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["ab", "cdef", "ghijkl", "mnopqr┃st"], - &["ab", "cdef", "ghijkl┃❮", "mnopqr❯st"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["ab", "cde┃f", "ghijkl", "mnopqrst"], - &["ab┃❮", "cde❯f", "ghijkl", "mnopqrst"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["abcdefgh", "ijklmn", "opqr", "st┃"], - &["abcdefgh", "ijklmn", "op┃❮qr", "st❯"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["abcdefgh", "ijklmn", "opqr┃", "st"], - &["abcdefgh", "ijkl┃❮mn", "opqr❯", "st"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["abcdefgh", "ijklmn┃", "opqr", "st"], - &["abcdef┃❮gh", "ijklmn❯", "opqr", "st"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["abcdefgh┃", "ijklmn", "opqr", "st"], - &["┃❮abcdefgh❯", "ijklmn", "opqr", "st"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["abcdefg┃h", "ijklmn", "opqr", "st"], - &["┃❮abcdefg❯h", "ijklmn", "opqr", "st"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["a┃bcdefgh", "ijklmn", "opqr", "st"], - &["┃❮a❯bcdefgh", "ijklmn", "opqr", "st"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["┃abcdefgh", "ijklmn", "opqr", "st"], - &["┃abcdefgh", "ijklmn", "opqr", "st"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["abc def gh ┃"], - &["┃❮abc def gh ❯"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["abc de┃f gh "], - &["┃❮abc de❯f gh "], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["ab┃c def gh "], - &["┃❮ab❯c def gh "], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["a┃bc def gh "], - &["┃❮a❯bc def gh "], - &shift_pressed(), - move_caret_up, - )?; - - Ok(()) - } - - #[test] - fn start_selection_home() -> Result<(), String> { - let move_caret_home = BigTextArea::move_caret_home; - assert_move(&["┃"], &["┃"], &shift_pressed(), move_caret_home)?; - assert_move(&["┃a"], &["┃a"], &shift_pressed(), move_caret_home)?; - assert_move(&["a┃"], &["┃❮a❯"], &shift_pressed(), move_caret_home)?; - assert_move(&[" a┃"], &[" ┃❮a❯"], &shift_pressed(), move_caret_home)?; - assert_move(&[" ┃a"], &["┃❮ ❯a"], &shift_pressed(), move_caret_home)?; - assert_move(&["\ta┃"], &["\t┃❮a❯"], &shift_pressed(), move_caret_home)?; - assert_move(&["abc┃"], &["┃❮abc❯"], &shift_pressed(), move_caret_home)?; - assert_move(&[" abc┃"], &[" ┃❮abc❯"], &shift_pressed(), move_caret_home)?; - assert_move( - &["\tabc┃"], - &["\t┃❮abc❯"], - &shift_pressed(), - move_caret_home, - )?; - assert_move(&["ab┃c"], &["┃❮ab❯c"], &shift_pressed(), move_caret_home)?; - assert_move(&[" ab┃c"], &[" ┃❮ab❯c"], &shift_pressed(), move_caret_home)?; - assert_move( - &["\tab┃c"], - &["\t┃❮ab❯c"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &[" abc def ghi┃"], - &[" ┃❮abc def ghi❯"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &["abc def ghi┃"], - &["┃❮abc def ghi❯"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &["abc def ┃ghi"], - &["┃❮abc def ❯ghi"], - &shift_pressed(), - move_caret_home, - )?; - - assert_move( - &["abc", "def", "ghi┃"], - &["abc", "def", "┃❮ghi❯"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &["abc", "def┃", "ghi"], - &["abc", "┃❮def❯", "ghi"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &["abc┃", "def", "ghi"], - &["┃❮abc❯", "def", "ghi"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &["┃abc", "def", "ghi"], - &["┃abc", "def", "ghi"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &["abc", "┃def", "ghi"], - &["abc", "┃def", "ghi"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &["abc", "def", "┃ghi"], - &["abc", "def", "┃ghi"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &[" ┃abc", "def", "ghi"], - &["┃❮ ❯abc", "def", "ghi"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &["abc", " ┃def", "ghi"], - &["abc", "┃❮ ❯def", "ghi"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &["abc", "def", " ┃ghi"], - &["abc", "def", "┃❮ ❯ghi"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &["┃ abc", "def", "ghi"], - &["❮ ❯┃abc", "def", "ghi"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &["abc", "┃ def", "ghi"], - &["abc", "❮ ❯┃def", "ghi"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &["abc", "def", "┃ ghi"], - &["abc", "def", "❮ ❯┃ghi"], - &shift_pressed(), - move_caret_home, - )?; - - Ok(()) - } - - #[test] - fn start_selection_end() -> Result<(), String> { - let move_caret_end = BigTextArea::move_caret_end; - assert_move(&["┃"], &["┃"], &shift_pressed(), move_caret_end)?; - assert_move(&["┃a"], &["❮a❯┃"], &shift_pressed(), move_caret_end)?; - assert_move(&["a┃"], &["a┃"], &shift_pressed(), move_caret_end)?; - assert_move(&[" a ┃"], &[" a ┃"], &shift_pressed(), move_caret_end)?; - assert_move(&["┃ a "], &["❮ a ❯┃"], &shift_pressed(), move_caret_end)?; - assert_move(&["┃abc"], &["❮abc❯┃"], &shift_pressed(), move_caret_end)?; - assert_move(&["┃abc\t"], &["❮abc\t❯┃"], &shift_pressed(), move_caret_end)?; - assert_move(&["┃abc "], &["❮abc ❯┃"], &shift_pressed(), move_caret_end)?; - assert_move(&["┃ abc "], &["❮ abc ❯┃"], &shift_pressed(), move_caret_end)?; - assert_move(&[" abc┃ "], &[" abc❮ ❯┃"], &shift_pressed(), move_caret_end)?; - assert_move(&[" ab┃c"], &[" ab❮c❯┃"], &shift_pressed(), move_caret_end)?; - assert_move( - &["abc", "def", "┃ghi"], - &["abc", "def", "❮ghi❯┃"], - &shift_pressed(), - move_caret_end, - )?; - assert_move( - &["abc", "┃def", "ghi"], - &["abc", "❮def❯┃", "ghi"], - &shift_pressed(), - move_caret_end, - )?; - assert_move( - &["┃abc", "def", "ghi"], - &["❮abc❯┃", "def", "ghi"], - &shift_pressed(), - move_caret_end, - )?; - assert_move( - &["abc", "def", "┃ghi "], - &["abc", "def", "❮ghi ❯┃"], - &shift_pressed(), - move_caret_end, - )?; - assert_move( - &["abc", "┃def ", "ghi"], - &["abc", "❮def ❯┃", "ghi"], - &shift_pressed(), - move_caret_end, - )?; - assert_move( - &["┃abc ", "def", "ghi"], - &["❮abc ❯┃", "def", "ghi"], - &shift_pressed(), - move_caret_end, - )?; - - Ok(()) - } - - #[test] - fn end_selection_right() -> Result<(), String> { - let move_caret_right = SelectableLines::move_caret_right; - - assert_move(&["❮A❯┃"], &["A┃"], &no_mods(), move_caret_right)?; - assert_move(&["❮a❯┃bc"], &["a┃bc"], &no_mods(), move_caret_right)?; - assert_move(&["a❮b❯┃c"], &["ab┃c"], &no_mods(), move_caret_right)?; - assert_move(&["ab❮c❯┃"], &["abc┃"], &no_mods(), move_caret_right)?; - assert_move(&["❮ ❯┃abc"], &[" ┃abc"], &no_mods(), move_caret_right)?; - assert_move(&["┃❮ ❯abc"], &[" ┃abc"], &no_mods(), move_caret_right)?; - assert_move(&["a┃❮b❯c"], &["ab┃c"], &no_mods(), move_caret_right)?; - assert_move( - &["abc❮", "❯┃d"], - &["abc", "┃d"], - &no_mods(), - move_caret_right, - )?; - assert_move( - &["abc┃❮", "❯d"], - &["abc", "┃d"], - &no_mods(), - move_caret_right, - )?; - assert_move(&["abc┃❮", "❯"], &["abc", "┃"], &no_mods(), move_caret_right)?; - assert_move( - &["abc", "❮d❯┃ef"], - &["abc", "d┃ef"], - &no_mods(), - move_caret_right, - )?; - assert_move( - &["abc", "def", "ghi❮", "❯┃jkl"], - &["abc", "def", "ghi", "┃jkl"], - &no_mods(), - move_caret_right, - )?; - assert_move(&["❮ab❯┃c"], &["ab┃c"], &no_mods(), move_caret_right)?; - assert_move(&["❮abc❯┃"], &["abc┃"], &no_mods(), move_caret_right)?; - assert_move( - &["ab┃❮c", "❯def", "ghi"], - &["abc", "┃def", "ghi"], - &no_mods(), - move_caret_right, - )?; - assert_move( - &["ab❮c", "❯┃def", "ghi"], - &["abc", "┃def", "ghi"], - &no_mods(), - move_caret_right, - )?; - assert_move( - &["a┃❮bc", "❯def", "ghi"], - &["abc", "┃def", "ghi"], - &no_mods(), - move_caret_right, - )?; - assert_move( - &["┃❮abc", "❯def", "ghi"], - &["abc", "┃def", "ghi"], - &no_mods(), - move_caret_right, - )?; - assert_move( - &["a┃❮bc", "d❯ef", "ghi"], - &["abc", "d┃ef", "ghi"], - &no_mods(), - move_caret_right, - )?; - assert_move( - &["┃❮abc", "def❯", "ghi"], - &["abc", "def┃", "ghi"], - &no_mods(), - move_caret_right, - )?; - assert_move( - &["❮ab", "cdef", "ghijkl", "mnopqrst❯┃"], - &["ab", "cdef", "ghijkl", "mnopqrst┃"], - &no_mods(), - move_caret_right, - )?; - assert_move( - &["┃❮ab", "cdef", "ghijkl", "mnopqrst❯"], - &["ab", "cdef", "ghijkl", "mnopqrst┃"], - &no_mods(), - move_caret_right, - )?; - assert_move( - &["ab", "c❮def", "ghijkl", "mno❯┃pqrst"], - &["ab", "cdef", "ghijkl", "mno┃pqrst"], - &no_mods(), - move_caret_right, - )?; - assert_move( - &["ab", "c┃❮def", "ghijkl", "mno❯pqrst"], - &["ab", "cdef", "ghijkl", "mno┃pqrst"], - &no_mods(), - move_caret_right, - )?; - - Ok(()) - } - - #[test] - fn end_selection_left() -> Result<(), String> { - let move_caret_left = SelectableLines::move_caret_left; - - assert_move(&["❮A❯┃"], &["┃A"], &no_mods(), move_caret_left)?; - /*assert_move(&["❮a❯┃bc"], &["┃abc"], &no_mods(), move_caret_left)?; - assert_move(&["a❮b❯┃c"], &["a┃bc"], &no_mods(), move_caret_left)?; - assert_move(&["ab❮c❯┃"], &["ab┃c"], &no_mods(), move_caret_left)?; - assert_move(&["❮ ❯┃abc"], &["┃ abc"], &no_mods(), move_caret_left)?; - assert_move(&["┃❮ ❯abc"], &["┃ abc"], &no_mods(), move_caret_left)?; - assert_move(&["a┃❮b❯c"], &["a┃bc"], &no_mods(), move_caret_left)?; - assert_move( - &["abc❮", "❯┃d"], - &["abc┃", "d"], - &no_mods(), - move_caret_left, - )?; - assert_move( - &["abc┃❮", "❯d"], - &["abc┃", "d"], - &no_mods(), - move_caret_left, - )?; - assert_move( - &["abc┃❮", "❯"], - &["abc┃", ""], - &no_mods(), - move_caret_left, - )?; - assert_move( - &["abc", "❮d❯┃ef"], - &["abc", "┃def"], - &no_mods(), - move_caret_left, - )?; - assert_move( - &["abc", "def", "ghi❮", "❯┃jkl"], - &["abc", "def", "ghi┃", "jkl"], - &no_mods(), - move_caret_left, - )?; - assert_move(&["❮ab❯┃c"], &["┃abc"], &no_mods(), move_caret_left)?; - assert_move(&["❮abc❯┃"], &["┃abc"], &no_mods(), move_caret_left)?; - assert_move( - &["ab┃❮c", "❯def", "ghi"], - &["ab┃c", "def", "ghi"], - &no_mods(), - move_caret_left, - )?; - assert_move( - &["ab❮c", "❯┃def", "ghi"], - &["ab┃c", "def", "ghi"], - &no_mods(), - move_caret_left, - )?; - assert_move( - &["a┃❮bc", "❯def", "ghi"], - &["a┃bc", "def", "ghi"], - &no_mods(), - move_caret_left, - )?; - assert_move( - &["┃❮abc", "❯def", "ghi"], - &["┃abc", "def", "ghi"], - &no_mods(), - move_caret_left, - )?; - assert_move( - &["a┃❮bc", "d❯ef", "ghi"], - &["a┃bc", "def", "ghi"], - &no_mods(), - move_caret_left, - )?; - assert_move( - &["┃❮abc", "def❯", "ghi"], - &["┃abc", "def", "ghi"], - &no_mods(), - move_caret_left, - )?; - assert_move( - &["❮ab", "cdef", "ghijkl", "mnopqrst❯┃"], - &["┃ab", "cdef", "ghijkl", "mnopqrst"], - &no_mods(), - move_caret_left, - )?; - assert_move( - &["┃❮ab", "cdef", "ghijkl", "mnopqrst❯"], - &["┃ab", "cdef", "ghijkl", "mnopqrst"], - &no_mods(), - move_caret_left, - )?; - assert_move( - &["ab", "c❮def", "ghijkl", "mno❯┃pqrst"], - &["ab", "c┃def", "ghijkl", "mnopqrst"], - &no_mods(), - move_caret_left, - )?; - assert_move( - &["ab", "c┃❮def", "ghijkl", "mno❯pqrst"], - &["ab", "c┃def", "ghijkl", "mnopqrst"], - &no_mods(), - move_caret_left, - )?;*/ - - Ok(()) - } - - #[test] - fn end_selection_down() -> Result<(), String> { - let move_caret_down = SelectableLines::move_caret_down; - - assert_move(&["❮a❯┃"], &["a┃"], &no_mods(), move_caret_down)?; - assert_move(&["┃❮a❯"], &["a┃"], &no_mods(), move_caret_down)?; - assert_move(&["a┃❮bc❯"], &["abc┃"], &no_mods(), move_caret_down)?; - assert_move(&["ab❮c❯┃"], &["abc┃"], &no_mods(), move_caret_down)?; - assert_move(&["abc┃❮ ❯"], &["abc ┃"], &no_mods(), move_caret_down)?; - assert_move( - &["abc", "┃❮def❯"], - &["abc", "def┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abc", "d┃❮ef❯"], - &["abc", "def┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abc", "de┃❮f❯"], - &["abc", "def┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["❮abc", "❯┃def"], - &["abc", "┃def"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["a❮bc", "d❯┃ef"], - &["abc", "d┃ef"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["ab┃❮c", "de❯f"], - &["abc", "de┃f"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abc❮", "def❯┃"], - &["abc", "def┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abc", "┃❮def ", "❯ghi"], - &["abc", "def ", "┃ghi"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abc", "d❮ef ", "g❯┃hi"], - &["abc", "def ", "g┃hi"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abc", "de❮f ", "gh❯┃i"], - &["abc", "def ", "gh┃i"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abc", "def❮ ", "ghi❯┃"], - &["abc", "def ", "ghi┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abc", "def ❮", "ghi❯┃"], - &["abc", "def ", "ghi┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abc", "de❮", "gh❯┃i"], - &["abc", "de", "gh┃i"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abc┃❮", "de❯"], - &["abc", "de┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["ab❮c", "de❯┃"], - &["abc", "de┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["a┃❮bc", "d❯e"], - &["abc", "d┃e"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["❮abc", "❯┃de"], - &["abc", "┃de"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["ab❮", "cd❯┃ef", "ghijkl", "mnopqrst"], - &["ab", "cd┃ef", "ghijkl", "mnopqrst"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["ab", "cdef┃❮", "ghij❯kl", "mnopqrst"], - &["ab", "cdef", "ghij┃kl", "mnopqrst"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["ab", "cdef", "ghijkl❮", "mnopqr❯┃st"], - &["ab", "cdef", "ghijkl", "mnopqr┃st"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &[" ❮ab", " ❯┃cdef", "ghijkl", "mnopqrst"], - &[" ab", " ┃cdef", "ghijkl", "mnopqrst"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["ab", "┃❮cdef", "❯ghijkl", "mnopqrst"], - &["ab", "cdef", "┃ghijkl", "mnopqrst"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["ab", "cdef", "❮ghijkl", "❯┃mnopqrst"], - &["ab", "cdef", "ghijkl", "┃mnopqrst"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abcdefgh❮", "ijklmn❯┃", "opqr", "st"], - &["abcdefgh", "ijklmn┃", "opqr", "st"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abcdefgh", "ijklmn❮", "opqr❯┃", "st"], - &["abcdefgh", "ijklmn", "opqr┃", "st"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abcdefgh", "ijklmn", "opqr❮", "st❯┃"], - &["abcdefgh", "ijklmn", "opqr", "st┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abcdefgh", "ijklmn", "opqr", "❮st❯┃"], - &["abcdefgh", "ijklmn", "opqr", "st┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["abc de❮f gh ❯┃"], - &["abc def gh ┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["ab┃❮c def gh ❯"], - &["abc def gh ┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["a❮bc def gh ❯┃"], - &["abc def gh ┃"], - &no_mods(), - move_caret_down, - )?; - assert_move( - &["❮abc def gh ❯┃"], - &["abc def gh ┃"], - &no_mods(), - move_caret_down, - )?; - - Ok(()) - } - - #[test] - fn end_selection_up() -> Result<(), String> { - let move_caret_up = SelectableLines::move_caret_up; - - assert_move(&["❮a❯┃"], &["┃a"], &no_mods(), move_caret_up)?; - assert_move(&["┃❮a❯"], &["┃a"], &no_mods(), move_caret_up)?; - assert_move(&["a┃❮bc❯"], &["a┃bc"], &no_mods(), move_caret_up)?; - assert_move(&["ab❮c❯┃"], &["ab┃c"], &no_mods(), move_caret_up)?; - assert_move(&["abc┃❮ ❯"], &["abc┃ "], &no_mods(), move_caret_up)?; - assert_move( - &["abc", "┃❮def❯"], - &["abc", "┃def"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abc", "d┃❮ef❯"], - &["abc", "d┃ef"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abc", "de┃❮f❯"], - &["abc", "de┃f"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["❮abc", "❯┃def"], - &["┃abc", "def"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["a❮bc", "d❯┃ef"], - &["a┃bc", "def"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["ab┃❮c", "de❯f"], - &["ab┃c", "def"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abc❮", "def❯┃"], - &["abc┃", "def"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abc", "┃❮def ", "❯ghi"], - &["abc", "┃def ", "ghi"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abc", "d❮ef ", "g❯┃hi"], - &["abc", "d┃ef ", "ghi"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abc", "de┃❮f ", "gh❯i"], - &["abc", "de┃f ", "ghi"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abc", "def❮ ", "ghi❯┃"], - &["abc", "def┃ ", "ghi"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abc", "def ❮", "ghi❯┃"], - &["abc", "def ┃", "ghi"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abc", "de❮", "gh❯┃i"], - &["abc", "de┃", "ghi"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abc┃❮", "de❯"], - &["abc┃", "de"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["ab❮c", "de❯┃"], - &["ab┃c", "de"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["a┃❮bc", "d❯e"], - &["a┃bc", "de"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["❮abc", "❯┃de"], - &["┃abc", "de"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["ab❮", "cd❯┃ef", "ghijkl", "mnopqrst"], - &["ab┃", "cdef", "ghijkl", "mnopqrst"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["ab", "cdef┃❮", "ghij❯kl", "mnopqrst"], - &["ab", "cdef┃", "ghijkl", "mnopqrst"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["ab", "cdef", "ghijkl❮", "mnopqr❯┃st"], - &["ab", "cdef", "ghijkl┃", "mnopqrst"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &[" ❮ab", " ❯┃cdef", "ghijkl", "mnopqrst"], - &[" ┃ab", " cdef", "ghijkl", "mnopqrst"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["ab", "┃❮cdef", "❯ghijkl", "mnopqrst"], - &["ab", "┃cdef", "ghijkl", "mnopqrst"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["ab", "cdef", "❮ghijkl", "❯┃mnopqrst"], - &["ab", "cdef", "┃ghijkl", "mnopqrst"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abcdefgh❮", "ijklmn❯┃", "opqr", "st"], - &["abcdefgh┃", "ijklmn", "opqr", "st"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abcdefgh", "ijklmn❮", "opqr❯┃", "st"], - &["abcdefgh", "ijklmn┃", "opqr", "st"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abcdefgh", "ijklmn", "opqr❮", "st❯┃"], - &["abcdefgh", "ijklmn", "opqr┃", "st"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abcdefgh", "ijklmn", "opqr", "❮st❯┃"], - &["abcdefgh", "ijklmn", "opqr", "┃st"], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["abc de❮f gh ❯┃"], - &["abc de┃f gh "], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["ab┃❮c def gh ❯"], - &["ab┃c def gh "], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["a❮bc def gh ❯┃"], - &["a┃bc def gh "], - &no_mods(), - move_caret_up, - )?; - assert_move( - &["❮abc def gh ❯┃"], - &["┃abc def gh "], - &no_mods(), - move_caret_up, - )?; - - Ok(()) - } - - #[test] - fn end_selection_home() -> Result<(), String> { - let move_caret_home = BigTextArea::move_caret_home; - assert_move(&["❮a❯┃"], &["┃a"], &no_mods(), move_caret_home)?; - assert_move(&["┃❮a❯"], &["┃a"], &no_mods(), move_caret_home)?; - assert_move(&[" ┃❮a❯"], &["┃ a"], &no_mods(), move_caret_home)?; - assert_move(&["┃❮ a❯"], &[" ┃a"], &no_mods(), move_caret_home)?; - assert_move(&[" ❮a❯┃"], &[" ┃a"], &no_mods(), move_caret_home)?; - assert_move(&[" a❮bc ❯┃"], &[" ┃abc "], &no_mods(), move_caret_home)?; - assert_move(&["\t❮abc ❯┃"], &["\t┃abc "], &no_mods(), move_caret_home)?; - assert_move(&["\t┃❮abc❯ "], &["┃\tabc "], &no_mods(), move_caret_home)?; - assert_move(&["┃❮\tabc❯ "], &["\t┃abc "], &no_mods(), move_caret_home)?; - assert_move( - &["❮ abc def\tghi❯┃"], - &[" ┃abc def\tghi"], - &no_mods(), - move_caret_home, - )?; - assert_move( - &[" ┃❮abc❯ def\tghi"], - &["┃ abc def\tghi"], - &no_mods(), - move_caret_home, - )?; - assert_move( - &["┃❮ abc def\tghi❯"], - &[" ┃abc def\tghi"], - &no_mods(), - move_caret_home, - )?; - - assert_move( - &["abc", "d❮e❯┃", "ghi"], - &["abc", "┃de", "ghi"], - &no_mods(), - move_caret_home, - )?; - assert_move( - &["abc", " ❮d❯┃e", "ghi"], - &["abc", " ┃de", "ghi"], - &no_mods(), - move_caret_home, - )?; - assert_move( - &["❮abc", "❯┃ de", "ghi"], - &["abc", " ┃de", "ghi"], - &no_mods(), - move_caret_home, - )?; - assert_move( - &["abc", " ┃❮de❯", "ghi"], - &["abc", "┃ de", "ghi"], - &no_mods(), - move_caret_home, - )?; - assert_move( - &["abc┃❮", "de", "ghi❯"], - &["┃abc", "de", "ghi"], - &no_mods(), - move_caret_home, - )?; - - Ok(()) - } - - #[test] - fn end_selection_end() -> Result<(), String> { - let move_caret_end = BigTextArea::move_caret_end; - assert_move(&["┃❮a❯"], &["a┃"], &no_mods(), move_caret_end)?; - assert_move(&["❮a❯┃"], &["a┃"], &no_mods(), move_caret_end)?; - assert_move(&[" a┃❮ ❯"], &[" a ┃"], &no_mods(), move_caret_end)?; - assert_move(&["❮ a❯┃ "], &[" a ┃"], &no_mods(), move_caret_end)?; - assert_move(&[" ❮a❯┃ "], &[" a ┃"], &no_mods(), move_caret_end)?; - assert_move(&["❮ a ❯┃"], &[" a ┃"], &no_mods(), move_caret_end)?; - assert_move(&["┃❮ a❯bc "], &[" abc ┃"], &no_mods(), move_caret_end)?; - assert_move(&["┃❮\tabc❯ "], &["\tabc ┃"], &no_mods(), move_caret_end)?; - assert_move( - &[" abc d┃❮ef\tg❯hi"], - &[" abc def\tghi┃"], - &no_mods(), - move_caret_end, - )?; - - assert_move( - &["abc", "┃❮de", "ghi❯"], - &["abc", "de┃", "ghi"], - &no_mods(), - move_caret_end, - )?; - assert_move( - &["❮abc", " d❯┃e", "ghi"], - &["abc", " de┃", "ghi"], - &no_mods(), - move_caret_end, - )?; - assert_move( - &["┃❮abc", "de", "ghi❯"], - &["abc┃", "de", "ghi"], - &no_mods(), - move_caret_end, - )?; - assert_move( - &["abc", "de", "g┃❮hi❯"], - &["abc", "de", "ghi┃"], - &no_mods(), - move_caret_end, - )?; - - Ok(()) - } - - #[test] - fn extend_selection_right() -> Result<(), String> { - let move_caret_right = SelectableLines::move_caret_right; - - assert_move(&["❮a❯┃bc"], &["❮ab❯┃c"], &shift_pressed(), move_caret_right)?; - assert_move(&["a❮b❯┃c"], &["a❮bc❯┃"], &shift_pressed(), move_caret_right)?; - assert_move(&["❮ab❯┃c"], &["❮abc❯┃"], &shift_pressed(), move_caret_right)?; - assert_move( - &["❮ ❯┃abc"], - &["❮ a❯┃bc"], - &shift_pressed(), - move_caret_right, - )?; - assert_move(&["❮abc❯┃"], &["❮abc❯┃"], &shift_pressed(), move_caret_right)?; - assert_move(&["a❮bc❯┃"], &["a❮bc❯┃"], &shift_pressed(), move_caret_right)?; - assert_move(&["ab❮c❯┃"], &["ab❮c❯┃"], &shift_pressed(), move_caret_right)?; - assert_move( - &["abc❮", "❯┃d"], - &["abc❮", "d❯┃"], - &shift_pressed(), - move_caret_right, - )?; - assert_move( - &["ab❮c❯┃", ""], - &["ab❮c", "❯┃"], - &shift_pressed(), - move_caret_right, - )?; - assert_move( - &["ab❮c❯┃", "d"], - &["ab❮c", "❯┃d"], - &shift_pressed(), - move_caret_right, - )?; - assert_move( - &["abc", "def", "ghi❮", "❯┃jkl"], - &["abc", "def", "ghi❮", "j❯┃kl"], - &shift_pressed(), - move_caret_right, - )?; - assert_move( - &["ab❮c", "def", "ghi", "❯┃jkl"], - &["ab❮c", "def", "ghi", "j❯┃kl"], - &shift_pressed(), - move_caret_right, - )?; - assert_move( - &["ab❮c", "def", "❯┃ghi", "jkl"], - &["ab❮c", "def", "g❯┃hi", "jkl"], - &shift_pressed(), - move_caret_right, - )?; - assert_move( - &["❮abc", "def", "ghi", "jk❯┃l"], - &["❮abc", "def", "ghi", "jkl❯┃"], - &shift_pressed(), - move_caret_right, - )?; - assert_move( - &["❮abc", "def", "ghi", "jkl❯┃"], - &["❮abc", "def", "ghi", "jkl❯┃"], - &shift_pressed(), - move_caret_right, - )?; - - Ok(()) - } - - #[test] - fn extend_selection_left() -> Result<(), String> { - let move_caret_left = SelectableLines::move_caret_left; - - assert_move(&["ab┃❮c❯"], &["a┃❮bc❯"], &shift_pressed(), move_caret_left)?; - assert_move(&["a┃❮bc❯"], &["┃❮abc❯"], &shift_pressed(), move_caret_left)?; - assert_move(&["┃❮abc❯"], &["┃❮abc❯"], &shift_pressed(), move_caret_left)?; - assert_move(&["┃❮ab❯c"], &["┃❮ab❯c"], &shift_pressed(), move_caret_left)?; - assert_move(&["┃❮a❯bc"], &["┃❮a❯bc"], &shift_pressed(), move_caret_left)?; - assert_move( - &[" ┃❮a❯bc"], - &["┃❮ a❯bc"], - &shift_pressed(), - move_caret_left, - )?; - assert_move( - &["abc┃❮", "❯d"], - &["ab┃❮c", "❯d"], - &shift_pressed(), - move_caret_left, - )?; - assert_move( - &["abc", "┃❮d❯"], - &["abc┃❮", "d❯"], - &shift_pressed(), - move_caret_left, - )?; - assert_move( - &["ab┃❮c", "❯"], - &["a┃❮bc", "❯"], - &shift_pressed(), - move_caret_left, - )?; - assert_move( - &["abc", "def┃❮", "ghi", "j❯kl"], - &["abc", "de┃❮f", "ghi", "j❯kl"], - &shift_pressed(), - move_caret_left, - )?; - assert_move( - &["a┃❮bc", "def", "ghi", "jkl❯"], - &["┃❮abc", "def", "ghi", "jkl❯"], - &shift_pressed(), - move_caret_left, - )?; - assert_move( - &["abc", "def", "ghi", "┃❮jkl❯"], - &["abc", "def", "ghi┃❮", "jkl❯"], - &shift_pressed(), - move_caret_left, - )?; - - Ok(()) - } - - #[test] - fn extend_selection_up() -> Result<(), String> { - let move_caret_up = SelectableLines::move_caret_up; - - assert_move(&["ab┃❮c❯"], &["┃❮abc❯"], &shift_pressed(), move_caret_up)?; - assert_move(&["a┃❮bc❯"], &["┃❮abc❯"], &shift_pressed(), move_caret_up)?; - assert_move(&["┃❮abc❯"], &["┃❮abc❯"], &shift_pressed(), move_caret_up)?; - assert_move(&["┃❮ab❯c"], &["┃❮ab❯c"], &shift_pressed(), move_caret_up)?; - assert_move(&["┃❮a❯bc"], &["┃❮a❯bc"], &shift_pressed(), move_caret_up)?; - assert_move(&[" ┃❮a❯bc"], &["┃❮ a❯bc"], &shift_pressed(), move_caret_up)?; - assert_move(&["ab❮c❯┃"], &["┃❮ab❯c"], &shift_pressed(), move_caret_up)?; - assert_move(&["❮a❯┃"], &["┃a"], &shift_pressed(), move_caret_up)?; - assert_move(&["❮a❯┃bc"], &["┃abc"], &shift_pressed(), move_caret_up)?; - assert_move( - &["❮a❯┃bc", "d"], - &["┃abc", "d"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["abc", "de❮f❯┃"], - &["abc┃❮", "de❯f"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["abc", "de┃❮f❯"], - &["ab┃❮c", "def❯"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["ab┃❮c", "def❯"], - &["┃❮abc", "def❯"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["ab", "cdef", "ghijkl", "❮mnopqr❯┃st"], - &["ab", "cdef", "ghijkl┃❮", "❯mnopqrst"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["ab", "cdef", "ghijkl", "❮mnopqrs❯┃t"], - &["ab", "cdef", "ghijkl┃❮", "❯mnopqrst"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["abcdefgh", "ijklmn", "┃❮o❯pqr", "st"], - &["abcdefgh", "┃❮ijklmn", "o❯pqr", "st"], - &shift_pressed(), - move_caret_up, - )?; - - Ok(()) - } - - #[test] - fn extend_selection_down() -> Result<(), String> { - let move_caret_down = SelectableLines::move_caret_down; - - assert_move(&["❮ab❯┃c"], &["❮abc❯┃"], &shift_pressed(), move_caret_down)?; - assert_move(&["❮a❯┃bc"], &["❮abc❯┃"], &shift_pressed(), move_caret_down)?; - assert_move(&["❮abc❯┃"], &["❮abc❯┃"], &shift_pressed(), move_caret_down)?; - assert_move(&["┃❮ab❯c"], &["ab❮c❯┃"], &shift_pressed(), move_caret_down)?; - assert_move(&["┃❮a❯bc"], &["a❮bc❯┃"], &shift_pressed(), move_caret_down)?; - assert_move( - &["❮a❯┃bc", "d"], - &["❮abc", "d❯┃"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["❮a❯┃bc", "de"], - &["❮abc", "d❯┃e"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["❮abc", "d❯┃e"], - &["❮abc", "de❯┃"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["❮a❯┃bc", ""], - &["❮abc", "❯┃"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["ab", "cdef", "ghijkl", "❮mnopqr❯┃st"], - &["ab", "cdef", "ghijkl", "❮mnopqrst❯┃"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["a❮b", "cdef", "ghijkl", "mnopqr❯┃st"], - &["a❮b", "cdef", "ghijkl", "mnopqrst❯┃"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["❮ab", "cdef", "ghijkl", "mnopqrst❯┃"], - &["❮ab", "cdef", "ghijkl", "mnopqrst❯┃"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["abcd❮efgh❯┃", "ijklmn", "opqr", "st"], - &["abcd❮efgh", "ijklmn❯┃", "opqr", "st"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["abcd❮e❯┃fgh", "ijklmn", "opqr", "st"], - &["abcd❮efgh", "ijklm❯┃n", "opqr", "st"], - &shift_pressed(), - move_caret_down, - )?; - - Ok(()) - } - - #[test] - fn extend_selection_home() -> Result<(), String> { - let move_caret_home = SelectableLines::move_caret_home; - - assert_move(&["ab┃❮c❯"], &["┃❮abc❯"], &shift_pressed(), move_caret_home)?; - assert_move(&["a┃❮bc❯"], &["┃❮abc❯"], &shift_pressed(), move_caret_home)?; - assert_move(&["┃❮abc❯"], &["┃❮abc❯"], &shift_pressed(), move_caret_home)?; - assert_move(&["┃❮ab❯c"], &["┃❮ab❯c"], &shift_pressed(), move_caret_home)?; - assert_move(&["┃❮a❯bc"], &["┃❮a❯bc"], &shift_pressed(), move_caret_home)?; - assert_move( - &[" ┃❮a❯bc"], - &["┃❮ a❯bc"], - &shift_pressed(), - move_caret_home, - )?; - assert_move(&["ab❮c❯┃"], &["┃❮ab❯c"], &shift_pressed(), move_caret_home)?; - assert_move( - &["abc", "de❮f❯┃"], - &["abc", "┃❮de❯f"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &["abc", "de┃❮f❯"], - &["abc", "┃❮def❯"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &["ab┃❮c", "def❯"], - &["┃❮abc", "def❯"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &[" ab┃❮c", "def❯"], - &[" ┃❮abc", "def❯"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &[" ┃❮abc", "def❯"], - &["┃❮ abc", "def❯"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &["ab", "cdef", "ghijkl", "┃❮mnopqr❯st"], - &["ab", "cdef", "ghijkl", "┃❮mnopqr❯st"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &["ab", "cdef", "gh┃❮ijkl❯", "mnopqrst"], - &["ab", "cdef", "┃❮ghijkl❯", "mnopqrst"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &["abcdefgh", "ijklmn", "op❮qr❯┃", "st"], - &["abcdefgh", "ijklmn", "┃❮op❯qr", "st"], - &shift_pressed(), - move_caret_home, - )?; - - Ok(()) - } - - #[test] - fn extend_selection_end() -> Result<(), String> { - let move_caret_end = SelectableLines::move_caret_end; - - assert_move(&["❮ab❯┃c"], &["❮abc❯┃"], &shift_pressed(), move_caret_end)?; - assert_move(&["❮a❯┃bc"], &["❮abc❯┃"], &shift_pressed(), move_caret_end)?; - assert_move(&["❮abc❯┃"], &["❮abc❯┃"], &shift_pressed(), move_caret_end)?; - assert_move(&["┃❮ab❯c"], &["ab❮c❯┃"], &shift_pressed(), move_caret_end)?; - assert_move(&["┃❮a❯bc"], &["a❮bc❯┃"], &shift_pressed(), move_caret_end)?; - assert_move( - &["❮a❯┃bc", "d"], - &["❮abc❯┃", "d"], - &shift_pressed(), - move_caret_end, - )?; - assert_move( - &["❮a❯┃bc ", "de"], - &["❮abc ❯┃", "de"], - &shift_pressed(), - move_caret_end, - )?; - assert_move( - &["❮abc", "d❯┃e"], - &["❮abc", "de❯┃"], - &shift_pressed(), - move_caret_end, - )?; - assert_move( - &["ab", "cdef", "ghijkl", "❮mnopqr❯┃st"], - &["ab", "cdef", "ghijkl", "❮mnopqrst❯┃"], - &shift_pressed(), - move_caret_end, - )?; - assert_move( - &["a❮b", "cdef", "ghijkl", "mnopqr❯┃st"], - &["a❮b", "cdef", "ghijkl", "mnopqrst❯┃"], - &shift_pressed(), - move_caret_end, - )?; - assert_move( - &["❮ab", "cdef", "ghijkl", "mnopqrst❯┃"], - &["❮ab", "cdef", "ghijkl", "mnopqrst❯┃"], - &shift_pressed(), - move_caret_end, - )?; - assert_move( - &["abcd❮e❯┃fgh", "ijklmn", "opqr", "st"], - &["abcd❮efgh❯┃", "ijklmn", "opqr", "st"], - &shift_pressed(), - move_caret_end, - )?; - - Ok(()) - } - - #[test] - fn shrink_selection_right() -> Result<(), String> { - let move_caret_right = SelectableLines::move_caret_right; - - assert_move(&["ab┃❮c❯"], &["abc┃"], &shift_pressed(), move_caret_right)?; - assert_move(&["a┃❮bc❯"], &["ab┃❮c❯"], &shift_pressed(), move_caret_right)?; - assert_move(&["┃❮abc❯"], &["a┃❮bc❯"], &shift_pressed(), move_caret_right)?; - assert_move( - &["┃❮abc", "def", "ghi", "jkl❯"], - &["a┃❮bc", "def", "ghi", "jkl❯"], - &shift_pressed(), - move_caret_right, - )?; - assert_move( - &["abc", "d┃❮ef", "❯ghi", "jkl"], - &["abc", "de┃❮f", "❯ghi", "jkl"], - &shift_pressed(), - move_caret_right, - )?; - assert_move( - &["abc", "de┃❮f❯", "ghi", "jkl"], - &["abc", "def┃", "ghi", "jkl"], - &shift_pressed(), - move_caret_right, - )?; - - Ok(()) - } - - #[test] - fn shrink_selection_left() -> Result<(), String> { - let move_caret_left = SelectableLines::move_caret_left; - - assert_move(&["ab❮c❯┃"], &["ab┃c"], &shift_pressed(), move_caret_left)?; - assert_move(&["a❮bc❯┃"], &["a❮b❯┃c"], &shift_pressed(), move_caret_left)?; - assert_move(&["❮abc❯┃"], &["❮ab❯┃c"], &shift_pressed(), move_caret_left)?; - assert_move( - &["❮abc", "def", "ghi", "jkl❯┃"], - &["❮abc", "def", "ghi", "jk❯┃l"], - &shift_pressed(), - move_caret_left, - )?; - assert_move( - &["┃❮abc", "def", "ghi", "jkl❯"], - &["┃❮abc", "def", "ghi", "jkl❯"], - &shift_pressed(), - move_caret_left, - )?; - assert_move( - &["abc", "def❮", "❯┃ghi", "jkl"], - &["abc", "def┃", "ghi", "jkl"], - &shift_pressed(), - move_caret_left, - )?; - assert_move( - &["abc", "d❮ef", "gh❯┃i", "jkl"], - &["abc", "d❮ef", "g❯┃hi", "jkl"], - &shift_pressed(), - move_caret_left, - )?; - - Ok(()) - } - - #[test] - fn shrink_selection_up() -> Result<(), String> { - let move_caret_up = SelectableLines::move_caret_up; - - assert_move(&["❮abc❯┃"], &["┃abc"], &shift_pressed(), move_caret_up)?; - assert_move(&["❮ab❯┃c"], &["┃abc"], &shift_pressed(), move_caret_up)?; - assert_move(&["❮a❯┃bc"], &["┃abc"], &shift_pressed(), move_caret_up)?; - assert_move(&["┃abc"], &["┃abc"], &shift_pressed(), move_caret_up)?; - assert_move( - &["❮abc", "def❯┃"], - &["❮abc❯┃", "def"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["❮abc", "de❯┃f"], - &["❮ab❯┃c", "def"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["❮abc", "def", "ghi", "jkl❯┃"], - &["❮abc", "def", "ghi❯┃", "jkl"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["abc", "def", "ghi❮", "jkl❯┃"], - &["abc", "def", "ghi┃", "jkl"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["abc", "d❮ef", "ghi", "jk❯┃l"], - &["abc", "d❮ef", "gh❯┃i", "jkl"], - &shift_pressed(), - move_caret_up, - )?; - assert_move( - &["❮abc", "d❯┃ef", "ghi", "jkl"], - &["❮a❯┃bc", "def", "ghi", "jkl"], - &shift_pressed(), - move_caret_up, - )?; - - Ok(()) - } - - #[test] - fn shrink_selection_down() -> Result<(), String> { - let move_caret_down = SelectableLines::move_caret_down; - - assert_move(&["┃❮abc❯"], &["abc┃"], &shift_pressed(), move_caret_down)?; - assert_move( - &["┃❮abc", "def❯"], - &["abc", "┃❮def❯"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["a┃❮bc", "def❯"], - &["abc", "d┃❮ef❯"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["┃❮abc", "def", "ghi❯"], - &["abc", "┃❮def", "ghi❯"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["ab┃❮c", "def", "ghi❯"], - &["abc", "de┃❮f", "ghi❯"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["abc", "de┃❮f", "ghi❯"], - &["abc", "def", "gh┃❮i❯"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["abcdef┃❮", "ghij", "kl❯"], - &["abcdef", "ghij┃❮", "kl❯"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["abcde┃❮f", "ghij", "kl❯"], - &["abcdef", "ghij┃❮", "kl❯"], - &shift_pressed(), - move_caret_down, - )?; - assert_move( - &["ab┃❮cdef", "ghij", "kl❯"], - &["abcdef", "gh┃❮ij", "kl❯"], - &shift_pressed(), - move_caret_down, - )?; - - Ok(()) - } - - #[test] - fn shrink_selection_home() -> Result<(), String> { - let move_caret_home = SelectableLines::move_caret_home; - - assert_move(&["❮abc❯┃"], &["┃abc"], &shift_pressed(), move_caret_home)?; - assert_move(&["❮ab❯┃c"], &["┃abc"], &shift_pressed(), move_caret_home)?; - assert_move(&["❮a❯┃bc"], &["┃abc"], &shift_pressed(), move_caret_home)?; - assert_move(&[" ❮abc❯┃"], &[" ┃abc"], &shift_pressed(), move_caret_home)?; - assert_move(&[" ❮ab❯┃c"], &[" ┃abc"], &shift_pressed(), move_caret_home)?; - assert_move(&[" ❮a❯┃bc"], &[" ┃abc"], &shift_pressed(), move_caret_home)?; - assert_move(&["ab❮c❯┃"], &["┃❮ab❯c"], &shift_pressed(), move_caret_home)?; - assert_move(&["a❮b❯┃c"], &["┃❮a❯bc"], &shift_pressed(), move_caret_home)?; - assert_move(&["a❮bc❯┃"], &["┃❮a❯bc"], &shift_pressed(), move_caret_home)?; - - assert_move( - &["❮abc", "def❯┃"], - &["❮abc", "❯┃def"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &["❮abc", " de❯┃f"], - &["❮abc", " ❯┃def"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &["❮abc", "def", "ghi", "jkl❯┃"], - &["❮abc", "def", "ghi", "❯┃jkl"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &["abc", "def", "ghi❮", "jkl❯┃"], - &["abc", "def", "ghi❮", "❯┃jkl"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &["abc", "d❮ef", " ghi", " jk❯┃l"], - &["abc", "d❮ef", " ghi", " ❯┃jkl"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &["❮abc", "d❯┃ef", "ghi", "jkl"], - &["❮abc", "❯┃def", "ghi", "jkl"], - &shift_pressed(), - move_caret_home, - )?; - assert_move( - &["❮abc", "d❯┃ef", "ghi", "jkl"], - &["❮abc", "❯┃def", "ghi", "jkl"], - &shift_pressed(), - move_caret_home, - )?; - - Ok(()) - } - - #[test] - fn shrink_selection_end() -> Result<(), String> { - let move_caret_end = SelectableLines::move_caret_end; - - assert_move(&["┃❮abc❯"], &["abc┃"], &shift_pressed(), move_caret_end)?; - assert_move( - &["┃❮abc", "def❯"], - &["abc┃❮", "def❯"], - &shift_pressed(), - move_caret_end, - )?; - assert_move( - &["a┃❮bc", "def❯"], - &["abc┃❮", "def❯"], - &shift_pressed(), - move_caret_end, - )?; - assert_move( - &["a┃❮bc", "def", "ghi❯"], - &["abc┃❮", "def", "ghi❯"], - &shift_pressed(), - move_caret_end, - )?; - assert_move( - &["ab┃❮c", "def", "ghi❯"], - &["abc┃❮", "def", "ghi❯"], - &shift_pressed(), - move_caret_end, - )?; - assert_move( - &["abc", "de┃❮f", "ghi❯"], - &["abc", "def┃❮", "ghi❯"], - &shift_pressed(), - move_caret_end, - )?; - assert_move( - &["abcdef┃❮", "ghij", "kl❯"], - &["abcdef┃❮", "ghij", "kl❯"], - &shift_pressed(), - move_caret_end, - )?; - assert_move( - &["┃❮ abcdef", "ghij", "kl❯"], - &[" abcdef┃❮", "ghij", "kl❯"], - &shift_pressed(), - move_caret_end, - )?; - assert_move( - &["abcdef", "ghij", "┃❮kl❯"], - &["abcdef", "ghij", "kl┃"], - &shift_pressed(), - move_caret_end, - )?; - - Ok(()) - } -} diff --git a/crates/editor/src/ui/text/caret_w_select.rs b/crates/editor/src/ui/text/caret_w_select.rs deleted file mode 100644 index c5a72900d8..0000000000 --- a/crates/editor/src/ui/text/caret_w_select.rs +++ /dev/null @@ -1,320 +0,0 @@ -#![allow(dead_code)] - -use super::selection::validate_selection; -use super::selection::Selection; -use super::text_pos::TextPos; -use crate::ui::ui_error::UIResult; -use crate::window::keyboard_input::Modifiers; - -#[derive(Debug, Copy, Clone)] -pub struct CaretWSelect { - pub caret_pos: TextPos, - pub selection_opt: Option, -} - -pub enum CaretPos { - Start, - Exact(TextPos), - End, -} - -fn mk_some_sel(start_pos: TextPos, end_pos: TextPos) -> UIResult> { - if start_pos == end_pos { - Ok(None) - } else { - Ok(Some(validate_selection(start_pos, end_pos)?)) - } -} - -impl Default for CaretWSelect { - fn default() -> Self { - Self { - caret_pos: TextPos { line: 0, column: 0 }, - selection_opt: None, - } - } -} - -impl CaretWSelect { - pub fn new(caret_pos: TextPos, selection_opt: Option) -> Self { - Self { - caret_pos, - selection_opt, - } - } - - pub fn move_caret_w_mods(&self, new_pos: TextPos, mods: &Modifiers) -> UIResult { - let old_caret_pos = self.caret_pos; - - // one does not simply move the caret - let valid_sel_opt = if mods.shift { - if new_pos != old_caret_pos { - if let Some(old_sel) = self.selection_opt { - if new_pos < old_sel.start_pos { - if old_caret_pos > old_sel.start_pos { - mk_some_sel(new_pos, old_sel.start_pos)? - } else { - mk_some_sel(new_pos, old_sel.end_pos)? - } - } else if new_pos > old_sel.end_pos { - if old_caret_pos < old_sel.end_pos { - mk_some_sel(old_sel.end_pos, new_pos)? - } else { - mk_some_sel(old_sel.start_pos, new_pos)? - } - } else if new_pos > old_caret_pos { - mk_some_sel(new_pos, old_sel.end_pos)? - } else if new_pos < old_caret_pos { - mk_some_sel(old_sel.start_pos, new_pos)? - } else { - None - } - } else if new_pos < self.caret_pos { - mk_some_sel(new_pos, old_caret_pos)? - } else { - mk_some_sel(old_caret_pos, new_pos)? - } - } else { - self.selection_opt - } - } else { - None - }; - - Ok(CaretWSelect::new(new_pos, valid_sel_opt)) - } -} - -// VIEW -// ---- -use crate::graphics::primitives::rect::Rect; -use crate::ui::theme::UITheme; - -pub fn make_caret_rect_from_pos( - caret_pos: TextPos, - glyph_dim_rect: &Rect, - ui_theme: &UITheme, -) -> Rect { - let caret_x = - glyph_dim_rect.top_left_coords.x + glyph_dim_rect.width * (caret_pos.column as f32); - - let caret_y = - glyph_dim_rect.top_left_coords.y + (caret_pos.line as f32) * glyph_dim_rect.height; - - make_caret_rect(caret_x, caret_y, glyph_dim_rect, ui_theme) -} - -pub fn make_caret_rect( - caret_x: f32, - caret_y: f32, - glyph_dim_rect: &Rect, - ui_theme: &UITheme, -) -> Rect { - Rect { - top_left_coords: (caret_x, caret_y).into(), - height: glyph_dim_rect.height, - width: glyph_dim_rect.width / 6.0, - color: ui_theme.caret, - } -} - -pub fn make_selection_rect( - sel_rect_x: f32, - sel_rect_y: f32, - width: f32, - glyph_dim_rect: &Rect, - ui_theme: &UITheme, -) -> Rect { - Rect { - top_left_coords: (sel_rect_x, sel_rect_y).into(), - height: glyph_dim_rect.height, - width, - color: ui_theme.select_highlight, - } -} - -#[cfg(test)] -pub mod test_caret_w_select { - use crate::ui::text::caret_w_select::CaretWSelect; - use crate::ui::text::selection::validate_selection; - use crate::ui::text::text_pos::TextPos; - use crate::ui::ui_error::OutOfBoundsSnafu; - use crate::ui::ui_error::UIResult; - use crate::ui::util::slice_get; - use core::cmp::Ordering; - use pest::Parser; - use snafu::OptionExt; - use std::{collections::HashMap, slice::SliceIndex}; - - #[derive(Parser)] - #[grammar = "../tests/selection.pest"] - pub struct LineParser; - - // Retrieve selection and position from formatted string - pub fn convert_dsl_to_selection(lines: &[String]) -> Result { - let lines_str: String = lines.join("\n"); - - let parsed = LineParser::parse(Rule::linesWithSelect, &lines_str) - .expect("Selection test DSL parsing failed"); - - let mut caret_opt: Option<(usize, usize)> = None; - let mut sel_start_opt: Option<(usize, usize)> = None; - let mut sel_end_opt: Option<(usize, usize)> = None; - let mut line_nr = 0; - let mut col_nr = 0; - - for line in parsed { - for elt in line.into_inner() { - match elt.as_rule() { - Rule::optCaret => { - if elt.as_span().as_str() == "┃" { - if caret_opt.is_some() { - return Err( - "Multiple carets found, there should be only one".to_owned() - ); - } else { - caret_opt = Some((line_nr, col_nr)); - } - } - } - Rule::optSelStart => { - if sel_start_opt.is_some() { - if elt.as_span().as_str() == "❮" { - return Err("Found start of selection more than once, there should be only one".to_owned()); - } - } else if elt.as_span().as_str() == "❮" { - sel_start_opt = Some((line_nr, col_nr)); - } - } - Rule::optSelEnd => { - if sel_end_opt.is_some() { - if elt.as_span().as_str() == "❯" { - return Err("Found end of selection more than once, there should be only one".to_owned()); - } - } else if elt.as_span().as_str() == "❯" { - sel_end_opt = Some((line_nr, col_nr)); - } - } - Rule::text => { - let split_str = elt.as_span().as_str().split('\n').collect::>(); - - if split_str.len() > 1 { - line_nr += split_str.len() - 1; - col_nr = 0 - } - if let Some(last_str) = split_str.last() { - col_nr += last_str.len() - } - } - _ => {} - } - } - } - - // Make sure return makes sense - if let Some((line, column)) = caret_opt { - let caret_pos = TextPos { line, column }; - if sel_start_opt.is_none() && sel_end_opt.is_none() { - Ok(CaretWSelect::new(caret_pos, None)) - } else if let Some((start_line, start_column)) = sel_start_opt { - if let Some((end_line, end_column)) = sel_end_opt { - Ok(CaretWSelect::new( - caret_pos, - Some( - validate_selection( - TextPos { - line: start_line, - column: start_column, - }, - TextPos { - line: end_line, - column: end_column, - }, - ) - .unwrap(), - ), - )) - } else { - Err("Selection end '❯' was not found, but selection start '❮' was. Bad input string.".to_owned()) - } - } else { - Err("Selection start '❮' was not found, but selection end '❯' was. Bad input string.".to_owned()) - } - } else { - Err("No caret was found in lines.".to_owned()) - } - } - - // show selection and caret position as symbols in lines for easy testing - pub fn convert_selection_to_dsl( - caret_w_select: CaretWSelect, - lines: Vec, - ) -> UIResult> { - let selection_opt = caret_w_select.selection_opt; - let caret_pos = caret_w_select.caret_pos; - let mut mut_lines = lines; - - if let Some(sel) = selection_opt { - let mut to_insert = vec![(sel.start_pos, '❮'), (sel.end_pos, '❯'), (caret_pos, '┃')]; - let symbol_map: HashMap = - [('❮', 2), ('❯', 0), ('┃', 1)].iter().cloned().collect(); - - // sort for nice printing - to_insert.sort_by(|a, b| { - let pos_cmp = a.0.cmp(&b.0); - if pos_cmp == Ordering::Equal { - symbol_map.get(&a.1).cmp(&symbol_map.get(&b.1)) - } else { - pos_cmp - } - }); - - // insert symbols into text lines - for i in 0..to_insert.len() { - let (pos, insert_char) = *slice_get(i, &to_insert)?; - - insert_at_pos(&mut mut_lines, pos, insert_char)?; - - // shift position of following symbols now that symbol is inserted - for j in i..to_insert.len() { - let (old_pos, _) = get_mut_res(j, &mut to_insert)?; - - if old_pos.line == pos.line { - old_pos.column += 1; - } - } - } - } else { - insert_at_pos(&mut mut_lines, caret_pos, '┃')?; - } - - Ok(mut_lines) - } - - fn insert_at_pos(lines: &mut [String], pos: TextPos, insert_char: char) -> UIResult<()> { - let line = get_mut_res(pos.line, lines)?; - - let mut chars: Vec = line.chars().collect(); - chars.insert(pos.column, insert_char); - - *line = chars.into_iter().collect::(); - - Ok(()) - } - - // It's much nicer to have get_mut return a Result with clear error than an Option - fn get_mut_res( - index: usize, - vec: &mut [T], - ) -> UIResult<&mut >::Output> { - let vec_len = vec.len(); - - let elt_ref = vec.get_mut(index).context(OutOfBoundsSnafu { - index, - collection_name: "Slice", - len: vec_len, - })?; - - Ok(elt_ref) - } -} diff --git a/crates/editor/src/ui/text/lines.rs b/crates/editor/src/ui/text/lines.rs deleted file mode 100644 index 37e1d67452..0000000000 --- a/crates/editor/src/ui/text/lines.rs +++ /dev/null @@ -1,445 +0,0 @@ -// Adapted from https://github.com/cessen/ropey -// by Nathan Vegdahl - license information can be found in the LEGAL_DETAILS -// file in the root directory of this distribution. -// -// Thank you, Nathan! - -use crate::ui::text::caret_w_select::CaretWSelect; -use crate::ui::text::selection::validate_sel_opt; -use crate::ui::text::{ - selection::{RawSelection, Selection}, - text_pos::TextPos, -}; -use crate::ui::ui_error::UIResult; -use crate::ui::util::is_newline; -use crate::window::keyboard_input::Modifiers; -use std::cmp::max; -use std::cmp::min; -use winit::event::VirtualKeyCode; - -pub trait Lines { - fn get_line_ref(&self, line_nr: usize) -> UIResult<&str>; - - fn line_len(&self, line_nr: usize) -> UIResult; - - fn nr_of_lines(&self) -> usize; - - fn nr_of_chars(&self) -> usize; - - fn all_lines_as_string(&self) -> String; - - fn is_last_line(&self, line_nr: usize) -> bool; - - fn last_char(&self, line_nr: usize) -> UIResult>; -} - -pub trait SelectableLines { - fn get_caret(&self) -> TextPos; - - fn set_caret(&mut self, caret_pos: TextPos); - - fn move_caret_left(&mut self, modifiers: &Modifiers) -> UIResult<()>; - - fn move_caret_right(&mut self, modifiers: &Modifiers) -> UIResult<()>; - - fn move_caret_up(&mut self, modifiers: &Modifiers) -> UIResult<()>; - - fn move_caret_down(&mut self, modifiers: &Modifiers) -> UIResult<()>; - - fn move_caret_home(&mut self, modifiers: &Modifiers) -> UIResult<()>; - - fn move_caret_end(&mut self, modifiers: &Modifiers) -> UIResult<()>; - - fn get_selection(&self) -> Option; - - fn is_selection_active(&self) -> bool; - - fn get_selected_str(&self) -> UIResult>; - - fn set_raw_sel(&mut self, raw_sel: RawSelection) -> UIResult<()>; - - fn set_sel_none(&mut self); - - fn set_caret_w_sel(&mut self, caret_w_sel: CaretWSelect); - - fn select_all(&mut self) -> UIResult<()>; - - fn last_text_pos(&self) -> UIResult; - - fn handle_key_down( - &mut self, - modifiers: &Modifiers, - virtual_keycode: VirtualKeyCode, - ) -> UIResult<()>; -} - -pub trait MutSelectableLines { - fn insert_char(&mut self, new_char: &char) -> UIResult<()>; - - // could be for insertion, backspace, del... - fn handle_new_char(&mut self, received_char: &char) -> UIResult<()>; - - fn insert_str(&mut self, new_str: &str) -> UIResult<()>; - - fn backspace(&mut self) -> UIResult<()>; - - fn del_selection(&mut self) -> UIResult<()>; -} - -// T: Lines -pub type MoveCaretFun = fn(&T, CaretWSelect, &Modifiers) -> UIResult; - -pub fn move_caret_left( - lines: &T, - caret_w_select: CaretWSelect, - modifiers: &Modifiers, -) -> UIResult { - let old_selection_opt = caret_w_select.selection_opt; - let old_caret_pos = caret_w_select.caret_pos; - let old_line_nr = old_caret_pos.line; - let old_col_nr = old_caret_pos.column; - - let shift_pressed = modifiers.shift; - - let (line_nr, col_nr) = if old_selection_opt.is_some() && !shift_pressed { - match old_selection_opt { - Some(old_selection) => (old_selection.start_pos.line, old_selection.start_pos.column), - None => unreachable!(), - } - } else if old_col_nr == 0 { - if old_line_nr == 0 { - (0, 0) - } else { - let curr_line_len = lines.line_len(old_line_nr - 1)?; - - (old_line_nr - 1, curr_line_len) - } - } else { - (old_line_nr, old_col_nr - 1) - }; - - let new_caret_pos = TextPos { - line: line_nr, - column: col_nr, - }; - - let new_selection_opt = if shift_pressed { - if let Some(old_selection) = old_selection_opt { - if old_caret_pos >= old_selection.end_pos { - if new_caret_pos == old_selection.start_pos { - None - } else { - validate_sel_opt(old_selection.start_pos, new_caret_pos)? - } - } else { - validate_sel_opt( - TextPos { - line: line_nr, - column: col_nr, - }, - old_selection.end_pos, - )? - } - } else if !(old_line_nr == line_nr && old_col_nr == col_nr) { - validate_sel_opt( - TextPos { - line: line_nr, - column: col_nr, - }, - TextPos { - line: old_line_nr, - column: old_col_nr, - }, - )? - } else { - None - } - } else { - None - }; - - Ok(CaretWSelect::new(new_caret_pos, new_selection_opt)) -} - -pub fn move_caret_right( - lines: &T, - caret_w_select: CaretWSelect, - modifiers: &Modifiers, -) -> UIResult { - let old_selection_opt = caret_w_select.selection_opt; - let old_caret_pos = caret_w_select.caret_pos; - let old_line_nr = old_caret_pos.line; - let old_col_nr = old_caret_pos.column; - - let shift_pressed = modifiers.shift; - - let (line_nr, col_nr) = if old_selection_opt.is_some() && !shift_pressed { - match old_selection_opt { - Some(old_selection) => (old_selection.end_pos.line, old_selection.end_pos.column), - None => unreachable!(), - } - } else { - let curr_line_len = lines.line_len(old_line_nr)?; - let is_last_line = lines.is_last_line(old_line_nr); - - if !is_last_line { - if old_col_nr + 1 > curr_line_len { - (old_line_nr + 1, 0) - } else { - (old_line_nr, old_col_nr + 1) - } - } else if old_col_nr < curr_line_len { - (old_line_nr, old_col_nr + 1) - } else { - (old_line_nr, old_col_nr) - } - }; - - let new_caret_pos = TextPos { - line: line_nr, - column: col_nr, - }; - - let new_selection_opt = if shift_pressed { - if let Some(old_selection) = old_selection_opt { - if old_caret_pos <= old_selection.start_pos { - if new_caret_pos == old_selection.end_pos { - None - } else { - validate_sel_opt(new_caret_pos, old_selection.end_pos)? - } - } else { - validate_sel_opt( - old_selection.start_pos, - TextPos { - line: line_nr, - column: col_nr, - }, - )? - } - } else if !(old_line_nr == line_nr && old_col_nr == col_nr) { - validate_sel_opt( - TextPos { - line: old_line_nr, - column: old_col_nr, - }, - TextPos { - line: line_nr, - column: col_nr, - }, - )? - } else { - None - } - } else { - None - }; - - Ok(CaretWSelect::new(new_caret_pos, new_selection_opt)) -} - -pub fn move_caret_up( - lines: &T, - caret_w_select: CaretWSelect, - modifiers: &Modifiers, -) -> UIResult { - let old_selection_opt = caret_w_select.selection_opt; - let old_caret_pos = caret_w_select.caret_pos; - let old_line_nr = old_caret_pos.line; - let old_col_nr = old_caret_pos.column; - - let shift_pressed = modifiers.shift; - - let (line_nr, col_nr) = if old_selection_opt.is_some() && !shift_pressed { - match old_selection_opt { - Some(old_selection) => (old_selection.start_pos.line, old_selection.start_pos.column), - None => unreachable!(), - } - } else if old_line_nr == 0 { - (old_line_nr, 0) - } else { - let prev_line_len = lines.line_len(old_line_nr - 1)?; - - if prev_line_len <= old_col_nr { - let new_column = if prev_line_len > 0 { prev_line_len } else { 0 }; - - (old_line_nr - 1, new_column) - } else { - (old_line_nr - 1, old_col_nr) - } - }; - - let new_caret_pos = TextPos { - line: line_nr, - column: col_nr, - }; - - let new_selection_opt = if shift_pressed { - if let Some(old_selection) = old_selection_opt { - if old_selection.end_pos <= old_caret_pos { - if new_caret_pos == old_selection.start_pos { - None - } else { - validate_sel_opt( - min(old_selection.start_pos, new_caret_pos), - max(old_selection.start_pos, new_caret_pos), - )? - } - } else { - validate_sel_opt(new_caret_pos, old_selection.end_pos)? - } - } else if !(old_line_nr == line_nr && old_col_nr == col_nr) { - validate_sel_opt( - min(old_caret_pos, new_caret_pos), - max(old_caret_pos, new_caret_pos), - )? - } else { - None - } - } else { - None - }; - - Ok(CaretWSelect::new(new_caret_pos, new_selection_opt)) -} - -pub fn move_caret_down( - lines: &T, - caret_w_select: CaretWSelect, - modifiers: &Modifiers, -) -> UIResult { - let old_selection_opt = caret_w_select.selection_opt; - let old_caret_pos = caret_w_select.caret_pos; - let old_line_nr = old_caret_pos.line; - let old_col_nr = old_caret_pos.column; - - let shift_pressed = modifiers.shift; - - let (line_nr, col_nr) = if old_selection_opt.is_some() && !shift_pressed { - match old_selection_opt { - Some(old_selection) => (old_selection.end_pos.line, old_selection.end_pos.column), - None => unreachable!(), - } - } else if old_line_nr + 1 >= lines.nr_of_lines() { - let curr_line_len = lines.line_len(old_line_nr)?; - - (old_line_nr, curr_line_len) - } else { - let next_line_index = old_line_nr + 1; - let next_line_len = lines.line_len(next_line_index)?; - let is_last_line = lines.is_last_line(next_line_index); - - if next_line_len <= old_col_nr { - if !is_last_line { - let new_column = if next_line_len > 0 { next_line_len } else { 0 }; - - (old_line_nr + 1, new_column) - } else { - (old_line_nr + 1, next_line_len) - } - } else { - (old_line_nr + 1, old_col_nr) - } - }; - - let new_caret_pos = TextPos { - line: line_nr, - column: col_nr, - }; - - let new_selection_opt = if shift_pressed { - if let Some(old_selection) = old_selection_opt { - if old_caret_pos <= old_selection.start_pos { - if new_caret_pos == old_selection.end_pos { - None - } else { - validate_sel_opt( - min(old_selection.end_pos, new_caret_pos), - max(old_selection.end_pos, new_caret_pos), - )? - } - } else { - validate_sel_opt(old_selection.start_pos, new_caret_pos)? - } - } else if !(old_line_nr == line_nr && old_col_nr == col_nr) { - validate_sel_opt( - min(old_caret_pos, new_caret_pos), - max(old_caret_pos, new_caret_pos), - )? - } else { - None - } - } else { - None - }; - - Ok(CaretWSelect::new(new_caret_pos, new_selection_opt)) -} - -pub fn move_caret_home( - lines: &T, - caret_w_select: CaretWSelect, - modifiers: &Modifiers, -) -> UIResult { - let curr_line_nr = caret_w_select.caret_pos.line; - let old_col_nr = caret_w_select.caret_pos.column; - - let curr_line_str = lines.get_line_ref(curr_line_nr)?; - let line_char_iter = curr_line_str.chars(); - - let mut first_no_space_char_col = 0; - let mut non_space_found = false; - - for c in line_char_iter { - if !c.is_whitespace() { - non_space_found = true; - break; - } else { - first_no_space_char_col += 1; - } - } - - if !non_space_found { - first_no_space_char_col = 0; - } - - let new_col_nr = if first_no_space_char_col == old_col_nr { - 0 - } else { - first_no_space_char_col - }; - - caret_w_select.move_caret_w_mods( - TextPos { - line: curr_line_nr, - column: new_col_nr, - }, - modifiers, - ) -} - -pub fn move_caret_end( - lines: &T, - caret_w_select: CaretWSelect, - modifiers: &Modifiers, -) -> UIResult { - let curr_line_nr = caret_w_select.caret_pos.line; - let curr_line_len = lines.line_len(curr_line_nr)?; - - let new_col = if let Some(last_char) = lines.last_char(curr_line_nr)? { - if is_newline(&last_char) { - curr_line_len - 1 - } else { - curr_line_len - } - } else { - 0 - }; - - let new_pos = TextPos { - line: curr_line_nr, - column: new_col, - }; - - caret_w_select.move_caret_w_mods(new_pos, modifiers) -} diff --git a/crates/editor/src/ui/text/mod.rs b/crates/editor/src/ui/text/mod.rs deleted file mode 100644 index feaa7946f8..0000000000 --- a/crates/editor/src/ui/text/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod big_text_area; -pub mod caret_w_select; -pub mod lines; -pub mod selection; -mod text_buffer; -pub mod text_pos; diff --git a/crates/editor/src/ui/text/selection.rs b/crates/editor/src/ui/text/selection.rs deleted file mode 100644 index aff0499467..0000000000 --- a/crates/editor/src/ui/text/selection.rs +++ /dev/null @@ -1,152 +0,0 @@ -#![allow(dead_code)] - -use super::lines::Lines; -use super::text_pos::TextPos; -use crate::ui::theme::UITheme; -use crate::ui::ui_error::{InvalidSelectionSnafu, UIResult}; -use bumpalo::collections::Vec as BumpVec; -use snafu::ensure; -use std::fmt; - -#[derive(Debug, Copy, Clone)] -pub struct RawSelection { - pub start_pos: TextPos, - pub end_pos: TextPos, -} - -impl std::fmt::Display for RawSelection { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "RawSelection: start_pos: line:{} col:{}, end_pos: line:{} col:{}", - self.start_pos.line, self.start_pos.column, self.end_pos.line, self.end_pos.column - ) - } -} - -#[derive(Debug, Copy, Clone)] -pub struct Selection { - pub start_pos: TextPos, - pub end_pos: TextPos, -} - -impl Selection { - pub fn is_on_same_line(&self) -> bool { - self.start_pos.line == self.end_pos.line - } -} - -pub fn validate_raw_sel(raw_sel: RawSelection) -> UIResult { - validate_selection(raw_sel.start_pos, raw_sel.end_pos) -} - -pub fn validate_sel_opt(start_pos: TextPos, end_pos: TextPos) -> UIResult> { - Ok(Some(validate_selection(start_pos, end_pos)?)) -} - -pub fn validate_selection(start_pos: TextPos, end_pos: TextPos) -> UIResult { - ensure!( - start_pos.line <= end_pos.line, - InvalidSelectionSnafu { - err_msg: format!( - "start_pos.line ({}) should be smaller than or equal to end_pos.line ({})", - start_pos.line, end_pos.line - ) - } - ); - - ensure!( - !(start_pos.line == end_pos.line && start_pos.column >= end_pos.column), - InvalidSelectionSnafu { - err_msg: format!( - "start_pos.column ({}) should be smaller than end_pos.column ({}) when start_pos.line equals end_pos.line", - start_pos.column, - end_pos.column - ) - } - ); - - Ok(Selection { start_pos, end_pos }) -} - -use crate::graphics::primitives::rect::Rect; -use bumpalo::Bump; - -pub fn create_selection_rects<'a>( - valid_sel: Selection, - lines: &dyn Lines, - glyph_dim_rect: &Rect, - theme: &UITheme, - arena: &'a Bump, -) -> UIResult> { - let Selection { start_pos, end_pos } = valid_sel; - - let mut all_rects: BumpVec = BumpVec::new_in(arena); - - let height = glyph_dim_rect.height; - let start_y = glyph_dim_rect.top_left_coords.y + height * (start_pos.line as f32); - let line_start_x = glyph_dim_rect.top_left_coords.x; - - if start_pos.line == end_pos.line { - let width = ((end_pos.column as f32) * glyph_dim_rect.width) - - ((start_pos.column as f32) * glyph_dim_rect.width); - let sel_rect_x = line_start_x + ((start_pos.column as f32) * glyph_dim_rect.width); - - all_rects.push(Rect { - top_left_coords: (sel_rect_x, start_y).into(), - width, - height, - color: theme.select_highlight, - }); - } else { - // first line - let end_col = lines.line_len(start_pos.line)?; - let width = ((end_col as f32) * glyph_dim_rect.width) - - ((start_pos.column as f32) * glyph_dim_rect.width); - - let sel_rect_x = line_start_x + ((start_pos.column as f32) * glyph_dim_rect.width); - - all_rects.push(Rect { - top_left_coords: (sel_rect_x, start_y).into(), - width, - height, - color: theme.select_highlight, - }); - - //middle lines - let nr_mid_lines = (end_pos.line - start_pos.line) - 1; - let first_mid_line = start_pos.line + 1; - - for i in first_mid_line..(first_mid_line + nr_mid_lines) { - let mid_line_len = lines.line_len(i)?; - - let width = (mid_line_len as f32) * glyph_dim_rect.width; - - let sel_rect_y = start_y + ((i - start_pos.line) as f32) * glyph_dim_rect.height; - - all_rects.push(Rect { - top_left_coords: (line_start_x, sel_rect_y).into(), - width, - height, - color: theme.select_highlight, - }); - } - - //last line - if end_pos.column > 0 { - let sel_rect_y = - start_y + ((end_pos.line - start_pos.line) as f32) * glyph_dim_rect.height; - - let width = (end_pos.column as f32) * glyph_dim_rect.width; - - all_rects.push(Rect { - top_left_coords: (line_start_x, sel_rect_y).into(), - width, - height, - color: theme.select_highlight, - }); - } - } - - Ok(all_rects) -} diff --git a/crates/editor/src/ui/text/text_buffer.rs b/crates/editor/src/ui/text/text_buffer.rs deleted file mode 100644 index faa455e1b6..0000000000 --- a/crates/editor/src/ui/text/text_buffer.rs +++ /dev/null @@ -1,193 +0,0 @@ -use std::path::Path; - -use crate::ui::{ - ui_error::{OutOfBoundsSnafu, TextBufReadFailedSnafu, UIResult}, - util::{path_to_string, reader_from_path}, -}; -use snafu::ensure; - -use super::{selection::Selection, text_pos::TextPos}; -use std::io::BufRead; - -// Do not use for large amounts of text. -// This should become a trait in the future and be implemented by a SmallTextBuffer and Rope(for large amounts of text) -#[derive(Debug)] -pub struct TextBuffer { - pub lines: Vec, -} - -impl TextBuffer { - pub fn from_path(path: &Path) -> UIResult { - let buf_reader = reader_from_path(path)?; - let mut lines: Vec = Vec::new(); - - for line in buf_reader.lines() { - match line { - Ok(line_str) => lines.push(line_str), - Err(e) => { - TextBufReadFailedSnafu { - path_str: path_to_string(path), - err_msg: e.to_string(), - } - .fail()?; - } - } - } - - Ok(TextBuffer { lines }) - } - - pub fn nr_of_chars(&self) -> usize { - let mut nr_of_chars = 0; - - for line in self.lines.iter() { - nr_of_chars += line.len(); - } - - nr_of_chars - } - - pub fn nr_of_lines(&self) -> usize { - self.lines.len() - } - - pub fn get_line_ref(&self, line_nr: usize) -> UIResult<&str> { - self.ensure_bounds(line_nr)?; - // safe unwrap because we checked the length - Ok(self.lines.get(line_nr).unwrap()) - } - - pub fn line_len(&self, line_nr: usize) -> UIResult { - Ok(self.get_line_ref(line_nr)?.len()) - } - - fn ensure_bounds(&self, line_nr: usize) -> UIResult<()> { - ensure!( - line_nr < self.nr_of_lines(), - OutOfBoundsSnafu { - index: line_nr, - collection_name: "TextBuffer", - len: self.nr_of_lines(), - } - ); - - Ok(()) - } - - fn ensure_bounds_txt_pos(&self, txt_pos: TextPos) -> UIResult<()> { - ensure!( - txt_pos.line < self.nr_of_lines(), - OutOfBoundsSnafu { - index: txt_pos.line, - collection_name: "TextBuffer", - len: self.nr_of_lines(), - } - ); - - let line_ref = self.get_line_ref(txt_pos.line)?; - let line_len = line_ref.len(); - - ensure!( - txt_pos.column <= line_len, - OutOfBoundsSnafu { - index: txt_pos.column, - collection_name: format!("Line in TextBuffer: {line_ref}"), - len: line_len, - } - ); - - Ok(()) - } - - pub fn all_lines_ref(&self) -> &[String] { - &self.lines - } - - pub fn get_selected_str(&self, selection: Selection) -> UIResult { - let start_line_nr = selection.start_pos.line; - let start_col_nr = selection.start_pos.column; - - let end_line_nr = selection.end_pos.line; - let end_col_nr = selection.end_pos.column; - - let mut selected_str = String::new(); - - if end_line_nr > start_line_nr { - selected_str.push_str(&self.get_line_ref(start_line_nr)?[start_col_nr..]); - - for line_nr in start_line_nr + 1..end_line_nr - 1 { - selected_str.push_str(self.get_line_ref(line_nr)?); - } - - selected_str.push_str(&self.get_line_ref(end_line_nr)?[..end_col_nr]); - } else { - // start_line_nr == end_line_nr - selected_str.push_str(&self.get_line_ref(start_line_nr)?[start_col_nr..end_col_nr]); - } - - Ok(selected_str) - } - - pub fn insert_str(&mut self, txt_pos: TextPos, new_str: &str) -> UIResult<()> { - self.ensure_bounds_txt_pos(txt_pos)?; - - // safe unwrap because we checked the length - self.lines - .get_mut(txt_pos.line) - .unwrap() - .insert_str(txt_pos.column, new_str); - - Ok(()) - } - - pub fn backspace_char(&mut self, txt_pos: TextPos) -> UIResult<()> { - if txt_pos.column > 0 { - let prev_col_pos = TextPos { - line: txt_pos.line, - column: txt_pos.column - 1, - }; - - self.ensure_bounds_txt_pos(prev_col_pos)?; - - let line_ref = self.lines.get_mut(prev_col_pos.line).unwrap(); // safe because of earlier bounds check - - line_ref.remove(prev_col_pos.column); - } else if txt_pos.line > 0 { - self.lines.remove(txt_pos.line); - } - - Ok(()) - } - - pub fn del_selection(&mut self, selection: Selection) -> UIResult<()> { - self.ensure_bounds_txt_pos(selection.start_pos)?; - self.ensure_bounds_txt_pos(selection.end_pos)?; - - let start_line_nr = selection.start_pos.line; - let start_col_nr = selection.start_pos.column; - let end_line_nr = selection.end_pos.line; - let end_col_nr = selection.end_pos.column; - - if end_line_nr > start_line_nr { - // remove in reverse to prevent shifting indices - if end_col_nr == self.line_len(end_line_nr)? { - self.lines.remove(end_line_nr); - } else { - let line_ref = self.lines.get_mut(end_line_nr).unwrap(); // safe because of earlier bounds check - line_ref.replace_range(..end_col_nr, ""); - } - - self.lines.drain(start_line_nr + 1..end_line_nr); - - let line_ref = self.lines.get_mut(start_line_nr).unwrap(); // safe because of earlier bounds check - line_ref.replace_range(start_col_nr.., "") - } else { - // selection.end_pos.line == selection.start_pos.line - let line_ref = self.lines.get_mut(selection.start_pos.line).unwrap(); // safe because of earlier bounds check - - line_ref.replace_range(selection.start_pos.column..selection.end_pos.column, "") - } - - Ok(()) - } -} diff --git a/crates/editor/src/ui/text/text_pos.rs b/crates/editor/src/ui/text/text_pos.rs deleted file mode 100644 index 894fb9c568..0000000000 --- a/crates/editor/src/ui/text/text_pos.rs +++ /dev/null @@ -1,49 +0,0 @@ -use std::cmp::Ordering; - -#[derive(Debug, Copy, Clone)] -pub struct TextPos { - pub line: usize, - pub column: usize, -} - -impl TextPos { - pub fn increment_col(&self) -> TextPos { - TextPos { - line: self.line, - column: self.column + 1, - } - } - - pub fn decrement_col(&self) -> TextPos { - let new_col = if self.column > 0 { - self.column - 1 - } else { - self.column - }; - - TextPos { - line: self.line, - column: new_col, - } - } -} - -impl Ord for TextPos { - fn cmp(&self, other: &Self) -> Ordering { - (self.line, self.column).cmp(&(other.line, other.column)) - } -} - -impl PartialOrd for TextPos { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl PartialEq for TextPos { - fn eq(&self, other: &Self) -> bool { - (self.line, self.column) == (other.line, other.column) - } -} - -impl Eq for TextPos {} diff --git a/crates/editor/src/ui/theme.rs b/crates/editor/src/ui/theme.rs deleted file mode 100644 index 640c8daa6a..0000000000 --- a/crates/editor/src/ui/theme.rs +++ /dev/null @@ -1,34 +0,0 @@ -use gr_colors::{from_hsb, from_hsba, RgbaTup}; -use serde::{Deserialize, Serialize}; - -use crate::graphics::colors as gr_colors; - -pub const LIGHT_BRAND_COL: RgbaTup = (0.506, 0.337, 0.902, 1.0); // #8257e5 hsb(258, 62, 90) -pub const DARK_BRAND_COL: RgbaTup = (0.380, 0.169, 0.871, 1.0); // #612bde hsb(258, 81, 87) - -#[derive(Deserialize, Serialize)] -pub struct UITheme { - pub light_brand: RgbaTup, - pub dark_brand: RgbaTup, - pub text: RgbaTup, - pub caret: RgbaTup, - pub select_highlight: RgbaTup, - pub tooltip_bg: RgbaTup, - pub tooltip_text: RgbaTup, - pub default_font_size: f32, -} - -impl Default for UITheme { - fn default() -> Self { - Self { - light_brand: LIGHT_BRAND_COL, - dark_brand: DARK_BRAND_COL, - text: gr_colors::WHITE, - caret: gr_colors::WHITE, - select_highlight: from_hsba(240, 55, 100, 0.3), - tooltip_bg: from_hsb(240, 60, 50), - tooltip_text: gr_colors::WHITE, - default_font_size: 30.0, - } - } -} diff --git a/crates/editor/src/ui/tooltip.rs b/crates/editor/src/ui/tooltip.rs deleted file mode 100644 index 2a488b0ccd..0000000000 --- a/crates/editor/src/ui/tooltip.rs +++ /dev/null @@ -1,87 +0,0 @@ -use crate::graphics::primitives::rect::Rect; -use crate::graphics::primitives::text as gr_text; -use crate::graphics::primitives::text::Text; -use crate::ui::theme::UITheme; - -pub struct ToolTip<'a> { - pub position_x: f32, - pub position_y: f32, - pub text: &'a str, -} - -impl<'a> ToolTip<'a> { - fn make_tooltip_rect( - &self, - width: f32, - height: f32, - height_padding: f32, - y_margin: f32, - ui_theme: &UITheme, - ) -> Rect { - Rect { - top_left_coords: ( - self.position_x, - self.position_y - (height_padding + y_margin), - ) - .into(), - height: height + height_padding, - width, - color: ui_theme.tooltip_bg, - } - } - - fn make_tooltip_text<'b>( - &'b self, - x_offset: f32, - y_offset: f32, - y_margin: f32, - ui_theme: &UITheme, - code_font_size: f32, - ) -> Text<'b> { - Text { - position: ( - self.position_x + x_offset, - self.position_y - (y_offset + y_margin), - ) - .into(), - color: ui_theme.tooltip_text, - text: self.text, - size: code_font_size, - ..Default::default() - } - } - - pub fn render_tooltip( - &self, - glyph_dim_rect: &Rect, - ui_theme: &UITheme, - code_font_size: f32, - ) -> (Rect, glyph_brush::OwnedSection) { - let width_padding = glyph_dim_rect.height / 1.3; - let height_padding = width_padding / 1.3; - - let text_x_offset = width_padding / 2.0; - let text_y_offset = height_padding / 2.0; - - let y_margin = glyph_dim_rect.height / 4.0; - - let text = self.make_tooltip_text( - text_x_offset, - text_y_offset, - y_margin, - ui_theme, - code_font_size, - ); - let text_section = gr_text::owned_section_from_text(&text); - - let rect = self.make_tooltip_rect( - glyph_dim_rect.width * (text.text.len() as f32) + width_padding, - glyph_dim_rect.height, - height_padding, - y_margin, - ui_theme, - ); - - (rect, text_section) - } -} diff --git a/crates/editor/src/ui/ui_error.rs b/crates/editor/src/ui/ui_error.rs deleted file mode 100644 index af4f34fd74..0000000000 --- a/crates/editor/src/ui/ui_error.rs +++ /dev/null @@ -1,69 +0,0 @@ -use std::io; - -use snafu::{Backtrace, Snafu}; - -//import errors as follows: -// `use crate::error::OutOfBounds;` -// *not* `use crate::error::EdError::OutOfBounds;` -// see https://github.com/shepmaster/snafu/issues/211 - -#[derive(Debug, Snafu)] -#[snafu(visibility(pub))] -pub enum UIError { - #[snafu(display( - "LineInsertionFailed: line_nr ({}) needs to be <= nr_of_lines ({}).", - line_nr, - nr_of_lines - ))] - LineInsertionFailed { - line_nr: usize, - nr_of_lines: usize, - backtrace: Backtrace, - }, - #[snafu(display( - "OutOfBounds: index {} was out of bounds for {} with length {}.", - index, - collection_name, - len - ))] - OutOfBounds { - index: usize, - collection_name: String, - len: usize, - backtrace: Backtrace, - }, - - #[snafu(display("InvalidSelection: {}.", err_msg))] - InvalidSelection { - err_msg: String, - backtrace: Backtrace, - }, - - #[snafu(display( - "FileOpenFailed: failed to open file with path {} with the following error: {}.", - path_str, - err_msg - ))] - FileOpenFailed { path_str: String, err_msg: String }, - - #[snafu(display( - "FileWriteFailed: failed to write to file with path {}, I got this IO error: {}.", - path_str, - source - ))] - FileWriteFailed { source: io::Error, path_str: String }, - - #[snafu(display("TextBufReadFailed: the file {} could be opened but we encountered the following error while trying to read it: {}.", path_str, err_msg))] - TextBufReadFailed { path_str: String, err_msg: String }, - - #[snafu(display("MissingGlyphDims: glyph_dim_rect_opt was None. It needs to be set using the example_code_glyph_rect function."))] - MissingGlyphDims { backtrace: Backtrace }, -} - -pub type UIResult = std::result::Result; - -impl From for String { - fn from(ui_error: UIError) -> Self { - format!("{ui_error}") - } -} diff --git a/crates/editor/src/ui/util.rs b/crates/editor/src/ui/util.rs deleted file mode 100644 index c6c9936ebe..0000000000 --- a/crates/editor/src/ui/util.rs +++ /dev/null @@ -1,59 +0,0 @@ -use super::ui_error::{FileOpenFailedSnafu, FileWriteFailedSnafu, OutOfBoundsSnafu, UIResult}; -use snafu::{OptionExt, ResultExt}; -use std::{fs::File, io::BufReader, path::Path, slice::SliceIndex}; - -pub fn is_newline(char_ref: &char) -> bool { - let newline_codes = vec!['\u{d}', '\n']; - - newline_codes.contains(char_ref) -} - -// replace slice method that return Option with one that return Result and proper Error -pub fn slice_get(index: usize, slice: &[T]) -> UIResult<&>::Output> { - let elt_ref = slice.get(index).context(OutOfBoundsSnafu { - index, - collection_name: "Slice", - len: slice.len(), - })?; - - Ok(elt_ref) -} - -pub fn slice_get_mut( - index: usize, - slice: &mut [T], -) -> UIResult<&mut >::Output> { - let slice_len = slice.len(); - - let elt_ref = slice.get_mut(index).context(OutOfBoundsSnafu { - index, - collection_name: "Slice", - len: slice_len, - })?; - - Ok(elt_ref) -} - -pub fn reader_from_path(path: &Path) -> UIResult> { - match File::open(path) { - Ok(file) => Ok(BufReader::new(file)), - Err(e) => FileOpenFailedSnafu { - path_str: path_to_string(path), - err_msg: e.to_string(), - } - .fail()?, - } -} - -pub fn path_to_string(path: &Path) -> String { - let mut path_str = String::new(); - path_str.push_str(&path.to_string_lossy()); - - path_str -} - -pub fn write_to_file(path: &Path, content: &str) -> UIResult<()> { - std::fs::write(path, content).with_context(|_| FileWriteFailedSnafu { - path_str: path_to_string(path), - }) -} diff --git a/crates/editor/src/window/keyboard_input.rs b/crates/editor/src/window/keyboard_input.rs deleted file mode 100644 index 1f06293d1c..0000000000 --- a/crates/editor/src/window/keyboard_input.rs +++ /dev/null @@ -1,74 +0,0 @@ -// note: the Default is that these are all False -#[derive(Debug, Default)] -pub struct Modifiers { - pub shift: bool, - pub ctrl: bool, - pub alt: bool, - pub logo: bool, -} - -impl Modifiers { - pub fn cmd_or_ctrl(&self) -> bool { - #[cfg(target_os = "macos")] - let active = self.logo; - - #[cfg(not(target_os = "macos"))] - let active = self.ctrl; - - active - } - - // returns true if modifiers are active that can be active when the user wants to insert a new char; e.g.: shift+a to make A - pub fn new_char_modifiers(&self) -> bool { - self.no_modifiers() - || (self.shift && !self.ctrl && !self.alt && !self.logo) // e.g.: shift+a to make A - || (self.cmd_or_ctrl() && self.alt) // e.g.: ctrl+alt+2 to make @ on azerty keyboard - } - - fn no_modifiers(&self) -> bool { - !self.shift && !self.ctrl && !self.alt && !self.logo - } -} - -pub fn no_mods() -> Modifiers { - Modifiers { - shift: false, - ctrl: false, - alt: false, - logo: false, - } -} - -pub fn from_winit(winit_mods: &winit::event::ModifiersState) -> Modifiers { - Modifiers { - shift: winit_mods.shift(), - ctrl: winit_mods.ctrl(), - alt: winit_mods.alt(), - logo: winit_mods.logo(), - } -} - -#[cfg(test)] -pub mod test_modifiers { - use crate::window::keyboard_input::Modifiers; - - pub fn ctrl_cmd_shift() -> Modifiers { - #[cfg(target_os = "macos")] - let mods = Modifiers { - shift: true, - ctrl: false, - alt: false, - logo: true, - }; - - #[cfg(not(target_os = "macos"))] - let mods = Modifiers { - shift: true, - ctrl: true, - alt: false, - logo: false, - }; - - mods - } -} diff --git a/crates/editor/src/window/mod.rs b/crates/editor/src/window/mod.rs deleted file mode 100644 index 939d83aa33..0000000000 --- a/crates/editor/src/window/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod keyboard_input; diff --git a/crates/editor/tests/README.md b/crates/editor/tests/README.md deleted file mode 100644 index c32f7a8af0..0000000000 --- a/crates/editor/tests/README.md +++ /dev/null @@ -1,5 +0,0 @@ - -# Where are the tests? - -We have a lot of tests at the end of source files, this allows us to test functions that are not exposed by the editor itself. -`editor/mvc/ed_update.rs` and `editor/ui/text/big_text_area.rs` have many important tests. diff --git a/crates/editor/tests/modules/SimpleUnformatted.roc b/crates/editor/tests/modules/SimpleUnformatted.roc deleted file mode 100644 index a6185d3460..0000000000 --- a/crates/editor/tests/modules/SimpleUnformatted.roc +++ /dev/null @@ -1,13 +0,0 @@ -interface Simple - exposes [ - v, - x, - ] - imports [] - -v : Str - -v = "Value!" - -x : Int -x = 4 diff --git a/crates/editor/tests/selection.pest b/crates/editor/tests/selection.pest deleted file mode 100644 index 6c70079a54..0000000000 --- a/crates/editor/tests/selection.pest +++ /dev/null @@ -1,11 +0,0 @@ -text = { (ASCII_ALPHANUMERIC | " " | "\t" | "\n" | "{" | "}" | "," | "." | "[" | "]" | ":" | "<" | ">" | "-" | "\"" | "=" )* } - -caret = {"┃"} - -optSelStart = { "❮"{0,1} } - -optSelEnd = { "❯"{0,1} } - -optCaret = { caret{0,1} } - -linesWithSelect = { SOI ~ text ~ optCaret ~ text ~ optSelStart ~ text ~ optCaret ~ text ~ optCaret ~ text ~ optSelEnd ~ text ~ optCaret ~ text ~ EOI} diff --git a/crates/highlight/Cargo.toml b/crates/highlight/Cargo.toml index d612c8a0e7..b5b47385c8 100644 --- a/crates/highlight/Cargo.toml +++ b/crates/highlight/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "roc_highlight" -description = "For syntax highlighting, starts with a string and returns our markup nodes." +description = "For syntax highlighting, starts with a string and returns HTML." authors.workspace = true edition.workspace = true diff --git a/default.nix b/default.nix index 4e0ba26fb5..491d61ad85 100644 --- a/default.nix +++ b/default.nix @@ -75,31 +75,16 @@ in buildInputs = (with pkgs; [ libffi - libiconv - libxkbcommon libxml2 ncurses zlib cargo makeWrapper # necessary for postBuild wrapProgram - ] ++ lib.optionals pkgs.stdenv.isLinux [ - valgrind - vulkan-headers - vulkan-loader - vulkan-tools - vulkan-validation-layers - xorg.libX11 - xorg.libXcursor - xorg.libXi - xorg.libXrandr - xorg.libxcb ] ++ lib.optionals pkgs.stdenv.isDarwin [ pkgs.darwin.apple_sdk.frameworks.AppKit pkgs.darwin.apple_sdk.frameworks.CoreFoundation pkgs.darwin.apple_sdk.frameworks.CoreServices - pkgs.darwin.apple_sdk.frameworks.CoreVideo pkgs.darwin.apple_sdk.frameworks.Foundation - pkgs.darwin.apple_sdk.frameworks.Metal pkgs.darwin.apple_sdk.frameworks.Security ]); diff --git a/crates/editor/Inconsolata-Regular.ttf b/examples/gui/Inconsolata-Regular.ttf similarity index 100% rename from crates/editor/Inconsolata-Regular.ttf rename to examples/gui/Inconsolata-Regular.ttf diff --git a/examples/gui/breakout/platform/src/graphics/primitives/text.rs b/examples/gui/breakout/platform/src/graphics/primitives/text.rs index 45189fdc96..2ebccf9161 100644 --- a/examples/gui/breakout/platform/src/graphics/primitives/text.rs +++ b/examples/gui/breakout/platform/src/graphics/primitives/text.rs @@ -127,7 +127,7 @@ pub fn build_glyph_brush( render_format: wgpu::TextureFormat, ) -> Result, InvalidFont> { let inconsolata = FontArc::try_from_slice(include_bytes!( - "../../../../../../../crates/editor/Inconsolata-Regular.ttf" + "../../../../../Inconsolata-Regular.ttf" ))?; Ok(GlyphBrushBuilder::using_font(inconsolata).build(gpu_device, render_format)) diff --git a/examples/gui/breakout/platform/src/gui.rs b/examples/gui/breakout/platform/src/gui.rs index 0330398126..e4af20f705 100644 --- a/examples/gui/breakout/platform/src/gui.rs +++ b/examples/gui/breakout/platform/src/gui.rs @@ -73,7 +73,9 @@ pub fn run_event_loop(title: &str, window_bounds: Bounds) -> Result<(), Box`. + See extra docs here: github.com/guibou/nixGL "#); adapter diff --git a/examples/gui/platform/src/graphics/primitives/text.rs b/examples/gui/platform/src/graphics/primitives/text.rs index 424c885004..c746063b77 100644 --- a/examples/gui/platform/src/graphics/primitives/text.rs +++ b/examples/gui/platform/src/graphics/primitives/text.rs @@ -130,7 +130,7 @@ pub fn build_glyph_brush( render_format: wgpu::TextureFormat, ) -> Result, InvalidFont> { let inconsolata = FontArc::try_from_slice(include_bytes!( - "../../../../../../crates/editor/Inconsolata-Regular.ttf" + "../../../../Inconsolata-Regular.ttf" ))?; Ok(GlyphBrushBuilder::using_font(inconsolata).build(gpu_device, render_format)) diff --git a/examples/gui/platform/src/gui.rs b/examples/gui/platform/src/gui.rs index c1ab9c8ce7..e054a1895b 100644 --- a/examples/gui/platform/src/gui.rs +++ b/examples/gui/platform/src/gui.rs @@ -56,7 +56,9 @@ fn run_event_loop(title: &str, root: RocElem) -> Result<(), Box> { }) .await .expect(r#"Request adapter - If you're running this from inside nix, follow the instructions here to resolve this: https://github.com/roc-lang/roc/blob/main/BUILDING_FROM_SOURCE.md#editor + If you're running this from inside nix, run with: + `nixVulkanIntel `. + See extra docs here: github.com/guibou/nixGL "#); adapter diff --git a/flake.nix b/flake.nix index 091c11bb1c..68800a6168 100644 --- a/flake.nix +++ b/flake.nix @@ -11,7 +11,7 @@ }; # to easily make configs for multiple architectures flake-utils.url = "github:numtide/flake-utils"; - # to be able to use vulkan system libs for editor graphics + # to be able to use vulkan system libs for graphics in examples/gui nixgl = { url = "github:guibou/nixGL"; inputs.nixpkgs.follows = "nixpkgs"; @@ -32,10 +32,11 @@ rust = pkgs.rust-bin.fromRustupToolchainFile "${cwd}/rust-toolchain.toml"; - linuxInputs = with pkgs; + # DevInputs are not necessary to build roc as a user + linuxDevInputs = with pkgs; lib.optionals stdenv.isLinux [ valgrind # used in cli tests, see cli/tests/cli_run.rs - vulkan-headers + vulkan-headers # here and below is all graphics stuff for examples/gui vulkan-loader vulkan-tools vulkan-validation-layers @@ -52,10 +53,16 @@ AppKit CoreFoundation CoreServices - CoreVideo Foundation - Metal Security + ]); + + # DevInputs are not necessary to build roc as a user + darwinDevInputs = with pkgs; + lib.optionals stdenv.isDarwin + (with pkgs.darwin.apple_sdk.frameworks; [ + CoreVideo # for examples/gui + Metal # for examples/gui curl # for wasm-bindgen-cli libcurl (see ./ci/www-repl.sh) ]); @@ -85,29 +92,30 @@ sharedInputs = (with pkgs; [ # build libraries cmake - git - python3 llvmPkgs.llvm.dev llvmPkgs.clang - libxkbcommon pkg-config zig_0_9 # roc builtins are implemented in zig, see compiler/builtins/bitcode/ - # lib deps libffi libxml2 ncurses zlib - libiconv - # faster builds - see https://github.com/roc-lang/roc/blob/main/BUILDING_FROM_SOURCE.md#use-lld-for-the-linker llvmPkgs.lld - debugir rust + ]); + + sharedDevInputs = (with pkgs; [ + git + python3 + libiconv # for examples/gui + libxkbcommon # for examples/gui + debugir # used in crates/compiler/build/src/program.rs cargo-criterion # for benchmarks simple-http-server # to view roc website when trying out edits wasm-pack # for repl_wasm - jq + jq # used in several bash scripts ]); aliases = '' @@ -119,7 +127,7 @@ in { devShell = pkgs.mkShell { - buildInputs = sharedInputs ++ darwinInputs ++ linuxInputs + buildInputs = sharedInputs ++ sharedDevInputs ++ darwinInputs ++ darwinDevInputs++ linuxDevInputs ++ (if system == "x86_64-linux" then [ pkgs.nixgl.nixVulkanIntel ] else @@ -138,7 +146,7 @@ LD_LIBRARY_PATH = with pkgs; lib.makeLibraryPath ([ pkg-config stdenv.cc.cc.lib libffi ncurses zlib ] - ++ linuxInputs); + ++ linuxDevInputs); NIXPKGS_ALLOW_UNFREE = 1; # to run the editor with NVIDIA's closed source drivers