From ad3b40d538e56f19c73514de03728d8dde6738d8 Mon Sep 17 00:00:00 2001 From: Ulyssa Date: Thu, 6 Apr 2023 16:10:48 -0700 Subject: [PATCH] Interpret newlines as line breaks when converting Markdown to HTML (#74) --- Cargo.lock | 386 +++++++++++++++++++++++++++++++++++---- Cargo.toml | 3 +- src/message/html.rs | 20 +- src/message/mod.rs | 59 ++++++ src/message/printer.rs | 9 +- src/windows/room/chat.rs | 7 +- 6 files changed, 442 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 048603e..d7ef7e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -215,6 +215,30 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitflags" version = "1.3.2" @@ -397,6 +421,7 @@ dependencies = [ "once_cell", "strsim", "termcolor", + "terminal_size", ] [[package]] @@ -456,6 +481,25 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "comrak" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "482aa5695bca086022be453c700a40c02893f1ba7098a2c88351de55341ae894" +dependencies = [ + "clap", + "entities", + "memchr", + "once_cell", + "regex", + "shell-words", + "slug", + "syntect", + "typed-arena", + "unicode_categories", + "xdg", +] + [[package]] name = "const-oid" version = "0.7.1" @@ -777,6 +821,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "deunicode" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "850878694b7933ca4c9569d30a34b55031b9b139ee1fc7b94a527c4ef960d690" + [[package]] name = "digest" version = "0.9.0" @@ -889,6 +939,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" +[[package]] +name = "entities" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5320ae4c3782150d900b79807611a59a99fc9a1d61d686faafc24b93fc8d7ca" + [[package]] name = "errno" version = "0.2.8" @@ -900,6 +956,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "errno" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.45.0", +] + [[package]] name = "errno-dragonfly" version = "0.1.2" @@ -926,6 +993,16 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "fancy-regex" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6b8560a05112eb52f04b00e5d3790c0dd75d9d980eb8a122fb23b92a623ccf" +dependencies = [ + "bit-set", + "regex", +] + [[package]] name = "flate2" version = "1.0.25" @@ -1337,6 +1414,7 @@ dependencies = [ "bitflags 1.3.2", "chrono", "clap", + "comrak", "css-color-parser", "dirs", "emojis", @@ -1499,7 +1577,7 @@ checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", - "rustix", + "rustix 0.36.11", "windows-sys 0.45.0", ] @@ -1569,6 +1647,15 @@ version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +[[package]] +name = "line-wrap" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" +dependencies = [ + "safemem", +] + [[package]] name = "link-cplusplus" version = "1.0.8" @@ -1578,12 +1665,24 @@ dependencies = [ "cc", ] +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "linux-raw-sys" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +[[package]] +name = "linux-raw-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" + [[package]] name = "lock_api" version = "0.4.9" @@ -2049,6 +2148,28 @@ version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +[[package]] +name = "onig" +version = "6.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c4b31c8722ad9171c6d77d3557db078cab2bd50afcc9d09c8b315c59df8ca4f" +dependencies = [ + "bitflags 1.3.2", + "libc", + "once_cell", + "onig_sys", +] + +[[package]] +name = "onig_sys" +version = "69.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b829e3d7e9cc74c7e315ee8edb185bf4190da5acde74afd7fc59c35b1f086e7" +dependencies = [ + "cc", + "pkg-config", +] + [[package]] name = "opaque-debug" version = "0.3.0" @@ -2258,6 +2379,26 @@ dependencies = [ "spki", ] +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "plist" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd9647b268a3d3e14ff09c23201133a62589c658db02bb7388c7246aafe0590" +dependencies = [ + "base64 0.21.0", + "indexmap", + "line-wrap", + "quick-xml", + "serde", + "time 0.3.20", +] + [[package]] name = "png" version = "0.17.7" @@ -2345,14 +2486,12 @@ dependencies = [ ] [[package]] -name = "pulldown-cmark" -version = "0.9.2" +name = "quick-xml" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63" +checksum = "e5c1a97b1bc42b1d550bfb48d4262153fe400a12bab1511821736f7eac76d7e2" dependencies = [ - "bitflags 1.3.2", "memchr", - "unicase", ] [[package]] @@ -2739,7 +2878,6 @@ dependencies = [ "js_int", "js_option", "percent-encoding", - "pulldown-cmark", "rand 0.8.5", "regex", "ruma-identifiers-validation", @@ -2807,10 +2945,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" dependencies = [ "bitflags 1.3.2", - "errno", + "errno 0.2.8", "io-lifetimes", "libc", - "linux-raw-sys", + "linux-raw-sys 0.1.4", + "windows-sys 0.45.0", +] + +[[package]] +name = "rustix" +version = "0.37.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aae838e49b3d63e9274e1c01833cc8139d3fec468c3b84688c628f44b1ae11d" +dependencies = [ + "bitflags 1.3.2", + "errno 0.3.0", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.1", "windows-sys 0.45.0", ] @@ -2841,6 +2993,21 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scopeguard" version = "1.1.0" @@ -2954,6 +3121,12 @@ dependencies = [ "lazy_static 1.4.0", ] +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + [[package]] name = "signal-hook" version = "0.3.15" @@ -3021,6 +3194,15 @@ dependencies = [ "parking_lot 0.11.2", ] +[[package]] +name = "slug" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3bc762e6a4b6c6fcaade73e77f9ebc6991b676f88bb2358bddb56560f073373" +dependencies = [ + "deunicode", +] + [[package]] name = "smallvec" version = "1.10.0" @@ -3147,6 +3329,30 @@ dependencies = [ "unicode-xid 0.2.4", ] +[[package]] +name = "syntect" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6c454c27d9d7d9a84c7803aaa3c50cd088d2906fe3c6e42da3209aa623576a8" +dependencies = [ + "bincode", + "bitflags 1.3.2", + "fancy-regex", + "flate2", + "fnv", + "lazy_static 1.4.0", + "once_cell", + "onig", + "plist", + "regex-syntax", + "serde", + "serde_derive", + "serde_json", + "thiserror", + "walkdir", + "yaml-rust", +] + [[package]] name = "tendril" version = "0.4.3" @@ -3167,6 +3373,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "terminal_size" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237" +dependencies = [ + "rustix 0.37.7", + "windows-sys 0.48.0", +] + [[package]] name = "thiserror" version = "1.0.40" @@ -3435,6 +3651,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + [[package]] name = "typenum" version = "1.16.0" @@ -3495,6 +3717,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + [[package]] name = "universal-hash" version = "0.4.1" @@ -3594,6 +3822,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.0" @@ -3792,13 +4030,13 @@ version = "0.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04662ed0e3e5630dfa9b26e4cb823b817f1a9addda855d973a9458c236556244" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -3807,7 +4045,7 @@ version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", ] [[package]] @@ -3816,13 +4054,13 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -3831,7 +4069,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", ] [[package]] @@ -3840,13 +4087,28 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] @@ -3855,42 +4117,84 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "winnow" version = "0.4.0" @@ -3943,6 +4247,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "xdg" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4583db5cbd4c4c0303df2d15af80f0539db703fa1c68802d4cbbd2dd0f88f6" +dependencies = [ + "dirs", +] + [[package]] name = "xml5ever" version = "0.17.0" @@ -3954,6 +4267,15 @@ dependencies = [ "markup5ever", ] +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "zeroize" version = "1.3.0" diff --git a/Cargo.toml b/Cargo.toml index 999c313..e0cfa18 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ rust-version = "1.66" bitflags = "1.3.2" chrono = "0.4" clap = {version = "4.0", features = ["derive"]} +comrak = "0.18.0" css-color-parser = "0.1.2" dirs = "4.0.0" emojis = "~0.5.2" @@ -44,7 +45,7 @@ version = "0.0.14" [dependencies.matrix-sdk] version = "0.6" default-features = false -features = ["e2e-encryption", "markdown", "sled", "rustls-tls"] +features = ["e2e-encryption", "sled", "rustls-tls"] [dependencies.tokio] version = "1.24.1" diff --git a/src/message/html.rs b/src/message/html.rs index 161da0f..c5620ac 100644 --- a/src/message/html.rs +++ b/src/message/html.rs @@ -1027,9 +1027,8 @@ pub mod tests { Span::raw("│"), Span::raw(" "), Span::raw("│"), - Span::styled(" ", bold), Span::styled("3", bold), - Span::styled(" ", bold), + Span::styled(" ", bold), Span::raw("│") ]); @@ -1157,4 +1156,21 @@ pub mod tests { assert_eq!(text.lines[1], Spans(vec![Span::raw("World"), Span::raw(" "),])); assert_eq!(text.lines[2], Spans(vec![Span::raw("Goodbye")]),); } + + #[test] + fn test_embedded_newline() { + let s = "

Hello\nWorld

"; + let tree = parse_matrix_html(s); + let text = tree.to_text(15, Style::default(), true); + assert_eq!(text.lines.len(), 1); + assert_eq!( + text.lines[0], + Spans(vec![ + Span::raw("Hello"), + Span::raw(" "), + Span::raw("World"), + Span::raw(" ") + ]) + ); + } } diff --git a/src/message/mod.rs b/src/message/mod.rs index 544c065..b80aae2 100644 --- a/src/message/mod.rs +++ b/src/message/mod.rs @@ -7,6 +7,7 @@ use std::hash::{Hash, Hasher}; use std::slice::Iter; use chrono::{DateTime, Local as LocalTz, NaiveDateTime, TimeZone}; +use comrak::{markdown_to_html, ComrakOptions}; use unicode_width::UnicodeWidthStr; use matrix_sdk::ruma::{ @@ -26,6 +27,7 @@ use matrix_sdk::ruma::{ Relation, RoomMessageEvent, RoomMessageEventContent, + TextMessageEventContent, }, redaction::SyncRoomRedactionEvent, }, @@ -93,6 +95,19 @@ const USER_GUTTER_EMPTY_SPAN: Span<'static> = span_static(USER_GUTTER_EMPTY); const TIME_GUTTER_EMPTY: &str = " "; const TIME_GUTTER_EMPTY_SPAN: Span<'static> = span_static(TIME_GUTTER_EMPTY); +fn text_to_message_content(input: String) -> TextMessageEventContent { + let mut options = ComrakOptions::default(); + options.render.hardbreaks = true; + let html = markdown_to_html(input.as_str(), &options); + + TextMessageEventContent::html(input, html) +} + +pub fn text_to_message(input: String) -> RoomMessageEventContent { + let msg = MessageType::Text(text_to_message_content(input)); + RoomMessageEventContent::new(msg) +} + #[inline] fn millis_to_datetime(ms: UInt) -> DateTime { let time = i64::from(ms) / 1000; @@ -972,4 +987,48 @@ pub mod tests { // MessageCursor::latest() should point at the most recent message after conversion. assert_eq!(identity(&mc6), mc1); } + + #[test] + fn test_markdown_message() { + let input = "**bold**\n"; + let content = text_to_message_content(input.into()); + assert_eq!(content.body, input); + assert_eq!(content.formatted.unwrap().body, "

bold

\n"); + + let input = "*emphasis*\n"; + let content = text_to_message_content(input.into()); + assert_eq!(content.body, input); + assert_eq!(content.formatted.unwrap().body, "

emphasis

\n"); + + let input = "`code`\n"; + let content = text_to_message_content(input.into()); + assert_eq!(content.body, input); + assert_eq!(content.formatted.unwrap().body, "

code

\n"); + + let input = "```rust\nconst A: usize = 1;\n```\n"; + let content = text_to_message_content(input.into()); + assert_eq!(content.body, input); + assert_eq!( + content.formatted.unwrap().body, + "
const A: usize = 1;\n
\n" + ); + + let input = "para 1\n\npara 2\n"; + let content = text_to_message_content(input.into()); + assert_eq!(content.body, input); + assert_eq!(content.formatted.unwrap().body, "

para 1

\n

para 2

\n"); + + let input = "line 1\nline 2\n"; + let content = text_to_message_content(input.into()); + assert_eq!(content.body, input); + assert_eq!(content.formatted.unwrap().body, "

line 1
\nline 2

\n"); + + let input = "# Heading\n## Subheading\n\ntext\n"; + let content = text_to_message_content(input.into()); + assert_eq!(content.body, input); + assert_eq!( + content.formatted.unwrap().body, + "

Heading

\n

Subheading

\n

text

\n" + ); + } } diff --git a/src/message/printer.rs b/src/message/printer.rs index 7d946a9..03196b9 100644 --- a/src/message/printer.rs +++ b/src/message/printer.rs @@ -156,8 +156,13 @@ impl<'a> TextPrinter<'a> { pub fn push_str(&mut self, s: &'a str, style: Style) { let style = self.base_style.patch(style); - for word in UnicodeSegmentation::split_word_bounds(s) { - if self.width == 0 && word.chars().all(char::is_whitespace) { + for mut word in UnicodeSegmentation::split_word_bounds(s) { + if let "\n" | "\r\n" = word { + // Render embedded newlines as spaces. + word = " "; + } + + if self.curr_width == 0 && word.chars().all(char::is_whitespace) { // Drop leading whitespace. continue; } diff --git a/src/windows/room/chat.rs b/src/windows/room/chat.rs index 88ae70d..1e06453 100644 --- a/src/windows/room/chat.rs +++ b/src/windows/room/chat.rs @@ -75,7 +75,7 @@ use crate::base::{ SendAction, }; -use crate::message::{Message, MessageEvent, MessageKey, MessageTimeStamp}; +use crate::message::{text_to_message, Message, MessageEvent, MessageKey, MessageTimeStamp}; use crate::worker::Requester; use super::scrollback::{Scrollback, ScrollbackState}; @@ -407,10 +407,7 @@ impl ChatState { return Ok(None); } - let msg = TextMessageEventContent::markdown(msg.to_string()); - let msg = MessageType::Text(msg); - - let mut msg = RoomMessageEventContent::new(msg); + let mut msg = text_to_message(msg.to_string()); if let Some((_, event_id)) = &self.editing { msg.relates_to = Some(Relation::Replacement(Replacement::new(