Detailed changes
@@ -8,7 +8,7 @@ version = "0.1.0"
dependencies = [
"auto_update",
"editor",
- "futures",
+ "futures 0.3.24",
"gpui",
"language",
"project",
@@ -52,9 +52,9 @@ dependencies = [
[[package]]
name = "aho-corasick"
-version = "0.7.18"
+version = "0.7.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
+checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
dependencies = [
"memchr",
]
@@ -113,6 +113,15 @@ version = "0.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec8ad6edb4840b78c5c3d88de606b22252d552b55f3a4699fbb10fc070ec3049"
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "ansi_term"
version = "0.12.1"
@@ -124,9 +133,9 @@ dependencies = [
[[package]]
name = "anyhow"
-version = "1.0.58"
+version = "1.0.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704"
+checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602"
[[package]]
name = "arrayref"
@@ -148,9 +157,9 @@ checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
[[package]]
name = "ascii"
-version = "1.0.0"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbf56136a5198c7b01a49e3afcbef6cf84597273d298f54432926024107b0109"
+checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16"
[[package]]
name = "assets"
@@ -174,20 +183,33 @@ dependencies = [
[[package]]
name = "async-channel"
-version = "1.6.1"
+version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319"
+checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28"
dependencies = [
"concurrent-queue",
"event-listener",
"futures-core",
]
+[[package]]
+name = "async-compat"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b48b4ff0c2026db683dea961cd8ea874737f56cffca86fa84415eaddc51c00d"
+dependencies = [
+ "futures-core",
+ "futures-io",
+ "once_cell",
+ "pin-project-lite 0.2.9",
+ "tokio",
+]
+
[[package]]
name = "async-compression"
-version = "0.3.14"
+version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "345fd392ab01f746c717b1357165b76f0b67a60192007b234058c9045fdcf695"
+checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a"
dependencies = [
"flate2",
"futures-core",
@@ -212,21 +234,23 @@ dependencies = [
[[package]]
name = "async-fs"
-version = "1.5.0"
+version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b3ca4f8ff117c37c278a2f7415ce9be55560b846b5bc4412aaa5d29c1c3dae2"
+checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06"
dependencies = [
"async-lock",
+ "autocfg 1.1.0",
"blocking",
"futures-lite",
]
[[package]]
name = "async-io"
-version = "1.7.0"
+version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5e18f61464ae81cde0a23e713ae8fd299580c54d697a35820cfd0625b8b0e07"
+checksum = "83e21f3a490c72b3b0cf44962180e60045de2925d8dff97918f7ee43c8f637c7"
dependencies = [
+ "autocfg 1.1.0",
"concurrent-queue",
"futures-lite",
"libc",
@@ -251,11 +275,12 @@ dependencies = [
[[package]]
name = "async-net"
-version = "1.6.1"
+version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5373304df79b9b4395068fb080369ec7178608827306ce4d081cba51cac551df"
+checksum = "4051e67316bc7eff608fe723df5d32ed639946adcd69e07df41fd42a7b411f1f"
dependencies = [
"async-io",
+ "autocfg 1.1.0",
"blocking",
"futures-lite",
]
@@ -265,17 +290,18 @@ name = "async-pipe"
version = "0.1.3"
source = "git+https://github.com/zed-industries/async-pipe-rs?rev=82d00a04211cf4e1236029aa03e6b6ce2a74c553#82d00a04211cf4e1236029aa03e6b6ce2a74c553"
dependencies = [
- "futures",
+ "futures 0.3.24",
"log",
]
[[package]]
name = "async-process"
-version = "1.4.0"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf2c06e30a24e8c78a3987d07f0930edf76ef35e027e7bdb063fccafdad1f60c"
+checksum = "02111fd8655a613c25069ea89fc8d9bb89331fa77486eb3bc059ee757cfa481c"
dependencies = [
"async-io",
+ "autocfg 1.1.0",
"blocking",
"cfg-if 1.0.0",
"event-listener",
@@ -338,9 +364,9 @@ dependencies = [
[[package]]
name = "async-trait"
-version = "0.1.56"
+version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716"
+checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f"
dependencies = [
"proc-macro2",
"quote",
@@ -435,15 +461,15 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "axum"
-version = "0.5.11"
+version = "0.5.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2cc6e8e8c993cb61a005fab8c1e5093a29199b7253b05a6883999312935c1ff"
+checksum = "c9e3356844c4d6a6d6467b8da2cffb4a2820be256f50a3a386c9d152bab31043"
dependencies = [
"async-trait",
"axum-core",
"base64",
"bitflags",
- "bytes",
+ "bytes 1.2.1",
"futures-util",
"headers",
"http",
@@ -470,26 +496,28 @@ dependencies = [
[[package]]
name = "axum-core"
-version = "0.2.6"
+version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf4d047478b986f14a13edad31a009e2e05cb241f9805d0d75e4cba4e129ad4d"
+checksum = "d9f0c0a60006f2a293d82d571f635042a72edf927539b7685bd62d361963839b"
dependencies = [
"async-trait",
- "bytes",
+ "bytes 1.2.1",
"futures-util",
"http",
"http-body",
"mime",
+ "tower-layer",
+ "tower-service",
]
[[package]]
name = "axum-extra"
-version = "0.3.6"
+version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "277c75e6c814b061ae4947d02335d9659db9771b9950cca670002ae986372f44"
+checksum = "69034b3b0fd97923eee2ce8a47540edb21e07f48f87f67d44bb4271cec622bdb"
dependencies = [
"axum",
- "bytes",
+ "bytes 1.2.1",
"futures-util",
"http",
"mime",
@@ -505,16 +533,16 @@ dependencies = [
[[package]]
name = "backtrace"
-version = "0.3.65"
+version = "0.3.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61"
+checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7"
dependencies = [
"addr2line",
"cc",
"cfg-if 1.0.0",
"libc",
- "miniz_oxide 0.5.3",
- "object",
+ "miniz_oxide 0.5.4",
+ "object 0.29.0",
"rustc-demangle",
]
@@ -526,9 +554,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]]
name = "base64ct"
-version = "1.5.1"
+version = "1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3bdca834647821e0b13d9539a8634eb62d3501b6b6c2cec1722786ee6671b851"
+checksum = "ea2b2456fd614d856680dcd9fcc660a51a820fa09daef2e49772b56a193c8474"
[[package]]
name = "bincode"
@@ -585,9 +613,9 @@ dependencies = [
[[package]]
name = "block-buffer"
-version = "0.10.2"
+version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
+checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
dependencies = [
"generic-array",
]
@@ -645,15 +673,15 @@ dependencies = [
[[package]]
name = "bumpalo"
-version = "3.10.0"
+version = "3.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
+checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d"
[[package]]
name = "bytemuck"
-version = "1.10.0"
+version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c53dfa917ec274df8ed3c572698f381a24eef2efba9492d797301b72b6db408a"
+checksum = "2f5715e491b5a1598fc2bef5a606847b5dc1d48ea625bd3c02c00de8285591da"
[[package]]
name = "byteorder"
@@ -661,6 +689,16 @@ version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+[[package]]
+name = "bytes"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c"
+dependencies = [
+ "byteorder",
+ "iovec",
+]
+
[[package]]
name = "bytes"
version = "1.2.1"
@@ -758,12 +796,12 @@ dependencies = [
"bindgen",
"block",
"byteorder",
- "bytes",
+ "bytes 1.2.1",
"cocoa",
"core-foundation",
"core-graphics",
"foreign-types",
- "futures",
+ "futures 0.3.24",
"gpui",
"hmac 0.12.1",
"jwt",
@@ -774,7 +812,7 @@ dependencies = [
"parking_lot 0.11.2",
"postage",
"serde",
- "sha2 0.10.2",
+ "sha2 0.10.6",
"simplelog",
]
@@ -825,21 +863,23 @@ dependencies = [
"postage",
"settings",
"theme",
- "time 0.3.11",
+ "time 0.3.15",
"util",
"workspace",
]
[[package]]
name = "chrono"
-version = "0.4.19"
+version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
+checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1"
dependencies = [
- "libc",
+ "iana-time-zone",
+ "js-sys",
"num-integer",
"num-traits",
"time 0.1.44",
+ "wasm-bindgen",
"winapi 0.3.9",
]
@@ -860,9 +900,9 @@ dependencies = [
[[package]]
name = "clang-sys"
-version = "1.3.3"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a050e2153c5be08febd6734e29298e844fdb0fa21aeddd63b4eb7baa106c69b"
+checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3"
dependencies = [
"glob",
"libc",
@@ -886,9 +926,9 @@ dependencies = [
[[package]]
name = "clap"
-version = "3.2.8"
+version = "3.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "190814073e85d238f31ff738fcb0bf6910cedeb73376c87cd69291028966fd83"
+checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750"
dependencies = [
"atty",
"bitflags",
@@ -898,14 +938,14 @@ dependencies = [
"once_cell",
"strsim 0.10.0",
"termcolor",
- "textwrap 0.15.0",
+ "textwrap 0.15.1",
]
[[package]]
name = "clap_derive"
-version = "3.2.7"
+version = "3.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "759bf187376e1afa7b85b959e6a664a3e7a95203415dba952ad19139e798f902"
+checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65"
dependencies = [
"heck 0.4.0",
"proc-macro-error",
@@ -928,7 +968,7 @@ name = "cli"
version = "0.1.0"
dependencies = [
"anyhow",
- "clap 3.2.8",
+ "clap 3.2.22",
"core-foundation",
"core-services",
"dirs 3.0.2",
@@ -946,7 +986,7 @@ dependencies = [
"async-tungstenite",
"collections",
"db",
- "futures",
+ "futures 0.3.24",
"gpui",
"image",
"isahc",
@@ -961,11 +1001,11 @@ dependencies = [
"sum_tree",
"tempfile",
"thiserror",
- "time 0.3.11",
+ "time 0.3.15",
"tiny_http",
"url",
"util",
- "uuid 1.1.2",
+ "uuid 1.2.1",
]
[[package]]
@@ -1013,6 +1053,16 @@ dependencies = [
"objc",
]
+[[package]]
+name = "codespan-reporting"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
+dependencies = [
+ "termcolor",
+ "unicode-width",
+]
+
[[package]]
name = "collab"
version = "0.1.0"
@@ -1023,14 +1073,14 @@ dependencies = [
"axum",
"axum-extra",
"base64",
- "clap 3.2.8",
+ "clap 3.2.22",
"client",
"collections",
"ctor",
"editor",
"env_logger",
"envy",
- "futures",
+ "futures 0.3.24",
"git",
"gpui",
"hyper",
@@ -1053,7 +1103,7 @@ dependencies = [
"sha-1 0.9.8",
"sqlx",
"theme",
- "time 0.3.11",
+ "time 0.3.15",
"tokio",
"tokio-tungstenite",
"toml",
@@ -1101,9 +1151,9 @@ dependencies = [
[[package]]
name = "concurrent-queue"
-version = "1.2.2"
+version = "1.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3"
+checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c"
dependencies = [
"cache-padded",
]
@@ -1116,7 +1166,7 @@ dependencies = [
"client",
"collections",
"editor",
- "futures",
+ "futures 0.3.24",
"fuzzy",
"gpui",
"language",
@@ -1140,7 +1190,7 @@ dependencies = [
"client",
"collections",
"editor",
- "futures",
+ "futures 0.3.24",
"fuzzy",
"gpui",
"language",
@@ -1236,27 +1286,27 @@ dependencies = [
[[package]]
name = "cpufeatures"
-version = "0.2.2"
+version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
+checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
dependencies = [
"libc",
]
[[package]]
name = "cranelift-bforest"
-version = "0.85.1"
+version = "0.85.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7901fbba05decc537080b07cb3f1cadf53be7b7602ca8255786288a8692ae29a"
+checksum = "749d0d6022c9038dccf480bdde2a38d435937335bf2bb0f14e815d94517cdce8"
dependencies = [
"cranelift-entity",
]
[[package]]
name = "cranelift-codegen"
-version = "0.85.1"
+version = "0.85.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37ba1b45d243a4a28e12d26cd5f2507da74e77c45927d40de8b6ffbf088b46b5"
+checksum = "e94370cc7b37bf652ccd8bb8f09bd900997f7ccf97520edfc75554bb5c4abbea"
dependencies = [
"cranelift-bforest",
"cranelift-codegen-meta",
@@ -1272,33 +1322,33 @@ dependencies = [
[[package]]
name = "cranelift-codegen-meta"
-version = "0.85.1"
+version = "0.85.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54cc30032171bf230ce22b99c07c3a1de1221cb5375bd6dbe6dbe77d0eed743c"
+checksum = "e0a3cea8fdab90e44018c5b9a1dfd460d8ee265ac354337150222a354628bdb6"
dependencies = [
"cranelift-codegen-shared",
]
[[package]]
name = "cranelift-codegen-shared"
-version = "0.85.1"
+version = "0.85.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a23f2672426d2bb4c9c3ef53e023076cfc4d8922f0eeaebaf372c92fae8b5c69"
+checksum = "5ac72f76f2698598951ab26d8c96eaa854810e693e7dd52523958b5909fde6b2"
[[package]]
name = "cranelift-entity"
-version = "0.85.1"
+version = "0.85.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "886c59a5e0de1f06dbb7da80db149c75de10d5e2caca07cdd9fef8a5918a6336"
+checksum = "09eaeacfcd2356fe0e66b295e8f9d59fdd1ac3ace53ba50de14d628ec902f72d"
dependencies = [
"serde",
]
[[package]]
name = "cranelift-frontend"
-version = "0.85.1"
+version = "0.85.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ace74eeca11c439a9d4ed1a5cb9df31a54cd0f7fbddf82c8ce4ea8e9ad2a8fe0"
+checksum = "dba69c9980d5ffd62c18a2bde927855fcd7c8dc92f29feaf8636052662cbd99c"
dependencies = [
"cranelift-codegen",
"log",
@@ -1308,15 +1358,15 @@ dependencies = [
[[package]]
name = "cranelift-isle"
-version = "0.85.1"
+version = "0.85.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db1ae52a5cc2cad0d86fdd3dcb16b7217d2f1e65ab4f5814aa4f014ad335fa43"
+checksum = "d2920dc1e05cac40304456ed3301fde2c09bd6a9b0210bcfa2f101398d628d5b"
[[package]]
name = "cranelift-native"
-version = "0.85.1"
+version = "0.85.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dadcfb7852900780d37102bce5698bcd401736403f07b52e714ff7a180e0e22f"
+checksum = "f04dfa45f9b2a6f587c564d6b63388e00cd6589d2df6ea2758cf79e1a13285e6"
dependencies = [
"cranelift-codegen",
"libc",
@@ -1325,9 +1375,9 @@ dependencies = [
[[package]]
name = "cranelift-wasm"
-version = "0.85.1"
+version = "0.85.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c84e3410960389110b88f97776f39f6d2c8becdaa4cd59e390e6b76d9d0e7190"
+checksum = "31a46513ae6f26f3f267d8d75b5373d555fbbd1e68681f348d99df43f747ec54"
dependencies = [
"cranelift-codegen",
"cranelift-entity",
@@ -1375,47 +1425,46 @@ dependencies = [
[[package]]
name = "crossbeam-channel"
-version = "0.5.5"
+version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c"
+checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
dependencies = [
"cfg-if 1.0.0",
- "crossbeam-utils 0.8.10",
+ "crossbeam-utils 0.8.12",
]
[[package]]
name = "crossbeam-deque"
-version = "0.8.1"
+version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
+checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-epoch",
- "crossbeam-utils 0.8.10",
+ "crossbeam-utils 0.8.12",
]
[[package]]
name = "crossbeam-epoch"
-version = "0.9.9"
+version = "0.9.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d"
+checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348"
dependencies = [
"autocfg 1.1.0",
"cfg-if 1.0.0",
- "crossbeam-utils 0.8.10",
+ "crossbeam-utils 0.8.12",
"memoffset",
- "once_cell",
"scopeguard",
]
[[package]]
name = "crossbeam-queue"
-version = "0.3.5"
+version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f25d8400f4a7a5778f0e4e52384a48cbd9b5c495d110786187fc750075277a2"
+checksum = "1cd42583b04998a5363558e5f9291ee5a5ff6b49944332103f251e7479a82aa7"
dependencies = [
"cfg-if 1.0.0",
- "crossbeam-utils 0.8.10",
+ "crossbeam-utils 0.8.12",
]
[[package]]
@@ -1431,19 +1480,18 @@ dependencies = [
[[package]]
name = "crossbeam-utils"
-version = "0.8.10"
+version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83"
+checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac"
dependencies = [
"cfg-if 1.0.0",
- "once_cell",
]
[[package]]
name = "crypto-common"
-version = "0.1.4"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5999502d32b9c48d492abe66392408144895020ec4709e549e840799f3bb74c0"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
@@ -1461,9 +1509,9 @@ dependencies = [
[[package]]
name = "ctor"
-version = "0.1.22"
+version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c"
+checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb"
dependencies = [
"quote",
"syn",
@@ -1471,9 +1519,9 @@ dependencies = [
[[package]]
name = "curl"
-version = "0.4.43"
+version = "0.4.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37d855aeef205b43f65a5001e0997d81f8efca7badad4fad7d897aa7f0d0651f"
+checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22"
dependencies = [
"curl-sys",
"libc",
@@ -1486,9 +1534,9 @@ dependencies = [
[[package]]
name = "curl-sys"
-version = "0.4.55+curl-7.83.1"
+version = "0.4.56+curl-7.83.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23734ec77368ec583c2e61dd3f0b0e5c98b93abe6d2a004ca06b91dd7e3e2762"
+checksum = "6093e169dd4de29e468fa649fbae11cdcd5551c81fe5bf1b0677adad7ef3d26f"
dependencies = [
"cc",
"libc",
@@ -1500,6 +1548,50 @@ dependencies = [
"winapi 0.3.9",
]
+[[package]]
+name = "cxx"
+version = "1.0.78"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19f39818dcfc97d45b03953c1292efc4e80954e1583c4aa770bac1383e2310a4"
+dependencies = [
+ "cc",
+ "cxxbridge-flags",
+ "cxxbridge-macro",
+ "link-cplusplus",
+]
+
+[[package]]
+name = "cxx-build"
+version = "1.0.78"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e580d70777c116df50c390d1211993f62d40302881e54d4b79727acb83d0199"
+dependencies = [
+ "cc",
+ "codespan-reporting",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "scratch",
+ "syn",
+]
+
+[[package]]
+name = "cxxbridge-flags"
+version = "1.0.78"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56a46460b88d1cec95112c8c363f0e2c39afdb237f60583b0b36343bf627ea9c"
+
+[[package]]
+name = "cxxbridge-macro"
+version = "1.0.78"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "747b608fecf06b0d72d440f27acc99288207324b793be2c17991839f3d4995ea"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "data-url"
version = "0.1.1"
@@ -1534,13 +1626,13 @@ dependencies = [
[[package]]
name = "dhat"
-version = "0.3.0"
+version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "47003dc9f6368a88e85956c3b2573a7e6872746a3e5d762a8885da3a136a0381"
+checksum = "0684eaa19a59be283a6f99369917b679bd4d1d06604b2eb2e2f87b4bbd67668d"
dependencies = [
"backtrace",
"lazy_static",
- "parking_lot 0.11.2",
+ "parking_lot 0.12.1",
"rustc-hash",
"serde",
"serde_json",
@@ -1579,11 +1671,11 @@ dependencies = [
[[package]]
name = "digest"
-version = "0.10.3"
+version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
+checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c"
dependencies = [
- "block-buffer 0.10.2",
+ "block-buffer 0.10.3",
"crypto-common",
"subtle",
]
@@ -1649,10 +1741,13 @@ dependencies = [
]
[[package]]
-name = "dotenv"
-version = "0.15.0"
+name = "dotenvy"
+version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
+checksum = "ed9155c8f4dc55c7470ae9da3f63c6785245093b3f6aeb0f5bf2e968efbba314"
+dependencies = [
+ "dirs 4.0.0",
+]
[[package]]
name = "drag_and_drop"
@@ -1676,9 +1771,9 @@ dependencies = [
[[package]]
name = "dyn-clone"
-version = "1.0.6"
+version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "140206b78fb2bc3edbcfc9b5ccbd0b30699cfe8d348b8b31b330e47df5291a5a"
+checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2"
[[package]]
name = "easy-parallel"
@@ -1697,7 +1792,7 @@ dependencies = [
"context_menu",
"ctor",
"env_logger",
- "futures",
+ "futures 0.3.24",
"fuzzy",
"git",
"gpui",
@@ -1732,9 +1827,9 @@ dependencies = [
[[package]]
name = "either"
-version = "1.7.0"
+version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be"
+checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[package]]
name = "encoding_rs"
@@ -1747,9 +1842,9 @@ dependencies = [
[[package]]
name = "env_logger"
-version = "0.9.0"
+version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
+checksum = "c90bf5f19754d10198ccb95b70664fc925bd1fc090a0fd9a6ebc54acc8cd6272"
dependencies = [
"atty",
"humantime",
@@ -1769,9 +1864,9 @@ dependencies = [
[[package]]
name = "erased-serde"
-version = "0.3.21"
+version = "0.3.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81d013529d5574a60caeda29e179e695125448e5de52e3874f7b4c1d7360e18e"
+checksum = "54558e0ba96fbe24280072642eceb9d7d442e32c7ec0ea9e7ecd7b4ea2cf4e11"
dependencies = [
"serde",
]
@@ -1818,9 +1913,9 @@ dependencies = [
[[package]]
name = "event-listener"
-version = "2.5.2"
+version = "2.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71"
+checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "expat-sys"
@@ -1840,9 +1935,9 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
[[package]]
name = "fastrand"
-version = "1.7.0"
+version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
+checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
dependencies = [
"instant",
]
@@ -1890,7 +1985,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6"
dependencies = [
"crc32fast",
- "miniz_oxide 0.5.3",
+ "miniz_oxide 0.5.4",
]
[[package]]
@@ -1963,11 +2058,10 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "form_urlencoded"
-version = "1.0.1"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
+checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
dependencies = [
- "matches",
"percent-encoding",
]
@@ -2046,9 +2140,15 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
[[package]]
name = "futures"
-version = "0.3.21"
+version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e"
+checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678"
+
+[[package]]
+name = "futures"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c"
dependencies = [
"futures-channel",
"futures-core",
@@ -2061,9 +2161,9 @@ dependencies = [
[[package]]
name = "futures-channel"
-version = "0.3.21"
+version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
+checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050"
dependencies = [
"futures-core",
"futures-sink",
@@ -2071,15 +2171,15 @@ dependencies = [
[[package]]
name = "futures-core"
-version = "0.3.21"
+version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
+checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf"
[[package]]
name = "futures-executor"
-version = "0.3.21"
+version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
+checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab"
dependencies = [
"futures-core",
"futures-task",
@@ -2099,9 +2199,9 @@ dependencies = [
[[package]]
name = "futures-io"
-version = "0.3.21"
+version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
+checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68"
[[package]]
name = "futures-lite"
@@ -38,7 +38,23 @@
}
],
"%": "vim::Matching",
- "escape": "editor::Cancel"
+ "escape": "editor::Cancel",
+ "i": [
+ "vim::PushOperator",
+ {
+ "Object": {
+ "around": false
+ }
+ }
+ ],
+ "a": [
+ "vim::PushOperator",
+ {
+ "Object": {
+ "around": true
+ }
+ }
+ ]
}
},
{
@@ -134,6 +150,20 @@
"y": "vim::CurrentLine"
}
},
+ {
+ "context": "Editor && VimObject",
+ "bindings": {
+ "w": "vim::Word",
+ "shift-w": [
+ "vim::Word",
+ {
+ "ignorePunctuation": true
+ }
+ ],
+ "s": "vim::Sentence",
+ "p": "vim::Paragraph"
+ }
+ },
{
"context": "Editor && vim_mode == visual",
"bindings": {
@@ -330,34 +330,91 @@ impl DisplaySnapshot {
DisplayPoint(self.blocks_snapshot.max_point())
}
+ /// Returns text chunks starting at the given display row until the end of the file
pub fn text_chunks(&self, display_row: u32) -> impl Iterator<Item = &str> {
self.blocks_snapshot
.chunks(display_row..self.max_point().row() + 1, false, None)
.map(|h| h.text)
}
+ // Returns text chunks starting at the end of the given display row in reverse until the start of the file
+ pub fn reverse_text_chunks(&self, display_row: u32) -> impl Iterator<Item = &str> {
+ (0..=display_row).into_iter().rev().flat_map(|row| {
+ self.blocks_snapshot
+ .chunks(row..row + 1, false, None)
+ .map(|h| h.text)
+ .collect::<Vec<_>>()
+ .into_iter()
+ .rev()
+ })
+ }
+
pub fn chunks(&self, display_rows: Range<u32>, language_aware: bool) -> DisplayChunks<'_> {
self.blocks_snapshot
.chunks(display_rows, language_aware, Some(&self.text_highlights))
}
- pub fn chars_at(&self, point: DisplayPoint) -> impl Iterator<Item = char> + '_ {
- let mut column = 0;
- let mut chars = self.text_chunks(point.row()).flat_map(str::chars);
- while column < point.column() {
- if let Some(c) = chars.next() {
- column += c.len_utf8() as u32;
- } else {
- break;
- }
- }
- chars
+ pub fn chars_at(
+ &self,
+ mut point: DisplayPoint,
+ ) -> impl Iterator<Item = (char, DisplayPoint)> + '_ {
+ point = DisplayPoint(self.blocks_snapshot.clip_point(point.0, Bias::Left));
+ self.text_chunks(point.row())
+ .flat_map(str::chars)
+ .skip_while({
+ let mut column = 0;
+ move |char| {
+ let at_point = column >= point.column();
+ column += char.len_utf8() as u32;
+ !at_point
+ }
+ })
+ .map(move |ch| {
+ let result = (ch, point);
+ if ch == '\n' {
+ *point.row_mut() += 1;
+ *point.column_mut() = 0;
+ } else {
+ *point.column_mut() += ch.len_utf8() as u32;
+ }
+ result
+ })
+ }
+
+ pub fn reverse_chars_at(
+ &self,
+ mut point: DisplayPoint,
+ ) -> impl Iterator<Item = (char, DisplayPoint)> + '_ {
+ point = DisplayPoint(self.blocks_snapshot.clip_point(point.0, Bias::Left));
+ self.reverse_text_chunks(point.row())
+ .flat_map(|chunk| chunk.chars().rev())
+ .skip_while({
+ let mut column = self.line_len(point.row());
+ if self.max_point().row() > point.row() {
+ column += 1;
+ }
+
+ move |char| {
+ let at_point = column <= point.column();
+ column = column.saturating_sub(char.len_utf8() as u32);
+ !at_point
+ }
+ })
+ .map(move |ch| {
+ if ch == '\n' {
+ *point.row_mut() -= 1;
+ *point.column_mut() = self.line_len(point.row());
+ } else {
+ *point.column_mut() = point.column().saturating_sub(ch.len_utf8() as u32);
+ }
+ (ch, point)
+ })
}
pub fn column_to_chars(&self, display_row: u32, target: u32) -> u32 {
let mut count = 0;
let mut column = 0;
- for c in self.chars_at(DisplayPoint::new(display_row, 0)) {
+ for (c, _) in self.chars_at(DisplayPoint::new(display_row, 0)) {
if column >= target {
break;
}
@@ -370,7 +427,7 @@ impl DisplaySnapshot {
pub fn column_from_chars(&self, display_row: u32, char_count: u32) -> u32 {
let mut column = 0;
- for (count, c) in self.chars_at(DisplayPoint::new(display_row, 0)).enumerate() {
+ for (count, (c, _)) in self.chars_at(DisplayPoint::new(display_row, 0)).enumerate() {
if c == '\n' || count >= char_count as usize {
break;
}
@@ -454,7 +511,7 @@ impl DisplaySnapshot {
pub fn line_indent(&self, display_row: u32) -> (u32, bool) {
let mut indent = 0;
let mut is_blank = true;
- for c in self.chars_at(DisplayPoint::new(display_row, 0)) {
+ for (c, _) in self.chars_at(DisplayPoint::new(display_row, 0)) {
if c == ' ' {
indent += 1;
} else {
@@ -4074,7 +4074,7 @@ impl Editor {
self.change_selections(Some(Autoscroll::Fit), cx, |s| {
s.move_cursors_with(|map, head, _| {
(
- movement::line_beginning(map, head, true),
+ movement::indented_line_beginning(map, head, true),
SelectionGoal::None,
)
});
@@ -4089,7 +4089,7 @@ impl Editor {
self.change_selections(Some(Autoscroll::Fit), cx, |s| {
s.move_heads_with(|map, head, _| {
(
- movement::line_beginning(map, head, action.stop_at_soft_wraps),
+ movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
SelectionGoal::None,
)
});
@@ -752,7 +752,7 @@ impl EditorElement {
.snapshot
.chars_at(cursor_position)
.next()
- .and_then(|character| {
+ .and_then(|(character, _)| {
let font_id =
cursor_row_layout.font_for_index(cursor_column)?;
let text = character.to_string();
@@ -101,6 +101,22 @@ pub fn line_beginning(
map: &DisplaySnapshot,
display_point: DisplayPoint,
stop_at_soft_boundaries: bool,
+) -> DisplayPoint {
+ let point = display_point.to_point(map);
+ let soft_line_start = map.clip_point(DisplayPoint::new(display_point.row(), 0), Bias::Right);
+ let line_start = map.prev_line_boundary(point).1;
+
+ if stop_at_soft_boundaries && display_point != soft_line_start {
+ soft_line_start
+ } else {
+ line_start
+ }
+}
+
+pub fn indented_line_beginning(
+ map: &DisplaySnapshot,
+ display_point: DisplayPoint,
+ stop_at_soft_boundaries: bool,
) -> DisplayPoint {
let point = display_point.to_point(map);
let soft_line_start = map.clip_point(DisplayPoint::new(display_point.row(), 0), Bias::Right);
@@ -167,54 +183,79 @@ pub fn next_subword_end(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPo
})
}
-/// Scans for a boundary from the start of each line preceding the given end point until a boundary
-/// is found, indicated by the given predicate returning true. The predicate is called with the
-/// character to the left and right of the candidate boundary location, and will be called with `\n`
-/// characters indicating the start or end of a line. If the predicate returns true multiple times
-/// on a line, the *rightmost* boundary is returned.
+/// Scans for a boundary preceding the given start point `from` until a boundary is found, indicated by the
+/// given predicate returning true. The predicate is called with the character to the left and right
+/// of the candidate boundary location, and will be called with `\n` characters indicating the start
+/// or end of a line.
pub fn find_preceding_boundary(
map: &DisplaySnapshot,
- end: DisplayPoint,
+ from: DisplayPoint,
mut is_boundary: impl FnMut(char, char) -> bool,
) -> DisplayPoint {
- let mut point = end;
- loop {
- *point.column_mut() = 0;
- if point.row() > 0 {
- if let Some(indent) = map.soft_wrap_indent(point.row() - 1) {
- *point.column_mut() = indent;
+ let mut start_column = 0;
+ let mut soft_wrap_row = from.row() + 1;
+
+ let mut prev = None;
+ for (ch, point) in map.reverse_chars_at(from) {
+ // Recompute soft_wrap_indent if the row has changed
+ if point.row() != soft_wrap_row {
+ soft_wrap_row = point.row();
+
+ if point.row() == 0 {
+ start_column = 0;
+ } else if let Some(indent) = map.soft_wrap_indent(point.row() - 1) {
+ start_column = indent;
}
}
- let mut boundary = None;
- let mut prev_ch = if point.is_zero() { None } else { Some('\n') };
- for ch in map.chars_at(point) {
- if point >= end {
- break;
- }
+ // If the current point is in the soft_wrap, skip comparing it
+ if point.column() < start_column {
+ continue;
+ }
- if let Some(prev_ch) = prev_ch {
- if is_boundary(prev_ch, ch) {
- boundary = Some(point);
- }
+ if let Some((prev_ch, prev_point)) = prev {
+ if is_boundary(ch, prev_ch) {
+ return prev_point;
}
+ }
- if ch == '\n' {
- break;
- }
+ prev = Some((ch, point));
+ }
+ DisplayPoint::zero()
+}
- prev_ch = Some(ch);
- *point.column_mut() += ch.len_utf8() as u32;
+/// Scans for a boundary preceding the given start point `from` until a boundary is found, indicated by the
+/// given predicate returning true. The predicate is called with the character to the left and right
+/// of the candidate boundary location, and will be called with `\n` characters indicating the start
+/// or end of a line. If no boundary is found, the start of the line is returned.
+pub fn find_preceding_boundary_in_line(
+ map: &DisplaySnapshot,
+ from: DisplayPoint,
+ mut is_boundary: impl FnMut(char, char) -> bool,
+) -> DisplayPoint {
+ let mut start_column = 0;
+ if from.row() > 0 {
+ if let Some(indent) = map.soft_wrap_indent(from.row() - 1) {
+ start_column = indent;
}
+ }
- if let Some(boundary) = boundary {
- return boundary;
- } else if point.row() == 0 {
- return DisplayPoint::zero();
- } else {
- *point.row_mut() -= 1;
+ let mut prev = None;
+ for (ch, point) in map.reverse_chars_at(from) {
+ if let Some((prev_ch, prev_point)) = prev {
+ if is_boundary(ch, prev_ch) {
+ return prev_point;
+ }
}
+
+ if ch == '\n' || point.column() < start_column {
+ break;
+ }
+
+ prev = Some((ch, point));
}
+
+ prev.map(|(_, point)| point).unwrap_or(from)
}
/// Scans for a boundary following the given start point until a boundary is found, indicated by the
@@ -223,26 +264,48 @@ pub fn find_preceding_boundary(
/// or end of a line.
pub fn find_boundary(
map: &DisplaySnapshot,
- mut point: DisplayPoint,
+ from: DisplayPoint,
mut is_boundary: impl FnMut(char, char) -> bool,
) -> DisplayPoint {
let mut prev_ch = None;
- for ch in map.chars_at(point) {
+ for (ch, point) in map.chars_at(from) {
if let Some(prev_ch) = prev_ch {
if is_boundary(prev_ch, ch) {
- break;
+ return map.clip_point(point, Bias::Right);
+ }
+ }
+
+ prev_ch = Some(ch);
+ }
+ map.clip_point(map.max_point(), Bias::Right)
+}
+
+/// Scans for a boundary following the given start point until a boundary is found, indicated by the
+/// given predicate returning true. The predicate is called with the character to the left and right
+/// of the candidate boundary location, and will be called with `\n` characters indicating the start
+/// or end of a line. If no boundary is found, the end of the line is returned
+pub fn find_boundary_in_line(
+ map: &DisplaySnapshot,
+ from: DisplayPoint,
+ mut is_boundary: impl FnMut(char, char) -> bool,
+) -> DisplayPoint {
+ let mut prev = None;
+ for (ch, point) in map.chars_at(from) {
+ if let Some((prev_ch, _)) = prev {
+ if is_boundary(prev_ch, ch) {
+ return map.clip_point(point, Bias::Right);
}
}
+ prev = Some((ch, point));
+
if ch == '\n' {
- *point.row_mut() += 1;
- *point.column_mut() = 0;
- } else {
- *point.column_mut() += ch.len_utf8() as u32;
+ break;
}
- prev_ch = Some(ch);
}
- map.clip_point(point, Bias::Right)
+
+ // Return the last position checked so that we give a point right before the newline or eof.
+ map.clip_point(prev.map(|(_, point)| point).unwrap_or(from), Bias::Right)
}
pub fn is_inside_word(map: &DisplaySnapshot, point: DisplayPoint) -> bool {
@@ -7,7 +7,20 @@ edition = "2021"
path = "src/vim.rs"
doctest = false
+[features]
+neovim = ["nvim-rs", "async-compat", "async-trait", "tokio"]
+
[dependencies]
+serde = { version = "1.0", features = ["derive", "rc"] }
+itertools = "0.10"
+log = { version = "0.4.16", features = ["kv_unstable_serde"] }
+
+async-compat = { version = "0.2.1", "optional" = true }
+async-trait = { version = "0.1", "optional" = true }
+nvim-rs = { git = "https://github.com/KillTheMule/nvim-rs", branch = "master", features = ["use_tokio"], optional = true }
+tokio = { version = "1.15", "optional" = true }
+serde_json = { version = "1.0", features = ["preserve_order"] }
+
assets = { path = "../assets" }
collections = { path = "../collections" }
command_palette = { path = "../command_palette" }
@@ -15,14 +28,12 @@ editor = { path = "../editor" }
gpui = { path = "../gpui" }
language = { path = "../language" }
search = { path = "../search" }
-serde = { version = "1.0", features = ["derive", "rc"] }
settings = { path = "../settings" }
workspace = { path = "../workspace" }
-itertools = "0.10"
-log = { version = "0.4.16", features = ["kv_unstable_serde"] }
[dev-dependencies]
indoc = "1.0.4"
+
editor = { path = "../editor", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
language = { path = "../language", features = ["test-support"] }
@@ -26,7 +26,7 @@ fn normal_before(_: &mut Workspace, _: &NormalBefore, cx: &mut ViewContext<Works
#[cfg(test)]
mod test {
- use crate::{state::Mode, vim_test_context::VimTestContext};
+ use crate::{state::Mode, test_contexts::VimTestContext};
#[gpui::test]
async fn test_enter_and_exit_insert_mode(cx: &mut gpui::TestAppContext) {
@@ -206,7 +206,7 @@ impl Motion {
}
}
- selection.end = map.next_line_boundary(selection.end.to_point(map)).1;
+ (_, selection.end) = map.next_line_boundary(selection.end.to_point(map));
} else {
// If the motion is exclusive and the end of the motion is in column 1, the
// end of the motion is moved to the end of the previous line and the motion
@@ -239,12 +239,12 @@ fn left(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint {
map.clip_point(point, Bias::Left)
}
-fn right(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint {
+pub(crate) fn right(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint {
*point.column_mut() += 1;
map.clip_point(point, Bias::Right)
}
-fn next_word_start(
+pub(crate) fn next_word_start(
map: &DisplaySnapshot,
point: DisplayPoint,
ignore_punctuation: bool,
@@ -255,7 +255,7 @@ fn next_word_start(
let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation);
let at_newline = right == '\n';
- let found = (left_kind != right_kind && !right.is_whitespace())
+ let found = (left_kind != right_kind && right_kind != CharKind::Whitespace)
|| at_newline && crossed_newline
|| at_newline && left == '\n'; // Prevents skipping repeated empty lines
@@ -272,23 +272,28 @@ fn next_word_end(
ignore_punctuation: bool,
) -> DisplayPoint {
*point.column_mut() += 1;
+ dbg!(point);
point = movement::find_boundary(map, point, |left, right| {
+ dbg!(left);
let left_kind = char_kind(left).coerce_punctuation(ignore_punctuation);
let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation);
- left_kind != right_kind && !left.is_whitespace()
+ left_kind != right_kind && left_kind != CharKind::Whitespace
});
+
+ dbg!(point);
+
// find_boundary clips, so if the character after the next character is a newline or at the end of the document, we know
// we have backtraced already
if !map
.chars_at(point)
.nth(1)
- .map(|c| c == '\n')
+ .map(|(c, _)| c == '\n')
.unwrap_or(true)
{
*point.column_mut() = point.column().saturating_sub(1);
}
- map.clip_point(point, Bias::Left)
+ dbg!(map.clip_point(point, Bias::Left))
}
fn previous_word_start(
@@ -307,22 +312,21 @@ fn previous_word_start(
point
}
-fn first_non_whitespace(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint {
- let mut column = 0;
- for ch in map.chars_at(DisplayPoint::new(point.row(), 0)) {
+fn first_non_whitespace(map: &DisplaySnapshot, from: DisplayPoint) -> DisplayPoint {
+ let mut last_point = DisplayPoint::new(from.row(), 0);
+ for (ch, point) in map.chars_at(last_point) {
if ch == '\n' {
- return point;
+ return from;
}
+ last_point = point;
+
if char_kind(ch) != CharKind::Whitespace {
break;
}
-
- column += ch.len_utf8() as u32;
}
- *point.column_mut() = column;
- map.clip_point(point, Bias::Left)
+ map.clip_point(last_point, Bias::Left)
}
fn start_of_line(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint {
@@ -6,6 +6,7 @@ use std::borrow::Cow;
use crate::{
motion::Motion,
+ object::Object,
state::{Mode, Operator},
Vim,
};
@@ -16,7 +17,11 @@ use gpui::{actions, MutableAppContext, ViewContext};
use language::{AutoindentMode, Point, SelectionGoal};
use workspace::Workspace;
-use self::{change::change_over, delete::delete_over, yank::yank_over};
+use self::{
+ change::{change_motion, change_object},
+ delete::{delete_motion, delete_object},
+ yank::{yank_motion, yank_object},
+};
actions!(
vim,
@@ -43,22 +48,22 @@ pub fn init(cx: &mut MutableAppContext) {
cx.add_action(insert_line_below);
cx.add_action(|_: &mut Workspace, _: &DeleteLeft, cx| {
Vim::update(cx, |vim, cx| {
- delete_over(vim, Motion::Left, cx);
+ delete_motion(vim, Motion::Left, cx);
})
});
cx.add_action(|_: &mut Workspace, _: &DeleteRight, cx| {
Vim::update(cx, |vim, cx| {
- delete_over(vim, Motion::Right, cx);
+ delete_motion(vim, Motion::Right, cx);
})
});
cx.add_action(|_: &mut Workspace, _: &ChangeToEndOfLine, cx| {
Vim::update(cx, |vim, cx| {
- change_over(vim, Motion::EndOfLine, cx);
+ change_motion(vim, Motion::EndOfLine, cx);
})
});
cx.add_action(|_: &mut Workspace, _: &DeleteToEndOfLine, cx| {
Vim::update(cx, |vim, cx| {
- delete_over(vim, Motion::EndOfLine, cx);
+ delete_motion(vim, Motion::EndOfLine, cx);
})
});
cx.add_action(paste);
@@ -70,17 +75,36 @@ pub fn normal_motion(motion: Motion, cx: &mut MutableAppContext) {
Vim::update(cx, |vim, cx| {
match vim.state.operator_stack.pop() {
None => move_cursor(vim, motion, cx),
- Some(Operator::Namespace(_)) => {
- // Can't do anything for a namespace operator. Ignoring
+ Some(Operator::Change) => change_motion(vim, motion, cx),
+ Some(Operator::Delete) => delete_motion(vim, motion, cx),
+ Some(Operator::Yank) => yank_motion(vim, motion, cx),
+ _ => {
+ // Can't do anything for text objects or namespace operators. Ignoring
}
- Some(Operator::Change) => change_over(vim, motion, cx),
- Some(Operator::Delete) => delete_over(vim, motion, cx),
- Some(Operator::Yank) => yank_over(vim, motion, cx),
}
vim.clear_operator(cx);
});
}
+pub fn normal_object(object: Object, cx: &mut MutableAppContext) {
+ Vim::update(cx, |vim, cx| {
+ match vim.state.operator_stack.pop() {
+ Some(Operator::Object { around }) => match vim.state.operator_stack.pop() {
+ Some(Operator::Change) => change_object(vim, object, around, cx),
+ Some(Operator::Delete) => delete_object(vim, object, around, cx),
+ Some(Operator::Yank) => yank_object(vim, object, around, cx),
+ _ => {
+ // Can't do anything for namespace operators. Ignoring
+ }
+ },
+ _ => {
+ // Can't do anything with change/delete/yank and text objects. Ignoring
+ }
+ }
+ vim.clear_operator(cx);
+ })
+}
+
fn move_cursor(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) {
vim.update_active_editor(cx, |editor, cx| {
editor.change_selections(Some(Autoscroll::Fit), cx, |s| {
@@ -304,7 +328,7 @@ mod test {
Mode::{self, *},
Namespace, Operator,
},
- vim_test_context::VimTestContext,
+ test_contexts::VimTestContext,
};
#[gpui::test]
@@ -1,4 +1,4 @@
-use crate::{motion::Motion, state::Mode, utils::copy_selections_content, Vim};
+use crate::{motion::Motion, object::Object, state::Mode, utils::copy_selections_content, Vim};
use editor::{char_kind, movement, Autoscroll};
use gpui::{impl_actions, MutableAppContext, ViewContext};
use serde::Deserialize;
@@ -17,7 +17,7 @@ pub fn init(cx: &mut MutableAppContext) {
cx.add_action(change_word);
}
-pub fn change_over(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) {
+pub fn change_motion(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) {
vim.update_active_editor(cx, |editor, cx| {
editor.transact(cx, |editor, cx| {
// We are swapping to insert mode anyway. Just set the line end clipping behavior now
@@ -34,6 +34,23 @@ pub fn change_over(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) {
vim.switch_mode(Mode::Insert, false, cx)
}
+pub fn change_object(vim: &mut Vim, object: Object, around: bool, cx: &mut MutableAppContext) {
+ vim.update_active_editor(cx, |editor, cx| {
+ editor.transact(cx, |editor, cx| {
+ // We are swapping to insert mode anyway. Just set the line end clipping behavior now
+ editor.set_clip_at_line_ends(false, cx);
+ editor.change_selections(Some(Autoscroll::Fit), cx, |s| {
+ s.move_with(|map, selection| {
+ object.expand_selection(map, selection, around);
+ });
+ });
+ copy_selections_content(editor, false, cx);
+ editor.insert("", cx);
+ });
+ });
+ vim.switch_mode(Mode::Insert, false, cx);
+}
+
// From the docs https://vimhelp.org/change.txt.html#cw
// Special case: When the cursor is in a word, "cw" and "cW" do not include the
// white space after a word, they only change up to the end of the word. This is
@@ -78,7 +95,7 @@ fn change_word(
mod test {
use indoc::indoc;
- use crate::{state::Mode, vim_test_context::VimTestContext};
+ use crate::{state::Mode, test_contexts::VimTestContext};
#[gpui::test]
async fn test_change_h(cx: &mut gpui::TestAppContext) {
@@ -170,8 +187,7 @@ mod test {
test"},
indoc! {"
Test test
- ˇ
- test"},
+ ˇ"},
);
let mut cx = cx.binding(["c", "shift-e"]);
@@ -193,6 +209,7 @@ mod test {
Test ˇ
test"},
);
+ println!("Marker");
cx.assert(
indoc! {"
Test test
@@ -1,9 +1,9 @@
-use crate::{motion::Motion, utils::copy_selections_content, Vim};
-use collections::HashMap;
-use editor::{Autoscroll, Bias};
+use crate::{motion::Motion, object::Object, utils::copy_selections_content, Vim};
+use collections::{HashMap, HashSet};
+use editor::{display_map::ToDisplayPoint, Autoscroll, Bias};
use gpui::MutableAppContext;
-pub fn delete_over(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) {
+pub fn delete_motion(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) {
vim.update_active_editor(cx, |editor, cx| {
editor.transact(cx, |editor, cx| {
editor.set_clip_at_line_ends(false, cx);
@@ -36,11 +36,67 @@ pub fn delete_over(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) {
});
}
+pub fn delete_object(vim: &mut Vim, object: Object, around: bool, cx: &mut MutableAppContext) {
+ vim.update_active_editor(cx, |editor, cx| {
+ editor.transact(cx, |editor, cx| {
+ editor.set_clip_at_line_ends(false, cx);
+ // Emulates behavior in vim where if we expanded backwards to include a newline
+ // the cursor gets set back to the start of the line
+ let mut should_move_to_start: HashSet<_> = Default::default();
+ editor.change_selections(Some(Autoscroll::Fit), cx, |s| {
+ s.move_with(|map, selection| {
+ object.expand_selection(map, selection, around);
+ let offset_range = selection.map(|p| p.to_offset(map, Bias::Left)).range();
+ let contains_only_newlines = map
+ .chars_at(selection.start)
+ .take_while(|(_, p)| p < &selection.end)
+ .all(|(char, _)| char == '\n')
+ || offset_range.is_empty();
+ let end_at_newline = map
+ .chars_at(selection.end)
+ .next()
+ .map(|(c, _)| c == '\n')
+ .unwrap_or(false);
+
+ // If expanded range contains only newlines and
+ // the object is around or sentence, expand to include a newline
+ // at the end or start
+ if (around || object == Object::Sentence) && contains_only_newlines {
+ if end_at_newline {
+ selection.end =
+ (offset_range.end + '\n'.len_utf8()).to_display_point(map);
+ } else if selection.start.row() > 0 {
+ should_move_to_start.insert(selection.id);
+ selection.start =
+ (offset_range.start - '\n'.len_utf8()).to_display_point(map);
+ }
+ }
+ });
+ });
+ copy_selections_content(editor, false, cx);
+ editor.insert("", cx);
+
+ // Fixup cursor position after the deletion
+ editor.set_clip_at_line_ends(true, cx);
+ editor.change_selections(Some(Autoscroll::Fit), cx, |s| {
+ s.move_with(|map, selection| {
+ let mut cursor = selection.head();
+ if should_move_to_start.contains(&selection.id) {
+ *cursor.column_mut() = 0;
+ }
+ cursor = map.clip_point(cursor, Bias::Left);
+ selection.collapse_to(cursor, selection.goal)
+ });
+ });
+ });
+ });
+}
+
#[cfg(test)]
mod test {
use indoc::indoc;
- use crate::{state::Mode, vim_test_context::VimTestContext};
+ use crate::{state::Mode, test_contexts::VimTestContext};
#[gpui::test]
async fn test_delete_h(cx: &mut gpui::TestAppContext) {
@@ -140,8 +196,7 @@ mod test {
test"},
indoc! {"
Test test
- ˇ
- test"},
+ ˇ"},
);
let mut cx = cx.binding(["d", "shift-e"]);
@@ -1,8 +1,8 @@
-use crate::{motion::Motion, utils::copy_selections_content, Vim};
+use crate::{motion::Motion, object::Object, utils::copy_selections_content, Vim};
use collections::HashMap;
use gpui::MutableAppContext;
-pub fn yank_over(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) {
+pub fn yank_motion(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) {
vim.update_active_editor(cx, |editor, cx| {
editor.transact(cx, |editor, cx| {
editor.set_clip_at_line_ends(false, cx);
@@ -24,3 +24,26 @@ pub fn yank_over(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) {
});
});
}
+
+pub fn yank_object(vim: &mut Vim, object: Object, around: bool, cx: &mut MutableAppContext) {
+ vim.update_active_editor(cx, |editor, cx| {
+ editor.transact(cx, |editor, cx| {
+ editor.set_clip_at_line_ends(false, cx);
+ let mut original_positions: HashMap<_, _> = Default::default();
+ editor.change_selections(None, cx, |s| {
+ s.move_with(|map, selection| {
+ let original_position = (selection.head(), selection.goal);
+ object.expand_selection(map, selection, around);
+ original_positions.insert(selection.id, original_position);
+ });
+ });
+ copy_selections_content(editor, false, cx);
+ editor.change_selections(None, cx, |s| {
+ s.move_with(|_, selection| {
+ let (head, goal) = original_positions.remove(&selection.id).unwrap();
+ selection.collapse_to(head, goal);
+ });
+ });
+ });
+ });
+}
@@ -0,0 +1,488 @@
+use std::ops::Range;
+
+use editor::{char_kind, display_map::DisplaySnapshot, movement, Bias, CharKind, DisplayPoint};
+use gpui::{actions, impl_actions, MutableAppContext};
+use language::Selection;
+use serde::Deserialize;
+use workspace::Workspace;
+
+use crate::{motion, normal::normal_object, state::Mode, visual::visual_object, Vim};
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum Object {
+ Word { ignore_punctuation: bool },
+ Sentence,
+ Paragraph,
+}
+
+#[derive(Clone, Deserialize, PartialEq)]
+#[serde(rename_all = "camelCase")]
+struct Word {
+ #[serde(default)]
+ ignore_punctuation: bool,
+}
+
+actions!(vim, [Sentence, Paragraph]);
+impl_actions!(vim, [Word]);
+
+pub fn init(cx: &mut MutableAppContext) {
+ cx.add_action(
+ |_: &mut Workspace, &Word { ignore_punctuation }: &Word, cx: _| {
+ object(Object::Word { ignore_punctuation }, cx)
+ },
+ );
+ cx.add_action(|_: &mut Workspace, _: &Sentence, cx: _| object(Object::Sentence, cx));
+ cx.add_action(|_: &mut Workspace, _: &Paragraph, cx: _| object(Object::Paragraph, cx));
+}
+
+fn object(object: Object, cx: &mut MutableAppContext) {
+ match Vim::read(cx).state.mode {
+ Mode::Normal => normal_object(object, cx),
+ Mode::Visual { .. } => visual_object(object, cx),
+ Mode::Insert => {
+ // Shouldn't execute a text object in insert mode. Ignoring
+ }
+ }
+}
+
+impl Object {
+ pub fn object_range(
+ self,
+ map: &DisplaySnapshot,
+ relative_to: DisplayPoint,
+ around: bool,
+ ) -> Range<DisplayPoint> {
+ match self {
+ Object::Word { ignore_punctuation } => {
+ if around {
+ around_word(map, relative_to, ignore_punctuation)
+ } else {
+ in_word(map, relative_to, ignore_punctuation)
+ }
+ }
+ Object::Sentence => sentence(map, relative_to, around),
+ _ => relative_to..relative_to,
+ }
+ }
+
+ pub fn expand_selection(
+ self,
+ map: &DisplaySnapshot,
+ selection: &mut Selection<DisplayPoint>,
+ around: bool,
+ ) {
+ let range = self.object_range(map, selection.head(), around);
+ selection.start = range.start;
+ selection.end = range.end;
+ }
+}
+
+/// Return a range that surrounds the word relative_to is in
+/// If relative_to is at the start of a word, return the word.
+/// If relative_to is between words, return the space between
+fn in_word(
+ map: &DisplaySnapshot,
+ relative_to: DisplayPoint,
+ ignore_punctuation: bool,
+) -> Range<DisplayPoint> {
+ // Use motion::right so that we consider the character under the cursor when looking for the start
+ let start = movement::find_preceding_boundary_in_line(
+ map,
+ motion::right(map, relative_to),
+ |left, right| {
+ char_kind(left).coerce_punctuation(ignore_punctuation)
+ != char_kind(right).coerce_punctuation(ignore_punctuation)
+ },
+ );
+ let end = movement::find_boundary_in_line(map, relative_to, |left, right| {
+ char_kind(left).coerce_punctuation(ignore_punctuation)
+ != char_kind(right).coerce_punctuation(ignore_punctuation)
+ });
+
+ start..end
+}
+
+/// Return a range that surrounds the word and following whitespace
+/// relative_to is in.
+/// If relative_to is at the start of a word, return the word and following whitespace.
+/// If relative_to is between words, return the whitespace back and the following word
+
+/// if in word
+/// delete that word
+/// if there is whitespace following the word, delete that as well
+/// otherwise, delete any preceding whitespace
+/// otherwise
+/// delete whitespace around cursor
+/// delete word following the cursor
+fn around_word(
+ map: &DisplaySnapshot,
+ relative_to: DisplayPoint,
+ ignore_punctuation: bool,
+) -> Range<DisplayPoint> {
+ let in_word = map
+ .chars_at(relative_to)
+ .next()
+ .map(|(c, _)| char_kind(c) != CharKind::Whitespace)
+ .unwrap_or(false);
+
+ if in_word {
+ around_containing_word(map, relative_to, ignore_punctuation)
+ } else {
+ around_next_word(map, relative_to, ignore_punctuation)
+ }
+}
+
+fn around_containing_word(
+ map: &DisplaySnapshot,
+ relative_to: DisplayPoint,
+ ignore_punctuation: bool,
+) -> Range<DisplayPoint> {
+ expand_to_include_whitespace(map, in_word(map, relative_to, ignore_punctuation), true)
+}
+
+fn around_next_word(
+ map: &DisplaySnapshot,
+ relative_to: DisplayPoint,
+ ignore_punctuation: bool,
+) -> Range<DisplayPoint> {
+ // Get the start of the word
+ let start = movement::find_preceding_boundary_in_line(
+ map,
+ motion::right(map, relative_to),
+ |left, right| {
+ char_kind(left).coerce_punctuation(ignore_punctuation)
+ != char_kind(right).coerce_punctuation(ignore_punctuation)
+ },
+ );
+
+ let mut word_found = false;
+ let end = movement::find_boundary(map, relative_to, |left, right| {
+ let left_kind = char_kind(left).coerce_punctuation(ignore_punctuation);
+ let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation);
+
+ let found = (word_found && left_kind != right_kind) || right == '\n' && left == '\n';
+
+ if right_kind != CharKind::Whitespace {
+ word_found = true;
+ }
+
+ found
+ });
+
+ start..end
+}
+
+// /// Return the range containing a sentence.
+// fn sentence(map: &DisplaySnapshot, relative_to: DisplayPoint, around: bool) -> Range<DisplayPoint> {
+// let mut previous_end = relative_to;
+// let mut start = None;
+
+// // Seek backwards to find a period or double newline. Record the last non whitespace character as the
+// // possible start of the sentence. Alternatively if two newlines are found right after each other, return that.
+// let mut rev_chars = map.reverse_chars_at(relative_to).peekable();
+// while let Some((char, point)) = rev_chars.next() {
+// dbg!(char, point);
+// if char == '.' {
+// break;
+// }
+
+// if char == '\n'
+// && (rev_chars.peek().map(|(c, _)| c == &'\n').unwrap_or(false) || start.is_none())
+// {
+// break;
+// }
+
+// if !char.is_whitespace() {
+// start = Some(point);
+// }
+
+// previous_end = point;
+// }
+
+// let mut end = relative_to;
+// let mut chars = map.chars_at(relative_to).peekable();
+// while let Some((char, point)) = chars.next() {
+// if !char.is_whitespace() {
+// if start.is_none() {
+// start = Some(point);
+// }
+
+// // Set the end to the point after the current non whitespace character
+// end = point;
+// *end.column_mut() += char.len_utf8() as u32;
+// }
+
+// if char == '.' {
+// break;
+// }
+
+// if char == '\n' {
+// if start.is_none() {
+// if let Some((_, next_point)) = chars.peek() {
+// end = *next_point;
+// }
+// break;
+
+// if chars.peek().map(|(c, _)| c == &'\n').unwrap_or(false) {
+// break;
+// }
+// }
+// }
+
+// start.unwrap_or(previous_end)..end
+// }
+
+fn sentence(map: &DisplaySnapshot, relative_to: DisplayPoint, around: bool) -> Range<DisplayPoint> {
+ let mut start = None;
+ let mut previous_end = relative_to;
+
+ for (char, point) in map.reverse_chars_at(relative_to) {
+ if is_sentence_end(map, point) {
+ break;
+ }
+
+ if is_possible_sentence_start(char) {
+ start = Some(point);
+ }
+
+ previous_end = point;
+ }
+
+ // Handle case where cursor was before the sentence start
+ let mut chars = map.chars_at(relative_to).peekable();
+ if start.is_none() {
+ if let Some((char, point)) = chars.peek() {
+ if is_possible_sentence_start(*char) {
+ start = Some(*point);
+ }
+ }
+ }
+
+ let mut end = relative_to;
+ for (char, point) in chars {
+ if start.is_some() {
+ if !char.is_whitespace() {
+ end = point;
+ *end.column_mut() += char.len_utf8() as u32;
+ end = map.clip_point(end, Bias::Left);
+ }
+
+ if is_sentence_end(map, point) {
+ break;
+ }
+ } else if is_possible_sentence_start(char) {
+ if around {
+ start = Some(point);
+ } else {
+ end = point;
+ break;
+ }
+ }
+ }
+
+ let mut range = start.unwrap_or(previous_end)..end;
+ if around {
+ range = expand_to_include_whitespace(map, range, false);
+ }
+
+ range
+}
+
+fn is_possible_sentence_start(character: char) -> bool {
+ !character.is_whitespace() && character != '.'
+}
+
+const SENTENCE_END_PUNCTUATION: &[char] = &['.', '!', '?'];
+const SENTENCE_END_FILLERS: &[char] = &[')', ']', '"', '\''];
+const SENTENCE_END_WHITESPACE: &[char] = &[' ', '\t', '\n'];
+fn is_sentence_end(map: &DisplaySnapshot, point: DisplayPoint) -> bool {
+ let mut chars = map.chars_at(point).peekable();
+
+ if let Some((char, _)) = chars.next() {
+ if char == '\n' && chars.peek().map(|(c, _)| c == &'\n').unwrap_or(false) {
+ return true;
+ }
+
+ if !SENTENCE_END_PUNCTUATION.contains(&char) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ for (char, _) in chars {
+ if SENTENCE_END_WHITESPACE.contains(&char) {
+ return true;
+ }
+
+ if !SENTENCE_END_FILLERS.contains(&char) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/// Expands the passed range to include whitespace on one side or the other in a line. Attempts to add the
+/// whitespace to the end first and falls back to the start if there was none.
+fn expand_to_include_whitespace(
+ map: &DisplaySnapshot,
+ mut range: Range<DisplayPoint>,
+ stop_at_newline: bool,
+) -> Range<DisplayPoint> {
+ let mut whitespace_included = false;
+ for (char, point) in map.chars_at(range.end) {
+ range.end = point;
+
+ if char == '\n' && stop_at_newline {
+ break;
+ }
+
+ if char.is_whitespace() {
+ whitespace_included = true;
+ } else {
+ break;
+ }
+ }
+
+ if !whitespace_included {
+ for (char, point) in map.reverse_chars_at(range.start) {
+ if char == '\n' && stop_at_newline {
+ break;
+ }
+
+ if !char.is_whitespace() {
+ break;
+ }
+
+ range.start = point;
+ }
+ }
+
+ range
+}
+
+#[cfg(test)]
+mod test {
+ use indoc::indoc;
+
+ use crate::test_contexts::NeovimBackedTestContext;
+
+ const WORD_LOCATIONS: &'static str = indoc! {"
+ The quick ˇbrowˇnˇ
+ fox ˇjuˇmpsˇ over
+ the lazy dogˇ
+ ˇ
+ ˇ
+ ˇ
+ Thˇeˇ-ˇquˇickˇ ˇbrownˇ
+ ˇ
+ ˇ
+ ˇ fox-jumpˇs over
+ the lazy dogˇ
+ ˇ
+ "};
+
+ #[gpui::test]
+ async fn test_change_in_word(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new("test_change_in_word", cx)
+ .await
+ .binding(["c", "i", "w"]);
+ cx.assert_all(WORD_LOCATIONS).await;
+ let mut cx = cx.consume().binding(["c", "i", "shift-w"]);
+ cx.assert_all(WORD_LOCATIONS).await;
+ }
+
+ #[gpui::test]
+ async fn test_delete_in_word(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new("test_delete_in_word", cx)
+ .await
+ .binding(["d", "i", "w"]);
+ cx.assert_all(WORD_LOCATIONS).await;
+ let mut cx = cx.consume().binding(["d", "i", "shift-w"]);
+ cx.assert_all(WORD_LOCATIONS).await;
+ }
+
+ #[gpui::test]
+ async fn test_change_around_word(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new("test_change_around_word", cx)
+ .await
+ .binding(["c", "a", "w"]);
+ cx.assert_all(WORD_LOCATIONS).await;
+ let mut cx = cx.consume().binding(["c", "a", "shift-w"]);
+ cx.assert_all(WORD_LOCATIONS).await;
+ }
+
+ #[gpui::test]
+ async fn test_delete_around_word(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new("test_delete_around_word", cx)
+ .await
+ .binding(["d", "a", "w"]);
+ cx.assert_all(WORD_LOCATIONS).await;
+ let mut cx = cx.consume().binding(["d", "a", "shift-w"]);
+ cx.assert_all(WORD_LOCATIONS).await;
+ }
+
+ const SENTENCE_EXAMPLES: &[&'static str] = &[
+ "ˇThe quick ˇbrownˇ?ˇ ˇFox Jˇumpsˇ!ˇ Ovˇer theˇ lazyˇ.",
+ indoc! {"
+ ˇThe quick ˇbrownˇ
+ fox jumps over
+ the lazy doˇgˇ.ˇ ˇThe quick ˇ
+ brown fox jumps over
+ "},
+ // Double newlines are broken currently
+ // indoc! {"
+ // The quick brown fox jumps.
+ // Over the lazy dog
+ // ˇ
+ // ˇ
+ // ˇ fox-jumpˇs over
+ // the lazy dog.ˇ
+ // ˇ
+ // "},
+ r#"The quick brown.)]'" Brown fox jumps."#,
+ ];
+
+ #[gpui::test]
+ async fn test_change_in_sentence(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new("test_change_in_sentence", cx)
+ .await
+ .binding(["c", "i", "s"]);
+ for sentence_example in SENTENCE_EXAMPLES {
+ cx.assert_all(sentence_example).await;
+ }
+ }
+
+ #[gpui::test]
+ async fn test_delete_in_sentence(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new("test_delete_in_sentence", cx)
+ .await
+ .binding(["d", "i", "s"]);
+ for sentence_example in SENTENCE_EXAMPLES {
+ cx.assert_all(sentence_example).await;
+ }
+ }
+
+ #[gpui::test]
+ #[ignore] // End cursor position is incorrect
+ async fn test_change_around_sentence(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new("test_change_around_sentence", cx)
+ .await
+ .binding(["c", "a", "s"]);
+ for sentence_example in SENTENCE_EXAMPLES {
+ cx.assert_all(sentence_example).await;
+ }
+ }
+
+ #[gpui::test]
+ #[ignore] // End cursor position is incorrect
+ async fn test_delete_around_sentence(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new("test_delete_around_sentence", cx)
+ .await
+ .binding(["d", "a", "s"]);
+ for sentence_example in SENTENCE_EXAMPLES {
+ cx.assert_all(sentence_example).await;
+ }
+ }
+}
@@ -1,8 +1,8 @@
use editor::CursorShape;
use gpui::keymap::Context;
-use serde::Deserialize;
+use serde::{Deserialize, Serialize};
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub enum Mode {
Normal,
Insert,
@@ -26,6 +26,7 @@ pub enum Operator {
Change,
Delete,
Yank,
+ Object { around: bool },
}
#[derive(Default)]
@@ -77,7 +78,12 @@ impl VimState {
context.set.insert("VimControl".to_string());
}
- Operator::set_context(self.operator_stack.last(), &mut context);
+ let active_operator = self.operator_stack.last();
+ if matches!(active_operator, Some(Operator::Object { .. })) {
+ context.set.insert("VimObject".to_string());
+ }
+
+ Operator::set_context(active_operator, &mut context);
context
}
@@ -87,6 +93,8 @@ impl Operator {
pub fn set_context(operator: Option<&Operator>, context: &mut Context) {
let operator_context = match operator {
Some(Operator::Namespace(Namespace::G)) => "g",
+ Some(Operator::Object { around: false }) => "i",
+ Some(Operator::Object { around: true }) => "a",
Some(Operator::Change) => "c",
Some(Operator::Delete) => "d",
Some(Operator::Yank) => "y",
@@ -0,0 +1,9 @@
+mod neovim_backed_binding_test_context;
+mod neovim_backed_test_context;
+mod vim_binding_test_context;
+mod vim_test_context;
+
+pub use neovim_backed_binding_test_context::*;
+pub use neovim_backed_test_context::*;
+pub use vim_binding_test_context::*;
+pub use vim_test_context::*;
@@ -0,0 +1,56 @@
+use std::ops::{Deref, DerefMut};
+
+use util::test::marked_text_offsets;
+
+use super::NeovimBackedTestContext;
+
+pub struct NeovimBackedBindingTestContext<'a, const COUNT: usize> {
+ cx: NeovimBackedTestContext<'a>,
+ keystrokes_under_test: [&'static str; COUNT],
+}
+
+impl<'a, const COUNT: usize> NeovimBackedBindingTestContext<'a, COUNT> {
+ pub fn new(
+ keystrokes_under_test: [&'static str; COUNT],
+ cx: NeovimBackedTestContext<'a>,
+ ) -> Self {
+ Self {
+ cx,
+ keystrokes_under_test,
+ }
+ }
+
+ pub fn consume(self) -> NeovimBackedTestContext<'a> {
+ self.cx
+ }
+
+ pub async fn assert(&mut self, initial_state: &str) {
+ self.cx
+ .assert_binding_matches(self.keystrokes_under_test, initial_state)
+ .await
+ }
+
+ pub async fn assert_all(&mut self, marked_positions: &str) {
+ let (unmarked_text, cursor_offsets) = marked_text_offsets(marked_positions);
+
+ for cursor_offset in cursor_offsets.iter() {
+ let mut marked_text = unmarked_text.clone();
+ marked_text.insert(*cursor_offset, 'ˇ');
+ self.assert(&marked_text).await;
+ }
+ }
+}
+
+impl<'a, const COUNT: usize> Deref for NeovimBackedBindingTestContext<'a, COUNT> {
+ type Target = NeovimBackedTestContext<'a>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.cx
+ }
+}
+
+impl<'a, const COUNT: usize> DerefMut for NeovimBackedBindingTestContext<'a, COUNT> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.cx
+ }
+}
@@ -0,0 +1,374 @@
+use std::{
+ ops::{Deref, DerefMut},
+ path::PathBuf,
+};
+
+use editor::DisplayPoint;
+use gpui::keymap::Keystroke;
+
+#[cfg(feature = "neovim")]
+use async_compat::Compat;
+#[cfg(feature = "neovim")]
+use async_trait::async_trait;
+#[cfg(feature = "neovim")]
+use nvim_rs::{
+ create::tokio::new_child_cmd, error::LoopError, Handler, Neovim, UiAttachOptions, Value,
+};
+#[cfg(feature = "neovim")]
+use tokio::{
+ process::{Child, ChildStdin, Command},
+ task::JoinHandle,
+};
+
+use crate::state::Mode;
+
+use super::{NeovimBackedBindingTestContext, VimTestContext};
+
+pub struct NeovimBackedTestContext<'a> {
+ cx: VimTestContext<'a>,
+ test_case_id: &'static str,
+ data_counter: usize,
+ #[cfg(feature = "neovim")]
+ nvim: Neovim<nvim_rs::compat::tokio::Compat<ChildStdin>>,
+ #[cfg(feature = "neovim")]
+ _join_handle: JoinHandle<Result<(), Box<LoopError>>>,
+ #[cfg(feature = "neovim")]
+ _child: Child,
+}
+
+impl<'a> NeovimBackedTestContext<'a> {
+ pub async fn new(
+ test_case_id: &'static str,
+ cx: &'a mut gpui::TestAppContext,
+ ) -> NeovimBackedTestContext<'a> {
+ let cx = VimTestContext::new(cx, true).await;
+
+ #[cfg(feature = "neovim")]
+ let handler = NvimHandler {};
+ #[cfg(feature = "neovim")]
+ let (nvim, join_handle, child) = Compat::new(async {
+ let (nvim, join_handle, child) = new_child_cmd(
+ &mut Command::new("nvim").arg("--embed").arg("--clean"),
+ handler,
+ )
+ .await
+ .expect("Could not connect to neovim process");
+
+ nvim.ui_attach(100, 100, &UiAttachOptions::default())
+ .await
+ .expect("Could not attach to ui");
+
+ (nvim, join_handle, child)
+ })
+ .await;
+
+ let result = Self {
+ cx,
+ test_case_id,
+ data_counter: 0,
+ #[cfg(feature = "neovim")]
+ nvim,
+ #[cfg(feature = "neovim")]
+ _join_handle: join_handle,
+ #[cfg(feature = "neovim")]
+ _child: child,
+ };
+
+ #[cfg(feature = "neovim")]
+ {
+ result.clear_test_data()
+ }
+
+ result
+ }
+
+ pub async fn simulate_shared_keystroke(&mut self, keystroke_text: &str) {
+ let keystroke = Keystroke::parse(keystroke_text).unwrap();
+
+ #[cfg(feature = "neovim")]
+ {
+ let special = keystroke.shift
+ || keystroke.ctrl
+ || keystroke.alt
+ || keystroke.cmd
+ || keystroke.key.len() > 1;
+ let start = if special { "<" } else { "" };
+ let shift = if keystroke.shift { "S-" } else { "" };
+ let ctrl = if keystroke.ctrl { "C-" } else { "" };
+ let alt = if keystroke.alt { "M-" } else { "" };
+ let cmd = if keystroke.cmd { "D-" } else { "" };
+ let end = if special { ">" } else { "" };
+
+ let key = format!("{start}{shift}{ctrl}{alt}{cmd}{}{end}", keystroke.key);
+
+ self.nvim
+ .input(&key)
+ .await
+ .expect("Could not input keystroke");
+ }
+
+ let window_id = self.window_id;
+ self.cx.dispatch_keystroke(window_id, keystroke, false);
+ }
+
+ pub async fn simulate_shared_keystrokes<const COUNT: usize>(
+ &mut self,
+ keystroke_texts: [&str; COUNT],
+ ) {
+ for keystroke_text in keystroke_texts.into_iter() {
+ self.simulate_shared_keystroke(keystroke_text).await;
+ }
+ }
+
+ pub async fn set_shared_state(&mut self, marked_text: &str) {
+ self.set_state(marked_text, Mode::Normal);
+
+ #[cfg(feature = "neovim")]
+ {
+ let cursor_point =
+ self.editor(|editor, cx| editor.selections.newest::<language::Point>(cx));
+ let nvim_buffer = self
+ .nvim
+ .get_current_buf()
+ .await
+ .expect("Could not get neovim buffer");
+ let mut lines = self
+ .buffer_text()
+ .lines()
+ .map(|line| line.to_string())
+ .collect::<Vec<_>>();
+
+ if lines.len() > 1 {
+ // Add final newline which is missing from buffer_text
+ lines.push("".to_string());
+ }
+
+ nvim_buffer
+ .set_lines(0, -1, false, lines)
+ .await
+ .expect("Could not set nvim buffer text");
+
+ self.nvim
+ .input("<escape>")
+ .await
+ .expect("Could not send escape to nvim");
+ self.nvim
+ .input("<escape>")
+ .await
+ .expect("Could not send escape to nvim");
+
+ let nvim_window = self
+ .nvim
+ .get_current_win()
+ .await
+ .expect("Could not get neovim window");
+ nvim_window
+ .set_cursor((
+ cursor_point.head().row as i64 + 1,
+ cursor_point.head().column as i64,
+ ))
+ .await
+ .expect("Could not set nvim cursor position");
+ }
+ }
+
+ pub async fn assert_state_matches(&mut self) {
+ assert_eq!(self.neovim_text().await, self.buffer_text());
+
+ let zed_head = self.update_editor(|editor, cx| editor.selections.newest_display(cx).head());
+ assert_eq!(self.neovim_head().await, zed_head);
+
+ if let Some(neovim_mode) = self.neovim_mode().await {
+ assert_eq!(neovim_mode, self.mode());
+ }
+ }
+
+ #[cfg(feature = "neovim")]
+ pub async fn neovim_text(&mut self) -> String {
+ let nvim_buffer = self
+ .nvim
+ .get_current_buf()
+ .await
+ .expect("Could not get neovim buffer");
+ let text = nvim_buffer
+ .get_lines(0, -1, false)
+ .await
+ .expect("Could not get buffer text")
+ .join("\n");
+
+ self.write_test_data(text.clone(), "text");
+ text
+ }
+
+ #[cfg(not(feature = "neovim"))]
+ pub async fn neovim_text(&mut self) -> String {
+ self.read_test_data("text")
+ }
+
+ #[cfg(feature = "neovim")]
+ pub async fn neovim_head(&mut self) -> DisplayPoint {
+ let nvim_row: u32 = self
+ .nvim
+ .command_output("echo line('.')")
+ .await
+ .unwrap()
+ .parse::<u32>()
+ .unwrap()
+ - 1; // Neovim rows start at 1
+ let nvim_column: u32 = self
+ .nvim
+ .command_output("echo col('.')")
+ .await
+ .unwrap()
+ .parse::<u32>()
+ .unwrap()
+ - 1; // Neovim columns start at 1
+
+ let serialized = format!("{},{}", nvim_row.to_string(), nvim_column.to_string());
+ self.write_test_data(serialized, "head");
+
+ DisplayPoint::new(nvim_row, nvim_column)
+ }
+
+ #[cfg(not(feature = "neovim"))]
+ pub async fn neovim_head(&mut self) -> DisplayPoint {
+ let serialized = self.read_test_data("head");
+ let mut components = serialized.split(',');
+ let nvim_row = components.next().unwrap().parse::<u32>().unwrap();
+ let nvim_column = components.next().unwrap().parse::<u32>().unwrap();
+
+ DisplayPoint::new(nvim_row, nvim_column)
+ }
+
+ #[cfg(feature = "neovim")]
+ pub async fn neovim_mode(&mut self) -> Option<Mode> {
+ let nvim_mode_text = self
+ .nvim
+ .get_mode()
+ .await
+ .expect("Could not get mode")
+ .into_iter()
+ .find_map(|(key, value)| {
+ if key.as_str() == Some("mode") {
+ Some(value.as_str().unwrap().to_owned())
+ } else {
+ None
+ }
+ })
+ .expect("Could not find mode value");
+
+ let mode = match nvim_mode_text.as_ref() {
+ "i" => Some(Mode::Insert),
+ "n" => Some(Mode::Normal),
+ "v" => Some(Mode::Visual { line: false }),
+ "V" => Some(Mode::Visual { line: true }),
+ _ => None,
+ };
+
+ let serialized = serde_json::to_string(&mode).expect("Could not serialize mode");
+
+ self.write_test_data(serialized, "mode");
+
+ mode
+ }
+
+ #[cfg(not(feature = "neovim"))]
+ pub async fn neovim_mode(&mut self) -> Option<Mode> {
+ let serialized = self.read_test_data("mode");
+ serde_json::from_str(&serialized).expect("Could not deserialize test data")
+ }
+
+ fn test_data_directory(&self) -> PathBuf {
+ let mut data_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
+ data_path.push("test_data");
+ data_path.push(self.test_case_id);
+ data_path
+ }
+
+ fn next_data_path(&mut self, kind: &str) -> PathBuf {
+ let mut data_path = self.test_data_directory();
+ data_path.push(format!("{}{}.txt", self.data_counter, kind));
+ self.data_counter += 1;
+ data_path
+ }
+
+ #[cfg(not(feature = "neovim"))]
+ fn read_test_data(&mut self, kind: &str) -> String {
+ let path = self.next_data_path(kind);
+ std::fs::read_to_string(path).expect(
+ "Could not read test data. Is it generated? Try running test with '--features neovim'",
+ )
+ }
+
+ #[cfg(feature = "neovim")]
+ fn write_test_data(&mut self, data: String, kind: &str) {
+ let path = self.next_data_path(kind);
+ std::fs::create_dir_all(path.parent().unwrap())
+ .expect("Could not create test data directory");
+ std::fs::write(path, data).expect("Could not write out test data");
+ }
+
+ #[cfg(feature = "neovim")]
+ fn clear_test_data(&self) {
+ // If the path does not exist, no biggy, we will create it
+ std::fs::remove_dir_all(self.test_data_directory()).ok();
+ }
+
+ pub async fn assert_binding_matches<const COUNT: usize>(
+ &mut self,
+ keystrokes: [&str; COUNT],
+ initial_state: &str,
+ ) {
+ dbg!(keystrokes, initial_state);
+ self.set_shared_state(initial_state).await;
+ self.simulate_shared_keystrokes(keystrokes).await;
+ self.assert_state_matches().await;
+ }
+
+ pub fn binding<const COUNT: usize>(
+ self,
+ keystrokes: [&'static str; COUNT],
+ ) -> NeovimBackedBindingTestContext<'a, COUNT> {
+ NeovimBackedBindingTestContext::new(keystrokes, self)
+ }
+}
+
+#[derive(Clone)]
+struct NvimHandler {}
+
+#[cfg(feature = "neovim")]
+#[async_trait]
+impl Handler for NvimHandler {
+ type Writer = nvim_rs::compat::tokio::Compat<ChildStdin>;
+
+ async fn handle_request(
+ &self,
+ _event_name: String,
+ _arguments: Vec<Value>,
+ _neovim: Neovim<Self::Writer>,
+ ) -> Result<Value, Value> {
+ unimplemented!();
+ }
+
+ async fn handle_notify(
+ &self,
+ _event_name: String,
+ _arguments: Vec<Value>,
+ _neovim: Neovim<Self::Writer>,
+ ) {
+ }
+}
+
+impl<'a> Deref for NeovimBackedTestContext<'a> {
+ type Target = VimTestContext<'a>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.cx
+ }
+}
+
+impl<'a> DerefMut for NeovimBackedTestContext<'a> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.cx
+ }
+}
@@ -0,0 +1,69 @@
+use std::ops::{Deref, DerefMut};
+
+use crate::*;
+
+use super::VimTestContext;
+
+pub struct VimBindingTestContext<'a, const COUNT: usize> {
+ cx: VimTestContext<'a>,
+ keystrokes_under_test: [&'static str; COUNT],
+ mode_before: Mode,
+ mode_after: Mode,
+}
+
+impl<'a, const COUNT: usize> VimBindingTestContext<'a, COUNT> {
+ pub fn new(
+ keystrokes_under_test: [&'static str; COUNT],
+ mode_before: Mode,
+ mode_after: Mode,
+ cx: VimTestContext<'a>,
+ ) -> Self {
+ Self {
+ cx,
+ keystrokes_under_test,
+ mode_before,
+ mode_after,
+ }
+ }
+
+ pub fn binding<const NEW_COUNT: usize>(
+ self,
+ keystrokes_under_test: [&'static str; NEW_COUNT],
+ ) -> VimBindingTestContext<'a, NEW_COUNT> {
+ VimBindingTestContext {
+ keystrokes_under_test,
+ cx: self.cx,
+ mode_before: self.mode_before,
+ mode_after: self.mode_after,
+ }
+ }
+
+ pub fn mode_after(mut self, mode_after: Mode) -> Self {
+ self.mode_after = mode_after;
+ self
+ }
+
+ pub fn assert(&mut self, initial_state: &str, state_after: &str) {
+ self.cx.assert_binding(
+ self.keystrokes_under_test,
+ initial_state,
+ self.mode_before,
+ state_after,
+ self.mode_after,
+ )
+ }
+}
+
+impl<'a, const COUNT: usize> Deref for VimBindingTestContext<'a, COUNT> {
+ type Target = VimTestContext<'a>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.cx
+ }
+}
+
+impl<'a, const COUNT: usize> DerefMut for VimBindingTestContext<'a, COUNT> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.cx
+ }
+}
@@ -8,6 +8,8 @@ use workspace::{pane, AppState, WorkspaceHandle};
use crate::{state::Operator, *};
+use super::VimBindingTestContext;
+
pub struct VimTestContext<'a> {
cx: EditorTestContext<'a>,
workspace: ViewHandle<Workspace>,
@@ -168,67 +170,3 @@ impl<'a> DerefMut for VimTestContext<'a> {
&mut self.cx
}
}
-
-pub struct VimBindingTestContext<'a, const COUNT: usize> {
- cx: VimTestContext<'a>,
- keystrokes_under_test: [&'static str; COUNT],
- mode_before: Mode,
- mode_after: Mode,
-}
-
-impl<'a, const COUNT: usize> VimBindingTestContext<'a, COUNT> {
- pub fn new(
- keystrokes_under_test: [&'static str; COUNT],
- mode_before: Mode,
- mode_after: Mode,
- cx: VimTestContext<'a>,
- ) -> Self {
- Self {
- cx,
- keystrokes_under_test,
- mode_before,
- mode_after,
- }
- }
-
- pub fn binding<const NEW_COUNT: usize>(
- self,
- keystrokes_under_test: [&'static str; NEW_COUNT],
- ) -> VimBindingTestContext<'a, NEW_COUNT> {
- VimBindingTestContext {
- keystrokes_under_test,
- cx: self.cx,
- mode_before: self.mode_before,
- mode_after: self.mode_after,
- }
- }
-
- pub fn mode_after(mut self, mode_after: Mode) -> Self {
- self.mode_after = mode_after;
- self
- }
-
- pub fn assert(&mut self, initial_state: &str, state_after: &str) {
- self.cx.assert_binding(
- self.keystrokes_under_test,
- initial_state,
- self.mode_before,
- state_after,
- self.mode_after,
- )
- }
-}
-
-impl<'a, const COUNT: usize> Deref for VimBindingTestContext<'a, COUNT> {
- type Target = VimTestContext<'a>;
-
- fn deref(&self) -> &Self::Target {
- &self.cx
- }
-}
-
-impl<'a, const COUNT: usize> DerefMut for VimBindingTestContext<'a, COUNT> {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.cx
- }
-}
@@ -1,10 +1,11 @@
#[cfg(test)]
-mod vim_test_context;
+mod test_contexts;
mod editor_events;
mod insert;
mod motion;
mod normal;
+mod object;
mod state;
mod utils;
mod visual;
@@ -32,6 +33,7 @@ pub fn init(cx: &mut MutableAppContext) {
normal::init(cx);
visual::init(cx);
insert::init(cx);
+ object::init(cx);
motion::init(cx);
// Vim Actions
@@ -144,7 +146,8 @@ impl Vim {
}
fn pop_operator(&mut self, cx: &mut MutableAppContext) -> Operator {
- let popped_operator = self.state.operator_stack.pop().expect("Operator popped when no operator was on the stack. This likely means there is an invalid keymap config");
+ let popped_operator = self.state.operator_stack.pop()
+ .expect("Operator popped when no operator was on the stack. This likely means there is an invalid keymap config");
self.sync_vim_settings(cx);
popped_operator
}
@@ -210,7 +213,10 @@ mod test {
use indoc::indoc;
use search::BufferSearchBar;
- use crate::{state::Mode, vim_test_context::VimTestContext};
+ use crate::{
+ state::Mode,
+ test_contexts::{NeovimBackedTestContext, VimTestContext},
+ };
#[gpui::test]
async fn test_initially_disabled(cx: &mut gpui::TestAppContext) {
@@ -219,6 +225,19 @@ mod test {
cx.assert_editor_state("hjklˇ");
}
+ #[gpui::test]
+ async fn test_neovim(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new("test_neovim", cx).await;
+
+ cx.simulate_shared_keystroke("i").await;
+ cx.simulate_shared_keystrokes([
+ "shift-T", "e", "s", "t", " ", "t", "e", "s", "t", "escape", "0", "d", "w",
+ ])
+ .await;
+ cx.assert_state_matches().await;
+ cx.assert_editor_state("ˇtest");
+ }
+
#[gpui::test]
async fn test_toggle_through_settings(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
@@ -6,7 +6,7 @@ use gpui::{actions, MutableAppContext, ViewContext};
use language::{AutoindentMode, SelectionGoal};
use workspace::Workspace;
-use crate::{motion::Motion, state::Mode, utils::copy_selections_content, Vim};
+use crate::{motion::Motion, object::Object, state::Mode, utils::copy_selections_content, Vim};
actions!(vim, [VisualDelete, VisualChange, VisualYank, VisualPaste]);
@@ -43,6 +43,8 @@ pub fn visual_motion(motion: Motion, cx: &mut MutableAppContext) {
});
}
+pub fn visual_object(_object: Object, _cx: &mut MutableAppContext) {}
+
pub fn change(_: &mut Workspace, _: &VisualChange, cx: &mut ViewContext<Workspace>) {
Vim::update(cx, |vim, cx| {
vim.update_active_editor(cx, |editor, cx| {
@@ -274,7 +276,7 @@ pub fn paste(_: &mut Workspace, _: &VisualPaste, cx: &mut ViewContext<Workspace>
mod test {
use indoc::indoc;
- use crate::{state::Mode, vim_test_context::VimTestContext};
+ use crate::{state::Mode, test_contexts::VimTestContext};
#[gpui::test]
async fn test_enter_visual_mode(cx: &mut gpui::TestAppContext) {
@@ -0,0 +1 @@
+Fox Jumps! Over the lazy.
@@ -0,0 +1 @@
+0,16
@@ -0,0 +1 @@
+0,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+Fox Jumps! Over the lazy.
@@ -0,0 +1 @@
+0,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+Fox Jumps! Over the lazy.
@@ -0,0 +1 @@
+0,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+The quick brown? Over the lazy.
@@ -0,0 +1,12 @@
+The quick
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+1,4
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,9
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,10
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown over
+the lazy dog
+
@@ -0,0 +1 @@
+6,15
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,10 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ over
+the lazy dog
+
@@ -0,0 +1 @@
+7,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,11 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+ over
+the lazy dog
+
@@ -0,0 +1 @@
+8,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ over
+the lazy dog
+
@@ -0,0 +1 @@
+9,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ over
+the lazy dog
+
@@ -0,0 +1,12 @@
+The quick brown
+fox over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+9,2
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,11 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+10,12
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,11 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+11,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+1,4
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+1,9
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,11 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+2,12
@@ -0,0 +1 @@
+0,10
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,11 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+3,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,11 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+4,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,11 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+5,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+Thequick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,3
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,4
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1,12 @@
+The quick
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,4
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,9
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,10
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,15
@@ -0,0 +1 @@
+0,10
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,10 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+7,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,11 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+8,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+9,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-over
+the lazy dog
+
@@ -0,0 +1 @@
+9,6
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,11 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+10,12
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,11 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+11,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1,11 @@
+The quick brown jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+0,10
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+0,10
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,11 @@
+The quick brown jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+0,15
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+1,4
@@ -0,0 +1 @@
+0,15
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+1,4
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+1,9
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,11 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+2,12
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,11 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+3,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,11 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+4,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,11 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+ brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+5,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1,12 @@
+The quick brown
+fox over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+ Fox Jumps! Over the lazy.
@@ -0,0 +1 @@
+0,16
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+The quick brown? Over the lazy.
@@ -0,0 +1 @@
+0,17
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+The quick brown? Over the lazy.
@@ -0,0 +1 @@
+0,17
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+The quick brown? Over the lazy.
@@ -0,0 +1 @@
+0,17
@@ -0,0 +1 @@
+0,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+The quick brown? Fox Jumps!Over the lazy.
@@ -0,0 +1 @@
+0,27
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+The quick brown? Fox Jumps!
@@ -0,0 +1 @@
+0,28
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+The quick brown? Fox Jumps!
@@ -0,0 +1 @@
+0,28
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+The quick brown? Fox Jumps!
@@ -0,0 +1 @@
+0,28
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,2 @@
+ The quick
+brown fox jumps over
@@ -0,0 +1 @@
+0,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,2 @@
+ The quick
+brown fox jumps over
@@ -0,0 +1 @@
+0,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,2 @@
+ The quick
+brown fox jumps over
@@ -0,0 +1 @@
+ Fox Jumps! Over the lazy.
@@ -0,0 +1 @@
+0,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,2 @@
+ The quick
+brown fox jumps over
@@ -0,0 +1 @@
+0,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,2 @@
+ The quick
+brown fox jumps over
@@ -0,0 +1 @@
+0,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,4 @@
+The quick brown
+fox jumps over
+the lazy dog.The quick
+brown fox jumps over
@@ -0,0 +1 @@
+2,13
@@ -0,0 +1 @@
+0,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,3 @@
+The quick brown
+fox jumps over
+the lazy dog.
@@ -0,0 +1 @@
+2,14
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,3 @@
+The quick brown
+fox jumps over
+the lazy dog.
@@ -0,0 +1 @@
+2,14
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+ Fox Jumps! Over the lazy.
@@ -0,0 +1 @@
+0,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+The quick brown?Fox Jumps! Over the lazy.
@@ -0,0 +1,12 @@
+The quick
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+ brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+ brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+ brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+1,4
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quickbrown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,9
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,10
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,15
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+7,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+8,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+9,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ over
+the lazy dog
+
@@ -0,0 +1,12 @@
+The quick brown
+fox over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+9,2
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+10,12
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+11,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+1,4
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumpsover
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+1,9
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+2,12
@@ -0,0 +1 @@
+0,10
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+3,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+4,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+5,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+Thequick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,3
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The- brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,4
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The- brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1,12 @@
+The quick
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,4
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quickbrown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,9
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,10
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,15
@@ -0,0 +1 @@
+0,10
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+7,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+8,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+9,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox- over
+the lazy dog
+
@@ -0,0 +1 @@
+9,6
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+10,12
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+11,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+0,10
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+0,10
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+0,15
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+1,4
@@ -0,0 +1 @@
+0,15
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+1,4
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumpsover
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+1,9
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+2,12
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+3,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+4,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+5,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+ brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1,12 @@
+The quick brown
+fox over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+Fox Jumps! Over the lazy.
@@ -0,0 +1 @@
+0,16
@@ -0,0 +1 @@
+0,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1 @@
+Fox Jumps! Over the lazy.
@@ -0,0 +1 @@
+0,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1 @@
+Fox Jumps! Over the lazy.
@@ -0,0 +1 @@
+0,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1 @@
+The quick brown? Over the lazy.
@@ -0,0 +1,12 @@
+The quick
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+1,4
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,9
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,9
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown over
+the lazy dog
+
@@ -0,0 +1 @@
+6,15
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,10 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ over
+the lazy dog
+
@@ -0,0 +1 @@
+7,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,11 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+ over
+the lazy dog
+
@@ -0,0 +1 @@
+8,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ over
+the lazy dog
+
@@ -0,0 +1 @@
+9,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ over
+the lazy dog
+
@@ -0,0 +1,12 @@
+The quick brown
+fox over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+9,2
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,11 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+10,11
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,11 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+10,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1 @@
+1,4
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+1,8
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,11 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+2,11
@@ -0,0 +1 @@
+0,9
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,10 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+3,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,10 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+4,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,11 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+5,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+Thequick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,3
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,4
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1,12 @@
+The quick
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,4
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,9
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,9
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,15
@@ -0,0 +1 @@
+0,9
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,10 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+7,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,11 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+8,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+9,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-over
+the lazy dog
+
@@ -0,0 +1 @@
+9,6
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,11 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+10,11
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,11 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+10,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1,11 @@
+The quick brown jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+0,9
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+0,9
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,11 @@
+The quick brown jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+0,15
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+1,4
@@ -0,0 +1 @@
+0,15
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+1,4
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+1,8
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,11 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+2,11
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,10 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+3,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,10 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+4,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,11 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+ brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+5,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1,12 @@
+The quick brown
+fox over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+ Fox Jumps! Over the lazy.
@@ -0,0 +1 @@
+0,16
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1 @@
+The quick brown? Over the lazy.
@@ -0,0 +1 @@
+0,17
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1 @@
+The quick brown? Over the lazy.
@@ -0,0 +1 @@
+0,17
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1 @@
+The quick brown? Over the lazy.
@@ -0,0 +1 @@
+0,17
@@ -0,0 +1 @@
+0,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1 @@
+The quick brown? Fox Jumps!Over the lazy.
@@ -0,0 +1 @@
+0,27
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1 @@
+The quick brown? Fox Jumps!
@@ -0,0 +1 @@
+0,27
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1 @@
+The quick brown? Fox Jumps!
@@ -0,0 +1 @@
+0,27
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1 @@
+The quick brown? Fox Jumps!
@@ -0,0 +1 @@
+0,27
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,2 @@
+ The quick
+brown fox jumps over
@@ -0,0 +1 @@
+0,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,2 @@
+ The quick
+brown fox jumps over
@@ -0,0 +1 @@
+0,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,2 @@
+ The quick
+brown fox jumps over
@@ -0,0 +1 @@
+ Fox Jumps! Over the lazy.
@@ -0,0 +1 @@
+0,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,2 @@
+ The quick
+brown fox jumps over
@@ -0,0 +1 @@
+0,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,2 @@
+ The quick
+brown fox jumps over
@@ -0,0 +1 @@
+0,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,4 @@
+The quick brown
+fox jumps over
+the lazy dog.The quick
+brown fox jumps over
@@ -0,0 +1 @@
+2,13
@@ -0,0 +1 @@
+0,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,3 @@
+The quick brown
+fox jumps over
+the lazy dog.
@@ -0,0 +1 @@
+2,13
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,3 @@
+The quick brown
+fox jumps over
+the lazy dog.
@@ -0,0 +1 @@
+2,13
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1 @@
+ Fox Jumps! Over the lazy.
@@ -0,0 +1 @@
+0,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1 @@
+The quick brown?Fox Jumps! Over the lazy.
@@ -0,0 +1,12 @@
+The quick
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+ brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+ brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+ brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+1,4
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quickbrown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,9
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,10
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,14
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+7,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+8,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+9,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ over
+the lazy dog
+
@@ -0,0 +1,12 @@
+The quick brown
+fox over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+9,2
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+10,11
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+11,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1 @@
+1,4
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumpsover
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+1,9
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+2,11
@@ -0,0 +1 @@
+0,10
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+3,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+4,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+5,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+Thequick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,3
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The- brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,4
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The- brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1,12 @@
+The quick
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,4
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quickbrown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,9
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,10
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+6,14
@@ -0,0 +1 @@
+0,10
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+7,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+8,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+9,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox- over
+the lazy dog
+
@@ -0,0 +1 @@
+9,6
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+10,11
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+11,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+0,10
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+0,10
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+0,14
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+1,4
@@ -0,0 +1 @@
+0,14
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+1,4
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumpsover
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+1,9
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+2,11
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+3,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+4,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+5,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,12 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+ brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1,12 @@
+The quick brown
+fox over
+the lazy dog
+
+
+
+The-quick brown
+
+
+ fox-jumps over
+the lazy dog
+
@@ -0,0 +1 @@
+test
@@ -0,0 +1 @@
+0,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,9 @@
+The quick
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,9
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,10
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,15
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+7,0
@@ -0,0 +1 @@
+1,4
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ over
+the lazy dog
@@ -0,0 +1 @@
+7,2
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+0,10
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+0,10
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,8 @@
+The quick brown jumps over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+0,15
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+1,4
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+1,4
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1,9 @@
+The quick brown
+fox over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+1,9
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,8 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+2,12
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,8 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+3,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,8 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+4,0
@@ -0,0 +1 @@
+1,4
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,8 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+5,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+Thequick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,3
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,4
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,4
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,9
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick
+ fox-jumps over
+the lazy dog
@@ -0,0 +1,9 @@
+The quick brown
+fox jumpsover
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,10
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,8 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,15
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+-jumps over
+the lazy dog
@@ -0,0 +1 @@
+7,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ fox-over
+the lazy dog
@@ -0,0 +1 @@
+7,6
@@ -0,0 +1 @@
+1,9
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+0,10
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+0,10
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,8 @@
+The quick brown jumps over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+0,15
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+1,4
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+1,4
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+1,9
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,8 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+2,12
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,8 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+3,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,8 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+4,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,8 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+ brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+5,0
@@ -0,0 +1 @@
+2,12
@@ -0,0 +1 @@
+0,10
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,9
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,10
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,8 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown over
+the lazy dog
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,15
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ over
+the lazy dog
@@ -0,0 +1 @@
+7,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ over
+the lazy dog
@@ -0,0 +1 @@
+7,2
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+0,10
@@ -0,0 +1 @@
+3,0
@@ -0,0 +1 @@
+"Normal"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+4,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+5,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+Thequick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,3
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The- brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,4
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The- brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1,9 @@
+The quick
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,4
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quickbrown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,9
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,10
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,15
@@ -0,0 +1 @@
+0,10
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+7,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ fox- over
+the lazy dog
@@ -0,0 +1 @@
+7,6
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+0,10
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+0,10
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+0,15
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+1,4
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+1,4
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumpsover
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+1,9
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+2,12
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+3,0
@@ -0,0 +1 @@
+0,15
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+4,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+5,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+ brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+ brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+ brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+ brown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1 @@
+6,0
@@ -0,0 +1 @@
+"Insert"
@@ -0,0 +1,9 @@
+The quick brown
+fox jumps over
+the lazy dog
+
+
+
+The-quickbrown
+ fox-jumps over
+the lazy dog
@@ -0,0 +1,9 @@
+The quick brown
+fox over
+the lazy dog
+
+
+
+The-quick brown
+ fox-jumps over
+the lazy dog