diff --git a/.config/hakari.toml b/.config/hakari.toml index 9cc9c1e4eea246b3bbf13d09e7206ee44c31468b..1e8386a14115be2e36b287ace0d47d464df9e620 100644 --- a/.config/hakari.toml +++ b/.config/hakari.toml @@ -24,7 +24,7 @@ workspace-members = [ third-party = [ { name = "reqwest", version = "0.11.27" }, # build of remote_server should not include scap / its x11 dependency - { name = "scap", git = "https://github.com/zed-industries/scap", rev = "808aa5c45b41e8f44729d02e38fd00a2fe2722e7" }, + { name = "zed-scap", git = "https://github.com/zed-industries/scap", rev = "4afea48c3b002197176fb19cd0f9b180dd36eaac", version = "0.0.8-zed" }, # build of remote_server should not need to include on libalsa through rodio { name = "rodio", git = "https://github.com/RustAudio/rodio" }, ] @@ -37,8 +37,6 @@ workspace-members = [ "zed_glsl", "zed_html", "zed_proto", - "zed_ruff", "slash_commands_example", - "zed_snippets", "zed_test_extension", ] diff --git a/.github/actions/run_tests/action.yml b/.github/actions/run_tests/action.yml index e46bc26945e4b5f94ad9f98a882aaa51fc6189af..faf94017976f4b06fdaaa80a5db8083405a7950a 100644 --- a/.github/actions/run_tests/action.yml +++ b/.github/actions/run_tests/action.yml @@ -20,4 +20,4 @@ runs: - name: Run tests shell: bash -euxo pipefail {0} - run: cargo nextest run --workspace --no-fail-fast + run: cargo nextest run --workspace --no-fail-fast --failure-output immediate-final diff --git a/.github/actions/run_tests_windows/action.yml b/.github/actions/run_tests_windows/action.yml index 8392ca1d375856c7f649e73d2445ce4f873924b1..d85d47cb969e22ca3c73c9ab8caca279a9b5ba88 100644 --- a/.github/actions/run_tests_windows/action.yml +++ b/.github/actions/run_tests_windows/action.yml @@ -24,4 +24,4 @@ runs: shell: powershell working-directory: ${{ inputs.working-directory }} run: | - cargo nextest run --workspace --no-fail-fast + cargo nextest run --workspace --no-fail-fast --failure-output immediate-final diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 34ef3c20cc3497d27ece10b1b8f1619b23f95f97..1d2500e2f794ca06bdbc6c06b21ce39001b57973 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -826,8 +826,9 @@ jobs: timeout-minutes: 120 name: Create a Windows installer runs-on: [self-32vcpu-windows-2022] - if: contains(github.event.pull_request.labels.*.name, 'run-bundling') - # if: (startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-bundling')) + if: | + ( startsWith(github.ref, 'refs/tags/v') + || contains(github.event.pull_request.labels.*.name, 'run-bundling') ) needs: [windows_tests] env: AZURE_TENANT_ID: ${{ secrets.AZURE_SIGNING_TENANT_ID }} @@ -870,8 +871,7 @@ jobs: - name: Upload Artifacts to release uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1 - # Re-enable when we are ready to publish windows preview releases - if: ${{ !(contains(github.event.pull_request.labels.*.name, 'run-bundling')) && env.RELEASE_CHANNEL == 'preview' }} # upload only preview + if: ${{ !(contains(github.event.pull_request.labels.*.name, 'run-bundling')) }} with: draft: true prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }} diff --git a/.github/workflows/community_release_actions.yml b/.github/workflows/community_release_actions.yml index 0f7a73649e9e1180c78a66ddf54055bf66f243f9..4a042a5e06b499b1ca278f152798c171971129ee 100644 --- a/.github/workflows/community_release_actions.yml +++ b/.github/workflows/community_release_actions.yml @@ -39,7 +39,7 @@ jobs: content: ${{ steps.get-content.outputs.string }} send_release_notes_email: - if: github.repository_owner == 'zed-industries' && !github.event.release.prerelease + if: false && github.repository_owner == 'zed-industries' && !github.event.release.prerelease runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 diff --git a/.github/workflows/issue_response.yml b/.github/workflows/issue_response.yml deleted file mode 100644 index f084b9ba9dfc4aba46a766986bafcffa3e9fd0ce..0000000000000000000000000000000000000000 --- a/.github/workflows/issue_response.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: Issue Response - -on: - schedule: - - cron: "0 12 * * 2" - workflow_dispatch: - -jobs: - issue-response: - if: github.repository_owner == 'zed-industries' - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - - - uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 - with: - version: 9 - - - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 - with: - node-version: "20" - cache: "pnpm" - cache-dependency-path: "script/issue_response/pnpm-lock.yaml" - - - run: pnpm install --dir script/issue_response - - - name: Run Issue Response - run: pnpm run --dir script/issue_response start - env: - ISSUE_RESPONSE_GITHUB_TOKEN: ${{ secrets.ISSUE_RESPONSE_GITHUB_TOKEN }} - SLACK_ISSUE_RESPONSE_WEBHOOK_URL: ${{ secrets.SLACK_ISSUE_RESPONSE_WEBHOOK_URL }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8615ba2c9ad324357792772392c49c7899ffa410..9cbac4af2b57f0350fa9f5665e110e0d6e7f6341 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -63,6 +63,7 @@ Although there are few hard and fast rules, typically we don't merge: - New file icons. Zed's default icon theme consists of icons that are hand-designed to fit together in a cohesive manner, please don't submit PRs with off-the-shelf SVGs. - Giant refactorings. - Non-trivial changes with no tests. +- Stylistic code changes that do not alter any app logic. Reducing allocations, removing `.unwrap()`s, fixing typos is great; making code "more readable" — maybe not so much. - Features where (in our subjective opinion) the extra complexity isn't worth it for the number of people who will benefit. - Anything that seems completely AI generated. diff --git a/Cargo.lock b/Cargo.lock index ea528b0cc670fd9d846ec4787f904f4ffa8f74eb..7b7ffde83d8072556207195c0d86a3e3e093c9c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,7 +11,6 @@ dependencies = [ "agent_settings", "anyhow", "buffer_diff", - "collections", "editor", "env_logger 0.11.8", "file_icons", @@ -36,10 +35,11 @@ dependencies = [ "terminal", "ui", "url", - "util", "uuid", "watch", "workspace-hack", + "zed-collections", + "zed-util", ] [[package]] @@ -47,7 +47,6 @@ name = "acp_tools" version = "0.1.0" dependencies = [ "agent-client-protocol", - "collections", "gpui", "language", "markdown", @@ -57,9 +56,10 @@ dependencies = [ "settings", "theme", "ui", - "util", "workspace", "workspace-hack", + "zed-collections", + "zed-util", ] [[package]] @@ -69,7 +69,6 @@ dependencies = [ "anyhow", "buffer_diff", "clock", - "collections", "ctor", "futures 0.3.31", "gpui", @@ -82,9 +81,10 @@ dependencies = [ "serde_json", "settings", "text", - "util", "watch", "workspace-hack", + "zed-collections", + "zed-util", "zlog", ] @@ -104,9 +104,9 @@ dependencies = [ "release_channel", "smallvec", "ui", - "util", "workspace", "workspace-hack", + "zed-util", ] [[package]] @@ -149,7 +149,6 @@ dependencies = [ "chrono", "client", "cloud_llm_client", - "collections", "component", "context_server", "convert_case 0.8.0", @@ -158,7 +157,6 @@ dependencies = [ "git", "gpui", "heed", - "http_client", "icons", "indoc", "itertools 0.14.0", @@ -185,10 +183,12 @@ dependencies = [ "theme", "thiserror 2.0.12", "time", - "util", "uuid", "workspace", "workspace-hack", + "zed-collections", + "zed-http-client", + "zed-util", "zed_env_vars", "zstd", ] @@ -228,7 +228,6 @@ dependencies = [ "client", "clock", "cloud_llm_client", - "collections", "context_server", "ctor", "db", @@ -241,7 +240,6 @@ dependencies = [ "gpui_tokio", "handlebars 4.5.0", "html_to_markdown", - "http_client", "indoc", "itertools 0.14.0", "language", @@ -273,12 +271,14 @@ dependencies = [ "tree-sitter-rust", "ui", "unindent", - "util", "uuid", "watch", "web_search", "workspace-hack", "worktree", + "zed-collections", + "zed-http-client", + "zed-util", "zed_env_vars", "zlog", "zstd", @@ -296,13 +296,11 @@ dependencies = [ "anyhow", "async-trait", "client", - "collections", "env_logger 0.11.8", "fs", "futures 0.3.31", "gpui", "gpui_tokio", - "http_client", "indoc", "language", "language_model", @@ -318,11 +316,15 @@ dependencies = [ "smol", "task", "tempfile", + "terminal", "thiserror 2.0.12", "ui", - "util", + "uuid", "watch", "workspace-hack", + "zed-collections", + "zed-http-client", + "zed-util", ] [[package]] @@ -331,7 +333,6 @@ version = "0.1.0" dependencies = [ "anyhow", "cloud_llm_client", - "collections", "convert_case 0.8.0", "fs", "gpui", @@ -343,8 +344,9 @@ dependencies = [ "serde_json", "serde_json_lenient", "settings", - "util", "workspace-hack", + "zed-collections", + "zed-util", ] [[package]] @@ -371,7 +373,6 @@ dependencies = [ "chrono", "client", "cloud_llm_client", - "collections", "command_palette_hooks", "component", "context_server", @@ -386,7 +387,6 @@ dependencies = [ "fuzzy", "gpui", "html_to_markdown", - "http_client", "indoc", "itertools 0.14.0", "jsonschema", @@ -419,7 +419,6 @@ dependencies = [ "serde_json", "serde_json_lenient", "settings", - "shlex", "smol", "streaming_diff", "task", @@ -437,10 +436,12 @@ dependencies = [ "unindent", "url", "urlencoding", - "util", "watch", "workspace", "workspace-hack", + "zed-collections", + "zed-http-client", + "zed-util", "zed_actions", ] @@ -516,7 +517,7 @@ dependencies = [ "rustix-openpty", "serde", "signal-hook", - "unicode-width 0.2.0", + "unicode-width", "vte", "windows-sys 0.59.0", ] @@ -658,7 +659,6 @@ dependencies = [ "anyhow", "chrono", "futures 0.3.31", - "http_client", "schemars 1.0.1", "serde", "serde_json", @@ -666,6 +666,7 @@ dependencies = [ "strum 0.27.1", "thiserror 2.0.12", "workspace-hack", + "zed-http-client", ] [[package]] @@ -805,13 +806,14 @@ dependencies = [ "anyhow", "futures 0.3.31", "gpui", + "log", "net", "proto", "smol", "tempfile", - "util", "windows 0.61.1", "workspace-hack", + "zed-util", "zeroize", ] @@ -837,7 +839,6 @@ dependencies = [ "client", "clock", "cloud_llm_client", - "collections", "context_server", "fs", "futures 0.3.31", @@ -866,10 +867,11 @@ dependencies = [ "text", "ui", "unindent", - "util", "uuid", "workspace", "workspace-hack", + "zed-collections", + "zed-util", "zed_env_vars", ] @@ -879,7 +881,6 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", - "collections", "derive_more", "extension", "futures 0.3.31", @@ -891,9 +892,10 @@ dependencies = [ "serde", "serde_json", "ui", - "util", "workspace", "workspace-hack", + "zed-collections", + "zed-util", ] [[package]] @@ -903,7 +905,6 @@ dependencies = [ "anyhow", "assistant_slash_command", "chrono", - "collections", "context_server", "editor", "feature_flags", @@ -913,7 +914,6 @@ dependencies = [ "globset", "gpui", "html_to_markdown", - "http_client", "language", "pretty_assertions", "project", @@ -925,10 +925,12 @@ dependencies = [ "smol", "text", "ui", - "util", "workspace", "workspace-hack", "worktree", + "zed-collections", + "zed-http-client", + "zed-util", "zlog", ] @@ -940,7 +942,6 @@ dependencies = [ "anyhow", "buffer_diff", "clock", - "collections", "ctor", "derive_more", "gpui", @@ -958,9 +959,10 @@ dependencies = [ "serde_json", "settings", "text", - "util", "workspace", "workspace-hack", + "zed-collections", + "zed-util", "zlog", ] @@ -977,7 +979,6 @@ dependencies = [ "client", "clock", "cloud_llm_client", - "collections", "component", "derive_more", "diffy", @@ -989,7 +990,6 @@ dependencies = [ "gpui_tokio", "handlebars 4.5.0", "html_to_markdown", - "http_client", "indoc", "itertools 0.14.0", "language", @@ -1024,11 +1024,13 @@ dependencies = [ "tree-sitter-rust", "ui", "unindent", - "util", "watch", "web_search", "workspace", "workspace-hack", + "zed-collections", + "zed-http-client", + "zed-util", "zlog", ] @@ -1403,7 +1405,6 @@ version = "0.1.0" dependencies = [ "anyhow", "async-tar", - "collections", "crossbeam", "denoise", "gpui", @@ -1415,8 +1416,9 @@ dependencies = [ "settings", "smol", "thiserror 2.0.12", - "util", "workspace-hack", + "zed-collections", + "zed-util", ] [[package]] @@ -1439,7 +1441,6 @@ dependencies = [ "client", "db", "gpui", - "http_client", "log", "paths", "release_channel", @@ -1451,6 +1452,7 @@ dependencies = [ "which 6.0.3", "workspace", "workspace-hack", + "zed-http-client", ] [[package]] @@ -1474,15 +1476,15 @@ dependencies = [ "client", "editor", "gpui", - "http_client", "markdown_preview", "release_channel", "serde", "serde_json", "smol", - "util", "workspace", "workspace-hack", + "zed-http-client", + "zed-util", ] [[package]] @@ -2009,8 +2011,8 @@ version = "0.1.0" dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", - "http_client", "workspace-hack", + "zed-http-client", ] [[package]] @@ -2307,14 +2309,15 @@ dependencies = [ [[package]] name = "blade-graphics" -version = "0.6.0" -source = "git+https://github.com/kvark/blade?rev=bfa594ea697d4b6326ea29f747525c85ecf933b9#bfa594ea697d4b6326ea29f747525c85ecf933b9" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4deb8f595ce7f00dee3543ebf6fd9a20ea86fc421ab79600dac30876250bdae" dependencies = [ "ash", "ash-window", "bitflags 2.9.0", "bytemuck", - "codespan-reporting 0.11.1", + "codespan-reporting", "glow", "gpu-alloc", "gpu-alloc-ash", @@ -2332,6 +2335,7 @@ dependencies = [ "objc2-metal", "objc2-quartz-core", "objc2-ui-kit", + "once_cell", "raw-window-handle", "slab", "wasm-bindgen", @@ -2341,7 +2345,8 @@ dependencies = [ [[package]] name = "blade-macros" version = "0.3.0" -source = "git+https://github.com/kvark/blade?rev=bfa594ea697d4b6326ea29f747525c85ecf933b9#bfa594ea697d4b6326ea29f747525c85ecf933b9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27142319e2f4c264581067eaccb9f80acccdde60d8b4bf57cc50cd3152f109ca" dependencies = [ "proc-macro2", "quote", @@ -2350,8 +2355,9 @@ dependencies = [ [[package]] name = "blade-util" -version = "0.2.0" -source = "git+https://github.com/kvark/blade?rev=bfa594ea697d4b6326ea29f747525c85ecf933b9#bfa594ea697d4b6326ea29f747525c85ecf933b9" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6be3a82c001ba7a17b6f8e413ede5d1004e6047213f8efaf0ffc15b5c4904c" dependencies = [ "blade-graphics", "bytemuck", @@ -2476,11 +2482,11 @@ dependencies = [ "rand 0.9.1", "rope", "serde_json", - "sum_tree", "text", "unindent", - "util", "workspace-hack", + "zed-sum-tree", + "zed-util", "zlog", ] @@ -2618,13 +2624,11 @@ dependencies = [ "anyhow", "audio", "client", - "collections", "feature_flags", "fs", "futures 0.3.31", "gpui", "gpui_tokio", - "http_client", "language", "livekit_client", "log", @@ -2633,8 +2637,10 @@ dependencies = [ "serde", "settings", "telemetry", - "util", "workspace-hack", + "zed-collections", + "zed-http-client", + "zed-util", ] [[package]] @@ -2933,10 +2939,8 @@ dependencies = [ "anyhow", "client", "clock", - "collections", "futures 0.3.31", "gpui", - "http_client", "language", "log", "postage", @@ -2945,8 +2949,10 @@ dependencies = [ "settings", "text", "time", - "util", "workspace-hack", + "zed-collections", + "zed-http-client", + "zed-util", ] [[package]] @@ -3080,8 +3086,8 @@ name = "cli" version = "0.1.0" dependencies = [ "anyhow", + "askpass", "clap", - "collections", "core-foundation 0.10.0", "core-services", "exec", @@ -3093,9 +3099,10 @@ dependencies = [ "release_channel", "serde", "tempfile", - "util", "windows 0.61.1", "workspace-hack", + "zed-collections", + "zed-util", ] [[package]] @@ -3109,7 +3116,6 @@ dependencies = [ "clock", "cloud_api_client", "cloud_llm_client", - "collections", "credentials_provider", "derive_more", "feature_flags", @@ -3117,7 +3123,6 @@ dependencies = [ "futures 0.3.31", "gpui", "gpui_tokio", - "http_client", "http_client_tls", "httparse", "log", @@ -3147,10 +3152,12 @@ dependencies = [ "tokio-rustls 0.26.2", "tokio-socks", "url", - "util", "windows 0.61.1", "workspace-hack", "worktree", + "zed-collections", + "zed-http-client", + "zed-util", ] [[package]] @@ -3172,11 +3179,11 @@ dependencies = [ "futures 0.3.31", "gpui", "gpui_tokio", - "http_client", "parking_lot", "serde_json", "workspace-hack", "yawc", + "zed-http-client", ] [[package]] @@ -3216,6 +3223,7 @@ dependencies = [ "indoc", "ordered-float 2.10.1", "rustc-hash 2.1.1", + "serde", "strum 0.27.1", "workspace-hack", ] @@ -3295,16 +3303,6 @@ dependencies = [ "objc", ] -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width 0.1.14", -] - [[package]] name = "codespan-reporting" version = "0.12.0" @@ -3313,7 +3311,7 @@ checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" dependencies = [ "serde", "termcolor", - "unicode-width 0.2.0", + "unicode-width", ] [[package]] @@ -3340,7 +3338,6 @@ dependencies = [ "client", "clock", "collab_ui", - "collections", "command_palette_hooks", "context_server", "ctor", @@ -3361,7 +3358,6 @@ dependencies = [ "gpui", "gpui_tokio", "hex", - "http_client", "hyper 0.14.32", "indoc", "language", @@ -3389,10 +3385,8 @@ dependencies = [ "reqwest 0.11.27", "reqwest_client", "rpc", - "rustc-demangle", "scrypt", "sea-orm", - "semantic_version", "semver", "serde", "serde_json", @@ -3416,11 +3410,14 @@ dependencies = [ "tracing", "tracing-subscriber", "unindent", - "util", "uuid", "workspace", "workspace-hack", "worktree", + "zed-collections", + "zed-http-client", + "zed-semantic-version", + "zed-util", "zlog", ] @@ -3433,13 +3430,11 @@ dependencies = [ "channel", "chrono", "client", - "collections", "db", "editor", "futures 0.3.31", "fuzzy", "gpui", - "http_client", "log", "menu", "notifications", @@ -3460,18 +3455,11 @@ dependencies = [ "title_bar", "tree-sitter-md", "ui", - "util", "workspace", "workspace-hack", -] - -[[package]] -name = "collections" -version = "0.1.0" -dependencies = [ - "indexmap 2.9.0", - "rustc-hash 2.1.1", - "workspace-hack", + "zed-collections", + "zed-http-client", + "zed-util", ] [[package]] @@ -3512,7 +3500,6 @@ version = "0.1.0" dependencies = [ "anyhow", "client", - "collections", "command_palette_hooks", "ctor", "db", @@ -3534,9 +3521,10 @@ dependencies = [ "theme", "time", "ui", - "util", "workspace", "workspace-hack", + "zed-collections", + "zed-util", "zed_actions", ] @@ -3544,17 +3532,16 @@ dependencies = [ name = "command_palette_hooks" version = "0.1.0" dependencies = [ - "collections", "derive_more", "gpui", "workspace-hack", + "zed-collections", ] [[package]] name = "component" version = "0.1.0" dependencies = [ - "collections", "documented", "gpui", "inventory", @@ -3562,6 +3549,7 @@ dependencies = [ "strum 0.27.1", "theme", "workspace-hack", + "zed-collections", ] [[package]] @@ -3582,7 +3570,7 @@ dependencies = [ "encode_unicode", "libc", "once_cell", - "unicode-width 0.2.0", + "unicode-width", "windows-sys 0.59.0", ] @@ -3624,7 +3612,6 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", - "collections", "futures 0.3.31", "gpui", "log", @@ -3638,8 +3625,9 @@ dependencies = [ "smol", "tempfile", "url", - "util", "workspace-hack", + "zed-collections", + "zed-util", ] [[package]] @@ -3675,7 +3663,6 @@ dependencies = [ "chrono", "client", "clock", - "collections", "command_palette_hooks", "ctor", "dirs 4.0.0", @@ -3684,7 +3671,6 @@ dependencies = [ "fs", "futures 0.3.31", "gpui", - "http_client", "indoc", "itertools 0.14.0", "language", @@ -3696,16 +3682,19 @@ dependencies = [ "paths", "project", "rpc", + "semver", "serde", "serde_json", "settings", - "sum_tree", "task", "theme", "ui", - "util", "workspace", "workspace-hack", + "zed-collections", + "zed-http-client", + "zed-sum-tree", + "zed-util", "zlog", ] @@ -4110,9 +4099,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.2.1" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" dependencies = [ "crc-catalog", ] @@ -4371,7 +4360,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b4400e26ea4b99417e4263b1ce2d8452404d750ba0809a7bd043072593d430d" dependencies = [ "cc", - "codespan-reporting 0.12.0", + "codespan-reporting", "proc-macro2", "quote", "scratch", @@ -4385,7 +4374,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31860c98f69fc14da5742c5deaf78983e846c7b27804ca8c8319e32eef421bde" dependencies = [ "clap", - "codespan-reporting 0.12.0", + "codespan-reporting", "proc-macro2", "quote", "syn 2.0.101", @@ -4419,12 +4408,10 @@ dependencies = [ "async-tar", "async-trait", "client", - "collections", "dap-types", "fs", "futures 0.3.31", "gpui", - "http_client", "language", "libc", "log", @@ -4442,8 +4429,10 @@ dependencies = [ "telemetry", "tree-sitter", "tree-sitter-go", - "util", "workspace-hack", + "zed-collections", + "zed-http-client", + "zed-util", "zlog", ] @@ -4463,7 +4452,6 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", - "collections", "dap", "dotenvy", "fs", @@ -4478,8 +4466,9 @@ dependencies = [ "shlex", "smol", "task", - "util", "workspace-hack", + "zed-collections", + "zed-util", ] [[package]] @@ -4576,8 +4565,8 @@ dependencies = [ "sqlez", "sqlez_macros", "tempfile", - "util", "workspace-hack", + "zed-util", "zed_env_vars", ] @@ -4603,8 +4592,8 @@ dependencies = [ "gpui", "serde_json", "task", - "util", "workspace-hack", + "zed-util", ] [[package]] @@ -4620,9 +4609,9 @@ dependencies = [ "serde_json", "settings", "smol", - "util", "workspace", "workspace-hack", + "zed-util", ] [[package]] @@ -4633,7 +4622,6 @@ dependencies = [ "anyhow", "bitflags 2.9.0", "client", - "collections", "command_palette_hooks", "dap", "dap_adapters", @@ -4676,9 +4664,10 @@ dependencies = [ "tree-sitter-json", "ui", "unindent", - "util", "workspace", "workspace-hack", + "zed-collections", + "zed-util", "zed_actions", "zlog", ] @@ -4698,11 +4687,11 @@ version = "0.1.0" dependencies = [ "anyhow", "futures 0.3.31", - "http_client", "schemars 1.0.1", "serde", "serde_json", "workspace-hack", + "zed-http-client", ] [[package]] @@ -4780,23 +4769,12 @@ dependencies = [ "syn 2.0.101", ] -[[package]] -name = "derive_refineable" -version = "0.1.0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", - "workspace-hack", -] - [[package]] name = "diagnostics" version = "0.1.0" dependencies = [ "anyhow", "client", - "collections", "component", "ctor", "editor", @@ -4816,9 +4794,10 @@ dependencies = [ "theme", "ui", "unindent", - "util", "workspace", "workspace-hack", + "zed-collections", + "zed-util", "zlog", ] @@ -4973,9 +4952,9 @@ dependencies = [ "serde", "serde_json", "settings", - "util", "workspace-hack", "zed", + "zed-util", "zlog", ] @@ -5124,7 +5103,6 @@ dependencies = [ "client", "gpui", "language", - "project", "workspace-hack", ] @@ -5168,7 +5146,6 @@ dependencies = [ "arrayvec", "clap", "cloud_llm_client", - "collections", "futures 0.3.31", "gpui", "hashbrown 0.15.3", @@ -5177,6 +5154,7 @@ dependencies = [ "language", "log", "ordered-float 2.10.1", + "postage", "pretty_assertions", "project", "regex", @@ -5187,8 +5165,9 @@ dependencies = [ "strum 0.27.1", "text", "tree-sitter", - "util", "workspace-hack", + "zed-collections", + "zed-util", "zlog", ] @@ -5202,7 +5181,6 @@ dependencies = [ "buffer_diff", "client", "clock", - "collections", "convert_case 0.8.0", "criterion", "ctor", @@ -5216,7 +5194,6 @@ dependencies = [ "fuzzy", "git", "gpui", - "http_client", "indoc", "itertools 0.14.0", "language", @@ -5242,7 +5219,6 @@ dependencies = [ "smallvec", "smol", "snippet", - "sum_tree", "task", "telemetry", "tempfile", @@ -5261,11 +5237,14 @@ dependencies = [ "unicode-segmentation", "unindent", "url", - "util", "uuid", "vim_mode_setting", "workspace", "workspace-hack", + "zed-collections", + "zed-http-client", + "zed-sum-tree", + "zed-util", "zed_actions", "zlog", ] @@ -5547,7 +5526,6 @@ dependencies = [ "clap", "client", "cloud_llm_client", - "collections", "debug_adapter_extension", "dirs 4.0.0", "dotenvy", @@ -5582,10 +5560,11 @@ dependencies = [ "terminal_view", "toml 0.8.20", "unindent", - "util", "uuid", "watch", "workspace-hack", + "zed-collections", + "zed-util", ] [[package]] @@ -5664,28 +5643,28 @@ dependencies = [ "async-compression", "async-tar", "async-trait", - "collections", "dap", "fs", "futures 0.3.31", "gpui", "heck 0.5.0", - "http_client", "language", "log", "lsp", "parking_lot", "pretty_assertions", - "semantic_version", "serde", "serde_json", "task", "toml 0.8.20", "url", - "util", "wasm-encoder 0.221.3", "wasmparser 0.221.3", "workspace-hack", + "zed-collections", + "zed-http-client", + "zed-semantic-version", + "zed-util", ] [[package]] @@ -5721,7 +5700,6 @@ dependencies = [ "async-tar", "async-trait", "client", - "collections", "criterion", "ctor", "dap", @@ -5729,7 +5707,6 @@ dependencies = [ "fs", "futures 0.3.31", "gpui", - "http_client", "language", "language_extension", "log", @@ -5743,7 +5720,6 @@ dependencies = [ "release_channel", "remote", "reqwest_client", - "semantic_version", "serde", "serde_json", "serde_json_lenient", @@ -5755,11 +5731,14 @@ dependencies = [ "theme_extension", "toml 0.8.20", "url", - "util", "wasmparser 0.221.3", "wasmtime", "wasmtime-wasi", "workspace-hack", + "zed-collections", + "zed-http-client", + "zed-semantic-version", + "zed-util", "zlog", ] @@ -5769,7 +5748,6 @@ version = "0.1.0" dependencies = [ "anyhow", "client", - "collections", "db", "editor", "extension", @@ -5783,7 +5761,6 @@ dependencies = [ "picker", "project", "release_channel", - "semantic_version", "serde", "settings", "smallvec", @@ -5791,10 +5768,12 @@ dependencies = [ "telemetry", "theme", "ui", - "util", "vim_mode_setting", "workspace", "workspace-hack", + "zed-collections", + "zed-semantic-version", + "zed-util", "zed_actions", ] @@ -5887,9 +5866,9 @@ dependencies = [ "system_specs", "ui", "urlencoding", - "util", "workspace", "workspace-hack", + "zed-util", "zed_actions", ] @@ -5908,7 +5887,6 @@ name = "file_finder" version = "0.1.0" dependencies = [ "anyhow", - "collections", "ctor", "editor", "file_icons", @@ -5928,9 +5906,10 @@ dependencies = [ "text", "theme", "ui", - "util", "workspace", "workspace-hack", + "zed-collections", + "zed-util", "zlog", ] @@ -5940,10 +5919,9 @@ version = "0.1.0" dependencies = [ "gpui", "serde", - "settings", "theme", - "util", "workspace-hack", + "zed-util", ] [[package]] @@ -6050,30 +6028,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" -[[package]] -name = "font-kit" -version = "0.14.1" -source = "git+https://github.com/zed-industries/font-kit?rev=5474cfad4b719a72ec8ed2cb7327b2b01fd10568#5474cfad4b719a72ec8ed2cb7327b2b01fd10568" -dependencies = [ - "bitflags 2.9.0", - "byteorder", - "core-foundation 0.10.0", - "core-graphics 0.24.0", - "core-text", - "dirs 5.0.1", - "dwrote", - "float-ord", - "freetype-sys", - "lazy_static", - "libc", - "log", - "pathfinder_geometry", - "pathfinder_simd", - "walkdir", - "winapi", - "yeslogic-fontconfig-sys", -] - [[package]] name = "font-types" version = "0.8.4" @@ -6210,7 +6164,6 @@ dependencies = [ "async-tar", "async-trait", "cocoa 0.26.0", - "collections", "fsevent", "futures 0.3.31", "git", @@ -6230,9 +6183,10 @@ dependencies = [ "tempfile", "text", "time", - "util", "windows 0.61.1", "workspace-hack", + "zed-collections", + "zed-util", ] [[package]] @@ -6269,6 +6223,7 @@ dependencies = [ "bitflags 2.9.0", "core-foundation 0.10.0", "fsevent-sys 3.1.0", + "log", "parking_lot", "tempfile", "workspace-hack", @@ -6450,8 +6405,8 @@ version = "0.1.0" dependencies = [ "gpui", "log", - "util", "workspace-hack", + "zed-util", ] [[package]] @@ -6789,12 +6744,10 @@ dependencies = [ "anyhow", "askpass", "async-trait", - "collections", "derive_more", "futures 0.3.31", "git2", "gpui", - "http_client", "log", "parking_lot", "pretty_assertions", @@ -6805,16 +6758,18 @@ dependencies = [ "serde", "serde_json", "smol", - "sum_tree", "tempfile", "text", "thiserror 2.0.12", "time", "unindent", "url", - "util", "uuid", "workspace-hack", + "zed-collections", + "zed-http-client", + "zed-sum-tree", + "zed-util", ] [[package]] @@ -6839,7 +6794,6 @@ dependencies = [ "futures 0.3.31", "git", "gpui", - "http_client", "indoc", "pretty_assertions", "regex", @@ -6847,8 +6801,9 @@ dependencies = [ "serde_json", "settings", "url", - "util", "workspace-hack", + "zed-http-client", + "zed-util", ] [[package]] @@ -6862,7 +6817,6 @@ dependencies = [ "call", "chrono", "cloud_llm_client", - "collections", "command_palette_hooks", "component", "ctor", @@ -6898,11 +6852,12 @@ dependencies = [ "time_format", "ui", "unindent", - "util", "watch", "windows 0.61.1", "workspace", "workspace-hack", + "zed-collections", + "zed-util", "zed_actions", "zeroize", "zlog", @@ -6941,9 +6896,9 @@ dependencies = [ [[package]] name = "glow" -version = "0.14.2" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51fa363f025f5c111e03f13eda21162faeacb6911fe8caa0c0349f9cf0c4483" +checksum = "c5e5ea60d70410161c8bf5da3fdfeaa1c72ed2c15f8bbb9d19fe3a4fad085f08" dependencies = [ "js-sys", "slotmap", @@ -6970,9 +6925,9 @@ dependencies = [ "tree-sitter-rust", "tree-sitter-typescript", "ui", - "util", "workspace", "workspace-hack", + "zed-util", ] [[package]] @@ -6992,13 +6947,13 @@ version = "0.1.0" dependencies = [ "anyhow", "futures 0.3.31", - "http_client", "schemars 1.0.1", "serde", "serde_json", "settings", "strum 0.27.1", "workspace-hack", + "zed-http-client", ] [[package]] @@ -7050,7 +7005,6 @@ dependencies = [ "calloop-wayland-source", "cbindgen", "cocoa 0.26.0", - "collections", "core-foundation 0.10.0", "core-foundation-sys", "core-graphics 0.24.0", @@ -7064,18 +7018,15 @@ dependencies = [ "etagere", "filedescriptor", "flume", - "font-kit", "foreign-types 0.5.0", "futures 0.3.31", - "gpui_macros", - "http_client", + "gpui-macros", "image", "inventory", "itertools 0.14.0", "libc", "log", "lyon", - "media", "metal", "naga", "num_cpus", @@ -7092,13 +7043,10 @@ dependencies = [ "profiling", "rand 0.9.1", "raw-window-handle", - "refineable", "reqwest_client", "resvg", - "scap", "schemars 1.0.1", "seahash", - "semantic_version", "serde", "serde_json", "slotmap", @@ -7106,13 +7054,10 @@ dependencies = [ "smol", "stacksafe", "strum 0.27.1", - "sum_tree", "taffy", "thiserror 2.0.12", "unicode-segmentation", "usvg", - "util", - "util_macros", "uuid", "waker-fn", "wayland-backend", @@ -7127,12 +7072,22 @@ dependencies = [ "workspace-hack", "x11-clipboard", "x11rb", - "xim", "xkbcommon", + "zed-collections", + "zed-font-kit", + "zed-http-client", + "zed-media", + "zed-refineable", + "zed-scap", + "zed-semantic-version", + "zed-sum-tree", + "zed-util", + "zed-util-macros", + "zed-xim", ] [[package]] -name = "gpui_macros" +name = "gpui-macros" version = "0.1.0" dependencies = [ "gpui", @@ -7150,8 +7105,8 @@ dependencies = [ "anyhow", "gpui", "tokio", - "util", "workspace-hack", + "zed-util", ] [[package]] @@ -7556,25 +7511,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" -[[package]] -name = "http_client" -version = "0.1.0" -dependencies = [ - "anyhow", - "bytes 1.10.1", - "derive_more", - "futures 0.3.31", - "http 1.3.1", - "http-body 1.0.1", - "log", - "parking_lot", - "reqwest 0.12.15 (git+https://github.com/zed-industries/reqwest.git?rev=951c770a32f1998d6e999cef3e59e0013e6c4415)", - "serde", - "serde_json", - "url", - "workspace-hack", -] - [[package]] name = "http_client_tls" version = "0.1.0" @@ -7968,9 +7904,9 @@ dependencies = [ "settings", "theme", "ui", - "util", "workspace", "workspace-hack", + "zed-util", ] [[package]] @@ -8089,10 +8025,10 @@ dependencies = [ "serde_json_lenient", "theme", "ui", - "util", - "util_macros", "workspace", "workspace-hack", + "zed-util", + "zed-util-macros", "zed_actions", ] @@ -8105,9 +8041,9 @@ dependencies = [ "gpui", "release_channel", "smol", - "util", "workspace", "workspace-hack", + "zed-util", ] [[package]] @@ -8389,6 +8325,28 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "json_schema_store" +version = "0.1.0" +dependencies = [ + "anyhow", + "dap", + "extension", + "gpui", + "language", + "paths", + "project", + "schemars 1.0.1", + "serde", + "serde_json", + "settings", + "snippet_provider", + "task", + "theme", + "workspace-hack", + "zed-util", +] + [[package]] name = "jsonschema" version = "0.30.0" @@ -8410,7 +8368,7 @@ dependencies = [ "referencing", "regex", "regex-syntax", - "reqwest 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.12.15", "serde", "serde_json", "uuid-simd", @@ -8467,7 +8425,6 @@ name = "keymap_editor" version = "0.1.0" dependencies = [ "anyhow", - "collections", "command_palette", "component", "db", @@ -8476,6 +8433,7 @@ dependencies = [ "fuzzy", "gpui", "itertools 0.14.0", + "json_schema_store", "language", "log", "menu", @@ -8493,10 +8451,11 @@ dependencies = [ "tree-sitter-rust", "ui", "ui_input", - "util", "vim", "workspace", "workspace-hack", + "zed-collections", + "zed-util", "zed_actions", ] @@ -8556,7 +8515,6 @@ dependencies = [ "anyhow", "async-trait", "clock", - "collections", "ctor", "diffy", "ec4rs", @@ -8565,7 +8523,6 @@ dependencies = [ "fuzzy", "globset", "gpui", - "http_client", "imara-diff", "indoc", "itertools 0.14.0", @@ -8586,7 +8543,6 @@ dependencies = [ "smol", "streaming-iterator", "strsim", - "sum_tree", "task", "text", "theme", @@ -8604,9 +8560,12 @@ dependencies = [ "tree-sitter-typescript", "unicase", "unindent", - "util", "watch", "workspace-hack", + "zed-collections", + "zed-http-client", + "zed-sum-tree", + "zed-util", "zlog", ] @@ -8616,7 +8575,6 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", - "collections", "extension", "fs", "futures 0.3.31", @@ -8627,8 +8585,9 @@ dependencies = [ "project", "serde", "serde_json", - "util", "workspace-hack", + "zed-collections", + "zed-util", ] [[package]] @@ -8641,10 +8600,8 @@ dependencies = [ "client", "cloud_api_types", "cloud_llm_client", - "collections", "futures 0.3.31", "gpui", - "http_client", "icons", "image", "log", @@ -8658,8 +8615,10 @@ dependencies = [ "smol", "telemetry_events", "thiserror 2.0.12", - "util", "workspace-hack", + "zed-collections", + "zed-http-client", + "zed-util", ] [[package]] @@ -8676,7 +8635,6 @@ dependencies = [ "chrono", "client", "cloud_llm_client", - "collections", "component", "convert_case 0.8.0", "copilot", @@ -8688,7 +8646,6 @@ dependencies = [ "google_ai", "gpui", "gpui_tokio", - "http_client", "language", "language_model", "lmstudio", @@ -8712,10 +8669,12 @@ dependencies = [ "tokio", "ui", "ui_input", - "util", "vercel", "workspace-hack", "x_ai", + "zed-collections", + "zed-http-client", + "zed-util", "zed_env_vars", ] @@ -8747,9 +8706,9 @@ dependencies = [ "project", "settings", "ui", - "util", "workspace", "workspace-hack", + "zed-util", ] [[package]] @@ -8758,7 +8717,6 @@ version = "0.1.0" dependencies = [ "anyhow", "client", - "collections", "command_palette_hooks", "copilot", "editor", @@ -8775,9 +8733,10 @@ dependencies = [ "theme", "tree-sitter", "ui", - "util", "workspace", "workspace-hack", + "zed-collections", + "zed-util", "zed_actions", "zlog", ] @@ -8792,18 +8751,15 @@ dependencies = [ "async-tar", "async-trait", "chrono", - "collections", - "dap", "futures 0.3.31", "gpui", - "http_client", "itertools 0.14.0", + "json_schema_store", "language", "log", "lsp", "node_runtime", "parking_lot", - "paths", "pet", "pet-conda", "pet-core", @@ -8816,17 +8772,13 @@ dependencies = [ "regex", "rope", "rust-embed", - "schemars 1.0.1", "serde", "serde_json", "serde_json_lenient", "settings", - "sha2", "shlex", "smol", - "snippet_provider", "task", - "tempfile", "text", "theme", "toml 0.8.20", @@ -8850,9 +8802,11 @@ dependencies = [ "tree-sitter-yaml", "unindent", "url", - "util", "workspace", "workspace-hack", + "zed-collections", + "zed-http-client", + "zed-util", ] [[package]] @@ -9034,9 +8988,9 @@ dependencies = [ "picker", "project", "ui", - "util", "workspace", "workspace-hack", + "zed-util", ] [[package]] @@ -9159,9 +9113,9 @@ dependencies = [ "prost 0.9.0", "prost-build 0.9.0", "prost-types 0.9.0", - "reqwest 0.12.15 (git+https://github.com/zed-industries/reqwest.git?rev=951c770a32f1998d6e999cef3e59e0013e6c4415)", "serde", "workspace-hack", + "zed-reqwest", ] [[package]] @@ -9171,7 +9125,6 @@ dependencies = [ "anyhow", "async-trait", "audio", - "collections", "core-foundation 0.10.0", "core-video", "coreaudio-rs 0.12.1", @@ -9190,7 +9143,6 @@ dependencies = [ "parking_lot", "postage", "rodio", - "scap", "serde", "serde_json", "serde_urlencoded", @@ -9200,8 +9152,10 @@ dependencies = [ "smallvec", "tokio-tungstenite 0.26.2", "ui", - "util", "workspace-hack", + "zed-collections", + "zed-scap", + "zed-util", ] [[package]] @@ -9221,11 +9175,11 @@ version = "0.1.0" dependencies = [ "anyhow", "futures 0.3.31", - "http_client", "schemars 1.0.1", "serde", "serde_json", "workspace-hack", + "zed-http-client", ] [[package]] @@ -9285,7 +9239,6 @@ version = "0.1.0" dependencies = [ "anyhow", "async-pipe", - "collections", "ctor", "futures 0.3.31", "gpui", @@ -9298,8 +9251,9 @@ dependencies = [ "serde", "serde_json", "smol", - "util", "workspace-hack", + "zed-collections", + "zed-util", "zlog", ] @@ -9422,7 +9376,6 @@ version = "0.1.0" dependencies = [ "assets", "base64 0.22.1", - "collections", "env_logger 0.11.8", "fs", "futures 0.3.31", @@ -9434,11 +9387,12 @@ dependencies = [ "node_runtime", "pulldown-cmark 0.12.2", "settings", - "sum_tree", "theme", "ui", - "util", "workspace-hack", + "zed-collections", + "zed-sum-tree", + "zed-util", ] [[package]] @@ -9447,7 +9401,6 @@ version = "0.1.0" dependencies = [ "anyhow", "async-recursion", - "collections", "editor", "fs", "gpui", @@ -9461,9 +9414,10 @@ dependencies = [ "settings", "theme", "ui", - "util", "workspace", "workspace-hack", + "zed-collections", + "zed-util", ] [[package]] @@ -9591,21 +9545,6 @@ dependencies = [ "warp", ] -[[package]] -name = "media" -version = "0.1.0" -dependencies = [ - "anyhow", - "bindgen 0.71.1", - "core-foundation 0.10.0", - "core-video", - "ctor", - "foreign-types 0.5.0", - "metal", - "objc", - "workspace-hack", -] - [[package]] name = "memchr" version = "2.7.4" @@ -9668,14 +9607,18 @@ name = "migrator" version = "0.1.0" dependencies = [ "anyhow", - "collections", "convert_case 0.8.0", "log", "pretty_assertions", + "serde_json", + "serde_json_lenient", + "settings", "streaming-iterator", "tree-sitter", "tree-sitter-json", + "unindent", "workspace-hack", + "zed-collections", ] [[package]] @@ -9821,12 +9764,12 @@ version = "0.1.0" dependencies = [ "anyhow", "futures 0.3.31", - "http_client", "schemars 1.0.1", "serde", "serde_json", "strum 0.27.1", "workspace-hack", + "zed-http-client", ] [[package]] @@ -9864,7 +9807,6 @@ dependencies = [ "anyhow", "buffer_diff", "clock", - "collections", "ctor", "gpui", "indoc", @@ -9880,12 +9822,13 @@ dependencies = [ "settings", "smallvec", "smol", - "sum_tree", "text", "theme", "tree-sitter", - "util", "workspace-hack", + "zed-collections", + "zed-sum-tree", + "zed-util", "zlog", ] @@ -9905,7 +9848,7 @@ dependencies = [ "bit-set 0.8.0", "bitflags 2.9.0", "cfg_aliases 0.2.1", - "codespan-reporting 0.12.0", + "codespan-reporting", "half", "hashbrown 0.15.3", "hexf-parse", @@ -10074,17 +10017,17 @@ dependencies = [ "async-tar", "async-trait", "futures 0.3.31", - "http_client", "log", "paths", "semver", "serde", "serde_json", "smol", - "util", "watch", "which 6.0.3", "workspace-hack", + "zed-http-client", + "zed-util", ] [[package]] @@ -10128,18 +10071,18 @@ dependencies = [ "anyhow", "channel", "client", - "collections", "component", "db", "gpui", "rpc", "settings", - "sum_tree", "time", "ui", - "util", "workspace", "workspace-hack", + "zed-collections", + "zed-sum-tree", + "zed-util", "zed_actions", ] @@ -10578,12 +10521,12 @@ version = "0.1.0" dependencies = [ "anyhow", "futures 0.3.31", - "http_client", "schemars 1.0.1", "serde", "serde_json", "settings", "workspace-hack", + "zed-http-client", ] [[package]] @@ -10614,10 +10557,11 @@ dependencies = [ "telemetry", "theme", "ui", - "util", + "ui_input", "vim_mode_setting", "workspace", "workspace-hack", + "zed-util", "zed_actions", "zlog", ] @@ -10686,7 +10630,6 @@ version = "0.1.0" dependencies = [ "anyhow", "futures 0.3.31", - "http_client", "log", "schemars 1.0.1", "serde", @@ -10694,6 +10637,7 @@ dependencies = [ "settings", "strum 0.27.1", "workspace-hack", + "zed-http-client", ] [[package]] @@ -10702,7 +10646,6 @@ version = "0.1.0" dependencies = [ "anyhow", "futures 0.3.31", - "http_client", "schemars 1.0.1", "serde", "serde_json", @@ -10710,6 +10653,7 @@ dependencies = [ "strum 0.27.1", "thiserror 2.0.12", "workspace-hack", + "zed-http-client", ] [[package]] @@ -10858,9 +10802,9 @@ dependencies = [ "tree-sitter-rust", "tree-sitter-typescript", "ui", - "util", "workspace", "workspace-hack", + "zed-util", "zed_actions", ] @@ -10869,7 +10813,6 @@ name = "outline_panel" version = "0.1.0" dependencies = [ "anyhow", - "collections", "db", "editor", "file_icons", @@ -10890,10 +10833,11 @@ dependencies = [ "smol", "theme", "ui", - "util", "workspace", "workspace-hack", "worktree", + "zed-collections", + "zed-util", "zed_actions", ] @@ -11063,8 +11007,8 @@ version = "0.1.0" dependencies = [ "dirs 4.0.0", "ignore", - "util", "workspace-hack", + "zed-util", ] [[package]] @@ -11157,16 +11101,6 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" -[[package]] -name = "perf" -version = "0.1.0" -dependencies = [ - "collections", - "serde", - "serde_json", - "workspace-hack", -] - [[package]] name = "pest" version = "2.8.0" @@ -11932,7 +11866,6 @@ name = "prettier" version = "0.1.0" dependencies = [ "anyhow", - "collections", "fs", "gpui", "language", @@ -11943,8 +11876,9 @@ dependencies = [ "paths", "serde", "serde_json", - "util", "workspace-hack", + "zed-collections", + "zed-util", ] [[package]] @@ -12071,12 +12005,12 @@ dependencies = [ "circular-buffer", "client", "clock", - "collections", "context_server", "dap", "dap_adapters", "extension", "fancy-regex 0.14.0", + "feature_flags", "fs", "futures 0.3.31", "fuzzy", @@ -12085,7 +12019,6 @@ dependencies = [ "git_hosting_providers", "globset", "gpui", - "http_client", "image", "indexmap 2.9.0", "itertools 0.14.0", @@ -12116,7 +12049,6 @@ dependencies = [ "smol", "snippet", "snippet_provider", - "sum_tree", "task", "tempfile", "terminal", @@ -12124,11 +12056,14 @@ dependencies = [ "toml 0.8.20", "unindent", "url", - "util", "watch", "which 6.0.3", "workspace-hack", "worktree", + "zed-collections", + "zed-http-client", + "zed-sum-tree", + "zed-util", "zeroize", "zlog", ] @@ -12139,8 +12074,8 @@ version = "0.1.0" dependencies = [ "anyhow", "client", - "collections", "command_palette_hooks", + "criterion", "db", "editor", "file_icons", @@ -12151,6 +12086,7 @@ dependencies = [ "menu", "pretty_assertions", "project", + "rayon", "schemars 1.0.1", "search", "serde", @@ -12160,10 +12096,11 @@ dependencies = [ "telemetry", "theme", "ui", - "util", "workspace", "workspace-hack", "worktree", + "zed-collections", + "zed-util", "zed_actions", ] @@ -12185,9 +12122,9 @@ dependencies = [ "serde_json", "settings", "theme", - "util", "workspace", "workspace-hack", + "zed-util", ] [[package]] @@ -12212,7 +12149,6 @@ dependencies = [ "anyhow", "assets", "chrono", - "collections", "fs", "futures 0.3.31", "fuzzy", @@ -12227,9 +12163,10 @@ dependencies = [ "serde", "serde_json", "text", - "util", "uuid", "workspace-hack", + "zed-collections", + "zed-util", ] [[package]] @@ -12343,12 +12280,12 @@ name = "proto" version = "0.1.0" dependencies = [ "anyhow", - "collections", "prost 0.9.0", "prost-build 0.9.0", "serde", "typed-path", "workspace-hack", + "zed-collections", ] [[package]] @@ -12832,10 +12769,10 @@ dependencies = [ "telemetry", "theme", "ui", - "util", "windows-registry 0.6.0", "workspace", "workspace-hack", + "zed-util", "zed_actions", ] @@ -12913,14 +12850,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "refineable" -version = "0.1.0" -dependencies = [ - "derive_refineable", - "workspace-hack", -] - [[package]] name = "regalloc2" version = "0.11.2" @@ -12986,11 +12915,9 @@ dependencies = [ "anyhow", "askpass", "async-trait", - "collections", "fs", "futures 0.3.31", "gpui", - "itertools 0.14.0", "log", "parking_lot", "paths", @@ -13005,9 +12932,10 @@ dependencies = [ "tempfile", "thiserror 2.0.12", "urlencoding", - "util", "which 6.0.3", "workspace-hack", + "zed-collections", + "zed-util", ] [[package]] @@ -13023,7 +12951,6 @@ dependencies = [ "clap", "client", "clock", - "collections", "crash-handler", "crashes", "dap", @@ -13041,7 +12968,7 @@ dependencies = [ "git_hosting_providers", "gpui", "gpui_tokio", - "http_client", + "json_schema_store", "language", "language_extension", "language_model", @@ -13069,10 +12996,12 @@ dependencies = [ "thiserror 2.0.12", "toml 0.8.20", "unindent", - "util", "watch", "workspace", "worktree", + "zed-collections", + "zed-http-client", + "zed-util", "zlog", ] @@ -13095,7 +13024,6 @@ dependencies = [ "async-tungstenite", "base64 0.22.1", "client", - "collections", "command_palette_hooks", "editor", "env_logger 0.11.8", @@ -13103,7 +13031,6 @@ dependencies = [ "file_icons", "futures 0.3.31", "gpui", - "http_client", "image", "indoc", "jupyter-protocol", @@ -13130,10 +13057,12 @@ dependencies = [ "tree-sitter-python", "tree-sitter-typescript", "ui", - "util", "uuid", "workspace", "workspace-hack", + "zed-collections", + "zed-http-client", + "zed-util", ] [[package]] @@ -13217,55 +13146,6 @@ dependencies = [ "windows-registry 0.4.0", ] -[[package]] -name = "reqwest" -version = "0.12.15" -source = "git+https://github.com/zed-industries/reqwest.git?rev=951c770a32f1998d6e999cef3e59e0013e6c4415#951c770a32f1998d6e999cef3e59e0013e6c4415" -dependencies = [ - "base64 0.22.1", - "bytes 1.10.1", - "encoding_rs", - "futures-core", - "futures-util", - "h2 0.4.9", - "http 1.3.1", - "http-body 1.0.1", - "http-body-util", - "hyper 1.6.0", - "hyper-rustls 0.27.5", - "hyper-util", - "ipnet", - "js-sys", - "log", - "mime", - "mime_guess", - "once_cell", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls 0.23.26", - "rustls-native-certs 0.8.1", - "rustls-pemfile 2.2.0", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 1.0.2", - "system-configuration 0.6.1", - "tokio", - "tokio-rustls 0.26.2", - "tokio-socks", - "tokio-util", - "tower 0.5.2", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "windows-registry 0.4.0", -] - [[package]] name = "reqwest_client" version = "0.1.0" @@ -13274,14 +13154,14 @@ dependencies = [ "bytes 1.10.1", "futures 0.3.31", "gpui", - "http_client", "http_client_tls", "log", "regex", - "reqwest 0.12.15 (git+https://github.com/zed-industries/reqwest.git?rev=951c770a32f1998d6e999cef3e59e0013e6c4415)", "serde", "tokio", "workspace-hack", + "zed-http-client", + "zed-reqwest", ] [[package]] @@ -13329,8 +13209,8 @@ dependencies = [ "pulldown-cmark 0.12.2", "theme", "ui", - "util", "workspace-hack", + "zed-util", ] [[package]] @@ -13423,10 +13303,10 @@ dependencies = [ "rand 0.9.1", "rayon", "smallvec", - "sum_tree", "unicode-segmentation", - "util", "workspace-hack", + "zed-sum-tree", + "zed-util", "zlog", ] @@ -13444,7 +13324,6 @@ dependencies = [ "async-tungstenite", "base64 0.22.1", "chrono", - "collections", "futures 0.3.31", "gpui", "parking_lot", @@ -13456,8 +13335,9 @@ dependencies = [ "sha2", "strum 0.27.1", "tracing", - "util", "workspace-hack", + "zed-collections", + "zed-util", "zlog", "zstd", ] @@ -13493,7 +13373,6 @@ name = "rules_library" version = "0.1.0" dependencies = [ "anyhow", - "collections", "editor", "gpui", "language", @@ -13509,9 +13388,10 @@ dependencies = [ "theme", "title_bar", "ui", - "util", "workspace", "workspace-hack", + "zed-collections", + "zed-util", "zed_actions", ] @@ -13577,9 +13457,9 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.37.1" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faa7de2ba56ac291bd90c6b9bece784a52ae1411f9506544b3eae36dd2356d50" +checksum = "c8975fc98059f365204d635119cf9c5a60ae67b841ed49b5422a9a7e56cdfac0" dependencies = [ "arrayvec", "borsh", @@ -13883,27 +13763,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scap" -version = "0.0.8" -source = "git+https://github.com/zed-industries/scap?rev=808aa5c45b41e8f44729d02e38fd00a2fe2722e7#808aa5c45b41e8f44729d02e38fd00a2fe2722e7" -dependencies = [ - "anyhow", - "cocoa 0.25.0", - "core-graphics-helmer-fork", - "log", - "objc", - "rand 0.8.5", - "screencapturekit", - "screencapturekit-sys", - "sysinfo", - "tao-core-video-sys", - "windows 0.61.1", - "windows-capture", - "x11", - "xcb", -] - [[package]] name = "schannel" version = "0.1.27" @@ -14163,7 +14022,6 @@ dependencies = [ "anyhow", "bitflags 2.9.0", "client", - "collections", "editor", "futures 0.3.31", "gpui", @@ -14178,9 +14036,10 @@ dependencies = [ "theme", "ui", "unindent", - "util", "workspace", "workspace-hack", + "zed-collections", + "zed-util", "zed_actions", ] @@ -14240,15 +14099,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749" -[[package]] -name = "semantic_version" -version = "0.1.0" -dependencies = [ - "anyhow", - "serde", - "workspace-hack", -] - [[package]] name = "semver" version = "1.0.26" @@ -14431,9 +14281,9 @@ dependencies = [ "db", "gpui", "serde_json", - "util", "uuid", "workspace-hack", + "zed-util", ] [[package]] @@ -14441,7 +14291,7 @@ name = "settings" version = "0.1.0" dependencies = [ "anyhow", - "collections", + "derive_more", "ec4rs", "fs", "futures 0.3.31", @@ -14466,8 +14316,9 @@ dependencies = [ "tree-sitter", "tree-sitter-json", "unindent", - "util", "workspace-hack", + "zed-collections", + "zed-util", "zlog", ] @@ -14514,11 +14365,13 @@ dependencies = [ "feature_flags", "fs", "futures 0.3.31", + "fuzzy", "gpui", "language", "menu", "node_runtime", "paths", + "pretty_assertions", "project", "serde", "session", @@ -14526,9 +14379,11 @@ dependencies = [ "strum 0.27.1", "theme", "ui", - "util", + "ui_input", "workspace", "workspace-hack", + "zed-util", + "zed_actions", "zlog", ] @@ -14551,9 +14406,9 @@ checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -14799,7 +14654,6 @@ name = "snippet_provider" version = "0.1.0" dependencies = [ "anyhow", - "collections", "extension", "fs", "futures 0.3.31", @@ -14809,10 +14663,12 @@ dependencies = [ "paths", "schemars 1.0.1", "serde", + "serde_json", "serde_json_lenient", "snippet", - "util", "workspace-hack", + "zed-collections", + "zed-util", ] [[package]] @@ -14828,9 +14684,9 @@ dependencies = [ "picker", "settings", "ui", - "util", "workspace", "workspace-hack", + "zed-util", ] [[package]] @@ -14901,7 +14757,6 @@ name = "sqlez" version = "0.1.0" dependencies = [ "anyhow", - "collections", "futures 0.3.31", "indoc", "libsqlite3-sys", @@ -14910,9 +14765,10 @@ dependencies = [ "smol", "sqlformat", "thread_local", - "util", "uuid", "workspace-hack", + "zed-collections", + "zed-util", ] [[package]] @@ -15245,8 +15101,8 @@ dependencies = [ "ordered-float 2.10.1", "rand 0.9.1", "rope", - "util", "workspace-hack", + "zed-util", ] [[package]] @@ -15356,32 +15212,17 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" -[[package]] -name = "sum_tree" -version = "0.1.0" -dependencies = [ - "arrayvec", - "ctor", - "log", - "rand 0.9.1", - "rayon", - "workspace-hack", - "zlog", -] - [[package]] name = "supermaven" version = "0.1.0" dependencies = [ "anyhow", "client", - "collections", "edit_prediction", "editor", "env_logger 0.11.8", "futures 0.3.31", "gpui", - "http_client", "language", "log", "postage", @@ -15395,8 +15236,10 @@ dependencies = [ "theme", "ui", "unicode-segmentation", - "util", "workspace-hack", + "zed-collections", + "zed-http-client", + "zed-util", ] [[package]] @@ -15405,13 +15248,13 @@ version = "0.1.0" dependencies = [ "anyhow", "futures 0.3.31", - "http_client", "paths", "serde", "serde_json", "smol", - "util", "workspace-hack", + "zed-http-client", + "zed-util", ] [[package]] @@ -15878,7 +15721,6 @@ name = "tab_switcher" version = "0.1.0" dependencies = [ "anyhow", - "collections", "ctor", "editor", "fuzzy", @@ -15894,9 +15736,10 @@ dependencies = [ "smol", "theme", "ui", - "util", "workspace", "workspace-hack", + "zed-collections", + "zed-util", "zlog", ] @@ -15959,7 +15802,6 @@ name = "task" version = "0.1.0" dependencies = [ "anyhow", - "collections", "futures 0.3.31", "gpui", "hex", @@ -15973,8 +15815,9 @@ dependencies = [ "serde_json_lenient", "sha2", "shellexpand 2.1.2", - "util", "workspace-hack", + "zed-collections", + "zed-util", "zed_actions", ] @@ -15983,7 +15826,6 @@ name = "tasks_ui" version = "0.1.0" dependencies = [ "anyhow", - "collections", "editor", "file_icons", "fuzzy", @@ -15999,9 +15841,10 @@ dependencies = [ "tree-sitter-rust", "tree-sitter-typescript", "ui", - "util", "workspace", "workspace-hack", + "zed-collections", + "zed-util", "zed_actions", ] @@ -16020,10 +15863,10 @@ dependencies = [ name = "telemetry_events" version = "0.1.0" dependencies = [ - "semantic_version", "serde", "serde_json", "workspace-hack", + "zed-semantic-version", ] [[package]] @@ -16065,7 +15908,6 @@ version = "0.1.0" dependencies = [ "alacritty_terminal", "anyhow", - "collections", "futures 0.3.31", "gpui", "itertools 0.14.0", @@ -16084,9 +15926,10 @@ dependencies = [ "thiserror 2.0.12", "url", "urlencoding", - "util", "windows 0.61.1", "workspace-hack", + "zed-collections", + "zed-util", ] [[package]] @@ -16108,7 +15951,6 @@ dependencies = [ "async-recursion", "breadcrumbs", "client", - "collections", "db", "dirs 4.0.0", "editor", @@ -16132,9 +15974,10 @@ dependencies = [ "terminal", "theme", "ui", - "util", "workspace", "workspace-hack", + "zed-collections", + "zed-util", "zed_actions", ] @@ -16144,10 +15987,8 @@ version = "0.1.0" dependencies = [ "anyhow", "clock", - "collections", "ctor", "gpui", - "http_client", "log", "parking_lot", "postage", @@ -16155,9 +15996,11 @@ dependencies = [ "regex", "rope", "smallvec", - "sum_tree", - "util", "workspace-hack", + "zed-collections", + "zed-http-client", + "zed-sum-tree", + "zed-util", "zlog", ] @@ -16166,7 +16009,6 @@ name = "theme" version = "0.1.0" dependencies = [ "anyhow", - "collections", "derive_more", "fs", "futures 0.3.31", @@ -16174,7 +16016,6 @@ dependencies = [ "log", "palette", "parking_lot", - "refineable", "schemars 1.0.1", "serde", "serde_json", @@ -16182,9 +16023,11 @@ dependencies = [ "settings", "strum 0.27.1", "thiserror 2.0.12", - "util", "uuid", "workspace-hack", + "zed-collections", + "zed-refineable", + "zed-util", ] [[package]] @@ -16205,7 +16048,6 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", - "collections", "gpui", "indexmap 2.9.0", "log", @@ -16218,6 +16060,7 @@ dependencies = [ "theme", "vscode_theme", "workspace-hack", + "zed-collections", ] [[package]] @@ -16234,9 +16077,9 @@ dependencies = [ "telemetry", "theme", "ui", - "util", "workspace", "workspace-hack", + "zed-util", "zed_actions", ] @@ -16452,10 +16295,8 @@ dependencies = [ "chrono", "client", "cloud_llm_client", - "collections", "db", "gpui", - "http_client", "notifications", "pretty_assertions", "project", @@ -16470,10 +16311,12 @@ dependencies = [ "theme", "tree-sitter-md", "ui", - "util", "windows 0.61.1", "workspace", "workspace-hack", + "zed-collections", + "zed-http-client", + "zed-util", "zed_actions", ] @@ -16690,9 +16533,9 @@ dependencies = [ "picker", "project", "ui", - "util", "workspace", "workspace-hack", + "zed-util", ] [[package]] @@ -16879,9 +16722,9 @@ dependencies = [ [[package]] name = "tree-sitter" -version = "0.25.6" +version = "0.25.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cf18d43cbf0bfca51f657132cc616a5097edc4424d538bae6fa60142eaf9f0" +checksum = "78f873475d258561b06f1c595d93308a7ed124d9977cb26b148c2084a4a3cc87" dependencies = [ "cc", "regex", @@ -17054,8 +16897,9 @@ dependencies = [ [[package]] name = "tree-sitter-python" -version = "0.23.6" -source = "git+https://github.com/zed-industries/tree-sitter-python?rev=218fcbf3fda3d029225f3dec005cb497d111b35e#218fcbf3fda3d029225f3dec005cb497d111b35e" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bf85fd39652e740bf60f46f4cda9492c3a9ad75880575bf14960f775cb74a1c" dependencies = [ "cc", "tree-sitter-language", @@ -17267,7 +17111,7 @@ dependencies = [ "component", "documented", "gpui", - "gpui_macros", + "gpui-macros", "icons", "itertools 0.14.0", "menu", @@ -17279,9 +17123,9 @@ dependencies = [ "strum 0.27.1", "theme", "ui_macros", - "util", "windows 0.61.1", "workspace-hack", + "zed-util", ] [[package]] @@ -17290,7 +17134,10 @@ version = "0.1.0" dependencies = [ "component", "editor", + "fuzzy", "gpui", + "menu", + "picker", "settings", "theme", "ui", @@ -17403,12 +17250,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" -[[package]] -name = "unicode-width" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" - [[package]] name = "unicode-width" version = "0.2.0" @@ -17508,55 +17349,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" -[[package]] -name = "util" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-fs", - "async_zip", - "collections", - "command-fds", - "dirs 4.0.0", - "dunce", - "futures 0.3.31", - "futures-lite 1.13.0", - "git2", - "globset", - "indoc", - "itertools 0.14.0", - "libc", - "log", - "nix 0.29.0", - "pretty_assertions", - "rand 0.9.1", - "regex", - "rust-embed", - "schemars 1.0.1", - "serde", - "serde_json", - "serde_json_lenient", - "shlex", - "smol", - "take-until", - "tempfile", - "tendril", - "unicase", - "util_macros", - "walkdir", - "workspace-hack", -] - -[[package]] -name = "util_macros" -version = "0.1.0" -dependencies = [ - "perf", - "quote", - "syn 2.0.101", - "workspace-hack", -] - [[package]] name = "uuid" version = "1.16.0" @@ -17669,7 +17461,6 @@ dependencies = [ "assets", "async-compat", "async-trait", - "collections", "command_palette", "command_palette_hooks", "db", @@ -17683,10 +17474,10 @@ dependencies = [ "language", "log", "lsp", + "menu", "multi_buffer", "nvim-rs", "parking_lot", - "perf", "picker", "project", "project_panel", @@ -17702,11 +17493,13 @@ dependencies = [ "theme", "tokio", "ui", - "util", - "util_macros", "vim_mode_setting", "workspace", "workspace-hack", + "zed-collections", + "zed-perf", + "zed-util", + "zed-util-macros", "zed_actions", ] @@ -18449,10 +18242,10 @@ version = "0.1.0" dependencies = [ "anyhow", "cloud_llm_client", - "collections", "gpui", "serde", "workspace-hack", + "zed-collections", ] [[package]] @@ -18464,12 +18257,12 @@ dependencies = [ "cloud_llm_client", "futures 0.3.31", "gpui", - "http_client", "language_model", "serde", "serde_json", "web_search", "workspace-hack", + "zed-http-client", ] [[package]] @@ -19585,14 +19378,12 @@ dependencies = [ "call", "client", "clock", - "collections", "component", "dap", "db", "fs", "futures 0.3.31", "gpui", - "http_client", "itertools 0.14.0", "language", "log", @@ -19616,10 +19407,12 @@ dependencies = [ "tempfile", "theme", "ui", - "util", "uuid", "windows 0.61.1", "workspace-hack", + "zed-collections", + "zed-http-client", + "zed-util", "zed_actions", "zlog", ] @@ -19660,12 +19453,11 @@ dependencies = [ "cipher", "clap", "clap_builder", - "codespan-reporting 0.12.0", + "codespan-reporting", "concurrent-queue", "core-foundation 0.9.4", "core-foundation-sys", "cranelift-codegen", - "crc32fast", "crossbeam-channel", "crossbeam-epoch", "crossbeam-utils", @@ -19823,14 +19615,12 @@ version = "0.1.0" dependencies = [ "anyhow", "clock", - "collections", "fs", "futures 0.3.31", "fuzzy", "git", "git2", "gpui", - "http_client", "ignore", "language", "log", @@ -19845,10 +19635,12 @@ dependencies = [ "settings", "smallvec", "smol", - "sum_tree", "text", - "util", "workspace-hack", + "zed-collections", + "zed-http-client", + "zed-sum-tree", + "zed-util", "zlog", ] @@ -19950,23 +19742,10 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" -[[package]] -name = "xim" -version = "0.4.0" -source = "git+https://github.com/zed-industries/xim-rs?rev=c0a70c1bd2ce197364216e5e818a2cb3adb99a8d#c0a70c1bd2ce197364216e5e818a2cb3adb99a8d" -dependencies = [ - "ahash 0.8.11", - "hashbrown 0.14.5", - "log", - "x11rb", - "xim-ctext", - "xim-parser", -] - [[package]] name = "xim-ctext" version = "0.3.0" -source = "git+https://github.com/zed-industries/xim-rs?rev=c0a70c1bd2ce197364216e5e818a2cb3adb99a8d#c0a70c1bd2ce197364216e5e818a2cb3adb99a8d" +source = "git+https://github.com/zed-industries/xim-rs.git?rev=16f35a2c881b815a2b6cdfd6687988e84f8447d8#16f35a2c881b815a2b6cdfd6687988e84f8447d8" dependencies = [ "encoding_rs", ] @@ -19974,7 +19753,7 @@ dependencies = [ [[package]] name = "xim-parser" version = "0.2.1" -source = "git+https://github.com/zed-industries/xim-rs?rev=c0a70c1bd2ce197364216e5e818a2cb3adb99a8d#c0a70c1bd2ce197364216e5e818a2cb3adb99a8d" +source = "git+https://github.com/zed-industries/xim-rs.git?rev=16f35a2c881b815a2b6cdfd6687988e84f8447d8#16f35a2c881b815a2b6cdfd6687988e84f8447d8" dependencies = [ "bitflags 2.9.0", ] @@ -20028,6 +19807,9 @@ dependencies = [ "cargo_metadata", "cargo_toml", "clap", + "indoc", + "toml 0.8.20", + "toml_edit", "workspace-hack", ] @@ -20180,7 +19962,7 @@ dependencies = [ [[package]] name = "zed" -version = "0.207.0" +version = "0.208.0" dependencies = [ "acp_tools", "activity_indicator", @@ -20204,7 +19986,6 @@ dependencies = [ "cli", "client", "collab_ui", - "collections", "command_palette", "component", "copilot", @@ -20233,12 +20014,12 @@ dependencies = [ "go_to_line", "gpui", "gpui_tokio", - "http_client", "image_viewer", "inspector_ui", "install_cli", "itertools 0.14.0", "journal", + "json_schema_store", "keymap_editor", "language", "language_extension", @@ -20276,7 +20057,6 @@ dependencies = [ "release_channel", "remote", "repl", - "reqwest 0.12.15 (git+https://github.com/zed-industries/reqwest.git?rev=951c770a32f1998d6e999cef3e59e0013e6c4415)", "reqwest_client", "rope", "search", @@ -20313,7 +20093,6 @@ dependencies = [ "ui_prompt", "url", "urlencoding", - "util", "uuid", "vim", "vim_mode_setting", @@ -20324,6 +20103,10 @@ dependencies = [ "winresource", "workspace", "workspace-hack", + "zed-collections", + "zed-http-client", + "zed-reqwest", + "zed-util", "zed_actions", "zed_env_vars", "zeta", @@ -20333,6 +20116,262 @@ dependencies = [ "zlog_settings", ] +[[package]] +name = "zed-collections" +version = "0.1.0" +dependencies = [ + "indexmap 2.9.0", + "rustc-hash 2.1.1", + "workspace-hack", +] + +[[package]] +name = "zed-derive-refineable" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", + "workspace-hack", +] + +[[package]] +name = "zed-font-kit" +version = "0.14.1-zed" +source = "git+https://github.com/zed-industries/font-kit?rev=110523127440aefb11ce0cf280ae7c5071337ec5#110523127440aefb11ce0cf280ae7c5071337ec5" +dependencies = [ + "bitflags 2.9.0", + "byteorder", + "core-foundation 0.10.0", + "core-graphics 0.24.0", + "core-text", + "dirs 5.0.1", + "dwrote", + "float-ord", + "freetype-sys", + "lazy_static", + "libc", + "log", + "pathfinder_geometry", + "pathfinder_simd", + "walkdir", + "winapi", + "yeslogic-fontconfig-sys", +] + +[[package]] +name = "zed-http-client" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-compression", + "async-fs", + "async-tar", + "bytes 1.10.1", + "derive_more", + "futures 0.3.31", + "http 1.3.1", + "http-body 1.0.1", + "log", + "parking_lot", + "serde", + "serde_json", + "sha2", + "tempfile", + "url", + "workspace-hack", + "zed-reqwest", + "zed-util", +] + +[[package]] +name = "zed-media" +version = "0.1.0" +dependencies = [ + "anyhow", + "bindgen 0.71.1", + "core-foundation 0.10.0", + "core-video", + "ctor", + "foreign-types 0.5.0", + "metal", + "objc", + "workspace-hack", +] + +[[package]] +name = "zed-perf" +version = "0.1.0" +dependencies = [ + "serde", + "serde_json", + "workspace-hack", + "zed-collections", +] + +[[package]] +name = "zed-refineable" +version = "0.1.0" +dependencies = [ + "workspace-hack", + "zed-derive-refineable", +] + +[[package]] +name = "zed-reqwest" +version = "0.12.15-zed" +source = "git+https://github.com/zed-industries/reqwest.git?rev=c15662463bda39148ba154100dd44d3fba5873a4#c15662463bda39148ba154100dd44d3fba5873a4" +dependencies = [ + "base64 0.22.1", + "bytes 1.10.1", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.4.9", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "hyper 1.6.0", + "hyper-rustls 0.27.5", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "mime_guess", + "once_cell", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls 0.23.26", + "rustls-native-certs 0.8.1", + "rustls-pemfile 2.2.0", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.2", + "system-configuration 0.6.1", + "tokio", + "tokio-rustls 0.26.2", + "tokio-socks", + "tokio-util", + "tower 0.5.2", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "windows-registry 0.4.0", +] + +[[package]] +name = "zed-scap" +version = "0.0.8-zed" +source = "git+https://github.com/zed-industries/scap?rev=4afea48c3b002197176fb19cd0f9b180dd36eaac#4afea48c3b002197176fb19cd0f9b180dd36eaac" +dependencies = [ + "anyhow", + "cocoa 0.25.0", + "core-graphics-helmer-fork", + "log", + "objc", + "rand 0.8.5", + "screencapturekit", + "screencapturekit-sys", + "sysinfo", + "tao-core-video-sys", + "windows 0.61.1", + "windows-capture", + "x11", + "xcb", +] + +[[package]] +name = "zed-semantic-version" +version = "0.1.0" +dependencies = [ + "anyhow", + "serde", + "workspace-hack", +] + +[[package]] +name = "zed-sum-tree" +version = "0.1.0" +dependencies = [ + "arrayvec", + "ctor", + "log", + "rand 0.9.1", + "rayon", + "workspace-hack", + "zlog", +] + +[[package]] +name = "zed-util" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-fs", + "async_zip", + "command-fds", + "dirs 4.0.0", + "dunce", + "futures 0.3.31", + "futures-lite 1.13.0", + "git2", + "globset", + "indoc", + "itertools 0.14.0", + "libc", + "log", + "nix 0.29.0", + "pretty_assertions", + "rand 0.9.1", + "regex", + "rust-embed", + "schemars 1.0.1", + "serde", + "serde_json", + "serde_json_lenient", + "shlex", + "smol", + "take-until", + "tempfile", + "tendril", + "unicase", + "walkdir", + "which 6.0.3", + "workspace-hack", + "zed-collections", + "zed-util-macros", +] + +[[package]] +name = "zed-util-macros" +version = "0.1.0" +dependencies = [ + "quote", + "syn 2.0.101", + "workspace-hack", + "zed-perf", +] + +[[package]] +name = "zed-xim" +version = "0.4.0-zed" +source = "git+https://github.com/zed-industries/xim-rs.git?rev=16f35a2c881b815a2b6cdfd6687988e84f8447d8#16f35a2c881b815a2b6cdfd6687988e84f8447d8" +dependencies = [ + "ahash 0.8.11", + "hashbrown 0.14.5", + "log", + "x11rb", + "xim-ctext", + "xim-parser", +] + [[package]] name = "zed_actions" version = "0.1.0" @@ -20372,6 +20411,17 @@ dependencies = [ "wit-bindgen 0.41.0", ] +[[package]] +name = "zed_extension_api" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0729d50b4ca0a7e28e590bbe32e3ca0194d97ef654961451a424c661a366fca0" +dependencies = [ + "serde", + "serde_json", + "wit-bindgen 0.41.0", +] + [[package]] name = "zed_glsl" version = "0.1.0" @@ -20381,9 +20431,9 @@ dependencies = [ [[package]] name = "zed_html" -version = "0.2.2" +version = "0.2.3" dependencies = [ - "zed_extension_api 0.1.0", + "zed_extension_api 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -20393,21 +20443,6 @@ dependencies = [ "zed_extension_api 0.1.0", ] -[[package]] -name = "zed_ruff" -version = "0.1.1" -dependencies = [ - "zed_extension_api 0.1.0", -] - -[[package]] -name = "zed_snippets" -version = "0.0.6" -dependencies = [ - "serde_json", - "zed_extension_api 0.1.0", -] - [[package]] name = "zed_test_extension" version = "0.1.0" @@ -20560,7 +20595,6 @@ dependencies = [ "clock", "cloud_api_types", "cloud_llm_client", - "collections", "command_palette_hooks", "copilot", "ctor", @@ -20571,7 +20605,6 @@ dependencies = [ "fs", "futures 0.3.31", "gpui", - "http_client", "indoc", "itertools 0.14.0", "language", @@ -20597,11 +20630,13 @@ dependencies = [ "tree-sitter-go", "tree-sitter-rust", "ui", - "util", "uuid", "workspace", "workspace-hack", "worktree", + "zed-collections", + "zed-http-client", + "zed-util", "zed_actions", "zlog", ] @@ -20632,11 +20667,11 @@ dependencies = [ "serde_json", "settings", "thiserror 2.0.12", - "util", "uuid", "workspace", "workspace-hack", "worktree", + "zed-util", ] [[package]] @@ -20647,7 +20682,6 @@ dependencies = [ "clap", "client", "cloud_llm_client", - "collections", "edit_prediction_context", "editor", "futures 0.3.31", @@ -20663,9 +20697,10 @@ dependencies = [ "text", "ui", "ui_input", - "util", "workspace", "workspace-hack", + "zed-collections", + "zed-util", "zeta2", "zlog", ] @@ -20691,7 +20726,9 @@ dependencies = [ "language_model", "language_models", "languages", + "log", "node_runtime", + "ordered-float 2.10.1", "paths", "project", "prompt_store", @@ -20703,11 +20740,12 @@ dependencies = [ "shellexpand 2.1.2", "smol", "terminal_view", - "util", "watch", "workspace-hack", + "zed-util", "zeta", "zeta2", + "zlog", ] [[package]] @@ -20751,20 +20789,20 @@ version = "0.1.0" dependencies = [ "anyhow", "chrono", - "collections", "log", "tempfile", "workspace-hack", + "zed-collections", ] [[package]] name = "zlog_settings" version = "0.1.0" dependencies = [ - "collections", "gpui", "settings", "workspace-hack", + "zed-collections", "zlog", ] diff --git a/Cargo.toml b/Cargo.toml index be6699edb6d19efac2cb981cbd75efd64580629e..8bcde70b55f4bbc99fdafaddc057f571efa5abce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -91,6 +91,7 @@ members = [ "crates/inspector_ui", "crates/install_cli", "crates/journal", + "crates/json_schema_store", "crates/keymap_editor", "crates/language", "crates/language_extension", @@ -211,9 +212,7 @@ members = [ "extensions/glsl", "extensions/html", "extensions/proto", - "extensions/ruff", "extensions/slash-commands-example", - "extensions/snippets", "extensions/test-extension", # @@ -274,7 +273,7 @@ cloud_llm_client = { path = "crates/cloud_llm_client" } cloud_zeta2_prompt = { path = "crates/cloud_zeta2_prompt" } collab = { path = "crates/collab" } collab_ui = { path = "crates/collab_ui" } -collections = { path = "crates/collections" } +collections = { path = "crates/collections", package = "zed-collections", version = "0.1.0" } command_palette = { path = "crates/command_palette" } command_palette_hooks = { path = "crates/command_palette_hooks" } component = { path = "crates/component" } @@ -290,6 +289,7 @@ debug_adapter_extension = { path = "crates/debug_adapter_extension" } debugger_tools = { path = "crates/debugger_tools" } debugger_ui = { path = "crates/debugger_ui" } deepseek = { path = "crates/deepseek" } +derive_refineable = { path = "crates/refineable/derive_refineable", package = "zed-derive-refineable", version = "0.1.0" } diagnostics = { path = "crates/diagnostics" } editor = { path = "crates/editor" } extension = { path = "crates/extension" } @@ -308,10 +308,10 @@ git_ui = { path = "crates/git_ui" } go_to_line = { path = "crates/go_to_line" } google_ai = { path = "crates/google_ai" } gpui = { path = "crates/gpui", default-features = false } -gpui_macros = { path = "crates/gpui_macros" } +gpui_macros = { path = "crates/gpui_macros", package = "gpui-macros", version = "0.1.0" } gpui_tokio = { path = "crates/gpui_tokio" } html_to_markdown = { path = "crates/html_to_markdown" } -http_client = { path = "crates/http_client" } +http_client = { path = "crates/http_client", package = "zed-http-client", version = "0.1.0" } http_client_tls = { path = "crates/http_client_tls" } icons = { path = "crates/icons" } image_viewer = { path = "crates/image_viewer" } @@ -322,6 +322,7 @@ zeta2_tools = { path = "crates/zeta2_tools" } inspector_ui = { path = "crates/inspector_ui" } install_cli = { path = "crates/install_cli" } journal = { path = "crates/journal" } +json_schema_store = { path = "crates/json_schema_store" } keymap_editor = { path = "crates/keymap_editor" } language = { path = "crates/language" } language_extension = { path = "crates/language_extension" } @@ -339,7 +340,7 @@ lsp = { path = "crates/lsp" } markdown = { path = "crates/markdown" } markdown_preview = { path = "crates/markdown_preview" } svg_preview = { path = "crates/svg_preview" } -media = { path = "crates/media" } +media = { path = "crates/media", package = "zed-media", version = "0.1.0" } menu = { path = "crates/menu" } migrator = { path = "crates/migrator" } mistral = { path = "crates/mistral" } @@ -356,7 +357,7 @@ outline = { path = "crates/outline" } outline_panel = { path = "crates/outline_panel" } panel = { path = "crates/panel" } paths = { path = "crates/paths" } -perf = { path = "tooling/perf" } +perf = { path = "tooling/perf", package = "zed-perf", version = "0.1.0" } picker = { path = "crates/picker" } plugin = { path = "crates/plugin" } plugin_macros = { path = "crates/plugin_macros" } @@ -368,7 +369,7 @@ project_symbols = { path = "crates/project_symbols" } prompt_store = { path = "crates/prompt_store" } proto = { path = "crates/proto" } recent_projects = { path = "crates/recent_projects" } -refineable = { path = "crates/refineable" } +refineable = { path = "crates/refineable", package = "zed-refineable", version = "0.1.0" } release_channel = { path = "crates/release_channel" } scheduler = { path = "crates/scheduler" } remote = { path = "crates/remote" } @@ -381,9 +382,10 @@ rope = { path = "crates/rope" } rpc = { path = "crates/rpc" } rules_library = { path = "crates/rules_library" } search = { path = "crates/search" } -semantic_version = { path = "crates/semantic_version" } +semantic_version = { path = "crates/semantic_version", package = "zed-semantic-version", version = "0.1.0" } session = { path = "crates/session" } settings = { path = "crates/settings" } +settings_macros = { path = "crates/settings_macros" } settings_ui = { path = "crates/settings_ui" } snippet = { path = "crates/snippet" } snippet_provider = { path = "crates/snippet_provider" } @@ -393,7 +395,7 @@ sqlez_macros = { path = "crates/sqlez_macros" } story = { path = "crates/story" } storybook = { path = "crates/storybook" } streaming_diff = { path = "crates/streaming_diff" } -sum_tree = { path = "crates/sum_tree" } +sum_tree = { path = "crates/sum_tree", package = "zed-sum-tree", version = "0.1.0" } supermaven = { path = "crates/supermaven" } supermaven_api = { path = "crates/supermaven_api" } system_specs = { path = "crates/system_specs" } @@ -416,8 +418,8 @@ ui = { path = "crates/ui" } ui_input = { path = "crates/ui_input" } ui_macros = { path = "crates/ui_macros" } ui_prompt = { path = "crates/ui_prompt" } -util = { path = "crates/util" } -util_macros = { path = "crates/util_macros" } +util = { path = "crates/util", package = "zed-util", version = "0.1.0" } +util_macros = { path = "crates/util_macros", package = "zed-util-macros", version = "0.1.0" } vercel = { path = "crates/vercel" } vim = { path = "crates/vim" } vim_mode_setting = { path = "crates/vim_mode_setting" } @@ -471,9 +473,9 @@ backtrace = "0.3" base64 = "0.22" bincode = "1.2.1" bitflags = "2.6.0" -blade-graphics = { git = "https://github.com/kvark/blade", rev = "bfa594ea697d4b6326ea29f747525c85ecf933b9" } -blade-macros = { git = "https://github.com/kvark/blade", rev = "bfa594ea697d4b6326ea29f747525c85ecf933b9" } -blade-util = { git = "https://github.com/kvark/blade", rev = "bfa594ea697d4b6326ea29f747525c85ecf933b9" } +blade-graphics = { version = "0.7.0" } +blade-macros = { version = "0.3.0" } +blade-util = { version = "0.3.0" } blake3 = "1.5.3" bytes = "1.0" cargo_metadata = "0.19" @@ -547,6 +549,7 @@ nanoid = "0.4" nbformat = { git = "https://github.com/ConradIrwin/runtimed", rev = "7130c804216b6914355d15d0b91ea91f6babd734" } nix = "0.29" num-format = "0.4.4" +num-traits = "0.2" objc = "0.2" objc2-foundation = { version = "0.3", default-features = false, features = [ "NSArray", @@ -603,7 +606,8 @@ rand = "0.9" rayon = "1.8" ref-cast = "1.0.24" regex = "1.5" -reqwest = { git = "https://github.com/zed-industries/reqwest.git", rev = "951c770a32f1998d6e999cef3e59e0013e6c4415", default-features = false, features = [ +# WARNING: If you change this, you must also publish a new version of zed-reqwest to crates.io +reqwest = { git = "https://github.com/zed-industries/reqwest.git", rev = "c15662463bda39148ba154100dd44d3fba5873a4", default-features = false, features = [ "charset", "http2", "macos-system-configuration", @@ -611,17 +615,17 @@ reqwest = { git = "https://github.com/zed-industries/reqwest.git", rev = "951c77 "rustls-tls-native-roots", "socks", "stream", -] } +], package = "zed-reqwest", version = "0.12.15-zed" } rsa = "0.9.6" runtimelib = { git = "https://github.com/ConradIrwin/runtimed", rev = "7130c804216b6914355d15d0b91ea91f6babd734", default-features = false, features = [ "async-dispatcher-runtime", ] } rust-embed = { version = "8.4", features = ["include-exclude"] } -rustc-demangle = "0.1.23" rustc-hash = "2.1.0" rustls = { version = "0.23.26" } rustls-platform-verifier = "0.5.0" -scap = { git = "https://github.com/zed-industries/scap", rev = "808aa5c45b41e8f44729d02e38fd00a2fe2722e7", default-features = false } +# WARNING: If you change this, you must also publish a new version of zed-scap to crates.io +scap = { git = "https://github.com/zed-industries/scap", rev = "4afea48c3b002197176fb19cd0f9b180dd36eaac", default-features = false, package = "zed-scap", version = "0.0.8-zed" } schemars = { version = "1.0", features = ["indexmap2"] } semver = "1.0" serde = { version = "1.0.221", features = ["derive", "rc"] } @@ -647,7 +651,7 @@ streaming-iterator = "0.1" strsim = "0.11" strum = { version = "0.27.0", features = ["derive"] } subtle = "2.5.0" -syn = { version = "2.0.101", features = ["full", "extra-traits"] } +syn = { version = "2.0.101", features = ["full", "extra-traits", "visit-mut"] } sys-locale = "0.3.1" sysinfo = "0.31.0" take-until = "0.2.0" @@ -665,8 +669,9 @@ tiny_http = "0.8" tokio = { version = "1" } tokio-tungstenite = { version = "0.26", features = ["__rustls-tls"] } toml = "0.8" +toml_edit = { version = "0.22", default-features = false, features = ["display", "parse", "serde"] } tower-http = "0.4.4" -tree-sitter = { version = "0.25.6", features = ["wasm"] } +tree-sitter = { version = "0.25.10", features = ["wasm"] } tree-sitter-bash = "0.25.0" tree-sitter-c = "0.23" tree-sitter-cpp = { git = "https://github.com/tree-sitter/tree-sitter-cpp", rev = "5cb9b693cfd7bfacab1d9ff4acac1a4150700609" } @@ -683,7 +688,7 @@ tree-sitter-html = "0.23" tree-sitter-jsdoc = "0.23" tree-sitter-json = "0.24" tree-sitter-md = { git = "https://github.com/tree-sitter-grammars/tree-sitter-markdown", rev = "9a23c1a96c0513d8fc6520972beedd419a973539" } -tree-sitter-python = { git = "https://github.com/zed-industries/tree-sitter-python", rev = "218fcbf3fda3d029225f3dec005cb497d111b35e" } +tree-sitter-python = "0.25" tree-sitter-regex = "0.24" tree-sitter-ruby = "0.23" tree-sitter-rust = "0.24" @@ -799,7 +804,7 @@ wasmtime = { opt-level = 3 } activity_indicator = { codegen-units = 1 } assets = { codegen-units = 1 } breadcrumbs = { codegen-units = 1 } -collections = { codegen-units = 1 } +zed-collections = { codegen-units = 1 } command_palette = { codegen-units = 1 } command_palette_hooks = { codegen-units = 1 } extension_cli = { codegen-units = 1 } @@ -810,6 +815,7 @@ image_viewer = { codegen-units = 1 } edit_prediction_button = { codegen-units = 1 } install_cli = { codegen-units = 1 } journal = { codegen-units = 1 } +json_schema_store = { codegen-units = 1 } lmstudio = { codegen-units = 1 } menu = { codegen-units = 1 } notifications = { codegen-units = 1 } @@ -818,11 +824,11 @@ outline = { codegen-units = 1 } paths = { codegen-units = 1 } prettier = { codegen-units = 1 } project_symbols = { codegen-units = 1 } -refineable = { codegen-units = 1 } +zed-refineable = { codegen-units = 1 } release_channel = { codegen-units = 1 } reqwest_client = { codegen-units = 1 } rich_text = { codegen-units = 1 } -semantic_version = { codegen-units = 1 } +zed-semantic-version = { codegen-units = 1 } session = { codegen-units = 1 } snippet = { codegen-units = 1 } snippets_ui = { codegen-units = 1 } @@ -861,6 +867,7 @@ todo = "deny" declare_interior_mutable_const = "deny" redundant_clone = "deny" +disallowed_methods = "deny" # We currently do not restrict any style rules # as it slows down shipping code to Zed. diff --git a/GEMINI.md b/GEMINI.md new file mode 120000 index 0000000000000000000000000000000000000000..8a63b64bdb0afcda986ba715cb39849ac574e096 --- /dev/null +++ b/GEMINI.md @@ -0,0 +1 @@ +.rules \ No newline at end of file diff --git a/assets/icons/paperclip.svg b/assets/icons/paperclip.svg new file mode 100644 index 0000000000000000000000000000000000000000..7a864103c013823096b523f3e0f56db2d7e76009 --- /dev/null +++ b/assets/icons/paperclip.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json index ceb25c1835172068a809c6e5492d000cde7da5fe..b59a823816ffd81fa78deb1c8fac84f8c654d169 100644 --- a/assets/keymaps/default-linux.json +++ b/assets/keymaps/default-linux.json @@ -30,7 +30,8 @@ "ctrl-+": ["zed::IncreaseBufferFontSize", { "persist": false }], "ctrl--": ["zed::DecreaseBufferFontSize", { "persist": false }], "ctrl-0": ["zed::ResetBufferFontSize", { "persist": false }], - "ctrl-,": "zed::OpenSettings", + "ctrl-,": "zed::OpenSettingsEditor", + "ctrl-alt-,": "zed::OpenSettings", "ctrl-q": "zed::Quit", "f4": "debugger::Start", "shift-f5": "debugger::Stop", @@ -250,7 +251,7 @@ "alt-enter": "agent::ContinueWithBurnMode", "ctrl-y": "agent::AllowOnce", "ctrl-alt-y": "agent::AllowAlways", - "ctrl-d": "agent::RejectOnce" + "ctrl-alt-z": "agent::RejectOnce" } }, { @@ -369,7 +370,15 @@ "bindings": { "new": "rules_library::NewRule", "ctrl-n": "rules_library::NewRule", - "ctrl-shift-s": "rules_library::ToggleDefaultRule" + "ctrl-shift-s": "rules_library::ToggleDefaultRule", + "ctrl-w": "workspace::CloseWindow" + } + }, + { + "context": "SettingsWindow", + "use_key_equivalents": true, + "bindings": { + "ctrl-w": "workspace::CloseWindow" } }, { diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json index 4cf25c3b71047b7eb19791cee91f062d5720fe61..dc4d74f84d040576c1c02378472618c105f7aac8 100644 --- a/assets/keymaps/default-macos.json +++ b/assets/keymaps/default-macos.json @@ -39,7 +39,8 @@ "cmd-+": ["zed::IncreaseBufferFontSize", { "persist": false }], "cmd--": ["zed::DecreaseBufferFontSize", { "persist": false }], "cmd-0": ["zed::ResetBufferFontSize", { "persist": false }], - "cmd-,": "zed::OpenSettings", + "cmd-,": "zed::OpenSettingsEditor", + "cmd-alt-,": "zed::OpenSettings", "cmd-q": "zed::Quit", "cmd-h": "zed::Hide", "alt-cmd-h": "zed::HideOthers", @@ -289,7 +290,7 @@ "alt-enter": "agent::ContinueWithBurnMode", "cmd-y": "agent::AllowOnce", "cmd-alt-y": "agent::AllowAlways", - "cmd-d": "agent::RejectOnce" + "cmd-alt-z": "agent::RejectOnce" } }, { @@ -430,6 +431,13 @@ "cmd-w": "workspace::CloseWindow" } }, + { + "context": "SettingsWindow", + "use_key_equivalents": true, + "bindings": { + "cmd-w": "workspace::CloseWindow" + } + }, { "context": "BufferSearchBar", "use_key_equivalents": true, diff --git a/assets/keymaps/default-windows.json b/assets/keymaps/default-windows.json index fe9efe0f720e7c76bd25878426b9601e6910099b..3e513d8603f611b92abaff654ee5676d0be85ad8 100644 --- a/assets/keymaps/default-windows.json +++ b/assets/keymaps/default-windows.json @@ -29,7 +29,8 @@ "ctrl-shift-=": ["zed::IncreaseBufferFontSize", { "persist": false }], "ctrl--": ["zed::DecreaseBufferFontSize", { "persist": false }], "ctrl-0": ["zed::ResetBufferFontSize", { "persist": false }], - "ctrl-,": "zed::OpenSettings", + "ctrl-,": "zed::OpenSettingsEditor", + "ctrl-alt-,": "zed::OpenSettings", "ctrl-q": "zed::Quit", "f4": "debugger::Start", "shift-f5": "debugger::Stop", @@ -251,7 +252,7 @@ "alt-enter": "agent::ContinueWithBurnMode", "ctrl-y": "agent::AllowOnce", "ctrl-alt-y": "agent::AllowAlways", - "ctrl-d": "agent::RejectOnce" + "ctrl-alt-z": "agent::RejectOnce" } }, { @@ -345,7 +346,7 @@ } }, { - "context": "AcpThread > Editor", + "context": "AcpThread > Editor && !use_modifier_to_send", "use_key_equivalents": true, "bindings": { "enter": "agent::Chat", @@ -355,6 +356,17 @@ "shift-tab": "agent::CycleModeSelector" } }, + { + "context": "AcpThread > Editor && use_modifier_to_send", + "use_key_equivalents": true, + "bindings": { + "ctrl-enter": "agent::Chat", + "ctrl-shift-r": "agent::OpenAgentDiff", + "ctrl-shift-y": "agent::KeepAll", + "ctrl-shift-n": "agent::RejectAll", + "shift-tab": "agent::CycleModeSelector" + } + }, { "context": "ThreadHistory", "use_key_equivalents": true, @@ -367,7 +379,15 @@ "use_key_equivalents": true, "bindings": { "ctrl-n": "rules_library::NewRule", - "ctrl-shift-s": "rules_library::ToggleDefaultRule" + "ctrl-shift-s": "rules_library::ToggleDefaultRule", + "ctrl-w": "workspace::CloseWindow" + } + }, + { + "context": "SettingsWindow", + "use_key_equivalents": true, + "bindings": { + "ctrl-w": "workspace::CloseWindow" } }, { diff --git a/assets/keymaps/macos/emacs.json b/assets/keymaps/macos/emacs.json index 0f936ba2f968abe0759e4bb294271a5e5f501848..78e2235965335ac2914355fc0c51abe38d390897 100755 --- a/assets/keymaps/macos/emacs.json +++ b/assets/keymaps/macos/emacs.json @@ -4,6 +4,7 @@ // from the command palette. [ { + "context": "!GitPanel", "bindings": { "ctrl-g": "menu::Cancel" } diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json index 5d98fd2a1c833f3003edf623b997cb321656fdbe..4d296667ff572a644d4f6b37e1704c0b250a652c 100644 --- a/assets/keymaps/vim.json +++ b/assets/keymaps/vim.json @@ -240,6 +240,7 @@ "delete": "vim::DeleteRight", "g shift-j": "vim::JoinLinesNoWhitespace", "y": "vim::PushYank", + "shift-y": "vim::YankLine", "x": "vim::DeleteRight", "shift-x": "vim::DeleteLeft", "ctrl-a": "vim::Increment", @@ -883,10 +884,12 @@ "/": "project_panel::NewSearchInDirectory", "d": "project_panel::NewDirectory", "enter": "project_panel::OpenPermanent", - "escape": "project_panel::ToggleFocus", + "escape": "vim::ToggleProjectPanelFocus", "h": "project_panel::CollapseSelectedEntry", - "j": "menu::SelectNext", - "k": "menu::SelectPrevious", + "j": "vim::MenuSelectNext", + "k": "vim::MenuSelectPrevious", + "down": "vim::MenuSelectNext", + "up": "vim::MenuSelectPrevious", "l": "project_panel::ExpandSelectedEntry", "shift-d": "project_panel::Delete", "shift-r": "project_panel::Rename", @@ -905,7 +908,22 @@ "{": "project_panel::SelectPrevDirectory", "shift-g": "menu::SelectLast", "g g": "menu::SelectFirst", - "-": "project_panel::SelectParent" + "-": "project_panel::SelectParent", + "ctrl-u": "project_panel::ScrollUp", + "ctrl-d": "project_panel::ScrollDown", + "z t": "project_panel::ScrollCursorTop", + "z z": "project_panel::ScrollCursorCenter", + "z b": "project_panel::ScrollCursorBottom", + "0": ["vim::Number", 0], + "1": ["vim::Number", 1], + "2": ["vim::Number", 2], + "3": ["vim::Number", 3], + "4": ["vim::Number", 4], + "5": ["vim::Number", 5], + "6": ["vim::Number", 6], + "7": ["vim::Number", 7], + "8": ["vim::Number", 8], + "9": ["vim::Number", 9] } }, { diff --git a/assets/prompts/content_prompt.hbs b/assets/prompts/content_prompt.hbs index e601e6dc63376af54e6e1a9bfa53cdbd57190e22..6db53ff48ff251b90f9120398852a9160ca41755 100644 --- a/assets/prompts/content_prompt.hbs +++ b/assets/prompts/content_prompt.hbs @@ -29,7 +29,9 @@ Generate {{content_type}} based on the following prompt: Match the indentation in the original file in the inserted {{content_type}}, don't include any indentation on blank lines. -Immediately start with the following format with no remarks: +Return ONLY the {{content_type}} to insert. Do NOT include any XML tags like , , or any surrounding markup from the input. + +Respond with a code block containing the {{content_type}} to insert. Replace \{{INSERTED_CODE}} with your actual {{content_type}}: ``` \{{INSERTED_CODE}} @@ -66,7 +68,9 @@ Only make changes that are necessary to fulfill the prompt, leave everything els Start at the indentation level in the original file in the rewritten {{content_type}}. Don't stop until you've rewritten the entire section, even if you have no more changes to make, always write out the whole section with no unnecessary elisions. -Immediately start with the following format with no remarks: +Return ONLY the rewritten {{content_type}}. Do NOT include any XML tags like , , or any surrounding markup from the input. + +Respond with a code block containing the rewritten {{content_type}}. Replace \{{REWRITTEN_CODE}} with your actual rewritten {{content_type}}: ``` \{{REWRITTEN_CODE}} diff --git a/assets/settings/default.json b/assets/settings/default.json index 298bce824569c85416e7557231357ab016473a22..a77ce82f00b746adf9efa3886474f4d0fe53847c 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -1,5 +1,7 @@ { - "project_name": null, + /// The displayed name of this project. If not set or empty, the root directory name + /// will be displayed. + "project_name": "", // The name of the Zed theme to use for the UI. // // `mode` is one of: @@ -72,8 +74,10 @@ "ui_font_weight": 400, // The default font size for text in the UI "ui_font_size": 16, - // The default font size for text in the agent panel. Falls back to the UI font size if unset. - "agent_font_size": null, + // The default font size for agent responses in the agent panel. Falls back to the UI font size if unset. + "agent_ui_font_size": null, + // The default font size for user messages in the agent panel. Falls back to the buffer font size if unset. + "agent_buffer_font_size": 12, // How much to fade out unused code. "unnecessary_code_fade": 0.3, // Active pane styling settings. @@ -1227,6 +1231,10 @@ // 2. Hide the gutter // "git_gutter": "hide" "git_gutter": "tracked_files", + /// Sets the debounce threshold (in milliseconds) after which changes are reflected in the git gutter. + /// + /// Default: null + "gutter_debounce": null, // Control whether the git blame information is shown inline, // in the currently focused line. "inline_blame": { @@ -1242,6 +1250,9 @@ // The minimum column number to show the inline blame information at "min_column": 0 }, + "blame": { + "show_avatar": true + }, // Control which information is shown in the branch picker. "branch_picker": { "show_author_name": true @@ -1322,6 +1333,8 @@ }, // Status bar-related settings. "status_bar": { + // Whether to show the status bar. + "experimental.show": true, // Whether to show the active language button in the status bar. "active_language_button": true, // Whether to show the cursor position button in the status bar. @@ -1557,6 +1570,14 @@ "auto_install_extensions": { "html": true }, + // The capabilities granted to extensions. + // + // This list can be customized to restrict what extensions are able to do. + "granted_extension_capabilities": [ + { "kind": "process:exec", "command": "*", "args": ["**"] }, + { "kind": "download_file", "host": "*", "path": ["**"] }, + { "kind": "npm:install", "package": "*" } + ], // Controls how completions are processed for this language. "completions": { // Controls how words are completed. @@ -1855,21 +1876,19 @@ // 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 - // - // // Use regular Prettier json configuration. - // // If Prettier is allowed, Zed will use this for its Prettier instance for any applicable file, if - // // the project has no other Prettier installed. - // "plugins": [], - // - // // Use regular Prettier json configuration. - // // If Prettier is allowed, Zed will use this for its Prettier instance for any applicable file, if - // // the project has no other Prettier installed. + // Enables or disables formatting with Prettier for any given language. + "allowed": false, + // Forces Prettier integration to use a specific parser name when formatting files with the language. + "plugins": [], + // Default Prettier options, in the format as in package.json section for Prettier. + // If project installs Prettier via its package.json, these options will be ignored. // "trailingComma": "es5", // "tabWidth": 4, // "semi": false, // "singleQuote": true + // Forces Prettier integration to use a specific parser name when formatting files with the language + // when set to a non-empty string. + "parser": "" }, // Settings for auto-closing of JSX tags. "jsx_tag_auto_close": { @@ -2019,7 +2038,7 @@ // Examples: // "profiles": { // "Presenting": { - // "agent_font_size": 20.0, + // "agent_ui_font_size": 20.0, // "buffer_font_size": 20.0, // "theme": "One Light", // "ui_font_size": 20.0 diff --git a/assets/sounds/guest_joined_call.wav b/assets/sounds/guest_joined_call.wav new file mode 100644 index 0000000000000000000000000000000000000000..336a6ca754b09c408f63c411193157a20c14bb78 Binary files /dev/null and b/assets/sounds/guest_joined_call.wav differ diff --git a/assets/themes/ayu/ayu.json b/assets/themes/ayu/ayu.json index f71048caafba7156b53fd8637ca35c715ad300f2..7c84c603bda7fd7590067ec9f566f3582ba6aefd 100644 --- a/assets/themes/ayu/ayu.json +++ b/assets/themes/ayu/ayu.json @@ -192,7 +192,7 @@ "font_weight": null }, "comment": { - "color": "#abb5be8c", + "color": "#5c6773ff", "font_style": null, "font_weight": null }, @@ -239,7 +239,7 @@ "hint": { "color": "#628b80ff", "font_style": null, - "font_weight": 700 + "font_weight": null }, "keyword": { "color": "#ff8f3fff", @@ -583,7 +583,7 @@ "font_weight": null }, "comment": { - "color": "#787b8099", + "color": "#abb0b6ff", "font_style": null, "font_weight": null }, @@ -630,7 +630,7 @@ "hint": { "color": "#8ca7c2ff", "font_style": null, - "font_weight": 700 + "font_weight": null }, "keyword": { "color": "#fa8d3eff", @@ -974,7 +974,7 @@ "font_weight": null }, "comment": { - "color": "#b8cfe680", + "color": "#5c6773ff", "font_style": null, "font_weight": null }, @@ -1021,7 +1021,7 @@ "hint": { "color": "#7399a3ff", "font_style": null, - "font_weight": 700 + "font_weight": null }, "keyword": { "color": "#ffad65ff", diff --git a/assets/themes/gruvbox/gruvbox.json b/assets/themes/gruvbox/gruvbox.json index fc11cac55f638349778c88869dcb217c89111022..4e6f8334b269e3e5090b0f91d995834906c09083 100644 --- a/assets/themes/gruvbox/gruvbox.json +++ b/assets/themes/gruvbox/gruvbox.json @@ -248,7 +248,7 @@ "hint": { "color": "#8c957dff", "font_style": null, - "font_weight": 700 + "font_weight": null }, "keyword": { "color": "#fb4833ff", @@ -653,7 +653,7 @@ "hint": { "color": "#8c957dff", "font_style": null, - "font_weight": 700 + "font_weight": null }, "keyword": { "color": "#fb4833ff", @@ -1058,7 +1058,7 @@ "hint": { "color": "#8c957dff", "font_style": null, - "font_weight": 700 + "font_weight": null }, "keyword": { "color": "#fb4833ff", @@ -1463,7 +1463,7 @@ "hint": { "color": "#677562ff", "font_style": null, - "font_weight": 700 + "font_weight": null }, "keyword": { "color": "#9d0006ff", @@ -1868,7 +1868,7 @@ "hint": { "color": "#677562ff", "font_style": null, - "font_weight": 700 + "font_weight": null }, "keyword": { "color": "#9d0006ff", @@ -2273,7 +2273,7 @@ "hint": { "color": "#677562ff", "font_style": null, - "font_weight": 700 + "font_weight": null }, "keyword": { "color": "#9d0006ff", diff --git a/assets/themes/one/one.json b/assets/themes/one/one.json index 7cc8c96a23f32aab69596722188e3c5ec87aba08..6849cd05dc70752216789ae04e81fad232f7b14b 100644 --- a/assets/themes/one/one.json +++ b/assets/themes/one/one.json @@ -244,7 +244,7 @@ "hint": { "color": "#788ca6ff", "font_style": null, - "font_weight": 700 + "font_weight": null }, "keyword": { "color": "#b477cfff", @@ -643,7 +643,7 @@ "hint": { "color": "#7274a7ff", "font_style": null, - "font_weight": 700 + "font_weight": null }, "keyword": { "color": "#a449abff", diff --git a/clippy.toml b/clippy.toml index e606ad4c79b5cfe289b1f8460b1f46715103fe1b..57f6f59385a4885730015c3d09f040a5f340d379 100644 --- a/clippy.toml +++ b/clippy.toml @@ -5,3 +5,14 @@ ignore-interior-mutability = [ # and Hash impls do not use fields with interior mutability. "agent::context::AgentContextKey" ] +disallowed-methods = [ + { path = "std::process::Command::spawn", reason = "Spawning `std::process::Command` can block the current thread for an unknown duration", replacement = "smol::process::Command::spawn" }, + { path = "std::process::Command::output", reason = "Spawning `std::process::Command` can block the current thread for an unknown duration", replacement = "smol::process::Command::output" }, + { path = "std::process::Command::status", reason = "Spawning `std::process::Command` can block the current thread for an unknown duration", replacement = "smol::process::Command::status" }, +] +disallowed-types = [ + # { path = "std::collections::HashMap", replacement = "collections::HashMap" }, + # { path = "std::collections::HashSet", replacement = "collections::HashSet" }, + # { path = "indexmap::IndexSet", replacement = "collections::IndexSet" }, + # { path = "indexmap::IndexMap", replacement = "collections::IndexMap" }, +] diff --git a/crates/acp_thread/src/acp_thread.rs b/crates/acp_thread/src/acp_thread.rs index 185eed3cd7a89c8c0dad17979e34c7ffc5684e08..61486a475c4601a9d9201d3a6920c63566a1ba36 100644 --- a/crates/acp_thread/src/acp_thread.rs +++ b/crates/acp_thread/src/acp_thread.rs @@ -3,6 +3,7 @@ mod diff; mod mention; mod terminal; +use ::terminal::terminal_settings::TerminalSettings; use agent_settings::AgentSettings; use collections::HashSet; pub use connection::*; @@ -11,7 +12,7 @@ use language::language_settings::FormatOnSave; pub use mention::*; use project::lsp_store::{FormatTrigger, LspFormatTarget}; use serde::{Deserialize, Serialize}; -use settings::Settings as _; +use settings::{Settings as _, SettingsLocation}; use task::{Shell, ShellBuilder}; pub use terminal::*; @@ -34,7 +35,7 @@ use std::rc::Rc; use std::time::{Duration, Instant}; use std::{fmt::Display, mem, path::PathBuf, sync::Arc}; use ui::App; -use util::{ResultExt, get_default_system_shell}; +use util::{ResultExt, get_default_system_shell_preferring_bash}; use uuid::Uuid; #[derive(Debug)] @@ -573,7 +574,7 @@ impl ToolCallContent { ))), acp::ToolCallContent::Diff { diff } => Ok(Self::Diff(cx.new(|cx| { Diff::finalized( - diff.path.to_string_lossy().to_string(), + diff.path.to_string_lossy().into_owned(), diff.old_text, diff.new_text, language_registry, @@ -787,6 +788,8 @@ pub struct AcpThread { prompt_capabilities: acp::PromptCapabilities, _observe_prompt_capabilities: Task>, terminals: HashMap>, + pending_terminal_output: HashMap>>, + pending_terminal_exit: HashMap, } #[derive(Debug)] @@ -809,6 +812,126 @@ pub enum AcpThreadEvent { impl EventEmitter for AcpThread {} +#[derive(Debug, Clone)] +pub enum TerminalProviderEvent { + Created { + terminal_id: acp::TerminalId, + label: String, + cwd: Option, + output_byte_limit: Option, + terminal: Entity<::terminal::Terminal>, + }, + Output { + terminal_id: acp::TerminalId, + data: Vec, + }, + TitleChanged { + terminal_id: acp::TerminalId, + title: String, + }, + Exit { + terminal_id: acp::TerminalId, + status: acp::TerminalExitStatus, + }, +} + +#[derive(Debug, Clone)] +pub enum TerminalProviderCommand { + WriteInput { + terminal_id: acp::TerminalId, + bytes: Vec, + }, + Resize { + terminal_id: acp::TerminalId, + cols: u16, + rows: u16, + }, + Close { + terminal_id: acp::TerminalId, + }, +} + +impl AcpThread { + pub fn on_terminal_provider_event( + &mut self, + event: TerminalProviderEvent, + cx: &mut Context, + ) { + match event { + TerminalProviderEvent::Created { + terminal_id, + label, + cwd, + output_byte_limit, + terminal, + } => { + let entity = self.register_terminal_created( + terminal_id.clone(), + label, + cwd, + output_byte_limit, + terminal, + cx, + ); + + if let Some(mut chunks) = self.pending_terminal_output.remove(&terminal_id) { + for data in chunks.drain(..) { + entity.update(cx, |term, cx| { + term.inner().update(cx, |inner, cx| { + inner.write_output(&data, cx); + }) + }); + } + } + + if let Some(_status) = self.pending_terminal_exit.remove(&terminal_id) { + entity.update(cx, |_term, cx| { + cx.notify(); + }); + } + + cx.notify(); + } + TerminalProviderEvent::Output { terminal_id, data } => { + if let Some(entity) = self.terminals.get(&terminal_id) { + entity.update(cx, |term, cx| { + term.inner().update(cx, |inner, cx| { + inner.write_output(&data, cx); + }) + }); + } else { + self.pending_terminal_output + .entry(terminal_id) + .or_default() + .push(data); + } + } + TerminalProviderEvent::TitleChanged { terminal_id, title } => { + if let Some(entity) = self.terminals.get(&terminal_id) { + entity.update(cx, |term, cx| { + term.inner().update(cx, |inner, cx| { + inner.breadcrumb_text = title; + cx.emit(::terminal::Event::BreadcrumbsChanged); + }) + }); + } + } + TerminalProviderEvent::Exit { + terminal_id, + status, + } => { + if let Some(entity) = self.terminals.get(&terminal_id) { + entity.update(cx, |_term, cx| { + cx.notify(); + }); + } else { + self.pending_terminal_exit.insert(terminal_id, status); + } + } + } + } +} + #[derive(PartialEq, Eq, Debug)] pub enum ThreadStatus { Idle, @@ -886,6 +1009,8 @@ impl AcpThread { prompt_capabilities, _observe_prompt_capabilities: task, terminals: HashMap::default(), + pending_terminal_output: HashMap::default(), + pending_terminal_exit: HashMap::default(), } } @@ -1961,16 +2086,24 @@ impl AcpThread { ) -> Task>> { let env = match &cwd { Some(dir) => self.project.update(cx, |project, cx| { - project.directory_environment(dir.as_path().into(), cx) + let worktree = project.find_worktree(dir.as_path(), cx); + let shell = TerminalSettings::get( + worktree.as_ref().map(|(worktree, path)| SettingsLocation { + worktree_id: worktree.read(cx).id(), + path: &path, + }), + cx, + ) + .shell + .clone(); + project.directory_environment(&shell, dir.as_path().into(), cx) }), None => Task::ready(None).shared(), }; - let env = cx.spawn(async move |_, _| { let mut env = env.await.unwrap_or_default(); - if cfg!(unix) { - env.insert("PAGER".into(), "cat".into()); - } + // Disables paging for `git` and hopefully other commands + env.insert("PAGER".into(), "".into()); for var in extra_env { env.insert(var.name, var.value); } @@ -1985,24 +2118,22 @@ impl AcpThread { let terminal_id = terminal_id.clone(); async move |_this, cx| { let env = env.await; - let (command, args) = ShellBuilder::new( - project - .update(cx, |project, cx| { - project - .remote_client() - .and_then(|r| r.read(cx).default_system_shell()) - })? - .as_deref(), - &Shell::Program(get_default_system_shell()), - ) - .redirect_stdin_to_dev_null() - .build(Some(command), &args); + let shell = project + .update(cx, |project, cx| { + project + .remote_client() + .and_then(|r| r.read(cx).default_system_shell()) + })? + .unwrap_or_else(|| get_default_system_shell_preferring_bash()); + let (task_command, task_args) = ShellBuilder::new(&Shell::Program(shell)) + .redirect_stdin_to_dev_null() + .build(Some(command.clone()), &args); let terminal = project .update(cx, |project, cx| { project.create_terminal_task( task::SpawnInTerminal { - command: Some(command.clone()), - args: args.clone(), + command: Some(task_command), + args: task_args, cwd: cwd.clone(), env, ..Default::default() @@ -2079,6 +2210,32 @@ impl AcpThread { pub fn emit_load_error(&mut self, error: LoadError, cx: &mut Context) { cx.emit(AcpThreadEvent::LoadError(error)); } + + pub fn register_terminal_created( + &mut self, + terminal_id: acp::TerminalId, + command_label: String, + working_dir: Option, + output_byte_limit: Option, + terminal: Entity<::terminal::Terminal>, + cx: &mut Context, + ) -> Entity { + let language_registry = self.project.read(cx).languages().clone(); + + let entity = cx.new(|cx| { + Terminal::new( + terminal_id.clone(), + &command_label, + working_dir.clone(), + output_byte_limit.map(|l| l as usize), + terminal, + language_registry, + cx, + ) + }); + self.terminals.insert(terminal_id.clone(), entity.clone()); + entity + } } fn markdown_for_raw_output( @@ -2155,6 +2312,145 @@ mod tests { }); } + #[gpui::test] + async fn test_terminal_output_buffered_before_created_renders(cx: &mut gpui::TestAppContext) { + init_test(cx); + + let fs = FakeFs::new(cx.executor()); + let project = Project::test(fs, [], cx).await; + let connection = Rc::new(FakeAgentConnection::new()); + let thread = cx + .update(|cx| connection.new_thread(project, std::path::Path::new(path!("/test")), cx)) + .await + .unwrap(); + + let terminal_id = acp::TerminalId(uuid::Uuid::new_v4().to_string().into()); + + // Send Output BEFORE Created - should be buffered by acp_thread + thread.update(cx, |thread, cx| { + thread.on_terminal_provider_event( + TerminalProviderEvent::Output { + terminal_id: terminal_id.clone(), + data: b"hello buffered".to_vec(), + }, + cx, + ); + }); + + // Create a display-only terminal and then send Created + let lower = cx.new(|cx| { + let builder = ::terminal::TerminalBuilder::new_display_only( + ::terminal::terminal_settings::CursorShape::default(), + ::terminal::terminal_settings::AlternateScroll::On, + None, + 0, + ) + .unwrap(); + builder.subscribe(cx) + }); + + thread.update(cx, |thread, cx| { + thread.on_terminal_provider_event( + TerminalProviderEvent::Created { + terminal_id: terminal_id.clone(), + label: "Buffered Test".to_string(), + cwd: None, + output_byte_limit: None, + terminal: lower.clone(), + }, + cx, + ); + }); + + // After Created, buffered Output should have been flushed into the renderer + let content = thread.read_with(cx, |thread, cx| { + let term = thread.terminal(terminal_id.clone()).unwrap(); + term.read_with(cx, |t, cx| t.inner().read(cx).get_content()) + }); + + assert!( + content.contains("hello buffered"), + "expected buffered output to render, got: {content}" + ); + } + + #[gpui::test] + async fn test_terminal_output_and_exit_buffered_before_created(cx: &mut gpui::TestAppContext) { + init_test(cx); + + let fs = FakeFs::new(cx.executor()); + let project = Project::test(fs, [], cx).await; + let connection = Rc::new(FakeAgentConnection::new()); + let thread = cx + .update(|cx| connection.new_thread(project, std::path::Path::new(path!("/test")), cx)) + .await + .unwrap(); + + let terminal_id = acp::TerminalId(uuid::Uuid::new_v4().to_string().into()); + + // Send Output BEFORE Created + thread.update(cx, |thread, cx| { + thread.on_terminal_provider_event( + TerminalProviderEvent::Output { + terminal_id: terminal_id.clone(), + data: b"pre-exit data".to_vec(), + }, + cx, + ); + }); + + // Send Exit BEFORE Created + thread.update(cx, |thread, cx| { + thread.on_terminal_provider_event( + TerminalProviderEvent::Exit { + terminal_id: terminal_id.clone(), + status: acp::TerminalExitStatus { + exit_code: Some(0), + signal: None, + meta: None, + }, + }, + cx, + ); + }); + + // Now create a display-only lower-level terminal and send Created + let lower = cx.new(|cx| { + let builder = ::terminal::TerminalBuilder::new_display_only( + ::terminal::terminal_settings::CursorShape::default(), + ::terminal::terminal_settings::AlternateScroll::On, + None, + 0, + ) + .unwrap(); + builder.subscribe(cx) + }); + + thread.update(cx, |thread, cx| { + thread.on_terminal_provider_event( + TerminalProviderEvent::Created { + terminal_id: terminal_id.clone(), + label: "Buffered Exit Test".to_string(), + cwd: None, + output_byte_limit: None, + terminal: lower.clone(), + }, + cx, + ); + }); + + // Output should be present after Created (flushed from buffer) + let content = thread.read_with(cx, |thread, cx| { + let term = thread.terminal(terminal_id.clone()).unwrap(); + term.read_with(cx, |t, cx| t.inner().read(cx).get_content()) + }); + + assert!( + content.contains("pre-exit data"), + "expected pre-exit data to render, got: {content}" + ); + } + #[gpui::test] async fn test_push_user_content_block(cx: &mut gpui::TestAppContext) { init_test(cx); diff --git a/crates/acp_thread/src/diff.rs b/crates/acp_thread/src/diff.rs index 753f157af934d6c92fcd6766aa645ec45c6b22f7..15de12af27fe233bad4ad8ebb2893ffa5fbdd598 100644 --- a/crates/acp_thread/src/diff.rs +++ b/crates/acp_thread/src/diff.rs @@ -31,7 +31,7 @@ impl Diff { let buffer = new_buffer.clone(); async move |_, cx| { let language = language_registry - .language_for_file_path(Path::new(&path)) + .load_language_for_file_path(Path::new(&path)) .await .log_err(); diff --git a/crates/agent/src/context.rs b/crates/agent/src/context.rs index 80062b1e46f01d1740359e6832c73a2aec255031..3b2922087a94c497c07f1df67a8d4d9adf759909 100644 --- a/crates/agent/src/context.rs +++ b/crates/agent/src/context.rs @@ -187,7 +187,7 @@ impl FileContextHandle { log::error!("file context missing path"); return Task::ready(None); }; - let full_path = file.full_path(cx).to_string_lossy().to_string(); + let full_path = file.full_path(cx).to_string_lossy().into_owned(); let rope = buffer_ref.as_rope().clone(); let buffer = self.buffer.clone(); @@ -283,7 +283,7 @@ impl DirectoryContextHandle { let descendants_future = future::join_all(file_paths.into_iter().map(|path| { let worktree_ref = worktree.read(cx); let worktree_id = worktree_ref.id(); - let full_path = worktree_ref.full_path(&path).to_string_lossy().to_string(); + let full_path = worktree_ref.full_path(&path).to_string_lossy().into_owned(); let rel_path = path .strip_prefix(&directory_path) @@ -403,7 +403,7 @@ impl SymbolContextHandle { log::error!("symbol context's file has no path"); return Task::ready(None); }; - let full_path = file.full_path(cx).to_string_lossy().to_string(); + let full_path = file.full_path(cx).to_string_lossy().into_owned(); let line_range = self.enclosing_range.to_point(&buffer_ref.snapshot()); let text = self.text(cx); let buffer = self.buffer.clone(); @@ -476,7 +476,7 @@ impl SelectionContextHandle { let text = self.text(cx); let buffer = self.buffer.clone(); let context = AgentContext::Selection(SelectionContext { - full_path: full_path.to_string_lossy().to_string(), + full_path: full_path.to_string_lossy().into_owned(), line_range: self.line_range(cx), text, handle: self, diff --git a/crates/agent/src/context_store.rs b/crates/agent/src/context_store.rs index c4aa0abc25544e943e0e91c3a95c8dd8fafad8ef..cf35840cc4215695a966931701257c838c00af18 100644 --- a/crates/agent/src/context_store.rs +++ b/crates/agent/src/context_store.rs @@ -312,7 +312,7 @@ impl ContextStore { let item = image_item.read(cx); this.insert_image( Some(item.project_path(cx)), - Some(item.file.full_path(cx).to_string_lossy().to_string()), + Some(item.file.full_path(cx).to_string_lossy().into_owned()), item.image.clone(), remove_if_exists, cx, diff --git a/crates/agent/src/history_store.rs b/crates/agent/src/history_store.rs index 285ce7c04b7268aea24e0a746737f65d7c3608df..4b1795047b7444dc74f8a41097c0c66aa54ecfd9 100644 --- a/crates/agent/src/history_store.rs +++ b/crates/agent/src/history_store.rs @@ -155,7 +155,7 @@ impl HistoryStore { .iter() .filter_map(|entry| match entry { HistoryEntryId::Context(path) => path.file_name().map(|file| { - SerializedRecentOpen::ContextName(file.to_string_lossy().to_string()) + SerializedRecentOpen::ContextName(file.to_string_lossy().into_owned()) }), HistoryEntryId::Thread(id) => Some(SerializedRecentOpen::Thread(id.to_string())), }) diff --git a/crates/agent/src/thread.rs b/crates/agent/src/thread.rs index 0790e8b2894dc5495cd46585a315696118afd33a..ba737cc1cfb708da101fdac1595eeef867f3a4da 100644 --- a/crates/agent/src/thread.rs +++ b/crates/agent/src/thread.rs @@ -2875,7 +2875,7 @@ impl Thread { // Get worktree path and snapshot let worktree_info = cx.update(|app_cx| { let worktree = worktree.read(app_cx); - let path = worktree.abs_path().to_string_lossy().to_string(); + let path = worktree.abs_path().to_string_lossy().into_owned(); let snapshot = worktree.snapshot(); (path, snapshot) }); @@ -3276,7 +3276,6 @@ mod tests { use settings::{LanguageModelParameters, Settings, SettingsStore}; use std::sync::Arc; use std::time::Duration; - use theme::ThemeSettings; use util::path; use workspace::Workspace; @@ -5337,7 +5336,7 @@ fn main() {{ thread_store::init(fs.clone(), cx); workspace::init_settings(cx); language_model::init_settings(cx); - ThemeSettings::register(cx); + theme::init(theme::LoadThemes::JustBase, cx); ToolRegistry::default_global(cx); assistant_tool::init(cx); diff --git a/crates/agent2/src/history_store.rs b/crates/agent2/src/history_store.rs index c656456e01780505c355c878c26d2405286e56b2..ff6caacc78e5dba4ee38f160fa6ded7fcb45a845 100644 --- a/crates/agent2/src/history_store.rs +++ b/crates/agent2/src/history_store.rs @@ -262,7 +262,7 @@ impl HistoryStore { .iter() .filter_map(|entry| match entry { HistoryEntryId::TextThread(path) => path.file_name().map(|file| { - SerializedRecentOpen::TextThread(file.to_string_lossy().to_string()) + SerializedRecentOpen::TextThread(file.to_string_lossy().into_owned()) }), HistoryEntryId::AcpThread(id) => { Some(SerializedRecentOpen::AcpThread(id.to_string())) diff --git a/crates/agent2/src/thread.rs b/crates/agent2/src/thread.rs index 630b4f906904df2410716f35ef7d15aa0f706e8b..7a991422838305e089dea453c294825e0f1c6e5b 100644 --- a/crates/agent2/src/thread.rs +++ b/crates/agent2/src/thread.rs @@ -898,7 +898,7 @@ impl Thread { // Get worktree path and snapshot let worktree_info = cx.update(|app_cx| { let worktree = worktree.read(app_cx); - let path = worktree.abs_path().to_string_lossy().to_string(); + let path = worktree.abs_path().to_string_lossy().into_owned(); let snapshot = worktree.snapshot(); (path, snapshot) }); diff --git a/crates/agent2/src/tools/edit_file_tool.rs b/crates/agent2/src/tools/edit_file_tool.rs index 3b1bf6f408e88ed0e6bf80b0b55815d122ab1854..7c51df0fae274e2d5906aa73e70c30105b1a2353 100644 --- a/crates/agent2/src/tools/edit_file_tool.rs +++ b/crates/agent2/src/tools/edit_file_tool.rs @@ -218,7 +218,7 @@ impl AgentTool for EditFileTool { .read(cx) .short_full_path_for_project_path(&project_path, cx) }) - .unwrap_or(input.path.to_string_lossy().to_string()) + .unwrap_or(input.path.to_string_lossy().into_owned()) .into(), Err(raw_input) => { if let Some(input) = @@ -476,7 +476,7 @@ impl AgentTool for EditFileTool { ) -> Result<()> { event_stream.update_diff(cx.new(|cx| { Diff::finalized( - output.input_path.to_string_lossy().to_string(), + output.input_path.to_string_lossy().into_owned(), Some(output.old_text.to_string()), output.new_text, self.language_registry.clone(), diff --git a/crates/agent2/src/tools/open_tool.rs b/crates/agent2/src/tools/open_tool.rs index 595a9f380b752635f97ef5d1819a1140c1db8be0..b98ae9af3bd98cd44bc9348e72519ceea53c6292 100644 --- a/crates/agent2/src/tools/open_tool.rs +++ b/crates/agent2/src/tools/open_tool.rs @@ -104,7 +104,7 @@ mod tests { async fn test_to_absolute_path(cx: &mut TestAppContext) { init_test(cx); let temp_dir = TempDir::new().expect("Failed to create temp directory"); - let temp_path = temp_dir.path().to_string_lossy().to_string(); + let temp_path = temp_dir.path().to_string_lossy().into_owned(); let fs = FakeFs::new(cx.executor()); fs.insert_tree( diff --git a/crates/agent_servers/Cargo.toml b/crates/agent_servers/Cargo.toml index ca6db6c663ddb2132c05d716e5b935c5855bccdb..bdf1b72fdc0c2c71d5e445633d1d4a8ce32a6ba4 100644 --- a/crates/agent_servers/Cargo.toml +++ b/crates/agent_servers/Cargo.toml @@ -47,6 +47,8 @@ task.workspace = true tempfile.workspace = true thiserror.workspace = true ui.workspace = true +terminal.workspace = true +uuid.workspace = true util.workspace = true watch.workspace = true workspace-hack.workspace = true diff --git a/crates/agent_servers/src/acp.rs b/crates/agent_servers/src/acp.rs index b14c0467c58d3f41e32e602996560e2cc672d76a..57ddfcf9dc9d635e33f252e6b6f35f015581cfc4 100644 --- a/crates/agent_servers/src/acp.rs +++ b/crates/agent_servers/src/acp.rs @@ -9,6 +9,7 @@ use futures::io::BufReader; use project::Project; use project::agent_server_store::AgentServerCommand; use serde::Deserialize; +use task::Shell; use util::ResultExt as _; use std::path::PathBuf; @@ -19,7 +20,9 @@ use thiserror::Error; use anyhow::{Context as _, Result}; use gpui::{App, AppContext as _, AsyncApp, Entity, SharedString, Task, WeakEntity}; -use acp_thread::{AcpThread, AuthRequired, LoadError}; +use acp_thread::{AcpThread, AuthRequired, LoadError, TerminalProviderEvent}; +use terminal::TerminalBuilder; +use terminal::terminal_settings::{AlternateScroll, CursorShape}; #[derive(Debug, Error)] #[error("Unsupported version")] @@ -79,7 +82,7 @@ impl AcpConnection { is_remote: bool, cx: &mut AsyncApp, ) -> Result { - let mut child = util::command::new_smol_command(command.path); + let mut child = util::command::new_smol_command(&command.path); child .args(command.args.iter().map(|arg| arg.as_str())) .envs(command.env.iter().flatten()) @@ -94,6 +97,11 @@ impl AcpConnection { let stdout = child.stdout.take().context("Failed to take stdout")?; let stdin = child.stdin.take().context("Failed to take stdin")?; let stderr = child.stderr.take().context("Failed to take stderr")?; + log::info!( + "Spawning external agent server: {:?}, {:?}", + command.path, + command.args + ); log::trace!("Spawned (pid: {})", child.id()); let sessions = Rc::new(RefCell::new(HashMap::default())); @@ -380,6 +388,10 @@ impl AgentConnection for AcpConnection { match result { Ok(response) => Ok(response), Err(err) => { + if err.code == acp::ErrorCode::AUTH_REQUIRED.code { + return Err(anyhow!(acp::Error::auth_required())); + } + if err.code != ErrorCode::INTERNAL_ERROR.code { anyhow::bail!(err) } @@ -696,10 +708,100 @@ impl acp::Client for ClientDelegate { } } + // Clone so we can inspect meta both before and after handing off to the thread + let update_clone = notification.update.clone(); + + // Pre-handle: if a ToolCall carries terminal_info, create/register a display-only terminal. + if let acp::SessionUpdate::ToolCall(tc) = &update_clone { + if let Some(meta) = &tc.meta { + if let Some(terminal_info) = meta.get("terminal_info") { + if let Some(id_str) = terminal_info.get("terminal_id").and_then(|v| v.as_str()) + { + let terminal_id = acp::TerminalId(id_str.into()); + let cwd = terminal_info + .get("cwd") + .and_then(|v| v.as_str().map(PathBuf::from)); + + // Create a minimal display-only lower-level terminal and register it. + let _ = session.thread.update(&mut self.cx.clone(), |thread, cx| { + let builder = TerminalBuilder::new_display_only( + CursorShape::default(), + AlternateScroll::On, + None, + 0, + )?; + let lower = cx.new(|cx| builder.subscribe(cx)); + thread.on_terminal_provider_event( + TerminalProviderEvent::Created { + terminal_id: terminal_id.clone(), + label: tc.title.clone(), + cwd, + output_byte_limit: None, + terminal: lower, + }, + cx, + ); + anyhow::Ok(()) + }); + } + } + } + } + + // Forward the update to the acp_thread as usual. session.thread.update(&mut self.cx.clone(), |thread, cx| { - thread.handle_session_update(notification.update, cx) + thread.handle_session_update(notification.update.clone(), cx) })??; + // Post-handle: stream terminal output/exit if present on ToolCallUpdate meta. + if let acp::SessionUpdate::ToolCallUpdate(tcu) = &update_clone { + if let Some(meta) = &tcu.meta { + if let Some(term_out) = meta.get("terminal_output") { + if let Some(id_str) = term_out.get("terminal_id").and_then(|v| v.as_str()) { + let terminal_id = acp::TerminalId(id_str.into()); + if let Some(s) = term_out.get("data").and_then(|v| v.as_str()) { + let data = s.as_bytes().to_vec(); + let _ = session.thread.update(&mut self.cx.clone(), |thread, cx| { + thread.on_terminal_provider_event( + TerminalProviderEvent::Output { + terminal_id: terminal_id.clone(), + data, + }, + cx, + ); + }); + } + } + } + + // terminal_exit + if let Some(term_exit) = meta.get("terminal_exit") { + if let Some(id_str) = term_exit.get("terminal_id").and_then(|v| v.as_str()) { + let terminal_id = acp::TerminalId(id_str.into()); + let status = acp::TerminalExitStatus { + exit_code: term_exit + .get("exit_code") + .and_then(|v| v.as_u64()) + .map(|i| i as u32), + signal: term_exit + .get("signal") + .and_then(|v| v.as_str().map(|s| s.to_string())), + meta: None, + }; + let _ = session.thread.update(&mut self.cx.clone(), |thread, cx| { + thread.on_terminal_provider_event( + TerminalProviderEvent::Exit { + terminal_id: terminal_id.clone(), + status, + }, + cx, + ); + }); + } + } + } + } + Ok(()) } @@ -707,25 +809,68 @@ impl acp::Client for ClientDelegate { &self, args: acp::CreateTerminalRequest, ) -> Result { - let terminal = self - .session_thread(&args.session_id)? - .update(&mut self.cx.clone(), |thread, cx| { - thread.create_terminal( - args.command, - args.args, - args.env, - args.cwd, - args.output_byte_limit, + let thread = self.session_thread(&args.session_id)?; + let project = thread.read_with(&self.cx, |thread, _cx| thread.project().clone())?; + + let mut env = if let Some(dir) = &args.cwd { + project + .update(&mut self.cx.clone(), |project, cx| { + project.directory_environment(&task::Shell::System, dir.clone().into(), cx) + })? + .await + .unwrap_or_default() + } else { + Default::default() + }; + for var in args.env { + env.insert(var.name, var.value); + } + + // Use remote shell or default system shell, as appropriate + let shell = project + .update(&mut self.cx.clone(), |project, cx| { + project + .remote_client() + .and_then(|r| r.read(cx).default_system_shell()) + .map(Shell::Program) + })? + .unwrap_or(task::Shell::System); + let (task_command, task_args) = task::ShellBuilder::new(&shell) + .redirect_stdin_to_dev_null() + .build(Some(args.command.clone()), &args.args); + + let terminal_entity = project + .update(&mut self.cx.clone(), |project, cx| { + project.create_terminal_task( + task::SpawnInTerminal { + command: Some(task_command), + args: task_args, + cwd: args.cwd.clone(), + env, + ..Default::default() + }, cx, ) })? .await?; - Ok( - terminal.read_with(&self.cx, |terminal, _| acp::CreateTerminalResponse { - terminal_id: terminal.id().clone(), - meta: None, - })?, - ) + + // Register with renderer + let terminal_entity = thread.update(&mut self.cx.clone(), |thread, cx| { + thread.register_terminal_created( + acp::TerminalId(uuid::Uuid::new_v4().to_string().into()), + format!("{} {}", args.command, args.args.join(" ")), + args.cwd.clone(), + args.output_byte_limit, + terminal_entity, + cx, + ) + })?; + let terminal_id = + terminal_entity.read_with(&self.cx, |terminal, _| terminal.id().clone())?; + Ok(acp::CreateTerminalResponse { + terminal_id, + meta: None, + }) } async fn kill_terminal_command( diff --git a/crates/agent_servers/src/agent_servers.rs b/crates/agent_servers/src/agent_servers.rs index b9751d7f63053bf073bcc8181f0cc2f8211d5c9f..b44c2123fb5052e2487464d813936cd1edf9821a 100644 --- a/crates/agent_servers/src/agent_servers.rs +++ b/crates/agent_servers/src/agent_servers.rs @@ -1,5 +1,6 @@ mod acp; mod claude; +mod codex; mod custom; mod gemini; @@ -8,6 +9,7 @@ pub mod e2e_tests; pub use claude::*; use client::ProxySettings; +pub use codex::*; use collections::HashMap; pub use custom::*; use fs::Fs; @@ -99,6 +101,9 @@ pub fn load_proxy_env(cx: &mut App) -> HashMap { if let Some(no_proxy) = read_no_proxy_from_env() { env.insert("NO_PROXY".to_owned(), no_proxy); + } else if proxy_url.is_some() { + // We sometimes need local MCP servers that we don't want to proxy + env.insert("NO_PROXY".to_owned(), "localhost,127.0.0.1".to_owned()); } env diff --git a/crates/agent_servers/src/claude.rs b/crates/agent_servers/src/claude.rs index 4646b2e8259fa2cd63c0daa67b47f66b5e78af05..b84a386679cee825be22d895634a6971b537fa89 100644 --- a/crates/agent_servers/src/claude.rs +++ b/crates/agent_servers/src/claude.rs @@ -62,7 +62,7 @@ impl AgentServer for ClaudeCode { cx: &mut App, ) -> Task, Option)>> { let name = self.name(); - let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().to_string()); + let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().into_owned()); let is_remote = delegate.project.read(cx).is_via_remote_server(); let store = delegate.store.downgrade(); let extra_env = load_proxy_env(cx); diff --git a/crates/agent_servers/src/codex.rs b/crates/agent_servers/src/codex.rs new file mode 100644 index 0000000000000000000000000000000000000000..0a19cfd03214972e9c7cd62aee713f3689d525df --- /dev/null +++ b/crates/agent_servers/src/codex.rs @@ -0,0 +1,80 @@ +use std::rc::Rc; +use std::{any::Any, path::Path}; + +use crate::{AgentServer, AgentServerDelegate, load_proxy_env}; +use acp_thread::AgentConnection; +use anyhow::{Context as _, Result}; +use gpui::{App, SharedString, Task}; +use project::agent_server_store::CODEX_NAME; + +#[derive(Clone)] +pub struct Codex; + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + + crate::common_e2e_tests!(async |_, _, _| Codex, allow_option_id = "proceed_once"); +} + +impl AgentServer for Codex { + fn telemetry_id(&self) -> &'static str { + "codex" + } + + fn name(&self) -> SharedString { + "Codex".into() + } + + fn logo(&self) -> ui::IconName { + ui::IconName::AiOpenAi + } + + fn connect( + &self, + root_dir: Option<&Path>, + delegate: AgentServerDelegate, + cx: &mut App, + ) -> Task, Option)>> { + let name = self.name(); + let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().into_owned()); + let is_remote = delegate.project.read(cx).is_via_remote_server(); + let store = delegate.store.downgrade(); + let extra_env = load_proxy_env(cx); + let default_mode = self.default_mode(cx); + + cx.spawn(async move |cx| { + let (command, root_dir, login) = store + .update(cx, |store, cx| { + let agent = store + .get_external_agent(&CODEX_NAME.into()) + .context("Codex is not registered")?; + anyhow::Ok(agent.get_command( + root_dir.as_deref(), + extra_env, + delegate.status_tx, + // For now, report that there are no updates. + // (A future PR will use the GitHub Releases API to fetch them.) + delegate.new_version_available, + &mut cx.to_async(), + )) + })?? + .await?; + + let connection = crate::acp::connect( + name, + command, + root_dir.as_ref(), + default_mode, + is_remote, + cx, + ) + .await?; + Ok((connection, login)) + }) + } + + fn into_any(self: Rc) -> Rc { + self + } +} diff --git a/crates/agent_servers/src/custom.rs b/crates/agent_servers/src/custom.rs index cb9a6dba3c6376fa5030c21523c86853c9b6d761..406a18965111a44bc4e78469b20aaf199cbda037 100644 --- a/crates/agent_servers/src/custom.rs +++ b/crates/agent_servers/src/custom.rs @@ -67,7 +67,7 @@ impl crate::AgentServer for CustomAgentServer { cx: &mut App, ) -> Task, Option)>> { let name = self.name(); - let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().to_string()); + let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().into_owned()); let is_remote = delegate.project.read(cx).is_via_remote_server(); let default_mode = self.default_mode(cx); let store = delegate.store.downgrade(); diff --git a/crates/agent_servers/src/e2e_tests.rs b/crates/agent_servers/src/e2e_tests.rs index 1ee2e099f0ae355267b5f0a5aaddb3371f427240..60480caa541ba1c39dba62ed709c157fd67fede0 100644 --- a/crates/agent_servers/src/e2e_tests.rs +++ b/crates/agent_servers/src/e2e_tests.rs @@ -483,6 +483,13 @@ pub async fn init_test(cx: &mut TestAppContext) -> Arc { default_mode: None, }), gemini: Some(crate::gemini::tests::local_command().into()), + codex: Some(BuiltinAgentServerSettings { + path: Some("codex-acp".into()), + args: None, + env: None, + ignore_system_version: None, + default_mode: None, + }), custom: collections::HashMap::default(), }, cx, diff --git a/crates/agent_servers/src/gemini.rs b/crates/agent_servers/src/gemini.rs index 9407a42e68d34e38e78f2103b29f980f874fb3db..8004f5caec4a7bd2e3e6b1d9a885f4943fa21147 100644 --- a/crates/agent_servers/src/gemini.rs +++ b/crates/agent_servers/src/gemini.rs @@ -31,7 +31,7 @@ impl AgentServer for Gemini { cx: &mut App, ) -> Task, Option)>> { let name = self.name(); - let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().to_string()); + let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().into_owned()); let is_remote = delegate.project.read(cx).is_via_remote_server(); let store = delegate.store.downgrade(); let mut extra_env = load_proxy_env(cx); diff --git a/crates/agent_settings/src/agent_settings.rs b/crates/agent_settings/src/agent_settings.rs index d862cacee18ea53f81cdc91981b22f5531f2d75e..ec05c95672fa29b6e4813207e3e592fff9d3be15 100644 --- a/crates/agent_settings/src/agent_settings.rs +++ b/crates/agent_settings/src/agent_settings.rs @@ -151,7 +151,7 @@ impl Default for AgentProfileId { } impl Settings for AgentSettings { - fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self { + fn from_settings(content: &settings::SettingsContent) -> Self { let agent = content.agent.clone().unwrap(); Self { enabled: agent.enabled.unwrap(), diff --git a/crates/agent_ui/Cargo.toml b/crates/agent_ui/Cargo.toml index 028db95c10a8c7a319bb05927dcabd0564a14683..47d9f6d6a27a2ad5102e831094912208e66a9b43 100644 --- a/crates/agent_ui/Cargo.toml +++ b/crates/agent_ui/Cargo.toml @@ -80,7 +80,6 @@ serde.workspace = true serde_json.workspace = true serde_json_lenient.workspace = true settings.workspace = true -shlex.workspace = true smol.workspace = true streaming_diff.workspace = true task.workspace = true diff --git a/crates/agent_ui/src/acp/entry_view_state.rs b/crates/agent_ui/src/acp/entry_view_state.rs index 0d4dfb0c206a78b2af932d5f3ef7d57c9bfbfc16..ee506b98810ba51d0fb933a2ca21e650d0cacc0b 100644 --- a/crates/agent_ui/src/acp/entry_view_state.rs +++ b/crates/agent_ui/src/acp/entry_view_state.rs @@ -203,7 +203,7 @@ impl EntryViewState { self.entries.drain(range); } - pub fn agent_font_size_changed(&mut self, cx: &mut App) { + pub fn agent_ui_font_size_changed(&mut self, cx: &mut App) { for entry in self.entries.iter() { match entry { Entry::UserMessage { .. } | Entry::AssistantMessage { .. } => {} @@ -387,7 +387,7 @@ fn diff_editor_text_style_refinement(cx: &mut App) -> TextStyleRefinement { font_size: Some( TextSize::Small .rems(cx) - .to_pixels(ThemeSettings::get_global(cx).agent_font_size(cx)) + .to_pixels(ThemeSettings::get_global(cx).agent_ui_font_size(cx)) .into(), ), ..Default::default() @@ -414,7 +414,6 @@ mod tests { use project::Project; use serde_json::json; use settings::{Settings as _, SettingsStore}; - use theme::ThemeSettings; use util::path; use workspace::Workspace; @@ -544,7 +543,7 @@ mod tests { Project::init_settings(cx); AgentSettings::register(cx); workspace::init_settings(cx); - ThemeSettings::register(cx); + theme::init(theme::LoadThemes::JustBase, cx); release_channel::init(SemanticVersion::default(), cx); EditorSettings::register(cx); }); diff --git a/crates/agent_ui/src/acp/message_editor.rs b/crates/agent_ui/src/acp/message_editor.rs index dec9b0beb23d9d7d49116a30404f244b5bb1f8e8..b7521517575a9831635f5e08a954c0e2bd493a80 100644 --- a/crates/agent_ui/src/acp/message_editor.rs +++ b/crates/agent_ui/src/acp/message_editor.rs @@ -76,7 +76,7 @@ pub enum MessageEditorEvent { impl EventEmitter for MessageEditor {} -const COMMAND_HINT_INLAY_ID: usize = 0; +const COMMAND_HINT_INLAY_ID: u32 = 0; impl MessageEditor { pub fn new( @@ -290,18 +290,18 @@ impl MessageEditor { let snapshot = self .editor .update(cx, |editor, cx| editor.snapshot(window, cx)); - let Some((excerpt_id, _, _)) = snapshot.buffer_snapshot.as_singleton() else { + let Some((excerpt_id, _, _)) = snapshot.buffer_snapshot().as_singleton() else { return Task::ready(()); }; let Some(start_anchor) = snapshot - .buffer_snapshot + .buffer_snapshot() .anchor_in_excerpt(*excerpt_id, start) else { return Task::ready(()); }; let end_anchor = snapshot - .buffer_snapshot - .anchor_before(start_anchor.to_offset(&snapshot.buffer_snapshot) + content_len + 1); + .buffer_snapshot() + .anchor_before(start_anchor.to_offset(&snapshot.buffer_snapshot()) + content_len + 1); let crease = if let MentionUri::File { abs_path } = &mention_uri && let Some(extension) = abs_path.extension() @@ -718,7 +718,7 @@ impl MessageEditor { continue; }; - let crease_range = crease.range().to_offset(&snapshot.buffer_snapshot); + let crease_range = crease.range().to_offset(&snapshot.buffer_snapshot()); if crease_range.start > ix { //todo(): Custom slash command ContentBlock? // let chunk = if prevent_slash_commands @@ -865,11 +865,11 @@ impl MessageEditor { self.editor.update(cx, |message_editor, cx| { let snapshot = message_editor.snapshot(window, cx); let (excerpt_id, _, buffer_snapshot) = - snapshot.buffer_snapshot.as_singleton().unwrap(); + snapshot.buffer_snapshot().as_singleton().unwrap(); let text_anchor = buffer_snapshot.anchor_before(buffer_snapshot.len()); let multibuffer_anchor = snapshot - .buffer_snapshot + .buffer_snapshot() .anchor_in_excerpt(*excerpt_id, text_anchor); message_editor.edit( [( @@ -1299,7 +1299,7 @@ impl Render for MessageEditor { font_family: settings.buffer_font.family.clone(), font_fallbacks: settings.buffer_font.fallbacks.clone(), font_features: settings.buffer_font.features.clone(), - font_size: settings.buffer_font_size(cx).into(), + font_size: settings.agent_buffer_font_size(cx).into(), line_height: relative(settings.buffer_line_height.value()), ..Default::default() }; @@ -1550,7 +1550,7 @@ impl MentionSet { fn remove_invalid(&mut self, snapshot: EditorSnapshot) { for (crease_id, crease) in snapshot.crease_snapshot.creases() { - if !crease.range().start.is_valid(&snapshot.buffer_snapshot) { + if !crease.range().start.is_valid(&snapshot.buffer_snapshot()) { self.mentions.remove(&crease_id); } } diff --git a/crates/agent_ui/src/acp/thread_view.rs b/crates/agent_ui/src/acp/thread_view.rs index 104b06049d44c2b9b08262b545ebe3a499f3155b..8d698265b9cb9aa92db7b791503cf9f379aa1099 100644 --- a/crates/agent_ui/src/acp/thread_view.rs +++ b/crates/agent_ui/src/acp/thread_view.rs @@ -9,7 +9,7 @@ use agent_client_protocol::{self as acp, PromptCapabilities}; use agent_servers::{AgentServer, AgentServerDelegate}; use agent_settings::{AgentProfileId, AgentSettings, CompletionMode}; use agent2::{DbThreadMetadata, HistoryEntry, HistoryEntryId, HistoryStore, NativeAgentServer}; -use anyhow::{Context as _, Result, anyhow, bail}; +use anyhow::{Result, anyhow, bail}; use arrayvec::ArrayVec; use audio::{Audio, Sound}; use buffer_diff::BufferDiff; @@ -26,7 +26,7 @@ use gpui::{ CursorStyle, EdgesRefinement, ElementId, Empty, Entity, FocusHandle, Focusable, Hsla, Length, ListOffset, ListState, PlatformDisplay, SharedString, StyleRefinement, Subscription, Task, TextStyle, TextStyleRefinement, UnderlineStyle, WeakEntity, Window, WindowHandle, div, - ease_in_out, linear_color_stop, linear_gradient, list, point, prelude::*, pulsating_between, + ease_in_out, linear_color_stop, linear_gradient, list, point, pulsating_between, }; use language::Buffer; @@ -289,8 +289,9 @@ pub struct AcpThreadView { available_commands: Rc>>, is_loading_contents: bool, new_server_version_available: Option, + resume_thread_metadata: Option, _cancel_task: Option>, - _subscriptions: [Subscription; 4], + _subscriptions: [Subscription; 5], } enum ThreadState { @@ -380,11 +381,17 @@ impl AcpThreadView { ) }); + let agent_server_store = project.read(cx).agent_server_store().clone(); let subscriptions = [ - cx.observe_global_in::(window, Self::agent_font_size_changed), - cx.observe_global_in::(window, Self::agent_font_size_changed), + cx.observe_global_in::(window, Self::agent_ui_font_size_changed), + cx.observe_global_in::(window, Self::agent_ui_font_size_changed), cx.subscribe_in(&message_editor, window, Self::handle_message_editor_event), cx.subscribe_in(&entry_view_state, window, Self::handle_entry_view_event), + cx.subscribe_in( + &agent_server_store, + window, + Self::handle_agent_servers_updated, + ), ]; Self { @@ -392,7 +399,14 @@ impl AcpThreadView { workspace: workspace.clone(), project: project.clone(), entry_view_state, - thread_state: Self::initial_state(agent, resume_thread, workspace, project, window, cx), + thread_state: Self::initial_state( + agent.clone(), + resume_thread.clone(), + workspace.clone(), + project.clone(), + window, + cx, + ), login: None, message_editor, model_selector: None, @@ -421,13 +435,14 @@ impl AcpThreadView { _cancel_task: None, focus_handle: cx.focus_handle(), new_server_version_available: None, + resume_thread_metadata: resume_thread, } } fn reset(&mut self, window: &mut Window, cx: &mut Context) { self.thread_state = Self::initial_state( self.agent.clone(), - None, + self.resume_thread_metadata.clone(), self.workspace.clone(), self.project.clone(), window, @@ -775,6 +790,25 @@ impl AcpThreadView { cx.notify(); } + fn handle_agent_servers_updated( + &mut self, + _agent_server_store: &Entity, + _event: &project::AgentServersUpdated, + window: &mut Window, + cx: &mut Context, + ) { + // If we're in a LoadError state OR have a thread_error set (which can happen + // when agent.connect() fails during loading), retry loading the thread. + // This handles the case where a thread is restored before authentication completes. + let should_retry = + matches!(&self.thread_state, ThreadState::LoadError(_)) || self.thread_error.is_some(); + + if should_retry { + self.thread_error = None; + self.reset(window, cx); + } + } + pub fn workspace(&self) -> &WeakEntity { &self.workspace } @@ -1012,11 +1046,13 @@ impl AcpThreadView { }; let connection = thread.read(cx).connection().clone(); - if !connection - .auth_methods() - .iter() - .any(|method| method.id.0.as_ref() == "claude-login") - { + let auth_methods = connection.auth_methods(); + let has_supported_auth = auth_methods.iter().any(|method| { + let id = method.id.0.as_ref(); + id == "claude-login" || id == "spawn-gemini-cli" + }); + let can_login = has_supported_auth || auth_methods.is_empty() || self.login.is_some(); + if !can_login { return; }; let this = cx.weak_entity(); @@ -1579,31 +1615,20 @@ impl AcpThreadView { return Task::ready(Ok(())); }; let project = workspace.read(cx).project().clone(); - let cwd = project.read(cx).first_project_directory(cx); - let shell = project.read(cx).terminal_settings(&cwd, cx).shell.clone(); window.spawn(cx, async move |cx| { let mut task = login.clone(); - task.command = task - .command - .map(|command| anyhow::Ok(shlex::try_quote(&command)?.to_string())) - .transpose()?; - task.args = task - .args - .iter() - .map(|arg| { - Ok(shlex::try_quote(arg) - .context("Failed to quote argument")? - .to_string()) - }) - .collect::>>()?; + task.shell = task::Shell::WithArguments { + program: task.command.take().expect("login command should be set"), + args: std::mem::take(&mut task.args), + title_override: None + }; task.full_label = task.label.clone(); task.id = task::TaskId(format!("external-agent-{}-login", task.label)); task.command_label = task.label.clone(); task.use_new_terminal = true; task.allow_concurrent_runs = true; task.hide = task::HideStrategy::Always; - task.shell = shell; let terminal = terminal_panel.update_in(cx, |terminal_panel, window, cx| { terminal_panel.spawn_task(&task, window, cx) @@ -2725,7 +2750,7 @@ impl AcpThreadView { let working_dir = working_dir .as_ref() - .map(|path| format!("{}", path.display())) + .map(|path| path.display().to_string()) .unwrap_or_else(|| "current directory".to_string()); let is_expanded = self.expanded_tool_calls.contains(&tool_call.id); @@ -3363,6 +3388,12 @@ impl AcpThreadView { .into_any_element() } + fn activity_bar_bg(&self, cx: &Context) -> Hsla { + let editor_bg_color = cx.theme().colors().editor_background; + let active_color = cx.theme().colors().element_selected; + editor_bg_color.blend(active_color.opacity(0.3)) + } + fn render_activity_bar( &self, thread_entity: &Entity, @@ -3378,10 +3409,6 @@ impl AcpThreadView { return None; } - let editor_bg_color = cx.theme().colors().editor_background; - let active_color = cx.theme().colors().element_selected; - let bg_edit_files_disclosure = editor_bg_color.blend(active_color.opacity(0.3)); - // Temporarily always enable ACP edit controls. This is temporary, to lessen the // impact of a nasty bug that causes them to sometimes be disabled when they shouldn't // be, which blocks you from being able to accept or reject edits. This switches the @@ -3392,7 +3419,7 @@ impl AcpThreadView { v_flex() .mt_1() .mx_2() - .bg(bg_edit_files_disclosure) + .bg(self.activity_bar_bg(cx)) .border_1() .border_b_0() .border_color(cx.theme().colors().border) @@ -3433,27 +3460,33 @@ impl AcpThreadView { .into() } - fn render_plan_summary(&self, plan: &Plan, window: &mut Window, cx: &Context) -> Div { + fn render_plan_summary( + &self, + plan: &Plan, + window: &mut Window, + cx: &Context, + ) -> impl IntoElement { let stats = plan.stats(); let title = if let Some(entry) = stats.in_progress_entry && !self.plan_expanded { h_flex() - .w_full() .cursor_default() + .relative() + .w_full() .gap_1() - .text_xs() - .text_color(cx.theme().colors().text_muted) - .justify_between() + .truncate() .child( - h_flex() - .gap_1() - .child( - Label::new("Current:") - .size(LabelSize::Small) - .color(Color::Muted), - ) + Label::new("Current:") + .size(LabelSize::Small) + .color(Color::Muted), + ) + .child( + div() + .text_xs() + .text_color(cx.theme().colors().text_muted) + .line_clamp(1) .child(MarkdownElement::new( entry.content.clone(), plan_label_markdown_style(&entry.status, window, cx), @@ -3461,10 +3494,23 @@ impl AcpThreadView { ) .when(stats.pending > 0, |this| { this.child( - Label::new(format!("{} left", stats.pending)) - .size(LabelSize::Small) - .color(Color::Muted) - .mr_1(), + h_flex() + .absolute() + .top_0() + .right_0() + .h_full() + .child(div().min_w_8().h_full().bg(linear_gradient( + 90., + linear_color_stop(self.activity_bar_bg(cx), 1.), + linear_color_stop(self.activity_bar_bg(cx).opacity(0.2), 0.), + ))) + .child( + div().pr_0p5().bg(self.activity_bar_bg(cx)).child( + Label::new(format!("{} left", stats.pending)) + .size(LabelSize::Small) + .color(Color::Muted), + ), + ), ) }) } else { @@ -3494,23 +3540,19 @@ impl AcpThreadView { }; h_flex() + .id("plan_summary") .p_1() - .justify_between() + .w_full() + .gap_1() .when(self.plan_expanded, |this| { this.border_b_1().border_color(cx.theme().colors().border) }) - .child( - h_flex() - .id("plan_summary") - .w_full() - .gap_1() - .child(Disclosure::new("plan_disclosure", self.plan_expanded)) - .child(title) - .on_click(cx.listener(|this, _, _, cx| { - this.plan_expanded = !this.plan_expanded; - cx.notify(); - })), - ) + .child(Disclosure::new("plan_disclosure", self.plan_expanded)) + .child(title) + .on_click(cx.listener(|this, _, _, cx| { + this.plan_expanded = !this.plan_expanded; + cx.notify(); + })) } fn render_plan_entries(&self, plan: &Plan, window: &mut Window, cx: &Context) -> Div { @@ -3712,13 +3754,10 @@ impl AcpThreadView { None } else { Some( - Label::new(format!( - "{separator}{}{separator}", - parent.display(path_style) - )) - .color(Color::Muted) - .size(LabelSize::XSmall) - .buffer_font(cx), + Label::new(format!("{}{separator}", parent.display(path_style))) + .color(Color::Muted) + .size(LabelSize::XSmall) + .buffer_font(cx), ) } }); @@ -3762,7 +3801,7 @@ impl AcpThreadView { .id(("file-name", index)) .pr_8() .gap_1p5() - .max_w_full() + .w_full() .overflow_x_scroll() .child(file_icon) .child(h_flex().gap_0p5().children(file_name).children(file_path)) @@ -4914,9 +4953,9 @@ impl AcpThreadView { ) } - fn agent_font_size_changed(&mut self, _window: &mut Window, cx: &mut Context) { + fn agent_ui_font_size_changed(&mut self, _window: &mut Window, cx: &mut Context) { self.entry_view_state.update(cx, |entry_view_state, cx| { - entry_view_state.agent_font_size_changed(cx); + entry_view_state.agent_ui_font_size_changed(cx); }); } @@ -5546,23 +5585,23 @@ fn default_markdown_style( }), code_block: StyleRefinement { padding: EdgesRefinement { - top: Some(DefiniteLength::Absolute(AbsoluteLength::Pixels(Pixels(8.)))), - left: Some(DefiniteLength::Absolute(AbsoluteLength::Pixels(Pixels(8.)))), - right: Some(DefiniteLength::Absolute(AbsoluteLength::Pixels(Pixels(8.)))), - bottom: Some(DefiniteLength::Absolute(AbsoluteLength::Pixels(Pixels(8.)))), + top: Some(DefiniteLength::Absolute(AbsoluteLength::Pixels(px(8.)))), + left: Some(DefiniteLength::Absolute(AbsoluteLength::Pixels(px(8.)))), + right: Some(DefiniteLength::Absolute(AbsoluteLength::Pixels(px(8.)))), + bottom: Some(DefiniteLength::Absolute(AbsoluteLength::Pixels(px(8.)))), }, margin: EdgesRefinement { - top: Some(Length::Definite(Pixels(8.).into())), - left: Some(Length::Definite(Pixels(0.).into())), - right: Some(Length::Definite(Pixels(0.).into())), - bottom: Some(Length::Definite(Pixels(12.).into())), + top: Some(Length::Definite(px(8.).into())), + left: Some(Length::Definite(px(0.).into())), + right: Some(Length::Definite(px(0.).into())), + bottom: Some(Length::Definite(px(12.).into())), }, border_style: Some(BorderStyle::Solid), border_widths: EdgesRefinement { - top: Some(AbsoluteLength::Pixels(Pixels(1.))), - left: Some(AbsoluteLength::Pixels(Pixels(1.))), - right: Some(AbsoluteLength::Pixels(Pixels(1.))), - bottom: Some(AbsoluteLength::Pixels(Pixels(1.))), + top: Some(AbsoluteLength::Pixels(px(1.))), + left: Some(AbsoluteLength::Pixels(px(1.))), + right: Some(AbsoluteLength::Pixels(px(1.))), + bottom: Some(AbsoluteLength::Pixels(px(1.))), }, border_color: Some(colors.border_variant), background: Some(colors.editor_background.into()), @@ -6047,7 +6086,7 @@ pub(crate) mod tests { Project::init_settings(cx); AgentSettings::register(cx); workspace::init_settings(cx); - ThemeSettings::register(cx); + theme::init(theme::LoadThemes::JustBase, cx); release_channel::init(SemanticVersion::default(), cx); EditorSettings::register(cx); prompt_store::init(cx) diff --git a/crates/agent_ui/src/agent_configuration.rs b/crates/agent_ui/src/agent_configuration.rs index 3fd78c44ec5a249c6acf4ddd9ac548988a51612c..3581baf4ec62b746a27bd78bade2d9e85ade069a 100644 --- a/crates/agent_ui/src/agent_configuration.rs +++ b/crates/agent_ui/src/agent_configuration.rs @@ -15,6 +15,7 @@ use context_server::ContextServerId; use editor::{Editor, SelectionEffects, scroll::Autoscroll}; use extension::ExtensionManifest; use extension_host::ExtensionStore; +use feature_flags::{CodexAcpFeatureFlag, FeatureFlagAppExt as _}; use fs::Fs; use gpui::{ Action, AnyView, App, AsyncWindowContext, Corner, Entity, EventEmitter, FocusHandle, Focusable, @@ -26,7 +27,7 @@ use language_model::{ }; use notifications::status_toast::{StatusToast, ToastIcon}; use project::{ - agent_server_store::{AgentServerStore, CLAUDE_CODE_NAME, GEMINI_NAME}, + agent_server_store::{AgentServerStore, CLAUDE_CODE_NAME, CODEX_NAME, GEMINI_NAME}, context_server_store::{ContextServerConfiguration, ContextServerStatus, ContextServerStore}, }; use settings::{Settings, SettingsStore, update_settings_file}; @@ -1014,7 +1015,9 @@ impl AgentConfiguration { .agent_server_store .read(cx) .external_agents() - .filter(|name| name.0 != GEMINI_NAME && name.0 != CLAUDE_CODE_NAME) + .filter(|name| { + name.0 != GEMINI_NAME && name.0 != CLAUDE_CODE_NAME && name.0 != CODEX_NAME + }) .cloned() .collect::>(); @@ -1078,13 +1081,21 @@ impl AgentConfiguration { ), ) .child(self.render_agent_server( - IconName::AiGemini, - "Gemini CLI", + IconName::AiClaude, + "Claude Code", )) .child(Divider::horizontal().color(DividerColor::BorderFaded)) + .when(cx.has_flag::(), |this| { + this + .child(self.render_agent_server( + IconName::AiOpenAi, + "Codex", + )) + .child(Divider::horizontal().color(DividerColor::BorderFaded)) + }) .child(self.render_agent_server( - IconName::AiClaude, - "Claude Code", + IconName::AiGemini, + "Gemini CLI", )) .map(|mut parent| { for agent in user_defined_agents { diff --git a/crates/agent_ui/src/agent_configuration/manage_profiles_modal.rs b/crates/agent_ui/src/agent_configuration/manage_profiles_modal.rs index cb9aabab9250b2f6175912c0a73242bc7f331dc6..9a7f0ed602a52d3b27dde565383453f2c5c325fb 100644 --- a/crates/agent_ui/src/agent_configuration/manage_profiles_modal.rs +++ b/crates/agent_ui/src/agent_configuration/manage_profiles_modal.rs @@ -317,6 +317,8 @@ impl ManageProfilesModal { window: &mut Window, cx: &mut Context, ) -> impl IntoElement + use<> { + let is_focused = profile.navigation.focus_handle.contains_focused(window, cx); + div() .id(SharedString::from(format!("profile-{}", profile.id))) .track_focus(&profile.navigation.focus_handle) @@ -328,25 +330,27 @@ impl ManageProfilesModal { }) .child( ListItem::new(SharedString::from(format!("profile-{}", profile.id))) - .toggle_state(profile.navigation.focus_handle.contains_focused(window, cx)) + .toggle_state(is_focused) .inset(true) .spacing(ListItemSpacing::Sparse) .child(Label::new(profile.name.clone())) - .end_slot( - h_flex() - .gap_1() - .child( - Label::new("Customize") - .size(LabelSize::Small) - .color(Color::Muted), - ) - .children(KeyBinding::for_action_in( - &menu::Confirm, - &self.focus_handle, - window, - cx, - )), - ) + .when(is_focused, |this| { + this.end_slot( + h_flex() + .gap_1() + .child( + Label::new("Customize") + .size(LabelSize::Small) + .color(Color::Muted), + ) + .children(KeyBinding::for_action_in( + &menu::Confirm, + &self.focus_handle, + window, + cx, + )), + ) + }) .on_click({ let profile_id = profile.id.clone(); cx.listener(move |this, _, window, cx| { diff --git a/crates/agent_ui/src/agent_diff.rs b/crates/agent_ui/src/agent_diff.rs index 5ae43398d626fd5b5208e5e73d2c034644a29d95..67014e3c3a4c8bd9b43f34d9cad3c23832efdc13 100644 --- a/crates/agent_ui/src/agent_diff.rs +++ b/crates/agent_ui/src/agent_diff.rs @@ -562,10 +562,6 @@ impl Item for AgentDiffPane { self.editor.for_each_project_item(cx, f) } - fn is_singleton(&self, _: &App) -> bool { - false - } - fn set_nav_history( &mut self, nav_history: ItemNavHistory, @@ -850,7 +846,7 @@ fn render_diff_hunk_controls( editor.update(cx, |editor, cx| { let snapshot = editor.snapshot(window, cx); let position = - hunk_range.end.to_point(&snapshot.buffer_snapshot); + hunk_range.end.to_point(&snapshot.buffer_snapshot()); editor.go_to_hunk_before_or_after_position( &snapshot, position, @@ -886,7 +882,7 @@ fn render_diff_hunk_controls( editor.update(cx, |editor, cx| { let snapshot = editor.snapshot(window, cx); let point = - hunk_range.start.to_point(&snapshot.buffer_snapshot); + hunk_range.start.to_point(&snapshot.buffer_snapshot()); editor.go_to_hunk_before_or_after_position( &snapshot, point, @@ -1818,7 +1814,6 @@ mod tests { use serde_json::json; use settings::{Settings, SettingsStore}; use std::{path::Path, rc::Rc}; - use theme::ThemeSettings; use util::path; #[gpui::test] @@ -1831,7 +1826,7 @@ mod tests { AgentSettings::register(cx); prompt_store::init(cx); workspace::init_settings(cx); - ThemeSettings::register(cx); + theme::init(theme::LoadThemes::JustBase, cx); EditorSettings::register(cx); language_model::init_settings(cx); }); @@ -1983,7 +1978,7 @@ mod tests { AgentSettings::register(cx); prompt_store::init(cx); workspace::init_settings(cx); - ThemeSettings::register(cx); + theme::init(theme::LoadThemes::JustBase, cx); EditorSettings::register(cx); language_model::init_settings(cx); workspace::register_project_item::(cx); diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs index ca6a5fb2f6c216e7886394da069c93e5029a5ed0..5b641dad41a4165fd0eaa0fd0502b982e58816c7 100644 --- a/crates/agent_ui/src/agent_panel.rs +++ b/crates/agent_ui/src/agent_panel.rs @@ -7,7 +7,7 @@ use acp_thread::AcpThread; use agent2::{DbThreadMetadata, HistoryEntry}; use db::kvp::{Dismissable, KEY_VALUE_STORE}; use project::agent_server_store::{ - AgentServerCommand, AllAgentServersSettings, CLAUDE_CODE_NAME, GEMINI_NAME, + AgentServerCommand, AllAgentServersSettings, CLAUDE_CODE_NAME, CODEX_NAME, GEMINI_NAME, }; use serde::{Deserialize, Serialize}; use settings::{ @@ -48,8 +48,8 @@ use editor::{Anchor, AnchorRangeExt as _, Editor, EditorEvent, MultiBuffer}; use fs::Fs; use gpui::{ Action, AnyElement, App, AsyncWindowContext, Corner, DismissEvent, Entity, EventEmitter, - ExternalPaths, FocusHandle, Focusable, KeyContext, Pixels, Subscription, Task, UpdateGlobal, - WeakEntity, prelude::*, + ExternalPaths, FocusHandle, Focusable, KeyContext, Pixels, ReadGlobal as _, Subscription, Task, + UpdateGlobal, WeakEntity, prelude::*, }; use language::LanguageRegistry; use language_model::{ConfigurationError, LanguageModelRegistry}; @@ -75,6 +75,7 @@ use zed_actions::{ assistant::{OpenRulesLibrary, ToggleFocus}, }; +use feature_flags::{CodexAcpFeatureFlag, FeatureFlagAppExt as _}; const AGENT_PANEL_KEY: &str = "agent_panel"; #[derive(Serialize, Deserialize, Debug)] @@ -216,6 +217,7 @@ pub enum AgentType { TextThread, Gemini, ClaudeCode, + Codex, NativeAgent, Custom { name: SharedString, @@ -230,6 +232,7 @@ impl AgentType { Self::NativeAgent => "Agent 2".into(), Self::Gemini => "Gemini CLI".into(), Self::ClaudeCode => "Claude Code".into(), + Self::Codex => "Codex".into(), Self::Custom { name, .. } => name.into(), } } @@ -239,6 +242,7 @@ impl AgentType { Self::Zed | Self::NativeAgent | Self::TextThread => None, Self::Gemini => Some(IconName::AiGemini), Self::ClaudeCode => Some(IconName::AiClaude), + Self::Codex => Some(IconName::AiOpenAi), Self::Custom { .. } => Some(IconName::Terminal), } } @@ -249,6 +253,7 @@ impl From for AgentType { match value { ExternalAgent::Gemini => Self::Gemini, ExternalAgent::ClaudeCode => Self::ClaudeCode, + ExternalAgent::Codex => Self::Codex, ExternalAgent::Custom { name, command } => Self::Custom { name, command }, ExternalAgent::NativeAgent => Self::NativeAgent, } @@ -514,6 +519,14 @@ impl AgentPanel { cx, ) }); + + if SettingsStore::global(cx) + .get::(None) + .disable_ai + { + return panel; + } + panel.as_mut(cx).loading = true; if let Some(serialized_panel) = serialized_panel { panel.update(cx, |panel, cx| { @@ -1103,15 +1116,15 @@ impl AgentPanel { 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 agent_ui_font_size = + ThemeSettings::get_global(cx).agent_ui_font_size(cx) + delta; let _ = settings .theme - .agent_font_size - .insert(theme::clamp_font_size(agent_font_size).into()); + .agent_ui_font_size + .insert(theme::clamp_font_size(agent_ui_font_size).into()); }); } else { - theme::adjust_agent_font_size(cx, |size| size + delta); + theme::adjust_agent_ui_font_size(cx, |size| size + delta); } } WhichFontSize::BufferFont => { @@ -1131,10 +1144,10 @@ impl AgentPanel { ) { if action.persist { update_settings_file(self.fs.clone(), cx, move |settings, _| { - settings.theme.agent_font_size = None; + settings.theme.agent_ui_font_size = None; }); } else { - theme::reset_agent_font_size(cx); + theme::reset_agent_ui_font_size(cx); } } @@ -1427,6 +1440,11 @@ impl AgentPanel { cx, ) } + AgentType::Codex => { + self.selected_agent = AgentType::Codex; + self.serialize(cx); + self.external_thread(Some(crate::ExternalAgent::Codex), None, None, window, cx) + } AgentType::Custom { name, command } => self.external_thread( Some(crate::ExternalAgent::Custom { name, command }), None, @@ -1940,10 +1958,10 @@ impl AgentPanel { .separator() .header("External Agents") .item( - ContextMenuEntry::new("New Gemini CLI Thread") - .icon(IconName::AiGemini) - .icon_color(Color::Muted) + ContextMenuEntry::new("New Claude Code Thread") + .icon(IconName::AiClaude) .disabled(is_via_collab) + .icon_color(Color::Muted) .handler({ let workspace = workspace.clone(); move |window, cx| { @@ -1954,7 +1972,7 @@ impl AgentPanel { { panel.update(cx, |panel, cx| { panel.new_agent_thread( - AgentType::Gemini, + AgentType::ClaudeCode, window, cx, ); @@ -1965,11 +1983,39 @@ impl AgentPanel { } }), ) + .when(cx.has_flag::(), |this| { + this.item( + ContextMenuEntry::new("New Codex Thread") + .icon(IconName::AiOpenAi) + .disabled(is_via_collab) + .icon_color(Color::Muted) + .handler({ + let workspace = workspace.clone(); + move |window, cx| { + if let Some(workspace) = workspace.upgrade() { + workspace.update(cx, |workspace, cx| { + if let Some(panel) = + workspace.panel::(cx) + { + panel.update(cx, |panel, cx| { + panel.new_agent_thread( + AgentType::Codex, + window, + cx, + ); + }); + } + }); + } + } + }), + ) + }) .item( - ContextMenuEntry::new("New Claude Code Thread") - .icon(IconName::AiClaude) - .disabled(is_via_collab) + ContextMenuEntry::new("New Gemini CLI Thread") + .icon(IconName::AiGemini) .icon_color(Color::Muted) + .disabled(is_via_collab) .handler({ let workspace = workspace.clone(); move |window, cx| { @@ -1980,7 +2026,7 @@ impl AgentPanel { { panel.update(cx, |panel, cx| { panel.new_agent_thread( - AgentType::ClaudeCode, + AgentType::Gemini, window, cx, ); @@ -1996,7 +2042,7 @@ impl AgentPanel { .read(cx) .external_agents() .filter(|name| { - name.0 != GEMINI_NAME && name.0 != CLAUDE_CODE_NAME + name.0 != GEMINI_NAME && name.0 != CLAUDE_CODE_NAME && name.0 != CODEX_NAME }) .cloned() .collect::>(); @@ -2532,7 +2578,7 @@ impl Render for AgentPanel { match self.active_view.which_font_size_used() { WhichFontSize::AgentFont => { - WithRemSize::new(ThemeSettings::get_global(cx).agent_font_size(cx)) + WithRemSize::new(ThemeSettings::get_global(cx).agent_ui_font_size(cx)) .size_full() .child(content) .into_any() diff --git a/crates/agent_ui/src/agent_ui.rs b/crates/agent_ui/src/agent_ui.rs index b64334f403bed8cfcf86e80e4fe4589ba920b06d..2c439a725456976f090ddc4cb754664c4953d626 100644 --- a/crates/agent_ui/src/agent_ui.rs +++ b/crates/agent_ui/src/agent_ui.rs @@ -167,6 +167,7 @@ enum ExternalAgent { #[default] Gemini, ClaudeCode, + Codex, NativeAgent, Custom { name: SharedString, @@ -188,6 +189,7 @@ impl ExternalAgent { Self::NativeAgent => "zed", Self::Gemini => "gemini-cli", Self::ClaudeCode => "claude-code", + Self::Codex => "codex", Self::Custom { .. } => "custom", } } @@ -200,6 +202,7 @@ impl ExternalAgent { match self { Self::Gemini => Rc::new(agent_servers::Gemini), Self::ClaudeCode => Rc::new(agent_servers::ClaudeCode), + Self::Codex => Rc::new(agent_servers::Codex), Self::NativeAgent => Rc::new(agent2::NativeAgentServer::new(fs, history)), Self::Custom { name, command: _ } => { Rc::new(agent_servers::CustomAgentServer::new(name.clone())) diff --git a/crates/agent_ui/src/inline_assistant.rs b/crates/agent_ui/src/inline_assistant.rs index 98e7276dc4fd3f94b01df82219f116a07cafa304..d24dc4ab781585e9ebabc7f19016e4da2457a873 100644 --- a/crates/agent_ui/src/inline_assistant.rs +++ b/crates/agent_ui/src/inline_assistant.rs @@ -18,7 +18,9 @@ use agent_settings::AgentSettings; use anyhow::{Context as _, Result}; use client::telemetry::Telemetry; use collections::{HashMap, HashSet, VecDeque, hash_map}; +use editor::RowExt; use editor::SelectionEffects; +use editor::scroll::ScrollOffset; use editor::{ Anchor, AnchorRangeExt, CodeActionProvider, Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset as _, ToPoint, @@ -380,7 +382,7 @@ impl InlineAssistant { if let Some(editor_assists) = self.assists_by_editor.get(&editor.downgrade()) { for assist_id in &editor_assists.assist_ids { let assist = &self.assists[assist_id]; - let range = assist.range.to_point(&snapshot.buffer_snapshot); + let range = assist.range.to_point(&snapshot.buffer_snapshot()); if range.start.row <= newest_selection.start.row && newest_selection.end.row <= range.end.row { @@ -400,16 +402,16 @@ impl InlineAssistant { selection.end.row -= 1; } selection.end.column = snapshot - .buffer_snapshot + .buffer_snapshot() .line_len(MultiBufferRow(selection.end.row)); } else if let Some(fold) = snapshot.crease_for_buffer_row(MultiBufferRow(selection.end.row)) { selection.start = fold.range().start; selection.end = fold.range().end; - if MultiBufferRow(selection.end.row) < snapshot.buffer_snapshot.max_row() { + if MultiBufferRow(selection.end.row) < snapshot.buffer_snapshot().max_row() { let chars = snapshot - .buffer_snapshot + .buffer_snapshot() .chars_at(Point::new(selection.end.row + 1, 0)); for c in chars { @@ -425,7 +427,7 @@ impl InlineAssistant { { selection.end.row += 1; selection.end.column = snapshot - .buffer_snapshot + .buffer_snapshot() .line_len(MultiBufferRow(selection.end.row)); } } @@ -445,7 +447,7 @@ impl InlineAssistant { } selections.push(selection); } - let snapshot = &snapshot.buffer_snapshot; + let snapshot = &snapshot.buffer_snapshot(); let newest_selection = newest_selection.unwrap(); let mut codegen_ranges = Vec::new(); @@ -744,7 +746,7 @@ impl InlineAssistant { let scroll_bottom = scroll_top + editor.visible_line_count().unwrap_or(0.); editor_assists.scroll_lock = editor .row_for_block(decorations.prompt_block_id, cx) - .map(|row| row.0 as f32) + .map(|row| row.as_f64()) .filter(|prompt_row| (scroll_top..scroll_bottom).contains(&prompt_row)) .map(|prompt_row| InlineAssistScrollLock { assist_id, @@ -910,7 +912,9 @@ impl InlineAssistant { editor.update(cx, |editor, cx| { let scroll_position = editor.scroll_position(cx); - let target_scroll_top = editor.row_for_block(decorations.prompt_block_id, cx)?.0 as f32 + let target_scroll_top = editor + .row_for_block(decorations.prompt_block_id, cx)? + .as_f64() - scroll_lock.distance_from_top; if target_scroll_top != scroll_position.y { editor.set_scroll_position(point(scroll_position.x, target_scroll_top), window, cx); @@ -959,8 +963,9 @@ impl InlineAssistant { if let Some(decorations) = assist.decorations.as_ref() { let distance_from_top = editor.update(cx, |editor, cx| { let scroll_top = editor.scroll_position(cx).y; - let prompt_row = - editor.row_for_block(decorations.prompt_block_id, cx)?.0 as f32; + let prompt_row = editor + .row_for_block(decorations.prompt_block_id, cx)? + .0 as ScrollOffset; Some(prompt_row - scroll_top) }); @@ -1192,8 +1197,8 @@ impl InlineAssistant { let mut scroll_target_range = None; if let Some(decorations) = assist.decorations.as_ref() { scroll_target_range = maybe!({ - let top = editor.row_for_block(decorations.prompt_block_id, cx)?.0 as f32; - let bottom = editor.row_for_block(decorations.end_block_id, cx)?.0 as f32; + let top = editor.row_for_block(decorations.prompt_block_id, cx)?.0 as f64; + let bottom = editor.row_for_block(decorations.end_block_id, cx)?.0 as f64; Some((top, bottom)) }); if scroll_target_range.is_none() { @@ -1207,15 +1212,15 @@ impl InlineAssistant { .start .to_display_point(&snapshot.display_snapshot) .row(); - let top = start_row.0 as f32; + let top = start_row.0 as ScrollOffset; let bottom = top + 1.0; (top, bottom) }); let mut scroll_target_top = scroll_target_range.0; let mut scroll_target_bottom = scroll_target_range.1; - scroll_target_top -= editor.vertical_scroll_margin() as f32; - scroll_target_bottom += editor.vertical_scroll_margin() as f32; + scroll_target_top -= editor.vertical_scroll_margin() as ScrollOffset; + scroll_target_bottom += editor.vertical_scroll_margin() as ScrollOffset; let height_in_lines = editor.visible_line_count().unwrap_or(0.); let scroll_top = editor.scroll_position(cx).y; @@ -1543,7 +1548,7 @@ struct EditorInlineAssists { struct InlineAssistScrollLock { assist_id: InlineAssistId, - distance_from_top: f32, + distance_from_top: ScrollOffset, } impl EditorInlineAssists { diff --git a/crates/agent_ui/src/profile_selector.rs b/crates/agent_ui/src/profile_selector.rs index af2354f7a854fd84483889f18e0f51e1c294d8a2..d52b2436d68c4b43cd7e7ec73b27007e34a43153 100644 --- a/crates/agent_ui/src/profile_selector.rs +++ b/crates/agent_ui/src/profile_selector.rs @@ -3,12 +3,20 @@ use agent_settings::{ AgentProfile, AgentProfileId, AgentSettings, AvailableProfiles, builtin_profiles, }; use fs::Fs; -use gpui::{Action, Entity, FocusHandle, Subscription, prelude::*}; -use settings::{DockPosition, Settings as _, SettingsStore, update_settings_file}; -use std::sync::Arc; +use fuzzy::{StringMatch, StringMatchCandidate, match_strings}; +use gpui::{ + Action, AnyElement, App, BackgroundExecutor, Context, DismissEvent, Entity, FocusHandle, + Focusable, SharedString, Subscription, Task, Window, +}; +use picker::{Picker, PickerDelegate, popover_menu::PickerPopoverMenu}; +use settings::{Settings as _, SettingsStore, update_settings_file}; +use std::{ + sync::atomic::Ordering, + sync::{Arc, atomic::AtomicBool}, +}; use ui::{ - ContextMenu, ContextMenuEntry, DocumentationEdge, DocumentationSide, PopoverMenu, - PopoverMenuHandle, TintColor, Tooltip, prelude::*, + DocumentationAside, DocumentationEdge, DocumentationSide, HighlightedLabel, LabelSize, + ListItem, ListItemSpacing, PopoverMenuHandle, TintColor, Tooltip, prelude::*, }; /// Trait for types that can provide and manage agent profiles @@ -25,9 +33,11 @@ pub trait ProfileProvider { pub struct ProfileSelector { profiles: AvailableProfiles, + pending_refresh: bool, fs: Arc, provider: Arc, - menu_handle: PopoverMenuHandle, + picker: Option>>, + picker_handle: PopoverMenuHandle>, focus_handle: FocusHandle, _subscriptions: Vec, } @@ -40,188 +50,686 @@ impl ProfileSelector { cx: &mut Context, ) -> Self { let settings_subscription = cx.observe_global::(move |this, cx| { - this.refresh_profiles(cx); + this.pending_refresh = true; + cx.notify(); }); Self { profiles: AgentProfile::available_profiles(cx), + pending_refresh: false, fs, provider, - menu_handle: PopoverMenuHandle::default(), + picker: None, + picker_handle: PopoverMenuHandle::default(), focus_handle, _subscriptions: vec![settings_subscription], } } - pub fn menu_handle(&self) -> PopoverMenuHandle { - self.menu_handle.clone() - } - - fn refresh_profiles(&mut self, cx: &mut Context) { - self.profiles = AgentProfile::available_profiles(cx); + pub fn menu_handle(&self) -> PopoverMenuHandle> { + self.picker_handle.clone() } - fn build_context_menu( - &self, + fn ensure_picker( + &mut self, window: &mut Window, cx: &mut Context, - ) -> Entity { - ContextMenu::build(window, cx, |mut menu, _window, cx| { - let settings = AgentSettings::get_global(cx); - - let mut found_non_builtin = false; - for (profile_id, profile_name) in self.profiles.iter() { - if !builtin_profiles::is_builtin(profile_id) { - found_non_builtin = true; - continue; - } - menu = menu.item(self.menu_entry_for_profile( - profile_id.clone(), - profile_name, - settings, - cx, - )); - } + ) -> Entity> { + if self.picker.is_none() { + let delegate = ProfilePickerDelegate::new( + self.fs.clone(), + self.provider.clone(), + self.profiles.clone(), + cx.background_executor().clone(), + cx, + ); - if found_non_builtin { - menu = menu.separator().header("Custom Profiles"); - for (profile_id, profile_name) in self.profiles.iter() { - if builtin_profiles::is_builtin(profile_id) { - continue; - } - menu = menu.item(self.menu_entry_for_profile( - profile_id.clone(), - profile_name, - settings, - cx, - )); - } + let picker = cx.new(|cx| { + Picker::list(delegate, window, cx) + .show_scrollbar(true) + .width(rems(18.)) + .max_height(Some(rems(20.).into())) + }); + + self.picker = Some(picker); + } + + if self.pending_refresh { + if let Some(picker) = &self.picker { + let profiles = AgentProfile::available_profiles(cx); + self.profiles = profiles.clone(); + picker.update(cx, |picker, cx| { + let query = picker.query(cx); + picker + .delegate + .refresh_profiles(profiles.clone(), query, cx); + }); } + self.pending_refresh = false; + } - menu = menu.separator(); - menu = menu.item(ContextMenuEntry::new("Configure Profiles…").handler( - move |window, cx| { - window.dispatch_action(ManageProfiles::default().boxed_clone(), cx); - }, - )); + self.picker.as_ref().unwrap().clone() + } +} - menu - }) +impl Focusable for ProfileSelector { + fn focus_handle(&self, cx: &App) -> FocusHandle { + if let Some(picker) = &self.picker { + picker.focus_handle(cx) + } else { + self.focus_handle.clone() + } } +} - fn menu_entry_for_profile( - &self, - profile_id: AgentProfileId, - profile_name: &SharedString, - settings: &AgentSettings, - cx: &App, - ) -> ContextMenuEntry { - let documentation = match profile_name.to_lowercase().as_str() { +impl Render for ProfileSelector { + fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { + if !self.provider.profiles_supported(cx) { + return Button::new("tools-not-supported-button", "Tools Unsupported") + .disabled(true) + .label_size(LabelSize::Small) + .color(Color::Muted) + .tooltip(Tooltip::text("This model does not support tools.")) + .into_any_element(); + } + + let picker = self.ensure_picker(window, cx); + + let settings = AgentSettings::get_global(cx); + let profile_id = self.provider.profile_id(cx); + let profile = settings.profiles.get(&profile_id); + + let selected_profile = profile + .map(|profile| profile.name.clone()) + .unwrap_or_else(|| "Unknown".into()); + let focus_handle = self.focus_handle.clone(); + + let trigger_button = Button::new("profile-selector", selected_profile) + .label_size(LabelSize::Small) + .color(Color::Muted) + .icon(IconName::ChevronDown) + .icon_size(IconSize::XSmall) + .icon_position(IconPosition::End) + .icon_color(Color::Muted) + .selected_style(ButtonStyle::Tinted(TintColor::Accent)); + + PickerPopoverMenu::new( + picker, + trigger_button, + move |window, cx| { + Tooltip::for_action_in( + "Toggle Profile Menu", + &ToggleProfileSelector, + &focus_handle, + window, + cx, + ) + }, + gpui::Corner::BottomRight, + cx, + ) + .with_handle(self.picker_handle.clone()) + .render(window, cx) + .into_any_element() + } +} + +#[derive(Clone)] +struct ProfileCandidate { + id: AgentProfileId, + name: SharedString, + is_builtin: bool, +} + +#[derive(Clone)] +struct ProfileMatchEntry { + candidate_index: usize, + positions: Vec, +} + +enum ProfilePickerEntry { + Header(SharedString), + Profile(ProfileMatchEntry), +} + +pub(crate) struct ProfilePickerDelegate { + fs: Arc, + provider: Arc, + background: BackgroundExecutor, + candidates: Vec, + string_candidates: Arc>, + filtered_entries: Vec, + selected_index: usize, + query: String, + cancel: Option>, +} + +impl ProfilePickerDelegate { + fn new( + fs: Arc, + provider: Arc, + profiles: AvailableProfiles, + background: BackgroundExecutor, + cx: &mut Context, + ) -> Self { + let candidates = Self::candidates_from(profiles); + let string_candidates = Arc::new(Self::string_candidates(&candidates)); + let filtered_entries = Self::entries_from_candidates(&candidates); + + let mut this = Self { + fs, + provider, + background, + candidates, + string_candidates, + filtered_entries, + selected_index: 0, + query: String::new(), + cancel: None, + }; + + this.selected_index = this + .index_of_profile(&this.provider.profile_id(cx)) + .unwrap_or_else(|| this.first_selectable_index().unwrap_or(0)); + + this + } + + fn refresh_profiles( + &mut self, + profiles: AvailableProfiles, + query: String, + cx: &mut Context>, + ) { + self.candidates = Self::candidates_from(profiles); + self.string_candidates = Arc::new(Self::string_candidates(&self.candidates)); + self.query = query; + + if self.query.is_empty() { + self.filtered_entries = Self::entries_from_candidates(&self.candidates); + } else { + let matches = self.search_blocking(&self.query); + self.filtered_entries = self.entries_from_matches(matches); + } + + self.selected_index = self + .index_of_profile(&self.provider.profile_id(cx)) + .unwrap_or_else(|| self.first_selectable_index().unwrap_or(0)); + cx.notify(); + } + + fn candidates_from(profiles: AvailableProfiles) -> Vec { + profiles + .into_iter() + .map(|(id, name)| ProfileCandidate { + is_builtin: builtin_profiles::is_builtin(&id), + id, + name, + }) + .collect() + } + + fn string_candidates(candidates: &[ProfileCandidate]) -> Vec { + candidates + .iter() + .enumerate() + .map(|(index, candidate)| StringMatchCandidate::new(index, candidate.name.as_ref())) + .collect() + } + + fn documentation(candidate: &ProfileCandidate) -> Option<&'static str> { + match candidate.id.as_str() { builtin_profiles::WRITE => Some("Get help to write anything."), builtin_profiles::ASK => Some("Chat about your codebase."), builtin_profiles::MINIMAL => Some("Chat about anything with no tools."), _ => None, + } + } + + fn entries_from_candidates(candidates: &[ProfileCandidate]) -> Vec { + let mut entries = Vec::new(); + let mut inserted_custom_header = false; + + for (idx, candidate) in candidates.iter().enumerate() { + if !candidate.is_builtin && !inserted_custom_header { + if !entries.is_empty() { + entries.push(ProfilePickerEntry::Header("Custom Profiles".into())); + } + inserted_custom_header = true; + } + + entries.push(ProfilePickerEntry::Profile(ProfileMatchEntry { + candidate_index: idx, + positions: Vec::new(), + })); + } + + entries + } + + fn entries_from_matches(&self, matches: Vec) -> Vec { + let mut entries = Vec::new(); + for mat in matches { + if self.candidates.get(mat.candidate_id).is_some() { + entries.push(ProfilePickerEntry::Profile(ProfileMatchEntry { + candidate_index: mat.candidate_id, + positions: mat.positions, + })); + } + } + entries + } + + fn first_selectable_index(&self) -> Option { + self.filtered_entries + .iter() + .position(|entry| matches!(entry, ProfilePickerEntry::Profile(_))) + } + + fn index_of_profile(&self, profile_id: &AgentProfileId) -> Option { + self.filtered_entries.iter().position(|entry| { + matches!(entry, ProfilePickerEntry::Profile(profile) if self + .candidates + .get(profile.candidate_index) + .map(|candidate| &candidate.id == profile_id) + .unwrap_or(false)) + }) + } + + fn search_blocking(&self, query: &str) -> Vec { + if query.is_empty() { + return self + .string_candidates + .iter() + .map(|candidate| StringMatch { + candidate_id: candidate.id, + score: 0.0, + positions: Vec::new(), + string: candidate.string.clone(), + }) + .collect(); + } + + let cancel_flag = AtomicBool::new(false); + + self.background.block(match_strings( + self.string_candidates.as_ref(), + query, + false, + true, + 100, + &cancel_flag, + self.background.clone(), + )) + } +} + +impl PickerDelegate for ProfilePickerDelegate { + type ListItem = AnyElement; + + fn placeholder_text(&self, _: &mut Window, _: &mut App) -> Arc { + "Search profiles…".into() + } + + fn no_matches_text(&self, _window: &mut Window, _cx: &mut App) -> Option { + let text = if self.candidates.is_empty() { + "No profiles.".into() + } else { + "No profiles match your search.".into() }; - let thread_profile_id = self.provider.profile_id(cx); + Some(text) + } + + fn match_count(&self) -> usize { + self.filtered_entries.len() + } - let entry = ContextMenuEntry::new(profile_name.clone()) - .toggleable(IconPosition::End, profile_id == thread_profile_id); + fn selected_index(&self) -> usize { + self.selected_index + } + + fn set_selected_index(&mut self, ix: usize, _: &mut Window, cx: &mut Context>) { + self.selected_index = ix.min(self.filtered_entries.len().saturating_sub(1)); + cx.notify(); + } - let entry = if let Some(doc_text) = documentation { - entry.documentation_aside( - documentation_side(settings.dock), - DocumentationEdge::Top, - move |_| Label::new(doc_text).into_any_element(), + fn can_select( + &mut self, + ix: usize, + _window: &mut Window, + _cx: &mut Context>, + ) -> bool { + match self.filtered_entries.get(ix) { + Some(ProfilePickerEntry::Profile(_)) => true, + Some(ProfilePickerEntry::Header(_)) | None => false, + } + } + + fn update_matches( + &mut self, + query: String, + window: &mut Window, + cx: &mut Context>, + ) -> Task<()> { + if query.is_empty() { + self.query.clear(); + self.filtered_entries = Self::entries_from_candidates(&self.candidates); + self.selected_index = self + .index_of_profile(&self.provider.profile_id(cx)) + .unwrap_or_else(|| self.first_selectable_index().unwrap_or(0)); + cx.notify(); + return Task::ready(()); + } + + if let Some(prev) = &self.cancel { + prev.store(true, Ordering::Relaxed); + } + let cancel = Arc::new(AtomicBool::new(false)); + self.cancel = Some(cancel.clone()); + + let string_candidates = self.string_candidates.clone(); + let background = self.background.clone(); + let provider = self.provider.clone(); + self.query = query.clone(); + + let cancel_for_future = cancel; + + cx.spawn_in(window, async move |this, cx| { + let matches = match_strings( + string_candidates.as_ref(), + &query, + false, + true, + 100, + cancel_for_future.as_ref(), + background, ) - } else { - entry + .await; + + this.update_in(cx, |this, _, cx| { + if this.delegate.query != query { + return; + } + + this.delegate.filtered_entries = this.delegate.entries_from_matches(matches); + this.delegate.selected_index = this + .delegate + .index_of_profile(&provider.profile_id(cx)) + .unwrap_or_else(|| this.delegate.first_selectable_index().unwrap_or(0)); + cx.notify(); + }) + .ok(); + }) + } + + fn confirm(&mut self, _: bool, _window: &mut Window, cx: &mut Context>) { + match self.filtered_entries.get(self.selected_index) { + Some(ProfilePickerEntry::Profile(entry)) => { + if let Some(candidate) = self.candidates.get(entry.candidate_index) { + let profile_id = candidate.id.clone(); + let fs = self.fs.clone(); + let provider = self.provider.clone(); + + update_settings_file(fs, cx, { + let profile_id = profile_id.clone(); + move |settings, _cx| { + settings + .agent + .get_or_insert_default() + .set_profile(profile_id.0); + } + }); + + provider.set_profile(profile_id.clone(), cx); + + telemetry::event!( + "agent_profile_switched", + profile_id = profile_id.as_str(), + source = "picker" + ); + } + + cx.emit(DismissEvent); + } + _ => {} + } + } + + fn dismissed(&mut self, window: &mut Window, cx: &mut Context>) { + cx.defer_in(window, |picker, window, cx| { + picker.set_query("", window, cx); + }); + cx.emit(DismissEvent); + } + + fn render_match( + &self, + ix: usize, + selected: bool, + _: &mut Window, + cx: &mut Context>, + ) -> Option { + match self.filtered_entries.get(ix)? { + ProfilePickerEntry::Header(label) => Some( + div() + .px_2p5() + .pb_0p5() + .when(ix > 0, |this| { + this.mt_1p5() + .pt_2() + .border_t_1() + .border_color(cx.theme().colors().border_variant) + }) + .child( + Label::new(label.clone()) + .size(LabelSize::XSmall) + .color(Color::Muted), + ) + .into_any_element(), + ), + ProfilePickerEntry::Profile(entry) => { + let candidate = self.candidates.get(entry.candidate_index)?; + let active_id = self.provider.profile_id(cx); + let is_active = active_id == candidate.id; + + Some( + ListItem::new(SharedString::from(candidate.id.0.clone())) + .inset(true) + .spacing(ListItemSpacing::Sparse) + .toggle_state(selected) + .child(HighlightedLabel::new( + candidate.name.clone(), + entry.positions.clone(), + )) + .when(is_active, |this| { + this.end_slot( + div() + .pr_2() + .child(Icon::new(IconName::Check).color(Color::Accent)), + ) + }) + .into_any_element(), + ) + } + } + } + + fn documentation_aside( + &self, + _window: &mut Window, + cx: &mut Context>, + ) -> Option { + use std::rc::Rc; + + let entry = match self.filtered_entries.get(self.selected_index)? { + ProfilePickerEntry::Profile(entry) => entry, + ProfilePickerEntry::Header(_) => return None, }; - entry.handler({ - let fs = self.fs.clone(); - let provider = self.provider.clone(); - move |_window, cx| { - update_settings_file(fs.clone(), cx, { - let profile_id = profile_id.clone(); - move |settings, _cx| { - settings - .agent - .get_or_insert_default() - .set_profile(profile_id.0); - } - }); + let candidate = self.candidates.get(entry.candidate_index)?; + let docs_aside = Self::documentation(candidate)?.to_string(); - provider.set_profile(profile_id.clone(), cx); + let settings = AgentSettings::get_global(cx); + let side = match settings.dock { + settings::DockPosition::Left => DocumentationSide::Right, + settings::DockPosition::Bottom | settings::DockPosition::Right => { + DocumentationSide::Left } + }; + + Some(DocumentationAside { + side, + edge: DocumentationEdge::Top, + render: Rc::new(move |_| Label::new(docs_aside.clone()).into_any_element()), }) } + + fn render_footer( + &self, + _: &mut Window, + cx: &mut Context>, + ) -> Option { + Some( + h_flex() + .w_full() + .border_t_1() + .border_color(cx.theme().colors().border_variant) + .p_1() + .gap_4() + .justify_between() + .child( + Button::new("configure", "Configure") + .icon(IconName::Settings) + .icon_size(IconSize::Small) + .icon_color(Color::Muted) + .icon_position(IconPosition::Start) + .on_click(|_, window, cx| { + window.dispatch_action(ManageProfiles::default().boxed_clone(), cx); + }), + ) + .into_any(), + ) + } } -impl Render for ProfileSelector { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - let settings = AgentSettings::get_global(cx); - let profile_id = self.provider.profile_id(cx); - let profile = settings.profiles.get(&profile_id); +#[cfg(test)] +mod tests { + use super::*; + use fs::FakeFs; + use gpui::TestAppContext; - let selected_profile = profile - .map(|profile| profile.name.clone()) - .unwrap_or_else(|| "Unknown".into()); + #[gpui::test] + fn entries_include_custom_profiles(_cx: &mut TestAppContext) { + let candidates = vec![ + ProfileCandidate { + id: AgentProfileId("write".into()), + name: SharedString::from("Write"), + is_builtin: true, + }, + ProfileCandidate { + id: AgentProfileId("my-custom".into()), + name: SharedString::from("My Custom"), + is_builtin: false, + }, + ]; - if self.provider.profiles_supported(cx) { - let this = cx.entity(); - let focus_handle = self.focus_handle.clone(); - let trigger_button = Button::new("profile-selector-model", selected_profile) - .label_size(LabelSize::Small) - .color(Color::Muted) - .icon(IconName::ChevronDown) - .icon_size(IconSize::XSmall) - .icon_position(IconPosition::End) - .icon_color(Color::Muted) - .selected_style(ButtonStyle::Tinted(TintColor::Accent)); - - PopoverMenu::new("profile-selector") - .trigger_with_tooltip(trigger_button, { - move |window, cx| { - Tooltip::for_action_in( - "Toggle Profile Menu", - &ToggleProfileSelector, - &focus_handle, - window, - cx, - ) - } - }) - .anchor( - if documentation_side(settings.dock) == DocumentationSide::Left { - gpui::Corner::BottomRight - } else { - gpui::Corner::BottomLeft - }, - ) - .with_handle(self.menu_handle.clone()) - .menu(move |window, cx| { - Some(this.update(cx, |this, cx| this.build_context_menu(window, cx))) - }) - .offset(gpui::Point { - x: px(0.0), - y: px(-2.0), - }) - .into_any_element() - } else { - Button::new("tools-not-supported-button", "Tools Unsupported") - .disabled(true) - .label_size(LabelSize::Small) - .color(Color::Muted) - .tooltip(Tooltip::text("This model does not support tools.")) - .into_any_element() + let entries = ProfilePickerDelegate::entries_from_candidates(&candidates); + + assert!(entries.iter().any(|entry| matches!( + entry, + ProfilePickerEntry::Profile(profile) + if candidates[profile.candidate_index].id.as_str() == "my-custom" + ))); + assert!(entries.iter().any(|entry| matches!( + entry, + ProfilePickerEntry::Header(label) if label.as_ref() == "Custom Profiles" + ))); + } + + #[gpui::test] + fn fuzzy_filter_returns_no_results_and_keeps_configure(cx: &mut TestAppContext) { + let candidates = vec![ProfileCandidate { + id: AgentProfileId("write".into()), + name: SharedString::from("Write"), + is_builtin: true, + }]; + + let delegate = ProfilePickerDelegate { + fs: FakeFs::new(cx.executor()), + provider: Arc::new(TestProfileProvider::new(AgentProfileId("write".into()))), + background: cx.executor(), + candidates, + string_candidates: Arc::new(Vec::new()), + filtered_entries: Vec::new(), + selected_index: 0, + query: String::new(), + cancel: None, + }; + + let matches = Vec::new(); // No matches + let _entries = delegate.entries_from_matches(matches); + } + + #[gpui::test] + fn active_profile_selection_logic_works(cx: &mut TestAppContext) { + let candidates = vec![ + ProfileCandidate { + id: AgentProfileId("write".into()), + name: SharedString::from("Write"), + is_builtin: true, + }, + ProfileCandidate { + id: AgentProfileId("ask".into()), + name: SharedString::from("Ask"), + is_builtin: true, + }, + ]; + + let delegate = ProfilePickerDelegate { + fs: FakeFs::new(cx.executor()), + provider: Arc::new(TestProfileProvider::new(AgentProfileId("write".into()))), + background: cx.executor(), + candidates, + string_candidates: Arc::new(Vec::new()), + filtered_entries: vec![ + ProfilePickerEntry::Profile(ProfileMatchEntry { + candidate_index: 0, + positions: Vec::new(), + }), + ProfilePickerEntry::Profile(ProfileMatchEntry { + candidate_index: 1, + positions: Vec::new(), + }), + ], + selected_index: 0, + query: String::new(), + cancel: None, + }; + + // Active profile should be found at index 0 + let active_index = delegate.index_of_profile(&AgentProfileId("write".into())); + assert_eq!(active_index, Some(0)); + } + + struct TestProfileProvider { + profile_id: AgentProfileId, + } + + impl TestProfileProvider { + fn new(profile_id: AgentProfileId) -> Self { + Self { profile_id } } } -} -fn documentation_side(position: DockPosition) -> DocumentationSide { - match position { - DockPosition::Left => DocumentationSide::Right, - DockPosition::Bottom => DocumentationSide::Left, - DockPosition::Right => DocumentationSide::Left, + impl ProfileProvider for TestProfileProvider { + fn profile_id(&self, _cx: &App) -> AgentProfileId { + self.profile_id.clone() + } + + fn set_profile(&self, _profile_id: AgentProfileId, _cx: &mut App) {} + + fn profiles_supported(&self, _cx: &App) -> bool { + true + } } } diff --git a/crates/agent_ui/src/terminal_inline_assistant.rs b/crates/agent_ui/src/terminal_inline_assistant.rs index e7070c0d7fc4878c1f73a6d5f874607422ae53d6..4385d2420511c8a148b2a7a58fa8845bd2c19a07 100644 --- a/crates/agent_ui/src/terminal_inline_assistant.rs +++ b/crates/agent_ui/src/terminal_inline_assistant.rs @@ -238,7 +238,7 @@ impl TerminalInlineAssistant { let latest_output = terminal.last_n_non_empty_lines(DEFAULT_CONTEXT_LINES); let working_directory = terminal .working_directory() - .map(|path| path.to_string_lossy().to_string()); + .map(|path| path.to_string_lossy().into_owned()); (latest_output, working_directory) }) .ok() diff --git a/crates/agent_ui/src/text_thread_editor.rs b/crates/agent_ui/src/text_thread_editor.rs index e01bfc1d0d0745ed612429bc2dae71967fde36d5..e1265e923ef06b0bad945d3751dfbf24e0f1ee00 100644 --- a/crates/agent_ui/src/text_thread_editor.rs +++ b/crates/agent_ui/src/text_thread_editor.rs @@ -17,6 +17,7 @@ use editor::{ BlockPlacement, BlockProperties, BlockStyle, Crease, CreaseMetadata, CustomBlockId, FoldId, RenderBlock, ToDisplayPoint, }, + scroll::ScrollOffset, }; use editor::{FoldPlaceholder, display_map::CreaseId}; use fs::Fs; @@ -108,7 +109,7 @@ pub enum InsertDraggedFiles { #[derive(Copy, Clone, Debug, PartialEq)] struct ScrollPosition { - offset_before_cursor: gpui::Point, + offset_before_cursor: gpui::Point, cursor: Anchor, } @@ -631,7 +632,7 @@ impl TextThreadEditor { let snapshot = editor.snapshot(window, cx); let cursor_point = scroll_position.cursor.to_display_point(&snapshot); let scroll_top = - cursor_point.row().as_f32() - scroll_position.offset_before_cursor.y; + cursor_point.row().as_f64() - scroll_position.offset_before_cursor.y; editor.set_scroll_position( point(scroll_position.offset_before_cursor.x, scroll_top), window, @@ -979,7 +980,7 @@ impl TextThreadEditor { let cursor_row = cursor .to_display_point(&snapshot.display_snapshot) .row() - .as_f32(); + .as_f64(); let scroll_position = editor .scroll_manager .anchor() diff --git a/crates/agent_ui/src/ui/burn_mode_tooltip.rs b/crates/agent_ui/src/ui/burn_mode_tooltip.rs index 72faaa614d0d531365fef9ba5ff0e62a6fbcf145..f95dc1250e36bba388452ce11e6ec783e44248e1 100644 --- a/crates/agent_ui/src/ui/burn_mode_tooltip.rs +++ b/crates/agent_ui/src/ui/burn_mode_tooltip.rs @@ -48,7 +48,7 @@ impl Render for BurnModeTooltip { let keybinding = KeyBinding::for_action(&ToggleBurnMode, window, cx) .map(|kb| kb.size(rems_from_px(12.))); - tooltip_container(window, cx, |this, _, _| { + tooltip_container(cx, |this, _| { this .child( h_flex() diff --git a/crates/agent_ui/src/ui/context_pill.rs b/crates/agent_ui/src/ui/context_pill.rs index c42d33c6d346cf869e7b27dc04c63202b3502068..f85a06455439d8e52a7b4272bc7f8069f36548ac 100644 --- a/crates/agent_ui/src/ui/context_pill.rs +++ b/crates/agent_ui/src/ui/context_pill.rs @@ -704,7 +704,7 @@ impl ContextPillHover { impl Render for ContextPillHover { fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { - tooltip_container(window, cx, move |this, window, cx| { + tooltip_container(cx, move |this, cx| { this.occlude() .on_mouse_move(|_, _, cx| cx.stop_propagation()) .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation()) diff --git a/crates/agent_ui/src/ui/onboarding_modal.rs b/crates/agent_ui/src/ui/onboarding_modal.rs index b8b038bdfca334ba048aed0a59cecd0f3da285b0..ad404afa784974631f914e6fece2de6b6c7d6a46 100644 --- a/crates/agent_ui/src/ui/onboarding_modal.rs +++ b/crates/agent_ui/src/ui/onboarding_modal.rs @@ -40,7 +40,7 @@ impl AgentOnboardingModal { } fn view_blog(&mut self, _: &ClickEvent, _: &mut Window, cx: &mut Context) { - cx.open_url("http://zed.dev/blog/fastest-ai-code-editor"); + cx.open_url("https://zed.dev/blog/fastest-ai-code-editor"); cx.notify(); agent_onboarding_event!("Blog Link Clicked"); diff --git a/crates/agent_ui/src/ui/unavailable_editing_tooltip.rs b/crates/agent_ui/src/ui/unavailable_editing_tooltip.rs index 78d4c64e0acc7bff86516657f76007e78a54d304..2993fb89a989619ecfe3d79b06d82a2a6f71fc31 100644 --- a/crates/agent_ui/src/ui/unavailable_editing_tooltip.rs +++ b/crates/agent_ui/src/ui/unavailable_editing_tooltip.rs @@ -12,8 +12,8 @@ impl UnavailableEditingTooltip { } impl Render for UnavailableEditingTooltip { - fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { - tooltip_container(window, cx, |this, _, _| { + fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { + tooltip_container(cx, |this, _| { this.child(Label::new("Unavailable Editing")).child( div().max_w_64().child( Label::new(format!( diff --git a/crates/anthropic/src/anthropic.rs b/crates/anthropic/src/anthropic.rs index a9a4fbb6340b59cafe3bbc555efb6ad5c9e11806..93334fd950f27309000ff175aeafb18767bd2867 100644 --- a/crates/anthropic/src/anthropic.rs +++ b/crates/anthropic/src/anthropic.rs @@ -67,7 +67,6 @@ pub enum Model { alias = "claude-opus-4-1-thinking-latest" )] ClaudeOpus4_1Thinking, - #[default] #[serde(rename = "claude-sonnet-4", alias = "claude-sonnet-4-latest")] ClaudeSonnet4, #[serde( @@ -75,6 +74,14 @@ pub enum Model { alias = "claude-sonnet-4-thinking-latest" )] ClaudeSonnet4Thinking, + #[default] + #[serde(rename = "claude-sonnet-4-5", alias = "claude-sonnet-4-5-latest")] + ClaudeSonnet4_5, + #[serde( + rename = "claude-sonnet-4-5-thinking", + alias = "claude-sonnet-4-5-thinking-latest" + )] + ClaudeSonnet4_5Thinking, #[serde(rename = "claude-3-7-sonnet", alias = "claude-3-7-sonnet-latest")] Claude3_7Sonnet, #[serde( @@ -133,6 +140,14 @@ impl Model { return Ok(Self::ClaudeOpus4); } + if id.starts_with("claude-sonnet-4-5-thinking") { + return Ok(Self::ClaudeSonnet4_5Thinking); + } + + if id.starts_with("claude-sonnet-4-5") { + return Ok(Self::ClaudeSonnet4_5); + } + if id.starts_with("claude-sonnet-4-thinking") { return Ok(Self::ClaudeSonnet4Thinking); } @@ -180,6 +195,8 @@ impl Model { Self::ClaudeOpus4_1Thinking => "claude-opus-4-1-thinking-latest", Self::ClaudeSonnet4 => "claude-sonnet-4-latest", Self::ClaudeSonnet4Thinking => "claude-sonnet-4-thinking-latest", + Self::ClaudeSonnet4_5 => "claude-sonnet-4-5-latest", + Self::ClaudeSonnet4_5Thinking => "claude-sonnet-4-5-thinking-latest", Self::Claude3_5Sonnet => "claude-3-5-sonnet-latest", Self::Claude3_7Sonnet => "claude-3-7-sonnet-latest", Self::Claude3_7SonnetThinking => "claude-3-7-sonnet-thinking-latest", @@ -197,6 +214,7 @@ impl Model { Self::ClaudeOpus4 | Self::ClaudeOpus4Thinking => "claude-opus-4-20250514", Self::ClaudeOpus4_1 | Self::ClaudeOpus4_1Thinking => "claude-opus-4-1-20250805", Self::ClaudeSonnet4 | Self::ClaudeSonnet4Thinking => "claude-sonnet-4-20250514", + Self::ClaudeSonnet4_5 | Self::ClaudeSonnet4_5Thinking => "claude-sonnet-4-5-20250929", Self::Claude3_5Sonnet => "claude-3-5-sonnet-latest", Self::Claude3_7Sonnet | Self::Claude3_7SonnetThinking => "claude-3-7-sonnet-latest", Self::Claude3_5Haiku => "claude-3-5-haiku-latest", @@ -215,6 +233,8 @@ impl Model { Self::ClaudeOpus4_1Thinking => "Claude Opus 4.1 Thinking", Self::ClaudeSonnet4 => "Claude Sonnet 4", Self::ClaudeSonnet4Thinking => "Claude Sonnet 4 Thinking", + Self::ClaudeSonnet4_5 => "Claude Sonnet 4.5", + Self::ClaudeSonnet4_5Thinking => "Claude Sonnet 4.5 Thinking", Self::Claude3_7Sonnet => "Claude 3.7 Sonnet", Self::Claude3_5Sonnet => "Claude 3.5 Sonnet", Self::Claude3_7SonnetThinking => "Claude 3.7 Sonnet Thinking", @@ -236,6 +256,8 @@ impl Model { | Self::ClaudeOpus4_1Thinking | Self::ClaudeSonnet4 | Self::ClaudeSonnet4Thinking + | Self::ClaudeSonnet4_5 + | Self::ClaudeSonnet4_5Thinking | Self::Claude3_5Sonnet | Self::Claude3_5Haiku | Self::Claude3_7Sonnet @@ -261,6 +283,8 @@ impl Model { | Self::ClaudeOpus4_1Thinking | Self::ClaudeSonnet4 | Self::ClaudeSonnet4Thinking + | Self::ClaudeSonnet4_5 + | Self::ClaudeSonnet4_5Thinking | Self::Claude3_5Sonnet | Self::Claude3_5Haiku | Self::Claude3_7Sonnet @@ -280,6 +304,8 @@ impl Model { | Self::ClaudeOpus4_1Thinking | Self::ClaudeSonnet4 | Self::ClaudeSonnet4Thinking + | Self::ClaudeSonnet4_5 + | Self::ClaudeSonnet4_5Thinking | Self::Claude3_5Sonnet | Self::Claude3_7Sonnet | Self::Claude3_7SonnetThinking @@ -299,6 +325,8 @@ impl Model { | Self::ClaudeOpus4_1Thinking | Self::ClaudeSonnet4 | Self::ClaudeSonnet4Thinking + | Self::ClaudeSonnet4_5 + | Self::ClaudeSonnet4_5Thinking | Self::Claude3_5Sonnet | Self::Claude3_7Sonnet | Self::Claude3_7SonnetThinking @@ -318,6 +346,7 @@ impl Model { Self::ClaudeOpus4 | Self::ClaudeOpus4_1 | Self::ClaudeSonnet4 + | Self::ClaudeSonnet4_5 | Self::Claude3_5Sonnet | Self::Claude3_7Sonnet | Self::Claude3_5Haiku @@ -327,6 +356,7 @@ impl Model { Self::ClaudeOpus4Thinking | Self::ClaudeOpus4_1Thinking | Self::ClaudeSonnet4Thinking + | Self::ClaudeSonnet4_5Thinking | Self::Claude3_7SonnetThinking => AnthropicModelMode::Thinking { budget_tokens: Some(4_096), }, diff --git a/crates/askpass/Cargo.toml b/crates/askpass/Cargo.toml index 297139545caeb1c23386cb9f5d8f97b84791dc10..5c7367b9dbf0fe72fb97b0904bd6570f93270299 100644 --- a/crates/askpass/Cargo.toml +++ b/crates/askpass/Cargo.toml @@ -18,6 +18,7 @@ gpui.workspace = true net.workspace = true proto.workspace = true smol.workspace = true +log.workspace = true tempfile.workspace = true util.workspace = true workspace-hack.workspace = true @@ -25,3 +26,6 @@ zeroize.workspace = true [target.'cfg(target_os = "windows")'.dependencies] windows.workspace = true + +[package.metadata.cargo-machete] +ignored = ["log"] diff --git a/crates/askpass/src/askpass.rs b/crates/askpass/src/askpass.rs index 6405be5966ba512d68a9ab638d1216d678200968..2dfdd792d94b67d2b6e39305b804fcbf1957f41e 100644 --- a/crates/askpass/src/askpass.rs +++ b/crates/askpass/src/askpass.rs @@ -1,8 +1,8 @@ mod encrypted_password; pub use encrypted_password::{EncryptedPassword, ProcessExt}; +use util::paths::PathExt; -#[cfg(target_os = "windows")] use std::sync::OnceLock; use std::{ffi::OsStr, time::Duration}; @@ -14,10 +14,16 @@ use futures::{ }; use gpui::{AsyncApp, BackgroundExecutor, Task}; use smol::fs; -use util::ResultExt as _; +use util::{ResultExt as _, debug_panic}; use crate::encrypted_password::decrypt; +/// Path to the program used for askpass +/// +/// On Unix and remote servers, this defaults to the current executable +/// On Windows, this is set to the CLI variant of zed +static ASKPASS_PROGRAM: OnceLock = OnceLock::new(); + #[derive(PartialEq, Eq)] pub enum AskPassResult { CancelledByUser, @@ -46,10 +52,10 @@ impl AskPassDelegate { Self { tx, _task: task } } - pub async fn ask_password(&mut self, prompt: String) -> Result { + pub async fn ask_password(&mut self, prompt: String) -> Option { let (tx, rx) = oneshot::channel(); - self.tx.send((prompt, tx)).await?; - Ok(rx.await?) + self.tx.send((prompt, tx)).await.ok()?; + rx.await.ok() } } @@ -85,11 +91,15 @@ impl AskPassSession { let askpass_script_path = temp_dir.path().join(ASKPASS_SCRIPT_NAME); let (askpass_opened_tx, askpass_opened_rx) = oneshot::channel::<()>(); let listener = UnixListener::bind(&askpass_socket).context("creating askpass socket")?; - #[cfg(not(target_os = "windows"))] - let zed_path = util::get_shell_safe_zed_path()?; - #[cfg(target_os = "windows")] - let zed_path = std::env::current_exe() - .context("finding current executable path for use in askpass")?; + + let current_exec = + std::env::current_exe().context("Failed to determine current zed executable path.")?; + + let askpass_program = ASKPASS_PROGRAM + .get_or_init(|| current_exec) + .try_shell_safe() + .context("Failed to shell-escape Askpass program path.")? + .to_string(); let (askpass_kill_master_tx, askpass_kill_master_rx) = oneshot::channel::<()>(); let mut kill_tx = Some(askpass_kill_master_tx); @@ -109,12 +119,7 @@ impl AskPassSession { buffer.clear(); } let prompt = String::from_utf8_lossy(&buffer); - if let Some(password) = delegate - .ask_password(prompt.to_string()) - .await - .context("getting askpass password") - .log_err() - { + if let Some(password) = delegate.ask_password(prompt.into_owned()).await { #[cfg(target_os = "windows")] { askpass_secret.get_or_init(|| password.clone()); @@ -137,7 +142,7 @@ impl AskPassSession { }); // Create an askpass script that communicates back to this process. - let askpass_script = generate_askpass_script(&zed_path, &askpass_socket); + let askpass_script = generate_askpass_script(&askpass_program, &askpass_socket); fs::write(&askpass_script_path, askpass_script) .await .with_context(|| format!("creating askpass script at {askpass_script_path:?}"))?; @@ -252,12 +257,17 @@ pub fn main(socket: &str) { } } +pub fn set_askpass_program(path: std::path::PathBuf) { + if ASKPASS_PROGRAM.set(path).is_err() { + debug_panic!("askpass program has already been set"); + } +} + #[inline] #[cfg(not(target_os = "windows"))] -fn generate_askpass_script(zed_path: &str, askpass_socket: &std::path::Path) -> String { +fn generate_askpass_script(askpass_program: &str, askpass_socket: &std::path::Path) -> String { format!( - "{shebang}\n{print_args} | {zed_exe} --askpass={askpass_socket} 2> /dev/null \n", - zed_exe = zed_path, + "{shebang}\n{print_args} | {askpass_program} --askpass={askpass_socket} 2> /dev/null \n", askpass_socket = askpass_socket.display(), print_args = "printf '%s\\0' \"$@\"", shebang = "#!/bin/sh", @@ -266,13 +276,12 @@ fn generate_askpass_script(zed_path: &str, askpass_socket: &std::path::Path) -> #[inline] #[cfg(target_os = "windows")] -fn generate_askpass_script(zed_path: &std::path::Path, askpass_socket: &std::path::Path) -> String { +fn generate_askpass_script(askpass_program: &str, askpass_socket: &std::path::Path) -> String { format!( r#" $ErrorActionPreference = 'Stop'; - ($args -join [char]0) | & "{zed_exe}" --askpass={askpass_socket} 2> $null + ($args -join [char]0) | & "{askpass_program}" --askpass={askpass_socket} 2> $null "#, - zed_exe = zed_path.display(), askpass_socket = askpass_socket.display(), ) } diff --git a/crates/askpass/src/encrypted_password.rs b/crates/askpass/src/encrypted_password.rs index 2684f76e8992fa5f33bbeb07996e834fccbf04fa..b5495df6e43cab8efe7a3e505602df11d03a1092 100644 --- a/crates/askpass/src/encrypted_password.rs +++ b/crates/askpass/src/encrypted_password.rs @@ -67,7 +67,7 @@ impl TryFrom<&str> for EncryptedPassword { unsafe { CryptProtectMemory( value.as_mut_ptr() as _, - len, + padded_length, CRYPTPROTECTMEMORY_SAME_PROCESS, )?; } @@ -97,7 +97,7 @@ pub(crate) fn decrypt(mut password: EncryptedPassword) -> Result { unsafe { CryptUnprotectMemory( password.0.as_mut_ptr() as _, - password.1, + password.0.len().try_into()?, CRYPTPROTECTMEMORY_SAME_PROCESS, ) .context("while decrypting a SSH password")? diff --git a/crates/assistant_slash_command/src/extension_slash_command.rs b/crates/assistant_slash_command/src/extension_slash_command.rs index 301cf65cb45895ff2475b74510264260d827da61..6dd2c05f192358f9fcd843add21df94e301dc6b7 100644 --- a/crates/assistant_slash_command/src/extension_slash_command.rs +++ b/crates/assistant_slash_command/src/extension_slash_command.rs @@ -50,7 +50,7 @@ impl WorktreeDelegate for WorktreeDelegateAdapter { } fn root_path(&self) -> String { - self.0.worktree_root_path().to_string_lossy().to_string() + self.0.worktree_root_path().to_string_lossy().into_owned() } async fn read_text_file(&self, path: &RelPath) -> Result { @@ -61,7 +61,7 @@ impl WorktreeDelegate for WorktreeDelegateAdapter { self.0 .which(binary_name.as_ref()) .await - .map(|path| path.to_string_lossy().to_string()) + .map(|path| path.to_string_lossy().into_owned()) } async fn shell_env(&self) -> Vec<(String, String)> { diff --git a/crates/assistant_slash_commands/src/diagnostics_command.rs b/crates/assistant_slash_commands/src/diagnostics_command.rs index 77048b1108577689ade4a5e149e54e9784ba0d33..3a9c33061575d385652b685dcca70ee87c6cac35 100644 --- a/crates/assistant_slash_commands/src/diagnostics_command.rs +++ b/crates/assistant_slash_commands/src/diagnostics_command.rs @@ -6,7 +6,7 @@ use assistant_slash_command::{ use fuzzy::{PathMatch, StringMatchCandidate}; use gpui::{App, Entity, Task, WeakEntity}; use language::{ - Anchor, BufferSnapshot, DiagnosticEntry, DiagnosticSeverity, LspAdapterDelegate, + Anchor, BufferSnapshot, DiagnosticEntryRef, DiagnosticSeverity, LspAdapterDelegate, OffsetRangeExt, ToOffset, }; use project::{DiagnosticSummary, PathMatchCandidateSet, Project}; @@ -367,7 +367,7 @@ pub fn collect_buffer_diagnostics( fn collect_diagnostic( output: &mut SlashCommandOutput, - entry: &DiagnosticEntry, + entry: &DiagnosticEntryRef<'_, Anchor>, snapshot: &BufferSnapshot, include_warnings: bool, ) { diff --git a/crates/assistant_slash_commands/src/selection_command.rs b/crates/assistant_slash_commands/src/selection_command.rs index 068a66339b9a3c8619e1050feb4d70b32f97f657..c8692dec718a03af777753f35ae646f245878ed9 100644 --- a/crates/assistant_slash_commands/src/selection_command.rs +++ b/crates/assistant_slash_commands/src/selection_command.rs @@ -139,7 +139,7 @@ pub fn selections_creases( let language_name = language_name.as_deref().unwrap_or(""); let filename = snapshot .file_at(range.start) - .map(|file| file.full_path(cx).to_string_lossy().to_string()); + .map(|file| file.full_path(cx).to_string_lossy().into_owned()); let text = if language_name == "markdown" { selected_text .lines() diff --git a/crates/assistant_tools/src/edit_file_tool.rs b/crates/assistant_tools/src/edit_file_tool.rs index c8b7add0b8f17f70d2157681b3ae115535a7616e..f88978650a32cdc6922a1ff864b0ee898721df80 100644 --- a/crates/assistant_tools/src/edit_file_tool.rs +++ b/crates/assistant_tools/src/edit_file_tool.rs @@ -17,7 +17,7 @@ use editor::{ use futures::StreamExt; use gpui::{ Animation, AnimationExt, AnyWindowHandle, App, AppContext, AsyncApp, Entity, Task, - TextStyleRefinement, WeakEntity, pulsating_between, px, + TextStyleRefinement, WeakEntity, pulsating_between, }; use indoc::formatdoc; use language::{ @@ -1003,7 +1003,7 @@ impl ToolCard for EditFileToolCard { font_size: Some( TextSize::Small .rems(cx) - .to_pixels(ThemeSettings::get_global(cx).agent_font_size(cx)) + .to_pixels(ThemeSettings::get_global(cx).agent_ui_font_size(cx)) .into(), ), ..TextStyleRefinement::default() @@ -1102,7 +1102,7 @@ impl ToolCard for EditFileToolCard { .relative() .h_full() .when(!self.full_height_expanded, |editor_container| { - editor_container.max_h(px(COLLAPSED_LINES as f32 * editor_line_height.0)) + editor_container.max_h(COLLAPSED_LINES as f32 * editor_line_height) }) .overflow_hidden() .border_t_1() @@ -1161,7 +1161,7 @@ async fn build_buffer( LineEnding::normalize(&mut text); let text = Rope::from(text); let language = cx - .update(|_cx| language_registry.language_for_file_path(&path))? + .update(|_cx| language_registry.load_language_for_file_path(&path))? .await .ok(); let buffer = cx.new(|cx| { diff --git a/crates/assistant_tools/src/find_path_tool.rs b/crates/assistant_tools/src/find_path_tool.rs index 53da3106d2f9fa5fd7928b4291cc8e80daa3bfdb..0bc478251cb5d3d558dda4fb41df02e85eaafde2 100644 --- a/crates/assistant_tools/src/find_path_tool.rs +++ b/crates/assistant_tools/src/find_path_tool.rs @@ -262,7 +262,7 @@ impl ToolCard for FindPathToolCard { .children(self.paths.iter().enumerate().map(|(index, path)| { let path_clone = path.clone(); let workspace_clone = workspace.clone(); - let button_label = path.to_string_lossy().to_string(); + let button_label = path.to_string_lossy().into_owned(); Button::new(("path", index), button_label) .icon(IconName::ArrowUpRight) diff --git a/crates/assistant_tools/src/open_tool.rs b/crates/assistant_tools/src/open_tool.rs index 6dbf66749b932804df6e00fed726360f0492c7f3..a1aafad041364b0ffca01cc1890c2cc10b3d7b01 100644 --- a/crates/assistant_tools/src/open_tool.rs +++ b/crates/assistant_tools/src/open_tool.rs @@ -104,7 +104,7 @@ mod tests { async fn test_to_absolute_path(cx: &mut TestAppContext) { init_test(cx); let temp_dir = TempDir::new().expect("Failed to create temp directory"); - let temp_path = temp_dir.path().to_string_lossy().to_string(); + let temp_path = temp_dir.path().to_string_lossy().into_owned(); let fs = FakeFs::new(cx.executor()); fs.insert_tree( diff --git a/crates/assistant_tools/src/terminal_tool.rs b/crates/assistant_tools/src/terminal_tool.rs index 8014a39e23137ad71b91e5c24d5d79699b530e5d..bc6f5f2a612bf17468577624e34d49119f3813c8 100644 --- a/crates/assistant_tools/src/terminal_tool.rs +++ b/crates/assistant_tools/src/terminal_tool.rs @@ -18,7 +18,7 @@ use portable_pty::{CommandBuilder, PtySize, native_pty_system}; use project::Project; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::Settings; +use settings::{Settings, SettingsLocation}; use std::{ env, path::{Path, PathBuf}, @@ -27,12 +27,13 @@ use std::{ time::{Duration, Instant}, }; use task::{Shell, ShellBuilder}; +use terminal::terminal_settings::TerminalSettings; use terminal_view::TerminalView; use theme::ThemeSettings; use ui::{CommonAnimationExt, Disclosure, Tooltip, prelude::*}; use util::{ - ResultExt, get_default_system_shell, markdown::MarkdownInlineCode, size::format_file_size, - time::duration_alt_display, + ResultExt, get_default_system_shell_preferring_bash, markdown::MarkdownInlineCode, + size::format_file_size, time::duration_alt_display, }; use workspace::Workspace; @@ -119,17 +120,29 @@ impl Tool for TerminalTool { }; let cwd = working_dir.clone(); - let env = match &working_dir { + let env = match &cwd { Some(dir) => project.update(cx, |project, cx| { - project.directory_environment(dir.as_path().into(), cx) + let worktree = project.find_worktree(dir.as_path(), cx); + let shell = TerminalSettings::get( + worktree.as_ref().map(|(worktree, path)| SettingsLocation { + worktree_id: worktree.read(cx).id(), + path: &path, + }), + cx, + ) + .shell + .clone(); + project.directory_environment(&shell, dir.as_path().into(), cx) }), None => Task::ready(None).shared(), }; - let remote_shell = project.update(cx, |project, cx| { - project - .remote_client() - .and_then(|r| r.read(cx).default_system_shell()) - }); + let shell = project + .update(cx, |project, cx| { + project + .remote_client() + .and_then(|r| r.read(cx).default_system_shell()) + }) + .unwrap_or_else(|| get_default_system_shell_preferring_bash()); let env = cx.spawn(async move |_| { let mut env = env.await.unwrap_or_default(); @@ -139,18 +152,22 @@ impl Tool for TerminalTool { env }); + let build_cmd = { + let input_command = input.command.clone(); + move || { + ShellBuilder::new(&Shell::Program(shell)) + .redirect_stdin_to_dev_null() + .build(Some(input_command), &[]) + } + }; + let Some(window) = window else { // Headless setup, a test or eval. Our terminal subsystem requires a workspace, // so bypass it and provide a convincing imitation using a pty. let task = cx.background_spawn(async move { let env = env.await; let pty_system = native_pty_system(); - let (command, args) = ShellBuilder::new( - remote_shell.as_deref(), - &Shell::Program(get_default_system_shell()), - ) - .redirect_stdin_to_dev_null() - .build(Some(input.command.clone()), &[]); + let (command, args) = build_cmd(); let mut cmd = CommandBuilder::new(command); cmd.args(args); for (k, v) in env { @@ -187,16 +204,10 @@ impl Tool for TerminalTool { }; }; - let command = input.command.clone(); let terminal = cx.spawn({ let project = project.downgrade(); async move |cx| { - let (command, args) = ShellBuilder::new( - remote_shell.as_deref(), - &Shell::Program(get_default_system_shell()), - ) - .redirect_stdin_to_dev_null() - .build(Some(input.command), &[]); + let (command, args) = build_cmd(); let env = env.await; project .update(cx, |project, cx| { @@ -215,18 +226,18 @@ impl Tool for TerminalTool { } }); - let command_markdown = - cx.new(|cx| Markdown::new(format!("```bash\n{}\n```", command).into(), None, None, cx)); - - let card = cx.new(|cx| { - TerminalToolCard::new( - command_markdown.clone(), - working_dir.clone(), - cx.entity_id(), + let command_markdown = cx.new(|cx| { + Markdown::new( + format!("```bash\n{}\n```", input.command).into(), + None, + None, cx, ) }); + let card = + cx.new(|cx| TerminalToolCard::new(command_markdown, working_dir, cx.entity_id(), cx)); + let output = cx.spawn({ let card = card.clone(); async move |cx| { @@ -267,7 +278,7 @@ impl Tool for TerminalTool { let previous_len = content.len(); let (processed_content, finished_with_empty_output) = process_content( &content, - &command, + &input.command, exit_status.map(portable_pty::ExitStatus::from), ); @@ -475,7 +486,7 @@ impl ToolCard for TerminalToolCard { .as_ref() .cloned() .or_else(|| env::current_dir().ok()) - .map(|path| format!("{}", path.display())) + .map(|path| path.display().to_string()) .unwrap_or_else(|| "current directory".to_string()); let header = h_flex() @@ -693,7 +704,6 @@ mod tests { use serde_json::json; use settings::{Settings, SettingsStore}; use terminal::terminal_settings::TerminalSettings; - use theme::ThemeSettings; use util::{ResultExt as _, test::TempTree}; use super::*; @@ -708,7 +718,7 @@ mod tests { language::init(cx); Project::init_settings(cx); workspace::init_settings(cx); - ThemeSettings::register(cx); + theme::init(theme::LoadThemes::JustBase, cx); TerminalSettings::register(cx); EditorSettings::register(cx); }); diff --git a/crates/audio/src/audio.rs b/crates/audio/src/audio.rs index 379af34751f91cafadd8c4849102624a3aebdcad..955713b7f0105775bb8a6e4d0d315a3fb52fdd07 100644 --- a/crates/audio/src/audio.rs +++ b/crates/audio/src/audio.rs @@ -55,6 +55,7 @@ pub fn init(cx: &mut App) { #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)] pub enum Sound { Joined, + GuestJoined, Leave, Mute, Unmute, @@ -67,6 +68,7 @@ impl Sound { fn file(&self) -> &'static str { match self { Self::Joined => "joined_call", + Self::GuestJoined => "guest_joined_call", Self::Leave => "leave_call", Self::Mute => "mute", Self::Unmute => "unmute", diff --git a/crates/audio/src/audio_settings.rs b/crates/audio/src/audio_settings.rs index cba7d45c31f4674be6a69c10ab34f00e0b8cbbd1..61a993c3358e5e2bf39b626a0764833508bee742 100644 --- a/crates/audio/src/audio_settings.rs +++ b/crates/audio/src/audio_settings.rs @@ -42,7 +42,7 @@ pub struct AudioSettings { /// Configuration of audio in Zed impl Settings for AudioSettings { - fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self { + fn from_settings(content: &settings::SettingsContent) -> Self { let audio = &content.audio.as_ref().unwrap(); AudioSettings { rodio_audio: audio.rodio_audio.unwrap(), diff --git a/crates/auto_update/src/auto_update.rs b/crates/auto_update/src/auto_update.rs index 31121aa6f6d68984e1b9a81a4e500d6a3e9c21d1..2d1ea7269e2ab102962faeae848d94a8c491d8f2 100644 --- a/crates/auto_update/src/auto_update.rs +++ b/crates/auto_update/src/auto_update.rs @@ -127,7 +127,7 @@ struct AutoUpdateSetting(bool); /// /// Default: true impl Settings for AutoUpdateSetting { - fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self { + fn from_settings(content: &settings::SettingsContent) -> Self { Self(content.auto_update.unwrap()) } } @@ -310,10 +310,10 @@ impl AutoUpdater { // the app after an update, we use `set_restart_path` to run the auto // update helper instead of the app, so that it can overwrite the app // and then spawn the new binary. - let quit_subscription = Some(cx.on_app_quit(|_, _| async move { - #[cfg(target_os = "windows")] - finalize_auto_update_on_quit(); - })); + #[cfg(target_os = "windows")] + let quit_subscription = Some(cx.on_app_quit(|_, _| finalize_auto_update_on_quit())); + #[cfg(not(target_os = "windows"))] + let quit_subscription = None; cx.on_app_restart(|this, _| { this.quit_subscription.take(); @@ -942,11 +942,12 @@ async fn install_release_windows(downloaded_installer: PathBuf) -> Result