diff --git a/Cargo.lock b/Cargo.lock index 100ca3cc5c..e11fcd579f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,10 +1,20 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] -name = "adler32" -version = "1.0.4" +name = "ab_glyph" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" +checksum = "7d4cab67fcb3051906d685028ad5e846bad471a6d468943c6cbcc498a8a33559" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b7e4e8cf778db814365e46839949ca74df4efb10e87ba4913e6ec5967ef0285" [[package]] name = "aho-corasick" @@ -59,12 +69,6 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d663a8e9a99154b5fb793032533f6328da35e23aac63d5c152279aa8ba356825" -[[package]] -name = "arrayref" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" - [[package]] name = "arrayvec" version = "0.5.1" @@ -80,6 +84,12 @@ dependencies = [ "libloading", ] +[[package]] +name = "atom" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c86699c3f02778ec07158376991c8f783dd1f2f95c579ffaf0738dc984b2fe2" + [[package]] name = "atty" version = "0.2.14" @@ -103,16 +113,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" -[[package]] -name = "bincode" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5753e2a71534719bf3f4e57006c3a4f0d2c672a4b676eec84161f763eca87dbf" -dependencies = [ - "byteorder", - "serde", -] - [[package]] name = "bitflags" version = "1.2.1" @@ -134,16 +134,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" -[[package]] -name = "block-buffer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" -dependencies = [ - "arrayref", - "byte-tools", -] - [[package]] name = "bstr" version = "0.2.12" @@ -162,18 +152,6 @@ version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187" -[[package]] -name = "byte-tools" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" - -[[package]] -name = "bytemuck" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37fa13df2292ecb479ec23aa06f4507928bef07839be9ef15281411076629431" - [[package]] name = "byteorder" version = "1.3.4" @@ -238,15 +216,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "cmake" -version = "0.1.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fb25b677f8bf1eb325017cb6bb8452f87969db0fedb4f757b297bee78a7c62" -dependencies = [ - "cc", -] - [[package]] name = "cocoa" version = "0.19.1" @@ -277,12 +246,6 @@ dependencies = [ "objc", ] -[[package]] -name = "color_quant" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dbbb57365263e881e805dc77d94697c9118fd94d8da011240555aa7b23445bd" - [[package]] name = "copyless" version = "0.1.4" @@ -358,15 +321,6 @@ dependencies = [ "objc", ] -[[package]] -name = "crc32fast" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" -dependencies = [ - "cfg-if", -] - [[package]] name = "criterion" version = "0.3.1" @@ -402,6 +356,16 @@ dependencies = [ "itertools", ] +[[package]] +name = "crossbeam-channel" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cced8691919c02aac3cb0a1bc2e9b73d89e832bf9a06fc579d4e71b68a2da061" +dependencies = [ + "crossbeam-utils", + "maybe-uninit", +] + [[package]] name = "crossbeam-deque" version = "0.7.3" @@ -482,31 +446,12 @@ dependencies = [ "winapi 0.3.8", ] -[[package]] -name = "deflate" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7e5d2a2273fed52a7f947ee55b092c4057025d7a3e04e5ecdbd25d6c3fb1bd7" -dependencies = [ - "adler32", - "byteorder", -] - [[package]] name = "difference" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" -[[package]] -name = "digest" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90" -dependencies = [ - "generic-array", -] - [[package]] name = "dispatch" version = "0.2.0" @@ -551,10 +496,17 @@ dependencies = [ ] [[package]] -name = "fake-simd" -version = "0.1.2" +name = "env_logger" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] [[package]] name = "fnv" @@ -599,6 +551,101 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +[[package]] +name = "futures" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" + +[[package]] +name = "futures-executor" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" + +[[package]] +name = "futures-macro" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" +dependencies = [ + "proc-macro-hack", + "proc-macro2 1.0.10", + "quote 1.0.3", + "syn 1.0.17", +] + +[[package]] +name = "futures-sink" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" + +[[package]] +name = "futures-task" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" +dependencies = [ + "once_cell", +] + +[[package]] +name = "futures-util" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + [[package]] name = "fxhash" version = "0.2.1" @@ -608,15 +655,6 @@ dependencies = [ "byteorder", ] -[[package]] -name = "generic-array" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" -dependencies = [ - "typenum", -] - [[package]] name = "getrandom" version = "0.1.14" @@ -636,7 +674,38 @@ checksum = "3b46e6f0031330a0be08d17820f2dcaaa91cb36710a97a9500cb4f1c36e785c8" dependencies = [ "fxhash", "gfx-hal", - "spirv_cross", + "spirv_cross 0.18.0", +] + +[[package]] +name = "gfx-auxil" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67bdbf8e8d6883c70e5a0d7379ad8ab3ac95127a3761306b36122d8f1c177a8e" +dependencies = [ + "fxhash", + "gfx-hal", + "spirv_cross 0.20.0", +] + +[[package]] +name = "gfx-backend-dx11" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92de0ddc0fde1a89b2a0e92dcc6bbb554bd34af0135e53a28d5ef064611094a4" +dependencies = [ + "bitflags", + "gfx-auxil 0.4.0", + "gfx-hal", + "libloading", + "log", + "parking_lot", + "range-alloc", + "raw-window-handle", + "smallvec", + "spirv_cross 0.20.0", + "winapi 0.3.8", + "wio", ] [[package]] @@ -647,16 +716,26 @@ checksum = "6facbfcdbb383b3cb7ea0709932ad1273e600a31a242255e80597297ce803dca" dependencies = [ "bitflags", "d3d12", - "gfx-auxil", + "gfx-auxil 0.3.0", "gfx-hal", "log", "range-alloc", "raw-window-handle", "smallvec", - "spirv_cross", + "spirv_cross 0.18.0", "winapi 0.3.8", ] +[[package]] +name = "gfx-backend-empty" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67bd2d7bc022b257ddbdabc5fa3b10c29c292372c3409f2b6a6e3f4e11cdb85" +dependencies = [ + "gfx-hal", + "raw-window-handle", +] + [[package]] name = "gfx-backend-metal" version = "0.5.1" @@ -670,7 +749,7 @@ dependencies = [ "copyless", "core-graphics 0.19.0", "foreign-types", - "gfx-auxil", + "gfx-auxil 0.3.0", "gfx-hal", "lazy_static", "log", @@ -680,7 +759,7 @@ dependencies = [ "range-alloc", "raw-window-handle", "smallvec", - "spirv_cross", + "spirv_cross 0.18.0", "storage-map", ] @@ -704,6 +783,17 @@ dependencies = [ "x11", ] +[[package]] +name = "gfx-descriptor" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf35f5d66d1bc56e63e68d7528441453f25992bd954b84309d23c659df2c5da" +dependencies = [ + "fxhash", + "gfx-hal", + "log", +] + [[package]] name = "gfx-hal" version = "0.5.0" @@ -715,24 +805,55 @@ dependencies = [ ] [[package]] -name = "gif" -version = "0.10.3" +name = "gfx-memory" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "471d90201b3b223f3451cd4ad53e34295f16a1df17b1edf3736d47761c3981af" +checksum = "c2eed6cda674d9cd4d92229102dbd544292124533d236904f987e9afab456137" dependencies = [ - "color_quant", - "lzw", + "fxhash", + "gfx-hal", + "hibitset", + "log", + "slab", ] [[package]] -name = "glsl-to-spirv" -version = "0.1.7" +name = "glyph_brush" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28caebc98746d507603a2d3df66dcbe04e41d4febad0320f3eec1ef72b6bbef1" +checksum = "afd3e2cfd503a5218dd56172a8bf7c8655a4a7cf745737c606a6edfeea1b343f" dependencies = [ - "cmake", - "sha2", - "tempfile", + "glyph_brush_draw_cache", + "glyph_brush_layout", + "log", + "ordered-float", + "rustc-hash", + "twox-hash", +] + +[[package]] +name = "glyph_brush_draw_cache" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "588662439c80d2784e6bb1274e2a6378ffcc74f8006b02e67e905039e200764d" +dependencies = [ + "ab_glyph", + "crossbeam-channel", + "crossbeam-deque", + "linked-hash-map", + "rayon", + "rustc-hash", +] + +[[package]] +name = "glyph_brush_layout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa49abf7dcf7bfe68f42c1c8ab7473505aaba14de84afb8899a0109b6c61717" +dependencies = [ + "ab_glyph", + "approx", + "xi-unicode", ] [[package]] @@ -744,6 +865,24 @@ dependencies = [ "libc", ] +[[package]] +name = "hibitset" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93a1bb8316a44459a7d14253c4d28dd7395cbd23cc04a68c46e851b8e46d64b1" +dependencies = [ + "atom", +] + +[[package]] +name = "humantime" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +dependencies = [ + "quick-error", +] + [[package]] name = "im" version = "14.3.0" @@ -772,24 +911,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "image" -version = "0.23.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f4167a8f21fa2bb3f17a652a760acd7572645281c98e3b612a26242c96ee" -dependencies = [ - "bytemuck", - "byteorder", - "gif", - "jpeg-decoder", - "num-iter", - "num-rational", - "num-traits", - "png", - "scoped_threadpool", - "tiff", -] - [[package]] name = "indoc" version = "0.3.5" @@ -813,15 +934,6 @@ dependencies = [ "unindent", ] -[[package]] -name = "inflate" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" -dependencies = [ - "adler32", -] - [[package]] name = "inkwell" version = "0.1.0" @@ -882,16 +994,6 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" -[[package]] -name = "jpeg-decoder" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b47b4c4e017b01abdc5bcc126d2d1002e5a75bbe3ce73f9f4f311a916363704" -dependencies = [ - "byteorder", - "rayon", -] - [[package]] name = "js-sys" version = "0.3.37" @@ -948,6 +1050,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "linked-hash-map" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" + [[package]] name = "llvm-sys" version = "100.0.1" @@ -979,12 +1087,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "lzw" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084" - [[package]] name = "malloc_buf" version = "0.0.6" @@ -1046,15 +1148,6 @@ dependencies = [ "objc", ] -[[package]] -name = "miniz_oxide" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa679ff6578b1cddee93d7e82e263b94a575e0bfced07284eb0c037c1d2416a5" -dependencies = [ - "adler32", -] - [[package]] name = "mio" version = "0.6.21" @@ -1155,38 +1248,6 @@ dependencies = [ "void", ] -[[package]] -name = "num-integer" -version = "0.1.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" -dependencies = [ - "autocfg 1.0.0", - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb0800a0291891dd9f4fe7bd9c19384f98f7fbe0cd0f39a2c6b88b9868bbc00" -dependencies = [ - "autocfg 1.0.0", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" -dependencies = [ - "autocfg 1.0.0", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.11" @@ -1246,6 +1307,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "owned_ttf_parser" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f923fb806c46266c02ab4a5b239735c144bdeda724a50ed058e5226f594cde3" +dependencies = [ + "ttf-parser", +] + [[package]] name = "parking_lot" version = "0.10.2" @@ -1270,18 +1340,66 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "peek-poke" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93fd6a575ebf1ac2668d08443c97a22872cfb463fd8b7ddd141e9f6be59af2f" +dependencies = [ + "peek-poke-derive", +] + +[[package]] +name = "peek-poke-derive" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb44a25c5bba983be0fc8592dfaf3e6d0935ce8be0c6b15b2a39507af34a926" +dependencies = [ + "proc-macro2 1.0.10", + "quote 1.0.3", + "syn 1.0.17", + "synstructure", + "unicode-xid 0.2.0", +] + [[package]] name = "percent-encoding" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "pin-project" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b044170ce52ac41b78bdf855a045f6fe6ba72c293a33a2e3f654642127680563" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "babd76ce3c0d7c677fd01a3c3f9be24fa760adb73fd6db48f151662c1ec7eaba" +dependencies = [ + "proc-macro2 1.0.10", + "quote 1.0.3", + "syn 1.0.17", +] + [[package]] name = "pin-project-lite" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "pkg-config" version = "0.3.17" @@ -1300,18 +1418,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "png" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c68a431ed29933a4eb5709aca9800989758c97759345860fa5db3cfced0b65d" -dependencies = [ - "bitflags", - "crc32fast", - "deflate", - "inflate", -] - [[package]] name = "ppv-lite86" version = "0.2.6" @@ -1334,6 +1440,12 @@ version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63" +[[package]] +name = "proc-macro-nested" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0afe1bd463b9e9ed51d0e0f0b50b6b146aec855c56fd182bb242388710a9b6de" + [[package]] name = "proc-macro2" version = "0.4.30" @@ -1352,13 +1464,19 @@ dependencies = [ "unicode-xid 0.2.0", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quickcheck" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c35d9c36a562f37eca96e79f66d5fd56eefbc22560dacc4a864cabd2d277456" dependencies = [ - "env_logger", + "env_logger 0.6.2", "log", "rand 0.6.5", "rand_core 0.4.2", @@ -1748,19 +1866,16 @@ dependencies = [ name = "roc_editor" version = "0.1.0" dependencies = [ - "bincode", "bumpalo", - "gfx-backend-dx12", - "gfx-backend-metal", - "gfx-backend-vulkan", - "gfx-hal", - "glsl-to-spirv", + "env_logger 0.7.1", + "futures", + "glyph_brush", "im", "im-rc", - "image", "indoc", "inkwell", "inlinable_string", + "log", "maplit", "pretty_assertions", "quickcheck", @@ -1781,10 +1896,12 @@ dependencies = [ "roc_types", "roc_unify", "roc_uniq", - "serde", "target-lexicon", "tokio", + "wgpu", + "wgpu_glyph", "winit", + "zerocopy", ] [[package]] @@ -2045,6 +2162,12 @@ dependencies = [ "roc_types", ] +[[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.2.3" @@ -2089,12 +2212,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scoped_threadpool" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" - [[package]] name = "scopeguard" version = "1.1.0" @@ -2121,9 +2238,6 @@ name = "serde" version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" -dependencies = [ - "serde_derive", -] [[package]] name = "serde_derive" @@ -2147,18 +2261,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sha2" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0" -dependencies = [ - "block-buffer", - "byte-tools", - "digest", - "fake-simd", -] - [[package]] name = "signal-hook-registry" version = "1.2.0" @@ -2230,6 +2332,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "spirv_cross" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33a9478e9c78782dd694d05dee074703a9c4c74b511de742b88a7e8149f1b37" +dependencies = [ + "cc", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "stb_truetype" version = "0.3.1" @@ -2270,6 +2383,18 @@ dependencies = [ "unicode-xid 0.2.0", ] +[[package]] +name = "synstructure" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" +dependencies = [ + "proc-macro2 1.0.10", + "quote 1.0.3", + "syn 1.0.17", + "unicode-xid 0.2.0", +] + [[package]] name = "target-lexicon" version = "0.10.0" @@ -2317,17 +2442,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "tiff" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "002351e428db1eb1d8656d4ca61947c3519ac3191e1c804d4600cd32093b77ad" -dependencies = [ - "byteorder", - "lzw", - "miniz_oxide", -] - [[package]] name = "tinytemplate" version = "1.0.3" @@ -2358,6 +2472,21 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "ttf-parser" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52fbe7769f5af5d7d25aea74b9443b64e544a5ffb4d2b2968295ddea934f1a06" + +[[package]] +name = "twox-hash" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bfd5b7557925ce778ff9b9ef90e3ade34c524b5ff10e239c69a42d546d2af56" +dependencies = [ + "rand 0.7.3", +] + [[package]] name = "typed-arena" version = "2.0.1" @@ -2394,6 +2523,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63f18aa3b0e35fed5a0048f029558b1518095ffe2a0a31fb87c93dece93a4993" +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "ven_ena" version = "0.13.1" @@ -2573,6 +2708,85 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "wgpu" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf715eb8571da470b856ecc67b057221360d9fce16f3e38001b2fb158d04012" +dependencies = [ + "arrayvec", + "parking_lot", + "raw-window-handle", + "smallvec", + "wgpu-core", + "wgpu-native", + "wgpu-types", +] + +[[package]] +name = "wgpu-core" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b093098e0782b0f46f154fac5c670ba27b7a5bff98dc943422c13852c696a2b0" +dependencies = [ + "arrayvec", + "bitflags", + "copyless", + "fxhash", + "gfx-backend-dx11", + "gfx-backend-dx12", + "gfx-backend-empty", + "gfx-backend-metal", + "gfx-backend-vulkan", + "gfx-descriptor", + "gfx-hal", + "gfx-memory", + "log", + "parking_lot", + "peek-poke", + "smallvec", + "vec_map", + "wgpu-types", +] + +[[package]] +name = "wgpu-native" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19a5051a357d071fd69c24671e0ea6d644a83c7418e47eac3511427379007403" +dependencies = [ + "arrayvec", + "lazy_static", + "libc", + "objc", + "parking_lot", + "raw-window-handle", + "wgpu-core", + "wgpu-types", +] + +[[package]] +name = "wgpu-types" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3474b5ce2ed628e158c2fe4387a469b2ee119604556aa2debd10d830cedc3bc" +dependencies = [ + "bitflags", + "peek-poke", +] + +[[package]] +name = "wgpu_glyph" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fe5558ee779dfad0d53d444be128c539cf8a6e7cad2e7ad9df6a6a28ff5a483" +dependencies = [ + "glyph_brush", + "log", + "wgpu", + "zerocopy", +] + [[package]] name = "winapi" version = "0.2.8" @@ -2645,6 +2859,15 @@ dependencies = [ "x11-dl", ] +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi 0.3.8", +] + [[package]] name = "ws2_32-sys" version = "0.2.1" @@ -2692,8 +2915,35 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" +[[package]] +name = "xi-unicode" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7395cdb9d0a6219fa0ea77d08c946adf9c1984c72fcd443ace30365f3daadef7" + [[package]] name = "xml-rs" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a" + +[[package]] +name = "zerocopy" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6580539ad917b7c026220c4b3f2c08d52ce54d6ce0dc491e66002e35388fab46" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d498dbd1fd7beb83c86709ae1c33ca50942889473473d287d56ce4770a18edfb" +dependencies = [ + "proc-macro2 1.0.10", + "syn 1.0.17", + "synstructure", +] diff --git a/cli/Cargo.toml b/cli/Cargo.toml index a545ff8e7d..c11cb29fe0 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -54,7 +54,7 @@ clap = { git = "https://github.com/rtfeldman/clap", branch = "master" } im = "14" # im and im-rc should always have the same version! im-rc = "14" # im and im-rc should always have the same version! bumpalo = { version = "3.2", features = ["collections"] } -inlinable_string = "0.1.0" +inlinable_string = "0.1" tokio = { version = "0.2", features = ["blocking", "fs", "sync", "rt-threaded", "process", "io-driver"] } # NOTE: rtfeldman/inkwell is a fork of TheDan64/inkwell which does not change anything. # diff --git a/compiler/builtins/src/std.rs b/compiler/builtins/src/std.rs index d7503fc605..db3edf7173 100644 --- a/compiler/builtins/src/std.rs +++ b/compiler/builtins/src/std.rs @@ -331,6 +331,18 @@ pub fn types() -> MutMap { // Float module + // isGt or (>) : Num a, Num a -> Bool + add_type( + Symbol::FLOAT_GT, + SolvedType::Func(vec![float_type(), float_type()], Box::new(bool_type())), + ); + + // eq or (==) : Num a, Num a -> Bool + add_type( + Symbol::FLOAT_EQ, + SolvedType::Func(vec![float_type(), float_type()], Box::new(bool_type())), + ); + // div : Float, Float -> Float add_type( Symbol::FLOAT_DIV, @@ -361,6 +373,24 @@ pub fn types() -> MutMap { SolvedType::Func(vec![float_type()], Box::new(float_type())), ); + // sin : Float -> Float + add_type( + Symbol::FLOAT_SIN, + SolvedType::Func(vec![float_type()], Box::new(float_type())), + ); + + // cos : Float -> Float + add_type( + Symbol::FLOAT_COS, + SolvedType::Func(vec![float_type()], Box::new(float_type())), + ); + + // tan : Float -> Float + add_type( + Symbol::FLOAT_TAN, + SolvedType::Func(vec![float_type()], Box::new(float_type())), + ); + // highest : Float add_type(Symbol::FLOAT_HIGHEST, float_type()); @@ -477,6 +507,12 @@ pub fn types() -> MutMap { ), ); + // single : a -> List a + add_type( + Symbol::LIST_SINGLE, + SolvedType::Func(vec![flex(TVAR1)], Box::new(list_type(flex(TVAR1)))), + ); + // len : List * -> Int add_type( Symbol::LIST_LEN, diff --git a/compiler/builtins/src/unique.rs b/compiler/builtins/src/unique.rs index 5521db13d5..546747f06a 100644 --- a/compiler/builtins/src/unique.rs +++ b/compiler/builtins/src/unique.rs @@ -415,6 +415,18 @@ pub fn types() -> MutMap { // Float module + // isGt or (>) : Num a, Num a -> Bool + add_type( + Symbol::FLOAT_GT, + unique_function(vec![float_type(UVAR1), float_type(UVAR2)], bool_type(UVAR3)), + ); + + // eq or (==) : Num a, Num a -> Bool + add_type( + Symbol::FLOAT_EQ, + unique_function(vec![float_type(UVAR1), float_type(UVAR2)], bool_type(UVAR3)), + ); + // div : Float, Float -> Float add_type( Symbol::FLOAT_DIV, @@ -451,6 +463,24 @@ pub fn types() -> MutMap { unique_function(vec![float_type(UVAR1)], float_type(UVAR2)), ); + // sin : Float -> Float + add_type( + Symbol::FLOAT_SIN, + unique_function(vec![float_type(UVAR1)], float_type(UVAR2)), + ); + + // cos : Float -> Float + add_type( + Symbol::FLOAT_COS, + unique_function(vec![float_type(UVAR1)], float_type(UVAR2)), + ); + + // tan : Float -> Float + add_type( + Symbol::FLOAT_TAN, + unique_function(vec![float_type(UVAR1)], float_type(UVAR2)), + ); + // highest : Float add_type(Symbol::FLOAT_HIGHEST, float_type(UVAR1)); @@ -563,6 +593,28 @@ pub fn types() -> MutMap { ) }); + add_type(Symbol::LIST_SINGLE, { + let u = UVAR1; + let v = UVAR2; + let star = UVAR4; + + let a = TVAR1; + + unique_function( + vec![SolvedType::Apply( + Symbol::ATTR_ATTR, + vec![disjunction(u, vec![v]), flex(a)], + )], + SolvedType::Apply( + Symbol::ATTR_ATTR, + vec![ + boolean(star), + SolvedType::Apply(Symbol::LIST_LIST, vec![attr_type(u, a)]), + ], + ), + ) + }); + // push : Attr (w | u | v) (List (Attr u a)) // , Attr (u | v) a // -> Attr * (List (Attr u a)) diff --git a/compiler/can/Cargo.toml b/compiler/can/Cargo.toml index b652c82b45..4d5b7782bb 100644 --- a/compiler/can/Cargo.toml +++ b/compiler/can/Cargo.toml @@ -16,7 +16,7 @@ ven_graph = { path = "../../vendor/pathfinding" } im = "14" # im and im-rc should always have the same version! im-rc = "14" # im and im-rc should always have the same version! bumpalo = { version = "3.2", features = ["collections"] } -inlinable_string = "0.1.0" +inlinable_string = "0.1" [dev-dependencies] pretty_assertions = "0.5.1" diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index 181d3eaf4e..278885a785 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -1,6 +1,5 @@ -use crate::def::Def; use crate::expr::{Expr, Recursive}; -use roc_collections::all::{MutMap, SendMap}; +use roc_collections::all::MutMap; use roc_module::ident::TagName; use roc_module::low_level::LowLevel; use roc_module::operator::CalledVia; @@ -25,7 +24,7 @@ use roc_types::subs::{VarStore, Variable}; /// delegates to the compiler-internal List.getUnsafe function to do the actual /// lookup (if the bounds check passed). That internal function is hardcoded in code gen, /// which works fine because it doesn't involve any open tag unions. -pub fn builtin_defs(var_store: &VarStore) -> MutMap { +pub fn builtin_defs(var_store: &VarStore) -> MutMap { mut_map! { Symbol::LIST_LEN => list_len(var_store), Symbol::LIST_GET => list_get(var_store), @@ -34,12 +33,151 @@ pub fn builtin_defs(var_store: &VarStore) -> MutMap { Symbol::INT_ABS => int_abs(var_store), Symbol::INT_REM => int_rem(var_store), Symbol::INT_IS_ODD => int_is_odd(var_store), - Symbol::INT_IS_EVEN => int_is_even(var_store) + Symbol::INT_IS_EVEN => int_is_even(var_store), + Symbol::INT_IS_ZERO => int_is_zero(var_store), + Symbol::INT_IS_POSITIVE => int_is_positive(var_store), + Symbol::INT_IS_NEGATIVE => int_is_negative(var_store), + Symbol::FLOAT_IS_POSITIVE => float_is_positive(var_store), + Symbol::FLOAT_IS_NEGATIVE => float_is_negative(var_store), + Symbol::FLOAT_IS_ZERO => float_is_zero(var_store), + Symbol::FLOAT_TAN => float_tan(var_store), } } +/// Float.tan : Float -> Float +fn float_tan(var_store: &VarStore) -> Expr { + use crate::expr::Expr::*; + + defn( + Symbol::FLOAT_TAN, + vec![Symbol::FLOAT_TAN_ARG], + var_store, + call( + Symbol::FLOAT_DIV, + vec![ + call( + Symbol::FLOAT_SIN, + vec![Var(Symbol::FLOAT_TAN_ARG)], + var_store, + ), + call( + Symbol::FLOAT_COS, + vec![Var(Symbol::FLOAT_TAN_ARG)], + var_store, + ), + ], + var_store, + ), + ) +} + +/// Float.isZero : Float -> Bool +fn float_is_zero(var_store: &VarStore) -> Expr { + use crate::expr::Expr::*; + + defn( + Symbol::FLOAT_IS_ZERO, + vec![Symbol::FLOAT_IS_ZERO_ARG], + var_store, + call( + Symbol::FLOAT_EQ, + vec![ + Float(var_store.fresh(), 0.0), + Var(Symbol::FLOAT_IS_ZERO_ARG), + ], + var_store, + ), + ) +} + +/// Float.isNegative : Float -> Bool +fn float_is_negative(var_store: &VarStore) -> Expr { + use crate::expr::Expr::*; + + defn( + Symbol::FLOAT_IS_NEGATIVE, + vec![Symbol::FLOAT_IS_NEGATIVE_ARG], + var_store, + call( + Symbol::FLOAT_GT, + vec![ + Float(var_store.fresh(), 0.0), + Var(Symbol::FLOAT_IS_NEGATIVE_ARG), + ], + var_store, + ), + ) +} + +/// Float.isPositive : Float -> Bool +fn float_is_positive(var_store: &VarStore) -> Expr { + use crate::expr::Expr::*; + + defn( + Symbol::FLOAT_IS_POSITIVE, + vec![Symbol::FLOAT_IS_POSITIVE_ARG], + var_store, + call( + Symbol::FLOAT_GT, + vec![ + Var(Symbol::FLOAT_IS_POSITIVE_ARG), + Float(var_store.fresh(), 0.0), + ], + var_store, + ), + ) +} + +/// Int.isNegative : Int -> Bool +fn int_is_negative(var_store: &VarStore) -> Expr { + use crate::expr::Expr::*; + + defn( + Symbol::INT_IS_NEGATIVE, + vec![Symbol::INT_IS_NEGATIVE_ARG], + var_store, + call( + Symbol::NUM_LT, + vec![Var(Symbol::INT_IS_NEGATIVE_ARG), Int(var_store.fresh(), 0)], + var_store, + ), + ) +} + +/// Int.isPositive : Int -> Bool +fn int_is_positive(var_store: &VarStore) -> Expr { + use crate::expr::Expr::*; + + defn( + Symbol::INT_IS_POSITIVE, + vec![Symbol::INT_IS_POSITIVE_ARG], + var_store, + call( + Symbol::NUM_GT, + vec![Var(Symbol::INT_IS_POSITIVE_ARG), Int(var_store.fresh(), 0)], + var_store, + ), + ) +} + +/// Int.isZero : Int -> Bool +fn int_is_zero(var_store: &VarStore) -> Expr { + use crate::expr::Expr::*; + + defn( + Symbol::INT_IS_ZERO, + vec![Symbol::INT_IS_ZERO_ARG], + var_store, + call( + Symbol::INT_EQ_I64, + vec![Var(Symbol::INT_IS_ZERO_ARG), Int(var_store.fresh(), 0)], + var_store, + ), + ) +} + /// Int.isOdd : Int -> Bool -fn int_is_odd(var_store: &VarStore) -> Def { +fn int_is_odd(var_store: &VarStore) -> Expr { use crate::expr::Expr::*; defn( @@ -62,7 +200,7 @@ fn int_is_odd(var_store: &VarStore) -> Def { } /// Int.isEven : Int -> Bool -fn int_is_even(var_store: &VarStore) -> Def { +fn int_is_even(var_store: &VarStore) -> Expr { use crate::expr::Expr::*; defn( @@ -85,7 +223,7 @@ fn int_is_even(var_store: &VarStore) -> Def { } /// List.len : List * -> Int -fn list_len(var_store: &VarStore) -> Def { +fn list_len(var_store: &VarStore) -> Expr { use crate::expr::Expr::*; // Polymorphic wrapper around LowLevel::ListLen @@ -100,7 +238,7 @@ fn list_len(var_store: &VarStore) -> Def { } /// List.get : List elem, Int -> Result elem [ OutOfBounds ]* -fn list_get(var_store: &VarStore) -> Def { +fn list_get(var_store: &VarStore) -> Expr { use crate::expr::Expr::*; defn( @@ -169,7 +307,7 @@ fn list_get(var_store: &VarStore) -> Def { } /// Int.rem : Int, Int -> Int -fn int_rem(var_store: &VarStore) -> Def { +fn int_rem(var_store: &VarStore) -> Expr { use crate::expr::Expr::*; defn( @@ -216,7 +354,7 @@ fn int_rem(var_store: &VarStore) -> Def { } /// Int.abs : Int -> Int -fn int_abs(var_store: &VarStore) -> Def { +fn int_abs(var_store: &VarStore) -> Expr { use crate::expr::Expr::*; defn( @@ -253,7 +391,7 @@ fn int_abs(var_store: &VarStore) -> Def { } /// Int.div : Int, Int -> Result Int [ DivByZero ]* -fn int_div(var_store: &VarStore) -> Def { +fn int_div(var_store: &VarStore) -> Expr { use crate::expr::Expr::*; defn( @@ -312,7 +450,7 @@ fn int_div(var_store: &VarStore) -> Def { } /// List.first : List elem -> Result elem [ ListWasEmpty ]* -fn list_first(var_store: &VarStore) -> Def { +fn list_first(var_store: &VarStore) -> Expr { use crate::expr::Expr::*; defn( @@ -404,7 +542,7 @@ fn call(symbol: Symbol, args: Vec, var_store: &VarStore) -> Expr { } #[inline(always)] -fn defn(fn_name: Symbol, args: Vec, var_store: &VarStore, body: Expr) -> Def { +fn defn(fn_name: Symbol, args: Vec, var_store: &VarStore, body: Expr) -> Expr { use crate::expr::Expr::*; use crate::pattern::Pattern::*; @@ -413,19 +551,11 @@ fn defn(fn_name: Symbol, args: Vec, var_store: &VarStore, body: Expr) -> .map(|symbol| (var_store.fresh(), no_region(Identifier(symbol)))) .collect(); - let expr = Closure( + Closure( var_store.fresh(), fn_name, Recursive::NotRecursive, closure_args, Box::new((no_region(body), var_store.fresh())), - ); - - Def { - loc_pattern: no_region(Identifier(fn_name)), - loc_expr: no_region(expr), - expr_var: var_store.fresh(), - pattern_vars: SendMap::default(), - annotation: None, - } + ) } diff --git a/compiler/can/src/def.rs b/compiler/can/src/def.rs index 2cd85482f7..6579137692 100644 --- a/compiler/can/src/def.rs +++ b/compiler/can/src/def.rs @@ -360,8 +360,8 @@ pub fn sort_can_defs( let mut defined_symbols_set: ImSet = ImSet::default(); for symbol in can_defs_by_symbol.keys().into_iter() { - defined_symbols.push(symbol.clone()); - defined_symbols_set.insert(symbol.clone()); + defined_symbols.push(*symbol); + defined_symbols_set.insert(*symbol); } // Use topological sort to reorder the defs based on their dependencies to one another. @@ -690,7 +690,7 @@ fn pattern_to_vars_by_symbol( use Pattern::*; match pattern { Identifier(symbol) => { - vars_by_symbol.insert(symbol.clone(), expr_var); + vars_by_symbol.insert(*symbol, expr_var); } AppliedTag { arguments, .. } => { @@ -701,7 +701,7 @@ fn pattern_to_vars_by_symbol( RecordDestructure { destructs, .. } => { for destruct in destructs { - vars_by_symbol.insert(destruct.value.symbol.clone(), destruct.value.var); + vars_by_symbol.insert(destruct.value.symbol, destruct.value.var); } } @@ -949,7 +949,7 @@ fn canonicalize_pending_def<'a>( ) = ( &loc_pattern.value, &loc_can_pattern.value, - &loc_can_expr.value.clone(), + &loc_can_expr.value, ) { is_closure = true; @@ -966,7 +966,7 @@ fn canonicalize_pending_def<'a>( // closures don't have a name, and therefore pick a fresh symbol. But in this // case, the closure has a proper name (e.g. `foo` in `foo = \x y -> ...` // and we want to reference it by that name. - env.closures.insert(defined_symbol.clone(), references); + env.closures.insert(*defined_symbol, references); // The closure is self tail recursive iff it tail calls itself (by defined name). let is_recursive = match can_output.tail_call { @@ -977,7 +977,7 @@ fn canonicalize_pending_def<'a>( // Recursion doesn't count as referencing. (If it did, all recursive functions // would result in circular def errors!) refs_by_symbol - .entry(defined_symbol.clone()) + .entry(*defined_symbol) .and_modify(|(_, refs)| { refs.lookups = refs.lookups.without(defined_symbol); }); @@ -1010,7 +1010,7 @@ fn canonicalize_pending_def<'a>( }; refs_by_symbol.insert( - symbol.clone(), + *symbol, ( Located { value: ident.clone(), @@ -1057,7 +1057,7 @@ fn canonicalize_pending_def<'a>( env.tailcallable_symbol = Some(*defined_symbol); // TODO isn't types_by_symbol enough? Do we need vars_by_symbol too? - vars_by_symbol.insert(defined_symbol.clone(), expr_var); + vars_by_symbol.insert(*defined_symbol, expr_var); }; let (mut loc_can_expr, can_output) = @@ -1082,7 +1082,7 @@ fn canonicalize_pending_def<'a>( ) = ( &loc_pattern.value, &loc_can_pattern.value, - &loc_can_expr.value.clone(), + &loc_can_expr.value, ) { is_closure = true; @@ -1099,7 +1099,7 @@ fn canonicalize_pending_def<'a>( // closures don't have a name, and therefore pick a fresh symbol. But in this // case, the closure has a proper name (e.g. `foo` in `foo = \x y -> ...` // and we want to reference it by that name. - env.closures.insert(defined_symbol.clone(), references); + env.closures.insert(*defined_symbol, references); // The closure is self tail recursive iff it tail calls itself (by defined name). let is_recursive = match can_output.tail_call { @@ -1110,7 +1110,7 @@ fn canonicalize_pending_def<'a>( // Recursion doesn't count as referencing. (If it did, all recursive functions // would result in circular def errors!) refs_by_symbol - .entry(defined_symbol.clone()) + .entry(*defined_symbol) .and_modify(|(_, refs)| { refs.lookups = refs.lookups.without(defined_symbol); }); @@ -1147,7 +1147,7 @@ fn canonicalize_pending_def<'a>( }); refs_by_symbol.insert( - symbol.clone(), + symbol, ( Located { value: ident.clone().into(), @@ -1265,7 +1265,7 @@ fn closure_recursivity(symbol: Symbol, closures: &MutMap) -> if let Some(references) = closures.get(&symbol) { for v in &references.calls { - stack.push(v.clone()); + stack.push(*v); } // while there are symbols left to visit @@ -1281,7 +1281,7 @@ fn closure_recursivity(symbol: Symbol, closures: &MutMap) -> if let Some(nested_references) = closures.get(&nested_symbol) { // add its called to the stack for v in &nested_references.calls { - stack.push(v.clone()); + stack.push(*v); } } visited.insert(nested_symbol); diff --git a/compiler/can/src/expr.rs b/compiler/can/src/expr.rs index 70c10d8c73..6fa2deac34 100644 --- a/compiler/can/src/expr.rs +++ b/compiler/can/src/expr.rs @@ -452,7 +452,7 @@ pub fn canonicalize_expr<'a>( } } - env.register_closure(symbol.clone(), output.references.clone()); + env.register_closure(symbol, output.references.clone()); ( Closure( @@ -820,7 +820,7 @@ where answer = answer.union(other_refs); } - answer.lookups.insert(local.clone()); + answer.lookups.insert(*local); } for call in refs.calls.iter() { @@ -830,7 +830,7 @@ where answer = answer.union(other_refs); } - answer.calls.insert(call.clone()); + answer.calls.insert(*call); } answer @@ -862,7 +862,7 @@ where answer = answer.union(other_refs); } - answer.lookups.insert(closed_over_local.clone()); + answer.lookups.insert(*closed_over_local); } for call in references.calls.iter() { diff --git a/compiler/can/src/pattern.rs b/compiler/can/src/pattern.rs index d068ce19cd..7911f2a3b8 100644 --- a/compiler/can/src/pattern.rs +++ b/compiler/can/src/pattern.rs @@ -57,7 +57,7 @@ pub fn symbols_from_pattern_help(pattern: &Pattern, symbols: &mut Vec) { match pattern { Identifier(symbol) => { - symbols.push(symbol.clone()); + symbols.push(*symbol); } AppliedTag { arguments, .. } => { @@ -67,7 +67,7 @@ pub fn symbols_from_pattern_help(pattern: &Pattern, symbols: &mut Vec) { } RecordDestructure { destructs, .. } => { for destruct in destructs { - symbols.push(destruct.value.symbol.clone()); + symbols.push(destruct.value.symbol); } } diff --git a/compiler/constrain/src/pattern.rs b/compiler/constrain/src/pattern.rs index 332104561e..2340175905 100644 --- a/compiler/constrain/src/pattern.rs +++ b/compiler/constrain/src/pattern.rs @@ -47,7 +47,7 @@ fn headers_from_annotation_help( ) -> bool { match pattern { Identifier(symbol) => { - headers.insert(symbol.clone(), annotation.clone()); + headers.insert(*symbol, annotation.clone()); true } Underscore @@ -64,7 +64,7 @@ fn headers_from_annotation_help( // NOTE ignores the .guard field. if let Some(field_type) = fields.get(&destruct.value.label) { headers.insert( - destruct.value.symbol.clone(), + destruct.value.symbol, Located::at(annotation.region, field_type.clone()), ); } else { @@ -123,7 +123,7 @@ pub fn constrain_pattern( Identifier(symbol) => { state.headers.insert( - symbol.clone(), + *symbol, Located { region, value: expected.get_type(), @@ -197,7 +197,7 @@ pub fn constrain_pattern( if !state.headers.contains_key(&symbol) { state .headers - .insert(symbol.clone(), Located::at(region, pat_type.clone())); + .insert(*symbol, Located::at(region, pat_type.clone())); } field_types.insert(label.clone(), pat_type.clone()); diff --git a/compiler/constrain/src/uniq.rs b/compiler/constrain/src/uniq.rs index 9496cd2700..8f2cd05384 100644 --- a/compiler/constrain/src/uniq.rs +++ b/compiler/constrain/src/uniq.rs @@ -148,7 +148,7 @@ fn constrain_pattern( match &pattern.value { Identifier(symbol) => { state.headers.insert( - symbol.clone(), + *symbol, Located { region: pattern.region, value: expected.get_type(), @@ -223,10 +223,9 @@ fn constrain_pattern( let expected = PExpected::NoExpectation(pat_type.clone()); if !state.headers.contains_key(&symbol) { - state.headers.insert( - symbol.clone(), - Located::at(pattern.region, pat_type.clone()), - ); + state + .headers + .insert(*symbol, Located::at(pattern.region, pat_type.clone())); } field_types.insert(label.clone(), pat_type.clone()); @@ -1396,7 +1395,7 @@ fn constrain_var( Lookup(symbol_for_lookup, expected, region) } Some(Usage::Access(_, _, _)) | Some(Usage::Update(_, _, _)) => { - applied_usage_constraint.insert(symbol_for_lookup.clone()); + applied_usage_constraint.insert(symbol_for_lookup); let mut variables = Vec::new(); let (free, rest, inner_type) = diff --git a/compiler/fmt/Cargo.toml b/compiler/fmt/Cargo.toml index 9193d0f948..59dce02d63 100644 --- a/compiler/fmt/Cargo.toml +++ b/compiler/fmt/Cargo.toml @@ -15,7 +15,7 @@ roc_types = { path = "../types" } im = "14" # im and im-rc should always have the same version! im-rc = "14" # im and im-rc should always have the same version! bumpalo = { version = "3.2", features = ["collections"] } -inlinable_string = "0.1.0" +inlinable_string = "0.1" [dev-dependencies] pretty_assertions = "0.5.1" diff --git a/compiler/gen/Cargo.toml b/compiler/gen/Cargo.toml index 515280d995..095894228d 100644 --- a/compiler/gen/Cargo.toml +++ b/compiler/gen/Cargo.toml @@ -20,7 +20,7 @@ roc_mono = { path = "../mono" } im = "14" # im and im-rc should always have the same version! im-rc = "14" # im and im-rc should always have the same version! bumpalo = { version = "3.2", features = ["collections"] } -inlinable_string = "0.1.0" +inlinable_string = "0.1" # NOTE: rtfeldman/inkwell is a fork of TheDan64/inkwell which does not change anything. # # The reason for this fork is that the way Inkwell is designed, you have to use diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index 4fca260b16..f7e957f6e4 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -88,11 +88,25 @@ fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) { LLVM_FABS_F64, f64_type.fn_type(&[f64_type.into()], false), ); + + add_intrinsic( + module, + LLVM_SIN_F64, + f64_type.fn_type(&[f64_type.into()], false), + ); + + add_intrinsic( + module, + LLVM_COS_F64, + f64_type.fn_type(&[f64_type.into()], false), + ); } static LLVM_SQRT_F64: &str = "llvm.sqrt.f64"; static LLVM_LROUND_I64_F64: &str = "llvm.lround.i64.f64"; static LLVM_FABS_F64: &str = "llvm.fabs.f64"; +static LLVM_SIN_F64: &str = "llvm.sin.f64"; +static LLVM_COS_F64: &str = "llvm.cos.f64"; fn add_intrinsic<'ctx>( module: &Module<'ctx>, @@ -422,7 +436,7 @@ pub fn build_expr<'a, 'ctx, 'env>( if elems.is_empty() { let struct_type = collection(ctx, env.ptr_bytes); - // THe pointer should be null (aka zero) and the length should be zero, + // The pointer should be null (aka zero) and the length should be zero, // so the whole struct should be a const_zero BasicValueEnum::StructValue(struct_type.const_zero()) } else { @@ -1203,6 +1217,8 @@ fn call_with_args<'a, 'ctx, 'env>( BasicValueEnum::IntValue(bool_val) } + Symbol::FLOAT_SIN => call_intrinsic(LLVM_SIN_F64, env, args), + Symbol::FLOAT_COS => call_intrinsic(LLVM_COS_F64, env, args), Symbol::NUM_MUL => { debug_assert!(args.len() == 2); @@ -1396,6 +1412,74 @@ fn call_with_args<'a, 'ctx, 'env>( Symbol::FLOAT_ROUND => call_intrinsic(LLVM_LROUND_I64_F64, env, args), Symbol::LIST_SET => list_set(parent, args, env, InPlace::Clone), Symbol::LIST_SET_IN_PLACE => list_set(parent, args, env, InPlace::InPlace), + Symbol::LIST_SINGLE => { + // List.single : a -> List a + debug_assert!(args.len() == 1); + + let (elem, elem_layout) = args[0]; + + let builder = env.builder; + let ctx = env.context; + + let elem_type = basic_type_from_layout(env.arena, ctx, elem_layout, env.ptr_bytes); + let elem_bytes = elem_layout.stack_size(env.ptr_bytes) as u64; + + let ptr = { + let bytes_len = elem_bytes; + let len_type = env.ptr_int(); + let len = len_type.const_int(bytes_len, false); + + env.builder + .build_array_malloc(elem_type, len, "create_list_ptr") + .unwrap() + + // TODO check if malloc returned null; if so, runtime error for OOM! + }; + + // Put the element into the list + let elem_ptr = unsafe { + builder.build_in_bounds_gep( + ptr, + &[ctx.i32_type().const_int( + // 0 as in 0 index of our new list + 0 as u64, false, + )], + "index", + ) + }; + + builder.build_store(elem_ptr, elem); + + let ptr_bytes = env.ptr_bytes; + let int_type = ptr_int(ctx, ptr_bytes); + let ptr_as_int = builder.build_ptr_to_int(ptr, int_type, "list_cast_ptr"); + let struct_type = collection(ctx, ptr_bytes); + let len = BasicValueEnum::IntValue(env.ptr_int().const_int(1, false)); + + let mut struct_val; + + // Store the pointer + struct_val = builder + .build_insert_value( + struct_type.get_undef(), + ptr_as_int, + Builtin::WRAPPER_PTR, + "insert_ptr", + ) + .unwrap(); + + // Store the length + struct_val = builder + .build_insert_value(struct_val, len, Builtin::WRAPPER_LEN, "insert_len") + .unwrap(); + + // + builder.build_bitcast( + struct_val.into_struct_value(), + collection(ctx, ptr_bytes), + "cast_collection", + ) + } Symbol::INT_DIV_UNSAFE => { debug_assert!(args.len() == 2); diff --git a/compiler/gen/tests/gen_builtins.rs b/compiler/gen/tests/gen_builtins.rs index dd9b928612..72622d2732 100644 --- a/compiler/gen/tests/gen_builtins.rs +++ b/compiler/gen/tests/gen_builtins.rs @@ -95,15 +95,17 @@ mod gen_builtins { #[test] fn gen_add_f64() { - assert_evals_to!( - indoc!( - r#" + with_larger_debug_stack(|| { + assert_evals_to!( + indoc!( + r#" 1.1 + 2.4 + 3 "# - ), - 6.5, - f64 - ); + ), + 6.5, + f64 + ); + }) } #[test] @@ -147,15 +149,17 @@ mod gen_builtins { #[test] fn gen_add_i64() { - assert_evals_to!( - indoc!( - r#" + with_larger_debug_stack(|| { + assert_evals_to!( + indoc!( + r#" 1 + 2 + 3 "# - ), - 6, - i64 - ); + ), + 6, + i64 + ); + }) } #[test] @@ -257,6 +261,48 @@ mod gen_builtins { ); } + #[test] + fn gen_is_zero_i64() { + assert_evals_to!("Int.isZero 0", true, bool); + assert_evals_to!("Int.isZero 1", false, bool); + } + + #[test] + fn gen_is_positive_i64() { + assert_evals_to!("Int.isPositive 0", false, bool); + assert_evals_to!("Int.isPositive 1", true, bool); + assert_evals_to!("Int.isPositive -5", false, bool); + } + + #[test] + fn gen_is_negative_i64() { + assert_evals_to!("Int.isNegative 0", false, bool); + assert_evals_to!("Int.isNegative 3", false, bool); + assert_evals_to!("Int.isNegative -2", true, bool); + } + + #[test] + fn gen_is_positive_f64() { + assert_evals_to!("Float.isPositive 0.0", false, bool); + assert_evals_to!("Float.isPositive 4.7", true, bool); + assert_evals_to!("Float.isPositive -8.5", false, bool); + } + + #[test] + fn gen_is_negative_f64() { + assert_evals_to!("Float.isNegative 0.0", false, bool); + assert_evals_to!("Float.isNegative 9.9", false, bool); + assert_evals_to!("Float.isNegative -4.4", true, bool); + } + + #[test] + fn gen_is_zero_f64() { + assert_evals_to!("Float.isZero 0", true, bool); + assert_evals_to!("Float.isZero 0_0", true, bool); + assert_evals_to!("Float.isZero 0.0", true, bool); + assert_evals_to!("Float.isZero 1", false, bool); + } + #[test] fn gen_is_odd() { assert_evals_to!("Int.isOdd 4", false, bool); @@ -269,6 +315,24 @@ mod gen_builtins { assert_evals_to!("Int.isEven 7", false, bool); } + #[test] + fn sin() { + assert_evals_to!("Float.sin 0", 0.0, f64); + assert_evals_to!("Float.sin 1.41421356237", 0.9877659459922529, f64); + } + + #[test] + fn cos() { + assert_evals_to!("Float.cos 0", 1.0, f64); + assert_evals_to!("Float.cos 3.14159265359", -1.0, f64); + } + + #[test] + fn tan() { + assert_evals_to!("Float.tan 0", 0.0, f64); + assert_evals_to!("Float.tan 1", 1.557407724654902, f64); + } + #[test] fn lt_i64() { assert_evals_to!("1 < 2", true, bool); @@ -387,9 +451,10 @@ mod gen_builtins { } #[test] fn tail_call_elimination() { - assert_evals_to!( - indoc!( - r#" + with_larger_debug_stack(|| { + assert_evals_to!( + indoc!( + r#" sum = \n, accum -> when n is 0 -> accum @@ -397,10 +462,11 @@ mod gen_builtins { sum 1_000_000 0 "# - ), - 500000500000, - i64 - ); + ), + 500000500000, + i64 + ); + }) } #[test] fn int_negate() { @@ -423,14 +489,29 @@ mod gen_builtins { ); } + // #[test] + // fn list_push() { + // assert_evals_to!("List.push [] 1", &[1], &'static [i64]); + // } + + #[test] + fn list_single() { + assert_evals_to!("List.single 1", &[1], &'static [i64]); + assert_evals_to!("List.single 5.6", &[5.6], &'static [f64]); + } + #[test] fn empty_list_len() { - assert_evals_to!("List.len []", 0, usize); + with_larger_debug_stack(|| { + assert_evals_to!("List.len []", 0, usize); + }) } #[test] fn basic_int_list_len() { - assert_evals_to!("List.len [ 12, 9, 6, 3 ]", 4, usize); + with_larger_debug_stack(|| { + assert_evals_to!("List.len [ 12, 9, 6, 3 ]", 4, usize); + }) } #[test] @@ -472,37 +553,43 @@ mod gen_builtins { #[test] fn empty_list_is_empty() { - assert_evals_to!("List.isEmpty []", true, bool); + with_larger_debug_stack(|| { + assert_evals_to!("List.isEmpty []", true, bool); + }) } #[test] fn first_int_list() { - assert_evals_to!( - indoc!( - r#" + with_larger_debug_stack(|| { + assert_evals_to!( + indoc!( + r#" when List.first [ 12, 9, 6, 3 ] is Ok val -> val Err _ -> -1 "# - ), - 12, - i64 - ); + ), + 12, + i64 + ); + }) } #[test] fn first_empty_list() { - assert_evals_to!( - indoc!( - r#" + with_larger_debug_stack(|| { + assert_evals_to!( + indoc!( + r#" when List.first [] is Ok val -> val Err _ -> -1 "# - ), - -1, - i64 - ); + ), + -1, + i64 + ); + }) } #[test] diff --git a/compiler/gen/tests/gen_primitives.rs b/compiler/gen/tests/gen_primitives.rs index 0818c0e144..8923cb24f8 100644 --- a/compiler/gen/tests/gen_primitives.rs +++ b/compiler/gen/tests/gen_primitives.rs @@ -13,7 +13,7 @@ mod helpers; #[cfg(test)] mod gen_primitives { - use crate::helpers::{can_expr, infer_expr, uniq_expr, CanExprOut}; + use crate::helpers::{can_expr, infer_expr, uniq_expr, with_larger_debug_stack, CanExprOut}; use bumpalo::Bump; use inkwell::context::Context; use inkwell::execution_engine::JitFunction; @@ -406,27 +406,30 @@ mod gen_primitives { #[test] fn gen_chained_defs() { - assert_evals_to!( - indoc!( - r#" - x = i1 - i3 = i2 - i1 = 1337 - i2 = i1 - y = 12.4 - - i3 - "# - ), - 1337, - i64 - ); + with_larger_debug_stack(|| { + assert_evals_to!( + indoc!( + r#" + x = i1 + i3 = i2 + i1 = 1337 + i2 = i1 + y = 12.4 + + i3 + "# + ), + 1337, + i64 + ); + }) } #[test] fn gen_nested_defs() { - assert_evals_to!( - indoc!( - r#" + with_larger_debug_stack(|| { + assert_evals_to!( + indoc!( + r#" x = 5 answer = @@ -457,9 +460,10 @@ mod gen_primitives { answer "# - ), - 1337, - i64 - ); + ), + 1337, + i64 + ); + }) } } diff --git a/compiler/gen/tests/gen_tags.rs b/compiler/gen/tests/gen_tags.rs index 96ae1a3477..1e04a956ca 100644 --- a/compiler/gen/tests/gen_tags.rs +++ b/compiler/gen/tests/gen_tags.rs @@ -13,7 +13,7 @@ mod helpers; #[cfg(test)] mod gen_tags { - use crate::helpers::{can_expr, infer_expr, uniq_expr, CanExprOut}; + use crate::helpers::{can_expr, infer_expr, uniq_expr, with_larger_debug_stack, CanExprOut}; use bumpalo::Bump; use inkwell::context::Context; use inkwell::execution_engine::JitFunction; @@ -213,9 +213,10 @@ mod gen_tags { #[test] fn even_odd() { - assert_evals_to!( - indoc!( - r#" + with_larger_debug_stack(|| { + assert_evals_to!( + indoc!( + r#" even = \n -> when n is 0 -> True @@ -230,10 +231,11 @@ mod gen_tags { odd 5 && even 42 "# - ), - true, - bool - ); + ), + true, + bool + ); + }) } #[test] diff --git a/compiler/gen/tests/helpers/mod.rs b/compiler/gen/tests/helpers/mod.rs index 30302c84a6..8eb384b750 100644 --- a/compiler/gen/tests/helpers/mod.rs +++ b/compiler/gen/tests/helpers/mod.rs @@ -6,10 +6,12 @@ pub mod eval; use self::bumpalo::Bump; use roc_builtins::unique::uniq_stdlib; use roc_can::constraint::Constraint; +use roc_can::def::Def; use roc_can::env::Env; use roc_can::expected::Expected; use roc_can::expr::{canonicalize_expr, Expr, Output}; use roc_can::operator; +use roc_can::pattern::Pattern; use roc_can::scope::Scope; use roc_collections::all::{ImMap, ImSet, MutMap, SendMap, SendSet}; use roc_constrain::expr::constrain_expr; @@ -245,16 +247,31 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut // since we aren't using modules here. let builtin_defs = roc_can::builtins::builtin_defs(&var_store); - for def in builtin_defs { - with_builtins = Expr::LetNonRec( - Box::new(def), - Box::new(Located { - region: Region::zero(), - value: with_builtins, - }), - var_store.fresh(), - SendMap::default(), - ); + for (symbol, expr) in builtin_defs { + if output.references.lookups.contains(&symbol) || output.references.calls.contains(&symbol) + { + with_builtins = Expr::LetNonRec( + Box::new(Def { + loc_pattern: Located { + region: Region::zero(), + value: Pattern::Identifier(symbol), + }, + loc_expr: Located { + region: Region::zero(), + value: expr, + }, + expr_var: var_store.fresh(), + pattern_vars: SendMap::default(), + annotation: None, + }), + Box::new(Located { + region: Region::zero(), + value: with_builtins, + }), + var_store.fresh(), + SendMap::default(), + ); + } } let loc_expr = Located { diff --git a/compiler/load/Cargo.toml b/compiler/load/Cargo.toml index 179f8bec67..f2aea40e27 100644 --- a/compiler/load/Cargo.toml +++ b/compiler/load/Cargo.toml @@ -18,7 +18,7 @@ roc_unify = { path = "../unify" } roc_parse = { path = "../parse" } roc_solve = { path = "../solve" } bumpalo = { version = "3.2", features = ["collections"] } -inlinable_string = "0.1.0" +inlinable_string = "0.1" tokio = { version = "0.2", features = ["blocking", "fs", "sync", "rt-threaded"] } [dev-dependencies] diff --git a/compiler/module/Cargo.toml b/compiler/module/Cargo.toml index 47695cdecd..0cc70a1010 100644 --- a/compiler/module/Cargo.toml +++ b/compiler/module/Cargo.toml @@ -9,7 +9,7 @@ license = "Apache-2.0" roc_region = { path = "../region" } roc_collections = { path = "../collections" } bumpalo = { version = "3.2", features = ["collections"] } -inlinable_string = "0.1.0" +inlinable_string = "0.1" lazy_static = "1.4" [dev-dependencies] diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index 981ebb6e60..a28509b562 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -623,6 +623,12 @@ define_builtins! { 29 INT_IS_ODD_ARG: "isOdd#arg" 30 INT_IS_EVEN: "isEven" 31 INT_IS_EVEN_ARG: "isEven#arg" + 32 INT_IS_ZERO: "isZero" + 33 INT_IS_ZERO_ARG: "isZero#arg" + 34 INT_IS_POSITIVE: "isPositive" + 35 INT_IS_POSITIVE_ARG: "isPositive#arg" + 36 INT_IS_NEGATIVE: "isNegative" + 37 INT_IS_NEGATIVE_ARG: "isNegative#arg" } 3 FLOAT: "Float" => { 0 FLOAT_FLOAT: "Float" imported // the Float.Float type alias @@ -635,13 +641,23 @@ define_builtins! { 7 FLOAT_LOWEST: "lowest" 8 FLOAT_ADD: "#add" 9 FLOAT_SUB: "#sub" - 10 FLOAT_EQ: "#eq" + 10 FLOAT_EQ: "eq" 11 FLOAT_ROUND: "round" 12 FLOAT_LT: "#lt" 13 FLOAT_LTE: "#lte" - 14 FLOAT_GT: "#gt" + 14 FLOAT_GT: "gt" 15 FLOAT_GTE: "#gte" 16 FLOAT_ABS: "abs" + 17 FLOAT_IS_POSITIVE: "isPositive" + 18 FLOAT_IS_POSITIVE_ARG: "isPositive#arg" + 19 FLOAT_IS_NEGATIVE: "isNegative" + 20 FLOAT_IS_NEGATIVE_ARG: "isNegative#arg" + 21 FLOAT_IS_ZERO: "isZero" + 22 FLOAT_IS_ZERO_ARG: "isZero#arg" + 23 FLOAT_SIN: "sin" + 24 FLOAT_COS: "cos" + 25 FLOAT_TAN: "tan" + 26 FLOAT_TAN_ARG: "tan#arg" } 4 BOOL: "Bool" => { 0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias @@ -676,6 +692,7 @@ define_builtins! { 15 LIST_CONCAT: "concat" 16 LIST_FIRST: "first" 17 LIST_FIRST_ARG: "first#list" + 18 LIST_SINGLE: "single" } 7 RESULT: "Result" => { 0 RESULT_RESULT: "Result" imported // the Result.Result type alias diff --git a/compiler/parse/Cargo.toml b/compiler/parse/Cargo.toml index 49c3e86efc..8d129df1e5 100644 --- a/compiler/parse/Cargo.toml +++ b/compiler/parse/Cargo.toml @@ -10,7 +10,7 @@ roc_collections = { path = "../collections" } roc_region = { path = "../region" } roc_module = { path = "../module" } bumpalo = { version = "3.2", features = ["collections"] } -inlinable_string = "0.1.0" +inlinable_string = "0.1" [dev-dependencies] pretty_assertions = "0.5.1" diff --git a/compiler/problem/Cargo.toml b/compiler/problem/Cargo.toml index 6b8ac711c8..a103480fd6 100644 --- a/compiler/problem/Cargo.toml +++ b/compiler/problem/Cargo.toml @@ -10,7 +10,7 @@ roc_collections = { path = "../collections" } roc_region = { path = "../region" } roc_module = { path = "../module" } roc_parse = { path = "../parse" } -inlinable_string = "0.1.0" +inlinable_string = "0.1" [dev-dependencies] pretty_assertions = "0.5.1" diff --git a/compiler/reporting/Cargo.toml b/compiler/reporting/Cargo.toml index fbc3209589..fea377a8a2 100644 --- a/compiler/reporting/Cargo.toml +++ b/compiler/reporting/Cargo.toml @@ -17,7 +17,7 @@ roc_can = { path = "../can" } roc_solve = { path = "../solve" } roc_mono = { path = "../mono" } ven_pretty = { path = "../../vendor/pretty" } -inlinable_string = "0.1.0" +inlinable_string = "0.1" im = "14" # im and im-rc should always have the same version! im-rc = "14" # im and im-rc should always have the same version! distance = "0.4.0" diff --git a/compiler/reporting/src/error/canonicalize.rs b/compiler/reporting/src/error/canonicalize.rs index 9d691a7ffb..9710ab32d5 100644 --- a/compiler/reporting/src/error/canonicalize.rs +++ b/compiler/reporting/src/error/canonicalize.rs @@ -280,7 +280,7 @@ fn pretty_runtime_error<'b>( if idents.is_empty() { alloc .reflow("The ") - .append(alloc.ident(first.value.clone())) + .append(alloc.ident(first.value)) .append(alloc.reflow( " value is defined directly in terms of itself, causing an infinite loop.", )) diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index 89da188fa2..0e621891cb 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -333,7 +333,7 @@ fn solve( let var = type_to_var(subs, rank, pools, cached_aliases, &loc_type.value); local_def_vars.insert( - symbol.clone(), + *symbol, Located { value: var, region: loc_type.region, @@ -344,7 +344,7 @@ fn solve( 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.clone(), loc_var.value); + new_env.vars_by_symbol.insert(*symbol, loc_var.value); } } @@ -398,7 +398,7 @@ fn solve( type_to_var(subs, next_rank, next_pools, cached_aliases, &def_type); local_def_vars.insert( - symbol.clone(), + *symbol, Located { value: var, region: loc_type.region, @@ -469,7 +469,7 @@ fn solve( 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.clone(), loc_var.value); + new_env.vars_by_symbol.insert(*symbol, loc_var.value); } } diff --git a/compiler/solve/tests/test_uniq_solve.rs b/compiler/solve/tests/test_uniq_solve.rs index 717c518b4d..4a81e568fb 100644 --- a/compiler/solve/tests/test_uniq_solve.rs +++ b/compiler/solve/tests/test_uniq_solve.rs @@ -116,14 +116,16 @@ mod test_uniq_solve { #[test] fn empty_list_literal() { - infer_eq( - indoc!( - r#" + with_larger_debug_stack(|| { + infer_eq( + indoc!( + r#" [] "# - ), - "Attr * (List *)", - ); + ), + "Attr * (List *)", + ); + }) } #[test] diff --git a/compiler/types/Cargo.toml b/compiler/types/Cargo.toml index 14cec69191..21765d30ac 100644 --- a/compiler/types/Cargo.toml +++ b/compiler/types/Cargo.toml @@ -12,7 +12,7 @@ roc_module = { path = "../module" } roc_parse = { path = "../parse" } roc_problem = { path = "../problem" } ven_ena = { path = "../../vendor/ena" } -inlinable_string = "0.1.0" +inlinable_string = "0.1" [dev-dependencies] pretty_assertions = "0.5.1" diff --git a/compiler/types/src/boolean_algebra.rs b/compiler/types/src/boolean_algebra.rs index 1e2b24914e..6e53046a66 100644 --- a/compiler/types/src/boolean_algebra.rs +++ b/compiler/types/src/boolean_algebra.rs @@ -74,7 +74,7 @@ impl Bool { for atom in &self.1 { if let Variable(v) = atom { - result.insert(v.clone()); + result.insert(*v); } } @@ -152,7 +152,7 @@ impl Bool { if let Variable(v) = atom { new_bound.insert(Variable(f(*v))); } else { - new_bound.insert(atom.clone()); + new_bound.insert(*atom); } } Bool(new_free, new_bound) diff --git a/compiler/uniq/src/sharing.rs b/compiler/uniq/src/sharing.rs index 6756b61afe..dd3a87d6e2 100644 --- a/compiler/uniq/src/sharing.rs +++ b/compiler/uniq/src/sharing.rs @@ -416,7 +416,7 @@ impl Composable for VarUsage { } }; - self.usage.insert(symbol.clone(), value); + self.usage.insert(*symbol, value); } } } diff --git a/editor/Cargo.toml b/editor/Cargo.toml index fdaea372ed..8af155eade 100644 --- a/editor/Cargo.toml +++ b/editor/Cargo.toml @@ -27,7 +27,7 @@ roc_reporting = { path = "../compiler/reporting" } im = "14" # im and im-rc should always have the same version! im-rc = "14" # im and im-rc should always have the same version! bumpalo = { version = "3.2", features = ["collections"] } -inlinable_string = "0.1.0" +inlinable_string = "0.1" tokio = { version = "0.2", features = ["blocking", "fs", "sync", "rt-threaded", "process", "io-driver"] } # NOTE: rtfeldman/inkwell is a fork of TheDan64/inkwell which does not change anything. # @@ -49,27 +49,13 @@ tokio = { version = "0.2", features = ["blocking", "fs", "sync", "rt-threaded", inkwell = { git = "https://github.com/rtfeldman/inkwell", tag = "llvm10-0.release1" } target-lexicon = "0.10" winit = "0.22" -image = "0.23" -gfx-hal = "0.5" -glsl-to-spirv = "0.1" -bincode = "1.2" -serde = { version = "1.0", features = ["derive"] } - -[target.'cfg(target_os = "macos")'.dependencies.backend] -package = "gfx-backend-metal" -version = "0.5" - -[target.'cfg(windows)'.dependencies.backend] -package = "gfx-backend-dx12" -version = "0.5" - -[target.'cfg(all(unix, not(target_os = "macos")))'.dependencies.backend] -package = "gfx-backend-vulkan" -features = ["x11"] -version = "0.5" - -[build-dependencies] -glsl-to-spirv = "0.1" +wgpu = "0.5" +glyph_brush = "0.7" +log = "0.4" +zerocopy = "0.3" +env_logger = "0.7" +futures = "0.3" +wgpu_glyph = "0.9" [dev-dependencies] pretty_assertions = "0.5.1" diff --git a/editor/Inconsolata-Regular.ttf b/editor/Inconsolata-Regular.ttf new file mode 100644 index 0000000000..3e547460b7 Binary files /dev/null and b/editor/Inconsolata-Regular.ttf differ diff --git a/editor/creature.png b/editor/creature.png new file mode 100644 index 0000000000..f0fd87e6b0 Binary files /dev/null and b/editor/creature.png differ diff --git a/editor/src/ast.rs b/editor/src/ast.rs new file mode 100644 index 0000000000..768b155421 --- /dev/null +++ b/editor/src/ast.rs @@ -0,0 +1,704 @@ +use inlinable_string::string_ext::StringExt; +use inlinable_string::InlinableString; +use roc_types::subs::Variable; +use std::fmt; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum Problem { + RanOutOfNodeIds, +} + +pub type Res = Result; + +/// The index into a decl's array of nodes. +/// This is a u32 index because no decl is allowed to hold more than 2^32 entries, +/// and it saves space on 64-bit systems compared to a pointer. +#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)] +pub struct NodeId(u32); + +impl NodeId { + pub const NONE: NodeId = NodeId(std::u32::MAX); + + pub fn as_index(self) -> usize { + self.0 as usize + } +} + +impl fmt::Debug for NodeId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self == &NodeId::NONE { + write!(f, "none") + } else { + write!(f, "#{}", self.0) + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum UndoAction { + UndoEdit { + caret_id: NodeId, + caret_parent_id: NodeId, + caret_child_id: NodeId, + caret_offset: u16, + child: Node, + redo_action: Action, + }, + /// Used in undo logs to mean "the next N undo actions in the log should + /// be replayed together in a batch." + Multiple(u16), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Action { + Backspace, + Paste, + + /// Used in redo logs to mean "the next N redo actions in the log should + /// be replayed together in a batch." + Multiple(u16), +} + +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct Nodes { + nodes: Vec, +} + +impl Nodes { + pub fn push_expr(&mut self, parent_id: NodeId, expr: Expr) -> Res { + let node_id = self.push_node(Node { + parent_id, + content: NodeContent::Expr(expr), + })?; + + self.set_child(parent_id, node_id); + + Ok(node_id) + } + + fn set_child(&mut self, parent_id: NodeId, _child_id: NodeId) { + match self.nodes.get_mut(parent_id.as_index()) { + Some(parent) => match &mut parent.content { + NodeContent::Expr(Expr::Int { .. }) | NodeContent::Expr(Expr::Float { .. }) => { + // This node has no children. No further action needed! + } + NodeContent::Caret { .. } => { + // This node has no children. No further action needed! + } + other => { + todo!("handle set_child for {:?}", other); + } + }, + None => { + // The only reason this bounds check should ever fail + // is that we were passed no parent. + debug_assert!(parent_id == NodeId::NONE); + } + } + } + + // fn remove_node(&mut self, target_id: NodeId) { + // debug_assert!( + // target_id != NodeId::NONE, + // "Called remove_node on NodeId::NONE" + // ); + + // { + // let target = self + // .nodes + // .get_mut(target_id.as_index()) + // .unwrap_or_else(|| panic!("Tried to remove nonexistant node {:?}", target_id)); + // let opt_parent = self.nodes.get_mut(target.parent_id.as_index()); + + // match &mut target.content { + // NodeContent::Expr(Expr::Int { .. }) => { + // // This node has no children, so remove it from its parent and move on. + // match opt_parent { + // Some(parent) => parent.remove_child(target_id), + // None => { + // // The only valid reason the node's parent_id might + // // not have been in the nodes vec is that it's NONE. + // debug_assert!(target.parent_id == NodeId::NONE); + // } + // } + // } + + // NodeContent::Caret { child_id, offset } => { + // match opt_parent { + // Some(parent) => { + // // Go into the parent and replace this caret with + // // the new child_id + // // parent.replace_child(target_id, child_id) + // } + // None => { + // // The only valid reason the node's parent_id might + // // not have been in the nodes vec is that it's NONE. + // debug_assert!(target.parent_id == NodeId::NONE); + // } + // } + // } + + // NodeContent::Removed => { + // panic!("Tried to remove a node that was already removed."); + // } + // } + // } + // } + + fn push_node(&mut self, node: Node) -> Res { + // TODO check to see if we have any removed nodes, and if so, use those + // instead of pushing to the end. This way, we can avoid needing to defrag. + + // The length *before* we pushed == the index of the node we pushed. + let index = self.nodes.len(); + + self.nodes.push(node); + + if index < std::u32::MAX as usize { + Ok(NodeId(index as u32)) + } else { + // u32::MAX is reserved for NodeId::NONE, so if we hit that on a + // saturating add, we've overflowed. Game over. + Err(Problem::RanOutOfNodeIds) + } + } + + pub fn replace(&mut self, node_id: NodeId, node: Node) { + let elem = self.nodes.get_mut(node_id.as_index()).unwrap_or_else(|| { + panic!( + "Tried to replace element at nonexistant NodeId {:?} with {:?}", + node_id, node + ) + }); + + *elem = node; + } + + pub fn wrap_with_caret(&mut self, target_id: NodeId, offset: u16) -> Res { + let parent_id = self.get(target_id).parent_id; + let caret_id = { + let content = NodeContent::Caret { + child_id: target_id, + offset, + }; + + self.push_node(Node { content, parent_id })? + }; + + // Now that we have the caret_id, update the old + if parent_id != NodeId::NONE { + self.get_mut(target_id).replace_child(target_id, caret_id); + } + + Ok(caret_id) + } + + pub fn len(&self) -> usize { + self.nodes.len() + } + + pub fn is_empty(&self) -> bool { + self.nodes.is_empty() + } + + pub fn split_at_mut(&mut self, index: NodeId) -> (&mut [Node], &mut [Node]) { + self.nodes.split_at_mut(index.0 as usize) + } + + pub fn get(&self, index: NodeId) -> &Node { + self.nodes + .get(index.0 as usize) + .unwrap_or_else(|| panic!("Unable to find node at index {:?}", index.0)) + } + + pub fn get_mut(&mut self, index: NodeId) -> &mut Node { + self.nodes + .get_mut(index.0 as usize) + .unwrap_or_else(|| panic!("Unable to find node at index {:?}", index.0)) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Node { + parent_id: NodeId, + content: NodeContent, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum NodeContent { + Expr(Expr /* TODO should Node have a Variable? */), + // Pattern(Pattern), + Caret { child_id: NodeId, offset: u16 }, + // SelectionStart { offset: u16, child: NodeId }, + // SelectionEnd { offset: u16, child: NodeId }, + Removed, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Expr { + /// An integer literal (without a dot) + Int { + text: InlinableString, + var: Variable, + }, + /// An floating-point literal (with a dot) + Float { + text: InlinableString, + var: Variable, + }, + // /// A partial lookup that has not yet been completed, e.g. + // /// `Foo.` or `pkg.Foo.Bar` + // PartialLookup { + // /// dot-separated sections, e.g. `Foo.Bar.` would be ["Foo", "Bar", ""] + // sections: Vec, + // var: Variable, + // }, + // Lookup { + // name: InlinableString, + // var: Variable, + // }, + // If { + // conditional: NodeId, + // then_node: NodeId, + // else_node: NodeId, + // var: Variable, + // }, + // Then { + // child: NodeId, + // var: Variable, + // }, + // Else { + // child: NodeId, + // var: Variable, + // }, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Pattern { + Identifier { text: String, var: Variable }, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Decl { + Def(Def), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Def { + Body { pattern: NodeId, expr: NodeId }, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Module { + pub nodes: Nodes, + pub decls: Vec, + // Use a Vec over a Set because it'll always be of small length + pub carets: Vec, + pub selections: Vec<(NodeId, NodeId)>, + + /// Because these actions store NodeId values, it's critically important + /// that when we take an action and then redo it, everything (including Nodes) + /// ends up back in the same state, including NodeId values. + pub undo_log: Vec, + + pub redos: Vec, +} + +impl Node { + /// Returns the number of characters removed, so that + /// the caret can be moved back this many indices. + /// (For example, if you backspace over an emoji, that can move the + /// caret back multiple indices.) + pub fn backspace(&mut self, index: u16) -> u16 { + use Expr::*; + + match &mut self.content { + NodeContent::Expr(Int { text, .. }) | NodeContent::Expr(Float { text, .. }) => { + // TODO remove an entire *grapheme cluster* here, not just a + // single character! This will require using a 3rd-party crate. + let removed = text.remove(index as usize); + + // TODO need to re-index any other carets/selections which + // were also in this node - they need to be moved too! + removed.len_utf8() as u16 + } + other => { + todo!("handle backspace for {:?}", other); + } + } + } + + // fn remove_child(&mut self, id_to_remove: NodeId) { + // match &mut self.content { + // NodeContent::Expr(Expr::Int { .. }) | NodeContent::Expr(Expr::Float { .. }) => { + // // This node has no children, so no action is needed. + // } + + // NodeContent::Caret { child_id, .. } => { + // if *child_id == id_to_remove { + // *child_id = NodeId::NONE; + // } + // } + + // NodeContent::Removed => { + // panic!( + // "Tried to remove a child node ({:?}) from a NodeContent::Removed", + // id_to_remove + // ); + // } + // } + // } + + fn replace_child(&mut self, old_id: NodeId, new_id: NodeId) { + match &mut self.content { + NodeContent::Expr(Expr::Int { .. }) | NodeContent::Expr(Expr::Float { .. }) => { + // This node has no children, so no action is needed. + } + + NodeContent::Caret { child_id, .. } => { + if *child_id == old_id { + *child_id = new_id; + } + } + + NodeContent::Removed => { + panic!( + "Tried to replace child node ID {:?} with {:?} in a NodeContent::Removed", + old_id, new_id + ); + } + } + } +} + +impl Module { + pub fn undo(&mut self) { + let mut iterations_remaining: u16 = 0; + + loop { + match self.undo_log.pop() { + Some(action) => { + iterations_remaining += self.apply_undo_action(action); + } + None => { + if iterations_remaining > 0 { + panic!("Expected to be able to do {} more Undo iterations, but ran out of actions to undo.", iterations_remaining); + } + } + } + + if iterations_remaining == 0 { + return; + } else { + iterations_remaining -= 1; + } + } + } + + pub fn redo(&mut self) { + let mut iterations_remaining: u16 = 0; + + loop { + match self.redos.pop() { + Some(action) => { + iterations_remaining += self.apply_action(action); + } + None => { + if iterations_remaining > 0 { + panic!("Expected to be able to do {} more Redo iterations, but ran out of actions to redo.", iterations_remaining); + } + } + } + + if iterations_remaining == 0 { + return; + } else { + iterations_remaining -= 1; + } + } + } + + fn apply_action(&mut self, action: Action) -> u16 { + use Action::*; + + match action { + Backspace => { + self.backspace(); + + 0 + } + Paste => { + todo!("TODO support Paste action"); + } + Multiple(iterations) => iterations, + } + } + + fn apply_undo_action(&mut self, action: UndoAction) -> u16 { + use UndoAction::*; + + match action { + UndoEdit { + caret_id, + caret_parent_id, + caret_child_id, + caret_offset, + child, + redo_action, + } => { + self.redos.push(redo_action); + self.nodes.replace(caret_child_id, child); + + let caret = Node { + parent_id: caret_parent_id, + content: NodeContent::Caret { + child_id: caret_child_id, + offset: caret_offset, + }, + }; + + self.nodes.replace(caret_id, caret); + + 0 + } + Multiple(iterations) => iterations, + } + } + + pub fn paste(&mut self, text: &str) { + todo!( + "TODO paste this string, taking carets and selections into account: {:?}", + text + ); + } + + pub fn backspace(&mut self) { + for &caret_node_id in self.carets.iter() { + debug_assert!(caret_node_id.as_index() <= self.nodes.len()); + + // Use slices around the caret because we'll need to modify the + // child node. Without these slices we'd need multiple simultaneous + // mutable references to self.nodes, which is never allowed. + let (before_caret, caret_and_after) = self.nodes.split_at_mut(caret_node_id); + let (caret_only, after_caret) = caret_and_after.split_at_mut(1); + + let caret = caret_only.first_mut().unwrap(); + let parent_id = caret.parent_id; + + match &mut caret.content { + NodeContent::Caret { offset, child_id } => { + let child_id = *child_id; + let offset_index = *offset; + + if offset_index != 0 { + // Get the child node from the appropriate slice + let child_node: &mut Node = { + debug_assert!( + child_id != caret_node_id, + "A caret had itself as its own child: {:?}", + caret_node_id + ); + + if child_id > caret_node_id { + after_caret.get_mut(child_id.as_index() - (before_caret.len() + 1)) + } else { + before_caret.get_mut(child_id.as_index()) + } + } + .unwrap_or_else(|| { + panic!("Could not get child node for caret {:?}", caret_node_id) + }); + + // Add an entry to the undo log to undo this edit. + self.undo_log.push(UndoAction::UndoEdit { + caret_id: caret_node_id, + caret_parent_id: parent_id, + caret_offset: offset_index, + caret_child_id: child_id, + child: child_node.clone(), + redo_action: Action::Backspace, + }); + + // Mutate the child node to apply the backspace operation. + // + // -1 because index 0 is *before* the first character + // in the string (which we already ruled out using the + // above conditional), and child_node.backspace expects + // to be given the index *after* the char to be removed. + let chars_removed = child_node.backspace(offset_index - 1); + + // Mutate the caret to decrement its offset + *offset = offset_index - chars_removed; + } else { + todo!( + "Backspace when caret offset is 0, into parent: {:?}", + parent_id + ); + } + } + other => { + unreachable!("Caret pointed to a non-caret node: {:?}", other); + } + } + } + } +} + +#[test] +fn single_backspace_1_caret() { + use roc_types::subs::VarStore; + + let var_store = VarStore::default(); + let int_var = var_store.fresh(); + let int_node_id; + let caret_node_id; + let expected = { + let mut nodes = Nodes::default(); + + int_node_id = nodes + .push_expr( + NodeId::NONE, + Expr::Int { + text: "abd".into(), + var: int_var, + }, + ) + .unwrap(); + + caret_node_id = nodes.wrap_with_caret(int_node_id, 2).unwrap(); + + nodes + }; + + let actual = { + let mut nodes = Nodes::default(); + + let actual_node_id = nodes + .push_expr( + NodeId::NONE, + Expr::Int { + text: "abcd".into(), + var: int_var, + }, + ) + .unwrap(); + + assert!(int_node_id == actual_node_id); + + let actual_node_id = nodes.wrap_with_caret(int_node_id, 3).unwrap(); + + assert!(caret_node_id == actual_node_id); + + nodes + }; + + let mut module = Module { + nodes: actual, + decls: Vec::new(), + carets: vec![caret_node_id], + selections: Vec::new(), + undo_log: Vec::new(), + redos: Vec::new(), + }; + + let original_module = module.clone(); + + module.backspace(); + + let altered_module = module.clone(); + + assert_eq!(expected, module.nodes); + + module.undo(); + + let stashed_redos = module.redos; + + module.redos = Vec::new(); + + assert_eq!(original_module, module); + + module.redos = stashed_redos; + + module.redo(); + + assert_eq!(altered_module, module); +} + +#[test] +fn double_backspace_1_caret() { + use roc_types::subs::VarStore; + + let var_store = VarStore::default(); + let int_var = var_store.fresh(); + let int_node_id; + let caret_node_id; + let expected = { + let mut nodes = Nodes::default(); + + int_node_id = nodes + .push_expr( + NodeId::NONE, + Expr::Int { + text: "ad".into(), + var: int_var, + }, + ) + .unwrap(); + + caret_node_id = nodes.wrap_with_caret(int_node_id, 1).unwrap(); + + nodes + }; + + let actual = { + let mut nodes = Nodes::default(); + let actual_node_id = nodes + .push_expr( + NodeId::NONE, + Expr::Int { + text: "abcd".into(), + var: int_var, + }, + ) + .unwrap(); + + assert!(int_node_id == actual_node_id); + assert!(caret_node_id == nodes.wrap_with_caret(int_node_id, 3).unwrap()); + + nodes + }; + + let mut module = Module { + nodes: actual, + decls: Vec::new(), + carets: vec![caret_node_id], + selections: Vec::new(), + undo_log: Vec::new(), + redos: Vec::new(), + }; + + let original_module = module.clone(); + + module.backspace(); + module.backspace(); + + let altered_module = module.clone(); + + assert_eq!(expected, module.nodes); + + module.undo(); + module.undo(); + + let stashed_redos = module.redos; + + module.redos = Vec::new(); + + assert_eq!(original_module, module); + + module.redos = stashed_redos; + + module.redo(); + module.redo(); + + assert_eq!(altered_module, module); +} diff --git a/editor/src/lib.rs b/editor/src/lib.rs index 97edea7596..cc40041b12 100644 --- a/editor/src/lib.rs +++ b/editor/src/lib.rs @@ -1,326 +1,99 @@ -use gfx_hal::{ - device::Device, - window::{Extent2D, PresentationSurface, Surface}, - Instance, -}; -use glsl_to_spirv::ShaderType; +#![warn(clippy::all, clippy::dbg_macro)] +// I'm skeptical that clippy:large_enum_variant is a good lint to have globally enabled. +// +// It warns about a performance problem where the only quick remediation is +// to allocate more on the heap, which has lots of tradeoffs - including making it +// long-term unclear which allocations *need* to happen for compilation's sake +// (e.g. recursive structures) versus those which were only added to appease clippy. +// +// Effectively optimizing data struture memory layout isn't a quick fix, +// and encouraging shortcuts here creates bad incentives. I would rather temporarily +// re-enable this when working on performance optimizations than have it block PRs. +#![allow(clippy::large_enum_variant)] + +use std::error::Error; use std::io; -use std::mem::ManuallyDrop; use std::path::Path; +use wgpu_glyph::{ab_glyph, GlyphBrushBuilder, Section, Text}; use winit::event::{ElementState, ModifiersState, VirtualKeyCode}; +use winit::event_loop::ControlFlow; + +pub mod ast; +pub mod text_state; /// 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(_filepaths: &[&Path]) -> io::Result<()> { // TODO do any initialization here - run_event_loop(); + run_event_loop().expect("Error running event loop"); Ok(()) } -use winit::{event_loop::EventLoop, window::WindowBuilder}; +fn run_event_loop() -> Result<(), Box> { + env_logger::init(); -struct Resources { - instance: B::Instance, - surface: B::Surface, - device: B::Device, - render_passes: Vec, - pipeline_layouts: Vec, - pipelines: Vec, - command_pool: B::CommandPool, - submission_complete_fence: B::Fence, - rendering_complete_semaphore: B::Semaphore, -} + // Open window and create a surface + let event_loop = winit::event_loop::EventLoop::new(); -struct ResourceHolder(ManuallyDrop>); - -impl Drop for ResourceHolder { - fn drop(&mut self) { - unsafe { - let Resources { - instance, - mut surface, - device, - command_pool, - render_passes, - pipeline_layouts, - pipelines, - submission_complete_fence, - rendering_complete_semaphore, - } = ManuallyDrop::take(&mut self.0); - - device.destroy_semaphore(rendering_complete_semaphore); - device.destroy_fence(submission_complete_fence); - for pipeline in pipelines { - device.destroy_graphics_pipeline(pipeline); - } - for pipeline_layout in pipeline_layouts { - device.destroy_pipeline_layout(pipeline_layout); - } - for render_pass in render_passes { - device.destroy_render_pass(render_pass); - } - device.destroy_command_pool(command_pool); - surface.unconfigure_swapchain(&device); - instance.destroy_surface(surface); - } - } -} - -#[repr(C)] -#[derive(Debug, Clone, Copy)] -struct PushConstants { - color: [f32; 4], - pos: [f32; 2], - scale: [f32; 2], -} - -fn run_event_loop() { - // TODO do a better window size - const WINDOW_SIZE: [u32; 2] = [512, 512]; - - // TODO try configuring the swapchain explicitly, in particular in order - // to experiment with different PresentMode settings to see how they - // affect input latency. - // - // https://rust-tutorials.github.io/learn-gfx-hal/03_clear_the_window.html - let event_loop = EventLoop::new(); - - let (logical_window_size, physical_window_size) = { - use winit::dpi::{LogicalSize, PhysicalSize}; - - let dpi = event_loop.primary_monitor().scale_factor(); - let logical: LogicalSize = WINDOW_SIZE.into(); - let physical: PhysicalSize = logical.to_physical(dpi); - - (logical, physical) - }; - - let mut surface_extent = Extent2D { - width: physical_window_size.width, - height: physical_window_size.height, - }; - - let window = WindowBuilder::new() - .with_title("roc") - .with_inner_size(logical_window_size) + let window = winit::window::WindowBuilder::new() .build(&event_loop) .unwrap(); - let mut should_configure_swapchain = true; + let surface = wgpu::Surface::create(&window); - let (instance, surface, adapter) = { - let instance = backend::Instance::create("roc_editor", 1).expect("Backend not supported"); - - let surface = unsafe { - instance - .create_surface(&window) - .expect("Failed to create surface for window") - }; - - let adapter = instance.enumerate_adapters().remove(0); - - (instance, surface, adapter) - }; - - let (device, mut queue_group) = { - use gfx_hal::queue::QueueFamily; - - let queue_family = adapter - .queue_families - .iter() - .find(|family| { - surface.supports_queue_family(family) && family.queue_type().supports_graphics() - }) - .expect("No compatible queue family found"); - - let mut gpu = unsafe { - use gfx_hal::adapter::PhysicalDevice; - - adapter - .physical_device - .open(&[(queue_family, &[1.0])], gfx_hal::Features::empty()) - .expect("Failed to open device") - }; - - (gpu.device, gpu.queue_groups.pop().unwrap()) - }; - - let (command_pool, mut command_buffer) = unsafe { - use gfx_hal::command::Level; - use gfx_hal::pool::{CommandPool, CommandPoolCreateFlags}; - - let mut command_pool = device - .create_command_pool(queue_group.family, CommandPoolCreateFlags::empty()) - .expect("Out of memory"); - - let command_buffer = command_pool.allocate_one(Level::Primary); - - (command_pool, command_buffer) - }; - - let surface_color_format = { - use gfx_hal::format::{ChannelType, Format}; - - let supported_formats = surface - .supported_formats(&adapter.physical_device) - .unwrap_or_else(|| vec![]); - - let default_format = *supported_formats.get(0).unwrap_or(&Format::Rgba8Srgb); - - supported_formats - .into_iter() - .find(|format| format.base_format().1 == ChannelType::Srgb) - .unwrap_or(default_format) - }; - - let render_pass = { - use gfx_hal::image::Layout; - use gfx_hal::pass::{ - Attachment, AttachmentLoadOp, AttachmentOps, AttachmentStoreOp, SubpassDesc, - }; - - let color_attachment = Attachment { - format: Some(surface_color_format), - samples: 1, - ops: AttachmentOps::new(AttachmentLoadOp::Clear, AttachmentStoreOp::Store), - stencil_ops: AttachmentOps::DONT_CARE, - layouts: Layout::Undefined..Layout::Present, - }; - - let subpass = SubpassDesc { - colors: &[(0, Layout::ColorAttachmentOptimal)], - depth_stencil: None, - inputs: &[], - resolves: &[], - preserves: &[], - }; - - unsafe { - device - .create_render_pass(&[color_attachment], &[subpass], &[]) - .expect("Out of memory") - } - }; - - let pipeline_layout = unsafe { - use gfx_hal::pso::ShaderStageFlags; - - let push_constant_bytes = std::mem::size_of::() as u32; - - device - .create_pipeline_layout(&[], &[(ShaderStageFlags::VERTEX, 0..push_constant_bytes)]) - .expect("Out of memory") - }; - - let vertex_shader = include_str!("../shaders/triangle.vert"); - let fragment_shader = include_str!("../shaders/triangle.frag"); - - /// Create a pipeline with the given layout and shaders. - unsafe fn make_pipeline( - device: &B::Device, - render_pass: &B::RenderPass, - pipeline_layout: &B::PipelineLayout, - vertex_shader: &str, - fragment_shader: &str, - ) -> B::GraphicsPipeline { - use gfx_hal::pass::Subpass; - use gfx_hal::pso::{ - BlendState, ColorBlendDesc, ColorMask, EntryPoint, Face, GraphicsPipelineDesc, - GraphicsShaderSet, Primitive, Rasterizer, Specialization, - }; - - let vertex_shader_module = device - .create_shader_module(&compile_shader(vertex_shader, ShaderType::Vertex)) - .expect("Failed to create vertex shader module"); - - let fragment_shader_module = device - .create_shader_module(&compile_shader(fragment_shader, ShaderType::Fragment)) - .expect("Failed to create fragment shader module"); - - let (vs_entry, fs_entry) = ( - EntryPoint { - entry: "main", - module: &vertex_shader_module, - specialization: Specialization::default(), + // Initialize GPU + let (device, queue) = futures::executor::block_on(async { + let adapter = wgpu::Adapter::request( + &wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::HighPerformance, + compatible_surface: Some(&surface), }, - EntryPoint { - entry: "main", - module: &fragment_shader_module, - specialization: Specialization::default(), - }, - ); - - let shader_entries = GraphicsShaderSet { - vertex: vs_entry, - hull: None, - domain: None, - geometry: None, - fragment: Some(fs_entry), - }; - - let mut pipeline_desc = GraphicsPipelineDesc::new( - shader_entries, - Primitive::TriangleList, - Rasterizer { - cull_face: Face::BACK, - ..Rasterizer::FILL - }, - pipeline_layout, - Subpass { - index: 0, - main_pass: render_pass, - }, - ); - - pipeline_desc.blender.targets.push(ColorBlendDesc { - mask: ColorMask::ALL, - blend: Some(BlendState::ALPHA), - }); - - let pipeline = device - .create_graphics_pipeline(&pipeline_desc, None) - .expect("Failed to create graphics pipeline"); - - device.destroy_shader_module(vertex_shader_module); - device.destroy_shader_module(fragment_shader_module); - - pipeline - }; - - let pipeline = unsafe { - make_pipeline::( - &device, - &render_pass, - &pipeline_layout, - vertex_shader, - fragment_shader, + wgpu::BackendBit::all(), ) - }; + .await + .expect("Request adapter"); + + adapter + .request_device(&wgpu::DeviceDescriptor { + extensions: wgpu::Extensions { + anisotropic_filtering: false, + }, + limits: wgpu::Limits { max_bind_groups: 1 }, + }) + .await + }); + + // Prepare swap chain + let render_format = wgpu::TextureFormat::Bgra8UnormSrgb; + let mut size = window.inner_size(); + + let mut swap_chain = device.create_swap_chain( + &surface, + &wgpu::SwapChainDescriptor { + usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, + format: render_format, + width: size.width, + height: size.height, + present_mode: wgpu::PresentMode::Immediate, + }, + ); + + // Prepare glyph_brush + let inconsolata = + ab_glyph::FontArc::try_from_slice(include_bytes!("../Inconsolata-Regular.ttf"))?; + + let mut glyph_brush = GlyphBrushBuilder::using_font(inconsolata).build(&device, render_format); - let submission_complete_fence = device.create_fence(true).expect("Out of memory"); - let rendering_complete_semaphore = device.create_semaphore().expect("Out of memory"); - let mut resource_holder: ResourceHolder = - ResourceHolder(ManuallyDrop::new(Resources { - instance, - surface, - device, - command_pool, - render_passes: vec![render_pass], - pipeline_layouts: vec![pipeline_layout], - pipelines: vec![pipeline], - submission_complete_fence, - rendering_complete_semaphore, - })); let is_animating = true; let mut text_state = String::new(); let mut keyboard_modifiers = ModifiersState::empty(); - event_loop.run(move |event, _, control_flow| { - use winit::event::{Event, WindowEvent}; - use winit::event_loop::ControlFlow; + // Render loop + window.request_redraw(); + event_loop.run(move |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 { @@ -330,240 +103,131 @@ fn run_event_loop() { } match event { - Event::WindowEvent { - event: window_event, + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::CloseRequested, .. - } => match window_event { - WindowEvent::CloseRequested => { - println!("✈️ Thank you for flying Roc Airlines!"); - *control_flow = ControlFlow::Exit - } - WindowEvent::Resized(dims) => { - surface_extent = Extent2D { - width: dims.width, - height: dims.height, - }; - should_configure_swapchain = true; - } - WindowEvent::KeyboardInput { input, .. } => { - if let Some(virtual_keycode) = input.virtual_keycode { - handle_text_input( - &mut text_state, - input.state, - virtual_keycode, - keyboard_modifiers, - ); + } => *control_flow = winit::event_loop::ControlFlow::Exit, + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::Resized(new_size), + .. + } => { + size = new_size; + + swap_chain = device.create_swap_chain( + &surface, + &wgpu::SwapChainDescriptor { + usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, + format: render_format, + width: size.width, + height: size.height, + present_mode: wgpu::PresentMode::Immediate, + }, + ); + } + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::ReceivedCharacter(ch), + .. + } => { + match ch { + '\u{8}' => { + // In Linux, we get one of these when you press + // backspace, but in macOS we don't. In both, we + // get a Back keydown event. Therefore, we use the + // Back keydown event and ignore this, resulting + // in a system that works in both Linux and macOS. } - } - WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { - surface_extent = Extent2D { - width: new_inner_size.width, - height: new_inner_size.height, - }; - should_configure_swapchain = true; - } - WindowEvent::ModifiersChanged(modifiers) => { - keyboard_modifiers = modifiers; - } - _ => (), - }, - Event::MainEventsCleared => window.request_redraw(), - Event::RedrawRequested(_) => { - let res: &mut Resources<_> = &mut resource_holder.0; - let render_pass = &res.render_passes[0]; - let pipeline_layout = &res.pipeline_layouts[0]; - let pipeline = &res.pipelines[0]; - - let triangles = text_state.chars().enumerate().map(|(index, char)| { - if char == ' ' { - PushConstants { - color: [0.0, 0.0, 0.0, 0.0], - pos: [0.0, 0.0], - scale: [0.00, 0.00], - } - } else { - PushConstants { - color: [1.0, 1.0, 1.0, 1.0], - pos: [0.06 * index as f32, 0.0], - scale: [0.05, 0.05], - } + '\u{e000}'..='\u{f8ff}' + | '\u{f0000}'..='\u{ffffd}' + | '\u{100000}'..='\u{10fffd}' => { + // These are private use characters; ignore them. + // See http://www.unicode.org/faq/private_use.html } - }); - - unsafe { - use gfx_hal::pool::CommandPool; - - // We refuse to wait more than a second, to avoid hanging. - let render_timeout_ns = 1_000_000_000; - - res.device - .wait_for_fence(&res.submission_complete_fence, render_timeout_ns) - .expect("Out of memory or device lost"); - - res.device - .reset_fence(&res.submission_complete_fence) - .expect("Out of memory"); - - res.command_pool.reset(false); - } - - if should_configure_swapchain { - use gfx_hal::window::SwapchainConfig; - - let caps = res.surface.capabilities(&adapter.physical_device); - - let mut swapchain_config = - SwapchainConfig::from_caps(&caps, surface_color_format, surface_extent); - - // This seems to fix some fullscreen slowdown on macOS. - if caps.image_count.contains(&3) { - swapchain_config.image_count = 3; + _ => { + text_state.push(ch); } - - surface_extent = swapchain_config.extent; - - unsafe { - res.surface - .configure_swapchain(&res.device, swapchain_config) - .expect("Failed to configure swapchain"); - }; - - should_configure_swapchain = false; - } - - let surface_image = unsafe { - // We refuse to wait more than a second, to avoid hanging. - let acquire_timeout_ns = 1_000_000_000; - - match res.surface.acquire_image(acquire_timeout_ns) { - Ok((image, _)) => image, - Err(_) => { - should_configure_swapchain = true; - return; - } - } - }; - - let framebuffer = unsafe { - use std::borrow::Borrow; - - use gfx_hal::image::Extent; - - res.device - .create_framebuffer( - render_pass, - vec![surface_image.borrow()], - Extent { - width: surface_extent.width, - height: surface_extent.height, - depth: 1, - }, - ) - .unwrap() - }; - - let viewport = { - use gfx_hal::pso::{Rect, Viewport}; - - Viewport { - rect: Rect { - x: 0, - y: 0, - w: surface_extent.width as i16, - h: surface_extent.height as i16, - }, - depth: 0.0..1.0, - } - }; - - unsafe { - use gfx_hal::command::{ - ClearColor, ClearValue, CommandBuffer, CommandBufferFlags, SubpassContents, - }; - - command_buffer.begin_primary(CommandBufferFlags::ONE_TIME_SUBMIT); - - command_buffer.set_viewports(0, &[viewport.clone()]); - command_buffer.set_scissors(0, &[viewport.rect]); - command_buffer.begin_render_pass( - render_pass, - &framebuffer, - viewport.rect, - &[ClearValue { - color: ClearColor { - float32: [0.0, 0.0, 0.0, 1.0], - }, - }], - SubpassContents::Inline, - ); - command_buffer.bind_graphics_pipeline(pipeline); - - for triangle in triangles { - use gfx_hal::pso::ShaderStageFlags; - - command_buffer.push_graphics_constants( - pipeline_layout, - ShaderStageFlags::VERTEX, - 0, - push_constant_bytes(&triangle), - ); - - command_buffer.draw(0..3, 0..1); - } - - command_buffer.end_render_pass(); - command_buffer.finish(); - } - - unsafe { - use gfx_hal::queue::{CommandQueue, Submission}; - - let submission = Submission { - command_buffers: vec![&command_buffer], - wait_semaphores: None, - signal_semaphores: vec![&res.rendering_complete_semaphore], - }; - - queue_group.queues[0].submit(submission, Some(&res.submission_complete_fence)); - let result = queue_group.queues[0].present_surface( - &mut res.surface, - surface_image, - Some(&res.rendering_complete_semaphore), - ); - - should_configure_swapchain |= result.is_err(); - - res.device.destroy_framebuffer(framebuffer); } } - _ => (), + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::KeyboardInput { input, .. }, + .. + } => { + if let Some(virtual_keycode) = input.virtual_keycode { + handle_keydown( + &mut text_state, + input.state, + virtual_keycode, + keyboard_modifiers, + ); + } + } + winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::ModifiersChanged(modifiers), + .. + } => { + keyboard_modifiers = modifiers; + } + winit::event::Event::MainEventsCleared => window.request_redraw(), + winit::event::Event::RedrawRequested { .. } => { + // Get a command encoder for the current frame + let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("Redraw"), + }); + + // Get the next frame + let frame = swap_chain.get_next_texture().expect("Get next frame"); + + // Clear frame + { + let _ = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { + attachment: &frame.view, + resolve_target: None, + load_op: wgpu::LoadOp::Clear, + store_op: wgpu::StoreOp::Store, + clear_color: wgpu::Color { + r: 0.007, + g: 0.007, + b: 0.007, + a: 1.0, + }, + }], + depth_stencil_attachment: None, + }); + } + + glyph_brush.queue(Section { + screen_position: (30.0, 30.0), + bounds: (size.width as f32, size.height as f32), + text: vec![Text::new("Enter some text:") + .with_color([0.4666, 0.2, 1.0, 1.0]) + .with_scale(40.0)], + ..Section::default() + }); + + glyph_brush.queue(Section { + screen_position: (30.0, 90.0), + bounds: (size.width as f32, size.height as f32), + text: vec![Text::new(format!("{}|", text_state).as_str()) + .with_color([1.0, 1.0, 1.0, 1.0]) + .with_scale(40.0)], + ..Section::default() + }); + + // Draw the text! + glyph_brush + .draw_queued(&device, &mut encoder, &frame.view, size.width, size.height) + .expect("Draw queued"); + + queue.submit(&[encoder.finish()]); + } + _ => { + *control_flow = winit::event_loop::ControlFlow::Wait; + } } - }); + }) } -/// Compile some GLSL shader source to SPIR-V. -/// TODO do this at build time - possibly in CI only -fn compile_shader(glsl: &str, shader_type: ShaderType) -> Vec { - use std::io::{Cursor, Read}; - - let mut compiled_file = - glsl_to_spirv::compile(glsl, shader_type).expect("Failed to compile shader"); - - let mut spirv_bytes = vec![]; - compiled_file.read_to_end(&mut spirv_bytes).unwrap(); - - gfx_hal::pso::read_spirv(Cursor::new(&spirv_bytes)).expect("Invalid SPIR-V") -} - -/// Returns a view of a struct as a slice of `u32`s. -unsafe fn push_constant_bytes(push_constants: &T) -> &[u32] { - let size_in_bytes = std::mem::size_of::(); - let size_in_u32s = size_in_bytes / std::mem::size_of::(); - let start_ptr = push_constants as *const T as *const u32; - std::slice::from_raw_parts(start_ptr, size_in_u32s) -} - -fn handle_text_input( +fn handle_keydown( text_state: &mut String, elem_state: ElementState, virtual_keycode: VirtualKeyCode, @@ -576,109 +240,12 @@ fn handle_text_input( } match virtual_keycode { - Key1 | Numpad1 => text_state.push_str("1"), - Key2 | Numpad2 => text_state.push_str("2"), - Key3 | Numpad3 => text_state.push_str("3"), - Key4 | Numpad4 => text_state.push_str("4"), - Key5 | Numpad5 => text_state.push_str("5"), - Key6 | Numpad6 => text_state.push_str("6"), - Key7 | Numpad7 => text_state.push_str("7"), - Key8 | Numpad8 => text_state.push_str("8"), - Key9 | Numpad9 => text_state.push_str("9"), - Key0 | Numpad0 => text_state.push_str("0"), - A => text_state.push_str("a"), - B => text_state.push_str("b"), - C => text_state.push_str("c"), - D => text_state.push_str("d"), - E => text_state.push_str("e"), - F => text_state.push_str("f"), - G => text_state.push_str("g"), - H => text_state.push_str("h"), - I => text_state.push_str("i"), - J => text_state.push_str("j"), - K => text_state.push_str("k"), - L => text_state.push_str("l"), - M => text_state.push_str("m"), - N => text_state.push_str("n"), - O => text_state.push_str("o"), - P => text_state.push_str("p"), - Q => text_state.push_str("q"), - R => text_state.push_str("r"), - S => text_state.push_str("s"), - T => text_state.push_str("t"), - U => text_state.push_str("u"), - V => text_state.push_str("v"), - W => text_state.push_str("w"), - X => text_state.push_str("x"), - Y => text_state.push_str("y"), - Z => text_state.push_str("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 - dbg!(virtual_keycode); - } Back => { + // Backspace deletes a character. + // In Linux, we get a Unicode character for backspace events + // (which is handled elsewhere), but on macOS we only get one of these. text_state.pop(); } - Return | NumpadEnter => { - text_state.push_str("\n"); - } - Space => { - text_state.push_str(" "); - } - Comma | NumpadComma => { - text_state.push_str(","); - } - Add => { - text_state.push_str("+"); - } - Apostrophe => { - text_state.push_str("'"); - } - At => { - text_state.push_str("@"); - } - Backslash => { - text_state.push_str("\\"); - } - Colon => { - text_state.push_str(":"); - } - Period | Decimal => { - text_state.push_str("."); - } - Equals | NumpadEquals => { - text_state.push_str("="); - } - Grave => { - text_state.push_str("`"); - } - Minus | Subtract => { - text_state.push_str("-"); - } - Multiply => { - text_state.push_str("*"); - } - Semicolon => { - text_state.push_str(";"); - } - Slash | Divide => { - text_state.push_str("/"); - } - Underline => { - text_state.push_str("_"); - } - Yen => { - text_state.push_str("¥"); - } Copy => { todo!("copy"); } @@ -688,5 +255,6 @@ fn handle_text_input( Cut => { todo!("cut"); } + _ => {} } } diff --git a/editor/src/text_state.rs b/editor/src/text_state.rs new file mode 100644 index 0000000000..42f6474482 --- /dev/null +++ b/editor/src/text_state.rs @@ -0,0 +1,128 @@ +use winit::event::{ElementState, ModifiersState, VirtualKeyCode}; + +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_str("1"), + Key2 | Numpad2 => text_state.push_str("2"), + Key3 | Numpad3 => text_state.push_str("3"), + Key4 | Numpad4 => text_state.push_str("4"), + Key5 | Numpad5 => text_state.push_str("5"), + Key6 | Numpad6 => text_state.push_str("6"), + Key7 | Numpad7 => text_state.push_str("7"), + Key8 | Numpad8 => text_state.push_str("8"), + Key9 | Numpad9 => text_state.push_str("9"), + Key0 | Numpad0 => text_state.push_str("0"), + A => text_state.push_str("a"), + B => text_state.push_str("b"), + C => text_state.push_str("c"), + D => text_state.push_str("d"), + E => text_state.push_str("e"), + F => text_state.push_str("f"), + G => text_state.push_str("g"), + H => text_state.push_str("h"), + I => text_state.push_str("i"), + J => text_state.push_str("j"), + K => text_state.push_str("k"), + L => text_state.push_str("l"), + M => text_state.push_str("m"), + N => text_state.push_str("n"), + O => text_state.push_str("o"), + P => text_state.push_str("p"), + Q => text_state.push_str("q"), + R => text_state.push_str("r"), + S => text_state.push_str("s"), + T => text_state.push_str("t"), + U => text_state.push_str("u"), + V => text_state.push_str("v"), + W => text_state.push_str("w"), + X => text_state.push_str("x"), + Y => text_state.push_str("y"), + Z => text_state.push_str("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_str("\n"); + } + Space => { + text_state.push_str(" "); + } + Comma | NumpadComma => { + text_state.push_str(","); + } + Add => { + text_state.push_str("+"); + } + Apostrophe => { + text_state.push_str("'"); + } + At => { + text_state.push_str("@"); + } + Backslash => { + text_state.push_str("\\"); + } + Colon => { + text_state.push_str(":"); + } + Period | Decimal => { + text_state.push_str("."); + } + Equals | NumpadEquals => { + text_state.push_str("="); + } + Grave => { + text_state.push_str("`"); + } + Minus | Subtract => { + text_state.push_str("-"); + } + Multiply => { + text_state.push_str("*"); + } + Semicolon => { + text_state.push_str(";"); + } + Slash | Divide => { + text_state.push_str("/"); + } + Underline => { + text_state.push_str("_"); + } + Yen => { + text_state.push_str("¥"); + } + Copy => { + todo!("copy"); + } + Paste => { + todo!("paste"); + } + Cut => { + todo!("cut"); + } + } +} diff --git a/www/favicon.svg b/www/favicon.svg deleted file mode 100644 index e0cff74b57..0000000000 --- a/www/favicon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/www/index.html b/www/index.html deleted file mode 100644 index 35234a94db..0000000000 --- a/www/index.html +++ /dev/null @@ -1,145 +0,0 @@ - - - - - - - The Roc Programming Language - - - - - - - - -
-
- -
-
- -
-
-

Str

-

Dealing with text is a deep topic, so by design, Roc's Str module sticks - to the basics. -

-

For more advanced use cases like working with raw code points, - see the roc/unicode package. For locale-specific text - functions (including capitalizing a string, as capitalization rules vary by locale) - see the roc/locale package.

-

Unicode

-

Unicode can represent text values which span multiple languages, symbols, and emoji. - Here are some valid Roc strings:

-
"Roc"
-"鹏"
-"🐼"
-

Every Unicode string is a sequence of grapheme clusters. - A grapheme cluster corresponds to what a person reading a string might call a "character", - but because the term "character" is used to mean many different concepts across - different programming languages, the documentation for Roc strings intentionally - avoids it. Instead, we use the term "clusters" as a shorthand for "grapheme clusters."

-

You can get the number of grapheme clusters in a string by calling Str.countClusters on it:

-
Str.countClusters "Roc!"   # 4
-Str.countClusters "折り紙"  # 3
-Str.countClusters "🐼"     # 1
-

The countClusters function walks through the entire string to get its answer, - so if you want to check whether a string is empty, you'll get much better performance - by calling Str.isEmpty myStr instead of Str.countClusters myStr == 0.

-

Escape sequences

-

If you put a \ in a Roc string literal, it begins an escape sequence. - An escape sequence is a convenient way to insert certain strings into other strings. - For example, suppose you write this Roc string:

-
"It wasn't a rock\nIt was a rock lobster!"
-

The "\n" in the middle will insert a line break into this string. There are - other ways of getting a line break in there, but "\n" is the most common.

-

Another way you could insert a newline is by writing \u{0x0A} instead of \n. - That would result in the same string, because the \u escape sequence inserts - Unicode code points directly into - the string. The Unicode code point 10 is a newline, and 10 is 0A in hexadecimal. - 0x0A is a Roc hexadecimal literal, and \u escape sequences are always - followed by a hexadecimal literal inside { and } like this.

-

As another example, "R\u{0x6F}c" is the same string as "Roc", because - "\u{0x6F}" corresponds to the Unicode code point for lowercase o. If you - want to spice things up a bit, - you can write "R\u{0xF6}c" as an alternative way to get the string "Röc".

-

Roc strings also support these escape sequences:

-
    -
  • \\ - an actual backslash (writing a single \ always begins an escape sequence!)
  • -
  • \" - an actual quotation mark (writing a " without a \ ends the string)
  • -
  • \r - carriage return
  • -
  • \t - horizontal tab
  • -
  • \v - vertical tab
  • -
-

You can also use escape sequences to insert named strings into other strings, like so:

-
name = "Lee"
-city = "Roctown"
-
-greeting = "Hello, \(name)! Welcome to \(city)."
- -

Here, greeting will become the string "Hello, Lee! Welcome to Roctown.". - This is known as string interpolation, - and you can use it as many times as you like inside a string. The name - between the parentheses must refer to a Str value that is currently in - scope, and it must be a name - it can't be an arbitrary expression like a function call.

-

Encoding

-

Roc strings are not coupled to any particular - encoding. As it happens, - they are currently encoded in UTF-8, but this module is intentionally designed - not to rely on that implementation detail so that a future release of Roc can - potentially change it without breaking existing Roc applications. (UTF-8 - seems pretty great today, but so did UTF-16 at an earlier point in history.)

-

This module has functions to can convert a Str to a List of raw code unit - integers (not to be confused with the code points - mentioned earlier) in a particular encoding. If you need encoding-specific functions, - you should take a look at the roc/unicode package. - It has many more tools than this module does! -

Types

- Str -

A Unicode text value.

-
-
-
-
-

Made by people who like to make nice things.

-

© 2020-present

-
- - diff --git a/www/logo.svg b/www/logo.svg deleted file mode 100644 index 8cdc52a7d3..0000000000 --- a/www/logo.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/www/styles.css b/www/styles.css deleted file mode 100644 index 018aa05973..0000000000 --- a/www/styles.css +++ /dev/null @@ -1,399 +0,0 @@ -:root { - --link-color: #5c0bff; - --text-color: #333333; - --logo-solid: #aaaaaa; - --code-color: #222222; - --main-bg-color: #fdfdfd; - --border-bg-color: #E9E9E9; - --code-bg-color: #eeeeee; - --logo-gradient-start: #aaaaaa; - --logo-gradient-mid: #777777; - --logo-gradient-end: #333333; - --faded-color: #4C4C4C; - --monospace-font; - --font-sans: -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif; - --font-mono: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; - --sidebar-width: 240px; -} - -a { - color: #972395; -} - -.top-header { - box-sizing: border-box; - flex-direction: row; - align-items: center; - display: flex; - font-family: var(--font-sans); - font-size: 24px; -} - -.main-nav { - display: flex; - flex-direction: row; - align-items: center; - flex-wrap: nowrap; - flex-grow: 1; - box-sizing: border-box; - padding: 6px 0; - min-width: 0; /* necessary for text-overflow: ellipsis to work in descendants */ -} - -.logo { - padding-left: 16px; - padding-right: 8px; -} - -.logo svg { - height: 48px; - width: 48px; -} - -.logo:hover { - text-decoration: none; -} - -.logo-solid { - fill: url("#logo-gradient") -} - -.logo:hover .logo-solid { - fill: var(--link-color); -} - -.pkg-full-name { - color: var(--text-color); - display: inline-block; - font-size: 32px; - margin: 0 18px; - font-weight: normal; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - padding-bottom: 8px; -} - -.pkg-full-name a { - color: inherit; -} - -a { - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - -.pkg-and-logo { - min-width: 0; /* necessary for text-overflow: ellipsis to work in descendants */ - display: flex; - align-items: flex-end; - height: 55px; -} - -.main-container { - min-width: 0; /* necessary for text-overflow: ellipsis to work in descendants */ -} - -.search-button { - flex-shrink: 0; /* always shrink the package name before these; they have a relatively constrained length */ - padding: 12px 18px; - margin-right: 42px; - display: none; /* only show this in the mobile view */ -} - -.version { - font-weight: bold; - padding: 10px; - margin-right: 8px; - color: var(--faded-color); -} - -.container { - box-sizing: border-box; - display: flex; - flex-direction: column; - margin: 0 auto; - min-width: 780px; - max-width: 1280px; -} - -body { - box-sizing: border-box; - margin: 0; - padding: 0; - font-family: var(--font-sans); - color: var(--text-color); - background-color: var(--border-bg-color); -} - -p { - overflow-wrap: break-word; - margin: 24px 0; -} - -footer { - font-size: 14px; - box-sizing: border-box; - padding: 16px; -} - -footer p { - display: inline-block; - margin-top: 0; - margin-bottom: 8px; -} - -.content { - box-sizing: border-box; - display: flex; - flex-direction: row-reverse; /* We want the sidebar in the DOM first for tab ordering, but it should render on the right. */ - justify-content: space-between; -} - -main { - box-sizing: border-box; - position: relative; - font-size: 18px; - line-height: 1.85em; - padding: 32px; - background-color: var(--main-bg-color); - clip-path: polygon(0 100%, 100% 100%, 100% 48px, calc(100% - 48px) 0, 0px 0px); -} - -.sidebar { - position: relative; - box-sizing: border-box; - padding-left: 20px; - width: var(--sidebar-width); - margin-top: 128px; - flex-shrink: 0; -} - -#sidebar-heading { - font-size: 32px; - font-weight: normal; - margin: 0; - padding: 0; - color: var(--faded-color); - font-family: var(--font-sans); - cursor: default; -} - -.module-name { - font-size: 56px; - line-height: 1em; - font-family: var(--font-mono); - font-weight: bold; - margin-top: 18px; - margin-bottom: 48px; -} - -.module-name a:visited { - color: var(--link-color); -} - -.sidebar-link { - box-sizing: border-box; - font-size: 18px; - font-family: var(--font-mono); - font-weight: bold; - color: var(--faded-color); - display: block; - width: 100%; - padding: 8px 16px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -a { - color: var(--link-color); -} - -a:visited { - color: inherit; -} - -h3 { - font-size: 32px; - margin: 48px 0 24px 0; -} - -h4 { - font-size: 24px; -} - -.type-def { - font-size: 24px; - color: var(--link-color); -} - -.code-snippet { - padding: 12px 16px; - display: block; - box-sizing: border-box; - font-family: var(--font-mono); - background-color: var(--border-bg-color); -} - -code { - font-family: var(--font-mono); - color: var(--code-color); - background-color: var(--code-bg-color); - padding: 0 8px; - display: inline-block; -} - -code a { - color: var(--link-color); -} - -code a:visited { - color: var(--link-color); -} - -pre { - margin: 36px 0; - padding: 8px; - box-sizing: border-box; - background-color: var(--code-bg-color); - overflow-x: auto; -} - -#module-search { - width: 100%; - font-family: var(--font-mono); - display: block; - box-sizing: border-box; - font-size: 18px; - margin: 12px 0; - padding: 12px 16px; - border: none; - color: var(--faded-color); - background: none; -} - -#module-search::placeholder { - color: var(--faded-color); - opacity: 1; -} - -#module-search:hover { - background-color: var(--main-bg-color); -} - -#module-search:focus { - background-color: var(--main-bg-color); -} - -@media (prefers-color-scheme: dark) { - :root { - --main-bg-color: #363636; - --border-bg-color: #111111; - --code-color: #eeeeee; - --code-bg-color: #2b2b2b; - --text-color: #cccccc; - --logo-solid: #777777; - --faded-color: #bbbbbb; - --link-color: #b894ff; - --logo-gradient-start: #333333; - --logo-gradient-mid: #777777; - --logo-gradient-end: #aaaaaa; - } - - html { - scrollbar-color: #444444 #2f2f2f; - } -} - -@media only screen and (max-device-width: 480px) { - .search-button { - display: block; /* This is only visible in mobile. */ - } - - .top-header { - width: auto; - } - - .pkg-full-name { - margin-left: 8px; - margin-right: 12px; - font-size: 24px; - padding-bottom: 14px; - } - - .pkg-full-name a { - vertical-align: middle; - padding: 18px 0; - } - - .logo { - padding-left: 2px; - width: 50px; - height: 54px; - } - - .version { - margin: 0; - font-weight: normal; - font-size: 18px; - padding-bottom: 16px; - } - - .module-name { - font-size: 36px; - margin-top: 8px; - margin-bottom: 8px; - max-width: calc(100% - 18px); - overflow: hidden; - text-overflow: ellipsis; - } - - main { - padding: 18px; - font-size: 16px; - } - - .container { - margin: 0; - min-width: 320px; - max-width: 100%; - } - - .content { - flex-direction: column; - } - - .sidebar { - margin-top: 0; - padding-left: 0; - width: auto; - } - - #sidebar-heading { - font-size: 24px; - margin: 16px; - } - - h3 { - font-size: 18px; - margin: 0; - padding: 0; - } - - h4 { - font-size: 16px; - } - - .main-nav { - justify-content: space-between; - } - - .content { - /* Display the sidebar below
without affecting tab index */ - flex-direction: column-reverse; - } -}