Remove 2 suffix for ui, storybook, text
Co-authored-by: Mikayla <mikayla@zed.dev>
This commit is contained in:
parent
0cf65223ce
commit
4305c5fdbe
142 changed files with 106 additions and 5018 deletions
111
Cargo.lock
generated
111
Cargo.lock
generated
|
@ -16,7 +16,7 @@ dependencies = [
|
||||||
"settings",
|
"settings",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"theme2",
|
"theme2",
|
||||||
"ui2",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
@ -333,7 +333,7 @@ dependencies = [
|
||||||
"smol",
|
"smol",
|
||||||
"theme2",
|
"theme2",
|
||||||
"tiktoken-rs",
|
"tiktoken-rs",
|
||||||
"ui2",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
"uuid 1.4.1",
|
"uuid 1.4.1",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
@ -1018,7 +1018,7 @@ dependencies = [
|
||||||
"search",
|
"search",
|
||||||
"settings",
|
"settings",
|
||||||
"theme2",
|
"theme2",
|
||||||
"ui2",
|
"ui",
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1217,7 +1217,7 @@ dependencies = [
|
||||||
"smol",
|
"smol",
|
||||||
"sum_tree",
|
"sum_tree",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"text2",
|
"text",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"time",
|
"time",
|
||||||
"tiny_http",
|
"tiny_http",
|
||||||
|
@ -1390,7 +1390,7 @@ dependencies = [
|
||||||
"sum_tree",
|
"sum_tree",
|
||||||
"sysinfo",
|
"sysinfo",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"text2",
|
"text",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"time",
|
"time",
|
||||||
"tiny_http",
|
"tiny_http",
|
||||||
|
@ -1500,7 +1500,7 @@ dependencies = [
|
||||||
"sha-1 0.9.8",
|
"sha-1 0.9.8",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"text2",
|
"text",
|
||||||
"theme2",
|
"theme2",
|
||||||
"time",
|
"time",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
@ -1556,7 +1556,7 @@ dependencies = [
|
||||||
"theme_selector",
|
"theme_selector",
|
||||||
"time",
|
"time",
|
||||||
"tree-sitter-markdown",
|
"tree-sitter-markdown",
|
||||||
"ui2",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
"vcs_menu",
|
"vcs_menu",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
@ -1612,7 +1612,7 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"settings",
|
"settings",
|
||||||
"theme2",
|
"theme2",
|
||||||
"ui2",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
"zed_actions",
|
"zed_actions",
|
||||||
|
@ -1714,7 +1714,7 @@ dependencies = [
|
||||||
"settings",
|
"settings",
|
||||||
"smol",
|
"smol",
|
||||||
"theme2",
|
"theme2",
|
||||||
"ui2",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -2242,7 +2242,7 @@ dependencies = [
|
||||||
"settings",
|
"settings",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"theme2",
|
"theme2",
|
||||||
"ui2",
|
"ui",
|
||||||
"unindent",
|
"unindent",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
@ -2422,13 +2422,13 @@ dependencies = [
|
||||||
"snippet",
|
"snippet",
|
||||||
"sqlez",
|
"sqlez",
|
||||||
"sum_tree",
|
"sum_tree",
|
||||||
"text2",
|
"text",
|
||||||
"theme2",
|
"theme2",
|
||||||
"tree-sitter",
|
"tree-sitter",
|
||||||
"tree-sitter-html",
|
"tree-sitter-html",
|
||||||
"tree-sitter-rust",
|
"tree-sitter-rust",
|
||||||
"tree-sitter-typescript",
|
"tree-sitter-typescript",
|
||||||
"ui2",
|
"ui",
|
||||||
"unindent",
|
"unindent",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
@ -2638,7 +2638,7 @@ dependencies = [
|
||||||
"sysinfo",
|
"sysinfo",
|
||||||
"theme2",
|
"theme2",
|
||||||
"tree-sitter-markdown",
|
"tree-sitter-markdown",
|
||||||
"ui2",
|
"ui",
|
||||||
"urlencoding",
|
"urlencoding",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
@ -2662,9 +2662,9 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"settings",
|
"settings",
|
||||||
"text2",
|
"text",
|
||||||
"theme2",
|
"theme2",
|
||||||
"ui2",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
@ -2835,7 +2835,7 @@ dependencies = [
|
||||||
"smol",
|
"smol",
|
||||||
"sum_tree",
|
"sum_tree",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"text2",
|
"text",
|
||||||
"time",
|
"time",
|
||||||
"util",
|
"util",
|
||||||
]
|
]
|
||||||
|
@ -3095,7 +3095,7 @@ dependencies = [
|
||||||
"parking_lot 0.11.2",
|
"parking_lot 0.11.2",
|
||||||
"smol",
|
"smol",
|
||||||
"sum_tree",
|
"sum_tree",
|
||||||
"text2",
|
"text",
|
||||||
"unindent",
|
"unindent",
|
||||||
"util",
|
"util",
|
||||||
]
|
]
|
||||||
|
@ -3154,9 +3154,9 @@ dependencies = [
|
||||||
"postage",
|
"postage",
|
||||||
"serde",
|
"serde",
|
||||||
"settings",
|
"settings",
|
||||||
"text2",
|
"text",
|
||||||
"theme2",
|
"theme2",
|
||||||
"ui2",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
@ -3960,7 +3960,7 @@ dependencies = [
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"smol",
|
"smol",
|
||||||
"sum_tree",
|
"sum_tree",
|
||||||
"text2",
|
"text",
|
||||||
"theme2",
|
"theme2",
|
||||||
"tree-sitter",
|
"tree-sitter",
|
||||||
"tree-sitter-elixir",
|
"tree-sitter-elixir",
|
||||||
|
@ -3991,7 +3991,7 @@ dependencies = [
|
||||||
"project",
|
"project",
|
||||||
"settings",
|
"settings",
|
||||||
"theme2",
|
"theme2",
|
||||||
"ui2",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
@ -4014,7 +4014,7 @@ dependencies = [
|
||||||
"settings",
|
"settings",
|
||||||
"theme2",
|
"theme2",
|
||||||
"tree-sitter",
|
"tree-sitter",
|
||||||
"ui2",
|
"ui",
|
||||||
"unindent",
|
"unindent",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
@ -4624,7 +4624,7 @@ dependencies = [
|
||||||
"smol",
|
"smol",
|
||||||
"snippet",
|
"snippet",
|
||||||
"sum_tree",
|
"sum_tree",
|
||||||
"text2",
|
"text",
|
||||||
"theme2",
|
"theme2",
|
||||||
"tree-sitter",
|
"tree-sitter",
|
||||||
"tree-sitter-html",
|
"tree-sitter-html",
|
||||||
|
@ -4798,7 +4798,7 @@ dependencies = [
|
||||||
"rpc",
|
"rpc",
|
||||||
"settings",
|
"settings",
|
||||||
"sum_tree",
|
"sum_tree",
|
||||||
"text2",
|
"text",
|
||||||
"time",
|
"time",
|
||||||
"util",
|
"util",
|
||||||
]
|
]
|
||||||
|
@ -5202,9 +5202,9 @@ dependencies = [
|
||||||
"postage",
|
"postage",
|
||||||
"settings",
|
"settings",
|
||||||
"smol",
|
"smol",
|
||||||
"text2",
|
"text",
|
||||||
"theme2",
|
"theme2",
|
||||||
"ui2",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
@ -5425,7 +5425,7 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"settings",
|
"settings",
|
||||||
"theme2",
|
"theme2",
|
||||||
"ui2",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
@ -5746,7 +5746,7 @@ dependencies = [
|
||||||
"sum_tree",
|
"sum_tree",
|
||||||
"tempdir",
|
"tempdir",
|
||||||
"terminal",
|
"terminal",
|
||||||
"text2",
|
"text",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"toml 0.5.11",
|
"toml 0.5.11",
|
||||||
"unindent",
|
"unindent",
|
||||||
|
@ -5777,7 +5777,7 @@ dependencies = [
|
||||||
"settings",
|
"settings",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"theme2",
|
"theme2",
|
||||||
"ui2",
|
"ui",
|
||||||
"unicase",
|
"unicase",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
@ -5800,7 +5800,7 @@ dependencies = [
|
||||||
"project",
|
"project",
|
||||||
"settings",
|
"settings",
|
||||||
"smol",
|
"smol",
|
||||||
"text2",
|
"text",
|
||||||
"theme2",
|
"theme2",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
@ -5970,7 +5970,7 @@ dependencies = [
|
||||||
"editor",
|
"editor",
|
||||||
"gpui2",
|
"gpui2",
|
||||||
"search",
|
"search",
|
||||||
"ui2",
|
"ui",
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -6149,9 +6149,9 @@ dependencies = [
|
||||||
"postage",
|
"postage",
|
||||||
"settings",
|
"settings",
|
||||||
"smol",
|
"smol",
|
||||||
"text2",
|
"text",
|
||||||
"theme2",
|
"theme2",
|
||||||
"ui2",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
@ -6934,7 +6934,7 @@ dependencies = [
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"smol",
|
"smol",
|
||||||
"theme2",
|
"theme2",
|
||||||
"ui2",
|
"ui",
|
||||||
"unindent",
|
"unindent",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
@ -7753,7 +7753,7 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "storybook2"
|
name = "storybook"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
@ -7778,7 +7778,7 @@ dependencies = [
|
||||||
"story",
|
"story",
|
||||||
"strum",
|
"strum",
|
||||||
"theme2",
|
"theme2",
|
||||||
"ui2",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -8115,7 +8115,7 @@ dependencies = [
|
||||||
"terminal",
|
"terminal",
|
||||||
"theme2",
|
"theme2",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"ui2",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
@ -8123,29 +8123,6 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "text"
|
name = "text"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"clock",
|
|
||||||
"collections",
|
|
||||||
"ctor",
|
|
||||||
"digest 0.9.0",
|
|
||||||
"env_logger",
|
|
||||||
"gpui",
|
|
||||||
"lazy_static",
|
|
||||||
"log",
|
|
||||||
"parking_lot 0.11.2",
|
|
||||||
"postage",
|
|
||||||
"rand 0.8.5",
|
|
||||||
"regex",
|
|
||||||
"rope",
|
|
||||||
"smallvec",
|
|
||||||
"sum_tree",
|
|
||||||
"util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "text2"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clock",
|
"clock",
|
||||||
|
@ -8251,7 +8228,7 @@ dependencies = [
|
||||||
"settings",
|
"settings",
|
||||||
"smol",
|
"smol",
|
||||||
"theme2",
|
"theme2",
|
||||||
"ui2",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
@ -9055,7 +9032,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ui2"
|
name = "ui"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
@ -9315,7 +9292,7 @@ dependencies = [
|
||||||
"fuzzy",
|
"fuzzy",
|
||||||
"gpui2",
|
"gpui2",
|
||||||
"picker",
|
"picker",
|
||||||
"ui2",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
@ -9354,7 +9331,7 @@ dependencies = [
|
||||||
"settings",
|
"settings",
|
||||||
"theme2",
|
"theme2",
|
||||||
"tokio",
|
"tokio",
|
||||||
"ui2",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
"zed_actions",
|
"zed_actions",
|
||||||
|
@ -9767,7 +9744,7 @@ dependencies = [
|
||||||
"settings",
|
"settings",
|
||||||
"theme2",
|
"theme2",
|
||||||
"theme_selector",
|
"theme_selector",
|
||||||
"ui2",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
"vim",
|
"vim",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
@ -10045,7 +10022,7 @@ dependencies = [
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"terminal",
|
"terminal",
|
||||||
"theme2",
|
"theme2",
|
||||||
"ui2",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
"uuid 1.4.1",
|
"uuid 1.4.1",
|
||||||
]
|
]
|
||||||
|
@ -10212,7 +10189,7 @@ dependencies = [
|
||||||
"sum_tree",
|
"sum_tree",
|
||||||
"tempdir",
|
"tempdir",
|
||||||
"terminal_view",
|
"terminal_view",
|
||||||
"text2",
|
"text",
|
||||||
"theme2",
|
"theme2",
|
||||||
"theme_selector",
|
"theme_selector",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
|
|
@ -70,7 +70,7 @@ members = [
|
||||||
"crates/sqlez",
|
"crates/sqlez",
|
||||||
"crates/sqlez_macros",
|
"crates/sqlez_macros",
|
||||||
"crates/rich_text",
|
"crates/rich_text",
|
||||||
"crates/storybook2",
|
"crates/storybook",
|
||||||
"crates/sum_tree",
|
"crates/sum_tree",
|
||||||
"crates/terminal",
|
"crates/terminal",
|
||||||
"crates/terminal_view",
|
"crates/terminal_view",
|
||||||
|
@ -79,7 +79,7 @@ members = [
|
||||||
"crates/theme2",
|
"crates/theme2",
|
||||||
"crates/theme_importer",
|
"crates/theme_importer",
|
||||||
"crates/theme_selector",
|
"crates/theme_selector",
|
||||||
"crates/ui2",
|
"crates/ui",
|
||||||
"crates/util",
|
"crates/util",
|
||||||
"crates/story",
|
"crates/story",
|
||||||
"crates/vim",
|
"crates/vim",
|
||||||
|
|
|
@ -15,7 +15,7 @@ language = { path = "../language" }
|
||||||
gpui = { path = "../gpui2", package = "gpui2" }
|
gpui = { path = "../gpui2", package = "gpui2" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
ui = { path = "../ui2", package = "ui2" }
|
ui = { path = "../ui" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
theme = { path = "../theme2", package = "theme2" }
|
theme = { path = "../theme2", package = "theme2" }
|
||||||
workspace = { path = "../workspace", package = "workspace" }
|
workspace = { path = "../workspace", package = "workspace" }
|
||||||
|
|
|
@ -23,7 +23,7 @@ search = { path = "../search" }
|
||||||
semantic_index = { path = "../semantic_index" }
|
semantic_index = { path = "../semantic_index" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
theme = { package = "theme2", path = "../theme2" }
|
theme = { package = "theme2", path = "../theme2" }
|
||||||
ui = { package = "ui2", path = "../ui2" }
|
ui = { path = "../ui" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ doctest = false
|
||||||
collections = { path = "../collections" }
|
collections = { path = "../collections" }
|
||||||
editor = { path = "../editor" }
|
editor = { path = "../editor" }
|
||||||
gpui = { package = "gpui2", path = "../gpui2" }
|
gpui = { package = "gpui2", path = "../gpui2" }
|
||||||
ui = { package = "ui2", path = "../ui2" }
|
ui = { path = "../ui" }
|
||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
search = { path = "../search" }
|
search = { path = "../search" }
|
||||||
|
|
|
@ -18,7 +18,7 @@ db = { path = "../db" }
|
||||||
gpui = { package = "gpui2", path = "../gpui2" }
|
gpui = { package = "gpui2", path = "../gpui2" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
rpc = { path = "../rpc" }
|
rpc = { path = "../rpc" }
|
||||||
text = { package = "text2", path = "../text2" }
|
text = { path = "../text" }
|
||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
feature_flags = { path = "../feature_flags" }
|
feature_flags = { path = "../feature_flags" }
|
||||||
|
|
|
@ -18,7 +18,7 @@ db = { path = "../db" }
|
||||||
gpui = { package = "gpui2", path = "../gpui2" }
|
gpui = { package = "gpui2", path = "../gpui2" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
rpc = { path = "../rpc" }
|
rpc = { path = "../rpc" }
|
||||||
text = { package = "text2", path = "../text2" }
|
text = { path = "../text" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
feature_flags = { path = "../feature_flags" }
|
feature_flags = { path = "../feature_flags" }
|
||||||
sum_tree = { path = "../sum_tree" }
|
sum_tree = { path = "../sum_tree" }
|
||||||
|
|
|
@ -17,7 +17,7 @@ required-features = ["seed-support"]
|
||||||
clock = { path = "../clock" }
|
clock = { path = "../clock" }
|
||||||
collections = { path = "../collections" }
|
collections = { path = "../collections" }
|
||||||
live_kit_server = { path = "../live_kit_server" }
|
live_kit_server = { path = "../live_kit_server" }
|
||||||
text = { package = "text2", path = "../text2" }
|
text = { path = "../text" }
|
||||||
rpc = { path = "../rpc" }
|
rpc = { path = "../rpc" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ feature_flags = { path = "../feature_flags"}
|
||||||
theme = { package = "theme2", path = "../theme2" }
|
theme = { package = "theme2", path = "../theme2" }
|
||||||
theme_selector = { path = "../theme_selector" }
|
theme_selector = { path = "../theme_selector" }
|
||||||
vcs_menu = { path = "../vcs_menu" }
|
vcs_menu = { path = "../vcs_menu" }
|
||||||
ui = { package = "ui2", path = "../ui2" }
|
ui = { path = "../ui" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
zed_actions = { path = "../zed_actions"}
|
zed_actions = { path = "../zed_actions"}
|
||||||
|
|
|
@ -16,7 +16,7 @@ gpui = { package = "gpui2", path = "../gpui2" }
|
||||||
picker = { path = "../picker" }
|
picker = { path = "../picker" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
ui = { package = "ui2", path = "../ui2" }
|
ui = { path = "../ui" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
theme = { package = "theme2", path = "../theme2" }
|
theme = { package = "theme2", path = "../theme2" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
|
|
|
@ -28,7 +28,7 @@ theme = { package = "theme2", path = "../theme2" }
|
||||||
lsp = { path = "../lsp" }
|
lsp = { path = "../lsp" }
|
||||||
node_runtime = { path = "../node_runtime"}
|
node_runtime = { path = "../node_runtime"}
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
ui = { package = "ui2", path = "../ui2" }
|
ui = { path = "../ui" }
|
||||||
async-compression.workspace = true
|
async-compression.workspace = true
|
||||||
async-tar = "0.4.2"
|
async-tar = "0.4.2"
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
|
|
|
@ -12,7 +12,7 @@ doctest = false
|
||||||
collections = { path = "../collections" }
|
collections = { path = "../collections" }
|
||||||
editor = { path = "../editor" }
|
editor = { path = "../editor" }
|
||||||
gpui = { package = "gpui2", path = "../gpui2" }
|
gpui = { package = "gpui2", path = "../gpui2" }
|
||||||
ui = { package = "ui2", path = "../ui2" }
|
ui = { path = "../ui" }
|
||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
lsp = { path = "../lsp" }
|
lsp = { path = "../lsp" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
|
|
|
@ -41,9 +41,9 @@ rich_text = { path = "../rich_text" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
snippet = { path = "../snippet" }
|
snippet = { path = "../snippet" }
|
||||||
sum_tree = { path = "../sum_tree" }
|
sum_tree = { path = "../sum_tree" }
|
||||||
text = { package="text2", path = "../text2" }
|
text = { path = "../text" }
|
||||||
theme = { package="theme2", path = "../theme2" }
|
theme = { package="theme2", path = "../theme2" }
|
||||||
ui = { package = "ui2", path = "../ui2" }
|
ui = { path = "../ui" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
sqlez = { path = "../sqlez" }
|
sqlez = { path = "../sqlez" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
|
@ -73,7 +73,7 @@ tree-sitter-typescript = { workspace = true, optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
copilot = { path = "../copilot", features = ["test-support"] }
|
copilot = { path = "../copilot", features = ["test-support"] }
|
||||||
text = { package="text2", path = "../text2", features = ["test-support"] }
|
text = { path = "../text", features = ["test-support"] }
|
||||||
language = { path = "../language", features = ["test-support"] }
|
language = { path = "../language", features = ["test-support"] }
|
||||||
lsp = { path = "../lsp", features = ["test-support"] }
|
lsp = { path = "../lsp", features = ["test-support"] }
|
||||||
gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
|
gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
|
||||||
|
|
|
@ -21,7 +21,7 @@ project = { path = "../project" }
|
||||||
search = { path = "../search" }
|
search = { path = "../search" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
theme = { package = "theme2", path = "../theme2" }
|
theme = { package = "theme2", path = "../theme2" }
|
||||||
ui = { package = "ui2", path = "../ui2" }
|
ui = { path = "../ui" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
workspace = { path = "../workspace"}
|
workspace = { path = "../workspace"}
|
||||||
|
|
||||||
|
|
|
@ -17,10 +17,10 @@ menu = { path = "../menu" }
|
||||||
picker = { path = "../picker" }
|
picker = { path = "../picker" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
text = { package = "text2", path = "../text2" }
|
text = { path = "../text" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
theme = { package = "theme2", path = "../theme2" }
|
theme = { package = "theme2", path = "../theme2" }
|
||||||
ui = { package = "ui2", path = "../ui2" }
|
ui = { path = "../ui" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
postage.workspace = true
|
postage.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
|
|
|
@ -10,7 +10,7 @@ path = "src/fs.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
collections = { path = "../collections" }
|
collections = { path = "../collections" }
|
||||||
rope = { path = "../rope" }
|
rope = { path = "../rope" }
|
||||||
text = { package = "text2", path = "../text2" }
|
text = { path = "../text" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
sum_tree = { path = "../sum_tree" }
|
sum_tree = { path = "../sum_tree" }
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ anyhow.workspace = true
|
||||||
clock = { path = "../clock" }
|
clock = { path = "../clock" }
|
||||||
lazy_static.workspace = true
|
lazy_static.workspace = true
|
||||||
sum_tree = { path = "../sum_tree" }
|
sum_tree = { path = "../sum_tree" }
|
||||||
text = { package = "text2", path = "../text2" }
|
text = { path = "../text" }
|
||||||
collections = { path = "../collections" }
|
collections = { path = "../collections" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
|
|
|
@ -14,11 +14,11 @@ gpui = { package = "gpui2", path = "../gpui2" }
|
||||||
menu = { path = "../menu" }
|
menu = { path = "../menu" }
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
text = { package = "text2", path = "../text2" }
|
text = { path = "../text" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
postage.workspace = true
|
postage.workspace = true
|
||||||
theme = { package = "theme2", path = "../theme2" }
|
theme = { package = "theme2", path = "../theme2" }
|
||||||
ui = { package = "ui2", path = "../ui2" }
|
ui = { path = "../ui" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
@ -31,7 +31,7 @@ lsp = { path = "../lsp" }
|
||||||
rpc = { path = "../rpc" }
|
rpc = { path = "../rpc" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
sum_tree = { path = "../sum_tree" }
|
sum_tree = { path = "../sum_tree" }
|
||||||
text = { package = "text2", path = "../text2" }
|
text = { path = "../text" }
|
||||||
theme = { package = "theme2", path = "../theme2" }
|
theme = { package = "theme2", path = "../theme2" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ client = { path = "../client", features = ["test-support"] }
|
||||||
collections = { path = "../collections", features = ["test-support"] }
|
collections = { path = "../collections", features = ["test-support"] }
|
||||||
gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
|
gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
|
||||||
lsp = { path = "../lsp", features = ["test-support"] }
|
lsp = { path = "../lsp", features = ["test-support"] }
|
||||||
text = { package = "text2", path = "../text2", features = ["test-support"] }
|
text = { path = "../text", features = ["test-support"] }
|
||||||
settings = { path = "../settings", features = ["test-support"] }
|
settings = { path = "../settings", features = ["test-support"] }
|
||||||
util = { path = "../util", features = ["test-support"] }
|
util = { path = "../util", features = ["test-support"] }
|
||||||
ctor.workspace = true
|
ctor.workspace = true
|
||||||
|
|
|
@ -16,7 +16,7 @@ gpui = { package = "gpui2", path = "../gpui2" }
|
||||||
picker = { path = "../picker" }
|
picker = { path = "../picker" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
theme = { package = "theme2", path = "../theme2" }
|
theme = { package = "theme2", path = "../theme2" }
|
||||||
ui = { package = "ui2", path = "../ui2" }
|
ui = { path = "../ui" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
|
|
|
@ -17,7 +17,7 @@ language = { path = "../language" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
gpui = { package = "gpui2", path = "../gpui2" }
|
gpui = { package = "gpui2", path = "../gpui2" }
|
||||||
ui = { package = "ui2", path = "../ui2" }
|
ui = { path = "../ui" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
lsp = { path = "../lsp" }
|
lsp = { path = "../lsp" }
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
|
|
|
@ -31,7 +31,7 @@ rich_text = { path = "../rich_text" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
snippet = { path = "../snippet" }
|
snippet = { path = "../snippet" }
|
||||||
sum_tree = { path = "../sum_tree" }
|
sum_tree = { path = "../sum_tree" }
|
||||||
text = { package = "text2", path = "../text2" }
|
text = { path = "../text" }
|
||||||
theme = { package = "theme2", path = "../theme2" }
|
theme = { package = "theme2", path = "../theme2" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ tree-sitter-typescript = { workspace = true, optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
copilot = { path = "../copilot", features = ["test-support"] }
|
copilot = { path = "../copilot", features = ["test-support"] }
|
||||||
text = { package = "text2", path = "../text2", features = ["test-support"] }
|
text = { path = "../text", features = ["test-support"] }
|
||||||
language = { path = "../language", features = ["test-support"] }
|
language = { path = "../language", features = ["test-support"] }
|
||||||
lsp = { path = "../lsp", features = ["test-support"] }
|
lsp = { path = "../lsp", features = ["test-support"] }
|
||||||
gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
|
gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
|
||||||
|
|
|
@ -27,7 +27,7 @@ gpui = { package = "gpui2", path = "../gpui2" }
|
||||||
rpc = { path = "../rpc" }
|
rpc = { path = "../rpc" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
sum_tree = { path = "../sum_tree" }
|
sum_tree = { path = "../sum_tree" }
|
||||||
text = { package = "text2", path = "../text2" }
|
text = { path = "../text" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
|
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
|
|
|
@ -12,11 +12,11 @@ doctest = false
|
||||||
editor = { path = "../editor" }
|
editor = { path = "../editor" }
|
||||||
fuzzy = { path = "../fuzzy" }
|
fuzzy = { path = "../fuzzy" }
|
||||||
gpui = { package = "gpui2", path = "../gpui2" }
|
gpui = { package = "gpui2", path = "../gpui2" }
|
||||||
ui = { package = "ui2", path = "../ui2" }
|
ui = { path = "../ui" }
|
||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
picker = { path = "../picker" }
|
picker = { path = "../picker" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
text = { package = "text2", path = "../text2" }
|
text = { path = "../text" }
|
||||||
theme = { package = "theme2", path = "../theme2" }
|
theme = { package = "theme2", path = "../theme2" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
|
|
|
@ -10,7 +10,7 @@ doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
editor = { path = "../editor" }
|
editor = { path = "../editor" }
|
||||||
ui = { package = "ui2", path = "../ui2" }
|
ui = { path = "../ui" }
|
||||||
gpui = { package = "gpui2", path = "../gpui2" }
|
gpui = { package = "gpui2", path = "../gpui2" }
|
||||||
menu = { path = "../menu" }
|
menu = { path = "../menu" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
|
|
|
@ -20,7 +20,7 @@ test-support = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
text = { package = "text2", path = "../text2" }
|
text = { path = "../text" }
|
||||||
copilot = { path = "../copilot" }
|
copilot = { path = "../copilot" }
|
||||||
client = { path = "../client" }
|
client = { path = "../client" }
|
||||||
clock = { path = "../clock" }
|
clock = { path = "../clock" }
|
||||||
|
|
|
@ -18,7 +18,7 @@ project = { path = "../project" }
|
||||||
search = { path = "../search" }
|
search = { path = "../search" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
theme = { path = "../theme2", package = "theme2" }
|
theme = { path = "../theme2", package = "theme2" }
|
||||||
ui = { path = "../ui2", package = "ui2" }
|
ui = { path = "../ui" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
workspace = { path = "../workspace", package = "workspace" }
|
workspace = { path = "../workspace", package = "workspace" }
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
|
|
|
@ -14,7 +14,7 @@ fuzzy = { path = "../fuzzy" }
|
||||||
gpui = { package = "gpui2", path = "../gpui2" }
|
gpui = { package = "gpui2", path = "../gpui2" }
|
||||||
picker = { path = "../picker" }
|
picker = { path = "../picker" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
text = { package = "text2", path = "../text2" }
|
text = { path = "../text" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
theme = { package = "theme2", path = "../theme2" }
|
theme = { package = "theme2", path = "../theme2" }
|
||||||
|
|
|
@ -14,7 +14,7 @@ editor = { path = "../editor" }
|
||||||
gpui = { package = "gpui2", path = "../gpui2" }
|
gpui = { package = "gpui2", path = "../gpui2" }
|
||||||
search = { path = "../search" }
|
search = { path = "../search" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
ui = { package = "ui2", path = "../ui2" }
|
ui = { path = "../ui" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
editor = { path = "../editor", features = ["test-support"] }
|
editor = { path = "../editor", features = ["test-support"] }
|
||||||
|
|
|
@ -15,10 +15,10 @@ gpui = { package = "gpui2", path = "../gpui2" }
|
||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
picker = { path = "../picker" }
|
picker = { path = "../picker" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
text = { package = "text2", path = "../text2" }
|
text = { path = "../text" }
|
||||||
util = { path = "../util"}
|
util = { path = "../util"}
|
||||||
theme = { package = "theme2", path = "../theme2" }
|
theme = { package = "theme2", path = "../theme2" }
|
||||||
ui = { package = "ui2", path = "../ui2" }
|
ui = { path = "../ui" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
|
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
|
|
|
@ -19,7 +19,7 @@ project = { path = "../project" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
theme = { package = "theme2", path = "../theme2" }
|
theme = { package = "theme2", path = "../theme2" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
ui = {package = "ui2", path = "../ui2"}
|
ui = {path = "../ui"}
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
semantic_index = { path = "../semantic_index" }
|
semantic_index = { path = "../semantic_index" }
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
[package]
|
[package]
|
||||||
name = "storybook2"
|
name = "storybook"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "storybook"
|
name = "storybook"
|
||||||
path = "src/storybook2.rs"
|
path = "src/storybook.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
|
@ -31,7 +31,7 @@ story = { path = "../story" }
|
||||||
strum = { version = "0.25.0", features = ["derive"] }
|
strum = { version = "0.25.0", features = ["derive"] }
|
||||||
theme2 = { path = "../theme2" }
|
theme2 = { path = "../theme2" }
|
||||||
menu = { path = "../menu" }
|
menu = { path = "../menu" }
|
||||||
ui = { package = "ui2", path = "../ui2", features = ["stories"] }
|
ui = { path = "../ui", features = ["stories"] }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
picker = { path = "../picker" }
|
picker = { path = "../picker" }
|
||||||
|
|
|
@ -15,7 +15,7 @@ impl TextStory {
|
||||||
|
|
||||||
impl Render for TextStory {
|
impl Render for TextStory {
|
||||||
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {
|
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {
|
||||||
StoryContainer::new("Text Story", "crates/storybook2/src/stories/text.rs")
|
StoryContainer::new("Text Story", "crates/storybook/src/stories/text.rs")
|
||||||
.children(
|
.children(
|
||||||
vec![
|
vec![
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "terminal"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
publish = false
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
path = "src/terminal.rs"
|
|
||||||
doctest = false
|
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
gpui = { package = "gpui2", path = "../gpui2" }
|
|
||||||
settings = { path = "../settings" }
|
|
||||||
db = { path = "../db" }
|
|
||||||
theme = { package = "theme2", path = "../theme2" }
|
|
||||||
util = { path = "../util" }
|
|
||||||
|
|
||||||
alacritty_terminal = { git = "https://github.com/zed-industries/alacritty", rev = "33306142195b354ef3485ca2b1d8a85dfc6605ca" }
|
|
||||||
procinfo = { git = "https://github.com/zed-industries/wezterm", rev = "5cd757e5f2eb039ed0c6bb6512223e69d5efc64d", default-features = false }
|
|
||||||
smallvec.workspace = true
|
|
||||||
smol.workspace = true
|
|
||||||
mio-extras = "2.0.6"
|
|
||||||
futures.workspace = true
|
|
||||||
ordered-float.workspace = true
|
|
||||||
itertools = "0.10"
|
|
||||||
dirs = "4.0.0"
|
|
||||||
shellexpand = "2.1.0"
|
|
||||||
libc = "0.2"
|
|
||||||
anyhow.workspace = true
|
|
||||||
schemars.workspace = true
|
|
||||||
thiserror.workspace = true
|
|
||||||
lazy_static.workspace = true
|
|
||||||
serde.workspace = true
|
|
||||||
serde_derive.workspace = true
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
rand.workspace = true
|
|
|
@ -21,7 +21,7 @@ workspace = { path = "../workspace" }
|
||||||
db = { path = "../db" }
|
db = { path = "../db" }
|
||||||
procinfo = { git = "https://github.com/zed-industries/wezterm", rev = "5cd757e5f2eb039ed0c6bb6512223e69d5efc64d", default-features = false }
|
procinfo = { git = "https://github.com/zed-industries/wezterm", rev = "5cd757e5f2eb039ed0c6bb6512223e69d5efc64d", default-features = false }
|
||||||
terminal = { path = "../terminal" }
|
terminal = { path = "../terminal" }
|
||||||
ui = { package = "ui2", path = "../ui2" }
|
ui = { path = "../ui" }
|
||||||
smallvec.workspace = true
|
smallvec.workspace = true
|
||||||
smol.workspace = true
|
smol.workspace = true
|
||||||
mio-extras = "2.0.6"
|
mio-extras = "2.0.6"
|
||||||
|
|
|
@ -30,7 +30,7 @@ regex.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
collections = { path = "../collections", features = ["test-support"] }
|
collections = { path = "../collections", features = ["test-support"] }
|
||||||
gpui = { path = "../gpui", features = ["test-support"] }
|
gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
|
||||||
util = { path = "../util", features = ["test-support"] }
|
util = { path = "../util", features = ["test-support"] }
|
||||||
ctor.workspace = true
|
ctor.workspace = true
|
||||||
env_logger.workspace = true
|
env_logger.workspace = true
|
||||||
|
|
|
@ -20,11 +20,11 @@ impl Locator {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn min_ref() -> &'static Self {
|
pub fn min_ref() -> &'static Self {
|
||||||
&*MIN
|
&MIN
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn max_ref() -> &'static Self {
|
pub fn max_ref() -> &'static Self {
|
||||||
&*MAX
|
&MAX
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assign(&mut self, other: &Self) {
|
pub fn assign(&mut self, other: &Self) {
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::ops::Range;
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub enum SelectionGoal {
|
pub enum SelectionGoal {
|
||||||
None,
|
None,
|
||||||
HorizontalPosition(f32),
|
HorizontalPosition(f32), // todo!("Can we use pixels here without adding a runtime gpui dependency?")
|
||||||
HorizontalRange { start: f32, end: f32 },
|
HorizontalRange { start: f32, end: f32 },
|
||||||
WrappedHorizontalPosition((u32, f32)),
|
WrappedHorizontalPosition((u32, f32)),
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ impl Topic {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn publish(&self, edits: impl Clone + IntoIterator<Item = Edit<usize>>) {
|
pub fn publish(&self, edits: impl Clone + IntoIterator<Item = Edit<usize>>) {
|
||||||
publish(&mut *self.0.lock(), edits);
|
publish(&mut self.0.lock(), edits);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn publish_mut(&mut self, edits: impl Clone + IntoIterator<Item = Edit<usize>>) {
|
pub fn publish_mut(&mut self, edits: impl Clone + IntoIterator<Item = Edit<usize>>) {
|
||||||
|
|
|
@ -1189,7 +1189,6 @@ impl Buffer {
|
||||||
self.undo_or_redo(transaction).log_err()
|
self.undo_or_redo(transaction).log_err()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::needless_collect)]
|
|
||||||
pub fn undo_to_transaction(&mut self, transaction_id: TransactionId) -> Vec<Operation> {
|
pub fn undo_to_transaction(&mut self, transaction_id: TransactionId) -> Vec<Operation> {
|
||||||
let transactions = self
|
let transactions = self
|
||||||
.history
|
.history
|
||||||
|
@ -1223,7 +1222,6 @@ impl Buffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::needless_collect)]
|
|
||||||
pub fn redo_to_transaction(&mut self, transaction_id: TransactionId) -> Vec<Operation> {
|
pub fn redo_to_transaction(&mut self, transaction_id: TransactionId) -> Vec<Operation> {
|
||||||
let transactions = self
|
let transactions = self
|
||||||
.history
|
.history
|
||||||
|
@ -1536,7 +1534,6 @@ impl Buffer {
|
||||||
edits
|
edits
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
|
||||||
pub fn randomly_edit<T>(
|
pub fn randomly_edit<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut T,
|
rng: &mut T,
|
||||||
|
@ -2655,7 +2652,7 @@ impl LineEnding {
|
||||||
max_ix -= 1;
|
max_ix -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ix) = text[..max_ix].find(&['\n']) {
|
if let Some(ix) = text[..max_ix].find(['\n']) {
|
||||||
if ix > 0 && text.as_bytes()[ix - 1] == b'\r' {
|
if ix > 0 && text.as_bytes()[ix - 1] == b'\r' {
|
||||||
Self::Windows
|
Self::Windows
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "text2"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
publish = false
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
path = "src/text2.rs"
|
|
||||||
doctest = false
|
|
||||||
|
|
||||||
[features]
|
|
||||||
test-support = ["rand"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
clock = { path = "../clock" }
|
|
||||||
collections = { path = "../collections" }
|
|
||||||
rope = { path = "../rope" }
|
|
||||||
sum_tree = { path = "../sum_tree" }
|
|
||||||
util = { path = "../util" }
|
|
||||||
|
|
||||||
anyhow.workspace = true
|
|
||||||
digest = { version = "0.9", features = ["std"] }
|
|
||||||
lazy_static.workspace = true
|
|
||||||
log.workspace = true
|
|
||||||
parking_lot.workspace = true
|
|
||||||
postage.workspace = true
|
|
||||||
rand = { workspace = true, optional = true }
|
|
||||||
smallvec.workspace = true
|
|
||||||
regex.workspace = true
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
collections = { path = "../collections", features = ["test-support"] }
|
|
||||||
gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
|
|
||||||
util = { path = "../util", features = ["test-support"] }
|
|
||||||
ctor.workspace = true
|
|
||||||
env_logger.workspace = true
|
|
||||||
rand.workspace = true
|
|
|
@ -1,144 +0,0 @@
|
||||||
use crate::{
|
|
||||||
locator::Locator, BufferSnapshot, Point, PointUtf16, TextDimension, ToOffset, ToPoint,
|
|
||||||
ToPointUtf16,
|
|
||||||
};
|
|
||||||
use anyhow::Result;
|
|
||||||
use std::{cmp::Ordering, fmt::Debug, ops::Range};
|
|
||||||
use sum_tree::Bias;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, Default)]
|
|
||||||
pub struct Anchor {
|
|
||||||
pub timestamp: clock::Lamport,
|
|
||||||
pub offset: usize,
|
|
||||||
pub bias: Bias,
|
|
||||||
pub buffer_id: Option<u64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Anchor {
|
|
||||||
pub const MIN: Self = Self {
|
|
||||||
timestamp: clock::Lamport::MIN,
|
|
||||||
offset: usize::MIN,
|
|
||||||
bias: Bias::Left,
|
|
||||||
buffer_id: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const MAX: Self = Self {
|
|
||||||
timestamp: clock::Lamport::MAX,
|
|
||||||
offset: usize::MAX,
|
|
||||||
bias: Bias::Right,
|
|
||||||
buffer_id: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn cmp(&self, other: &Anchor, buffer: &BufferSnapshot) -> Ordering {
|
|
||||||
let fragment_id_comparison = if self.timestamp == other.timestamp {
|
|
||||||
Ordering::Equal
|
|
||||||
} else {
|
|
||||||
buffer
|
|
||||||
.fragment_id_for_anchor(self)
|
|
||||||
.cmp(buffer.fragment_id_for_anchor(other))
|
|
||||||
};
|
|
||||||
|
|
||||||
fragment_id_comparison
|
|
||||||
.then_with(|| self.offset.cmp(&other.offset))
|
|
||||||
.then_with(|| self.bias.cmp(&other.bias))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn min(&self, other: &Self, buffer: &BufferSnapshot) -> Self {
|
|
||||||
if self.cmp(other, buffer).is_le() {
|
|
||||||
*self
|
|
||||||
} else {
|
|
||||||
*other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn max(&self, other: &Self, buffer: &BufferSnapshot) -> Self {
|
|
||||||
if self.cmp(other, buffer).is_ge() {
|
|
||||||
*self
|
|
||||||
} else {
|
|
||||||
*other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bias(&self, bias: Bias, buffer: &BufferSnapshot) -> Anchor {
|
|
||||||
if bias == Bias::Left {
|
|
||||||
self.bias_left(buffer)
|
|
||||||
} else {
|
|
||||||
self.bias_right(buffer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bias_left(&self, buffer: &BufferSnapshot) -> Anchor {
|
|
||||||
if self.bias == Bias::Left {
|
|
||||||
*self
|
|
||||||
} else {
|
|
||||||
buffer.anchor_before(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bias_right(&self, buffer: &BufferSnapshot) -> Anchor {
|
|
||||||
if self.bias == Bias::Right {
|
|
||||||
*self
|
|
||||||
} else {
|
|
||||||
buffer.anchor_after(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn summary<D>(&self, content: &BufferSnapshot) -> D
|
|
||||||
where
|
|
||||||
D: TextDimension,
|
|
||||||
{
|
|
||||||
content.summary_for_anchor(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true when the [Anchor] is located inside a visible fragment.
|
|
||||||
pub fn is_valid(&self, buffer: &BufferSnapshot) -> bool {
|
|
||||||
if *self == Anchor::MIN || *self == Anchor::MAX {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
let fragment_id = buffer.fragment_id_for_anchor(self);
|
|
||||||
let mut fragment_cursor = buffer.fragments.cursor::<(Option<&Locator>, usize)>();
|
|
||||||
fragment_cursor.seek(&Some(fragment_id), Bias::Left, &None);
|
|
||||||
fragment_cursor
|
|
||||||
.item()
|
|
||||||
.map_or(false, |fragment| fragment.visible)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait OffsetRangeExt {
|
|
||||||
fn to_offset(&self, snapshot: &BufferSnapshot) -> Range<usize>;
|
|
||||||
fn to_point(&self, snapshot: &BufferSnapshot) -> Range<Point>;
|
|
||||||
fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> Range<PointUtf16>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> OffsetRangeExt for Range<T>
|
|
||||||
where
|
|
||||||
T: ToOffset,
|
|
||||||
{
|
|
||||||
fn to_offset(&self, snapshot: &BufferSnapshot) -> Range<usize> {
|
|
||||||
self.start.to_offset(snapshot)..self.end.to_offset(snapshot)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_point(&self, snapshot: &BufferSnapshot) -> Range<Point> {
|
|
||||||
self.start.to_offset(snapshot).to_point(snapshot)
|
|
||||||
..self.end.to_offset(snapshot).to_point(snapshot)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> Range<PointUtf16> {
|
|
||||||
self.start.to_offset(snapshot).to_point_utf16(snapshot)
|
|
||||||
..self.end.to_offset(snapshot).to_point_utf16(snapshot)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait AnchorRangeExt {
|
|
||||||
fn cmp(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> Result<Ordering>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AnchorRangeExt for Range<Anchor> {
|
|
||||||
fn cmp(&self, other: &Range<Anchor>, buffer: &BufferSnapshot) -> Result<Ordering> {
|
|
||||||
Ok(match self.start.cmp(&other.start, buffer) {
|
|
||||||
Ordering::Equal => other.end.cmp(&self.end, buffer),
|
|
||||||
ord => ord,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,125 +0,0 @@
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use smallvec::{smallvec, SmallVec};
|
|
||||||
use std::iter;
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref MIN: Locator = Locator::min();
|
|
||||||
static ref MAX: Locator = Locator::max();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
pub struct Locator(SmallVec<[u64; 4]>);
|
|
||||||
|
|
||||||
impl Locator {
|
|
||||||
pub fn min() -> Self {
|
|
||||||
Self(smallvec![u64::MIN])
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn max() -> Self {
|
|
||||||
Self(smallvec![u64::MAX])
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn min_ref() -> &'static Self {
|
|
||||||
&MIN
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn max_ref() -> &'static Self {
|
|
||||||
&MAX
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn assign(&mut self, other: &Self) {
|
|
||||||
self.0.resize(other.0.len(), 0);
|
|
||||||
self.0.copy_from_slice(&other.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn between(lhs: &Self, rhs: &Self) -> Self {
|
|
||||||
let lhs = lhs.0.iter().copied().chain(iter::repeat(u64::MIN));
|
|
||||||
let rhs = rhs.0.iter().copied().chain(iter::repeat(u64::MAX));
|
|
||||||
let mut location = SmallVec::new();
|
|
||||||
for (lhs, rhs) in lhs.zip(rhs) {
|
|
||||||
let mid = lhs + ((rhs.saturating_sub(lhs)) >> 48);
|
|
||||||
location.push(mid);
|
|
||||||
if mid > lhs {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self(location)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.0.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.len() == 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Locator {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::min()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl sum_tree::Item for Locator {
|
|
||||||
type Summary = Locator;
|
|
||||||
|
|
||||||
fn summary(&self) -> Self::Summary {
|
|
||||||
self.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl sum_tree::KeyedItem for Locator {
|
|
||||||
type Key = Locator;
|
|
||||||
|
|
||||||
fn key(&self) -> Self::Key {
|
|
||||||
self.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl sum_tree::Summary for Locator {
|
|
||||||
type Context = ();
|
|
||||||
|
|
||||||
fn add_summary(&mut self, summary: &Self, _: &()) {
|
|
||||||
self.assign(summary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use rand::prelude::*;
|
|
||||||
use std::mem;
|
|
||||||
|
|
||||||
#[gpui::test(iterations = 100)]
|
|
||||||
fn test_locators(mut rng: StdRng) {
|
|
||||||
let mut lhs = Default::default();
|
|
||||||
let mut rhs = Default::default();
|
|
||||||
while lhs == rhs {
|
|
||||||
lhs = Locator(
|
|
||||||
(0..rng.gen_range(1..=5))
|
|
||||||
.map(|_| rng.gen_range(0..=100))
|
|
||||||
.collect(),
|
|
||||||
);
|
|
||||||
rhs = Locator(
|
|
||||||
(0..rng.gen_range(1..=5))
|
|
||||||
.map(|_| rng.gen_range(0..=100))
|
|
||||||
.collect(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if lhs > rhs {
|
|
||||||
mem::swap(&mut lhs, &mut rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
let middle = Locator::between(&lhs, &rhs);
|
|
||||||
assert!(middle > lhs);
|
|
||||||
assert!(middle < rhs);
|
|
||||||
for ix in 0..middle.0.len() - 1 {
|
|
||||||
assert!(
|
|
||||||
middle.0[ix] == *lhs.0.get(ix).unwrap_or(&0)
|
|
||||||
|| middle.0[ix] == *rhs.0.get(ix).unwrap_or(&0)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
use clock::ReplicaId;
|
|
||||||
|
|
||||||
pub struct Network<T: Clone, R: rand::Rng> {
|
|
||||||
inboxes: std::collections::BTreeMap<ReplicaId, Vec<Envelope<T>>>,
|
|
||||||
all_messages: Vec<T>,
|
|
||||||
rng: R,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct Envelope<T: Clone> {
|
|
||||||
message: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Clone, R: rand::Rng> Network<T, R> {
|
|
||||||
pub fn new(rng: R) -> Self {
|
|
||||||
Network {
|
|
||||||
inboxes: Default::default(),
|
|
||||||
all_messages: Vec::new(),
|
|
||||||
rng,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_peer(&mut self, id: ReplicaId) {
|
|
||||||
self.inboxes.insert(id, Vec::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn replicate(&mut self, old_replica_id: ReplicaId, new_replica_id: ReplicaId) {
|
|
||||||
self.inboxes
|
|
||||||
.insert(new_replica_id, self.inboxes[&old_replica_id].clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_idle(&self) -> bool {
|
|
||||||
self.inboxes.values().all(|i| i.is_empty())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn broadcast(&mut self, sender: ReplicaId, messages: Vec<T>) {
|
|
||||||
for (replica, inbox) in self.inboxes.iter_mut() {
|
|
||||||
if *replica != sender {
|
|
||||||
for message in &messages {
|
|
||||||
// Insert one or more duplicates of this message, potentially *before* the previous
|
|
||||||
// message sent by this peer to simulate out-of-order delivery.
|
|
||||||
for _ in 0..self.rng.gen_range(1..4) {
|
|
||||||
let insertion_index = self.rng.gen_range(0..inbox.len() + 1);
|
|
||||||
inbox.insert(
|
|
||||||
insertion_index,
|
|
||||||
Envelope {
|
|
||||||
message: message.clone(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.all_messages.extend(messages);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn has_unreceived(&self, receiver: ReplicaId) -> bool {
|
|
||||||
!self.inboxes[&receiver].is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn receive(&mut self, receiver: ReplicaId) -> Vec<T> {
|
|
||||||
let inbox = self.inboxes.get_mut(&receiver).unwrap();
|
|
||||||
let count = self.rng.gen_range(0..inbox.len() + 1);
|
|
||||||
inbox
|
|
||||||
.drain(0..count)
|
|
||||||
.map(|envelope| envelope.message)
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,153 +0,0 @@
|
||||||
use std::{fmt::Debug, ops::Add};
|
|
||||||
use sum_tree::{Dimension, Edit, Item, KeyedItem, SumTree, Summary};
|
|
||||||
|
|
||||||
pub trait Operation: Clone + Debug {
|
|
||||||
fn lamport_timestamp(&self) -> clock::Lamport;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
struct OperationItem<T>(T);
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct OperationQueue<T: Operation>(SumTree<OperationItem<T>>);
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
|
|
||||||
pub struct OperationKey(clock::Lamport);
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
|
||||||
pub struct OperationSummary {
|
|
||||||
pub key: OperationKey,
|
|
||||||
pub len: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OperationKey {
|
|
||||||
pub fn new(timestamp: clock::Lamport) -> Self {
|
|
||||||
Self(timestamp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Operation> Default for OperationQueue<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
OperationQueue::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Operation> OperationQueue<T> {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
OperationQueue(SumTree::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.0.summary().len
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.len() == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert(&mut self, mut ops: Vec<T>) {
|
|
||||||
ops.sort_by_key(|op| op.lamport_timestamp());
|
|
||||||
ops.dedup_by_key(|op| op.lamport_timestamp());
|
|
||||||
self.0.edit(
|
|
||||||
ops.into_iter()
|
|
||||||
.map(|op| Edit::Insert(OperationItem(op)))
|
|
||||||
.collect(),
|
|
||||||
&(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn drain(&mut self) -> Self {
|
|
||||||
let clone = self.clone();
|
|
||||||
self.0 = SumTree::new();
|
|
||||||
clone
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
|
||||||
self.0.iter().map(|i| &i.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Summary for OperationSummary {
|
|
||||||
type Context = ();
|
|
||||||
|
|
||||||
fn add_summary(&mut self, other: &Self, _: &()) {
|
|
||||||
assert!(self.key < other.key);
|
|
||||||
self.key = other.key;
|
|
||||||
self.len += other.len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Add<&'a Self> for OperationSummary {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
fn add(self, other: &Self) -> Self {
|
|
||||||
assert!(self.key < other.key);
|
|
||||||
OperationSummary {
|
|
||||||
key: other.key,
|
|
||||||
len: self.len + other.len,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Dimension<'a, OperationSummary> for OperationKey {
|
|
||||||
fn add_summary(&mut self, summary: &OperationSummary, _: &()) {
|
|
||||||
assert!(*self <= summary.key);
|
|
||||||
*self = summary.key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Operation> Item for OperationItem<T> {
|
|
||||||
type Summary = OperationSummary;
|
|
||||||
|
|
||||||
fn summary(&self) -> Self::Summary {
|
|
||||||
OperationSummary {
|
|
||||||
key: OperationKey::new(self.0.lamport_timestamp()),
|
|
||||||
len: 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Operation> KeyedItem for OperationItem<T> {
|
|
||||||
type Key = OperationKey;
|
|
||||||
|
|
||||||
fn key(&self) -> Self::Key {
|
|
||||||
OperationKey::new(self.0.lamport_timestamp())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_len() {
|
|
||||||
let mut clock = clock::Lamport::new(0);
|
|
||||||
|
|
||||||
let mut queue = OperationQueue::new();
|
|
||||||
assert_eq!(queue.len(), 0);
|
|
||||||
|
|
||||||
queue.insert(vec![
|
|
||||||
TestOperation(clock.tick()),
|
|
||||||
TestOperation(clock.tick()),
|
|
||||||
]);
|
|
||||||
assert_eq!(queue.len(), 2);
|
|
||||||
|
|
||||||
queue.insert(vec![TestOperation(clock.tick())]);
|
|
||||||
assert_eq!(queue.len(), 3);
|
|
||||||
|
|
||||||
drop(queue.drain());
|
|
||||||
assert_eq!(queue.len(), 0);
|
|
||||||
|
|
||||||
queue.insert(vec![TestOperation(clock.tick())]);
|
|
||||||
assert_eq!(queue.len(), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
||||||
struct TestOperation(clock::Lamport);
|
|
||||||
|
|
||||||
impl Operation for TestOperation {
|
|
||||||
fn lamport_timestamp(&self) -> clock::Lamport {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,594 +0,0 @@
|
||||||
use crate::Edit;
|
|
||||||
use std::{
|
|
||||||
cmp, mem,
|
|
||||||
ops::{Add, AddAssign, Sub},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Patch<T>(Vec<Edit<T>>);
|
|
||||||
|
|
||||||
impl<T> Patch<T>
|
|
||||||
where
|
|
||||||
T: 'static
|
|
||||||
+ Clone
|
|
||||||
+ Copy
|
|
||||||
+ Ord
|
|
||||||
+ Sub<T, Output = T>
|
|
||||||
+ Add<T, Output = T>
|
|
||||||
+ AddAssign
|
|
||||||
+ Default
|
|
||||||
+ PartialEq,
|
|
||||||
{
|
|
||||||
pub fn new(edits: Vec<Edit<T>>) -> Self {
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
{
|
|
||||||
let mut last_edit: Option<&Edit<T>> = None;
|
|
||||||
for edit in &edits {
|
|
||||||
if let Some(last_edit) = last_edit {
|
|
||||||
assert!(edit.old.start > last_edit.old.end);
|
|
||||||
assert!(edit.new.start > last_edit.new.end);
|
|
||||||
}
|
|
||||||
last_edit = Some(edit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self(edits)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn edits(&self) -> &[Edit<T>] {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_inner(self) -> Vec<Edit<T>> {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn compose(&self, new_edits_iter: impl IntoIterator<Item = Edit<T>>) -> Self {
|
|
||||||
let mut old_edits_iter = self.0.iter().cloned().peekable();
|
|
||||||
let mut new_edits_iter = new_edits_iter.into_iter().peekable();
|
|
||||||
let mut composed = Patch(Vec::new());
|
|
||||||
|
|
||||||
let mut old_start = T::default();
|
|
||||||
let mut new_start = T::default();
|
|
||||||
loop {
|
|
||||||
let old_edit = old_edits_iter.peek_mut();
|
|
||||||
let new_edit = new_edits_iter.peek_mut();
|
|
||||||
|
|
||||||
// Push the old edit if its new end is before the new edit's old start.
|
|
||||||
if let Some(old_edit) = old_edit.as_ref() {
|
|
||||||
let new_edit = new_edit.as_ref();
|
|
||||||
if new_edit.map_or(true, |new_edit| old_edit.new.end < new_edit.old.start) {
|
|
||||||
let catchup = old_edit.old.start - old_start;
|
|
||||||
old_start += catchup;
|
|
||||||
new_start += catchup;
|
|
||||||
|
|
||||||
let old_end = old_start + old_edit.old_len();
|
|
||||||
let new_end = new_start + old_edit.new_len();
|
|
||||||
composed.push(Edit {
|
|
||||||
old: old_start..old_end,
|
|
||||||
new: new_start..new_end,
|
|
||||||
});
|
|
||||||
old_start = old_end;
|
|
||||||
new_start = new_end;
|
|
||||||
old_edits_iter.next();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push the new edit if its old end is before the old edit's new start.
|
|
||||||
if let Some(new_edit) = new_edit.as_ref() {
|
|
||||||
let old_edit = old_edit.as_ref();
|
|
||||||
if old_edit.map_or(true, |old_edit| new_edit.old.end < old_edit.new.start) {
|
|
||||||
let catchup = new_edit.new.start - new_start;
|
|
||||||
old_start += catchup;
|
|
||||||
new_start += catchup;
|
|
||||||
|
|
||||||
let old_end = old_start + new_edit.old_len();
|
|
||||||
let new_end = new_start + new_edit.new_len();
|
|
||||||
composed.push(Edit {
|
|
||||||
old: old_start..old_end,
|
|
||||||
new: new_start..new_end,
|
|
||||||
});
|
|
||||||
old_start = old_end;
|
|
||||||
new_start = new_end;
|
|
||||||
new_edits_iter.next();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we still have edits by this point then they must intersect, so we compose them.
|
|
||||||
if let Some((old_edit, new_edit)) = old_edit.zip(new_edit) {
|
|
||||||
if old_edit.new.start < new_edit.old.start {
|
|
||||||
let catchup = old_edit.old.start - old_start;
|
|
||||||
old_start += catchup;
|
|
||||||
new_start += catchup;
|
|
||||||
|
|
||||||
let overshoot = new_edit.old.start - old_edit.new.start;
|
|
||||||
let old_end = cmp::min(old_start + overshoot, old_edit.old.end);
|
|
||||||
let new_end = new_start + overshoot;
|
|
||||||
composed.push(Edit {
|
|
||||||
old: old_start..old_end,
|
|
||||||
new: new_start..new_end,
|
|
||||||
});
|
|
||||||
|
|
||||||
old_edit.old.start = old_end;
|
|
||||||
old_edit.new.start += overshoot;
|
|
||||||
old_start = old_end;
|
|
||||||
new_start = new_end;
|
|
||||||
} else {
|
|
||||||
let catchup = new_edit.new.start - new_start;
|
|
||||||
old_start += catchup;
|
|
||||||
new_start += catchup;
|
|
||||||
|
|
||||||
let overshoot = old_edit.new.start - new_edit.old.start;
|
|
||||||
let old_end = old_start + overshoot;
|
|
||||||
let new_end = cmp::min(new_start + overshoot, new_edit.new.end);
|
|
||||||
composed.push(Edit {
|
|
||||||
old: old_start..old_end,
|
|
||||||
new: new_start..new_end,
|
|
||||||
});
|
|
||||||
|
|
||||||
new_edit.old.start += overshoot;
|
|
||||||
new_edit.new.start = new_end;
|
|
||||||
old_start = old_end;
|
|
||||||
new_start = new_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if old_edit.new.end > new_edit.old.end {
|
|
||||||
let old_end = old_start + cmp::min(old_edit.old_len(), new_edit.old_len());
|
|
||||||
let new_end = new_start + new_edit.new_len();
|
|
||||||
composed.push(Edit {
|
|
||||||
old: old_start..old_end,
|
|
||||||
new: new_start..new_end,
|
|
||||||
});
|
|
||||||
|
|
||||||
old_edit.old.start = old_end;
|
|
||||||
old_edit.new.start = new_edit.old.end;
|
|
||||||
old_start = old_end;
|
|
||||||
new_start = new_end;
|
|
||||||
new_edits_iter.next();
|
|
||||||
} else {
|
|
||||||
let old_end = old_start + old_edit.old_len();
|
|
||||||
let new_end = new_start + cmp::min(old_edit.new_len(), new_edit.new_len());
|
|
||||||
composed.push(Edit {
|
|
||||||
old: old_start..old_end,
|
|
||||||
new: new_start..new_end,
|
|
||||||
});
|
|
||||||
|
|
||||||
new_edit.old.start = old_edit.new.end;
|
|
||||||
new_edit.new.start = new_end;
|
|
||||||
old_start = old_end;
|
|
||||||
new_start = new_end;
|
|
||||||
old_edits_iter.next();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
composed
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn invert(&mut self) -> &mut Self {
|
|
||||||
for edit in &mut self.0 {
|
|
||||||
mem::swap(&mut edit.old, &mut edit.new);
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
self.0.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.0.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push(&mut self, edit: Edit<T>) {
|
|
||||||
if edit.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(last) = self.0.last_mut() {
|
|
||||||
if last.old.end >= edit.old.start {
|
|
||||||
last.old.end = edit.old.end;
|
|
||||||
last.new.end = edit.new.end;
|
|
||||||
} else {
|
|
||||||
self.0.push(edit);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.0.push(edit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn old_to_new(&self, old: T) -> T {
|
|
||||||
let ix = match self.0.binary_search_by(|probe| probe.old.start.cmp(&old)) {
|
|
||||||
Ok(ix) => ix,
|
|
||||||
Err(ix) => {
|
|
||||||
if ix == 0 {
|
|
||||||
return old;
|
|
||||||
} else {
|
|
||||||
ix - 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if let Some(edit) = self.0.get(ix) {
|
|
||||||
if old >= edit.old.end {
|
|
||||||
edit.new.end + (old - edit.old.end)
|
|
||||||
} else {
|
|
||||||
edit.new.start
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
old
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Clone> IntoIterator for Patch<T> {
|
|
||||||
type Item = Edit<T>;
|
|
||||||
type IntoIter = std::vec::IntoIter<Edit<T>>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.0.into_iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: Clone> IntoIterator for &'a Patch<T> {
|
|
||||||
type Item = Edit<T>;
|
|
||||||
type IntoIter = std::iter::Cloned<std::slice::Iter<'a, Edit<T>>>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.0.iter().cloned()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: Clone> IntoIterator for &'a mut Patch<T> {
|
|
||||||
type Item = Edit<T>;
|
|
||||||
type IntoIter = std::iter::Cloned<std::slice::Iter<'a, Edit<T>>>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.0.iter().cloned()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use rand::prelude::*;
|
|
||||||
use std::env;
|
|
||||||
|
|
||||||
#[gpui::test]
|
|
||||||
fn test_one_disjoint_edit() {
|
|
||||||
assert_patch_composition(
|
|
||||||
Patch(vec![Edit {
|
|
||||||
old: 1..3,
|
|
||||||
new: 1..4,
|
|
||||||
}]),
|
|
||||||
Patch(vec![Edit {
|
|
||||||
old: 0..0,
|
|
||||||
new: 0..4,
|
|
||||||
}]),
|
|
||||||
Patch(vec![
|
|
||||||
Edit {
|
|
||||||
old: 0..0,
|
|
||||||
new: 0..4,
|
|
||||||
},
|
|
||||||
Edit {
|
|
||||||
old: 1..3,
|
|
||||||
new: 5..8,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_patch_composition(
|
|
||||||
Patch(vec![Edit {
|
|
||||||
old: 1..3,
|
|
||||||
new: 1..4,
|
|
||||||
}]),
|
|
||||||
Patch(vec![Edit {
|
|
||||||
old: 5..9,
|
|
||||||
new: 5..7,
|
|
||||||
}]),
|
|
||||||
Patch(vec![
|
|
||||||
Edit {
|
|
||||||
old: 1..3,
|
|
||||||
new: 1..4,
|
|
||||||
},
|
|
||||||
Edit {
|
|
||||||
old: 4..8,
|
|
||||||
new: 5..7,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[gpui::test]
|
|
||||||
fn test_one_overlapping_edit() {
|
|
||||||
assert_patch_composition(
|
|
||||||
Patch(vec![Edit {
|
|
||||||
old: 1..3,
|
|
||||||
new: 1..4,
|
|
||||||
}]),
|
|
||||||
Patch(vec![Edit {
|
|
||||||
old: 3..5,
|
|
||||||
new: 3..6,
|
|
||||||
}]),
|
|
||||||
Patch(vec![Edit {
|
|
||||||
old: 1..4,
|
|
||||||
new: 1..6,
|
|
||||||
}]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[gpui::test]
|
|
||||||
fn test_two_disjoint_and_overlapping() {
|
|
||||||
assert_patch_composition(
|
|
||||||
Patch(vec![
|
|
||||||
Edit {
|
|
||||||
old: 1..3,
|
|
||||||
new: 1..4,
|
|
||||||
},
|
|
||||||
Edit {
|
|
||||||
old: 8..12,
|
|
||||||
new: 9..11,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
Patch(vec![
|
|
||||||
Edit {
|
|
||||||
old: 0..0,
|
|
||||||
new: 0..4,
|
|
||||||
},
|
|
||||||
Edit {
|
|
||||||
old: 3..10,
|
|
||||||
new: 7..9,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
Patch(vec![
|
|
||||||
Edit {
|
|
||||||
old: 0..0,
|
|
||||||
new: 0..4,
|
|
||||||
},
|
|
||||||
Edit {
|
|
||||||
old: 1..12,
|
|
||||||
new: 5..10,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[gpui::test]
|
|
||||||
fn test_two_new_edits_overlapping_one_old_edit() {
|
|
||||||
assert_patch_composition(
|
|
||||||
Patch(vec![Edit {
|
|
||||||
old: 0..0,
|
|
||||||
new: 0..3,
|
|
||||||
}]),
|
|
||||||
Patch(vec![
|
|
||||||
Edit {
|
|
||||||
old: 0..0,
|
|
||||||
new: 0..1,
|
|
||||||
},
|
|
||||||
Edit {
|
|
||||||
old: 1..2,
|
|
||||||
new: 2..2,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
Patch(vec![Edit {
|
|
||||||
old: 0..0,
|
|
||||||
new: 0..3,
|
|
||||||
}]),
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_patch_composition(
|
|
||||||
Patch(vec![Edit {
|
|
||||||
old: 2..3,
|
|
||||||
new: 2..4,
|
|
||||||
}]),
|
|
||||||
Patch(vec![
|
|
||||||
Edit {
|
|
||||||
old: 0..2,
|
|
||||||
new: 0..1,
|
|
||||||
},
|
|
||||||
Edit {
|
|
||||||
old: 3..3,
|
|
||||||
new: 2..5,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
Patch(vec![Edit {
|
|
||||||
old: 0..3,
|
|
||||||
new: 0..6,
|
|
||||||
}]),
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_patch_composition(
|
|
||||||
Patch(vec![Edit {
|
|
||||||
old: 0..0,
|
|
||||||
new: 0..2,
|
|
||||||
}]),
|
|
||||||
Patch(vec![
|
|
||||||
Edit {
|
|
||||||
old: 0..0,
|
|
||||||
new: 0..2,
|
|
||||||
},
|
|
||||||
Edit {
|
|
||||||
old: 2..5,
|
|
||||||
new: 4..4,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
Patch(vec![Edit {
|
|
||||||
old: 0..3,
|
|
||||||
new: 0..4,
|
|
||||||
}]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[gpui::test]
|
|
||||||
fn test_two_new_edits_touching_one_old_edit() {
|
|
||||||
assert_patch_composition(
|
|
||||||
Patch(vec![
|
|
||||||
Edit {
|
|
||||||
old: 2..3,
|
|
||||||
new: 2..4,
|
|
||||||
},
|
|
||||||
Edit {
|
|
||||||
old: 7..7,
|
|
||||||
new: 8..11,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
Patch(vec![
|
|
||||||
Edit {
|
|
||||||
old: 2..3,
|
|
||||||
new: 2..2,
|
|
||||||
},
|
|
||||||
Edit {
|
|
||||||
old: 4..4,
|
|
||||||
new: 3..4,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
Patch(vec![
|
|
||||||
Edit {
|
|
||||||
old: 2..3,
|
|
||||||
new: 2..4,
|
|
||||||
},
|
|
||||||
Edit {
|
|
||||||
old: 7..7,
|
|
||||||
new: 8..11,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[gpui::test]
|
|
||||||
fn test_old_to_new() {
|
|
||||||
let patch = Patch(vec![
|
|
||||||
Edit {
|
|
||||||
old: 2..4,
|
|
||||||
new: 2..4,
|
|
||||||
},
|
|
||||||
Edit {
|
|
||||||
old: 7..8,
|
|
||||||
new: 7..11,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
assert_eq!(patch.old_to_new(0), 0);
|
|
||||||
assert_eq!(patch.old_to_new(1), 1);
|
|
||||||
assert_eq!(patch.old_to_new(2), 2);
|
|
||||||
assert_eq!(patch.old_to_new(3), 2);
|
|
||||||
assert_eq!(patch.old_to_new(4), 4);
|
|
||||||
assert_eq!(patch.old_to_new(5), 5);
|
|
||||||
assert_eq!(patch.old_to_new(6), 6);
|
|
||||||
assert_eq!(patch.old_to_new(7), 7);
|
|
||||||
assert_eq!(patch.old_to_new(8), 11);
|
|
||||||
assert_eq!(patch.old_to_new(9), 12);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[gpui::test(iterations = 100)]
|
|
||||||
fn test_random_patch_compositions(mut rng: StdRng) {
|
|
||||||
let operations = env::var("OPERATIONS")
|
|
||||||
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
|
|
||||||
.unwrap_or(20);
|
|
||||||
|
|
||||||
let initial_chars = (0..rng.gen_range(0..=100))
|
|
||||||
.map(|_| rng.gen_range(b'a'..=b'z') as char)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
log::info!("initial chars: {:?}", initial_chars);
|
|
||||||
|
|
||||||
// Generate two sequential patches
|
|
||||||
let mut patches = Vec::new();
|
|
||||||
let mut expected_chars = initial_chars.clone();
|
|
||||||
for i in 0..2 {
|
|
||||||
log::info!("patch {}:", i);
|
|
||||||
|
|
||||||
let mut delta = 0i32;
|
|
||||||
let mut last_edit_end = 0;
|
|
||||||
let mut edits = Vec::new();
|
|
||||||
|
|
||||||
for _ in 0..operations {
|
|
||||||
if last_edit_end >= expected_chars.len() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let end = rng.gen_range(last_edit_end..=expected_chars.len());
|
|
||||||
let start = rng.gen_range(last_edit_end..=end);
|
|
||||||
let old_len = end - start;
|
|
||||||
|
|
||||||
let mut new_len = rng.gen_range(0..=3);
|
|
||||||
if start == end && new_len == 0 {
|
|
||||||
new_len += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
last_edit_end = start + new_len + 1;
|
|
||||||
|
|
||||||
let new_chars = (0..new_len)
|
|
||||||
.map(|_| rng.gen_range(b'A'..=b'Z') as char)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
log::info!(
|
|
||||||
" editing {:?}: {:?}",
|
|
||||||
start..end,
|
|
||||||
new_chars.iter().collect::<String>()
|
|
||||||
);
|
|
||||||
edits.push(Edit {
|
|
||||||
old: (start as i32 - delta) as u32..(end as i32 - delta) as u32,
|
|
||||||
new: start as u32..(start + new_len) as u32,
|
|
||||||
});
|
|
||||||
expected_chars.splice(start..end, new_chars);
|
|
||||||
|
|
||||||
delta += new_len as i32 - old_len as i32;
|
|
||||||
}
|
|
||||||
|
|
||||||
patches.push(Patch(edits));
|
|
||||||
}
|
|
||||||
|
|
||||||
log::info!("old patch: {:?}", &patches[0]);
|
|
||||||
log::info!("new patch: {:?}", &patches[1]);
|
|
||||||
log::info!("initial chars: {:?}", initial_chars);
|
|
||||||
log::info!("final chars: {:?}", expected_chars);
|
|
||||||
|
|
||||||
// Compose the patches, and verify that it has the same effect as applying the
|
|
||||||
// two patches separately.
|
|
||||||
let composed = patches[0].compose(&patches[1]);
|
|
||||||
log::info!("composed patch: {:?}", &composed);
|
|
||||||
|
|
||||||
let mut actual_chars = initial_chars;
|
|
||||||
for edit in composed.0 {
|
|
||||||
actual_chars.splice(
|
|
||||||
edit.new.start as usize..edit.new.start as usize + edit.old.len(),
|
|
||||||
expected_chars[edit.new.start as usize..edit.new.end as usize]
|
|
||||||
.iter()
|
|
||||||
.copied(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(actual_chars, expected_chars);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn assert_patch_composition(old: Patch<u32>, new: Patch<u32>, composed: Patch<u32>) {
|
|
||||||
let original = ('a'..'z').collect::<Vec<_>>();
|
|
||||||
let inserted = ('A'..'Z').collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let mut expected = original.clone();
|
|
||||||
apply_patch(&mut expected, &old, &inserted);
|
|
||||||
apply_patch(&mut expected, &new, &inserted);
|
|
||||||
|
|
||||||
let mut actual = original;
|
|
||||||
apply_patch(&mut actual, &composed, &expected);
|
|
||||||
assert_eq!(
|
|
||||||
actual.into_iter().collect::<String>(),
|
|
||||||
expected.into_iter().collect::<String>(),
|
|
||||||
"expected patch is incorrect"
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(old.compose(&new), composed);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_patch(text: &mut Vec<char>, patch: &Patch<u32>, new_text: &[char]) {
|
|
||||||
for edit in patch.0.iter().rev() {
|
|
||||||
text.splice(
|
|
||||||
edit.old.start as usize..edit.old.end as usize,
|
|
||||||
new_text[edit.new.start as usize..edit.new.end as usize]
|
|
||||||
.iter()
|
|
||||||
.copied(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,123 +0,0 @@
|
||||||
use crate::{Anchor, BufferSnapshot, TextDimension};
|
|
||||||
use std::cmp::Ordering;
|
|
||||||
use std::ops::Range;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
|
||||||
pub enum SelectionGoal {
|
|
||||||
None,
|
|
||||||
HorizontalPosition(f32), // todo!("Can we use pixels here without adding a runtime gpui dependency?")
|
|
||||||
HorizontalRange { start: f32, end: f32 },
|
|
||||||
WrappedHorizontalPosition((u32, f32)),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub struct Selection<T> {
|
|
||||||
pub id: usize,
|
|
||||||
pub start: T,
|
|
||||||
pub end: T,
|
|
||||||
pub reversed: bool,
|
|
||||||
pub goal: SelectionGoal,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for SelectionGoal {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Clone> Selection<T> {
|
|
||||||
pub fn head(&self) -> T {
|
|
||||||
if self.reversed {
|
|
||||||
self.start.clone()
|
|
||||||
} else {
|
|
||||||
self.end.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tail(&self) -> T {
|
|
||||||
if self.reversed {
|
|
||||||
self.end.clone()
|
|
||||||
} else {
|
|
||||||
self.start.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn map<F, S>(&self, f: F) -> Selection<S>
|
|
||||||
where
|
|
||||||
F: Fn(T) -> S,
|
|
||||||
{
|
|
||||||
Selection::<S> {
|
|
||||||
id: self.id,
|
|
||||||
start: f(self.start.clone()),
|
|
||||||
end: f(self.end.clone()),
|
|
||||||
reversed: self.reversed,
|
|
||||||
goal: self.goal,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn collapse_to(&mut self, point: T, new_goal: SelectionGoal) {
|
|
||||||
self.start = point.clone();
|
|
||||||
self.end = point;
|
|
||||||
self.goal = new_goal;
|
|
||||||
self.reversed = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Copy + Ord> Selection<T> {
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.start == self.end
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_head(&mut self, head: T, new_goal: SelectionGoal) {
|
|
||||||
if head.cmp(&self.tail()) < Ordering::Equal {
|
|
||||||
if !self.reversed {
|
|
||||||
self.end = self.start;
|
|
||||||
self.reversed = true;
|
|
||||||
}
|
|
||||||
self.start = head;
|
|
||||||
} else {
|
|
||||||
if self.reversed {
|
|
||||||
self.start = self.end;
|
|
||||||
self.reversed = false;
|
|
||||||
}
|
|
||||||
self.end = head;
|
|
||||||
}
|
|
||||||
self.goal = new_goal;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn range(&self) -> Range<T> {
|
|
||||||
self.start..self.end
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Selection<usize> {
|
|
||||||
#[cfg(feature = "test-support")]
|
|
||||||
pub fn from_offset(offset: usize) -> Self {
|
|
||||||
Selection {
|
|
||||||
id: 0,
|
|
||||||
start: offset,
|
|
||||||
end: offset,
|
|
||||||
goal: SelectionGoal::None,
|
|
||||||
reversed: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn equals(&self, offset_range: &Range<usize>) -> bool {
|
|
||||||
self.start == offset_range.start && self.end == offset_range.end
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Selection<Anchor> {
|
|
||||||
pub fn resolve<'a, D: 'a + TextDimension>(
|
|
||||||
&'a self,
|
|
||||||
snapshot: &'a BufferSnapshot,
|
|
||||||
) -> Selection<D> {
|
|
||||||
Selection {
|
|
||||||
id: self.id,
|
|
||||||
start: snapshot.summary_for_anchor(&self.start),
|
|
||||||
end: snapshot.summary_for_anchor(&self.end),
|
|
||||||
reversed: self.reversed,
|
|
||||||
goal: self.goal,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
use crate::{Edit, Patch};
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
use std::{
|
|
||||||
mem,
|
|
||||||
sync::{Arc, Weak},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Topic(Mutex<Vec<Weak<Mutex<Patch<usize>>>>>);
|
|
||||||
|
|
||||||
pub struct Subscription(Arc<Mutex<Patch<usize>>>);
|
|
||||||
|
|
||||||
impl Topic {
|
|
||||||
pub fn subscribe(&mut self) -> Subscription {
|
|
||||||
let subscription = Subscription(Default::default());
|
|
||||||
self.0.get_mut().push(Arc::downgrade(&subscription.0));
|
|
||||||
subscription
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn publish(&self, edits: impl Clone + IntoIterator<Item = Edit<usize>>) {
|
|
||||||
publish(&mut self.0.lock(), edits);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn publish_mut(&mut self, edits: impl Clone + IntoIterator<Item = Edit<usize>>) {
|
|
||||||
publish(self.0.get_mut(), edits);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Subscription {
|
|
||||||
pub fn consume(&self) -> Patch<usize> {
|
|
||||||
mem::take(&mut *self.0.lock())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn publish(
|
|
||||||
subscriptions: &mut Vec<Weak<Mutex<Patch<usize>>>>,
|
|
||||||
edits: impl Clone + IntoIterator<Item = Edit<usize>>,
|
|
||||||
) {
|
|
||||||
subscriptions.retain(|subscription| {
|
|
||||||
if let Some(subscription) = subscription.upgrade() {
|
|
||||||
let mut patch = subscription.lock();
|
|
||||||
*patch = patch.compose(edits.clone());
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,764 +0,0 @@
|
||||||
use super::{network::Network, *};
|
|
||||||
use clock::ReplicaId;
|
|
||||||
use rand::prelude::*;
|
|
||||||
use std::{
|
|
||||||
cmp::Ordering,
|
|
||||||
env,
|
|
||||||
iter::Iterator,
|
|
||||||
time::{Duration, Instant},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
#[ctor::ctor]
|
|
||||||
fn init_logger() {
|
|
||||||
if std::env::var("RUST_LOG").is_ok() {
|
|
||||||
env_logger::init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_edit() {
|
|
||||||
let mut buffer = Buffer::new(0, 0, "abc".into());
|
|
||||||
assert_eq!(buffer.text(), "abc");
|
|
||||||
buffer.edit([(3..3, "def")]);
|
|
||||||
assert_eq!(buffer.text(), "abcdef");
|
|
||||||
buffer.edit([(0..0, "ghi")]);
|
|
||||||
assert_eq!(buffer.text(), "ghiabcdef");
|
|
||||||
buffer.edit([(5..5, "jkl")]);
|
|
||||||
assert_eq!(buffer.text(), "ghiabjklcdef");
|
|
||||||
buffer.edit([(6..7, "")]);
|
|
||||||
assert_eq!(buffer.text(), "ghiabjlcdef");
|
|
||||||
buffer.edit([(4..9, "mno")]);
|
|
||||||
assert_eq!(buffer.text(), "ghiamnoef");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[gpui::test(iterations = 100)]
|
|
||||||
fn test_random_edits(mut rng: StdRng) {
|
|
||||||
let operations = env::var("OPERATIONS")
|
|
||||||
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
|
|
||||||
.unwrap_or(10);
|
|
||||||
|
|
||||||
let reference_string_len = rng.gen_range(0..3);
|
|
||||||
let mut reference_string = RandomCharIter::new(&mut rng)
|
|
||||||
.take(reference_string_len)
|
|
||||||
.collect::<String>();
|
|
||||||
let mut buffer = Buffer::new(0, 0, reference_string.clone());
|
|
||||||
LineEnding::normalize(&mut reference_string);
|
|
||||||
|
|
||||||
buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
|
|
||||||
let mut buffer_versions = Vec::new();
|
|
||||||
log::info!(
|
|
||||||
"buffer text {:?}, version: {:?}",
|
|
||||||
buffer.text(),
|
|
||||||
buffer.version()
|
|
||||||
);
|
|
||||||
|
|
||||||
for _i in 0..operations {
|
|
||||||
let (edits, _) = buffer.randomly_edit(&mut rng, 5);
|
|
||||||
for (old_range, new_text) in edits.iter().rev() {
|
|
||||||
reference_string.replace_range(old_range.clone(), new_text);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(buffer.text(), reference_string);
|
|
||||||
log::info!(
|
|
||||||
"buffer text {:?}, version: {:?}",
|
|
||||||
buffer.text(),
|
|
||||||
buffer.version()
|
|
||||||
);
|
|
||||||
|
|
||||||
if rng.gen_bool(0.25) {
|
|
||||||
buffer.randomly_undo_redo(&mut rng);
|
|
||||||
reference_string = buffer.text();
|
|
||||||
log::info!(
|
|
||||||
"buffer text {:?}, version: {:?}",
|
|
||||||
buffer.text(),
|
|
||||||
buffer.version()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let range = buffer.random_byte_range(0, &mut rng);
|
|
||||||
assert_eq!(
|
|
||||||
buffer.text_summary_for_range::<TextSummary, _>(range.clone()),
|
|
||||||
TextSummary::from(&reference_string[range])
|
|
||||||
);
|
|
||||||
|
|
||||||
buffer.check_invariants();
|
|
||||||
|
|
||||||
if rng.gen_bool(0.3) {
|
|
||||||
buffer_versions.push((buffer.clone(), buffer.subscribe()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (old_buffer, subscription) in buffer_versions {
|
|
||||||
let edits = buffer
|
|
||||||
.edits_since::<usize>(&old_buffer.version)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
log::info!(
|
|
||||||
"applying edits since version {:?} to old text: {:?}: {:?}",
|
|
||||||
old_buffer.version(),
|
|
||||||
old_buffer.text(),
|
|
||||||
edits,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut text = old_buffer.visible_text.clone();
|
|
||||||
for edit in edits {
|
|
||||||
let new_text: String = buffer.text_for_range(edit.new.clone()).collect();
|
|
||||||
text.replace(edit.new.start..edit.new.start + edit.old.len(), &new_text);
|
|
||||||
}
|
|
||||||
assert_eq!(text.to_string(), buffer.text());
|
|
||||||
|
|
||||||
for _ in 0..5 {
|
|
||||||
let end_ix = old_buffer.clip_offset(rng.gen_range(0..=old_buffer.len()), Bias::Right);
|
|
||||||
let start_ix = old_buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
|
|
||||||
let range = old_buffer.anchor_before(start_ix)..old_buffer.anchor_after(end_ix);
|
|
||||||
let mut old_text = old_buffer.text_for_range(range.clone()).collect::<String>();
|
|
||||||
let edits = buffer
|
|
||||||
.edits_since_in_range::<usize>(&old_buffer.version, range.clone())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
log::info!(
|
|
||||||
"applying edits since version {:?} to old text in range {:?}: {:?}: {:?}",
|
|
||||||
old_buffer.version(),
|
|
||||||
start_ix..end_ix,
|
|
||||||
old_text,
|
|
||||||
edits,
|
|
||||||
);
|
|
||||||
|
|
||||||
let new_text = buffer.text_for_range(range).collect::<String>();
|
|
||||||
for edit in edits {
|
|
||||||
old_text.replace_range(
|
|
||||||
edit.new.start..edit.new.start + edit.old_len(),
|
|
||||||
&new_text[edit.new],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
assert_eq!(old_text, new_text);
|
|
||||||
}
|
|
||||||
|
|
||||||
let subscription_edits = subscription.consume();
|
|
||||||
log::info!(
|
|
||||||
"applying subscription edits since version {:?} to old text: {:?}: {:?}",
|
|
||||||
old_buffer.version(),
|
|
||||||
old_buffer.text(),
|
|
||||||
subscription_edits,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut text = old_buffer.visible_text.clone();
|
|
||||||
for edit in subscription_edits.into_inner() {
|
|
||||||
let new_text: String = buffer.text_for_range(edit.new.clone()).collect();
|
|
||||||
text.replace(edit.new.start..edit.new.start + edit.old.len(), &new_text);
|
|
||||||
}
|
|
||||||
assert_eq!(text.to_string(), buffer.text());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_line_endings() {
|
|
||||||
assert_eq!(LineEnding::detect(&"🍐✅\n".repeat(1000)), LineEnding::Unix);
|
|
||||||
assert_eq!(LineEnding::detect(&"abcd\n".repeat(1000)), LineEnding::Unix);
|
|
||||||
assert_eq!(
|
|
||||||
LineEnding::detect(&"🍐✅\r\n".repeat(1000)),
|
|
||||||
LineEnding::Windows
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
LineEnding::detect(&"abcd\r\n".repeat(1000)),
|
|
||||||
LineEnding::Windows
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut buffer = Buffer::new(0, 0, "one\r\ntwo\rthree".into());
|
|
||||||
assert_eq!(buffer.text(), "one\ntwo\nthree");
|
|
||||||
assert_eq!(buffer.line_ending(), LineEnding::Windows);
|
|
||||||
buffer.check_invariants();
|
|
||||||
|
|
||||||
buffer.edit([(buffer.len()..buffer.len(), "\r\nfour")]);
|
|
||||||
buffer.edit([(0..0, "zero\r\n")]);
|
|
||||||
assert_eq!(buffer.text(), "zero\none\ntwo\nthree\nfour");
|
|
||||||
assert_eq!(buffer.line_ending(), LineEnding::Windows);
|
|
||||||
buffer.check_invariants();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_line_len() {
|
|
||||||
let mut buffer = Buffer::new(0, 0, "".into());
|
|
||||||
buffer.edit([(0..0, "abcd\nefg\nhij")]);
|
|
||||||
buffer.edit([(12..12, "kl\nmno")]);
|
|
||||||
buffer.edit([(18..18, "\npqrs\n")]);
|
|
||||||
buffer.edit([(18..21, "\nPQ")]);
|
|
||||||
|
|
||||||
assert_eq!(buffer.line_len(0), 4);
|
|
||||||
assert_eq!(buffer.line_len(1), 3);
|
|
||||||
assert_eq!(buffer.line_len(2), 5);
|
|
||||||
assert_eq!(buffer.line_len(3), 3);
|
|
||||||
assert_eq!(buffer.line_len(4), 4);
|
|
||||||
assert_eq!(buffer.line_len(5), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_common_prefix_at_position() {
|
|
||||||
let text = "a = str; b = δα";
|
|
||||||
let buffer = Buffer::new(0, 0, text.into());
|
|
||||||
|
|
||||||
let offset1 = offset_after(text, "str");
|
|
||||||
let offset2 = offset_after(text, "δα");
|
|
||||||
|
|
||||||
// the preceding word is a prefix of the suggestion
|
|
||||||
assert_eq!(
|
|
||||||
buffer.common_prefix_at(offset1, "string"),
|
|
||||||
range_of(text, "str"),
|
|
||||||
);
|
|
||||||
// a suffix of the preceding word is a prefix of the suggestion
|
|
||||||
assert_eq!(
|
|
||||||
buffer.common_prefix_at(offset1, "tree"),
|
|
||||||
range_of(text, "tr"),
|
|
||||||
);
|
|
||||||
// the preceding word is a substring of the suggestion, but not a prefix
|
|
||||||
assert_eq!(
|
|
||||||
buffer.common_prefix_at(offset1, "astro"),
|
|
||||||
empty_range_after(text, "str"),
|
|
||||||
);
|
|
||||||
|
|
||||||
// prefix matching is case insensitive.
|
|
||||||
assert_eq!(
|
|
||||||
buffer.common_prefix_at(offset1, "Strαngε"),
|
|
||||||
range_of(text, "str"),
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
buffer.common_prefix_at(offset2, "ΔΑΜΝ"),
|
|
||||||
range_of(text, "δα"),
|
|
||||||
);
|
|
||||||
|
|
||||||
fn offset_after(text: &str, part: &str) -> usize {
|
|
||||||
text.find(part).unwrap() + part.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn empty_range_after(text: &str, part: &str) -> Range<usize> {
|
|
||||||
let offset = offset_after(text, part);
|
|
||||||
offset..offset
|
|
||||||
}
|
|
||||||
|
|
||||||
fn range_of(text: &str, part: &str) -> Range<usize> {
|
|
||||||
let start = text.find(part).unwrap();
|
|
||||||
start..start + part.len()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_text_summary_for_range() {
|
|
||||||
let buffer = Buffer::new(0, 0, "ab\nefg\nhklm\nnopqrs\ntuvwxyz".into());
|
|
||||||
assert_eq!(
|
|
||||||
buffer.text_summary_for_range::<TextSummary, _>(1..3),
|
|
||||||
TextSummary {
|
|
||||||
len: 2,
|
|
||||||
len_utf16: OffsetUtf16(2),
|
|
||||||
lines: Point::new(1, 0),
|
|
||||||
first_line_chars: 1,
|
|
||||||
last_line_chars: 0,
|
|
||||||
last_line_len_utf16: 0,
|
|
||||||
longest_row: 0,
|
|
||||||
longest_row_chars: 1,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
buffer.text_summary_for_range::<TextSummary, _>(1..12),
|
|
||||||
TextSummary {
|
|
||||||
len: 11,
|
|
||||||
len_utf16: OffsetUtf16(11),
|
|
||||||
lines: Point::new(3, 0),
|
|
||||||
first_line_chars: 1,
|
|
||||||
last_line_chars: 0,
|
|
||||||
last_line_len_utf16: 0,
|
|
||||||
longest_row: 2,
|
|
||||||
longest_row_chars: 4,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
buffer.text_summary_for_range::<TextSummary, _>(0..20),
|
|
||||||
TextSummary {
|
|
||||||
len: 20,
|
|
||||||
len_utf16: OffsetUtf16(20),
|
|
||||||
lines: Point::new(4, 1),
|
|
||||||
first_line_chars: 2,
|
|
||||||
last_line_chars: 1,
|
|
||||||
last_line_len_utf16: 1,
|
|
||||||
longest_row: 3,
|
|
||||||
longest_row_chars: 6,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
buffer.text_summary_for_range::<TextSummary, _>(0..22),
|
|
||||||
TextSummary {
|
|
||||||
len: 22,
|
|
||||||
len_utf16: OffsetUtf16(22),
|
|
||||||
lines: Point::new(4, 3),
|
|
||||||
first_line_chars: 2,
|
|
||||||
last_line_chars: 3,
|
|
||||||
last_line_len_utf16: 3,
|
|
||||||
longest_row: 3,
|
|
||||||
longest_row_chars: 6,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
buffer.text_summary_for_range::<TextSummary, _>(7..22),
|
|
||||||
TextSummary {
|
|
||||||
len: 15,
|
|
||||||
len_utf16: OffsetUtf16(15),
|
|
||||||
lines: Point::new(2, 3),
|
|
||||||
first_line_chars: 4,
|
|
||||||
last_line_chars: 3,
|
|
||||||
last_line_len_utf16: 3,
|
|
||||||
longest_row: 1,
|
|
||||||
longest_row_chars: 6,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_chars_at() {
|
|
||||||
let mut buffer = Buffer::new(0, 0, "".into());
|
|
||||||
buffer.edit([(0..0, "abcd\nefgh\nij")]);
|
|
||||||
buffer.edit([(12..12, "kl\nmno")]);
|
|
||||||
buffer.edit([(18..18, "\npqrs")]);
|
|
||||||
buffer.edit([(18..21, "\nPQ")]);
|
|
||||||
|
|
||||||
let chars = buffer.chars_at(Point::new(0, 0));
|
|
||||||
assert_eq!(chars.collect::<String>(), "abcd\nefgh\nijkl\nmno\nPQrs");
|
|
||||||
|
|
||||||
let chars = buffer.chars_at(Point::new(1, 0));
|
|
||||||
assert_eq!(chars.collect::<String>(), "efgh\nijkl\nmno\nPQrs");
|
|
||||||
|
|
||||||
let chars = buffer.chars_at(Point::new(2, 0));
|
|
||||||
assert_eq!(chars.collect::<String>(), "ijkl\nmno\nPQrs");
|
|
||||||
|
|
||||||
let chars = buffer.chars_at(Point::new(3, 0));
|
|
||||||
assert_eq!(chars.collect::<String>(), "mno\nPQrs");
|
|
||||||
|
|
||||||
let chars = buffer.chars_at(Point::new(4, 0));
|
|
||||||
assert_eq!(chars.collect::<String>(), "PQrs");
|
|
||||||
|
|
||||||
// Regression test:
|
|
||||||
let mut buffer = Buffer::new(0, 0, "".into());
|
|
||||||
buffer.edit([(0..0, "[workspace]\nmembers = [\n \"xray_core\",\n \"xray_server\",\n \"xray_cli\",\n \"xray_wasm\",\n]\n")]);
|
|
||||||
buffer.edit([(60..60, "\n")]);
|
|
||||||
|
|
||||||
let chars = buffer.chars_at(Point::new(6, 0));
|
|
||||||
assert_eq!(chars.collect::<String>(), " \"xray_wasm\",\n]\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_anchors() {
|
|
||||||
let mut buffer = Buffer::new(0, 0, "".into());
|
|
||||||
buffer.edit([(0..0, "abc")]);
|
|
||||||
let left_anchor = buffer.anchor_before(2);
|
|
||||||
let right_anchor = buffer.anchor_after(2);
|
|
||||||
|
|
||||||
buffer.edit([(1..1, "def\n")]);
|
|
||||||
assert_eq!(buffer.text(), "adef\nbc");
|
|
||||||
assert_eq!(left_anchor.to_offset(&buffer), 6);
|
|
||||||
assert_eq!(right_anchor.to_offset(&buffer), 6);
|
|
||||||
assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });
|
|
||||||
assert_eq!(right_anchor.to_point(&buffer), Point { row: 1, column: 1 });
|
|
||||||
|
|
||||||
buffer.edit([(2..3, "")]);
|
|
||||||
assert_eq!(buffer.text(), "adf\nbc");
|
|
||||||
assert_eq!(left_anchor.to_offset(&buffer), 5);
|
|
||||||
assert_eq!(right_anchor.to_offset(&buffer), 5);
|
|
||||||
assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });
|
|
||||||
assert_eq!(right_anchor.to_point(&buffer), Point { row: 1, column: 1 });
|
|
||||||
|
|
||||||
buffer.edit([(5..5, "ghi\n")]);
|
|
||||||
assert_eq!(buffer.text(), "adf\nbghi\nc");
|
|
||||||
assert_eq!(left_anchor.to_offset(&buffer), 5);
|
|
||||||
assert_eq!(right_anchor.to_offset(&buffer), 9);
|
|
||||||
assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });
|
|
||||||
assert_eq!(right_anchor.to_point(&buffer), Point { row: 2, column: 0 });
|
|
||||||
|
|
||||||
buffer.edit([(7..9, "")]);
|
|
||||||
assert_eq!(buffer.text(), "adf\nbghc");
|
|
||||||
assert_eq!(left_anchor.to_offset(&buffer), 5);
|
|
||||||
assert_eq!(right_anchor.to_offset(&buffer), 7);
|
|
||||||
assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 },);
|
|
||||||
assert_eq!(right_anchor.to_point(&buffer), Point { row: 1, column: 3 });
|
|
||||||
|
|
||||||
// Ensure anchoring to a point is equivalent to anchoring to an offset.
|
|
||||||
assert_eq!(
|
|
||||||
buffer.anchor_before(Point { row: 0, column: 0 }),
|
|
||||||
buffer.anchor_before(0)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
buffer.anchor_before(Point { row: 0, column: 1 }),
|
|
||||||
buffer.anchor_before(1)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
buffer.anchor_before(Point { row: 0, column: 2 }),
|
|
||||||
buffer.anchor_before(2)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
buffer.anchor_before(Point { row: 0, column: 3 }),
|
|
||||||
buffer.anchor_before(3)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
buffer.anchor_before(Point { row: 1, column: 0 }),
|
|
||||||
buffer.anchor_before(4)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
buffer.anchor_before(Point { row: 1, column: 1 }),
|
|
||||||
buffer.anchor_before(5)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
buffer.anchor_before(Point { row: 1, column: 2 }),
|
|
||||||
buffer.anchor_before(6)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
buffer.anchor_before(Point { row: 1, column: 3 }),
|
|
||||||
buffer.anchor_before(7)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
buffer.anchor_before(Point { row: 1, column: 4 }),
|
|
||||||
buffer.anchor_before(8)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Comparison between anchors.
|
|
||||||
let anchor_at_offset_0 = buffer.anchor_before(0);
|
|
||||||
let anchor_at_offset_1 = buffer.anchor_before(1);
|
|
||||||
let anchor_at_offset_2 = buffer.anchor_before(2);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
anchor_at_offset_0.cmp(&anchor_at_offset_0, &buffer),
|
|
||||||
Ordering::Equal
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
anchor_at_offset_1.cmp(&anchor_at_offset_1, &buffer),
|
|
||||||
Ordering::Equal
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
anchor_at_offset_2.cmp(&anchor_at_offset_2, &buffer),
|
|
||||||
Ordering::Equal
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
anchor_at_offset_0.cmp(&anchor_at_offset_1, &buffer),
|
|
||||||
Ordering::Less
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
anchor_at_offset_1.cmp(&anchor_at_offset_2, &buffer),
|
|
||||||
Ordering::Less
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
anchor_at_offset_0.cmp(&anchor_at_offset_2, &buffer),
|
|
||||||
Ordering::Less
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
anchor_at_offset_1.cmp(&anchor_at_offset_0, &buffer),
|
|
||||||
Ordering::Greater
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
anchor_at_offset_2.cmp(&anchor_at_offset_1, &buffer),
|
|
||||||
Ordering::Greater
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
anchor_at_offset_2.cmp(&anchor_at_offset_0, &buffer),
|
|
||||||
Ordering::Greater
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_anchors_at_start_and_end() {
|
|
||||||
let mut buffer = Buffer::new(0, 0, "".into());
|
|
||||||
let before_start_anchor = buffer.anchor_before(0);
|
|
||||||
let after_end_anchor = buffer.anchor_after(0);
|
|
||||||
|
|
||||||
buffer.edit([(0..0, "abc")]);
|
|
||||||
assert_eq!(buffer.text(), "abc");
|
|
||||||
assert_eq!(before_start_anchor.to_offset(&buffer), 0);
|
|
||||||
assert_eq!(after_end_anchor.to_offset(&buffer), 3);
|
|
||||||
|
|
||||||
let after_start_anchor = buffer.anchor_after(0);
|
|
||||||
let before_end_anchor = buffer.anchor_before(3);
|
|
||||||
|
|
||||||
buffer.edit([(3..3, "def")]);
|
|
||||||
buffer.edit([(0..0, "ghi")]);
|
|
||||||
assert_eq!(buffer.text(), "ghiabcdef");
|
|
||||||
assert_eq!(before_start_anchor.to_offset(&buffer), 0);
|
|
||||||
assert_eq!(after_start_anchor.to_offset(&buffer), 3);
|
|
||||||
assert_eq!(before_end_anchor.to_offset(&buffer), 6);
|
|
||||||
assert_eq!(after_end_anchor.to_offset(&buffer), 9);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_undo_redo() {
|
|
||||||
let mut buffer = Buffer::new(0, 0, "1234".into());
|
|
||||||
// Set group interval to zero so as to not group edits in the undo stack.
|
|
||||||
buffer.set_group_interval(Duration::from_secs(0));
|
|
||||||
|
|
||||||
buffer.edit([(1..1, "abx")]);
|
|
||||||
buffer.edit([(3..4, "yzef")]);
|
|
||||||
buffer.edit([(3..5, "cd")]);
|
|
||||||
assert_eq!(buffer.text(), "1abcdef234");
|
|
||||||
|
|
||||||
let entries = buffer.history.undo_stack.clone();
|
|
||||||
assert_eq!(entries.len(), 3);
|
|
||||||
|
|
||||||
buffer.undo_or_redo(entries[0].transaction.clone()).unwrap();
|
|
||||||
assert_eq!(buffer.text(), "1cdef234");
|
|
||||||
buffer.undo_or_redo(entries[0].transaction.clone()).unwrap();
|
|
||||||
assert_eq!(buffer.text(), "1abcdef234");
|
|
||||||
|
|
||||||
buffer.undo_or_redo(entries[1].transaction.clone()).unwrap();
|
|
||||||
assert_eq!(buffer.text(), "1abcdx234");
|
|
||||||
buffer.undo_or_redo(entries[2].transaction.clone()).unwrap();
|
|
||||||
assert_eq!(buffer.text(), "1abx234");
|
|
||||||
buffer.undo_or_redo(entries[1].transaction.clone()).unwrap();
|
|
||||||
assert_eq!(buffer.text(), "1abyzef234");
|
|
||||||
buffer.undo_or_redo(entries[2].transaction.clone()).unwrap();
|
|
||||||
assert_eq!(buffer.text(), "1abcdef234");
|
|
||||||
|
|
||||||
buffer.undo_or_redo(entries[2].transaction.clone()).unwrap();
|
|
||||||
assert_eq!(buffer.text(), "1abyzef234");
|
|
||||||
buffer.undo_or_redo(entries[0].transaction.clone()).unwrap();
|
|
||||||
assert_eq!(buffer.text(), "1yzef234");
|
|
||||||
buffer.undo_or_redo(entries[1].transaction.clone()).unwrap();
|
|
||||||
assert_eq!(buffer.text(), "1234");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_history() {
|
|
||||||
let mut now = Instant::now();
|
|
||||||
let mut buffer = Buffer::new(0, 0, "123456".into());
|
|
||||||
buffer.set_group_interval(Duration::from_millis(300));
|
|
||||||
|
|
||||||
let transaction_1 = buffer.start_transaction_at(now).unwrap();
|
|
||||||
buffer.edit([(2..4, "cd")]);
|
|
||||||
buffer.end_transaction_at(now);
|
|
||||||
assert_eq!(buffer.text(), "12cd56");
|
|
||||||
|
|
||||||
buffer.start_transaction_at(now);
|
|
||||||
buffer.edit([(4..5, "e")]);
|
|
||||||
buffer.end_transaction_at(now).unwrap();
|
|
||||||
assert_eq!(buffer.text(), "12cde6");
|
|
||||||
|
|
||||||
now += buffer.transaction_group_interval() + Duration::from_millis(1);
|
|
||||||
buffer.start_transaction_at(now);
|
|
||||||
buffer.edit([(0..1, "a")]);
|
|
||||||
buffer.edit([(1..1, "b")]);
|
|
||||||
buffer.end_transaction_at(now).unwrap();
|
|
||||||
assert_eq!(buffer.text(), "ab2cde6");
|
|
||||||
|
|
||||||
// Last transaction happened past the group interval, undo it on its own.
|
|
||||||
buffer.undo();
|
|
||||||
assert_eq!(buffer.text(), "12cde6");
|
|
||||||
|
|
||||||
// First two transactions happened within the group interval, undo them together.
|
|
||||||
buffer.undo();
|
|
||||||
assert_eq!(buffer.text(), "123456");
|
|
||||||
|
|
||||||
// Redo the first two transactions together.
|
|
||||||
buffer.redo();
|
|
||||||
assert_eq!(buffer.text(), "12cde6");
|
|
||||||
|
|
||||||
// Redo the last transaction on its own.
|
|
||||||
buffer.redo();
|
|
||||||
assert_eq!(buffer.text(), "ab2cde6");
|
|
||||||
|
|
||||||
buffer.start_transaction_at(now);
|
|
||||||
assert!(buffer.end_transaction_at(now).is_none());
|
|
||||||
buffer.undo();
|
|
||||||
assert_eq!(buffer.text(), "12cde6");
|
|
||||||
|
|
||||||
// Redo stack gets cleared after performing an edit.
|
|
||||||
buffer.start_transaction_at(now);
|
|
||||||
buffer.edit([(0..0, "X")]);
|
|
||||||
buffer.end_transaction_at(now);
|
|
||||||
assert_eq!(buffer.text(), "X12cde6");
|
|
||||||
buffer.redo();
|
|
||||||
assert_eq!(buffer.text(), "X12cde6");
|
|
||||||
buffer.undo();
|
|
||||||
assert_eq!(buffer.text(), "12cde6");
|
|
||||||
buffer.undo();
|
|
||||||
assert_eq!(buffer.text(), "123456");
|
|
||||||
|
|
||||||
// Transactions can be grouped manually.
|
|
||||||
buffer.redo();
|
|
||||||
buffer.redo();
|
|
||||||
assert_eq!(buffer.text(), "X12cde6");
|
|
||||||
buffer.group_until_transaction(transaction_1);
|
|
||||||
buffer.undo();
|
|
||||||
assert_eq!(buffer.text(), "123456");
|
|
||||||
buffer.redo();
|
|
||||||
assert_eq!(buffer.text(), "X12cde6");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_finalize_last_transaction() {
|
|
||||||
let now = Instant::now();
|
|
||||||
let mut buffer = Buffer::new(0, 0, "123456".into());
|
|
||||||
|
|
||||||
buffer.start_transaction_at(now);
|
|
||||||
buffer.edit([(2..4, "cd")]);
|
|
||||||
buffer.end_transaction_at(now);
|
|
||||||
assert_eq!(buffer.text(), "12cd56");
|
|
||||||
|
|
||||||
buffer.finalize_last_transaction();
|
|
||||||
buffer.start_transaction_at(now);
|
|
||||||
buffer.edit([(4..5, "e")]);
|
|
||||||
buffer.end_transaction_at(now).unwrap();
|
|
||||||
assert_eq!(buffer.text(), "12cde6");
|
|
||||||
|
|
||||||
buffer.start_transaction_at(now);
|
|
||||||
buffer.edit([(0..1, "a")]);
|
|
||||||
buffer.edit([(1..1, "b")]);
|
|
||||||
buffer.end_transaction_at(now).unwrap();
|
|
||||||
assert_eq!(buffer.text(), "ab2cde6");
|
|
||||||
|
|
||||||
buffer.undo();
|
|
||||||
assert_eq!(buffer.text(), "12cd56");
|
|
||||||
|
|
||||||
buffer.undo();
|
|
||||||
assert_eq!(buffer.text(), "123456");
|
|
||||||
|
|
||||||
buffer.redo();
|
|
||||||
assert_eq!(buffer.text(), "12cd56");
|
|
||||||
|
|
||||||
buffer.redo();
|
|
||||||
assert_eq!(buffer.text(), "ab2cde6");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_edited_ranges_for_transaction() {
|
|
||||||
let now = Instant::now();
|
|
||||||
let mut buffer = Buffer::new(0, 0, "1234567".into());
|
|
||||||
|
|
||||||
buffer.start_transaction_at(now);
|
|
||||||
buffer.edit([(2..4, "cd")]);
|
|
||||||
buffer.edit([(6..6, "efg")]);
|
|
||||||
buffer.end_transaction_at(now);
|
|
||||||
assert_eq!(buffer.text(), "12cd56efg7");
|
|
||||||
|
|
||||||
let tx = buffer.finalize_last_transaction().unwrap().clone();
|
|
||||||
assert_eq!(
|
|
||||||
buffer
|
|
||||||
.edited_ranges_for_transaction::<usize>(&tx)
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
[2..4, 6..9]
|
|
||||||
);
|
|
||||||
|
|
||||||
buffer.edit([(5..5, "hijk")]);
|
|
||||||
assert_eq!(buffer.text(), "12cd5hijk6efg7");
|
|
||||||
assert_eq!(
|
|
||||||
buffer
|
|
||||||
.edited_ranges_for_transaction::<usize>(&tx)
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
[2..4, 10..13]
|
|
||||||
);
|
|
||||||
|
|
||||||
buffer.edit([(4..4, "l")]);
|
|
||||||
assert_eq!(buffer.text(), "12cdl5hijk6efg7");
|
|
||||||
assert_eq!(
|
|
||||||
buffer
|
|
||||||
.edited_ranges_for_transaction::<usize>(&tx)
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
[2..4, 11..14]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_concurrent_edits() {
|
|
||||||
let text = "abcdef";
|
|
||||||
|
|
||||||
let mut buffer1 = Buffer::new(1, 0, text.into());
|
|
||||||
let mut buffer2 = Buffer::new(2, 0, text.into());
|
|
||||||
let mut buffer3 = Buffer::new(3, 0, text.into());
|
|
||||||
|
|
||||||
let buf1_op = buffer1.edit([(1..2, "12")]);
|
|
||||||
assert_eq!(buffer1.text(), "a12cdef");
|
|
||||||
let buf2_op = buffer2.edit([(3..4, "34")]);
|
|
||||||
assert_eq!(buffer2.text(), "abc34ef");
|
|
||||||
let buf3_op = buffer3.edit([(5..6, "56")]);
|
|
||||||
assert_eq!(buffer3.text(), "abcde56");
|
|
||||||
|
|
||||||
buffer1.apply_op(buf2_op.clone()).unwrap();
|
|
||||||
buffer1.apply_op(buf3_op.clone()).unwrap();
|
|
||||||
buffer2.apply_op(buf1_op.clone()).unwrap();
|
|
||||||
buffer2.apply_op(buf3_op).unwrap();
|
|
||||||
buffer3.apply_op(buf1_op).unwrap();
|
|
||||||
buffer3.apply_op(buf2_op).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(buffer1.text(), "a12c34e56");
|
|
||||||
assert_eq!(buffer2.text(), "a12c34e56");
|
|
||||||
assert_eq!(buffer3.text(), "a12c34e56");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[gpui::test(iterations = 100)]
|
|
||||||
fn test_random_concurrent_edits(mut rng: StdRng) {
|
|
||||||
let peers = env::var("PEERS")
|
|
||||||
.map(|i| i.parse().expect("invalid `PEERS` variable"))
|
|
||||||
.unwrap_or(5);
|
|
||||||
let operations = env::var("OPERATIONS")
|
|
||||||
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
|
|
||||||
.unwrap_or(10);
|
|
||||||
|
|
||||||
let base_text_len = rng.gen_range(0..10);
|
|
||||||
let base_text = RandomCharIter::new(&mut rng)
|
|
||||||
.take(base_text_len)
|
|
||||||
.collect::<String>();
|
|
||||||
let mut replica_ids = Vec::new();
|
|
||||||
let mut buffers = Vec::new();
|
|
||||||
let mut network = Network::new(rng.clone());
|
|
||||||
|
|
||||||
for i in 0..peers {
|
|
||||||
let mut buffer = Buffer::new(i as ReplicaId, 0, base_text.clone());
|
|
||||||
buffer.history.group_interval = Duration::from_millis(rng.gen_range(0..=200));
|
|
||||||
buffers.push(buffer);
|
|
||||||
replica_ids.push(i as u16);
|
|
||||||
network.add_peer(i as u16);
|
|
||||||
}
|
|
||||||
|
|
||||||
log::info!("initial text: {:?}", base_text);
|
|
||||||
|
|
||||||
let mut mutation_count = operations;
|
|
||||||
loop {
|
|
||||||
let replica_index = rng.gen_range(0..peers);
|
|
||||||
let replica_id = replica_ids[replica_index];
|
|
||||||
let buffer = &mut buffers[replica_index];
|
|
||||||
match rng.gen_range(0..=100) {
|
|
||||||
0..=50 if mutation_count != 0 => {
|
|
||||||
let op = buffer.randomly_edit(&mut rng, 5).1;
|
|
||||||
network.broadcast(buffer.replica_id, vec![op]);
|
|
||||||
log::info!("buffer {} text: {:?}", buffer.replica_id, buffer.text());
|
|
||||||
mutation_count -= 1;
|
|
||||||
}
|
|
||||||
51..=70 if mutation_count != 0 => {
|
|
||||||
let ops = buffer.randomly_undo_redo(&mut rng);
|
|
||||||
network.broadcast(buffer.replica_id, ops);
|
|
||||||
mutation_count -= 1;
|
|
||||||
}
|
|
||||||
71..=100 if network.has_unreceived(replica_id) => {
|
|
||||||
let ops = network.receive(replica_id);
|
|
||||||
if !ops.is_empty() {
|
|
||||||
log::info!(
|
|
||||||
"peer {} applying {} ops from the network.",
|
|
||||||
replica_id,
|
|
||||||
ops.len()
|
|
||||||
);
|
|
||||||
buffer.apply_ops(ops).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
buffer.check_invariants();
|
|
||||||
|
|
||||||
if mutation_count == 0 && network.is_idle() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let first_buffer = &buffers[0];
|
|
||||||
for buffer in &buffers[1..] {
|
|
||||||
assert_eq!(
|
|
||||||
buffer.text(),
|
|
||||||
first_buffer.text(),
|
|
||||||
"Replica {} text != Replica 0 text",
|
|
||||||
buffer.replica_id
|
|
||||||
);
|
|
||||||
buffer.check_invariants();
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,112 +0,0 @@
|
||||||
use crate::UndoOperation;
|
|
||||||
use std::cmp;
|
|
||||||
use sum_tree::{Bias, SumTree};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
struct UndoMapEntry {
|
|
||||||
key: UndoMapKey,
|
|
||||||
undo_count: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl sum_tree::Item for UndoMapEntry {
|
|
||||||
type Summary = UndoMapKey;
|
|
||||||
|
|
||||||
fn summary(&self) -> Self::Summary {
|
|
||||||
self.key
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl sum_tree::KeyedItem for UndoMapEntry {
|
|
||||||
type Key = UndoMapKey;
|
|
||||||
|
|
||||||
fn key(&self) -> Self::Key {
|
|
||||||
self.key
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
struct UndoMapKey {
|
|
||||||
edit_id: clock::Lamport,
|
|
||||||
undo_id: clock::Lamport,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl sum_tree::Summary for UndoMapKey {
|
|
||||||
type Context = ();
|
|
||||||
|
|
||||||
fn add_summary(&mut self, summary: &Self, _: &Self::Context) {
|
|
||||||
*self = cmp::max(*self, *summary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
|
||||||
pub struct UndoMap(SumTree<UndoMapEntry>);
|
|
||||||
|
|
||||||
impl UndoMap {
|
|
||||||
pub fn insert(&mut self, undo: &UndoOperation) {
|
|
||||||
let edits = undo
|
|
||||||
.counts
|
|
||||||
.iter()
|
|
||||||
.map(|(edit_id, count)| {
|
|
||||||
sum_tree::Edit::Insert(UndoMapEntry {
|
|
||||||
key: UndoMapKey {
|
|
||||||
edit_id: *edit_id,
|
|
||||||
undo_id: undo.timestamp,
|
|
||||||
},
|
|
||||||
undo_count: *count,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
self.0.edit(edits, &());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_undone(&self, edit_id: clock::Lamport) -> bool {
|
|
||||||
self.undo_count(edit_id) % 2 == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn was_undone(&self, edit_id: clock::Lamport, version: &clock::Global) -> bool {
|
|
||||||
let mut cursor = self.0.cursor::<UndoMapKey>();
|
|
||||||
cursor.seek(
|
|
||||||
&UndoMapKey {
|
|
||||||
edit_id,
|
|
||||||
undo_id: Default::default(),
|
|
||||||
},
|
|
||||||
Bias::Left,
|
|
||||||
&(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut undo_count = 0;
|
|
||||||
for entry in cursor {
|
|
||||||
if entry.key.edit_id != edit_id {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if version.observed(entry.key.undo_id) {
|
|
||||||
undo_count = cmp::max(undo_count, entry.undo_count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
undo_count % 2 == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn undo_count(&self, edit_id: clock::Lamport) -> u32 {
|
|
||||||
let mut cursor = self.0.cursor::<UndoMapKey>();
|
|
||||||
cursor.seek(
|
|
||||||
&UndoMapKey {
|
|
||||||
edit_id,
|
|
||||||
undo_id: Default::default(),
|
|
||||||
},
|
|
||||||
Bias::Left,
|
|
||||||
&(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut undo_count = 0;
|
|
||||||
for entry in cursor {
|
|
||||||
if entry.key.edit_id != edit_id {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
undo_count = cmp::max(undo_count, entry.undo_count);
|
|
||||||
}
|
|
||||||
undo_count
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -18,7 +18,7 @@ gpui = { package = "gpui2", path = "../gpui2" }
|
||||||
picker = { path = "../picker" }
|
picker = { path = "../picker" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
theme = { package = "theme2", path = "../theme2" }
|
theme = { package = "theme2", path = "../theme2" }
|
||||||
ui = { package = "ui2", path = "../ui2" }
|
ui = { path = "../ui" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
[package]
|
[package]
|
||||||
name = "ui2"
|
name = "ui"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "ui2"
|
name = "ui"
|
||||||
path = "src/ui2.rs"
|
path = "src/ui.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue