diff --git a/.github/ISSUE_TEMPLATE/07_bug_windows_alpha.yml b/.github/ISSUE_TEMPLATE/07_bug_windows_beta.yml
similarity index 86%
rename from .github/ISSUE_TEMPLATE/07_bug_windows_alpha.yml
rename to .github/ISSUE_TEMPLATE/07_bug_windows_beta.yml
index 826c2b8027144d4b658108e09c79e40490c3005d..b2b2a0f9dfcd5ddaa0dda41650864b053c5bb933 100644
--- a/.github/ISSUE_TEMPLATE/07_bug_windows_alpha.yml
+++ b/.github/ISSUE_TEMPLATE/07_bug_windows_beta.yml
@@ -1,8 +1,8 @@
-name: Bug Report (Windows Alpha)
-description: Zed Windows Alpha Related Bugs
+name: Bug Report (Windows Beta)
+description: Zed Windows Beta Related Bugs
type: "Bug"
labels: ["windows"]
-title: "Windows Alpha: "
+title: "Windows Beta: "
body:
- type: textarea
attributes:
diff --git a/.github/workflows/community_release_actions.yml b/.github/workflows/community_release_actions.yml
index 37ade90574e76cd95755aad6b5601a43946a271c..0f7a73649e9e1180c78a66ddf54055bf66f243f9 100644
--- a/.github/workflows/community_release_actions.yml
+++ b/.github/workflows/community_release_actions.yml
@@ -1,3 +1,6 @@
+# IF YOU UPDATE THE NAME OF ANY GITHUB SECRET, YOU MUST CHERRY PICK THE COMMIT
+# TO BOTH STABLE AND PREVIEW CHANNELS
+
name: Release Actions
on:
@@ -13,9 +16,9 @@ jobs:
id: get-release-url
run: |
if [ "${{ github.event.release.prerelease }}" == "true" ]; then
- URL="https://zed.dev/releases/preview/latest"
+ URL="https://zed.dev/releases/preview"
else
- URL="https://zed.dev/releases/stable/latest"
+ URL="https://zed.dev/releases/stable"
fi
echo "URL=$URL" >> "$GITHUB_OUTPUT"
diff --git a/Cargo.lock b/Cargo.lock
index 27c266a351c326d7d8f3f000e277dbf4b66321f5..71ef3081fd64233b94212a4ca511057bd62ea0f5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -174,7 +174,7 @@ dependencies = [
"rand 0.9.1",
"ref-cast",
"rope",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"settings",
@@ -195,16 +195,17 @@ dependencies = [
[[package]]
name = "agent-client-protocol"
-version = "0.2.1"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "003fb91bf1b8d6e15f72c45fb9171839af8241e81e3839fbb73536af113b7a79"
+checksum = "cc2526e80463b9742afed4829aedd6ae5632d6db778c6cc1fecb80c960c3521b"
dependencies = [
"anyhow",
"async-broadcast",
+ "async-trait",
"futures 0.3.31",
"log",
"parking_lot",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
]
@@ -256,7 +257,7 @@ dependencies = [
"prompt_store",
"reqwest_client",
"rust-embed",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"settings",
@@ -293,6 +294,7 @@ dependencies = [
"agent-client-protocol",
"agent_settings",
"anyhow",
+ "async-trait",
"client",
"collections",
"env_logger 0.11.8",
@@ -335,7 +337,7 @@ dependencies = [
"gpui",
"language_model",
"paths",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"serde_json_lenient",
@@ -410,7 +412,7 @@ dependencies = [
"release_channel",
"rope",
"rules_library",
- "schemars",
+ "schemars 1.0.1",
"search",
"serde",
"serde_json",
@@ -654,9 +656,10 @@ dependencies = [
"chrono",
"futures 0.3.31",
"http_client",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
+ "settings",
"strum 0.27.1",
"thiserror 2.0.12",
"workspace-hack",
@@ -899,7 +902,6 @@ version = "0.1.0"
dependencies = [
"anyhow",
"assistant_slash_command",
- "cargo_toml",
"chrono",
"collections",
"context_server",
@@ -922,7 +924,6 @@ dependencies = [
"settings",
"smol",
"text",
- "toml 0.8.20",
"ui",
"util",
"workspace",
@@ -1007,7 +1008,7 @@ dependencies = [
"regex",
"reqwest_client",
"rust-embed",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"settings",
@@ -1410,7 +1411,6 @@ dependencies = [
"log",
"parking_lot",
"rodio",
- "schemars",
"serde",
"settings",
"smol",
@@ -1443,7 +1443,6 @@ dependencies = [
"log",
"paths",
"release_channel",
- "schemars",
"serde",
"serde_json",
"settings",
@@ -2145,7 +2144,7 @@ dependencies = [
"aws-sdk-bedrockruntime",
"aws-smithy-types",
"futures 0.3.31",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"strum 0.27.1",
@@ -2191,7 +2190,7 @@ dependencies = [
"bitflags 2.9.0",
"cexpr",
"clang-sys",
- "itertools 0.11.0",
+ "itertools 0.12.1",
"lazy_static",
"lazycell",
"log",
@@ -2646,7 +2645,6 @@ dependencies = [
"log",
"postage",
"project",
- "schemars",
"serde",
"settings",
"telemetry",
@@ -2869,7 +2867,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eadd868a2ce9ca38de7eeafdcec9c7065ef89b42b32f0839278d55f35c54d1ff"
dependencies = [
"heck 0.4.1",
- "indexmap",
+ "indexmap 2.9.0",
"log",
"proc-macro2",
"quote",
@@ -2978,7 +2976,7 @@ dependencies = [
"num-traits",
"serde",
"wasm-bindgen",
- "windows-link",
+ "windows-link 0.1.1",
]
[[package]]
@@ -3147,7 +3145,7 @@ dependencies = [
"release_channel",
"rpc",
"rustls-pki-types",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"serde_urlencoded",
@@ -3458,7 +3456,6 @@ dependencies = [
"project",
"release_channel",
"rpc",
- "schemars",
"serde",
"serde_json",
"settings",
@@ -3480,7 +3477,7 @@ dependencies = [
name = "collections"
version = "0.1.0"
dependencies = [
- "indexmap",
+ "indexmap 2.9.0",
"rustc-hash 2.1.1",
"workspace-hack",
]
@@ -3642,9 +3639,10 @@ dependencies = [
"net",
"parking_lot",
"postage",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
+ "settings",
"smol",
"tempfile",
"url",
@@ -4442,7 +4440,7 @@ dependencies = [
"parking_lot",
"paths",
"proto",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"settings",
@@ -4462,7 +4460,7 @@ name = "dap-types"
version = "0.0.1"
source = "git+https://github.com/zed-industries/dap-types?rev=1b461b310481d01e02b2603c16d7144b926339f8#1b461b310481d01e02b2603c16d7144b926339f8"
dependencies = [
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
]
@@ -4492,6 +4490,41 @@ dependencies = [
"workspace-hack",
]
+[[package]]
+name = "darling"
+version = "0.20.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
+dependencies = [
+ "darling_core",
+ "darling_macro",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.20.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.20.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
+dependencies = [
+ "darling_core",
+ "quote",
+ "syn 2.0.101",
+]
+
[[package]]
name = "dashmap"
version = "5.5.3"
@@ -4633,7 +4666,7 @@ dependencies = [
"pretty_assertions",
"project",
"rpc",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"serde_json_lenient",
@@ -4674,7 +4707,7 @@ dependencies = [
"anyhow",
"futures 0.3.31",
"http_client",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"workspace-hack",
@@ -5202,7 +5235,7 @@ dependencies = [
"regex",
"release_channel",
"rpc",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"settings",
@@ -5709,7 +5742,6 @@ dependencies = [
"release_channel",
"remote",
"reqwest_client",
- "schemars",
"semantic_version",
"serde",
"serde_json",
@@ -5906,7 +5938,7 @@ dependencies = [
"picker",
"pretty_assertions",
"project",
- "schemars",
+ "schemars 1.0.1",
"search",
"serde",
"serde_json",
@@ -6765,7 +6797,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
dependencies = [
"fallible-iterator",
- "indexmap",
+ "indexmap 2.9.0",
"stable_deref_trait",
]
@@ -6788,7 +6820,7 @@ dependencies = [
"rand 0.9.1",
"regex",
"rope",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"smol",
@@ -6830,7 +6862,6 @@ dependencies = [
"indoc",
"pretty_assertions",
"regex",
- "schemars",
"serde",
"serde_json",
"settings",
@@ -6875,7 +6906,7 @@ dependencies = [
"postage",
"pretty_assertions",
"project",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"settings",
@@ -7702,7 +7733,6 @@ dependencies = [
name = "go_to_line"
version = "0.1.0"
dependencies = [
- "anyhow",
"editor",
"gpui",
"indoc",
@@ -7710,7 +7740,6 @@ dependencies = [
"menu",
"project",
"rope",
- "schemars",
"serde",
"serde_json",
"settings",
@@ -7742,9 +7771,10 @@ dependencies = [
"anyhow",
"futures 0.3.31",
"http_client",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
+ "settings",
"strum 0.27.1",
"workspace-hack",
]
@@ -7843,7 +7873,7 @@ dependencies = [
"reqwest_client",
"resvg",
"scap",
- "schemars",
+ "schemars 1.0.1",
"seahash",
"semantic_version",
"serde",
@@ -7929,7 +7959,7 @@ dependencies = [
"futures-sink",
"futures-util",
"http 0.2.12",
- "indexmap",
+ "indexmap 2.9.0",
"slab",
"tokio",
"tokio-util",
@@ -7948,7 +7978,7 @@ dependencies = [
"futures-core",
"futures-sink",
"http 1.3.1",
- "indexmap",
+ "indexmap 2.9.0",
"slab",
"tokio",
"tokio-util",
@@ -8647,6 +8677,12 @@ version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
[[package]]
name = "idna"
version = "1.0.3"
@@ -8729,7 +8765,6 @@ dependencies = [
"language",
"log",
"project",
- "schemars",
"serde",
"settings",
"theme",
@@ -8760,6 +8795,17 @@ version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408"
+[[package]]
+name = "indexmap"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
+dependencies = [
+ "autocfg",
+ "hashbrown 0.12.3",
+ "serde",
+]
+
[[package]]
name = "indexmap"
version = "2.9.0"
@@ -9124,7 +9170,7 @@ dependencies = [
"hashbrown 0.15.3",
"hex",
"ignore",
- "indexmap",
+ "indexmap 2.9.0",
"interim",
"itertools 0.14.0",
"jj-lib-proc-macros",
@@ -9221,10 +9267,10 @@ dependencies = [
"editor",
"gpui",
"log",
- "schemars",
"serde",
"settings",
"shellexpand 2.1.2",
+ "util",
"workspace",
"workspace-hack",
]
@@ -9455,7 +9501,7 @@ dependencies = [
"rand 0.9.1",
"regex",
"rpc",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"settings",
@@ -9529,9 +9575,10 @@ dependencies = [
"open_router",
"parking_lot",
"proto",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
+ "settings",
"smol",
"telemetry_events",
"thiserror 2.0.12",
@@ -9578,7 +9625,7 @@ dependencies = [
"partial-json-fixer",
"project",
"release_channel",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"settings",
@@ -9694,7 +9741,7 @@ dependencies = [
"regex",
"rope",
"rust-embed",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"serde_json_lenient",
@@ -10108,7 +10155,7 @@ dependencies = [
"anyhow",
"futures 0.3.31",
"http_client",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"workspace-hack",
@@ -10214,7 +10261,7 @@ dependencies = [
"parking_lot",
"postage",
"release_channel",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"smol",
@@ -10752,7 +10799,7 @@ dependencies = [
"anyhow",
"futures 0.3.31",
"http_client",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"strum 0.27.1",
@@ -10839,7 +10886,7 @@ dependencies = [
"half",
"hashbrown 0.15.3",
"hexf-parse",
- "indexmap",
+ "indexmap 2.9.0",
"log",
"num-traits",
"once_cell",
@@ -11498,7 +11545,7 @@ checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
dependencies = [
"crc32fast",
"hashbrown 0.15.3",
- "indexmap",
+ "indexmap 2.9.0",
"memchr",
]
@@ -11509,9 +11556,10 @@ dependencies = [
"anyhow",
"futures 0.3.31",
"http_client",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
+ "settings",
"workspace-hack",
]
@@ -11537,7 +11585,7 @@ dependencies = [
"notifications",
"picker",
"project",
- "schemars",
+ "schemars 1.0.1",
"serde",
"settings",
"telemetry",
@@ -11617,9 +11665,10 @@ dependencies = [
"futures 0.3.31",
"http_client",
"log",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
+ "settings",
"strum 0.27.1",
"workspace-hack",
]
@@ -11631,12 +11680,12 @@ dependencies = [
"anyhow",
"futures 0.3.31",
"http_client",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
+ "settings",
"strum 0.27.1",
"thiserror 2.0.12",
- "util",
"workspace-hack",
]
@@ -11810,7 +11859,6 @@ dependencies = [
"outline",
"pretty_assertions",
"project",
- "schemars",
"search",
"serde",
"serde_json",
@@ -12511,7 +12559,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
dependencies = [
"fixedbitset",
- "indexmap",
+ "indexmap 2.9.0",
]
[[package]]
@@ -12585,7 +12633,7 @@ dependencies = [
"env_logger 0.11.8",
"gpui",
"menu",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"ui",
@@ -12692,7 +12740,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eac26e981c03a6e53e0aee43c113e3202f5581d5360dae7bd2c70e800dd0451d"
dependencies = [
"base64 0.22.1",
- "indexmap",
+ "indexmap 2.9.0",
"quick-xml 0.32.0",
"serde",
"time",
@@ -13022,7 +13070,7 @@ dependencies = [
"gpui",
"http_client",
"image",
- "indexmap",
+ "indexmap 2.9.0",
"itertools 0.14.0",
"language",
"log",
@@ -13040,7 +13088,7 @@ dependencies = [
"release_channel",
"remote",
"rpc",
- "schemars",
+ "schemars 1.0.1",
"semver",
"serde",
"serde_json",
@@ -13082,12 +13130,12 @@ dependencies = [
"git",
"git_ui",
"gpui",
- "indexmap",
+ "indexmap 2.9.0",
"language",
"menu",
"pretty_assertions",
"project",
- "schemars",
+ "schemars 1.0.1",
"search",
"serde",
"serde_json",
@@ -13226,7 +13274,7 @@ checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4"
dependencies = [
"bytes 1.10.1",
"heck 0.5.0",
- "itertools 0.11.0",
+ "itertools 0.12.1",
"log",
"multimap",
"once_cell",
@@ -13259,7 +13307,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1"
dependencies = [
"anyhow",
- "itertools 0.11.0",
+ "itertools 0.12.1",
"proc-macro2",
"quote",
"syn 2.0.101",
@@ -13771,6 +13819,7 @@ dependencies = [
"futures 0.3.31",
"fuzzy",
"gpui",
+ "indoc",
"language",
"log",
"markdown",
@@ -13781,7 +13830,6 @@ dependencies = [
"project",
"release_channel",
"remote",
- "schemars",
"serde",
"serde_json",
"settings",
@@ -13791,6 +13839,7 @@ dependencies = [
"theme",
"ui",
"util",
+ "windows-registry 0.6.0",
"workspace",
"workspace-hack",
"zed_actions",
@@ -13954,9 +14003,9 @@ dependencies = [
"prost 0.9.0",
"release_channel",
"rpc",
- "schemars",
"serde",
"serde_json",
+ "settings",
"shlex",
"smol",
"tempfile",
@@ -13980,6 +14029,7 @@ dependencies = [
"clap",
"client",
"clock",
+ "collections",
"crash-handler",
"crashes",
"dap",
@@ -14008,6 +14058,7 @@ dependencies = [
"minidumper",
"node_runtime",
"paths",
+ "pretty_assertions",
"project",
"proto",
"release_channel",
@@ -14073,7 +14124,6 @@ dependencies = [
"picker",
"project",
"runtimelib",
- "schemars",
"serde",
"serde_json",
"settings",
@@ -14890,13 +14940,25 @@ dependencies = [
"anyhow",
"clap",
"env_logger 0.11.8",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"theme",
"workspace-hack",
]
+[[package]]
+name = "schemars"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f"
+dependencies = [
+ "dyn-clone",
+ "ref-cast",
+ "serde",
+ "serde_json",
+]
+
[[package]]
name = "schemars"
version = "1.0.1"
@@ -14904,7 +14966,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe8c9d1c68d67dd9f97ecbc6f932b60eb289c5dbddd8aa1405484a8fd2fcd984"
dependencies = [
"dyn-clone",
- "indexmap",
+ "indexmap 2.9.0",
"ref-cast",
"schemars_derive",
"serde",
@@ -15115,7 +15177,7 @@ dependencies = [
"language",
"menu",
"project",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"settings",
@@ -15265,7 +15327,7 @@ version = "1.0.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56177480b00303e689183f110b4e727bb4211d692c62d4fcd16d02be93077d40"
dependencies = [
- "indexmap",
+ "indexmap 2.9.0",
"itoa",
"memchr",
"ryu",
@@ -15278,7 +15340,7 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e033097bf0d2b59a62b42c18ebbb797503839b26afdda2c4e1415cb6c813540"
dependencies = [
- "indexmap",
+ "indexmap 2.9.0",
"itoa",
"memchr",
"ryu",
@@ -15327,6 +15389,37 @@ dependencies = [
"serde",
]
+[[package]]
+name = "serde_with"
+version = "3.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf65a400f8f66fb7b0552869ad70157166676db75ed8181f8104ea91cf9d0b42"
+dependencies = [
+ "base64 0.22.1",
+ "chrono",
+ "hex",
+ "indexmap 1.9.3",
+ "indexmap 2.9.0",
+ "schemars 0.9.0",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "serde_with_macros",
+ "time",
+]
+
+[[package]]
+name = "serde_with_macros"
+version = "3.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81679d9ed988d5e9a5e6531dc3f2c28efbd639cbd1dfb628df08edea6004da77"
+dependencies = [
+ "darling",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
[[package]]
name = "serial2"
version = "0.2.29"
@@ -15367,11 +15460,13 @@ dependencies = [
"pretty_assertions",
"release_channel",
"rust-embed",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"serde_json_lenient",
"serde_path_to_error",
+ "serde_repr",
+ "serde_with",
"settings_ui_macros",
"smallvec",
"tree-sitter",
@@ -15403,27 +15498,6 @@ dependencies = [
"zed_actions",
]
-[[package]]
-name = "settings_ui"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "command_palette_hooks",
- "debugger_ui",
- "editor",
- "feature_flags",
- "gpui",
- "menu",
- "serde",
- "serde_json",
- "settings",
- "smallvec",
- "theme",
- "ui",
- "workspace",
- "workspace-hack",
-]
-
[[package]]
name = "settings_ui_macros"
version = "0.1.0"
@@ -15721,7 +15795,7 @@ dependencies = [
"indoc",
"parking_lot",
"paths",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json_lenient",
"snippet",
@@ -15882,7 +15956,7 @@ dependencies = [
"futures-util",
"hashbrown 0.15.3",
"hashlink 0.10.0",
- "indexmap",
+ "indexmap 2.9.0",
"log",
"memchr",
"once_cell",
@@ -16801,7 +16875,7 @@ dependencies = [
"menu",
"picker",
"project",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"settings",
@@ -16881,7 +16955,7 @@ dependencies = [
"parking_lot",
"pretty_assertions",
"proto",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"serde_json_lenient",
@@ -16987,7 +17061,7 @@ dependencies = [
"rand 0.9.1",
"regex",
"release_channel",
- "schemars",
+ "schemars 1.0.1",
"serde",
"settings",
"smol",
@@ -17033,7 +17107,7 @@ dependencies = [
"project",
"rand 0.9.1",
"regex",
- "schemars",
+ "schemars 1.0.1",
"search",
"serde",
"serde_json",
@@ -17083,17 +17157,16 @@ dependencies = [
"fs",
"futures 0.3.31",
"gpui",
- "indexmap",
+ "indexmap 2.9.0",
"inventory",
"log",
"palette",
"parking_lot",
"refineable",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"serde_json_lenient",
- "serde_repr",
"settings",
"strum 0.27.1",
"thiserror 2.0.12",
@@ -17120,8 +17193,9 @@ version = "0.1.0"
dependencies = [
"anyhow",
"clap",
+ "collections",
"gpui",
- "indexmap",
+ "indexmap 2.9.0",
"log",
"palette",
"serde",
@@ -17375,7 +17449,7 @@ dependencies = [
"project",
"remote",
"rpc",
- "schemars",
+ "schemars 1.0.1",
"serde",
"settings",
"smallvec",
@@ -17574,7 +17648,7 @@ version = "0.22.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e"
dependencies = [
- "indexmap",
+ "indexmap 2.9.0",
"serde",
"serde_spanned",
"toml_datetime",
@@ -18185,7 +18259,7 @@ dependencies = [
"icons",
"itertools 0.14.0",
"menu",
- "schemars",
+ "schemars 1.0.1",
"serde",
"settings",
"smallvec",
@@ -18460,7 +18534,7 @@ dependencies = [
"rand 0.9.1",
"regex",
"rust-embed",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"serde_json_lenient",
@@ -18570,7 +18644,7 @@ name = "vercel"
version = "0.1.0"
dependencies = [
"anyhow",
- "schemars",
+ "schemars 1.0.1",
"serde",
"strum 0.27.1",
"workspace-hack",
@@ -18618,7 +18692,7 @@ dependencies = [
"project_panel",
"regex",
"release_channel",
- "schemars",
+ "schemars 1.0.1",
"search",
"serde",
"serde_json",
@@ -18639,10 +18713,7 @@ dependencies = [
name = "vim_mode_setting"
version = "0.1.0"
dependencies = [
- "anyhow",
"gpui",
- "schemars",
- "serde",
"settings",
"workspace-hack",
]
@@ -18877,7 +18948,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fd83062c17b9f4985d438603cde0a5e8c5c8198201a6937f778b607924c7da2"
dependencies = [
"anyhow",
- "indexmap",
+ "indexmap 2.9.0",
"serde",
"serde_derive",
"serde_json",
@@ -18895,7 +18966,7 @@ dependencies = [
"anyhow",
"auditable-serde",
"flate2",
- "indexmap",
+ "indexmap 2.9.0",
"serde",
"serde_derive",
"serde_json",
@@ -18925,7 +18996,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84e5df6dba6c0d7fafc63a450f1738451ed7a0b52295d83e868218fa286bf708"
dependencies = [
"bitflags 2.9.0",
- "indexmap",
+ "indexmap 2.9.0",
"semver",
]
@@ -18937,7 +19008,7 @@ checksum = "d06bfa36ab3ac2be0dee563380147a5b81ba10dd8885d7fbbc9eb574be67d185"
dependencies = [
"bitflags 2.9.0",
"hashbrown 0.15.3",
- "indexmap",
+ "indexmap 2.9.0",
"semver",
"serde",
]
@@ -18950,7 +19021,7 @@ checksum = "0f51cad774fb3c9461ab9bccc9c62dfb7388397b5deda31bf40e8108ccd678b2"
dependencies = [
"bitflags 2.9.0",
"hashbrown 0.15.3",
- "indexmap",
+ "indexmap 2.9.0",
"semver",
]
@@ -18979,7 +19050,7 @@ dependencies = [
"cfg-if",
"encoding_rs",
"hashbrown 0.14.5",
- "indexmap",
+ "indexmap 2.9.0",
"libc",
"log",
"mach2 0.4.2",
@@ -19103,7 +19174,7 @@ dependencies = [
"cranelift-bitset",
"cranelift-entity",
"gimli",
- "indexmap",
+ "indexmap 2.9.0",
"log",
"object",
"postcard",
@@ -19228,7 +19299,7 @@ checksum = "8358319c2dd1e4db79e3c1c5d3a5af84956615343f9f89f4e4996a36816e06e6"
dependencies = [
"anyhow",
"heck 0.5.0",
- "indexmap",
+ "indexmap 2.9.0",
"wit-parser 0.221.3",
]
@@ -19614,7 +19685,7 @@ dependencies = [
"windows-collections",
"windows-core 0.61.0",
"windows-future",
- "windows-link",
+ "windows-link 0.1.1",
"windows-numerics",
]
@@ -19684,7 +19755,7 @@ checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
dependencies = [
"windows-implement 0.60.0",
"windows-interface 0.59.1",
- "windows-link",
+ "windows-link 0.1.1",
"windows-result 0.3.2",
"windows-strings 0.4.0",
]
@@ -19696,7 +19767,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32"
dependencies = [
"windows-core 0.61.0",
- "windows-link",
+ "windows-link 0.1.1",
]
[[package]]
@@ -19771,6 +19842,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
+[[package]]
+name = "windows-link"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65"
+
[[package]]
name = "windows-numerics"
version = "0.2.0"
@@ -19778,7 +19855,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
dependencies = [
"windows-core 0.61.0",
- "windows-link",
+ "windows-link 0.1.1",
]
[[package]]
@@ -19798,11 +19875,22 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad1da3e436dc7653dfdf3da67332e22bff09bb0e28b0239e1624499c7830842e"
dependencies = [
- "windows-link",
+ "windows-link 0.1.1",
"windows-result 0.3.2",
"windows-strings 0.4.0",
]
+[[package]]
+name = "windows-registry"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f91f87ce112ffb7275000ea98eb1940912c21c1567c9312fde20261f3eadd29"
+dependencies = [
+ "windows-link 0.2.0",
+ "windows-result 0.4.0",
+ "windows-strings 0.5.0",
+]
+
[[package]]
name = "windows-result"
version = "0.1.2"
@@ -19827,7 +19915,16 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
dependencies = [
- "windows-link",
+ "windows-link 0.1.1",
+]
+
+[[package]]
+name = "windows-result"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f"
+dependencies = [
+ "windows-link 0.2.0",
]
[[package]]
@@ -19846,7 +19943,7 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319"
dependencies = [
- "windows-link",
+ "windows-link 0.1.1",
]
[[package]]
@@ -19855,7 +19952,16 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
dependencies = [
- "windows-link",
+ "windows-link 0.1.1",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda"
+dependencies = [
+ "windows-link 0.2.0",
]
[[package]]
@@ -20304,7 +20410,7 @@ checksum = "d8a39a15d1ae2077688213611209849cad40e9e5cccf6e61951a425850677ff3"
dependencies = [
"anyhow",
"heck 0.4.1",
- "indexmap",
+ "indexmap 2.9.0",
"wasm-metadata 0.201.0",
"wit-bindgen-core 0.22.0",
"wit-component 0.201.0",
@@ -20318,7 +20424,7 @@ checksum = "9d0809dc5ba19e2e98661bf32fc0addc5a3ca5bf3a6a7083aa6ba484085ff3ce"
dependencies = [
"anyhow",
"heck 0.5.0",
- "indexmap",
+ "indexmap 2.9.0",
"prettyplease",
"syn 2.0.101",
"wasm-metadata 0.227.1",
@@ -20363,7 +20469,7 @@ checksum = "421c0c848a0660a8c22e2fd217929a0191f14476b68962afd2af89fd22e39825"
dependencies = [
"anyhow",
"bitflags 2.9.0",
- "indexmap",
+ "indexmap 2.9.0",
"log",
"serde",
"serde_derive",
@@ -20382,7 +20488,7 @@ checksum = "635c3adc595422cbf2341a17fb73a319669cc8d33deed3a48368a841df86b676"
dependencies = [
"anyhow",
"bitflags 2.9.0",
- "indexmap",
+ "indexmap 2.9.0",
"log",
"serde",
"serde_derive",
@@ -20401,7 +20507,7 @@ checksum = "196d3ecfc4b759a8573bf86a9b3f8996b304b3732e4c7de81655f875f6efdca6"
dependencies = [
"anyhow",
"id-arena",
- "indexmap",
+ "indexmap 2.9.0",
"log",
"semver",
"serde",
@@ -20419,7 +20525,7 @@ checksum = "896112579ed56b4a538b07a3d16e562d101ff6265c46b515ce0c701eef16b2ac"
dependencies = [
"anyhow",
"id-arena",
- "indexmap",
+ "indexmap 2.9.0",
"log",
"semver",
"serde",
@@ -20437,7 +20543,7 @@ checksum = "ddf445ed5157046e4baf56f9138c124a0824d4d1657e7204d71886ad8ce2fc11"
dependencies = [
"anyhow",
"id-arena",
- "indexmap",
+ "indexmap 2.9.0",
"log",
"semver",
"serde",
@@ -20487,7 +20593,7 @@ dependencies = [
"pretty_assertions",
"project",
"remote",
- "schemars",
+ "schemars 1.0.1",
"serde",
"serde_json",
"session",
@@ -20584,7 +20690,7 @@ dependencies = [
"hyper 0.14.32",
"hyper-rustls 0.27.5",
"idna",
- "indexmap",
+ "indexmap 2.9.0",
"inout",
"itertools 0.11.0",
"itertools 0.13.0",
@@ -20728,7 +20834,6 @@ dependencies = [
"pretty_assertions",
"rand 0.9.1",
"rpc",
- "schemars",
"serde",
"serde_json",
"settings",
@@ -20806,7 +20911,7 @@ name = "x_ai"
version = "0.1.0"
dependencies = [
"anyhow",
- "schemars",
+ "schemars 1.0.1",
"serde",
"strum 0.27.1",
"workspace-hack",
@@ -21069,7 +21174,7 @@ dependencies = [
[[package]]
name = "zed"
-version = "0.205.0"
+version = "0.206.0"
dependencies = [
"acp_tools",
"activity_indicator",
@@ -21175,7 +21280,6 @@ dependencies = [
"session",
"settings",
"settings_profile_selector",
- "settings_ui",
"shellexpand 2.1.2",
"smol",
"snippet_provider",
@@ -21226,7 +21330,7 @@ name = "zed_actions"
version = "0.1.0"
dependencies = [
"gpui",
- "schemars",
+ "schemars 1.0.1",
"serde",
"uuid",
"workspace-hack",
@@ -21560,7 +21664,7 @@ dependencies = [
"crc32fast",
"crossbeam-utils",
"displaydoc",
- "indexmap",
+ "indexmap 2.9.0",
"num_enum",
"thiserror 1.0.69",
]
@@ -21577,6 +21681,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"chrono",
+ "collections",
"log",
"tempfile",
"workspace-hack",
@@ -21586,10 +21691,8 @@ dependencies = [
name = "zlog_settings"
version = "0.1.0"
dependencies = [
- "anyhow",
+ "collections",
"gpui",
- "schemars",
- "serde",
"settings",
"workspace-hack",
"zlog",
diff --git a/Cargo.toml b/Cargo.toml
index f362a380af930823084072dc53260d8e2be0d749..03b630e142d13beaa411c598c6fefe12086e617e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -150,7 +150,6 @@ members = [
"crates/session",
"crates/settings",
"crates/settings_profile_selector",
- "crates/settings_ui",
"crates/settings_ui_macros",
"crates/snippet",
"crates/snippet_provider",
@@ -437,7 +436,7 @@ zlog_settings = { path = "crates/zlog_settings" }
# External crates
#
-agent-client-protocol = { version = "0.2.1", features = ["unstable"] }
+agent-client-protocol = { version = "0.4.0", features = ["unstable"] }
aho-corasick = "1.1"
alacritty_terminal = { git = "https://github.com/zed-industries/alacritty.git", branch = "add-hush-login-flag" }
any_vec = "0.14"
@@ -630,6 +629,7 @@ serde_json_lenient = { version = "0.2", features = [
serde_path_to_error = "0.1.17"
serde_repr = "0.1"
serde_urlencoded = "0.7"
+serde_with = "3.4.0"
sha2 = "0.10"
shellexpand = "2.1.0"
shlex = "1.3.0"
diff --git a/assets/icons/linux.svg b/assets/icons/linux.svg
new file mode 100644
index 0000000000000000000000000000000000000000..fc76742a3f236650cb8c514c8263ec2c3b2d4521
--- /dev/null
+++ b/assets/icons/linux.svg
@@ -0,0 +1,11 @@
+
diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json
index 8464e03d251afc166ac45a349894ecf2f7247944..817198659657814dcc597926d689063ae2182c78 100644
--- a/assets/keymaps/vim.json
+++ b/assets/keymaps/vim.json
@@ -442,9 +442,8 @@
">": "vim::Indent",
"<": "vim::Outdent",
"=": "vim::AutoIndent",
- "g u": "vim::PushLowercase",
- "g shift-u": "vim::PushUppercase",
- "g ~": "vim::PushOppositeCase",
+ "`": "vim::ConvertToLowerCase",
+ "alt-`": "vim::ConvertToUpperCase",
"g q": "vim::PushRewrap",
"g w": "vim::PushRewrap",
"insert": "vim::InsertBefore",
diff --git a/assets/settings/default.json b/assets/settings/default.json
index daab56dc93976728667ed6995cb445f363ee2be8..dc6a94a00d9d3241ed9fd2c38b9263ed7082f25e 100644
--- a/assets/settings/default.json
+++ b/assets/settings/default.json
@@ -311,7 +311,7 @@
// bracket, brace, single or double quote characters.
// For example, when you select text and type (, Zed will surround the text with ().
"use_auto_surround": true,
- /// Whether indentation should be adjusted based on the context whilst typing.
+ // Whether indentation should be adjusted based on the context whilst typing.
"auto_indent": true,
// Whether indentation of pasted content should be adjusted based on the context.
"auto_indent_on_paste": true,
@@ -408,6 +408,39 @@
// Whether to show the menus in the titlebar.
"show_menus": false
},
+ "audio": {
+ // Opt into the new audio system.
+ "experimental.rodio_audio": true,
+ /// Requires 'rodio_audio: true'
+ ///
+ /// Automatically increase or decrease you microphone's volume. This affects how
+ /// loud you sound to others.
+ ///
+ /// Recommended: off (default)
+ /// Microphones are too quite in zed, until everyone is on experimental
+ /// audio and has auto speaker volume on this will make you very loud
+ /// compared to other speakers.
+ "experimental.auto_microphone_volume": false,
+ /// Requires 'rodio_audio: true'
+ ///
+ /// Automatically increate or decrease the volume of other call members.
+ /// This only affects how things sound for you.
+ "experimental.auto_speaker_volume": true,
+ /// Requires 'rodio_audio: true'
+ ///
+ /// Remove background noises. Works great for typing, cars, dogs, AC. Does
+ /// not work well on music.
+ "experimental.denoise": true,
+ /// Requires 'rodio_audio: true'
+ ///
+ /// Use audio parameters compatible with the previous versions of
+ /// experimental audio and non-experimental audio. When this is false you
+ /// will sound strange to anyone not on the latest experimental audio. In
+ /// the future we will migrate by setting this to false
+ ///
+ /// You need to rejoin a call for this setting to apply
+ "experimental.legacy_audio_compatible": true
+ },
// Scrollbar related settings
"scrollbar": {
// When to show the scrollbar in the editor.
@@ -588,6 +621,7 @@
// Toggle certain types of hints on and off, all switched on by default.
"show_type_hints": true,
"show_parameter_hints": true,
+ "show_value_hints": true,
// Corresponds to null/None LSP hint type value.
"show_other_hints": true,
// Whether to show a background for inlay hints.
@@ -796,7 +830,7 @@
"agent": {
// Whether the agent is enabled.
"enabled": true,
- /// What completion mode to start new threads in, if available. Can be 'normal' or 'burn'.
+ // What completion mode to start new threads in, if available. Can be 'normal' or 'burn'.
"preferred_completion_mode": "normal",
// Whether to show the agent panel button in the status bar.
"button": true,
@@ -806,6 +840,8 @@
"default_width": 640,
// Default height when the agent panel is docked to the bottom.
"default_height": 320,
+ // The view to use by default (thread, or text_thread)
+ "default_view": "thread",
// The default model to use when creating new threads.
"default_model": {
// The provider to use.
@@ -907,27 +943,23 @@
// Default: false
"play_sound_when_agent_done": false,
- /// Whether to have edit cards in the agent panel expanded, showing a preview of the full diff.
- ///
- /// Default: true
+ // Whether to have edit cards in the agent panel expanded, showing a preview of the full diff.
+ //
+ // Default: true
"expand_edit_card": true,
- /// Whether to have terminal cards in the agent panel expanded, showing the whole command output.
- ///
- /// Default: true
+ // Whether to have terminal cards in the agent panel expanded, showing the whole command output.
+ //
+ // Default: true
"expand_terminal_card": true,
+ // Whether to always use cmd-enter (or ctrl-enter on Linux or Windows) to send messages in the agent panel.
+ //
+ // Default: false
+ "use_modifier_to_send": false,
// Minimum number of lines to display in the agent message editor.
//
// Default: 4
"message_editor_min_lines": 4
},
- // The settings for slash commands.
- "slash_commands": {
- // Settings for the `/project` slash command.
- "project": {
- // Whether `/project` is enabled.
- "enabled": false
- }
- },
// Whether the screen sharing icon is shown in the os status bar.
"show_call_status_icon": true,
// Whether to use language servers to provide code intelligence.
@@ -939,6 +971,7 @@
//
// This is typically customized on a per-language basis.
"language_servers": ["..."],
+
// When to automatically save edited buffers. This setting can
// take four values.
//
@@ -1270,7 +1303,13 @@
// },
// Whether edit predictions are enabled when editing text threads.
// This setting has no effect if globally disabled.
- "enabled_in_text_threads": true
+ "enabled_in_text_threads": true,
+
+ "copilot": {
+ "enterprise_uri": null,
+ "proxy": null,
+ "proxy_no_verify": null
+ }
},
// Settings specific to journaling
"journal": {
@@ -1773,6 +1812,7 @@
"anthropic": {
"api_url": "https://api.anthropic.com"
},
+ "bedrock": {},
"google": {
"api_url": "https://generativelanguage.googleapis.com"
},
@@ -1794,14 +1834,30 @@
},
"mistral": {
"api_url": "https://api.mistral.ai/v1"
- }
+ },
+ "vercel": {
+ "api_url": "https://api.v0.dev/v1"
+ },
+ "x_ai": {
+ "api_url": "https://api.x.ai/v1"
+ },
+ "zed.dev": {}
+ },
+ "session": {
+ // Whether or not to restore unsaved buffers on restart.
+ //
+ // If this is true, user won't be prompted whether to save/discard
+ // dirty files when closing the application.
+ //
+ // Default: true
+ "restore_unsaved_buffers": true
},
// Zed's Prettier integration settings.
// Allows to enable/disable formatting with Prettier
// and configure default Prettier, used when no project-level Prettier installation is found.
"prettier": {
// // Whether to consider prettier formatter or not when attempting to format a file.
- // "allowed": false,
+ "allowed": false
//
// // Use regular Prettier json configuration.
// // If Prettier is allowed, Zed will use this for its Prettier instance for any applicable file, if
@@ -1834,6 +1890,10 @@
// }
// }
},
+ // DAP Specific settings.
+ "dap": {
+ // Specify the DAP name as a key here.
+ },
// Common language server settings.
"global_lsp_settings": {
// Whether to show the LSP servers button in the status bar.
@@ -1841,7 +1901,8 @@
},
// Jupyter settings
"jupyter": {
- "enabled": true
+ "enabled": true,
+ "kernel_selections": {}
// Specify the language name as the key and the kernel name as the value.
// "kernel_selections": {
// "python": "conda-base"
@@ -1972,5 +2033,11 @@
// }
// }
// }
- "profiles": []
+ "profiles": [],
+
+ // A map of log scopes to the desired log level.
+ // Useful for filtering out noisy logs or enabling more verbose logging.
+ //
+ // Example: {"log": {"client": "warn"}}
+ "log": {}
}
diff --git a/crates/acp_thread/src/acp_thread.rs b/crates/acp_thread/src/acp_thread.rs
index 68e5266f06aa8bddfaa252bdc1cf5b21891c7f10..f2327ca70b104de12f44d74aacd1a5a2bb1eca3b 100644
--- a/crates/acp_thread/src/acp_thread.rs
+++ b/crates/acp_thread/src/acp_thread.rs
@@ -1781,6 +1781,9 @@ impl AcpThread {
reuse_shared_snapshot: bool,
cx: &mut Context,
) -> Task> {
+ // Args are 1-based, move to 0-based
+ let line = line.unwrap_or_default().saturating_sub(1);
+ let limit = limit.unwrap_or(u32::MAX);
let project = self.project.clone();
let action_log = self.action_log.clone();
cx.spawn(async move |this, cx| {
@@ -1808,44 +1811,37 @@ impl AcpThread {
action_log.update(cx, |action_log, cx| {
action_log.buffer_read(buffer.clone(), cx);
})?;
- project.update(cx, |project, cx| {
- let position = buffer
- .read(cx)
- .snapshot()
- .anchor_before(Point::new(line.unwrap_or_default(), 0));
- project.set_agent_location(
- Some(AgentLocation {
- buffer: buffer.downgrade(),
- position,
- }),
- cx,
- );
- })?;
- buffer.update(cx, |buffer, _| buffer.snapshot())?
+ let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot())?;
+ this.update(cx, |this, _| {
+ this.shared_buffers.insert(buffer.clone(), snapshot.clone());
+ })?;
+ snapshot
};
- this.update(cx, |this, _| {
- let text = snapshot.text();
- this.shared_buffers.insert(buffer.clone(), snapshot);
- if line.is_none() && limit.is_none() {
- return Ok(text);
- }
- let limit = limit.unwrap_or(u32::MAX) as usize;
- let Some(line) = line else {
- return Ok(text.lines().take(limit).collect::());
- };
+ let max_point = snapshot.max_point();
+ if line >= max_point.row {
+ anyhow::bail!(
+ "Attempting to read beyond the end of the file, line {}:{}",
+ max_point.row + 1,
+ max_point.column
+ );
+ }
- let count = text.lines().count();
- if count < line as usize {
- anyhow::bail!("There are only {} lines", count);
- }
- Ok(text
- .lines()
- .skip(line as usize + 1)
- .take(limit)
- .collect::())
- })?
+ let start = snapshot.anchor_before(Point::new(line, 0));
+ let end = snapshot.anchor_before(Point::new(line.saturating_add(limit), 0));
+
+ project.update(cx, |project, cx| {
+ project.set_agent_location(
+ Some(AgentLocation {
+ buffer: buffer.downgrade(),
+ position: start,
+ }),
+ cx,
+ );
+ })?;
+
+ Ok(snapshot.text_for_range(start..end).collect::())
})
}
@@ -2391,6 +2387,82 @@ mod tests {
request.await.unwrap();
}
+ #[gpui::test]
+ async fn test_reading_from_line(cx: &mut TestAppContext) {
+ init_test(cx);
+
+ let fs = FakeFs::new(cx.executor());
+ fs.insert_tree(path!("/tmp"), json!({"foo": "one\ntwo\nthree\nfour\n"}))
+ .await;
+ let project = Project::test(fs.clone(), [], cx).await;
+ project
+ .update(cx, |project, cx| {
+ project.find_or_create_worktree(path!("/tmp/foo"), true, cx)
+ })
+ .await
+ .unwrap();
+
+ let connection = Rc::new(FakeAgentConnection::new());
+
+ let thread = cx
+ .update(|cx| connection.new_thread(project, Path::new(path!("/tmp")), cx))
+ .await
+ .unwrap();
+
+ // Whole file
+ let content = thread
+ .update(cx, |thread, cx| {
+ thread.read_text_file(path!("/tmp/foo").into(), None, None, false, cx)
+ })
+ .await
+ .unwrap();
+
+ assert_eq!(content, "one\ntwo\nthree\nfour\n");
+
+ // Only start line
+ let content = thread
+ .update(cx, |thread, cx| {
+ thread.read_text_file(path!("/tmp/foo").into(), Some(3), None, false, cx)
+ })
+ .await
+ .unwrap();
+
+ assert_eq!(content, "three\nfour\n");
+
+ // Only limit
+ let content = thread
+ .update(cx, |thread, cx| {
+ thread.read_text_file(path!("/tmp/foo").into(), None, Some(2), false, cx)
+ })
+ .await
+ .unwrap();
+
+ assert_eq!(content, "one\ntwo\n");
+
+ // Range
+ let content = thread
+ .update(cx, |thread, cx| {
+ thread.read_text_file(path!("/tmp/foo").into(), Some(2), Some(2), false, cx)
+ })
+ .await
+ .unwrap();
+
+ assert_eq!(content, "two\nthree\n");
+
+ // Invalid
+ let err = thread
+ .update(cx, |thread, cx| {
+ thread.read_text_file(path!("/tmp/foo").into(), Some(5), Some(2), false, cx)
+ })
+ .await
+ .unwrap_err();
+
+ assert_eq!(
+ err.to_string(),
+ "Attempting to read beyond the end of the file, line 5:0"
+ );
+ }
+
#[gpui::test]
async fn test_succeeding_canceled_toolcall(cx: &mut TestAppContext) {
init_test(cx);
diff --git a/crates/agent/src/agent_profile.rs b/crates/agent/src/agent_profile.rs
index c9e73372f60686cf330531926f4129e9c9b25db8..40ba2f07db7ad425a5d0e9befe91499eb746b74e 100644
--- a/crates/agent/src/agent_profile.rs
+++ b/crates/agent/src/agent_profile.rs
@@ -49,10 +49,10 @@ impl AgentProfile {
.unwrap_or_default(),
};
- update_settings_file::(fs, cx, {
+ update_settings_file(fs, cx, {
let id = id.clone();
move |settings, _cx| {
- settings.create_profile(id, profile_settings).log_err();
+ profile_settings.save_to_settings(id, settings).log_err();
}
});
diff --git a/crates/agent/src/thread.rs b/crates/agent/src/thread.rs
index 7b70fde56ab1e7acb6705aeace82f142dc28a9f3..8b9d489ccf472ca16435934e48a12b70dc783c40 100644
--- a/crates/agent/src/thread.rs
+++ b/crates/agent/src/thread.rs
@@ -3272,7 +3272,7 @@ mod tests {
// Test-specific constants
const TEST_RATE_LIMIT_RETRY_SECS: u64 = 30;
- use agent_settings::{AgentProfileId, AgentSettings, LanguageModelParameters};
+ use agent_settings::{AgentProfileId, AgentSettings};
use assistant_tool::ToolRegistry;
use assistant_tools;
use futures::StreamExt;
@@ -3289,7 +3289,7 @@ mod tests {
use project::{FakeFs, Project};
use prompt_store::PromptBuilder;
use serde_json::json;
- use settings::{Settings, SettingsStore};
+ use settings::{LanguageModelParameters, Settings, SettingsStore};
use std::sync::Arc;
use std::time::Duration;
use theme::ThemeSettings;
diff --git a/crates/agent2/src/agent.rs b/crates/agent2/src/agent.rs
index 44d4075b89306b6c6dd81d6de503888c036e6fbf..86fb50242c64917248df5c620782af066e639b54 100644
--- a/crates/agent2/src/agent.rs
+++ b/crates/agent2/src/agent.rs
@@ -6,7 +6,6 @@ use crate::{HistoryStore, TerminalHandle, ThreadEnvironment, TitleUpdated, Token
use acp_thread::{AcpThread, AgentModelSelector};
use action_log::ActionLog;
use agent_client_protocol as acp;
-use agent_settings::AgentSettings;
use anyhow::{Context as _, Result, anyhow};
use collections::{HashSet, IndexMap};
use fs::Fs;
@@ -21,7 +20,7 @@ use project::{Project, ProjectItem, ProjectPath, Worktree};
use prompt_store::{
ProjectContext, PromptId, PromptStore, RulesFileContext, UserRulesContext, WorktreeContext,
};
-use settings::update_settings_file;
+use settings::{LanguageModelSelection, update_settings_file};
use std::any::Any;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
@@ -873,13 +872,17 @@ impl AgentModelSelector for NativeAgentConnection {
thread.set_model(model.clone(), cx);
});
- update_settings_file::(
- self.0.read(cx).fs.clone(),
- cx,
- move |settings, _cx| {
- settings.set_model(model);
- },
- );
+ update_settings_file(self.0.read(cx).fs.clone(), cx, move |settings, _cx| {
+ let provider = model.provider_id().0.to_string();
+ let model = model.id().0.to_string();
+ settings
+ .agent
+ .get_or_insert_default()
+ .set_model(LanguageModelSelection {
+ provider: provider.into(),
+ model,
+ });
+ });
Task::ready(Ok(()))
}
diff --git a/crates/agent2/src/thread.rs b/crates/agent2/src/thread.rs
index b19d34adafe4a7b6567be9a1864b88ea2504bf12..18f993cbe33ca8bffc2235906baf76c627da0030 100644
--- a/crates/agent2/src/thread.rs
+++ b/crates/agent2/src/thread.rs
@@ -2477,8 +2477,11 @@ impl ToolCallEventStream {
"always_allow" => {
if let Some(fs) = fs.clone() {
cx.update(|cx| {
- update_settings_file::(fs, cx, |settings, _| {
- settings.set_always_allow_tool_actions(true);
+ update_settings_file(fs, cx, |settings, _| {
+ settings
+ .agent
+ .get_or_insert_default()
+ .set_always_allow_tool_actions(true);
});
})?;
}
diff --git a/crates/agent2/src/tools/edit_file_tool.rs b/crates/agent2/src/tools/edit_file_tool.rs
index 1e725b8f59d1219a0761334c5364940ee0e8bf6a..81f340b0b5c83648b1ec92210986b475b71c5bcf 100644
--- a/crates/agent2/src/tools/edit_file_tool.rs
+++ b/crates/agent2/src/tools/edit_file_tool.rs
@@ -791,14 +791,11 @@ mod tests {
// First, test with format_on_save enabled
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(
- cx,
- |settings| {
- settings.defaults.format_on_save = Some(FormatOnSave::On);
- settings.defaults.formatter =
- Some(language::language_settings::SelectedFormatter::Auto);
- },
- );
+ store.update_user_settings(cx, |settings| {
+ settings.project.all_languages.defaults.format_on_save = Some(FormatOnSave::On);
+ settings.project.all_languages.defaults.formatter =
+ Some(language::language_settings::SelectedFormatter::Auto);
+ });
});
});
@@ -853,12 +850,10 @@ mod tests {
// Next, test with format_on_save disabled
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(
- cx,
- |settings| {
- settings.defaults.format_on_save = Some(FormatOnSave::Off);
- },
- );
+ store.update_user_settings(cx, |settings| {
+ settings.project.all_languages.defaults.format_on_save =
+ Some(FormatOnSave::Off);
+ });
});
});
@@ -935,12 +930,13 @@ mod tests {
// First, test with remove_trailing_whitespace_on_save enabled
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(
- cx,
- |settings| {
- settings.defaults.remove_trailing_whitespace_on_save = Some(true);
- },
- );
+ store.update_user_settings(cx, |settings| {
+ settings
+ .project
+ .all_languages
+ .defaults
+ .remove_trailing_whitespace_on_save = Some(true);
+ });
});
});
@@ -991,12 +987,13 @@ mod tests {
// Next, test with remove_trailing_whitespace_on_save disabled
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(
- cx,
- |settings| {
- settings.defaults.remove_trailing_whitespace_on_save = Some(false);
- },
- );
+ store.update_user_settings(cx, |settings| {
+ settings
+ .project
+ .all_languages
+ .defaults
+ .remove_trailing_whitespace_on_save = Some(false);
+ });
});
});
diff --git a/crates/agent2/src/tools/grep_tool.rs b/crates/agent2/src/tools/grep_tool.rs
index 2bcfbd711bd7507be235481197e16cf76b391b6b..a1cd088c858683429103670c604ed3e08d179483 100644
--- a/crates/agent2/src/tools/grep_tool.rs
+++ b/crates/agent2/src/tools/grep_tool.rs
@@ -308,7 +308,7 @@ mod tests {
use super::*;
use gpui::{TestAppContext, UpdateGlobal};
use language::{Language, LanguageConfig, LanguageMatcher};
- use project::{FakeFs, Project, WorktreeSettings};
+ use project::{FakeFs, Project};
use serde_json::json;
use settings::SettingsStore;
use unindent::Unindent;
@@ -827,15 +827,14 @@ mod tests {
cx.update(|cx| {
use gpui::UpdateGlobal;
- use project::WorktreeSettings;
use settings::SettingsStore;
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |settings| {
- settings.file_scan_exclusions = Some(vec![
+ store.update_user_settings(cx, |settings| {
+ settings.project.worktree.file_scan_exclusions = Some(vec![
"**/.secretdir".to_string(),
"**/.mymetadata".to_string(),
]);
- settings.private_files = Some(vec![
+ settings.project.worktree.private_files = Some(vec![
"**/.mysecrets".to_string(),
"**/*.privatekey".to_string(),
"**/*.mysensitive".to_string(),
@@ -1062,10 +1061,10 @@ mod tests {
// Set global settings
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |settings| {
- settings.file_scan_exclusions =
+ store.update_user_settings(cx, |settings| {
+ settings.project.worktree.file_scan_exclusions =
Some(vec!["**/.git".to_string(), "**/node_modules".to_string()]);
- settings.private_files = Some(vec!["**/.env".to_string()]);
+ settings.project.worktree.private_files = Some(vec!["**/.env".to_string()]);
});
});
});
diff --git a/crates/agent2/src/tools/list_directory_tool.rs b/crates/agent2/src/tools/list_directory_tool.rs
index 0fbe23fe205e6a9bd5a77e737460c17b997f9175..41198269d69c17e028cca250069c2e1000ac8dfe 100644
--- a/crates/agent2/src/tools/list_directory_tool.rs
+++ b/crates/agent2/src/tools/list_directory_tool.rs
@@ -214,7 +214,7 @@ mod tests {
use super::*;
use gpui::{TestAppContext, UpdateGlobal};
use indoc::indoc;
- use project::{FakeFs, Project, WorktreeSettings};
+ use project::{FakeFs, Project};
use serde_json::json;
use settings::SettingsStore;
use util::path;
@@ -421,13 +421,13 @@ mod tests {
// Configure settings explicitly
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |settings| {
- settings.file_scan_exclusions = Some(vec![
+ store.update_user_settings(cx, |settings| {
+ settings.project.worktree.file_scan_exclusions = Some(vec![
"**/.secretdir".to_string(),
"**/.mymetadata".to_string(),
"**/.hidden_subdir".to_string(),
]);
- settings.private_files = Some(vec![
+ settings.project.worktree.private_files = Some(vec![
"**/.mysecrets".to_string(),
"**/*.privatekey".to_string(),
"**/*.mysensitive".to_string(),
@@ -565,10 +565,10 @@ mod tests {
// Set global settings
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |settings| {
- settings.file_scan_exclusions =
+ store.update_user_settings(cx, |settings| {
+ settings.project.worktree.file_scan_exclusions =
Some(vec!["**/.git".to_string(), "**/node_modules".to_string()]);
- settings.private_files = Some(vec!["**/.env".to_string()]);
+ settings.project.worktree.private_files = Some(vec!["**/.env".to_string()]);
});
});
});
diff --git a/crates/agent2/src/tools/read_file_tool.rs b/crates/agent2/src/tools/read_file_tool.rs
index 87163e769c26b0cee053fcf149d047fc451c470f..3a9ddb88fe1a44677e43248184902c12724f6936 100644
--- a/crates/agent2/src/tools/read_file_tool.rs
+++ b/crates/agent2/src/tools/read_file_tool.rs
@@ -201,7 +201,6 @@ impl AgentTool for ReadFileTool {
// Check if specific line ranges are provided
let result = if input.start_line.is_some() || input.end_line.is_some() {
let result = buffer.read_with(cx, |buffer, _cx| {
- let text = buffer.text();
// .max(1) because despite instructions to be 1-indexed, sometimes the model passes 0.
let start = input.start_line.unwrap_or(1).max(1);
let start_row = start - 1;
@@ -210,13 +209,13 @@ impl AgentTool for ReadFileTool {
anchor = Some(buffer.anchor_before(Point::new(start_row, column)));
}
- let lines = text.split('\n').skip(start_row as usize);
- if let Some(end) = input.end_line {
- let count = end.saturating_sub(start).saturating_add(1); // Ensure at least 1 line
- itertools::intersperse(lines.take(count as usize), "\n").collect::()
- } else {
- itertools::intersperse(lines, "\n").collect::()
+ let mut end_row = input.end_line.unwrap_or(u32::MAX);
+ if end_row <= start_row {
+ end_row = start_row + 1; // read at least one lines
}
+ let start = buffer.anchor_before(Point::new(start_row, 0));
+ let end = buffer.anchor_before(Point::new(end_row, 0));
+ buffer.text_for_range(start..end).collect::()
})?;
action_log.update(cx, |log, cx| {
@@ -445,7 +444,7 @@ mod test {
tool.run(input, ToolCallEventStream::test().0, cx)
})
.await;
- assert_eq!(result.unwrap(), "Line 2\nLine 3\nLine 4".into());
+ assert_eq!(result.unwrap(), "Line 2\nLine 3\nLine 4\n".into());
}
#[gpui::test]
@@ -475,7 +474,7 @@ mod test {
tool.clone().run(input, ToolCallEventStream::test().0, cx)
})
.await;
- assert_eq!(result.unwrap(), "Line 1\nLine 2".into());
+ assert_eq!(result.unwrap(), "Line 1\nLine 2\n".into());
// end_line of 0 should result in at least 1 line
let result = cx
@@ -488,7 +487,7 @@ mod test {
tool.clone().run(input, ToolCallEventStream::test().0, cx)
})
.await;
- assert_eq!(result.unwrap(), "Line 1".into());
+ assert_eq!(result.unwrap(), "Line 1\n".into());
// when start_line > end_line, should still return at least 1 line
let result = cx
@@ -501,7 +500,7 @@ mod test {
tool.clone().run(input, ToolCallEventStream::test().0, cx)
})
.await;
- assert_eq!(result.unwrap(), "Line 3".into());
+ assert_eq!(result.unwrap(), "Line 3\n".into());
}
fn init_test(cx: &mut TestAppContext) {
@@ -587,15 +586,14 @@ mod test {
cx.update(|cx| {
use gpui::UpdateGlobal;
- use project::WorktreeSettings;
use settings::SettingsStore;
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |settings| {
- settings.file_scan_exclusions = Some(vec![
+ store.update_user_settings(cx, |settings| {
+ settings.project.worktree.file_scan_exclusions = Some(vec![
"**/.secretdir".to_string(),
"**/.mymetadata".to_string(),
]);
- settings.private_files = Some(vec![
+ settings.project.worktree.private_files = Some(vec![
"**/.mysecrets".to_string(),
"**/*.privatekey".to_string(),
"**/*.mysensitive".to_string(),
@@ -803,10 +801,10 @@ mod test {
// Set global settings
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |settings| {
- settings.file_scan_exclusions =
+ store.update_user_settings(cx, |settings| {
+ settings.project.worktree.file_scan_exclusions =
Some(vec!["**/.git".to_string(), "**/node_modules".to_string()]);
- settings.private_files = Some(vec!["**/.env".to_string()]);
+ settings.project.worktree.private_files = Some(vec!["**/.env".to_string()]);
});
});
});
diff --git a/crates/agent_servers/Cargo.toml b/crates/agent_servers/Cargo.toml
index a168da05c83482f9d5b34118a74ee5e1f15e2e37..ca6db6c663ddb2132c05d716e5b935c5855bccdb 100644
--- a/crates/agent_servers/Cargo.toml
+++ b/crates/agent_servers/Cargo.toml
@@ -23,6 +23,7 @@ action_log.workspace = true
agent-client-protocol.workspace = true
agent_settings.workspace = true
anyhow.workspace = true
+async-trait.workspace = true
client.workspace = true
collections.workspace = true
env_logger = { workspace = true, optional = true }
diff --git a/crates/agent_servers/src/acp.rs b/crates/agent_servers/src/acp.rs
index cc897d85e7b4de149a0dca84df84d2b8c2c5bc98..b8c75a01a2e2965c255e32bd3c0746b26d78ecab 100644
--- a/crates/agent_servers/src/acp.rs
+++ b/crates/agent_servers/src/acp.rs
@@ -13,7 +13,7 @@ use util::ResultExt as _;
use std::path::PathBuf;
use std::{any::Any, cell::RefCell};
-use std::{path::Path, rc::Rc, sync::Arc};
+use std::{path::Path, rc::Rc};
use thiserror::Error;
use anyhow::{Context as _, Result};
@@ -505,6 +505,7 @@ struct ClientDelegate {
cx: AsyncApp,
}
+#[async_trait::async_trait(?Send)]
impl acp::Client for ClientDelegate {
async fn request_permission(
&self,
@@ -638,19 +639,11 @@ impl acp::Client for ClientDelegate {
Ok(Default::default())
}
- async fn ext_method(
- &self,
- _name: Arc,
- _params: Arc,
- ) -> Result, acp::Error> {
+ async fn ext_method(&self, _args: acp::ExtRequest) -> Result {
Err(acp::Error::method_not_found())
}
- async fn ext_notification(
- &self,
- _name: Arc,
- _params: Arc,
- ) -> Result<(), acp::Error> {
+ async fn ext_notification(&self, _args: acp::ExtNotification) -> Result<(), acp::Error> {
Err(acp::Error::method_not_found())
}
diff --git a/crates/agent_servers/src/claude.rs b/crates/agent_servers/src/claude.rs
index dafb7ea5b706af8e52562681d634dadf07ae488f..4646b2e8259fa2cd63c0daa67b47f66b5e78af05 100644
--- a/crates/agent_servers/src/claude.rs
+++ b/crates/agent_servers/src/claude.rs
@@ -45,8 +45,13 @@ impl AgentServer for ClaudeCode {
}
fn set_default_mode(&self, mode_id: Option, fs: Arc, cx: &mut App) {
- update_settings_file::(fs, cx, |settings, _| {
- settings.claude.get_or_insert_default().default_mode = mode_id.map(|m| m.to_string())
+ update_settings_file(fs, cx, |settings, _| {
+ settings
+ .agent_servers
+ .get_or_insert_default()
+ .claude
+ .get_or_insert_default()
+ .default_mode = mode_id.map(|m| m.to_string())
});
}
diff --git a/crates/agent_servers/src/custom.rs b/crates/agent_servers/src/custom.rs
index 7c8924a3dd65e9f963b418aa6872b1bc14886040..cb9a6dba3c6376fa5030c21523c86853c9b6d761 100644
--- a/crates/agent_servers/src/custom.rs
+++ b/crates/agent_servers/src/custom.rs
@@ -49,8 +49,14 @@ impl crate::AgentServer for CustomAgentServer {
fn set_default_mode(&self, mode_id: Option, fs: Arc, cx: &mut App) {
let name = self.name();
- update_settings_file::(fs, cx, move |settings, _| {
- settings.custom.get_mut(&name).unwrap().default_mode = mode_id.map(|m| m.to_string())
+ update_settings_file(fs, cx, move |settings, _| {
+ settings
+ .agent_servers
+ .get_or_insert_default()
+ .custom
+ .get_mut(&name)
+ .unwrap()
+ .default_mode = mode_id.map(|m| m.to_string())
});
}
diff --git a/crates/agent_settings/src/agent_profile.rs b/crates/agent_settings/src/agent_profile.rs
index 42a273e2dcfccfb839e6e7d97efb42dcd7b0bba9..999ddc8083a1a4b4c271ea9bde4c1e45307e9542 100644
--- a/crates/agent_settings/src/agent_profile.rs
+++ b/crates/agent_settings/src/agent_profile.rs
@@ -1,15 +1,17 @@
use std::sync::Arc;
+use anyhow::{Result, bail};
use collections::IndexMap;
use convert_case::{Case, Casing as _};
use fs::Fs;
use gpui::{App, SharedString};
-use schemars::JsonSchema;
-use serde::{Deserialize, Serialize};
-use settings::{Settings as _, update_settings_file};
+use settings::{
+ AgentProfileContent, ContextServerPresetContent, Settings as _, SettingsContent,
+ update_settings_file,
+};
use util::ResultExt as _;
-use crate::AgentSettings;
+use crate::{AgentProfileId, AgentSettings};
pub mod builtin_profiles {
use super::AgentProfileId;
@@ -23,27 +25,6 @@ pub mod builtin_profiles {
}
}
-#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, JsonSchema)]
-pub struct AgentProfileId(pub Arc);
-
-impl AgentProfileId {
- pub fn as_str(&self) -> &str {
- &self.0
- }
-}
-
-impl std::fmt::Display for AgentProfileId {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{}", self.0)
- }
-}
-
-impl Default for AgentProfileId {
- fn default() -> Self {
- Self("write".into())
- }
-}
-
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct AgentProfile {
id: AgentProfileId,
@@ -87,10 +68,10 @@ impl AgentProfile {
.unwrap_or_default(),
};
- update_settings_file::(fs, cx, {
+ update_settings_file(fs, cx, {
let id = id.clone();
move |settings, _cx| {
- settings.create_profile(id, profile_settings).log_err();
+ profile_settings.save_to_settings(id, settings).log_err();
}
});
@@ -129,9 +110,71 @@ impl AgentProfileSettings {
.get(server_id)
.is_some_and(|preset| preset.tools.get(tool_name) == Some(&true))
}
+
+ pub fn save_to_settings(
+ &self,
+ profile_id: AgentProfileId,
+ content: &mut SettingsContent,
+ ) -> Result<()> {
+ let profiles = content
+ .agent
+ .get_or_insert_default()
+ .profiles
+ .get_or_insert_default();
+ if profiles.contains_key(&profile_id.0) {
+ bail!("profile with ID '{profile_id}' already exists");
+ }
+
+ profiles.insert(
+ profile_id.0,
+ AgentProfileContent {
+ name: self.name.clone().into(),
+ tools: self.tools.clone(),
+ enable_all_context_servers: Some(self.enable_all_context_servers),
+ context_servers: self
+ .context_servers
+ .clone()
+ .into_iter()
+ .map(|(server_id, preset)| {
+ (
+ server_id,
+ ContextServerPresetContent {
+ tools: preset.tools,
+ },
+ )
+ })
+ .collect(),
+ },
+ );
+
+ Ok(())
+ }
+}
+
+impl From for AgentProfileSettings {
+ fn from(content: AgentProfileContent) -> Self {
+ Self {
+ name: content.name.into(),
+ tools: content.tools,
+ enable_all_context_servers: content.enable_all_context_servers.unwrap_or_default(),
+ context_servers: content
+ .context_servers
+ .into_iter()
+ .map(|(server_id, preset)| (server_id, preset.into()))
+ .collect(),
+ }
+ }
}
#[derive(Debug, Clone, Default)]
pub struct ContextServerPreset {
pub tools: IndexMap, bool>,
}
+
+impl From for ContextServerPreset {
+ fn from(content: settings::ContextServerPresetContent) -> Self {
+ Self {
+ tools: content.tools,
+ }
+ }
+}
diff --git a/crates/agent_settings/src/agent_settings.rs b/crates/agent_settings/src/agent_settings.rs
index 71516b568c2db65d8266d4233d6f7d2256379e1e..e0389a47ce015f0644f7ebfe0025b8c0d74fdcd0 100644
--- a/crates/agent_settings/src/agent_settings.rs
+++ b/crates/agent_settings/src/agent_settings.rs
@@ -2,14 +2,16 @@ mod agent_profile;
use std::sync::Arc;
-use anyhow::{Result, bail};
use collections::IndexMap;
-use gpui::{App, Pixels, SharedString};
+use gpui::{App, Pixels, px};
use language_model::LanguageModel;
-use schemars::{JsonSchema, json_schema};
+use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
-use settings::{Settings, SettingsKey, SettingsSources, SettingsUi};
-use std::borrow::Cow;
+use settings::{
+ DefaultAgentView, DockPosition, LanguageModelParameters, LanguageModelSelection,
+ NotifyWhenAgentWaiting, Settings, SettingsContent,
+};
+use util::MergeFrom;
pub use crate::agent_profile::*;
@@ -22,37 +24,11 @@ pub fn init(cx: &mut App) {
AgentSettings::register(cx);
}
-#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema)]
-#[serde(rename_all = "snake_case")]
-pub enum AgentDockPosition {
- Left,
- #[default]
- Right,
- Bottom,
-}
-
-#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema)]
-#[serde(rename_all = "snake_case")]
-pub enum DefaultView {
- #[default]
- Thread,
- TextThread,
-}
-
-#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
-#[serde(rename_all = "snake_case")]
-pub enum NotifyWhenAgentWaiting {
- #[default]
- PrimaryScreen,
- AllScreens,
- Never,
-}
-
-#[derive(Default, Clone, Debug)]
+#[derive(Clone, Debug)]
pub struct AgentSettings {
pub enabled: bool,
pub button: bool,
- pub dock: AgentDockPosition,
+ pub dock: DockPosition,
pub default_width: Pixels,
pub default_height: Pixels,
pub default_model: Option,
@@ -60,9 +36,8 @@ pub struct AgentSettings {
pub commit_message_model: Option,
pub thread_summary_model: Option,
pub inline_alternatives: Vec,
- pub using_outdated_settings_version: bool,
pub default_profile: AgentProfileId,
- pub default_view: DefaultView,
+ pub default_view: DefaultAgentView,
pub profiles: IndexMap,
pub always_allow_tool_actions: bool,
pub notify_when_agent_waiting: NotifyWhenAgentWaiting,
@@ -81,11 +56,20 @@ pub struct AgentSettings {
impl AgentSettings {
pub fn temperature_for_model(model: &Arc, cx: &App) -> Option {
let settings = Self::get_global(cx);
- settings
- .model_parameters
- .iter()
- .rfind(|setting| setting.matches(model))
- .and_then(|m| m.temperature)
+ for setting in settings.model_parameters.iter().rev() {
+ if let Some(provider) = &setting.provider
+ && provider.0 != model.provider_id().0
+ {
+ continue;
+ }
+ if let Some(setting_model) = &setting.model
+ && *setting_model != model.id().0
+ {
+ continue;
+ }
+ return setting.temperature;
+ }
+ return None;
}
pub fn set_inline_assistant_model(&mut self, provider: String, model: String) {
@@ -114,223 +98,6 @@ impl AgentSettings {
}
}
-#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
-pub struct LanguageModelParameters {
- pub provider: Option,
- pub model: Option,
- pub temperature: Option,
-}
-
-impl LanguageModelParameters {
- pub fn matches(&self, model: &Arc) -> bool {
- if let Some(provider) = &self.provider
- && provider.0 != model.provider_id().0
- {
- return false;
- }
- if let Some(setting_model) = &self.model
- && *setting_model != model.id().0
- {
- return false;
- }
- true
- }
-}
-
-impl AgentSettingsContent {
- pub fn set_dock(&mut self, dock: AgentDockPosition) {
- self.dock = Some(dock);
- }
-
- pub fn set_model(&mut self, language_model: Arc) {
- let model = language_model.id().0.to_string();
- let provider = language_model.provider_id().0.to_string();
-
- self.default_model = Some(LanguageModelSelection {
- provider: provider.into(),
- model,
- });
- }
-
- pub fn set_inline_assistant_model(&mut self, provider: String, model: String) {
- self.inline_assistant_model = Some(LanguageModelSelection {
- provider: provider.into(),
- model,
- });
- }
-
- pub fn set_commit_message_model(&mut self, provider: String, model: String) {
- self.commit_message_model = Some(LanguageModelSelection {
- provider: provider.into(),
- model,
- });
- }
-
- pub fn set_thread_summary_model(&mut self, provider: String, model: String) {
- self.thread_summary_model = Some(LanguageModelSelection {
- provider: provider.into(),
- model,
- });
- }
-
- pub fn set_always_allow_tool_actions(&mut self, allow: bool) {
- self.always_allow_tool_actions = Some(allow);
- }
-
- pub fn set_play_sound_when_agent_done(&mut self, allow: bool) {
- self.play_sound_when_agent_done = Some(allow);
- }
-
- pub fn set_single_file_review(&mut self, allow: bool) {
- self.single_file_review = Some(allow);
- }
-
- pub fn set_use_modifier_to_send(&mut self, always_use: bool) {
- self.use_modifier_to_send = Some(always_use);
- }
-
- pub fn set_profile(&mut self, profile_id: AgentProfileId) {
- self.default_profile = Some(profile_id);
- }
-
- pub fn create_profile(
- &mut self,
- profile_id: AgentProfileId,
- profile_settings: AgentProfileSettings,
- ) -> Result<()> {
- let profiles = self.profiles.get_or_insert_default();
- if profiles.contains_key(&profile_id) {
- bail!("profile with ID '{profile_id}' already exists");
- }
-
- profiles.insert(
- profile_id,
- AgentProfileContent {
- name: profile_settings.name.into(),
- tools: profile_settings.tools,
- enable_all_context_servers: Some(profile_settings.enable_all_context_servers),
- context_servers: profile_settings
- .context_servers
- .into_iter()
- .map(|(server_id, preset)| {
- (
- server_id,
- ContextServerPresetContent {
- tools: preset.tools,
- },
- )
- })
- .collect(),
- },
- );
-
- Ok(())
- }
-}
-
-#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug, Default, SettingsUi, SettingsKey)]
-#[settings_key(key = "agent", fallback_key = "assistant")]
-pub struct AgentSettingsContent {
- /// Whether the Agent is enabled.
- ///
- /// Default: true
- enabled: Option,
- /// Whether to show the agent panel button in the status bar.
- ///
- /// Default: true
- button: Option,
- /// Where to dock the agent panel.
- ///
- /// Default: right
- dock: Option,
- /// Default width in pixels when the agent panel is docked to the left or right.
- ///
- /// Default: 640
- default_width: Option,
- /// Default height in pixels when the agent panel is docked to the bottom.
- ///
- /// Default: 320
- default_height: Option,
- /// The default model to use when creating new chats and for other features when a specific model is not specified.
- default_model: Option,
- /// Model to use for the inline assistant. Defaults to default_model when not specified.
- inline_assistant_model: Option,
- /// Model to use for generating git commit messages. Defaults to default_model when not specified.
- commit_message_model: Option,
- /// Model to use for generating thread summaries. Defaults to default_model when not specified.
- thread_summary_model: Option,
- /// Additional models with which to generate alternatives when performing inline assists.
- inline_alternatives: Option>,
- /// The default profile to use in the Agent.
- ///
- /// Default: write
- default_profile: Option,
- /// Which view type to show by default in the agent panel.
- ///
- /// Default: "thread"
- default_view: Option,
- /// The available agent profiles.
- pub profiles: Option>,
- /// Whenever a tool action would normally wait for your confirmation
- /// that you allow it, always choose to allow it.
- ///
- /// This setting has no effect on external agents that support permission modes, such as Claude Code.
- ///
- /// Set `agent_servers.claude.default_mode` to `bypassPermissions`, to disable all permission requests when using Claude Code.
- ///
- /// Default: false
- always_allow_tool_actions: Option,
- /// Where to show a popup notification when the agent is waiting for user input.
- ///
- /// Default: "primary_screen"
- notify_when_agent_waiting: Option,
- /// Whether to play a sound when the agent has either completed its response, or needs user input.
- ///
- /// Default: false
- play_sound_when_agent_done: Option,
- /// Whether to stream edits from the agent as they are received.
- ///
- /// Default: false
- stream_edits: Option,
- /// Whether to display agent edits in single-file editors in addition to the review multibuffer pane.
- ///
- /// Default: true
- single_file_review: Option,
- /// Additional parameters for language model requests. When making a request
- /// to a model, parameters will be taken from the last entry in this list
- /// that matches the model's provider and name. In each entry, both provider
- /// and model are optional, so that you can specify parameters for either
- /// one.
- ///
- /// Default: []
- #[serde(default)]
- model_parameters: Vec,
- /// What completion mode to enable for new threads
- ///
- /// Default: normal
- preferred_completion_mode: Option,
- /// Whether to show thumb buttons for feedback in the agent panel.
- ///
- /// Default: true
- enable_feedback: Option,
- /// Whether to have edit cards in the agent panel expanded, showing a preview of the full diff.
- ///
- /// Default: true
- expand_edit_card: Option,
- /// Whether to have terminal cards in the agent panel expanded, showing the whole command output.
- ///
- /// Default: true
- expand_terminal_card: Option,
- /// Whether to always use cmd-enter (or ctrl-enter on Linux or Windows) to send messages in the agent panel.
- ///
- /// Default: false
- use_modifier_to_send: Option,
- /// Minimum number of lines of height the agent message editor should have.
- ///
- /// Default: 4
- message_editor_min_lines: Option,
-}
-
#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Default)]
#[serde(rename_all = "snake_case")]
pub enum CompletionMode {
@@ -349,215 +116,139 @@ impl From for cloud_llm_client::CompletionMode {
}
}
-#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
-pub struct LanguageModelSelection {
- pub provider: LanguageModelProviderSetting,
- pub model: String,
+impl From for CompletionMode {
+ fn from(value: settings::CompletionMode) -> Self {
+ match value {
+ settings::CompletionMode::Normal => CompletionMode::Normal,
+ settings::CompletionMode::Burn => CompletionMode::Burn,
+ }
+ }
}
-#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
-pub struct LanguageModelProviderSetting(pub String);
+#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, JsonSchema)]
+pub struct AgentProfileId(pub Arc);
-impl JsonSchema for LanguageModelProviderSetting {
- fn schema_name() -> Cow<'static, str> {
- "LanguageModelProviderSetting".into()
- }
-
- fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
- // list the builtin providers as a subset so that we still auto complete them in the settings
- json_schema!({
- "anyOf": [
- {
- "type": "string",
- "enum": [
- "amazon-bedrock",
- "anthropic",
- "copilot_chat",
- "deepseek",
- "google",
- "lmstudio",
- "mistral",
- "ollama",
- "openai",
- "openrouter",
- "vercel",
- "x_ai",
- "zed.dev"
- ]
- },
- {
- "type": "string",
- }
- ]
- })
+impl AgentProfileId {
+ pub fn as_str(&self) -> &str {
+ &self.0
}
}
-impl From for LanguageModelProviderSetting {
- fn from(provider: String) -> Self {
- Self(provider)
+impl std::fmt::Display for AgentProfileId {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.0)
}
}
-impl From<&str> for LanguageModelProviderSetting {
- fn from(provider: &str) -> Self {
- Self(provider.to_string())
+impl Default for AgentProfileId {
+ fn default() -> Self {
+ Self("write".into())
}
}
-#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, JsonSchema)]
-pub struct AgentProfileContent {
- pub name: Arc,
- #[serde(default)]
- pub tools: IndexMap, bool>,
- /// Whether all context servers are enabled by default.
- pub enable_all_context_servers: Option,
- #[serde(default)]
- pub context_servers: IndexMap, ContextServerPresetContent>,
-}
-
-#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize, JsonSchema)]
-pub struct ContextServerPresetContent {
- pub tools: IndexMap, bool>,
-}
-
impl Settings for AgentSettings {
- const PRESERVED_KEYS: Option<&'static [&'static str]> = Some(&["version"]);
-
- type FileContent = AgentSettingsContent;
-
- fn load(
- sources: SettingsSources,
- _: &mut gpui::App,
- ) -> anyhow::Result {
- let mut settings = AgentSettings::default();
+ // todo!() test preserved keys logic
+ fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
+ let agent = content.agent.clone().unwrap();
+ Self {
+ enabled: agent.enabled.unwrap(),
+ button: agent.button.unwrap(),
+ dock: agent.dock.unwrap(),
+ default_width: px(agent.default_width.unwrap()),
+ default_height: px(agent.default_height.unwrap()),
+ default_model: Some(agent.default_model.unwrap()),
+ inline_assistant_model: agent.inline_assistant_model,
+ commit_message_model: agent.commit_message_model,
+ thread_summary_model: agent.thread_summary_model,
+ inline_alternatives: agent.inline_alternatives.unwrap_or_default(),
+ default_profile: AgentProfileId(agent.default_profile.unwrap()),
+ default_view: agent.default_view.unwrap(),
+ profiles: agent
+ .profiles
+ .unwrap()
+ .into_iter()
+ .map(|(key, val)| (AgentProfileId(key), val.into()))
+ .collect(),
+ always_allow_tool_actions: agent.always_allow_tool_actions.unwrap(),
+ notify_when_agent_waiting: agent.notify_when_agent_waiting.unwrap(),
+ play_sound_when_agent_done: agent.play_sound_when_agent_done.unwrap(),
+ stream_edits: agent.stream_edits.unwrap(),
+ single_file_review: agent.single_file_review.unwrap(),
+ model_parameters: agent.model_parameters,
+ preferred_completion_mode: agent.preferred_completion_mode.unwrap().into(),
+ enable_feedback: agent.enable_feedback.unwrap(),
+ expand_edit_card: agent.expand_edit_card.unwrap(),
+ expand_terminal_card: agent.expand_terminal_card.unwrap(),
+ use_modifier_to_send: agent.use_modifier_to_send.unwrap(),
+ message_editor_min_lines: agent.message_editor_min_lines.unwrap(),
+ }
+ }
- for value in sources.defaults_and_customizations() {
- merge(&mut settings.enabled, value.enabled);
- merge(&mut settings.button, value.button);
- merge(&mut settings.dock, value.dock);
- merge(
- &mut settings.default_width,
- value.default_width.map(Into::into),
- );
- merge(
- &mut settings.default_height,
- value.default_height.map(Into::into),
- );
- settings.default_model = value
- .default_model
- .clone()
- .or(settings.default_model.take());
- settings.inline_assistant_model = value
- .inline_assistant_model
- .clone()
- .or(settings.inline_assistant_model.take());
- settings.commit_message_model = value
- .clone()
- .commit_message_model
- .or(settings.commit_message_model.take());
- settings.thread_summary_model = value
- .clone()
- .thread_summary_model
- .or(settings.thread_summary_model.take());
- merge(
- &mut settings.inline_alternatives,
- value.inline_alternatives.clone(),
- );
- merge(
- &mut settings.notify_when_agent_waiting,
- value.notify_when_agent_waiting,
- );
- merge(
- &mut settings.play_sound_when_agent_done,
- value.play_sound_when_agent_done,
- );
- merge(&mut settings.stream_edits, value.stream_edits);
- merge(&mut settings.single_file_review, value.single_file_review);
- merge(&mut settings.default_profile, value.default_profile.clone());
- merge(&mut settings.default_view, value.default_view);
- merge(
- &mut settings.preferred_completion_mode,
- value.preferred_completion_mode,
- );
- merge(&mut settings.enable_feedback, value.enable_feedback);
- merge(&mut settings.expand_edit_card, value.expand_edit_card);
- merge(
- &mut settings.expand_terminal_card,
- value.expand_terminal_card,
- );
- merge(
- &mut settings.use_modifier_to_send,
- value.use_modifier_to_send,
- );
- merge(
- &mut settings.message_editor_min_lines,
- value.message_editor_min_lines,
+ fn refine(&mut self, content: &settings::SettingsContent, _: &mut App) {
+ let Some(value) = &content.agent else { return };
+ self.enabled.merge_from(&value.enabled);
+ self.button.merge_from(&value.button);
+ self.dock.merge_from(&value.dock);
+ self.default_width
+ .merge_from(&value.default_width.map(Into::into));
+ self.default_height
+ .merge_from(&value.default_height.map(Into::into));
+ self.default_model = value.default_model.clone().or(self.default_model.take());
+
+ self.inline_assistant_model = value
+ .inline_assistant_model
+ .clone()
+ .or(self.inline_assistant_model.take());
+ self.commit_message_model = value
+ .clone()
+ .commit_message_model
+ .or(self.commit_message_model.take());
+ self.thread_summary_model = value
+ .clone()
+ .thread_summary_model
+ .or(self.thread_summary_model.take());
+ self.inline_alternatives
+ .merge_from(&value.inline_alternatives.clone());
+ self.notify_when_agent_waiting
+ .merge_from(&value.notify_when_agent_waiting);
+ self.play_sound_when_agent_done
+ .merge_from(&value.play_sound_when_agent_done);
+ self.stream_edits.merge_from(&value.stream_edits);
+ self.single_file_review
+ .merge_from(&value.single_file_review);
+ self.default_profile
+ .merge_from(&value.default_profile.clone().map(AgentProfileId));
+ self.default_view.merge_from(&value.default_view);
+ self.preferred_completion_mode
+ .merge_from(&value.preferred_completion_mode.map(Into::into));
+ self.enable_feedback.merge_from(&value.enable_feedback);
+ self.expand_edit_card.merge_from(&value.expand_edit_card);
+ self.expand_terminal_card
+ .merge_from(&value.expand_terminal_card);
+ self.use_modifier_to_send
+ .merge_from(&value.use_modifier_to_send);
+
+ self.model_parameters
+ .extend_from_slice(&value.model_parameters);
+ self.message_editor_min_lines
+ .merge_from(&value.message_editor_min_lines);
+
+ if let Some(profiles) = value.profiles.as_ref() {
+ self.profiles.extend(
+ profiles
+ .into_iter()
+ .map(|(id, profile)| (AgentProfileId(id.clone()), profile.clone().into())),
);
-
- settings
- .model_parameters
- .extend_from_slice(&value.model_parameters);
-
- if let Some(profiles) = value.profiles.as_ref() {
- settings
- .profiles
- .extend(profiles.into_iter().map(|(id, profile)| {
- (
- id.clone(),
- AgentProfileSettings {
- name: profile.name.clone().into(),
- tools: profile.tools.clone(),
- enable_all_context_servers: profile
- .enable_all_context_servers
- .unwrap_or_default(),
- context_servers: profile
- .context_servers
- .iter()
- .map(|(context_server_id, preset)| {
- (
- context_server_id.clone(),
- ContextServerPreset {
- tools: preset.tools.clone(),
- },
- )
- })
- .collect(),
- },
- )
- }));
- }
}
-
- debug_assert!(
- !sources.default.always_allow_tool_actions.unwrap_or(false),
- "For security, agent.always_allow_tool_actions should always be false in default.json. If it's true, that is a bug that should be fixed!"
- );
-
- // For security reasons, only trust the user's global settings for whether to always allow tool actions.
- // If this could be overridden locally, an attacker could (e.g. by committing to source control and
- // convincing you to switch branches) modify your project-local settings to disable the agent's safety checks.
- settings.always_allow_tool_actions = sources
- .user
- .and_then(|setting| setting.always_allow_tool_actions)
- .unwrap_or(false);
-
- Ok(settings)
}
- fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut Self::FileContent) {
+ fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut SettingsContent) {
if let Some(b) = vscode
.read_value("chat.agent.enabled")
.and_then(|b| b.as_bool())
{
- current.enabled = Some(b);
- current.button = Some(b);
+ current.agent.get_or_insert_default().enabled = Some(b);
+ current.agent.get_or_insert_default().button = Some(b);
}
}
}
-
-fn merge(target: &mut T, value: Option) {
- if let Some(value) = value {
- *target = value;
- }
-}
diff --git a/crates/agent_ui/src/acp/thread_view.rs b/crates/agent_ui/src/acp/thread_view.rs
index 8e8f908bc2eea651babb73749e26cb2d6474f74f..cd72be9b184ded0d53125bfd569da89acff59a48 100644
--- a/crates/agent_ui/src/acp/thread_view.rs
+++ b/crates/agent_ui/src/acp/thread_view.rs
@@ -7,7 +7,7 @@ use acp_thread::{AgentConnection, Plan};
use action_log::ActionLog;
use agent_client_protocol::{self as acp, PromptCapabilities};
use agent_servers::{AgentServer, AgentServerDelegate};
-use agent_settings::{AgentProfileId, AgentSettings, CompletionMode, NotifyWhenAgentWaiting};
+use agent_settings::{AgentProfileId, AgentSettings, CompletionMode};
use agent2::{DbThreadMetadata, HistoryEntry, HistoryEntryId, HistoryStore, NativeAgentServer};
use anyhow::{Result, anyhow, bail};
use arrayvec::ArrayVec;
@@ -35,7 +35,7 @@ use markdown::{HeadingLevelStyles, Markdown, MarkdownElement, MarkdownStyle};
use project::{Project, ProjectEntryId};
use prompt_store::{PromptId, PromptStore};
use rope::Point;
-use settings::{Settings as _, SettingsStore};
+use settings::{NotifyWhenAgentWaiting, Settings as _, SettingsStore};
use std::cell::RefCell;
use std::path::Path;
use std::sync::Arc;
@@ -1591,7 +1591,7 @@ impl AcpThreadView {
task.shell = shell;
let terminal = terminal_panel.update_in(cx, |terminal_panel, window, cx| {
- terminal_panel.spawn_task(login.clone(), window, cx)
+ terminal_panel.spawn_task(&login, window, cx)
})?;
let terminal = terminal.await?;
diff --git a/crates/agent_ui/src/agent_configuration.rs b/crates/agent_ui/src/agent_configuration.rs
index aacfb423539496e6c5cb93ad8c12f1ed8ede346a..382a9db2573a21bcb74e75d15a6a87c0aa412588 100644
--- a/crates/agent_ui/src/agent_configuration.rs
+++ b/crates/agent_ui/src/agent_configuration.rs
@@ -26,11 +26,9 @@ use language_model::{
use notifications::status_toast::{StatusToast, ToastIcon};
use project::{
agent_server_store::{
- AgentServerCommand, AgentServerStore, AllAgentServersSettings, CLAUDE_CODE_NAME,
- CustomAgentServerSettings, GEMINI_NAME,
+ AgentServerStore, AllAgentServersSettings, CLAUDE_CODE_NAME, GEMINI_NAME,
},
context_server_store::{ContextServerConfiguration, ContextServerStatus, ContextServerStore},
- project_settings::{ContextServerSettings, ProjectSettings},
};
use settings::{Settings, SettingsStore, update_settings_file};
use ui::{
@@ -417,8 +415,8 @@ impl AgentConfiguration {
always_allow_tool_actions,
move |state, _window, cx| {
let allow = state == &ToggleState::Selected;
- update_settings_file::(fs.clone(), cx, move |settings, _| {
- settings.set_always_allow_tool_actions(allow);
+ update_settings_file(fs.clone(), cx, move |settings, _| {
+ settings.agent.get_or_insert_default().set_always_allow_tool_actions(allow);
});
},
)
@@ -435,8 +433,11 @@ impl AgentConfiguration {
single_file_review,
move |state, _window, cx| {
let allow = state == &ToggleState::Selected;
- update_settings_file::(fs.clone(), cx, move |settings, _| {
- settings.set_single_file_review(allow);
+ update_settings_file(fs.clone(), cx, move |settings, _| {
+ settings
+ .agent
+ .get_or_insert_default()
+ .set_single_file_review(allow);
});
},
)
@@ -455,8 +456,8 @@ impl AgentConfiguration {
play_sound_when_agent_done,
move |state, _window, cx| {
let allow = state == &ToggleState::Selected;
- update_settings_file::(fs.clone(), cx, move |settings, _| {
- settings.set_play_sound_when_agent_done(allow);
+ update_settings_file(fs.clone(), cx, move |settings, _| {
+ settings.agent.get_or_insert_default().set_play_sound_when_agent_done(allow);
});
},
)
@@ -475,8 +476,8 @@ impl AgentConfiguration {
use_modifier_to_send,
move |state, _window, cx| {
let allow = state == &ToggleState::Selected;
- update_settings_file::(fs.clone(), cx, move |settings, _| {
- settings.set_use_modifier_to_send(allow);
+ update_settings_file(fs.clone(), cx, move |settings, _| {
+ settings.agent.get_or_insert_default().set_use_modifier_to_send(allow);
});
},
)
@@ -795,14 +796,14 @@ impl AgentConfiguration {
async move |cx| {
uninstall_extension_task.await?;
cx.update(|cx| {
- update_settings_file::(
+ update_settings_file(
fs.clone(),
cx,
{
let context_server_id =
context_server_id.clone();
move |settings, _| {
- settings
+ settings.project
.context_servers
.remove(&context_server_id.0);
}
@@ -902,67 +903,53 @@ impl AgentConfiguration {
.flex_none()
.child(context_server_configuration_menu)
.child(
- Switch::new("context-server-switch", is_running.into())
- .color(SwitchColor::Accent)
- .on_click({
- let context_server_manager =
- self.context_server_store.clone();
- let fs = self.fs.clone();
-
- move |state, _window, cx| {
- let is_enabled = match state {
- ToggleState::Unselected
- | ToggleState::Indeterminate => {
- context_server_manager.update(
- cx,
- |this, cx| {
- this.stop_server(
- &context_server_id,
- cx,
- )
- .log_err();
- },
- );
- false
- }
- ToggleState::Selected => {
- context_server_manager.update(
- cx,
- |this, cx| {
- if let Some(server) =
- this.get_server(&context_server_id)
- {
- this.start_server(server, cx);
- }
- },
- );
- true
- }
- };
- update_settings_file::(
- fs.clone(),
- cx,
- {
- let context_server_id =
- context_server_id.clone();
-
- move |settings, _| {
- settings
- .context_servers
- .entry(context_server_id.0)
- .or_insert_with(|| {
- ContextServerSettings::Extension {
- enabled: is_enabled,
- settings: serde_json::json!({}),
- }
- })
- .set_enabled(is_enabled);
+ Switch::new("context-server-switch", is_running.into())
+ .color(SwitchColor::Accent)
+ .on_click({
+ let context_server_manager = self.context_server_store.clone();
+ let fs = self.fs.clone();
+
+ move |state, _window, cx| {
+ let is_enabled = match state {
+ ToggleState::Unselected
+ | ToggleState::Indeterminate => {
+ context_server_manager.update(cx, |this, cx| {
+ this.stop_server(&context_server_id, cx)
+ .log_err();
+ });
+ false
+ }
+ ToggleState::Selected => {
+ context_server_manager.update(cx, |this, cx| {
+ if let Some(server) =
+ this.get_server(&context_server_id)
+ {
+ this.start_server(server, cx);
}
- },
- );
- }
- }),
- ),
+ });
+ true
+ }
+ };
+ update_settings_file(fs.clone(), cx, {
+ let context_server_id = context_server_id.clone();
+
+ move |settings, _| {
+ settings
+ .project
+ .context_servers
+ .entry(context_server_id.0)
+ .or_insert_with(|| {
+ settings::ContextServerSettingsContent::Extension {
+ enabled: is_enabled,
+ settings: serde_json::json!({}),
+ }
+ })
+ .set_enabled(is_enabled);
+ }
+ });
+ }
+ }),
+ ),
),
)
.map(|parent| {
@@ -1260,15 +1247,12 @@ fn show_unable_to_uninstall_extension_with_context_server(
let context_server_id = context_server_id.clone();
async move |_workspace_handle, cx| {
cx.update(|cx| {
- update_settings_file::(
- fs,
- cx,
- move |settings, _| {
- settings
- .context_servers
- .remove(&context_server_id.0);
- },
- );
+ update_settings_file(fs, cx, move |settings, _| {
+ settings
+ .project
+ .context_servers
+ .remove(&context_server_id.0);
+ });
})?;
anyhow::Ok(())
}
@@ -1306,7 +1290,7 @@ async fn open_new_agent_servers_entry_in_settings_editor(
let settings = cx.global::();
let mut unique_server_name = None;
- let edits = settings.edits_for_update::(&text, |file| {
+ let edits = settings.edits_for_update(&text, |settings| {
let server_name: Option = (0..u8::MAX)
.map(|i| {
if i == 0 {
@@ -1315,20 +1299,27 @@ async fn open_new_agent_servers_entry_in_settings_editor(
format!("your_agent_{}", i).into()
}
})
- .find(|name| !file.custom.contains_key(name));
+ .find(|name| {
+ !settings
+ .agent_servers
+ .as_ref()
+ .is_some_and(|agent_servers| agent_servers.custom.contains_key(name))
+ });
if let Some(server_name) = server_name {
unique_server_name = Some(server_name.clone());
- file.custom.insert(
- server_name,
- CustomAgentServerSettings {
- command: AgentServerCommand {
+ settings
+ .agent_servers
+ .get_or_insert_default()
+ .custom
+ .insert(
+ server_name,
+ settings::CustomAgentServerSettings {
path: "path_to_executable".into(),
args: vec![],
env: Some(HashMap::default()),
+ default_mode: None,
},
- default_mode: None,
- },
- );
+ );
}
});
diff --git a/crates/agent_ui/src/agent_configuration/add_llm_provider_modal.rs b/crates/agent_ui/src/agent_configuration/add_llm_provider_modal.rs
index 182831f488870997d175cce0ad7e1c94e392f1ea..373756b2c45ceeb65afebaf1f2d82b1fc16c017d 100644
--- a/crates/agent_ui/src/agent_configuration/add_llm_provider_modal.rs
+++ b/crates/agent_ui/src/agent_configuration/add_llm_provider_modal.rs
@@ -5,11 +5,8 @@ use collections::HashSet;
use fs::Fs;
use gpui::{DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Render, Task};
use language_model::LanguageModelRegistry;
-use language_models::{
- AllLanguageModelSettings, OpenAiCompatibleSettingsContent,
- provider::open_ai_compatible::{AvailableModel, ModelCapabilities},
-};
-use settings::update_settings_file;
+use language_models::provider::open_ai_compatible::{AvailableModel, ModelCapabilities};
+use settings::{OpenAiCompatibleSettingsContent, update_settings_file};
use ui::{
Banner, Checkbox, KeyBinding, Modal, ModalFooter, ModalHeader, Section, ToggleState, prelude::*,
};
@@ -238,14 +235,19 @@ fn save_provider_to_settings(
task.await
.map_err(|_| "Failed to write API key to keychain")?;
cx.update(|cx| {
- update_settings_file::(fs, cx, |settings, _cx| {
- settings.openai_compatible.get_or_insert_default().insert(
- provider_name,
- OpenAiCompatibleSettingsContent {
- api_url,
- available_models: models,
- },
- );
+ update_settings_file(fs, cx, |settings, _cx| {
+ settings
+ .language_models
+ .get_or_insert_default()
+ .openai_compatible
+ .get_or_insert_default()
+ .insert(
+ provider_name,
+ OpenAiCompatibleSettingsContent {
+ api_url,
+ available_models: models,
+ },
+ );
});
})
.ok();
diff --git a/crates/agent_ui/src/agent_configuration/configure_context_server_modal.rs b/crates/agent_ui/src/agent_configuration/configure_context_server_modal.rs
index 4d338840143fbcf007f7d5c66e2406ef4bb9fc88..ce8e167dab3ed2e4d84c4afd747cb266740f1d42 100644
--- a/crates/agent_ui/src/agent_configuration/configure_context_server_modal.rs
+++ b/crates/agent_ui/src/agent_configuration/configure_context_server_modal.rs
@@ -422,18 +422,17 @@ impl ConfigureContextServerModal {
workspace.update(cx, |workspace, cx| {
let fs = workspace.app_state().fs.clone();
let original_server_id = self.original_server_id.clone();
- update_settings_file::(
- fs.clone(),
- cx,
- move |project_settings, _| {
- if let Some(original_id) = original_server_id {
- if original_id != id {
- project_settings.context_servers.remove(&original_id.0);
- }
+ update_settings_file(fs.clone(), cx, move |current, _| {
+ if let Some(original_id) = original_server_id {
+ if original_id != id {
+ current.project.context_servers.remove(&original_id.0);
}
- project_settings.context_servers.insert(id.0, settings);
- },
- );
+ }
+ current
+ .project
+ .context_servers
+ .insert(id.0, settings.into());
+ });
});
} else if let Some(existing_server) = existing_server {
self.context_server_store
diff --git a/crates/agent_ui/src/agent_configuration/tool_picker.rs b/crates/agent_ui/src/agent_configuration/tool_picker.rs
index 2ba92fa6b7993664d278cfd57d851dcfd9cb0922..c624948944c0624e75e385d1b4b15aa77fea9bcd 100644
--- a/crates/agent_ui/src/agent_configuration/tool_picker.rs
+++ b/crates/agent_ui/src/agent_configuration/tool_picker.rs
@@ -1,14 +1,11 @@
use std::{collections::BTreeMap, sync::Arc};
-use agent_settings::{
- AgentProfileContent, AgentProfileId, AgentProfileSettings, AgentSettings, AgentSettingsContent,
- ContextServerPresetContent,
-};
+use agent_settings::{AgentProfileId, AgentProfileSettings};
use assistant_tool::{ToolSource, ToolWorkingSet};
use fs::Fs;
use gpui::{App, Context, DismissEvent, Entity, EventEmitter, Focusable, Task, WeakEntity, Window};
use picker::{Picker, PickerDelegate};
-use settings::update_settings_file;
+use settings::{AgentProfileContent, ContextServerPresetContent, update_settings_file};
use ui::{ListItem, ListItemSpacing, prelude::*};
use util::ResultExt as _;
@@ -266,15 +263,19 @@ impl PickerDelegate for ToolPickerDelegate {
is_enabled
};
- update_settings_file::(self.fs.clone(), cx, {
+ update_settings_file(self.fs.clone(), cx, {
let profile_id = self.profile_id.clone();
let default_profile = self.profile_settings.clone();
let server_id = server_id.clone();
let tool_name = tool_name.clone();
- move |settings: &mut AgentSettingsContent, _cx| {
- let profiles = settings.profiles.get_or_insert_default();
+ move |settings, _cx| {
+ let profiles = settings
+ .agent
+ .get_or_insert_default()
+ .profiles
+ .get_or_insert_default();
let profile = profiles
- .entry(profile_id)
+ .entry(profile_id.0)
.or_insert_with(|| AgentProfileContent {
name: default_profile.name.into(),
tools: default_profile.tools,
diff --git a/crates/agent_ui/src/agent_model_selector.rs b/crates/agent_ui/src/agent_model_selector.rs
index 58b95d9b1f1b1e8abe7335a2299bee7545b7653e..fe25cadc3c1df785c89318882a246e2209cb42e6 100644
--- a/crates/agent_ui/src/agent_model_selector.rs
+++ b/crates/agent_ui/src/agent_model_selector.rs
@@ -2,7 +2,6 @@ use crate::{
ModelUsageContext,
language_model_selector::{LanguageModelSelector, language_model_selector},
};
-use agent_settings::AgentSettings;
use fs::Fs;
use gpui::{Entity, FocusHandle, SharedString};
use picker::popover_menu::PickerPopoverMenu;
@@ -39,14 +38,12 @@ impl AgentModelSelector {
let model_id = model.id().0.to_string();
match &model_usage_context {
ModelUsageContext::InlineAssistant => {
- update_settings_file::(
- fs.clone(),
- cx,
- move |settings, _cx| {
- settings
- .set_inline_assistant_model(provider.clone(), model_id);
- },
- );
+ update_settings_file(fs.clone(), cx, move |settings, _cx| {
+ settings
+ .agent
+ .get_or_insert_default()
+ .set_inline_assistant_model(provider.clone(), model_id);
+ });
}
}
},
diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs
index 7334f1cf1283ad45e0e98d296b3641c9b8812ef5..976dbb411990b4eb647bdec9af83f97c3a9bda84 100644
--- a/crates/agent_ui/src/agent_panel.rs
+++ b/crates/agent_ui/src/agent_panel.rs
@@ -10,6 +10,9 @@ use project::agent_server_store::{
AgentServerCommand, AllAgentServersSettings, CLAUDE_CODE_NAME, GEMINI_NAME,
};
use serde::{Deserialize, Serialize};
+use settings::{
+ DefaultAgentView as DefaultView, LanguageModelProviderSetting, LanguageModelSelection,
+};
use zed_actions::OpenBrowser;
use zed_actions::agent::{OpenClaudeCodeOnboardingModal, ReauthenticateAgent};
@@ -33,7 +36,7 @@ use agent::{
history_store::{HistoryEntryId, HistoryStore},
thread_store::{TextThreadStore, ThreadStore},
};
-use agent_settings::{AgentDockPosition, AgentSettings, DefaultView};
+use agent_settings::AgentSettings;
use ai_onboarding::AgentPanelOnboarding;
use anyhow::{Result, anyhow};
use assistant_context::{AssistantContext, ContextEvent, ContextSummary};
@@ -1058,17 +1061,14 @@ impl AgentPanel {
match self.active_view.which_font_size_used() {
WhichFontSize::AgentFont => {
if persist {
- update_settings_file::(
- self.fs.clone(),
- cx,
- move |settings, cx| {
- let agent_font_size =
- ThemeSettings::get_global(cx).agent_font_size(cx) + delta;
- let _ = settings
- .agent_font_size
- .insert(Some(theme::clamp_font_size(agent_font_size).into()));
- },
- );
+ update_settings_file(self.fs.clone(), cx, move |settings, cx| {
+ let agent_font_size =
+ ThemeSettings::get_global(cx).agent_font_size(cx) + delta;
+ let _ = settings
+ .theme
+ .agent_font_size
+ .insert(Some(theme::clamp_font_size(agent_font_size).into()));
+ });
} else {
theme::adjust_agent_font_size(cx, |size| size + delta);
}
@@ -1089,8 +1089,8 @@ impl AgentPanel {
cx: &mut Context,
) {
if action.persist {
- update_settings_file::(self.fs.clone(), cx, move |settings, _| {
- settings.agent_font_size = None;
+ update_settings_file(self.fs.clone(), cx, move |settings, _| {
+ settings.theme.agent_font_size = None;
});
} else {
theme::reset_agent_font_size(cx);
@@ -1175,11 +1175,17 @@ impl AgentPanel {
.is_none_or(|model| model.provider.id() != provider.id())
&& let Some(model) = provider.default_model(cx)
{
- update_settings_file::(
- self.fs.clone(),
- cx,
- move |settings, _| settings.set_model(model),
- );
+ update_settings_file(self.fs.clone(), cx, move |settings, _| {
+ let provider = model.provider_id().0.to_string();
+ let model = model.id().0.to_string();
+ settings
+ .agent
+ .get_or_insert_default()
+ .set_model(LanguageModelSelection {
+ provider: LanguageModelProviderSetting(provider),
+ model,
+ })
+ });
}
self.new_thread(&NewThread::default(), window, cx);
@@ -1424,11 +1430,7 @@ impl Focusable for AgentPanel {
}
fn agent_panel_dock_position(cx: &App) -> DockPosition {
- match AgentSettings::get_global(cx).dock {
- AgentDockPosition::Left => DockPosition::Left,
- AgentDockPosition::Bottom => DockPosition::Bottom,
- AgentDockPosition::Right => DockPosition::Right,
- }
+ AgentSettings::get_global(cx).dock.into()
}
impl EventEmitter for AgentPanel {}
@@ -1447,13 +1449,11 @@ impl Panel for AgentPanel {
}
fn set_position(&mut self, position: DockPosition, _: &mut Window, cx: &mut Context) {
- settings::update_settings_file::(self.fs.clone(), cx, move |settings, _| {
- let dock = match position {
- DockPosition::Left => AgentDockPosition::Left,
- DockPosition::Bottom => AgentDockPosition::Bottom,
- DockPosition::Right => AgentDockPosition::Right,
- };
- settings.set_dock(dock);
+ settings::update_settings_file(self.fs.clone(), cx, move |settings, _| {
+ settings
+ .agent
+ .get_or_insert_default()
+ .set_dock(position.into());
});
}
diff --git a/crates/agent_ui/src/agent_ui.rs b/crates/agent_ui/src/agent_ui.rs
index 94efa767c5f1cd126695b8345230fba78583eb2f..e34789d62d2c95b06f5c4f03b93b60f01c6dbf6a 100644
--- a/crates/agent_ui/src/agent_ui.rs
+++ b/crates/agent_ui/src/agent_ui.rs
@@ -14,7 +14,6 @@ mod message_editor;
mod profile_selector;
mod slash_command;
mod slash_command_picker;
-mod slash_command_settings;
mod terminal_codegen;
mod terminal_inline_assistant;
mod text_thread_editor;
@@ -24,7 +23,7 @@ use std::rc::Rc;
use std::sync::Arc;
use agent::ThreadId;
-use agent_settings::{AgentProfileId, AgentSettings, LanguageModelSelection};
+use agent_settings::{AgentProfileId, AgentSettings};
use assistant_slash_command::SlashCommandRegistry;
use client::Client;
use command_palette_hooks::CommandPaletteFilter;
@@ -40,13 +39,12 @@ use project::agent_server_store::AgentServerCommand;
use prompt_store::PromptBuilder;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
-use settings::{Settings as _, SettingsStore};
+use settings::{LanguageModelSelection, Settings as _, SettingsStore};
use std::any::TypeId;
use crate::agent_configuration::{ConfigureContextServerModal, ManageProfilesModal};
pub use crate::agent_panel::{AgentPanel, ConcreteAssistantPanelDelegate};
pub use crate::inline_assistant::InlineAssistant;
-use crate::slash_command_settings::SlashCommandSettings;
pub use agent_diff::{AgentDiffPane, AgentDiffToolbar};
pub use text_thread_editor::{AgentPanelDelegate, TextThreadEditor};
use zed_actions;
@@ -257,7 +255,6 @@ pub fn init(
cx: &mut App,
) {
AgentSettings::register(cx);
- SlashCommandSettings::register(cx);
assistant_context::init(client.clone(), cx);
rules_library::init(cx);
@@ -413,8 +410,6 @@ fn register_slash_commands(cx: &mut App) {
slash_command_registry.register_command(assistant_slash_commands::DeltaSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::OutlineSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::TabSlashCommand, true);
- slash_command_registry
- .register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::PromptSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::SelectionCommand, true);
slash_command_registry.register_command(assistant_slash_commands::DefaultSlashCommand, false);
@@ -434,21 +429,4 @@ fn register_slash_commands(cx: &mut App) {
}
})
.detach();
-
- update_slash_commands_from_settings(cx);
- cx.observe_global::(update_slash_commands_from_settings)
- .detach();
-}
-
-fn update_slash_commands_from_settings(cx: &mut App) {
- let slash_command_registry = SlashCommandRegistry::global(cx);
- let settings = SlashCommandSettings::get_global(cx);
-
- if settings.cargo_workspace.enabled {
- slash_command_registry
- .register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true);
- } else {
- slash_command_registry
- .unregister_command(assistant_slash_commands::CargoWorkspaceSlashCommand);
- }
}
diff --git a/crates/agent_ui/src/context_picker/completion_provider.rs b/crates/agent_ui/src/context_picker/completion_provider.rs
index c9cd69bf8e49b2e4f20148640cd029caea51264f..01a7a51316eee4709eaf9c17c8840e3cd637a62b 100644
--- a/crates/agent_ui/src/context_picker/completion_provider.rs
+++ b/crates/agent_ui/src/context_picker/completion_provider.rs
@@ -743,15 +743,15 @@ impl CompletionProvider for ContextPickerCompletionProvider {
_window: &mut Window,
cx: &mut Context,
) -> Task>> {
- let state = buffer.update(cx, |buffer, _cx| {
- let position = buffer_position.to_point(buffer);
- let line_start = Point::new(position.row, 0);
- let offset_to_line = buffer.point_to_offset(line_start);
- let mut lines = buffer.text_for_range(line_start..position).lines();
- let line = lines.next()?;
- MentionCompletion::try_parse(line, offset_to_line)
- });
- let Some(state) = state else {
+ let snapshot = buffer.read(cx).snapshot();
+ let position = buffer_position.to_point(&snapshot);
+ let line_start = Point::new(position.row, 0);
+ let offset_to_line = snapshot.point_to_offset(line_start);
+ let mut lines = snapshot.text_for_range(line_start..position).lines();
+ let Some(line) = lines.next() else {
+ return Task::ready(Ok(Vec::new()));
+ };
+ let Some(state) = MentionCompletion::try_parse(line, offset_to_line) else {
return Task::ready(Ok(Vec::new()));
};
@@ -761,7 +761,6 @@ impl CompletionProvider for ContextPickerCompletionProvider {
return Task::ready(Ok(Vec::new()));
};
- let snapshot = buffer.read(cx).snapshot();
let source_range = snapshot.anchor_before(state.source_range.start)
..snapshot.anchor_after(state.source_range.end);
diff --git a/crates/agent_ui/src/context_server_configuration.rs b/crates/agent_ui/src/context_server_configuration.rs
index fe15e8606d4026054ef0d04f1aab08375a440cf7..3a1a8695172fcb68e52b6269b88b7b1576c2a5cb 100644
--- a/crates/agent_ui/src/context_server_configuration.rs
+++ b/crates/agent_ui/src/context_server_configuration.rs
@@ -5,7 +5,6 @@ use extension::ExtensionManifest;
use fs::Fs;
use gpui::WeakEntity;
use language::LanguageRegistry;
-use project::project_settings::ProjectSettings;
use settings::update_settings_file;
use ui::prelude::*;
use util::ResultExt;
@@ -69,8 +68,9 @@ fn remove_context_server_settings(
fs: Arc,
cx: &mut App,
) {
- update_settings_file::(fs, cx, move |settings, _| {
+ update_settings_file(fs, cx, move |settings, _| {
settings
+ .project
.context_servers
.retain(|server_id, _| !context_server_ids.contains(server_id));
});
diff --git a/crates/agent_ui/src/profile_selector.rs b/crates/agent_ui/src/profile_selector.rs
index 85f74a0f7445df03a24bf083f31d56f97e5a07b2..af2354f7a854fd84483889f18e0f51e1c294d8a2 100644
--- a/crates/agent_ui/src/profile_selector.rs
+++ b/crates/agent_ui/src/profile_selector.rs
@@ -1,11 +1,10 @@
use crate::{ManageProfiles, ToggleProfileSelector};
use agent_settings::{
- AgentDockPosition, AgentProfile, AgentProfileId, AgentSettings, AvailableProfiles,
- builtin_profiles,
+ AgentProfile, AgentProfileId, AgentSettings, AvailableProfiles, builtin_profiles,
};
use fs::Fs;
use gpui::{Action, Entity, FocusHandle, Subscription, prelude::*};
-use settings::{Settings as _, SettingsStore, update_settings_file};
+use settings::{DockPosition, Settings as _, SettingsStore, update_settings_file};
use std::sync::Arc;
use ui::{
ContextMenu, ContextMenuEntry, DocumentationEdge, DocumentationSide, PopoverMenu,
@@ -142,10 +141,13 @@ impl ProfileSelector {
let fs = self.fs.clone();
let provider = self.provider.clone();
move |_window, cx| {
- update_settings_file::(fs.clone(), cx, {
+ update_settings_file(fs.clone(), cx, {
let profile_id = profile_id.clone();
move |settings, _cx| {
- settings.set_profile(profile_id);
+ settings
+ .agent
+ .get_or_insert_default()
+ .set_profile(profile_id.0);
}
});
@@ -216,10 +218,10 @@ impl Render for ProfileSelector {
}
}
-fn documentation_side(position: AgentDockPosition) -> DocumentationSide {
+fn documentation_side(position: DockPosition) -> DocumentationSide {
match position {
- AgentDockPosition::Left => DocumentationSide::Right,
- AgentDockPosition::Bottom => DocumentationSide::Left,
- AgentDockPosition::Right => DocumentationSide::Left,
+ DockPosition::Left => DocumentationSide::Right,
+ DockPosition::Bottom => DocumentationSide::Left,
+ DockPosition::Right => DocumentationSide::Left,
}
}
diff --git a/crates/agent_ui/src/slash_command_settings.rs b/crates/agent_ui/src/slash_command_settings.rs
deleted file mode 100644
index 9580ffef0f317fbe726c57041fad4f0fa438e143..0000000000000000000000000000000000000000
--- a/crates/agent_ui/src/slash_command_settings.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-use anyhow::Result;
-use gpui::App;
-use schemars::JsonSchema;
-use serde::{Deserialize, Serialize};
-use settings::{Settings, SettingsKey, SettingsSources, SettingsUi};
-
-/// Settings for slash commands.
-#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema, SettingsUi, SettingsKey)]
-#[settings_key(key = "slash_commands")]
-pub struct SlashCommandSettings {
- /// Settings for the `/cargo-workspace` slash command.
- #[serde(default)]
- pub cargo_workspace: CargoWorkspaceCommandSettings,
-}
-
-/// Settings for the `/cargo-workspace` slash command.
-#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema)]
-pub struct CargoWorkspaceCommandSettings {
- /// Whether `/cargo-workspace` is enabled.
- #[serde(default)]
- pub enabled: bool,
-}
-
-impl Settings for SlashCommandSettings {
- type FileContent = Self;
-
- fn load(sources: SettingsSources, _cx: &mut App) -> Result {
- SettingsSources::::json_merge_with(
- [sources.default]
- .into_iter()
- .chain(sources.user)
- .chain(sources.server),
- )
- }
-
- fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
-}
diff --git a/crates/agent_ui/src/text_thread_editor.rs b/crates/agent_ui/src/text_thread_editor.rs
index d979db5e0468b696d32ed755aec1ef47e2fd3df3..3c09e47852ffae8f45a5315859a7bb3392b1680d 100644
--- a/crates/agent_ui/src/text_thread_editor.rs
+++ b/crates/agent_ui/src/text_thread_editor.rs
@@ -3,7 +3,7 @@ use crate::{
language_model_selector::{LanguageModelSelector, language_model_selector},
ui::BurnModeTooltip,
};
-use agent_settings::{AgentSettings, CompletionMode};
+use agent_settings::CompletionMode;
use anyhow::Result;
use assistant_slash_command::{SlashCommand, SlashCommandOutputSection, SlashCommandWorkingSet};
use assistant_slash_commands::{DefaultSlashCommand, FileSlashCommand, selections_creases};
@@ -41,7 +41,10 @@ use project::{Project, Worktree};
use project::{ProjectPath, lsp_store::LocalLspAdapterDelegate};
use rope::Point;
use serde::{Deserialize, Serialize};
-use settings::{Settings, SettingsStore, update_settings_file};
+use settings::{
+ LanguageModelProviderSetting, LanguageModelSelection, Settings, SettingsStore,
+ update_settings_file,
+};
use std::{
any::TypeId,
cmp,
@@ -294,11 +297,16 @@ impl TextThreadEditor {
language_model_selector(
|cx| LanguageModelRegistry::read_global(cx).default_model(),
move |model, cx| {
- update_settings_file::(
- fs.clone(),
- cx,
- move |settings, _| settings.set_model(model.clone()),
- );
+ update_settings_file(fs.clone(), cx, move |settings, _| {
+ let provider = model.provider_id().0.to_string();
+ let model = model.id().0.to_string();
+ settings.agent.get_or_insert_default().set_model(
+ LanguageModelSelection {
+ provider: LanguageModelProviderSetting(provider),
+ model,
+ },
+ )
+ });
},
window,
cx,
@@ -477,7 +485,7 @@ impl TextThreadEditor {
return;
}
- let selections = self.editor.read(cx).selections.disjoint_anchors();
+ let selections = self.editor.read(cx).selections.disjoint_anchors_arc();
let mut commands_by_range = HashMap::default();
let workspace = self.workspace.clone();
self.context.update(cx, |context, cx| {
@@ -1823,7 +1831,7 @@ impl TextThreadEditor {
fn split(&mut self, _: &Split, _window: &mut Window, cx: &mut Context) {
self.context.update(cx, |context, cx| {
- let selections = self.editor.read(cx).selections.disjoint_anchors();
+ let selections = self.editor.read(cx).selections.disjoint_anchors_arc();
for selection in selections.as_ref() {
let buffer = self.editor.read(cx).buffer().read(cx).snapshot(cx);
let range = selection
diff --git a/crates/anthropic/Cargo.toml b/crates/anthropic/Cargo.toml
index 8e82c7cdd6a6a1ac7dd0e8b165f4d924c85d39ab..c8103e5bfb533a0f7f8e88995ac0927073a9793f 100644
--- a/crates/anthropic/Cargo.toml
+++ b/crates/anthropic/Cargo.toml
@@ -23,6 +23,7 @@ http_client.workspace = true
schemars = { workspace = true, optional = true }
serde.workspace = true
serde_json.workspace = true
+settings.workspace = true
strum.workspace = true
thiserror.workspace = true
workspace-hack.workspace = true
diff --git a/crates/anthropic/src/anthropic.rs b/crates/anthropic/src/anthropic.rs
index 7fd0fb4bc5abd983c57507522c2a37dffcbfa258..a9a4fbb6340b59cafe3bbc555efb6ad5c9e11806 100644
--- a/crates/anthropic/src/anthropic.rs
+++ b/crates/anthropic/src/anthropic.rs
@@ -8,6 +8,7 @@ use futures::{AsyncBufReadExt, AsyncReadExt, StreamExt, io::BufReader, stream::B
use http_client::http::{self, HeaderMap, HeaderValue};
use http_client::{AsyncBody, HttpClient, Method, Request as HttpRequest, StatusCode};
use serde::{Deserialize, Serialize};
+pub use settings::{AnthropicAvailableModel as AvailableModel, ModelMode};
use strum::{EnumIter, EnumString};
use thiserror::Error;
@@ -31,6 +32,24 @@ pub enum AnthropicModelMode {
},
}
+impl From for AnthropicModelMode {
+ fn from(value: ModelMode) -> Self {
+ match value {
+ ModelMode::Default => AnthropicModelMode::Default,
+ ModelMode::Thinking { budget_tokens } => AnthropicModelMode::Thinking { budget_tokens },
+ }
+ }
+}
+
+impl From for ModelMode {
+ fn from(value: AnthropicModelMode) -> Self {
+ match value {
+ AnthropicModelMode::Default => ModelMode::Default,
+ AnthropicModelMode::Thinking { budget_tokens } => ModelMode::Thinking { budget_tokens },
+ }
+ }
+}
+
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, EnumIter)]
pub enum Model {
diff --git a/crates/assistant_slash_commands/Cargo.toml b/crates/assistant_slash_commands/Cargo.toml
index c054c3ced84825bcd131bdd76644c00595c4c4a9..f151515d4235b7ecb150539aceb1c5478960517b 100644
--- a/crates/assistant_slash_commands/Cargo.toml
+++ b/crates/assistant_slash_commands/Cargo.toml
@@ -14,7 +14,6 @@ path = "src/assistant_slash_commands.rs"
[dependencies]
anyhow.workspace = true
assistant_slash_command.workspace = true
-cargo_toml.workspace = true
chrono.workspace = true
collections.workspace = true
context_server.workspace = true
@@ -35,7 +34,6 @@ serde.workspace = true
serde_json.workspace = true
smol.workspace = true
text.workspace = true
-toml.workspace = true
ui.workspace = true
util.workspace = true
workspace.workspace = true
diff --git a/crates/assistant_slash_commands/src/assistant_slash_commands.rs b/crates/assistant_slash_commands/src/assistant_slash_commands.rs
index fb00a912197e07942a67ad92418b85c4920ad66b..2bf2573e99d7a5a0140c1972967ec68523b0b56a 100644
--- a/crates/assistant_slash_commands/src/assistant_slash_commands.rs
+++ b/crates/assistant_slash_commands/src/assistant_slash_commands.rs
@@ -1,4 +1,3 @@
-mod cargo_workspace_command;
mod context_server_command;
mod default_command;
mod delta_command;
@@ -12,7 +11,6 @@ mod streaming_example_command;
mod symbols_command;
mod tab_command;
-pub use crate::cargo_workspace_command::*;
pub use crate::context_server_command::*;
pub use crate::default_command::*;
pub use crate::delta_command::*;
diff --git a/crates/assistant_slash_commands/src/cargo_workspace_command.rs b/crates/assistant_slash_commands/src/cargo_workspace_command.rs
deleted file mode 100644
index d58b2edc4c3dffd799dd9eb1c104686dc6488687..0000000000000000000000000000000000000000
--- a/crates/assistant_slash_commands/src/cargo_workspace_command.rs
+++ /dev/null
@@ -1,158 +0,0 @@
-use anyhow::{Context as _, Result, anyhow};
-use assistant_slash_command::{
- ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
- SlashCommandResult,
-};
-use fs::Fs;
-use gpui::{App, Entity, Task, WeakEntity};
-use language::{BufferSnapshot, LspAdapterDelegate};
-use project::{Project, ProjectPath};
-use std::{
- fmt::Write,
- path::Path,
- sync::{Arc, atomic::AtomicBool},
-};
-use ui::prelude::*;
-use workspace::Workspace;
-
-pub struct CargoWorkspaceSlashCommand;
-
-impl CargoWorkspaceSlashCommand {
- async fn build_message(fs: Arc, path_to_cargo_toml: &Path) -> Result {
- let buffer = fs.load(path_to_cargo_toml).await?;
- let cargo_toml: cargo_toml::Manifest = toml::from_str(&buffer)?;
-
- let mut message = String::new();
- writeln!(message, "You are in a Rust project.")?;
-
- if let Some(workspace) = cargo_toml.workspace {
- writeln!(
- message,
- "The project is a Cargo workspace with the following members:"
- )?;
- for member in workspace.members {
- writeln!(message, "- {member}")?;
- }
-
- if !workspace.default_members.is_empty() {
- writeln!(message, "The default members are:")?;
- for member in workspace.default_members {
- writeln!(message, "- {member}")?;
- }
- }
-
- if !workspace.dependencies.is_empty() {
- writeln!(
- message,
- "The following workspace dependencies are installed:"
- )?;
- for dependency in workspace.dependencies.keys() {
- writeln!(message, "- {dependency}")?;
- }
- }
- } else if let Some(package) = cargo_toml.package {
- writeln!(
- message,
- "The project name is \"{name}\".",
- name = package.name
- )?;
-
- let description = package
- .description
- .as_ref()
- .and_then(|description| description.get().ok().cloned());
- if let Some(description) = description.as_ref() {
- writeln!(message, "It describes itself as \"{description}\".")?;
- }
-
- if !cargo_toml.dependencies.is_empty() {
- writeln!(message, "The following dependencies are installed:")?;
- for dependency in cargo_toml.dependencies.keys() {
- writeln!(message, "- {dependency}")?;
- }
- }
- }
-
- Ok(message)
- }
-
- fn path_to_cargo_toml(project: Entity, cx: &mut App) -> Option> {
- let worktree = project.read(cx).worktrees(cx).next()?;
- let worktree = worktree.read(cx);
- let entry = worktree.entry_for_path("Cargo.toml")?;
- let path = ProjectPath {
- worktree_id: worktree.id(),
- path: entry.path.clone(),
- };
- Some(Arc::from(
- project.read(cx).absolute_path(&path, cx)?.as_path(),
- ))
- }
-}
-
-impl SlashCommand for CargoWorkspaceSlashCommand {
- fn name(&self) -> String {
- "cargo-workspace".into()
- }
-
- fn description(&self) -> String {
- "insert project workspace metadata".into()
- }
-
- fn menu_text(&self) -> String {
- "Insert Project Workspace Metadata".into()
- }
-
- fn complete_argument(
- self: Arc,
- _arguments: &[String],
- _cancel: Arc,
- _workspace: Option>,
- _window: &mut Window,
- _cx: &mut App,
- ) -> Task>> {
- Task::ready(Err(anyhow!("this command does not require argument")))
- }
-
- fn requires_argument(&self) -> bool {
- false
- }
-
- fn run(
- self: Arc,
- _arguments: &[String],
- _context_slash_command_output_sections: &[SlashCommandOutputSection],
- _context_buffer: BufferSnapshot,
- workspace: WeakEntity,
- _delegate: Option>,
- _window: &mut Window,
- cx: &mut App,
- ) -> Task {
- let output = workspace.update(cx, |workspace, cx| {
- let project = workspace.project().clone();
- let fs = workspace.project().read(cx).fs().clone();
- let path = Self::path_to_cargo_toml(project, cx);
- let output = cx.background_spawn(async move {
- let path = path.with_context(|| "Cargo.toml not found")?;
- Self::build_message(fs, &path).await
- });
-
- cx.foreground_executor().spawn(async move {
- let text = output.await?;
- let range = 0..text.len();
- Ok(SlashCommandOutput {
- text,
- sections: vec![SlashCommandOutputSection {
- range,
- icon: IconName::FileTree,
- label: "Project".into(),
- metadata: None,
- }],
- run_commands_in_text: false,
- }
- .into_event_stream())
- })
- });
- output.unwrap_or_else(|error| Task::ready(Err(error)))
- }
-}
diff --git a/crates/assistant_tools/src/edit_file_tool.rs b/crates/assistant_tools/src/edit_file_tool.rs
index d13f9891c3af1933ee49428c223d3e6737871047..1fcd7bbf14fb2e37646902102d51392bc8a470f8 100644
--- a/crates/assistant_tools/src/edit_file_tool.rs
+++ b/crates/assistant_tools/src/edit_file_tool.rs
@@ -1445,8 +1445,8 @@ mod tests {
fn init_test_with_config(cx: &mut TestAppContext, data_dir: &Path) {
cx.update(|cx| {
- // Set custom data directory (config will be under data_dir/config)
paths::set_custom_data_dir(data_dir.to_str().unwrap());
+ // Set custom data directory (config will be under data_dir/config)
let settings_store = SettingsStore::test(cx);
cx.set_global(settings_store);
@@ -1537,14 +1537,11 @@ mod tests {
// First, test with format_on_save enabled
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(
- cx,
- |settings| {
- settings.defaults.format_on_save = Some(FormatOnSave::On);
- settings.defaults.formatter =
- Some(language::language_settings::SelectedFormatter::Auto);
- },
- );
+ store.update_user_settings(cx, |settings| {
+ settings.project.all_languages.defaults.format_on_save = Some(FormatOnSave::On);
+ settings.project.all_languages.defaults.formatter =
+ Some(language::language_settings::SelectedFormatter::Auto);
+ });
});
});
@@ -1603,12 +1600,10 @@ mod tests {
// Next, test with format_on_save disabled
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(
- cx,
- |settings| {
- settings.defaults.format_on_save = Some(FormatOnSave::Off);
- },
- );
+ store.update_user_settings(cx, |settings| {
+ settings.project.all_languages.defaults.format_on_save =
+ Some(FormatOnSave::Off);
+ });
});
});
@@ -1679,12 +1674,13 @@ mod tests {
// First, test with remove_trailing_whitespace_on_save enabled
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(
- cx,
- |settings| {
- settings.defaults.remove_trailing_whitespace_on_save = Some(true);
- },
- );
+ store.update_user_settings(cx, |settings| {
+ settings
+ .project
+ .all_languages
+ .defaults
+ .remove_trailing_whitespace_on_save = Some(true);
+ });
});
});
@@ -1741,12 +1737,13 @@ mod tests {
// Next, test with remove_trailing_whitespace_on_save disabled
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(
- cx,
- |settings| {
- settings.defaults.remove_trailing_whitespace_on_save = Some(false);
- },
- );
+ store.update_user_settings(cx, |settings| {
+ settings
+ .project
+ .all_languages
+ .defaults
+ .remove_trailing_whitespace_on_save = Some(false);
+ });
});
});
diff --git a/crates/assistant_tools/src/grep_tool.rs b/crates/assistant_tools/src/grep_tool.rs
index e43a54661ca146902a49fa1d975e44d486e18587..9f536df995cdfc5860cc3377dce65871386d50e0 100644
--- a/crates/assistant_tools/src/grep_tool.rs
+++ b/crates/assistant_tools/src/grep_tool.rs
@@ -314,7 +314,7 @@ mod tests {
use gpui::{AppContext, TestAppContext, UpdateGlobal};
use language::{Language, LanguageConfig, LanguageMatcher};
use language_model::fake_provider::FakeLanguageModel;
- use project::{FakeFs, Project, WorktreeSettings};
+ use project::{FakeFs, Project};
use serde_json::json;
use settings::SettingsStore;
use unindent::Unindent;
@@ -849,15 +849,14 @@ mod tests {
cx.update(|cx| {
use gpui::UpdateGlobal;
- use project::WorktreeSettings;
use settings::SettingsStore;
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |settings| {
- settings.file_scan_exclusions = Some(vec![
+ store.update_user_settings(cx, |settings| {
+ settings.project.worktree.file_scan_exclusions = Some(vec![
"**/.secretdir".to_string(),
"**/.mymetadata".to_string(),
]);
- settings.private_files = Some(vec![
+ settings.project.worktree.private_files = Some(vec![
"**/.mysecrets".to_string(),
"**/*.privatekey".to_string(),
"**/*.mysensitive".to_string(),
@@ -1158,10 +1157,10 @@ mod tests {
// Set global settings
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |settings| {
- settings.file_scan_exclusions =
+ store.update_user_settings(cx, |settings| {
+ settings.project.worktree.file_scan_exclusions =
Some(vec!["**/.git".to_string(), "**/node_modules".to_string()]);
- settings.private_files = Some(vec!["**/.env".to_string()]);
+ settings.project.worktree.private_files = Some(vec!["**/.env".to_string()]);
});
});
});
diff --git a/crates/assistant_tools/src/list_directory_tool.rs b/crates/assistant_tools/src/list_directory_tool.rs
index 5471d8923b557ac26d06a16c90fdeffb152049d1..9b4b501bfa55c3037bf6658686d92668cac966a6 100644
--- a/crates/assistant_tools/src/list_directory_tool.rs
+++ b/crates/assistant_tools/src/list_directory_tool.rs
@@ -230,7 +230,7 @@ mod tests {
use gpui::{AppContext, TestAppContext, UpdateGlobal};
use indoc::indoc;
use language_model::fake_provider::FakeLanguageModel;
- use project::{FakeFs, Project, WorktreeSettings};
+ use project::{FakeFs, Project};
use serde_json::json;
use settings::SettingsStore;
use util::path;
@@ -507,13 +507,13 @@ mod tests {
// Configure settings explicitly
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |settings| {
- settings.file_scan_exclusions = Some(vec![
+ store.update_user_settings(cx, |settings| {
+ settings.project.worktree.file_scan_exclusions = Some(vec![
"**/.secretdir".to_string(),
"**/.mymetadata".to_string(),
"**/.hidden_subdir".to_string(),
]);
- settings.private_files = Some(vec![
+ settings.project.worktree.private_files = Some(vec![
"**/.mysecrets".to_string(),
"**/*.privatekey".to_string(),
"**/*.mysensitive".to_string(),
@@ -698,10 +698,10 @@ mod tests {
// Set global settings
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |settings| {
- settings.file_scan_exclusions =
+ store.update_user_settings(cx, |settings| {
+ settings.project.worktree.file_scan_exclusions =
Some(vec!["**/.git".to_string(), "**/node_modules".to_string()]);
- settings.private_files = Some(vec!["**/.env".to_string()]);
+ settings.project.worktree.private_files = Some(vec!["**/.env".to_string()]);
});
});
});
diff --git a/crates/assistant_tools/src/read_file_tool.rs b/crates/assistant_tools/src/read_file_tool.rs
index 7222f061c7caba54ee2e3294378c4a7d957914f5..4ac2ec14ab9dde97b0ff89a40db356fef42d3741 100644
--- a/crates/assistant_tools/src/read_file_tool.rs
+++ b/crates/assistant_tools/src/read_file_tool.rs
@@ -299,7 +299,7 @@ mod test {
use gpui::{AppContext, TestAppContext, UpdateGlobal};
use language::{Language, LanguageConfig, LanguageMatcher};
use language_model::fake_provider::FakeLanguageModel;
- use project::{FakeFs, Project, WorktreeSettings};
+ use project::{FakeFs, Project};
use serde_json::json;
use settings::SettingsStore;
use util::path;
@@ -677,15 +677,14 @@ mod test {
cx.update(|cx| {
use gpui::UpdateGlobal;
- use project::WorktreeSettings;
use settings::SettingsStore;
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |settings| {
- settings.file_scan_exclusions = Some(vec![
+ store.update_user_settings(cx, |settings| {
+ settings.project.worktree.file_scan_exclusions = Some(vec![
"**/.secretdir".to_string(),
"**/.mymetadata".to_string(),
]);
- settings.private_files = Some(vec![
+ settings.project.worktree.private_files = Some(vec![
"**/.mysecrets".to_string(),
"**/*.privatekey".to_string(),
"**/*.mysensitive".to_string(),
@@ -968,10 +967,10 @@ mod test {
// Set global settings
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |settings| {
- settings.file_scan_exclusions =
+ store.update_user_settings(cx, |settings| {
+ settings.project.worktree.file_scan_exclusions =
Some(vec!["**/.git".to_string(), "**/node_modules".to_string()]);
- settings.private_files = Some(vec!["**/.env".to_string()]);
+ settings.project.worktree.private_files = Some(vec!["**/.env".to_string()]);
});
});
});
diff --git a/crates/audio/Cargo.toml b/crates/audio/Cargo.toml
index 354abc6ca6a053ff31ab1f1c3641da122cf874ce..fbf339c32df37efd30d3282c05fc2015e92288d8 100644
--- a/crates/audio/Cargo.toml
+++ b/crates/audio/Cargo.toml
@@ -22,7 +22,6 @@ gpui.workspace = true
log.workspace = true
parking_lot.workspace = true
rodio = { workspace = true, features = [ "wav", "playback", "wav_output" ] }
-schemars.workspace = true
serde.workspace = true
settings.workspace = true
smol.workspace = true
diff --git a/crates/audio/src/audio_settings.rs b/crates/audio/src/audio_settings.rs
index cb99bb89e0447746c593ae5298f09f02cabe01ce..e04c235ae19509a484c03cc28dacde14c1390f2b 100644
--- a/crates/audio/src/audio_settings.rs
+++ b/crates/audio/src/audio_settings.rs
@@ -1,17 +1,14 @@
use std::sync::atomic::{AtomicBool, Ordering};
-use anyhow::Result;
use gpui::App;
-use schemars::JsonSchema;
-use serde::{Deserialize, Serialize};
-use settings::{Settings, SettingsKey, SettingsSources, SettingsStore, SettingsUi};
+use settings::{Settings, SettingsStore};
+use util::MergeFrom as _;
-#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi)]
+#[derive(Clone, Debug)]
pub struct AudioSettings {
/// Opt into the new audio system.
///
/// You need to rejoin a call for this setting to apply
- #[serde(rename = "experimental.rodio_audio", default)]
pub rodio_audio: bool, // default is false
/// Requires 'rodio_audio: true'
///
@@ -22,22 +19,16 @@ pub struct AudioSettings {
/// Microphones are too quite in zed, until everyone is on experimental
/// audio and has auto speaker volume on this will make you very loud
/// compared to other speakers.
- #[serde(
- rename = "experimental.auto_microphone_volume",
- default = "default_false"
- )]
pub auto_microphone_volume: bool,
/// Requires 'rodio_audio: true'
///
/// Automatically increate or decrease the volume of other call members.
/// This only affects how things sound for you.
- #[serde(rename = "experimental.auto_speaker_volume", default = "default_true")]
pub auto_speaker_volume: bool,
/// Requires 'rodio_audio: true'
///
/// Remove background noises. Works great for typing, cars, dogs, AC. Does
/// not work well on music.
- #[serde(rename = "experimental.denoise", default = "default_false")]
pub denoise: bool,
/// Requires 'rodio_audio: true'
///
@@ -47,79 +38,35 @@ pub struct AudioSettings {
/// the future we will migrate by setting this to false
///
/// You need to rejoin a call for this setting to apply
- #[serde(
- rename = "experimental.legacy_audio_compatible",
- default = "default_true"
- )]
pub legacy_audio_compatible: bool,
}
-/// Configuration of audio in Zed.
-#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey)]
-#[serde(default)]
-#[settings_key(key = "audio")]
-pub struct AudioSettingsContent {
- /// Opt into the new audio system.
- ///
- /// You need to rejoin a call for this setting to apply
- #[serde(rename = "experimental.rodio_audio", default)]
- pub rodio_audio: bool, // default is false
- /// Requires 'rodio_audio: true'
- ///
- /// Automatically increase or decrease you microphone's volume. This affects how
- /// loud you sound to others.
- ///
- /// Recommended: off (default)
- /// Microphones are too quite in zed, until everyone is on experimental
- /// audio and has auto speaker volume on this will make you very loud
- /// compared to other speakers.
- #[serde(
- rename = "experimental.auto_microphone_volume",
- default = "default_false"
- )]
- pub auto_microphone_volume: bool,
- /// Requires 'rodio_audio: true'
- ///
- /// Automatically increate or decrease the volume of other call members.
- /// This only affects how things sound for you.
- #[serde(rename = "experimental.auto_speaker_volume", default = "default_true")]
- pub auto_speaker_volume: bool,
- /// Requires 'rodio_audio: true'
- ///
- /// Remove background noises. Works great for typing, cars, dogs, AC. Does
- /// not work well on music.
- #[serde(rename = "experimental.denoise", default = "default_false")]
- pub denoise: bool,
- /// Requires 'rodio_audio: true'
- ///
- /// Use audio parameters compatible with the previous versions of
- /// experimental audio and non-experimental audio. When this is false you
- /// will sound strange to anyone not on the latest experimental audio. In
- /// the future we will migrate by setting this to false
- ///
- /// You need to rejoin a call for this setting to apply
- #[serde(
- rename = "experimental.legacy_audio_compatible",
- default = "default_true"
- )]
- pub legacy_audio_compatible: bool,
-}
-
-fn default_true() -> bool {
- true
-}
-
-fn default_false() -> bool {
- false
-}
+/// Configuration of audio in Zed
impl Settings for AudioSettings {
- type FileContent = AudioSettingsContent;
-
- fn load(sources: SettingsSources, _cx: &mut App) -> Result {
- sources.json_merge()
+ fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
+ let audio = &content.audio.as_ref().unwrap();
+ AudioSettings {
+ rodio_audio: audio.rodio_audio.unwrap(),
+ auto_microphone_volume: audio.auto_microphone_volume.unwrap(),
+ auto_speaker_volume: audio.auto_speaker_volume.unwrap(),
+ denoise: audio.denoise.unwrap(),
+ legacy_audio_compatible: audio.legacy_audio_compatible.unwrap(),
+ }
}
- fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
+ fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
+ let Some(audio) = content.audio.as_ref() else {
+ return;
+ };
+ self.rodio_audio.merge_from(&audio.rodio_audio);
+ self.auto_microphone_volume
+ .merge_from(&audio.auto_microphone_volume);
+ self.auto_speaker_volume
+ .merge_from(&audio.auto_speaker_volume);
+ self.denoise.merge_from(&audio.denoise);
+ self.legacy_audio_compatible
+ .merge_from(&audio.legacy_audio_compatible);
+ }
}
/// See docs on [LIVE_SETTINGS]
diff --git a/crates/auto_update/Cargo.toml b/crates/auto_update/Cargo.toml
index 35cef84f0366b16d9e31b2416e7a6a10173ff5ef..21df028a88f027b1ce3796ef3e04998ca205ce51 100644
--- a/crates/auto_update/Cargo.toml
+++ b/crates/auto_update/Cargo.toml
@@ -21,7 +21,6 @@ http_client.workspace = true
log.workspace = true
paths.workspace = true
release_channel.workspace = true
-schemars.workspace = true
serde.workspace = true
serde_json.workspace = true
settings.workspace = true
diff --git a/crates/auto_update/src/auto_update.rs b/crates/auto_update/src/auto_update.rs
index e6274cf3988d4bd35dead9c988a294c922c2aaf3..4e0348575e687c2b4e36fcde7df83b8f329733d0 100644
--- a/crates/auto_update/src/auto_update.rs
+++ b/crates/auto_update/src/auto_update.rs
@@ -8,9 +8,8 @@ use gpui::{
use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
use paths::remote_servers_dir;
use release_channel::{AppCommitSha, ReleaseChannel};
-use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
-use settings::{Settings, SettingsKey, SettingsSources, SettingsStore, SettingsUi};
+use settings::{Settings, SettingsContent, SettingsStore};
use smol::{fs, io::AsyncReadExt};
use smol::{fs::File, process::Command};
use std::{
@@ -113,47 +112,27 @@ impl Drop for MacOsUnmounter {
}
}
+#[derive(Clone, Copy, Debug)]
struct AutoUpdateSetting(bool);
/// Whether or not to automatically check for updates.
///
/// Default: true
-#[derive(Clone, Copy, Default, JsonSchema, Deserialize, Serialize, SettingsUi, SettingsKey)]
-#[settings_key(None)]
-#[settings_ui(group = "Auto Update")]
-struct AutoUpdateSettingContent {
- pub auto_update: Option,
-}
-
impl Settings for AutoUpdateSetting {
- type FileContent = AutoUpdateSettingContent;
-
- fn load(sources: SettingsSources, _: &mut App) -> Result {
- let auto_update = [
- sources.server,
- sources.release_channel,
- sources.operating_system,
- sources.user,
- ]
- .into_iter()
- .find_map(|value| value.and_then(|val| val.auto_update))
- .or(sources.default.auto_update)
- .ok_or_else(Self::missing_default)?;
-
- Ok(Self(auto_update))
+ fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
+ debug_assert_eq!(content.auto_update.unwrap(), true);
+ Self(content.auto_update.unwrap())
}
- fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut Self::FileContent) {
- let mut cur = &mut Some(*current);
- vscode.enum_setting("update.mode", &mut cur, |s| match s {
- "none" | "manual" => Some(AutoUpdateSettingContent {
- auto_update: Some(false),
- }),
- _ => Some(AutoUpdateSettingContent {
- auto_update: Some(true),
- }),
- });
- *current = cur.unwrap();
+ fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
+ if let Some(auto_update) = content.auto_update {
+ self.0 = auto_update;
+ }
+ }
+
+ fn import_from_vscode(_: &settings::VsCodeSettings, _: &mut SettingsContent) {
+ // We could match on vscode's update.mode here, but
+ // I think it's more important to have more people updating zed by default.
}
}
@@ -1002,7 +981,7 @@ mod tests {
#[gpui::test]
fn test_auto_update_defaults_to_true(cx: &mut TestAppContext) {
cx.update(|cx| {
- let mut store = SettingsStore::new(cx);
+ let mut store = SettingsStore::new(cx, &settings::default_settings());
store
.set_default_settings(&default_settings(), cx)
.expect("Unable to set default settings");
diff --git a/crates/call/Cargo.toml b/crates/call/Cargo.toml
index 9dfb4acbb424724ce976f918f3bf5124a450e372..1d5fbccb4644d9f168a2afd321a205f01c8f9cdc 100644
--- a/crates/call/Cargo.toml
+++ b/crates/call/Cargo.toml
@@ -35,7 +35,6 @@ language.workspace = true
log.workspace = true
postage.workspace = true
project.workspace = true
-schemars.workspace = true
serde.workspace = true
settings.workspace = true
telemetry.workspace = true
diff --git a/crates/call/src/call_settings.rs b/crates/call/src/call_settings.rs
index c458c17e9fcd8788aae2661fbaaa0d45b117e10f..cce8142903c17ec41244d346a79feb8f4cad70e6 100644
--- a/crates/call/src/call_settings.rs
+++ b/crates/call/src/call_settings.rs
@@ -1,36 +1,32 @@
-use anyhow::Result;
use gpui::App;
-use schemars::JsonSchema;
-use serde::{Deserialize, Serialize};
-use settings::{Settings, SettingsKey, SettingsSources, SettingsUi};
+use settings::Settings;
+use util::MergeFrom;
-#[derive(Deserialize, Debug)]
+#[derive(Debug)]
pub struct CallSettings {
pub mute_on_join: bool,
pub share_on_join: bool,
}
-/// Configuration of voice calls in Zed.
-#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey)]
-#[settings_key(key = "calls")]
-pub struct CallSettingsContent {
- /// Whether the microphone should be muted when joining a channel or a call.
- ///
- /// Default: false
- pub mute_on_join: Option,
-
- /// Whether your current project should be shared when joining an empty channel.
- ///
- /// Default: false
- pub share_on_join: Option,
-}
-
impl Settings for CallSettings {
- type FileContent = CallSettingsContent;
+ fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
+ let call = content.calls.clone().unwrap();
+ CallSettings {
+ mute_on_join: call.mute_on_join.unwrap(),
+ share_on_join: call.share_on_join.unwrap(),
+ }
+ }
- fn load(sources: SettingsSources, _: &mut App) -> Result {
- sources.json_merge()
+ fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
+ if let Some(call) = content.calls.clone() {
+ self.mute_on_join.merge_from(&call.mute_on_join);
+ self.share_on_join.merge_from(&call.share_on_join);
+ }
}
- fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
+ fn import_from_vscode(
+ _vscode: &settings::VsCodeSettings,
+ _current: &mut settings::SettingsContent,
+ ) {
+ }
}
diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs
index 8888c73f4496d39b33cb868444f39234f7d074f7..237eaa11d954db7c95eaa513e8e921a1000faac6 100644
--- a/crates/client/src/client.rs
+++ b/crates/client/src/client.rs
@@ -31,7 +31,7 @@ use release_channel::{AppVersion, ReleaseChannel};
use rpc::proto::{AnyTypedEnvelope, EnvelopedMessage, PeerId, RequestMessage};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
-use settings::{Settings, SettingsKey, SettingsSources, SettingsUi};
+use settings::{Settings, SettingsContent, SettingsKey, SettingsUi};
use std::{
any::TypeId,
convert::TryFrom,
@@ -50,7 +50,7 @@ use telemetry::Telemetry;
use thiserror::Error;
use tokio::net::TcpStream;
use url::Url;
-use util::{ConnectionResult, ResultExt};
+use util::{ConnectionResult, MergeFrom, ResultExt};
pub use rpc::*;
pub use telemetry_events::Event;
@@ -96,29 +96,33 @@ actions!(
]
);
-#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)]
-#[settings_key(None)]
-pub struct ClientSettingsContent {
- server_url: Option,
-}
-
#[derive(Deserialize)]
pub struct ClientSettings {
pub server_url: String,
}
impl Settings for ClientSettings {
- type FileContent = ClientSettingsContent;
-
- fn load(sources: SettingsSources, _: &mut App) -> Result {
- let mut result = sources.json_merge::()?;
+ fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
if let Some(server_url) = &*ZED_SERVER_URL {
- result.server_url.clone_from(server_url)
+ return Self {
+ server_url: server_url.clone(),
+ };
+ }
+ Self {
+ server_url: content.server_url.clone().unwrap(),
}
- Ok(result)
}
- fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
+ fn refine(&mut self, content: &settings::SettingsContent, _: &mut App) {
+ if ZED_SERVER_URL.is_some() {
+ return;
+ }
+ if let Some(server_url) = content.server_url.clone() {
+ self.server_url = server_url;
+ }
+ }
+
+ fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut SettingsContent) {}
}
#[derive(Default, Clone, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)]
@@ -147,19 +151,19 @@ impl ProxySettings {
}
impl Settings for ProxySettings {
- type FileContent = ProxySettingsContent;
-
- fn load(sources: SettingsSources, _: &mut App) -> Result {
- Ok(Self {
- proxy: sources
- .user
- .or(sources.server)
- .and_then(|value| value.proxy.clone())
- .or(sources.default.proxy.clone()),
- })
+ fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
+ Self {
+ proxy: content.proxy.clone(),
+ }
}
- fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut Self::FileContent) {
+ fn refine(&mut self, content: &settings::SettingsContent, _: &mut App) {
+ if let Some(proxy) = content.proxy.clone() {
+ self.proxy = Some(proxy)
+ }
+ }
+
+ fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut SettingsContent) {
vscode.string_setting("http.proxy", &mut current.proxy);
}
}
@@ -538,37 +542,41 @@ pub struct TelemetrySettings {
pub metrics: bool,
}
-/// Control what info is collected by Zed.
-#[derive(Default, Clone, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey)]
-#[settings_key(key = "telemetry")]
-pub struct TelemetrySettingsContent {
- /// Send debug info like crash reports.
- ///
- /// Default: true
- pub diagnostics: Option,
- /// Send anonymized usage data like what languages you're using Zed with.
- ///
- /// Default: true
- pub metrics: Option,
-}
-
impl settings::Settings for TelemetrySettings {
- type FileContent = TelemetrySettingsContent;
+ fn from_defaults(content: &SettingsContent, _cx: &mut App) -> Self {
+ Self {
+ diagnostics: content.telemetry.as_ref().unwrap().diagnostics.unwrap(),
+ metrics: content.telemetry.as_ref().unwrap().metrics.unwrap(),
+ }
+ }
- fn load(sources: SettingsSources, _: &mut App) -> Result {
- sources.json_merge()
+ fn refine(&mut self, content: &SettingsContent, _cx: &mut App) {
+ let Some(telemetry) = &content.telemetry else {
+ return;
+ };
+ self.diagnostics.merge_from(&telemetry.diagnostics);
+ self.metrics.merge_from(&telemetry.metrics);
}
- fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut Self::FileContent) {
- vscode.enum_setting("telemetry.telemetryLevel", &mut current.metrics, |s| {
+ fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut SettingsContent) {
+ let mut telemetry = settings::TelemetrySettingsContent::default();
+ vscode.enum_setting("telemetry.telemetryLevel", &mut telemetry.metrics, |s| {
Some(s == "all")
});
- vscode.enum_setting("telemetry.telemetryLevel", &mut current.diagnostics, |s| {
- Some(matches!(s, "all" | "error" | "crash"))
- });
+ vscode.enum_setting(
+ "telemetry.telemetryLevel",
+ &mut telemetry.diagnostics,
+ |s| Some(matches!(s, "all" | "error" | "crash")),
+ );
// we could translate telemetry.telemetryLevel, but just because users didn't want
// to send microsoft telemetry doesn't mean they don't want to send it to zed. their
// all/error/crash/off correspond to combinations of our "diagnostics" and "metrics".
+ if let Some(diagnostics) = telemetry.diagnostics {
+ current.telemetry.get_or_insert_default().diagnostics = Some(diagnostics)
+ }
+ if let Some(metrics) = telemetry.metrics {
+ current.telemetry.get_or_insert_default().metrics = Some(metrics)
+ }
}
}
diff --git a/crates/collab/src/db/queries/users.rs b/crates/collab/src/db/queries/users.rs
index 4b0f66fcbe09d23af58b0a30ffebf68455651fd8..89211130b88c69d4bf524bba25ae116790321d3e 100644
--- a/crates/collab/src/db/queries/users.rs
+++ b/crates/collab/src/db/queries/users.rs
@@ -342,79 +342,6 @@ impl Database {
result
}
- /// Returns all feature flags.
- pub async fn list_feature_flags(&self) -> Result> {
- self.transaction(|tx| async move { Ok(feature_flag::Entity::find().all(&*tx).await?) })
- .await
- }
-
- /// Creates a new feature flag.
- pub async fn create_user_flag(&self, flag: &str, enabled_for_all: bool) -> Result {
- self.transaction(|tx| async move {
- let flag = feature_flag::Entity::insert(feature_flag::ActiveModel {
- flag: ActiveValue::set(flag.to_string()),
- enabled_for_all: ActiveValue::set(enabled_for_all),
- ..Default::default()
- })
- .exec(&*tx)
- .await?
- .last_insert_id;
-
- Ok(flag)
- })
- .await
- }
-
- /// Add the given user to the feature flag
- pub async fn add_user_flag(&self, user: UserId, flag: FlagId) -> Result<()> {
- self.transaction(|tx| async move {
- user_feature::Entity::insert(user_feature::ActiveModel {
- user_id: ActiveValue::set(user),
- feature_id: ActiveValue::set(flag),
- })
- .exec(&*tx)
- .await?;
-
- Ok(())
- })
- .await
- }
-
- /// Returns the active flags for the user.
- pub async fn get_user_flags(&self, user: UserId) -> Result> {
- self.transaction(|tx| async move {
- #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
- enum QueryAs {
- Flag,
- }
-
- let flags_enabled_for_all = feature_flag::Entity::find()
- .filter(feature_flag::Column::EnabledForAll.eq(true))
- .select_only()
- .column(feature_flag::Column::Flag)
- .into_values::<_, QueryAs>()
- .all(&*tx)
- .await?;
-
- let flags_enabled_for_user = user::Model {
- id: user,
- ..Default::default()
- }
- .find_linked(user::UserFlags)
- .select_only()
- .column(feature_flag::Column::Flag)
- .into_values::<_, QueryAs>()
- .all(&*tx)
- .await?;
-
- let mut all_flags = HashSet::from_iter(flags_enabled_for_all);
- all_flags.extend(flags_enabled_for_user);
-
- Ok(all_flags.into_iter().collect())
- })
- .await
- }
-
pub async fn get_users_missing_github_user_created_at(&self) -> Result> {
self.transaction(|tx| async move {
Ok(user::Entity::find()
diff --git a/crates/collab/src/db/tables.rs b/crates/collab/src/db/tables.rs
index 0082a9fb030a27e4be13af725f08ea9c82217377..32c4570af5893b503f0fcfdaa1759616cf9be387 100644
--- a/crates/collab/src/db/tables.rs
+++ b/crates/collab/src/db/tables.rs
@@ -13,7 +13,6 @@ pub mod contributor;
pub mod embedding;
pub mod extension;
pub mod extension_version;
-pub mod feature_flag;
pub mod follower;
pub mod language_server;
pub mod notification;
@@ -29,7 +28,6 @@ pub mod room_participant;
pub mod server;
pub mod signup;
pub mod user;
-pub mod user_feature;
pub mod worktree;
pub mod worktree_diagnostic_summary;
pub mod worktree_entry;
diff --git a/crates/collab/src/db/tables/feature_flag.rs b/crates/collab/src/db/tables/feature_flag.rs
deleted file mode 100644
index 5bbfedd71e70b7f1cc58219475c49c28bc62ff3d..0000000000000000000000000000000000000000
--- a/crates/collab/src/db/tables/feature_flag.rs
+++ /dev/null
@@ -1,41 +0,0 @@
-use sea_orm::entity::prelude::*;
-
-use crate::db::FlagId;
-
-#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
-#[sea_orm(table_name = "feature_flags")]
-pub struct Model {
- #[sea_orm(primary_key)]
- pub id: FlagId,
- pub flag: String,
- pub enabled_for_all: bool,
-}
-
-#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
-pub enum Relation {
- #[sea_orm(has_many = "super::user_feature::Entity")]
- UserFeature,
-}
-
-impl Related for Entity {
- fn to() -> RelationDef {
- Relation::UserFeature.def()
- }
-}
-
-impl ActiveModelBehavior for ActiveModel {}
-
-pub struct FlaggedUsers;
-
-impl Linked for FlaggedUsers {
- type FromEntity = Entity;
-
- type ToEntity = super::user::Entity;
-
- fn link(&self) -> Vec {
- vec![
- super::user_feature::Relation::Flag.def().rev(),
- super::user_feature::Relation::User.def(),
- ]
- }
-}
diff --git a/crates/collab/src/db/tables/user.rs b/crates/collab/src/db/tables/user.rs
index af43fe300a6cc1224487541ca72af9d887a6fae3..8e8c03fafc92127f8754f473e04dfab39592ea14 100644
--- a/crates/collab/src/db/tables/user.rs
+++ b/crates/collab/src/db/tables/user.rs
@@ -35,8 +35,6 @@ pub enum Relation {
HostedProjects,
#[sea_orm(has_many = "super::channel_member::Entity")]
ChannelMemberships,
- #[sea_orm(has_many = "super::user_feature::Entity")]
- UserFeatures,
#[sea_orm(has_one = "super::contributor::Entity")]
Contributor,
}
@@ -84,25 +82,4 @@ impl Related for Entity {
}
}
-impl Related for Entity {
- fn to() -> RelationDef {
- Relation::UserFeatures.def()
- }
-}
-
impl ActiveModelBehavior for ActiveModel {}
-
-pub struct UserFlags;
-
-impl Linked for UserFlags {
- type FromEntity = Entity;
-
- type ToEntity = super::feature_flag::Entity;
-
- fn link(&self) -> Vec {
- vec![
- super::user_feature::Relation::User.def().rev(),
- super::user_feature::Relation::Flag.def(),
- ]
- }
-}
diff --git a/crates/collab/src/db/tables/user_feature.rs b/crates/collab/src/db/tables/user_feature.rs
deleted file mode 100644
index cc24b5e796342f7733f59933362d46a0df2be112..0000000000000000000000000000000000000000
--- a/crates/collab/src/db/tables/user_feature.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-use sea_orm::entity::prelude::*;
-
-use crate::db::{FlagId, UserId};
-
-#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
-#[sea_orm(table_name = "user_features")]
-pub struct Model {
- #[sea_orm(primary_key)]
- pub user_id: UserId,
- #[sea_orm(primary_key)]
- pub feature_id: FlagId,
-}
-
-#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
-pub enum Relation {
- #[sea_orm(
- belongs_to = "super::feature_flag::Entity",
- from = "Column::FeatureId",
- to = "super::feature_flag::Column::Id"
- )]
- Flag,
- #[sea_orm(
- belongs_to = "super::user::Entity",
- from = "Column::UserId",
- to = "super::user::Column::Id"
- )]
- User,
-}
-
-impl Related for Entity {
- fn to() -> RelationDef {
- Relation::Flag.def()
- }
-}
-
-impl Related for Entity {
- fn to() -> RelationDef {
- Relation::User.def()
- }
-}
-
-impl ActiveModelBehavior for ActiveModel {}
diff --git a/crates/collab/src/db/tests.rs b/crates/collab/src/db/tests.rs
index 25e03f1320a25455ede347b43477761d591fbd57..141262d5e94a4bf1d4d897e78f6281ab9ee3ccfc 100644
--- a/crates/collab/src/db/tests.rs
+++ b/crates/collab/src/db/tests.rs
@@ -6,7 +6,6 @@ mod db_tests;
#[cfg(target_os = "macos")]
mod embedding_tests;
mod extension_tests;
-mod feature_flag_tests;
mod user_tests;
use crate::migrations::run_database_migrations;
diff --git a/crates/collab/src/db/tests/feature_flag_tests.rs b/crates/collab/src/db/tests/feature_flag_tests.rs
deleted file mode 100644
index 0e68dcc941cdb2488c3822548dada56746667bc2..0000000000000000000000000000000000000000
--- a/crates/collab/src/db/tests/feature_flag_tests.rs
+++ /dev/null
@@ -1,66 +0,0 @@
-use crate::{
- db::{Database, NewUserParams},
- test_both_dbs,
-};
-use pretty_assertions::assert_eq;
-use std::sync::Arc;
-
-test_both_dbs!(
- test_get_user_flags,
- test_get_user_flags_postgres,
- test_get_user_flags_sqlite
-);
-
-async fn test_get_user_flags(db: &Arc) {
- let user_1 = db
- .create_user(
- "user1@example.com",
- None,
- false,
- NewUserParams {
- github_login: "user1".to_string(),
- github_user_id: 1,
- },
- )
- .await
- .unwrap()
- .user_id;
-
- let user_2 = db
- .create_user(
- "user2@example.com",
- None,
- false,
- NewUserParams {
- github_login: "user2".to_string(),
- github_user_id: 2,
- },
- )
- .await
- .unwrap()
- .user_id;
-
- const FEATURE_FLAG_ONE: &str = "brand-new-ux";
- const FEATURE_FLAG_TWO: &str = "cool-feature";
- const FEATURE_FLAG_THREE: &str = "feature-enabled-for-everyone";
-
- let feature_flag_one = db.create_user_flag(FEATURE_FLAG_ONE, false).await.unwrap();
- let feature_flag_two = db.create_user_flag(FEATURE_FLAG_TWO, false).await.unwrap();
- db.create_user_flag(FEATURE_FLAG_THREE, true).await.unwrap();
-
- db.add_user_flag(user_1, feature_flag_one).await.unwrap();
- db.add_user_flag(user_1, feature_flag_two).await.unwrap();
-
- db.add_user_flag(user_2, feature_flag_one).await.unwrap();
-
- let mut user_1_flags = db.get_user_flags(user_1).await.unwrap();
- user_1_flags.sort();
- assert_eq!(
- user_1_flags,
- &[FEATURE_FLAG_ONE, FEATURE_FLAG_TWO, FEATURE_FLAG_THREE]
- );
-
- let mut user_2_flags = db.get_user_flags(user_2).await.unwrap();
- user_2_flags.sort();
- assert_eq!(user_2_flags, &[FEATURE_FLAG_ONE, FEATURE_FLAG_THREE]);
-}
diff --git a/crates/collab/src/seed.rs b/crates/collab/src/seed.rs
index 2d070b30abada79dc177b2b600d9ecc40aa472e1..5f5779e1e4990d1a03461bb3ec2222e82d9f544e 100644
--- a/crates/collab/src/seed.rs
+++ b/crates/collab/src/seed.rs
@@ -46,27 +46,6 @@ pub async fn seed(config: &Config, db: &Database, force: bool) -> anyhow::Result
let mut first_user = None;
let mut others = vec![];
- let flag_names = ["language-models"];
- let mut flags = Vec::new();
-
- let existing_feature_flags = db.list_feature_flags().await?;
-
- for flag_name in flag_names {
- if existing_feature_flags
- .iter()
- .any(|flag| flag.flag == flag_name)
- {
- log::info!("Flag {flag_name:?} already exists");
- continue;
- }
-
- let flag = db
- .create_user_flag(flag_name, false)
- .await
- .unwrap_or_else(|err| panic!("failed to create flag: '{flag_name}': {err}"));
- flags.push(flag);
- }
-
for admin_login in seed_config.admins {
let user = fetch_github::(
&client,
@@ -90,15 +69,6 @@ pub async fn seed(config: &Config, db: &Database, force: bool) -> anyhow::Result
} else {
others.push(user.user_id)
}
-
- for flag in &flags {
- db.add_user_flag(user.user_id, *flag)
- .await
- .context(format!(
- "Unable to enable flag '{}' for user '{}'",
- flag, user.user_id
- ))?;
- }
}
for channel in seed_config.channels {
@@ -126,24 +96,16 @@ pub async fn seed(config: &Config, db: &Database, force: bool) -> anyhow::Result
for github_user in github_users {
log::info!("Seeding {:?} from GitHub", github_user.login);
- let user = db
- .update_or_create_user_by_github_account(
- &github_user.login,
- github_user.id,
- github_user.email.as_deref(),
- github_user.name.as_deref(),
- github_user.created_at,
- None,
- )
- .await
- .expect("failed to insert user");
-
- for flag in &flags {
- db.add_user_flag(user.id, *flag).await.context(format!(
- "Unable to enable flag '{}' for user '{}'",
- flag, user.id
- ))?;
- }
+ db.update_or_create_user_by_github_account(
+ &github_user.login,
+ github_user.id,
+ github_user.email.as_deref(),
+ github_user.name.as_deref(),
+ github_user.created_at,
+ None,
+ )
+ .await
+ .expect("failed to insert user");
}
Ok(())
diff --git a/crates/collab/src/tests/editor_tests.rs b/crates/collab/src/tests/editor_tests.rs
index a3f63c527693a19bb7ac1cd87c104cee3d5cfa6e..cc84ddc800d7c4c4979dc824d25fa705405a0aa7 100644
--- a/crates/collab/src/tests/editor_tests.rs
+++ b/crates/collab/src/tests/editor_tests.rs
@@ -4,7 +4,7 @@ use crate::{
};
use call::ActiveCall;
use editor::{
- DocumentColorsRenderMode, Editor, EditorSettings, RowInfo, SelectionEffects,
+ DocumentColorsRenderMode, Editor, RowInfo, SelectionEffects,
actions::{
ConfirmCodeAction, ConfirmCompletion, ConfirmRename, ContextMenuFirst,
ExpandMacroRecursively, MoveToEnd, Redo, Rename, SelectAll, ToggleCodeActions, Undo,
@@ -18,20 +18,16 @@ use fs::Fs;
use futures::{SinkExt, StreamExt, channel::mpsc, lock::Mutex};
use gpui::{App, Rgba, TestAppContext, UpdateGlobal, VisualContext, VisualTestContext};
use indoc::indoc;
-use language::{
- FakeLspAdapter,
- language_settings::{AllLanguageSettings, InlayHintSettings},
-};
+use language::FakeLspAdapter;
use lsp::LSP_REQUEST_TIMEOUT;
use project::{
ProjectPath, SERVER_PROGRESS_THROTTLE_TIMEOUT,
lsp_store::lsp_ext_command::{ExpandedMacro, LspExtExpandMacro},
- project_settings::{InlineBlameSettings, ProjectSettings},
};
use recent_projects::disconnected_overlay::DisconnectedOverlay;
use rpc::RECEIVE_TIMEOUT;
use serde_json::json;
-use settings::SettingsStore;
+use settings::{InlayHintSettingsContent, InlineBlameSettings, SettingsStore};
use std::{
collections::BTreeSet,
ops::{Deref as _, Range},
@@ -1789,35 +1785,37 @@ async fn test_mutual_editor_inlay_hint_cache_update(
cx_a.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |settings| {
- settings.defaults.inlay_hints = Some(InlayHintSettings {
- enabled: true,
- show_value_hints: true,
- edit_debounce_ms: 0,
- scroll_debounce_ms: 0,
- show_type_hints: true,
- show_parameter_hints: false,
- show_other_hints: true,
- show_background: false,
- toggle_on_modifiers_press: None,
- })
+ store.update_user_settings(cx, |settings| {
+ settings.project.all_languages.defaults.inlay_hints =
+ Some(InlayHintSettingsContent {
+ enabled: Some(true),
+ show_value_hints: Some(true),
+ edit_debounce_ms: Some(0),
+ scroll_debounce_ms: Some(0),
+ show_type_hints: Some(true),
+ show_parameter_hints: Some(false),
+ show_other_hints: Some(true),
+ show_background: Some(false),
+ toggle_on_modifiers_press: None,
+ })
});
});
});
cx_b.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |settings| {
- settings.defaults.inlay_hints = Some(InlayHintSettings {
- show_value_hints: true,
- enabled: true,
- edit_debounce_ms: 0,
- scroll_debounce_ms: 0,
- show_type_hints: true,
- show_parameter_hints: false,
- show_other_hints: true,
- show_background: false,
- toggle_on_modifiers_press: None,
- })
+ store.update_user_settings(cx, |settings| {
+ settings.project.all_languages.defaults.inlay_hints =
+ Some(InlayHintSettingsContent {
+ show_value_hints: Some(true),
+ enabled: Some(true),
+ edit_debounce_ms: Some(0),
+ scroll_debounce_ms: Some(0),
+ show_type_hints: Some(true),
+ show_parameter_hints: Some(false),
+ show_other_hints: Some(true),
+ show_background: Some(false),
+ toggle_on_modifiers_press: None,
+ })
});
});
});
@@ -2039,35 +2037,37 @@ async fn test_inlay_hint_refresh_is_forwarded(
cx_a.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |settings| {
- settings.defaults.inlay_hints = Some(InlayHintSettings {
- show_value_hints: true,
- enabled: false,
- edit_debounce_ms: 0,
- scroll_debounce_ms: 0,
- show_type_hints: false,
- show_parameter_hints: false,
- show_other_hints: false,
- show_background: false,
- toggle_on_modifiers_press: None,
- })
+ store.update_user_settings(cx, |settings| {
+ settings.project.all_languages.defaults.inlay_hints =
+ Some(InlayHintSettingsContent {
+ show_value_hints: Some(true),
+ enabled: Some(false),
+ edit_debounce_ms: Some(0),
+ scroll_debounce_ms: Some(0),
+ show_type_hints: Some(false),
+ show_parameter_hints: Some(false),
+ show_other_hints: Some(false),
+ show_background: Some(false),
+ toggle_on_modifiers_press: None,
+ })
});
});
});
cx_b.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |settings| {
- settings.defaults.inlay_hints = Some(InlayHintSettings {
- show_value_hints: true,
- enabled: true,
- edit_debounce_ms: 0,
- scroll_debounce_ms: 0,
- show_type_hints: true,
- show_parameter_hints: true,
- show_other_hints: true,
- show_background: false,
- toggle_on_modifiers_press: None,
- })
+ store.update_user_settings(cx, |settings| {
+ settings.project.all_languages.defaults.inlay_hints =
+ Some(InlayHintSettingsContent {
+ show_value_hints: Some(true),
+ enabled: Some(true),
+ edit_debounce_ms: Some(0),
+ scroll_debounce_ms: Some(0),
+ show_type_hints: Some(true),
+ show_parameter_hints: Some(true),
+ show_other_hints: Some(true),
+ show_background: Some(false),
+ toggle_on_modifiers_press: None,
+ })
});
});
});
@@ -2241,15 +2241,15 @@ async fn test_lsp_document_color(cx_a: &mut TestAppContext, cx_b: &mut TestAppCo
cx_a.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |settings| {
- settings.lsp_document_colors = Some(DocumentColorsRenderMode::None);
+ store.update_user_settings(cx, |settings| {
+ settings.editor.lsp_document_colors = Some(DocumentColorsRenderMode::None);
});
});
});
cx_b.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |settings| {
- settings.lsp_document_colors = Some(DocumentColorsRenderMode::Inlay);
+ store.update_user_settings(cx, |settings| {
+ settings.editor.lsp_document_colors = Some(DocumentColorsRenderMode::Inlay);
});
});
});
@@ -2422,8 +2422,8 @@ async fn test_lsp_document_color(cx_a: &mut TestAppContext, cx_b: &mut TestAppCo
cx_b.update(|_, cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |settings| {
- settings.lsp_document_colors = Some(DocumentColorsRenderMode::Background);
+ store.update_user_settings(cx, |settings| {
+ settings.editor.lsp_document_colors = Some(DocumentColorsRenderMode::Background);
});
});
});
@@ -2450,8 +2450,8 @@ async fn test_lsp_document_color(cx_a: &mut TestAppContext, cx_b: &mut TestAppCo
cx_b.update(|_, cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |settings| {
- settings.lsp_document_colors = Some(DocumentColorsRenderMode::Inlay);
+ store.update_user_settings(cx, |settings| {
+ settings.editor.lsp_document_colors = Some(DocumentColorsRenderMode::Inlay);
});
});
});
@@ -2478,8 +2478,8 @@ async fn test_lsp_document_color(cx_a: &mut TestAppContext, cx_b: &mut TestAppCo
cx_a.update(|_, cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |settings| {
- settings.lsp_document_colors = Some(DocumentColorsRenderMode::Border);
+ store.update_user_settings(cx, |settings| {
+ settings.editor.lsp_document_colors = Some(DocumentColorsRenderMode::Border);
});
});
});
@@ -3306,20 +3306,20 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
cx_b.update(editor::init);
// Turn inline-blame-off by default so no state is transferred without us explicitly doing so
let inline_blame_off_settings = Some(InlineBlameSettings {
- enabled: false,
+ enabled: Some(false),
..Default::default()
});
cx_a.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |settings| {
- settings.git.inline_blame = inline_blame_off_settings;
+ store.update_user_settings(cx, |settings| {
+ settings.git.get_or_insert_default().inline_blame = inline_blame_off_settings;
});
});
});
cx_b.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |settings| {
- settings.git.inline_blame = inline_blame_off_settings;
+ store.update_user_settings(cx, |settings| {
+ settings.git.get_or_insert_default().inline_blame = inline_blame_off_settings;
});
});
});
diff --git a/crates/collab/src/tests/following_tests.rs b/crates/collab/src/tests/following_tests.rs
index 0a9a69bfca9cdda3fc446ac48e9c63da5e75fe28..e6bab12934d9c262cbba378e0d2a94143e0a7602 100644
--- a/crates/collab/src/tests/following_tests.rs
+++ b/crates/collab/src/tests/following_tests.rs
@@ -12,7 +12,6 @@ use gpui::{
VisualContext, VisualTestContext, point,
};
use language::Capability;
-use project::WorktreeSettings;
use rpc::proto::PeerId;
use serde_json::json;
use settings::SettingsStore;
@@ -1710,8 +1709,9 @@ async fn test_following_into_excluded_file(
for cx in [&mut cx_a, &mut cx_b] {
cx.update(|cx| {
cx.update_global::(|store, cx| {
- store.update_user_settings::(cx, |settings| {
- settings.file_scan_exclusions = Some(vec!["**/.git".to_string()]);
+ store.update_user_settings(cx, |settings| {
+ settings.project.worktree.file_scan_exclusions =
+ Some(vec!["**/.git".to_string()]);
});
});
});
diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs
index 646dbfbd1575756e6955c0d60ae5af64a2760328..e3925b7ecd9d5ec50722ff9b3f521243c264e6ec 100644
--- a/crates/collab/src/tests/integration_tests.rs
+++ b/crates/collab/src/tests/integration_tests.rs
@@ -22,9 +22,7 @@ use gpui::{
use language::{
Diagnostic, DiagnosticEntry, DiagnosticSourceKind, FakeLspAdapter, Language, LanguageConfig,
LanguageMatcher, LineEnding, OffsetRangeExt, Point, Rope,
- language_settings::{
- AllLanguageSettings, Formatter, FormatterList, PrettierSettings, SelectedFormatter,
- },
+ language_settings::{Formatter, FormatterList, SelectedFormatter},
tree_sitter_rust, tree_sitter_typescript,
};
use lsp::{LanguageServerId, OneOf};
@@ -38,7 +36,7 @@ use project::{
use prompt_store::PromptBuilder;
use rand::prelude::*;
use serde_json::json;
-use settings::SettingsStore;
+use settings::{PrettierSettingsContent, SettingsStore};
use std::{
cell::{Cell, RefCell},
env, future, mem,
@@ -4598,15 +4596,15 @@ async fn test_formatting_buffer(
// host's configuration is honored as opposed to using the guest's settings.
cx_a.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |file| {
- file.defaults.formatter = Some(SelectedFormatter::List(FormatterList::Single(
- Formatter::External {
+ store.update_user_settings(cx, |file| {
+ file.project.all_languages.defaults.formatter = Some(SelectedFormatter::List(
+ FormatterList::Single(Formatter::External {
command: "awk".into(),
arguments: Some(
vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()].into(),
),
- },
- )));
+ }),
+ ));
});
});
});
@@ -4694,24 +4692,24 @@ async fn test_prettier_formatting_buffer(
cx_a.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |file| {
- file.defaults.formatter = Some(SelectedFormatter::Auto);
- file.defaults.prettier = Some(PrettierSettings {
- allowed: true,
- ..PrettierSettings::default()
+ store.update_user_settings(cx, |file| {
+ file.project.all_languages.defaults.formatter = Some(SelectedFormatter::Auto);
+ file.project.all_languages.defaults.prettier = Some(PrettierSettingsContent {
+ allowed: Some(true),
+ ..Default::default()
});
});
});
});
cx_b.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |file| {
- file.defaults.formatter = Some(SelectedFormatter::List(FormatterList::Single(
- Formatter::LanguageServer { name: None },
- )));
- file.defaults.prettier = Some(PrettierSettings {
- allowed: true,
- ..PrettierSettings::default()
+ store.update_user_settings(cx, |file| {
+ file.project.all_languages.defaults.formatter = Some(SelectedFormatter::List(
+ FormatterList::Single(Formatter::LanguageServer { name: None }),
+ ));
+ file.project.all_languages.defaults.prettier = Some(PrettierSettingsContent {
+ allowed: Some(true),
+ ..Default::default()
});
});
});
diff --git a/crates/collab/src/tests/remote_editing_collaboration_tests.rs b/crates/collab/src/tests/remote_editing_collaboration_tests.rs
index 6b46459a59b16717d965b42c4e19820f6d1dc062..e2e6d7b724386afafad27e72f867be70671263bc 100644
--- a/crates/collab/src/tests/remote_editing_collaboration_tests.rs
+++ b/crates/collab/src/tests/remote_editing_collaboration_tests.rs
@@ -14,10 +14,7 @@ use gpui::{
use http_client::BlockedHttpClient;
use language::{
FakeLspAdapter, Language, LanguageConfig, LanguageMatcher, LanguageRegistry,
- language_settings::{
- AllLanguageSettings, Formatter, FormatterList, PrettierSettings, SelectedFormatter,
- language_settings,
- },
+ language_settings::{Formatter, FormatterList, SelectedFormatter, language_settings},
tree_sitter_typescript,
};
use node_runtime::NodeRuntime;
@@ -30,7 +27,7 @@ use remote::RemoteClient;
use remote_server::{HeadlessAppState, HeadlessProject};
use rpc::proto;
use serde_json::json;
-use settings::SettingsStore;
+use settings::{PrettierSettingsContent, SettingsStore};
use std::{
path::Path,
sync::{Arc, atomic::AtomicUsize},
@@ -499,24 +496,24 @@ async fn test_ssh_collaboration_formatting_with_prettier(
cx_a.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |file| {
- file.defaults.formatter = Some(SelectedFormatter::Auto);
- file.defaults.prettier = Some(PrettierSettings {
- allowed: true,
- ..PrettierSettings::default()
+ store.update_user_settings(cx, |file| {
+ file.project.all_languages.defaults.formatter = Some(SelectedFormatter::Auto);
+ file.project.all_languages.defaults.prettier = Some(PrettierSettingsContent {
+ allowed: Some(true),
+ ..Default::default()
});
});
});
});
cx_b.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |file| {
- file.defaults.formatter = Some(SelectedFormatter::List(FormatterList::Single(
- Formatter::LanguageServer { name: None },
- )));
- file.defaults.prettier = Some(PrettierSettings {
- allowed: true,
- ..PrettierSettings::default()
+ store.update_user_settings(cx, |file| {
+ file.project.all_languages.defaults.formatter = Some(SelectedFormatter::List(
+ FormatterList::Single(Formatter::LanguageServer { name: None }),
+ ));
+ file.project.all_languages.defaults.prettier = Some(PrettierSettingsContent {
+ allowed: Some(true),
+ ..Default::default()
});
});
});
@@ -556,11 +553,11 @@ async fn test_ssh_collaboration_formatting_with_prettier(
cx_a.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
- store.update_user_settings::(cx, |file| {
- file.defaults.formatter = Some(SelectedFormatter::Auto);
- file.defaults.prettier = Some(PrettierSettings {
- allowed: true,
- ..PrettierSettings::default()
+ store.update_user_settings(cx, |file| {
+ file.project.all_languages.defaults.formatter = Some(SelectedFormatter::Auto);
+ file.project.all_languages.defaults.prettier = Some(PrettierSettingsContent {
+ allowed: Some(true),
+ ..Default::default()
});
});
});
diff --git a/crates/collab_ui/Cargo.toml b/crates/collab_ui/Cargo.toml
index b3b0f1b0e37207582293fcc6d8930cc004ad7405..24202445a79b5c906d4f6fe1f1a633422f24772a 100644
--- a/crates/collab_ui/Cargo.toml
+++ b/crates/collab_ui/Cargo.toml
@@ -47,7 +47,6 @@ picker.workspace = true
project.workspace = true
release_channel.workspace = true
rpc.workspace = true
-schemars.workspace = true
serde.workspace = true
serde_json.workspace = true
settings.workspace = true
diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs
index 1698fcb89376787bef52c0ee69b5d799976eda3b..6290878483e69d8a7b56ee865672b7f19d74d4de 100644
--- a/crates/collab_ui/src/collab_panel.rs
+++ b/crates/collab_ui/src/collab_panel.rs
@@ -2993,11 +2993,9 @@ impl Panel for CollabPanel {
_window: &mut Window,
cx: &mut Context,
) {
- settings::update_settings_file::(
- self.fs.clone(),
- cx,
- move |settings, _| settings.dock = Some(position),
- );
+ settings::update_settings_file(self.fs.clone(), cx, move |settings, _| {
+ settings.collaboration_panel.get_or_insert_default().dock = Some(position.into())
+ });
}
fn size(&self, _window: &Window, cx: &App) -> Pixels {
diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs
index f75dd663c838c84f167b3070b50a4e1f44e9aa2d..c43e865ef2dcbcd05a9b75cdde5e06bb0679de89 100644
--- a/crates/collab_ui/src/collab_ui.rs
+++ b/crates/collab_ui/src/collab_ui.rs
@@ -11,7 +11,6 @@ use gpui::{
App, Pixels, PlatformDisplay, Size, WindowBackgroundAppearance, WindowBounds,
WindowDecorations, WindowKind, WindowOptions, point,
};
-use panel_settings::MessageEditorSettings;
pub use panel_settings::{CollaborationPanelSettings, NotificationPanelSettings};
use release_channel::ReleaseChannel;
use settings::Settings;
@@ -21,7 +20,6 @@ use workspace::AppState;
pub fn init(app_state: &Arc, cx: &mut App) {
CollaborationPanelSettings::register(cx);
NotificationPanelSettings::register(cx);
- MessageEditorSettings::register(cx);
channel_view::init(cx);
collab_panel::init(cx);
diff --git a/crates/collab_ui/src/notification_panel.rs b/crates/collab_ui/src/notification_panel.rs
index 9731b89521e29ebda21ad5ce2cfca6e0531ae437..3d988c4634ded9bd2c94d8a75886cf452e64eacb 100644
--- a/crates/collab_ui/src/notification_panel.rs
+++ b/crates/collab_ui/src/notification_panel.rs
@@ -621,11 +621,9 @@ impl Panel for NotificationPanel {
}
fn set_position(&mut self, position: DockPosition, _: &mut Window, cx: &mut Context) {
- settings::update_settings_file::(
- self.fs.clone(),
- cx,
- move |settings, _| settings.dock = Some(position),
- );
+ settings::update_settings_file(self.fs.clone(), cx, move |settings, _| {
+ settings.notification_panel.get_or_insert_default().dock = Some(position.into())
+ });
}
fn size(&self, _: &Window, cx: &App) -> Pixels {
diff --git a/crates/collab_ui/src/panel_settings.rs b/crates/collab_ui/src/panel_settings.rs
index 98559ffd34006bf2f65427a899fd1fe5d41a4d11..f1cd24c820a631f86c215f810011cc5870ae1ff9 100644
--- a/crates/collab_ui/src/panel_settings.rs
+++ b/crates/collab_ui/src/panel_settings.rs
@@ -1,102 +1,72 @@
use gpui::Pixels;
-use schemars::JsonSchema;
-use serde::{Deserialize, Serialize};
-use settings::{Settings, SettingsKey, SettingsSources, SettingsUi};
+use settings::Settings;
+use ui::px;
+use util::MergeFrom as _;
use workspace::dock::DockPosition;
-#[derive(Deserialize, Debug)]
+#[derive(Debug)]
pub struct CollaborationPanelSettings {
pub button: bool,
pub dock: DockPosition,
pub default_width: Pixels,
}
-#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey)]
-#[settings_key(key = "collaboration_panel")]
-pub struct PanelSettingsContent {
- /// Whether to show the panel button in the status bar.
- ///
- /// Default: true
- pub button: Option,
- /// Where to dock the panel.
- ///
- /// Default: left
- pub dock: Option,
- /// Default width of the panel in pixels.
- ///
- /// Default: 240
- pub default_width: Option,
-}
-
-#[derive(Deserialize, Debug)]
+#[derive(Debug)]
pub struct NotificationPanelSettings {
pub button: bool,
pub dock: DockPosition,
pub default_width: Pixels,
}
-#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey)]
-#[settings_key(key = "notification_panel")]
-pub struct NotificationPanelSettingsContent {
- /// Whether to show the panel button in the status bar.
- ///
- /// Default: true
- pub button: Option,
- /// Where to dock the panel.
- ///
- /// Default: right
- pub dock: Option,
- /// Default width of the panel in pixels.
- ///
- /// Default: 300
- pub default_width: Option,
-}
-
-#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey)]
-#[settings_key(key = "message_editor")]
-pub struct MessageEditorSettings {
- /// Whether to automatically replace emoji shortcodes with emoji characters.
- /// For example: typing `:wave:` gets replaced with `👋`.
- ///
- /// Default: false
- pub auto_replace_emoji_shortcode: Option,
-}
-
impl Settings for CollaborationPanelSettings {
- type FileContent = PanelSettingsContent;
+ fn from_defaults(content: &settings::SettingsContent, _cx: &mut ui::App) -> Self {
+ let panel = content.collaboration_panel.as_ref().unwrap();
- fn load(
- sources: SettingsSources,
- _: &mut gpui::App,
- ) -> anyhow::Result {
- sources.json_merge()
+ Self {
+ button: panel.button.unwrap(),
+ dock: panel.dock.unwrap().into(),
+ default_width: panel.default_width.map(px).unwrap(),
+ }
}
- fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
-}
-
-impl Settings for NotificationPanelSettings {
- type FileContent = NotificationPanelSettingsContent;
-
- fn load(
- sources: SettingsSources,
- _: &mut gpui::App,
- ) -> anyhow::Result {
- sources.json_merge()
+ fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut ui::App) {
+ if let Some(panel) = content.collaboration_panel.as_ref() {
+ self.button.merge_from(&panel.button);
+ self.default_width
+ .merge_from(&panel.default_width.map(Pixels::from));
+ self.dock.merge_from(&panel.dock.map(Into::into));
+ }
}
- fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
+ fn import_from_vscode(
+ _vscode: &settings::VsCodeSettings,
+ _content: &mut settings::SettingsContent,
+ ) {
+ }
}
-impl Settings for MessageEditorSettings {
- type FileContent = MessageEditorSettings;
+impl Settings for NotificationPanelSettings {
+ fn from_defaults(content: &settings::SettingsContent, _cx: &mut ui::App) -> Self {
+ let panel = content.notification_panel.as_ref().unwrap();
+ return Self {
+ button: panel.button.unwrap(),
+ dock: panel.dock.unwrap().into(),
+ default_width: panel.default_width.map(px).unwrap(),
+ };
+ }
- fn load(
- sources: SettingsSources,
- _: &mut gpui::App,
- ) -> anyhow::Result {
- sources.json_merge()
+ fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut ui::App) {
+ let Some(panel) = content.notification_panel.as_ref() else {
+ return;
+ };
+ self.button.merge_from(&panel.button);
+ self.dock.merge_from(&panel.dock.map(Into::into));
+ self.default_width.merge_from(&panel.default_width.map(px));
}
- fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
+ fn import_from_vscode(
+ _vscode: &settings::VsCodeSettings,
+ _current: &mut settings::SettingsContent,
+ ) {
+ }
}
diff --git a/crates/context_server/Cargo.toml b/crates/context_server/Cargo.toml
index 5e4f8369c45f0edb58efda1618bf8fe0aad55749..1c5745408041ae2f67e91ba0d9365188ab957d4e 100644
--- a/crates/context_server/Cargo.toml
+++ b/crates/context_server/Cargo.toml
@@ -25,8 +25,9 @@ net.workspace = true
parking_lot.workspace = true
postage.workspace = true
schemars.workspace = true
-serde.workspace = true
serde_json.workspace = true
+serde.workspace = true
+settings.workspace = true
smol.workspace = true
tempfile.workspace = true
url = { workspace = true, features = ["serde"] }
diff --git a/crates/context_server/src/context_server.rs b/crates/context_server/src/context_server.rs
index b126bb393784664692b5de39fee5ed7f66e9948a..52ed524220947430df3e63fced367ca4eb223fff 100644
--- a/crates/context_server/src/context_server.rs
+++ b/crates/context_server/src/context_server.rs
@@ -12,12 +12,9 @@ use std::{fmt::Display, path::PathBuf};
use anyhow::Result;
use client::Client;
-use collections::HashMap;
use gpui::AsyncApp;
use parking_lot::RwLock;
-use schemars::JsonSchema;
-use serde::{Deserialize, Serialize};
-use util::redact::should_redact;
+pub use settings::ContextServerCommand;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ContextServerId(pub Arc);
@@ -28,32 +25,6 @@ impl Display for ContextServerId {
}
}
-#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, JsonSchema)]
-pub struct ContextServerCommand {
- #[serde(rename = "command")]
- pub path: PathBuf,
- pub args: Vec,
- pub env: Option>,
- /// Timeout for tool calls in milliseconds. Defaults to 60000 (60 seconds) if not specified.
- pub timeout: Option,
-}
-
-impl std::fmt::Debug for ContextServerCommand {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- let filtered_env = self.env.as_ref().map(|env| {
- env.iter()
- .map(|(k, v)| (k, if should_redact(k) { "[REDACTED]" } else { v }))
- .collect::>()
- });
-
- f.debug_struct("ContextServerCommand")
- .field("path", &self.path)
- .field("args", &self.args)
- .field("env", &filtered_env)
- .finish()
- }
-}
-
enum ContextServerTransport {
Stdio(ContextServerCommand, Option),
Custom(Arc),
diff --git a/crates/copilot/src/copilot_completion_provider.rs b/crates/copilot/src/copilot_completion_provider.rs
index 52d75175e5b5ba265bb32c6c15c713e1bd8faecd..c122dccec069ff636d39c64f24ef0aca41145012 100644
--- a/crates/copilot/src/copilot_completion_provider.rs
+++ b/crates/copilot/src/copilot_completion_provider.rs
@@ -281,14 +281,11 @@ mod tests {
use indoc::indoc;
use language::{
Point,
- language_settings::{
- AllLanguageSettings, AllLanguageSettingsContent, CompletionSettings, LspInsertMode,
- WordsCompletionMode,
- },
+ language_settings::{CompletionSettingsContent, LspInsertMode, WordsCompletionMode},
};
use project::Project;
use serde_json::json;
- use settings::SettingsStore;
+ use settings::{AllLanguageSettingsContent, SettingsStore};
use std::future::Future;
use util::{
path,
@@ -299,12 +296,11 @@ mod tests {
async fn test_copilot(executor: BackgroundExecutor, cx: &mut TestAppContext) {
// flaky
init_test(cx, |settings| {
- settings.defaults.completions = Some(CompletionSettings {
- words: WordsCompletionMode::Disabled,
- words_min_length: 0,
- lsp: true,
- lsp_fetch_timeout_ms: 0,
- lsp_insert_mode: LspInsertMode::Insert,
+ settings.defaults.completions = Some(CompletionSettingsContent {
+ words: Some(WordsCompletionMode::Disabled),
+ words_min_length: Some(0),
+ lsp_insert_mode: Some(LspInsertMode::Insert),
+ ..Default::default()
});
});
@@ -532,12 +528,11 @@ mod tests {
) {
// flaky
init_test(cx, |settings| {
- settings.defaults.completions = Some(CompletionSettings {
- words: WordsCompletionMode::Disabled,
- words_min_length: 0,
- lsp: true,
- lsp_fetch_timeout_ms: 0,
- lsp_insert_mode: LspInsertMode::Insert,
+ settings.defaults.completions = Some(CompletionSettingsContent {
+ words: Some(WordsCompletionMode::Disabled),
+ words_min_length: Some(0),
+ lsp_insert_mode: Some(LspInsertMode::Insert),
+ ..Default::default()
});
});
@@ -1128,7 +1123,7 @@ mod tests {
Project::init_settings(cx);
workspace::init_settings(cx);
SettingsStore::update_global(cx, |store: &mut SettingsStore, cx| {
- store.update_user_settings::(cx, f);
+ store.update_user_settings(cx, |settings| f(&mut settings.project.all_languages));
});
});
}
diff --git a/crates/dap/src/debugger_settings.rs b/crates/dap/src/debugger_settings.rs
index 4b841450462f1f59787df584cc4ba48eddf792c1..5fa3d60dfc08cd847dd961c322555b5ef54c78f7 100644
--- a/crates/dap/src/debugger_settings.rs
+++ b/crates/dap/src/debugger_settings.rs
@@ -1,28 +1,12 @@
use dap_types::SteppingGranularity;
-use gpui::{App, Global};
-use schemars::JsonSchema;
-use serde::{Deserialize, Serialize};
-use settings::{Settings, SettingsKey, SettingsSources, SettingsUi};
+use gpui::App;
+use settings::{Settings, SettingsContent};
+use util::MergeFrom;
-#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, SettingsUi)]
-#[serde(rename_all = "snake_case")]
-pub enum DebugPanelDockPosition {
- Left,
- Bottom,
- Right,
-}
-
-#[derive(Serialize, Deserialize, JsonSchema, Clone, SettingsUi, SettingsKey)]
-#[serde(default)]
-// todo(settings_ui) @ben: I'm pretty sure not having the fields be optional here is a bug,
-// it means the defaults will override previously set values if a single key is missing
-#[settings_ui(group = "Debugger")]
-#[settings_key(key = "debugger")]
pub struct DebuggerSettings {
/// Determines the stepping granularity.
///
/// Default: line
- #[settings_ui(skip)]
pub stepping_granularity: SteppingGranularity,
/// Whether the breakpoints should be reused across Zed sessions.
///
@@ -47,31 +31,53 @@ pub struct DebuggerSettings {
/// The dock position of the debug panel
///
/// Default: Bottom
- pub dock: DebugPanelDockPosition,
+ pub dock: settings::DockPosition,
}
-impl Default for DebuggerSettings {
- fn default() -> Self {
+impl Settings for DebuggerSettings {
+ fn from_defaults(content: &SettingsContent, _cx: &mut App) -> Self {
+ let content = content.debugger.clone().unwrap();
Self {
- button: true,
- save_breakpoints: true,
- stepping_granularity: SteppingGranularity::Line,
- timeout: 2000,
- log_dap_communications: true,
- format_dap_log_messages: true,
- dock: DebugPanelDockPosition::Bottom,
+ stepping_granularity: dap_granularity_from_settings(
+ content.stepping_granularity.unwrap(),
+ ),
+ save_breakpoints: content.save_breakpoints.unwrap(),
+ button: content.button.unwrap(),
+ timeout: content.timeout.unwrap(),
+ log_dap_communications: content.log_dap_communications.unwrap(),
+ format_dap_log_messages: content.format_dap_log_messages.unwrap(),
+ dock: content.dock.unwrap(),
}
}
-}
-impl Settings for DebuggerSettings {
- type FileContent = Self;
-
- fn load(sources: SettingsSources, _: &mut App) -> anyhow::Result {
- sources.json_merge()
+ fn refine(&mut self, content: &SettingsContent, _cx: &mut App) {
+ let Some(content) = &content.debugger else {
+ return;
+ };
+ self.stepping_granularity.merge_from(
+ &content
+ .stepping_granularity
+ .map(dap_granularity_from_settings),
+ );
+ self.save_breakpoints.merge_from(&content.save_breakpoints);
+ self.button.merge_from(&content.button);
+ self.timeout.merge_from(&content.timeout);
+ self.log_dap_communications
+ .merge_from(&content.log_dap_communications);
+ self.format_dap_log_messages
+ .merge_from(&content.format_dap_log_messages);
+ self.dock.merge_from(&content.dock);
}
- fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
+ fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut SettingsContent) {}
}
-impl Global for DebuggerSettings {}
+fn dap_granularity_from_settings(
+ granularity: settings::SteppingGranularity,
+) -> dap_types::SteppingGranularity {
+ match granularity {
+ settings::SteppingGranularity::Instruction => dap_types::SteppingGranularity::Instruction,
+ settings::SteppingGranularity::Line => dap_types::SteppingGranularity::Line,
+ settings::SteppingGranularity::Statement => dap_types::SteppingGranularity::Statement,
+ }
+}
diff --git a/crates/debugger_ui/src/debugger_panel.rs b/crates/debugger_ui/src/debugger_panel.rs
index ef714a1f6710f54c5673eac097e7530b3c605b58..12a31a7360bb6e7fb1ff6fdcaf18f73d679693a3 100644
--- a/crates/debugger_ui/src/debugger_panel.rs
+++ b/crates/debugger_ui/src/debugger_panel.rs
@@ -12,7 +12,6 @@ use crate::{
use anyhow::{Context as _, Result, anyhow};
use collections::IndexMap;
use dap::adapters::DebugAdapterName;
-use dap::debugger_settings::DebugPanelDockPosition;
use dap::{DapRegistry, StartDebuggingRequestArguments};
use dap::{client::SessionId, debugger_settings::DebuggerSettings};
use editor::Editor;
@@ -1400,11 +1399,7 @@ impl Panel for DebugPanel {
}
fn position(&self, _window: &Window, cx: &App) -> DockPosition {
- match DebuggerSettings::get_global(cx).dock {
- DebugPanelDockPosition::Left => DockPosition::Left,
- DebugPanelDockPosition::Bottom => DockPosition::Bottom,
- DebugPanelDockPosition::Right => DockPosition::Right,
- }
+ DebuggerSettings::get_global(cx).dock.into()
}
fn position_is_valid(&self, _: DockPosition) -> bool {
@@ -1426,18 +1421,9 @@ impl Panel for DebugPanel {
});
}
- settings::update_settings_file::(
- self.fs.clone(),
- cx,
- move |settings, _| {
- let dock = match position {
- DockPosition::Left => DebugPanelDockPosition::Left,
- DockPosition::Bottom => DebugPanelDockPosition::Bottom,
- DockPosition::Right => DebugPanelDockPosition::Right,
- };
- settings.dock = dock;
- },
- );
+ settings::update_settings_file(self.fs.clone(), cx, move |settings, _| {
+ settings.debugger.get_or_insert_default().dock = Some(position.into());
+ });
}
fn size(&self, _window: &Window, _: &App) -> Pixels {
diff --git a/crates/denoise/LICENSE-GPL b/crates/denoise/LICENSE-GPL
index e0f9dbd5d63fef1630c297edc4ceba4790be6f02..89e542f750cd3860a0598eff0dc34b56d7336dc4 120000
--- a/crates/denoise/LICENSE-GPL
+++ b/crates/denoise/LICENSE-GPL
@@ -1 +1 @@
-LICENSE-GPL
\ No newline at end of file
+../../LICENSE-GPL
\ No newline at end of file
diff --git a/crates/edit_prediction_button/src/edit_prediction_button.rs b/crates/edit_prediction_button/src/edit_prediction_button.rs
index d4c1763c405825f834b640dfb03b38bd3016288d..8822c05f6c4d1842fd74d45d876f64736eb62fbc 100644
--- a/crates/edit_prediction_button/src/edit_prediction_button.rs
+++ b/crates/edit_prediction_button/src/edit_prediction_button.rs
@@ -910,8 +910,10 @@ async fn open_disabled_globs_setting_in_editor(
let settings = cx.global::();
// Ensure that we always have "edit_predictions { "disabled_globs": [] }"
- let edits = settings.edits_for_update::(&text, |file| {
- file.edit_predictions
+ let edits = settings.edits_for_update(&text, |file| {
+ file.project
+ .all_languages
+ .edit_predictions
.get_or_insert_with(Default::default)
.disabled_globs
.get_or_insert_with(Vec::new);
@@ -948,9 +950,12 @@ async fn open_disabled_globs_setting_in_editor(
}
fn set_completion_provider(fs: Arc, cx: &mut App, provider: EditPredictionProvider) {
- update_settings_file::(fs, cx, move |file, _| {
- file.features
- .get_or_insert(Default::default())
+ update_settings_file(fs, cx, move |settings, _| {
+ settings
+ .project
+ .all_languages
+ .features
+ .get_or_insert_default()
.edit_prediction_provider = Some(provider);
});
}
@@ -962,18 +967,24 @@ fn toggle_show_edit_predictions_for_language(
) {
let show_edit_predictions =
all_language_settings(None, cx).show_edit_predictions(Some(&language), cx);
- update_settings_file::(fs, cx, move |file, _| {
- file.languages
+ update_settings_file(fs, cx, move |settings, _| {
+ settings
+ .project
+ .all_languages
+ .languages
.0
- .entry(language.name())
+ .entry(language.name().0)
.or_default()
.show_edit_predictions = Some(!show_edit_predictions);
});
}
fn hide_copilot(fs: Arc, cx: &mut App) {
- update_settings_file::(fs, cx, move |file, _| {
- file.features
+ update_settings_file(fs, cx, move |settings, _| {
+ settings
+ .project
+ .all_languages
+ .features
.get_or_insert(Default::default())
.edit_prediction_provider = Some(EditPredictionProvider::None);
});
@@ -984,13 +995,14 @@ fn toggle_edit_prediction_mode(fs: Arc, mode: EditPredictionsMode, cx: &
let current_mode = settings.edit_predictions_mode();
if current_mode != mode {
- update_settings_file::(fs, cx, move |settings, _cx| {
- if let Some(edit_predictions) = settings.edit_predictions.as_mut() {
- edit_predictions.mode = mode;
+ update_settings_file(fs, cx, move |settings, _cx| {
+ if let Some(edit_predictions) = settings.project.all_languages.edit_predictions.as_mut()
+ {
+ edit_predictions.mode = Some(mode);
} else {
- settings.edit_predictions =
- Some(language_settings::EditPredictionSettingsContent {
- mode,
+ settings.project.all_languages.edit_predictions =
+ Some(settings::EditPredictionSettingsContent {
+ mode: Some(mode),
..Default::default()
});
}
diff --git a/crates/edit_prediction_context/src/tree_sitter_index.rs b/crates/edit_prediction_context/src/tree_sitter_index.rs
index 4dc00941fe1b8a7a095fffd5605b040001c02eb7..f905aa7a01f29d26083d219bc8d2bd600847036a 100644
--- a/crates/edit_prediction_context/src/tree_sitter_index.rs
+++ b/crates/edit_prediction_context/src/tree_sitter_index.rs
@@ -506,7 +506,6 @@ mod tests {
use super::*;
use std::{path::Path, sync::Arc};
- use futures::channel::oneshot;
use gpui::TestAppContext;
use indoc::indoc;
use language::{Language, LanguageConfig, LanguageId, LanguageMatcher, tree_sitter_rust};
@@ -655,17 +654,10 @@ mod tests {
expect_file_decl("a.rs", &decls[1], &project, cx);
});
- // Drop the buffer and wait for release
- let (release_tx, release_rx) = oneshot::channel();
- cx.update(|cx| {
- cx.observe_release(&buffer, |_, _| {
- release_tx.send(()).ok();
- })
- .detach();
+ // Need to trigger flush_effects so that the observe_release handler will run.
+ cx.update(|_cx| {
+ drop(buffer);
});
- drop(buffer);
- cx.run_until_parked();
- release_rx.await.ok();
cx.run_until_parked();
index.read_with(cx, |index, cx| {
diff --git a/crates/editor/src/code_completion_tests.rs b/crates/editor/src/code_completion_tests.rs
index a1d9f04a9c590ef1f20779bf19c2fe0be8905709..ec97c0ebb31952da9ad8e9e6f4f75b4b0078c4a3 100644
--- a/crates/editor/src/code_completion_tests.rs
+++ b/crates/editor/src/code_completion_tests.rs
@@ -1,9 +1,10 @@
-use crate::{code_context_menus::CompletionsMenu, editor_settings::SnippetSortOrder};
+use crate::code_context_menus::CompletionsMenu;
use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::TestAppContext;
use language::CodeLabel;
use lsp::{CompletionItem, CompletionItemKind, LanguageServerId};
use project::{Completion, CompletionSource};
+use settings::SnippetSortOrder;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use text::Anchor;
diff --git a/crates/editor/src/code_context_menus.rs b/crates/editor/src/code_context_menus.rs
index 9384e2efed162e1a21b0b31fdc0e7f65ba25b130..a89125a3aa6aebe23665469f34962dbacddc52d6 100644
--- a/crates/editor/src/code_context_menus.rs
+++ b/crates/editor/src/code_context_menus.rs
@@ -32,7 +32,6 @@ use ui::{Color, IntoElement, ListItem, Pixels, Popover, Styled, prelude::*};
use util::ResultExt;
use crate::CodeActionSource;
-use crate::editor_settings::SnippetSortOrder;
use crate::hover_popover::{hover_markdown_style, open_markdown_url};
use crate::{
CodeActionProvider, CompletionId, CompletionItemKind, CompletionProvider, DisplayRow, Editor,
@@ -40,6 +39,7 @@ use crate::{
actions::{ConfirmCodeAction, ConfirmCompletion},
split_words, styled_runs_for_code_label,
};
+use settings::SnippetSortOrder;
pub const MENU_GAP: Pixels = px(4.);
pub const MENU_ASIDE_X_PADDING: Pixels = px(16.);
diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs
index 1acbdab7a6646fe46b9ad9d9cb09c1549d64bb1a..cc6bb3571bc11c836fa4d13abb76f6cd4a554755 100644
--- a/crates/editor/src/display_map.rs
+++ b/crates/editor/src/display_map.rs
@@ -1529,12 +1529,11 @@ pub mod tests {
use language::{
Buffer, Diagnostic, DiagnosticEntry, DiagnosticSet, Language, LanguageConfig,
LanguageMatcher,
- language_settings::{AllLanguageSettings, AllLanguageSettingsContent},
};
use lsp::LanguageServerId;
use project::Project;
use rand::{Rng, prelude::*};
- use settings::SettingsStore;
+ use settings::{SettingsContent, SettingsStore};
use smol::stream::StreamExt;
use std::{env, sync::Arc};
use text::PointUtf16;
@@ -1564,7 +1563,9 @@ pub mod tests {
log::info!("wrap width: {:?}", wrap_width);
cx.update(|cx| {
- init_test(cx, |s| s.defaults.tab_size = NonZeroU32::new(tab_size));
+ init_test(cx, |s| {
+ s.project.all_languages.defaults.tab_size = NonZeroU32::new(tab_size)
+ });
});
let buffer = cx.update(|cx| {
@@ -1623,8 +1624,9 @@ pub mod tests {
log::info!("setting tab size to {:?}", tab_size);
cx.update(|cx| {
cx.update_global::(|store, cx| {
- store.update_user_settings::(cx, |s| {
- s.defaults.tab_size = NonZeroU32::new(tab_size);
+ store.update_user_settings(cx, |s| {
+ s.project.all_languages.defaults.tab_size =
+ NonZeroU32::new(tab_size);
});
});
});
@@ -2084,7 +2086,11 @@ pub mod tests {
);
language.set_theme(&theme);
- cx.update(|cx| init_test(cx, |s| s.defaults.tab_size = Some(2.try_into().unwrap())));
+ cx.update(|cx| {
+ init_test(cx, |s| {
+ s.project.all_languages.defaults.tab_size = Some(2.try_into().unwrap())
+ })
+ });
let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
@@ -2910,7 +2916,7 @@ pub mod tests {
chunks
}
- fn init_test(cx: &mut App, f: impl Fn(&mut AllLanguageSettingsContent)) {
+ fn init_test(cx: &mut App, f: impl Fn(&mut SettingsContent)) {
let settings = SettingsStore::test(cx);
cx.set_global(settings);
workspace::init_settings(cx);
@@ -2919,7 +2925,7 @@ pub mod tests {
Project::init_settings(cx);
theme::init(LoadThemes::JustBase, cx);
cx.update_global::(|store, cx| {
- store.update_user_settings::(cx, f);
+ store.update_user_settings(cx, f);
});
}
}
diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs
index 03d04e7010248293604d10c2f3e553430e74c9c6..2d16e6af8b469ff6e94b1b9fc7d11f7186e7b3c3 100644
--- a/crates/editor/src/display_map/block_map.rs
+++ b/crates/editor/src/display_map/block_map.rs
@@ -1264,36 +1264,30 @@ impl BlockMapWriter<'_> {
range: Range,
inclusive: bool,
) -> &[Arc] {
+ if range.is_empty() && !inclusive {
+ return &[];
+ }
let wrap_snapshot = self.0.wrap_snapshot.borrow();
let buffer = wrap_snapshot.buffer_snapshot();
let start_block_ix = match self.0.custom_blocks.binary_search_by(|block| {
let block_end = block.end().to_offset(buffer);
- block_end.cmp(&range.start).then_with(|| {
- if inclusive || (range.is_empty() && block.start().to_offset(buffer) == block_end) {
- Ordering::Greater
- } else {
- Ordering::Less
- }
- })
+ block_end.cmp(&range.start).then(Ordering::Greater)
}) {
Ok(ix) | Err(ix) => ix,
};
- let end_block_ix = match self.0.custom_blocks.binary_search_by(|block| {
- block
- .start()
- .to_offset(buffer)
- .cmp(&range.end)
- .then(if inclusive {
- Ordering::Less
- } else {
- Ordering::Greater
- })
+ let end_block_ix = match self.0.custom_blocks[start_block_ix..].binary_search_by(|block| {
+ let block_start = block.start().to_offset(buffer);
+ block_start.cmp(&range.end).then(if inclusive {
+ Ordering::Less
+ } else {
+ Ordering::Greater
+ })
}) {
Ok(ix) | Err(ix) => ix,
};
- &self.0.custom_blocks[start_block_ix..end_block_ix]
+ &self.0.custom_blocks[start_block_ix..][..end_block_ix]
}
}
diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs
index 49a09385d521e2b79c01acc4b2ff2ad9db3be936..d8501f2104a00183be68dc461f9128a600227aa6 100644
--- a/crates/editor/src/editor.rs
+++ b/crates/editor/src/editor.rs
@@ -160,9 +160,7 @@ use project::{
},
git_store::{GitStoreEvent, RepositoryEvent},
lsp_store::{CompletionDocumentation, FormatTrigger, LspFormatTarget, OpenLspBufferHandle},
- project_settings::{
- DiagnosticSeverity, GitGutterSetting, GoToDiagnosticSeverityFilter, ProjectSettings,
- },
+ project_settings::{DiagnosticSeverity, GoToDiagnosticSeverityFilter, ProjectSettings},
};
use rand::seq::SliceRandom;
use rpc::{ErrorCode, ErrorExt, proto::PeerId};
@@ -171,7 +169,7 @@ use selections_collection::{
MutableSelectionsCollection, SelectionsCollection, resolve_selections,
};
use serde::{Deserialize, Serialize};
-use settings::{Settings, SettingsLocation, SettingsStore, update_settings_file};
+use settings::{GitGutterSetting, Settings, SettingsLocation, SettingsStore, update_settings_file};
use smallvec::{SmallVec, smallvec};
use snippet::Snippet;
use std::{
@@ -2189,7 +2187,7 @@ impl Editor {
show_selection_menu: None,
show_git_blame_inline_delay_task: None,
git_blame_inline_enabled: full_mode
- && ProjectSettings::get_global(cx).git.inline_blame_enabled(),
+ && ProjectSettings::get_global(cx).git.inline_blame.enabled,
render_diff_hunk_controls: Arc::new(render_diff_hunk_controls),
serialize_dirty_buffers: !is_minimap
&& ProjectSettings::get_global(cx)
@@ -2414,14 +2412,10 @@ impl Editor {
pub fn is_range_selected(&mut self, range: &Range, cx: &mut Context) -> bool {
if self
.selections
- .pending
- .as_ref()
+ .pending_anchor()
.is_some_and(|pending_selection| {
let snapshot = self.buffer().read(cx).snapshot(cx);
- pending_selection
- .selection
- .range()
- .includes(range, &snapshot)
+ pending_selection.range().includes(range, &snapshot)
})
{
return true;
@@ -3054,7 +3048,7 @@ impl Editor {
}
}
- let selection_anchors = self.selections.disjoint_anchors();
+ let selection_anchors = self.selections.disjoint_anchors_arc();
if self.focus_handle.is_focused(window) && self.leader_id.is_none() {
self.buffer.update(cx, |buffer, cx| {
@@ -3170,7 +3164,7 @@ impl Editor {
self.blink_manager.update(cx, BlinkManager::pause_blinking);
cx.emit(EditorEvent::SelectionsChanged { local });
- let selections = &self.selections.disjoint;
+ let selections = &self.selections.disjoint_anchors_arc();
if selections.len() == 1 {
cx.emit(SearchEvent::ActiveMatchChanged)
}
@@ -3282,14 +3276,14 @@ impl Editor {
other: Entity,
cx: &mut Context,
) -> gpui::Subscription {
- let other_selections = other.read(cx).selections.disjoint.to_vec();
+ let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
self.selections.change_with(cx, |selections| {
selections.select_anchors(other_selections);
});
let other_subscription = cx.subscribe(&other, |this, other, other_evt, cx| {
if let EditorEvent::SelectionsChanged { local: true } = other_evt {
- let other_selections = other.read(cx).selections.disjoint.to_vec();
+ let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
if other_selections.is_empty() {
return;
}
@@ -3301,7 +3295,7 @@ impl Editor {
let this_subscription = cx.subscribe_self::(move |this, this_evt, cx| {
if let EditorEvent::SelectionsChanged { local: true } = this_evt {
- let these_selections = this.selections.disjoint.to_vec();
+ let these_selections = this.selections.disjoint_anchors().to_vec();
if these_selections.is_empty() {
return;
}
@@ -3339,7 +3333,7 @@ impl Editor {
effects,
old_cursor_position: self.selections.newest_anchor().head(),
history_entry: SelectionHistoryEntry {
- selections: self.selections.disjoint_anchors(),
+ selections: self.selections.disjoint_anchors_arc(),
select_next_state: self.select_next_state.clone(),
select_prev_state: self.select_prev_state.clone(),
add_selections_state: self.add_selections_state.clone(),
@@ -3499,6 +3493,7 @@ impl Editor {
let mut pending_selection = self
.selections
.pending_anchor()
+ .cloned()
.expect("extend_selection not called with pending selection");
if position >= tail {
pending_selection.start = tail_anchor;
@@ -3520,7 +3515,7 @@ impl Editor {
};
self.change_selections(effects, window, cx, |s| {
- s.set_pending(pending_selection, pending_mode)
+ s.set_pending(pending_selection.clone(), pending_mode)
});
}
@@ -3595,7 +3590,7 @@ impl Editor {
Some(selected_points[0].id)
} else {
let clicked_point_already_selected =
- self.selections.disjoint.iter().find(|selection| {
+ self.selections.disjoint_anchors().iter().find(|selection| {
selection.start.to_point(buffer) == start.to_point(buffer)
|| selection.end.to_point(buffer) == end.to_point(buffer)
});
@@ -3700,7 +3695,7 @@ impl Editor {
if self.columnar_selection_state.is_some() {
self.select_columns(position, goal_column, &display_map, window, cx);
- } else if let Some(mut pending) = self.selections.pending_anchor() {
+ } else if let Some(mut pending) = self.selections.pending_anchor().cloned() {
let buffer = &display_map.buffer_snapshot;
let head;
let tail;
@@ -3776,7 +3771,7 @@ impl Editor {
}
self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
- s.set_pending(pending, mode);
+ s.set_pending(pending.clone(), mode);
});
} else {
log::error!("update_selection dispatched with no pending selection");
@@ -3885,7 +3880,8 @@ impl Editor {
};
pending_nonempty_selection
- || (self.columnar_selection_state.is_some() && self.selections.disjoint.len() > 1)
+ || (self.columnar_selection_state.is_some()
+ && self.selections.disjoint_anchors().len() > 1)
}
pub fn has_pending_selection(&self) -> bool {
@@ -5473,19 +5469,33 @@ impl Editor {
if position.diff_base_anchor.is_some() {
return;
}
- let (buffer, buffer_position) =
- if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
- output
- } else {
- return;
- };
+ let buffer_position = multibuffer_snapshot.anchor_before(position);
+ let Some(buffer) = buffer_position
+ .buffer_id
+ .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
+ else {
+ return;
+ };
let buffer_snapshot = buffer.read(cx).snapshot();
let query: Option> =
- Self::completion_query(&multibuffer_snapshot, position).map(|query| query.into());
+ Self::completion_query(&multibuffer_snapshot, buffer_position)
+ .map(|query| query.into());
drop(multibuffer_snapshot);
+ // Hide the current completions menu when query is empty. Without this, cached
+ // completions from before the trigger char may be reused (#32774).
+ if query.is_none() {
+ let menu_is_open = matches!(
+ self.context_menu.borrow().as_ref(),
+ Some(CodeContextMenu::Completions(_))
+ );
+ if menu_is_open {
+ self.hide_context_menu(window, cx);
+ }
+ }
+
let mut ignore_word_threshold = false;
let provider = match requested_source {
Some(CompletionsMenuSource::Normal) | None => self.completion_provider.clone(),
@@ -5507,37 +5517,6 @@ impl Editor {
.as_ref()
.is_none_or(|provider| provider.filter_completions());
- let trigger_kind = match trigger {
- Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
- CompletionTriggerKind::TRIGGER_CHARACTER
- }
- _ => CompletionTriggerKind::INVOKED,
- };
- let completion_context = CompletionContext {
- trigger_character: trigger.and_then(|trigger| {
- if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
- Some(String::from(trigger))
- } else {
- None
- }
- }),
- trigger_kind,
- };
-
- // Hide the current completions menu when a trigger char is typed. Without this, cached
- // completions from before the trigger char may be reused (#32774). Snippet choices could
- // involve trigger chars, so this is skipped in that case.
- if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER && self.snippet_stack.is_empty()
- {
- let menu_is_open = matches!(
- self.context_menu.borrow().as_ref(),
- Some(CodeContextMenu::Completions(_))
- );
- if menu_is_open {
- self.hide_context_menu(window, cx);
- }
- }
-
if let Some(CodeContextMenu::Completions(menu)) = self.context_menu.borrow_mut().as_mut() {
if filter_completions {
menu.filter(query.clone(), provider.clone(), window, cx);
@@ -5568,6 +5547,29 @@ impl Editor {
}
};
+ let trigger_kind = match trigger {
+ Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
+ CompletionTriggerKind::TRIGGER_CHARACTER
+ }
+ _ => CompletionTriggerKind::INVOKED,
+ };
+ let completion_context = CompletionContext {
+ trigger_character: trigger.and_then(|trigger| {
+ if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
+ Some(String::from(trigger))
+ } else {
+ None
+ }
+ }),
+ trigger_kind,
+ };
+
+ let Anchor {
+ excerpt_id: buffer_excerpt_id,
+ text_anchor: buffer_position,
+ ..
+ } = buffer_position;
+
let (word_replace_range, word_to_exclude) = if let (word_range, Some(CharKind::Word)) =
buffer_snapshot.surrounding_word(buffer_position, false)
{
@@ -5587,8 +5589,9 @@ impl Editor {
.language_at(buffer_position)
.map(|language| language.name());
- let completion_settings =
- language_settings(language.clone(), buffer_snapshot.file(), cx).completions;
+ let completion_settings = language_settings(language.clone(), buffer_snapshot.file(), cx)
+ .completions
+ .clone();
let show_completion_documentation = buffer_snapshot
.settings_at(buffer_position, cx)
@@ -5623,7 +5626,7 @@ impl Editor {
let (mut words, provider_responses) = match &provider {
Some(provider) => {
let provider_responses = provider.completions(
- position.excerpt_id,
+ buffer_excerpt_id,
&buffer,
buffer_position,
completion_context,
@@ -6059,7 +6062,7 @@ impl Editor {
editor.refresh_edit_prediction(true, false, window, cx);
});
- self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), &snapshot);
+ self.invalidate_autoclose_regions(&self.selections.disjoint_anchors_arc(), &snapshot);
let show_new_completions_on_confirm = completion
.confirm
@@ -7466,7 +7469,7 @@ impl Editor {
s.select_anchor_ranges([last_edit_end..last_edit_end]);
});
- let selections = self.selections.disjoint_anchors();
+ let selections = self.selections.disjoint_anchors_arc();
if let Some(transaction_id_now) = self.buffer.read(cx).last_transaction_id(cx) {
let has_new_transaction = transaction_id_prev != Some(transaction_id_now);
if has_new_transaction {
@@ -7711,7 +7714,7 @@ impl Editor {
let Some(mode) = Self::columnar_selection_mode(modifiers, cx) else {
return;
};
- if self.selections.pending.is_none() {
+ if self.selections.pending_anchor().is_none() {
return;
}
@@ -10511,7 +10514,7 @@ impl Editor {
fn enable_wrap_selections_in_tag(&self, cx: &App) -> bool {
let snapshot = self.buffer.read(cx).snapshot(cx);
- for selection in self.selections.disjoint_anchors().iter() {
+ for selection in self.selections.disjoint_anchors_arc().iter() {
if snapshot
.language_at(selection.start)
.and_then(|lang| lang.config().wrap_characters.as_ref())
@@ -10845,7 +10848,7 @@ impl Editor {
let snapshot = self.snapshot(window, cx);
let cursors = self
.selections
- .disjoint_anchors()
+ .disjoint_anchors_arc()
.iter()
.map(|selection| {
let cursor_position: Point = selection.head().to_point(&snapshot.buffer_snapshot);
@@ -14536,7 +14539,7 @@ impl Editor {
window: &mut Window,
cx: &mut Context,
) -> Result<()> {
- let selections = self.selections.disjoint_anchors();
+ let selections = self.selections.disjoint_anchors_arc();
match selections.first() {
Some(first) if selections.len() >= 2 => {
self.change_selections(Default::default(), window, cx, |s| {
@@ -14560,7 +14563,7 @@ impl Editor {
window: &mut Window,
cx: &mut Context,
) -> Result<()> {
- let selections = self.selections.disjoint_anchors();
+ let selections = self.selections.disjoint_anchors_arc();
match selections.last() {
Some(last) if selections.len() >= 2 => {
self.change_selections(Default::default(), window, cx, |s| {
@@ -15117,11 +15120,9 @@ impl Editor {
let full_edits = selections
.into_iter()
.filter_map(|selection| {
- // Only requires two branches once if-let-chains stabilize (#53667)
- let child = if !selection.is_empty() {
- selection.range()
- } else if let Some((_, ancestor_range)) =
- buffer.syntax_ancestor(selection.start..selection.end)
+ let child = if selection.is_empty()
+ && let Some((_, ancestor_range)) =
+ buffer.syntax_ancestor(selection.start..selection.end)
{
match ancestor_range {
MultiOrSingleBufferOffsetRange::Single(range) => range,
@@ -15149,6 +15150,9 @@ impl Editor {
Some((selection.id, parent, text))
})
.collect::>();
+ if full_edits.is_empty() {
+ return;
+ }
self.transact(window, cx, |this, window, cx| {
this.buffer.update(cx, |buffer, cx| {
@@ -15657,7 +15661,7 @@ impl Editor {
cx: &mut Context,
) {
- let selections = self.selections.disjoint_anchors();
+ let selections = self.selections.disjoint_anchors_arc();
let lines = if lines == 0 {
EditorSettings::get_global(cx).expand_excerpt_lines
@@ -17116,7 +17120,7 @@ impl Editor {
.transaction(transaction_id_prev)
.map(|t| t.0.clone())
})
- .unwrap_or_else(|| self.selections.disjoint_anchors());
+ .unwrap_or_else(|| self.selections.disjoint_anchors_arc());
let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
let format = project.update(cx, |project, cx| {
@@ -17656,7 +17660,7 @@ impl Editor {
.update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
{
self.selection_history
- .insert_transaction(tx_id, self.selections.disjoint_anchors());
+ .insert_transaction(tx_id, self.selections.disjoint_anchors_arc());
cx.emit(EditorEvent::TransactionBegun {
transaction_id: tx_id,
});
@@ -17678,7 +17682,7 @@ impl Editor {
if let Some((_, end_selections)) =
self.selection_history.transaction_mut(transaction_id)
{
- *end_selections = Some(self.selections.disjoint_anchors());
+ *end_selections = Some(self.selections.disjoint_anchors_arc());
} else {
log::error!("unexpectedly ended a transaction that wasn't started by this editor");
}
@@ -18348,7 +18352,12 @@ impl Editor {
_window: &mut Window,
cx: &mut Context,
) {
- let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
+ let ranges: Vec<_> = self
+ .selections
+ .disjoint_anchors()
+ .iter()
+ .map(|s| s.range())
+ .collect();
self.toggle_diff_hunks_in_ranges(ranges, cx);
}
@@ -18386,7 +18395,12 @@ impl Editor {
cx: &mut Context,
) {
let snapshot = self.buffer.read(cx).snapshot(cx);
- let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
+ let ranges: Vec<_> = self
+ .selections
+ .disjoint_anchors()
+ .iter()
+ .map(|s| s.range())
+ .collect();
let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
self.stage_or_unstage_diff_hunks(stage, ranges, cx);
}
@@ -18550,7 +18564,12 @@ impl Editor {
}
pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context) {
- let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
+ let ranges: Vec<_> = self
+ .selections
+ .disjoint_anchors()
+ .iter()
+ .map(|s| s.range())
+ .collect();
self.buffer
.update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
}
@@ -18988,8 +19007,8 @@ impl Editor {
};
let fs = workspace.read(cx).app_state().fs.clone();
let current_show = TabBarSettings::get_global(cx).show;
- update_settings_file::