Compare commits
10 Commits
json-types
...
multi-auth
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74e80993a3 | ||
|
|
70993b3ce3 | ||
|
|
a47a387186 | ||
|
|
5f6f7edc99 | ||
|
|
4f46ac8c2d | ||
|
|
946c41b4b3 | ||
|
|
7f7312ca8f | ||
|
|
4232b59a59 | ||
|
|
b00901c126 | ||
|
|
e1a83a5fe6 |
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@@ -847,8 +847,7 @@ jobs:
|
||||
auto-release-preview:
|
||||
name: Auto release preview
|
||||
if: |
|
||||
false
|
||||
&& startsWith(github.ref, 'refs/tags/v')
|
||||
startsWith(github.ref, 'refs/tags/v')
|
||||
&& endsWith(github.ref, '-pre') && !endsWith(github.ref, '.0-pre')
|
||||
needs: [bundle-mac, bundle-linux-x86_x64, bundle-linux-aarch64, bundle-windows-x64]
|
||||
runs-on:
|
||||
|
||||
111
Cargo.lock
generated
111
Cargo.lock
generated
@@ -151,7 +151,7 @@ dependencies = [
|
||||
"context_server",
|
||||
"ctor",
|
||||
"db",
|
||||
"derive_more 0.99.20",
|
||||
"derive_more",
|
||||
"editor",
|
||||
"env_logger 0.11.8",
|
||||
"fs",
|
||||
@@ -210,30 +210,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "agent-client-protocol"
|
||||
version = "0.5.0"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f655394a107cd601bd2e5375c2d909ea83adc65678a0e0e8d77613d3c848a7d"
|
||||
checksum = "3aaa2bd05a2401887945f8bfd70026e90bc3cf96c62ab9eba2779835bf21dc60"
|
||||
dependencies = [
|
||||
"agent-client-protocol-schema",
|
||||
"anyhow",
|
||||
"async-broadcast",
|
||||
"async-trait",
|
||||
"derive_more 2.0.1",
|
||||
"futures 0.3.31",
|
||||
"log",
|
||||
"parking_lot",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "agent-client-protocol-schema"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61be4454304d7df1a5b44c4ae55e707ffe72eac4dfb1ef8762510ce8d8f6d924"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"derive_more 2.0.1",
|
||||
"schemars 1.0.4",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -856,7 +842,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"collections",
|
||||
"derive_more 0.99.20",
|
||||
"derive_more",
|
||||
"extension",
|
||||
"futures 0.3.31",
|
||||
"gpui",
|
||||
@@ -2078,7 +2064,7 @@ dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"itertools 0.12.1",
|
||||
"itertools 0.11.0",
|
||||
"log",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
@@ -2098,7 +2084,7 @@ dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"itertools 0.12.1",
|
||||
"itertools 0.11.0",
|
||||
"log",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
@@ -3093,7 +3079,7 @@ dependencies = [
|
||||
"cloud_llm_client",
|
||||
"collections",
|
||||
"credentials_provider",
|
||||
"derive_more 0.99.20",
|
||||
"derive_more",
|
||||
"feature_flags",
|
||||
"fs",
|
||||
"futures 0.3.31",
|
||||
@@ -3553,7 +3539,7 @@ name = "command_palette_hooks"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"collections",
|
||||
"derive_more 0.99.20",
|
||||
"derive_more",
|
||||
"gpui",
|
||||
"workspace",
|
||||
]
|
||||
@@ -4870,27 +4856,6 @@ dependencies = [
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
|
||||
dependencies = [
|
||||
"derive_more-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more-impl"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.106",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_refineable"
|
||||
version = "0.1.0"
|
||||
@@ -5037,7 +5002,7 @@ dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users 0.5.2",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5641,7 +5606,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6376,14 +6341,6 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fs_benchmarks"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"fs",
|
||||
"gpui",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fs_extra"
|
||||
version = "1.3.0"
|
||||
@@ -6918,7 +6875,7 @@ dependencies = [
|
||||
"askpass",
|
||||
"async-trait",
|
||||
"collections",
|
||||
"derive_more 0.99.20",
|
||||
"derive_more",
|
||||
"futures 0.3.31",
|
||||
"git2",
|
||||
"gpui",
|
||||
@@ -7184,7 +7141,7 @@ dependencies = [
|
||||
"core-video",
|
||||
"cosmic-text",
|
||||
"ctor",
|
||||
"derive_more 0.99.20",
|
||||
"derive_more",
|
||||
"embed-resource",
|
||||
"env_logger 0.11.8",
|
||||
"etagere",
|
||||
@@ -7683,7 +7640,7 @@ dependencies = [
|
||||
"async-fs",
|
||||
"async-tar",
|
||||
"bytes 1.10.1",
|
||||
"derive_more 0.99.20",
|
||||
"derive_more",
|
||||
"futures 0.3.31",
|
||||
"http 1.3.1",
|
||||
"http-body 1.0.1",
|
||||
@@ -10337,7 +10294,7 @@ version = "0.50.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -12890,23 +12847,6 @@ dependencies = [
|
||||
"zlog",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "project_benchmarks"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"client",
|
||||
"futures 0.3.31",
|
||||
"gpui",
|
||||
"http_client",
|
||||
"language",
|
||||
"node_runtime",
|
||||
"project",
|
||||
"settings",
|
||||
"watch",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "project_panel"
|
||||
version = "0.1.0"
|
||||
@@ -13343,7 +13283,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"socket2 0.6.1",
|
||||
"tracing",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -14426,7 +14366,7 @@ dependencies = [
|
||||
"errno 0.3.14",
|
||||
"libc",
|
||||
"linux-raw-sys 0.11.0",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -15211,7 +15151,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collections",
|
||||
"derive_more 0.99.20",
|
||||
"derive_more",
|
||||
"ec4rs",
|
||||
"fs",
|
||||
"futures 0.3.31",
|
||||
@@ -15294,7 +15234,6 @@ dependencies = [
|
||||
"schemars 1.0.4",
|
||||
"search",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"session",
|
||||
"settings",
|
||||
"strum 0.27.2",
|
||||
@@ -16877,7 +16816,7 @@ dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
"once_cell",
|
||||
"rustix 1.1.2",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -17005,7 +16944,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collections",
|
||||
"derive_more 0.99.20",
|
||||
"derive_more",
|
||||
"fs",
|
||||
"futures 0.3.31",
|
||||
"gpui",
|
||||
@@ -19559,7 +19498,7 @@ version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -20608,16 +20547,6 @@ dependencies = [
|
||||
"zlog",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "worktree_benchmarks"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"fs",
|
||||
"gpui",
|
||||
"settings",
|
||||
"worktree",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.6.1"
|
||||
|
||||
@@ -70,7 +70,6 @@ members = [
|
||||
"crates/file_finder",
|
||||
"crates/file_icons",
|
||||
"crates/fs",
|
||||
"crates/fs_benchmarks",
|
||||
"crates/fsevent",
|
||||
"crates/fuzzy",
|
||||
"crates/git",
|
||||
@@ -126,7 +125,6 @@ members = [
|
||||
"crates/picker",
|
||||
"crates/prettier",
|
||||
"crates/project",
|
||||
"crates/project_benchmarks",
|
||||
"crates/project_panel",
|
||||
"crates/project_symbols",
|
||||
"crates/prompt_store",
|
||||
@@ -195,7 +193,6 @@ members = [
|
||||
"crates/web_search_providers",
|
||||
"crates/workspace",
|
||||
"crates/worktree",
|
||||
"crates/worktree_benchmarks",
|
||||
"crates/x_ai",
|
||||
"crates/zed",
|
||||
"crates/zed_actions",
|
||||
@@ -440,7 +437,7 @@ zlog_settings = { path = "crates/zlog_settings" }
|
||||
# External crates
|
||||
#
|
||||
|
||||
agent-client-protocol = { version = "0.5.0", features = ["unstable"] }
|
||||
agent-client-protocol = { version = "=0.4.3", features = ["unstable"] }
|
||||
aho-corasick = "1.1"
|
||||
alacritty_terminal = "0.25.1-rc1"
|
||||
any_vec = "0.14"
|
||||
|
||||
@@ -1080,8 +1080,7 @@
|
||||
{
|
||||
"context": "StashList || (StashList > Picker > Editor)",
|
||||
"bindings": {
|
||||
"ctrl-shift-backspace": "stash_picker::DropStashItem",
|
||||
"ctrl-shift-v": "stash_picker::ShowStashItem"
|
||||
"ctrl-shift-backspace": "stash_picker::DropStashItem"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1267,22 +1266,12 @@
|
||||
"ctrl-pagedown": "settings_editor::FocusNextFile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "StashDiff > Editor",
|
||||
"bindings": {
|
||||
"ctrl-space": "git::ApplyCurrentStash",
|
||||
"ctrl-shift-space": "git::PopCurrentStash",
|
||||
"ctrl-shift-backspace": "git::DropCurrentStash"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "SettingsWindow > NavigationMenu",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"up": "settings_editor::FocusPreviousNavEntry",
|
||||
"shift-tab": "settings_editor::FocusPreviousNavEntry",
|
||||
"down": "settings_editor::FocusNextNavEntry",
|
||||
"tab": "settings_editor::FocusNextNavEntry",
|
||||
"right": "settings_editor::ExpandNavEntry",
|
||||
"left": "settings_editor::CollapseNavEntry",
|
||||
"pageup": "settings_editor::FocusPreviousRootNavEntry",
|
||||
|
||||
@@ -1153,8 +1153,7 @@
|
||||
"context": "StashList || (StashList > Picker > Editor)",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"ctrl-shift-backspace": "stash_picker::DropStashItem",
|
||||
"ctrl-shift-v": "stash_picker::ShowStashItem"
|
||||
"ctrl-shift-backspace": "stash_picker::DropStashItem"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1372,23 +1371,12 @@
|
||||
"cmd-}": "settings_editor::FocusNextFile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "StashDiff > Editor",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"ctrl-space": "git::ApplyCurrentStash",
|
||||
"ctrl-shift-space": "git::PopCurrentStash",
|
||||
"ctrl-shift-backspace": "git::DropCurrentStash"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "SettingsWindow > NavigationMenu",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"up": "settings_editor::FocusPreviousNavEntry",
|
||||
"shift-tab": "settings_editor::FocusPreviousNavEntry",
|
||||
"down": "settings_editor::FocusNextNavEntry",
|
||||
"tab": "settings_editor::FocusNextNavEntry",
|
||||
"right": "settings_editor::ExpandNavEntry",
|
||||
"left": "settings_editor::CollapseNavEntry",
|
||||
"pageup": "settings_editor::FocusPreviousRootNavEntry",
|
||||
|
||||
@@ -1106,8 +1106,7 @@
|
||||
"context": "StashList || (StashList > Picker > Editor)",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"ctrl-shift-backspace": "stash_picker::DropStashItem",
|
||||
"ctrl-shift-v": "stash_picker::ShowStashItem"
|
||||
"ctrl-shift-backspace": "stash_picker::DropStashItem"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1295,23 +1294,12 @@
|
||||
"ctrl-pagedown": "settings_editor::FocusNextFile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "StashDiff > Editor",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"ctrl-space": "git::ApplyCurrentStash",
|
||||
"ctrl-shift-space": "git::PopCurrentStash",
|
||||
"ctrl-shift-backspace": "git::DropCurrentStash"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "SettingsWindow > NavigationMenu",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"up": "settings_editor::FocusPreviousNavEntry",
|
||||
"shift-tab": "settings_editor::FocusPreviousNavEntry",
|
||||
"down": "settings_editor::FocusNextNavEntry",
|
||||
"tab": "settings_editor::FocusNextNavEntry",
|
||||
"right": "settings_editor::ExpandNavEntry",
|
||||
"left": "settings_editor::CollapseNavEntry",
|
||||
"pageup": "settings_editor::FocusPreviousRootNavEntry",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"$schema": "zed://schemas/settings",
|
||||
/// The displayed name of this project. If not set or null, the root directory name
|
||||
/// The displayed name of this project. If not set or empty, the root directory name
|
||||
/// will be displayed.
|
||||
"project_name": null,
|
||||
"project_name": "",
|
||||
// The name of the Zed theme to use for the UI.
|
||||
//
|
||||
// `mode` is one of:
|
||||
@@ -1091,10 +1091,10 @@
|
||||
// Only the file Zed had indexed will be used, not necessary all the gitignored files.
|
||||
//
|
||||
// Can accept 3 values:
|
||||
// * "all": Use all gitignored files
|
||||
// * "indexed": Use only the files Zed had indexed
|
||||
// * "smart": Be smart and search for ignored when called from a gitignored worktree
|
||||
"include_ignored": "smart"
|
||||
// * `true`: Use all gitignored files
|
||||
// * `false`: Use only the files Zed had indexed
|
||||
// * `null`: Be smart and search for ignored when called from a gitignored worktree
|
||||
"include_ignored": null
|
||||
},
|
||||
// Whether or not to remove any trailing whitespace from lines of a buffer
|
||||
// before saving it.
|
||||
@@ -1350,9 +1350,7 @@
|
||||
// Whether to show the active language button in the status bar.
|
||||
"active_language_button": true,
|
||||
// Whether to show the cursor position button in the status bar.
|
||||
"cursor_position_button": true,
|
||||
// Whether to show active line endings button in the status bar.
|
||||
"line_endings_button": false
|
||||
"cursor_position_button": true
|
||||
},
|
||||
// Settings specific to the terminal
|
||||
"terminal": {
|
||||
@@ -1823,7 +1821,7 @@
|
||||
"use_on_type_format": false
|
||||
},
|
||||
"Vue.js": {
|
||||
"language_servers": ["vue-language-server", "vtsls", "..."],
|
||||
"language_servers": ["vue-language-server", "..."],
|
||||
"prettier": {
|
||||
"allowed": true
|
||||
}
|
||||
|
||||
@@ -49,9 +49,8 @@
|
||||
"panel.background": "#3a3735ff",
|
||||
"panel.focused_border": "#83a598ff",
|
||||
"pane.focused_border": null,
|
||||
"scrollbar.thumb.active_background": "#83a598ac",
|
||||
"scrollbar.thumb.hover_background": "#fbf1c74c",
|
||||
"scrollbar.thumb.background": "#a899844c",
|
||||
"scrollbar.thumb.background": "#fbf1c74c",
|
||||
"scrollbar.thumb.hover_background": "#494340ff",
|
||||
"scrollbar.thumb.border": "#494340ff",
|
||||
"scrollbar.track.background": "#00000000",
|
||||
"scrollbar.track.border": "#373432ff",
|
||||
@@ -455,9 +454,8 @@
|
||||
"panel.background": "#393634ff",
|
||||
"panel.focused_border": "#83a598ff",
|
||||
"pane.focused_border": null,
|
||||
"scrollbar.thumb.active_background": "#83a598ac",
|
||||
"scrollbar.thumb.hover_background": "#fbf1c74c",
|
||||
"scrollbar.thumb.background": "#a899844c",
|
||||
"scrollbar.thumb.background": "#fbf1c74c",
|
||||
"scrollbar.thumb.hover_background": "#494340ff",
|
||||
"scrollbar.thumb.border": "#494340ff",
|
||||
"scrollbar.track.background": "#00000000",
|
||||
"scrollbar.track.border": "#343130ff",
|
||||
@@ -861,9 +859,8 @@
|
||||
"panel.background": "#3b3735ff",
|
||||
"panel.focused_border": null,
|
||||
"pane.focused_border": null,
|
||||
"scrollbar.thumb.active_background": "#83a598ac",
|
||||
"scrollbar.thumb.hover_background": "#fbf1c74c",
|
||||
"scrollbar.thumb.background": "#a899844c",
|
||||
"scrollbar.thumb.background": "#fbf1c74c",
|
||||
"scrollbar.thumb.hover_background": "#494340ff",
|
||||
"scrollbar.thumb.border": "#494340ff",
|
||||
"scrollbar.track.background": "#00000000",
|
||||
"scrollbar.track.border": "#393634ff",
|
||||
@@ -1267,9 +1264,8 @@
|
||||
"panel.background": "#ecddb4ff",
|
||||
"panel.focused_border": null,
|
||||
"pane.focused_border": null,
|
||||
"scrollbar.thumb.active_background": "#458588ac",
|
||||
"scrollbar.thumb.hover_background": "#2828284c",
|
||||
"scrollbar.thumb.background": "#7c6f644c",
|
||||
"scrollbar.thumb.background": "#2828284c",
|
||||
"scrollbar.thumb.hover_background": "#ddcca7ff",
|
||||
"scrollbar.thumb.border": "#ddcca7ff",
|
||||
"scrollbar.track.background": "#00000000",
|
||||
"scrollbar.track.border": "#eee0b7ff",
|
||||
@@ -1673,9 +1669,8 @@
|
||||
"panel.background": "#ecddb5ff",
|
||||
"panel.focused_border": null,
|
||||
"pane.focused_border": null,
|
||||
"scrollbar.thumb.active_background": "#458588ac",
|
||||
"scrollbar.thumb.hover_background": "#2828284c",
|
||||
"scrollbar.thumb.background": "#7c6f644c",
|
||||
"scrollbar.thumb.background": "#2828284c",
|
||||
"scrollbar.thumb.hover_background": "#ddcca7ff",
|
||||
"scrollbar.thumb.border": "#ddcca7ff",
|
||||
"scrollbar.track.background": "#00000000",
|
||||
"scrollbar.track.border": "#eee1bbff",
|
||||
@@ -2079,9 +2074,8 @@
|
||||
"panel.background": "#ecdcb3ff",
|
||||
"panel.focused_border": null,
|
||||
"pane.focused_border": null,
|
||||
"scrollbar.thumb.active_background": "#458588ac",
|
||||
"scrollbar.thumb.hover_background": "#2828284c",
|
||||
"scrollbar.thumb.background": "#7c6f644c",
|
||||
"scrollbar.thumb.background": "#2828284c",
|
||||
"scrollbar.thumb.hover_background": "#ddcca7ff",
|
||||
"scrollbar.thumb.border": "#ddcca7ff",
|
||||
"scrollbar.track.background": "#00000000",
|
||||
"scrollbar.track.border": "#eddeb5ff",
|
||||
|
||||
@@ -328,7 +328,7 @@ impl ToolCall {
|
||||
location: acp::ToolCallLocation,
|
||||
project: WeakEntity<Project>,
|
||||
cx: &mut AsyncApp,
|
||||
) -> Option<ResolvedLocation> {
|
||||
) -> Option<AgentLocation> {
|
||||
let buffer = project
|
||||
.update(cx, |project, cx| {
|
||||
project
|
||||
@@ -350,14 +350,17 @@ impl ToolCall {
|
||||
})
|
||||
.ok()?;
|
||||
|
||||
Some(ResolvedLocation { buffer, position })
|
||||
Some(AgentLocation {
|
||||
buffer: buffer.downgrade(),
|
||||
position,
|
||||
})
|
||||
}
|
||||
|
||||
fn resolve_locations(
|
||||
&self,
|
||||
project: Entity<Project>,
|
||||
cx: &mut App,
|
||||
) -> Task<Vec<Option<ResolvedLocation>>> {
|
||||
) -> Task<Vec<Option<AgentLocation>>> {
|
||||
let locations = self.locations.clone();
|
||||
project.update(cx, |_, cx| {
|
||||
cx.spawn(async move |project, cx| {
|
||||
@@ -371,23 +374,6 @@ impl ToolCall {
|
||||
}
|
||||
}
|
||||
|
||||
// Separate so we can hold a strong reference to the buffer
|
||||
// for saving on the thread
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
struct ResolvedLocation {
|
||||
buffer: Entity<Buffer>,
|
||||
position: Anchor,
|
||||
}
|
||||
|
||||
impl From<&ResolvedLocation> for AgentLocation {
|
||||
fn from(value: &ResolvedLocation) -> Self {
|
||||
Self {
|
||||
buffer: value.buffer.downgrade(),
|
||||
position: value.position,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ToolCallStatus {
|
||||
/// The tool call hasn't started running yet, but we start showing it to
|
||||
@@ -1407,43 +1393,35 @@ impl AcpThread {
|
||||
let task = tool_call.resolve_locations(project, cx);
|
||||
cx.spawn(async move |this, cx| {
|
||||
let resolved_locations = task.await;
|
||||
|
||||
this.update(cx, |this, cx| {
|
||||
let project = this.project.clone();
|
||||
|
||||
for location in resolved_locations.iter().flatten() {
|
||||
this.shared_buffers
|
||||
.insert(location.buffer.clone(), location.buffer.read(cx).snapshot());
|
||||
}
|
||||
let Some((ix, tool_call)) = this.tool_call_mut(&id) else {
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(Some(location)) = resolved_locations.last() {
|
||||
project.update(cx, |project, cx| {
|
||||
let should_ignore = if let Some(agent_location) = project.agent_location() {
|
||||
let snapshot = location.buffer.read(cx).snapshot();
|
||||
let old_position = agent_location.position.to_point(&snapshot);
|
||||
let new_position = location.position.to_point(&snapshot);
|
||||
agent_location.buffer == location.buffer
|
||||
// ignore this so that when we get updates from the edit tool
|
||||
// the position doesn't reset to the startof line
|
||||
&& (old_position.row == new_position.row
|
||||
&& old_position.column > new_position.column)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if !should_ignore {
|
||||
project.set_agent_location(Some(location.into()), cx);
|
||||
if let Some(agent_location) = project.agent_location() {
|
||||
let should_ignore = agent_location.buffer == location.buffer
|
||||
&& location
|
||||
.buffer
|
||||
.update(cx, |buffer, _| {
|
||||
let snapshot = buffer.snapshot();
|
||||
let old_position =
|
||||
agent_location.position.to_point(&snapshot);
|
||||
let new_position = location.position.to_point(&snapshot);
|
||||
// ignore this so that when we get updates from the edit tool
|
||||
// the position doesn't reset to the startof line
|
||||
old_position.row == new_position.row
|
||||
&& old_position.column > new_position.column
|
||||
})
|
||||
.ok()
|
||||
.unwrap_or_default();
|
||||
if !should_ignore {
|
||||
project.set_agent_location(Some(location.clone()), cx);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let resolved_locations = resolved_locations
|
||||
.iter()
|
||||
.map(|l| l.as_ref().map(|l| AgentLocation::from(l)))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if tool_call.resolved_locations != resolved_locations {
|
||||
tool_call.resolved_locations = resolved_locations;
|
||||
cx.emit(AcpThreadEvent::EntryUpdated(ix));
|
||||
|
||||
@@ -236,21 +236,21 @@ impl PendingDiff {
|
||||
fn finalize(&self, cx: &mut Context<Diff>) -> FinalizedDiff {
|
||||
let ranges = self.excerpt_ranges(cx);
|
||||
let base_text = self.base_text.clone();
|
||||
let new_buffer = self.new_buffer.read(cx);
|
||||
let language_registry = new_buffer.language_registry();
|
||||
let language_registry = self.new_buffer.read(cx).language_registry();
|
||||
|
||||
let path = new_buffer
|
||||
let path = self
|
||||
.new_buffer
|
||||
.read(cx)
|
||||
.file()
|
||||
.map(|file| file.path().display(file.path_style(cx)))
|
||||
.unwrap_or("untitled".into())
|
||||
.into();
|
||||
let replica_id = new_buffer.replica_id();
|
||||
|
||||
// Replace the buffer in the multibuffer with the snapshot
|
||||
let buffer = cx.new(|cx| {
|
||||
let language = self.new_buffer.read(cx).language().cloned();
|
||||
let buffer = TextBuffer::new_normalized(
|
||||
replica_id,
|
||||
0,
|
||||
cx.entity_id().as_non_zero_u64().into(),
|
||||
self.new_buffer.read(cx).line_ending(),
|
||||
self.new_buffer.read(cx).as_rope().clone(),
|
||||
|
||||
@@ -93,8 +93,8 @@ struct WatchedConnection {
|
||||
messages: Vec<WatchedConnectionMessage>,
|
||||
list_state: ListState,
|
||||
connection: Weak<acp::ClientSideConnection>,
|
||||
incoming_request_methods: HashMap<acp::RequestId, Arc<str>>,
|
||||
outgoing_request_methods: HashMap<acp::RequestId, Arc<str>>,
|
||||
incoming_request_methods: HashMap<i32, Arc<str>>,
|
||||
outgoing_request_methods: HashMap<i32, Arc<str>>,
|
||||
_task: Task<()>,
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ impl AcpTools {
|
||||
}
|
||||
};
|
||||
|
||||
method_map.insert(id.clone(), method.clone());
|
||||
method_map.insert(id, method.clone());
|
||||
(Some(id), method.into(), MessageType::Request, Ok(params))
|
||||
}
|
||||
acp::StreamMessageContent::Response { id, result } => {
|
||||
@@ -338,7 +338,6 @@ impl AcpTools {
|
||||
.children(
|
||||
message
|
||||
.request_id
|
||||
.as_ref()
|
||||
.map(|req_id| div().child(ui::Chip::new(req_id.to_string()))),
|
||||
),
|
||||
)
|
||||
@@ -390,7 +389,7 @@ impl AcpTools {
|
||||
|
||||
struct WatchedConnectionMessage {
|
||||
name: SharedString,
|
||||
request_id: Option<acp::RequestId>,
|
||||
request_id: Option<i32>,
|
||||
direction: acp::StreamMessageDirection,
|
||||
message_type: MessageType,
|
||||
params: Result<Option<serde_json::Value>, acp::Error>,
|
||||
|
||||
@@ -308,13 +308,12 @@ mod tests {
|
||||
use indoc::indoc;
|
||||
use language::{BufferId, TextBuffer};
|
||||
use rand::prelude::*;
|
||||
use text::ReplicaId;
|
||||
use util::test::{generate_marked_text, marked_text_ranges};
|
||||
|
||||
#[test]
|
||||
fn test_empty_query() {
|
||||
let buffer = TextBuffer::new(
|
||||
ReplicaId::LOCAL,
|
||||
0,
|
||||
BufferId::new(1).unwrap(),
|
||||
"Hello world\nThis is a test\nFoo bar baz",
|
||||
);
|
||||
@@ -328,7 +327,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_streaming_exact_match() {
|
||||
let buffer = TextBuffer::new(
|
||||
ReplicaId::LOCAL,
|
||||
0,
|
||||
BufferId::new(1).unwrap(),
|
||||
"Hello world\nThis is a test\nFoo bar baz",
|
||||
);
|
||||
@@ -352,7 +351,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_streaming_fuzzy_match() {
|
||||
let buffer = TextBuffer::new(
|
||||
ReplicaId::LOCAL,
|
||||
0,
|
||||
BufferId::new(1).unwrap(),
|
||||
indoc! {"
|
||||
function foo(a, b) {
|
||||
@@ -386,7 +385,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_incremental_improvement() {
|
||||
let buffer = TextBuffer::new(
|
||||
ReplicaId::LOCAL,
|
||||
0,
|
||||
BufferId::new(1).unwrap(),
|
||||
"Line 1\nLine 2\nLine 3\nLine 4\nLine 5",
|
||||
);
|
||||
@@ -411,7 +410,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_incomplete_lines_buffering() {
|
||||
let buffer = TextBuffer::new(
|
||||
ReplicaId::LOCAL,
|
||||
0,
|
||||
BufferId::new(1).unwrap(),
|
||||
indoc! {"
|
||||
The quick brown fox
|
||||
@@ -438,7 +437,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_multiline_fuzzy_match() {
|
||||
let buffer = TextBuffer::new(
|
||||
ReplicaId::LOCAL,
|
||||
0,
|
||||
BufferId::new(1).unwrap(),
|
||||
indoc! {r#"
|
||||
impl Display for User {
|
||||
@@ -692,11 +691,7 @@ mod tests {
|
||||
}
|
||||
"#};
|
||||
|
||||
let buffer = TextBuffer::new(
|
||||
ReplicaId::LOCAL,
|
||||
BufferId::new(1).unwrap(),
|
||||
text.to_string(),
|
||||
);
|
||||
let buffer = TextBuffer::new(0, BufferId::new(1).unwrap(), text.to_string());
|
||||
let snapshot = buffer.snapshot();
|
||||
let mut matcher = StreamingFuzzyMatcher::new(snapshot.clone());
|
||||
|
||||
@@ -729,7 +724,7 @@ mod tests {
|
||||
#[track_caller]
|
||||
fn assert_location_resolution(text_with_expected_range: &str, query: &str, rng: &mut StdRng) {
|
||||
let (text, expected_ranges) = marked_text_ranges(text_with_expected_range, false);
|
||||
let buffer = TextBuffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), text.clone());
|
||||
let buffer = TextBuffer::new(0, BufferId::new(1).unwrap(), text.clone());
|
||||
let snapshot = buffer.snapshot();
|
||||
|
||||
let mut matcher = StreamingFuzzyMatcher::new(snapshot);
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::{any::Any, path::Path, rc::Rc, sync::Arc};
|
||||
|
||||
use agent_servers::{AgentServer, AgentServerDelegate};
|
||||
use anyhow::Result;
|
||||
use collections::HashMap;
|
||||
use fs::Fs;
|
||||
use gpui::{App, Entity, SharedString, Task};
|
||||
use prompt_store::PromptStore;
|
||||
@@ -41,7 +42,7 @@ impl AgentServer for NativeAgentServer {
|
||||
) -> Task<
|
||||
Result<(
|
||||
Rc<dyn acp_thread::AgentConnection>,
|
||||
Option<task::SpawnInTerminal>,
|
||||
HashMap<String, task::SpawnInTerminal>,
|
||||
)>,
|
||||
> {
|
||||
log::debug!(
|
||||
@@ -67,7 +68,7 @@ impl AgentServer for NativeAgentServer {
|
||||
|
||||
Ok((
|
||||
Rc::new(connection) as Rc<dyn acp_thread::AgentConnection>,
|
||||
None,
|
||||
HashMap::default(),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ pub struct AcpConnection {
|
||||
// NB: Don't move this into the wait_task, since we need to ensure the process is
|
||||
// killed on drop (setting kill_on_drop on the command seems to not always work).
|
||||
child: smol::process::Child,
|
||||
_io_task: Task<Result<(), acp::Error>>,
|
||||
_io_task: Task<Result<()>>,
|
||||
_wait_task: Task<Result<()>>,
|
||||
_stderr_task: Task<Result<()>>,
|
||||
}
|
||||
|
||||
@@ -73,7 +73,12 @@ pub trait AgentServer: Send {
|
||||
root_dir: Option<&Path>,
|
||||
delegate: AgentServerDelegate,
|
||||
cx: &mut App,
|
||||
) -> Task<Result<(Rc<dyn AgentConnection>, Option<task::SpawnInTerminal>)>>;
|
||||
) -> Task<
|
||||
Result<(
|
||||
Rc<dyn AgentConnection>,
|
||||
HashMap<String, task::SpawnInTerminal>,
|
||||
)>,
|
||||
>;
|
||||
|
||||
fn into_any(self: Rc<Self>) -> Rc<dyn Any>;
|
||||
}
|
||||
@@ -84,6 +89,30 @@ impl dyn AgentServer {
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait for ACP-specific agent capabilities.
|
||||
/// This trait is only implemented by agents that use the Agent Client Protocol (ACP).
|
||||
pub trait AcpAgentServer: AgentServer {
|
||||
/// Returns the list of slash commands that should trigger Zed's authentication UI
|
||||
/// when running locally (e.g., "/login").
|
||||
/// These commands will be intercepted by Zed to show the auth method selection UI.
|
||||
fn local_login_commands(&self) -> Vec<String>;
|
||||
|
||||
/// Returns the list of slash commands that should trigger Zed's authentication UI
|
||||
/// when running remotely (e.g., "/login").
|
||||
/// These commands will be intercepted by Zed to show the auth method selection UI.
|
||||
fn remote_login_commands(&self) -> Vec<String>;
|
||||
|
||||
/// Returns the list of logout-related slash commands that should be sent to the agent
|
||||
/// when running locally to let it reset internal state (e.g., "/logout").
|
||||
/// These commands will be added to available_commands and passed through to the agent.
|
||||
fn local_logout_commands(&self) -> Vec<String>;
|
||||
|
||||
/// Returns the list of logout-related slash commands that should be sent to the agent
|
||||
/// when running remotely to let it reset internal state (e.g., "/logout").
|
||||
/// These commands will be added to available_commands and passed through to the agent.
|
||||
fn remote_logout_commands(&self) -> Vec<String>;
|
||||
}
|
||||
|
||||
/// Load the default proxy environment variables to pass through to the agent
|
||||
pub fn load_proxy_env(cx: &mut App) -> HashMap<String, String> {
|
||||
let proxy_url = cx
|
||||
|
||||
@@ -7,10 +7,11 @@ use std::sync::Arc;
|
||||
use std::{any::Any, path::PathBuf};
|
||||
|
||||
use anyhow::{Context as _, Result};
|
||||
use collections::HashMap;
|
||||
use gpui::{App, AppContext as _, SharedString, Task};
|
||||
use project::agent_server_store::{AllAgentServersSettings, CLAUDE_CODE_NAME};
|
||||
|
||||
use crate::{AgentServer, AgentServerDelegate, load_proxy_env};
|
||||
use crate::{AcpAgentServer, AgentServer, AgentServerDelegate, load_proxy_env};
|
||||
use acp_thread::AgentConnection;
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -60,7 +61,12 @@ impl AgentServer for ClaudeCode {
|
||||
root_dir: Option<&Path>,
|
||||
delegate: AgentServerDelegate,
|
||||
cx: &mut App,
|
||||
) -> Task<Result<(Rc<dyn AgentConnection>, Option<task::SpawnInTerminal>)>> {
|
||||
) -> Task<
|
||||
Result<(
|
||||
Rc<dyn AgentConnection>,
|
||||
HashMap<String, task::SpawnInTerminal>,
|
||||
)>,
|
||||
> {
|
||||
let name = self.name();
|
||||
let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().into_owned());
|
||||
let is_remote = delegate.project.read(cx).is_via_remote_server();
|
||||
@@ -69,7 +75,7 @@ impl AgentServer for ClaudeCode {
|
||||
let default_mode = self.default_mode(cx);
|
||||
|
||||
cx.spawn(async move |cx| {
|
||||
let (command, root_dir, login) = store
|
||||
let (command, root_dir, auth_commands) = store
|
||||
.update(cx, |store, cx| {
|
||||
let agent = store
|
||||
.get_external_agent(&CLAUDE_CODE_NAME.into())
|
||||
@@ -92,7 +98,7 @@ impl AgentServer for ClaudeCode {
|
||||
cx,
|
||||
)
|
||||
.await?;
|
||||
Ok((connection, login))
|
||||
Ok((connection, auth_commands))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -100,3 +106,21 @@ impl AgentServer for ClaudeCode {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl AcpAgentServer for ClaudeCode {
|
||||
fn local_login_commands(&self) -> Vec<String> {
|
||||
vec!["login".to_string()]
|
||||
}
|
||||
|
||||
fn remote_login_commands(&self) -> Vec<String> {
|
||||
vec!["login".to_string()]
|
||||
}
|
||||
|
||||
fn local_logout_commands(&self) -> Vec<String> {
|
||||
vec!["logout".to_string()]
|
||||
}
|
||||
|
||||
fn remote_logout_commands(&self) -> Vec<String> {
|
||||
vec!["logout".to_string()]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,13 @@ use std::{any::Any, path::Path};
|
||||
use acp_thread::AgentConnection;
|
||||
use agent_client_protocol as acp;
|
||||
use anyhow::{Context as _, Result};
|
||||
use collections::HashMap;
|
||||
use fs::Fs;
|
||||
use gpui::{App, AppContext as _, SharedString, Task};
|
||||
use project::agent_server_store::{AllAgentServersSettings, CODEX_NAME};
|
||||
use settings::{SettingsStore, update_settings_file};
|
||||
|
||||
use crate::{AgentServer, AgentServerDelegate, load_proxy_env};
|
||||
use crate::{AcpAgentServer, AgentServer, AgentServerDelegate, load_proxy_env};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Codex;
|
||||
@@ -61,7 +62,12 @@ impl AgentServer for Codex {
|
||||
root_dir: Option<&Path>,
|
||||
delegate: AgentServerDelegate,
|
||||
cx: &mut App,
|
||||
) -> Task<Result<(Rc<dyn AgentConnection>, Option<task::SpawnInTerminal>)>> {
|
||||
) -> Task<
|
||||
Result<(
|
||||
Rc<dyn AgentConnection>,
|
||||
HashMap<String, task::SpawnInTerminal>,
|
||||
)>,
|
||||
> {
|
||||
let name = self.name();
|
||||
let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().into_owned());
|
||||
let is_remote = delegate.project.read(cx).is_via_remote_server();
|
||||
@@ -70,7 +76,7 @@ impl AgentServer for Codex {
|
||||
let default_mode = self.default_mode(cx);
|
||||
|
||||
cx.spawn(async move |cx| {
|
||||
let (command, root_dir, login) = store
|
||||
let (command, root_dir, auth_commands) = store
|
||||
.update(cx, |store, cx| {
|
||||
let agent = store
|
||||
.get_external_agent(&CODEX_NAME.into())
|
||||
@@ -96,7 +102,7 @@ impl AgentServer for Codex {
|
||||
cx,
|
||||
)
|
||||
.await?;
|
||||
Ok((connection, login))
|
||||
Ok((connection, auth_commands))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -104,3 +110,21 @@ impl AgentServer for Codex {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl AcpAgentServer for Codex {
|
||||
fn local_login_commands(&self) -> Vec<String> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn remote_login_commands(&self) -> Vec<String> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn local_logout_commands(&self) -> Vec<String> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn remote_logout_commands(&self) -> Vec<String> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use crate::{AgentServerDelegate, load_proxy_env};
|
||||
use crate::{AcpAgentServer, AgentServerDelegate, load_proxy_env};
|
||||
use acp_thread::AgentConnection;
|
||||
use agent_client_protocol as acp;
|
||||
use anyhow::{Context as _, Result};
|
||||
use collections::HashMap;
|
||||
use fs::Fs;
|
||||
use gpui::{App, AppContext as _, SharedString, Task};
|
||||
use project::agent_server_store::{AllAgentServersSettings, ExternalAgentServerName};
|
||||
use settings::{SettingsStore, update_settings_file};
|
||||
use std::{path::Path, rc::Rc, sync::Arc};
|
||||
use std::{any::Any, path::Path, rc::Rc, sync::Arc};
|
||||
use ui::IconName;
|
||||
|
||||
/// A generic agent server implementation for custom user-defined agents
|
||||
@@ -65,7 +66,12 @@ impl crate::AgentServer for CustomAgentServer {
|
||||
root_dir: Option<&Path>,
|
||||
delegate: AgentServerDelegate,
|
||||
cx: &mut App,
|
||||
) -> Task<Result<(Rc<dyn AgentConnection>, Option<task::SpawnInTerminal>)>> {
|
||||
) -> Task<
|
||||
Result<(
|
||||
Rc<dyn AgentConnection>,
|
||||
HashMap<String, task::SpawnInTerminal>,
|
||||
)>,
|
||||
> {
|
||||
let name = self.name();
|
||||
let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().into_owned());
|
||||
let is_remote = delegate.project.read(cx).is_via_remote_server();
|
||||
@@ -74,7 +80,7 @@ impl crate::AgentServer for CustomAgentServer {
|
||||
let extra_env = load_proxy_env(cx);
|
||||
|
||||
cx.spawn(async move |cx| {
|
||||
let (command, root_dir, login) = store
|
||||
let (command, root_dir, auth_commands) = store
|
||||
.update(cx, |store, cx| {
|
||||
let agent = store
|
||||
.get_external_agent(&ExternalAgentServerName(name.clone()))
|
||||
@@ -99,11 +105,29 @@ impl crate::AgentServer for CustomAgentServer {
|
||||
cx,
|
||||
)
|
||||
.await?;
|
||||
Ok((connection, login))
|
||||
Ok((connection, auth_commands))
|
||||
})
|
||||
}
|
||||
|
||||
fn into_any(self: Rc<Self>) -> Rc<dyn std::any::Any> {
|
||||
fn into_any(self: Rc<Self>) -> Rc<dyn Any> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl AcpAgentServer for CustomAgentServer {
|
||||
fn local_login_commands(&self) -> Vec<String> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn remote_login_commands(&self) -> Vec<String> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn local_logout_commands(&self) -> Vec<String> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn remote_logout_commands(&self) -> Vec<String> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use std::rc::Rc;
|
||||
use std::{any::Any, path::Path};
|
||||
|
||||
use crate::{AgentServer, AgentServerDelegate, load_proxy_env};
|
||||
use crate::{AcpAgentServer, AgentServer, AgentServerDelegate, load_proxy_env};
|
||||
use acp_thread::AgentConnection;
|
||||
use anyhow::{Context as _, Result};
|
||||
use collections::HashMap;
|
||||
use gpui::{App, SharedString, Task};
|
||||
use language_models::provider::google::GoogleLanguageModelProvider;
|
||||
use project::agent_server_store::GEMINI_NAME;
|
||||
@@ -29,7 +30,12 @@ impl AgentServer for Gemini {
|
||||
root_dir: Option<&Path>,
|
||||
delegate: AgentServerDelegate,
|
||||
cx: &mut App,
|
||||
) -> Task<Result<(Rc<dyn AgentConnection>, Option<task::SpawnInTerminal>)>> {
|
||||
) -> Task<
|
||||
Result<(
|
||||
Rc<dyn AgentConnection>,
|
||||
HashMap<String, task::SpawnInTerminal>,
|
||||
)>,
|
||||
> {
|
||||
let name = self.name();
|
||||
let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().into_owned());
|
||||
let is_remote = delegate.project.read(cx).is_via_remote_server();
|
||||
@@ -47,7 +53,7 @@ impl AgentServer for Gemini {
|
||||
{
|
||||
extra_env.insert("GEMINI_API_KEY".into(), api_key);
|
||||
}
|
||||
let (command, root_dir, login) = store
|
||||
let (command, root_dir, auth_commands) = store
|
||||
.update(cx, |store, cx| {
|
||||
let agent = store
|
||||
.get_external_agent(&GEMINI_NAME.into())
|
||||
@@ -71,7 +77,7 @@ impl AgentServer for Gemini {
|
||||
cx,
|
||||
)
|
||||
.await?;
|
||||
Ok((connection, login))
|
||||
Ok((connection, auth_commands))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -80,6 +86,26 @@ impl AgentServer for Gemini {
|
||||
}
|
||||
}
|
||||
|
||||
impl AcpAgentServer for Gemini {
|
||||
fn local_login_commands(&self) -> Vec<String> {
|
||||
vec!["login".to_string()]
|
||||
}
|
||||
|
||||
fn remote_login_commands(&self) -> Vec<String> {
|
||||
// When remote, OAuth doesn't work, so login is handled via the
|
||||
// auth_commands mapping (oauth-personal -> spawn-gemini-cli)
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn local_logout_commands(&self) -> Vec<String> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn remote_logout_commands(&self) -> Vec<String> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use project::agent_server_store::AgentServerCommand;
|
||||
|
||||
@@ -260,10 +260,11 @@ impl ThreadFeedbackState {
|
||||
|
||||
pub struct AcpThreadView {
|
||||
agent: Rc<dyn AgentServer>,
|
||||
acp_agent: Option<Rc<dyn agent_servers::AcpAgentServer>>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
project: Entity<Project>,
|
||||
thread_state: ThreadState,
|
||||
login: Option<task::SpawnInTerminal>,
|
||||
auth_commands: HashMap<String, task::SpawnInTerminal>,
|
||||
history_store: Entity<HistoryStore>,
|
||||
hovered_recent_history_item: Option<usize>,
|
||||
entry_view_state: Entity<EntryViewState>,
|
||||
@@ -403,8 +404,38 @@ impl AcpThreadView {
|
||||
let show_codex_windows_warning = crate::ExternalAgent::parse_built_in(agent.as_ref())
|
||||
== Some(crate::ExternalAgent::Codex);
|
||||
|
||||
// Try to downcast to AcpAgentServer for ACP-specific functionality
|
||||
let acp_agent = agent
|
||||
.clone()
|
||||
.into_any()
|
||||
.downcast::<agent_servers::Gemini>()
|
||||
.map(|a| a as Rc<dyn agent_servers::AcpAgentServer>)
|
||||
.or_else(|_| {
|
||||
agent
|
||||
.clone()
|
||||
.into_any()
|
||||
.downcast::<agent_servers::ClaudeCode>()
|
||||
.map(|a| a as Rc<dyn agent_servers::AcpAgentServer>)
|
||||
})
|
||||
.or_else(|_| {
|
||||
agent
|
||||
.clone()
|
||||
.into_any()
|
||||
.downcast::<agent_servers::Codex>()
|
||||
.map(|a| a as Rc<dyn agent_servers::AcpAgentServer>)
|
||||
})
|
||||
.or_else(|_| {
|
||||
agent
|
||||
.clone()
|
||||
.into_any()
|
||||
.downcast::<agent_servers::CustomAgentServer>()
|
||||
.map(|a| a as Rc<dyn agent_servers::AcpAgentServer>)
|
||||
})
|
||||
.ok();
|
||||
|
||||
Self {
|
||||
agent: agent.clone(),
|
||||
acp_agent,
|
||||
workspace: workspace.clone(),
|
||||
project: project.clone(),
|
||||
entry_view_state,
|
||||
@@ -416,7 +447,7 @@ impl AcpThreadView {
|
||||
window,
|
||||
cx,
|
||||
),
|
||||
login: None,
|
||||
auth_commands: HashMap::default(),
|
||||
message_editor,
|
||||
model_selector: None,
|
||||
profile_selector: None,
|
||||
@@ -509,8 +540,9 @@ impl AcpThreadView {
|
||||
let connect_task = agent.connect(root_dir.as_deref(), delegate, cx);
|
||||
let load_task = cx.spawn_in(window, async move |this, cx| {
|
||||
let connection = match connect_task.await {
|
||||
Ok((connection, login)) => {
|
||||
this.update(cx, |this, _| this.login = login).ok();
|
||||
Ok((connection, auth_commands)) => {
|
||||
this.update(cx, |this, _| this.auth_commands = auth_commands)
|
||||
.ok();
|
||||
connection
|
||||
}
|
||||
Err(err) => {
|
||||
@@ -1051,20 +1083,52 @@ impl AcpThreadView {
|
||||
|
||||
let text = self.message_editor.read(cx).text(cx);
|
||||
let text = text.trim();
|
||||
if text == "/login" || text == "/logout" {
|
||||
|
||||
// Check if this is a login or logout command (only for ACP agents)
|
||||
let command_name = text.strip_prefix('/');
|
||||
let is_remote = self.project.read(cx).is_via_remote_server();
|
||||
let (login_commands, logout_commands) = if let Some(acp_agent) = &self.acp_agent {
|
||||
let login = if is_remote {
|
||||
acp_agent.remote_login_commands()
|
||||
} else {
|
||||
acp_agent.local_login_commands()
|
||||
};
|
||||
let logout = if is_remote {
|
||||
acp_agent.remote_logout_commands()
|
||||
} else {
|
||||
acp_agent.local_logout_commands()
|
||||
};
|
||||
(login, logout)
|
||||
} else {
|
||||
(vec![], vec![])
|
||||
};
|
||||
let is_login_command = if let Some(cmd) = command_name {
|
||||
login_commands.iter().any(|c| c == cmd)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let is_logout_command = if let Some(cmd) = command_name {
|
||||
logout_commands.iter().any(|c| c == cmd)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if is_login_command || is_logout_command {
|
||||
let ThreadState::Ready { thread, .. } = &self.thread_state else {
|
||||
return;
|
||||
};
|
||||
|
||||
let connection = thread.read(cx).connection().clone();
|
||||
let can_login = !connection.auth_methods().is_empty() || self.login.is_some();
|
||||
let can_login = !connection.auth_methods().is_empty() || !self.auth_commands.is_empty();
|
||||
|
||||
// Does the agent have a specific logout command? Prefer that in case they need to reset internal state.
|
||||
let logout_supported = text == "/logout"
|
||||
let logout_supported = is_logout_command
|
||||
&& self
|
||||
.available_commands
|
||||
.borrow()
|
||||
.iter()
|
||||
.any(|command| command.name == "logout");
|
||||
.any(|command| command_name == Some(command.name.as_str()));
|
||||
|
||||
if can_login && !logout_supported {
|
||||
self.message_editor
|
||||
.update(cx, |editor, cx| editor.clear(window, cx));
|
||||
@@ -1421,25 +1485,39 @@ impl AcpThreadView {
|
||||
AcpThreadEvent::AvailableCommandsUpdated(available_commands) => {
|
||||
let mut available_commands = available_commands.clone();
|
||||
|
||||
if thread
|
||||
.read(cx)
|
||||
.connection()
|
||||
.auth_methods()
|
||||
.iter()
|
||||
.any(|method| method.id.0.as_ref() == "claude-login")
|
||||
{
|
||||
available_commands.push(acp::AvailableCommand {
|
||||
name: "login".to_owned(),
|
||||
description: "Authenticate".to_owned(),
|
||||
input: None,
|
||||
meta: None,
|
||||
});
|
||||
available_commands.push(acp::AvailableCommand {
|
||||
name: "logout".to_owned(),
|
||||
description: "Authenticate".to_owned(),
|
||||
input: None,
|
||||
meta: None,
|
||||
});
|
||||
// Add auth commands only for ACP agents
|
||||
if let Some(acp_agent) = &self.acp_agent {
|
||||
let is_remote = self.project.read(cx).is_via_remote_server();
|
||||
let login_commands = if is_remote {
|
||||
acp_agent.remote_login_commands()
|
||||
} else {
|
||||
acp_agent.local_login_commands()
|
||||
};
|
||||
let logout_commands = if is_remote {
|
||||
acp_agent.remote_logout_commands()
|
||||
} else {
|
||||
acp_agent.local_logout_commands()
|
||||
};
|
||||
|
||||
// Add login commands from the agent
|
||||
for command_name in login_commands {
|
||||
available_commands.push(acp::AvailableCommand {
|
||||
name: command_name,
|
||||
description: "Authenticate".to_owned(),
|
||||
input: None,
|
||||
meta: None,
|
||||
});
|
||||
}
|
||||
|
||||
// Add logout commands from the agent
|
||||
for command_name in logout_commands {
|
||||
available_commands.push(acp::AvailableCommand {
|
||||
name: command_name,
|
||||
description: "Authenticate".to_owned(),
|
||||
input: None,
|
||||
meta: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
self.available_commands.replace(available_commands);
|
||||
@@ -1561,10 +1639,7 @@ impl AcpThreadView {
|
||||
self.thread_error.take();
|
||||
configuration_view.take();
|
||||
pending_auth_method.replace(method.clone());
|
||||
let authenticate = if (method.0.as_ref() == "claude-login"
|
||||
|| method.0.as_ref() == "spawn-gemini-cli")
|
||||
&& let Some(login) = self.login.clone()
|
||||
{
|
||||
let authenticate = if let Some(login) = self.auth_commands.get(method.0.as_ref()).cloned() {
|
||||
if let Some(workspace) = self.workspace.upgrade() {
|
||||
Self::spawn_external_agent_login(login, workspace, false, window, cx)
|
||||
} else {
|
||||
@@ -3270,48 +3345,34 @@ impl AcpThreadView {
|
||||
})
|
||||
.children(connection.auth_methods().iter().enumerate().rev().map(
|
||||
|(ix, method)| {
|
||||
let (method_id, name) = if self
|
||||
.project
|
||||
.read(cx)
|
||||
.is_via_remote_server()
|
||||
&& method.id.0.as_ref() == "oauth-personal"
|
||||
&& method.name == "Log in with Google"
|
||||
{
|
||||
("spawn-gemini-cli".into(), "Log in with Gemini CLI".into())
|
||||
} else {
|
||||
(method.id.0.clone(), method.name.clone())
|
||||
};
|
||||
let method_id = method.id.clone();
|
||||
let method_id_str = method.id.0.to_string();
|
||||
Button::new(
|
||||
SharedString::from(method.id.0.clone()),
|
||||
method.name.clone(),
|
||||
)
|
||||
.label_size(LabelSize::Small)
|
||||
.map(|this| {
|
||||
if ix == 0 {
|
||||
this.style(ButtonStyle::Tinted(TintColor::Warning))
|
||||
} else {
|
||||
this.style(ButtonStyle::Outlined)
|
||||
}
|
||||
})
|
||||
.when_some(method.description.clone(), |this, description| {
|
||||
this.tooltip(Tooltip::text(description))
|
||||
})
|
||||
.on_click({
|
||||
cx.listener(move |this, _, window, cx| {
|
||||
telemetry::event!(
|
||||
"Authenticate Agent Started",
|
||||
agent = this.agent.telemetry_id(),
|
||||
method = method_id_str
|
||||
);
|
||||
|
||||
Button::new(SharedString::from(method_id.clone()), name)
|
||||
.label_size(LabelSize::Small)
|
||||
.map(|this| {
|
||||
if ix == 0 {
|
||||
this.style(ButtonStyle::Tinted(TintColor::Warning))
|
||||
} else {
|
||||
this.style(ButtonStyle::Outlined)
|
||||
}
|
||||
})
|
||||
.when_some(
|
||||
method.description.clone(),
|
||||
|this, description| {
|
||||
this.tooltip(Tooltip::text(description))
|
||||
},
|
||||
)
|
||||
.on_click({
|
||||
cx.listener(move |this, _, window, cx| {
|
||||
telemetry::event!(
|
||||
"Authenticate Agent Started",
|
||||
agent = this.agent.telemetry_id(),
|
||||
method = method_id
|
||||
);
|
||||
|
||||
this.authenticate(
|
||||
acp::AuthMethodId(method_id.clone()),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
this.authenticate(method_id.clone(), window, cx)
|
||||
})
|
||||
})
|
||||
},
|
||||
)),
|
||||
)
|
||||
@@ -6033,8 +6094,13 @@ pub(crate) mod tests {
|
||||
_root_dir: Option<&Path>,
|
||||
_delegate: AgentServerDelegate,
|
||||
_cx: &mut App,
|
||||
) -> Task<gpui::Result<(Rc<dyn AgentConnection>, Option<task::SpawnInTerminal>)>> {
|
||||
Task::ready(Ok((Rc::new(self.connection.clone()), None)))
|
||||
) -> Task<
|
||||
gpui::Result<(
|
||||
Rc<dyn AgentConnection>,
|
||||
HashMap<String, task::SpawnInTerminal>,
|
||||
)>,
|
||||
> {
|
||||
Task::ready(Ok((Rc::new(self.connection.clone()), HashMap::default())))
|
||||
}
|
||||
|
||||
fn into_any(self: Rc<Self>) -> Rc<dyn Any> {
|
||||
|
||||
@@ -1395,10 +1395,6 @@ impl Panel for AgentPanel {
|
||||
"AgentPanel"
|
||||
}
|
||||
|
||||
fn panel_key() -> &'static str {
|
||||
AGENT_PANEL_KEY
|
||||
}
|
||||
|
||||
fn position(&self, _window: &Window, cx: &App) -> DockPosition {
|
||||
agent_panel_dock_position(cx)
|
||||
}
|
||||
|
||||
@@ -84,32 +84,10 @@ impl ZedAiOnboarding {
|
||||
self
|
||||
}
|
||||
|
||||
fn render_dismiss_button(&self) -> Option<AnyElement> {
|
||||
self.dismiss_onboarding.as_ref().map(|dismiss_callback| {
|
||||
let callback = dismiss_callback.clone();
|
||||
|
||||
h_flex()
|
||||
.absolute()
|
||||
.top_0()
|
||||
.right_0()
|
||||
.child(
|
||||
IconButton::new("dismiss_onboarding", IconName::Close)
|
||||
.icon_size(IconSize::Small)
|
||||
.tooltip(Tooltip::text("Dismiss"))
|
||||
.on_click(move |_, window, cx| {
|
||||
telemetry::event!("Banner Dismissed", source = "AI Onboarding",);
|
||||
callback(window, cx)
|
||||
}),
|
||||
)
|
||||
.into_any_element()
|
||||
})
|
||||
}
|
||||
|
||||
fn render_sign_in_disclaimer(&self, _cx: &mut App) -> AnyElement {
|
||||
let signing_in = matches!(self.sign_in_status, SignInStatus::SigningIn);
|
||||
|
||||
v_flex()
|
||||
.relative()
|
||||
.gap_1()
|
||||
.child(Headline::new("Welcome to Zed AI"))
|
||||
.child(
|
||||
@@ -131,7 +109,6 @@ impl ZedAiOnboarding {
|
||||
}
|
||||
}),
|
||||
)
|
||||
.children(self.render_dismiss_button())
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
@@ -203,7 +180,27 @@ impl ZedAiOnboarding {
|
||||
)
|
||||
.child(PlanDefinitions.free_plan(is_v2)),
|
||||
)
|
||||
.children(self.render_dismiss_button())
|
||||
.when_some(
|
||||
self.dismiss_onboarding.as_ref(),
|
||||
|this, dismiss_callback| {
|
||||
let callback = dismiss_callback.clone();
|
||||
|
||||
this.child(
|
||||
h_flex().absolute().top_0().right_0().child(
|
||||
IconButton::new("dismiss_onboarding", IconName::Close)
|
||||
.icon_size(IconSize::Small)
|
||||
.tooltip(Tooltip::text("Dismiss"))
|
||||
.on_click(move |_, window, cx| {
|
||||
telemetry::event!(
|
||||
"Banner Dismissed",
|
||||
source = "AI Onboarding",
|
||||
);
|
||||
callback(window, cx)
|
||||
}),
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
.child(
|
||||
v_flex()
|
||||
.mt_2()
|
||||
@@ -248,7 +245,26 @@ impl ZedAiOnboarding {
|
||||
.mb_2(),
|
||||
)
|
||||
.child(PlanDefinitions.pro_trial(is_v2, false))
|
||||
.children(self.render_dismiss_button())
|
||||
.when_some(
|
||||
self.dismiss_onboarding.as_ref(),
|
||||
|this, dismiss_callback| {
|
||||
let callback = dismiss_callback.clone();
|
||||
this.child(
|
||||
h_flex().absolute().top_0().right_0().child(
|
||||
IconButton::new("dismiss_onboarding", IconName::Close)
|
||||
.icon_size(IconSize::Small)
|
||||
.tooltip(Tooltip::text("Dismiss"))
|
||||
.on_click(move |_, window, cx| {
|
||||
telemetry::event!(
|
||||
"Banner Dismissed",
|
||||
source = "AI Onboarding",
|
||||
);
|
||||
callback(window, cx)
|
||||
}),
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
@@ -262,7 +278,26 @@ impl ZedAiOnboarding {
|
||||
.mb_2(),
|
||||
)
|
||||
.child(PlanDefinitions.pro_plan(is_v2, false))
|
||||
.children(self.render_dismiss_button())
|
||||
.when_some(
|
||||
self.dismiss_onboarding.as_ref(),
|
||||
|this, dismiss_callback| {
|
||||
let callback = dismiss_callback.clone();
|
||||
this.child(
|
||||
h_flex().absolute().top_0().right_0().child(
|
||||
IconButton::new("dismiss_onboarding", IconName::Close)
|
||||
.icon_size(IconSize::Small)
|
||||
.tooltip(Tooltip::text("Dismiss"))
|
||||
.on_click(move |_, window, cx| {
|
||||
telemetry::event!(
|
||||
"Banner Dismissed",
|
||||
source = "AI Onboarding",
|
||||
);
|
||||
callback(window, cx)
|
||||
}),
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -486,7 +486,7 @@ pub enum ContextSummary {
|
||||
Error,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[derive(Default, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct ContextSummaryContent {
|
||||
pub text: String,
|
||||
pub done: bool,
|
||||
@@ -523,11 +523,7 @@ impl ContextSummary {
|
||||
match self {
|
||||
ContextSummary::Content(content) => content,
|
||||
ContextSummary::Pending | ContextSummary::Error => {
|
||||
let content = ContextSummaryContent {
|
||||
text: "".to_string(),
|
||||
done: false,
|
||||
timestamp: clock::Lamport::MIN,
|
||||
};
|
||||
let content = ContextSummaryContent::default();
|
||||
*self = ContextSummary::Content(content);
|
||||
self.content_as_mut().unwrap()
|
||||
}
|
||||
@@ -800,7 +796,7 @@ impl AssistantContext {
|
||||
};
|
||||
|
||||
let first_message_id = MessageId(clock::Lamport {
|
||||
replica_id: ReplicaId::LOCAL,
|
||||
replica_id: 0,
|
||||
value: 0,
|
||||
});
|
||||
let message = MessageAnchor {
|
||||
@@ -2696,7 +2692,7 @@ impl AssistantContext {
|
||||
self.summary = ContextSummary::Content(ContextSummaryContent {
|
||||
text: "".to_string(),
|
||||
done: false,
|
||||
timestamp: clock::Lamport::MIN,
|
||||
timestamp: clock::Lamport::default(),
|
||||
});
|
||||
replace_old = true;
|
||||
}
|
||||
@@ -3121,7 +3117,7 @@ impl SavedContext {
|
||||
|
||||
let mut first_message_metadata = None;
|
||||
for message in self.messages {
|
||||
if message.id == MessageId(clock::Lamport::MIN) {
|
||||
if message.id == MessageId(clock::Lamport::default()) {
|
||||
first_message_metadata = Some(message.metadata);
|
||||
} else {
|
||||
operations.push(ContextOperation::InsertMessage {
|
||||
@@ -3145,7 +3141,7 @@ impl SavedContext {
|
||||
if let Some(metadata) = first_message_metadata {
|
||||
let timestamp = next_timestamp.tick();
|
||||
operations.push(ContextOperation::UpdateMessage {
|
||||
message_id: MessageId(clock::Lamport::MIN),
|
||||
message_id: MessageId(clock::Lamport::default()),
|
||||
metadata: MessageMetadata {
|
||||
role: metadata.role,
|
||||
status: metadata.status,
|
||||
|
||||
@@ -741,7 +741,7 @@ async fn test_serialization(cx: &mut TestAppContext) {
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test(iterations = 25)]
|
||||
#[gpui::test(iterations = 100)]
|
||||
async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: StdRng) {
|
||||
cx.update(init_test);
|
||||
|
||||
@@ -771,7 +771,7 @@ async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: Std
|
||||
let context = cx.new(|cx| {
|
||||
AssistantContext::new(
|
||||
context_id.clone(),
|
||||
ReplicaId::new(i as u16),
|
||||
i as ReplicaId,
|
||||
language::Capability::ReadWrite,
|
||||
registry.clone(),
|
||||
prompt_builder.clone(),
|
||||
@@ -789,7 +789,7 @@ async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: Std
|
||||
if let ContextEvent::Operation(op) = event {
|
||||
network
|
||||
.lock()
|
||||
.broadcast(ReplicaId::new(i as u16), vec![op.to_proto()]);
|
||||
.broadcast(i as ReplicaId, vec![op.to_proto()]);
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -797,7 +797,7 @@ async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: Std
|
||||
});
|
||||
|
||||
contexts.push(context);
|
||||
network.lock().add_peer(ReplicaId::new(i as u16));
|
||||
network.lock().add_peer(i as ReplicaId);
|
||||
}
|
||||
|
||||
let mut mutation_count = operations;
|
||||
@@ -943,9 +943,9 @@ async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: Std
|
||||
mutation_count -= 1;
|
||||
}
|
||||
_ => {
|
||||
let replica_id = ReplicaId::new(context_index as u16);
|
||||
let replica_id = context_index as ReplicaId;
|
||||
if network.lock().is_disconnected(replica_id) {
|
||||
network.lock().reconnect_peer(replica_id, ReplicaId::new(0));
|
||||
network.lock().reconnect_peer(replica_id, 0);
|
||||
|
||||
let (ops_to_send, ops_to_receive) = cx.read(|cx| {
|
||||
let host_context = &contexts[0].read(cx);
|
||||
@@ -971,7 +971,7 @@ async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: Std
|
||||
|
||||
network.lock().broadcast(replica_id, ops_to_send);
|
||||
context.update(cx, |context, cx| context.apply_ops(ops_to_receive, cx));
|
||||
} else if rng.random_bool(0.1) && replica_id != ReplicaId::new(0) {
|
||||
} else if rng.random_bool(0.1) && replica_id != 0 {
|
||||
log::info!("Context {}: disconnecting", context_index);
|
||||
network.lock().disconnect_peer(replica_id);
|
||||
} else if network.lock().has_unreceived(replica_id) {
|
||||
@@ -996,25 +996,25 @@ async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: Std
|
||||
assert_eq!(
|
||||
context.buffer.read(cx).text(),
|
||||
first_context.buffer.read(cx).text(),
|
||||
"Context {:?} text != Context 0 text",
|
||||
"Context {} text != Context 0 text",
|
||||
context.buffer.read(cx).replica_id()
|
||||
);
|
||||
assert_eq!(
|
||||
context.message_anchors,
|
||||
first_context.message_anchors,
|
||||
"Context {:?} messages != Context 0 messages",
|
||||
"Context {} messages != Context 0 messages",
|
||||
context.buffer.read(cx).replica_id()
|
||||
);
|
||||
assert_eq!(
|
||||
context.messages_metadata,
|
||||
first_context.messages_metadata,
|
||||
"Context {:?} message metadata != Context 0 message metadata",
|
||||
"Context {} message metadata != Context 0 message metadata",
|
||||
context.buffer.read(cx).replica_id()
|
||||
);
|
||||
assert_eq!(
|
||||
context.slash_command_output_sections,
|
||||
first_context.slash_command_output_sections,
|
||||
"Context {:?} slash command output sections != Context 0 slash command output sections",
|
||||
"Context {} slash command output sections != Context 0 slash command output sections",
|
||||
context.buffer.read(cx).replica_id()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ struct PendingHunk {
|
||||
new_status: DiffHunkSecondaryStatus,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct DiffHunkSummary {
|
||||
buffer_range: Range<Anchor>,
|
||||
}
|
||||
@@ -114,9 +114,7 @@ impl sum_tree::Summary for DiffHunkSummary {
|
||||
type Context<'a> = &'a text::BufferSnapshot;
|
||||
|
||||
fn zero(_cx: Self::Context<'_>) -> Self {
|
||||
DiffHunkSummary {
|
||||
buffer_range: Anchor::MIN..Anchor::MIN,
|
||||
}
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, other: &Self, buffer: Self::Context<'_>) {
|
||||
@@ -939,9 +937,7 @@ impl BufferDiff {
|
||||
|
||||
pub fn clear_pending_hunks(&mut self, cx: &mut Context<Self>) {
|
||||
if self.secondary_diff.is_some() {
|
||||
self.inner.pending_hunks = SumTree::from_summary(DiffHunkSummary {
|
||||
buffer_range: Anchor::MIN..Anchor::MIN,
|
||||
});
|
||||
self.inner.pending_hunks = SumTree::from_summary(DiffHunkSummary::default());
|
||||
cx.emit(BufferDiffEvent::DiffChanged {
|
||||
changed_range: Some(Anchor::MIN..Anchor::MAX),
|
||||
});
|
||||
@@ -1372,7 +1368,7 @@ mod tests {
|
||||
use gpui::TestAppContext;
|
||||
use pretty_assertions::{assert_eq, assert_ne};
|
||||
use rand::{Rng as _, rngs::StdRng};
|
||||
use text::{Buffer, BufferId, ReplicaId, Rope};
|
||||
use text::{Buffer, BufferId, Rope};
|
||||
use unindent::Unindent as _;
|
||||
use util::test::marked_text_ranges;
|
||||
|
||||
@@ -1397,7 +1393,7 @@ mod tests {
|
||||
"
|
||||
.unindent();
|
||||
|
||||
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text);
|
||||
let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), buffer_text);
|
||||
let mut diff = BufferDiffSnapshot::new_sync(buffer.clone(), diff_base.clone(), cx);
|
||||
assert_hunks(
|
||||
diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &buffer),
|
||||
@@ -1471,7 +1467,7 @@ mod tests {
|
||||
"
|
||||
.unindent();
|
||||
|
||||
let buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text);
|
||||
let buffer = Buffer::new(0, BufferId::new(1).unwrap(), buffer_text);
|
||||
let unstaged_diff = BufferDiffSnapshot::new_sync(buffer.clone(), index_text, cx);
|
||||
let mut uncommitted_diff =
|
||||
BufferDiffSnapshot::new_sync(buffer.clone(), head_text.clone(), cx);
|
||||
@@ -1540,7 +1536,7 @@ mod tests {
|
||||
"
|
||||
.unindent();
|
||||
|
||||
let buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text);
|
||||
let buffer = Buffer::new(0, BufferId::new(1).unwrap(), buffer_text);
|
||||
let diff = cx
|
||||
.update(|cx| {
|
||||
BufferDiffSnapshot::new_with_base_text(
|
||||
@@ -1803,7 +1799,7 @@ mod tests {
|
||||
|
||||
for example in table {
|
||||
let (buffer_text, ranges) = marked_text_ranges(&example.buffer_marked_text, false);
|
||||
let buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text);
|
||||
let buffer = Buffer::new(0, BufferId::new(1).unwrap(), buffer_text);
|
||||
let hunk_range =
|
||||
buffer.anchor_before(ranges[0].start)..buffer.anchor_before(ranges[0].end);
|
||||
|
||||
@@ -1876,11 +1872,7 @@ mod tests {
|
||||
"
|
||||
.unindent();
|
||||
|
||||
let buffer = Buffer::new(
|
||||
ReplicaId::LOCAL,
|
||||
BufferId::new(1).unwrap(),
|
||||
buffer_text.clone(),
|
||||
);
|
||||
let buffer = Buffer::new(0, BufferId::new(1).unwrap(), buffer_text.clone());
|
||||
let unstaged = BufferDiffSnapshot::new_sync(buffer.clone(), index_text, cx);
|
||||
let uncommitted = BufferDiffSnapshot::new_sync(buffer.clone(), head_text.clone(), cx);
|
||||
let unstaged_diff = cx.new(|cx| {
|
||||
@@ -1953,7 +1945,7 @@ mod tests {
|
||||
"
|
||||
.unindent();
|
||||
|
||||
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text_1);
|
||||
let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), buffer_text_1);
|
||||
|
||||
let empty_diff = cx.update(|cx| BufferDiffSnapshot::empty(&buffer, cx));
|
||||
let diff_1 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
|
||||
|
||||
@@ -9,7 +9,7 @@ use rpc::{
|
||||
proto::{self, PeerId},
|
||||
};
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use text::{BufferId, ReplicaId};
|
||||
use text::BufferId;
|
||||
use util::ResultExt;
|
||||
|
||||
pub const ACKNOWLEDGE_DEBOUNCE_INTERVAL: Duration = Duration::from_millis(250);
|
||||
@@ -65,12 +65,7 @@ impl ChannelBuffer {
|
||||
|
||||
let buffer = cx.new(|cx| {
|
||||
let capability = channel_store.read(cx).channel_capability(channel.id);
|
||||
language::Buffer::remote(
|
||||
buffer_id,
|
||||
ReplicaId::new(response.replica_id as u16),
|
||||
capability,
|
||||
base_text,
|
||||
)
|
||||
language::Buffer::remote(buffer_id, response.replica_id as u16, capability, base_text)
|
||||
})?;
|
||||
buffer.update(cx, |buffer, cx| buffer.apply_ops(operations, cx))?;
|
||||
|
||||
@@ -277,7 +272,7 @@ impl ChannelBuffer {
|
||||
self.connected
|
||||
}
|
||||
|
||||
pub fn replica_id(&self, cx: &App) -> ReplicaId {
|
||||
pub fn replica_id(&self, cx: &App) -> u16 {
|
||||
self.buffer.read(cx).replica_id()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ pub enum CliRequest {
|
||||
wsl: Option<String>,
|
||||
wait: bool,
|
||||
open_new_workspace: Option<bool>,
|
||||
reuse: bool,
|
||||
env: Option<HashMap<String, String>>,
|
||||
user_data_dir: Option<String>,
|
||||
},
|
||||
|
||||
@@ -62,14 +62,11 @@ struct Args {
|
||||
#[arg(short, long)]
|
||||
wait: bool,
|
||||
/// Add files to the currently open workspace
|
||||
#[arg(short, long, overrides_with_all = ["new", "reuse"])]
|
||||
#[arg(short, long, overrides_with = "new")]
|
||||
add: bool,
|
||||
/// Create a new workspace
|
||||
#[arg(short, long, overrides_with_all = ["add", "reuse"])]
|
||||
#[arg(short, long, overrides_with = "add")]
|
||||
new: bool,
|
||||
/// Reuse an existing window, replacing its workspace
|
||||
#[arg(short, long, overrides_with_all = ["add", "new"])]
|
||||
reuse: bool,
|
||||
/// Sets a custom directory for all user data (e.g., database, extensions, logs).
|
||||
/// This overrides the default platform-specific data directory location:
|
||||
#[cfg_attr(target_os = "macos", doc = "`~/Library/Application Support/Zed`.")]
|
||||
@@ -377,7 +374,6 @@ fn main() -> Result<()> {
|
||||
wsl,
|
||||
wait: args.wait,
|
||||
open_new_workspace,
|
||||
reuse: args.reuse,
|
||||
env,
|
||||
user_data_dir: user_data_dir_for_thread,
|
||||
})?;
|
||||
|
||||
@@ -943,7 +943,7 @@ impl Collaborator {
|
||||
pub fn from_proto(message: proto::Collaborator) -> Result<Self> {
|
||||
Ok(Self {
|
||||
peer_id: message.peer_id.context("invalid peer id")?,
|
||||
replica_id: ReplicaId::new(message.replica_id as u16),
|
||||
replica_id: message.replica_id as ReplicaId,
|
||||
user_id: message.user_id as UserId,
|
||||
is_host: message.is_host,
|
||||
committer_name: message.committer_name,
|
||||
|
||||
@@ -4,73 +4,33 @@ use serde::{Deserialize, Serialize};
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
cmp::{self, Ordering},
|
||||
fmt,
|
||||
fmt, iter,
|
||||
};
|
||||
|
||||
pub use system_clock::*;
|
||||
|
||||
pub const LOCAL_BRANCH_REPLICA_ID: u16 = u16::MAX;
|
||||
pub const AGENT_REPLICA_ID: u16 = u16::MAX - 1;
|
||||
|
||||
/// A unique identifier for each distributed node.
|
||||
#[derive(Clone, Copy, Default, Eq, Hash, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct ReplicaId(u16);
|
||||
|
||||
impl ReplicaId {
|
||||
/// The local replica
|
||||
pub const LOCAL: ReplicaId = ReplicaId(0);
|
||||
/// The remote replica of the connected remote server.
|
||||
pub const REMOTE_SERVER: ReplicaId = ReplicaId(1);
|
||||
/// The agent's unique identifier.
|
||||
pub const AGENT: ReplicaId = ReplicaId(2);
|
||||
/// A local branch.
|
||||
pub const LOCAL_BRANCH: ReplicaId = ReplicaId(3);
|
||||
/// The first collaborative replica ID, any replica equal or greater than this is a collaborative replica.
|
||||
pub const FIRST_COLLAB_ID: ReplicaId = ReplicaId(8);
|
||||
|
||||
pub fn new(id: u16) -> Self {
|
||||
ReplicaId(id)
|
||||
}
|
||||
|
||||
pub fn as_u16(&self) -> u16 {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn is_remote(self) -> bool {
|
||||
self == ReplicaId::REMOTE_SERVER || self >= ReplicaId::FIRST_COLLAB_ID
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ReplicaId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if *self == ReplicaId::LOCAL {
|
||||
write!(f, "<local>")
|
||||
} else if *self == ReplicaId::REMOTE_SERVER {
|
||||
write!(f, "<remote>")
|
||||
} else if *self == ReplicaId::AGENT {
|
||||
write!(f, "<agent>")
|
||||
} else if *self == ReplicaId::LOCAL_BRANCH {
|
||||
write!(f, "<branch>")
|
||||
} else {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
pub type ReplicaId = u16;
|
||||
|
||||
/// A [Lamport sequence number](https://en.wikipedia.org/wiki/Lamport_timestamp).
|
||||
pub type Seq = u32;
|
||||
|
||||
/// A [Lamport timestamp](https://en.wikipedia.org/wiki/Lamport_timestamp),
|
||||
/// used to determine the ordering of events in the editor.
|
||||
#[derive(Clone, Copy, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Copy, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Lamport {
|
||||
pub replica_id: ReplicaId,
|
||||
pub value: Seq,
|
||||
}
|
||||
|
||||
/// A [version vector](https://en.wikipedia.org/wiki/Version_vector).
|
||||
/// A [vector clock](https://en.wikipedia.org/wiki/Vector_clock).
|
||||
#[derive(Clone, Default, Hash, Eq, PartialEq)]
|
||||
pub struct Global {
|
||||
// 4 is chosen as it is the biggest count that does not increase the size of the field itself.
|
||||
// Coincidentally, it also covers all the important non-collab replica ids.
|
||||
values: SmallVec<[u32; 4]>,
|
||||
values: SmallVec<[u32; 8]>,
|
||||
local_branch_value: u32,
|
||||
}
|
||||
|
||||
impl Global {
|
||||
@@ -78,31 +38,30 @@ impl Global {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Fetches the sequence number for the given replica ID.
|
||||
pub fn get(&self, replica_id: ReplicaId) -> Seq {
|
||||
self.values.get(replica_id.0 as usize).copied().unwrap_or(0) as Seq
|
||||
}
|
||||
|
||||
/// Observe the lamport timestampe.
|
||||
///
|
||||
/// This sets the current sequence number of the observed replica ID to the maximum of this global's observed sequence and the observed timestamp.
|
||||
pub fn observe(&mut self, timestamp: Lamport) {
|
||||
debug_assert_ne!(timestamp.replica_id, Lamport::MAX.replica_id);
|
||||
if timestamp.value > 0 {
|
||||
let new_len = timestamp.replica_id.0 as usize + 1;
|
||||
if new_len > self.values.len() {
|
||||
self.values.resize(new_len, 0);
|
||||
}
|
||||
|
||||
let entry = &mut self.values[timestamp.replica_id.0 as usize];
|
||||
*entry = cmp::max(*entry, timestamp.value);
|
||||
if replica_id == LOCAL_BRANCH_REPLICA_ID {
|
||||
self.local_branch_value
|
||||
} else {
|
||||
self.values.get(replica_id as usize).copied().unwrap_or(0) as Seq
|
||||
}
|
||||
}
|
||||
|
||||
pub fn observe(&mut self, timestamp: Lamport) {
|
||||
if timestamp.value > 0 {
|
||||
if timestamp.replica_id == LOCAL_BRANCH_REPLICA_ID {
|
||||
self.local_branch_value = cmp::max(self.local_branch_value, timestamp.value);
|
||||
} else {
|
||||
let new_len = timestamp.replica_id as usize + 1;
|
||||
if new_len > self.values.len() {
|
||||
self.values.resize(new_len, 0);
|
||||
}
|
||||
|
||||
let entry = &mut self.values[timestamp.replica_id as usize];
|
||||
*entry = cmp::max(*entry, timestamp.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Join another global.
|
||||
///
|
||||
/// This observes all timestamps from the other global.
|
||||
#[doc(alias = "synchronize")]
|
||||
pub fn join(&mut self, other: &Self) {
|
||||
if other.values.len() > self.values.len() {
|
||||
self.values.resize(other.values.len(), 0);
|
||||
@@ -111,36 +70,34 @@ impl Global {
|
||||
for (left, right) in self.values.iter_mut().zip(&other.values) {
|
||||
*left = cmp::max(*left, *right);
|
||||
}
|
||||
|
||||
self.local_branch_value = cmp::max(self.local_branch_value, other.local_branch_value);
|
||||
}
|
||||
|
||||
/// Meet another global.
|
||||
///
|
||||
/// Sets all unobserved timestamps of this global to the sequences of other and sets all observed timestamps of this global to the minimum observed of both globals.
|
||||
pub fn meet(&mut self, other: &Self) {
|
||||
if other.values.len() > self.values.len() {
|
||||
self.values.resize(other.values.len(), 0);
|
||||
}
|
||||
|
||||
let mut new_len = 0;
|
||||
for (ix, (left, &right)) in self.values.iter_mut().zip(&other.values).enumerate() {
|
||||
match (*left, right) {
|
||||
// left has not observed the replica
|
||||
(0, _) => *left = right,
|
||||
// right has not observed the replica
|
||||
(_, 0) => (),
|
||||
(_, _) => *left = cmp::min(*left, right),
|
||||
for (ix, (left, right)) in self
|
||||
.values
|
||||
.iter_mut()
|
||||
.zip(other.values.iter().chain(iter::repeat(&0)))
|
||||
.enumerate()
|
||||
{
|
||||
if *left == 0 {
|
||||
*left = *right;
|
||||
} else if *right > 0 {
|
||||
*left = cmp::min(*left, *right);
|
||||
}
|
||||
|
||||
if *left != 0 {
|
||||
new_len = ix + 1;
|
||||
}
|
||||
}
|
||||
if other.values.len() == self.values.len() {
|
||||
// only truncate if other was equal or shorter (which at this point
|
||||
// cant be due to the resize above) to `self` as otherwise we would
|
||||
// truncate the unprocessed tail that is guaranteed to contain
|
||||
// non-null timestamps
|
||||
self.values.truncate(new_len);
|
||||
}
|
||||
self.values.resize(new_len, 0);
|
||||
self.local_branch_value = cmp::min(self.local_branch_value, other.local_branch_value);
|
||||
}
|
||||
|
||||
pub fn observed(&self, timestamp: Lamport) -> bool {
|
||||
@@ -148,18 +105,20 @@ impl Global {
|
||||
}
|
||||
|
||||
pub fn observed_any(&self, other: &Self) -> bool {
|
||||
self.iter()
|
||||
.zip(other.iter())
|
||||
.any(|(left, right)| right.value > 0 && left.value >= right.value)
|
||||
self.values
|
||||
.iter()
|
||||
.zip(other.values.iter())
|
||||
.any(|(left, right)| *right > 0 && left >= right)
|
||||
|| (other.local_branch_value > 0 && self.local_branch_value >= other.local_branch_value)
|
||||
}
|
||||
|
||||
pub fn observed_all(&self, other: &Self) -> bool {
|
||||
if self.values.len() < other.values.len() {
|
||||
return false;
|
||||
}
|
||||
self.iter()
|
||||
.zip(other.iter())
|
||||
.all(|(left, right)| left.value >= right.value)
|
||||
let mut rhs = other.values.iter();
|
||||
self.values.iter().all(|left| match rhs.next() {
|
||||
Some(right) => left >= right,
|
||||
None => true,
|
||||
}) && rhs.next().is_none()
|
||||
&& self.local_branch_value >= other.local_branch_value
|
||||
}
|
||||
|
||||
pub fn changed_since(&self, other: &Self) -> bool {
|
||||
@@ -169,21 +128,21 @@ impl Global {
|
||||
.iter()
|
||||
.zip(other.values.iter())
|
||||
.any(|(left, right)| left > right)
|
||||
|| self.local_branch_value > other.local_branch_value
|
||||
}
|
||||
|
||||
pub fn most_recent(&self) -> Option<Lamport> {
|
||||
self.iter().max_by_key(|timestamp| timestamp.value)
|
||||
}
|
||||
|
||||
/// Iterates all replicas observed by this global as well as any unobserved replicas whose ID is lower than the highest observed replica.
|
||||
pub fn iter(&self) -> impl Iterator<Item = Lamport> + '_ {
|
||||
self.values
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(replica_id, seq)| Lamport {
|
||||
replica_id: ReplicaId(replica_id as u16),
|
||||
replica_id: replica_id as ReplicaId,
|
||||
value: *seq,
|
||||
})
|
||||
.chain((self.local_branch_value > 0).then_some(Lamport {
|
||||
replica_id: LOCAL_BRANCH_REPLICA_ID,
|
||||
value: self.local_branch_value,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,12 +173,12 @@ impl PartialOrd for Lamport {
|
||||
|
||||
impl Lamport {
|
||||
pub const MIN: Self = Self {
|
||||
replica_id: ReplicaId(u16::MIN),
|
||||
replica_id: ReplicaId::MIN,
|
||||
value: Seq::MIN,
|
||||
};
|
||||
|
||||
pub const MAX: Self = Self {
|
||||
replica_id: ReplicaId(u16::MAX),
|
||||
replica_id: ReplicaId::MAX,
|
||||
value: Seq::MAX,
|
||||
};
|
||||
|
||||
@@ -231,7 +190,7 @@ impl Lamport {
|
||||
}
|
||||
|
||||
pub fn as_u64(self) -> u64 {
|
||||
((self.value as u64) << 32) | (self.replica_id.0 as u64)
|
||||
((self.value as u64) << 32) | (self.replica_id as u64)
|
||||
}
|
||||
|
||||
pub fn tick(&mut self) -> Self {
|
||||
@@ -252,7 +211,7 @@ impl fmt::Debug for Lamport {
|
||||
} else if *self == Self::MIN {
|
||||
write!(f, "Lamport {{MIN}}")
|
||||
} else {
|
||||
write!(f, "Lamport {{{:?}: {}}}", self.replica_id, self.value)
|
||||
write!(f, "Lamport {{{}: {}}}", self.replica_id, self.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -261,10 +220,16 @@ impl fmt::Debug for Global {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Global {{")?;
|
||||
for timestamp in self.iter() {
|
||||
if timestamp.replica_id.0 > 0 {
|
||||
if timestamp.replica_id > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{:?}: {}", timestamp.replica_id, timestamp.value)?;
|
||||
if timestamp.replica_id == LOCAL_BRANCH_REPLICA_ID {
|
||||
write!(f, "<branch>: {}", timestamp.value)?;
|
||||
} else if timestamp.replica_id == AGENT_REPLICA_ID {
|
||||
write!(f, "<agent>: {}", timestamp.value)?;
|
||||
} else {
|
||||
write!(f, "{}: {}", timestamp.replica_id, timestamp.value)?;
|
||||
}
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
|
||||
@@ -62,9 +62,9 @@ impl Database {
|
||||
.iter()
|
||||
.map(|c| c.replica_id)
|
||||
.collect::<HashSet<_>>();
|
||||
let mut replica_id = ReplicaId(clock::ReplicaId::FIRST_COLLAB_ID.as_u16() as i32);
|
||||
let mut replica_id = ReplicaId(0);
|
||||
while replica_ids.contains(&replica_id) {
|
||||
replica_id = ReplicaId(replica_id.0 + 1);
|
||||
replica_id.0 += 1;
|
||||
}
|
||||
let collaborator = channel_buffer_collaborator::ActiveModel {
|
||||
channel_id: ActiveValue::Set(channel_id),
|
||||
@@ -203,7 +203,7 @@ impl Database {
|
||||
while let Some(row) = rows.next().await {
|
||||
let row = row?;
|
||||
let timestamp = clock::Lamport {
|
||||
replica_id: clock::ReplicaId::new(row.replica_id as u16),
|
||||
replica_id: row.replica_id as u16,
|
||||
value: row.lamport_timestamp as u32,
|
||||
};
|
||||
server_version.observe(timestamp);
|
||||
@@ -701,11 +701,7 @@ impl Database {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut text_buffer = text::Buffer::new(
|
||||
clock::ReplicaId::LOCAL,
|
||||
text::BufferId::new(1).unwrap(),
|
||||
base_text,
|
||||
);
|
||||
let mut text_buffer = text::Buffer::new(0, text::BufferId::new(1).unwrap(), base_text);
|
||||
text_buffer.apply_ops(operations.into_iter().filter_map(operation_from_wire));
|
||||
|
||||
let base_text = text_buffer.text();
|
||||
@@ -938,7 +934,7 @@ pub fn operation_from_wire(operation: proto::Operation) -> Option<text::Operatio
|
||||
match operation.variant? {
|
||||
proto::operation::Variant::Edit(edit) => Some(text::Operation::Edit(EditOperation {
|
||||
timestamp: clock::Lamport {
|
||||
replica_id: clock::ReplicaId::new(edit.replica_id as u16),
|
||||
replica_id: edit.replica_id as text::ReplicaId,
|
||||
value: edit.lamport_timestamp,
|
||||
},
|
||||
version: version_from_wire(&edit.version),
|
||||
@@ -953,7 +949,7 @@ pub fn operation_from_wire(operation: proto::Operation) -> Option<text::Operatio
|
||||
})),
|
||||
proto::operation::Variant::Undo(undo) => Some(text::Operation::Undo(UndoOperation {
|
||||
timestamp: clock::Lamport {
|
||||
replica_id: clock::ReplicaId::new(undo.replica_id as u16),
|
||||
replica_id: undo.replica_id as text::ReplicaId,
|
||||
value: undo.lamport_timestamp,
|
||||
},
|
||||
version: version_from_wire(&undo.version),
|
||||
@@ -963,7 +959,7 @@ pub fn operation_from_wire(operation: proto::Operation) -> Option<text::Operatio
|
||||
.map(|c| {
|
||||
(
|
||||
clock::Lamport {
|
||||
replica_id: clock::ReplicaId::new(c.replica_id as u16),
|
||||
replica_id: c.replica_id as text::ReplicaId,
|
||||
value: c.lamport_timestamp,
|
||||
},
|
||||
c.count,
|
||||
@@ -979,7 +975,7 @@ fn version_from_wire(message: &[proto::VectorClockEntry]) -> clock::Global {
|
||||
let mut version = clock::Global::new();
|
||||
for entry in message {
|
||||
version.observe(clock::Lamport {
|
||||
replica_id: clock::ReplicaId::new(entry.replica_id as u16),
|
||||
replica_id: entry.replica_id as text::ReplicaId,
|
||||
value: entry.timestamp,
|
||||
});
|
||||
}
|
||||
@@ -990,7 +986,7 @@ fn version_to_wire(version: &clock::Global) -> Vec<proto::VectorClockEntry> {
|
||||
let mut message = Vec::new();
|
||||
for entry in version.iter() {
|
||||
message.push(proto::VectorClockEntry {
|
||||
replica_id: entry.replica_id.as_u16() as u32,
|
||||
replica_id: entry.replica_id as u32,
|
||||
timestamp: entry.value,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -91,18 +91,14 @@ impl Database {
|
||||
.await?;
|
||||
}
|
||||
|
||||
let replica_id = if is_ssh_project {
|
||||
clock::ReplicaId::REMOTE_SERVER
|
||||
} else {
|
||||
clock::ReplicaId::LOCAL
|
||||
};
|
||||
let replica_id = if is_ssh_project { 1 } else { 0 };
|
||||
|
||||
project_collaborator::ActiveModel {
|
||||
project_id: ActiveValue::set(project.id),
|
||||
connection_id: ActiveValue::set(connection.id as i32),
|
||||
connection_server_id: ActiveValue::set(ServerId(connection.owner_id as i32)),
|
||||
user_id: ActiveValue::set(participant.user_id),
|
||||
replica_id: ActiveValue::set(ReplicaId(replica_id.as_u16() as i32)),
|
||||
replica_id: ActiveValue::set(ReplicaId(replica_id)),
|
||||
is_host: ActiveValue::set(true),
|
||||
id: ActiveValue::NotSet,
|
||||
committer_name: ActiveValue::Set(None),
|
||||
@@ -845,7 +841,7 @@ impl Database {
|
||||
.iter()
|
||||
.map(|c| c.replica_id)
|
||||
.collect::<HashSet<_>>();
|
||||
let mut replica_id = ReplicaId(clock::ReplicaId::FIRST_COLLAB_ID.as_u16() as i32);
|
||||
let mut replica_id = ReplicaId(1);
|
||||
while replica_ids.contains(&replica_id) {
|
||||
replica_id.0 += 1;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::*;
|
||||
use crate::test_both_dbs;
|
||||
use language::proto::{self, serialize_version};
|
||||
use text::{Buffer, ReplicaId};
|
||||
use text::Buffer;
|
||||
|
||||
test_both_dbs!(
|
||||
test_channel_buffers,
|
||||
@@ -70,11 +70,7 @@ async fn test_channel_buffers(db: &Arc<Database>) {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mut buffer_a = Buffer::new(
|
||||
ReplicaId::new(0),
|
||||
text::BufferId::new(1).unwrap(),
|
||||
"".to_string(),
|
||||
);
|
||||
let mut buffer_a = Buffer::new(0, text::BufferId::new(1).unwrap(), "".to_string());
|
||||
let operations = vec![
|
||||
buffer_a.edit([(0..0, "hello world")]),
|
||||
buffer_a.edit([(5..5, ", cruel")]),
|
||||
@@ -99,7 +95,7 @@ async fn test_channel_buffers(db: &Arc<Database>) {
|
||||
.unwrap();
|
||||
|
||||
let mut buffer_b = Buffer::new(
|
||||
ReplicaId::new(0),
|
||||
0,
|
||||
text::BufferId::new(1).unwrap(),
|
||||
buffer_response_b.base_text,
|
||||
);
|
||||
@@ -128,7 +124,7 @@ async fn test_channel_buffers(db: &Arc<Database>) {
|
||||
rpc::proto::Collaborator {
|
||||
user_id: a_id.to_proto(),
|
||||
peer_id: Some(rpc::proto::PeerId { id: 1, owner_id }),
|
||||
replica_id: ReplicaId::FIRST_COLLAB_ID.as_u16() as u32,
|
||||
replica_id: 0,
|
||||
is_host: false,
|
||||
committer_name: None,
|
||||
committer_email: None,
|
||||
@@ -136,7 +132,7 @@ async fn test_channel_buffers(db: &Arc<Database>) {
|
||||
rpc::proto::Collaborator {
|
||||
user_id: b_id.to_proto(),
|
||||
peer_id: Some(rpc::proto::PeerId { id: 2, owner_id }),
|
||||
replica_id: ReplicaId::FIRST_COLLAB_ID.as_u16() as u32 + 1,
|
||||
replica_id: 1,
|
||||
is_host: false,
|
||||
committer_name: None,
|
||||
committer_email: None,
|
||||
@@ -232,8 +228,7 @@ async fn test_channel_buffers_last_operations(db: &Database) {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let res = db
|
||||
.join_channel_buffer(channel, user_id, connection_id)
|
||||
db.join_channel_buffer(channel, user_id, connection_id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -244,7 +239,7 @@ async fn test_channel_buffers_last_operations(db: &Database) {
|
||||
);
|
||||
|
||||
text_buffers.push(Buffer::new(
|
||||
ReplicaId::new(res.replica_id as u16),
|
||||
0,
|
||||
text::BufferId::new(1).unwrap(),
|
||||
"".to_string(),
|
||||
));
|
||||
@@ -281,12 +276,7 @@ async fn test_channel_buffers_last_operations(db: &Database) {
|
||||
db.join_channel_buffer(buffers[1].channel_id, user_id, connection_id)
|
||||
.await
|
||||
.unwrap();
|
||||
let replica_id = text_buffers[1].replica_id();
|
||||
text_buffers[1] = Buffer::new(
|
||||
replica_id,
|
||||
text::BufferId::new(1).unwrap(),
|
||||
"def".to_string(),
|
||||
);
|
||||
text_buffers[1] = Buffer::new(1, text::BufferId::new(1).unwrap(), "def".to_string());
|
||||
update_buffer(
|
||||
buffers[1].channel_id,
|
||||
user_id,
|
||||
@@ -314,32 +304,20 @@ async fn test_channel_buffers_last_operations(db: &Database) {
|
||||
rpc::proto::ChannelBufferVersion {
|
||||
channel_id: buffers[0].channel_id.to_proto(),
|
||||
epoch: 0,
|
||||
version: serialize_version(&text_buffers[0].version())
|
||||
.into_iter()
|
||||
.filter(
|
||||
|vector| vector.replica_id == text_buffers[0].replica_id().as_u16() as u32
|
||||
)
|
||||
.collect::<Vec<_>>(),
|
||||
version: serialize_version(&text_buffers[0].version()),
|
||||
},
|
||||
rpc::proto::ChannelBufferVersion {
|
||||
channel_id: buffers[1].channel_id.to_proto(),
|
||||
epoch: 1,
|
||||
version: serialize_version(&text_buffers[1].version())
|
||||
.into_iter()
|
||||
.filter(
|
||||
|vector| vector.replica_id == text_buffers[1].replica_id().as_u16() as u32
|
||||
)
|
||||
.filter(|vector| vector.replica_id == text_buffers[1].replica_id() as u32)
|
||||
.collect::<Vec<_>>(),
|
||||
},
|
||||
rpc::proto::ChannelBufferVersion {
|
||||
channel_id: buffers[2].channel_id.to_proto(),
|
||||
epoch: 0,
|
||||
version: serialize_version(&text_buffers[2].version())
|
||||
.into_iter()
|
||||
.filter(
|
||||
|vector| vector.replica_id == text_buffers[2].replica_id().as_u16() as u32
|
||||
)
|
||||
.collect::<Vec<_>>(),
|
||||
version: serialize_version(&text_buffers[2].version()),
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
@@ -3037,10 +3037,6 @@ impl Panel for CollabPanel {
|
||||
"CollabPanel"
|
||||
}
|
||||
|
||||
fn panel_key() -> &'static str {
|
||||
COLLABORATION_PANEL_KEY
|
||||
}
|
||||
|
||||
fn activation_priority(&self) -> u32 {
|
||||
6
|
||||
}
|
||||
|
||||
@@ -612,10 +612,6 @@ impl Panel for NotificationPanel {
|
||||
"NotificationPanel"
|
||||
}
|
||||
|
||||
fn panel_key() -> &'static str {
|
||||
NOTIFICATION_PANEL_KEY
|
||||
}
|
||||
|
||||
fn position(&self, _: &Window, cx: &App) -> DockPosition {
|
||||
NotificationPanelSettings::get_global(cx).dock
|
||||
}
|
||||
|
||||
@@ -43,8 +43,6 @@ use workspace::{
|
||||
};
|
||||
use zed_actions::ToggleFocus;
|
||||
|
||||
const DEBUG_PANEL_KEY: &str = "DebugPanel";
|
||||
|
||||
pub struct DebugPanel {
|
||||
size: Pixels,
|
||||
active_session: Option<Entity<DebugSession>>,
|
||||
@@ -1416,10 +1414,6 @@ impl Panel for DebugPanel {
|
||||
"DebugPanel"
|
||||
}
|
||||
|
||||
fn panel_key() -> &'static str {
|
||||
DEBUG_PANEL_KEY
|
||||
}
|
||||
|
||||
fn position(&self, _window: &Window, cx: &App) -> DockPosition {
|
||||
DebuggerSettings::get_global(cx).dock.into()
|
||||
}
|
||||
|
||||
@@ -386,7 +386,6 @@ pub(crate) fn new_debugger_pane(
|
||||
Default::default(),
|
||||
None,
|
||||
NoAction.boxed_clone(),
|
||||
true,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
|
||||
@@ -1341,7 +1341,7 @@ async fn test_hover_diagnostic_and_info_popovers(cx: &mut gpui::TestAppContext)
|
||||
range: Some(range),
|
||||
}))
|
||||
});
|
||||
let delay = cx.update(|_, cx| EditorSettings::get_global(cx).hover_popover_delay.0 + 1);
|
||||
let delay = cx.update(|_, cx| EditorSettings::get_global(cx).hover_popover_delay + 1);
|
||||
cx.background_executor
|
||||
.advance_clock(Duration::from_millis(delay));
|
||||
|
||||
|
||||
@@ -27,9 +27,9 @@ pub use predict_edits_v3::Line;
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct EditPredictionContextOptions {
|
||||
pub use_imports: bool,
|
||||
pub use_references: bool,
|
||||
pub excerpt: EditPredictionExcerptOptions,
|
||||
pub score: EditPredictionScoreOptions,
|
||||
pub max_retrieved_declarations: u8,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -118,7 +118,7 @@ impl EditPredictionContext {
|
||||
)?;
|
||||
let excerpt_text = excerpt.text(buffer);
|
||||
|
||||
let declarations = if options.max_retrieved_declarations > 0
|
||||
let declarations = if options.use_references
|
||||
&& let Some(index_state) = index_state
|
||||
{
|
||||
let excerpt_occurrences =
|
||||
@@ -136,7 +136,7 @@ impl EditPredictionContext {
|
||||
|
||||
let references = get_references(&excerpt, &excerpt_text, buffer);
|
||||
|
||||
let mut declarations = scored_declarations(
|
||||
scored_declarations(
|
||||
&options.score,
|
||||
&index_state,
|
||||
&excerpt,
|
||||
@@ -146,10 +146,7 @@ impl EditPredictionContext {
|
||||
references,
|
||||
cursor_offset_in_file,
|
||||
buffer,
|
||||
);
|
||||
// TODO [zeta2] if we need this when we ship, we should probably do it in a smarter way
|
||||
declarations.truncate(options.max_retrieved_declarations as usize);
|
||||
declarations
|
||||
)
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
@@ -203,6 +200,7 @@ mod tests {
|
||||
buffer_snapshot,
|
||||
EditPredictionContextOptions {
|
||||
use_imports: true,
|
||||
use_references: true,
|
||||
excerpt: EditPredictionExcerptOptions {
|
||||
max_bytes: 60,
|
||||
min_bytes: 10,
|
||||
@@ -211,7 +209,6 @@ mod tests {
|
||||
score: EditPredictionScoreOptions {
|
||||
omit_excerpt_overlaps: true,
|
||||
},
|
||||
max_retrieved_declarations: u8::MAX,
|
||||
},
|
||||
Some(index.clone()),
|
||||
cx,
|
||||
|
||||
@@ -458,8 +458,6 @@ actions!(
|
||||
/// Expands all diff hunks in the editor.
|
||||
#[action(deprecated_aliases = ["editor::ExpandAllHunkDiffs"])]
|
||||
ExpandAllDiffHunks,
|
||||
/// Collapses all diff hunks in the editor.
|
||||
CollapseAllDiffHunks,
|
||||
/// Expands macros recursively at cursor position.
|
||||
ExpandMacroRecursively,
|
||||
/// Finds all references to the symbol at cursor.
|
||||
|
||||
@@ -594,11 +594,7 @@ impl DisplayMap {
|
||||
self.block_map.read(snapshot, edits);
|
||||
}
|
||||
|
||||
pub fn remove_inlays_for_excerpts(
|
||||
&mut self,
|
||||
excerpts_removed: &[ExcerptId],
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
pub fn remove_inlays_for_excerpts(&mut self, excerpts_removed: &[ExcerptId]) {
|
||||
let to_remove = self
|
||||
.inlay_map
|
||||
.current_inlays()
|
||||
@@ -610,7 +606,7 @@ impl DisplayMap {
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
self.splice_inlays(&to_remove, Vec::new(), cx);
|
||||
self.inlay_map.splice(&to_remove, Vec::new());
|
||||
}
|
||||
|
||||
fn tab_size(buffer: &Entity<MultiBuffer>, cx: &App) -> NonZeroU32 {
|
||||
|
||||
@@ -1521,11 +1521,10 @@ impl BlockSnapshot {
|
||||
}
|
||||
|
||||
pub(super) fn line_len(&self, row: BlockRow) -> u32 {
|
||||
let (start, _, item) =
|
||||
self.transforms
|
||||
.find::<Dimensions<BlockRow, WrapRow>, _>((), &row, Bias::Right);
|
||||
if let Some(transform) = item {
|
||||
let Dimensions(output_start, input_start, _) = start;
|
||||
let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
|
||||
cursor.seek(&BlockRow(row.0), Bias::Right);
|
||||
if let Some(transform) = cursor.item() {
|
||||
let Dimensions(output_start, input_start, _) = cursor.start();
|
||||
let overshoot = row.0 - output_start.0;
|
||||
if transform.block.is_some() {
|
||||
0
|
||||
@@ -1540,13 +1539,15 @@ impl BlockSnapshot {
|
||||
}
|
||||
|
||||
pub(super) fn is_block_line(&self, row: BlockRow) -> bool {
|
||||
let (_, _, item) = self.transforms.find::<BlockRow, _>((), &row, Bias::Right);
|
||||
item.is_some_and(|t| t.block.is_some())
|
||||
let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
|
||||
cursor.seek(&row, Bias::Right);
|
||||
cursor.item().is_some_and(|t| t.block.is_some())
|
||||
}
|
||||
|
||||
pub(super) fn is_folded_buffer_header(&self, row: BlockRow) -> bool {
|
||||
let (_, _, item) = self.transforms.find::<BlockRow, _>((), &row, Bias::Right);
|
||||
let Some(transform) = item else {
|
||||
let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
|
||||
cursor.seek(&row, Bias::Right);
|
||||
let Some(transform) = cursor.item() else {
|
||||
return false;
|
||||
};
|
||||
matches!(transform.block, Some(Block::FoldedBuffer { .. }))
|
||||
@@ -1556,10 +1557,9 @@ impl BlockSnapshot {
|
||||
let wrap_point = self
|
||||
.wrap_snapshot
|
||||
.make_wrap_point(Point::new(row.0, 0), Bias::Left);
|
||||
let (_, _, item) =
|
||||
self.transforms
|
||||
.find::<WrapRow, _>((), &WrapRow(wrap_point.row()), Bias::Right);
|
||||
item.is_some_and(|transform| {
|
||||
let mut cursor = self.transforms.cursor::<Dimensions<WrapRow, BlockRow>>(());
|
||||
cursor.seek(&WrapRow(wrap_point.row()), Bias::Right);
|
||||
cursor.item().is_some_and(|transform| {
|
||||
transform
|
||||
.block
|
||||
.as_ref()
|
||||
@@ -1627,16 +1627,13 @@ impl BlockSnapshot {
|
||||
}
|
||||
|
||||
pub fn to_block_point(&self, wrap_point: WrapPoint) -> BlockPoint {
|
||||
let (start, _, item) = self.transforms.find::<Dimensions<WrapRow, BlockRow>, _>(
|
||||
(),
|
||||
&WrapRow(wrap_point.row()),
|
||||
Bias::Right,
|
||||
);
|
||||
if let Some(transform) = item {
|
||||
let mut cursor = self.transforms.cursor::<Dimensions<WrapRow, BlockRow>>(());
|
||||
cursor.seek(&WrapRow(wrap_point.row()), Bias::Right);
|
||||
if let Some(transform) = cursor.item() {
|
||||
if transform.block.is_some() {
|
||||
BlockPoint::new(start.1.0, 0)
|
||||
BlockPoint::new(cursor.start().1.0, 0)
|
||||
} else {
|
||||
let Dimensions(input_start_row, output_start_row, _) = start;
|
||||
let Dimensions(input_start_row, output_start_row, _) = cursor.start();
|
||||
let input_start = Point::new(input_start_row.0, 0);
|
||||
let output_start = Point::new(output_start_row.0, 0);
|
||||
let input_overshoot = wrap_point.0 - input_start;
|
||||
@@ -1648,29 +1645,26 @@ impl BlockSnapshot {
|
||||
}
|
||||
|
||||
pub fn to_wrap_point(&self, block_point: BlockPoint, bias: Bias) -> WrapPoint {
|
||||
let (start, end, item) = self.transforms.find::<Dimensions<BlockRow, WrapRow>, _>(
|
||||
(),
|
||||
&BlockRow(block_point.row),
|
||||
Bias::Right,
|
||||
);
|
||||
if let Some(transform) = item {
|
||||
let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
|
||||
cursor.seek(&BlockRow(block_point.row), Bias::Right);
|
||||
if let Some(transform) = cursor.item() {
|
||||
match transform.block.as_ref() {
|
||||
Some(block) => {
|
||||
if block.place_below() {
|
||||
let wrap_row = start.1.0 - 1;
|
||||
let wrap_row = cursor.start().1.0 - 1;
|
||||
WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
|
||||
} else if block.place_above() {
|
||||
WrapPoint::new(start.1.0, 0)
|
||||
WrapPoint::new(cursor.start().1.0, 0)
|
||||
} else if bias == Bias::Left {
|
||||
WrapPoint::new(start.1.0, 0)
|
||||
WrapPoint::new(cursor.start().1.0, 0)
|
||||
} else {
|
||||
let wrap_row = end.1.0 - 1;
|
||||
let wrap_row = cursor.end().1.0 - 1;
|
||||
WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let overshoot = block_point.row - start.0.0;
|
||||
let wrap_row = start.1.0 + overshoot;
|
||||
let overshoot = block_point.row - cursor.start().0.0;
|
||||
let wrap_row = cursor.start().1.0 + overshoot;
|
||||
WrapPoint::new(wrap_row, block_point.column)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,26 +98,28 @@ impl FoldPoint {
|
||||
}
|
||||
|
||||
pub fn to_inlay_point(self, snapshot: &FoldSnapshot) -> InlayPoint {
|
||||
let (start, _, _) = snapshot
|
||||
let mut cursor = snapshot
|
||||
.transforms
|
||||
.find::<Dimensions<FoldPoint, InlayPoint>, _>((), &self, Bias::Right);
|
||||
let overshoot = self.0 - start.0.0;
|
||||
InlayPoint(start.1.0 + overshoot)
|
||||
.cursor::<Dimensions<FoldPoint, InlayPoint>>(());
|
||||
cursor.seek(&self, Bias::Right);
|
||||
let overshoot = self.0 - cursor.start().0.0;
|
||||
InlayPoint(cursor.start().1.0 + overshoot)
|
||||
}
|
||||
|
||||
pub fn to_offset(self, snapshot: &FoldSnapshot) -> FoldOffset {
|
||||
let (start, _, item) = snapshot
|
||||
let mut cursor = snapshot
|
||||
.transforms
|
||||
.find::<Dimensions<FoldPoint, TransformSummary>, _>((), &self, Bias::Right);
|
||||
let overshoot = self.0 - start.1.output.lines;
|
||||
let mut offset = start.1.output.len;
|
||||
.cursor::<Dimensions<FoldPoint, TransformSummary>>(());
|
||||
cursor.seek(&self, Bias::Right);
|
||||
let overshoot = self.0 - cursor.start().1.output.lines;
|
||||
let mut offset = cursor.start().1.output.len;
|
||||
if !overshoot.is_zero() {
|
||||
let transform = item.expect("display point out of range");
|
||||
let transform = cursor.item().expect("display point out of range");
|
||||
assert!(transform.placeholder.is_none());
|
||||
let end_inlay_offset = snapshot
|
||||
.inlay_snapshot
|
||||
.to_offset(InlayPoint(start.1.input.lines + overshoot));
|
||||
offset += end_inlay_offset.0 - start.1.input.len;
|
||||
.to_offset(InlayPoint(cursor.start().1.input.lines + overshoot));
|
||||
offset += end_inlay_offset.0 - cursor.start().1.input.len;
|
||||
}
|
||||
FoldOffset(offset)
|
||||
}
|
||||
@@ -704,18 +706,19 @@ impl FoldSnapshot {
|
||||
}
|
||||
|
||||
pub fn to_fold_point(&self, point: InlayPoint, bias: Bias) -> FoldPoint {
|
||||
let (start, end, item) = self
|
||||
let mut cursor = self
|
||||
.transforms
|
||||
.find::<Dimensions<InlayPoint, FoldPoint>, _>((), &point, Bias::Right);
|
||||
if item.is_some_and(|t| t.is_fold()) {
|
||||
if bias == Bias::Left || point == start.0 {
|
||||
start.1
|
||||
.cursor::<Dimensions<InlayPoint, FoldPoint>>(());
|
||||
cursor.seek(&point, Bias::Right);
|
||||
if cursor.item().is_some_and(|t| t.is_fold()) {
|
||||
if bias == Bias::Left || point == cursor.start().0 {
|
||||
cursor.start().1
|
||||
} else {
|
||||
end.1
|
||||
cursor.end().1
|
||||
}
|
||||
} else {
|
||||
let overshoot = point.0 - start.0.0;
|
||||
FoldPoint(cmp::min(start.1.0 + overshoot, end.1.0))
|
||||
let overshoot = point.0 - cursor.start().0.0;
|
||||
FoldPoint(cmp::min(cursor.start().1.0 + overshoot, cursor.end().1.0))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -784,10 +787,9 @@ impl FoldSnapshot {
|
||||
{
|
||||
let buffer_offset = offset.to_offset(&self.inlay_snapshot.buffer);
|
||||
let inlay_offset = self.inlay_snapshot.to_inlay_offset(buffer_offset);
|
||||
let (_, _, item) = self
|
||||
.transforms
|
||||
.find::<InlayOffset, _>((), &inlay_offset, Bias::Right);
|
||||
item.is_some_and(|t| t.placeholder.is_some())
|
||||
let mut cursor = self.transforms.cursor::<InlayOffset>(());
|
||||
cursor.seek(&inlay_offset, Bias::Right);
|
||||
cursor.item().is_some_and(|t| t.placeholder.is_some())
|
||||
}
|
||||
|
||||
pub fn is_line_folded(&self, buffer_row: MultiBufferRow) -> bool {
|
||||
@@ -889,22 +891,23 @@ impl FoldSnapshot {
|
||||
}
|
||||
|
||||
pub fn clip_point(&self, point: FoldPoint, bias: Bias) -> FoldPoint {
|
||||
let (start, end, item) = self
|
||||
let mut cursor = self
|
||||
.transforms
|
||||
.find::<Dimensions<FoldPoint, InlayPoint>, _>((), &point, Bias::Right);
|
||||
if let Some(transform) = item {
|
||||
let transform_start = start.0.0;
|
||||
.cursor::<Dimensions<FoldPoint, InlayPoint>>(());
|
||||
cursor.seek(&point, Bias::Right);
|
||||
if let Some(transform) = cursor.item() {
|
||||
let transform_start = cursor.start().0.0;
|
||||
if transform.placeholder.is_some() {
|
||||
if point.0 == transform_start || matches!(bias, Bias::Left) {
|
||||
FoldPoint(transform_start)
|
||||
} else {
|
||||
FoldPoint(end.0.0)
|
||||
FoldPoint(cursor.end().0.0)
|
||||
}
|
||||
} else {
|
||||
let overshoot = InlayPoint(point.0 - transform_start);
|
||||
let inlay_point = start.1 + overshoot;
|
||||
let inlay_point = cursor.start().1 + overshoot;
|
||||
let clipped_inlay_point = self.inlay_snapshot.clip_point(inlay_point, bias);
|
||||
FoldPoint(start.0.0 + (clipped_inlay_point - start.1).0)
|
||||
FoldPoint(cursor.start().0.0 + (clipped_inlay_point - cursor.start().1).0)
|
||||
}
|
||||
} else {
|
||||
FoldPoint(self.transforms.summary().output.lines)
|
||||
@@ -1477,26 +1480,28 @@ pub struct FoldOffset(pub usize);
|
||||
|
||||
impl FoldOffset {
|
||||
pub fn to_point(self, snapshot: &FoldSnapshot) -> FoldPoint {
|
||||
let (start, _, item) = snapshot
|
||||
let mut cursor = snapshot
|
||||
.transforms
|
||||
.find::<Dimensions<FoldOffset, TransformSummary>, _>((), &self, Bias::Right);
|
||||
let overshoot = if item.is_none_or(|t| t.is_fold()) {
|
||||
Point::new(0, (self.0 - start.0.0) as u32)
|
||||
.cursor::<Dimensions<FoldOffset, TransformSummary>>(());
|
||||
cursor.seek(&self, Bias::Right);
|
||||
let overshoot = if cursor.item().is_none_or(|t| t.is_fold()) {
|
||||
Point::new(0, (self.0 - cursor.start().0.0) as u32)
|
||||
} else {
|
||||
let inlay_offset = start.1.input.len + self.0 - start.0.0;
|
||||
let inlay_offset = cursor.start().1.input.len + self.0 - cursor.start().0.0;
|
||||
let inlay_point = snapshot.inlay_snapshot.to_point(InlayOffset(inlay_offset));
|
||||
inlay_point.0 - start.1.input.lines
|
||||
inlay_point.0 - cursor.start().1.input.lines
|
||||
};
|
||||
FoldPoint(start.1.output.lines + overshoot)
|
||||
FoldPoint(cursor.start().1.output.lines + overshoot)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn to_inlay_offset(self, snapshot: &FoldSnapshot) -> InlayOffset {
|
||||
let (start, _, _) = snapshot
|
||||
let mut cursor = snapshot
|
||||
.transforms
|
||||
.find::<Dimensions<FoldOffset, InlayOffset>, _>((), &self, Bias::Right);
|
||||
let overshoot = self.0 - start.0.0;
|
||||
InlayOffset(start.1.0 + overshoot)
|
||||
.cursor::<Dimensions<FoldOffset, InlayOffset>>(());
|
||||
cursor.seek(&self, Bias::Right);
|
||||
let overshoot = self.0 - cursor.start().0.0;
|
||||
InlayOffset(cursor.start().1.0 + overshoot)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -825,21 +825,22 @@ impl InlayMap {
|
||||
|
||||
impl InlaySnapshot {
|
||||
pub fn to_point(&self, offset: InlayOffset) -> InlayPoint {
|
||||
let (start, _, item) = self
|
||||
let mut cursor = self
|
||||
.transforms
|
||||
.find::<Dimensions<InlayOffset, InlayPoint, usize>, _>((), &offset, Bias::Right);
|
||||
let overshoot = offset.0 - start.0.0;
|
||||
match item {
|
||||
.cursor::<Dimensions<InlayOffset, InlayPoint, usize>>(());
|
||||
cursor.seek(&offset, Bias::Right);
|
||||
let overshoot = offset.0 - cursor.start().0.0;
|
||||
match cursor.item() {
|
||||
Some(Transform::Isomorphic(_)) => {
|
||||
let buffer_offset_start = start.2;
|
||||
let buffer_offset_start = cursor.start().2;
|
||||
let buffer_offset_end = buffer_offset_start + overshoot;
|
||||
let buffer_start = self.buffer.offset_to_point(buffer_offset_start);
|
||||
let buffer_end = self.buffer.offset_to_point(buffer_offset_end);
|
||||
InlayPoint(start.1.0 + (buffer_end - buffer_start))
|
||||
InlayPoint(cursor.start().1.0 + (buffer_end - buffer_start))
|
||||
}
|
||||
Some(Transform::Inlay(inlay)) => {
|
||||
let overshoot = inlay.text().offset_to_point(overshoot);
|
||||
InlayPoint(start.1.0 + overshoot)
|
||||
InlayPoint(cursor.start().1.0 + overshoot)
|
||||
}
|
||||
None => self.max_point(),
|
||||
}
|
||||
@@ -854,48 +855,47 @@ impl InlaySnapshot {
|
||||
}
|
||||
|
||||
pub fn to_offset(&self, point: InlayPoint) -> InlayOffset {
|
||||
let (start, _, item) = self
|
||||
let mut cursor = self
|
||||
.transforms
|
||||
.find::<Dimensions<InlayPoint, InlayOffset, Point>, _>((), &point, Bias::Right);
|
||||
let overshoot = point.0 - start.0.0;
|
||||
match item {
|
||||
.cursor::<Dimensions<InlayPoint, InlayOffset, Point>>(());
|
||||
cursor.seek(&point, Bias::Right);
|
||||
let overshoot = point.0 - cursor.start().0.0;
|
||||
match cursor.item() {
|
||||
Some(Transform::Isomorphic(_)) => {
|
||||
let buffer_point_start = start.2;
|
||||
let buffer_point_start = cursor.start().2;
|
||||
let buffer_point_end = buffer_point_start + overshoot;
|
||||
let buffer_offset_start = self.buffer.point_to_offset(buffer_point_start);
|
||||
let buffer_offset_end = self.buffer.point_to_offset(buffer_point_end);
|
||||
InlayOffset(start.1.0 + (buffer_offset_end - buffer_offset_start))
|
||||
InlayOffset(cursor.start().1.0 + (buffer_offset_end - buffer_offset_start))
|
||||
}
|
||||
Some(Transform::Inlay(inlay)) => {
|
||||
let overshoot = inlay.text().point_to_offset(overshoot);
|
||||
InlayOffset(start.1.0 + overshoot)
|
||||
InlayOffset(cursor.start().1.0 + overshoot)
|
||||
}
|
||||
None => self.len(),
|
||||
}
|
||||
}
|
||||
pub fn to_buffer_point(&self, point: InlayPoint) -> Point {
|
||||
let (start, _, item) =
|
||||
self.transforms
|
||||
.find::<Dimensions<InlayPoint, Point>, _>((), &point, Bias::Right);
|
||||
match item {
|
||||
let mut cursor = self.transforms.cursor::<Dimensions<InlayPoint, Point>>(());
|
||||
cursor.seek(&point, Bias::Right);
|
||||
match cursor.item() {
|
||||
Some(Transform::Isomorphic(_)) => {
|
||||
let overshoot = point.0 - start.0.0;
|
||||
start.1 + overshoot
|
||||
let overshoot = point.0 - cursor.start().0.0;
|
||||
cursor.start().1 + overshoot
|
||||
}
|
||||
Some(Transform::Inlay(_)) => start.1,
|
||||
Some(Transform::Inlay(_)) => cursor.start().1,
|
||||
None => self.buffer.max_point(),
|
||||
}
|
||||
}
|
||||
pub fn to_buffer_offset(&self, offset: InlayOffset) -> usize {
|
||||
let (start, _, item) =
|
||||
self.transforms
|
||||
.find::<Dimensions<InlayOffset, usize>, _>((), &offset, Bias::Right);
|
||||
match item {
|
||||
let mut cursor = self.transforms.cursor::<Dimensions<InlayOffset, usize>>(());
|
||||
cursor.seek(&offset, Bias::Right);
|
||||
match cursor.item() {
|
||||
Some(Transform::Isomorphic(_)) => {
|
||||
let overshoot = offset - start.0;
|
||||
start.1 + overshoot.0
|
||||
let overshoot = offset - cursor.start().0;
|
||||
cursor.start().1 + overshoot.0
|
||||
}
|
||||
Some(Transform::Inlay(_)) => start.1,
|
||||
Some(Transform::Inlay(_)) => cursor.start().1,
|
||||
None => self.buffer.len(),
|
||||
}
|
||||
}
|
||||
@@ -1278,7 +1278,7 @@ mod tests {
|
||||
Anchor::min(),
|
||||
&InlayHint {
|
||||
label: InlayHintLabel::String("a".to_string()),
|
||||
position: text::Anchor::MIN,
|
||||
position: text::Anchor::default(),
|
||||
padding_left: false,
|
||||
padding_right: false,
|
||||
tooltip: None,
|
||||
@@ -1298,7 +1298,7 @@ mod tests {
|
||||
Anchor::min(),
|
||||
&InlayHint {
|
||||
label: InlayHintLabel::String("a".to_string()),
|
||||
position: text::Anchor::MIN,
|
||||
position: text::Anchor::default(),
|
||||
padding_left: true,
|
||||
padding_right: true,
|
||||
tooltip: None,
|
||||
@@ -1318,7 +1318,7 @@ mod tests {
|
||||
Anchor::min(),
|
||||
&InlayHint {
|
||||
label: InlayHintLabel::String(" a ".to_string()),
|
||||
position: text::Anchor::MIN,
|
||||
position: text::Anchor::default(),
|
||||
padding_left: false,
|
||||
padding_right: false,
|
||||
tooltip: None,
|
||||
@@ -1338,7 +1338,7 @@ mod tests {
|
||||
Anchor::min(),
|
||||
&InlayHint {
|
||||
label: InlayHintLabel::String(" a ".to_string()),
|
||||
position: text::Anchor::MIN,
|
||||
position: text::Anchor::default(),
|
||||
padding_left: true,
|
||||
padding_right: true,
|
||||
tooltip: None,
|
||||
@@ -1361,7 +1361,7 @@ mod tests {
|
||||
Anchor::min(),
|
||||
&InlayHint {
|
||||
label: InlayHintLabel::String("🎨".to_string()),
|
||||
position: text::Anchor::MIN,
|
||||
position: text::Anchor::default(),
|
||||
padding_left: true,
|
||||
padding_right: true,
|
||||
tooltip: None,
|
||||
|
||||
@@ -568,17 +568,14 @@ impl WrapSnapshot {
|
||||
let mut old_start = old_cursor.start().output.lines;
|
||||
old_start += tab_edit.old.start.0 - old_cursor.start().input.lines;
|
||||
|
||||
// todo(lw): Should these be seek_forward?
|
||||
old_cursor.seek(&tab_edit.old.end, Bias::Right);
|
||||
let mut old_end = old_cursor.start().output.lines;
|
||||
old_end += tab_edit.old.end.0 - old_cursor.start().input.lines;
|
||||
|
||||
// todo(lw): Should these be seek_forward?
|
||||
new_cursor.seek(&tab_edit.new.start, Bias::Right);
|
||||
let mut new_start = new_cursor.start().output.lines;
|
||||
new_start += tab_edit.new.start.0 - new_cursor.start().input.lines;
|
||||
|
||||
// todo(lw): Should these be seek_forward?
|
||||
new_cursor.seek(&tab_edit.new.end, Bias::Right);
|
||||
let mut new_end = new_cursor.start().output.lines;
|
||||
new_end += tab_edit.new.end.0 - new_cursor.start().input.lines;
|
||||
@@ -631,22 +628,24 @@ impl WrapSnapshot {
|
||||
}
|
||||
|
||||
pub fn line_len(&self, row: u32) -> u32 {
|
||||
let (start, _, item) = self.transforms.find::<Dimensions<WrapPoint, TabPoint>, _>(
|
||||
(),
|
||||
&WrapPoint::new(row + 1, 0),
|
||||
Bias::Left,
|
||||
);
|
||||
if item.is_some_and(|transform| transform.is_isomorphic()) {
|
||||
let overshoot = row - start.0.row();
|
||||
let tab_row = start.1.row() + overshoot;
|
||||
let mut cursor = self
|
||||
.transforms
|
||||
.cursor::<Dimensions<WrapPoint, TabPoint>>(());
|
||||
cursor.seek(&WrapPoint::new(row + 1, 0), Bias::Left);
|
||||
if cursor
|
||||
.item()
|
||||
.is_some_and(|transform| transform.is_isomorphic())
|
||||
{
|
||||
let overshoot = row - cursor.start().0.row();
|
||||
let tab_row = cursor.start().1.row() + overshoot;
|
||||
let tab_line_len = self.tab_snapshot.line_len(tab_row);
|
||||
if overshoot == 0 {
|
||||
start.0.column() + (tab_line_len - start.1.column())
|
||||
cursor.start().0.column() + (tab_line_len - cursor.start().1.column())
|
||||
} else {
|
||||
tab_line_len
|
||||
}
|
||||
} else {
|
||||
start.0.column()
|
||||
cursor.start().0.column()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -712,10 +711,9 @@ impl WrapSnapshot {
|
||||
}
|
||||
|
||||
pub fn soft_wrap_indent(&self, row: u32) -> Option<u32> {
|
||||
let (.., item) =
|
||||
self.transforms
|
||||
.find::<WrapPoint, _>((), &WrapPoint::new(row + 1, 0), Bias::Right);
|
||||
item.and_then(|transform| {
|
||||
let mut cursor = self.transforms.cursor::<WrapPoint>(());
|
||||
cursor.seek(&WrapPoint::new(row + 1, 0), Bias::Right);
|
||||
cursor.item().and_then(|transform| {
|
||||
if transform.is_isomorphic() {
|
||||
None
|
||||
} else {
|
||||
@@ -751,12 +749,13 @@ impl WrapSnapshot {
|
||||
}
|
||||
|
||||
pub fn to_tab_point(&self, point: WrapPoint) -> TabPoint {
|
||||
let (start, _, item) =
|
||||
self.transforms
|
||||
.find::<Dimensions<WrapPoint, TabPoint>, _>((), &point, Bias::Right);
|
||||
let mut tab_point = start.1.0;
|
||||
if item.is_some_and(|t| t.is_isomorphic()) {
|
||||
tab_point += point.0 - start.0.0;
|
||||
let mut cursor = self
|
||||
.transforms
|
||||
.cursor::<Dimensions<WrapPoint, TabPoint>>(());
|
||||
cursor.seek(&point, Bias::Right);
|
||||
let mut tab_point = cursor.start().1.0;
|
||||
if cursor.item().is_some_and(|t| t.is_isomorphic()) {
|
||||
tab_point += point.0 - cursor.start().0.0;
|
||||
}
|
||||
TabPoint(tab_point)
|
||||
}
|
||||
@@ -770,19 +769,19 @@ impl WrapSnapshot {
|
||||
}
|
||||
|
||||
pub fn tab_point_to_wrap_point(&self, point: TabPoint) -> WrapPoint {
|
||||
let (start, ..) =
|
||||
self.transforms
|
||||
.find::<Dimensions<TabPoint, WrapPoint>, _>((), &point, Bias::Right);
|
||||
WrapPoint(start.1.0 + (point.0 - start.0.0))
|
||||
let mut cursor = self
|
||||
.transforms
|
||||
.cursor::<Dimensions<TabPoint, WrapPoint>>(());
|
||||
cursor.seek(&point, Bias::Right);
|
||||
WrapPoint(cursor.start().1.0 + (point.0 - cursor.start().0.0))
|
||||
}
|
||||
|
||||
pub fn clip_point(&self, mut point: WrapPoint, bias: Bias) -> WrapPoint {
|
||||
if bias == Bias::Left {
|
||||
let (start, _, item) = self
|
||||
.transforms
|
||||
.find::<WrapPoint, _>((), &point, Bias::Right);
|
||||
if item.is_some_and(|t| !t.is_isomorphic()) {
|
||||
point = start;
|
||||
let mut cursor = self.transforms.cursor::<WrapPoint>(());
|
||||
cursor.seek(&point, Bias::Right);
|
||||
if cursor.item().is_some_and(|t| !t.is_isomorphic()) {
|
||||
point = *cursor.start();
|
||||
*point.column_mut() -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ use anyhow::{Context as _, Result, anyhow};
|
||||
use blink_manager::BlinkManager;
|
||||
use buffer_diff::DiffHunkStatus;
|
||||
use client::{Collaborator, ParticipantIndex, parse_zed_link};
|
||||
use clock::ReplicaId;
|
||||
use clock::{AGENT_REPLICA_ID, ReplicaId};
|
||||
use code_context_menus::{
|
||||
AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
|
||||
CompletionsMenu, ContextMenuOrigin,
|
||||
@@ -358,7 +358,6 @@ pub fn init(cx: &mut App) {
|
||||
cx.observe_new(
|
||||
|workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
|
||||
workspace.register_action(Editor::new_file);
|
||||
workspace.register_action(Editor::new_file_split);
|
||||
workspace.register_action(Editor::new_file_vertical);
|
||||
workspace.register_action(Editor::new_file_horizontal);
|
||||
workspace.register_action(Editor::cancel_language_server_work);
|
||||
@@ -1302,7 +1301,7 @@ enum SelectionHistoryMode {
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
struct HoveredCursor {
|
||||
replica_id: ReplicaId,
|
||||
replica_id: u16,
|
||||
selection_id: usize,
|
||||
}
|
||||
|
||||
@@ -2466,15 +2465,15 @@ impl Editor {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn key_context(&self, window: &mut Window, cx: &mut App) -> KeyContext {
|
||||
pub fn key_context(&self, window: &Window, cx: &App) -> KeyContext {
|
||||
self.key_context_internal(self.has_active_edit_prediction(), window, cx)
|
||||
}
|
||||
|
||||
fn key_context_internal(
|
||||
&self,
|
||||
has_active_edit_prediction: bool,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
window: &Window,
|
||||
cx: &App,
|
||||
) -> KeyContext {
|
||||
let mut key_context = KeyContext::new_with_defaults();
|
||||
key_context.add("Editor");
|
||||
@@ -2551,17 +2550,6 @@ impl Editor {
|
||||
key_context.add("selection_mode");
|
||||
}
|
||||
|
||||
let disjoint = self.selections.disjoint_anchors();
|
||||
let snapshot = self.snapshot(window, cx);
|
||||
let snapshot = snapshot.buffer_snapshot();
|
||||
if self.mode == EditorMode::SingleLine
|
||||
&& let [selection] = disjoint
|
||||
&& selection.start == selection.end
|
||||
&& selection.end.to_offset(snapshot) == snapshot.len()
|
||||
{
|
||||
key_context.add("end_of_input");
|
||||
}
|
||||
|
||||
key_context
|
||||
}
|
||||
|
||||
@@ -2615,8 +2603,8 @@ impl Editor {
|
||||
pub fn accept_edit_prediction_keybind(
|
||||
&self,
|
||||
accept_partial: bool,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
window: &Window,
|
||||
cx: &App,
|
||||
) -> AcceptEditPredictionBinding {
|
||||
let key_context = self.key_context_internal(true, window, cx);
|
||||
let in_conflict = self.edit_prediction_in_conflict();
|
||||
@@ -2695,15 +2683,6 @@ impl Editor {
|
||||
Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
|
||||
}
|
||||
|
||||
fn new_file_split(
|
||||
workspace: &mut Workspace,
|
||||
action: &workspace::NewFileSplit,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Workspace>,
|
||||
) {
|
||||
Self::new_file_in_direction(workspace, action.0, window, cx)
|
||||
}
|
||||
|
||||
fn new_file_in_direction(
|
||||
workspace: &mut Workspace,
|
||||
direction: SplitDirection,
|
||||
@@ -2758,7 +2737,7 @@ impl Editor {
|
||||
self.buffer().read(cx).title(cx)
|
||||
}
|
||||
|
||||
pub fn snapshot(&self, window: &Window, cx: &mut App) -> EditorSnapshot {
|
||||
pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
|
||||
let git_blame_gutter_max_author_length = self
|
||||
.render_git_blame_gutter(cx)
|
||||
.then(|| {
|
||||
@@ -5309,8 +5288,8 @@ impl Editor {
|
||||
{
|
||||
self.splice_inlays(&to_remove, to_insert, cx);
|
||||
}
|
||||
self.display_map.update(cx, |display_map, cx| {
|
||||
display_map.remove_inlays_for_excerpts(&excerpts_removed, cx)
|
||||
self.display_map.update(cx, |display_map, _| {
|
||||
display_map.remove_inlays_for_excerpts(&excerpts_removed)
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -6793,7 +6772,7 @@ impl Editor {
|
||||
if let Some(state) = &mut self.inline_blame_popover {
|
||||
state.hide_task.take();
|
||||
} else {
|
||||
let blame_popover_delay = EditorSettings::get_global(cx).hover_popover_delay.0;
|
||||
let blame_popover_delay = EditorSettings::get_global(cx).hover_popover_delay;
|
||||
let blame_entry = blame_entry.clone();
|
||||
let show_task = cx.spawn(async move |editor, cx| {
|
||||
if !ignore_timeout {
|
||||
@@ -6884,7 +6863,7 @@ impl Editor {
|
||||
return None;
|
||||
}
|
||||
|
||||
let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce.0;
|
||||
let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
|
||||
self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
|
||||
cx.background_executor()
|
||||
.timer(Duration::from_millis(debounce))
|
||||
@@ -9296,7 +9275,7 @@ impl Editor {
|
||||
fn render_edit_prediction_accept_keybind(
|
||||
&self,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
cx: &App,
|
||||
) -> Option<AnyElement> {
|
||||
let accept_binding = self.accept_edit_prediction_keybind(false, window, cx);
|
||||
let accept_keystroke = accept_binding.keystroke()?;
|
||||
@@ -9342,7 +9321,7 @@ impl Editor {
|
||||
label: impl Into<SharedString>,
|
||||
icon: Option<IconName>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
cx: &App,
|
||||
) -> Stateful<Div> {
|
||||
let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
|
||||
|
||||
@@ -18803,17 +18782,6 @@ impl Editor {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn collapse_all_diff_hunks(
|
||||
&mut self,
|
||||
_: &CollapseAllDiffHunks,
|
||||
_window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.buffer.update(cx, |buffer, cx| {
|
||||
buffer.collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
|
||||
});
|
||||
}
|
||||
|
||||
pub fn toggle_selected_diff_hunks(
|
||||
&mut self,
|
||||
_: &ToggleSelectedDiffHunks,
|
||||
@@ -21062,7 +21030,6 @@ impl Editor {
|
||||
}
|
||||
multi_buffer::Event::ExcerptsExpanded { ids } => {
|
||||
self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
|
||||
self.refresh_document_highlights(cx);
|
||||
cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
|
||||
}
|
||||
multi_buffer::Event::Reparsed(buffer_id) => {
|
||||
@@ -23515,7 +23482,7 @@ impl EditorSnapshot {
|
||||
self.buffer_snapshot()
|
||||
.selections_in_range(range, false)
|
||||
.filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
|
||||
if replica_id == ReplicaId::AGENT {
|
||||
if replica_id == AGENT_REPLICA_ID {
|
||||
Some(RemoteSelection {
|
||||
replica_id,
|
||||
selection,
|
||||
|
||||
@@ -5,7 +5,7 @@ use language::CursorShape;
|
||||
use project::project_settings::DiagnosticSeverity;
|
||||
use settings::Settings;
|
||||
pub use settings::{
|
||||
CurrentLineHighlight, DelayMs, DisplayIn, DocumentColorsRenderMode, DoubleClickInMultibuffer,
|
||||
CurrentLineHighlight, DisplayIn, DocumentColorsRenderMode, DoubleClickInMultibuffer,
|
||||
GoToDefinitionFallback, HideMouseMode, MinimapThumb, MinimapThumbBorder, MultiCursorModifier,
|
||||
ScrollBeyondLastLine, ScrollbarDiagnostics, SeedQuerySetting, ShowMinimap, SnippetSortOrder,
|
||||
};
|
||||
@@ -20,9 +20,9 @@ pub struct EditorSettings {
|
||||
pub current_line_highlight: CurrentLineHighlight,
|
||||
pub selection_highlight: bool,
|
||||
pub rounded_selection: bool,
|
||||
pub lsp_highlight_debounce: DelayMs,
|
||||
pub lsp_highlight_debounce: u64,
|
||||
pub hover_popover_enabled: bool,
|
||||
pub hover_popover_delay: DelayMs,
|
||||
pub hover_popover_delay: u64,
|
||||
pub toolbar: Toolbar,
|
||||
pub scrollbar: Scrollbar,
|
||||
pub minimap: Minimap,
|
||||
@@ -147,7 +147,7 @@ pub struct DragAndDropSelection {
|
||||
/// The delay in milliseconds that must elapse before drag and drop is allowed. Otherwise, a new text selection is created.
|
||||
///
|
||||
/// Default: 300
|
||||
pub delay: DelayMs,
|
||||
pub delay: u64,
|
||||
}
|
||||
|
||||
/// Default options for buffer and project search items.
|
||||
|
||||
@@ -26838,24 +26838,3 @@ async fn test_copy_line_without_trailing_newline(cx: &mut TestAppContext) {
|
||||
|
||||
cx.assert_editor_state("line1\nline2\nˇ");
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_end_of_editor_context(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx).await;
|
||||
|
||||
cx.set_state("line1\nline2ˇ");
|
||||
cx.update_editor(|e, window, cx| {
|
||||
e.set_mode(EditorMode::SingleLine);
|
||||
assert!(e.key_context(window, cx).contains("end_of_input"));
|
||||
});
|
||||
cx.set_state("ˇline1\nline2");
|
||||
cx.update_editor(|e, window, cx| {
|
||||
assert!(!e.key_context(window, cx).contains("end_of_input"));
|
||||
});
|
||||
cx.set_state("line1ˇ\nline2");
|
||||
cx.update_editor(|e, window, cx| {
|
||||
assert!(!e.key_context(window, cx).contains("end_of_input"));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -493,7 +493,6 @@ impl EditorElement {
|
||||
register_action(editor, window, Editor::stage_and_next);
|
||||
register_action(editor, window, Editor::unstage_and_next);
|
||||
register_action(editor, window, Editor::expand_all_diff_hunks);
|
||||
register_action(editor, window, Editor::collapse_all_diff_hunks);
|
||||
register_action(editor, window, Editor::go_to_previous_change);
|
||||
register_action(editor, window, Editor::go_to_next_change);
|
||||
|
||||
@@ -1071,10 +1070,7 @@ impl EditorElement {
|
||||
ref mouse_down_time,
|
||||
} => {
|
||||
let drag_and_drop_delay = Duration::from_millis(
|
||||
EditorSettings::get_global(cx)
|
||||
.drag_and_drop_selection
|
||||
.delay
|
||||
.0,
|
||||
EditorSettings::get_global(cx).drag_and_drop_selection.delay,
|
||||
);
|
||||
if mouse_down_time.elapsed() >= drag_and_drop_delay {
|
||||
let drop_cursor = Selection {
|
||||
@@ -6176,10 +6172,7 @@ impl EditorElement {
|
||||
} = &editor.selection_drag_state
|
||||
{
|
||||
let drag_and_drop_delay = Duration::from_millis(
|
||||
EditorSettings::get_global(cx)
|
||||
.drag_and_drop_selection
|
||||
.delay
|
||||
.0,
|
||||
EditorSettings::get_global(cx).drag_and_drop_selection.delay,
|
||||
);
|
||||
if mouse_down_time.elapsed() >= drag_and_drop_delay {
|
||||
window.set_cursor_style(
|
||||
|
||||
@@ -154,7 +154,7 @@ pub fn hover_at_inlay(
|
||||
hide_hover(editor, cx);
|
||||
}
|
||||
|
||||
let hover_popover_delay = EditorSettings::get_global(cx).hover_popover_delay.0;
|
||||
let hover_popover_delay = EditorSettings::get_global(cx).hover_popover_delay;
|
||||
|
||||
let task = cx.spawn_in(window, async move |this, cx| {
|
||||
async move {
|
||||
@@ -275,7 +275,7 @@ fn show_hover(
|
||||
return None;
|
||||
}
|
||||
|
||||
let hover_popover_delay = EditorSettings::get_global(cx).hover_popover_delay.0;
|
||||
let hover_popover_delay = EditorSettings::get_global(cx).hover_popover_delay;
|
||||
let all_diagnostics_active = editor.active_diagnostics == ActiveDiagnostic::All;
|
||||
let active_group_id = if let ActiveDiagnostic::Group(group) = &editor.active_diagnostics {
|
||||
Some(group.group_id)
|
||||
@@ -1004,7 +1004,7 @@ mod tests {
|
||||
use text::Bias;
|
||||
|
||||
fn get_hover_popover_delay(cx: &gpui::TestAppContext) -> u64 {
|
||||
cx.read(|cx: &App| -> u64 { EditorSettings::get_global(cx).hover_popover_delay.0 })
|
||||
cx.read(|cx: &App| -> u64 { EditorSettings::get_global(cx).hover_popover_delay })
|
||||
}
|
||||
|
||||
impl InfoPopover {
|
||||
|
||||
@@ -21,9 +21,7 @@ use gpui::{
|
||||
};
|
||||
use open_path_prompt::OpenPathPrompt;
|
||||
use picker::{Picker, PickerDelegate};
|
||||
use project::{
|
||||
PathMatchCandidateSet, Project, ProjectPath, WorktreeId, worktree_store::WorktreeStore,
|
||||
};
|
||||
use project::{PathMatchCandidateSet, Project, ProjectPath, WorktreeId};
|
||||
use search::ToggleIncludeIgnored;
|
||||
use settings::Settings;
|
||||
use std::{
|
||||
@@ -540,14 +538,11 @@ impl Matches {
|
||||
|
||||
fn push_new_matches<'a>(
|
||||
&'a mut self,
|
||||
worktree_store: Entity<WorktreeStore>,
|
||||
cx: &'a App,
|
||||
history_items: impl IntoIterator<Item = &'a FoundPath> + Clone,
|
||||
currently_opened: Option<&'a FoundPath>,
|
||||
query: Option<&FileSearchQuery>,
|
||||
new_search_matches: impl Iterator<Item = ProjectPanelOrdMatch>,
|
||||
extend_old_matches: bool,
|
||||
path_style: PathStyle,
|
||||
) {
|
||||
let Some(query) = query else {
|
||||
// assuming that if there's no query, then there's no search matches.
|
||||
@@ -561,25 +556,8 @@ impl Matches {
|
||||
.extend(history_items.into_iter().map(path_to_entry));
|
||||
return;
|
||||
};
|
||||
// If several worktress are open we have to set the worktree root names in path prefix
|
||||
let several_worktrees = worktree_store.read(cx).worktrees().count() > 1;
|
||||
let worktree_name_by_id = several_worktrees.then(|| {
|
||||
worktree_store
|
||||
.read(cx)
|
||||
.worktrees()
|
||||
.map(|worktree| {
|
||||
let snapshot = worktree.read(cx).snapshot();
|
||||
(snapshot.id(), snapshot.root_name().into())
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
let new_history_matches = matching_history_items(
|
||||
history_items,
|
||||
currently_opened,
|
||||
worktree_name_by_id,
|
||||
query,
|
||||
path_style,
|
||||
);
|
||||
|
||||
let new_history_matches = matching_history_items(history_items, currently_opened, query);
|
||||
let new_search_matches: Vec<Match> = new_search_matches
|
||||
.filter(|path_match| {
|
||||
!new_history_matches.contains_key(&ProjectPath {
|
||||
@@ -716,9 +694,7 @@ impl Matches {
|
||||
fn matching_history_items<'a>(
|
||||
history_items: impl IntoIterator<Item = &'a FoundPath>,
|
||||
currently_opened: Option<&'a FoundPath>,
|
||||
worktree_name_by_id: Option<HashMap<WorktreeId, Arc<RelPath>>>,
|
||||
query: &FileSearchQuery,
|
||||
path_style: PathStyle,
|
||||
) -> HashMap<ProjectPath, Match> {
|
||||
let mut candidates_paths = HashMap::default();
|
||||
|
||||
@@ -758,18 +734,13 @@ fn matching_history_items<'a>(
|
||||
let mut matching_history_paths = HashMap::default();
|
||||
for (worktree, candidates) in history_items_by_worktrees {
|
||||
let max_results = candidates.len() + 1;
|
||||
let worktree_root_name = worktree_name_by_id
|
||||
.as_ref()
|
||||
.and_then(|w| w.get(&worktree).cloned());
|
||||
matching_history_paths.extend(
|
||||
fuzzy::match_fixed_path_set(
|
||||
candidates,
|
||||
worktree.to_usize(),
|
||||
worktree_root_name,
|
||||
query.path_query(),
|
||||
false,
|
||||
max_results,
|
||||
path_style,
|
||||
)
|
||||
.into_iter()
|
||||
.filter_map(|path_match| {
|
||||
@@ -966,18 +937,15 @@ impl FileFinderDelegate {
|
||||
self.matches.get(self.selected_index).cloned()
|
||||
};
|
||||
|
||||
let path_style = self.project.read(cx).path_style(cx);
|
||||
self.matches.push_new_matches(
|
||||
self.project.read(cx).worktree_store(),
|
||||
cx,
|
||||
&self.history_items,
|
||||
self.currently_opened_path.as_ref(),
|
||||
Some(&query),
|
||||
matches.into_iter(),
|
||||
extend_old_matches,
|
||||
path_style,
|
||||
);
|
||||
|
||||
let path_style = self.project.read(cx).path_style(cx);
|
||||
let query_path = query.raw_query.as_str();
|
||||
if let Ok(mut query_path) = RelPath::new(Path::new(query_path), path_style) {
|
||||
let available_worktree = self
|
||||
@@ -1397,11 +1365,7 @@ impl PickerDelegate for FileFinderDelegate {
|
||||
separate_history: self.separate_history,
|
||||
..Matches::default()
|
||||
};
|
||||
let path_style = self.project.read(cx).path_style(cx);
|
||||
|
||||
self.matches.push_new_matches(
|
||||
project.worktree_store(),
|
||||
cx,
|
||||
self.history_items.iter().filter(|history_item| {
|
||||
project
|
||||
.worktree_for_id(history_item.project.worktree_id, cx)
|
||||
@@ -1413,7 +1377,6 @@ impl PickerDelegate for FileFinderDelegate {
|
||||
None,
|
||||
None.into_iter(),
|
||||
false,
|
||||
path_style,
|
||||
);
|
||||
|
||||
self.first_update = false;
|
||||
|
||||
@@ -18,11 +18,7 @@ impl Settings for FileFinderSettings {
|
||||
file_icons: file_finder.file_icons.unwrap(),
|
||||
modal_max_width: file_finder.modal_max_width.unwrap().into(),
|
||||
skip_focus_for_active_in_search: file_finder.skip_focus_for_active_in_search.unwrap(),
|
||||
include_ignored: match file_finder.include_ignored.unwrap() {
|
||||
settings::IncludeIgnoredContent::All => Some(true),
|
||||
settings::IncludeIgnoredContent::Indexed => Some(false),
|
||||
settings::IncludeIgnoredContent::Smart => None,
|
||||
},
|
||||
include_ignored: file_finder.include_ignored,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2503,147 +2503,6 @@ async fn test_search_results_refreshed_on_adding_and_removing_worktrees(
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_history_items_uniqueness_for_multiple_worktree_open_all_files(
|
||||
cx: &mut TestAppContext,
|
||||
) {
|
||||
let app_state = init_test(cx);
|
||||
app_state
|
||||
.fs
|
||||
.as_fake()
|
||||
.insert_tree(
|
||||
path!("/repo1"),
|
||||
json!({
|
||||
"package.json": r#"{"name": "repo1"}"#,
|
||||
"src": {
|
||||
"index.js": "// Repo 1 index",
|
||||
}
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
app_state
|
||||
.fs
|
||||
.as_fake()
|
||||
.insert_tree(
|
||||
path!("/repo2"),
|
||||
json!({
|
||||
"package.json": r#"{"name": "repo2"}"#,
|
||||
"src": {
|
||||
"index.js": "// Repo 2 index",
|
||||
}
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
let project = Project::test(
|
||||
app_state.fs.clone(),
|
||||
[path!("/repo1").as_ref(), path!("/repo2").as_ref()],
|
||||
cx,
|
||||
)
|
||||
.await;
|
||||
|
||||
let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
|
||||
let (worktree_id1, worktree_id2) = cx.read(|cx| {
|
||||
let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>();
|
||||
(worktrees[0].read(cx).id(), worktrees[1].read(cx).id())
|
||||
});
|
||||
|
||||
workspace
|
||||
.update_in(cx, |workspace, window, cx| {
|
||||
workspace.open_path(
|
||||
ProjectPath {
|
||||
worktree_id: worktree_id1,
|
||||
path: rel_path("package.json").into(),
|
||||
},
|
||||
None,
|
||||
true,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
cx.dispatch_action(workspace::CloseActiveItem {
|
||||
save_intent: None,
|
||||
close_pinned: false,
|
||||
});
|
||||
workspace
|
||||
.update_in(cx, |workspace, window, cx| {
|
||||
workspace.open_path(
|
||||
ProjectPath {
|
||||
worktree_id: worktree_id2,
|
||||
path: rel_path("package.json").into(),
|
||||
},
|
||||
None,
|
||||
true,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
cx.dispatch_action(workspace::CloseActiveItem {
|
||||
save_intent: None,
|
||||
close_pinned: false,
|
||||
});
|
||||
|
||||
let picker = open_file_picker(&workspace, cx);
|
||||
cx.simulate_input("package.json");
|
||||
|
||||
picker.update(cx, |finder, _| {
|
||||
let matches = &finder.delegate.matches.matches;
|
||||
|
||||
assert_eq!(
|
||||
matches.len(),
|
||||
2,
|
||||
"Expected 1 history match + 1 search matches, but got {} matches: {:?}",
|
||||
matches.len(),
|
||||
matches
|
||||
);
|
||||
|
||||
assert_matches!(matches[0], Match::History { .. });
|
||||
|
||||
let search_matches = collect_search_matches(finder);
|
||||
assert_eq!(
|
||||
search_matches.history.len(),
|
||||
2,
|
||||
"Should have exactly 2 history match"
|
||||
);
|
||||
assert_eq!(
|
||||
search_matches.search.len(),
|
||||
0,
|
||||
"Should have exactly 0 search match (because we already opened the 2 package.json)"
|
||||
);
|
||||
|
||||
if let Match::History { path, panel_match } = &matches[0] {
|
||||
assert_eq!(path.project.worktree_id, worktree_id2);
|
||||
assert_eq!(path.project.path.as_ref(), rel_path("package.json"));
|
||||
let panel_match = panel_match.as_ref().unwrap();
|
||||
assert_eq!(panel_match.0.path_prefix, rel_path("repo2").into());
|
||||
assert_eq!(panel_match.0.path, rel_path("package.json").into());
|
||||
assert_eq!(
|
||||
panel_match.0.positions,
|
||||
vec![6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]
|
||||
);
|
||||
}
|
||||
|
||||
if let Match::History { path, panel_match } = &matches[1] {
|
||||
assert_eq!(path.project.worktree_id, worktree_id1);
|
||||
assert_eq!(path.project.path.as_ref(), rel_path("package.json"));
|
||||
let panel_match = panel_match.as_ref().unwrap();
|
||||
assert_eq!(panel_match.0.path_prefix, rel_path("repo1").into());
|
||||
assert_eq!(panel_match.0.path, rel_path("package.json").into());
|
||||
assert_eq!(
|
||||
panel_match.0.positions,
|
||||
vec![6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_selected_match_stays_selected_after_matches_refreshed(cx: &mut gpui::TestAppContext) {
|
||||
let app_state = init_test(cx);
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
[package]
|
||||
name = "fs_benchmarks"
|
||||
version = "0.1.0"
|
||||
publish.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
fs.workspace = true
|
||||
gpui = {workspace = true, features = ["windows-manifest"]}
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -88,11 +88,9 @@ impl Ord for PathMatch {
|
||||
pub fn match_fixed_path_set(
|
||||
candidates: Vec<PathMatchCandidate>,
|
||||
worktree_id: usize,
|
||||
worktree_root_name: Option<Arc<RelPath>>,
|
||||
query: &str,
|
||||
smart_case: bool,
|
||||
max_results: usize,
|
||||
path_style: PathStyle,
|
||||
) -> Vec<PathMatch> {
|
||||
let lowercase_query = query.to_lowercase().chars().collect::<Vec<_>>();
|
||||
let query = query.chars().collect::<Vec<_>>();
|
||||
@@ -100,31 +98,10 @@ pub fn match_fixed_path_set(
|
||||
|
||||
let mut matcher = Matcher::new(&query, &lowercase_query, query_char_bag, smart_case, true);
|
||||
|
||||
let mut results = Vec::with_capacity(candidates.len());
|
||||
let (path_prefix, path_prefix_chars, lowercase_prefix) = match worktree_root_name {
|
||||
Some(worktree_root_name) => {
|
||||
let mut path_prefix_chars = worktree_root_name
|
||||
.display(path_style)
|
||||
.chars()
|
||||
.collect::<Vec<_>>();
|
||||
path_prefix_chars.extend(path_style.separator().chars());
|
||||
let lowercase_pfx = path_prefix_chars
|
||||
.iter()
|
||||
.map(|c| c.to_ascii_lowercase())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
(worktree_root_name, path_prefix_chars, lowercase_pfx)
|
||||
}
|
||||
None => (
|
||||
RelPath::empty().into(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
),
|
||||
};
|
||||
|
||||
let mut results = Vec::new();
|
||||
matcher.match_candidates(
|
||||
&path_prefix_chars,
|
||||
&lowercase_prefix,
|
||||
&[],
|
||||
&[],
|
||||
candidates.into_iter(),
|
||||
&mut results,
|
||||
&AtomicBool::new(false),
|
||||
@@ -134,7 +111,7 @@ pub fn match_fixed_path_set(
|
||||
positions: positions.clone(),
|
||||
is_dir: candidate.is_dir,
|
||||
path: candidate.path.into(),
|
||||
path_prefix: path_prefix.clone(),
|
||||
path_prefix: RelPath::empty().into(),
|
||||
distance_to_relative_ancestor: usize::MAX,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -693,11 +693,10 @@ impl GitRepository for RealGitRepository {
|
||||
.args([
|
||||
"--no-optional-locks",
|
||||
"show",
|
||||
"--format=",
|
||||
"--format=%P",
|
||||
"-z",
|
||||
"--no-renames",
|
||||
"--name-status",
|
||||
"--first-parent",
|
||||
])
|
||||
.arg(&commit)
|
||||
.stdin(Stdio::null())
|
||||
@@ -708,8 +707,9 @@ impl GitRepository for RealGitRepository {
|
||||
.context("starting git show process")?;
|
||||
|
||||
let show_stdout = String::from_utf8_lossy(&show_output.stdout);
|
||||
let changes = parse_git_diff_name_status(&show_stdout);
|
||||
let parent_sha = format!("{}^", commit);
|
||||
let mut lines = show_stdout.split('\n');
|
||||
let parent_sha = lines.next().unwrap().trim().trim_end_matches('\0');
|
||||
let changes = parse_git_diff_name_status(lines.next().unwrap_or(""));
|
||||
|
||||
let mut cat_file_process = util::command::new_smol_command(&git_binary_path)
|
||||
.current_dir(&working_directory)
|
||||
|
||||
@@ -98,10 +98,25 @@ impl BlameRenderer for GitBlameRenderer {
|
||||
let workspace = workspace.clone();
|
||||
move |_, window, cx| {
|
||||
CommitView::open(
|
||||
blame_entry.sha.to_string(),
|
||||
CommitSummary {
|
||||
sha: blame_entry.sha.to_string().into(),
|
||||
subject: blame_entry
|
||||
.summary
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
.into(),
|
||||
commit_timestamp: blame_entry
|
||||
.committer_time
|
||||
.unwrap_or_default(),
|
||||
author_name: blame_entry
|
||||
.committer_name
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
.into(),
|
||||
has_parent: true,
|
||||
},
|
||||
repository.downgrade(),
|
||||
workspace.clone(),
|
||||
None,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
@@ -320,10 +335,9 @@ impl BlameRenderer for GitBlameRenderer {
|
||||
.icon_size(IconSize::Small)
|
||||
.on_click(move |_, window, cx| {
|
||||
CommitView::open(
|
||||
commit_summary.sha.clone().into(),
|
||||
commit_summary.clone(),
|
||||
repository.downgrade(),
|
||||
workspace.clone(),
|
||||
None,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
@@ -360,10 +374,15 @@ impl BlameRenderer for GitBlameRenderer {
|
||||
cx: &mut App,
|
||||
) {
|
||||
CommitView::open(
|
||||
blame_entry.sha.to_string(),
|
||||
CommitSummary {
|
||||
sha: blame_entry.sha.to_string().into(),
|
||||
subject: blame_entry.summary.clone().unwrap_or_default().into(),
|
||||
commit_timestamp: blame_entry.committer_time.unwrap_or_default(),
|
||||
author_name: blame_entry.committer_name.unwrap_or_default().into(),
|
||||
has_parent: true,
|
||||
},
|
||||
repository.downgrade(),
|
||||
workspace,
|
||||
None,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
|
||||
@@ -137,13 +137,13 @@ impl BranchList {
|
||||
})
|
||||
.await;
|
||||
|
||||
let _ = this.update_in(cx, |this, window, cx| {
|
||||
this.update_in(cx, |this, window, cx| {
|
||||
this.picker.update(cx, |picker, cx| {
|
||||
picker.delegate.default_branch = default_branch;
|
||||
picker.delegate.all_branches = Some(all_branches);
|
||||
picker.refresh(window, cx);
|
||||
})
|
||||
});
|
||||
})?;
|
||||
|
||||
anyhow::Ok(())
|
||||
})
|
||||
@@ -410,20 +410,37 @@ impl PickerDelegate for BranchListDelegate {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(repo) = self.repo.clone() else {
|
||||
return;
|
||||
};
|
||||
cx.spawn_in(window, {
|
||||
let branch = entry.branch.clone();
|
||||
async move |picker, cx| {
|
||||
let branch_change_task = picker.update(cx, |this, cx| {
|
||||
let repo = this
|
||||
.delegate
|
||||
.repo
|
||||
.as_ref()
|
||||
.context("No active repository")?
|
||||
.clone();
|
||||
|
||||
let branch = entry.branch.clone();
|
||||
cx.spawn(async move |_, cx| {
|
||||
repo.update(cx, |repo, _| repo.change_branch(branch.name().to_string()))?
|
||||
.await??;
|
||||
let mut cx = cx.to_async();
|
||||
|
||||
anyhow::Ok(())
|
||||
anyhow::Ok(async move {
|
||||
repo.update(&mut cx, |repo, _| {
|
||||
repo.change_branch(branch.name().to_string())
|
||||
})?
|
||||
.await?
|
||||
})
|
||||
})??;
|
||||
|
||||
branch_change_task.await?;
|
||||
|
||||
picker.update(cx, |_, cx| {
|
||||
cx.emit(DismissEvent);
|
||||
|
||||
anyhow::Ok(())
|
||||
})
|
||||
}
|
||||
})
|
||||
.detach_and_prompt_err("Failed to change branch", window, cx, |_, _, _| None);
|
||||
|
||||
cx.emit(DismissEvent);
|
||||
}
|
||||
|
||||
fn dismissed(&mut self, _: &mut Window, cx: &mut Context<Picker<Self>>) {
|
||||
|
||||
@@ -318,10 +318,9 @@ impl Render for CommitTooltip {
|
||||
.on_click(
|
||||
move |_, window, cx| {
|
||||
CommitView::open(
|
||||
commit_summary.sha.to_string(),
|
||||
commit_summary.clone(),
|
||||
repo.downgrade(),
|
||||
workspace.clone(),
|
||||
None,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
use anyhow::{Context as _, Result};
|
||||
use buffer_diff::{BufferDiff, BufferDiffSnapshot};
|
||||
use editor::{Editor, EditorEvent, MultiBuffer, SelectionEffects, multibuffer_context_lines};
|
||||
use git::repository::{CommitDetails, CommitDiff, RepoPath};
|
||||
use git::repository::{CommitDetails, CommitDiff, CommitSummary, RepoPath};
|
||||
use gpui::{
|
||||
Action, AnyElement, AnyView, App, AppContext as _, AsyncApp, AsyncWindowContext, Context,
|
||||
Entity, EventEmitter, FocusHandle, Focusable, IntoElement, PromptLevel, Render, WeakEntity,
|
||||
Window, actions,
|
||||
AnyElement, AnyView, App, AppContext as _, AsyncApp, Context, Entity, EventEmitter,
|
||||
FocusHandle, Focusable, IntoElement, Render, WeakEntity, Window,
|
||||
};
|
||||
use language::{
|
||||
Anchor, Buffer, Capability, DiskState, File, LanguageRegistry, LineEnding, OffsetRangeExt as _,
|
||||
Point, ReplicaId, Rope, TextBuffer,
|
||||
Point, Rope, TextBuffer,
|
||||
};
|
||||
use multi_buffer::PathKey;
|
||||
use project::{Project, WorktreeId, git_store::Repository};
|
||||
@@ -19,42 +18,17 @@ use std::{
|
||||
path::PathBuf,
|
||||
sync::Arc,
|
||||
};
|
||||
use ui::{
|
||||
Button, Color, Icon, IconName, Label, LabelCommon as _, SharedString, Tooltip, prelude::*,
|
||||
};
|
||||
use ui::{Color, Icon, IconName, Label, LabelCommon as _, SharedString};
|
||||
use util::{ResultExt, paths::PathStyle, rel_path::RelPath, truncate_and_trailoff};
|
||||
use workspace::{
|
||||
Item, ItemHandle, ItemNavHistory, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView,
|
||||
Workspace,
|
||||
Item, ItemHandle as _, ItemNavHistory, ToolbarItemLocation, Workspace,
|
||||
item::{BreadcrumbText, ItemEvent, TabContentParams},
|
||||
notifications::NotifyTaskExt,
|
||||
pane::SaveIntent,
|
||||
searchable::SearchableItemHandle,
|
||||
};
|
||||
|
||||
use crate::git_panel::GitPanel;
|
||||
|
||||
actions!(git, [ApplyCurrentStash, PopCurrentStash, DropCurrentStash,]);
|
||||
|
||||
pub fn init(cx: &mut App) {
|
||||
cx.observe_new(|workspace: &mut Workspace, _window, _cx| {
|
||||
register_workspace_action(workspace, |toolbar, _: &ApplyCurrentStash, window, cx| {
|
||||
toolbar.apply_stash(window, cx);
|
||||
});
|
||||
register_workspace_action(workspace, |toolbar, _: &DropCurrentStash, window, cx| {
|
||||
toolbar.remove_stash(window, cx);
|
||||
});
|
||||
register_workspace_action(workspace, |toolbar, _: &PopCurrentStash, window, cx| {
|
||||
toolbar.pop_stash(window, cx);
|
||||
});
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
pub struct CommitView {
|
||||
commit: CommitDetails,
|
||||
editor: Entity<Editor>,
|
||||
stash: Option<usize>,
|
||||
multibuffer: Entity<MultiBuffer>,
|
||||
}
|
||||
|
||||
@@ -74,18 +48,17 @@ const FILE_NAMESPACE_SORT_PREFIX: u64 = 1;
|
||||
|
||||
impl CommitView {
|
||||
pub fn open(
|
||||
commit_sha: String,
|
||||
commit: CommitSummary,
|
||||
repo: WeakEntity<Repository>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
stash: Option<usize>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) {
|
||||
let commit_diff = repo
|
||||
.update(cx, |repo, _| repo.load_commit_diff(commit_sha.clone()))
|
||||
.update(cx, |repo, _| repo.load_commit_diff(commit.sha.to_string()))
|
||||
.ok();
|
||||
let commit_details = repo
|
||||
.update(cx, |repo, _| repo.show(commit_sha.clone()))
|
||||
.update(cx, |repo, _| repo.show(commit.sha.to_string()))
|
||||
.ok();
|
||||
|
||||
window
|
||||
@@ -104,7 +77,6 @@ impl CommitView {
|
||||
commit_diff,
|
||||
repo,
|
||||
project.clone(),
|
||||
stash,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
@@ -115,7 +87,7 @@ impl CommitView {
|
||||
let ix = pane.items().position(|item| {
|
||||
let commit_view = item.downcast::<CommitView>();
|
||||
commit_view
|
||||
.is_some_and(|view| view.read(cx).commit.sha == commit_sha)
|
||||
.is_some_and(|view| view.read(cx).commit.sha == commit.sha)
|
||||
});
|
||||
if let Some(ix) = ix {
|
||||
pane.activate_item(ix, true, true, window, cx);
|
||||
@@ -134,7 +106,6 @@ impl CommitView {
|
||||
commit_diff: CommitDiff,
|
||||
repository: Entity<Repository>,
|
||||
project: Entity<Project>,
|
||||
stash: Option<usize>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
@@ -156,21 +127,18 @@ impl CommitView {
|
||||
|
||||
let mut metadata_buffer_id = None;
|
||||
if let Some(worktree_id) = first_worktree_id {
|
||||
let title = if let Some(stash) = stash {
|
||||
format!("stash@{{{}}}", stash)
|
||||
} else {
|
||||
format!("commit {}", commit.sha)
|
||||
};
|
||||
let file = Arc::new(CommitMetadataFile {
|
||||
title: RelPath::unix(&title).unwrap().into(),
|
||||
title: RelPath::unix(&format!("commit {}", commit.sha))
|
||||
.unwrap()
|
||||
.into(),
|
||||
worktree_id,
|
||||
});
|
||||
let buffer = cx.new(|cx| {
|
||||
let buffer = TextBuffer::new_normalized(
|
||||
ReplicaId::LOCAL,
|
||||
0,
|
||||
cx.entity_id().as_non_zero_u64().into(),
|
||||
LineEnding::default(),
|
||||
format_commit(&commit, stash.is_some()).into(),
|
||||
format_commit(&commit).into(),
|
||||
);
|
||||
metadata_buffer_id = Some(buffer.remote_id());
|
||||
Buffer::build(buffer, Some(file.clone()), Capability::ReadWrite)
|
||||
@@ -243,7 +211,6 @@ impl CommitView {
|
||||
commit,
|
||||
editor,
|
||||
multibuffer,
|
||||
stash,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -349,7 +316,7 @@ async fn build_buffer(
|
||||
};
|
||||
let buffer = cx.new(|cx| {
|
||||
let buffer = TextBuffer::new_normalized(
|
||||
ReplicaId::LOCAL,
|
||||
0,
|
||||
cx.entity_id().as_non_zero_u64().into(),
|
||||
line_ending,
|
||||
text,
|
||||
@@ -402,13 +369,9 @@ async fn build_buffer_diff(
|
||||
})
|
||||
}
|
||||
|
||||
fn format_commit(commit: &CommitDetails, is_stash: bool) -> String {
|
||||
fn format_commit(commit: &CommitDetails) -> String {
|
||||
let mut result = String::new();
|
||||
if is_stash {
|
||||
writeln!(&mut result, "stash commit {}", commit.sha).unwrap();
|
||||
} else {
|
||||
writeln!(&mut result, "commit {}", commit.sha).unwrap();
|
||||
}
|
||||
writeln!(&mut result, "commit {}", commit.sha).unwrap();
|
||||
writeln!(
|
||||
&mut result,
|
||||
"Author: {} <{}>",
|
||||
@@ -575,296 +538,13 @@ impl Item for CommitView {
|
||||
editor,
|
||||
multibuffer,
|
||||
commit: self.commit.clone(),
|
||||
stash: self.stash,
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for CommitView {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let is_stash = self.stash.is_some();
|
||||
div()
|
||||
.key_context(if is_stash { "StashDiff" } else { "CommitDiff" })
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.size_full()
|
||||
.child(self.editor.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CommitViewToolbar {
|
||||
commit_view: Option<WeakEntity<CommitView>>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
}
|
||||
|
||||
impl CommitViewToolbar {
|
||||
pub fn new(workspace: &Workspace, _: &mut Context<Self>) -> Self {
|
||||
Self {
|
||||
commit_view: None,
|
||||
workspace: workspace.weak_handle(),
|
||||
}
|
||||
}
|
||||
|
||||
fn commit_view(&self, _: &App) -> Option<Entity<CommitView>> {
|
||||
self.commit_view.as_ref()?.upgrade()
|
||||
}
|
||||
|
||||
async fn close_commit_view(
|
||||
commit_view: Entity<CommitView>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
cx: &mut AsyncWindowContext,
|
||||
) -> anyhow::Result<()> {
|
||||
workspace
|
||||
.update_in(cx, |workspace, window, cx| {
|
||||
let active_pane = workspace.active_pane();
|
||||
let commit_view_id = commit_view.entity_id();
|
||||
active_pane.update(cx, |pane, cx| {
|
||||
pane.close_item_by_id(commit_view_id, SaveIntent::Skip, window, cx)
|
||||
})
|
||||
})?
|
||||
.await?;
|
||||
anyhow::Ok(())
|
||||
}
|
||||
|
||||
fn apply_stash(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.stash_action(
|
||||
"Apply",
|
||||
window,
|
||||
cx,
|
||||
async move |repository, sha, stash, commit_view, workspace, cx| {
|
||||
let result = repository.update(cx, |repo, cx| {
|
||||
if !stash_matches_index(&sha, stash, repo) {
|
||||
return Err(anyhow::anyhow!("Stash has changed, not applying"));
|
||||
}
|
||||
Ok(repo.stash_apply(Some(stash), cx))
|
||||
})?;
|
||||
|
||||
match result {
|
||||
Ok(task) => task.await?,
|
||||
Err(err) => {
|
||||
Self::close_commit_view(commit_view, workspace, cx).await?;
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
Self::close_commit_view(commit_view, workspace, cx).await?;
|
||||
anyhow::Ok(())
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn pop_stash(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.stash_action(
|
||||
"Pop",
|
||||
window,
|
||||
cx,
|
||||
async move |repository, sha, stash, commit_view, workspace, cx| {
|
||||
let result = repository.update(cx, |repo, cx| {
|
||||
if !stash_matches_index(&sha, stash, repo) {
|
||||
return Err(anyhow::anyhow!("Stash has changed, pop aborted"));
|
||||
}
|
||||
Ok(repo.stash_pop(Some(stash), cx))
|
||||
})?;
|
||||
|
||||
match result {
|
||||
Ok(task) => task.await?,
|
||||
Err(err) => {
|
||||
Self::close_commit_view(commit_view, workspace, cx).await?;
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
Self::close_commit_view(commit_view, workspace, cx).await?;
|
||||
anyhow::Ok(())
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn remove_stash(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.stash_action(
|
||||
"Drop",
|
||||
window,
|
||||
cx,
|
||||
async move |repository, sha, stash, commit_view, workspace, cx| {
|
||||
let result = repository.update(cx, |repo, cx| {
|
||||
if !stash_matches_index(&sha, stash, repo) {
|
||||
return Err(anyhow::anyhow!("Stash has changed, drop aborted"));
|
||||
}
|
||||
Ok(repo.stash_drop(Some(stash), cx))
|
||||
})?;
|
||||
|
||||
match result {
|
||||
Ok(task) => task.await??,
|
||||
Err(err) => {
|
||||
Self::close_commit_view(commit_view, workspace, cx).await?;
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
Self::close_commit_view(commit_view, workspace, cx).await?;
|
||||
anyhow::Ok(())
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn stash_action<AsyncFn>(
|
||||
&mut self,
|
||||
str_action: &str,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
callback: AsyncFn,
|
||||
) where
|
||||
AsyncFn: AsyncFnOnce(
|
||||
Entity<Repository>,
|
||||
&SharedString,
|
||||
usize,
|
||||
Entity<CommitView>,
|
||||
WeakEntity<Workspace>,
|
||||
&mut AsyncWindowContext,
|
||||
) -> anyhow::Result<()>
|
||||
+ 'static,
|
||||
{
|
||||
let Some(commit_view) = self.commit_view(cx) else {
|
||||
return;
|
||||
};
|
||||
let Some(stash) = commit_view.read(cx).stash else {
|
||||
return;
|
||||
};
|
||||
let sha = commit_view.read(cx).commit.sha.clone();
|
||||
let answer = window.prompt(
|
||||
PromptLevel::Info,
|
||||
&format!("{} stash@{{{}}}?", str_action, stash),
|
||||
None,
|
||||
&[str_action, "Cancel"],
|
||||
cx,
|
||||
);
|
||||
|
||||
let workspace = self.workspace.clone();
|
||||
cx.spawn_in(window, async move |_, cx| {
|
||||
if answer.await != Ok(0) {
|
||||
return anyhow::Ok(());
|
||||
}
|
||||
let repo = workspace.update(cx, |workspace, cx| {
|
||||
workspace
|
||||
.panel::<GitPanel>(cx)
|
||||
.and_then(|p| p.read(cx).active_repository.clone())
|
||||
})?;
|
||||
|
||||
let Some(repo) = repo else {
|
||||
return Ok(());
|
||||
};
|
||||
callback(repo, &sha, stash, commit_view, workspace, cx).await?;
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach_and_notify_err(window, cx);
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<ToolbarItemEvent> for CommitViewToolbar {}
|
||||
|
||||
impl ToolbarItemView for CommitViewToolbar {
|
||||
fn set_active_pane_item(
|
||||
&mut self,
|
||||
active_pane_item: Option<&dyn ItemHandle>,
|
||||
_: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> ToolbarItemLocation {
|
||||
if let Some(entity) = active_pane_item.and_then(|i| i.act_as::<CommitView>(cx))
|
||||
&& entity.read(cx).stash.is_some()
|
||||
{
|
||||
self.commit_view = Some(entity.downgrade());
|
||||
return ToolbarItemLocation::PrimaryRight;
|
||||
}
|
||||
ToolbarItemLocation::Hidden
|
||||
}
|
||||
|
||||
fn pane_focus_update(
|
||||
&mut self,
|
||||
_pane_focused: bool,
|
||||
_window: &mut Window,
|
||||
_cx: &mut Context<Self>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for CommitViewToolbar {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let Some(commit_view) = self.commit_view(cx) else {
|
||||
return div();
|
||||
};
|
||||
|
||||
let is_stash = commit_view.read(cx).stash.is_some();
|
||||
if !is_stash {
|
||||
return div();
|
||||
}
|
||||
|
||||
let focus_handle = commit_view.focus_handle(cx);
|
||||
|
||||
h_group_xl().my_neg_1().py_1().items_center().child(
|
||||
h_group_sm()
|
||||
.child(
|
||||
Button::new("apply-stash", "Apply")
|
||||
.tooltip(Tooltip::for_action_title_in(
|
||||
"Apply current stash",
|
||||
&ApplyCurrentStash,
|
||||
&focus_handle,
|
||||
))
|
||||
.on_click(cx.listener(|this, _, window, cx| this.apply_stash(window, cx))),
|
||||
)
|
||||
.child(
|
||||
Button::new("pop-stash", "Pop")
|
||||
.tooltip(Tooltip::for_action_title_in(
|
||||
"Pop current stash",
|
||||
&PopCurrentStash,
|
||||
&focus_handle,
|
||||
))
|
||||
.on_click(cx.listener(|this, _, window, cx| this.pop_stash(window, cx))),
|
||||
)
|
||||
.child(
|
||||
Button::new("remove-stash", "Remove")
|
||||
.icon(IconName::Trash)
|
||||
.tooltip(Tooltip::for_action_title_in(
|
||||
"Remove current stash",
|
||||
&DropCurrentStash,
|
||||
&focus_handle,
|
||||
))
|
||||
.on_click(cx.listener(|this, _, window, cx| this.remove_stash(window, cx))),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn register_workspace_action<A: Action>(
|
||||
workspace: &mut Workspace,
|
||||
callback: fn(&mut CommitViewToolbar, &A, &mut Window, &mut Context<CommitViewToolbar>),
|
||||
) {
|
||||
workspace.register_action(move |workspace, action: &A, window, cx| {
|
||||
if workspace.has_active_modal(window, cx) {
|
||||
cx.propagate();
|
||||
return;
|
||||
}
|
||||
|
||||
workspace.active_pane().update(cx, |pane, cx| {
|
||||
pane.toolbar().update(cx, move |workspace, cx| {
|
||||
if let Some(toolbar) = workspace.item_of_type::<CommitViewToolbar>() {
|
||||
toolbar.update(cx, move |toolbar, cx| {
|
||||
callback(toolbar, action, window, cx);
|
||||
cx.notify();
|
||||
});
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn stash_matches_index(sha: &str, index: usize, repo: &mut Repository) -> bool {
|
||||
match repo
|
||||
.cached_stash()
|
||||
.entries
|
||||
.iter()
|
||||
.find(|entry| entry.index == index)
|
||||
{
|
||||
Some(entry) => entry.oid.to_string() == sha,
|
||||
None => false,
|
||||
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
|
||||
self.editor.clone()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3611,10 +3611,9 @@ impl GitPanel {
|
||||
let repo = active_repository.downgrade();
|
||||
move |_, window, cx| {
|
||||
CommitView::open(
|
||||
commit.sha.to_string(),
|
||||
commit.clone(),
|
||||
repo.clone(),
|
||||
workspace.clone(),
|
||||
None,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
@@ -4420,10 +4419,6 @@ impl Panel for GitPanel {
|
||||
"GitPanel"
|
||||
}
|
||||
|
||||
fn panel_key() -> &'static str {
|
||||
GIT_PANEL_KEY
|
||||
}
|
||||
|
||||
fn position(&self, _: &Window, cx: &App) -> DockPosition {
|
||||
GitPanelSettings::get_global(cx).dock
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ mod askpass_modal;
|
||||
pub mod branch_picker;
|
||||
mod commit_modal;
|
||||
pub mod commit_tooltip;
|
||||
pub mod commit_view;
|
||||
mod commit_view;
|
||||
mod conflict_view;
|
||||
pub mod file_diff_view;
|
||||
pub mod git_panel;
|
||||
@@ -59,7 +59,6 @@ pub fn init(cx: &mut App) {
|
||||
GitPanelSettings::register(cx);
|
||||
|
||||
editor::set_blame_renderer(blame_ui::GitBlameRenderer, cx);
|
||||
commit_view::init(cx);
|
||||
|
||||
cx.observe_new(|editor: &mut Editor, _, cx| {
|
||||
conflict_view::register_editor(editor, editor.buffer().clone(), cx);
|
||||
|
||||
@@ -5,21 +5,18 @@ use git::stash::StashEntry;
|
||||
use gpui::{
|
||||
Action, AnyElement, App, Context, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable,
|
||||
InteractiveElement, IntoElement, Modifiers, ModifiersChangedEvent, ParentElement, Render,
|
||||
SharedString, Styled, Subscription, Task, WeakEntity, Window, actions, rems, svg,
|
||||
SharedString, Styled, Subscription, Task, Window, actions, rems,
|
||||
};
|
||||
use picker::{Picker, PickerDelegate};
|
||||
use project::git_store::{Repository, RepositoryEvent};
|
||||
use std::sync::Arc;
|
||||
use time::{OffsetDateTime, UtcOffset};
|
||||
use time_format;
|
||||
use ui::{
|
||||
ButtonLike, HighlightedLabel, KeyBinding, ListItem, ListItemSpacing, Tooltip, prelude::*,
|
||||
};
|
||||
use ui::{HighlightedLabel, KeyBinding, ListItem, ListItemSpacing, Tooltip, prelude::*};
|
||||
use util::ResultExt;
|
||||
use workspace::notifications::DetachAndPromptErr;
|
||||
use workspace::{ModalView, Workspace};
|
||||
|
||||
use crate::commit_view::CommitView;
|
||||
use crate::stash_picker;
|
||||
|
||||
actions!(
|
||||
@@ -27,8 +24,6 @@ actions!(
|
||||
[
|
||||
/// Drop the selected stash entry.
|
||||
DropStashItem,
|
||||
/// Show the diff view of the selected stash entry.
|
||||
ShowStashItem,
|
||||
]
|
||||
);
|
||||
|
||||
@@ -43,9 +38,8 @@ pub fn open(
|
||||
cx: &mut Context<Workspace>,
|
||||
) {
|
||||
let repository = workspace.project().read(cx).active_repository(cx);
|
||||
let weak_workspace = workspace.weak_handle();
|
||||
workspace.toggle_modal(window, cx, |window, cx| {
|
||||
StashList::new(repository, weak_workspace, rems(34.), window, cx)
|
||||
StashList::new(repository, rems(34.), window, cx)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -59,7 +53,6 @@ pub struct StashList {
|
||||
impl StashList {
|
||||
fn new(
|
||||
repository: Option<Entity<Repository>>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
width: Rems,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
@@ -105,7 +98,7 @@ impl StashList {
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
|
||||
let delegate = StashListDelegate::new(repository, workspace, window, cx);
|
||||
let delegate = StashListDelegate::new(repository, window, cx);
|
||||
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
|
||||
let picker_focus_handle = picker.focus_handle(cx);
|
||||
picker.update(cx, |picker, _| {
|
||||
@@ -138,20 +131,6 @@ impl StashList {
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn handle_show_stash(
|
||||
&mut self,
|
||||
_: &ShowStashItem,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.picker.update(cx, |picker, cx| {
|
||||
picker
|
||||
.delegate
|
||||
.show_stash_at(picker.delegate.selected_index(), window, cx);
|
||||
});
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn handle_modifiers_changed(
|
||||
&mut self,
|
||||
ev: &ModifiersChangedEvent,
|
||||
@@ -178,7 +157,6 @@ impl Render for StashList {
|
||||
.w(self.width)
|
||||
.on_modifiers_changed(cx.listener(Self::handle_modifiers_changed))
|
||||
.on_action(cx.listener(Self::handle_drop_stash))
|
||||
.on_action(cx.listener(Self::handle_show_stash))
|
||||
.child(self.picker.clone())
|
||||
}
|
||||
}
|
||||
@@ -194,7 +172,6 @@ pub struct StashListDelegate {
|
||||
matches: Vec<StashEntryMatch>,
|
||||
all_stash_entries: Option<Vec<StashEntry>>,
|
||||
repo: Option<Entity<Repository>>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
selected_index: usize,
|
||||
last_query: String,
|
||||
modifiers: Modifiers,
|
||||
@@ -205,7 +182,6 @@ pub struct StashListDelegate {
|
||||
impl StashListDelegate {
|
||||
fn new(
|
||||
repo: Option<Entity<Repository>>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
_window: &mut Window,
|
||||
cx: &mut Context<StashList>,
|
||||
) -> Self {
|
||||
@@ -216,7 +192,6 @@ impl StashListDelegate {
|
||||
Self {
|
||||
matches: vec![],
|
||||
repo,
|
||||
workspace,
|
||||
all_stash_entries: None,
|
||||
selected_index: 0,
|
||||
last_query: Default::default(),
|
||||
@@ -260,25 +235,6 @@ impl StashListDelegate {
|
||||
});
|
||||
}
|
||||
|
||||
fn show_stash_at(&self, ix: usize, window: &mut Window, cx: &mut Context<Picker<Self>>) {
|
||||
let Some(entry_match) = self.matches.get(ix) else {
|
||||
return;
|
||||
};
|
||||
let stash_sha = entry_match.entry.oid.to_string();
|
||||
let stash_index = entry_match.entry.index;
|
||||
let Some(repo) = self.repo.clone() else {
|
||||
return;
|
||||
};
|
||||
CommitView::open(
|
||||
stash_sha,
|
||||
repo.downgrade(),
|
||||
self.workspace.clone(),
|
||||
Some(stash_index),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
|
||||
fn pop_stash(&self, stash_index: usize, window: &mut Window, cx: &mut Context<Picker<Self>>) {
|
||||
let Some(repo) = self.repo.clone() else {
|
||||
return;
|
||||
@@ -434,7 +390,7 @@ impl PickerDelegate for StashListDelegate {
|
||||
ix: usize,
|
||||
selected: bool,
|
||||
_window: &mut Window,
|
||||
cx: &mut Context<Picker<Self>>,
|
||||
_cx: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let entry_match = &self.matches[ix];
|
||||
|
||||
@@ -476,35 +432,11 @@ impl PickerDelegate for StashListDelegate {
|
||||
.size(LabelSize::Small),
|
||||
);
|
||||
|
||||
let show_button = div()
|
||||
.group("show-button-hover")
|
||||
.child(
|
||||
ButtonLike::new("show-button")
|
||||
.child(
|
||||
svg()
|
||||
.size(IconSize::Medium.rems())
|
||||
.flex_none()
|
||||
.path(IconName::Eye.path())
|
||||
.text_color(Color::Default.color(cx))
|
||||
.group_hover("show-button-hover", |this| {
|
||||
this.text_color(Color::Accent.color(cx))
|
||||
})
|
||||
.hover(|this| this.text_color(Color::Accent.color(cx))),
|
||||
)
|
||||
.tooltip(Tooltip::for_action_title("Show Stash", &ShowStashItem))
|
||||
.on_click(cx.listener(move |picker, _, window, cx| {
|
||||
cx.stop_propagation();
|
||||
picker.delegate.show_stash_at(ix, window, cx);
|
||||
})),
|
||||
)
|
||||
.into_any_element();
|
||||
|
||||
Some(
|
||||
ListItem::new(SharedString::from(format!("stash-{ix}")))
|
||||
.inset(true)
|
||||
.spacing(ListItemSpacing::Sparse)
|
||||
.toggle_state(selected)
|
||||
.end_slot(show_button)
|
||||
.child(
|
||||
v_flex()
|
||||
.w_full()
|
||||
|
||||
@@ -553,7 +553,7 @@ pub struct App {
|
||||
pub(crate) entities: EntityMap,
|
||||
pub(crate) window_update_stack: Vec<WindowId>,
|
||||
pub(crate) new_entity_observers: SubscriberSet<TypeId, NewEntityListener>,
|
||||
pub(crate) windows: SlotMap<WindowId, Option<Box<Window>>>,
|
||||
pub(crate) windows: SlotMap<WindowId, Option<Window>>,
|
||||
pub(crate) window_handles: FxHashMap<WindowId, AnyWindowHandle>,
|
||||
pub(crate) focus_handles: Arc<FocusMap>,
|
||||
pub(crate) keymap: Rc<RefCell<Keymap>>,
|
||||
@@ -964,7 +964,7 @@ impl App {
|
||||
clear.clear();
|
||||
|
||||
cx.window_handles.insert(id, window.handle);
|
||||
cx.windows.get_mut(id).unwrap().replace(Box::new(window));
|
||||
cx.windows.get_mut(id).unwrap().replace(window);
|
||||
Ok(handle)
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -1239,7 +1239,7 @@ impl App {
|
||||
.windows
|
||||
.values()
|
||||
.filter_map(|window| {
|
||||
let window = window.as_deref()?;
|
||||
let window = window.as_ref()?;
|
||||
window.invalidator.is_dirty().then_some(window.handle)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
@@ -1320,7 +1320,7 @@ impl App {
|
||||
|
||||
fn apply_refresh_effect(&mut self) {
|
||||
for window in self.windows.values_mut() {
|
||||
if let Some(window) = window.as_deref_mut() {
|
||||
if let Some(window) = window.as_mut() {
|
||||
window.refreshing = true;
|
||||
window.invalidator.set_dirty(true);
|
||||
}
|
||||
@@ -2199,7 +2199,7 @@ impl AppContext for App {
|
||||
.windows
|
||||
.get(window.id)
|
||||
.context("window not found")?
|
||||
.as_deref()
|
||||
.as_ref()
|
||||
.expect("attempted to read a window that is already on the stack");
|
||||
|
||||
let root_view = window.root.clone().unwrap();
|
||||
|
||||
@@ -455,7 +455,7 @@ impl TestAppContext {
|
||||
.windows
|
||||
.get_mut(window.id)
|
||||
.unwrap()
|
||||
.as_deref_mut()
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.platform_window
|
||||
.as_test()
|
||||
|
||||
@@ -509,11 +509,10 @@ impl StateInner {
|
||||
if self.alignment == ListAlignment::Bottom && new_scroll_top == scroll_max {
|
||||
self.logical_scroll_top = None;
|
||||
} else {
|
||||
let (start, ..) =
|
||||
self.items
|
||||
.find::<ListItemSummary, _>((), &Height(new_scroll_top), Bias::Right);
|
||||
let item_ix = start.count;
|
||||
let offset_in_item = new_scroll_top - start.height;
|
||||
let mut cursor = self.items.cursor::<ListItemSummary>(());
|
||||
cursor.seek(&Height(new_scroll_top), Bias::Right);
|
||||
let item_ix = cursor.start().count;
|
||||
let offset_in_item = new_scroll_top - cursor.start().height;
|
||||
self.logical_scroll_top = Some(ListOffset {
|
||||
item_ix,
|
||||
offset_in_item,
|
||||
@@ -551,12 +550,9 @@ impl StateInner {
|
||||
}
|
||||
|
||||
fn scroll_top(&self, logical_scroll_top: &ListOffset) -> Pixels {
|
||||
let (start, ..) = self.items.find::<ListItemSummary, _>(
|
||||
(),
|
||||
&Count(logical_scroll_top.item_ix),
|
||||
Bias::Right,
|
||||
);
|
||||
start.height + logical_scroll_top.offset_in_item
|
||||
let mut cursor = self.items.cursor::<ListItemSummary>(());
|
||||
cursor.seek(&Count(logical_scroll_top.item_ix), Bias::Right);
|
||||
cursor.start().height + logical_scroll_top.offset_in_item
|
||||
}
|
||||
|
||||
fn layout_all_items(
|
||||
@@ -886,12 +882,11 @@ impl StateInner {
|
||||
if self.alignment == ListAlignment::Bottom && new_scroll_top == scroll_max {
|
||||
self.logical_scroll_top = None;
|
||||
} else {
|
||||
let (start, _, _) =
|
||||
self.items
|
||||
.find::<ListItemSummary, _>((), &Height(new_scroll_top), Bias::Right);
|
||||
let mut cursor = self.items.cursor::<ListItemSummary>(());
|
||||
cursor.seek(&Height(new_scroll_top), Bias::Right);
|
||||
|
||||
let item_ix = start.count;
|
||||
let offset_in_item = new_scroll_top - start.height;
|
||||
let item_ix = cursor.start().count;
|
||||
let offset_in_item = new_scroll_top - cursor.start().height;
|
||||
self.logical_scroll_top = Some(ListOffset {
|
||||
item_ix,
|
||||
offset_in_item,
|
||||
|
||||
@@ -364,7 +364,17 @@ impl Element for UniformList {
|
||||
content_size,
|
||||
window,
|
||||
cx,
|
||||
|_style, mut scroll_offset, hitbox, window, cx| {
|
||||
|style, mut scroll_offset, hitbox, window, cx| {
|
||||
let border = style.border_widths.to_pixels(window.rem_size());
|
||||
let padding = style
|
||||
.padding
|
||||
.to_pixels(bounds.size.into(), window.rem_size());
|
||||
|
||||
let padded_bounds = Bounds::from_corners(
|
||||
bounds.origin + point(border.left + padding.left, border.top),
|
||||
bounds.bottom_right() - point(border.right + padding.right, border.bottom),
|
||||
);
|
||||
|
||||
let y_flipped = if let Some(scroll_handle) = &self.scroll_handle {
|
||||
let scroll_state = scroll_handle.0.borrow();
|
||||
scroll_state.y_flipped
|
||||
|
||||
@@ -289,13 +289,10 @@ pub trait PlatformDisplay: Send + Sync + Debug {
|
||||
|
||||
/// Get the default bounds for this display to place a window
|
||||
fn default_bounds(&self) -> Bounds<Pixels> {
|
||||
let bounds = self.bounds();
|
||||
let center = bounds.center();
|
||||
let clipped_window_size = DEFAULT_WINDOW_SIZE.min(&bounds.size);
|
||||
|
||||
let offset = clipped_window_size / 2.0;
|
||||
let center = self.bounds().center();
|
||||
let offset = DEFAULT_WINDOW_SIZE / 2.0;
|
||||
let origin = point(center.x - offset.width, center.y - offset.height);
|
||||
Bounds::new(origin, clipped_window_size)
|
||||
Bounds::new(origin, DEFAULT_WINDOW_SIZE)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -951,30 +951,17 @@ fn file_save_dialog(
|
||||
) -> Result<Option<PathBuf>> {
|
||||
let dialog: IFileSaveDialog = unsafe { CoCreateInstance(&FileSaveDialog, None, CLSCTX_ALL)? };
|
||||
if !directory.to_string_lossy().is_empty()
|
||||
&& let Some(full_path) = directory
|
||||
.canonicalize()
|
||||
.context("failed to canonicalize directory")
|
||||
.log_err()
|
||||
&& let Some(full_path) = directory.canonicalize().log_err()
|
||||
{
|
||||
let full_path = SanitizedPath::new(&full_path);
|
||||
let full_path_string = full_path.to_string();
|
||||
let path_item: IShellItem =
|
||||
unsafe { SHCreateItemFromParsingName(&HSTRING::from(full_path_string), None)? };
|
||||
unsafe {
|
||||
dialog
|
||||
.SetFolder(&path_item)
|
||||
.context("failed to set dialog folder")
|
||||
.log_err()
|
||||
};
|
||||
unsafe { dialog.SetFolder(&path_item).log_err() };
|
||||
}
|
||||
|
||||
if let Some(suggested_name) = suggested_name {
|
||||
unsafe {
|
||||
dialog
|
||||
.SetFileName(&HSTRING::from(suggested_name))
|
||||
.context("failed to set file name")
|
||||
.log_err()
|
||||
};
|
||||
unsafe { dialog.SetFileName(&HSTRING::from(suggested_name)).log_err() };
|
||||
}
|
||||
|
||||
unsafe {
|
||||
|
||||
@@ -169,9 +169,7 @@ impl WindowsWindowState {
|
||||
length: std::mem::size_of::<WINDOWPLACEMENT>() as u32,
|
||||
..Default::default()
|
||||
};
|
||||
GetWindowPlacement(self.hwnd, &mut placement)
|
||||
.context("failed to get window placement")
|
||||
.log_err();
|
||||
GetWindowPlacement(self.hwnd, &mut placement).log_err();
|
||||
placement
|
||||
};
|
||||
(
|
||||
@@ -256,9 +254,7 @@ impl WindowsWindowInner {
|
||||
lock.fullscreen_restore_bounds = window_bounds;
|
||||
let style = WINDOW_STYLE(unsafe { get_window_long(this.hwnd, GWL_STYLE) } as _);
|
||||
let mut rc = RECT::default();
|
||||
unsafe { GetWindowRect(this.hwnd, &mut rc) }
|
||||
.context("failed to get window rect")
|
||||
.log_err();
|
||||
unsafe { GetWindowRect(this.hwnd, &mut rc) }.log_err();
|
||||
let _ = lock.fullscreen.insert(StyleAndBounds {
|
||||
style,
|
||||
x: rc.left,
|
||||
@@ -305,20 +301,15 @@ impl WindowsWindowInner {
|
||||
};
|
||||
match open_status.state {
|
||||
WindowOpenState::Maximized => unsafe {
|
||||
SetWindowPlacement(self.hwnd, &open_status.placement)
|
||||
.context("failed to set window placement")?;
|
||||
SetWindowPlacement(self.hwnd, &open_status.placement)?;
|
||||
ShowWindowAsync(self.hwnd, SW_MAXIMIZE).ok()?;
|
||||
},
|
||||
WindowOpenState::Fullscreen => {
|
||||
unsafe {
|
||||
SetWindowPlacement(self.hwnd, &open_status.placement)
|
||||
.context("failed to set window placement")?
|
||||
};
|
||||
unsafe { SetWindowPlacement(self.hwnd, &open_status.placement)? };
|
||||
self.toggle_fullscreen();
|
||||
}
|
||||
WindowOpenState::Windowed => unsafe {
|
||||
SetWindowPlacement(self.hwnd, &open_status.placement)
|
||||
.context("failed to set window placement")?;
|
||||
SetWindowPlacement(self.hwnd, &open_status.placement)?;
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -4313,14 +4313,14 @@ impl Window {
|
||||
}
|
||||
|
||||
/// Returns a generic handler that invokes the given handler with the view and context associated with the given view handle.
|
||||
pub fn handler_for<E: 'static, Callback: Fn(&mut E, &mut Window, &mut Context<E>) + 'static>(
|
||||
pub fn handler_for<V: Render, Callback: Fn(&mut V, &mut Window, &mut Context<V>) + 'static>(
|
||||
&self,
|
||||
entity: &Entity<E>,
|
||||
view: &Entity<V>,
|
||||
f: Callback,
|
||||
) -> impl Fn(&mut Window, &mut App) + 'static {
|
||||
let entity = entity.downgrade();
|
||||
) -> impl Fn(&mut Window, &mut App) + use<V, Callback> {
|
||||
let view = view.downgrade();
|
||||
move |window: &mut Window, cx: &mut App| {
|
||||
entity.update(cx, |entity, cx| f(entity, window, cx)).ok();
|
||||
view.update(cx, |view, cx| f(view, window, cx)).ok();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4718,7 +4718,7 @@ impl<V: 'static + Render> WindowHandle<V> {
|
||||
.get(self.id)
|
||||
.and_then(|window| {
|
||||
window
|
||||
.as_deref()
|
||||
.as_ref()
|
||||
.and_then(|window| window.root.clone())
|
||||
.map(|root_view| root_view.downcast::<V>())
|
||||
})
|
||||
|
||||
@@ -18,8 +18,8 @@ pub use crate::{
|
||||
proto,
|
||||
};
|
||||
use anyhow::{Context as _, Result};
|
||||
use clock::Lamport;
|
||||
pub use clock::ReplicaId;
|
||||
use clock::{AGENT_REPLICA_ID, Lamport};
|
||||
use collections::HashMap;
|
||||
use fs::MTime;
|
||||
use futures::channel::oneshot;
|
||||
@@ -828,11 +828,7 @@ impl Buffer {
|
||||
/// Create a new buffer with the given base text.
|
||||
pub fn local<T: Into<String>>(base_text: T, cx: &Context<Self>) -> Self {
|
||||
Self::build(
|
||||
TextBuffer::new(
|
||||
ReplicaId::LOCAL,
|
||||
cx.entity_id().as_non_zero_u64().into(),
|
||||
base_text.into(),
|
||||
),
|
||||
TextBuffer::new(0, cx.entity_id().as_non_zero_u64().into(), base_text.into()),
|
||||
None,
|
||||
Capability::ReadWrite,
|
||||
)
|
||||
@@ -846,7 +842,7 @@ impl Buffer {
|
||||
) -> Self {
|
||||
Self::build(
|
||||
TextBuffer::new_normalized(
|
||||
ReplicaId::LOCAL,
|
||||
0,
|
||||
cx.entity_id().as_non_zero_u64().into(),
|
||||
line_ending,
|
||||
base_text_normalized,
|
||||
@@ -995,10 +991,10 @@ impl Buffer {
|
||||
language: None,
|
||||
remote_selections: Default::default(),
|
||||
diagnostics: Default::default(),
|
||||
diagnostics_timestamp: Lamport::MIN,
|
||||
diagnostics_timestamp: Default::default(),
|
||||
completion_triggers: Default::default(),
|
||||
completion_triggers_per_language_server: Default::default(),
|
||||
completion_triggers_timestamp: Lamport::MIN,
|
||||
completion_triggers_timestamp: Default::default(),
|
||||
deferred_ops: OperationQueue::new(),
|
||||
has_conflict: false,
|
||||
change_bits: Default::default(),
|
||||
@@ -1016,8 +1012,7 @@ impl Buffer {
|
||||
let buffer_id = entity_id.as_non_zero_u64().into();
|
||||
async move {
|
||||
let text =
|
||||
TextBuffer::new_normalized(ReplicaId::LOCAL, buffer_id, Default::default(), text)
|
||||
.snapshot();
|
||||
TextBuffer::new_normalized(0, buffer_id, Default::default(), text).snapshot();
|
||||
let mut syntax = SyntaxMap::new(&text).snapshot();
|
||||
if let Some(language) = language.clone() {
|
||||
let language_registry = language_registry.clone();
|
||||
@@ -1038,13 +1033,8 @@ impl Buffer {
|
||||
pub fn build_empty_snapshot(cx: &mut App) -> BufferSnapshot {
|
||||
let entity_id = cx.reserve_entity::<Self>().entity_id();
|
||||
let buffer_id = entity_id.as_non_zero_u64().into();
|
||||
let text = TextBuffer::new_normalized(
|
||||
ReplicaId::LOCAL,
|
||||
buffer_id,
|
||||
Default::default(),
|
||||
Rope::new(),
|
||||
)
|
||||
.snapshot();
|
||||
let text =
|
||||
TextBuffer::new_normalized(0, buffer_id, Default::default(), Rope::new()).snapshot();
|
||||
let syntax = SyntaxMap::new(&text).snapshot();
|
||||
BufferSnapshot {
|
||||
text,
|
||||
@@ -1066,9 +1056,7 @@ impl Buffer {
|
||||
) -> BufferSnapshot {
|
||||
let entity_id = cx.reserve_entity::<Self>().entity_id();
|
||||
let buffer_id = entity_id.as_non_zero_u64().into();
|
||||
let text =
|
||||
TextBuffer::new_normalized(ReplicaId::LOCAL, buffer_id, Default::default(), text)
|
||||
.snapshot();
|
||||
let text = TextBuffer::new_normalized(0, buffer_id, Default::default(), text).snapshot();
|
||||
let mut syntax = SyntaxMap::new(&text).snapshot();
|
||||
if let Some(language) = language.clone() {
|
||||
syntax.reparse(&text, language_registry, language);
|
||||
@@ -2272,7 +2260,7 @@ impl Buffer {
|
||||
) {
|
||||
let lamport_timestamp = self.text.lamport_clock.tick();
|
||||
self.remote_selections.insert(
|
||||
ReplicaId::AGENT,
|
||||
AGENT_REPLICA_ID,
|
||||
SelectionSet {
|
||||
selections,
|
||||
lamport_timestamp,
|
||||
@@ -2929,7 +2917,7 @@ impl Buffer {
|
||||
|
||||
edits.push((range, new_text));
|
||||
}
|
||||
log::info!("mutating buffer {:?} with {:?}", self.replica_id(), edits);
|
||||
log::info!("mutating buffer {} with {:?}", self.replica_id(), edits);
|
||||
self.edit(edits, None, cx);
|
||||
}
|
||||
|
||||
|
||||
@@ -70,13 +70,7 @@ fn test_line_endings(cx: &mut gpui::App) {
|
||||
fn test_set_line_ending(cx: &mut TestAppContext) {
|
||||
let base = cx.new(|cx| Buffer::local("one\ntwo\nthree\n", cx));
|
||||
let base_replica = cx.new(|cx| {
|
||||
Buffer::from_proto(
|
||||
ReplicaId::new(1),
|
||||
Capability::ReadWrite,
|
||||
base.read(cx).to_proto(cx),
|
||||
None,
|
||||
)
|
||||
.unwrap()
|
||||
Buffer::from_proto(1, Capability::ReadWrite, base.read(cx).to_proto(cx), None).unwrap()
|
||||
});
|
||||
base.update(cx, |_buffer, cx| {
|
||||
cx.subscribe(&base_replica, |this, _, event, cx| {
|
||||
@@ -403,7 +397,7 @@ fn test_edit_events(cx: &mut gpui::App) {
|
||||
let buffer2 = cx.new(|cx| {
|
||||
Buffer::remote(
|
||||
BufferId::from(cx.entity_id().as_non_zero_u64()),
|
||||
ReplicaId::new(1),
|
||||
1,
|
||||
Capability::ReadWrite,
|
||||
"abcdef",
|
||||
)
|
||||
@@ -2781,8 +2775,7 @@ fn test_serialization(cx: &mut gpui::App) {
|
||||
.background_executor()
|
||||
.block(buffer1.read(cx).serialize_ops(None, cx));
|
||||
let buffer2 = cx.new(|cx| {
|
||||
let mut buffer =
|
||||
Buffer::from_proto(ReplicaId::new(1), Capability::ReadWrite, state, None).unwrap();
|
||||
let mut buffer = Buffer::from_proto(1, Capability::ReadWrite, state, None).unwrap();
|
||||
buffer.apply_ops(
|
||||
ops.into_iter()
|
||||
.map(|op| proto::deserialize_operation(op).unwrap()),
|
||||
@@ -2801,13 +2794,7 @@ fn test_branch_and_merge(cx: &mut TestAppContext) {
|
||||
|
||||
// Create a remote replica of the base buffer.
|
||||
let base_replica = cx.new(|cx| {
|
||||
Buffer::from_proto(
|
||||
ReplicaId::new(1),
|
||||
Capability::ReadWrite,
|
||||
base.read(cx).to_proto(cx),
|
||||
None,
|
||||
)
|
||||
.unwrap()
|
||||
Buffer::from_proto(1, Capability::ReadWrite, base.read(cx).to_proto(cx), None).unwrap()
|
||||
});
|
||||
base.update(cx, |_buffer, cx| {
|
||||
cx.subscribe(&base_replica, |this, _, event, cx| {
|
||||
@@ -3121,8 +3108,7 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
|
||||
.background_executor()
|
||||
.block(base_buffer.read(cx).serialize_ops(None, cx));
|
||||
let mut buffer =
|
||||
Buffer::from_proto(ReplicaId::new(i as u16), Capability::ReadWrite, state, None)
|
||||
.unwrap();
|
||||
Buffer::from_proto(i as ReplicaId, Capability::ReadWrite, state, None).unwrap();
|
||||
buffer.apply_ops(
|
||||
ops.into_iter()
|
||||
.map(|op| proto::deserialize_operation(op).unwrap()),
|
||||
@@ -3147,9 +3133,9 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
|
||||
});
|
||||
|
||||
buffers.push(buffer);
|
||||
replica_ids.push(ReplicaId::new(i as u16));
|
||||
network.lock().add_peer(ReplicaId::new(i as u16));
|
||||
log::info!("Adding initial peer with replica id {:?}", replica_ids[i]);
|
||||
replica_ids.push(i as ReplicaId);
|
||||
network.lock().add_peer(i as ReplicaId);
|
||||
log::info!("Adding initial peer with replica id {}", i);
|
||||
}
|
||||
|
||||
log::info!("initial text: {:?}", base_text);
|
||||
@@ -3169,14 +3155,14 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
|
||||
buffer.start_transaction_at(now);
|
||||
buffer.randomly_edit(&mut rng, 5, cx);
|
||||
buffer.end_transaction_at(now, cx);
|
||||
log::info!("buffer {:?} text: {:?}", buffer.replica_id(), buffer.text());
|
||||
log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
|
||||
});
|
||||
mutation_count -= 1;
|
||||
}
|
||||
30..=39 if mutation_count != 0 => {
|
||||
buffer.update(cx, |buffer, cx| {
|
||||
if rng.random_bool(0.2) {
|
||||
log::info!("peer {:?} clearing active selections", replica_id);
|
||||
log::info!("peer {} clearing active selections", replica_id);
|
||||
active_selections.remove(&replica_id);
|
||||
buffer.remove_active_selections(cx);
|
||||
} else {
|
||||
@@ -3193,7 +3179,7 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
|
||||
}
|
||||
let selections: Arc<[Selection<Anchor>]> = selections.into();
|
||||
log::info!(
|
||||
"peer {:?} setting active selections: {:?}",
|
||||
"peer {} setting active selections: {:?}",
|
||||
replica_id,
|
||||
selections
|
||||
);
|
||||
@@ -3203,7 +3189,7 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
|
||||
});
|
||||
mutation_count -= 1;
|
||||
}
|
||||
40..=49 if mutation_count != 0 && replica_id == ReplicaId::REMOTE_SERVER => {
|
||||
40..=49 if mutation_count != 0 && replica_id == 0 => {
|
||||
let entry_count = rng.random_range(1..=5);
|
||||
buffer.update(cx, |buffer, cx| {
|
||||
let diagnostics = DiagnosticSet::new(
|
||||
@@ -3221,11 +3207,7 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
|
||||
}),
|
||||
buffer,
|
||||
);
|
||||
log::info!(
|
||||
"peer {:?} setting diagnostics: {:?}",
|
||||
replica_id,
|
||||
diagnostics
|
||||
);
|
||||
log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
|
||||
buffer.update_diagnostics(LanguageServerId(0), diagnostics, cx);
|
||||
});
|
||||
mutation_count -= 1;
|
||||
@@ -3235,13 +3217,12 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
|
||||
let old_buffer_ops = cx
|
||||
.background_executor()
|
||||
.block(buffer.read(cx).serialize_ops(None, cx));
|
||||
let new_replica_id = (0..=replica_ids.len() as u16)
|
||||
.map(ReplicaId::new)
|
||||
let new_replica_id = (0..=replica_ids.len() as ReplicaId)
|
||||
.filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
|
||||
.choose(&mut rng)
|
||||
.unwrap();
|
||||
log::info!(
|
||||
"Adding new replica {:?} (replicating from {:?})",
|
||||
"Adding new replica {} (replicating from {})",
|
||||
new_replica_id,
|
||||
replica_id
|
||||
);
|
||||
@@ -3260,7 +3241,7 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
|
||||
cx,
|
||||
);
|
||||
log::info!(
|
||||
"New replica {:?} text: {:?}",
|
||||
"New replica {} text: {:?}",
|
||||
new_buffer.replica_id(),
|
||||
new_buffer.text()
|
||||
);
|
||||
@@ -3283,7 +3264,7 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
|
||||
}));
|
||||
network.lock().replicate(replica_id, new_replica_id);
|
||||
|
||||
if new_replica_id.as_u16() as usize == replica_ids.len() {
|
||||
if new_replica_id as usize == replica_ids.len() {
|
||||
replica_ids.push(new_replica_id);
|
||||
} else {
|
||||
let new_buffer = new_buffer.take().unwrap();
|
||||
@@ -3295,7 +3276,7 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
|
||||
.map(|op| proto::deserialize_operation(op).unwrap());
|
||||
if ops.len() > 0 {
|
||||
log::info!(
|
||||
"peer {:?} (version: {:?}) applying {} ops from the network. {:?}",
|
||||
"peer {} (version: {:?}) applying {} ops from the network. {:?}",
|
||||
new_replica_id,
|
||||
buffer.read(cx).version(),
|
||||
ops.len(),
|
||||
@@ -3306,13 +3287,13 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
|
||||
});
|
||||
}
|
||||
}
|
||||
buffers[new_replica_id.as_u16() as usize] = new_buffer;
|
||||
buffers[new_replica_id as usize] = new_buffer;
|
||||
}
|
||||
}
|
||||
60..=69 if mutation_count != 0 => {
|
||||
buffer.update(cx, |buffer, cx| {
|
||||
buffer.randomly_undo_redo(&mut rng, cx);
|
||||
log::info!("buffer {:?} text: {:?}", buffer.replica_id(), buffer.text());
|
||||
log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
|
||||
});
|
||||
mutation_count -= 1;
|
||||
}
|
||||
@@ -3324,7 +3305,7 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
|
||||
.map(|op| proto::deserialize_operation(op).unwrap());
|
||||
if ops.len() > 0 {
|
||||
log::info!(
|
||||
"peer {:?} (version: {:?}) applying {} ops from the network. {:?}",
|
||||
"peer {} (version: {:?}) applying {} ops from the network. {:?}",
|
||||
replica_id,
|
||||
buffer.read(cx).version(),
|
||||
ops.len(),
|
||||
@@ -3354,13 +3335,13 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
|
||||
assert_eq!(
|
||||
buffer.version(),
|
||||
first_buffer.version(),
|
||||
"Replica {:?} version != Replica 0 version",
|
||||
"Replica {} version != Replica 0 version",
|
||||
buffer.replica_id()
|
||||
);
|
||||
assert_eq!(
|
||||
buffer.text(),
|
||||
first_buffer.text(),
|
||||
"Replica {:?} text != Replica 0 text",
|
||||
"Replica {} text != Replica 0 text",
|
||||
buffer.replica_id()
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -3370,7 +3351,7 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
|
||||
first_buffer
|
||||
.diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
|
||||
.collect::<Vec<_>>(),
|
||||
"Replica {:?} diagnostics != Replica 0 diagnostics",
|
||||
"Replica {} diagnostics != Replica 0 diagnostics",
|
||||
buffer.replica_id()
|
||||
);
|
||||
}
|
||||
@@ -3389,7 +3370,7 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
|
||||
assert_eq!(
|
||||
actual_remote_selections,
|
||||
expected_remote_selections,
|
||||
"Replica {:?} remote selections != expected selections",
|
||||
"Replica {} remote selections != expected selections",
|
||||
buffer.replica_id()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -39,14 +39,14 @@ pub fn serialize_operation(operation: &crate::Operation) -> proto::Operation {
|
||||
|
||||
crate::Operation::Buffer(text::Operation::Undo(undo)) => {
|
||||
proto::operation::Variant::Undo(proto::operation::Undo {
|
||||
replica_id: undo.timestamp.replica_id.as_u16() as u32,
|
||||
replica_id: undo.timestamp.replica_id as u32,
|
||||
lamport_timestamp: undo.timestamp.value,
|
||||
version: serialize_version(&undo.version),
|
||||
counts: undo
|
||||
.counts
|
||||
.iter()
|
||||
.map(|(edit_id, count)| proto::UndoCount {
|
||||
replica_id: edit_id.replica_id.as_u16() as u32,
|
||||
replica_id: edit_id.replica_id as u32,
|
||||
lamport_timestamp: edit_id.value,
|
||||
count: *count,
|
||||
})
|
||||
@@ -60,7 +60,7 @@ pub fn serialize_operation(operation: &crate::Operation) -> proto::Operation {
|
||||
lamport_timestamp,
|
||||
cursor_shape,
|
||||
} => proto::operation::Variant::UpdateSelections(proto::operation::UpdateSelections {
|
||||
replica_id: lamport_timestamp.replica_id.as_u16() as u32,
|
||||
replica_id: lamport_timestamp.replica_id as u32,
|
||||
lamport_timestamp: lamport_timestamp.value,
|
||||
selections: serialize_selections(selections),
|
||||
line_mode: *line_mode,
|
||||
@@ -72,7 +72,7 @@ pub fn serialize_operation(operation: &crate::Operation) -> proto::Operation {
|
||||
server_id,
|
||||
diagnostics,
|
||||
} => proto::operation::Variant::UpdateDiagnostics(proto::UpdateDiagnostics {
|
||||
replica_id: lamport_timestamp.replica_id.as_u16() as u32,
|
||||
replica_id: lamport_timestamp.replica_id as u32,
|
||||
lamport_timestamp: lamport_timestamp.value,
|
||||
server_id: server_id.0 as u64,
|
||||
diagnostics: serialize_diagnostics(diagnostics.iter()),
|
||||
@@ -84,7 +84,7 @@ pub fn serialize_operation(operation: &crate::Operation) -> proto::Operation {
|
||||
server_id,
|
||||
} => proto::operation::Variant::UpdateCompletionTriggers(
|
||||
proto::operation::UpdateCompletionTriggers {
|
||||
replica_id: lamport_timestamp.replica_id.as_u16() as u32,
|
||||
replica_id: lamport_timestamp.replica_id as u32,
|
||||
lamport_timestamp: lamport_timestamp.value,
|
||||
triggers: triggers.clone(),
|
||||
language_server_id: server_id.to_proto(),
|
||||
@@ -95,7 +95,7 @@ pub fn serialize_operation(operation: &crate::Operation) -> proto::Operation {
|
||||
line_ending,
|
||||
lamport_timestamp,
|
||||
} => proto::operation::Variant::UpdateLineEnding(proto::operation::UpdateLineEnding {
|
||||
replica_id: lamport_timestamp.replica_id.as_u16() as u32,
|
||||
replica_id: lamport_timestamp.replica_id as u32,
|
||||
lamport_timestamp: lamport_timestamp.value,
|
||||
line_ending: serialize_line_ending(*line_ending) as i32,
|
||||
}),
|
||||
@@ -106,7 +106,7 @@ pub fn serialize_operation(operation: &crate::Operation) -> proto::Operation {
|
||||
/// Serializes an [`EditOperation`] to be sent over RPC.
|
||||
pub fn serialize_edit_operation(operation: &EditOperation) -> proto::operation::Edit {
|
||||
proto::operation::Edit {
|
||||
replica_id: operation.timestamp.replica_id.as_u16() as u32,
|
||||
replica_id: operation.timestamp.replica_id as u32,
|
||||
lamport_timestamp: operation.timestamp.value,
|
||||
version: serialize_version(&operation.version),
|
||||
ranges: operation.ranges.iter().map(serialize_range).collect(),
|
||||
@@ -123,12 +123,12 @@ pub fn serialize_undo_map_entry(
|
||||
(edit_id, counts): (&clock::Lamport, &[(clock::Lamport, u32)]),
|
||||
) -> proto::UndoMapEntry {
|
||||
proto::UndoMapEntry {
|
||||
replica_id: edit_id.replica_id.as_u16() as u32,
|
||||
replica_id: edit_id.replica_id as u32,
|
||||
local_timestamp: edit_id.value,
|
||||
counts: counts
|
||||
.iter()
|
||||
.map(|(undo_id, count)| proto::UndoCount {
|
||||
replica_id: undo_id.replica_id.as_u16() as u32,
|
||||
replica_id: undo_id.replica_id as u32,
|
||||
lamport_timestamp: undo_id.value,
|
||||
count: *count,
|
||||
})
|
||||
@@ -246,7 +246,7 @@ pub fn serialize_diagnostics<'a>(
|
||||
/// Serializes an [`Anchor`] to be sent over RPC.
|
||||
pub fn serialize_anchor(anchor: &Anchor) -> proto::Anchor {
|
||||
proto::Anchor {
|
||||
replica_id: anchor.timestamp.replica_id.as_u16() as u32,
|
||||
replica_id: anchor.timestamp.replica_id as u32,
|
||||
timestamp: anchor.timestamp.value,
|
||||
offset: anchor.offset as u64,
|
||||
bias: match anchor.bias {
|
||||
@@ -283,7 +283,7 @@ pub fn deserialize_operation(message: proto::Operation) -> Result<crate::Operati
|
||||
proto::operation::Variant::Undo(undo) => {
|
||||
crate::Operation::Buffer(text::Operation::Undo(UndoOperation {
|
||||
timestamp: clock::Lamport {
|
||||
replica_id: ReplicaId::new(undo.replica_id as u16),
|
||||
replica_id: undo.replica_id as ReplicaId,
|
||||
value: undo.lamport_timestamp,
|
||||
},
|
||||
version: deserialize_version(&undo.version),
|
||||
@@ -293,7 +293,7 @@ pub fn deserialize_operation(message: proto::Operation) -> Result<crate::Operati
|
||||
.map(|c| {
|
||||
(
|
||||
clock::Lamport {
|
||||
replica_id: ReplicaId::new(c.replica_id as u16),
|
||||
replica_id: c.replica_id as ReplicaId,
|
||||
value: c.lamport_timestamp,
|
||||
},
|
||||
c.count,
|
||||
@@ -319,7 +319,7 @@ pub fn deserialize_operation(message: proto::Operation) -> Result<crate::Operati
|
||||
|
||||
crate::Operation::UpdateSelections {
|
||||
lamport_timestamp: clock::Lamport {
|
||||
replica_id: ReplicaId::new(message.replica_id as u16),
|
||||
replica_id: message.replica_id as ReplicaId,
|
||||
value: message.lamport_timestamp,
|
||||
},
|
||||
selections: Arc::from(selections),
|
||||
@@ -333,7 +333,7 @@ pub fn deserialize_operation(message: proto::Operation) -> Result<crate::Operati
|
||||
proto::operation::Variant::UpdateDiagnostics(message) => {
|
||||
crate::Operation::UpdateDiagnostics {
|
||||
lamport_timestamp: clock::Lamport {
|
||||
replica_id: ReplicaId::new(message.replica_id as u16),
|
||||
replica_id: message.replica_id as ReplicaId,
|
||||
value: message.lamport_timestamp,
|
||||
},
|
||||
server_id: LanguageServerId(message.server_id as usize),
|
||||
@@ -344,7 +344,7 @@ pub fn deserialize_operation(message: proto::Operation) -> Result<crate::Operati
|
||||
crate::Operation::UpdateCompletionTriggers {
|
||||
triggers: message.triggers,
|
||||
lamport_timestamp: clock::Lamport {
|
||||
replica_id: ReplicaId::new(message.replica_id as u16),
|
||||
replica_id: message.replica_id as ReplicaId,
|
||||
value: message.lamport_timestamp,
|
||||
},
|
||||
server_id: LanguageServerId::from_proto(message.language_server_id),
|
||||
@@ -353,7 +353,7 @@ pub fn deserialize_operation(message: proto::Operation) -> Result<crate::Operati
|
||||
proto::operation::Variant::UpdateLineEnding(message) => {
|
||||
crate::Operation::UpdateLineEnding {
|
||||
lamport_timestamp: clock::Lamport {
|
||||
replica_id: ReplicaId::new(message.replica_id as u16),
|
||||
replica_id: message.replica_id as ReplicaId,
|
||||
value: message.lamport_timestamp,
|
||||
},
|
||||
line_ending: deserialize_line_ending(
|
||||
@@ -370,7 +370,7 @@ pub fn deserialize_operation(message: proto::Operation) -> Result<crate::Operati
|
||||
pub fn deserialize_edit_operation(edit: proto::operation::Edit) -> EditOperation {
|
||||
EditOperation {
|
||||
timestamp: clock::Lamport {
|
||||
replica_id: ReplicaId::new(edit.replica_id as u16),
|
||||
replica_id: edit.replica_id as ReplicaId,
|
||||
value: edit.lamport_timestamp,
|
||||
},
|
||||
version: deserialize_version(&edit.version),
|
||||
@@ -385,7 +385,7 @@ pub fn deserialize_undo_map_entry(
|
||||
) -> (clock::Lamport, Vec<(clock::Lamport, u32)>) {
|
||||
(
|
||||
clock::Lamport {
|
||||
replica_id: ReplicaId::new(entry.replica_id as u16),
|
||||
replica_id: entry.replica_id as u16,
|
||||
value: entry.local_timestamp,
|
||||
},
|
||||
entry
|
||||
@@ -394,7 +394,7 @@ pub fn deserialize_undo_map_entry(
|
||||
.map(|undo_count| {
|
||||
(
|
||||
clock::Lamport {
|
||||
replica_id: ReplicaId::new(undo_count.replica_id as u16),
|
||||
replica_id: undo_count.replica_id as u16,
|
||||
value: undo_count.lamport_timestamp,
|
||||
},
|
||||
undo_count.count,
|
||||
@@ -480,7 +480,7 @@ pub fn deserialize_anchor(anchor: proto::Anchor) -> Option<Anchor> {
|
||||
};
|
||||
Some(Anchor {
|
||||
timestamp: clock::Lamport {
|
||||
replica_id: ReplicaId::new(anchor.replica_id as u16),
|
||||
replica_id: anchor.replica_id as ReplicaId,
|
||||
value: anchor.timestamp,
|
||||
},
|
||||
offset: anchor.offset as usize,
|
||||
@@ -524,7 +524,7 @@ pub fn lamport_timestamp_for_operation(operation: &proto::Operation) -> Option<c
|
||||
}
|
||||
|
||||
Some(clock::Lamport {
|
||||
replica_id: ReplicaId::new(replica_id as u16),
|
||||
replica_id: replica_id as ReplicaId,
|
||||
value,
|
||||
})
|
||||
}
|
||||
@@ -559,7 +559,7 @@ pub fn deserialize_transaction(transaction: proto::Transaction) -> Result<Transa
|
||||
/// Serializes a [`clock::Lamport`] timestamp to be sent over RPC.
|
||||
pub fn serialize_timestamp(timestamp: clock::Lamport) -> proto::LamportTimestamp {
|
||||
proto::LamportTimestamp {
|
||||
replica_id: timestamp.replica_id.as_u16() as u32,
|
||||
replica_id: timestamp.replica_id as u32,
|
||||
value: timestamp.value,
|
||||
}
|
||||
}
|
||||
@@ -567,7 +567,7 @@ pub fn serialize_timestamp(timestamp: clock::Lamport) -> proto::LamportTimestamp
|
||||
/// Deserializes a [`clock::Lamport`] timestamp from the RPC representation.
|
||||
pub fn deserialize_timestamp(timestamp: proto::LamportTimestamp) -> clock::Lamport {
|
||||
clock::Lamport {
|
||||
replica_id: ReplicaId::new(timestamp.replica_id as u16),
|
||||
replica_id: timestamp.replica_id as ReplicaId,
|
||||
value: timestamp.value,
|
||||
}
|
||||
}
|
||||
@@ -590,7 +590,7 @@ pub fn deserialize_version(message: &[proto::VectorClockEntry]) -> clock::Global
|
||||
let mut version = clock::Global::new();
|
||||
for entry in message {
|
||||
version.observe(clock::Lamport {
|
||||
replica_id: ReplicaId::new(entry.replica_id as u16),
|
||||
replica_id: entry.replica_id as ReplicaId,
|
||||
value: entry.timestamp,
|
||||
});
|
||||
}
|
||||
@@ -602,7 +602,7 @@ pub fn serialize_version(version: &clock::Global) -> Vec<proto::VectorClockEntry
|
||||
version
|
||||
.iter()
|
||||
.map(|entry| proto::VectorClockEntry {
|
||||
replica_id: entry.replica_id.as_u16() as u32,
|
||||
replica_id: entry.replica_id as u32,
|
||||
timestamp: entry.value,
|
||||
})
|
||||
.collect()
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::{
|
||||
use gpui::App;
|
||||
use rand::rngs::StdRng;
|
||||
use std::{env, ops::Range, sync::Arc};
|
||||
use text::{Buffer, BufferId, ReplicaId};
|
||||
use text::{Buffer, BufferId};
|
||||
use tree_sitter::Node;
|
||||
use unindent::Unindent as _;
|
||||
use util::test::marked_text_ranges;
|
||||
@@ -88,7 +88,7 @@ fn test_syntax_map_layers_for_range(cx: &mut App) {
|
||||
registry.add(language.clone());
|
||||
|
||||
let mut buffer = Buffer::new(
|
||||
ReplicaId::LOCAL,
|
||||
0,
|
||||
BufferId::new(1).unwrap(),
|
||||
r#"
|
||||
fn a() {
|
||||
@@ -189,7 +189,7 @@ fn test_dynamic_language_injection(cx: &mut App) {
|
||||
registry.add(Arc::new(ruby_lang()));
|
||||
|
||||
let mut buffer = Buffer::new(
|
||||
ReplicaId::LOCAL,
|
||||
0,
|
||||
BufferId::new(1).unwrap(),
|
||||
r#"
|
||||
This is a code block:
|
||||
@@ -811,7 +811,7 @@ fn test_syntax_map_languages_loading_with_erb(cx: &mut App) {
|
||||
.unindent();
|
||||
|
||||
let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
|
||||
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), text);
|
||||
let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), text);
|
||||
|
||||
let mut syntax_map = SyntaxMap::new(&buffer);
|
||||
syntax_map.set_language_registry(registry.clone());
|
||||
@@ -978,7 +978,7 @@ fn test_random_edits(
|
||||
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
|
||||
.unwrap_or(10);
|
||||
|
||||
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), text);
|
||||
let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), text);
|
||||
|
||||
let mut syntax_map = SyntaxMap::new(&buffer);
|
||||
syntax_map.set_language_registry(registry.clone());
|
||||
@@ -1159,7 +1159,7 @@ fn test_edit_sequence(language_name: &str, steps: &[&str], cx: &mut App) -> (Buf
|
||||
.now_or_never()
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "");
|
||||
let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), "");
|
||||
|
||||
let mut mutated_syntax_map = SyntaxMap::new(&buffer);
|
||||
mutated_syntax_map.set_language_registry(registry.clone());
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
((comment) @injection.content
|
||||
(#set! injection.language "comment")
|
||||
)
|
||||
@@ -1,30 +1,27 @@
|
||||
[
|
||||
"const"
|
||||
"enum"
|
||||
"extern"
|
||||
"inline"
|
||||
"sizeof"
|
||||
"static"
|
||||
"struct"
|
||||
"typedef"
|
||||
"union"
|
||||
"volatile"
|
||||
] @keyword
|
||||
|
||||
[
|
||||
"break"
|
||||
"case"
|
||||
"const"
|
||||
"continue"
|
||||
"default"
|
||||
"do"
|
||||
"else"
|
||||
"enum"
|
||||
"extern"
|
||||
"for"
|
||||
"goto"
|
||||
"if"
|
||||
"inline"
|
||||
"return"
|
||||
"sizeof"
|
||||
"static"
|
||||
"struct"
|
||||
"switch"
|
||||
"typedef"
|
||||
"union"
|
||||
"volatile"
|
||||
"while"
|
||||
] @keyword.control
|
||||
] @keyword
|
||||
|
||||
[
|
||||
"#define"
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
((comment) @injection.content
|
||||
(#set! injection.language "comment")
|
||||
)
|
||||
|
||||
(preproc_def
|
||||
value: (preproc_arg) @injection.content
|
||||
(#set! injection.language "c"))
|
||||
|
||||
@@ -106,19 +106,32 @@ type: (primitive_type) @type.builtin
|
||||
[
|
||||
"alignas"
|
||||
"alignof"
|
||||
"break"
|
||||
"case"
|
||||
"catch"
|
||||
"class"
|
||||
"co_await"
|
||||
"co_return"
|
||||
"co_yield"
|
||||
"concept"
|
||||
"consteval"
|
||||
"constexpr"
|
||||
"constinit"
|
||||
"continue"
|
||||
"decltype"
|
||||
"default"
|
||||
"delete"
|
||||
"do"
|
||||
"else"
|
||||
"enum"
|
||||
"explicit"
|
||||
"export"
|
||||
"extern"
|
||||
"final"
|
||||
"for"
|
||||
"friend"
|
||||
"goto"
|
||||
"if"
|
||||
"import"
|
||||
"inline"
|
||||
"module"
|
||||
@@ -131,40 +144,24 @@ type: (primitive_type) @type.builtin
|
||||
"protected"
|
||||
"public"
|
||||
"requires"
|
||||
"return"
|
||||
"sizeof"
|
||||
"struct"
|
||||
"switch"
|
||||
"template"
|
||||
"thread_local"
|
||||
"throw"
|
||||
"try"
|
||||
"typedef"
|
||||
"typename"
|
||||
"union"
|
||||
"using"
|
||||
"virtual"
|
||||
"while"
|
||||
(storage_class_specifier)
|
||||
(type_qualifier)
|
||||
] @keyword
|
||||
|
||||
[
|
||||
"break"
|
||||
"case"
|
||||
"catch"
|
||||
"co_await"
|
||||
"co_return"
|
||||
"co_yield"
|
||||
"continue"
|
||||
"default"
|
||||
"do"
|
||||
"else"
|
||||
"for"
|
||||
"goto"
|
||||
"if"
|
||||
"return"
|
||||
"switch"
|
||||
"throw"
|
||||
"try"
|
||||
"while"
|
||||
] @keyword.control
|
||||
|
||||
[
|
||||
"#define"
|
||||
"#elif"
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
((comment) @injection.content
|
||||
(#set! injection.language "comment")
|
||||
)
|
||||
|
||||
(preproc_def
|
||||
value: (preproc_arg) @injection.content
|
||||
(#set! injection.language "c++"))
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
((comment) @content
|
||||
(#set! injection.language "comment")
|
||||
)
|
||||
|
||||
((scissors) @content
|
||||
(#set! "language" "diff"))
|
||||
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
; Refer to https://github.com/nvim-treesitter/nvim-treesitter/blob/master/queries/go/injections.scm#L4C1-L16C41
|
||||
((comment) @injection.content
|
||||
(#set! injection.language "comment")
|
||||
)
|
||||
|
||||
(call_expression
|
||||
(selector_expression) @_function
|
||||
(#any-of? @_function
|
||||
|
||||
@@ -171,51 +171,46 @@
|
||||
"as"
|
||||
"async"
|
||||
"await"
|
||||
"break"
|
||||
"case"
|
||||
"catch"
|
||||
"class"
|
||||
"const"
|
||||
"continue"
|
||||
"debugger"
|
||||
"default"
|
||||
"delete"
|
||||
"do"
|
||||
"else"
|
||||
"export"
|
||||
"extends"
|
||||
"finally"
|
||||
"for"
|
||||
"from"
|
||||
"function"
|
||||
"get"
|
||||
"if"
|
||||
"import"
|
||||
"in"
|
||||
"instanceof"
|
||||
"let"
|
||||
"new"
|
||||
"of"
|
||||
"return"
|
||||
"set"
|
||||
"static"
|
||||
"switch"
|
||||
"target"
|
||||
"throw"
|
||||
"try"
|
||||
"typeof"
|
||||
"using"
|
||||
"var"
|
||||
"void"
|
||||
"with"
|
||||
] @keyword
|
||||
|
||||
[
|
||||
"break"
|
||||
"case"
|
||||
"catch"
|
||||
"continue"
|
||||
"do"
|
||||
"else"
|
||||
"finally"
|
||||
"for"
|
||||
"if"
|
||||
"return"
|
||||
"switch"
|
||||
"throw"
|
||||
"try"
|
||||
"while"
|
||||
"with"
|
||||
"yield"
|
||||
] @keyword.control
|
||||
|
||||
(switch_default "default" @keyword.control)
|
||||
] @keyword
|
||||
|
||||
(template_substitution
|
||||
"${" @punctuation.special
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
((comment) @injection.content
|
||||
(#set! injection.language "comment")
|
||||
)
|
||||
|
||||
(((comment) @_jsdoc_comment
|
||||
(#match? @_jsdoc_comment "(?s)^/[*][*][^*].*[*]/$")) @injection.content
|
||||
(#set! injection.language "jsdoc"))
|
||||
|
||||
@@ -31,103 +31,38 @@
|
||||
(export_statement
|
||||
(lexical_declaration
|
||||
["let" "const"] @context
|
||||
; Multiple names may be exported - @item is on the declarator to keep
|
||||
; ranges distinct.
|
||||
(variable_declarator
|
||||
name: (identifier) @name) @item)))
|
||||
|
||||
; Exported array destructuring
|
||||
(program
|
||||
(export_statement
|
||||
(lexical_declaration
|
||||
["let" "const"] @context
|
||||
(variable_declarator
|
||||
name: (array_pattern
|
||||
[
|
||||
(identifier) @name @item
|
||||
(assignment_pattern left: (identifier) @name @item)
|
||||
(rest_pattern (identifier) @name @item)
|
||||
])))))
|
||||
|
||||
; Exported object destructuring
|
||||
(program
|
||||
(export_statement
|
||||
(lexical_declaration
|
||||
["let" "const"] @context
|
||||
(variable_declarator
|
||||
name: (object_pattern
|
||||
[(shorthand_property_identifier_pattern) @name @item
|
||||
(pair_pattern
|
||||
value: (identifier) @name @item)
|
||||
(pair_pattern
|
||||
value: (assignment_pattern left: (identifier) @name @item))
|
||||
(rest_pattern (identifier) @name @item)])))))
|
||||
name: (_) @name) @item)))
|
||||
|
||||
(program
|
||||
(lexical_declaration
|
||||
["let" "const"] @context
|
||||
; Multiple names may be defined - @item is on the declarator to keep
|
||||
; ranges distinct.
|
||||
(variable_declarator
|
||||
name: (identifier) @name) @item))
|
||||
|
||||
; Top-level array destructuring
|
||||
(program
|
||||
(lexical_declaration
|
||||
["let" "const"] @context
|
||||
(variable_declarator
|
||||
name: (array_pattern
|
||||
[
|
||||
(identifier) @name @item
|
||||
(assignment_pattern left: (identifier) @name @item)
|
||||
(rest_pattern (identifier) @name @item)
|
||||
]))))
|
||||
|
||||
; Top-level object destructuring
|
||||
(program
|
||||
(lexical_declaration
|
||||
["let" "const"] @context
|
||||
(variable_declarator
|
||||
name: (object_pattern
|
||||
[(shorthand_property_identifier_pattern) @name @item
|
||||
(pair_pattern
|
||||
value: (identifier) @name @item)
|
||||
(pair_pattern
|
||||
value: (assignment_pattern left: (identifier) @name @item))
|
||||
(rest_pattern (identifier) @name @item)]))))
|
||||
name: (_) @name) @item))
|
||||
|
||||
(class_declaration
|
||||
"class" @context
|
||||
name: (_) @name) @item
|
||||
|
||||
; Method definitions in classes (not in object literals)
|
||||
(class_body
|
||||
(method_definition
|
||||
[
|
||||
"get"
|
||||
"set"
|
||||
"async"
|
||||
"*"
|
||||
"readonly"
|
||||
"static"
|
||||
(override_modifier)
|
||||
(accessibility_modifier)
|
||||
]* @context
|
||||
name: (_) @name
|
||||
parameters: (formal_parameters
|
||||
"(" @context
|
||||
")" @context)) @item)
|
||||
|
||||
; Object literal methods
|
||||
(variable_declarator
|
||||
value: (object
|
||||
(method_definition
|
||||
[
|
||||
"get"
|
||||
"set"
|
||||
"async"
|
||||
"*"
|
||||
]* @context
|
||||
name: (_) @name
|
||||
parameters: (formal_parameters
|
||||
"(" @context
|
||||
")" @context)) @item))
|
||||
(method_definition
|
||||
[
|
||||
"get"
|
||||
"set"
|
||||
"async"
|
||||
"*"
|
||||
"readonly"
|
||||
"static"
|
||||
(override_modifier)
|
||||
(accessibility_modifier)
|
||||
]* @context
|
||||
name: (_) @name
|
||||
parameters: (formal_parameters
|
||||
"(" @context
|
||||
")" @context)) @item
|
||||
|
||||
(public_field_definition
|
||||
[
|
||||
@@ -181,43 +116,4 @@
|
||||
)
|
||||
) @item
|
||||
|
||||
; Object properties
|
||||
(pair
|
||||
key: [
|
||||
(property_identifier) @name
|
||||
(string (string_fragment) @name)
|
||||
(number) @name
|
||||
(computed_property_name) @name
|
||||
]) @item
|
||||
|
||||
; Nested variables in function bodies
|
||||
(statement_block
|
||||
(lexical_declaration
|
||||
["let" "const"] @context
|
||||
(variable_declarator
|
||||
name: (identifier) @name) @item))
|
||||
|
||||
; Nested array destructuring in functions
|
||||
(statement_block
|
||||
(lexical_declaration
|
||||
["let" "const"] @context
|
||||
(variable_declarator
|
||||
name: (array_pattern
|
||||
[
|
||||
(identifier) @name @item
|
||||
(assignment_pattern left: (identifier) @name @item)
|
||||
(rest_pattern (identifier) @name @item)
|
||||
]))))
|
||||
|
||||
; Nested object destructuring in functions
|
||||
(statement_block
|
||||
(lexical_declaration
|
||||
["let" "const"] @context
|
||||
(variable_declarator
|
||||
name: (object_pattern
|
||||
[(shorthand_property_identifier_pattern) @name @item
|
||||
(pair_pattern value: (identifier) @name @item)
|
||||
(pair_pattern value: (assignment_pattern left: (identifier) @name @item))
|
||||
(rest_pattern (identifier) @name @item)]))))
|
||||
|
||||
(comment) @annotation
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
((comment) @injection.content
|
||||
(#set! injection.language "comment")
|
||||
)
|
||||
@@ -83,20 +83,29 @@
|
||||
"as"
|
||||
"async"
|
||||
"await"
|
||||
"break"
|
||||
"const"
|
||||
"continue"
|
||||
"default"
|
||||
"dyn"
|
||||
"else"
|
||||
"enum"
|
||||
"extern"
|
||||
"fn"
|
||||
"for"
|
||||
"if"
|
||||
"impl"
|
||||
"in"
|
||||
"let"
|
||||
"loop"
|
||||
"macro_rules!"
|
||||
"match"
|
||||
"mod"
|
||||
"move"
|
||||
"pub"
|
||||
"raw"
|
||||
"ref"
|
||||
"return"
|
||||
"static"
|
||||
"struct"
|
||||
"trait"
|
||||
@@ -105,25 +114,13 @@
|
||||
"unsafe"
|
||||
"use"
|
||||
"where"
|
||||
"while"
|
||||
"yield"
|
||||
(crate)
|
||||
(mutable_specifier)
|
||||
(super)
|
||||
] @keyword
|
||||
|
||||
[
|
||||
"break"
|
||||
"continue"
|
||||
"else"
|
||||
"for"
|
||||
"if"
|
||||
"in"
|
||||
"loop"
|
||||
"match"
|
||||
"return"
|
||||
"while"
|
||||
"yield"
|
||||
] @keyword.control
|
||||
|
||||
[
|
||||
(string_literal)
|
||||
(raw_string_literal)
|
||||
|
||||
@@ -171,16 +171,25 @@
|
||||
"as"
|
||||
"async"
|
||||
"await"
|
||||
"break"
|
||||
"case"
|
||||
"catch"
|
||||
"class"
|
||||
"const"
|
||||
"continue"
|
||||
"debugger"
|
||||
"default"
|
||||
"delete"
|
||||
"do"
|
||||
"else"
|
||||
"export"
|
||||
"extends"
|
||||
"finally"
|
||||
"for"
|
||||
"from"
|
||||
"function"
|
||||
"get"
|
||||
"if"
|
||||
"import"
|
||||
"in"
|
||||
"instanceof"
|
||||
@@ -188,36 +197,22 @@
|
||||
"let"
|
||||
"new"
|
||||
"of"
|
||||
"return"
|
||||
"satisfies"
|
||||
"set"
|
||||
"static"
|
||||
"switch"
|
||||
"target"
|
||||
"throw"
|
||||
"try"
|
||||
"typeof"
|
||||
"using"
|
||||
"var"
|
||||
"void"
|
||||
"with"
|
||||
] @keyword
|
||||
|
||||
[
|
||||
"break"
|
||||
"case"
|
||||
"catch"
|
||||
"continue"
|
||||
"do"
|
||||
"else"
|
||||
"finally"
|
||||
"for"
|
||||
"if"
|
||||
"return"
|
||||
"switch"
|
||||
"throw"
|
||||
"try"
|
||||
"while"
|
||||
"with"
|
||||
"yield"
|
||||
] @keyword.control
|
||||
|
||||
(switch_default "default" @keyword.control)
|
||||
] @keyword
|
||||
|
||||
(template_substitution
|
||||
"${" @punctuation.special
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
((comment) @injection.content
|
||||
(#set! injection.language "comment")
|
||||
)
|
||||
|
||||
(((comment) @_jsdoc_comment
|
||||
(#match? @_jsdoc_comment "(?s)^/[*][*][^*].*[*]/$")) @injection.content
|
||||
(#set! injection.language "jsdoc"))
|
||||
|
||||
@@ -1110,7 +1110,7 @@ mod tests {
|
||||
|
||||
let text = r#"
|
||||
function a() {
|
||||
// local variables are included
|
||||
// local variables are omitted
|
||||
let a1 = 1;
|
||||
// all functions are included
|
||||
async function a2() {}
|
||||
@@ -1133,7 +1133,6 @@ mod tests {
|
||||
.collect::<Vec<_>>(),
|
||||
&[
|
||||
("function a()", 0),
|
||||
("let a1", 1),
|
||||
("async function a2()", 1),
|
||||
("let b", 0),
|
||||
("function getB()", 0),
|
||||
@@ -1142,223 +1141,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_outline_with_destructuring(cx: &mut TestAppContext) {
|
||||
let language = crate::language(
|
||||
"typescript",
|
||||
tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),
|
||||
);
|
||||
|
||||
let text = r#"
|
||||
// Top-level destructuring
|
||||
const { a1, a2 } = a;
|
||||
const [b1, b2] = b;
|
||||
|
||||
// Defaults and rest
|
||||
const [c1 = 1, , c2, ...rest1] = c;
|
||||
const { d1, d2: e1, f1 = 2, g1: h1 = 3, ...rest2 } = d;
|
||||
|
||||
function processData() {
|
||||
// Nested object destructuring
|
||||
const { c1, c2 } = c;
|
||||
// Nested array destructuring
|
||||
const [d1, d2, d3] = d;
|
||||
// Destructuring with renaming
|
||||
const { f1: g1 } = f;
|
||||
// With defaults
|
||||
const [x = 10, y] = xy;
|
||||
}
|
||||
|
||||
class DataHandler {
|
||||
method() {
|
||||
// Destructuring in class method
|
||||
const { a1, a2 } = a;
|
||||
const [b1, ...b2] = b;
|
||||
}
|
||||
}
|
||||
"#
|
||||
.unindent();
|
||||
|
||||
let buffer = cx.new(|cx| language::Buffer::local(text, cx).with_language(language, cx));
|
||||
let outline = buffer.read_with(cx, |buffer, _| buffer.snapshot().outline(None));
|
||||
assert_eq!(
|
||||
outline
|
||||
.items
|
||||
.iter()
|
||||
.map(|item| (item.text.as_str(), item.depth))
|
||||
.collect::<Vec<_>>(),
|
||||
&[
|
||||
("const a1", 0),
|
||||
("const a2", 0),
|
||||
("const b1", 0),
|
||||
("const b2", 0),
|
||||
("const c1", 0),
|
||||
("const c2", 0),
|
||||
("const rest1", 0),
|
||||
("const d1", 0),
|
||||
("const e1", 0),
|
||||
("const h1", 0),
|
||||
("const rest2", 0),
|
||||
("function processData()", 0),
|
||||
("const c1", 1),
|
||||
("const c2", 1),
|
||||
("const d1", 1),
|
||||
("const d2", 1),
|
||||
("const d3", 1),
|
||||
("const g1", 1),
|
||||
("const x", 1),
|
||||
("const y", 1),
|
||||
("class DataHandler", 0),
|
||||
("method()", 1),
|
||||
("const a1", 2),
|
||||
("const a2", 2),
|
||||
("const b1", 2),
|
||||
("const b2", 2),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_outline_with_object_properties(cx: &mut TestAppContext) {
|
||||
let language = crate::language(
|
||||
"typescript",
|
||||
tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),
|
||||
);
|
||||
|
||||
let text = r#"
|
||||
// Object with function properties
|
||||
const o = { m() {}, async n() {}, g: function* () {}, h: () => {}, k: function () {} };
|
||||
|
||||
// Object with primitive properties
|
||||
const p = { p1: 1, p2: "hello", p3: true };
|
||||
|
||||
// Nested objects
|
||||
const q = {
|
||||
r: {
|
||||
// won't be included due to one-level depth limit
|
||||
s: 1
|
||||
},
|
||||
t: 2
|
||||
};
|
||||
|
||||
function getData() {
|
||||
const local = { x: 1, y: 2 };
|
||||
return local;
|
||||
}
|
||||
"#
|
||||
.unindent();
|
||||
|
||||
let buffer = cx.new(|cx| language::Buffer::local(text, cx).with_language(language, cx));
|
||||
let outline = buffer.read_with(cx, |buffer, _| buffer.snapshot().outline(None));
|
||||
assert_eq!(
|
||||
outline
|
||||
.items
|
||||
.iter()
|
||||
.map(|item| (item.text.as_str(), item.depth))
|
||||
.collect::<Vec<_>>(),
|
||||
&[
|
||||
("const o", 0),
|
||||
("m()", 1),
|
||||
("async n()", 1),
|
||||
("g", 1),
|
||||
("h", 1),
|
||||
("k", 1),
|
||||
("const p", 0),
|
||||
("p1", 1),
|
||||
("p2", 1),
|
||||
("p3", 1),
|
||||
("const q", 0),
|
||||
("r", 1),
|
||||
("s", 2),
|
||||
("t", 1),
|
||||
("function getData()", 0),
|
||||
("const local", 1),
|
||||
("x", 2),
|
||||
("y", 2),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_outline_with_computed_property_names(cx: &mut TestAppContext) {
|
||||
let language = crate::language(
|
||||
"typescript",
|
||||
tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),
|
||||
);
|
||||
|
||||
let text = r#"
|
||||
// Symbols as object keys
|
||||
const sym = Symbol("test");
|
||||
const obj1 = {
|
||||
[sym]: 1,
|
||||
[Symbol("inline")]: 2,
|
||||
normalKey: 3
|
||||
};
|
||||
|
||||
// Enums as object keys
|
||||
enum Color { Red, Blue, Green }
|
||||
|
||||
const obj2 = {
|
||||
[Color.Red]: "red value",
|
||||
[Color.Blue]: "blue value",
|
||||
regularProp: "normal"
|
||||
};
|
||||
|
||||
// Mixed computed properties
|
||||
const key = "dynamic";
|
||||
const obj3 = {
|
||||
[key]: 1,
|
||||
["string" + "concat"]: 2,
|
||||
[1 + 1]: 3,
|
||||
static: 4
|
||||
};
|
||||
|
||||
// Nested objects with computed properties
|
||||
const obj4 = {
|
||||
[sym]: {
|
||||
nested: 1
|
||||
},
|
||||
regular: {
|
||||
[key]: 2
|
||||
}
|
||||
};
|
||||
"#
|
||||
.unindent();
|
||||
|
||||
let buffer = cx.new(|cx| language::Buffer::local(text, cx).with_language(language, cx));
|
||||
let outline = buffer.read_with(cx, |buffer, _| buffer.snapshot().outline(None));
|
||||
assert_eq!(
|
||||
outline
|
||||
.items
|
||||
.iter()
|
||||
.map(|item| (item.text.as_str(), item.depth))
|
||||
.collect::<Vec<_>>(),
|
||||
&[
|
||||
("const sym", 0),
|
||||
("const obj1", 0),
|
||||
("[sym]", 1),
|
||||
("[Symbol(\"inline\")]", 1),
|
||||
("normalKey", 1),
|
||||
("enum Color", 0),
|
||||
("const obj2", 0),
|
||||
("[Color.Red]", 1),
|
||||
("[Color.Blue]", 1),
|
||||
("regularProp", 1),
|
||||
("const key", 0),
|
||||
("const obj3", 0),
|
||||
("[key]", 1),
|
||||
("[\"string\" + \"concat\"]", 1),
|
||||
("[1 + 1]", 1),
|
||||
("static", 1),
|
||||
("const obj4", 0),
|
||||
("[sym]", 1),
|
||||
("nested", 2),
|
||||
("regular", 1),
|
||||
("[key]", 2),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_generator_function_outline(cx: &mut TestAppContext) {
|
||||
let language = crate::language("javascript", tree_sitter_typescript::LANGUAGE_TSX.into());
|
||||
|
||||
@@ -218,18 +218,27 @@
|
||||
"as"
|
||||
"async"
|
||||
"await"
|
||||
"break"
|
||||
"case"
|
||||
"catch"
|
||||
"class"
|
||||
"const"
|
||||
"continue"
|
||||
"debugger"
|
||||
"declare"
|
||||
"default"
|
||||
"delete"
|
||||
"do"
|
||||
"else"
|
||||
"enum"
|
||||
"export"
|
||||
"extends"
|
||||
"finally"
|
||||
"for"
|
||||
"from"
|
||||
"function"
|
||||
"get"
|
||||
"if"
|
||||
"implements"
|
||||
"import"
|
||||
"in"
|
||||
@@ -248,34 +257,20 @@
|
||||
"protected"
|
||||
"public"
|
||||
"readonly"
|
||||
"return"
|
||||
"satisfies"
|
||||
"set"
|
||||
"static"
|
||||
"switch"
|
||||
"target"
|
||||
"throw"
|
||||
"try"
|
||||
"type"
|
||||
"typeof"
|
||||
"using"
|
||||
"var"
|
||||
"void"
|
||||
"with"
|
||||
] @keyword
|
||||
|
||||
[
|
||||
"break"
|
||||
"case"
|
||||
"catch"
|
||||
"continue"
|
||||
"do"
|
||||
"else"
|
||||
"finally"
|
||||
"for"
|
||||
"if"
|
||||
"return"
|
||||
"switch"
|
||||
"throw"
|
||||
"try"
|
||||
"while"
|
||||
"with"
|
||||
"yield"
|
||||
] @keyword.control
|
||||
|
||||
(switch_default "default" @keyword.control)
|
||||
] @keyword
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
((comment) @injection.content
|
||||
(#set! injection.language "comment")
|
||||
)
|
||||
|
||||
(((comment) @_jsdoc_comment
|
||||
(#match? @_jsdoc_comment "(?s)^/[*][*][^*].*[*]/$")) @injection.content
|
||||
(#set! injection.language "jsdoc"))
|
||||
|
||||
@@ -34,64 +34,18 @@
|
||||
(export_statement
|
||||
(lexical_declaration
|
||||
["let" "const"] @context
|
||||
; Multiple names may be exported - @item is on the declarator to keep
|
||||
; ranges distinct.
|
||||
(variable_declarator
|
||||
name: (identifier) @name) @item))
|
||||
|
||||
; Exported array destructuring
|
||||
(export_statement
|
||||
(lexical_declaration
|
||||
["let" "const"] @context
|
||||
(variable_declarator
|
||||
name: (array_pattern
|
||||
[
|
||||
(identifier) @name @item
|
||||
(assignment_pattern left: (identifier) @name @item)
|
||||
(rest_pattern (identifier) @name @item)
|
||||
]))))
|
||||
|
||||
; Exported object destructuring
|
||||
(export_statement
|
||||
(lexical_declaration
|
||||
["let" "const"] @context
|
||||
(variable_declarator
|
||||
name: (object_pattern
|
||||
[(shorthand_property_identifier_pattern) @name @item
|
||||
(pair_pattern
|
||||
value: (identifier) @name @item)
|
||||
(pair_pattern
|
||||
value: (assignment_pattern left: (identifier) @name @item))
|
||||
(rest_pattern (identifier) @name @item)]))))
|
||||
name: (_) @name) @item))
|
||||
|
||||
(program
|
||||
(lexical_declaration
|
||||
["let" "const"] @context
|
||||
; Multiple names may be defined - @item is on the declarator to keep
|
||||
; ranges distinct.
|
||||
(variable_declarator
|
||||
name: (identifier) @name) @item))
|
||||
|
||||
; Top-level array destructuring
|
||||
(program
|
||||
(lexical_declaration
|
||||
["let" "const"] @context
|
||||
(variable_declarator
|
||||
name: (array_pattern
|
||||
[
|
||||
(identifier) @name @item
|
||||
(assignment_pattern left: (identifier) @name @item)
|
||||
(rest_pattern (identifier) @name @item)
|
||||
]))))
|
||||
|
||||
; Top-level object destructuring
|
||||
(program
|
||||
(lexical_declaration
|
||||
["let" "const"] @context
|
||||
(variable_declarator
|
||||
name: (object_pattern
|
||||
[(shorthand_property_identifier_pattern) @name @item
|
||||
(pair_pattern
|
||||
value: (identifier) @name @item)
|
||||
(pair_pattern
|
||||
value: (assignment_pattern left: (identifier) @name @item))
|
||||
(rest_pattern (identifier) @name @item)]))))
|
||||
name: (_) @name) @item))
|
||||
|
||||
(class_declaration
|
||||
"class" @context
|
||||
@@ -102,38 +56,21 @@
|
||||
"class" @context
|
||||
name: (_) @name) @item
|
||||
|
||||
; Method definitions in classes (not in object literals)
|
||||
(class_body
|
||||
(method_definition
|
||||
[
|
||||
"get"
|
||||
"set"
|
||||
"async"
|
||||
"*"
|
||||
"readonly"
|
||||
"static"
|
||||
(override_modifier)
|
||||
(accessibility_modifier)
|
||||
]* @context
|
||||
name: (_) @name
|
||||
parameters: (formal_parameters
|
||||
"(" @context
|
||||
")" @context)) @item)
|
||||
|
||||
; Object literal methods
|
||||
(variable_declarator
|
||||
value: (object
|
||||
(method_definition
|
||||
[
|
||||
"get"
|
||||
"set"
|
||||
"async"
|
||||
"*"
|
||||
]* @context
|
||||
name: (_) @name
|
||||
parameters: (formal_parameters
|
||||
"(" @context
|
||||
")" @context)) @item))
|
||||
(method_definition
|
||||
[
|
||||
"get"
|
||||
"set"
|
||||
"async"
|
||||
"*"
|
||||
"readonly"
|
||||
"static"
|
||||
(override_modifier)
|
||||
(accessibility_modifier)
|
||||
]* @context
|
||||
name: (_) @name
|
||||
parameters: (formal_parameters
|
||||
"(" @context
|
||||
")" @context)) @item
|
||||
|
||||
(public_field_definition
|
||||
[
|
||||
@@ -187,44 +124,4 @@
|
||||
)
|
||||
) @item
|
||||
|
||||
; Object properties
|
||||
(pair
|
||||
key: [
|
||||
(property_identifier) @name
|
||||
(string (string_fragment) @name)
|
||||
(number) @name
|
||||
(computed_property_name) @name
|
||||
]) @item
|
||||
|
||||
|
||||
; Nested variables in function bodies
|
||||
(statement_block
|
||||
(lexical_declaration
|
||||
["let" "const"] @context
|
||||
(variable_declarator
|
||||
name: (identifier) @name) @item))
|
||||
|
||||
; Nested array destructuring in functions
|
||||
(statement_block
|
||||
(lexical_declaration
|
||||
["let" "const"] @context
|
||||
(variable_declarator
|
||||
name: (array_pattern
|
||||
[
|
||||
(identifier) @name @item
|
||||
(assignment_pattern left: (identifier) @name @item)
|
||||
(rest_pattern (identifier) @name @item)
|
||||
]))))
|
||||
|
||||
; Nested object destructuring in functions
|
||||
(statement_block
|
||||
(lexical_declaration
|
||||
["let" "const"] @context
|
||||
(variable_declarator
|
||||
name: (object_pattern
|
||||
[(shorthand_property_identifier_pattern) @name @item
|
||||
(pair_pattern value: (identifier) @name @item)
|
||||
(pair_pattern value: (assignment_pattern left: (identifier) @name @item))
|
||||
(rest_pattern (identifier) @name @item)]))))
|
||||
|
||||
(comment) @annotation
|
||||
|
||||
@@ -12,7 +12,7 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use util::{ResultExt, maybe, merge_json_value_into};
|
||||
use util::{ResultExt, maybe, merge_json_value_into, rel_path::RelPath};
|
||||
|
||||
fn typescript_server_binary_arguments(server_path: &Path) -> Vec<OsString> {
|
||||
vec![server_path.into(), "--stdio".into()]
|
||||
@@ -29,19 +29,19 @@ impl VtslsLspAdapter {
|
||||
|
||||
const TYPESCRIPT_PACKAGE_NAME: &'static str = "typescript";
|
||||
const TYPESCRIPT_TSDK_PATH: &'static str = "node_modules/typescript/lib";
|
||||
const TYPESCRIPT_YARN_TSDK_PATH: &'static str = ".yarn/sdks/typescript/lib";
|
||||
|
||||
pub fn new(node: NodeRuntime, fs: Arc<dyn Fs>) -> Self {
|
||||
VtslsLspAdapter { node, fs }
|
||||
}
|
||||
|
||||
async fn tsdk_path(&self, adapter: &Arc<dyn LspAdapterDelegate>) -> Option<&'static str> {
|
||||
let yarn_sdk = adapter
|
||||
.worktree_root_path()
|
||||
.join(Self::TYPESCRIPT_YARN_TSDK_PATH);
|
||||
let is_yarn = adapter
|
||||
.read_text_file(RelPath::unix(".yarn/sdks/typescript/lib/typescript.js").unwrap())
|
||||
.await
|
||||
.is_ok();
|
||||
|
||||
let tsdk_path = if self.fs.is_dir(&yarn_sdk).await {
|
||||
Self::TYPESCRIPT_YARN_TSDK_PATH
|
||||
let tsdk_path = if is_yarn {
|
||||
".yarn/sdks/typescript/lib"
|
||||
} else {
|
||||
Self::TYPESCRIPT_TSDK_PATH
|
||||
};
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
((comment) @injection.content
|
||||
(#set! injection.language "comment")
|
||||
)
|
||||
@@ -1,70 +0,0 @@
|
||||
use editor::Editor;
|
||||
use gpui::{Entity, Subscription, WeakEntity};
|
||||
use language::LineEnding;
|
||||
use ui::{Tooltip, prelude::*};
|
||||
use workspace::{StatusBarSettings, StatusItemView, item::ItemHandle, item::Settings};
|
||||
|
||||
use crate::{LineEndingSelector, Toggle};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct LineEndingIndicator {
|
||||
line_ending: Option<LineEnding>,
|
||||
active_editor: Option<WeakEntity<Editor>>,
|
||||
_observe_active_editor: Option<Subscription>,
|
||||
}
|
||||
|
||||
impl LineEndingIndicator {
|
||||
fn update(&mut self, editor: Entity<Editor>, _: &mut Window, cx: &mut Context<Self>) {
|
||||
self.line_ending = None;
|
||||
self.active_editor = None;
|
||||
|
||||
if let Some((_, buffer, _)) = editor.read(cx).active_excerpt(cx) {
|
||||
let line_ending = buffer.read(cx).line_ending();
|
||||
self.line_ending = Some(line_ending);
|
||||
self.active_editor = Some(editor.downgrade());
|
||||
}
|
||||
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for LineEndingIndicator {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
if !StatusBarSettings::get_global(cx).line_endings_button {
|
||||
return div();
|
||||
}
|
||||
|
||||
div().when_some(self.line_ending.as_ref(), |el, line_ending| {
|
||||
el.child(
|
||||
Button::new("change-line-ending", line_ending.label())
|
||||
.label_size(LabelSize::Small)
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
if let Some(editor) = this.active_editor.as_ref() {
|
||||
LineEndingSelector::toggle(editor, window, cx);
|
||||
}
|
||||
}))
|
||||
.tooltip(|window, cx| {
|
||||
Tooltip::for_action("Select Line Ending", &Toggle, window, cx)
|
||||
}),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl StatusItemView for LineEndingIndicator {
|
||||
fn set_active_pane_item(
|
||||
&mut self,
|
||||
active_pane_item: Option<&dyn ItemHandle>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
if let Some(editor) = active_pane_item.and_then(|item| item.downcast::<Editor>()) {
|
||||
self._observe_active_editor = Some(cx.observe_in(&editor, window, Self::update));
|
||||
self.update(editor, window, cx);
|
||||
} else {
|
||||
self.line_ending = None;
|
||||
self._observe_active_editor = None;
|
||||
}
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
mod line_ending_indicator;
|
||||
|
||||
use editor::Editor;
|
||||
use gpui::{DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Task, WeakEntity, actions};
|
||||
use language::{Buffer, LineEnding};
|
||||
pub use line_ending_indicator::LineEndingIndicator;
|
||||
use picker::{Picker, PickerDelegate};
|
||||
use project::Project;
|
||||
use std::sync::Arc;
|
||||
@@ -12,7 +9,7 @@ use util::ResultExt;
|
||||
use workspace::ModalView;
|
||||
|
||||
actions!(
|
||||
line_ending_selector,
|
||||
line_ending,
|
||||
[
|
||||
/// Toggles the line ending selector modal.
|
||||
Toggle
|
||||
@@ -175,7 +172,10 @@ impl PickerDelegate for LineEndingSelectorDelegate {
|
||||
_: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let line_ending = self.matches.get(ix)?;
|
||||
let label = line_ending.label();
|
||||
let label = match line_ending {
|
||||
LineEnding::Unix => "LF",
|
||||
LineEnding::Windows => "CRLF",
|
||||
};
|
||||
|
||||
let mut list_item = ListItem::new(ix)
|
||||
.inset(true)
|
||||
|
||||
@@ -19,8 +19,10 @@ use std::{
|
||||
};
|
||||
use theme::{ActiveTheme, SyntaxTheme, ThemeSettings};
|
||||
use ui::{
|
||||
Clickable, FluentBuilder, LinkPreview, StatefulInteractiveElement, StyledExt, StyledImage,
|
||||
ToggleState, Tooltip, VisibleOnHover, prelude::*, tooltip_container,
|
||||
ButtonCommon, Clickable, Color, FluentBuilder, IconButton, IconName, IconSize,
|
||||
InteractiveElement, Label, LabelCommon, LabelSize, LinkPreview, Pixels, Rems,
|
||||
StatefulInteractiveElement, StyledExt, StyledImage, ToggleState, Tooltip, VisibleOnHover,
|
||||
h_flex, relative, tooltip_container, v_flex,
|
||||
};
|
||||
use workspace::{OpenOptions, OpenVisible, Workspace};
|
||||
|
||||
@@ -49,8 +51,7 @@ pub struct RenderContext {
|
||||
buffer_text_style: TextStyle,
|
||||
text_style: TextStyle,
|
||||
border_color: Hsla,
|
||||
title_bar_background_color: Hsla,
|
||||
panel_background_color: Hsla,
|
||||
element_background_color: Hsla,
|
||||
text_color: Hsla,
|
||||
link_color: Hsla,
|
||||
window_rem_size: Pixels,
|
||||
@@ -86,8 +87,7 @@ impl RenderContext {
|
||||
text_style: window.text_style(),
|
||||
syntax_theme: theme.syntax().clone(),
|
||||
border_color: theme.colors().border,
|
||||
title_bar_background_color: theme.colors().title_bar_background,
|
||||
panel_background_color: theme.colors().panel_background,
|
||||
element_background_color: theme.colors().element_background,
|
||||
text_color: theme.colors().text,
|
||||
link_color: theme.colors().text_accent,
|
||||
window_rem_size: window.rem_size(),
|
||||
@@ -511,27 +511,28 @@ fn render_markdown_table(parsed: &ParsedMarkdownTable, cx: &mut RenderContext) -
|
||||
&parsed.column_alignments,
|
||||
&max_column_widths,
|
||||
true,
|
||||
0,
|
||||
cx,
|
||||
);
|
||||
|
||||
let body: Vec<AnyElement> = parsed
|
||||
.body
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, row)| {
|
||||
.map(|row| {
|
||||
render_markdown_table_row(
|
||||
row,
|
||||
&parsed.column_alignments,
|
||||
&max_column_widths,
|
||||
false,
|
||||
index,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
div().child(header).children(body).into_any()
|
||||
cx.with_common_p(v_flex())
|
||||
.w_full()
|
||||
.child(header)
|
||||
.children(body)
|
||||
.into_any()
|
||||
}
|
||||
|
||||
fn render_markdown_table_row(
|
||||
@@ -539,7 +540,6 @@ fn render_markdown_table_row(
|
||||
alignments: &Vec<ParsedMarkdownTableAlignment>,
|
||||
max_column_widths: &Vec<f32>,
|
||||
is_header: bool,
|
||||
row_index: usize,
|
||||
cx: &mut RenderContext,
|
||||
) -> AnyElement {
|
||||
let mut items = Vec::with_capacity(parsed.children.len());
|
||||
@@ -574,7 +574,7 @@ fn render_markdown_table_row(
|
||||
}
|
||||
|
||||
if is_header {
|
||||
cell = cell.bg(cx.title_bar_background_color).opacity(0.6)
|
||||
cell = cell.bg(cx.element_background_color)
|
||||
}
|
||||
|
||||
items.push(cell);
|
||||
@@ -588,10 +588,6 @@ fn render_markdown_table_row(
|
||||
row = row.border_b_1();
|
||||
}
|
||||
|
||||
if row_index % 2 == 1 {
|
||||
row = row.bg(cx.panel_background_color)
|
||||
}
|
||||
|
||||
row.children(items).into_any_element()
|
||||
}
|
||||
|
||||
|
||||
@@ -123,9 +123,3 @@ pub(crate) mod m_2025_10_16 {
|
||||
|
||||
pub(crate) use settings::restore_code_actions_on_format;
|
||||
}
|
||||
|
||||
pub(crate) mod m_2025_10_17 {
|
||||
mod settings;
|
||||
|
||||
pub(crate) use settings::make_file_finder_include_ignored_an_enum;
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
use anyhow::Result;
|
||||
use serde_json::Value;
|
||||
|
||||
pub fn make_file_finder_include_ignored_an_enum(value: &mut Value) -> Result<()> {
|
||||
let Some(file_finder) = value.get_mut("file_finder") else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let Some(file_finder_obj) = file_finder.as_object_mut() else {
|
||||
anyhow::bail!("Expected file_finder to be an object");
|
||||
};
|
||||
|
||||
let Some(include_ignored) = file_finder_obj.get_mut("include_ignored") else {
|
||||
return Ok(());
|
||||
};
|
||||
*include_ignored = match include_ignored {
|
||||
Value::Bool(true) => Value::String("all".to_string()),
|
||||
Value::Bool(false) => Value::String("indexed".to_string()),
|
||||
Value::Null => Value::String("smart".to_string()),
|
||||
_ => anyhow::bail!("Expected include_ignored to be a boolean or null"),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user