Compare commits

..

1 Commits

Author SHA1 Message Date
Antonio Scandurra
df43eb9590 WIP 2024-11-08 12:54:27 +01:00
889 changed files with 22874 additions and 65398 deletions

View File

@@ -13,12 +13,6 @@ rustflags = ["-C", "link-arg=-fuse-ld=mold"]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
[target.aarch64-apple-darwin]
rustflags = ["-C", "link-args=-Objc -all_load"]
[target.x86_64-apple-darwin]
rustflags = ["-C", "link-args=-Objc -all_load"]
# This cfg will reduce the size of `windows::core::Error` from 16 bytes to 4 bytes
[target.'cfg(target_os = "windows")']
rustflags = ["--cfg", "windows_slim_errors"]

View File

@@ -3,6 +3,15 @@ export default {
const url = new URL(request.url);
url.hostname = "docs-anw.pages.dev";
// These pages were removed, but may still be served due to Cloudflare's
// [asset retention](https://developers.cloudflare.com/pages/configuration/serving-pages/#asset-retention).
if (
url.pathname === "/docs/assistant/context-servers" ||
url.pathname === "/docs/assistant/model-context-protocol"
) {
return await fetch("https://zed.dev/404");
}
let res = await fetch(url, request);
if (res.status === 404) {

View File

@@ -19,7 +19,7 @@ body:
id: environment
attributes:
label: Environment
description: Run the `copy system specs into clipboard` command palette action and paste the output in the field below. If you are unable to run the command, please include your Zed version and release channel, operating system and version, RAM amount, and architecture.
description: Run the `copy system specs into clipboard` command palette action and paste the output in the field below.
validations:
required: true
- type: textarea

View File

@@ -21,13 +21,13 @@ body:
id: environment
attributes:
label: Environment
description: Run the `copy system specs into clipboard` command palette action and paste the output in the field below. If you are unable to run the command, please include your Zed version and release channel, operating system and version, RAM amount, and architecture.
description: Run the `copy system specs into clipboard` command palette action and paste the output in the field below.
validations:
required: true
- type: textarea
attributes:
label: If applicable, add screenshots or screencasts of the incorrect state / behavior
description: Drag images / videos into the text input below
label: If applicable, add mockups / screenshots to help explain present your vision of the feature
description: Drag issues into the text input below
validations:
required: false
- type: textarea

View File

@@ -20,7 +20,7 @@ body:
id: environment
attributes:
label: Environment
description: Run the `copy system specs into clipboard` command palette action and paste the output in the field below. If you are unable to run the command, please include your Zed version and release channel, operating system and version, RAM amount, and architecture.
description: Run the `copy system specs into clipboard` command palette action and paste the output in the field below.
validations:
required: true
- type: textarea

View File

@@ -1,4 +1,3 @@
# yaml-language-server: $schema=https://json.schemastore.org/github-issue-config.json
blank_issues_enabled: false
contact_links:
- name: Language Request

View File

@@ -113,12 +113,6 @@ jobs:
script/check-licenses
script/generate-licenses /tmp/zed_licenses_output
- name: Check for new vulnerable dependencies
if: github.event_name == 'pull_request'
uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4
with:
license-check: false
- name: Run tests
uses: ./.github/actions/run_tests
@@ -129,9 +123,7 @@ jobs:
run: |
cargo build --workspace --bins --all-features
cargo check -p gpui --features "macos-blade"
cargo check -p workspace
cargo build -p remote_server
script/check-rust-livekit-macos
linux_tests:
timeout-minutes: 60
@@ -163,10 +155,8 @@ jobs:
- name: Run tests
uses: ./.github/actions/run_tests
- name: Build other binaries and features
run: |
cargo build -p zed
cargo check -p workspace
- name: Build Zed
run: cargo build -p zed
build_remote_server:
timeout-minutes: 60
@@ -255,7 +245,6 @@ jobs:
# 25 was chosen arbitrarily.
fetch-depth: 25
clean: false
ref: ${{ github.ref }}
- name: Limit target directory size
run: script/clear-target-dir-if-larger-than 100
@@ -272,9 +261,6 @@ jobs:
mkdir -p target/
# Ignore any errors that occur while drafting release notes to not fail the build.
script/draft-release-notes "$RELEASE_VERSION" "$RELEASE_CHANNEL" > target/release-notes.md || true
script/create-draft-release target/release-notes.md
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Generate license file
run: script/generate-licenses
@@ -282,12 +268,18 @@ jobs:
- name: Create macOS app bundle
run: script/bundle-mac
- name: Rename binaries
- name: Rename single-architecture binaries
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
run: |
mv target/aarch64-apple-darwin/release/Zed.dmg target/aarch64-apple-darwin/release/Zed-aarch64.dmg
mv target/x86_64-apple-darwin/release/Zed.dmg target/x86_64-apple-darwin/release/Zed-x86_64.dmg
- name: Upload app bundle (universal) to workflow run if main branch or specific label
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
with:
name: Zed_${{ github.event.pull_request.head.sha || github.sha }}.dmg
path: target/release/Zed.dmg
- name: Upload app bundle (aarch64) to workflow run if main branch or specific label
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
@@ -313,6 +305,8 @@ jobs:
target/zed-remote-server-macos-aarch64.gz
target/aarch64-apple-darwin/release/Zed-aarch64.dmg
target/x86_64-apple-darwin/release/Zed-x86_64.dmg
target/release/Zed.dmg
body_path: target/release-notes.md
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -359,6 +353,7 @@ jobs:
files: |
target/zed-remote-server-linux-x86_64.gz
target/release/zed-linux-x86_64.tar.gz
body: ""
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -405,18 +400,6 @@ jobs:
files: |
target/zed-remote-server-linux-aarch64.gz
target/release/zed-linux-aarch64.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
auto-release-preview:
name: Auto release preview
if: ${{ startsWith(github.ref, 'refs/tags/v') && endsWith(github.ref, '-pre') && !endsWith(github.ref, '.0-pre') }}
needs: [bundle-mac, bundle-linux, bundle-linux-aarch64]
runs-on:
- self-hosted
- bundle
steps:
- name: gh release
run: gh release edit $GITHUB_REF_NAME --draft=false
body: ""
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -8,11 +8,11 @@ on:
jobs:
update_top_ranking_issues:
runs-on: ubuntu-latest
if: github.repository == 'zed-industries/zed'
if: github.repository_owner == 'zed-industries'
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Set up uv
uses: astral-sh/setup-uv@caf0cab7a618c569241d31dcd442f54681755d39 # v3
uses: astral-sh/setup-uv@2e657c127d5b1635d5a8e3fa40e0ac50a5bf6992 # v3
with:
version: "latest"
enable-cache: true

View File

@@ -8,11 +8,11 @@ on:
jobs:
update_top_ranking_issues:
runs-on: ubuntu-latest
if: github.repository == 'zed-industries/zed'
if: github.repository_owner == 'zed-industries'
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Set up uv
uses: astral-sh/setup-uv@caf0cab7a618c569241d31dcd442f54681755d39 # v3
uses: astral-sh/setup-uv@2e657c127d5b1635d5a8e3fa40e0ac50a5bf6992 # v3
with:
version: "latest"
enable-cache: true

View File

@@ -37,28 +37,28 @@ jobs:
mdbook build ./docs --dest-dir=../target/deploy/docs/
- name: Deploy Docs
uses: cloudflare/wrangler-action@6d58852c35a27e6034745c5d0bc373d739014f7f # v3
uses: cloudflare/wrangler-action@05f17c4a695b4d94b57b59997562c6a4624c64e4 # v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy target/deploy --project-name=docs
- name: Deploy Install
uses: cloudflare/wrangler-action@6d58852c35a27e6034745c5d0bc373d739014f7f # v3
uses: cloudflare/wrangler-action@05f17c4a695b4d94b57b59997562c6a4624c64e4 # v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: r2 object put -f script/install.sh zed-open-source-website-assets/install.sh
- name: Deploy Docs Workers
uses: cloudflare/wrangler-action@6d58852c35a27e6034745c5d0bc373d739014f7f # v3
uses: cloudflare/wrangler-action@05f17c4a695b4d94b57b59997562c6a4624c64e4 # v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy .cloudflare/docs-proxy/src/worker.js
- name: Deploy Install Workers
uses: cloudflare/wrangler-action@6d58852c35a27e6034745c5d0bc373d739014f7f # v3
uses: cloudflare/wrangler-action@05f17c4a695b4d94b57b59997562c6a4624c64e4 # v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}

View File

@@ -140,7 +140,7 @@ jobs:
name: Create a Linux *.tar.gz bundle for ARM
if: github.repository_owner == 'zed-industries'
runs-on:
- buildjet-16vcpu-ubuntu-2204-arm
- hosted-linux-arm-1
needs: tests
env:
DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}

View File

@@ -1,21 +0,0 @@
name: Script
on:
pull_request:
paths:
- "script/**"
push:
branches:
- main
jobs:
shellcheck:
name: "ShellCheck Scripts"
if: github.repository_owner == 'zed-industries'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Shellcheck ./scripts
run: |
./script/shellcheck-scripts error

1
.gitignore vendored
View File

@@ -1,5 +1,4 @@
/.direnv
.envrc
.idea
**/target
**/cargo-target

View File

@@ -22,14 +22,10 @@ Antonio Scandurra <me@as-cii.com> <antonio@zed.dev>
Bennet Bo Fenner <bennet@zed.dev>
Bennet Bo Fenner <bennet@zed.dev> <53836821+bennetbo@users.noreply.github.com>
Bennet Bo Fenner <bennet@zed.dev> <bennetbo@gmx.de>
Boris Cherny <boris@anthropic.com>
Boris Cherny <boris@anthropic.com> <boris@performancejs.com>
Chris Hayes <chris+git@hayes.software>
Christian Bergschneider <christian.bergschneider@gmx.de>
Christian Bergschneider <christian.bergschneider@gmx.de> <magiclake@gmx.de>
Conrad Irwin <conrad@zed.dev>
Conrad Irwin <conrad@zed.dev> <conrad.irwin@gmail.com>
Dairon Medina <dairon.medina@gmail.com>
Danilo Leal <danilo@zed.dev>
Danilo Leal <danilo@zed.dev> <67129314+danilo-leal@users.noreply.github.com>
Evren Sen <nervenes@icloud.com>
@@ -39,7 +35,6 @@ Fernando Tagawa <tagawafernando@gmail.com>
Fernando Tagawa <tagawafernando@gmail.com> <fernando.tagawa.gamail.com@gmail.com>
Greg Morenz <greg-morenz@droid.cafe>
Greg Morenz <greg-morenz@droid.cafe> <morenzg@gmail.com>
Ihnat Aŭtuška <autushka.ihnat@gmail.com>
Ivan Žužak <izuzak@gmail.com>
Ivan Žužak <izuzak@gmail.com> <ivan.zuzak@github.com>
Joseph T. Lyons <JosephTLyons@gmail.com>
@@ -66,13 +61,10 @@ Max Brunsfeld <maxbrunsfeld@gmail.com> <max@zed.dev>
Max Linke <maxlinke88@gmail.com>
Max Linke <maxlinke88@gmail.com> <kain88-de@users.noreply.github.com>
Michael Sloan <michael@zed.dev>
Michael Sloan <michael@zed.dev> <mgsloan@gmail.com>
Michael Sloan <michael@zed.dev> <mgsloan@google.com>
Mikayla Maki <mikayla@zed.dev>
Mikayla Maki <mikayla@zed.dev> <mikayla.c.maki@gmail.com>
Mikayla Maki <mikayla@zed.dev> <mikayla.c.maki@icloud.com>
Muhammad Talal Anwar <mail@talal.io>
Muhammad Talal Anwar <mail@talal.io> <talalanwar@outlook.com>
Nate Butler <iamnbutler@gmail.com>
Nate Butler <iamnbutler@gmail.com> <nate@zed.dev>
Nathan Sobo <nathan@zed.dev>
@@ -96,11 +88,7 @@ Robert Clover <git@clo4.net>
Robert Clover <git@clo4.net> <robert@clover.gdn>
Roy Williams <roy.williams.iii@gmail.com>
Roy Williams <roy.williams.iii@gmail.com> <roy@anthropic.com>
Sebastijan Kelnerič <sebastijan.kelneric@sebba.dev>
Sebastijan Kelnerič <sebastijan.kelneric@sebba.dev> <sebastijan.kelneric@vichava.com>
Sergey Onufrienko <sergey@onufrienko.com>
Shish <webmaster@shishnet.org>
Shish <webmaster@shishnet.org> <shish@shishnet.org>
Thorben Kröger <dev@thorben.net>
Thorben Kröger <dev@thorben.net> <thorben.kroeger@hexagon.com>
Thorsten Ball <thorsten@zed.dev>

3615
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,13 +5,10 @@ members = [
"crates/anthropic",
"crates/assets",
"crates/assistant",
"crates/assistant2",
"crates/assistant_slash_command",
"crates/assistant_tool",
"crates/assistant_tools",
"crates/audio",
"crates/auto_update",
"crates/auto_update_ui",
"crates/breadcrumbs",
"crates/call",
"crates/channel",
@@ -23,8 +20,7 @@ members = [
"crates/collections",
"crates/command_palette",
"crates/command_palette_hooks",
"crates/context_server",
"crates/context_server_settings",
"crates/context_servers",
"crates/copilot",
"crates/db",
"crates/diagnostics",
@@ -53,21 +49,16 @@ members = [
"crates/http_client",
"crates/image_viewer",
"crates/indexed_docs",
"crates/inline_completion",
"crates/inline_completion_button",
"crates/install_cli",
"crates/journal",
"crates/language",
"crates/language_extension",
"crates/language_model",
"crates/language_model_selector",
"crates/language_models",
"crates/language_selector",
"crates/language_tools",
"crates/languages",
"crates/livekit_client",
"crates/livekit_client_macos",
"crates/livekit_server",
"crates/live_kit_client",
"crates/live_kit_server",
"crates/lsp",
"crates/markdown",
"crates/markdown_preview",
@@ -87,6 +78,7 @@ members = [
"crates/project_panel",
"crates/project_symbols",
"crates/proto",
"crates/quick_action_bar",
"crates/recent_projects",
"crates/refineable",
"crates/refineable/derive_refineable",
@@ -122,7 +114,6 @@ members = [
"crates/terminal_view",
"crates/text",
"crates/theme",
"crates/theme_extension",
"crates/theme_importer",
"crates/theme_selector",
"crates/time_format",
@@ -135,14 +126,11 @@ members = [
"crates/util",
"crates/vcs_menu",
"crates/vim",
"crates/vim_mode_setting",
"crates/welcome",
"crates/workspace",
"crates/worktree",
"crates/zed",
"crates/zed_actions",
"crates/zeta",
"crates/git_ui",
#
# Extensions
@@ -160,6 +148,7 @@ members = [
"extensions/haskell",
"extensions/html",
"extensions/lua",
"extensions/ocaml",
"extensions/php",
"extensions/perplexity",
"extensions/prisma",
@@ -193,13 +182,10 @@ ai = { path = "crates/ai" }
anthropic = { path = "crates/anthropic" }
assets = { path = "crates/assets" }
assistant = { path = "crates/assistant" }
assistant2 = { path = "crates/assistant2" }
assistant_slash_command = { path = "crates/assistant_slash_command" }
assistant_tool = { path = "crates/assistant_tool" }
assistant_tools = { path = "crates/assistant_tools" }
audio = { path = "crates/audio" }
auto_update = { path = "crates/auto_update" }
auto_update_ui = { path = "crates/auto_update_ui" }
breadcrumbs = { path = "crates/breadcrumbs" }
call = { path = "crates/call" }
channel = { path = "crates/channel" }
@@ -211,8 +197,7 @@ collab_ui = { path = "crates/collab_ui" }
collections = { path = "crates/collections" }
command_palette = { path = "crates/command_palette" }
command_palette_hooks = { path = "crates/command_palette_hooks" }
context_server = { path = "crates/context_server" }
context_server_settings = { path = "crates/context_server_settings" }
context_servers = { path = "crates/context_servers" }
copilot = { path = "crates/copilot" }
db = { path = "crates/db" }
diagnostics = { path = "crates/diagnostics" }
@@ -228,33 +213,25 @@ fs = { path = "crates/fs" }
fsevent = { path = "crates/fsevent" }
fuzzy = { path = "crates/fuzzy" }
git = { path = "crates/git" }
git_ui = { path = "crates/git_ui" }
git_hosting_providers = { path = "crates/git_hosting_providers" }
go_to_line = { path = "crates/go_to_line" }
google_ai = { path = "crates/google_ai" }
gpui = { path = "crates/gpui", default-features = false, features = [
"http_client",
] }
gpui = { path = "crates/gpui", default-features = false, features = ["http_client"]}
gpui_macros = { path = "crates/gpui_macros" }
html_to_markdown = { path = "crates/html_to_markdown" }
http_client = { path = "crates/http_client" }
image_viewer = { path = "crates/image_viewer" }
indexed_docs = { path = "crates/indexed_docs" }
inline_completion = { path = "crates/inline_completion" }
inline_completion_button = { path = "crates/inline_completion_button" }
install_cli = { path = "crates/install_cli" }
journal = { path = "crates/journal" }
language = { path = "crates/language" }
language_extension = { path = "crates/language_extension" }
language_model = { path = "crates/language_model" }
language_model_selector = { path = "crates/language_model_selector" }
language_models = { path = "crates/language_models" }
language_selector = { path = "crates/language_selector" }
language_tools = { path = "crates/language_tools" }
languages = { path = "crates/languages" }
livekit_client = { path = "crates/livekit_client" }
livekit_client_macos = { path = "crates/livekit_client_macos" }
livekit_server = { path = "crates/livekit_server" }
live_kit_client = { path = "crates/live_kit_client" }
live_kit_server = { path = "crates/live_kit_server" }
lsp = { path = "crates/lsp" }
markdown = { path = "crates/markdown" }
markdown_preview = { path = "crates/markdown_preview" }
@@ -276,6 +253,7 @@ project = { path = "crates/project" }
project_panel = { path = "crates/project_panel" }
project_symbols = { path = "crates/project_symbols" }
proto = { path = "crates/proto" }
quick_action_bar = { path = "crates/quick_action_bar" }
recent_projects = { path = "crates/recent_projects" }
refineable = { path = "crates/refineable" }
release_channel = { path = "crates/release_channel" }
@@ -310,7 +288,6 @@ terminal = { path = "crates/terminal" }
terminal_view = { path = "crates/terminal_view" }
text = { path = "crates/text" }
theme = { path = "crates/theme" }
theme_extension = { path = "crates/theme_extension" }
theme_importer = { path = "crates/theme_importer" }
theme_selector = { path = "crates/theme_selector" }
time_format = { path = "crates/time_format" }
@@ -322,13 +299,11 @@ ui_macros = { path = "crates/ui_macros" }
util = { path = "crates/util" }
vcs_menu = { path = "crates/vcs_menu" }
vim = { path = "crates/vim" }
vim_mode_setting = { path = "crates/vim_mode_setting" }
welcome = { path = "crates/welcome" }
workspace = { path = "crates/workspace" }
worktree = { path = "crates/worktree" }
zed = { path = "crates/zed" }
zed_actions = { path = "crates/zed_actions" }
zeta = { path = "crates/zeta" }
#
# External crates
@@ -339,7 +314,7 @@ alacritty_terminal = { git = "https://github.com/alacritty/alacritty", rev = "91
any_vec = "0.14"
anyhow = "1.0.86"
arrayvec = { version = "0.7.4", features = ["serde"] }
ashpd = { version = "0.10", default-features = false, features = ["async-std"]}
ashpd = "0.9.1"
async-compat = "0.2.1"
async-compression = { version = "0.4", features = ["gzip", "futures-io"] }
async-dispatcher = "0.1"
@@ -348,7 +323,7 @@ async-pipe = { git = "https://github.com/zed-industries/async-pipe-rs", rev = "8
async-recursion = "1.0.0"
async-tar = "0.5.0"
async-trait = "0.1"
async-tungstenite = "0.28"
async-tungstenite = "0.24"
async-watch = "0.3.1"
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
base64 = "0.22"
@@ -358,12 +333,12 @@ blade-macros = { git = "https://github.com/kvark/blade", rev = "e142a3a5e678eb6a
blade-util = { git = "https://github.com/kvark/blade", rev = "e142a3a5e678eb6a13e642ad8401b1f3aa38e969" }
blake3 = "1.5.3"
bytes = "1.0"
cargo_metadata = "0.19"
cargo_metadata = "0.18"
cargo_toml = "0.20"
chrono = { version = "0.4", features = ["serde"] }
clap = { version = "4.4", features = ["derive"] }
clickhouse = "0.11.6"
cocoa = "0.26"
cocoa-foundation = "0.2.0"
convert_case = "0.6.0"
core-foundation = "0.9.3"
core-foundation-sys = "0.8.6"
@@ -387,23 +362,18 @@ heed = { version = "0.20.1", features = ["read-txn-no-tls"] }
hex = "0.4.3"
html5ever = "0.27.0"
hyper = "0.14"
http = "1.1"
ignore = "0.4.22"
image = "0.25.1"
indexmap = { version = "1.6.2", features = ["serde"] }
indoc = "2"
itertools = "0.13.0"
jsonwebtoken = "9.3"
jupyter-protocol = { version = "0.5.0" }
jupyter-websocket-client = { version = "0.8.0" }
libc = "0.2"
libsqlite3-sys = { version = "0.30.1", features = ["bundled"] }
linkify = "0.10.0"
livekit = { git = "https://github.com/zed-industries/rust-sdks", rev="799f10133d93ba2a88642cd480d01ec4da53408c", features = ["dispatcher", "services-dispatcher", "rustls-tls-native-roots"], default-features = false }
log = { version = "0.4.16", features = ["kv_unstable_serde", "serde"] }
markup5ever_rcdom = "0.3.0"
nanoid = "0.4"
nbformat = { version = "0.9.0" }
nbformat = "0.3.2"
nix = "0.29"
num-format = "0.4.4"
once_cell = "1.19.0"
@@ -413,12 +383,12 @@ parking_lot = "0.12.1"
pathdiff = "0.2"
pet = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "ffcbf3f28c46633abd5448a52b1f396c322e0d6c" }
pet-fs = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "ffcbf3f28c46633abd5448a52b1f396c322e0d6c" }
pet-conda = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "ffcbf3f28c46633abd5448a52b1f396c322e0d6c" }
pet-core = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "ffcbf3f28c46633abd5448a52b1f396c322e0d6c" }
pet-poetry = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "ffcbf3f28c46633abd5448a52b1f396c322e0d6c" }
pet-reporter = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "ffcbf3f28c46633abd5448a52b1f396c322e0d6c" }
pet-conda = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "ffcbf3f28c46633abd5448a52b1f396c322e0d6c" }
pet-core = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "ffcbf3f28c46633abd5448a52b1f396c322e0d6c" }
pet-poetry = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "ffcbf3f28c46633abd5448a52b1f396c322e0d6c" }
pet-reporter = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "ffcbf3f28c46633abd5448a52b1f396c322e0d6c" }
postage = { version = "0.5", features = ["futures-traits"] }
pretty_assertions = { version = "1.3.0", features = ["unstable"] }
pretty_assertions = "1.3.0"
profiling = "1"
prost = "0.9"
prost-build = "0.9"
@@ -437,12 +407,12 @@ reqwest = { git = "https://github.com/zed-industries/reqwest.git", rev = "fd110f
"stream",
] }
rsa = "0.9.6"
runtimelib = { version = "0.24.0", default-features = false, features = [
runtimelib = { version = "0.16.1", default-features = false, features = [
"async-dispatcher-runtime",
] }
rustc-demangle = "0.1.23"
rust-embed = { version = "8.4", features = ["include-exclude"] }
rustls = "0.21.12"
rustls = "0.20.3"
rustls-native-certs = "0.8.0"
schemars = { version = "0.8", features = ["impl_json_schema"] }
semver = "1.0"
@@ -509,7 +479,7 @@ unindent = "0.1.7"
unicode-segmentation = "1.10"
unicode-script = "0.5.7"
url = "2.2"
uuid = { version = "1.1.2", features = ["v4", "v5", "v7", "serde"] }
uuid = { version = "1.1.2", features = ["v4", "v5", "serde"] }
wasmparser = "0.215"
wasm-encoder = "0.215"
wasmtime = { version = "24", default-features = false, features = [
@@ -578,10 +548,6 @@ features = [
"Win32_UI_WindowsAndMessaging",
]
# TODO livekit https://github.com/RustAudio/cpal/pull/891
[patch.crates-io]
cpal = { git = "https://github.com/zed-industries/cpal", rev = "fd8bc2fd39f1f5fdee5a0690656caff9a26d9d50" }
[profile.dev]
split-debuginfo = "unpacked"
debug = "limited"
@@ -595,46 +561,6 @@ rustybuzz = { opt-level = 3 }
ttf-parser = { opt-level = 3 }
wasmtime-cranelift = { opt-level = 3 }
wasmtime = { opt-level = 3 }
# Build single-source-file crates with cg=1 as it helps make `cargo build` of a whole workspace a bit faster
activity_indicator = { codegen-units = 1 }
assets = { codegen-units = 1 }
breadcrumbs = { codegen-units = 1 }
collections = { codegen-units = 1 }
command_palette = { codegen-units = 1 }
command_palette_hooks = { codegen-units = 1 }
evals = { codegen-units = 1 }
extension_cli = { codegen-units = 1 }
feature_flags = { codegen-units = 1 }
file_icons = { codegen-units = 1 }
fsevent = { codegen-units = 1 }
image_viewer = { codegen-units = 1 }
inline_completion_button = { codegen-units = 1 }
install_cli = { codegen-units = 1 }
journal = { codegen-units = 1 }
menu = { codegen-units = 1 }
notifications = { codegen-units = 1 }
ollama = { codegen-units = 1 }
outline = { codegen-units = 1 }
paths = { codegen-units = 1 }
prettier = { codegen-units = 1 }
project_symbols = { codegen-units = 1 }
refineable = { codegen-units = 1 }
release_channel = { codegen-units = 1 }
reqwest_client = { codegen-units = 1 }
rich_text = { codegen-units = 1 }
semantic_version = { codegen-units = 1 }
session = { codegen-units = 1 }
snippet = { codegen-units = 1 }
snippets_ui = { codegen-units = 1 }
sqlez_macros = { codegen-units = 1 }
story = { codegen-units = 1 }
supermaven_api = { codegen-units = 1 }
telemetry_events = { codegen-units = 1 }
theme_selector = { codegen-units = 1 }
time_format = { codegen-units = 1 }
ui_input = { codegen-units = 1 }
vcs_menu = { codegen-units = 1 }
zed_actions = { codegen-units = 1 }
[profile.release]
debug = "limited"
@@ -684,7 +610,6 @@ new_ret_no_self = { level = "allow" }
# We have a few `next` functions that differ in lifetimes
# compared to Iterator::next. Yet, clippy complains about those.
should_implement_trait = { level = "allow" }
let_underscore_future = "allow"
[workspace.metadata.cargo-machete]
ignored = ["bindgen", "cbindgen", "prost_build", "serde"]

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-blocks"><rect width="7" height="7" x="14" y="3" rx="1"/><path d="M10 21V8a1 1 0 0 0-1-1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-5a1 1 0 0 0-1-1H3"/></svg>

Before

Width:  |  Height:  |  Size: 368 B

View File

@@ -1,5 +1 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17 20H16C14.9391 20 13.9217 19.6629 13.1716 19.0627C12.4214 18.4626 12 17.6487 12 16.8V7.2C12 6.35131 12.4214 5.53737 13.1716 4.93726C13.9217 4.33714 14.9391 4 16 4H17" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7 20H8C9.06087 20 10.0783 19.5786 10.8284 18.8284C11.5786 18.0783 12 17.0609 12 16V15" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7 4H8C9.06087 4 10.0783 4.42143 10.8284 5.17157C11.5786 5.92172 12 6.93913 12 8V9" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-text-cursor"><path d="M17 22h-1a4 4 0 0 1-4-4V6a4 4 0 0 1 4-4h1"/><path d="M7 22h1a4 4 0 0 0 4-4v-1"/><path d="M7 2h1a4 4 0 0 1 4 4v1"/></svg>

Before

Width:  |  Height:  |  Size: 715 B

After

Width:  |  Height:  |  Size: 345 B

View File

@@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-eraser">
<path d="m7 21-4.3-4.3c-1-1-1-2.5 0-3.4l9.6-9.6c1-1 2.5-1 3.4 0l5.6 5.6c1 1 1 2.5 0 3.4L13 21"/>
<path d="M22 21H7"/><path d="m5 11 9 9"/>
</svg>

Before

Width:  |  Height:  |  Size: 365 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-file-diff"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M9 10h6"/><path d="M12 13V7"/><path d="M9 17h6"/></svg>

Before

Width:  |  Height:  |  Size: 348 B

View File

@@ -1,8 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.5 6.66666V8.66666" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.5 5V11" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.5 3V13" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.5 5.33334V10" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.5 4V12" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.5 6.66666V8.66666" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.20721 10.8551C7.67694 10.8551 6.33401 11.0424 5.52243 11.1878C5.15088 11.2543 4.808 10.9114 4.87454 10.5399C5.01987 9.72825 5.20721 8.38532 5.20721 6.85505C5.20721 5.69375 5.09932 4.64034 4.98377 3.84516C4.9431 3.56522 5.40267 3.3722 5.5684 3.60145C6.30333 4.61809 7.44022 6.08806 8.70721 7.35505C9.9742 8.62204 11.4442 9.75893 12.4608 10.4939C12.69 10.6596 12.497 11.1192 12.2171 11.0785C11.4219 10.963 10.3685 10.8551 9.20721 10.8551Z" fill="black" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.11129 10.6816L5.28324 8.85355C5.08798 8.65829 4.7714 8.65829 4.57613 8.85355L3.35355 10.0761C3.15829 10.2714 3.15829 10.588 3.35355 10.7832L5.1816 12.6113C5.37686 12.8066 5.69345 12.8066 5.88871 12.6113L7.11129 11.3887C7.30655 11.1934 7.30655 10.8769 7.11129 10.6816Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 751 B

After

Width:  |  Height:  |  Size: 946 B

View File

@@ -1,5 +0,0 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.5 3L8.5 10" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5 6.5H12" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5 13H12" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 415 B

View File

@@ -34,7 +34,6 @@
"dat": "storage",
"db": "storage",
"dbf": "storage",
"diff": "diff",
"dll": "storage",
"doc": "document",
"docx": "document",
@@ -59,11 +58,6 @@
"gitignore": "vcs",
"gitkeep": "vcs",
"gitmodules": "vcs",
"TAG_EDITMSG": "vcs",
"MERGE_MSG": "vcs",
"COMMIT_EDITMSG": "vcs",
"NOTES_EDITMSG": "vcs",
"EDIT_DESCRIPTION": "vcs",
"gleam": "gleam",
"go": "go",
"gql": "graphql",
@@ -113,13 +107,11 @@
"mdf": "storage",
"mdx": "document",
"metadata": "code",
"metal": "metal",
"mjs": "javascript",
"mka": "audio",
"mkv": "video",
"ml": "ocaml",
"mli": "ocaml",
"mod": "go",
"mov": "video",
"mp3": "audio",
"mp4": "video",
@@ -135,7 +127,6 @@
"ogg": "audio",
"opus": "audio",
"otf": "font",
"pcss": "css",
"pdb": "storage",
"pdf": "document",
"php": "php",
@@ -182,9 +173,6 @@
"tsx": "react",
"ttf": "font",
"txt": "document",
"v": "v",
"vsh": "v",
"vv": "v",
"vue": "vue",
"wav": "audio",
"webm": "video",
@@ -193,7 +181,6 @@
"wmv": "video",
"woff": "font",
"woff2": "font",
"work": "go",
"wv": "audio",
"xls": "document",
"xlsx": "document",
@@ -248,9 +235,6 @@
"default": {
"icon": "icons/file_icons/file.svg"
},
"diff": {
"icon": "icons/file_icons/diff.svg"
},
"docker": {
"icon": "icons/file_icons/docker.svg"
},
@@ -323,9 +307,6 @@
"lua": {
"icon": "icons/file_icons/lua.svg"
},
"metal": {
"icon": "icons/file_icons/metal.svg"
},
"nim": {
"icon": "icons/file_icons/nim.svg"
},
@@ -398,9 +379,6 @@
"typescript": {
"icon": "icons/file_icons/typescript.svg"
},
"v": {
"icon": "icons/file_icons/v.svg"
},
"vcs": {
"icon": "icons/file_icons/git.svg"
},

View File

@@ -1 +0,0 @@
<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M4.56 4.502 3.25 3.027V11.5h1.5V6.973l2.69 3.025 1.31 1.475V7.918l3.306 3.582h2.042L8.55 5.491 7.25 4.081V7.528L4.56 4.502Z" fill="#000"/></svg>

Before

Width:  |  Height:  |  Size: 269 B

View File

@@ -1,4 +0,0 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.5" d="M10.0469 12.8661L13.3884 3.31889C13.4386 3.1754 13.3167 3.03055 13.1667 3.05554L10.7292 3.46179C10.5875 3.48542 10.4693 3.58324 10.4197 3.71807L7.24789 12.3271C7.12763 12.6536 7.36919 13 7.71706 13H9.8581C9.94309 13 10.0188 12.9463 10.0469 12.8661Z" fill="black"/>
<path d="M6.90625 12.7321L3.61161 3.31889C3.56139 3.1754 3.6833 3.03055 3.83326 3.05554L6.27076 3.46179C6.4125 3.48542 6.53067 3.58324 6.58034 3.71807L9.90084 12.7309C9.94895 12.8614 9.85232 13 9.71317 13H7.28379C7.11381 13 6.9624 12.8926 6.90625 12.7321Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 663 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-git-branch"><line x1="6" x2="6" y1="3" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></svg>

Before

Width:  |  Height:  |  Size: 348 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-globe"><circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/></svg>

Before

Width:  |  Height:  |  Size: 327 B

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.4662 14.9152C13.5801 15.0291 13.7648 15.0291 13.8787 14.9152L14.9145 13.8793C15.0284 13.7654 15.0284 13.5807 14.9145 13.4667L12.9483 11.5004L14.9145 9.53392C15.0285 9.42004 15.0285 9.23533 14.9145 9.12137L13.8787 8.08547C13.7648 7.97154 13.5801 7.97154 13.4662 8.08547L11.5 10.0519L9.53376 8.08545C9.41988 7.97152 9.23517 7.97152 9.12124 8.08545L8.08543 9.12136C7.97152 9.23533 7.97152 9.42004 8.08543 9.53392L10.0517 11.5004L8.08545 13.4667C7.97155 13.5807 7.97155 13.7654 8.08545 13.8793L9.12126 14.9152C9.23517 15.0292 9.41988 15.0292 9.53376 14.9152L11.5 12.9489L13.4662 14.9152Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 756 B

View File

@@ -1,12 +0,0 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2131_1193)">
<circle cx="7" cy="7" r="6" stroke="black" stroke-width="1.5"/>
<path d="M6 10H7M8 10H7M7 10V7.1C7 7.04477 6.95523 7 6.9 7H6" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
<circle cx="7" cy="4.5" r="1" fill="black"/>
</g>
<defs>
<clipPath id="clip0_2131_1193">
<rect width="14" height="14" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 479 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-keyboard"><path d="M10 8h.01"/><path d="M12 12h.01"/><path d="M14 8h.01"/><path d="M16 12h.01"/><path d="M18 8h.01"/><path d="M6 8h.01"/><path d="M7 16h10"/><path d="M8 12h.01"/><rect width="20" height="16" x="2" y="4" rx="2"/></svg>

Before

Width:  |  Height:  |  Size: 436 B

View File

@@ -1,3 +0,0 @@
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="2.1" y="2.1" width="6.8" height="6.8" rx="3.4" stroke="black" stroke-width="1.8"/>
</svg>

Before

Width:  |  Height:  |  Size: 195 B

View File

@@ -1,3 +0,0 @@
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="3" y="3" width="5" height="5" rx="2.5" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 166 B

View File

@@ -1,10 +0,0 @@
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2080_948)">
<path d="M1.52492 8.72287L1.04243 9.55H2H9H9.95757L9.47508 8.72287L5.97508 2.72287L5.5 1.90845L5.02492 2.72287L1.52492 8.72287Z" stroke="black" stroke-width="1.1"/>
</g>
<defs>
<clipPath id="clip0_2080_948">
<rect width="11" height="11" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 412 B

View File

@@ -1,3 +0,0 @@
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 9H9L5.5 3L2 9Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 146 B

View File

@@ -1,10 +0,0 @@
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2050_903)">
<path d="M1.83327 2.89393L1.19687 3.53033L1.83327 4.16672L3.16654 5.5L1.83327 6.83327L1.19687 7.46967L1.83327 8.10606L2.89393 9.16672L3.53033 9.80312L4.16672 9.16672L5.5 7.83345L6.83327 9.16672L7.46967 9.80312L8.10606 9.16672L9.16672 8.10606L9.80312 7.46967L9.16672 6.83327L7.83345 5.5L9.16672 4.16672L9.80312 3.53033L9.16672 2.89393L8.10606 1.83327L7.46967 1.19687L6.83327 1.83327L5.5 3.16654L4.16672 1.83327L3.53033 1.19687L2.89393 1.83327L1.83327 2.89393Z" stroke="black" stroke-width="1.8"/>
</g>
<defs>
<clipPath id="clip0_2050_903">
<rect width="11" height="11" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 743 B

View File

@@ -1,3 +0,0 @@
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 3L5.5 5.5M8 8L5.5 5.5M5.5 5.5L3 8M5.5 5.5L8 3" stroke="black" stroke-width="1.5"/>
</svg>

Before

Width:  |  Height:  |  Size: 198 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-panel-left"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M9 3v18"/></svg>

Before

Width:  |  Height:  |  Size: 289 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-panel-right"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M15 3v18"/></svg>

Before

Width:  |  Height:  |  Size: 291 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-phone-incoming"><polyline points="16 2 16 8 22 8"/><line x1="22" x2="16" y1="2" y2="8"/><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/></svg>

Before

Width:  |  Height:  |  Size: 594 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-square-dot"><rect width="18" height="18" x="3" y="3" rx="2"/><circle cx="12" cy="12" r="1"/></svg>

Before

Width:  |  Height:  |  Size: 301 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-square-minus"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M8 12h8"/></svg>

Before

Width:  |  Height:  |  Size: 291 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-square-plus"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M8 12h8"/><path d="M12 8v8"/></svg>

Before

Width:  |  Height:  |  Size: 309 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-swatch-book"><path d="M11 17a4 4 0 0 1-8 0V5a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2Z"/><path d="M16.7 13H19a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2H7"/><path d="M 7 17h.01"/><path d="m11 8 2.3-2.3a2.4 2.4 0 0 1 3.404.004L18.6 7.6a2.4 2.4 0 0 1 .026 3.434L9.9 19.8"/></svg>

Before

Width:  |  Height:  |  Size: 456 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-thumbs-down"><path d="M17 14V2"/><path d="M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22a3.13 3.13 0 0 1-3-3.88Z"/></svg>

Before

Width:  |  Height:  |  Size: 405 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-thumbs-up"><path d="M7 10v12"/><path d="M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88Z"/></svg>

Before

Width:  |  Height:  |  Size: 404 B

View File

@@ -1,3 +0,0 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.5 3L3 12H14L8.5 3Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 150 B

View File

@@ -1,3 +0,0 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5 4.5L12 11.5M12 4.5L5 11.5" stroke="black" stroke-width="2" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 199 B

View File

@@ -1,4 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 8.9V11C5.93097 11 5.06903 11 3 11V10.4L8 5.6V5H3V7.1" stroke="black" stroke-width="1.5"/>
<path d="M11 5L13 8L11 11" stroke="black" stroke-width="1.5"/>
</svg>

Before

Width:  |  Height:  |  Size: 268 B

View File

@@ -108,9 +108,7 @@
"ctrl-'": "editor::ToggleHunkDiff",
"ctrl-\"": "editor::ExpandAllHunkDiffs",
"ctrl-i": "editor::ShowSignatureHelp",
"alt-g b": "editor::ToggleGitBlame",
"menu": "editor::OpenContextMenu",
"shift-f10": "editor::OpenContextMenu"
"alt-g b": "editor::ToggleGitBlame"
}
},
{
@@ -144,7 +142,7 @@
"bindings": {
"alt-]": "editor::NextInlineCompletion",
"alt-[": "editor::PreviousInlineCompletion",
"alt-right": "editor::AcceptPartialInlineCompletion"
"ctrl-right": "editor::AcceptPartialInlineCompletion"
}
},
{
@@ -253,8 +251,6 @@
"ctrl-pagedown": "pane::ActivateNextItem",
"ctrl-shift-pageup": "pane::SwapItemLeft",
"ctrl-shift-pagedown": "pane::SwapItemRight",
"back": "pane::GoBack",
"forward": "pane::GoForward",
"ctrl-w": "pane::CloseActiveItem",
"ctrl-f4": "pane::CloseActiveItem",
"alt-ctrl-t": ["pane::CloseInactiveItems", { "close_pinned": false }],
@@ -407,7 +403,7 @@
"ctrl-shift-p": "command_palette::Toggle",
"f1": "command_palette::Toggle",
"ctrl-shift-m": "diagnostics::Deploy",
"ctrl-shift-e": "pane::RevealInProjectPanel",
"ctrl-shift-e": "project_panel::ToggleFocus",
"ctrl-shift-b": "outline_panel::ToggleFocus",
"ctrl-?": "assistant::ToggleFocus",
"ctrl-alt-s": "workspace::SaveAll",
@@ -468,21 +464,13 @@
},
{
"context": "Editor && showing_completions",
"use_key_equivalents": true,
"bindings": {
"enter": "editor::ConfirmCompletion"
}
},
{
"context": "Editor && !inline_completion && showing_completions",
"use_key_equivalents": true,
"bindings": {
"enter": "editor::ConfirmCompletion",
"tab": "editor::ComposeCompletion"
}
},
{
"context": "Editor && inline_completion",
"use_key_equivalents": true,
"context": "Editor && inline_completion && !showing_completions",
"bindings": {
"tab": "editor::AcceptInlineCompletion"
}
@@ -604,7 +592,6 @@
"ctrl-delete": ["project_panel::Delete", { "skip_prompt": false }],
"alt-ctrl-r": "project_panel::RevealInFileManager",
"ctrl-shift-enter": "project_panel::OpenWithSystem",
"ctrl-shift-e": "project_panel::ToggleFocus",
"ctrl-shift-f": "project_panel::NewSearchInDirectory",
"shift-down": "menu::SelectNext",
"shift-up": "menu::SelectPrev",
@@ -662,28 +649,7 @@
},
{
"context": "FileFinder",
"bindings": {
"ctrl": "file_finder::ToggleMenu"
}
},
{
"context": "FileFinder && !menu_open",
"bindings": {
"ctrl-shift-p": "file_finder::SelectPrev",
"ctrl-j": "pane::SplitDown",
"ctrl-k": "pane::SplitUp",
"ctrl-h": "pane::SplitLeft",
"ctrl-l": "pane::SplitRight"
}
},
{
"context": "FileFinder && menu_open",
"bindings": {
"j": "pane::SplitDown",
"k": "pane::SplitUp",
"h": "pane::SplitLeft",
"l": "pane::SplitRight"
}
"bindings": { "ctrl-shift-p": "file_finder::SelectPrev" }
},
{
"context": "TabSwitcher",

View File

@@ -1,7 +1,6 @@
[
// Standard macOS bindings
{
"use_key_equivalents": true,
"bindings": {
"up": "menu::SelectPrev",
"shift-tab": "menu::SelectPrev",
@@ -41,7 +40,6 @@
},
{
"context": "Editor",
"use_key_equivalents": true,
"bindings": {
"escape": "editor::Cancel",
"backspace": "editor::Backspace",
@@ -51,9 +49,8 @@
"ctrl-d": "editor::Delete",
"tab": "editor::Tab",
"shift-tab": "editor::TabPrev",
"ctrl-k": "editor::CutToEndOfLine",
"ctrl-t": "editor::Transpose",
"ctrl-k": "editor::KillRingCut",
"ctrl-y": "editor::KillRingYank",
"cmd-k q": "editor::Rewrap",
"cmd-k cmd-q": "editor::Rewrap",
"cmd-backspace": "editor::DeleteToBeginningOfLine",
@@ -66,7 +63,6 @@
"cmd-v": "editor::Paste",
"cmd-z": "editor::Undo",
"cmd-shift-z": "editor::Redo",
"ctrl-shift-z": "zeta::RateCompletions",
"up": "editor::MoveUp",
"ctrl-up": "editor::MoveToStartOfParagraph",
"pageup": "editor::MovePageUp",
@@ -134,7 +130,6 @@
},
{
"context": "Editor && mode == full",
"use_key_equivalents": true,
"bindings": {
"enter": "editor::Newline",
"shift-enter": "editor::Newline",
@@ -152,23 +147,20 @@
},
{
"context": "Editor && mode == full && inline_completion",
"use_key_equivalents": true,
"bindings": {
"alt-tab": "editor::NextInlineCompletion",
"alt-shift-tab": "editor::PreviousInlineCompletion",
"ctrl-right": "editor::AcceptPartialInlineCompletion"
"alt-]": "editor::NextInlineCompletion",
"alt-[": "editor::PreviousInlineCompletion",
"cmd-right": "editor::AcceptPartialInlineCompletion"
}
},
{
"context": "Editor && !inline_completion",
"use_key_equivalents": true,
"bindings": {
"alt-tab": "editor::ShowInlineCompletion"
"alt-\\": "editor::ShowInlineCompletion"
}
},
{
"context": "Editor && mode == auto_height",
"use_key_equivalents": true,
"bindings": {
"ctrl-enter": "editor::Newline",
"shift-enter": "editor::Newline",
@@ -177,14 +169,12 @@
},
{
"context": "Markdown",
"use_key_equivalents": true,
"bindings": {
"cmd-c": "markdown::Copy"
}
},
{
"context": "Editor && jupyter && !ContextEditor",
"use_key_equivalents": true,
"bindings": {
"ctrl-shift-enter": "repl::Run",
"ctrl-alt-enter": "repl::RunInPlace"
@@ -192,7 +182,6 @@
},
{
"context": "AssistantPanel",
"use_key_equivalents": true,
"bindings": {
"cmd-k c": "assistant::CopyCode",
"cmd-g": "search::SelectNextMatch",
@@ -205,7 +194,6 @@
},
{
"context": "ContextEditor > Editor",
"use_key_equivalents": true,
"bindings": {
"cmd-enter": "assistant::Assist",
"cmd-shift-enter": "assistant::Edit",
@@ -218,24 +206,8 @@
"alt-enter": "editor::Newline"
}
},
{
"context": "AssistantPanel2",
"use_key_equivalents": true,
"bindings": {
"cmd-n": "assistant2::NewThread",
"cmd-shift-h": "assistant2::OpenHistory"
}
},
{
"context": "MessageEditor > Editor",
"use_key_equivalents": true,
"bindings": {
"enter": "assistant2::Chat"
}
},
{
"context": "PromptLibrary",
"use_key_equivalents": true,
"bindings": {
"cmd-n": "prompt_library::NewPrompt",
"cmd-shift-s": "prompt_library::ToggleDefaultPrompt",
@@ -244,7 +216,6 @@
},
{
"context": "BufferSearchBar",
"use_key_equivalents": true,
"bindings": {
"escape": "buffer_search::Dismiss",
"tab": "buffer_search::FocusEditor",
@@ -258,7 +229,6 @@
},
{
"context": "BufferSearchBar && in_replace > Editor",
"use_key_equivalents": true,
"bindings": {
"enter": "search::ReplaceNext",
"cmd-enter": "search::ReplaceAll"
@@ -266,7 +236,6 @@
},
{
"context": "BufferSearchBar && !in_replace > Editor",
"use_key_equivalents": true,
"bindings": {
"up": "search::PreviousHistoryQuery",
"down": "search::NextHistoryQuery"
@@ -274,7 +243,6 @@
},
{
"context": "ProjectSearchBar",
"use_key_equivalents": true,
"bindings": {
"escape": "project_search::ToggleFocus",
"cmd-shift-j": "project_search::ToggleFilters",
@@ -286,7 +254,6 @@
},
{
"context": "ProjectSearchBar > Editor",
"use_key_equivalents": true,
"bindings": {
"up": "search::PreviousHistoryQuery",
"down": "search::NextHistoryQuery"
@@ -294,7 +261,6 @@
},
{
"context": "ProjectSearchBar && in_replace > Editor",
"use_key_equivalents": true,
"bindings": {
"enter": "search::ReplaceNext",
"cmd-enter": "search::ReplaceAll"
@@ -302,7 +268,6 @@
},
{
"context": "ProjectSearchView",
"use_key_equivalents": true,
"bindings": {
"escape": "project_search::ToggleFocus",
"cmd-shift-j": "project_search::ToggleFilters",
@@ -313,7 +278,6 @@
},
{
"context": "Pane",
"use_key_equivalents": true,
"bindings": {
"cmd-{": "pane::ActivatePrevItem",
"cmd-}": "pane::ActivateNextItem",
@@ -342,7 +306,6 @@
// Bindings from VS Code
{
"context": "Editor",
"use_key_equivalents": true,
"bindings": {
"cmd-[": "editor::Outdent",
"cmd-]": "editor::Indent",
@@ -377,7 +340,7 @@
"alt-cmd-f12": "editor::GoToTypeDefinitionSplit",
"alt-shift-f12": "editor::FindAllReferences",
"ctrl-m": "editor::MoveToEnclosingBracket",
"cmd-|": "editor::MoveToEnclosingBracket",
"cmd-shift-\\": "editor::MoveToEnclosingBracket",
"alt-cmd-[": "editor::Fold",
"alt-cmd-]": "editor::UnfoldLines",
"cmd-k cmd-l": "editor::ToggleFold",
@@ -406,7 +369,6 @@
},
{
"context": "Editor && mode == full",
"use_key_equivalents": true,
"bindings": {
"cmd-shift-o": "outline::Toggle",
"ctrl-g": "go_to_line::Toggle"
@@ -414,7 +376,6 @@
},
{
"context": "Pane",
"use_key_equivalents": true,
"bindings": {
"ctrl-1": ["pane::ActivateItem", 0],
"ctrl-2": ["pane::ActivateItem", 1],
@@ -434,7 +395,6 @@
},
{
"context": "Workspace",
"use_key_equivalents": true,
"bindings": {
// Change the default action on `menu::Confirm` by setting the parameter
// "alt-cmd-o": ["projects::OpenRecent", {"create_new_window": true }],
@@ -471,7 +431,7 @@
"ctrl-shift-tab": ["tab_switcher::Toggle", { "select_last": true }],
"cmd-shift-p": "command_palette::Toggle",
"cmd-shift-m": "diagnostics::Deploy",
"cmd-shift-e": "pane::RevealInProjectPanel",
"cmd-shift-e": "project_panel::ToggleFocus",
"cmd-shift-b": "outline_panel::ToggleFocus",
"cmd-?": "assistant::ToggleFocus",
"cmd-alt-s": "workspace::SaveAll",
@@ -490,7 +450,6 @@
},
{
"context": "Workspace && !Terminal",
"use_key_equivalents": true,
"bindings": {
"cmd-shift-r": "task::Spawn",
"cmd-alt-r": "task::Rerun",
@@ -501,7 +460,6 @@
// Bindings from Sublime Text
{
"context": "Editor",
"use_key_equivalents": true,
"bindings": {
"ctrl-j": "editor::JoinLines",
"ctrl-alt-backspace": "editor::DeleteToPreviousSubwordStart",
@@ -521,7 +479,6 @@
// Bindings from Atom
{
"context": "Pane",
"use_key_equivalents": true,
"bindings": {
"cmd-k up": "pane::SplitUp",
"cmd-k down": "pane::SplitDown",
@@ -532,42 +489,31 @@
// Bindings that should be unified with bindings for more general actions
{
"context": "Editor && renaming",
"use_key_equivalents": true,
"bindings": {
"enter": "editor::ConfirmRename"
}
},
{
"context": "Editor && showing_completions",
"use_key_equivalents": true,
"bindings": {
"enter": "editor::ConfirmCompletion"
}
},
{
"context": "Editor && !inline_completion && showing_completions",
"use_key_equivalents": true,
"bindings": {
"enter": "editor::ConfirmCompletion",
"tab": "editor::ComposeCompletion"
}
},
{
"context": "Editor && inline_completion",
"use_key_equivalents": true,
"context": "Editor && inline_completion && !showing_completions",
"bindings": {
"tab": "editor::AcceptInlineCompletion"
}
},
{
"context": "Editor && showing_code_actions",
"use_key_equivalents": true,
"bindings": {
"enter": "editor::ConfirmCodeAction"
}
},
{
"context": "Editor && (showing_code_actions || showing_completions)",
"use_key_equivalents": true,
"bindings": {
"up": "editor::ContextMenuPrev",
"ctrl-p": "editor::ContextMenuPrev",
@@ -579,7 +525,6 @@
},
// Custom bindings
{
"use_key_equivalents": true,
"bindings": {
"ctrl-alt-cmd-f": "workspace::FollowNextCollaborator",
// TODO: Move this to a dock open action
@@ -590,7 +535,6 @@
},
{
"context": "Editor && mode == full",
"use_key_equivalents": true,
"bindings": {
"alt-enter": "editor::OpenExcerpts",
"shift-enter": "editor::ExpandExcerpts",
@@ -602,7 +546,6 @@
},
{
"context": "ProposedChangesEditor",
"use_key_equivalents": true,
"bindings": {
"cmd-shift-y": "editor::ApplyDiffHunk",
"cmd-shift-a": "editor::ApplyAllDiffHunks"
@@ -610,7 +553,6 @@
},
{
"context": "PromptEditor",
"use_key_equivalents": true,
"bindings": {
"ctrl-[": "assistant::CyclePreviousInlineAssist",
"ctrl-]": "assistant::CycleNextInlineAssist"
@@ -618,14 +560,12 @@
},
{
"context": "ProjectSearchBar && !in_replace",
"use_key_equivalents": true,
"bindings": {
"cmd-enter": "project_search::SearchInNew"
}
},
{
"context": "OutlinePanel && not_editing",
"use_key_equivalents": true,
"bindings": {
"escape": "menu::Cancel",
"left": "outline_panel::CollapseSelectedEntry",
@@ -642,7 +582,6 @@
},
{
"context": "ProjectPanel",
"use_key_equivalents": true,
"bindings": {
"left": "project_panel::CollapseSelectedEntry",
"right": "project_panel::ExpandSelectedEntry",
@@ -662,7 +601,6 @@
"cmd-delete": ["project_panel::Delete", { "skip_prompt": false }],
"alt-cmd-r": "project_panel::RevealInFileManager",
"ctrl-shift-enter": "project_panel::OpenWithSystem",
"cmd-shift-e": "project_panel::ToggleFocus",
"cmd-alt-backspace": ["project_panel::Delete", { "skip_prompt": false }],
"cmd-shift-f": "project_panel::NewSearchInDirectory",
"shift-down": "menu::SelectNext",
@@ -672,14 +610,12 @@
},
{
"context": "ProjectPanel && not_editing",
"use_key_equivalents": true,
"bindings": {
"space": "project_panel::Open"
}
},
{
"context": "CollabPanel && not_editing",
"use_key_equivalents": true,
"bindings": {
"ctrl-backspace": "collab_panel::Remove",
"space": "menu::Confirm"
@@ -687,21 +623,18 @@
},
{
"context": "(CollabPanel && editing) > Editor",
"use_key_equivalents": true,
"bindings": {
"space": "collab_panel::InsertSpace"
}
},
{
"context": "ChannelModal",
"use_key_equivalents": true,
"bindings": {
"tab": "channel_modal::ToggleMode"
}
},
{
"context": "Picker > Editor",
"use_key_equivalents": true,
"bindings": {
"tab": "picker::ConfirmCompletion",
"alt-enter": ["picker::ConfirmInput", { "secondary": false }],
@@ -710,42 +643,16 @@
},
{
"context": "ChannelModal > Picker > Editor",
"use_key_equivalents": true,
"bindings": {
"tab": "channel_modal::ToggleMode"
}
},
{
"context": "FileFinder",
"use_key_equivalents": true,
"bindings": {
"cmd": "file_finder::ToggleMenu"
}
},
{
"context": "FileFinder && !menu_open",
"use_key_equivalents": true,
"bindings": {
"cmd-shift-p": "file_finder::SelectPrev",
"cmd-j": "pane::SplitDown",
"cmd-k": "pane::SplitUp",
"cmd-h": "pane::SplitLeft",
"cmd-l": "pane::SplitRight"
}
},
{
"context": "FileFinder && menu_open",
"use_key_equivalents": true,
"bindings": {
"j": "pane::SplitDown",
"k": "pane::SplitUp",
"h": "pane::SplitLeft",
"l": "pane::SplitRight"
}
"bindings": { "cmd-shift-p": "file_finder::SelectPrev" }
},
{
"context": "TabSwitcher",
"use_key_equivalents": true,
"bindings": {
"ctrl-up": "menu::SelectPrev",
"ctrl-down": "menu::SelectNext",
@@ -755,7 +662,6 @@
},
{
"context": "Terminal",
"use_key_equivalents": true,
"bindings": {
"ctrl-cmd-space": "terminal::ShowCharacterPalette",
"cmd-c": "terminal::Copy",
@@ -789,30 +695,7 @@
"cmd-end": "terminal::ScrollToBottom",
"shift-home": "terminal::ScrollToTop",
"shift-end": "terminal::ScrollToBottom",
"ctrl-shift-space": "terminal::ToggleViMode",
"ctrl-k up": "pane::SplitUp",
"ctrl-k down": "pane::SplitDown",
"ctrl-k left": "pane::SplitLeft",
"ctrl-k right": "pane::SplitRight"
}
},
{
"context": "RateCompletionModal",
"use_key_equivalents": true,
"bindings": {
"cmd-enter": "zeta::ThumbsUp",
"shift-down": "zeta::NextEdit",
"shift-up": "zeta::PreviousEdit",
"right": "zeta::PreviewCompletion"
}
},
{
"context": "RateCompletionModal > Editor",
"use_key_equivalents": true,
"bindings": {
"escape": "zeta::FocusCompletions",
"cmd-shift-enter": "zeta::ThumbsUpActiveCompletion",
"cmd-shift-backspace": "zeta::ThumbsDownActiveCompletion"
"ctrl-shift-space": "terminal::ToggleViMode"
}
}
]

View File

@@ -15,10 +15,8 @@
"ctrl-b": "editor::MoveLeft",
"ctrl-n": "editor::MoveDown",
"ctrl-p": "editor::MoveUp",
"home": ["editor::MoveToBeginningOfLine", { "stop_at_soft_wraps": false }],
"end": ["editor::MoveToEndOfLine", { "stop_at_soft_wraps": false }],
"ctrl-a": ["editor::MoveToBeginningOfLine", { "stop_at_soft_wraps": false }],
"ctrl-e": ["editor::MoveToEndOfLine", { "stop_at_soft_wraps": false }],
"ctrl-a": "editor::MoveToBeginningOfLine",
"ctrl-e": "editor::MoveToEndOfLine",
"alt-f": "editor::MoveToNextSubwordEnd",
"alt-b": "editor::MoveToPreviousSubwordStart",
"ctrl-d": "editor::Delete",
@@ -55,14 +53,6 @@
"shift shift": "file_finder::Toggle"
}
},
{
"context": "BufferSearchBar > Editor",
"bindings": {
"ctrl-s": "search::SelectNextMatch",
"ctrl-r": "search::SelectPrevMatch",
"ctrl-g": "buffer_search::Dismiss"
}
},
{
"context": "Pane",
"bindings": {

View File

@@ -1,7 +1,6 @@
[
{
"bindings": {
"ctrl-alt-s": "zed::OpenSettings",
"ctrl-shift-[": "pane::ActivatePrevItem",
"ctrl-shift-]": "pane::ActivateNextItem"
}
@@ -12,7 +11,7 @@
"ctrl->": "zed::IncreaseBufferFontSize",
"ctrl-<": "zed::DecreaseBufferFontSize",
"ctrl-shift-j": "editor::JoinLines",
"ctrl-d": "editor::DuplicateSelection",
"ctrl-d": "editor::DuplicateLineDown",
"ctrl-y": "editor::DeleteLine",
"ctrl-m": "editor::ScrollCursorCenter",
"ctrl-pagedown": "editor::MovePageDown",
@@ -44,7 +43,6 @@
"shift-f2": "editor::GoToPrevDiagnostic",
"ctrl-alt-shift-down": "editor::GoToHunk",
"ctrl-alt-shift-up": "editor::GoToPrevHunk",
"ctrl-alt-z": "editor::RevertSelectedHunks",
"ctrl-home": "editor::MoveToBeginning",
"ctrl-end": "editor::MoveToEnd",
"ctrl-shift-home": "editor::SelectToBeginning",

View File

@@ -4,7 +4,9 @@
"ctrl-shift-[": "pane::ActivatePrevItem",
"ctrl-shift-]": "pane::ActivateNextItem",
"ctrl-pageup": "pane::ActivatePrevItem",
"ctrl-pagedown": "pane::ActivateNextItem"
"ctrl-pagedown": "pane::ActivateNextItem",
"ctrl-tab": "pane::ActivateNextItem",
"ctrl-shift-tab": "pane::ActivatePrevItem"
}
},
{
@@ -15,8 +17,7 @@
"ctrl-shift-m": "editor::SelectLargerSyntaxNode",
"ctrl-shift-l": "editor::SplitSelectionIntoLines",
"ctrl-shift-a": "editor::SelectLargerSyntaxNode",
"ctrl-shift-d": "editor::DuplicateSelection",
"alt-f3": "editor::SelectAllMatches", // find_all_under
"ctrl-shift-d": "editor::DuplicateLineDown",
"f12": "editor::GoToDefinition",
"ctrl-f12": "editor::GoToDefinitionSplit",
"shift-f12": "editor::FindAllReferences",

View File

@@ -15,10 +15,8 @@
"ctrl-b": "editor::MoveLeft",
"ctrl-n": "editor::MoveDown",
"ctrl-p": "editor::MoveUp",
"home": ["editor::MoveToBeginningOfLine", { "stop_at_soft_wraps": false }],
"end": ["editor::MoveToEndOfLine", { "stop_at_soft_wraps": false }],
"ctrl-a": ["editor::MoveToBeginningOfLine", { "stop_at_soft_wraps": false }],
"ctrl-e": ["editor::MoveToEndOfLine", { "stop_at_soft_wraps": false }],
"ctrl-a": "editor::MoveToBeginningOfLine",
"ctrl-e": "editor::MoveToEndOfLine",
"alt-f": "editor::MoveToNextSubwordEnd",
"alt-b": "editor::MoveToPreviousSubwordStart",
"ctrl-d": "editor::Delete",
@@ -55,14 +53,6 @@
"shift shift": "file_finder::Toggle"
}
},
{
"context": "BufferSearchBar > Editor",
"bindings": {
"ctrl-s": "search::SelectNextMatch",
"ctrl-r": "search::SelectPrevMatch",
"ctrl-g": "buffer_search::Dismiss"
}
},
{
"context": "Pane",
"bindings": {

View File

@@ -11,7 +11,7 @@
"ctrl->": "zed::IncreaseBufferFontSize",
"ctrl-<": "zed::DecreaseBufferFontSize",
"ctrl-shift-j": "editor::JoinLines",
"cmd-d": "editor::DuplicateSelection",
"cmd-d": "editor::DuplicateLineDown",
"cmd-backspace": "editor::DeleteLine",
"cmd-pagedown": "editor::MovePageDown",
"cmd-pageup": "editor::MovePageUp",

View File

@@ -4,7 +4,9 @@
"cmd-shift-[": "pane::ActivatePrevItem",
"cmd-shift-]": "pane::ActivateNextItem",
"ctrl-pageup": "pane::ActivatePrevItem",
"ctrl-pagedown": "pane::ActivateNextItem"
"ctrl-pagedown": "pane::ActivateNextItem",
"ctrl-tab": "pane::ActivateNextItem",
"ctrl-shift-tab": "pane::ActivatePrevItem"
}
},
{
@@ -18,8 +20,7 @@
"ctrl-shift-m": "editor::SelectLargerSyntaxNode",
"cmd-shift-l": "editor::SplitSelectionIntoLines",
"cmd-shift-a": "editor::SelectLargerSyntaxNode",
"cmd-shift-d": "editor::DuplicateSelection",
"ctrl-cmd-g": "editor::SelectAllMatches", // find_all_under
"cmd-shift-d": "editor::DuplicateLineDown",
"shift-f12": "editor::FindAllReferences",
"alt-cmd-down": "editor::GoToDefinition",
"ctrl-alt-cmd-down": "editor::GoToDefinitionSplit",

View File

@@ -32,18 +32,6 @@
"(": "vim::SentenceBackward",
")": "vim::SentenceForward",
"|": "vim::GoToColumn",
"] ]": "vim::NextSectionStart",
"] [": "vim::NextSectionEnd",
"[ [": "vim::PreviousSectionStart",
"[ ]": "vim::PreviousSectionEnd",
"] m": "vim::NextMethodStart",
"] M": "vim::NextMethodEnd",
"[ m": "vim::PreviousMethodStart",
"[ M": "vim::PreviousMethodEnd",
"[ *": "vim::PreviousComment",
"[ /": "vim::PreviousComment",
"] *": "vim::NextComment",
"] /": "vim::NextComment",
// Word motions
"w": "vim::NextWordStart",
"e": "vim::NextWordEnd",
@@ -66,10 +54,6 @@
"n": "vim::MoveToNextMatch",
"shift-n": "vim::MoveToPrevMatch",
"%": "vim::Matching",
"] }": ["vim::UnmatchedForward", { "char": "}" }],
"[ {": ["vim::UnmatchedBackward", { "char": "{" }],
"] )": ["vim::UnmatchedForward", { "char": ")" }],
"[ (": ["vim::UnmatchedBackward", { "char": "(" }],
"f": ["vim::PushOperator", { "FindForward": { "before": false } }],
"t": ["vim::PushOperator", { "FindForward": { "before": true } }],
"shift-f": ["vim::PushOperator", { "FindBackward": { "after": false } }],
@@ -219,7 +203,6 @@
"shift-s": "vim::SubstituteLine",
">": ["vim::PushOperator", "Indent"],
"<": ["vim::PushOperator", "Outdent"],
"=": ["vim::PushOperator", "AutoIndent"],
"g u": ["vim::PushOperator", "Lowercase"],
"g shift-u": ["vim::PushOperator", "Uppercase"],
"g ~": ["vim::PushOperator", "OppositeCase"],
@@ -284,7 +267,6 @@
"ctrl-[": ["vim::SwitchMode", "Normal"],
">": "vim::Indent",
"<": "vim::Outdent",
"=": "vim::AutoIndent",
"i": ["vim::PushOperator", { "Object": { "around": false } }],
"a": ["vim::PushOperator", { "Object": { "around": true } }],
"g c": "vim::ToggleComments",
@@ -317,27 +299,9 @@
"ctrl-q": ["vim::PushOperator", { "Literal": {} }],
"ctrl-shift-q": ["vim::PushOperator", { "Literal": {} }],
"ctrl-r": ["vim::PushOperator", "Register"],
"insert": "vim::ToggleReplace",
"ctrl-o": "vim::TemporaryNormal"
"insert": "vim::ToggleReplace"
}
},
{
"context": "vim_mode == helix_normal",
"bindings": {
"i": "vim::InsertBefore",
"a": "vim::InsertAfter",
"d": "vim::HelixDelete",
"w": "vim::NextWordStart",
"e": "vim::NextWordEnd",
"b": "vim::PreviousWordStart",
"h": "vim::Left",
"j": "vim::Down",
"k": "vim::Up",
"l": "vim::Right"
}
},
{
"context": "vim_mode == insert && !(showing_code_actions || showing_completions)",
"bindings": {
@@ -380,8 +344,7 @@
"bindings": {
"escape": "vim::ClearOperators",
"ctrl-c": "vim::ClearOperators",
"ctrl-[": "vim::ClearOperators",
"g c": "vim::Comment"
"ctrl-[": "vim::ClearOperators"
}
},
{
@@ -407,11 +370,8 @@
"shift-b": "vim::CurlyBrackets",
"<": "vim::AngleBrackets",
">": "vim::AngleBrackets",
"a": "vim::Argument",
"i": "vim::IndentObj",
"shift-i": ["vim::IndentObj", { "includeBelow": true }],
"f": "vim::Method",
"c": "vim::Class"
"a": "vim::AngleBrackets",
"g": "vim::Argument"
}
},
{
@@ -486,12 +446,6 @@
"<": "vim::CurrentLine"
}
},
{
"context": "vim_operator == eq",
"bindings": {
"=": "vim::CurrentLine"
}
},
{
"context": "vim_operator == gc",
"bindings": {
@@ -573,12 +527,6 @@
"ctrl-w shift-l": ["workspace::SwapPaneInDirection", "Right"],
"ctrl-w shift-k": ["workspace::SwapPaneInDirection", "Up"],
"ctrl-w shift-j": ["workspace::SwapPaneInDirection", "Down"],
"ctrl-w >": ["vim::ResizePane", "Widen"],
"ctrl-w <": ["vim::ResizePane", "Narrow"],
"ctrl-w -": ["vim::ResizePane", "Shorten"],
"ctrl-w +": ["vim::ResizePane", "Lengthen"],
"ctrl-w _": "vim::MaximizePane",
"ctrl-w =": "vim::ResetPaneSizes",
"ctrl-w g t": "pane::ActivateNextItem",
"ctrl-w ctrl-g t": "pane::ActivateNextItem",
"ctrl-w g shift-t": "pane::ActivatePrevItem",
@@ -605,7 +553,7 @@
}
},
{
"context": "EmptyPane || SharedScreen || MarkdownPreview || KeyContextView || Welcome",
"context": "EmptyPane || SharedScreen || MarkdownPreview || KeyContextView",
"bindings": {
":": "command_palette::Toggle",
"g /": "pane::DeploySearch"
@@ -633,12 +581,6 @@
"p": "project_panel::Open",
"x": "project_panel::RevealInFileManager",
"s": "project_panel::OpenWithSystem",
"] c": "project_panel::SelectNextGitEntry",
"[ c": "project_panel::SelectPrevGitEntry",
"] d": "project_panel::SelectNextDiagnostic",
"[ d": "project_panel::SelectPrevDiagnostic",
"}": "project_panel::SelectNextDirectory",
"{": "project_panel::SelectPrevDirectory",
"shift-g": "menu::SelectLast",
"g g": "menu::SelectFirst",
"-": "project_panel::SelectParent",

View File

@@ -144,20 +144,20 @@
// 4. Highlight the full line (default):
// "all"
"current_line_highlight": "all",
// The debounce delay before querying highlights from the language
// server based on the current cursor location.
"lsp_highlight_debounce": 75,
// Whether to pop the completions menu while typing in an editor without
// explicitly requesting it.
"show_completions_on_input": true,
// Whether to display inline and alongside documentation for items in the
// completions menu
"show_completion_documentation": true,
// The debounce delay before re-querying the language server for completion
// documentation when not included in original completion list.
"completion_documentation_secondary_query_debounce": 300,
// Show method signatures in the editor, when inside parentheses.
"auto_signature_help": false,
/// Whether to show the signature help after completion or a bracket pair inserted.
/// If `auto_signature_help` is enabled, this setting will be treated as enabled also.
"show_signature_help_after_edits": false,
"show_signature_help_after_edits": true,
// Whether to show wrap guides (vertical rulers) in the editor.
// Setting this to true will show a guide at the 'preferred_line_length' value
// if 'soft_wrap' is set to 'preferred_line_length', and will show any
@@ -193,9 +193,6 @@
// Controls whether inline completions are shown immediately (true)
// or manually by triggering `editor::ShowInlineCompletion` (false).
"show_inline_completions": true,
// Controls whether inline completions are shown in a given language scope.
// Example: ["string", "comment"]
"inline_completions_disabled_in": [],
// Whether to show tabs and spaces in the editor.
// This setting can take three values:
//
@@ -300,8 +297,6 @@
"scroll_beyond_last_line": "one_page",
// The number of lines to keep above/below the cursor when scrolling.
"vertical_scroll_margin": 3,
// Whether to scroll when clicking near the edge of the visible text area.
"autoscroll_on_clicks": false,
// Scroll sensitivity multiplier. This multiplier is applied
// to both the horizontal and vertical delta values while scrolling.
"scroll_sensitivity": 1.0,
@@ -385,16 +380,6 @@
/// "never"
"show": null
},
/// Which files containing diagnostic errors/warnings to mark in the project panel.
/// This setting can take the following three values:
///
/// 1. Do not mark any files:
/// "off"
/// 2. Only mark files with errors:
/// "errors"
/// 3. Mark files with errors and warnings:
/// "all"
"show_diagnostics": "all",
// Settings related to indent guides in the project panel.
"indent_guides": {
// When to show indent guides in the project panel.
@@ -474,14 +459,6 @@
// Default width of the chat panel.
"default_width": 240
},
"git_panel": {
// Whether to show the git panel button in the status bar.
"button": true,
// Where to the git panel. Can be 'left' or 'right'.
"dock": "left",
// Default width of the git panel.
"default_width": 360
},
"message_editor": {
// Whether to automatically replace emoji shortcodes with emoji characters.
// For example: typing `:wave:` gets replaced with `👋`.
@@ -567,28 +544,13 @@
"close_position": "right",
// Whether to show the file icon for a tab.
"file_icons": false,
// Whether to always show the close button on tabs.
"always_show_close_button": false,
// What to do after closing the current tab.
//
// 1. Activate the tab that was open previously (default)
// "history"
// 2. Activate the right neighbour tab if present
// "neighbour"
// 3. Activate the left neighbour tab if present
// "left_neighbour"
"activate_on_close": "history",
/// Which files containing diagnostic errors/warnings to mark in the tabs.
/// Diagnostics are only shown when file icons are also active.
/// This setting only works when can take the following three values:
///
/// 1. Do not mark any files:
/// "off"
/// 2. Only mark files with errors:
/// "errors"
/// 3. Mark files with errors and warnings:
/// "all"
"show_diagnostics": "off"
// "History"
// 2. Activate the neighbour tab (prefers the right one, if present)
// "Neighbour"
"activate_on_close": "history"
},
// Settings related to preview tabs.
"preview_tabs": {
@@ -605,23 +567,7 @@
// Settings related to the file finder.
"file_finder": {
// Whether to show file icons in the file finder.
"file_icons": true,
// Determines how much space the file finder can take up in relation to the available window width.
// There are 5 possible width values:
//
// 1. Small: This value is essentially a fixed width.
// "modal_width": "small"
// 2. Medium:
// "modal_width": "medium"
// 3. Large:
// "modal_width": "large"
// 4. Extra Large:
// "modal_width": "xlarge"
// 5. Fullscreen: This value removes any horizontal padding, as it consumes the whole viewport width.
// "modal_width": "full"
//
// Default: small
"modal_max_width": "small"
"file_icons": true
},
// Whether or not to remove any trailing whitespace from lines of a buffer
// before saving it.
@@ -690,23 +636,17 @@
},
// Add files or globs of files that will be excluded by Zed entirely:
// they will be skipped during FS scan(s), file tree and file search
// will lack the corresponding file entries. Overrides `file_scan_inclusions`.
// will lack the corresponding file entries.
"file_scan_exclusions": [
"**/.git",
"**/.svn",
"**/.hg",
"**/.jj",
"**/CVS",
"**/.DS_Store",
"**/Thumbs.db",
"**/.classpath",
"**/.settings"
],
// Add files or globs of files that will be included by Zed, even when
// ignored by git. This is useful for files that are not tracked by git,
// but are still important to your project. Note that globs that are
// overly broad can slow down Zed's file scanning. Overridden by `file_scan_exclusions`.
"file_scan_inclusions": [".env*"],
// Git gutter behavior configuration.
"git": {
// Control whether the git gutter is shown. May take 2 values:
@@ -867,12 +807,8 @@
}
},
"toolbar": {
// Whether to display the terminal title in its toolbar's breadcrumbs.
// Only shown if the terminal title is not empty.
//
// The shell running in the terminal needs to be configured to emit the title.
// Example: `echo -e "\e]2;New Title\007";`
"breadcrumbs": true
// Whether to display the terminal title in its toolbar.
"title": true
}
// Set the terminal's font size. If this option is not included,
// the terminal will default to matching the buffer's font size.
@@ -908,8 +844,15 @@
//
"file_types": {
"Plain Text": ["txt"],
"JSONC": ["**/.zed/**/*.json", "**/zed/**/*.json", "**/Zed/**/*.json", "**/.vscode/**/*.json"],
"Shell Script": [".env.*"]
"JSON": ["flake.lock"],
"JSONC": [
"**/.zed/**/*.json",
"**/zed/**/*.json",
"**/Zed/**/*.json",
"tsconfig.json",
"pyrightconfig.json"
],
"TOML": ["uv.lock"]
},
/// By default use a recent system version of node, or install our own.
/// You can override this to use a version of node that is not in $PATH with:
@@ -1097,11 +1040,13 @@
"api_url": "https://generativelanguage.googleapis.com"
},
"ollama": {
"api_url": "http://localhost:11434"
"api_url": "http://localhost:11434",
"low_speed_timeout_in_seconds": 60
},
"openai": {
"version": "1",
"api_url": "https://api.openai.com/v1"
"api_url": "https://api.openai.com/v1",
"low_speed_timeout_in_seconds": 600
}
},
// Zed's Prettier integration settings.
@@ -1151,7 +1096,6 @@
"use_system_clipboard": "always",
"use_multiline_find": false,
"use_smartcase_find": false,
"highlight_on_yank_duration": 200,
"custom_digraphs": {}
},
// The server to connect to. If the environment variable
@@ -1209,8 +1153,6 @@
// "W": "workspace::Save"
// }
"command_aliases": {},
// Whether to show user picture in titlebar.
"show_user_picture": true,
// ssh_connections is an array of ssh connections.
// You can configure these from `project: Open Remote` in the command palette.
// Zed's ssh support will pull configuration from your ~/.ssh too.
@@ -1227,6 +1169,15 @@
// }
// ]
"ssh_connections": [],
// Configures context servers for use in the Assistant.
"context_servers": {}
// Configures the Context Server Protocol binaries
//
// Examples:
// {
// "id": "server-1",
// "executable": "/path",
// "args": ['arg1", "args2"]
// }
"experimental.context_servers": {
"servers": []
}
}

View File

@@ -1,5 +1,5 @@
{
"$schema": "https://zed.dev/schema/themes/v0.2.0.json",
"$schema": "https://zed.dev/schema/themes/v0.1.0.json",
"name": "Andromeda",
"author": "Zed Industries",
"themes": [
@@ -46,7 +46,7 @@
"tab.active_background": "#1e2025ff",
"search.match_background": "#11a79366",
"panel.background": "#21242bff",
"panel.focused_border": "#10a793ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar.thumb.background": "#f7f7f84c",
"scrollbar.thumb.hover_background": "#252931ff",

View File

@@ -1,5 +1,5 @@
{
"$schema": "https://zed.dev/schema/themes/v0.2.0.json",
"$schema": "https://zed.dev/schema/themes/v0.1.0.json",
"name": "Atelier",
"author": "Zed Industries",
"themes": [
@@ -46,7 +46,7 @@
"tab.active_background": "#19171cff",
"search.match_background": "#576dda66",
"panel.background": "#221f26ff",
"panel.focused_border": "#566ddaff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar.thumb.background": "#efecf44c",
"scrollbar.thumb.hover_background": "#332f38ff",
@@ -431,7 +431,7 @@
"tab.active_background": "#efecf4ff",
"search.match_background": "#586dda66",
"panel.background": "#e6e3ebff",
"panel.focused_border": "#586cdaff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar.thumb.background": "#19171c4c",
"scrollbar.thumb.hover_background": "#cbc8d1ff",

View File

@@ -1,5 +1,5 @@
{
"$schema": "https://zed.dev/schema/themes/v0.2.0.json",
"$schema": "https://zed.dev/schema/themes/v0.1.0.json",
"name": "Ayu",
"author": "Zed Industries",
"themes": [
@@ -46,7 +46,7 @@
"tab.active_background": "#0d1016ff",
"search.match_background": "#5ac2fe66",
"panel.background": "#1f2127ff",
"panel.focused_border": "#5ac1feff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar.thumb.background": "#bfbdb64c",
"scrollbar.thumb.hover_background": "#2d2f34ff",
@@ -416,7 +416,7 @@
"tab.active_background": "#fcfcfcff",
"search.match_background": "#3b9ee566",
"panel.background": "#ececedff",
"panel.focused_border": "#3b9ee5ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar.thumb.background": "#5c61664c",
"scrollbar.thumb.hover_background": "#dfe0e1ff",

View File

@@ -1,5 +1,5 @@
{
"$schema": "https://zed.dev/schema/themes/v0.2.0.json",
"$schema": "https://zed.dev/schema/themes/v0.1.0.json",
"name": "Gruvbox",
"author": "Zed Industries",
"themes": [
@@ -55,7 +55,7 @@
"tab.active_background": "#282828ff",
"search.match_background": "#83a59866",
"panel.background": "#3a3735ff",
"panel.focused_border": "#83a598ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar.thumb.background": "#fbf1c74c",
"scrollbar.thumb.hover_background": "#494340ff",
@@ -439,7 +439,7 @@
"tab.active_background": "#1d2021ff",
"search.match_background": "#83a59866",
"panel.background": "#393634ff",
"panel.focused_border": "#83a598ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar.thumb.background": "#fbf1c74c",
"scrollbar.thumb.hover_background": "#494340ff",

View File

@@ -1,5 +1,5 @@
{
"$schema": "https://zed.dev/schema/themes/v0.2.0.json",
"$schema": "https://zed.dev/schema/themes/v0.1.0.json",
"name": "One",
"author": "Zed Industries",
"themes": [

View File

@@ -1,5 +1,5 @@
{
"$schema": "https://zed.dev/schema/themes/v0.2.0.json",
"$schema": "https://zed.dev/schema/themes/v0.1.0.json",
"name": "Rosé Pine",
"author": "Zed Industries",
"themes": [
@@ -46,7 +46,7 @@
"tab.active_background": "#191724ff",
"search.match_background": "#57949f66",
"panel.background": "#1c1b2aff",
"panel.focused_border": "#9bced6ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar.thumb.background": "#e0def44c",
"scrollbar.thumb.hover_background": "#232132ff",
@@ -426,7 +426,7 @@
"tab.active_background": "#faf4edff",
"search.match_background": "#9cced766",
"panel.background": "#fef9f2ff",
"panel.focused_border": "#57949fff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar.thumb.background": "#5752794c",
"scrollbar.thumb.hover_background": "#e5e0dfff",
@@ -806,7 +806,7 @@
"tab.active_background": "#232136ff",
"search.match_background": "#9cced766",
"panel.background": "#28253cff",
"panel.focused_border": "#9bced6ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar.thumb.background": "#e0def44c",
"scrollbar.thumb.hover_background": "#322f48ff",

View File

@@ -1,5 +1,5 @@
{
"$schema": "https://zed.dev/schema/themes/v0.2.0.json",
"$schema": "https://zed.dev/schema/themes/v0.1.0.json",
"name": "Sandcastle",
"author": "Zed Industries",
"themes": [
@@ -46,7 +46,7 @@
"tab.active_background": "#282c33ff",
"search.match_background": "#528b8b66",
"panel.background": "#2b3038ff",
"panel.focused_border": "#518b8bff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar.thumb.background": "#fdf4c14c",
"scrollbar.thumb.hover_background": "#313741ff",

View File

@@ -1,5 +1,5 @@
{
"$schema": "https://zed.dev/schema/themes/v0.2.0.json",
"$schema": "https://zed.dev/schema/themes/v0.1.0.json",
"name": "Solarized",
"author": "Zed Industries",
"themes": [
@@ -46,7 +46,7 @@
"tab.active_background": "#002a35ff",
"search.match_background": "#288bd166",
"panel.background": "#04313bff",
"panel.focused_border": "#278ad1ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar.thumb.background": "#fdf6e34c",
"scrollbar.thumb.hover_background": "#053541ff",
@@ -416,7 +416,7 @@
"tab.active_background": "#fdf6e3ff",
"search.match_background": "#298bd166",
"panel.background": "#f3eddaff",
"panel.focused_border": "#288bd1ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar.thumb.background": "#002a354c",
"scrollbar.thumb.hover_background": "#dcdacbff",

View File

@@ -1,5 +1,5 @@
{
"$schema": "https://zed.dev/schema/themes/v0.2.0.json",
"$schema": "https://zed.dev/schema/themes/v0.1.0.json",
"name": "Summercamp",
"author": "Zed Industries",
"themes": [
@@ -46,7 +46,7 @@
"tab.active_background": "#1b1810ff",
"search.match_background": "#499bef66",
"panel.background": "#231f16ff",
"panel.focused_border": "#499befff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar.thumb.background": "#f8f5de4c",
"scrollbar.thumb.hover_background": "#29251bff",

View File

@@ -20,7 +20,6 @@ extension_host.workspace = true
futures.workspace = true
gpui.workspace = true
language.workspace = true
lsp.workspace = true
project.workspace = true
smallvec.workspace = true
ui.workspace = true

View File

@@ -7,8 +7,9 @@ use gpui::{
InteractiveElement as _, Model, ParentElement as _, Render, SharedString,
StatefulInteractiveElement, Styled, Transformation, View, ViewContext, VisualContext as _,
};
use language::{LanguageRegistry, LanguageServerBinaryStatus, LanguageServerId};
use lsp::LanguageServerName;
use language::{
LanguageRegistry, LanguageServerBinaryStatus, LanguageServerId, LanguageServerName,
};
use project::{EnvironmentErrorMessage, LanguageServerProgress, Project, WorktreeId};
use smallvec::SmallVec;
use std::{cmp::Reverse, fmt::Write, sync::Arc, time::Duration};

View File

@@ -1,12 +1,13 @@
mod supported_countries;
use std::time::Duration;
use std::{pin::Pin, str::FromStr};
use anyhow::{anyhow, Context, Result};
use chrono::{DateTime, Utc};
use futures::{io::BufReader, stream::BoxStream, AsyncBufReadExt, AsyncReadExt, Stream, StreamExt};
use http_client::http::{HeaderMap, HeaderValue};
use http_client::{AsyncBody, HttpClient, Method, Request as HttpRequest};
use http_client::{AsyncBody, HttpClient, HttpRequestExt, Method, Request as HttpRequest};
use serde::{Deserialize, Serialize};
use strum::{EnumIter, EnumString};
use thiserror::Error;
@@ -160,7 +161,10 @@ pub async fn complete(
.method(Method::POST)
.uri(uri)
.header("Anthropic-Version", "2023-06-01")
.header("Anthropic-Beta", "prompt-caching-2024-07-31")
.header(
"Anthropic-Beta",
"tools-2024-04-04,prompt-caching-2024-07-31,max-tokens-3-5-sonnet-2024-07-15",
)
.header("X-Api-Key", api_key)
.header("Content-Type", "application/json");
@@ -206,8 +210,9 @@ pub async fn stream_completion(
api_url: &str,
api_key: &str,
request: Request,
low_speed_timeout: Option<Duration>,
) -> Result<BoxStream<'static, Result<Event, AnthropicError>>, AnthropicError> {
stream_completion_with_rate_limit_info(client, api_url, api_key, request)
stream_completion_with_rate_limit_info(client, api_url, api_key, request, low_speed_timeout)
.await
.map(|output| output.0)
}
@@ -259,6 +264,7 @@ pub async fn stream_completion_with_rate_limit_info(
api_url: &str,
api_key: &str,
request: Request,
low_speed_timeout: Option<Duration>,
) -> Result<
(
BoxStream<'static, Result<Event, AnthropicError>>,
@@ -271,7 +277,7 @@ pub async fn stream_completion_with_rate_limit_info(
stream: true,
};
let uri = format!("{api_url}/v1/messages");
let request_builder = HttpRequest::builder()
let mut request_builder = HttpRequest::builder()
.method(Method::POST)
.uri(uri)
.header("Anthropic-Version", "2023-06-01")
@@ -281,6 +287,9 @@ pub async fn stream_completion_with_rate_limit_info(
)
.header("X-Api-Key", api_key)
.header("Content-Type", "application/json");
if let Some(low_speed_timeout) = low_speed_timeout {
request_builder = request_builder.read_timeout(low_speed_timeout);
}
let serialized_request =
serde_json::to_string(&request).context("failed to serialize request")?;
let request = request_builder

View File

@@ -33,7 +33,7 @@ client.workspace = true
clock.workspace = true
collections.workspace = true
command_palette_hooks.workspace = true
context_server.workspace = true
context_servers.workspace = true
db.workspace = true
editor.workspace = true
feature_flags.workspace = true
@@ -50,8 +50,6 @@ indexed_docs.workspace = true
indoc.workspace = true
language.workspace = true
language_model.workspace = true
language_model_selector.workspace = true
language_models.workspace = true
log.workspace = true
lsp.workspace = true
markdown.workspace = true

View File

@@ -5,6 +5,7 @@ pub mod assistant_settings;
mod context;
pub mod context_store;
mod inline_assistant;
mod model_selector;
mod patch;
mod prompt_library;
mod prompts;
@@ -14,12 +15,15 @@ pub mod slash_command_settings;
mod slash_command_working_set;
mod streaming_diff;
mod terminal_inline_assistant;
mod tool_working_set;
mod tools;
use crate::slash_command::project_command::ProjectSlashCommandFeatureFlag;
pub use crate::slash_command_working_set::{SlashCommandId, SlashCommandWorkingSet};
pub use crate::tool_working_set::{ToolId, ToolWorkingSet};
pub use assistant_panel::{AssistantPanel, AssistantPanelEvent};
use assistant_settings::AssistantSettings;
use assistant_slash_command::SlashCommandRegistry;
use assistant_tool::ToolRegistry;
use client::{proto, Client};
use command_palette_hooks::CommandPaletteFilter;
pub use context::*;
@@ -28,10 +32,12 @@ use feature_flags::FeatureFlagAppExt;
use fs::Fs;
use gpui::impl_actions;
use gpui::{actions, AppContext, Global, SharedString, UpdateGlobal};
use indexed_docs::IndexedDocsRegistry;
pub(crate) use inline_assistant::*;
use language_model::{
LanguageModelId, LanguageModelProviderId, LanguageModelRegistry, LanguageModelResponseMessage,
};
pub(crate) use model_selector::*;
pub use patch::*;
pub use prompts::PromptBuilder;
use prompts::PromptLoadingParams;
@@ -209,32 +215,23 @@ pub fn init(
});
}
cx.spawn(|mut cx| {
let client = client.clone();
async move {
let is_search_slash_command_enabled = cx
.update(|cx| cx.wait_for_flag::<SearchSlashCommandFeatureFlag>())?
.await;
let is_project_slash_command_enabled = cx
.update(|cx| cx.wait_for_flag::<ProjectSlashCommandFeatureFlag>())?
.await;
if cx.has_flag::<SearchSlashCommandFeatureFlag>() {
cx.spawn(|mut cx| {
let client = client.clone();
async move {
let embedding_provider = CloudEmbeddingProvider::new(client.clone());
let semantic_index = SemanticDb::new(
paths::embeddings_dir().join("semantic-index-db.0.mdb"),
Arc::new(embedding_provider),
&mut cx,
)
.await?;
if !is_search_slash_command_enabled && !is_project_slash_command_enabled {
return Ok(());
cx.update(|cx| cx.set_global(semantic_index))
}
let embedding_provider = CloudEmbeddingProvider::new(client.clone());
let semantic_index = SemanticDb::new(
paths::embeddings_dir().join("semantic-index-db.0.mdb"),
Arc::new(embedding_provider),
&mut cx,
)
.await?;
cx.update(|cx| cx.set_global(semantic_index))
}
})
.detach();
})
.detach();
}
context_store::init(&client.clone().into());
prompt_library::init(cx);
@@ -242,7 +239,7 @@ pub fn init(
assistant_slash_command::init(cx);
assistant_tool::init(cx);
assistant_panel::init(cx);
context_server::init(cx);
context_servers::init(cx);
let prompt_builder = prompts::PromptBuilder::new(Some(PromptLoadingParams {
fs: fs.clone(),
@@ -255,6 +252,7 @@ pub fn init(
.map(Arc::new)
.unwrap_or_else(|| Arc::new(prompts::PromptBuilder::new(None).unwrap()));
register_slash_commands(Some(prompt_builder.clone()), cx);
register_tools(cx);
inline_assistant::init(
fs.clone(),
prompt_builder.clone(),
@@ -267,7 +265,7 @@ pub fn init(
client.telemetry().clone(),
cx,
);
indexed_docs::init(cx);
IndexedDocsRegistry::init_global(cx);
CommandPaletteFilter::update_global(cx, |filter, _cx| {
filter.hide_namespace(Assistant::NAMESPACE);
@@ -342,7 +340,8 @@ fn register_slash_commands(prompt_builder: Option<Arc<PromptBuilder>>, cx: &mut
slash_command_registry.register_command(terminal_command::TerminalSlashCommand, true);
slash_command_registry.register_command(now_command::NowSlashCommand, false);
slash_command_registry.register_command(diagnostics_command::DiagnosticsSlashCommand, true);
slash_command_registry.register_command(fetch_command::FetchSlashCommand, true);
slash_command_registry.register_command(fetch_command::FetchSlashCommand, false);
slash_command_registry.register_command(fetch_command::FetchSlashCommand, false);
if let Some(prompt_builder) = prompt_builder {
cx.observe_flag::<project_command::ProjectSlashCommandFeatureFlag, _>({
@@ -417,6 +416,11 @@ fn update_slash_commands_from_settings(cx: &mut AppContext) {
}
}
fn register_tools(cx: &mut AppContext) {
let tool_registry = ToolRegistry::global(cx);
tool_registry.register_tool(tools::now_tool::NowTool);
}
pub fn humanize_token_count(count: usize) -> String {
match count {
0..=999 => count.to_string(),

View File

@@ -1,5 +1,6 @@
use crate::slash_command::file_command::codeblock_fence_for_path;
use crate::slash_command_working_set::SlashCommandWorkingSet;
use crate::ToolWorkingSet;
use crate::{
assistant_settings::{AssistantDockPosition, AssistantSettings},
humanize_token_count,
@@ -16,13 +17,12 @@ use crate::{
ContextEvent, ContextId, ContextStore, ContextStoreEvent, CopyCode, CycleMessageRole,
DeployHistory, DeployPromptLibrary, Edit, InlineAssistant, InsertDraggedFiles,
InsertIntoEditor, InvokedSlashCommandId, InvokedSlashCommandStatus, Message, MessageId,
MessageMetadata, MessageStatus, NewContext, ParsedSlashCommand, PendingSlashCommandStatus,
QuoteSelection, RemoteContextMetadata, RequestType, SavedContextMetadata, Split, ToggleFocus,
ToggleModelSelector,
MessageMetadata, MessageStatus, ModelPickerDelegate, ModelSelector, NewContext,
ParsedSlashCommand, PendingSlashCommandStatus, QuoteSelection, RemoteContextMetadata,
RequestType, SavedContextMetadata, Split, ToggleFocus, ToggleModelSelector,
};
use anyhow::Result;
use assistant_slash_command::{SlashCommand, SlashCommandOutputSection};
use assistant_tool::ToolWorkingSet;
use client::{proto, zed_urls, Client, Status};
use collections::{hash_map, BTreeSet, HashMap, HashSet};
use editor::{
@@ -50,12 +50,11 @@ use indexed_docs::IndexedDocsStore;
use language::{
language_settings::SoftWrap, BufferSnapshot, LanguageRegistry, LspAdapterDelegate, ToOffset,
};
use language_model::{LanguageModelImage, LanguageModelToolUse};
use language_model::{
LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry, Role,
ZED_CLOUD_PROVIDER_ID,
provider::cloud::PROVIDER_ID, LanguageModelProvider, LanguageModelProviderId,
LanguageModelRegistry, Role,
};
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
use language_model::{LanguageModelImage, LanguageModelToolUse};
use multi_buffer::MultiBufferRow;
use picker::{Picker, PickerDelegate};
use project::lsp_store::LocalLspAdapterDelegate;
@@ -143,7 +142,7 @@ pub struct AssistantPanel {
languages: Arc<LanguageRegistry>,
fs: Arc<dyn Fs>,
subscriptions: Vec<Subscription>,
model_selector_menu_handle: PopoverMenuHandle<LanguageModelSelector>,
model_selector_menu_handle: PopoverMenuHandle<Picker<ModelPickerDelegate>>,
model_summary_editor: View<Editor>,
authenticate_provider_task: Option<(LanguageModelProviderId, Task<()>)>,
configuration_subscription: Option<Subscription>,
@@ -305,7 +304,7 @@ impl PickerDelegate for SavedContextPickerDelegate {
ListItem::new(ix)
.inset(true)
.spacing(ListItemSpacing::Sparse)
.toggle_state(selected)
.selected(selected)
.child(item),
)
}
@@ -341,12 +340,11 @@ impl AssistantPanel {
) -> Self {
let model_selector_menu_handle = PopoverMenuHandle::default();
let model_summary_editor = cx.new_view(Editor::single_line);
let context_editor_toolbar = cx.new_view(|cx| {
let context_editor_toolbar = cx.new_view(|_| {
ContextEditorToolbarItem::new(
workspace,
model_selector_menu_handle.clone(),
model_summary_editor.clone(),
cx,
)
});
@@ -417,6 +415,7 @@ impl AssistantPanel {
ControlFlow::Break(())
});
pane.set_can_split(false, cx);
pane.set_can_navigate(true, cx);
pane.display_nav_history_buttons(None);
pane.set_should_display_tab_bar(|_| true);
@@ -442,16 +441,15 @@ impl AssistantPanel {
)
}
})
.toggle_state(
.selected(
pane.active_item()
.map_or(false, |item| item.downcast::<ContextHistory>().is_some()),
);
let _pane = cx.view().clone();
let right_children = h_flex()
.gap(DynamicSpacing::Base02.rems(cx))
.gap(Spacing::XSmall.rems(cx))
.child(
IconButton::new("new-chat", IconName::Plus)
.icon_size(IconSize::Small)
.on_click(
cx.listener(|_, _, cx| {
cx.dispatch_action(NewContext.boxed_clone())
@@ -666,7 +664,7 @@ impl AssistantPanel {
// If we're signed out and don't have a provider configured, or we're signed-out AND Zed.dev is
// the provider, we want to show a nudge to sign in.
let show_zed_ai_notice = client_status.is_signed_out()
&& active_provider.map_or(true, |provider| provider.id().0 == ZED_CLOUD_PROVIDER_ID);
&& active_provider.map_or(true, |provider| provider.id().0 == PROVIDER_ID);
self.show_zed_ai_notice = show_zed_ai_notice;
cx.notify();
@@ -1086,21 +1084,7 @@ impl AssistantPanel {
self.show_updated_summary(&context_editor, cx);
cx.notify()
}
EditorEvent::Edited { .. } => {
self.workspace
.update(cx, |workspace, cx| {
let is_via_ssh = workspace
.project()
.update(cx, |project, _| project.is_via_ssh());
workspace
.client()
.telemetry()
.log_edit_event("assistant panel", is_via_ssh);
})
.log_err();
cx.emit(AssistantPanelEvent::ContextEdited)
}
EditorEvent::Edited { .. } => cx.emit(AssistantPanelEvent::ContextEdited),
_ => {}
}
}
@@ -1317,7 +1301,7 @@ impl AssistantPanel {
fn restart_context_servers(
workspace: &mut Workspace,
_action: &context_server::Restart,
_action: &context_servers::Restart,
cx: &mut ViewContext<Workspace>,
) {
let Some(assistant_panel) = workspace.panel::<AssistantPanel>(cx) else {
@@ -1482,6 +1466,7 @@ struct ScrollPosition {
}
struct PatchViewState {
footer_block_id: CustomBlockId,
crease_id: CreaseId,
editor: Option<PatchEditorState>,
update_task: Option<Task<()>>,
@@ -1548,7 +1533,6 @@ impl ContextEditor {
cx: &mut ViewContext<Self>,
) -> Self {
let completion_provider = SlashCommandCompletionProvider::new(
context.read(cx).slash_commands.clone(),
Some(cx.view().downgrade()),
Some(workspace.clone()),
);
@@ -1926,7 +1910,7 @@ impl ContextEditor {
Content::ToolUse {
range: tool_use.source_range.clone(),
tool_use: LanguageModelToolUse {
id: tool_use.id.clone(),
id: tool_use.id.to_string(),
name: tool_use.name.clone(),
input: tool_use.input.clone(),
},
@@ -1935,7 +1919,7 @@ impl ContextEditor {
);
});
Crease::inline(
Crease::new(
start..end,
placeholder,
fold_toggle("tool-use"),
@@ -2033,7 +2017,7 @@ impl ContextEditor {
let end = buffer
.anchor_in_excerpt(excerpt_id, command.source_range.end)
.unwrap();
Crease::inline(start..end, placeholder, render_toggle, render_trailer)
Crease::new(start..end, placeholder, render_toggle, render_trailer)
}),
cx,
);
@@ -2052,6 +2036,30 @@ impl ContextEditor {
ContextEvent::SlashCommandOutputSectionAdded { section } => {
self.insert_slash_command_output_sections([section.clone()], false, cx);
}
ContextEvent::SlashCommandFinished {
output_range: _output_range,
run_commands_in_ranges,
} => {
for range in run_commands_in_ranges {
let commands = self.context.update(cx, |context, cx| {
context.reparse(cx);
context
.pending_commands_for_range(range.clone(), cx)
.to_vec()
});
for command in commands {
self.run_command(
command.source_range,
&command.name,
&command.arguments,
false,
self.workspace.clone(),
cx,
);
}
}
}
ContextEvent::UsePendingTools => {
let pending_tool_uses = self
.context
@@ -2101,7 +2109,7 @@ impl ContextEditor {
let buffer_row = MultiBufferRow(start.to_point(&buffer).row);
let crease = Crease::inline(
let crease = Crease::new(
start..end,
placeholder,
fold_toggle("tool-use"),
@@ -2130,37 +2138,6 @@ impl ContextEditor {
command_id: InvokedSlashCommandId,
cx: &mut ViewContext<Self>,
) {
if let Some(invoked_slash_command) =
self.context.read(cx).invoked_slash_command(&command_id)
{
if let InvokedSlashCommandStatus::Finished = invoked_slash_command.status {
let run_commands_in_ranges = invoked_slash_command
.run_commands_in_ranges
.iter()
.cloned()
.collect::<Vec<_>>();
for range in run_commands_in_ranges {
let commands = self.context.update(cx, |context, cx| {
context.reparse(cx);
context
.pending_commands_for_range(range.clone(), cx)
.to_vec()
});
for command in commands {
self.run_command(
command.source_range,
&command.name,
&command.arguments,
false,
self.workspace.clone(),
cx,
);
}
}
}
}
self.editor.update(cx, |editor, cx| {
if let Some(invoked_slash_command) =
self.context.read(cx).invoked_slash_command(&command_id)
@@ -2200,14 +2177,18 @@ impl ContextEditor {
let crease_end = buffer
.anchor_in_excerpt(excerpt_id, invoked_slash_command.range.end)
.unwrap();
let crease = Crease::inline(
crease_start..crease_end,
invoked_slash_command_fold_placeholder(command_id, context),
fold_toggle("invoked-slash-command"),
|_row, _folded, _cx| Empty.into_any(),
let fold_placeholder =
invoked_slash_command_fold_placeholder(command_id, context);
let crease_ids = editor.insert_creases(
[Crease::new(
crease_start..crease_end,
fold_placeholder.clone(),
fold_toggle("invoked-slash-command"),
|_row, _folded, _cx| Empty.into_any(),
)],
cx,
);
let crease_ids = editor.insert_creases([crease.clone()], cx);
editor.fold_creases(vec![crease], false, cx);
editor.fold_ranges([(crease_start..crease_end, fold_placeholder)], false, cx);
entry.insert(crease_ids[0]);
} else {
cx.notify()
@@ -2229,32 +2210,23 @@ impl ContextEditor {
cx: &mut ViewContext<ContextEditor>,
) {
let this = cx.view().downgrade();
let mut removed_crease_ids = Vec::new();
let mut removed_block_ids = HashSet::default();
let mut editors_to_close = Vec::new();
for range in removed {
if let Some(state) = self.patches.remove(range) {
editors_to_close.extend(state.editor.and_then(|state| state.editor.upgrade()));
removed_block_ids.insert(state.footer_block_id);
removed_crease_ids.push(state.crease_id);
}
}
self.editor.update(cx, |editor, cx| {
let snapshot = editor.snapshot(cx);
let multibuffer = &snapshot.buffer_snapshot;
let (&excerpt_id, _, _) = multibuffer.as_singleton().unwrap();
let mut removed_crease_ids = Vec::new();
let mut ranges_to_unfold: Vec<Range<Anchor>> = Vec::new();
for range in removed {
if let Some(state) = self.patches.remove(range) {
let patch_start = multibuffer
.anchor_in_excerpt(excerpt_id, range.start)
.unwrap();
let patch_end = multibuffer
.anchor_in_excerpt(excerpt_id, range.end)
.unwrap();
editors_to_close.extend(state.editor.and_then(|state| state.editor.upgrade()));
ranges_to_unfold.push(patch_start..patch_end);
removed_crease_ids.push(state.crease_id);
}
}
editor.unfold_ranges(&ranges_to_unfold, true, false, cx);
editor.remove_creases(removed_crease_ids, cx);
let mut replaced_blocks = HashMap::default();
for range in updated {
let Some(patch) = self.context.read(cx).patch_for_range(&range, cx).cloned() else {
continue;
@@ -2267,21 +2239,19 @@ impl ContextEditor {
let patch_end = multibuffer
.anchor_in_excerpt(excerpt_id, patch.range.end)
.unwrap();
let render_block: RenderBlock = Arc::new({
let render_block: RenderBlock = Box::new({
let this = this.clone();
let patch_range = range.clone();
move |cx: &mut BlockContext<'_, '_>| {
let max_width = cx.max_width;
let gutter_width = cx.gutter_dimensions.full_width();
let block_id = cx.block_id;
let selected = cx.selected;
this.update(&mut **cx, |this, cx| {
this.render_patch_block(
this.render_patch_footer(
patch_range.clone(),
max_width,
gutter_width,
block_id,
selected,
cx,
)
})
@@ -2291,16 +2261,25 @@ impl ContextEditor {
}
});
let height = path_count as u32 + 1;
let crease = Crease::block(
patch_start..patch_end,
height,
BlockStyle::Flex,
render_block.clone(),
);
let header_placeholder = FoldPlaceholder {
render: {
let this = this.clone();
let patch_range = range.clone();
Arc::new(move |fold_id, _range, cx| {
this.update(cx, |this, cx| {
this.render_patch_header(patch_range.clone(), fold_id, cx)
})
.ok()
.flatten()
.unwrap_or_else(|| Empty.into_any())
})
},
..Default::default()
};
let should_refold;
if let Some(state) = self.patches.get_mut(&range) {
replaced_blocks.insert(state.footer_block_id, render_block);
if let Some(editor_state) = &state.editor {
if editor_state.opened_patch != patch {
state.update_task = Some({
@@ -2317,11 +2296,33 @@ impl ContextEditor {
should_refold =
snapshot.intersects_fold(patch_start.to_offset(&snapshot.buffer_snapshot));
} else {
let crease_id = editor.insert_creases([crease.clone()], cx)[0];
let block_ids = editor.insert_blocks(
[BlockProperties {
height: path_count as u32 + 1,
style: BlockStyle::Flex,
render: render_block,
placement: BlockPlacement::Below(patch_start),
priority: 0,
}],
None,
cx,
);
let new_crease_ids = editor.insert_creases(
[Crease::new(
patch_start..patch_end,
header_placeholder.clone(),
fold_toggle("patch-header"),
|_, _, _| Empty.into_any_element(),
)],
cx,
);
self.patches.insert(
range.clone(),
PatchViewState {
crease_id,
footer_block_id: block_ids[0],
crease_id: new_crease_ids[0],
editor: None,
update_task: None,
},
@@ -2332,9 +2333,13 @@ impl ContextEditor {
if should_refold {
editor.unfold_ranges(&[patch_start..patch_end], true, false, cx);
editor.fold_creases(vec![crease], false, cx);
editor.fold_ranges([(patch_start..patch_end, header_placeholder)], false, cx);
}
}
editor.remove_creases(removed_crease_ids, cx);
editor.remove_blocks(removed_block_ids, None, cx);
editor.replace_blocks(replaced_blocks, None, cx);
});
for editor in editors_to_close {
@@ -2365,7 +2370,7 @@ impl ContextEditor {
let buffer_row = MultiBufferRow(start.to_point(&buffer).row);
buffer_rows_to_fold.insert(buffer_row);
creases.push(
Crease::inline(
Crease::new(
start..end,
FoldPlaceholder {
render: render_fold_icon_button(
@@ -2654,7 +2659,7 @@ impl ContextEditor {
let mut blocks_to_replace: HashMap<_, RenderBlock> = Default::default();
let render_block = |message: MessageMetadata| -> RenderBlock {
Arc::new({
Box::new({
let context = self.context.clone();
move |cx| {
@@ -2755,7 +2760,7 @@ impl ContextEditor {
.h_11()
.w_full()
.relative()
.gap_1p5()
.gap_1()
.child(sender)
.children(match &message.cache {
Some(cache) if cache.is_final_anchor => match cache.status {
@@ -2769,7 +2774,7 @@ impl ContextEditor {
)
.tooltip(|cx| {
Tooltip::with_meta(
"Context Cached",
"Context cached",
None,
"Large messages cached to optimize performance",
cx,
@@ -2797,9 +2802,16 @@ impl ContextEditor {
.selected_icon_color(Color::Error)
.icon(IconName::XCircle)
.icon_color(Color::Error)
.icon_size(IconSize::XSmall)
.icon_size(IconSize::Small)
.icon_position(IconPosition::Start)
.tooltip(move |cx| Tooltip::text("View Details", cx))
.tooltip(move |cx| {
Tooltip::with_meta(
"Error interacting with language model",
None,
"Click for more details",
cx,
)
})
.on_click({
let context = context.clone();
let error = error.clone();
@@ -2814,19 +2826,21 @@ impl ContextEditor {
.into_any_element(),
),
MessageStatus::Canceled => Some(
h_flex()
.gap_1()
.items_center()
.child(
Icon::new(IconName::XCircle)
.color(Color::Disabled)
.size(IconSize::XSmall),
)
ButtonLike::new("canceled")
.child(Icon::new(IconName::XCircle).color(Color::Disabled))
.child(
Label::new("Canceled")
.size(LabelSize::Small)
.color(Color::Disabled),
)
.tooltip(move |cx| {
Tooltip::with_meta(
"Canceled",
None,
"Interaction with the assistant was canceled",
cx,
)
})
.into_any_element(),
),
_ => None,
@@ -3107,7 +3121,7 @@ impl ContextEditor {
crease_title,
cx.view().downgrade(),
);
let crease = Crease::inline(
let crease = Crease::new(
anchor_before..anchor_after,
fold_placeholder,
render_quote_selection_output_toggle,
@@ -3197,29 +3211,31 @@ impl ContextEditor {
&snapshot,
)
.filter_map(|crease| {
if let Crease::Inline {
range, metadata, ..
} = &crease
{
let metadata = metadata.as_ref()?;
let start = range
if let Some(metadata) = &crease.metadata {
let start = crease
.range
.start
.to_offset(&snapshot)
.saturating_sub(selection_start);
let end = range
let end = crease
.range
.end
.to_offset(&snapshot)
.saturating_sub(selection_start);
let range_relative_to_selection = start..end;
if !range_relative_to_selection.is_empty() {
return Some(SelectedCreaseMetadata {
if range_relative_to_selection.is_empty() {
None
} else {
Some(SelectedCreaseMetadata {
range_relative_to_selection,
crease: metadata.clone(),
});
})
}
} else {
None
}
None
})
.collect::<Vec<_>>()
}),
@@ -3300,7 +3316,7 @@ impl ContextEditor {
let buffer_row = MultiBufferRow(start.to_point(&buffer).row);
buffer_rows_to_fold.insert(buffer_row);
Crease::inline(
Crease::new(
start..end,
FoldPlaceholder {
render: render_fold_icon_button(
@@ -3342,8 +3358,7 @@ impl ContextEditor {
self.context.update(cx, |context, cx| {
for image in images {
let Some(render_image) = image.to_image_data(cx.svg_renderer()).log_err()
else {
let Some(render_image) = image.to_image_data(cx).log_err() else {
continue;
};
let image_id = image.id();
@@ -3394,7 +3409,7 @@ impl ContextEditor {
placement: BlockPlacement::Above(anchor),
height: MAX_HEIGHT_IN_LINES,
style: BlockStyle::Sticky,
render: Arc::new(move |cx| {
render: Box::new(move |cx| {
let image_size = size_for_image(
&image,
size(
@@ -3451,13 +3466,33 @@ impl ContextEditor {
.unwrap_or_else(|| Cow::Borrowed(DEFAULT_TAB_TITLE))
}
fn render_patch_block(
fn render_patch_header(
&self,
range: Range<text::Anchor>,
_id: FoldId,
cx: &mut ViewContext<Self>,
) -> Option<AnyElement> {
let patch = self.context.read(cx).patch_for_range(&range, cx)?;
let theme = cx.theme().clone();
Some(
h_flex()
.px_1()
.py_0p5()
.border_b_1()
.border_color(theme.status().info_border)
.gap_1()
.child(Icon::new(IconName::Diff).size(IconSize::Small))
.child(Label::new(patch.title.clone()).size(LabelSize::Small))
.into_any(),
)
}
fn render_patch_footer(
&mut self,
range: Range<text::Anchor>,
max_width: Pixels,
gutter_width: Pixels,
id: BlockId,
selected: bool,
cx: &mut ViewContext<Self>,
) -> Option<AnyElement> {
let snapshot = self.editor.update(cx, |editor, cx| editor.snapshot(cx));
@@ -3468,7 +3503,10 @@ impl ContextEditor {
.anchor_in_excerpt(excerpt_id, range.start)
.unwrap();
let theme = cx.theme().clone();
if !snapshot.intersects_fold(anchor) {
return None;
}
let patch = self.context.read(cx).patch_for_range(&range, cx)?;
let paths = patch
.paths()
@@ -3478,18 +3516,9 @@ impl ContextEditor {
Some(
v_flex()
.id(id)
.bg(theme.colors().editor_background)
.ml(gutter_width)
.pb_1()
.w(max_width - gutter_width)
.rounded_md()
.border_1()
.border_color(theme.colors().border_variant)
.overflow_hidden()
.hover(|style| style.border_color(theme.colors().text_accent))
.when(selected, |this| {
this.border_color(theme.colors().text_accent)
})
.pl(gutter_width)
.w(max_width)
.py_2()
.cursor(CursorStyle::PointingHand)
.on_click(cx.listener(move |this, _, cx| {
this.editor.update(cx, |editor, cx| {
@@ -3499,60 +3528,24 @@ impl ContextEditor {
});
this.focus_active_patch(cx);
}))
.child(
div()
.px_2()
.py_1()
.overflow_hidden()
.text_ellipsis()
.border_b_1()
.border_color(theme.colors().border_variant)
.bg(theme.colors().element_background)
.child(
Label::new(patch.title.clone())
.size(LabelSize::Small)
.color(Color::Muted),
),
)
.children(paths.into_iter().map(|path| {
h_flex()
.px_2()
.pt_1()
.gap_1p5()
.pl_1()
.gap_1()
.child(Icon::new(IconName::File).size(IconSize::Small))
.child(Label::new(path).size(LabelSize::Small))
}))
.when(patch.status == AssistantPatchStatus::Pending, |div| {
div.child(
h_flex()
.pt_1()
.px_2()
.gap_1()
.child(
Icon::new(IconName::ArrowCircle)
.size(IconSize::XSmall)
.color(Color::Muted)
.with_animation(
"arrow-circle",
Animation::new(Duration::from_secs(2)).repeat(),
|icon, delta| {
icon.transform(Transformation::rotate(percentage(
delta,
)))
},
),
)
.child(
Label::new("Generating…")
.color(Color::Muted)
.size(LabelSize::Small)
.with_animation(
"pulsating-label",
Animation::new(Duration::from_secs(2))
.repeat()
.with_easing(pulsating_between(0.4, 0.8)),
|label, delta| label.alpha(delta),
),
Label::new("Generating")
.color(Color::Muted)
.size(LabelSize::Small)
.with_animation(
"pulsating-label",
Animation::new(Duration::from_secs(2))
.repeat()
.with_easing(pulsating_between(0.4, 1.)),
|label, delta| label.alpha(delta),
),
)
})
@@ -3930,7 +3923,7 @@ impl ContextEditor {
.child(
div()
.id("error-message")
.max_h_32()
.max_h_24()
.overflow_y_scroll()
.child(Label::new(error_message.clone())),
)
@@ -4456,36 +4449,23 @@ impl FollowableItem for ContextEditor {
}
pub struct ContextEditorToolbarItem {
fs: Arc<dyn Fs>,
active_context_editor: Option<WeakView<ContextEditor>>,
model_summary_editor: View<Editor>,
language_model_selector: View<LanguageModelSelector>,
language_model_selector_menu_handle: PopoverMenuHandle<LanguageModelSelector>,
model_selector_menu_handle: PopoverMenuHandle<Picker<ModelPickerDelegate>>,
}
impl ContextEditorToolbarItem {
pub fn new(
workspace: &Workspace,
model_selector_menu_handle: PopoverMenuHandle<LanguageModelSelector>,
model_selector_menu_handle: PopoverMenuHandle<Picker<ModelPickerDelegate>>,
model_summary_editor: View<Editor>,
cx: &mut ViewContext<Self>,
) -> Self {
Self {
fs: workspace.app_state().fs.clone(),
active_context_editor: None,
model_summary_editor,
language_model_selector: cx.new_view(|cx| {
let fs = workspace.app_state().fs.clone();
LanguageModelSelector::new(
move |model, cx| {
update_settings_file::<AssistantSettings>(
fs.clone(),
cx,
move |settings, _| settings.set_model(model.clone()),
);
},
cx,
)
}),
language_model_selector_menu_handle: model_selector_menu_handle,
model_selector_menu_handle,
}
}
@@ -4536,6 +4516,7 @@ impl Render for ContextEditorToolbarItem {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let left_side = h_flex()
.group("chat-title-group")
.pl_0p5()
.gap_1()
.items_center()
.flex_grow()
@@ -4574,8 +4555,8 @@ impl Render for ContextEditorToolbarItem {
// .map(|remaining_items| format!("Files to scan: {}", remaining_items))
// })
.child(
LanguageModelSelectorPopoverMenu::new(
self.language_model_selector.clone(),
ModelSelector::new(
self.fs.clone(),
ButtonLike::new("active-model")
.style(ButtonStyle::Subtle)
.child(
@@ -4621,12 +4602,11 @@ impl Render for ContextEditorToolbarItem {
Tooltip::for_action("Change Model", &ToggleModelSelector, cx)
}),
)
.with_handle(self.language_model_selector_menu_handle.clone()),
.with_handle(self.model_selector_menu_handle.clone()),
)
.children(self.render_remaining_tokens(cx));
h_flex()
.px_0p5()
.size_full()
.gap_2()
.justify_between()
@@ -4852,7 +4832,7 @@ impl ConfigurationView {
)
.child(
div()
.p(DynamicSpacing::Base08.rems(cx))
.p(Spacing::Large.rems(cx))
.bg(cx.theme().colors().surface_background)
.border_1()
.border_color(cx.theme().colors().border_variant)
@@ -4886,7 +4866,7 @@ impl Render for ConfigurationView {
.overflow_y_scroll()
.child(
v_flex()
.p(DynamicSpacing::Base16.rems(cx))
.p(Spacing::XXLarge.rems(cx))
.border_b_1()
.border_color(cx.theme().colors().border)
.gap_1()
@@ -4900,7 +4880,7 @@ impl Render for ConfigurationView {
)
.child(
v_flex()
.p(DynamicSpacing::Base16.rems(cx))
.p(Spacing::XXLarge.rems(cx))
.mt_1()
.gap_6()
.flex_1()
@@ -4956,7 +4936,7 @@ fn render_slash_command_output_toggle(
("slash-command-output-fold-indicator", row.0 as u64),
!is_folded,
)
.toggle_state(is_folded)
.selected(is_folded)
.on_click(move |_e, cx| fold(!is_folded, cx))
.into_any_element()
}
@@ -4971,7 +4951,7 @@ fn fold_toggle(
) -> AnyElement {
move |row, is_folded, fold, _cx| {
Disclosure::new((name, row.0 as u64), !is_folded)
.toggle_state(is_folded)
.selected(is_folded)
.on_click(move |_e, cx| fold(!is_folded, cx))
.into_any_element()
}
@@ -5013,7 +4993,7 @@ fn render_quote_selection_output_toggle(
_cx: &mut WindowContext,
) -> AnyElement {
Disclosure::new(("quote-selection-indicator", row.0 as u64), !is_folded)
.toggle_state(is_folded)
.selected(is_folded)
.on_click(move |_e, cx| fold(!is_folded, cx))
.into_any_element()
}
@@ -5036,7 +5016,7 @@ fn render_pending_slash_command_gutter_decoration(
icon = icon.icon_color(Color::Muted);
}
PendingSlashCommandStatus::Running { .. } => {
icon = icon.toggle_state(true);
icon = icon.selected(true);
}
PendingSlashCommandStatus::Error(_) => icon = icon.icon_color(Color::Error),
}
@@ -5118,11 +5098,9 @@ fn make_lsp_adapter_delegate(
return Ok(None::<Arc<dyn LspAdapterDelegate>>);
};
let http_client = project.client().http_client().clone();
project.lsp_store().update(cx, |_, cx| {
project.lsp_store().update(cx, |lsp_store, cx| {
Ok(Some(LocalLspAdapterDelegate::new(
project.languages().clone(),
project.environment(),
cx.weak_model(),
lsp_store,
&worktree,
http_client,
project.fs().clone(),

View File

@@ -5,12 +5,13 @@ use anthropic::Model as AnthropicModel;
use feature_flags::FeatureFlagAppExt;
use fs::Fs;
use gpui::{AppContext, Pixels};
use language_model::{CloudModel, LanguageModel};
use language_models::{
provider::open_ai, AllLanguageModelSettings, AnthropicSettingsContent,
AnthropicSettingsContentV1, OllamaSettingsContent, OpenAiSettingsContent,
OpenAiSettingsContentV1, VersionedAnthropicSettingsContent, VersionedOpenAiSettingsContent,
use language_model::provider::open_ai;
use language_model::settings::{
AnthropicSettingsContent, AnthropicSettingsContentV1, OllamaSettingsContent,
OpenAiSettingsContent, OpenAiSettingsContentV1, VersionedAnthropicSettingsContent,
VersionedOpenAiSettingsContent,
};
use language_model::{settings::AllLanguageModelSettings, CloudModel, LanguageModel};
use ollama::Model as OllamaModel;
use schemars::{schema::Schema, JsonSchema};
use serde::{Deserialize, Serialize};
@@ -34,17 +35,20 @@ pub enum AssistantProviderContentV1 {
OpenAi {
default_model: Option<OpenAiModel>,
api_url: Option<String>,
low_speed_timeout_in_seconds: Option<u64>,
available_models: Option<Vec<OpenAiModel>>,
},
#[serde(rename = "anthropic")]
Anthropic {
default_model: Option<AnthropicModel>,
api_url: Option<String>,
low_speed_timeout_in_seconds: Option<u64>,
},
#[serde(rename = "ollama")]
Ollama {
default_model: Option<OllamaModel>,
api_url: Option<String>,
low_speed_timeout_in_seconds: Option<u64>,
},
}
@@ -111,41 +115,47 @@ impl AssistantSettingsContent {
if let VersionedAssistantSettingsContent::V1(settings) = settings {
if let Some(provider) = settings.provider.clone() {
match provider {
AssistantProviderContentV1::Anthropic { api_url, .. } => {
update_settings_file::<AllLanguageModelSettings>(
fs,
cx,
move |content, _| {
if content.anthropic.is_none() {
content.anthropic =
Some(AnthropicSettingsContent::Versioned(
VersionedAnthropicSettingsContent::V1(
AnthropicSettingsContentV1 {
api_url,
available_models: None,
},
),
));
}
},
)
}
AssistantProviderContentV1::Ollama { api_url, .. } => {
update_settings_file::<AllLanguageModelSettings>(
fs,
cx,
move |content, _| {
if content.ollama.is_none() {
content.ollama = Some(OllamaSettingsContent {
api_url,
available_models: None,
});
}
},
)
}
AssistantProviderContentV1::Anthropic {
api_url,
low_speed_timeout_in_seconds,
..
} => update_settings_file::<AllLanguageModelSettings>(
fs,
cx,
move |content, _| {
if content.anthropic.is_none() {
content.anthropic = Some(AnthropicSettingsContent::Versioned(
VersionedAnthropicSettingsContent::V1(
AnthropicSettingsContentV1 {
api_url,
low_speed_timeout_in_seconds,
available_models: None,
},
),
));
}
},
),
AssistantProviderContentV1::Ollama {
api_url,
low_speed_timeout_in_seconds,
..
} => update_settings_file::<AllLanguageModelSettings>(
fs,
cx,
move |content, _| {
if content.ollama.is_none() {
content.ollama = Some(OllamaSettingsContent {
api_url,
low_speed_timeout_in_seconds,
available_models: None,
});
}
},
),
AssistantProviderContentV1::OpenAi {
api_url,
low_speed_timeout_in_seconds,
available_models,
..
} => update_settings_file::<AllLanguageModelSettings>(
@@ -178,6 +188,7 @@ impl AssistantSettingsContent {
VersionedOpenAiSettingsContent::V1(
OpenAiSettingsContentV1 {
api_url,
low_speed_timeout_in_seconds,
available_models,
},
),
@@ -287,41 +298,54 @@ impl AssistantSettingsContent {
log::warn!("attempted to set zed.dev model on outdated settings");
}
"anthropic" => {
let api_url = match &settings.provider {
Some(AssistantProviderContentV1::Anthropic { api_url, .. }) => {
api_url.clone()
}
_ => None,
let (api_url, low_speed_timeout_in_seconds) = match &settings.provider {
Some(AssistantProviderContentV1::Anthropic {
api_url,
low_speed_timeout_in_seconds,
..
}) => (api_url.clone(), *low_speed_timeout_in_seconds),
_ => (None, None),
};
settings.provider = Some(AssistantProviderContentV1::Anthropic {
default_model: AnthropicModel::from_id(&model).ok(),
api_url,
low_speed_timeout_in_seconds,
});
}
"ollama" => {
let api_url = match &settings.provider {
Some(AssistantProviderContentV1::Ollama { api_url, .. }) => {
api_url.clone()
}
_ => None,
let (api_url, low_speed_timeout_in_seconds) = match &settings.provider {
Some(AssistantProviderContentV1::Ollama {
api_url,
low_speed_timeout_in_seconds,
..
}) => (api_url.clone(), *low_speed_timeout_in_seconds),
_ => (None, None),
};
settings.provider = Some(AssistantProviderContentV1::Ollama {
default_model: Some(ollama::Model::new(&model, None, None)),
api_url,
low_speed_timeout_in_seconds,
});
}
"openai" => {
let (api_url, available_models) = match &settings.provider {
Some(AssistantProviderContentV1::OpenAi {
api_url,
available_models,
..
}) => (api_url.clone(), available_models.clone()),
_ => (None, None),
};
let (api_url, low_speed_timeout_in_seconds, available_models) =
match &settings.provider {
Some(AssistantProviderContentV1::OpenAi {
api_url,
low_speed_timeout_in_seconds,
available_models,
..
}) => (
api_url.clone(),
*low_speed_timeout_in_seconds,
available_models.clone(),
),
_ => (None, None, None),
};
settings.provider = Some(AssistantProviderContentV1::OpenAi {
default_model: OpenAiModel::from_id(&model).ok(),
api_url,
low_speed_timeout_in_seconds,
available_models,
});
}

View File

@@ -2,6 +2,7 @@
mod context_tests;
use crate::slash_command_working_set::SlashCommandWorkingSet;
use crate::ToolWorkingSet;
use crate::{
prompts::PromptBuilder,
slash_command::{file_command::FileCommandMetadata, SlashCommandLine},
@@ -11,11 +12,10 @@ use anyhow::{anyhow, Context as _, Result};
use assistant_slash_command::{
SlashCommandContent, SlashCommandEvent, SlashCommandOutputSection, SlashCommandResult,
};
use assistant_tool::ToolWorkingSet;
use client::{self, proto, telemetry::Telemetry};
use clock::ReplicaId;
use collections::{HashMap, HashSet};
use feature_flags::{FeatureFlagAppExt, ToolUseFeatureFlag};
use feature_flags::{FeatureFlag, FeatureFlagAppExt};
use fs::{Fs, RemoveOptions};
use futures::{future::Shared, FutureExt, StreamExt};
use gpui::{
@@ -25,14 +25,12 @@ use gpui::{
use language::{AnchorRangeExt, Bias, Buffer, LanguageRegistry, OffsetRangeExt, Point, ToOffset};
use language_model::{
logging::report_assistant_event,
provider::cloud::{MaxMonthlySpendReachedError, PaymentRequiredError},
LanguageModel, LanguageModelCacheConfiguration, LanguageModelCompletionEvent,
LanguageModelImage, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage,
LanguageModelRequestTool, LanguageModelToolResult, LanguageModelToolUse,
LanguageModelToolUseId, MessageContent, Role, StopReason,
};
use language_models::{
provider::cloud::{MaxMonthlySpendReachedError, PaymentRequiredError},
report_assistant_event,
LanguageModelRequestTool, LanguageModelToolResult, LanguageModelToolUse, MessageContent, Role,
StopReason,
};
use open_ai::Model as OpenAiModel;
use paths::contexts_dir;
@@ -383,9 +381,13 @@ pub enum ContextEvent {
SlashCommandOutputSectionAdded {
section: SlashCommandOutputSection<language::Anchor>,
},
SlashCommandFinished {
output_range: Range<language::Anchor>,
run_commands_in_ranges: Vec<Range<language::Anchor>>,
},
UsePendingTools,
ToolFinished {
tool_use_id: LanguageModelToolUseId,
tool_use_id: Arc<str>,
output_range: Range<language::Anchor>,
},
Operation(ContextOperation),
@@ -479,7 +481,7 @@ pub enum Content {
},
ToolResult {
range: Range<language::Anchor>,
tool_use_id: LanguageModelToolUseId,
tool_use_id: Arc<str>,
},
}
@@ -546,7 +548,7 @@ pub struct Context {
pub(crate) slash_commands: Arc<SlashCommandWorkingSet>,
pub(crate) tools: Arc<ToolWorkingSet>,
slash_command_output_sections: Vec<SlashCommandOutputSection<language::Anchor>>,
pending_tool_uses_by_id: HashMap<LanguageModelToolUseId, PendingToolUse>,
pending_tool_uses_by_id: HashMap<Arc<str>, PendingToolUse>,
message_anchors: Vec<MessageAnchor>,
contents: Vec<Content>,
messages_metadata: HashMap<MessageId, MessageMetadata>,
@@ -914,7 +916,6 @@ impl Context {
InvokedSlashCommand {
name: name.into(),
range: output_range,
run_commands_in_ranges: Vec::new(),
status: InvokedSlashCommandStatus::Running(Task::ready(())),
transaction: None,
timestamp: id.0,
@@ -1126,7 +1127,7 @@ impl Context {
self.pending_tool_uses_by_id.values().collect()
}
pub fn get_tool_use_by_id(&self, id: &LanguageModelToolUseId) -> Option<&PendingToolUse> {
pub fn get_tool_use_by_id(&self, id: &Arc<str>) -> Option<&PendingToolUse> {
self.pending_tool_uses_by_id.get(id)
}
@@ -1913,6 +1914,7 @@ impl Context {
}
let mut pending_section_stack: Vec<PendingSection> = Vec::new();
let mut run_commands_in_ranges: Vec<Range<language::Anchor>> = Vec::new();
let mut last_role: Option<Role> = None;
let mut last_section_range = None;
@@ -1978,13 +1980,7 @@ impl Context {
let end = this.buffer.read(cx).anchor_before(insert_position);
if run_commands_in_text {
if let Some(invoked_slash_command) =
this.invoked_slash_commands.get_mut(&command_id)
{
invoked_slash_command
.run_commands_in_ranges
.push(start..end);
}
run_commands_in_ranges.push(start..end);
}
}
SlashCommandEvent::EndSection => {
@@ -2104,7 +2100,6 @@ impl Context {
InvokedSlashCommand {
name: name.to_string().into(),
range: command_range.clone(),
run_commands_in_ranges: Vec::new(),
status: InvokedSlashCommandStatus::Running(insert_output_task),
transaction: Some(first_transaction),
timestamp: command_id.0,
@@ -2153,7 +2148,7 @@ impl Context {
pub fn insert_tool_output(
&mut self,
tool_use_id: LanguageModelToolUseId,
tool_use_id: Arc<str>,
output: Task<Result<String>>,
cx: &mut ModelContext<Self>,
) {
@@ -2340,10 +2335,11 @@ impl Context {
let source_range = buffer.anchor_after(start_ix)
..buffer.anchor_after(end_ix);
let tool_use_id: Arc<str> = tool_use.id.into();
this.pending_tool_uses_by_id.insert(
tool_use.id.clone(),
tool_use_id.clone(),
PendingToolUse {
id: tool_use.id,
id: tool_use_id,
name: tool_use.name,
input: tool_use.input,
status: PendingToolUseStatus::Idle,
@@ -2387,11 +2383,7 @@ impl Context {
});
Some(error.to_string())
} else {
let error_message = error
.chain()
.map(|err| err.to_string())
.collect::<Vec<_>>()
.join("\n");
let error_message = error.to_string().trim().to_string();
cx.emit(ContextEvent::ShowAssistError(SharedString::from(
error_message.clone(),
)));
@@ -2895,7 +2887,7 @@ impl Context {
request.messages.push(LanguageModelRequestMessage {
role: Role::User,
content: vec![
"Generate a concise 3-7 word title for this conversation, omitting punctuation. Go straight to the title, without any preamble and prefix like `Here's a concise suggestion:...` or `Title:`"
"Generate a concise 3-7 word title for this conversation, omitting punctuation"
.into(),
],
cache: false,
@@ -3180,7 +3172,6 @@ pub struct ParsedSlashCommand {
pub struct InvokedSlashCommand {
pub name: SharedString,
pub range: Range<language::Anchor>,
pub run_commands_in_ranges: Vec<Range<language::Anchor>>,
pub status: InvokedSlashCommandStatus,
pub transaction: Option<language::TransactionId>,
timestamp: clock::Lamport,
@@ -3200,9 +3191,19 @@ pub enum PendingSlashCommandStatus {
Error(String),
}
pub(crate) struct ToolUseFeatureFlag;
impl FeatureFlag for ToolUseFeatureFlag {
const NAME: &'static str = "assistant-tool-use";
fn enabled_for_staff() -> bool {
false
}
}
#[derive(Debug, Clone)]
pub struct PendingToolUse {
pub id: LanguageModelToolUseId,
pub id: Arc<str>,
pub name: String,
pub input: serde_json::Value,
pub status: PendingToolUseStatus,

View File

@@ -1,5 +1,6 @@
use super::{AssistantEdit, MessageCacheMetadata};
use crate::slash_command_working_set::SlashCommandWorkingSet;
use crate::ToolWorkingSet;
use crate::{
assistant_panel, prompt_library, slash_command::file_command, AssistantEditKind, CacheStatus,
Context, ContextEvent, ContextId, ContextOperation, InvokedSlashCommandId, MessageId,
@@ -10,14 +11,13 @@ use assistant_slash_command::{
ArgumentCompletion, SlashCommand, SlashCommandContent, SlashCommandEvent, SlashCommandOutput,
SlashCommandOutputSection, SlashCommandRegistry, SlashCommandResult,
};
use assistant_tool::ToolWorkingSet;
use collections::{HashMap, HashSet};
use fs::FakeFs;
use futures::{
channel::mpsc,
stream::{self, StreamExt},
};
use gpui::{prelude::*, AppContext, Model, SharedString, Task, TestAppContext, WeakView};
use gpui::{AppContext, Model, SharedString, Task, TestAppContext, WeakView};
use language::{Buffer, BufferSnapshot, LanguageRegistry, LspAdapterDelegate};
use language_model::{LanguageModelCacheConfiguration, LanguageModelRegistry, Role};
use parking_lot::Mutex;
@@ -35,7 +35,7 @@ use std::{
sync::{atomic::AtomicBool, Arc},
};
use text::{network::Network, OffsetRangeExt as _, ReplicaId, ToOffset};
use ui::{IconName, WindowContext};
use ui::{Context as _, IconName, WindowContext};
use unindent::Unindent;
use util::{
test::{generate_marked_text, marked_text_ranges},

View File

@@ -1,16 +1,16 @@
use crate::slash_command::context_server_command;
use crate::SlashCommandId;
use crate::{
prompts::PromptBuilder, slash_command_working_set::SlashCommandWorkingSet, Context,
ContextEvent, ContextId, ContextOperation, ContextVersion, SavedContext, SavedContextMetadata,
};
use crate::{tools, SlashCommandId, ToolId, ToolWorkingSet};
use anyhow::{anyhow, Context as _, Result};
use assistant_tool::{ToolId, ToolWorkingSet};
use client::{proto, telemetry::Telemetry, Client, TypedEnvelope};
use clock::ReplicaId;
use collections::HashMap;
use context_server::manager::ContextServerManager;
use context_server::{ContextServerFactoryRegistry, ContextServerTool};
use command_palette_hooks::CommandPaletteFilter;
use context_servers::manager::{ContextServerManager, ContextServerSettings};
use context_servers::CONTEXT_SERVERS_NAMESPACE;
use fs::Fs;
use futures::StreamExt;
use fuzzy::StringMatchCandidate;
@@ -22,6 +22,7 @@ use paths::contexts_dir;
use project::Project;
use regex::Regex;
use rpc::AnyProtoClient;
use settings::{Settings as _, SettingsStore};
use std::{
cmp::Reverse,
ffi::OsStr,
@@ -50,8 +51,8 @@ pub struct ContextStore {
contexts: Vec<ContextHandle>,
contexts_metadata: Vec<SavedContextMetadata>,
context_server_manager: Model<ContextServerManager>,
context_server_slash_command_ids: HashMap<Arc<str>, Vec<SlashCommandId>>,
context_server_tool_ids: HashMap<Arc<str>, Vec<ToolId>>,
context_server_slash_command_ids: HashMap<String, Vec<SlashCommandId>>,
context_server_tool_ids: HashMap<String, Vec<ToolId>>,
host_contexts: Vec<RemoteContextMetadata>,
fs: Arc<dyn Fs>,
languages: Arc<LanguageRegistry>,
@@ -110,11 +111,7 @@ impl ContextStore {
let (mut events, _) = fs.watch(contexts_dir(), CONTEXT_WATCH_DURATION).await;
let this = cx.new_model(|cx: &mut ModelContext<Self>| {
let context_server_factory_registry =
ContextServerFactoryRegistry::default_global(cx);
let context_server_manager = cx.new_model(|cx| {
ContextServerManager::new(context_server_factory_registry, project.clone(), cx)
});
let context_server_manager = cx.new_model(|_cx| ContextServerManager::new());
let mut this = Self {
contexts: Vec::new(),
contexts_metadata: Vec::new(),
@@ -148,7 +145,7 @@ impl ContextStore {
project: project.clone(),
prompt_builder,
};
this.handle_project_changed(project.clone(), cx);
this.handle_project_changed(project, cx);
this.synchronize_contexts(cx);
this.register_context_server_handlers(cx);
this
@@ -157,10 +154,41 @@ impl ContextStore {
.await
.log_err();
this.update(&mut cx, |this, cx| {
this.watch_context_server_settings(cx);
})
.log_err();
Ok(this)
})
}
fn watch_context_server_settings(&self, cx: &mut ModelContext<Self>) {
cx.observe_global::<SettingsStore>(move |this, cx| {
this.context_server_manager.update(cx, |manager, cx| {
let location = this.project.read(cx).worktrees(cx).next().map(|worktree| {
settings::SettingsLocation {
worktree_id: worktree.read(cx).id(),
path: Path::new(""),
}
});
let settings = ContextServerSettings::get(location, cx);
manager.maintain_servers(settings, cx);
let has_any_context_servers = !manager.servers().is_empty();
CommandPaletteFilter::update_global(cx, |filter, _cx| {
if has_any_context_servers {
filter.show_namespace(CONTEXT_SERVERS_NAMESPACE);
} else {
filter.hide_namespace(CONTEXT_SERVERS_NAMESPACE);
}
});
})
})
.detach();
}
async fn handle_advertise_contexts(
this: Model<Self>,
envelope: TypedEnvelope<proto::AdvertiseContexts>,
@@ -771,7 +799,7 @@ impl ContextStore {
contexts.push(SavedContextMetadata {
title: title.to_string(),
path,
mtime: metadata.mtime.timestamp_for_user().into(),
mtime: metadata.mtime.into(),
});
}
}
@@ -791,7 +819,7 @@ impl ContextStore {
|context_server_manager, cx| {
for server in context_server_manager.servers() {
context_server_manager
.restart_server(&server.id(), cx)
.restart_server(&server.id, cx)
.detach_and_log_err(cx);
}
},
@@ -809,24 +837,24 @@ impl ContextStore {
fn handle_context_server_event(
&mut self,
context_server_manager: Model<ContextServerManager>,
event: &context_server::manager::Event,
event: &context_servers::manager::Event,
cx: &mut ModelContext<Self>,
) {
let slash_command_working_set = self.slash_commands.clone();
let tool_working_set = self.tools.clone();
match event {
context_server::manager::Event::ServerStarted { server_id } => {
context_servers::manager::Event::ServerStarted { server_id } => {
if let Some(server) = context_server_manager.read(cx).get_server(server_id) {
let context_server_manager = context_server_manager.clone();
cx.spawn({
let server = server.clone();
let server_id = server_id.clone();
|this, mut cx| async move {
let Some(protocol) = server.client() else {
let Some(protocol) = server.client.read().clone() else {
return;
};
if protocol.capable(context_server::protocol::ServerCapability::Prompts) {
if protocol.capable(context_servers::protocol::ServerCapability::Prompts) {
if let Some(prompts) = protocol.list_prompts().await.log_err() {
let slash_command_ids = prompts
.into_iter()
@@ -854,14 +882,14 @@ impl ContextStore {
}
}
if protocol.capable(context_server::protocol::ServerCapability::Tools) {
if protocol.capable(context_servers::protocol::ServerCapability::Tools) {
if let Some(tools) = protocol.list_tools().await.log_err() {
let tool_ids = tools.tools.into_iter().map(|tool| {
log::info!("registering context server tool: {:?}", tool.name);
tool_working_set.insert(
Arc::new(ContextServerTool::new(
Arc::new(tools::context_server_tool::ContextServerTool::new(
context_server_manager.clone(),
server.id(),
server.id.clone(),
tool,
)),
)
@@ -881,7 +909,7 @@ impl ContextStore {
.detach();
}
}
context_server::manager::Event::ServerStopped { server_id } => {
context_servers::manager::Event::ServerStopped { server_id } => {
if let Some(slash_command_ids) =
self.context_server_slash_command_ids.remove(server_id)
{

View File

@@ -1,7 +1,7 @@
use crate::{
assistant_settings::AssistantSettings, humanize_token_count, prompts::PromptBuilder,
AssistantPanel, AssistantPanelEvent, CharOperation, CycleNextInlineAssist,
CyclePreviousInlineAssist, LineDiff, LineOperation, RequestType, StreamingDiff,
CyclePreviousInlineAssist, LineDiff, LineOperation, ModelSelector, RequestType, StreamingDiff,
};
use anyhow::{anyhow, Context as _, Result};
use client::{telemetry::Telemetry, ErrorExt};
@@ -24,22 +24,20 @@ use futures::{
join, SinkExt, Stream, StreamExt,
};
use gpui::{
anchored, deferred, point, AnyElement, AppContext, ClickEvent, CursorStyle, EventEmitter,
FocusHandle, FocusableView, FontWeight, Global, HighlightStyle, Model, ModelContext,
Subscription, Task, TextStyle, UpdateGlobal, View, ViewContext, WeakView, WindowContext,
anchored, deferred, point, AnyElement, AppContext, ClickEvent, EventEmitter, FocusHandle,
FocusableView, FontWeight, Global, HighlightStyle, Model, ModelContext, Subscription, Task,
TextStyle, UpdateGlobal, View, ViewContext, WeakView, WindowContext,
};
use language::{Buffer, IndentKind, Point, Selection, TransactionId};
use language_model::{
LanguageModel, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage,
LanguageModelTextStream, Role,
logging::report_assistant_event, LanguageModel, LanguageModelRegistry, LanguageModelRequest,
LanguageModelRequestMessage, LanguageModelTextStream, Role,
};
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
use language_models::report_assistant_event;
use multi_buffer::MultiBufferRow;
use parking_lot::Mutex;
use project::{CodeAction, ProjectTransaction};
use rope::Rope;
use settings::{update_settings_file, Settings, SettingsStore};
use settings::{Settings, SettingsStore};
use smol::future::FutureExt;
use std::{
cmp,
@@ -88,7 +86,7 @@ pub struct InlineAssistant {
confirmed_assists: HashMap<InlineAssistId, Model<CodegenAlternative>>,
prompt_history: VecDeque<String>,
prompt_builder: Arc<PromptBuilder>,
telemetry: Arc<Telemetry>,
telemetry: Option<Arc<Telemetry>>,
fs: Arc<dyn Fs>,
}
@@ -109,7 +107,7 @@ impl InlineAssistant {
confirmed_assists: HashMap::default(),
prompt_history: VecDeque::default(),
prompt_builder,
telemetry,
telemetry: Some(telemetry),
fs,
}
}
@@ -245,17 +243,19 @@ impl InlineAssistant {
codegen_ranges.push(start..end);
if let Some(model) = LanguageModelRegistry::read_global(cx).active_model() {
self.telemetry.report_assistant_event(AssistantEvent {
conversation_id: None,
kind: AssistantKind::Inline,
phase: AssistantPhase::Invoked,
message_id: None,
model: model.telemetry_id(),
model_provider: model.provider_id().to_string(),
response_latency: None,
error_message: None,
language_name: buffer.language().map(|language| language.name().to_proto()),
});
if let Some(telemetry) = self.telemetry.as_ref() {
telemetry.report_assistant_event(AssistantEvent {
conversation_id: None,
kind: AssistantKind::Inline,
phase: AssistantPhase::Invoked,
message_id: None,
model: model.telemetry_id(),
model_provider: model.provider_id().to_string(),
response_latency: None,
error_message: None,
language_name: buffer.language().map(|language| language.name().to_proto()),
});
}
}
}
@@ -462,7 +462,7 @@ impl InlineAssistant {
style: BlockStyle::Sticky,
placement: BlockPlacement::Below(range.end),
height: 0,
render: Arc::new(|cx| {
render: Box::new(|cx| {
v_flex()
.h_full()
.w_full()
@@ -818,7 +818,7 @@ impl InlineAssistant {
error_message: None,
language_name: language_name.map(|name| name.to_proto()),
},
Some(self.telemetry.clone()),
self.telemetry.clone(),
cx.http_client(),
model.api_key(cx),
cx.background_executor(),
@@ -1199,9 +1199,8 @@ impl InlineAssistant {
placement: BlockPlacement::Above(new_row),
height,
style: BlockStyle::Flex,
render: Arc::new(move |cx| {
render: Box::new(move |cx| {
div()
.block_mouse_down()
.bg(cx.theme().status().deleted_background)
.size_full()
.h(height as f32 * cx.line_height())
@@ -1320,7 +1319,7 @@ impl InlineAssistGroup {
fn build_assist_editor_renderer(editor: &View<PromptEditor>) -> RenderBlock {
let editor = editor.clone();
Arc::new(move |cx: &mut BlockContext| {
Box::new(move |cx: &mut BlockContext| {
*editor.read(cx).gutter_dimensions.lock() = *cx.gutter_dimensions;
editor.clone().into_any_element()
})
@@ -1358,8 +1357,8 @@ enum PromptEditorEvent {
struct PromptEditor {
id: InlineAssistId,
fs: Arc<dyn Fs>,
editor: View<Editor>,
language_model_selector: View<LanguageModelSelector>,
edited_since_done: bool,
gutter_dimensions: Arc<Mutex<GutterDimensions>>,
prompt_history: VecDeque<String>,
@@ -1483,8 +1482,6 @@ impl Render for PromptEditor {
h_flex()
.key_context("PromptEditor")
.bg(cx.theme().colors().editor_background)
.block_mouse_down()
.cursor(CursorStyle::Arrow)
.border_y_1()
.border_color(cx.theme().status().info_border)
.size_full()
@@ -1500,27 +1497,34 @@ impl Render for PromptEditor {
.w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0))
.justify_center()
.gap_2()
.child(LanguageModelSelectorPopoverMenu::new(
self.language_model_selector.clone(),
IconButton::new("context", IconName::SettingsAlt)
.shape(IconButtonShape::Square)
.icon_size(IconSize::Small)
.icon_color(Color::Muted)
.tooltip(move |cx| {
Tooltip::with_meta(
format!(
"Using {}",
LanguageModelRegistry::read_global(cx)
.active_model()
.map(|model| model.name().0)
.unwrap_or_else(|| "No model selected".into()),
),
None,
"Change Model",
cx,
)
}),
))
.child(
ModelSelector::new(
self.fs.clone(),
IconButton::new("context", IconName::SettingsAlt)
.shape(IconButtonShape::Square)
.icon_size(IconSize::Small)
.icon_color(Color::Muted)
.tooltip(move |cx| {
Tooltip::with_meta(
format!(
"Using {}",
LanguageModelRegistry::read_global(cx)
.active_model()
.map(|model| model.name().0)
.unwrap_or_else(|| "No model selected".into()),
),
None,
"Change Model",
cx,
)
}),
)
.with_info_text(
"Inline edits use context\n\
from the currently selected\n\
assistant panel tab.",
),
)
.map(|el| {
let CodegenStatus::Error(error) = self.codegen.read(cx).status(cx) else {
return el;
@@ -1534,7 +1538,7 @@ impl Render for PromptEditor {
v_flex()
.child(
IconButton::new("rate-limit-error", IconName::XCircle)
.toggle_state(self.show_rate_limit_notice)
.selected(self.show_rate_limit_notice)
.shape(IconButtonShape::Square)
.icon_size(IconSize::Small)
.on_click(cx.listener(Self::toggle_rate_limit_notice)),
@@ -1626,19 +1630,6 @@ impl PromptEditor {
let mut this = Self {
id,
editor: prompt_editor,
language_model_selector: cx.new_view(|cx| {
let fs = fs.clone();
LanguageModelSelector::new(
move |model, cx| {
update_settings_file::<AssistantSettings>(
fs.clone(),
cx,
move |settings, _| settings.set_model(model.clone()),
);
},
cx,
)
}),
edited_since_done: false,
gutter_dimensions,
prompt_history,
@@ -1647,6 +1638,7 @@ impl PromptEditor {
_codegen_subscription: cx.observe(&codegen, Self::handle_codegen_changed),
editor_subscriptions: Vec::new(),
codegen,
fs,
pending_token_count: Task::ready(Ok(())),
token_counts: None,
_token_count_subscriptions: token_count_subscriptions,
@@ -1767,20 +1759,6 @@ impl PromptEditor {
) {
match event {
EditorEvent::Edited { .. } => {
if let Some(workspace) = cx.window_handle().downcast::<Workspace>() {
workspace
.update(cx, |workspace, cx| {
let is_via_ssh = workspace
.project()
.update(cx, |project, _| project.is_via_ssh());
workspace
.client()
.telemetry()
.log_edit_event("inline assist", is_via_ssh);
})
.log_err();
}
let prompt = self.editor.read(cx).text(cx);
if self
.prompt_history_ix
@@ -2133,15 +2111,15 @@ impl PromptEditor {
"dont-show-again",
Label::new("Don't show again"),
if dismissed_rate_limit_notice() {
ui::ToggleState::Selected
ui::Selection::Selected
} else {
ui::ToggleState::Unselected
ui::Selection::Unselected
},
|selection, cx| {
let is_dismissed = match selection {
ui::ToggleState::Unselected => false,
ui::ToggleState::Indeterminate => return,
ui::ToggleState::Selected => true,
ui::Selection::Unselected => false,
ui::Selection::Indeterminate => return,
ui::Selection::Selected => true,
};
set_rate_limit_notice_dismissed(is_dismissed, cx)
@@ -2359,7 +2337,7 @@ pub struct Codegen {
buffer: Model<MultiBuffer>,
range: Range<Anchor>,
initial_transaction_id: Option<TransactionId>,
telemetry: Arc<Telemetry>,
telemetry: Option<Arc<Telemetry>>,
builder: Arc<PromptBuilder>,
is_insertion: bool,
}
@@ -2369,7 +2347,7 @@ impl Codegen {
buffer: Model<MultiBuffer>,
range: Range<Anchor>,
initial_transaction_id: Option<TransactionId>,
telemetry: Arc<Telemetry>,
telemetry: Option<Arc<Telemetry>>,
builder: Arc<PromptBuilder>,
cx: &mut ModelContext<Self>,
) -> Self {
@@ -2378,7 +2356,7 @@ impl Codegen {
buffer.clone(),
range.clone(),
false,
Some(telemetry.clone()),
telemetry.clone(),
builder.clone(),
cx,
)
@@ -2469,7 +2447,7 @@ impl Codegen {
self.buffer.clone(),
self.range.clone(),
false,
Some(self.telemetry.clone()),
self.telemetry.clone(),
self.builder.clone(),
cx,
)
@@ -2572,7 +2550,6 @@ pub struct CodegenAlternative {
line_operations: Vec<LineOperation>,
request: Option<LanguageModelRequest>,
elapsed_time: Option<f64>,
completion: Option<String>,
message_id: Option<String>,
}
@@ -2648,7 +2625,6 @@ impl CodegenAlternative {
range,
request: None,
elapsed_time: None,
completion: None,
}
}
@@ -2861,9 +2837,6 @@ impl CodegenAlternative {
self.diff = Diff::default();
self.status = CodegenStatus::Pending;
let mut edit_start = self.range.start.to_offset(&snapshot);
let completion = Arc::new(Mutex::new(String::new()));
let completion_clone = completion.clone();
self.generation = cx.spawn(|codegen, mut cx| {
async move {
let stream = stream.await;
@@ -2895,7 +2868,6 @@ impl CodegenAlternative {
response_latency = Some(request_start.elapsed());
}
let chunk = chunk?;
completion_clone.lock().push_str(&chunk);
let mut lines = chunk.split('\n').peekable();
while let Some(line) = lines.next() {
@@ -3065,7 +3037,6 @@ impl CodegenAlternative {
this.status = CodegenStatus::Done;
}
this.elapsed_time = Some(elapsed_time);
this.completion = Some(completion.lock().clone());
cx.emit(CodegenEvent::Finished);
cx.notify();
})

View File

@@ -1,115 +1,37 @@
use std::sync::Arc;
use feature_flags::ZedPro;
use gpui::{
Action, AnyElement, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Task,
View, WeakView,
};
use gpui::Action;
use gpui::DismissEvent;
use language_model::{LanguageModel, LanguageModelAvailability, LanguageModelRegistry};
use picker::{Picker, PickerDelegate};
use proto::Plan;
use ui::{prelude::*, ListItem, ListItemSpacing, PopoverMenu, PopoverMenuHandle, PopoverTrigger};
use workspace::ShowConfiguration;
use std::sync::Arc;
use ui::ListItemSpacing;
use crate::assistant_settings::AssistantSettings;
use fs::Fs;
use gpui::SharedString;
use gpui::Task;
use picker::{Picker, PickerDelegate};
use settings::update_settings_file;
use ui::{prelude::*, ListItem, PopoverMenu, PopoverMenuHandle, PopoverTrigger};
const TRY_ZED_PRO_URL: &str = "https://zed.dev/pro";
type OnModelChanged = Arc<dyn Fn(Arc<dyn LanguageModel>, &AppContext) + 'static>;
pub struct LanguageModelSelector {
picker: View<Picker<LanguageModelPickerDelegate>>,
}
impl LanguageModelSelector {
pub fn new(
on_model_changed: impl Fn(Arc<dyn LanguageModel>, &AppContext) + 'static,
cx: &mut ViewContext<Self>,
) -> Self {
let on_model_changed = Arc::new(on_model_changed);
let all_models = LanguageModelRegistry::global(cx)
.read(cx)
.providers()
.iter()
.flat_map(|provider| {
let icon = provider.icon();
provider.provided_models(cx).into_iter().map(move |model| {
let model = model.clone();
let icon = model.icon().unwrap_or(icon);
ModelInfo {
model: model.clone(),
icon,
availability: model.availability(),
}
})
})
.collect::<Vec<_>>();
let delegate = LanguageModelPickerDelegate {
language_model_selector: cx.view().downgrade(),
on_model_changed: on_model_changed.clone(),
all_models: all_models.clone(),
filtered_models: all_models,
selected_index: 0,
};
let picker =
cx.new_view(|cx| Picker::uniform_list(delegate, cx).max_height(Some(rems(20.).into())));
LanguageModelSelector { picker }
}
}
impl EventEmitter<DismissEvent> for LanguageModelSelector {}
impl FocusableView for LanguageModelSelector {
fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
self.picker.focus_handle(cx)
}
}
impl Render for LanguageModelSelector {
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
self.picker.clone()
}
}
#[derive(IntoElement)]
pub struct LanguageModelSelectorPopoverMenu<T>
where
T: PopoverTrigger,
{
language_model_selector: View<LanguageModelSelector>,
pub struct ModelSelector<T: PopoverTrigger> {
handle: Option<PopoverMenuHandle<Picker<ModelPickerDelegate>>>,
fs: Arc<dyn Fs>,
trigger: T,
handle: Option<PopoverMenuHandle<LanguageModelSelector>>,
info_text: Option<SharedString>,
}
impl<T: PopoverTrigger> LanguageModelSelectorPopoverMenu<T> {
pub fn new(language_model_selector: View<LanguageModelSelector>, trigger: T) -> Self {
Self {
language_model_selector,
trigger,
handle: None,
}
}
pub fn with_handle(mut self, handle: PopoverMenuHandle<LanguageModelSelector>) -> Self {
self.handle = Some(handle);
self
}
}
impl<T: PopoverTrigger> RenderOnce for LanguageModelSelectorPopoverMenu<T> {
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
let language_model_selector = self.language_model_selector.clone();
PopoverMenu::new("model-switcher")
.menu(move |_cx| Some(language_model_selector.clone()))
.trigger(self.trigger)
.attach(gpui::AnchorCorner::BottomLeft)
.when_some(self.handle.clone(), |menu, handle| menu.with_handle(handle))
}
pub struct ModelPickerDelegate {
fs: Arc<dyn Fs>,
all_models: Vec<ModelInfo>,
filtered_models: Vec<ModelInfo>,
selected_index: usize,
}
#[derive(Clone)]
@@ -117,17 +39,31 @@ struct ModelInfo {
model: Arc<dyn LanguageModel>,
icon: IconName,
availability: LanguageModelAvailability,
is_selected: bool,
}
pub struct LanguageModelPickerDelegate {
language_model_selector: WeakView<LanguageModelSelector>,
on_model_changed: OnModelChanged,
all_models: Vec<ModelInfo>,
filtered_models: Vec<ModelInfo>,
selected_index: usize,
impl<T: PopoverTrigger> ModelSelector<T> {
pub fn new(fs: Arc<dyn Fs>, trigger: T) -> Self {
ModelSelector {
handle: None,
fs,
trigger,
info_text: None,
}
}
pub fn with_handle(mut self, handle: PopoverMenuHandle<Picker<ModelPickerDelegate>>) -> Self {
self.handle = Some(handle);
self
}
pub fn with_info_text(mut self, text: impl Into<SharedString>) -> Self {
self.info_text = Some(text.into());
self
}
}
impl PickerDelegate for LanguageModelPickerDelegate {
impl PickerDelegate for ModelPickerDelegate {
type ListItem = ListItem;
fn match_count(&self) -> usize {
@@ -149,36 +85,14 @@ impl PickerDelegate for LanguageModelPickerDelegate {
fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
let all_models = self.all_models.clone();
let llm_registry = LanguageModelRegistry::global(cx);
let configured_models: Vec<_> = llm_registry
.read(cx)
.providers()
.iter()
.filter(|provider| provider.is_authenticated(cx))
.map(|provider| provider.id())
.collect();
cx.spawn(|this, mut cx| async move {
let filtered_models = cx
.background_executor()
.spawn(async move {
let displayed_models = if configured_models.is_empty() {
all_models
} else {
all_models
.into_iter()
.filter(|model_info| {
configured_models.contains(&model_info.model.provider_id())
})
.collect::<Vec<_>>()
};
if query.is_empty() {
displayed_models
all_models
} else {
displayed_models
all_models
.into_iter()
.filter(|model_info| {
model_info
@@ -205,40 +119,27 @@ impl PickerDelegate for LanguageModelPickerDelegate {
fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext<Picker<Self>>) {
if let Some(model_info) = self.filtered_models.get(self.selected_index) {
let model = model_info.model.clone();
(self.on_model_changed)(model.clone(), cx);
update_settings_file::<AssistantSettings>(self.fs.clone(), cx, move |settings, _| {
settings.set_model(model.clone())
});
// Update the selection status
let selected_model_id = model_info.model.id();
let selected_provider_id = model_info.model.provider_id();
for model in &mut self.all_models {
model.is_selected = model.model.id() == selected_model_id
&& model.model.provider_id() == selected_provider_id;
}
for model in &mut self.filtered_models {
model.is_selected = model.model.id() == selected_model_id
&& model.model.provider_id() == selected_provider_id;
}
cx.emit(DismissEvent);
}
}
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
self.language_model_selector
.update(cx, |_this, cx| cx.emit(DismissEvent))
.ok();
}
fn render_header(&self, cx: &mut ViewContext<Picker<Self>>) -> Option<AnyElement> {
let configured_models_count = LanguageModelRegistry::global(cx)
.read(cx)
.providers()
.iter()
.filter(|provider| provider.is_authenticated(cx))
.count();
if configured_models_count > 0 {
Some(
Label::new("Configured Models")
.size(LabelSize::Small)
.color(Color::Muted)
.mt_1()
.mb_0p5()
.ml_3()
.into_any_element(),
)
} else {
None
}
}
fn dismissed(&mut self, _cx: &mut ViewContext<Picker<Self>>) {}
fn render_match(
&self,
@@ -247,27 +148,15 @@ impl PickerDelegate for LanguageModelPickerDelegate {
cx: &mut ViewContext<Picker<Self>>,
) -> Option<Self::ListItem> {
use feature_flags::FeatureFlagAppExt;
let show_badges = cx.has_flag::<ZedPro>();
let model_info = self.filtered_models.get(ix)?;
let provider_name: String = model_info.model.provider_name().0.clone().into();
let active_provider_id = LanguageModelRegistry::read_global(cx)
.active_provider()
.map(|m| m.id());
let active_model_id = LanguageModelRegistry::read_global(cx)
.active_model()
.map(|m| m.id());
let is_selected = Some(model_info.model.provider_id()) == active_provider_id
&& Some(model_info.model.id()) == active_model_id;
let show_badges = cx.has_flag::<ZedPro>();
let provider_name: String = model_info.model.provider_name().0.into();
Some(
ListItem::new(ix)
.inset(true)
.spacing(ListItemSpacing::Sparse)
.toggle_state(selected)
.selected(selected)
.start_slot(
div().pr_0p5().child(
Icon::new(model_info.icon)
@@ -276,34 +165,29 @@ impl PickerDelegate for LanguageModelPickerDelegate {
),
)
.child(
h_flex()
.w_full()
.items_center()
.gap_1p5()
.min_w(px(200.))
.child(Label::new(model_info.model.name().0.clone()))
.child(
h_flex()
.gap_0p5()
.child(
Label::new(provider_name)
.size(LabelSize::XSmall)
.color(Color::Muted),
)
.children(match model_info.availability {
LanguageModelAvailability::Public => None,
LanguageModelAvailability::RequiresPlan(Plan::Free) => None,
LanguageModelAvailability::RequiresPlan(Plan::ZedPro) => {
show_badges.then(|| {
Label::new("Pro")
.size(LabelSize::XSmall)
.color(Color::Muted)
})
}
}),
),
h_flex().w_full().justify_between().min_w(px(200.)).child(
h_flex()
.gap_1p5()
.child(Label::new(model_info.model.name().0.clone()))
.child(
Label::new(provider_name)
.size(LabelSize::XSmall)
.color(Color::Muted),
)
.children(match model_info.availability {
LanguageModelAvailability::Public => None,
LanguageModelAvailability::RequiresPlan(Plan::Free) => None,
LanguageModelAvailability::RequiresPlan(Plan::ZedPro) => {
show_badges.then(|| {
Label::new("Pro")
.size(LabelSize::XSmall)
.color(Color::Muted)
})
}
}),
),
)
.end_slot(div().when(is_selected, |this| {
.end_slot(div().when(model_info.is_selected, |this| {
this.child(
Icon::new(IconName::Check)
.color(Color::Accent)
@@ -329,7 +213,7 @@ impl PickerDelegate for LanguageModelPickerDelegate {
.justify_between()
.when(cx.has_flag::<ZedPro>(), |this| {
this.child(match plan {
// Already a Zed Pro subscriber
// Already a zed pro subscriber
Plan::ZedPro => Button::new("zed-pro", "Zed Pro")
.icon(IconName::ZedAssistant)
.icon_size(IconSize::Small)
@@ -364,3 +248,57 @@ impl PickerDelegate for LanguageModelPickerDelegate {
)
}
}
impl<T: PopoverTrigger> RenderOnce for ModelSelector<T> {
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
let selected_provider = LanguageModelRegistry::read_global(cx)
.active_provider()
.map(|m| m.id());
let selected_model = LanguageModelRegistry::read_global(cx)
.active_model()
.map(|m| m.id());
let all_models = LanguageModelRegistry::global(cx)
.read(cx)
.providers()
.iter()
.flat_map(|provider| {
let provider_id = provider.id();
let icon = provider.icon();
let selected_model = selected_model.clone();
let selected_provider = selected_provider.clone();
provider.provided_models(cx).into_iter().map(move |model| {
let model = model.clone();
let icon = model.icon().unwrap_or(icon);
ModelInfo {
model: model.clone(),
icon,
availability: model.availability(),
is_selected: selected_model.as_ref() == Some(&model.id())
&& selected_provider.as_ref() == Some(&provider_id),
}
})
})
.collect::<Vec<_>>();
let delegate = ModelPickerDelegate {
fs: self.fs.clone(),
all_models: all_models.clone(),
filtered_models: all_models,
selected_index: 0,
};
let picker_view = cx.new_view(|cx| {
let picker = Picker::uniform_list(delegate, cx).max_height(Some(rems(20.).into()));
picker
});
PopoverMenu::new("model-switcher")
.menu(move |_cx| Some(picker_view.clone()))
.trigger(self.trigger)
.attach(gpui::AnchorCorner::BottomLeft)
.when_some(self.handle, |menu, handle| menu.with_handle(handle))
}
}

View File

@@ -1,4 +1,3 @@
use crate::SlashCommandWorkingSet;
use crate::{slash_command::SlashCommandCompletionProvider, AssistantPanel, InlineAssistant};
use anyhow::{anyhow, Result};
use chrono::{DateTime, Utc};
@@ -11,8 +10,8 @@ use futures::{
use fuzzy::StringMatchCandidate;
use gpui::{
actions, point, size, transparent_black, Action, AppContext, BackgroundExecutor, Bounds,
EventEmitter, Global, PromptLevel, ReadGlobal, Subscription, Task, TextStyle, TitlebarOptions,
UpdateGlobal, View, WindowBounds, WindowHandle, WindowOptions,
EventEmitter, Global, HighlightStyle, PromptLevel, ReadGlobal, Subscription, Task, TextStyle,
TitlebarOptions, UpdateGlobal, View, WindowBounds, WindowHandle, WindowOptions,
};
use heed::{
types::{SerdeBincode, SerdeJson, Str},
@@ -232,13 +231,13 @@ impl PickerDelegate for PromptPickerDelegate {
let element = ListItem::new(ix)
.inset(true)
.spacing(ListItemSpacing::Sparse)
.toggle_state(selected)
.selected(selected)
.child(h_flex().h_5().line_height(relative(1.)).child(Label::new(
prompt.title.clone().unwrap_or("Untitled".into()),
)))
.end_slot::<IconButton>(default.then(|| {
IconButton::new("toggle-default-prompt", IconName::SparkleFilled)
.toggle_state(true)
.selected(true)
.icon_color(Color::Accent)
.shape(IconButtonShape::Square)
.tooltip(move |cx| Tooltip::text("Remove from Default Prompt", cx))
@@ -274,7 +273,7 @@ impl PickerDelegate for PromptPickerDelegate {
})
.child(
IconButton::new("toggle-default-prompt", IconName::Sparkle)
.toggle_state(default)
.selected(default)
.selected_icon(IconName::SparkleFilled)
.icon_color(if default { Color::Accent } else { Color::Muted })
.shape(IconButtonShape::Square)
@@ -523,11 +522,7 @@ impl PromptLibrary {
editor.set_use_modal_editing(false);
editor.set_current_line_highlight(Some(CurrentLineHighlight::None));
editor.set_completion_provider(Some(Box::new(
SlashCommandCompletionProvider::new(
Arc::new(SlashCommandWorkingSet::default()),
None,
None,
),
SlashCommandCompletionProvider::new(None, None),
)));
if focus {
editor.focus(cx);
@@ -830,7 +825,7 @@ impl PromptLibrary {
.overflow_x_hidden()
.child(
h_flex()
.p(DynamicSpacing::Base04.rems(cx))
.p(Spacing::Small.rems(cx))
.h_9()
.w_full()
.flex_none()
@@ -871,17 +866,17 @@ impl PromptLibrary {
.size_full()
.relative()
.overflow_hidden()
.pl(DynamicSpacing::Base16.rems(cx))
.pt(DynamicSpacing::Base08.rems(cx))
.pl(Spacing::XXLarge.rems(cx))
.pt(Spacing::Large.rems(cx))
.on_click(cx.listener(move |_, _, cx| {
cx.focus(&focus_handle);
}))
.child(
h_flex()
.group("active-editor-header")
.pr(DynamicSpacing::Base16.rems(cx))
.pt(DynamicSpacing::Base02.rems(cx))
.pb(DynamicSpacing::Base08.rems(cx))
.pr(Spacing::XXLarge.rems(cx))
.pt(Spacing::XSmall.rems(cx))
.pb(Spacing::Large.rems(cx))
.justify_between()
.child(
h_flex().gap_1().child(
@@ -928,8 +923,10 @@ impl PromptLibrary {
status: cx.theme().status().clone(),
inlay_hints_style:
editor::make_inlay_hints_style(cx),
inline_completion_styles:
editor::make_suggestion_styles(cx),
suggestions_style: HighlightStyle {
color: Some(cx.theme().status().predictive),
..HighlightStyle::default()
},
..EditorStyle::default()
},
)),
@@ -941,13 +938,13 @@ impl PromptLibrary {
.child(
h_flex()
.h_full()
.gap(DynamicSpacing::Base16.rems(cx))
.gap(Spacing::XXLarge.rems(cx))
.child(div()),
)
.child(
h_flex()
.h_full()
.gap(DynamicSpacing::Base16.rems(cx))
.gap(Spacing::XXLarge.rems(cx))
.children(prompt_editor.token_count.map(
|token_count| {
let token_count: SharedString =
@@ -1053,7 +1050,7 @@ impl PromptLibrary {
IconName::Sparkle,
)
.style(ButtonStyle::Transparent)
.toggle_state(prompt_metadata.default)
.selected(prompt_metadata.default)
.selected_icon(IconName::SparkleFilled)
.icon_color(if prompt_metadata.default {
Color::Accent

View File

@@ -149,7 +149,7 @@ impl PromptBuilder {
if file_path.to_string_lossy().ends_with(".hbs") {
if let Ok(content) = params.fs.load(&file_path).await {
let file_name = file_path.file_stem().unwrap().to_string_lossy();
log::debug!("Registering prompt template override: {}", file_name);
log::info!("Registering prompt template override: {}", file_name);
handlebars.lock().register_template_string(&file_name, content).log_err();
}
}
@@ -194,7 +194,7 @@ impl PromptBuilder {
for path in Assets.list("prompts")? {
if let Some(id) = path.split('/').last().and_then(|s| s.strip_suffix(".hbs")) {
if let Some(prompt) = Assets.load(path.as_ref()).log_err().flatten() {
log::debug!("Registering built-in prompt template: {}", id);
log::info!("Registering built-in prompt template: {}", id);
let prompt = String::from_utf8_lossy(prompt.as_ref());
handlebars.register_template_string(id, LineEnding::normalize_cow(prompt))?
}

View File

@@ -1,8 +1,7 @@
use crate::assistant_panel::ContextEditor;
use crate::SlashCommandWorkingSet;
use anyhow::Result;
use assistant_slash_command::AfterCompletion;
pub use assistant_slash_command::{SlashCommand, SlashCommandOutput};
pub use assistant_slash_command::{SlashCommand, SlashCommandOutput, SlashCommandRegistry};
use editor::{CompletionProvider, Editor};
use fuzzy::{match_strings, StringMatchCandidate};
use gpui::{AppContext, Model, Task, ViewContext, WeakView, WindowContext};
@@ -40,7 +39,6 @@ pub mod terminal_command;
pub(crate) struct SlashCommandCompletionProvider {
cancel_flag: Mutex<Arc<AtomicBool>>,
slash_commands: Arc<SlashCommandWorkingSet>,
editor: Option<WeakView<ContextEditor>>,
workspace: Option<WeakView<Workspace>>,
}
@@ -54,13 +52,11 @@ pub(crate) struct SlashCommandLine {
impl SlashCommandCompletionProvider {
pub fn new(
slash_commands: Arc<SlashCommandWorkingSet>,
editor: Option<WeakView<ContextEditor>>,
workspace: Option<WeakView<Workspace>>,
) -> Self {
Self {
cancel_flag: Mutex::new(Arc::new(AtomicBool::new(false))),
slash_commands,
editor,
workspace,
}
@@ -73,9 +69,9 @@ impl SlashCommandCompletionProvider {
name_range: Range<Anchor>,
cx: &mut WindowContext,
) -> Task<Result<Vec<project::Completion>>> {
let slash_commands = self.slash_commands.clone();
let candidates = slash_commands
.command_names(cx)
let commands = SlashCommandRegistry::global(cx);
let candidates = commands
.command_names()
.into_iter()
.enumerate()
.map(|(ix, def)| StringMatchCandidate {
@@ -102,7 +98,7 @@ impl SlashCommandCompletionProvider {
matches
.into_iter()
.filter_map(|mat| {
let command = slash_commands.command(&mat.string, cx)?;
let command = commands.command(&mat.string)?;
let mut new_text = mat.string.clone();
let requires_argument = command.requires_argument();
let accepts_arguments = command.accepts_arguments();
@@ -171,7 +167,8 @@ impl SlashCommandCompletionProvider {
let mut flag = self.cancel_flag.lock();
flag.store(true, SeqCst);
*flag = new_cancel_flag.clone();
if let Some(command) = self.slash_commands.command(command_name, cx) {
let commands = SlashCommandRegistry::global(cx);
if let Some(command) = commands.command(command_name) {
let completions = command.complete_argument(
arguments,
new_cancel_flag.clone(),

View File

@@ -4,7 +4,7 @@ use assistant_slash_command::{
SlashCommandOutputSection, SlashCommandResult,
};
use collections::HashMap;
use context_server::{
use context_servers::{
manager::{ContextServer, ContextServerManager},
types::Prompt,
};
@@ -20,7 +20,7 @@ use crate::slash_command::create_label_for_command;
pub struct ContextServerSlashCommand {
server_manager: Model<ContextServerManager>,
server_id: Arc<str>,
server_id: String,
prompt: Prompt,
}
@@ -31,7 +31,7 @@ impl ContextServerSlashCommand {
prompt: Prompt,
) -> Self {
Self {
server_id: server.id(),
server_id: server.id.clone(),
prompt,
server_manager,
}
@@ -89,15 +89,15 @@ impl SlashCommand for ContextServerSlashCommand {
if let Some(server) = self.server_manager.read(cx).get_server(&server_id) {
cx.foreground_executor().spawn(async move {
let Some(protocol) = server.client() else {
let Some(protocol) = server.client.read().clone() else {
return Err(anyhow!("Context server not initialized"));
};
let completion_result = protocol
.completion(
context_server::types::CompletionReference::Prompt(
context_server::types::PromptReference {
r#type: context_server::types::PromptReferenceType::Prompt,
context_servers::types::CompletionReference::Prompt(
context_servers::types::PromptReference {
r#type: context_servers::types::PromptReferenceType::Prompt,
name: prompt_name,
},
),
@@ -143,7 +143,7 @@ impl SlashCommand for ContextServerSlashCommand {
let manager = self.server_manager.read(cx);
if let Some(server) = manager.get_server(&server_id) {
cx.foreground_executor().spawn(async move {
let Some(protocol) = server.client() else {
let Some(protocol) = server.client.read().clone() else {
return Err(anyhow!("Context server not initialized"));
};
let result = protocol.run_prompt(&prompt_name, prompt_args).await?;
@@ -152,7 +152,7 @@ impl SlashCommand for ContextServerSlashCommand {
if result
.messages
.iter()
.any(|msg| !matches!(msg.role, context_server::types::Role::User))
.any(|msg| !matches!(msg.role, context_servers::types::SamplingRole::User))
{
return Err(anyhow!(
"Prompt contains non-user roles, which is not supported"
@@ -164,7 +164,7 @@ impl SlashCommand for ContextServerSlashCommand {
.messages
.into_iter()
.filter_map(|msg| match msg.content {
context_server::types::MessageContent::Text { text, .. } => Some(text),
context_servers::types::SamplingContent::Text { text } => Some(text),
_ => None,
})
.collect::<Vec<String>>()

View File

@@ -69,10 +69,6 @@ impl SlashCommand for DefaultSlashCommand {
text.push('\n');
}
if !text.ends_with('\n') {
text.push('\n');
}
Ok(SlashCommandOutput {
sections: vec![SlashCommandOutputSection {
range: 0..text.len(),

View File

@@ -108,10 +108,6 @@ impl SlashCommand for FetchSlashCommand {
"Insert fetched URL contents".into()
}
fn icon(&self) -> IconName {
IconName::Globe
}
fn menu_text(&self) -> String {
self.description()
}
@@ -166,7 +162,7 @@ impl SlashCommand for FetchSlashCommand {
text,
sections: vec![SlashCommandOutputSection {
range,
icon: IconName::Globe,
icon: IconName::AtSign,
label: format!("fetch {}", url).into(),
metadata: None,
}],

View File

@@ -2,7 +2,7 @@ use std::sync::Arc;
use gpui::{AnyElement, DismissEvent, SharedString, Task, WeakView};
use picker::{Picker, PickerDelegate, PickerEditorPosition};
use ui::{prelude::*, ListItem, ListItemSpacing, PopoverMenu, PopoverTrigger, Tooltip};
use ui::{prelude::*, ListItem, ListItemSpacing, PopoverMenu, PopoverTrigger};
use crate::assistant_panel::ContextEditor;
use crate::SlashCommandWorkingSet;
@@ -176,18 +176,12 @@ impl PickerDelegate for SlashCommandDelegate {
ListItem::new(ix)
.inset(true)
.spacing(ListItemSpacing::Dense)
.toggle_state(selected)
.tooltip({
let description = info.description.clone();
move |cx| cx.new_view(|_| Tooltip::new(description.clone())).into()
})
.selected(selected)
.child(
v_flex()
.group(format!("command-entry-label-{ix}"))
.w_full()
.py_0p5()
.min_w(px(250.))
.max_w(px(400.))
.child(
h_flex()
.gap_1p5()
@@ -198,7 +192,7 @@ impl PickerDelegate for SlashCommandDelegate {
{
label.push_str(&args);
}
Label::new(label).single_line().size(LabelSize::Small)
Label::new(label).size(LabelSize::Small)
}))
.children(info.args.clone().filter(|_| !selected).map(
|args| {
@@ -206,7 +200,6 @@ impl PickerDelegate for SlashCommandDelegate {
.font_buffer(cx)
.child(
Label::new(args)
.single_line()
.size(LabelSize::Small)
.color(Color::Muted),
)
@@ -217,11 +210,9 @@ impl PickerDelegate for SlashCommandDelegate {
)),
)
.child(
div().overflow_hidden().text_ellipsis().child(
Label::new(info.description.clone())
.size(LabelSize::Small)
.color(Color::Muted),
),
Label::new(info.description.clone())
.size(LabelSize::Small)
.color(Color::Muted),
),
),
),
@@ -229,7 +220,7 @@ impl PickerDelegate for SlashCommandDelegate {
ListItem::new(ix)
.inset(true)
.spacing(ListItemSpacing::Dense)
.toggle_state(selected)
.selected(selected)
.child(renderer(cx)),
),
}

View File

@@ -4,7 +4,7 @@ use gpui::AppContext;
use parking_lot::Mutex;
use std::sync::Arc;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, Default)]
pub struct SlashCommandId(usize);
/// A working set of slash commands for use in one instance of the Assistant Panel.
@@ -16,7 +16,7 @@ pub struct SlashCommandWorkingSet {
#[derive(Default)]
struct WorkingSetState {
context_server_commands_by_id: HashMap<SlashCommandId, Arc<dyn SlashCommand>>,
context_server_commands_by_name: HashMap<Arc<str>, Arc<dyn SlashCommand>>,
context_server_commands_by_name: HashMap<String, Arc<dyn SlashCommand>>,
next_command_id: SlashCommandId,
}
@@ -30,19 +30,6 @@ impl SlashCommandWorkingSet {
.or_else(|| SlashCommandRegistry::global(cx).command(name))
}
pub fn command_names(&self, cx: &AppContext) -> Vec<Arc<str>> {
let mut command_names = SlashCommandRegistry::global(cx).command_names();
command_names.extend(
self.state
.lock()
.context_server_commands_by_name
.keys()
.cloned(),
);
command_names
}
pub fn featured_command_names(&self, cx: &AppContext) -> Vec<Arc<str>> {
SlashCommandRegistry::global(cx).featured_command_names()
}
@@ -73,7 +60,7 @@ impl WorkingSetState {
self.context_server_commands_by_name.extend(
self.context_server_commands_by_id
.values()
.map(|command| (command.name().into(), command.clone())),
.map(|command| (command.name(), command.clone())),
);
}
}

View File

@@ -1,7 +1,6 @@
use crate::assistant_settings::AssistantSettings;
use crate::{
humanize_token_count, prompts::PromptBuilder, AssistantPanel, AssistantPanelEvent, RequestType,
DEFAULT_CONTEXT_LINES,
humanize_token_count, prompts::PromptBuilder, AssistantPanel, AssistantPanelEvent,
ModelSelector, RequestType, DEFAULT_CONTEXT_LINES,
};
use anyhow::{Context as _, Result};
use client::telemetry::Telemetry;
@@ -18,11 +17,10 @@ use gpui::{
};
use language::Buffer;
use language_model::{
LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, Role,
logging::report_assistant_event, LanguageModelRegistry, LanguageModelRequest,
LanguageModelRequestMessage, Role,
};
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
use language_models::report_assistant_event;
use settings::{update_settings_file, Settings};
use settings::Settings;
use std::{
cmp,
sync::Arc,
@@ -32,7 +30,7 @@ use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};
use terminal::Terminal;
use terminal_view::TerminalView;
use theme::ThemeSettings;
use ui::{prelude::*, text_for_action, IconButtonShape, Tooltip};
use ui::{prelude::*, IconButtonShape, Tooltip};
use util::ResultExt;
use workspace::{notifications::NotificationId, Toast, Workspace};
@@ -476,9 +474,9 @@ enum PromptEditorEvent {
struct PromptEditor {
id: TerminalInlineAssistId,
fs: Arc<dyn Fs>,
height_in_lines: u8,
editor: View<Editor>,
language_model_selector: View<LanguageModelSelector>,
edited_since_done: bool,
prompt_history: VecDeque<String>,
prompt_history_ix: Option<usize>,
@@ -614,8 +612,8 @@ impl Render for PromptEditor {
.w_12()
.justify_center()
.gap_2()
.child(LanguageModelSelectorPopoverMenu::new(
self.language_model_selector.clone(),
.child(ModelSelector::new(
self.fs.clone(),
IconButton::new("context", IconName::SettingsAlt)
.shape(IconButtonShape::Square)
.icon_size(IconSize::Small)
@@ -695,7 +693,7 @@ impl PromptEditor {
cx,
);
editor.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
editor.set_placeholder_text(Self::placeholder_text(cx), cx);
editor.set_placeholder_text("Add a prompt…", cx);
editor
});
@@ -709,19 +707,6 @@ impl PromptEditor {
id,
height_in_lines: 1,
editor: prompt_editor,
language_model_selector: cx.new_view(|cx| {
let fs = fs.clone();
LanguageModelSelector::new(
move |model, cx| {
update_settings_file::<AssistantSettings>(
fs.clone(),
cx,
move |settings, _| settings.set_model(model.clone()),
);
},
cx,
)
}),
edited_since_done: false,
prompt_history,
prompt_history_ix: None,
@@ -729,6 +714,7 @@ impl PromptEditor {
_codegen_subscription: cx.observe(&codegen, Self::handle_codegen_changed),
editor_subscriptions: Vec::new(),
codegen,
fs,
pending_token_count: Task::ready(Ok(())),
token_count: None,
_token_count_subscriptions: token_count_subscriptions,
@@ -740,14 +726,6 @@ impl PromptEditor {
this
}
fn placeholder_text(cx: &WindowContext) -> String {
let context_keybinding = text_for_action(&crate::ToggleFocus, cx)
.map(|keybinding| format!("{keybinding} for context"))
.unwrap_or_default();
format!("Generate…{context_keybinding} • ↓↑ for history")
}
fn subscribe_to_editor(&mut self, cx: &mut ViewContext<Self>) {
self.editor_subscriptions.clear();
self.editor_subscriptions

View File

@@ -1,10 +1,8 @@
use std::sync::Arc;
use assistant_tool::{Tool, ToolRegistry};
use collections::HashMap;
use gpui::AppContext;
use parking_lot::Mutex;
use crate::{Tool, ToolRegistry};
use std::sync::Arc;
#[derive(Copy, Clone, PartialEq, Eq, Hash, Default)]
pub struct ToolId(usize);

View File

@@ -0,0 +1,2 @@
pub mod context_server_tool;
pub mod now_tool;

View File

@@ -1,22 +1,19 @@
use std::sync::Arc;
use anyhow::{anyhow, bail};
use assistant_tool::Tool;
use context_servers::manager::ContextServerManager;
use context_servers::types;
use gpui::{Model, Task};
use crate::manager::ContextServerManager;
use crate::types;
pub struct ContextServerTool {
server_manager: Model<ContextServerManager>,
server_id: Arc<str>,
server_id: String,
tool: types::Tool,
}
impl ContextServerTool {
pub fn new(
server_manager: Model<ContextServerManager>,
server_id: impl Into<Arc<str>>,
server_id: impl Into<String>,
tool: types::Tool,
) -> Self {
Self {
@@ -58,7 +55,7 @@ impl Tool for ContextServerTool {
cx.foreground_executor().spawn({
let tool_name = self.tool.name.clone();
async move {
let Some(protocol) = server.client() else {
let Some(protocol) = server.client.read().clone() else {
bail!("Context server not initialized");
};
@@ -75,21 +72,11 @@ impl Tool for ContextServerTool {
);
let response = protocol.run_tool(tool_name, arguments).await?;
let mut result = String::new();
for content in response.content {
match content {
types::ToolResponseContent::Text { text } => {
result.push_str(&text);
}
types::ToolResponseContent::Image { .. } => {
log::warn!("Ignoring image content from tool response");
}
types::ToolResponseContent::Resource { .. } => {
log::warn!("Ignoring resource content from tool response");
}
}
}
Ok(result)
let tool_result = match response.tool_result {
serde_json::Value::String(s) => s,
_ => serde_json::to_string(&response.tool_result)?,
};
Ok(tool_result)
}
})
} else {

View File

@@ -30,7 +30,7 @@ impl Tool for NowTool {
}
fn description(&self) -> String {
"Returns the current datetime in RFC 3339 format. Only use this tool when the user specifically asks for it or the current task would benefit from knowing the current datetime.".into()
"Returns the current datetime in RFC 3339 format.".into()
}
fn input_schema(&self) -> serde_json::Value {

View File

@@ -1,77 +0,0 @@
[package]
name = "assistant2"
version = "0.1.0"
edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lints]
workspace = true
[lib]
path = "src/assistant.rs"
doctest = false
[dependencies]
anthropic = { workspace = true, features = ["schemars"] }
anyhow.workspace = true
assets.workspace = true
assistant_tool.workspace = true
async-watch.workspace = true
client.workspace = true
chrono.workspace = true
collections.workspace = true
command_palette_hooks.workspace = true
context_server.workspace = true
db.workspace = true
editor.workspace = true
feature_flags.workspace = true
fs.workspace = true
futures.workspace = true
fuzzy.workspace = true
gpui.workspace = true
handlebars.workspace = true
html_to_markdown.workspace = true
http_client.workspace = true
language.workspace = true
language_model.workspace = true
language_model_selector.workspace = true
language_models.workspace = true
log.workspace = true
lsp.workspace = true
markdown.workspace = true
menu.workspace = true
multi_buffer.workspace = true
ollama = { workspace = true, features = ["schemars"] }
open_ai = { workspace = true, features = ["schemars"] }
ordered-float.workspace = true
paths.workspace = true
parking_lot.workspace = true
picker.workspace = true
project.workspace = true
proto.workspace = true
rope.workspace = true
schemars.workspace = true
serde.workspace = true
serde_json.workspace = true
serde_json_lenient.workspace = true
settings.workspace = true
similar.workspace = true
smol.workspace = true
telemetry_events.workspace = true
terminal_view.workspace = true
text.workspace = true
terminal.workspace = true
theme.workspace = true
time.workspace = true
time_format.workspace = true
ui.workspace = true
unindent.workspace = true
util.workspace = true
uuid.workspace = true
workspace.workspace = true
zed_actions.workspace = true
[dev-dependencies]
rand.workspace = true
indoc.workspace = true

View File

@@ -1,254 +0,0 @@
use std::sync::Arc;
use assistant_tool::ToolWorkingSet;
use collections::HashMap;
use gpui::{
list, AnyElement, AppContext, Empty, ListAlignment, ListState, Model, StyleRefinement,
Subscription, TextStyleRefinement, View, WeakView,
};
use language::LanguageRegistry;
use language_model::Role;
use markdown::{Markdown, MarkdownStyle};
use settings::Settings as _;
use theme::ThemeSettings;
use ui::prelude::*;
use workspace::Workspace;
use crate::thread::{MessageId, Thread, ThreadError, ThreadEvent};
use crate::ui::ContextPill;
pub struct ActiveThread {
workspace: WeakView<Workspace>,
language_registry: Arc<LanguageRegistry>,
tools: Arc<ToolWorkingSet>,
thread: Model<Thread>,
messages: Vec<MessageId>,
list_state: ListState,
rendered_messages_by_id: HashMap<MessageId, View<Markdown>>,
last_error: Option<ThreadError>,
_subscriptions: Vec<Subscription>,
}
impl ActiveThread {
pub fn new(
thread: Model<Thread>,
workspace: WeakView<Workspace>,
language_registry: Arc<LanguageRegistry>,
tools: Arc<ToolWorkingSet>,
cx: &mut ViewContext<Self>,
) -> Self {
let subscriptions = vec![
cx.observe(&thread, |_, _, cx| cx.notify()),
cx.subscribe(&thread, Self::handle_thread_event),
];
let mut this = Self {
workspace,
language_registry,
tools,
thread: thread.clone(),
messages: Vec::new(),
rendered_messages_by_id: HashMap::default(),
list_state: ListState::new(0, ListAlignment::Bottom, px(1024.), {
let this = cx.view().downgrade();
move |ix, cx: &mut WindowContext| {
this.update(cx, |this, cx| this.render_message(ix, cx))
.unwrap()
}
}),
last_error: None,
_subscriptions: subscriptions,
};
for message in thread.read(cx).messages().cloned().collect::<Vec<_>>() {
this.push_message(&message.id, message.text.clone(), cx);
}
this
}
pub fn is_empty(&self) -> bool {
self.messages.is_empty()
}
pub fn summary(&self, cx: &AppContext) -> Option<SharedString> {
self.thread.read(cx).summary()
}
pub fn last_error(&self) -> Option<ThreadError> {
self.last_error.clone()
}
pub fn clear_last_error(&mut self) {
self.last_error.take();
}
fn push_message(&mut self, id: &MessageId, text: String, cx: &mut ViewContext<Self>) {
let old_len = self.messages.len();
self.messages.push(*id);
self.list_state.splice(old_len..old_len, 1);
let theme_settings = ThemeSettings::get_global(cx);
let ui_font_size = TextSize::Default.rems(cx);
let buffer_font_size = theme_settings.buffer_font_size;
let mut text_style = cx.text_style();
text_style.refine(&TextStyleRefinement {
font_family: Some(theme_settings.ui_font.family.clone()),
font_size: Some(ui_font_size.into()),
color: Some(cx.theme().colors().text),
..Default::default()
});
let markdown_style = MarkdownStyle {
base_text_style: text_style,
syntax: cx.theme().syntax().clone(),
selection_background_color: cx.theme().players().local().selection,
code_block: StyleRefinement {
text: Some(TextStyleRefinement {
font_family: Some(theme_settings.buffer_font.family.clone()),
font_size: Some(buffer_font_size.into()),
..Default::default()
}),
..Default::default()
},
inline_code: TextStyleRefinement {
font_family: Some(theme_settings.buffer_font.family.clone()),
font_size: Some(ui_font_size.into()),
background_color: Some(cx.theme().colors().editor_background),
..Default::default()
},
..Default::default()
};
let markdown = cx.new_view(|cx| {
Markdown::new(
text,
markdown_style,
Some(self.language_registry.clone()),
None,
cx,
)
});
self.rendered_messages_by_id.insert(*id, markdown);
}
fn handle_thread_event(
&mut self,
_: Model<Thread>,
event: &ThreadEvent,
cx: &mut ViewContext<Self>,
) {
match event {
ThreadEvent::ShowError(error) => {
self.last_error = Some(error.clone());
}
ThreadEvent::StreamedCompletion => {}
ThreadEvent::SummaryChanged => {}
ThreadEvent::StreamedAssistantText(message_id, text) => {
if let Some(markdown) = self.rendered_messages_by_id.get_mut(&message_id) {
markdown.update(cx, |markdown, cx| {
markdown.append(text, cx);
});
}
}
ThreadEvent::MessageAdded(message_id) => {
if let Some(message_text) = self
.thread
.read(cx)
.message(*message_id)
.map(|message| message.text.clone())
{
self.push_message(message_id, message_text, cx);
}
cx.notify();
}
ThreadEvent::UsePendingTools => {
let pending_tool_uses = self
.thread
.read(cx)
.pending_tool_uses()
.into_iter()
.filter(|tool_use| tool_use.status.is_idle())
.cloned()
.collect::<Vec<_>>();
for tool_use in pending_tool_uses {
if let Some(tool) = self.tools.tool(&tool_use.name, cx) {
let task = tool.run(tool_use.input, self.workspace.clone(), cx);
self.thread.update(cx, |thread, cx| {
thread.insert_tool_output(
tool_use.assistant_message_id,
tool_use.id.clone(),
task,
cx,
);
});
}
}
}
ThreadEvent::ToolFinished { .. } => {}
}
}
fn render_message(&self, ix: usize, cx: &mut ViewContext<Self>) -> AnyElement {
let message_id = self.messages[ix];
let Some(message) = self.thread.read(cx).message(message_id) else {
return Empty.into_any();
};
let Some(markdown) = self.rendered_messages_by_id.get(&message_id) else {
return Empty.into_any();
};
let context = self.thread.read(cx).context_for_message(message_id);
let (role_icon, role_name) = match message.role {
Role::User => (IconName::Person, "You"),
Role::Assistant => (IconName::ZedAssistant, "Assistant"),
Role::System => (IconName::Settings, "System"),
};
div()
.id(("message-container", ix))
.p_2()
.child(
v_flex()
.border_1()
.border_color(cx.theme().colors().border_variant)
.rounded_md()
.child(
h_flex()
.justify_between()
.p_1p5()
.border_b_1()
.border_color(cx.theme().colors().border_variant)
.child(
h_flex()
.gap_2()
.child(Icon::new(role_icon).size(IconSize::Small))
.child(Label::new(role_name).size(LabelSize::Small)),
),
)
.child(v_flex().p_1p5().text_ui(cx).child(markdown.clone()))
.when_some(context, |parent, context| {
parent.child(
h_flex().flex_wrap().gap_2().p_1p5().children(
context
.iter()
.map(|context| ContextPill::new(context.clone())),
),
)
}),
)
.into_any()
}
}
impl Render for ActiveThread {
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
list(self.list_state.clone()).flex_1()
}
}

View File

@@ -1,98 +0,0 @@
mod active_thread;
mod assistant_panel;
mod assistant_settings;
mod context;
mod context_picker;
mod inline_assistant;
mod message_editor;
mod prompts;
mod streaming_diff;
mod terminal_inline_assistant;
mod thread;
mod thread_history;
mod thread_store;
mod ui;
use std::sync::Arc;
use assistant_settings::AssistantSettings;
use client::Client;
use command_palette_hooks::CommandPaletteFilter;
use feature_flags::{Assistant2FeatureFlag, FeatureFlagAppExt};
use fs::Fs;
use gpui::{actions, AppContext};
use prompts::PromptLoadingParams;
use settings::Settings as _;
use util::ResultExt;
pub use crate::assistant_panel::AssistantPanel;
actions!(
assistant2,
[
ToggleFocus,
NewThread,
ToggleModelSelector,
OpenHistory,
Chat,
ToggleInlineAssist,
CycleNextInlineAssist,
CyclePreviousInlineAssist
]
);
const NAMESPACE: &str = "assistant2";
/// Initializes the `assistant2` crate.
pub fn init(fs: Arc<dyn Fs>, client: Arc<Client>, stdout_is_a_pty: bool, cx: &mut AppContext) {
AssistantSettings::register(cx);
assistant_panel::init(cx);
let prompt_builder = prompts::PromptBuilder::new(Some(PromptLoadingParams {
fs: fs.clone(),
repo_path: stdout_is_a_pty
.then(|| std::env::current_dir().log_err())
.flatten(),
cx,
}))
.log_err()
.map(Arc::new)
.unwrap_or_else(|| Arc::new(prompts::PromptBuilder::new(None).unwrap()));
inline_assistant::init(
fs.clone(),
prompt_builder.clone(),
client.telemetry().clone(),
cx,
);
terminal_inline_assistant::init(
fs.clone(),
prompt_builder.clone(),
client.telemetry().clone(),
cx,
);
feature_gate_assistant2_actions(cx);
}
fn feature_gate_assistant2_actions(cx: &mut AppContext) {
const ASSISTANT1_NAMESPACE: &str = "assistant";
CommandPaletteFilter::update_global(cx, |filter, _cx| {
filter.hide_namespace(NAMESPACE);
});
cx.observe_flag::<Assistant2FeatureFlag, _>(move |is_enabled, cx| {
if is_enabled {
CommandPaletteFilter::update_global(cx, |filter, _cx| {
filter.show_namespace(NAMESPACE);
filter.hide_namespace(ASSISTANT1_NAMESPACE);
});
} else {
CommandPaletteFilter::update_global(cx, |filter, _cx| {
filter.hide_namespace(NAMESPACE);
filter.show_namespace(ASSISTANT1_NAMESPACE);
});
}
})
.detach();
}

View File

@@ -1,532 +0,0 @@
use std::sync::Arc;
use anyhow::Result;
use assistant_tool::ToolWorkingSet;
use client::zed_urls;
use gpui::{
prelude::*, px, svg, Action, AnyElement, AppContext, AsyncWindowContext, EventEmitter,
FocusHandle, FocusableView, FontWeight, Model, Pixels, Task, View, ViewContext, WeakView,
WindowContext,
};
use language::LanguageRegistry;
use time::UtcOffset;
use ui::{prelude::*, Divider, IconButtonShape, KeyBinding, Tab, Tooltip};
use workspace::dock::{DockPosition, Panel, PanelEvent};
use workspace::Workspace;
use crate::active_thread::ActiveThread;
use crate::message_editor::MessageEditor;
use crate::thread::{ThreadError, ThreadId};
use crate::thread_history::{PastThread, ThreadHistory};
use crate::thread_store::ThreadStore;
use crate::{NewThread, OpenHistory, ToggleFocus};
pub fn init(cx: &mut AppContext) {
cx.observe_new_views(
|workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
workspace.register_action(|workspace, _: &ToggleFocus, cx| {
workspace.toggle_panel_focus::<AssistantPanel>(cx);
});
},
)
.detach();
}
enum ActiveView {
Thread,
History,
}
pub struct AssistantPanel {
workspace: WeakView<Workspace>,
language_registry: Arc<LanguageRegistry>,
thread_store: Model<ThreadStore>,
thread: View<ActiveThread>,
message_editor: View<MessageEditor>,
tools: Arc<ToolWorkingSet>,
local_timezone: UtcOffset,
active_view: ActiveView,
history: View<ThreadHistory>,
}
impl AssistantPanel {
pub fn load(
workspace: WeakView<Workspace>,
cx: AsyncWindowContext,
) -> Task<Result<View<Self>>> {
cx.spawn(|mut cx| async move {
let tools = Arc::new(ToolWorkingSet::default());
let thread_store = workspace
.update(&mut cx, |workspace, cx| {
let project = workspace.project().clone();
ThreadStore::new(project, tools.clone(), cx)
})?
.await?;
workspace.update(&mut cx, |workspace, cx| {
cx.new_view(|cx| Self::new(workspace, thread_store, tools, cx))
})
})
}
fn new(
workspace: &Workspace,
thread_store: Model<ThreadStore>,
tools: Arc<ToolWorkingSet>,
cx: &mut ViewContext<Self>,
) -> Self {
let thread = thread_store.update(cx, |this, cx| this.create_thread(cx));
let language_registry = workspace.project().read(cx).languages().clone();
let workspace = workspace.weak_handle();
let weak_self = cx.view().downgrade();
Self {
active_view: ActiveView::Thread,
workspace: workspace.clone(),
language_registry: language_registry.clone(),
thread_store: thread_store.clone(),
thread: cx.new_view(|cx| {
ActiveThread::new(
thread.clone(),
workspace.clone(),
language_registry,
tools.clone(),
cx,
)
}),
message_editor: cx.new_view(|cx| MessageEditor::new(workspace, thread.clone(), cx)),
tools,
local_timezone: UtcOffset::from_whole_seconds(
chrono::Local::now().offset().local_minus_utc(),
)
.unwrap(),
history: cx.new_view(|cx| ThreadHistory::new(weak_self, thread_store, cx)),
}
}
pub(crate) fn local_timezone(&self) -> UtcOffset {
self.local_timezone
}
fn new_thread(&mut self, cx: &mut ViewContext<Self>) {
let thread = self
.thread_store
.update(cx, |this, cx| this.create_thread(cx));
self.active_view = ActiveView::Thread;
self.thread = cx.new_view(|cx| {
ActiveThread::new(
thread.clone(),
self.workspace.clone(),
self.language_registry.clone(),
self.tools.clone(),
cx,
)
});
self.message_editor =
cx.new_view(|cx| MessageEditor::new(self.workspace.clone(), thread, cx));
self.message_editor.focus_handle(cx).focus(cx);
}
pub(crate) fn open_thread(&mut self, thread_id: &ThreadId, cx: &mut ViewContext<Self>) {
let Some(thread) = self
.thread_store
.update(cx, |this, cx| this.open_thread(thread_id, cx))
else {
return;
};
self.active_view = ActiveView::Thread;
self.thread = cx.new_view(|cx| {
ActiveThread::new(
thread.clone(),
self.workspace.clone(),
self.language_registry.clone(),
self.tools.clone(),
cx,
)
});
self.message_editor =
cx.new_view(|cx| MessageEditor::new(self.workspace.clone(), thread, cx));
self.message_editor.focus_handle(cx).focus(cx);
}
pub(crate) fn delete_thread(&mut self, thread_id: &ThreadId, cx: &mut ViewContext<Self>) {
self.thread_store
.update(cx, |this, cx| this.delete_thread(thread_id, cx));
}
}
impl FocusableView for AssistantPanel {
fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
match self.active_view {
ActiveView::Thread => self.message_editor.focus_handle(cx),
ActiveView::History => self.history.focus_handle(cx),
}
}
}
impl EventEmitter<PanelEvent> for AssistantPanel {}
impl Panel for AssistantPanel {
fn persistent_name() -> &'static str {
"AssistantPanel2"
}
fn position(&self, _cx: &WindowContext) -> DockPosition {
DockPosition::Right
}
fn position_is_valid(&self, _: DockPosition) -> bool {
true
}
fn set_position(&mut self, _position: DockPosition, _cx: &mut ViewContext<Self>) {}
fn size(&self, _cx: &WindowContext) -> Pixels {
px(640.)
}
fn set_size(&mut self, _size: Option<Pixels>, _cx: &mut ViewContext<Self>) {}
fn set_active(&mut self, _active: bool, _cx: &mut ViewContext<Self>) {}
fn remote_id() -> Option<proto::PanelId> {
Some(proto::PanelId::AssistantPanel)
}
fn icon(&self, _cx: &WindowContext) -> Option<IconName> {
Some(IconName::ZedAssistant)
}
fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> {
Some("Assistant Panel")
}
fn toggle_action(&self) -> Box<dyn Action> {
Box::new(ToggleFocus)
}
}
impl AssistantPanel {
fn render_toolbar(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let focus_handle = self.focus_handle(cx);
h_flex()
.id("assistant-toolbar")
.justify_between()
.gap(DynamicSpacing::Base08.rems(cx))
.h(Tab::container_height(cx))
.px(DynamicSpacing::Base08.rems(cx))
.bg(cx.theme().colors().tab_bar_background)
.border_b_1()
.border_color(cx.theme().colors().border_variant)
.child(h_flex().children(self.thread.read(cx).summary(cx).map(Label::new)))
.child(
h_flex()
.gap(DynamicSpacing::Base08.rems(cx))
.child(Divider::vertical())
.child(
IconButton::new("new-thread", IconName::Plus)
.shape(IconButtonShape::Square)
.icon_size(IconSize::Small)
.style(ButtonStyle::Subtle)
.tooltip({
let focus_handle = focus_handle.clone();
move |cx| {
Tooltip::for_action_in(
"New Thread",
&NewThread,
&focus_handle,
cx,
)
}
})
.on_click(move |_event, cx| {
cx.dispatch_action(NewThread.boxed_clone());
}),
)
.child(
IconButton::new("open-history", IconName::HistoryRerun)
.shape(IconButtonShape::Square)
.icon_size(IconSize::Small)
.style(ButtonStyle::Subtle)
.tooltip({
let focus_handle = focus_handle.clone();
move |cx| {
Tooltip::for_action_in(
"Open History",
&OpenHistory,
&focus_handle,
cx,
)
}
})
.on_click(move |_event, cx| {
cx.dispatch_action(OpenHistory.boxed_clone());
}),
)
.child(
IconButton::new("configure-assistant", IconName::Settings)
.shape(IconButtonShape::Square)
.icon_size(IconSize::Small)
.style(ButtonStyle::Subtle)
.tooltip(move |cx| Tooltip::text("Configure Assistant", cx))
.on_click(move |_event, _cx| {
println!("Configure Assistant");
}),
),
)
}
fn render_active_thread_or_empty_state(&self, cx: &mut ViewContext<Self>) -> AnyElement {
if self.thread.read(cx).is_empty() {
return self.render_thread_empty_state(cx).into_any_element();
}
self.thread.clone().into_any()
}
fn render_thread_empty_state(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let recent_threads = self
.thread_store
.update(cx, |this, cx| this.recent_threads(3, cx));
v_flex()
.gap_2()
.mx_auto()
.child(
v_flex().w_full().child(
svg()
.path("icons/logo_96.svg")
.text_color(cx.theme().colors().text)
.w(px(40.))
.h(px(40.))
.mx_auto()
.mb_4(),
),
)
.when(!recent_threads.is_empty(), |parent| {
parent
.child(
h_flex()
.w_full()
.justify_center()
.child(Label::new("Recent Threads:").size(LabelSize::Small)),
)
.child(
v_flex().gap_2().children(
recent_threads
.into_iter()
.map(|thread| PastThread::new(thread, cx.view().downgrade())),
),
)
.child(
h_flex().w_full().justify_center().child(
Button::new("view-all-past-threads", "View All Past Threads")
.style(ButtonStyle::Subtle)
.label_size(LabelSize::Small)
.key_binding(KeyBinding::for_action_in(
&OpenHistory,
&self.focus_handle(cx),
cx,
))
.on_click(move |_event, cx| {
cx.dispatch_action(OpenHistory.boxed_clone());
}),
),
)
})
}
fn render_last_error(&self, cx: &mut ViewContext<Self>) -> Option<AnyElement> {
let last_error = self.thread.read(cx).last_error()?;
Some(
div()
.absolute()
.right_3()
.bottom_12()
.max_w_96()
.py_2()
.px_3()
.elevation_2(cx)
.occlude()
.child(match last_error {
ThreadError::PaymentRequired => self.render_payment_required_error(cx),
ThreadError::MaxMonthlySpendReached => {
self.render_max_monthly_spend_reached_error(cx)
}
ThreadError::Message(error_message) => {
self.render_error_message(&error_message, cx)
}
})
.into_any(),
)
}
fn render_payment_required_error(&self, cx: &mut ViewContext<Self>) -> AnyElement {
const ERROR_MESSAGE: &str = "Free tier exceeded. Subscribe and add payment to continue using Zed LLMs. You'll be billed at cost for tokens used.";
v_flex()
.gap_0p5()
.child(
h_flex()
.gap_1p5()
.items_center()
.child(Icon::new(IconName::XCircle).color(Color::Error))
.child(Label::new("Free Usage Exceeded").weight(FontWeight::MEDIUM)),
)
.child(
div()
.id("error-message")
.max_h_24()
.overflow_y_scroll()
.child(Label::new(ERROR_MESSAGE)),
)
.child(
h_flex()
.justify_end()
.mt_1()
.child(Button::new("subscribe", "Subscribe").on_click(cx.listener(
|this, _, cx| {
this.thread.update(cx, |this, _cx| {
this.clear_last_error();
});
cx.open_url(&zed_urls::account_url(cx));
cx.notify();
},
)))
.child(Button::new("dismiss", "Dismiss").on_click(cx.listener(
|this, _, cx| {
this.thread.update(cx, |this, _cx| {
this.clear_last_error();
});
cx.notify();
},
))),
)
.into_any()
}
fn render_max_monthly_spend_reached_error(&self, cx: &mut ViewContext<Self>) -> AnyElement {
const ERROR_MESSAGE: &str = "You have reached your maximum monthly spend. Increase your spend limit to continue using Zed LLMs.";
v_flex()
.gap_0p5()
.child(
h_flex()
.gap_1p5()
.items_center()
.child(Icon::new(IconName::XCircle).color(Color::Error))
.child(Label::new("Max Monthly Spend Reached").weight(FontWeight::MEDIUM)),
)
.child(
div()
.id("error-message")
.max_h_24()
.overflow_y_scroll()
.child(Label::new(ERROR_MESSAGE)),
)
.child(
h_flex()
.justify_end()
.mt_1()
.child(
Button::new("subscribe", "Update Monthly Spend Limit").on_click(
cx.listener(|this, _, cx| {
this.thread.update(cx, |this, _cx| {
this.clear_last_error();
});
cx.open_url(&zed_urls::account_url(cx));
cx.notify();
}),
),
)
.child(Button::new("dismiss", "Dismiss").on_click(cx.listener(
|this, _, cx| {
this.thread.update(cx, |this, _cx| {
this.clear_last_error();
});
cx.notify();
},
))),
)
.into_any()
}
fn render_error_message(
&self,
error_message: &SharedString,
cx: &mut ViewContext<Self>,
) -> AnyElement {
v_flex()
.gap_0p5()
.child(
h_flex()
.gap_1p5()
.items_center()
.child(Icon::new(IconName::XCircle).color(Color::Error))
.child(
Label::new("Error interacting with language model")
.weight(FontWeight::MEDIUM),
),
)
.child(
div()
.id("error-message")
.max_h_32()
.overflow_y_scroll()
.child(Label::new(error_message.clone())),
)
.child(
h_flex()
.justify_end()
.mt_1()
.child(Button::new("dismiss", "Dismiss").on_click(cx.listener(
|this, _, cx| {
this.thread.update(cx, |this, _cx| {
this.clear_last_error();
});
cx.notify();
},
))),
)
.into_any()
}
}
impl Render for AssistantPanel {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
v_flex()
.key_context("AssistantPanel2")
.justify_between()
.size_full()
.on_action(cx.listener(|this, _: &NewThread, cx| {
this.new_thread(cx);
}))
.on_action(cx.listener(|this, _: &OpenHistory, cx| {
this.active_view = ActiveView::History;
this.history.focus_handle(cx).focus(cx);
cx.notify();
}))
.child(self.render_toolbar(cx))
.map(|parent| match self.active_view {
ActiveView::Thread => parent
.child(self.render_active_thread_or_empty_state(cx))
.child(
h_flex()
.border_t_1()
.border_color(cx.theme().colors().border_variant)
.child(self.message_editor.clone()),
)
.children(self.render_last_error(cx)),
ActiveView::History => parent.child(self.history.clone()),
})
}
}

View File

@@ -1,485 +0,0 @@
use std::sync::Arc;
use ::open_ai::Model as OpenAiModel;
use anthropic::Model as AnthropicModel;
use gpui::Pixels;
use language_model::{CloudModel, LanguageModel};
use ollama::Model as OllamaModel;
use schemars::{schema::Schema, JsonSchema};
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsSources};
#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum AssistantDockPosition {
Left,
#[default]
Right,
Bottom,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
#[serde(tag = "name", rename_all = "snake_case")]
pub enum AssistantProviderContentV1 {
#[serde(rename = "zed.dev")]
ZedDotDev { default_model: Option<CloudModel> },
#[serde(rename = "openai")]
OpenAi {
default_model: Option<OpenAiModel>,
api_url: Option<String>,
available_models: Option<Vec<OpenAiModel>>,
},
#[serde(rename = "anthropic")]
Anthropic {
default_model: Option<AnthropicModel>,
api_url: Option<String>,
},
#[serde(rename = "ollama")]
Ollama {
default_model: Option<OllamaModel>,
api_url: Option<String>,
},
}
#[derive(Debug, Default)]
pub struct AssistantSettings {
pub enabled: bool,
pub button: bool,
pub dock: AssistantDockPosition,
pub default_width: Pixels,
pub default_height: Pixels,
pub default_model: LanguageModelSelection,
pub inline_alternatives: Vec<LanguageModelSelection>,
pub using_outdated_settings_version: bool,
pub enable_experimental_live_diffs: bool,
}
/// Assistant panel settings
#[derive(Clone, Serialize, Deserialize, Debug)]
#[serde(untagged)]
pub enum AssistantSettingsContent {
Versioned(VersionedAssistantSettingsContent),
Legacy(LegacyAssistantSettingsContent),
}
impl JsonSchema for AssistantSettingsContent {
fn schema_name() -> String {
VersionedAssistantSettingsContent::schema_name()
}
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> Schema {
VersionedAssistantSettingsContent::json_schema(gen)
}
fn is_referenceable() -> bool {
VersionedAssistantSettingsContent::is_referenceable()
}
}
impl Default for AssistantSettingsContent {
fn default() -> Self {
Self::Versioned(VersionedAssistantSettingsContent::default())
}
}
impl AssistantSettingsContent {
pub fn is_version_outdated(&self) -> bool {
match self {
AssistantSettingsContent::Versioned(settings) => match settings {
VersionedAssistantSettingsContent::V1(_) => true,
VersionedAssistantSettingsContent::V2(_) => false,
},
AssistantSettingsContent::Legacy(_) => true,
}
}
fn upgrade(&self) -> AssistantSettingsContentV2 {
match self {
AssistantSettingsContent::Versioned(settings) => match settings {
VersionedAssistantSettingsContent::V1(settings) => AssistantSettingsContentV2 {
enabled: settings.enabled,
button: settings.button,
dock: settings.dock,
default_width: settings.default_width,
default_height: settings.default_width,
default_model: settings
.provider
.clone()
.and_then(|provider| match provider {
AssistantProviderContentV1::ZedDotDev { default_model } => {
default_model.map(|model| LanguageModelSelection {
provider: "zed.dev".to_string(),
model: model.id().to_string(),
})
}
AssistantProviderContentV1::OpenAi { default_model, .. } => {
default_model.map(|model| LanguageModelSelection {
provider: "openai".to_string(),
model: model.id().to_string(),
})
}
AssistantProviderContentV1::Anthropic { default_model, .. } => {
default_model.map(|model| LanguageModelSelection {
provider: "anthropic".to_string(),
model: model.id().to_string(),
})
}
AssistantProviderContentV1::Ollama { default_model, .. } => {
default_model.map(|model| LanguageModelSelection {
provider: "ollama".to_string(),
model: model.id().to_string(),
})
}
}),
inline_alternatives: None,
enable_experimental_live_diffs: None,
},
VersionedAssistantSettingsContent::V2(settings) => settings.clone(),
},
AssistantSettingsContent::Legacy(settings) => AssistantSettingsContentV2 {
enabled: None,
button: settings.button,
dock: settings.dock,
default_width: settings.default_width,
default_height: settings.default_height,
default_model: Some(LanguageModelSelection {
provider: "openai".to_string(),
model: settings
.default_open_ai_model
.clone()
.unwrap_or_default()
.id()
.to_string(),
}),
inline_alternatives: None,
enable_experimental_live_diffs: None,
},
}
}
pub fn set_model(&mut self, language_model: Arc<dyn LanguageModel>) {
let model = language_model.id().0.to_string();
let provider = language_model.provider_id().0.to_string();
match self {
AssistantSettingsContent::Versioned(settings) => match settings {
VersionedAssistantSettingsContent::V1(settings) => match provider.as_ref() {
"zed.dev" => {
log::warn!("attempted to set zed.dev model on outdated settings");
}
"anthropic" => {
let api_url = match &settings.provider {
Some(AssistantProviderContentV1::Anthropic { api_url, .. }) => {
api_url.clone()
}
_ => None,
};
settings.provider = Some(AssistantProviderContentV1::Anthropic {
default_model: AnthropicModel::from_id(&model).ok(),
api_url,
});
}
"ollama" => {
let api_url = match &settings.provider {
Some(AssistantProviderContentV1::Ollama { api_url, .. }) => {
api_url.clone()
}
_ => None,
};
settings.provider = Some(AssistantProviderContentV1::Ollama {
default_model: Some(ollama::Model::new(&model, None, None)),
api_url,
});
}
"openai" => {
let (api_url, available_models) = match &settings.provider {
Some(AssistantProviderContentV1::OpenAi {
api_url,
available_models,
..
}) => (api_url.clone(), available_models.clone()),
_ => (None, None),
};
settings.provider = Some(AssistantProviderContentV1::OpenAi {
default_model: OpenAiModel::from_id(&model).ok(),
api_url,
available_models,
});
}
_ => {}
},
VersionedAssistantSettingsContent::V2(settings) => {
settings.default_model = Some(LanguageModelSelection { provider, model });
}
},
AssistantSettingsContent::Legacy(settings) => {
if let Ok(model) = OpenAiModel::from_id(&language_model.id().0) {
settings.default_open_ai_model = Some(model);
}
}
}
}
}
#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)]
#[serde(tag = "version")]
pub enum VersionedAssistantSettingsContent {
#[serde(rename = "1")]
V1(AssistantSettingsContentV1),
#[serde(rename = "2")]
V2(AssistantSettingsContentV2),
}
impl Default for VersionedAssistantSettingsContent {
fn default() -> Self {
Self::V2(AssistantSettingsContentV2 {
enabled: None,
button: None,
dock: None,
default_width: None,
default_height: None,
default_model: None,
inline_alternatives: None,
enable_experimental_live_diffs: None,
})
}
}
#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)]
pub struct AssistantSettingsContentV2 {
/// Whether the Assistant is enabled.
///
/// Default: true
enabled: Option<bool>,
/// Whether to show the assistant panel button in the status bar.
///
/// Default: true
button: Option<bool>,
/// Where to dock the assistant.
///
/// Default: right
dock: Option<AssistantDockPosition>,
/// Default width in pixels when the assistant is docked to the left or right.
///
/// Default: 640
default_width: Option<f32>,
/// Default height in pixels when the assistant is docked to the bottom.
///
/// Default: 320
default_height: Option<f32>,
/// The default model to use when creating new chats.
default_model: Option<LanguageModelSelection>,
/// Additional models with which to generate alternatives when performing inline assists.
inline_alternatives: Option<Vec<LanguageModelSelection>>,
/// Enable experimental live diffs in the assistant panel.
///
/// Default: false
enable_experimental_live_diffs: Option<bool>,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
pub struct LanguageModelSelection {
#[schemars(schema_with = "providers_schema")]
pub provider: String,
pub model: String,
}
fn providers_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
schemars::schema::SchemaObject {
enum_values: Some(vec![
"anthropic".into(),
"google".into(),
"ollama".into(),
"openai".into(),
"zed.dev".into(),
"copilot_chat".into(),
]),
..Default::default()
}
.into()
}
impl Default for LanguageModelSelection {
fn default() -> Self {
Self {
provider: "openai".to_string(),
model: "gpt-4".to_string(),
}
}
}
#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)]
pub struct AssistantSettingsContentV1 {
/// Whether the Assistant is enabled.
///
/// Default: true
enabled: Option<bool>,
/// Whether to show the assistant panel button in the status bar.
///
/// Default: true
button: Option<bool>,
/// Where to dock the assistant.
///
/// Default: right
dock: Option<AssistantDockPosition>,
/// Default width in pixels when the assistant is docked to the left or right.
///
/// Default: 640
default_width: Option<f32>,
/// Default height in pixels when the assistant is docked to the bottom.
///
/// Default: 320
default_height: Option<f32>,
/// The provider of the assistant service.
///
/// This can be "openai", "anthropic", "ollama", "zed.dev"
/// each with their respective default models and configurations.
provider: Option<AssistantProviderContentV1>,
}
#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)]
pub struct LegacyAssistantSettingsContent {
/// Whether to show the assistant panel button in the status bar.
///
/// Default: true
pub button: Option<bool>,
/// Where to dock the assistant.
///
/// Default: right
pub dock: Option<AssistantDockPosition>,
/// Default width in pixels when the assistant is docked to the left or right.
///
/// Default: 640
pub default_width: Option<f32>,
/// Default height in pixels when the assistant is docked to the bottom.
///
/// Default: 320
pub default_height: Option<f32>,
/// The default OpenAI model to use when creating new chats.
///
/// Default: gpt-4-1106-preview
pub default_open_ai_model: Option<OpenAiModel>,
/// OpenAI API base URL to use when creating new chats.
///
/// Default: https://api.openai.com/v1
pub openai_api_url: Option<String>,
}
impl Settings for AssistantSettings {
const KEY: Option<&'static str> = Some("assistant");
const PRESERVED_KEYS: Option<&'static [&'static str]> = Some(&["version"]);
type FileContent = AssistantSettingsContent;
fn load(
sources: SettingsSources<Self::FileContent>,
_: &mut gpui::AppContext,
) -> anyhow::Result<Self> {
let mut settings = AssistantSettings::default();
for value in sources.defaults_and_customizations() {
if value.is_version_outdated() {
settings.using_outdated_settings_version = true;
}
let value = value.upgrade();
merge(&mut settings.enabled, value.enabled);
merge(&mut settings.button, value.button);
merge(&mut settings.dock, value.dock);
merge(
&mut settings.default_width,
value.default_width.map(Into::into),
);
merge(
&mut settings.default_height,
value.default_height.map(Into::into),
);
merge(&mut settings.default_model, value.default_model);
merge(&mut settings.inline_alternatives, value.inline_alternatives);
merge(
&mut settings.enable_experimental_live_diffs,
value.enable_experimental_live_diffs,
);
}
Ok(settings)
}
}
fn merge<T>(target: &mut T, value: Option<T>) {
if let Some(value) = value {
*target = value;
}
}
#[cfg(test)]
mod tests {
use fs::Fs;
use gpui::{ReadGlobal, TestAppContext};
use super::*;
#[gpui::test]
async fn test_deserialize_assistant_settings_with_version(cx: &mut TestAppContext) {
let fs = fs::FakeFs::new(cx.executor().clone());
fs.create_dir(paths::settings_file().parent().unwrap())
.await
.unwrap();
cx.update(|cx| {
let test_settings = settings::SettingsStore::test(cx);
cx.set_global(test_settings);
AssistantSettings::register(cx);
});
cx.update(|cx| {
assert!(!AssistantSettings::get_global(cx).using_outdated_settings_version);
assert_eq!(
AssistantSettings::get_global(cx).default_model,
LanguageModelSelection {
provider: "zed.dev".into(),
model: "claude-3-5-sonnet".into(),
}
);
});
cx.update(|cx| {
settings::SettingsStore::global(cx).update_settings_file::<AssistantSettings>(
fs.clone(),
|settings, _| {
*settings = AssistantSettingsContent::Versioned(
VersionedAssistantSettingsContent::V2(AssistantSettingsContentV2 {
default_model: Some(LanguageModelSelection {
provider: "test-provider".into(),
model: "gpt-99".into(),
}),
inline_alternatives: None,
enabled: None,
button: None,
dock: None,
default_width: None,
default_height: None,
enable_experimental_live_diffs: None,
}),
)
},
);
});
cx.run_until_parked();
let raw_settings_value = fs.load(paths::settings_file()).await.unwrap();
assert!(raw_settings_value.contains(r#""version": "2""#));
#[derive(Debug, Deserialize)]
struct AssistantSettingsTest {
assistant: AssistantSettingsContent,
}
let assistant_settings: AssistantSettingsTest =
serde_json_lenient::from_str(&raw_settings_value).unwrap();
assert!(!assistant_settings.assistant.is_version_outdated());
}
}

View File

@@ -1,27 +0,0 @@
use gpui::SharedString;
use serde::{Deserialize, Serialize};
use util::post_inc;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Serialize, Deserialize)]
pub struct ContextId(pub(crate) usize);
impl ContextId {
pub fn post_inc(&mut self) -> Self {
Self(post_inc(&mut self.0))
}
}
/// Some context attached to a message in a thread.
#[derive(Debug, Clone)]
pub struct Context {
pub id: ContextId,
pub name: SharedString,
pub kind: ContextKind,
pub text: SharedString,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ContextKind {
File,
FetchedUrl,
}

View File

@@ -1,227 +0,0 @@
mod fetch_context_picker;
mod file_context_picker;
use std::sync::Arc;
use gpui::{
AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, SharedString, Task, View,
WeakView,
};
use picker::{Picker, PickerDelegate};
use ui::{prelude::*, ListItem, ListItemSpacing, Tooltip};
use util::ResultExt;
use workspace::Workspace;
use crate::context_picker::fetch_context_picker::FetchContextPicker;
use crate::context_picker::file_context_picker::FileContextPicker;
use crate::message_editor::MessageEditor;
#[derive(Debug, Clone)]
enum ContextPickerMode {
Default,
File(View<FileContextPicker>),
Fetch(View<FetchContextPicker>),
}
pub(super) struct ContextPicker {
mode: ContextPickerMode,
picker: View<Picker<ContextPickerDelegate>>,
}
impl ContextPicker {
pub fn new(
workspace: WeakView<Workspace>,
message_editor: WeakView<MessageEditor>,
cx: &mut ViewContext<Self>,
) -> Self {
let delegate = ContextPickerDelegate {
context_picker: cx.view().downgrade(),
workspace: workspace.clone(),
message_editor: message_editor.clone(),
entries: vec![
ContextPickerEntry {
name: "directory".into(),
description: "Insert any directory".into(),
icon: IconName::Folder,
},
ContextPickerEntry {
name: "file".into(),
description: "Insert any file".into(),
icon: IconName::File,
},
ContextPickerEntry {
name: "fetch".into(),
description: "Fetch content from URL".into(),
icon: IconName::Globe,
},
],
selected_ix: 0,
};
let picker = cx.new_view(|cx| {
Picker::nonsearchable_uniform_list(delegate, cx).max_height(Some(rems(20.).into()))
});
ContextPicker {
mode: ContextPickerMode::Default,
picker,
}
}
pub fn reset_mode(&mut self) {
self.mode = ContextPickerMode::Default;
}
}
impl EventEmitter<DismissEvent> for ContextPicker {}
impl FocusableView for ContextPicker {
fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
match &self.mode {
ContextPickerMode::Default => self.picker.focus_handle(cx),
ContextPickerMode::File(file_picker) => file_picker.focus_handle(cx),
ContextPickerMode::Fetch(fetch_picker) => fetch_picker.focus_handle(cx),
}
}
}
impl Render for ContextPicker {
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
v_flex()
.w(px(400.))
.min_w(px(400.))
.map(|parent| match &self.mode {
ContextPickerMode::Default => parent.child(self.picker.clone()),
ContextPickerMode::File(file_picker) => parent.child(file_picker.clone()),
ContextPickerMode::Fetch(fetch_picker) => parent.child(fetch_picker.clone()),
})
}
}
#[derive(Clone)]
struct ContextPickerEntry {
name: SharedString,
description: SharedString,
icon: IconName,
}
pub(crate) struct ContextPickerDelegate {
context_picker: WeakView<ContextPicker>,
workspace: WeakView<Workspace>,
message_editor: WeakView<MessageEditor>,
entries: Vec<ContextPickerEntry>,
selected_ix: usize,
}
impl PickerDelegate for ContextPickerDelegate {
type ListItem = ListItem;
fn match_count(&self) -> usize {
self.entries.len()
}
fn selected_index(&self) -> usize {
self.selected_ix
}
fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>) {
self.selected_ix = ix.min(self.entries.len().saturating_sub(1));
cx.notify();
}
fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
"Select a context source…".into()
}
fn update_matches(&mut self, _query: String, _cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
Task::ready(())
}
fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext<Picker<Self>>) {
if let Some(entry) = self.entries.get(self.selected_ix) {
self.context_picker
.update(cx, |this, cx| {
match entry.name.to_string().as_str() {
"file" => {
this.mode = ContextPickerMode::File(cx.new_view(|cx| {
FileContextPicker::new(
self.context_picker.clone(),
self.workspace.clone(),
self.message_editor.clone(),
cx,
)
}));
}
"fetch" => {
this.mode = ContextPickerMode::Fetch(cx.new_view(|cx| {
FetchContextPicker::new(
self.context_picker.clone(),
self.workspace.clone(),
self.message_editor.clone(),
cx,
)
}));
}
_ => {}
}
cx.focus_self();
})
.log_err();
}
}
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
self.context_picker
.update(cx, |this, cx| match this.mode {
ContextPickerMode::Default => cx.emit(DismissEvent),
ContextPickerMode::File(_) | ContextPickerMode::Fetch(_) => {}
})
.log_err();
}
fn render_match(
&self,
ix: usize,
selected: bool,
_cx: &mut ViewContext<Picker<Self>>,
) -> Option<Self::ListItem> {
let entry = &self.entries[ix];
Some(
ListItem::new(ix)
.inset(true)
.spacing(ListItemSpacing::Dense)
.toggle_state(selected)
.tooltip({
let description = entry.description.clone();
move |cx| cx.new_view(|_cx| Tooltip::new(description.clone())).into()
})
.child(
v_flex()
.group(format!("context-entry-label-{ix}"))
.w_full()
.py_0p5()
.min_w(px(250.))
.max_w(px(400.))
.child(
h_flex()
.gap_1p5()
.child(Icon::new(entry.icon).size(IconSize::XSmall))
.child(
Label::new(entry.name.clone())
.single_line()
.size(LabelSize::Small),
),
)
.child(
div().overflow_hidden().text_ellipsis().child(
Label::new(entry.description.clone())
.size(LabelSize::Small)
.color(Color::Muted),
),
),
),
)
}
}

View File

@@ -1,218 +0,0 @@
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;
use anyhow::{bail, Context as _, Result};
use futures::AsyncReadExt as _;
use gpui::{AppContext, DismissEvent, FocusHandle, FocusableView, Task, View, WeakView};
use html_to_markdown::{convert_html_to_markdown, markdown, TagHandler};
use http_client::{AsyncBody, HttpClientWithUrl};
use picker::{Picker, PickerDelegate};
use ui::{prelude::*, ListItem, ListItemSpacing, ViewContext};
use workspace::Workspace;
use crate::context::ContextKind;
use crate::context_picker::ContextPicker;
use crate::message_editor::MessageEditor;
pub struct FetchContextPicker {
picker: View<Picker<FetchContextPickerDelegate>>,
}
impl FetchContextPicker {
pub fn new(
context_picker: WeakView<ContextPicker>,
workspace: WeakView<Workspace>,
message_editor: WeakView<MessageEditor>,
cx: &mut ViewContext<Self>,
) -> Self {
let delegate = FetchContextPickerDelegate::new(context_picker, workspace, message_editor);
let picker = cx.new_view(|cx| Picker::uniform_list(delegate, cx));
Self { picker }
}
}
impl FocusableView for FetchContextPicker {
fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
self.picker.focus_handle(cx)
}
}
impl Render for FetchContextPicker {
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
self.picker.clone()
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
enum ContentType {
Html,
Plaintext,
Json,
}
pub struct FetchContextPickerDelegate {
context_picker: WeakView<ContextPicker>,
workspace: WeakView<Workspace>,
message_editor: WeakView<MessageEditor>,
url: String,
}
impl FetchContextPickerDelegate {
pub fn new(
context_picker: WeakView<ContextPicker>,
workspace: WeakView<Workspace>,
message_editor: WeakView<MessageEditor>,
) -> Self {
FetchContextPickerDelegate {
context_picker,
workspace,
message_editor,
url: String::new(),
}
}
async fn build_message(http_client: Arc<HttpClientWithUrl>, url: &str) -> Result<String> {
let mut url = url.to_owned();
if !url.starts_with("https://") && !url.starts_with("http://") {
url = format!("https://{url}");
}
let mut response = http_client.get(&url, AsyncBody::default(), true).await?;
let mut body = Vec::new();
response
.body_mut()
.read_to_end(&mut body)
.await
.context("error reading response body")?;
if response.status().is_client_error() {
let text = String::from_utf8_lossy(body.as_slice());
bail!(
"status error {}, response: {text:?}",
response.status().as_u16()
);
}
let Some(content_type) = response.headers().get("content-type") else {
bail!("missing Content-Type header");
};
let content_type = content_type
.to_str()
.context("invalid Content-Type header")?;
let content_type = match content_type {
"text/html" => ContentType::Html,
"text/plain" => ContentType::Plaintext,
"application/json" => ContentType::Json,
_ => ContentType::Html,
};
match content_type {
ContentType::Html => {
let mut handlers: Vec<TagHandler> = vec![
Rc::new(RefCell::new(markdown::WebpageChromeRemover)),
Rc::new(RefCell::new(markdown::ParagraphHandler)),
Rc::new(RefCell::new(markdown::HeadingHandler)),
Rc::new(RefCell::new(markdown::ListHandler)),
Rc::new(RefCell::new(markdown::TableHandler::new())),
Rc::new(RefCell::new(markdown::StyledTextHandler)),
];
if url.contains("wikipedia.org") {
use html_to_markdown::structure::wikipedia;
handlers.push(Rc::new(RefCell::new(wikipedia::WikipediaChromeRemover)));
handlers.push(Rc::new(RefCell::new(wikipedia::WikipediaInfoboxHandler)));
handlers.push(Rc::new(
RefCell::new(wikipedia::WikipediaCodeHandler::new()),
));
} else {
handlers.push(Rc::new(RefCell::new(markdown::CodeHandler)));
}
convert_html_to_markdown(&body[..], &mut handlers)
}
ContentType::Plaintext => Ok(std::str::from_utf8(&body)?.to_owned()),
ContentType::Json => {
let json: serde_json::Value = serde_json::from_slice(&body)?;
Ok(format!(
"```json\n{}\n```",
serde_json::to_string_pretty(&json)?
))
}
}
}
}
impl PickerDelegate for FetchContextPickerDelegate {
type ListItem = ListItem;
fn match_count(&self) -> usize {
1
}
fn selected_index(&self) -> usize {
0
}
fn set_selected_index(&mut self, _ix: usize, _cx: &mut ViewContext<Picker<Self>>) {}
fn placeholder_text(&self, _cx: &mut ui::WindowContext) -> Arc<str> {
"Enter a URL…".into()
}
fn update_matches(&mut self, query: String, _cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
self.url = query;
Task::ready(())
}
fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext<Picker<Self>>) {
let Some(workspace) = self.workspace.upgrade() else {
return;
};
let http_client = workspace.read(cx).client().http_client().clone();
let url = self.url.clone();
cx.spawn(|this, mut cx| async move {
let text = Self::build_message(http_client, &url).await?;
this.update(&mut cx, |this, cx| {
this.delegate
.message_editor
.update(cx, |message_editor, _cx| {
message_editor.insert_context(ContextKind::FetchedUrl, url, text);
})
})??;
anyhow::Ok(())
})
.detach_and_log_err(cx);
}
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
self.context_picker
.update(cx, |this, cx| {
this.reset_mode();
cx.emit(DismissEvent);
})
.ok();
}
fn render_match(
&self,
ix: usize,
selected: bool,
_cx: &mut ViewContext<Picker<Self>>,
) -> Option<Self::ListItem> {
Some(
ListItem::new(ix)
.inset(true)
.spacing(ListItemSpacing::Sparse)
.toggle_state(selected)
.child(self.url.clone()),
)
}
}

Some files were not shown because too many files have changed in this diff Show More