Compare commits
1 Commits
debug-blam
...
edit-syste
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e72b64547 |
@@ -1,15 +0,0 @@
|
||||
We have two cloudflare workers that let us serve some assets of this repo
|
||||
from Cloudflare.
|
||||
|
||||
* `open-source-website-assets` is used for `install.sh`
|
||||
* `docs-proxy` is used for `https://zed.dev/docs`
|
||||
|
||||
On push to `main`, both of these (and the files they depend on) are uploaded to Cloudflare.
|
||||
|
||||
### Deployment
|
||||
|
||||
These functions are deployed on push to main by the deploy_cloudflare.yml workflow. Worker Rules in Cloudflare intercept requests to zed.dev and proxy them to the appropriate workers.
|
||||
|
||||
### Testing
|
||||
|
||||
You can use [wrangler](https://developers.cloudflare.com/workers/cli-wrangler/install-update) to test these workers locally, or to deploy custom versions.
|
||||
@@ -1,14 +0,0 @@
|
||||
export default {
|
||||
async fetch(request, _env, _ctx) {
|
||||
const url = new URL(request.url);
|
||||
url.hostname = "docs-anw.pages.dev";
|
||||
|
||||
let res = await fetch(url, request);
|
||||
|
||||
if (res.status === 404) {
|
||||
res = await fetch("https://zed.dev/404");
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
};
|
||||
@@ -1,8 +0,0 @@
|
||||
name = "docs-proxy"
|
||||
main = "src/worker.js"
|
||||
compatibility_date = "2024-05-03"
|
||||
workers_dev = true
|
||||
|
||||
[[routes]]
|
||||
pattern = "zed.dev/docs*"
|
||||
zone_name = "zed.dev"
|
||||
@@ -1,19 +0,0 @@
|
||||
export default {
|
||||
async fetch(request, env) {
|
||||
const url = new URL(request.url);
|
||||
const key = url.pathname.slice(1);
|
||||
|
||||
const object = await env.OPEN_SOURCE_WEBSITE_ASSETS_BUCKET.get(key);
|
||||
if (!object) {
|
||||
return await fetch("https://zed.dev/404");
|
||||
}
|
||||
|
||||
const headers = new Headers();
|
||||
object.writeHttpMetadata(headers);
|
||||
headers.set("etag", object.httpEtag);
|
||||
|
||||
return new Response(object.body, {
|
||||
headers,
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -1,8 +0,0 @@
|
||||
name = "open-source-website-assets"
|
||||
main = "src/worker.js"
|
||||
compatibility_date = "2024-05-15"
|
||||
workers_dev = true
|
||||
|
||||
[[r2_buckets]]
|
||||
binding = 'OPEN_SOURCE_WEBSITE_ASSETS_BUCKET'
|
||||
bucket_name = 'zed-open-source-website-assets'
|
||||
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@@ -32,6 +32,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
clean: false
|
||||
submodules: "recursive"
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Remove untracked files
|
||||
@@ -86,6 +87,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
clean: false
|
||||
submodules: "recursive"
|
||||
|
||||
- name: cargo clippy
|
||||
run: cargo xtask clippy
|
||||
@@ -113,6 +115,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
clean: false
|
||||
submodules: "recursive"
|
||||
|
||||
- name: cargo clippy
|
||||
run: cargo xtask clippy
|
||||
@@ -129,6 +132,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
clean: false
|
||||
submodules: "recursive"
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: swatinem/rust-cache@v2
|
||||
@@ -171,6 +175,7 @@ jobs:
|
||||
# 25 was chosen arbitrarily.
|
||||
fetch-depth: 25
|
||||
clean: false
|
||||
submodules: "recursive"
|
||||
|
||||
- name: Limit target directory size
|
||||
run: script/clear-target-dir-if-larger-than 100
|
||||
@@ -268,6 +273,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
clean: false
|
||||
submodules: "recursive"
|
||||
|
||||
- name: Limit target directory size
|
||||
run: script/clear-target-dir-if-larger-than 100
|
||||
|
||||
56
.github/workflows/deploy_cloudflare.yml
vendored
56
.github/workflows/deploy_cloudflare.yml
vendored
@@ -1,56 +0,0 @@
|
||||
name: Deploy Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
deploy-docs:
|
||||
name: Deploy Docs
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
clean: false
|
||||
|
||||
- name: Setup mdBook
|
||||
uses: peaceiris/actions-mdbook@v2
|
||||
with:
|
||||
mdbook-version: "0.4.37"
|
||||
|
||||
- name: Build book
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p target/deploy
|
||||
mdbook build ./docs --dest-dir=../target/deploy/docs/
|
||||
|
||||
- name: Deploy Docs
|
||||
uses: cloudflare/wrangler-action@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@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@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@v3
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: deploy .cloudflare/docs-proxy/src/worker.js
|
||||
3
.github/workflows/deploy_collab.yml
vendored
3
.github/workflows/deploy_collab.yml
vendored
@@ -21,6 +21,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
clean: false
|
||||
submodules: "recursive"
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Run style checks
|
||||
@@ -40,6 +41,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
clean: false
|
||||
submodules: "recursive"
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install cargo nextest
|
||||
@@ -74,6 +76,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
clean: false
|
||||
submodules: "recursive"
|
||||
|
||||
- name: Build docker image
|
||||
run: docker build . --build-arg GITHUB_SHA=$GITHUB_SHA --tag registry.digitalocean.com/zed/collab:$GITHUB_SHA
|
||||
|
||||
35
.github/workflows/deploy_docs.yml
vendored
Normal file
35
.github/workflows/deploy_docs.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
name: Deploy Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
deploy-docs:
|
||||
name: Deploy Docs
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
clean: false
|
||||
|
||||
- name: Setup mdBook
|
||||
uses: peaceiris/actions-mdbook@v2
|
||||
with:
|
||||
mdbook-version: "0.4.37"
|
||||
|
||||
- name: Build book
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p target/deploy
|
||||
mdbook build ./docs --dest-dir=../target/deploy/docs/
|
||||
|
||||
- name: Deploy
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: pages deploy target/deploy --project-name=docs
|
||||
1
.github/workflows/publish_extension_cli.yml
vendored
1
.github/workflows/publish_extension_cli.yml
vendored
@@ -19,6 +19,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
clean: false
|
||||
submodules: "recursive"
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: swatinem/rust-cache@v2
|
||||
|
||||
1
.github/workflows/randomized_tests.yml
vendored
1
.github/workflows/randomized_tests.yml
vendored
@@ -31,6 +31,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
clean: false
|
||||
submodules: "recursive"
|
||||
|
||||
- name: Run randomized tests
|
||||
run: script/randomized-test-ci
|
||||
|
||||
4
.github/workflows/release_nightly.yml
vendored
4
.github/workflows/release_nightly.yml
vendored
@@ -25,6 +25,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
clean: false
|
||||
submodules: "recursive"
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Run style checks
|
||||
@@ -44,6 +45,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
clean: false
|
||||
submodules: "recursive"
|
||||
|
||||
- name: Run tests
|
||||
uses: ./.github/actions/run_tests
|
||||
@@ -73,6 +75,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
clean: false
|
||||
submodules: "recursive"
|
||||
|
||||
- name: Set release channel to nightly
|
||||
run: |
|
||||
@@ -106,6 +109,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
clean: false
|
||||
submodules: "recursive"
|
||||
|
||||
- name: Add Rust to the PATH
|
||||
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -24,4 +24,3 @@ DerivedData/
|
||||
.venv
|
||||
.blob_store
|
||||
.vscode
|
||||
.wrangler
|
||||
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "crates/live_kit_server/protocol"]
|
||||
path = crates/live_kit_server/protocol
|
||||
url = https://github.com/livekit/protocol
|
||||
326
Cargo.lock
generated
326
Cargo.lock
generated
@@ -224,12 +224,10 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures 0.3.28",
|
||||
"http 0.1.0",
|
||||
"isahc",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -240,9 +238,9 @@ checksum = "e78f17bacc1bc7b91fef7b1885c10772eb2b9e4e989356f6f0f6a972240f97cd"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.83"
|
||||
version = "1.0.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3"
|
||||
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||
|
||||
[[package]]
|
||||
name = "approx"
|
||||
@@ -334,9 +332,7 @@ dependencies = [
|
||||
name = "assistant"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anthropic",
|
||||
"anyhow",
|
||||
"cargo_toml",
|
||||
"chrono",
|
||||
"client",
|
||||
"collections",
|
||||
@@ -348,7 +344,6 @@ dependencies = [
|
||||
"fs",
|
||||
"futures 0.3.28",
|
||||
"gpui",
|
||||
"http 0.1.0",
|
||||
"indoc",
|
||||
"language",
|
||||
"log",
|
||||
@@ -369,7 +364,6 @@ dependencies = [
|
||||
"telemetry_events",
|
||||
"theme",
|
||||
"tiktoken-rs",
|
||||
"toml 0.8.10",
|
||||
"ui",
|
||||
"util",
|
||||
"uuid",
|
||||
@@ -389,16 +383,13 @@ dependencies = [
|
||||
"editor",
|
||||
"env_logger",
|
||||
"feature_flags",
|
||||
"file_icons",
|
||||
"fs",
|
||||
"futures 0.3.28",
|
||||
"fuzzy",
|
||||
"gpui",
|
||||
"http 0.1.0",
|
||||
"language",
|
||||
"languages",
|
||||
"log",
|
||||
"markdown",
|
||||
"node_runtime",
|
||||
"open_ai",
|
||||
"picker",
|
||||
@@ -406,6 +397,7 @@ dependencies = [
|
||||
"rand 0.8.5",
|
||||
"regex",
|
||||
"release_channel",
|
||||
"rich_text",
|
||||
"schemars",
|
||||
"semantic_index",
|
||||
"serde",
|
||||
@@ -414,7 +406,6 @@ dependencies = [
|
||||
"story",
|
||||
"theme",
|
||||
"ui",
|
||||
"unindent",
|
||||
"util",
|
||||
"workspace",
|
||||
]
|
||||
@@ -626,18 +617,6 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-native-tls"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9343dc5acf07e79ff82d0c37899f079db3534d99f189a1837c8e549c99405bec"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"native-tls",
|
||||
"thiserror",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-net"
|
||||
version = "1.7.0"
|
||||
@@ -832,7 +811,7 @@ version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5682ea0913e5c20780fe5785abacb85a411e7437bf52a1bedb93ddb3972cb8dd"
|
||||
dependencies = [
|
||||
"async-native-tls 0.3.3",
|
||||
"async-native-tls",
|
||||
"async-std",
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
@@ -908,7 +887,6 @@ dependencies = [
|
||||
"db",
|
||||
"editor",
|
||||
"gpui",
|
||||
"http 0.1.0",
|
||||
"isahc",
|
||||
"log",
|
||||
"markdown_preview",
|
||||
@@ -954,7 +932,7 @@ dependencies = [
|
||||
"hex",
|
||||
"http 0.2.9",
|
||||
"hyper",
|
||||
"ring",
|
||||
"ring 0.17.7",
|
||||
"time",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@@ -1114,7 +1092,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"p256",
|
||||
"percent-encoding",
|
||||
"ring",
|
||||
"ring 0.17.7",
|
||||
"sha2 0.10.7",
|
||||
"subtle",
|
||||
"time",
|
||||
@@ -1301,7 +1279,7 @@ checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
"base64 0.21.7",
|
||||
"base64 0.21.4",
|
||||
"bitflags 1.3.2",
|
||||
"bytes 1.5.0",
|
||||
"futures-util",
|
||||
@@ -1396,9 +1374,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.7"
|
||||
version = "0.21.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||
checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
@@ -1776,7 +1754,6 @@ dependencies = [
|
||||
"fs",
|
||||
"futures 0.3.28",
|
||||
"gpui",
|
||||
"http 0.1.0",
|
||||
"language",
|
||||
"live_kit_client",
|
||||
"log",
|
||||
@@ -1892,16 +1869,6 @@ dependencies = [
|
||||
"winx",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo_toml"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8cb1d556b8b8f36e5ca74938008be3ac102f5dcb5b68a0477e4249ae2291cd3"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"toml 0.8.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.3.0"
|
||||
@@ -1989,7 +1956,6 @@ dependencies = [
|
||||
"collections",
|
||||
"futures 0.3.28",
|
||||
"gpui",
|
||||
"http 0.1.0",
|
||||
"language",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
@@ -2212,7 +2178,6 @@ name = "client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-native-tls 0.5.0",
|
||||
"async-recursion 0.3.2",
|
||||
"async-tungstenite",
|
||||
"chrono",
|
||||
@@ -2221,7 +2186,6 @@ dependencies = [
|
||||
"feature_flags",
|
||||
"futures 0.3.28",
|
||||
"gpui",
|
||||
"http 0.1.0",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"once_cell",
|
||||
@@ -2352,7 +2316,6 @@ dependencies = [
|
||||
"gpui",
|
||||
"headless",
|
||||
"hex",
|
||||
"http 0.1.0",
|
||||
"indoc",
|
||||
"language",
|
||||
"live_kit_client",
|
||||
@@ -2360,7 +2323,6 @@ dependencies = [
|
||||
"log",
|
||||
"lsp",
|
||||
"menu",
|
||||
"multi_buffer",
|
||||
"nanoid",
|
||||
"node_runtime",
|
||||
"notifications",
|
||||
@@ -2422,7 +2384,6 @@ dependencies = [
|
||||
"futures 0.3.28",
|
||||
"fuzzy",
|
||||
"gpui",
|
||||
"http 0.1.0",
|
||||
"language",
|
||||
"lazy_static",
|
||||
"menu",
|
||||
@@ -2592,7 +2553,6 @@ dependencies = [
|
||||
"fs",
|
||||
"futures 0.3.28",
|
||||
"gpui",
|
||||
"http 0.1.0",
|
||||
"indoc",
|
||||
"language",
|
||||
"lsp",
|
||||
@@ -3451,7 +3411,6 @@ dependencies = [
|
||||
"fuzzy",
|
||||
"git",
|
||||
"gpui",
|
||||
"http 0.1.0",
|
||||
"indoc",
|
||||
"itertools 0.11.0",
|
||||
"language",
|
||||
@@ -3755,7 +3714,6 @@ dependencies = [
|
||||
"fs",
|
||||
"futures 0.3.28",
|
||||
"gpui",
|
||||
"http 0.1.0",
|
||||
"isahc",
|
||||
"language",
|
||||
"log",
|
||||
@@ -3836,9 +3794,9 @@ checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
|
||||
|
||||
[[package]]
|
||||
name = "fancy-regex"
|
||||
version = "0.12.0"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7493d4c459da9f84325ad297371a6b2b8a162800873a22e3b6b6512e61d18c05"
|
||||
checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"regex",
|
||||
@@ -3903,7 +3861,6 @@ dependencies = [
|
||||
"editor",
|
||||
"futures 0.3.28",
|
||||
"gpui",
|
||||
"http 0.1.0",
|
||||
"human_bytes",
|
||||
"isahc",
|
||||
"language",
|
||||
@@ -4207,7 +4164,7 @@ dependencies = [
|
||||
"text",
|
||||
"time",
|
||||
"util",
|
||||
"windows 0.56.0",
|
||||
"windows 0.53.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4512,7 +4469,6 @@ dependencies = [
|
||||
"derive_more",
|
||||
"git2",
|
||||
"gpui",
|
||||
"http 0.1.0",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"parking_lot",
|
||||
@@ -4527,7 +4483,7 @@ dependencies = [
|
||||
"unindent",
|
||||
"url",
|
||||
"util",
|
||||
"windows 0.56.0",
|
||||
"windows 0.53.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4552,7 +4508,6 @@ dependencies = [
|
||||
"futures 0.3.28",
|
||||
"git",
|
||||
"gpui",
|
||||
"http 0.1.0",
|
||||
"isahc",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
@@ -4560,6 +4515,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"unindent",
|
||||
"url",
|
||||
"util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4637,9 +4593,9 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures 0.3.28",
|
||||
"http 0.1.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4707,7 +4663,6 @@ dependencies = [
|
||||
"foreign-types 0.5.0",
|
||||
"futures 0.3.28",
|
||||
"gpui_macros",
|
||||
"http 0.1.0",
|
||||
"image",
|
||||
"itertools 0.11.0",
|
||||
"lazy_static",
|
||||
@@ -4750,8 +4705,7 @@ dependencies = [
|
||||
"wayland-cursor",
|
||||
"wayland-protocols",
|
||||
"wayland-protocols-plasma",
|
||||
"windows 0.56.0",
|
||||
"windows-core 0.56.0",
|
||||
"windows 0.53.0",
|
||||
"x11rb",
|
||||
"xkbcommon",
|
||||
]
|
||||
@@ -4767,9 +4721,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "grid"
|
||||
version = "0.13.0"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d196ffc1627db18a531359249b2bf8416178d84b729f3cebeb278f285fb9b58c"
|
||||
checksum = "1df00eed8d1f0db937f6be10e46e8072b0671accb504cf0f959c5c52c679f5b9"
|
||||
|
||||
[[package]]
|
||||
name = "group"
|
||||
@@ -4854,7 +4808,7 @@ version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"base64 0.21.4",
|
||||
"bytes 1.5.0",
|
||||
"headers-core",
|
||||
"http 0.2.9",
|
||||
@@ -5027,20 +4981,6 @@ version = "3.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d13cdbd5dbb29f9c88095bbdc2590c9cba0d0a1269b983fef6b2cdd7e9f4db1"
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures 0.3.28",
|
||||
"futures-lite 1.13.0",
|
||||
"isahc",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.9"
|
||||
@@ -5658,7 +5598,6 @@ dependencies = [
|
||||
"git",
|
||||
"globset",
|
||||
"gpui",
|
||||
"http 0.1.0",
|
||||
"indoc",
|
||||
"itertools 0.11.0",
|
||||
"lazy_static",
|
||||
@@ -5749,7 +5688,6 @@ dependencies = [
|
||||
"feature_flags",
|
||||
"futures 0.3.28",
|
||||
"gpui",
|
||||
"http 0.1.0",
|
||||
"language",
|
||||
"lazy_static",
|
||||
"log",
|
||||
@@ -5772,6 +5710,7 @@ dependencies = [
|
||||
"tree-sitter-c",
|
||||
"tree-sitter-cpp",
|
||||
"tree-sitter-css",
|
||||
"tree-sitter-embedded-template",
|
||||
"tree-sitter-go",
|
||||
"tree-sitter-gomod",
|
||||
"tree-sitter-gowork",
|
||||
@@ -5781,6 +5720,7 @@ dependencies = [
|
||||
"tree-sitter-proto",
|
||||
"tree-sitter-python",
|
||||
"tree-sitter-regex",
|
||||
"tree-sitter-ruby",
|
||||
"tree-sitter-rust",
|
||||
"tree-sitter-typescript",
|
||||
"tree-sitter-yaml",
|
||||
@@ -6033,7 +5973,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"smol",
|
||||
"util",
|
||||
"windows 0.56.0",
|
||||
"windows 0.53.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6342,7 +6282,6 @@ dependencies = [
|
||||
"log",
|
||||
"parking_lot",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"settings",
|
||||
"smallvec",
|
||||
"sum_tree",
|
||||
@@ -6494,7 +6433,6 @@ dependencies = [
|
||||
"async-trait",
|
||||
"async_zip",
|
||||
"futures 0.3.28",
|
||||
"http 0.1.0",
|
||||
"log",
|
||||
"semver",
|
||||
"serde",
|
||||
@@ -6503,7 +6441,7 @@ dependencies = [
|
||||
"tempfile",
|
||||
"util",
|
||||
"walkdir",
|
||||
"windows 0.56.0",
|
||||
"windows 0.53.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6889,11 +6827,10 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures 0.3.28",
|
||||
"http 0.1.0",
|
||||
"isahc",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6928,15 +6865,6 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "300.2.3+3.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cff92b6f71555b61bb9315f7c64da3ca43d87531622120fea0195fc761b4843"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.93"
|
||||
@@ -6945,7 +6873,6 @@ checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"openssl-src",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
@@ -7384,7 +7311,7 @@ version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a4a0cfc5fb21a09dc6af4bf834cf10d4a32fccd9e2ea468c4b1751a097487aa"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"base64 0.21.4",
|
||||
"indexmap 1.9.3",
|
||||
"line-wrap",
|
||||
"quick-xml 0.30.0",
|
||||
@@ -7640,7 +7567,6 @@ dependencies = [
|
||||
"git2",
|
||||
"globset",
|
||||
"gpui",
|
||||
"http 0.1.0",
|
||||
"itertools 0.11.0",
|
||||
"language",
|
||||
"log",
|
||||
@@ -8184,7 +8110,7 @@ version = "0.11.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"base64 0.21.4",
|
||||
"bytes 1.5.0",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
@@ -8263,6 +8189,21 @@ dependencies = [
|
||||
"util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"spin 0.5.2",
|
||||
"untrusted 0.7.1",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.7"
|
||||
@@ -8273,7 +8214,7 @@ dependencies = [
|
||||
"getrandom 0.2.10",
|
||||
"libc",
|
||||
"spin 0.9.8",
|
||||
"untrusted",
|
||||
"untrusted 0.9.0",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
@@ -8426,9 +8367,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed"
|
||||
version = "8.4.0"
|
||||
version = "8.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19549741604902eb99a7ed0ee177a0663ee1eda51a29f71401f166e47e77806a"
|
||||
checksum = "a82c0bbc10308ed323529fd3c1dce8badda635aa319a5ff0e6466f33b8101e3f"
|
||||
dependencies = [
|
||||
"rust-embed-impl",
|
||||
"rust-embed-utils",
|
||||
@@ -8437,9 +8378,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-impl"
|
||||
version = "8.4.0"
|
||||
version = "8.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb9f96e283ec64401f30d3df8ee2aaeb2561f34c824381efa24a35f79bf40ee4"
|
||||
checksum = "6227c01b1783cdfee1bcf844eb44594cd16ec71c35305bf1c9fb5aade2735e16"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -8450,9 +8391,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-utils"
|
||||
version = "8.4.0"
|
||||
version = "8.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38c74a686185620830701348de757fd36bef4aa9680fd23c49fc539ddcc1af32"
|
||||
checksum = "8cb0a25bfbb2d4b4402179c2cf030387d9990857ce08a32592c6238db9fa8665"
|
||||
dependencies = [
|
||||
"globset",
|
||||
"sha2 0.10.7",
|
||||
@@ -8538,12 +8479,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.21.12"
|
||||
version = "0.21.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
|
||||
checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"ring 0.17.7",
|
||||
"rustls-webpki",
|
||||
"sct",
|
||||
]
|
||||
@@ -8566,7 +8507,7 @@ version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"base64 0.21.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8575,8 +8516,8 @@ version = "0.101.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
"ring 0.17.7",
|
||||
"untrusted 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8700,12 +8641,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
version = "0.7.1"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
|
||||
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
"ring 0.16.20",
|
||||
"untrusted 0.7.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8897,7 +8838,6 @@ dependencies = [
|
||||
"futures-batch",
|
||||
"gpui",
|
||||
"heed",
|
||||
"http 0.1.0",
|
||||
"language",
|
||||
"languages",
|
||||
"log",
|
||||
@@ -9577,7 +9517,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "864b869fdf56263f4c95c45483191ea0af340f9f3e3e7b4d57a61c7c87a970db"
|
||||
dependencies = [
|
||||
"atoi",
|
||||
"base64 0.21.7",
|
||||
"base64 0.21.4",
|
||||
"bigdecimal",
|
||||
"bitflags 2.4.2",
|
||||
"byteorder",
|
||||
@@ -9624,7 +9564,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb7ae0e6a97fb3ba33b23ac2671a5ce6e3cabe003f451abd5a56e7951d975624"
|
||||
dependencies = [
|
||||
"atoi",
|
||||
"base64 0.21.7",
|
||||
"base64 0.21.4",
|
||||
"bigdecimal",
|
||||
"bitflags 2.4.2",
|
||||
"byteorder",
|
||||
@@ -9813,7 +9753,6 @@ dependencies = [
|
||||
"env_logger",
|
||||
"futures 0.3.28",
|
||||
"gpui",
|
||||
"http 0.1.0",
|
||||
"language",
|
||||
"log",
|
||||
"postage",
|
||||
@@ -9834,7 +9773,6 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures 0.3.28",
|
||||
"http 0.1.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smol",
|
||||
@@ -10038,14 +9976,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "taffy"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b2e140b328c6cb5e744bb2c65910b47df86b239afc793ee2c52262569cf9225"
|
||||
version = "0.3.11"
|
||||
source = "git+https://github.com/DioxusLabs/taffy?rev=1876f72bee5e376023eaa518aa7b8a34c769bd1b#1876f72bee5e376023eaa518aa7b8a34c769bd1b"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"grid",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"slotmap",
|
||||
]
|
||||
|
||||
@@ -10094,7 +10030,6 @@ dependencies = [
|
||||
"fuzzy",
|
||||
"gpui",
|
||||
"language",
|
||||
"menu",
|
||||
"picker",
|
||||
"project",
|
||||
"schemars",
|
||||
@@ -10173,7 +10108,7 @@ dependencies = [
|
||||
"theme",
|
||||
"thiserror",
|
||||
"util",
|
||||
"windows 0.56.0",
|
||||
"windows 0.53.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10217,7 +10152,6 @@ dependencies = [
|
||||
"ctor",
|
||||
"env_logger",
|
||||
"gpui",
|
||||
"http 0.1.0",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"parking_lot",
|
||||
@@ -10346,12 +10280,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tiktoken-rs"
|
||||
version = "0.5.9"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c314e7ce51440f9e8f5a497394682a57b7c323d0f4d0a6b1b13c429056e0e234"
|
||||
checksum = "a4427b6b1c6b38215b92dd47a83a0ecc6735573d0a5a4c14acc0ac5b33b28adb"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.21.7",
|
||||
"base64 0.21.4",
|
||||
"bstr",
|
||||
"fancy-regex",
|
||||
"lazy_static",
|
||||
@@ -11086,7 +11020,7 @@ dependencies = [
|
||||
"story",
|
||||
"strum",
|
||||
"theme",
|
||||
"windows 0.56.0",
|
||||
"windows 0.53.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11190,6 +11124,12 @@ version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
@@ -11260,6 +11200,7 @@ dependencies = [
|
||||
"futures-lite 1.13.0",
|
||||
"git2",
|
||||
"globset",
|
||||
"isahc",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
@@ -11271,6 +11212,7 @@ dependencies = [
|
||||
"tempfile",
|
||||
"tendril",
|
||||
"unicase",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11370,7 +11312,6 @@ dependencies = [
|
||||
"language",
|
||||
"log",
|
||||
"lsp",
|
||||
"multi_buffer",
|
||||
"nvim-rs",
|
||||
"parking_lot",
|
||||
"regex",
|
||||
@@ -12219,17 +12160,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
|
||||
dependencies = [
|
||||
"windows-core 0.52.0",
|
||||
"windows-targets 0.52.5",
|
||||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.56.0"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132"
|
||||
checksum = "efc5cf48f83140dcaab716eeaea345f9e93d0018fb81162753a3f76c3397b538"
|
||||
dependencies = [
|
||||
"windows-core 0.56.0",
|
||||
"windows-targets 0.52.5",
|
||||
"windows-core 0.53.0",
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -12238,26 +12181,24 @@ version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.5",
|
||||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.56.0"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4698e52ed2d08f8658ab0c39512a7c00ee5fe2688c65f8c0a4f06750d729f2a6"
|
||||
checksum = "9dcc5b895a6377f1ab9fa55acedab1fd5ac0db66ad1e6c7f47e28a22e446a5dd"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-result",
|
||||
"windows-targets 0.52.5",
|
||||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.56.0"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b"
|
||||
checksum = "942ac266be9249c84ca862f0a164a39533dc2f6f33dc98ec89c8da99b82ea0bd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -12266,9 +12207,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.56.0"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc"
|
||||
checksum = "da33557140a288fae4e1d5f8873aaf9eb6613a9cf82c3e070223ff177f598b60"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -12277,11 +12218,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.1.1"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b"
|
||||
checksum = "cd19df78e5168dfb0aedc343d1d1b8d422ab2db6756d2dc3fef75035402a3f64"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.5",
|
||||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -12308,7 +12249,7 @@ version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.5",
|
||||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -12343,18 +12284,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.5"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
|
||||
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.5",
|
||||
"windows_aarch64_msvc 0.52.5",
|
||||
"windows_i686_gnu 0.52.5",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc 0.52.5",
|
||||
"windows_x86_64_gnu 0.52.5",
|
||||
"windows_x86_64_gnullvm 0.52.5",
|
||||
"windows_x86_64_msvc 0.52.5",
|
||||
"windows_aarch64_gnullvm 0.52.4",
|
||||
"windows_aarch64_msvc 0.52.4",
|
||||
"windows_i686_gnu 0.52.4",
|
||||
"windows_i686_msvc 0.52.4",
|
||||
"windows_x86_64_gnu 0.52.4",
|
||||
"windows_x86_64_gnullvm 0.52.4",
|
||||
"windows_x86_64_msvc 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -12371,9 +12311,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.5"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
|
||||
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
@@ -12389,9 +12329,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.5"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
|
||||
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
@@ -12407,15 +12347,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.5"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
|
||||
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
@@ -12431,9 +12365,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.5"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
|
||||
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
@@ -12449,9 +12383,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.5"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
|
||||
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
@@ -12467,9 +12401,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.5"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
|
||||
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
@@ -12485,9 +12419,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.5"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
||||
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
@@ -12679,7 +12613,6 @@ dependencies = [
|
||||
"fs",
|
||||
"futures 0.3.28",
|
||||
"gpui",
|
||||
"http 0.1.0",
|
||||
"itertools 0.11.0",
|
||||
"language",
|
||||
"lazy_static",
|
||||
@@ -12715,7 +12648,6 @@ dependencies = [
|
||||
"git",
|
||||
"git2",
|
||||
"gpui",
|
||||
"http 0.1.0",
|
||||
"ignore",
|
||||
"itertools 0.11.0",
|
||||
"language",
|
||||
@@ -12834,9 +12766,7 @@ name = "xtask"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo_toml",
|
||||
"clap 4.4.4",
|
||||
"toml 0.8.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -12929,7 +12859,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed"
|
||||
version = "0.137.0"
|
||||
version = "0.136.0"
|
||||
dependencies = [
|
||||
"activity_indicator",
|
||||
"anyhow",
|
||||
@@ -12967,7 +12897,6 @@ dependencies = [
|
||||
"go_to_line",
|
||||
"gpui",
|
||||
"headless",
|
||||
"http 0.1.0",
|
||||
"image_viewer",
|
||||
"inline_completion_button",
|
||||
"install_cli",
|
||||
@@ -13030,7 +12959,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed_astro"
|
||||
version = "0.0.2"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"zed_extension_api 0.0.4",
|
||||
]
|
||||
@@ -13079,7 +13008,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed_emmet"
|
||||
version = "0.0.3"
|
||||
version = "0.0.2"
|
||||
dependencies = [
|
||||
"zed_extension_api 0.0.4",
|
||||
]
|
||||
@@ -13122,7 +13051,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed_gleam"
|
||||
version = "0.1.2"
|
||||
version = "0.1.1"
|
||||
dependencies = [
|
||||
"zed_extension_api 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -13164,7 +13093,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed_php"
|
||||
version = "0.0.3"
|
||||
version = "0.0.2"
|
||||
dependencies = [
|
||||
"zed_extension_api 0.0.4",
|
||||
]
|
||||
@@ -13183,13 +13112,6 @@ dependencies = [
|
||||
"zed_extension_api 0.0.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zed_ruby"
|
||||
version = "0.0.3"
|
||||
dependencies = [
|
||||
"zed_extension_api 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zed_svelte"
|
||||
version = "0.0.1"
|
||||
@@ -13220,7 +13142,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed_vue"
|
||||
version = "0.0.2"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"zed_extension_api 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
12
Cargo.toml
12
Cargo.toml
@@ -41,7 +41,6 @@ members = [
|
||||
"crates/gpui",
|
||||
"crates/gpui_macros",
|
||||
"crates/headless",
|
||||
"crates/http",
|
||||
"crates/image_viewer",
|
||||
"crates/inline_completion_button",
|
||||
"crates/install_cli",
|
||||
@@ -128,7 +127,6 @@ members = [
|
||||
"extensions/php",
|
||||
"extensions/prisma",
|
||||
"extensions/purescript",
|
||||
"extensions/ruby",
|
||||
"extensions/svelte",
|
||||
"extensions/terraform",
|
||||
"extensions/toml",
|
||||
@@ -184,7 +182,6 @@ google_ai = { path = "crates/google_ai" }
|
||||
gpui = { path = "crates/gpui" }
|
||||
gpui_macros = { path = "crates/gpui_macros" }
|
||||
headless = { path = "crates/headless" }
|
||||
http = { path = "crates/http" }
|
||||
install_cli = { path = "crates/install_cli" }
|
||||
image_viewer = { path = "crates/image_viewer" }
|
||||
inline_completion_button = { path = "crates/inline_completion_button" }
|
||||
@@ -265,7 +262,6 @@ bitflags = "2.4.2"
|
||||
blade-graphics = { git = "https://github.com/kvark/blade", rev = "e35b2d41f221a48b75f7cf2e78a81e7ecb7a383c" }
|
||||
blade-macros = { git = "https://github.com/kvark/blade", rev = "e35b2d41f221a48b75f7cf2e78a81e7ecb7a383c" }
|
||||
cap-std = "3.0"
|
||||
cargo_toml = "0.20"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
clap = { version = "4.4", features = ["derive"] }
|
||||
clickhouse = { version = "0.11.6" }
|
||||
@@ -317,7 +313,7 @@ refineable = { path = "./crates/refineable" }
|
||||
regex = "1.5"
|
||||
repair_json = "0.1.0"
|
||||
rusqlite = { version = "0.29.0", features = ["blob", "array", "modern_sqlite"] }
|
||||
rust-embed = { version = "8.4", features = ["include-exclude"] }
|
||||
rust-embed = { version = "8.0", features = ["include-exclude"] }
|
||||
schemars = "0.8"
|
||||
semver = "1.0"
|
||||
serde = { version = "1.0", features = ["derive", "rc"] }
|
||||
@@ -337,7 +333,7 @@ subtle = "2.5.0"
|
||||
sysinfo = "0.30.7"
|
||||
tempfile = "3.9.0"
|
||||
thiserror = "1.0.29"
|
||||
tiktoken-rs = "0.5.9"
|
||||
tiktoken-rs = "0.5.7"
|
||||
time = { version = "0.3", features = [
|
||||
"macros",
|
||||
"parsing",
|
||||
@@ -391,7 +387,7 @@ wit-component = "0.201"
|
||||
sys-locale = "0.3.1"
|
||||
|
||||
[workspace.dependencies.windows]
|
||||
version = "0.56.0"
|
||||
version = "0.53.0"
|
||||
features = [
|
||||
"implement",
|
||||
"Foundation_Numerics",
|
||||
@@ -402,11 +398,11 @@ features = [
|
||||
"Win32_Graphics_Direct2D",
|
||||
"Win32_Graphics_Direct2D_Common",
|
||||
"Win32_Graphics_DirectWrite",
|
||||
"Win32_Graphics_Dwm",
|
||||
"Win32_Graphics_Dxgi_Common",
|
||||
"Win32_Graphics_Gdi",
|
||||
"Win32_Graphics_Imaging",
|
||||
"Win32_Graphics_Imaging_D2D",
|
||||
"Win32_Media",
|
||||
"Win32_Security",
|
||||
"Win32_Security_Credentials",
|
||||
"Win32_Storage_FileSystem",
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M13.15 7.49998C13.15 4.66458 10.9402 1.84998 7.50002 1.84998C4.7217 1.84998 3.34851 3.90636 2.76336 4.99997H4.5C4.77614 4.99997 5 5.22383 5 5.49997C5 5.77611 4.77614 5.99997 4.5 5.99997H1.5C1.22386 5.99997 1 5.77611 1 5.49997V2.49997C1 2.22383 1.22386 1.99997 1.5 1.99997C1.77614 1.99997 2 2.22383 2 2.49997V4.31318C2.70453 3.07126 4.33406 0.849976 7.50002 0.849976C11.5628 0.849976 14.15 4.18537 14.15 7.49998C14.15 10.8146 11.5628 14.15 7.50002 14.15C5.55618 14.15 3.93778 13.3808 2.78548 12.2084C2.16852 11.5806 1.68668 10.839 1.35816 10.0407C1.25306 9.78536 1.37488 9.49315 1.63024 9.38806C1.8856 9.28296 2.17781 9.40478 2.2829 9.66014C2.56374 10.3425 2.97495 10.9745 3.4987 11.5074C4.47052 12.4963 5.83496 13.15 7.50002 13.15C10.9402 13.15 13.15 10.3354 13.15 7.49998ZM7 10V5.00001H8V10H7Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>
|
||||
|
Before Width: | Height: | Size: 974 B |
@@ -1,5 +0,0 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.5 6C1.5 6.89002 1.76392 7.76004 2.25839 8.50007C2.75285 9.24009 3.45566 9.81686 4.27792 10.1575C5.10019 10.4981 6.00499 10.5872 6.87791 10.4135C7.75082 10.2399 8.55264 9.81132 9.18198 9.18198C9.81132 8.55264 10.2399 7.75082 10.4135 6.87791C10.5872 6.00499 10.4981 5.10019 10.1575 4.27792C9.81686 3.45566 9.24009 2.75285 8.50007 2.25839C7.76004 1.76392 6.89002 1.5 6 1.5C4.74198 1.50473 3.53448 1.99561 2.63 2.87L1.5 4" stroke="#919081" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M1.5 1.5V4H4" stroke="#919081" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6 3.5V6L8 7" stroke="#919081" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 778 B |
@@ -53,9 +53,7 @@
|
||||
// "alt-d": "editor::DeleteToNextWordEnd",
|
||||
"ctrl-x": "editor::Cut",
|
||||
"ctrl-c": "editor::Copy",
|
||||
"ctrl-insert": "editor::Copy",
|
||||
"ctrl-v": "editor::Paste",
|
||||
"shift-insert": "editor::Paste",
|
||||
"ctrl-z": "editor::Undo",
|
||||
"ctrl-shift-z": "editor::Redo",
|
||||
"up": "editor::MoveUp",
|
||||
@@ -548,9 +546,7 @@
|
||||
"alt-ctrl-n": "project_panel::NewDirectory",
|
||||
"ctrl-x": "project_panel::Cut",
|
||||
"ctrl-c": "project_panel::Copy",
|
||||
"ctrl-insert": "project_panel::Copy",
|
||||
"ctrl-v": "project_panel::Paste",
|
||||
"shift-insert": "project_panel::Paste",
|
||||
"ctrl-alt-c": "project_panel::CopyPath",
|
||||
"alt-ctrl-shift-c": "project_panel::CopyRelativePath",
|
||||
"f2": "project_panel::Rename",
|
||||
@@ -612,9 +608,7 @@
|
||||
"bindings": {
|
||||
"ctrl-alt-space": "terminal::ShowCharacterPalette",
|
||||
"shift-ctrl-c": "terminal::Copy",
|
||||
"ctrl-insert": "terminal::Copy",
|
||||
"shift-ctrl-v": "terminal::Paste",
|
||||
"shift-insert": "terminal::Paste",
|
||||
"up": ["terminal::SendKeystroke", "up"],
|
||||
"pageup": ["terminal::SendKeystroke", "pageup"],
|
||||
"down": ["terminal::SendKeystroke", "down"],
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
"cmd-escape": "menu::Cancel",
|
||||
"ctrl-escape": "menu::Cancel",
|
||||
"ctrl-c": "menu::Cancel",
|
||||
"shift-enter": "picker::UseSelectedQuery",
|
||||
"alt-enter": ["picker::ConfirmInput", { "secondary": false }],
|
||||
"cmd-alt-enter": ["picker::ConfirmInput", { "secondary": true }],
|
||||
"cmd-shift-w": "workspace::CloseWindow",
|
||||
"shift-escape": "workspace::ToggleZoom",
|
||||
"cmd-o": "workspace::Open",
|
||||
@@ -627,14 +630,6 @@
|
||||
"ctrl-backspace": "tab_switcher::CloseSelectedItem"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Picker",
|
||||
"bindings": {
|
||||
"alt-e": "picker::UseSelectedQuery",
|
||||
"alt-enter": ["picker::ConfirmInput", { "secondary": false }],
|
||||
"cmd-alt-enter": ["picker::ConfirmInput", { "secondary": true }]
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Terminal",
|
||||
"bindings": {
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
[
|
||||
{
|
||||
"context": "ProjectPanel || Editor",
|
||||
"bindings": {
|
||||
"ctrl-6": "pane::AlternateFile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Editor && VimControl && !VimWaiting && !menu",
|
||||
"bindings": {
|
||||
|
||||
@@ -73,17 +73,6 @@
|
||||
"drop_target_size": 0.2,
|
||||
// Whether the cursor blinks in the editor.
|
||||
"cursor_blink": true,
|
||||
// How to highlight the current line in the editor.
|
||||
//
|
||||
// 1. Don't highlight the current line:
|
||||
// "none"
|
||||
// 2. Highlight the gutter area:
|
||||
// "gutter"
|
||||
// 3. Highlight the editor area:
|
||||
// "line"
|
||||
// 4. Highlight the full line (default):
|
||||
// "all"
|
||||
"current_line_highlight": "all",
|
||||
// Whether to pop the completions menu while typing in an editor without
|
||||
// explicitly requesting it.
|
||||
"show_completions_on_input": true,
|
||||
@@ -103,7 +92,14 @@
|
||||
// Hide the values of in variables from visual display in private files
|
||||
"redact_private_values": false,
|
||||
// Globs to match against file paths to determine if a file is private.
|
||||
"private_files": ["**/.env*", "**/*.pem", "**/*.key", "**/*.cert", "**/*.crt", "**/secrets.yml"],
|
||||
"private_files": [
|
||||
"**/.env*",
|
||||
"**/*.pem",
|
||||
"**/*.key",
|
||||
"**/*.cert",
|
||||
"**/*.crt",
|
||||
"**/secrets.yml"
|
||||
],
|
||||
// Whether to use additional LSP queries to format (and amend) the code after
|
||||
// every "trigger" symbol input, defined by LSP server capabilities.
|
||||
"use_on_type_format": true,
|
||||
@@ -293,8 +289,7 @@
|
||||
// 1. "gpt-3.5-turbo"
|
||||
// 2. "gpt-4"
|
||||
// 3. "gpt-4-turbo-preview"
|
||||
// 4. "gpt-4o"
|
||||
"default_model": "gpt-4o"
|
||||
"default_model": "gpt-4-turbo-preview"
|
||||
}
|
||||
},
|
||||
// Whether the screen sharing icon is shown in the os status bar.
|
||||
@@ -304,7 +299,9 @@
|
||||
// The list of language servers to use (or disable) for all languages.
|
||||
//
|
||||
// This is typically customized on a per-language basis.
|
||||
"language_servers": ["..."],
|
||||
"language_servers": [
|
||||
"..."
|
||||
],
|
||||
// When to automatically save edited buffers. This setting can
|
||||
// take four values.
|
||||
//
|
||||
@@ -352,8 +349,6 @@
|
||||
// when saving it.
|
||||
"ensure_final_newline_on_save": true,
|
||||
// Whether or not to perform a buffer format before saving
|
||||
//
|
||||
// Keep in mind, if the autosave with delay is enabled, format_on_save will be ignored
|
||||
"format_on_save": "on",
|
||||
// How to perform a buffer format. This setting can take 4 values:
|
||||
//
|
||||
@@ -439,7 +434,9 @@
|
||||
"copilot": {
|
||||
// The set of glob patterns for which copilot should be disabled
|
||||
// in any matching file.
|
||||
"disabled_globs": [".env"]
|
||||
"disabled_globs": [
|
||||
".env"
|
||||
]
|
||||
},
|
||||
// Settings specific to journaling
|
||||
"journal": {
|
||||
@@ -468,7 +465,7 @@
|
||||
// }
|
||||
// }
|
||||
"shell": "system",
|
||||
// Where to dock terminals panel. Can be `left`, `right`, `bottom`.
|
||||
// Where to dock terminals panel. Can be 'left', 'right', 'bottom'.
|
||||
"dock": "bottom",
|
||||
// Default width when the terminal is docked to the left or right.
|
||||
"default_width": 640,
|
||||
@@ -550,8 +547,13 @@
|
||||
// Default directories to search for virtual environments, relative
|
||||
// to the current working directory. We recommend overriding this
|
||||
// in your project's settings, rather than globally.
|
||||
"directories": [".env", "env", ".venv", "venv"],
|
||||
// Can also be `csh`, `fish`, and `nushell`
|
||||
"directories": [
|
||||
".env",
|
||||
"env",
|
||||
".venv",
|
||||
"venv"
|
||||
],
|
||||
// Can also be 'csh', 'fish', and `nushell`
|
||||
"activate_script": "default"
|
||||
}
|
||||
},
|
||||
@@ -576,7 +578,7 @@
|
||||
// use those languages.
|
||||
//
|
||||
// For example, to treat files like `foo.notjs` as JavaScript,
|
||||
// and `Embargo.lock` as TOML:
|
||||
// and 'Embargo.lock' as TOML:
|
||||
//
|
||||
// {
|
||||
// "JavaScript": ["notjs"],
|
||||
@@ -593,30 +595,19 @@
|
||||
},
|
||||
// Different settings for specific languages.
|
||||
"languages": {
|
||||
"Astro": {
|
||||
"prettier": {
|
||||
"allowed": true,
|
||||
"plugins": ["prettier-plugin-astro"]
|
||||
}
|
||||
},
|
||||
"Blade": {
|
||||
"prettier": {
|
||||
"allowed": true
|
||||
}
|
||||
"C++": {
|
||||
"format_on_save": "off"
|
||||
},
|
||||
"C": {
|
||||
"format_on_save": "off"
|
||||
},
|
||||
"C++": {
|
||||
"format_on_save": "off"
|
||||
},
|
||||
"CSS": {
|
||||
"prettier": {
|
||||
"allowed": true
|
||||
}
|
||||
},
|
||||
"Elixir": {
|
||||
"language_servers": ["elixir-ls", "!next-ls", "!lexical", "..."]
|
||||
"language_servers": [
|
||||
"elixir-ls",
|
||||
"!next-ls",
|
||||
"!lexical",
|
||||
"..."
|
||||
]
|
||||
},
|
||||
"Gleam": {
|
||||
"tab_size": 2
|
||||
@@ -626,120 +617,29 @@
|
||||
"source.organizeImports": true
|
||||
}
|
||||
},
|
||||
"GraphQL": {
|
||||
"prettier": {
|
||||
"allowed": true
|
||||
}
|
||||
},
|
||||
"HEEX": {
|
||||
"language_servers": ["elixir-ls", "!next-ls", "!lexical", "..."]
|
||||
},
|
||||
"HTML": {
|
||||
"prettier": {
|
||||
"allowed": true
|
||||
}
|
||||
},
|
||||
"Java": {
|
||||
"prettier": {
|
||||
"allowed": true,
|
||||
"plugins": ["prettier-plugin-java"]
|
||||
}
|
||||
},
|
||||
"JavaScript": {
|
||||
"prettier": {
|
||||
"allowed": true
|
||||
}
|
||||
},
|
||||
"JSON": {
|
||||
"prettier": {
|
||||
"allowed": true
|
||||
}
|
||||
"language_servers": [
|
||||
"elixir-ls",
|
||||
"!next-ls",
|
||||
"!lexical",
|
||||
"..."
|
||||
]
|
||||
},
|
||||
"Make": {
|
||||
"hard_tabs": true
|
||||
},
|
||||
"Markdown": {
|
||||
"format_on_save": "off",
|
||||
"prettier": {
|
||||
"allowed": true
|
||||
}
|
||||
},
|
||||
"PHP": {
|
||||
"prettier": {
|
||||
"allowed": true,
|
||||
"plugins": ["@prettier/plugin-php"]
|
||||
}
|
||||
"format_on_save": "off"
|
||||
},
|
||||
"Prisma": {
|
||||
"tab_size": 2
|
||||
},
|
||||
"Ruby": {
|
||||
"language_servers": ["solargraph", "!ruby-lsp", "..."]
|
||||
},
|
||||
"SCSS": {
|
||||
"prettier": {
|
||||
"allowed": true
|
||||
}
|
||||
},
|
||||
"SQL": {
|
||||
"prettier": {
|
||||
"allowed": true,
|
||||
"plugins": ["prettier-plugin-sql"]
|
||||
}
|
||||
},
|
||||
"Svelte": {
|
||||
"prettier": {
|
||||
"allowed": true,
|
||||
"plugins": ["prettier-plugin-svelte"]
|
||||
}
|
||||
},
|
||||
"TSX": {
|
||||
"prettier": {
|
||||
"allowed": true
|
||||
}
|
||||
},
|
||||
"Twig": {
|
||||
"prettier": {
|
||||
"allowed": true
|
||||
}
|
||||
},
|
||||
"TypeScript": {
|
||||
"prettier": {
|
||||
"allowed": true
|
||||
}
|
||||
},
|
||||
"Vue.js": {
|
||||
"prettier": {
|
||||
"allowed": true
|
||||
}
|
||||
},
|
||||
"XML": {
|
||||
"prettier": {
|
||||
"allowed": true,
|
||||
"plugins": ["@prettier/plugin-xml"]
|
||||
}
|
||||
},
|
||||
"YAML": {
|
||||
"prettier": {
|
||||
"allowed": true
|
||||
}
|
||||
}
|
||||
},
|
||||
// Zed's Prettier integration settings.
|
||||
// Allows to enable/disable formatting with Prettier
|
||||
// and configure default Prettier, used when no project-level Prettier installation is found.
|
||||
// If Prettier is enabled, Zed will use this for its Prettier instance for any applicable file, if
|
||||
// project has no other Prettier installed.
|
||||
"prettier": {
|
||||
// // Whether to consider prettier formatter or not when attempting to format a file.
|
||||
// "allowed": false,
|
||||
//
|
||||
// // Use regular Prettier json configuration.
|
||||
// // If Prettier is allowed, Zed will use this for its Prettier instance for any applicable file, if
|
||||
// // the project has no other Prettier installed.
|
||||
// "plugins": [],
|
||||
//
|
||||
// // Use regular Prettier json configuration.
|
||||
// // If Prettier is allowed, Zed will use this for its Prettier instance for any applicable file, if
|
||||
// // the project has no other Prettier installed.
|
||||
// Use regular Prettier json configuration:
|
||||
// "trailingComma": "es5",
|
||||
// "tabWidth": 4,
|
||||
// "semi": false,
|
||||
|
||||
@@ -5,10 +5,6 @@ edition = "2021"
|
||||
publish = false
|
||||
license = "AGPL-3.0-or-later"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
schemars = ["dep:schemars"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -18,11 +14,9 @@ path = "src/anthropic.rs"
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
futures.workspace = true
|
||||
http.workspace = true
|
||||
isahc.workspace = true
|
||||
schemars = { workspace = true, optional = true }
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
util.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
tokio.workspace = true
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use futures::{io::BufReader, stream::BoxStream, AsyncBufReadExt, AsyncReadExt, StreamExt};
|
||||
use http::{AsyncBody, HttpClient, Method, Request as HttpRequest};
|
||||
use isahc::config::Configurable;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{convert::TryFrom, time::Duration};
|
||||
use std::{convert::TryFrom, sync::Arc};
|
||||
use util::http::{AsyncBody, HttpClient, Method, Request as HttpRequest};
|
||||
|
||||
pub const ANTHROPIC_API_URL: &'static str = "https://api.anthropic.com";
|
||||
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
|
||||
pub enum Model {
|
||||
#[default]
|
||||
#[serde(alias = "claude-3-opus", rename = "claude-3-opus-20240229")]
|
||||
#[serde(rename = "claude-3-opus-20240229")]
|
||||
Claude3Opus,
|
||||
#[serde(alias = "claude-3-sonnet", rename = "claude-3-sonnet-20240229")]
|
||||
#[serde(rename = "claude-3-sonnet-20240229")]
|
||||
Claude3Sonnet,
|
||||
#[serde(alias = "claude-3-haiku", rename = "claude-3-haiku-20240307")]
|
||||
#[serde(rename = "claude-3-haiku-20240307")]
|
||||
Claude3Haiku,
|
||||
}
|
||||
|
||||
@@ -32,14 +28,6 @@ impl Model {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> &'static str {
|
||||
match self {
|
||||
Model::Claude3Opus => "claude-3-opus-20240229",
|
||||
Model::Claude3Sonnet => "claude-3-sonnet-20240229",
|
||||
Model::Claude3Haiku => "claude-3-opus-20240307",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn display_name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Claude3Opus => "Claude 3 Opus",
|
||||
@@ -153,24 +141,20 @@ pub enum TextDelta {
|
||||
}
|
||||
|
||||
pub async fn stream_completion(
|
||||
client: &dyn HttpClient,
|
||||
client: Arc<dyn HttpClient>,
|
||||
api_url: &str,
|
||||
api_key: &str,
|
||||
request: Request,
|
||||
low_speed_timeout: Option<Duration>,
|
||||
) -> Result<BoxStream<'static, Result<ResponseEvent>>> {
|
||||
let uri = format!("{api_url}/v1/messages");
|
||||
let mut request_builder = HttpRequest::builder()
|
||||
let request = HttpRequest::builder()
|
||||
.method(Method::POST)
|
||||
.uri(uri)
|
||||
.header("Anthropic-Version", "2023-06-01")
|
||||
.header("Anthropic-Beta", "tools-2024-04-04")
|
||||
.header("Anthropic-Beta", "messages-2023-12-15")
|
||||
.header("X-Api-Key", api_key)
|
||||
.header("Content-Type", "application/json");
|
||||
if let Some(low_speed_timeout) = low_speed_timeout {
|
||||
request_builder = request_builder.low_speed_timeout(100, low_speed_timeout);
|
||||
}
|
||||
let request = request_builder.body(AsyncBody::from(serde_json::to_string(&request)?))?;
|
||||
.header("Content-Type", "application/json")
|
||||
.body(AsyncBody::from(serde_json::to_string(&request)?))?;
|
||||
let mut response = client.send(request).await?;
|
||||
if response.status().is_success() {
|
||||
let reader = BufReader::new(response.into_body());
|
||||
@@ -212,7 +196,7 @@ pub async fn stream_completion(
|
||||
// #[cfg(test)]
|
||||
// mod tests {
|
||||
// use super::*;
|
||||
// use http::IsahcHttpClient;
|
||||
// use util::http::IsahcHttpClient;
|
||||
|
||||
// #[tokio::test]
|
||||
// async fn stream_completion_success() {
|
||||
|
||||
@@ -11,8 +11,6 @@ doctest = false
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
anthropic = { workspace = true, features = ["schemars"] }
|
||||
cargo_toml.workspace = true
|
||||
chrono.workspace = true
|
||||
client.workspace = true
|
||||
collections.workspace = true
|
||||
@@ -22,7 +20,6 @@ file_icons.workspace = true
|
||||
fs.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
http.workspace = true
|
||||
indoc.workspace = true
|
||||
language.workspace = true
|
||||
log.workspace = true
|
||||
@@ -42,7 +39,6 @@ smol.workspace = true
|
||||
telemetry_events.workspace = true
|
||||
theme.workspace = true
|
||||
tiktoken-rs.workspace = true
|
||||
toml.workspace = true
|
||||
ui.workspace = true
|
||||
util.workspace = true
|
||||
uuid.workspace = true
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
mod current_project;
|
||||
mod recent_buffers;
|
||||
|
||||
pub use current_project::*;
|
||||
pub use recent_buffers::*;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct AmbientContext {
|
||||
pub recent_buffers: RecentBuffersContext,
|
||||
pub current_project: CurrentProjectContext,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||
pub enum ContextUpdated {
|
||||
Updating,
|
||||
Disabled,
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use fs::Fs;
|
||||
use gpui::{AsyncAppContext, ModelContext, Task, WeakModel};
|
||||
use project::{Project, ProjectPath};
|
||||
use util::ResultExt;
|
||||
|
||||
use crate::ambient_context::ContextUpdated;
|
||||
use crate::assistant_panel::Conversation;
|
||||
use crate::{LanguageModelRequestMessage, Role};
|
||||
|
||||
/// Ambient context about the current project.
|
||||
pub struct CurrentProjectContext {
|
||||
pub enabled: bool,
|
||||
pub message: String,
|
||||
pub pending_message: Option<Task<()>>,
|
||||
}
|
||||
|
||||
#[allow(clippy::derivable_impls)]
|
||||
impl Default for CurrentProjectContext {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: false,
|
||||
message: String::new(),
|
||||
pending_message: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CurrentProjectContext {
|
||||
/// Returns the [`CurrentProjectContext`] as a message to the language model.
|
||||
pub fn to_message(&self) -> Option<LanguageModelRequestMessage> {
|
||||
self.enabled.then(|| LanguageModelRequestMessage {
|
||||
role: Role::System,
|
||||
content: self.message.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Updates the [`CurrentProjectContext`] for the given [`Project`].
|
||||
pub fn update(
|
||||
&mut self,
|
||||
fs: Arc<dyn Fs>,
|
||||
project: WeakModel<Project>,
|
||||
cx: &mut ModelContext<Conversation>,
|
||||
) -> ContextUpdated {
|
||||
if !self.enabled {
|
||||
self.message.clear();
|
||||
self.pending_message = None;
|
||||
cx.notify();
|
||||
return ContextUpdated::Disabled;
|
||||
}
|
||||
|
||||
self.pending_message = Some(cx.spawn(|conversation, mut cx| async move {
|
||||
const DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(100);
|
||||
cx.background_executor().timer(DEBOUNCE_TIMEOUT).await;
|
||||
|
||||
let Some(path_to_cargo_toml) = Self::path_to_cargo_toml(project, &mut cx).log_err()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(path_to_cargo_toml) = path_to_cargo_toml
|
||||
.ok_or_else(|| anyhow!("no Cargo.toml"))
|
||||
.log_err()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let message_task = cx
|
||||
.background_executor()
|
||||
.spawn(async move { Self::build_message(fs, &path_to_cargo_toml).await });
|
||||
|
||||
if let Some(message) = message_task.await.log_err() {
|
||||
conversation
|
||||
.update(&mut cx, |conversation, cx| {
|
||||
conversation.ambient_context.current_project.message = message;
|
||||
conversation.count_remaining_tokens(cx);
|
||||
cx.notify();
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
}));
|
||||
|
||||
ContextUpdated::Updating
|
||||
}
|
||||
|
||||
async fn build_message(fs: Arc<dyn Fs>, path_to_cargo_toml: &Path) -> Result<String> {
|
||||
let buffer = fs.load(path_to_cargo_toml).await?;
|
||||
let cargo_toml: cargo_toml::Manifest = toml::from_str(&buffer)?;
|
||||
|
||||
let mut message = String::new();
|
||||
|
||||
let name = cargo_toml
|
||||
.package
|
||||
.as_ref()
|
||||
.map(|package| package.name.as_str());
|
||||
if let Some(name) = name {
|
||||
message.push_str(&format!(" named \"{name}\""));
|
||||
}
|
||||
message.push_str(". ");
|
||||
|
||||
let description = cargo_toml
|
||||
.package
|
||||
.as_ref()
|
||||
.and_then(|package| package.description.as_ref())
|
||||
.and_then(|description| description.get().ok().cloned());
|
||||
if let Some(description) = description.as_ref() {
|
||||
message.push_str("It describes itself as ");
|
||||
message.push_str(&format!("\"{description}\""));
|
||||
message.push_str(". ");
|
||||
}
|
||||
|
||||
let dependencies = cargo_toml.dependencies.keys().cloned().collect::<Vec<_>>();
|
||||
if !dependencies.is_empty() {
|
||||
message.push_str("The following dependencies are installed: ");
|
||||
message.push_str(&dependencies.join(", "));
|
||||
message.push_str(". ");
|
||||
}
|
||||
|
||||
Ok(message)
|
||||
}
|
||||
|
||||
fn path_to_cargo_toml(
|
||||
project: WeakModel<Project>,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<Option<PathBuf>> {
|
||||
cx.update(|cx| {
|
||||
let worktree = project.update(cx, |project, _cx| {
|
||||
project
|
||||
.worktrees()
|
||||
.next()
|
||||
.ok_or_else(|| anyhow!("no worktree"))
|
||||
})??;
|
||||
|
||||
let path_to_cargo_toml = worktree.update(cx, |worktree, _cx| {
|
||||
let cargo_toml = worktree.entry_for_path("Cargo.toml")?;
|
||||
Some(ProjectPath {
|
||||
worktree_id: worktree.id(),
|
||||
path: cargo_toml.path.clone(),
|
||||
})
|
||||
});
|
||||
let path_to_cargo_toml = path_to_cargo_toml.and_then(|path| {
|
||||
project
|
||||
.update(cx, |project, cx| project.absolute_path(&path, cx))
|
||||
.ok()
|
||||
.flatten()
|
||||
});
|
||||
|
||||
Ok(path_to_cargo_toml)
|
||||
})?
|
||||
}
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
use std::fmt::Write;
|
||||
use std::iter;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
||||
use gpui::{ModelContext, Subscription, Task, WeakModel};
|
||||
use language::{Buffer, BufferSnapshot, DiagnosticEntry, Point};
|
||||
|
||||
use crate::ambient_context::ContextUpdated;
|
||||
use crate::assistant_panel::Conversation;
|
||||
use crate::{LanguageModelRequestMessage, Role};
|
||||
|
||||
pub struct RecentBuffersContext {
|
||||
pub enabled: bool,
|
||||
pub buffers: Vec<RecentBuffer>,
|
||||
pub message: String,
|
||||
pub pending_message: Option<Task<()>>,
|
||||
}
|
||||
|
||||
pub struct RecentBuffer {
|
||||
pub buffer: WeakModel<Buffer>,
|
||||
pub _subscription: Subscription,
|
||||
}
|
||||
|
||||
impl Default for RecentBuffersContext {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: true,
|
||||
buffers: Vec::new(),
|
||||
message: String::new(),
|
||||
pending_message: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RecentBuffersContext {
|
||||
/// Returns the [`RecentBuffersContext`] as a message to the language model.
|
||||
pub fn to_message(&self) -> Option<LanguageModelRequestMessage> {
|
||||
self.enabled.then(|| LanguageModelRequestMessage {
|
||||
role: Role::System,
|
||||
content: self.message.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update(&mut self, cx: &mut ModelContext<Conversation>) -> ContextUpdated {
|
||||
let buffers = self
|
||||
.buffers
|
||||
.iter()
|
||||
.filter_map(|recent| {
|
||||
recent
|
||||
.buffer
|
||||
.read_with(cx, |buffer, cx| {
|
||||
(
|
||||
buffer.file().map(|file| file.full_path(cx)),
|
||||
buffer.snapshot(),
|
||||
)
|
||||
})
|
||||
.ok()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if !self.enabled || buffers.is_empty() {
|
||||
self.message.clear();
|
||||
self.pending_message = None;
|
||||
cx.notify();
|
||||
ContextUpdated::Disabled
|
||||
} else {
|
||||
self.pending_message = Some(cx.spawn(|this, mut cx| async move {
|
||||
const DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(100);
|
||||
cx.background_executor().timer(DEBOUNCE_TIMEOUT).await;
|
||||
|
||||
let message = cx
|
||||
.background_executor()
|
||||
.spawn(async move { Self::build_message(&buffers) })
|
||||
.await;
|
||||
this.update(&mut cx, |conversation, cx| {
|
||||
conversation.ambient_context.recent_buffers.message = message;
|
||||
conversation.count_remaining_tokens(cx);
|
||||
cx.notify();
|
||||
})
|
||||
.ok();
|
||||
}));
|
||||
|
||||
ContextUpdated::Updating
|
||||
}
|
||||
}
|
||||
|
||||
fn build_message(buffers: &[(Option<PathBuf>, BufferSnapshot)]) -> String {
|
||||
let mut message = String::new();
|
||||
writeln!(
|
||||
message,
|
||||
"The following is a list of recent buffers that the user has opened."
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(
|
||||
message,
|
||||
"For every line in the buffer, I will include a row number that line corresponds to."
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(
|
||||
message,
|
||||
"Lines that don't have a number correspond to errors and warnings. For example:"
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(message, "path/to/file.md").unwrap();
|
||||
writeln!(message, "```markdown").unwrap();
|
||||
writeln!(message, "1 The quick brown fox").unwrap();
|
||||
writeln!(message, "2 jumps over one active").unwrap();
|
||||
writeln!(message, " --- error: should be 'the'").unwrap();
|
||||
writeln!(message, " ------ error: should be 'lazy'").unwrap();
|
||||
writeln!(message, "3 dog").unwrap();
|
||||
writeln!(message, "```").unwrap();
|
||||
|
||||
message.push('\n');
|
||||
writeln!(message, "Here's the actual recent buffer list:").unwrap();
|
||||
for (path, buffer) in buffers {
|
||||
if let Some(path) = path {
|
||||
writeln!(message, "{}", path.display()).unwrap();
|
||||
} else {
|
||||
writeln!(message, "untitled").unwrap();
|
||||
}
|
||||
|
||||
if let Some(language) = buffer.language() {
|
||||
writeln!(message, "```{}", language.name().to_lowercase()).unwrap();
|
||||
} else {
|
||||
writeln!(message, "```").unwrap();
|
||||
}
|
||||
|
||||
let mut diagnostics = buffer
|
||||
.diagnostics_in_range::<_, Point>(
|
||||
language::Anchor::MIN..language::Anchor::MAX,
|
||||
false,
|
||||
)
|
||||
.peekable();
|
||||
|
||||
let mut active_diagnostics = Vec::<DiagnosticEntry<Point>>::new();
|
||||
const GUTTER_PADDING: usize = 4;
|
||||
let gutter_width =
|
||||
((buffer.max_point().row + 1) as f32).log10() as usize + 1 + GUTTER_PADDING;
|
||||
for buffer_row in 0..=buffer.max_point().row {
|
||||
let display_row = buffer_row + 1;
|
||||
active_diagnostics.retain(|diagnostic| {
|
||||
(diagnostic.range.start.row..=diagnostic.range.end.row).contains(&buffer_row)
|
||||
});
|
||||
while diagnostics.peek().map_or(false, |diagnostic| {
|
||||
(diagnostic.range.start.row..=diagnostic.range.end.row).contains(&buffer_row)
|
||||
}) {
|
||||
active_diagnostics.push(diagnostics.next().unwrap());
|
||||
}
|
||||
|
||||
let row_width = (display_row as f32).log10() as usize + 1;
|
||||
write!(message, "{}", display_row).unwrap();
|
||||
if row_width < gutter_width {
|
||||
message.extend(iter::repeat(' ').take(gutter_width - row_width));
|
||||
}
|
||||
|
||||
for chunk in buffer.text_for_range(
|
||||
Point::new(buffer_row, 0)..Point::new(buffer_row, buffer.line_len(buffer_row)),
|
||||
) {
|
||||
message.push_str(chunk);
|
||||
}
|
||||
message.push('\n');
|
||||
|
||||
for diagnostic in &active_diagnostics {
|
||||
message.extend(iter::repeat(' ').take(gutter_width));
|
||||
|
||||
let start_column = if diagnostic.range.start.row == buffer_row {
|
||||
message
|
||||
.extend(iter::repeat(' ').take(diagnostic.range.start.column as usize));
|
||||
diagnostic.range.start.column
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let end_column = if diagnostic.range.end.row == buffer_row {
|
||||
diagnostic.range.end.column
|
||||
} else {
|
||||
buffer.line_len(buffer_row)
|
||||
};
|
||||
|
||||
message.extend(iter::repeat('-').take((end_column - start_column) as usize));
|
||||
writeln!(message, " {}", diagnostic.diagnostic.message).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
message.push('\n');
|
||||
}
|
||||
|
||||
writeln!(
|
||||
message,
|
||||
"When quoting the above code, mention which rows the code occurs at."
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(
|
||||
message,
|
||||
"Never include rows in the quoted code itself and only report lines that didn't start with a row number."
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
message
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
mod ambient_context;
|
||||
pub mod assistant_panel;
|
||||
pub mod assistant_settings;
|
||||
mod codegen;
|
||||
@@ -7,8 +6,11 @@ mod prompts;
|
||||
mod saved_conversation;
|
||||
mod streaming_diff;
|
||||
|
||||
mod embedded_scope;
|
||||
|
||||
pub use assistant_panel::AssistantPanel;
|
||||
use assistant_settings::{AnthropicModel, AssistantSettings, OpenAiModel, ZedDotDevModel};
|
||||
use assistant_settings::{AssistantSettings, OpenAiModel, ZedDotDevModel};
|
||||
use chrono::{DateTime, Local};
|
||||
use client::{proto, Client};
|
||||
use command_palette_hooks::CommandPaletteFilter;
|
||||
pub(crate) use completion_provider::*;
|
||||
@@ -24,6 +26,7 @@ use std::{
|
||||
actions!(
|
||||
assistant,
|
||||
[
|
||||
NewConversation,
|
||||
Assist,
|
||||
Split,
|
||||
CycleMessageRole,
|
||||
@@ -32,7 +35,6 @@ actions!(
|
||||
ResetKey,
|
||||
InlineAssist,
|
||||
ToggleIncludeConversation,
|
||||
ToggleHistory,
|
||||
]
|
||||
);
|
||||
|
||||
@@ -73,7 +75,6 @@ impl Display for Role {
|
||||
pub enum LanguageModel {
|
||||
ZedDotDev(ZedDotDevModel),
|
||||
OpenAi(OpenAiModel),
|
||||
Anthropic(AnthropicModel),
|
||||
}
|
||||
|
||||
impl Default for LanguageModel {
|
||||
@@ -86,23 +87,20 @@ impl LanguageModel {
|
||||
pub fn telemetry_id(&self) -> String {
|
||||
match self {
|
||||
LanguageModel::OpenAi(model) => format!("openai/{}", model.id()),
|
||||
LanguageModel::Anthropic(model) => format!("anthropic/{}", model.id()),
|
||||
LanguageModel::ZedDotDev(model) => format!("zed.dev/{}", model.id()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn display_name(&self) -> String {
|
||||
match self {
|
||||
LanguageModel::OpenAi(model) => model.display_name().into(),
|
||||
LanguageModel::Anthropic(model) => model.display_name().into(),
|
||||
LanguageModel::ZedDotDev(model) => model.display_name().into(),
|
||||
LanguageModel::OpenAi(model) => format!("openai/{}", model.display_name()),
|
||||
LanguageModel::ZedDotDev(model) => format!("zed.dev/{}", model.display_name()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max_token_count(&self) -> usize {
|
||||
match self {
|
||||
LanguageModel::OpenAi(model) => model.max_token_count(),
|
||||
LanguageModel::Anthropic(model) => model.max_token_count(),
|
||||
LanguageModel::ZedDotDev(model) => model.max_token_count(),
|
||||
}
|
||||
}
|
||||
@@ -110,7 +108,6 @@ impl LanguageModel {
|
||||
pub fn id(&self) -> &str {
|
||||
match self {
|
||||
LanguageModel::OpenAi(model) => model.id(),
|
||||
LanguageModel::Anthropic(model) => model.id(),
|
||||
LanguageModel::ZedDotDev(model) => model.id(),
|
||||
}
|
||||
}
|
||||
@@ -181,6 +178,7 @@ pub struct LanguageModelChoiceDelta {
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
struct MessageMetadata {
|
||||
role: Role,
|
||||
sent_at: DateTime<Local>,
|
||||
status: MessageStatus,
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,5 @@
|
||||
use std::fmt;
|
||||
|
||||
pub use anthropic::Model as AnthropicModel;
|
||||
use gpui::Pixels;
|
||||
pub use open_ai::Model as OpenAiModel;
|
||||
use schemars::{
|
||||
@@ -17,9 +16,8 @@ use settings::{Settings, SettingsSources};
|
||||
pub enum ZedDotDevModel {
|
||||
Gpt3Point5Turbo,
|
||||
Gpt4,
|
||||
Gpt4Turbo,
|
||||
#[default]
|
||||
Gpt4Omni,
|
||||
Gpt4Turbo,
|
||||
Claude3Opus,
|
||||
Claude3Sonnet,
|
||||
Claude3Haiku,
|
||||
@@ -57,7 +55,6 @@ impl<'de> Deserialize<'de> for ZedDotDevModel {
|
||||
"gpt-3.5-turbo" => Ok(ZedDotDevModel::Gpt3Point5Turbo),
|
||||
"gpt-4" => Ok(ZedDotDevModel::Gpt4),
|
||||
"gpt-4-turbo-preview" => Ok(ZedDotDevModel::Gpt4Turbo),
|
||||
"gpt-4o" => Ok(ZedDotDevModel::Gpt4Omni),
|
||||
_ => Ok(ZedDotDevModel::Custom(value.to_owned())),
|
||||
}
|
||||
}
|
||||
@@ -77,7 +74,6 @@ impl JsonSchema for ZedDotDevModel {
|
||||
"gpt-3.5-turbo".to_owned(),
|
||||
"gpt-4".to_owned(),
|
||||
"gpt-4-turbo-preview".to_owned(),
|
||||
"gpt-4o".to_owned(),
|
||||
];
|
||||
Schema::Object(SchemaObject {
|
||||
instance_type: Some(InstanceType::String.into()),
|
||||
@@ -104,7 +100,6 @@ impl ZedDotDevModel {
|
||||
Self::Gpt3Point5Turbo => "gpt-3.5-turbo",
|
||||
Self::Gpt4 => "gpt-4",
|
||||
Self::Gpt4Turbo => "gpt-4-turbo-preview",
|
||||
Self::Gpt4Omni => "gpt-4o",
|
||||
Self::Claude3Opus => "claude-3-opus",
|
||||
Self::Claude3Sonnet => "claude-3-sonnet",
|
||||
Self::Claude3Haiku => "claude-3-haiku",
|
||||
@@ -117,7 +112,6 @@ impl ZedDotDevModel {
|
||||
Self::Gpt3Point5Turbo => "GPT 3.5 Turbo",
|
||||
Self::Gpt4 => "GPT 4",
|
||||
Self::Gpt4Turbo => "GPT 4 Turbo",
|
||||
Self::Gpt4Omni => "GPT 4 Omni",
|
||||
Self::Claude3Opus => "Claude 3 Opus",
|
||||
Self::Claude3Sonnet => "Claude 3 Sonnet",
|
||||
Self::Claude3Haiku => "Claude 3 Haiku",
|
||||
@@ -129,7 +123,7 @@ impl ZedDotDevModel {
|
||||
match self {
|
||||
Self::Gpt3Point5Turbo => 2048,
|
||||
Self::Gpt4 => 4096,
|
||||
Self::Gpt4Turbo | Self::Gpt4Omni => 128000,
|
||||
Self::Gpt4Turbo => 128000,
|
||||
Self::Claude3Opus | Self::Claude3Sonnet | Self::Claude3Haiku => 200000,
|
||||
Self::Custom(_) => 4096, // TODO: Make this configurable
|
||||
}
|
||||
@@ -159,17 +153,6 @@ pub enum AssistantProvider {
|
||||
default_model: OpenAiModel,
|
||||
#[serde(default = "open_ai_url")]
|
||||
api_url: String,
|
||||
#[serde(default)]
|
||||
low_speed_timeout_in_seconds: Option<u64>,
|
||||
},
|
||||
#[serde(rename = "anthropic")]
|
||||
Anthropic {
|
||||
#[serde(default)]
|
||||
default_model: AnthropicModel,
|
||||
#[serde(default = "anthropic_api_url")]
|
||||
api_url: String,
|
||||
#[serde(default)]
|
||||
low_speed_timeout_in_seconds: Option<u64>,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -182,11 +165,7 @@ impl Default for AssistantProvider {
|
||||
}
|
||||
|
||||
fn open_ai_url() -> String {
|
||||
open_ai::OPEN_AI_API_URL.to_string()
|
||||
}
|
||||
|
||||
fn anthropic_api_url() -> String {
|
||||
anthropic::ANTHROPIC_API_URL.to_string()
|
||||
"https://api.openai.com/v1".into()
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Deserialize, Serialize)]
|
||||
@@ -243,14 +222,12 @@ impl AssistantSettingsContent {
|
||||
Some(AssistantProvider::OpenAi {
|
||||
default_model: settings.default_open_ai_model.clone().unwrap_or_default(),
|
||||
api_url: open_ai_api_url.clone(),
|
||||
low_speed_timeout_in_seconds: None,
|
||||
})
|
||||
} else {
|
||||
settings.default_open_ai_model.clone().map(|open_ai_model| {
|
||||
AssistantProvider::OpenAi {
|
||||
default_model: open_ai_model,
|
||||
api_url: open_ai_url(),
|
||||
low_speed_timeout_in_seconds: None,
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -387,17 +364,14 @@ impl Settings for AssistantSettings {
|
||||
AssistantProvider::OpenAi {
|
||||
default_model,
|
||||
api_url,
|
||||
low_speed_timeout_in_seconds,
|
||||
},
|
||||
AssistantProvider::OpenAi {
|
||||
default_model: default_model_override,
|
||||
api_url: api_url_override,
|
||||
low_speed_timeout_in_seconds: low_speed_timeout_in_seconds_override,
|
||||
},
|
||||
) => {
|
||||
*default_model = default_model_override;
|
||||
*api_url = api_url_override;
|
||||
*low_speed_timeout_in_seconds = low_speed_timeout_in_seconds_override;
|
||||
}
|
||||
(merged, provider_override) => {
|
||||
*merged = provider_override;
|
||||
@@ -433,9 +407,8 @@ mod tests {
|
||||
assert_eq!(
|
||||
AssistantSettings::get_global(cx).provider,
|
||||
AssistantProvider::OpenAi {
|
||||
default_model: OpenAiModel::FourOmni,
|
||||
api_url: open_ai_url(),
|
||||
low_speed_timeout_in_seconds: None,
|
||||
default_model: OpenAiModel::FourTurbo,
|
||||
api_url: open_ai_url()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -455,9 +428,8 @@ mod tests {
|
||||
assert_eq!(
|
||||
AssistantSettings::get_global(cx).provider,
|
||||
AssistantProvider::OpenAi {
|
||||
default_model: OpenAiModel::FourOmni,
|
||||
api_url: "test-url".into(),
|
||||
low_speed_timeout_in_seconds: None,
|
||||
default_model: OpenAiModel::FourTurbo,
|
||||
api_url: "test-url".into()
|
||||
}
|
||||
);
|
||||
cx.update_global::<SettingsStore, _>(|store, cx| {
|
||||
@@ -476,8 +448,7 @@ mod tests {
|
||||
AssistantSettings::get_global(cx).provider,
|
||||
AssistantProvider::OpenAi {
|
||||
default_model: OpenAiModel::Four,
|
||||
api_url: open_ai_url(),
|
||||
low_speed_timeout_in_seconds: None,
|
||||
api_url: open_ai_url()
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -3,13 +3,11 @@ use crate::{
|
||||
CompletionProvider, LanguageModelRequest,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use client::telemetry::Telemetry;
|
||||
use editor::{Anchor, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint};
|
||||
use futures::{channel::mpsc, SinkExt, Stream, StreamExt};
|
||||
use gpui::{EventEmitter, Model, ModelContext, Task};
|
||||
use language::{Rope, TransactionId};
|
||||
use multi_buffer::MultiBufferRow;
|
||||
use std::{cmp, future, ops::Range, sync::Arc, time::Instant};
|
||||
use std::{cmp, future, ops::Range};
|
||||
|
||||
pub enum Event {
|
||||
Finished,
|
||||
@@ -31,19 +29,13 @@ pub struct Codegen {
|
||||
error: Option<anyhow::Error>,
|
||||
generation: Task<()>,
|
||||
idle: bool,
|
||||
telemetry: Option<Arc<Telemetry>>,
|
||||
_subscription: gpui::Subscription,
|
||||
}
|
||||
|
||||
impl EventEmitter<Event> for Codegen {}
|
||||
|
||||
impl Codegen {
|
||||
pub fn new(
|
||||
buffer: Model<MultiBuffer>,
|
||||
kind: CodegenKind,
|
||||
telemetry: Option<Arc<Telemetry>>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Self {
|
||||
pub fn new(buffer: Model<MultiBuffer>, kind: CodegenKind, cx: &mut ModelContext<Self>) -> Self {
|
||||
let snapshot = buffer.read(cx).snapshot(cx);
|
||||
Self {
|
||||
buffer: buffer.clone(),
|
||||
@@ -54,7 +46,6 @@ impl Codegen {
|
||||
error: Default::default(),
|
||||
idle: true,
|
||||
generation: Task::ready(()),
|
||||
telemetry,
|
||||
_subscription: cx.subscribe(&buffer, Self::handle_buffer_event),
|
||||
}
|
||||
}
|
||||
@@ -109,11 +100,9 @@ impl Codegen {
|
||||
.suggested_indents(selection_start.row..selection_start.row + 1, cx)
|
||||
.into_values()
|
||||
.next()
|
||||
.unwrap_or_else(|| snapshot.indent_size_for_line(MultiBufferRow(selection_start.row)));
|
||||
.unwrap_or_else(|| snapshot.indent_size_for_line(selection_start.row));
|
||||
|
||||
let model_telemetry_id = prompt.model.telemetry_id();
|
||||
let response = CompletionProvider::global(cx).complete(prompt);
|
||||
let telemetry = self.telemetry.clone();
|
||||
self.generation = cx.spawn(|this, mut cx| {
|
||||
async move {
|
||||
let generate = async {
|
||||
@@ -121,89 +110,68 @@ impl Codegen {
|
||||
|
||||
let (mut hunks_tx, mut hunks_rx) = mpsc::channel(1);
|
||||
let diff = cx.background_executor().spawn(async move {
|
||||
let mut response_latency = None;
|
||||
let request_start = Instant::now();
|
||||
let diff = async {
|
||||
let chunks = strip_invalid_spans_from_codeblock(response.await?);
|
||||
futures::pin_mut!(chunks);
|
||||
let mut diff = StreamingDiff::new(selected_text.to_string());
|
||||
let chunks = strip_invalid_spans_from_codeblock(response.await?);
|
||||
futures::pin_mut!(chunks);
|
||||
let mut diff = StreamingDiff::new(selected_text.to_string());
|
||||
|
||||
let mut new_text = String::new();
|
||||
let mut base_indent = None;
|
||||
let mut line_indent = None;
|
||||
let mut first_line = true;
|
||||
let mut new_text = String::new();
|
||||
let mut base_indent = None;
|
||||
let mut line_indent = None;
|
||||
let mut first_line = true;
|
||||
|
||||
while let Some(chunk) = chunks.next().await {
|
||||
if response_latency.is_none() {
|
||||
response_latency = Some(request_start.elapsed());
|
||||
}
|
||||
let chunk = chunk?;
|
||||
while let Some(chunk) = chunks.next().await {
|
||||
let chunk = chunk?;
|
||||
|
||||
let mut lines = chunk.split('\n').peekable();
|
||||
while let Some(line) = lines.next() {
|
||||
new_text.push_str(line);
|
||||
if line_indent.is_none() {
|
||||
if let Some(non_whitespace_ch_ix) =
|
||||
new_text.find(|ch: char| !ch.is_whitespace())
|
||||
{
|
||||
line_indent = Some(non_whitespace_ch_ix);
|
||||
base_indent = base_indent.or(line_indent);
|
||||
let mut lines = chunk.split('\n').peekable();
|
||||
while let Some(line) = lines.next() {
|
||||
new_text.push_str(line);
|
||||
if line_indent.is_none() {
|
||||
if let Some(non_whitespace_ch_ix) =
|
||||
new_text.find(|ch: char| !ch.is_whitespace())
|
||||
{
|
||||
line_indent = Some(non_whitespace_ch_ix);
|
||||
base_indent = base_indent.or(line_indent);
|
||||
|
||||
let line_indent = line_indent.unwrap();
|
||||
let base_indent = base_indent.unwrap();
|
||||
let indent_delta =
|
||||
line_indent as i32 - base_indent as i32;
|
||||
let mut corrected_indent_len = cmp::max(
|
||||
0,
|
||||
suggested_line_indent.len as i32 + indent_delta,
|
||||
)
|
||||
as usize;
|
||||
if first_line {
|
||||
corrected_indent_len = corrected_indent_len
|
||||
.saturating_sub(
|
||||
selection_start.column as usize,
|
||||
);
|
||||
}
|
||||
|
||||
let indent_char = suggested_line_indent.char();
|
||||
let mut indent_buffer = [0; 4];
|
||||
let indent_str =
|
||||
indent_char.encode_utf8(&mut indent_buffer);
|
||||
new_text.replace_range(
|
||||
..line_indent,
|
||||
&indent_str.repeat(corrected_indent_len),
|
||||
);
|
||||
let line_indent = line_indent.unwrap();
|
||||
let base_indent = base_indent.unwrap();
|
||||
let indent_delta = line_indent as i32 - base_indent as i32;
|
||||
let mut corrected_indent_len = cmp::max(
|
||||
0,
|
||||
suggested_line_indent.len as i32 + indent_delta,
|
||||
)
|
||||
as usize;
|
||||
if first_line {
|
||||
corrected_indent_len = corrected_indent_len
|
||||
.saturating_sub(selection_start.column as usize);
|
||||
}
|
||||
}
|
||||
|
||||
if line_indent.is_some() {
|
||||
hunks_tx.send(diff.push_new(&new_text)).await?;
|
||||
new_text.clear();
|
||||
let indent_char = suggested_line_indent.char();
|
||||
let mut indent_buffer = [0; 4];
|
||||
let indent_str =
|
||||
indent_char.encode_utf8(&mut indent_buffer);
|
||||
new_text.replace_range(
|
||||
..line_indent,
|
||||
&indent_str.repeat(corrected_indent_len),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if lines.peek().is_some() {
|
||||
hunks_tx.send(diff.push_new("\n")).await?;
|
||||
line_indent = None;
|
||||
first_line = false;
|
||||
}
|
||||
if line_indent.is_some() {
|
||||
hunks_tx.send(diff.push_new(&new_text)).await?;
|
||||
new_text.clear();
|
||||
}
|
||||
|
||||
if lines.peek().is_some() {
|
||||
hunks_tx.send(diff.push_new("\n")).await?;
|
||||
line_indent = None;
|
||||
first_line = false;
|
||||
}
|
||||
}
|
||||
hunks_tx.send(diff.push_new(&new_text)).await?;
|
||||
hunks_tx.send(diff.finish()).await?;
|
||||
|
||||
anyhow::Ok(())
|
||||
};
|
||||
|
||||
let error_message = diff.await.err().map(|error| error.to_string());
|
||||
if let Some(telemetry) = telemetry {
|
||||
telemetry.report_assistant_event(
|
||||
None,
|
||||
telemetry_events::AssistantKind::Inline,
|
||||
model_telemetry_id,
|
||||
response_latency,
|
||||
error_message,
|
||||
);
|
||||
}
|
||||
hunks_tx.send(diff.push_new(&new_text)).await?;
|
||||
hunks_tx.send(diff.finish()).await?;
|
||||
|
||||
anyhow::Ok(())
|
||||
});
|
||||
|
||||
while let Some(hunks) = hunks_rx.next().await {
|
||||
@@ -266,8 +234,7 @@ impl Codegen {
|
||||
})?;
|
||||
}
|
||||
|
||||
diff.await;
|
||||
|
||||
diff.await?;
|
||||
anyhow::Ok(())
|
||||
};
|
||||
|
||||
@@ -428,9 +395,8 @@ mod tests {
|
||||
let snapshot = buffer.snapshot(cx);
|
||||
snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(4, 5))
|
||||
});
|
||||
let codegen = cx.new_model(|cx| {
|
||||
Codegen::new(buffer.clone(), CodegenKind::Transform { range }, None, cx)
|
||||
});
|
||||
let codegen =
|
||||
cx.new_model(|cx| Codegen::new(buffer.clone(), CodegenKind::Transform { range }, cx));
|
||||
|
||||
let request = LanguageModelRequest::default();
|
||||
codegen.update(cx, |codegen, cx| codegen.start(request, cx));
|
||||
@@ -487,9 +453,8 @@ mod tests {
|
||||
let snapshot = buffer.snapshot(cx);
|
||||
snapshot.anchor_before(Point::new(1, 6))
|
||||
});
|
||||
let codegen = cx.new_model(|cx| {
|
||||
Codegen::new(buffer.clone(), CodegenKind::Generate { position }, None, cx)
|
||||
});
|
||||
let codegen =
|
||||
cx.new_model(|cx| Codegen::new(buffer.clone(), CodegenKind::Generate { position }, cx));
|
||||
|
||||
let request = LanguageModelRequest::default();
|
||||
codegen.update(cx, |codegen, cx| codegen.start(request, cx));
|
||||
@@ -546,9 +511,8 @@ mod tests {
|
||||
let snapshot = buffer.snapshot(cx);
|
||||
snapshot.anchor_before(Point::new(1, 2))
|
||||
});
|
||||
let codegen = cx.new_model(|cx| {
|
||||
Codegen::new(buffer.clone(), CodegenKind::Generate { position }, None, cx)
|
||||
});
|
||||
let codegen =
|
||||
cx.new_model(|cx| Codegen::new(buffer.clone(), CodegenKind::Generate { position }, cx));
|
||||
|
||||
let request = LanguageModelRequest::default();
|
||||
codegen.update(cx, |codegen, cx| codegen.start(request, cx));
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
mod anthropic;
|
||||
#[cfg(test)]
|
||||
mod fake;
|
||||
mod open_ai;
|
||||
mod zed;
|
||||
|
||||
pub use anthropic::*;
|
||||
#[cfg(test)]
|
||||
pub use fake::*;
|
||||
pub use open_ai::*;
|
||||
@@ -20,7 +18,6 @@ use futures::{future::BoxFuture, stream::BoxStream};
|
||||
use gpui::{AnyView, AppContext, BorrowAppContext, Task, WindowContext};
|
||||
use settings::{Settings, SettingsStore};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
pub fn init(client: Arc<Client>, cx: &mut AppContext) {
|
||||
let mut settings_version = 0;
|
||||
@@ -36,23 +33,10 @@ pub fn init(client: Arc<Client>, cx: &mut AppContext) {
|
||||
AssistantProvider::OpenAi {
|
||||
default_model,
|
||||
api_url,
|
||||
low_speed_timeout_in_seconds,
|
||||
} => CompletionProvider::OpenAi(OpenAiCompletionProvider::new(
|
||||
default_model.clone(),
|
||||
api_url.clone(),
|
||||
client.http_client(),
|
||||
low_speed_timeout_in_seconds.map(Duration::from_secs),
|
||||
settings_version,
|
||||
)),
|
||||
AssistantProvider::Anthropic {
|
||||
default_model,
|
||||
api_url,
|
||||
low_speed_timeout_in_seconds,
|
||||
} => CompletionProvider::Anthropic(AnthropicCompletionProvider::new(
|
||||
default_model.clone(),
|
||||
api_url.clone(),
|
||||
client.http_client(),
|
||||
low_speed_timeout_in_seconds.map(Duration::from_secs),
|
||||
settings_version,
|
||||
)),
|
||||
};
|
||||
@@ -67,30 +51,9 @@ pub fn init(client: Arc<Client>, cx: &mut AppContext) {
|
||||
AssistantProvider::OpenAi {
|
||||
default_model,
|
||||
api_url,
|
||||
low_speed_timeout_in_seconds,
|
||||
},
|
||||
) => {
|
||||
provider.update(
|
||||
default_model.clone(),
|
||||
api_url.clone(),
|
||||
low_speed_timeout_in_seconds.map(Duration::from_secs),
|
||||
settings_version,
|
||||
);
|
||||
}
|
||||
(
|
||||
CompletionProvider::Anthropic(provider),
|
||||
AssistantProvider::Anthropic {
|
||||
default_model,
|
||||
api_url,
|
||||
low_speed_timeout_in_seconds,
|
||||
},
|
||||
) => {
|
||||
provider.update(
|
||||
default_model.clone(),
|
||||
api_url.clone(),
|
||||
low_speed_timeout_in_seconds.map(Duration::from_secs),
|
||||
settings_version,
|
||||
);
|
||||
provider.update(default_model.clone(), api_url.clone(), settings_version);
|
||||
}
|
||||
(
|
||||
CompletionProvider::ZedDotDev(provider),
|
||||
@@ -98,7 +61,7 @@ pub fn init(client: Arc<Client>, cx: &mut AppContext) {
|
||||
) => {
|
||||
provider.update(default_model.clone(), settings_version);
|
||||
}
|
||||
(_, AssistantProvider::ZedDotDev { default_model }) => {
|
||||
(CompletionProvider::OpenAi(_), AssistantProvider::ZedDotDev { default_model }) => {
|
||||
*provider = CompletionProvider::ZedDotDev(ZedDotDevCompletionProvider::new(
|
||||
default_model.clone(),
|
||||
client.clone(),
|
||||
@@ -107,37 +70,21 @@ pub fn init(client: Arc<Client>, cx: &mut AppContext) {
|
||||
));
|
||||
}
|
||||
(
|
||||
_,
|
||||
CompletionProvider::ZedDotDev(_),
|
||||
AssistantProvider::OpenAi {
|
||||
default_model,
|
||||
api_url,
|
||||
low_speed_timeout_in_seconds,
|
||||
},
|
||||
) => {
|
||||
*provider = CompletionProvider::OpenAi(OpenAiCompletionProvider::new(
|
||||
default_model.clone(),
|
||||
api_url.clone(),
|
||||
client.http_client(),
|
||||
low_speed_timeout_in_seconds.map(Duration::from_secs),
|
||||
settings_version,
|
||||
));
|
||||
}
|
||||
(
|
||||
_,
|
||||
AssistantProvider::Anthropic {
|
||||
default_model,
|
||||
api_url,
|
||||
low_speed_timeout_in_seconds,
|
||||
},
|
||||
) => {
|
||||
*provider = CompletionProvider::Anthropic(AnthropicCompletionProvider::new(
|
||||
default_model.clone(),
|
||||
api_url.clone(),
|
||||
client.http_client(),
|
||||
low_speed_timeout_in_seconds.map(Duration::from_secs),
|
||||
settings_version,
|
||||
));
|
||||
}
|
||||
#[cfg(test)]
|
||||
(CompletionProvider::Fake(_), _) => unimplemented!(),
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -146,7 +93,6 @@ pub fn init(client: Arc<Client>, cx: &mut AppContext) {
|
||||
|
||||
pub enum CompletionProvider {
|
||||
OpenAi(OpenAiCompletionProvider),
|
||||
Anthropic(AnthropicCompletionProvider),
|
||||
ZedDotDev(ZedDotDevCompletionProvider),
|
||||
#[cfg(test)]
|
||||
Fake(FakeCompletionProvider),
|
||||
@@ -162,7 +108,6 @@ impl CompletionProvider {
|
||||
pub fn settings_version(&self) -> usize {
|
||||
match self {
|
||||
CompletionProvider::OpenAi(provider) => provider.settings_version(),
|
||||
CompletionProvider::Anthropic(provider) => provider.settings_version(),
|
||||
CompletionProvider::ZedDotDev(provider) => provider.settings_version(),
|
||||
#[cfg(test)]
|
||||
CompletionProvider::Fake(_) => unimplemented!(),
|
||||
@@ -172,7 +117,6 @@ impl CompletionProvider {
|
||||
pub fn is_authenticated(&self) -> bool {
|
||||
match self {
|
||||
CompletionProvider::OpenAi(provider) => provider.is_authenticated(),
|
||||
CompletionProvider::Anthropic(provider) => provider.is_authenticated(),
|
||||
CompletionProvider::ZedDotDev(provider) => provider.is_authenticated(),
|
||||
#[cfg(test)]
|
||||
CompletionProvider::Fake(_) => true,
|
||||
@@ -182,7 +126,6 @@ impl CompletionProvider {
|
||||
pub fn authenticate(&self, cx: &AppContext) -> Task<Result<()>> {
|
||||
match self {
|
||||
CompletionProvider::OpenAi(provider) => provider.authenticate(cx),
|
||||
CompletionProvider::Anthropic(provider) => provider.authenticate(cx),
|
||||
CompletionProvider::ZedDotDev(provider) => provider.authenticate(cx),
|
||||
#[cfg(test)]
|
||||
CompletionProvider::Fake(_) => Task::ready(Ok(())),
|
||||
@@ -192,7 +135,6 @@ impl CompletionProvider {
|
||||
pub fn authentication_prompt(&self, cx: &mut WindowContext) -> AnyView {
|
||||
match self {
|
||||
CompletionProvider::OpenAi(provider) => provider.authentication_prompt(cx),
|
||||
CompletionProvider::Anthropic(provider) => provider.authentication_prompt(cx),
|
||||
CompletionProvider::ZedDotDev(provider) => provider.authentication_prompt(cx),
|
||||
#[cfg(test)]
|
||||
CompletionProvider::Fake(_) => unimplemented!(),
|
||||
@@ -202,7 +144,6 @@ impl CompletionProvider {
|
||||
pub fn reset_credentials(&self, cx: &AppContext) -> Task<Result<()>> {
|
||||
match self {
|
||||
CompletionProvider::OpenAi(provider) => provider.reset_credentials(cx),
|
||||
CompletionProvider::Anthropic(provider) => provider.reset_credentials(cx),
|
||||
CompletionProvider::ZedDotDev(_) => Task::ready(Ok(())),
|
||||
#[cfg(test)]
|
||||
CompletionProvider::Fake(_) => Task::ready(Ok(())),
|
||||
@@ -212,9 +153,6 @@ impl CompletionProvider {
|
||||
pub fn default_model(&self) -> LanguageModel {
|
||||
match self {
|
||||
CompletionProvider::OpenAi(provider) => LanguageModel::OpenAi(provider.default_model()),
|
||||
CompletionProvider::Anthropic(provider) => {
|
||||
LanguageModel::Anthropic(provider.default_model())
|
||||
}
|
||||
CompletionProvider::ZedDotDev(provider) => {
|
||||
LanguageModel::ZedDotDev(provider.default_model())
|
||||
}
|
||||
@@ -230,7 +168,6 @@ impl CompletionProvider {
|
||||
) -> BoxFuture<'static, Result<usize>> {
|
||||
match self {
|
||||
CompletionProvider::OpenAi(provider) => provider.count_tokens(request, cx),
|
||||
CompletionProvider::Anthropic(provider) => provider.count_tokens(request, cx),
|
||||
CompletionProvider::ZedDotDev(provider) => provider.count_tokens(request, cx),
|
||||
#[cfg(test)]
|
||||
CompletionProvider::Fake(_) => unimplemented!(),
|
||||
@@ -243,7 +180,6 @@ impl CompletionProvider {
|
||||
) -> BoxFuture<'static, Result<BoxStream<'static, Result<String>>>> {
|
||||
match self {
|
||||
CompletionProvider::OpenAi(provider) => provider.complete(request),
|
||||
CompletionProvider::Anthropic(provider) => provider.complete(request),
|
||||
CompletionProvider::ZedDotDev(provider) => provider.complete(request),
|
||||
#[cfg(test)]
|
||||
CompletionProvider::Fake(provider) => provider.complete(),
|
||||
|
||||
@@ -1,327 +0,0 @@
|
||||
use crate::count_open_ai_tokens;
|
||||
use crate::{
|
||||
assistant_settings::AnthropicModel, CompletionProvider, LanguageModel, LanguageModelRequest,
|
||||
Role,
|
||||
};
|
||||
use anthropic::{stream_completion, Request, RequestMessage, Role as AnthropicRole};
|
||||
use anyhow::{anyhow, Result};
|
||||
use editor::{Editor, EditorElement, EditorStyle};
|
||||
use futures::{future::BoxFuture, stream::BoxStream, FutureExt, StreamExt};
|
||||
use gpui::{AnyView, AppContext, FontStyle, FontWeight, Task, TextStyle, View, WhiteSpace};
|
||||
use http::HttpClient;
|
||||
use settings::Settings;
|
||||
use std::time::Duration;
|
||||
use std::{env, sync::Arc};
|
||||
use theme::ThemeSettings;
|
||||
use ui::prelude::*;
|
||||
use util::ResultExt;
|
||||
|
||||
pub struct AnthropicCompletionProvider {
|
||||
api_key: Option<String>,
|
||||
api_url: String,
|
||||
default_model: AnthropicModel,
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
low_speed_timeout: Option<Duration>,
|
||||
settings_version: usize,
|
||||
}
|
||||
|
||||
impl AnthropicCompletionProvider {
|
||||
pub fn new(
|
||||
default_model: AnthropicModel,
|
||||
api_url: String,
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
low_speed_timeout: Option<Duration>,
|
||||
settings_version: usize,
|
||||
) -> Self {
|
||||
Self {
|
||||
api_key: None,
|
||||
api_url,
|
||||
default_model,
|
||||
http_client,
|
||||
low_speed_timeout,
|
||||
settings_version,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(
|
||||
&mut self,
|
||||
default_model: AnthropicModel,
|
||||
api_url: String,
|
||||
low_speed_timeout: Option<Duration>,
|
||||
settings_version: usize,
|
||||
) {
|
||||
self.default_model = default_model;
|
||||
self.api_url = api_url;
|
||||
self.low_speed_timeout = low_speed_timeout;
|
||||
self.settings_version = settings_version;
|
||||
}
|
||||
|
||||
pub fn settings_version(&self) -> usize {
|
||||
self.settings_version
|
||||
}
|
||||
|
||||
pub fn is_authenticated(&self) -> bool {
|
||||
self.api_key.is_some()
|
||||
}
|
||||
|
||||
pub fn authenticate(&self, cx: &AppContext) -> Task<Result<()>> {
|
||||
if self.is_authenticated() {
|
||||
Task::ready(Ok(()))
|
||||
} else {
|
||||
let api_url = self.api_url.clone();
|
||||
cx.spawn(|mut cx| async move {
|
||||
let api_key = if let Ok(api_key) = env::var("ANTHROPIC_API_KEY") {
|
||||
api_key
|
||||
} else {
|
||||
let (_, api_key) = cx
|
||||
.update(|cx| cx.read_credentials(&api_url))?
|
||||
.await?
|
||||
.ok_or_else(|| anyhow!("credentials not found"))?;
|
||||
String::from_utf8(api_key)?
|
||||
};
|
||||
cx.update_global::<CompletionProvider, _>(|provider, _cx| {
|
||||
if let CompletionProvider::Anthropic(provider) = provider {
|
||||
provider.api_key = Some(api_key);
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset_credentials(&self, cx: &AppContext) -> Task<Result<()>> {
|
||||
let delete_credentials = cx.delete_credentials(&self.api_url);
|
||||
cx.spawn(|mut cx| async move {
|
||||
delete_credentials.await.log_err();
|
||||
cx.update_global::<CompletionProvider, _>(|provider, _cx| {
|
||||
if let CompletionProvider::Anthropic(provider) = provider {
|
||||
provider.api_key = None;
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn authentication_prompt(&self, cx: &mut WindowContext) -> AnyView {
|
||||
cx.new_view(|cx| AuthenticationPrompt::new(self.api_url.clone(), cx))
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn default_model(&self) -> AnthropicModel {
|
||||
self.default_model.clone()
|
||||
}
|
||||
|
||||
pub fn count_tokens(
|
||||
&self,
|
||||
request: LanguageModelRequest,
|
||||
cx: &AppContext,
|
||||
) -> BoxFuture<'static, Result<usize>> {
|
||||
count_open_ai_tokens(request, cx.background_executor())
|
||||
}
|
||||
|
||||
pub fn complete(
|
||||
&self,
|
||||
request: LanguageModelRequest,
|
||||
) -> BoxFuture<'static, Result<BoxStream<'static, Result<String>>>> {
|
||||
let request = self.to_anthropic_request(request);
|
||||
|
||||
let http_client = self.http_client.clone();
|
||||
let api_key = self.api_key.clone();
|
||||
let api_url = self.api_url.clone();
|
||||
let low_speed_timeout = self.low_speed_timeout;
|
||||
async move {
|
||||
let api_key = api_key.ok_or_else(|| anyhow!("missing api key"))?;
|
||||
let request = stream_completion(
|
||||
http_client.as_ref(),
|
||||
&api_url,
|
||||
&api_key,
|
||||
request,
|
||||
low_speed_timeout,
|
||||
);
|
||||
let response = request.await?;
|
||||
let stream = response
|
||||
.filter_map(|response| async move {
|
||||
match response {
|
||||
Ok(response) => match response {
|
||||
anthropic::ResponseEvent::ContentBlockStart {
|
||||
content_block, ..
|
||||
} => match content_block {
|
||||
anthropic::ContentBlock::Text { text } => Some(Ok(text)),
|
||||
},
|
||||
anthropic::ResponseEvent::ContentBlockDelta { delta, .. } => {
|
||||
match delta {
|
||||
anthropic::TextDelta::TextDelta { text } => Some(Ok(text)),
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
Err(error) => Some(Err(error)),
|
||||
}
|
||||
})
|
||||
.boxed();
|
||||
Ok(stream)
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn to_anthropic_request(&self, request: LanguageModelRequest) -> Request {
|
||||
let model = match request.model {
|
||||
LanguageModel::Anthropic(model) => model,
|
||||
_ => self.default_model(),
|
||||
};
|
||||
|
||||
let mut system_message = String::new();
|
||||
|
||||
let mut messages: Vec<RequestMessage> = Vec::new();
|
||||
for message in request.messages {
|
||||
if message.content.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
match message.role {
|
||||
Role::User | Role::Assistant => {
|
||||
let role = match message.role {
|
||||
Role::User => AnthropicRole::User,
|
||||
Role::Assistant => AnthropicRole::Assistant,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
if let Some(last_message) = messages.last_mut() {
|
||||
if last_message.role == role {
|
||||
last_message.content.push_str("\n\n");
|
||||
last_message.content.push_str(&message.content);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
messages.push(RequestMessage {
|
||||
role,
|
||||
content: message.content,
|
||||
});
|
||||
}
|
||||
Role::System => {
|
||||
if !system_message.is_empty() {
|
||||
system_message.push_str("\n\n");
|
||||
}
|
||||
system_message.push_str(&message.content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Request {
|
||||
model,
|
||||
messages,
|
||||
stream: true,
|
||||
system: system_message,
|
||||
max_tokens: 4092,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AuthenticationPrompt {
|
||||
api_key: View<Editor>,
|
||||
api_url: String,
|
||||
}
|
||||
|
||||
impl AuthenticationPrompt {
|
||||
fn new(api_url: String, cx: &mut WindowContext) -> Self {
|
||||
Self {
|
||||
api_key: cx.new_view(|cx| {
|
||||
let mut editor = Editor::single_line(cx);
|
||||
editor.set_placeholder_text(
|
||||
"sk-000000000000000000000000000000000000000000000000",
|
||||
cx,
|
||||
);
|
||||
editor
|
||||
}),
|
||||
api_url,
|
||||
}
|
||||
}
|
||||
|
||||
fn save_api_key(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
|
||||
let api_key = self.api_key.read(cx).text(cx);
|
||||
if api_key.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let write_credentials = cx.write_credentials(&self.api_url, "Bearer", api_key.as_bytes());
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
write_credentials.await?;
|
||||
cx.update_global::<CompletionProvider, _>(|provider, _cx| {
|
||||
if let CompletionProvider::Anthropic(provider) = provider {
|
||||
provider.api_key = Some(api_key);
|
||||
}
|
||||
})
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
|
||||
fn render_api_key_editor(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
let settings = ThemeSettings::get_global(cx);
|
||||
let text_style = TextStyle {
|
||||
color: cx.theme().colors().text,
|
||||
font_family: settings.ui_font.family.clone(),
|
||||
font_features: settings.ui_font.features.clone(),
|
||||
font_size: rems(0.875).into(),
|
||||
font_weight: FontWeight::NORMAL,
|
||||
font_style: FontStyle::Normal,
|
||||
line_height: relative(1.3),
|
||||
background_color: None,
|
||||
underline: None,
|
||||
strikethrough: None,
|
||||
white_space: WhiteSpace::Normal,
|
||||
};
|
||||
EditorElement::new(
|
||||
&self.api_key,
|
||||
EditorStyle {
|
||||
background: cx.theme().colors().editor_background,
|
||||
local_player: cx.theme().players().local(),
|
||||
text: text_style,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for AuthenticationPrompt {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
const INSTRUCTIONS: [&str; 4] = [
|
||||
"To use the assistant panel or inline assistant, you need to add your Anthropic API key.",
|
||||
"You can create an API key at: https://console.anthropic.com/settings/keys",
|
||||
"",
|
||||
"Paste your Anthropic API key below and hit enter to use the assistant:",
|
||||
];
|
||||
|
||||
v_flex()
|
||||
.p_4()
|
||||
.size_full()
|
||||
.on_action(cx.listener(Self::save_api_key))
|
||||
.children(
|
||||
INSTRUCTIONS.map(|instruction| Label::new(instruction).size(LabelSize::Small)),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.w_full()
|
||||
.my_2()
|
||||
.px_2()
|
||||
.py_1()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.rounded_md()
|
||||
.child(self.render_api_key_editor(cx)),
|
||||
)
|
||||
.child(
|
||||
Label::new(
|
||||
"You can also assign the ANTHROPIC_API_KEY environment variable and restart Zed.",
|
||||
)
|
||||
.size(LabelSize::Small),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.child(Label::new("Click on").size(LabelSize::Small))
|
||||
.child(Icon::new(IconName::Ai).size(IconSize::XSmall))
|
||||
.child(
|
||||
Label::new("in the status bar to close this panel.").size(LabelSize::Small),
|
||||
),
|
||||
)
|
||||
.into_any()
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
use crate::assistant_settings::ZedDotDevModel;
|
||||
use crate::{
|
||||
assistant_settings::OpenAiModel, CompletionProvider, LanguageModel, LanguageModelRequest, Role,
|
||||
};
|
||||
@@ -6,21 +5,18 @@ use anyhow::{anyhow, Result};
|
||||
use editor::{Editor, EditorElement, EditorStyle};
|
||||
use futures::{future::BoxFuture, stream::BoxStream, FutureExt, StreamExt};
|
||||
use gpui::{AnyView, AppContext, FontStyle, FontWeight, Task, TextStyle, View, WhiteSpace};
|
||||
use http::HttpClient;
|
||||
use open_ai::{stream_completion, Request, RequestMessage, Role as OpenAiRole};
|
||||
use settings::Settings;
|
||||
use std::time::Duration;
|
||||
use std::{env, sync::Arc};
|
||||
use theme::ThemeSettings;
|
||||
use ui::prelude::*;
|
||||
use util::ResultExt;
|
||||
use util::{http::HttpClient, ResultExt};
|
||||
|
||||
pub struct OpenAiCompletionProvider {
|
||||
api_key: Option<String>,
|
||||
api_url: String,
|
||||
default_model: OpenAiModel,
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
low_speed_timeout: Option<Duration>,
|
||||
settings_version: usize,
|
||||
}
|
||||
|
||||
@@ -29,7 +25,6 @@ impl OpenAiCompletionProvider {
|
||||
default_model: OpenAiModel,
|
||||
api_url: String,
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
low_speed_timeout: Option<Duration>,
|
||||
settings_version: usize,
|
||||
) -> Self {
|
||||
Self {
|
||||
@@ -37,21 +32,13 @@ impl OpenAiCompletionProvider {
|
||||
api_url,
|
||||
default_model,
|
||||
http_client,
|
||||
low_speed_timeout,
|
||||
settings_version,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(
|
||||
&mut self,
|
||||
default_model: OpenAiModel,
|
||||
api_url: String,
|
||||
low_speed_timeout: Option<Duration>,
|
||||
settings_version: usize,
|
||||
) {
|
||||
pub fn update(&mut self, default_model: OpenAiModel, api_url: String, settings_version: usize) {
|
||||
self.default_model = default_model;
|
||||
self.api_url = api_url;
|
||||
self.low_speed_timeout = low_speed_timeout;
|
||||
self.settings_version = settings_version;
|
||||
}
|
||||
|
||||
@@ -125,16 +112,9 @@ impl OpenAiCompletionProvider {
|
||||
let http_client = self.http_client.clone();
|
||||
let api_key = self.api_key.clone();
|
||||
let api_url = self.api_url.clone();
|
||||
let low_speed_timeout = self.low_speed_timeout;
|
||||
async move {
|
||||
let api_key = api_key.ok_or_else(|| anyhow!("missing api key"))?;
|
||||
let request = stream_completion(
|
||||
http_client.as_ref(),
|
||||
&api_url,
|
||||
&api_key,
|
||||
request,
|
||||
low_speed_timeout,
|
||||
);
|
||||
let request = stream_completion(http_client.as_ref(), &api_url, &api_key, request);
|
||||
let response = request.await?;
|
||||
let stream = response
|
||||
.filter_map(|response| async move {
|
||||
@@ -151,8 +131,8 @@ impl OpenAiCompletionProvider {
|
||||
|
||||
fn to_open_ai_request(&self, request: LanguageModelRequest) -> Request {
|
||||
let model = match request.model {
|
||||
LanguageModel::ZedDotDev(_) => self.default_model(),
|
||||
LanguageModel::OpenAi(model) => model,
|
||||
_ => self.default_model(),
|
||||
};
|
||||
|
||||
Request {
|
||||
@@ -203,17 +183,7 @@ pub fn count_open_ai_tokens(
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
match request.model {
|
||||
LanguageModel::Anthropic(_)
|
||||
| LanguageModel::ZedDotDev(ZedDotDevModel::Claude3Opus)
|
||||
| LanguageModel::ZedDotDev(ZedDotDevModel::Claude3Sonnet)
|
||||
| LanguageModel::ZedDotDev(ZedDotDevModel::Claude3Haiku) => {
|
||||
// Tiktoken doesn't yet support these models, so we manually use the
|
||||
// same tokenizer as GPT-4.
|
||||
tiktoken_rs::num_tokens_from_messages("gpt-4", &messages)
|
||||
}
|
||||
_ => tiktoken_rs::num_tokens_from_messages(request.model.id(), &messages),
|
||||
}
|
||||
tiktoken_rs::num_tokens_from_messages(request.model.id(), &messages)
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
|
||||
@@ -78,9 +78,9 @@ impl ZedDotDevCompletionProvider {
|
||||
cx: &AppContext,
|
||||
) -> BoxFuture<'static, Result<usize>> {
|
||||
match request.model {
|
||||
LanguageModel::OpenAi(_) => future::ready(Err(anyhow!("invalid model"))).boxed(),
|
||||
LanguageModel::ZedDotDev(ZedDotDevModel::Gpt4)
|
||||
| LanguageModel::ZedDotDev(ZedDotDevModel::Gpt4Turbo)
|
||||
| LanguageModel::ZedDotDev(ZedDotDevModel::Gpt4Omni)
|
||||
| LanguageModel::ZedDotDev(ZedDotDevModel::Gpt3Point5Turbo) => {
|
||||
count_open_ai_tokens(request, cx.background_executor())
|
||||
}
|
||||
@@ -107,7 +107,6 @@ impl ZedDotDevCompletionProvider {
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
_ => future::ready(Err(anyhow!("invalid model"))).boxed(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
91
crates/assistant/src/embedded_scope.rs
Normal file
91
crates/assistant/src/embedded_scope.rs
Normal file
@@ -0,0 +1,91 @@
|
||||
use editor::MultiBuffer;
|
||||
use gpui::{AppContext, Model, ModelContext, Subscription};
|
||||
|
||||
use crate::{assistant_panel::Conversation, LanguageModelRequestMessage, Role};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct EmbeddedScope {
|
||||
active_buffer: Option<Model<MultiBuffer>>,
|
||||
active_buffer_enabled: bool,
|
||||
active_buffer_subscription: Option<Subscription>,
|
||||
}
|
||||
|
||||
impl EmbeddedScope {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
active_buffer: None,
|
||||
active_buffer_enabled: true,
|
||||
active_buffer_subscription: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_active_buffer(
|
||||
&mut self,
|
||||
buffer: Option<Model<MultiBuffer>>,
|
||||
cx: &mut ModelContext<Conversation>,
|
||||
) {
|
||||
self.active_buffer_subscription.take();
|
||||
|
||||
if let Some(active_buffer) = buffer.clone() {
|
||||
self.active_buffer_subscription =
|
||||
Some(cx.subscribe(&active_buffer, |conversation, _, e, cx| {
|
||||
if let multi_buffer::Event::Edited { .. } = e {
|
||||
conversation.count_remaining_tokens(cx)
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
self.active_buffer = buffer;
|
||||
}
|
||||
|
||||
pub fn active_buffer(&self) -> Option<&Model<MultiBuffer>> {
|
||||
self.active_buffer.as_ref()
|
||||
}
|
||||
|
||||
pub fn active_buffer_enabled(&self) -> bool {
|
||||
self.active_buffer_enabled
|
||||
}
|
||||
|
||||
pub fn set_active_buffer_enabled(&mut self, enabled: bool) {
|
||||
self.active_buffer_enabled = enabled;
|
||||
}
|
||||
|
||||
/// Provide a message for the language model based on the active buffer.
|
||||
pub fn message(&self, cx: &AppContext) -> Option<LanguageModelRequestMessage> {
|
||||
if !self.active_buffer_enabled {
|
||||
return None;
|
||||
}
|
||||
|
||||
let active_buffer = self.active_buffer.as_ref()?;
|
||||
let buffer = active_buffer.read(cx);
|
||||
|
||||
if let Some(singleton) = buffer.as_singleton() {
|
||||
let singleton = singleton.read(cx);
|
||||
|
||||
let filename = singleton
|
||||
.file()
|
||||
.map(|file| file.path().to_string_lossy())
|
||||
.unwrap_or("Untitled".into());
|
||||
|
||||
let text = singleton.text();
|
||||
|
||||
let language = singleton
|
||||
.language()
|
||||
.map(|l| {
|
||||
let name = l.code_fence_block_name();
|
||||
name.to_string()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let markdown =
|
||||
format!("User's active file `{filename}`:\n\n```{language}\n{text}```\n\n");
|
||||
|
||||
return Some(LanguageModelRequestMessage {
|
||||
role: Role::System,
|
||||
content: markdown,
|
||||
});
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -23,18 +23,17 @@ chrono.workspace = true
|
||||
collections.workspace = true
|
||||
editor.workspace = true
|
||||
feature_flags.workspace = true
|
||||
file_icons.workspace = true
|
||||
fs.workspace = true
|
||||
futures.workspace = true
|
||||
fuzzy.workspace = true
|
||||
gpui.workspace = true
|
||||
language.workspace = true
|
||||
log.workspace = true
|
||||
markdown.workspace = true
|
||||
open_ai.workspace = true
|
||||
picker.workspace = true
|
||||
project.workspace = true
|
||||
regex.workspace = true
|
||||
rich_text.workspace = true
|
||||
schemars.workspace = true
|
||||
semantic_index.workspace = true
|
||||
serde.workspace = true
|
||||
@@ -44,7 +43,6 @@ story = { workspace = true, optional = true }
|
||||
theme.workspace = true
|
||||
ui.workspace = true
|
||||
util.workspace = true
|
||||
unindent.workspace = true
|
||||
workspace.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
@@ -54,7 +52,6 @@ env_logger.workspace = true
|
||||
gpui = { workspace = true, features = ["test-support"] }
|
||||
language = { workspace = true, features = ["test-support"] }
|
||||
languages.workspace = true
|
||||
markdown = { workspace = true, features = ["test-support"] }
|
||||
node_runtime.workspace = true
|
||||
project = { workspace = true, features = ["test-support"] }
|
||||
rand.workspace = true
|
||||
@@ -62,5 +59,4 @@ release_channel.workspace = true
|
||||
settings = { workspace = true, features = ["test-support"] }
|
||||
theme = { workspace = true, features = ["test-support"] }
|
||||
util = { workspace = true, features = ["test-support"] }
|
||||
http = { workspace = true, features = ["test-support"] }
|
||||
workspace = { workspace = true, features = ["test-support"] }
|
||||
|
||||
@@ -19,7 +19,6 @@ use collections::HashMap;
|
||||
use completion_provider::*;
|
||||
use editor::Editor;
|
||||
use feature_flags::FeatureFlagAppExt as _;
|
||||
use file_icons::FileIcons;
|
||||
use fs::Fs;
|
||||
use futures::{future::join_all, StreamExt};
|
||||
use gpui::{
|
||||
@@ -27,8 +26,8 @@ use gpui::{
|
||||
FocusableView, ListAlignment, ListState, Model, Render, Task, View, WeakView,
|
||||
};
|
||||
use language::{language_settings::SoftWrap, LanguageRegistry};
|
||||
use markdown::{Markdown, MarkdownStyle};
|
||||
use open_ai::{FunctionContent, ToolCall, ToolCallContent};
|
||||
use rich_text::RichText;
|
||||
use saved_conversation::{SavedAssistantMessagePart, SavedChatMessage, SavedConversation};
|
||||
use saved_conversations::SavedConversations;
|
||||
use semantic_index::{CloudEmbeddingProvider, ProjectIndex, ProjectIndexDebugView, SemanticIndex};
|
||||
@@ -49,18 +48,7 @@ pub use assistant_settings::AssistantSettings;
|
||||
const MAX_COMPLETION_CALLS_PER_SUBMISSION: usize = 5;
|
||||
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Deserialize)]
|
||||
pub struct Submit(SubmitMode);
|
||||
|
||||
/// There are multiple different ways to submit a model request, represented by this enum.
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Deserialize)]
|
||||
pub enum SubmitMode {
|
||||
/// Only include the conversation.
|
||||
Simple,
|
||||
/// Send the current file as context.
|
||||
CurrentFile,
|
||||
/// Search the codebase and send relevant excerpts.
|
||||
Codebase,
|
||||
}
|
||||
pub struct Submit;
|
||||
|
||||
gpui::actions!(assistant2, [Cancel, ToggleFocus, DebugProjectIndex,]);
|
||||
gpui::impl_actions!(assistant2, [Submit]);
|
||||
@@ -128,12 +116,6 @@ impl AssistantPanel {
|
||||
semantic_index.project_index(project.clone(), cx)
|
||||
});
|
||||
|
||||
// Used in tools to render file icons
|
||||
cx.observe_global::<FileIcons>(|_, cx| {
|
||||
cx.notify();
|
||||
})
|
||||
.detach();
|
||||
|
||||
let mut tool_registry = ToolRegistry::new();
|
||||
tool_registry
|
||||
.register(ProjectIndexTool::new(project_index.clone()))
|
||||
@@ -268,11 +250,11 @@ pub struct AssistantChat {
|
||||
tool_registry: Arc<ToolRegistry>,
|
||||
attachment_registry: Arc<AttachmentRegistry>,
|
||||
project_index: Model<ProjectIndex>,
|
||||
markdown_style: MarkdownStyle,
|
||||
}
|
||||
|
||||
struct EditingMessage {
|
||||
id: MessageId,
|
||||
old_body: Arc<str>,
|
||||
body: View<Editor>,
|
||||
}
|
||||
|
||||
@@ -355,49 +337,21 @@ impl AssistantChat {
|
||||
pending_completion: None,
|
||||
attachment_registry,
|
||||
tool_registry,
|
||||
markdown_style: MarkdownStyle {
|
||||
code_block: gpui::TextStyleRefinement {
|
||||
font_family: Some("Zed Mono".into()),
|
||||
color: Some(cx.theme().colors().editor_foreground),
|
||||
background_color: Some(cx.theme().colors().editor_background),
|
||||
..Default::default()
|
||||
},
|
||||
inline_code: gpui::TextStyleRefinement {
|
||||
font_family: Some("Zed Mono".into()),
|
||||
// @nate: Could we add inline-code specific styles to the theme?
|
||||
color: Some(cx.theme().colors().editor_foreground),
|
||||
background_color: Some(cx.theme().colors().editor_background),
|
||||
..Default::default()
|
||||
},
|
||||
rule_color: Color::Muted.color(cx),
|
||||
block_quote_border_color: Color::Muted.color(cx),
|
||||
block_quote: gpui::TextStyleRefinement {
|
||||
color: Some(Color::Muted.color(cx)),
|
||||
..Default::default()
|
||||
},
|
||||
link: gpui::TextStyleRefinement {
|
||||
color: Some(Color::Accent.color(cx)),
|
||||
underline: Some(gpui::UnderlineStyle {
|
||||
thickness: px(1.),
|
||||
color: Some(Color::Accent.color(cx)),
|
||||
wavy: false,
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
syntax: cx.theme().syntax().clone(),
|
||||
selection_background_color: {
|
||||
let mut selection = cx.theme().players().local().selection;
|
||||
selection.fade_out(0.7);
|
||||
selection
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn message_for_id(&self, id: MessageId) -> Option<&ChatMessage> {
|
||||
self.messages.iter().find(|message| match message {
|
||||
ChatMessage::User(message) => message.id == id,
|
||||
ChatMessage::Assistant(message) => message.id == id,
|
||||
fn editing_message_id(&self) -> Option<MessageId> {
|
||||
self.editing_message.as_ref().map(|message| message.id)
|
||||
}
|
||||
|
||||
fn focused_message_id(&self, cx: &WindowContext) -> Option<MessageId> {
|
||||
self.messages.iter().find_map(|message| match message {
|
||||
ChatMessage::User(message) => message
|
||||
.body
|
||||
.focus_handle(cx)
|
||||
.contains_focused(cx)
|
||||
.then_some(message.id),
|
||||
ChatMessage::Assistant(_) => None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -407,8 +361,10 @@ impl AssistantChat {
|
||||
|
||||
fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
|
||||
// If we're currently editing a message, cancel the edit.
|
||||
if self.editing_message.take().is_some() {
|
||||
cx.notify();
|
||||
if let Some(editing_message) = self.editing_message.take() {
|
||||
editing_message
|
||||
.body
|
||||
.update(cx, |body, cx| body.set_text(editing_message.old_body, cx));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -424,8 +380,15 @@ impl AssistantChat {
|
||||
cx.propagate();
|
||||
}
|
||||
|
||||
fn submit(&mut self, Submit(mode): &Submit, cx: &mut ViewContext<Self>) {
|
||||
if self.composer_editor.focus_handle(cx).is_focused(cx) {
|
||||
fn submit(&mut self, _: &Submit, cx: &mut ViewContext<Self>) {
|
||||
if let Some(focused_message_id) = self.focused_message_id(cx) {
|
||||
self.truncate_messages(focused_message_id, cx);
|
||||
self.pending_completion.take();
|
||||
self.composer_editor.focus_handle(cx).focus(cx);
|
||||
if self.editing_message_id() == Some(focused_message_id) {
|
||||
self.editing_message.take();
|
||||
}
|
||||
} else if self.composer_editor.focus_handle(cx).is_focused(cx) {
|
||||
// Don't allow multiple concurrent completions.
|
||||
if self.pending_completion.is_some() {
|
||||
cx.propagate();
|
||||
@@ -436,12 +399,10 @@ impl AssistantChat {
|
||||
let text = composer_editor.text(cx);
|
||||
let id = self.next_message_id.post_inc();
|
||||
let body = cx.new_view(|cx| {
|
||||
Markdown::new(
|
||||
text,
|
||||
self.markdown_style.clone(),
|
||||
self.language_registry.clone(),
|
||||
cx,
|
||||
)
|
||||
let mut editor = Editor::auto_height(80, cx);
|
||||
editor.set_text(text, cx);
|
||||
editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx);
|
||||
editor
|
||||
});
|
||||
composer_editor.clear(cx);
|
||||
|
||||
@@ -452,32 +413,11 @@ impl AssistantChat {
|
||||
})
|
||||
});
|
||||
self.push_message(message, cx);
|
||||
} else if let Some(editing_message) = self.editing_message.as_ref() {
|
||||
let focus_handle = editing_message.body.focus_handle(cx);
|
||||
if focus_handle.contains_focused(cx) {
|
||||
if let Some(ChatMessage::User(user_message)) =
|
||||
self.message_for_id(editing_message.id)
|
||||
{
|
||||
user_message.body.update(cx, |body, cx| {
|
||||
body.reset(editing_message.body.read(cx).text(cx), cx);
|
||||
});
|
||||
}
|
||||
|
||||
self.truncate_messages(editing_message.id, cx);
|
||||
|
||||
self.pending_completion.take();
|
||||
self.composer_editor.focus_handle(cx).focus(cx);
|
||||
self.editing_message.take();
|
||||
} else {
|
||||
log::error!("unexpected state: no user message editor is focused.");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
log::error!("unexpected state: no user message editor is focused.");
|
||||
return;
|
||||
}
|
||||
|
||||
let mode = *mode;
|
||||
self.pending_completion = Some(cx.spawn(move |this, mut cx| async move {
|
||||
let attachments_task = this.update(&mut cx, |this, cx| {
|
||||
let attachment_registry = this.attachment_registry.clone();
|
||||
@@ -502,14 +442,9 @@ impl AssistantChat {
|
||||
})
|
||||
.log_err();
|
||||
|
||||
Self::request_completion(
|
||||
this.clone(),
|
||||
mode,
|
||||
MAX_COMPLETION_CALLS_PER_SUBMISSION,
|
||||
&mut cx,
|
||||
)
|
||||
.await
|
||||
.log_err();
|
||||
Self::request_completion(this.clone(), MAX_COMPLETION_CALLS_PER_SUBMISSION, &mut cx)
|
||||
.await
|
||||
.log_err();
|
||||
|
||||
this.update(&mut cx, |this, _cx| {
|
||||
this.pending_completion = None;
|
||||
@@ -521,7 +456,6 @@ impl AssistantChat {
|
||||
|
||||
async fn request_completion(
|
||||
this: WeakView<Self>,
|
||||
mode: SubmitMode,
|
||||
limit: usize,
|
||||
cx: &mut AsyncWindowContext,
|
||||
) -> Result<()> {
|
||||
@@ -531,9 +465,7 @@ impl AssistantChat {
|
||||
let (tool_definitions, model_name, messages) = this.update(cx, |this, cx| {
|
||||
this.push_new_assistant_message(cx);
|
||||
|
||||
let definitions = if call_count < limit
|
||||
&& matches!(mode, SubmitMode::Codebase | SubmitMode::Simple)
|
||||
{
|
||||
let definitions = if call_count < limit {
|
||||
this.tool_registry.definitions()
|
||||
} else {
|
||||
Vec::new()
|
||||
@@ -560,6 +492,7 @@ impl AssistantChat {
|
||||
});
|
||||
|
||||
let mut stream = completion?.await?;
|
||||
let mut body = String::new();
|
||||
while let Some(delta) = stream.next().await {
|
||||
let delta = delta?;
|
||||
this.update(cx, |this, cx| {
|
||||
@@ -568,14 +501,7 @@ impl AssistantChat {
|
||||
{
|
||||
if messages.is_empty() {
|
||||
messages.push(AssistantMessagePart {
|
||||
body: cx.new_view(|cx| {
|
||||
Markdown::new(
|
||||
"".into(),
|
||||
this.markdown_style.clone(),
|
||||
this.language_registry.clone(),
|
||||
cx,
|
||||
)
|
||||
}),
|
||||
body: RichText::default(),
|
||||
tool_calls: Vec::new(),
|
||||
})
|
||||
}
|
||||
@@ -583,9 +509,7 @@ impl AssistantChat {
|
||||
let message = messages.last_mut().unwrap();
|
||||
|
||||
if let Some(content) = &delta.content {
|
||||
message
|
||||
.body
|
||||
.update(cx, |message, cx| message.append(&content, cx));
|
||||
body.push_str(content);
|
||||
}
|
||||
|
||||
for tool_call_delta in delta.tool_calls {
|
||||
@@ -614,6 +538,8 @@ impl AssistantChat {
|
||||
}
|
||||
}
|
||||
|
||||
message.body =
|
||||
RichText::new(body.clone(), &[], &this.language_registry);
|
||||
cx.notify();
|
||||
} else {
|
||||
unreachable!()
|
||||
@@ -662,14 +588,7 @@ impl AssistantChat {
|
||||
self.messages.last_mut()
|
||||
{
|
||||
messages.push(AssistantMessagePart {
|
||||
body: cx.new_view(|cx| {
|
||||
Markdown::new(
|
||||
"".into(),
|
||||
self.markdown_style.clone(),
|
||||
self.language_registry.clone(),
|
||||
cx,
|
||||
)
|
||||
}),
|
||||
body: RichText::default(),
|
||||
tool_calls: Vec::new(),
|
||||
});
|
||||
return;
|
||||
@@ -678,14 +597,7 @@ impl AssistantChat {
|
||||
let message = ChatMessage::Assistant(AssistantMessage {
|
||||
id: self.next_message_id.post_inc(),
|
||||
messages: vec![AssistantMessagePart {
|
||||
body: cx.new_view(|cx| {
|
||||
Markdown::new(
|
||||
"".into(),
|
||||
self.markdown_style.clone(),
|
||||
self.language_registry.clone(),
|
||||
cx,
|
||||
)
|
||||
}),
|
||||
body: RichText::default(),
|
||||
tool_calls: Vec::new(),
|
||||
}],
|
||||
error: None,
|
||||
@@ -828,69 +740,66 @@ impl AssistantChat {
|
||||
.id(SharedString::from(format!("message-{}-container", id.0)))
|
||||
.when(is_first, |this| this.pt(padding))
|
||||
.map(|element| {
|
||||
if let Some(editing_message) = self.editing_message.as_ref() {
|
||||
if editing_message.id == *id {
|
||||
return element.child(Composer::new(
|
||||
editing_message.body.clone(),
|
||||
self.project_index_button.clone(),
|
||||
self.active_file_button.clone(),
|
||||
crate::ui::ModelSelector::new(
|
||||
cx.view().downgrade(),
|
||||
self.model.clone(),
|
||||
)
|
||||
.into_any_element(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
element
|
||||
.on_click(cx.listener({
|
||||
let id = *id;
|
||||
let body = body.clone();
|
||||
move |assistant_chat, event: &ClickEvent, cx| {
|
||||
if event.up.click_count == 2 {
|
||||
let body = cx.new_view(|cx| {
|
||||
let mut editor = Editor::auto_height(80, cx);
|
||||
let source = Arc::from(body.read(cx).source());
|
||||
editor.set_text(source, cx);
|
||||
editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx);
|
||||
editor
|
||||
});
|
||||
assistant_chat.editing_message = Some(EditingMessage {
|
||||
id,
|
||||
body: body.clone(),
|
||||
});
|
||||
body.focus_handle(cx).focus(cx);
|
||||
}
|
||||
}
|
||||
}))
|
||||
.child(
|
||||
crate::ui::ChatMessage::new(
|
||||
*id,
|
||||
UserOrAssistant::User(self.user_store.read(cx).current_user()),
|
||||
// todo!(): clean up the vec usage
|
||||
vec![
|
||||
body.clone().into_any_element(),
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.children(
|
||||
attachments
|
||||
.iter()
|
||||
.map(|attachment| attachment.view.clone()),
|
||||
)
|
||||
.into_any_element(),
|
||||
],
|
||||
self.is_message_collapsed(id),
|
||||
Box::new(cx.listener({
|
||||
let id = *id;
|
||||
move |assistant_chat, _event, _cx| {
|
||||
assistant_chat.toggle_message_collapsed(id)
|
||||
}
|
||||
})),
|
||||
if self.editing_message_id() == Some(*id) {
|
||||
element.child(Composer::new(
|
||||
body.clone(),
|
||||
self.project_index_button.clone(),
|
||||
self.active_file_button.clone(),
|
||||
crate::ui::ModelSelector::new(
|
||||
cx.view().downgrade(),
|
||||
self.model.clone(),
|
||||
)
|
||||
// TODO: Wire up selections.
|
||||
.selected(is_last),
|
||||
)
|
||||
.into_any_element(),
|
||||
))
|
||||
} else {
|
||||
element
|
||||
.on_click(cx.listener({
|
||||
let id = *id;
|
||||
let body = body.clone();
|
||||
move |assistant_chat, event: &ClickEvent, cx| {
|
||||
if event.up.click_count == 2 {
|
||||
assistant_chat.editing_message = Some(EditingMessage {
|
||||
id,
|
||||
body: body.clone(),
|
||||
old_body: body.read(cx).text(cx).into(),
|
||||
});
|
||||
body.focus_handle(cx).focus(cx);
|
||||
}
|
||||
}
|
||||
}))
|
||||
.child(
|
||||
crate::ui::ChatMessage::new(
|
||||
*id,
|
||||
UserOrAssistant::User(self.user_store.read(cx).current_user()),
|
||||
// todo!(): clean up the vec usage
|
||||
vec![
|
||||
RichText::new(
|
||||
body.read(cx).text(cx),
|
||||
&[],
|
||||
&self.language_registry,
|
||||
)
|
||||
.element(ElementId::from(id.0), cx),
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.children(
|
||||
attachments
|
||||
.iter()
|
||||
.map(|attachment| attachment.view.clone()),
|
||||
)
|
||||
.into_any_element(),
|
||||
],
|
||||
self.is_message_collapsed(id),
|
||||
Box::new(cx.listener({
|
||||
let id = *id;
|
||||
move |assistant_chat, _event, _cx| {
|
||||
assistant_chat.toggle_message_collapsed(id)
|
||||
}
|
||||
})),
|
||||
)
|
||||
// TODO: Wire up selections.
|
||||
.selected(is_last),
|
||||
)
|
||||
}
|
||||
})
|
||||
.into_any(),
|
||||
ChatMessage::Assistant(AssistantMessage {
|
||||
@@ -902,8 +811,13 @@ impl AssistantChat {
|
||||
let mut message_elements = Vec::new();
|
||||
|
||||
for message in messages {
|
||||
if !message.body.read(cx).source().is_empty() {
|
||||
message_elements.push(div().child(message.body.clone()).into_any())
|
||||
if !message.body.text.is_empty() {
|
||||
message_elements.push(
|
||||
div()
|
||||
// todo!(): The element Id will need to be a combo of the base ID and the index within the grouping
|
||||
.child(message.body.element(ElementId::from(id.0), cx))
|
||||
.into_any_element(),
|
||||
)
|
||||
}
|
||||
|
||||
let tools = message
|
||||
@@ -913,7 +827,7 @@ impl AssistantChat {
|
||||
.collect::<Vec<AnyElement>>();
|
||||
|
||||
if !tools.is_empty() {
|
||||
message_elements.push(div().children(tools).into_any())
|
||||
message_elements.push(div().children(tools).into_any_element())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -953,6 +867,21 @@ impl AssistantChat {
|
||||
let mut project_context = ProjectContext::new(project, fs);
|
||||
let mut completion_messages = Vec::new();
|
||||
|
||||
completion_messages.push(CompletionMessage::System {
|
||||
content: r#"
|
||||
You are the assistant for the Zed code editor.
|
||||
Your job is to help the user understand and modify their own code.
|
||||
Use tools to retrieve the information needed to give answers that are
|
||||
specific to the user's codebase. Do NOT give generic answers that are
|
||||
not specific to the user's codebase.
|
||||
Whenever possible, use tools to display code in the editor.
|
||||
"#
|
||||
.lines()
|
||||
.map(|line| line.trim_start())
|
||||
.filter(|line| !line.is_empty())
|
||||
.collect(),
|
||||
});
|
||||
|
||||
for message in &self.messages {
|
||||
match message {
|
||||
ChatMessage::User(UserMessage {
|
||||
@@ -966,14 +895,14 @@ impl AssistantChat {
|
||||
|
||||
// Show user's message last so that the assistant is grounded in the user's request
|
||||
completion_messages.push(CompletionMessage::User {
|
||||
content: body.read(cx).source().to_string(),
|
||||
content: body.read(cx).text(cx),
|
||||
});
|
||||
}
|
||||
ChatMessage::Assistant(AssistantMessage { messages, .. }) => {
|
||||
for message in messages {
|
||||
let body = message.body.clone();
|
||||
|
||||
if body.read(cx).source().is_empty() && message.tool_calls.is_empty() {
|
||||
if body.text.is_empty() && message.tool_calls.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -992,7 +921,7 @@ impl AssistantChat {
|
||||
.collect();
|
||||
|
||||
completion_messages.push(CompletionMessage::Assistant {
|
||||
content: Some(body.read(cx).source().to_string()),
|
||||
content: Some(body.text.to_string()),
|
||||
tool_calls: tool_calls_from_assistant,
|
||||
});
|
||||
|
||||
@@ -1030,7 +959,7 @@ impl AssistantChat {
|
||||
match message {
|
||||
ChatMessage::User(message) => SavedChatMessage::User {
|
||||
id: message.id,
|
||||
body: message.body.read(cx).source().into(),
|
||||
body: message.body.read(cx).text(cx),
|
||||
attachments: message
|
||||
.attachments
|
||||
.iter()
|
||||
@@ -1047,7 +976,7 @@ impl AssistantChat {
|
||||
.messages
|
||||
.iter()
|
||||
.map(|message| SavedAssistantMessagePart {
|
||||
body: message.body.read(cx).source().to_string().into(),
|
||||
body: message.body.text.clone(),
|
||||
tool_calls: message
|
||||
.tool_calls
|
||||
.iter()
|
||||
@@ -1159,7 +1088,7 @@ enum ChatMessage {
|
||||
impl ChatMessage {
|
||||
fn focus_handle(&self, cx: &AppContext) -> Option<FocusHandle> {
|
||||
match self {
|
||||
ChatMessage::User(message) => Some(message.body.focus_handle(cx)),
|
||||
ChatMessage::User(UserMessage { body, .. }) => Some(body.focus_handle(cx)),
|
||||
ChatMessage::Assistant(_) => None,
|
||||
}
|
||||
}
|
||||
@@ -1167,12 +1096,12 @@ impl ChatMessage {
|
||||
|
||||
struct UserMessage {
|
||||
pub id: MessageId,
|
||||
pub body: View<Markdown>,
|
||||
pub body: View<Editor>,
|
||||
pub attachments: Vec<UserAttachment>,
|
||||
}
|
||||
|
||||
struct AssistantMessagePart {
|
||||
pub body: View<Markdown>,
|
||||
pub body: RichText,
|
||||
pub tool_calls: Vec<ToolFunctionCall>,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use anyhow::Result;
|
||||
use assistant_tooling::{LanguageModelTool, ProjectContext, ToolView};
|
||||
use assistant_tooling::{LanguageModelTool, ProjectContext, ToolOutput};
|
||||
use editor::{
|
||||
display_map::{BlockContext, BlockDisposition, BlockProperties, BlockStyle},
|
||||
Editor, MultiBuffer,
|
||||
@@ -45,7 +45,7 @@ struct Excerpt {
|
||||
path: String,
|
||||
/// A short, distinctive string that appears in the file, used to define a location in the file.
|
||||
text_passage: String,
|
||||
/// Text to display above the code excerpt
|
||||
/// Text to display above the code excerpt. All explanation of code should be included here.
|
||||
annotation: String,
|
||||
}
|
||||
|
||||
@@ -53,11 +53,17 @@ impl LanguageModelTool for AnnotationTool {
|
||||
type View = AnnotationResultView;
|
||||
|
||||
fn name(&self) -> String {
|
||||
"annotate_code".to_string()
|
||||
"show_code_file_excerpts".to_string()
|
||||
}
|
||||
|
||||
fn description(&self) -> String {
|
||||
"Dynamically annotate symbols in the current codebase. Opens a buffer in a panel in their editor, to the side of the conversation. The annotations are shown in the editor as a block decoration.".to_string()
|
||||
"
|
||||
Show and explain code from the current project
|
||||
Opens a buffer in a separate pane/tab, to the side of the conversation.
|
||||
The annotations are shown in the editor as block decorations.
|
||||
Many related excerpts can be shown at once.
|
||||
"
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn view(&self, cx: &mut WindowContext) -> View<Self::View> {
|
||||
@@ -230,7 +236,7 @@ impl Render for AnnotationResultView {
|
||||
}
|
||||
}
|
||||
|
||||
impl ToolView for AnnotationResultView {
|
||||
impl ToolOutput for AnnotationResultView {
|
||||
type Input = AnnotationInput;
|
||||
type SerializedState = Option<String>;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use assistant_tooling::{LanguageModelTool, ProjectContext, ToolView};
|
||||
use assistant_tooling::{LanguageModelTool, ProjectContext, ToolOutput};
|
||||
use editor::Editor;
|
||||
use gpui::{prelude::*, Model, Task, View, WeakView};
|
||||
use project::Project;
|
||||
@@ -35,11 +35,11 @@ impl LanguageModelTool for CreateBufferTool {
|
||||
type View = CreateBufferView;
|
||||
|
||||
fn name(&self) -> String {
|
||||
"create_file".to_string()
|
||||
"create_new_source_file".to_string()
|
||||
}
|
||||
|
||||
fn description(&self) -> String {
|
||||
"Create a new untitled file in the current codebase. Side effect: opens it in a new pane/tab for the user to edit.".to_string()
|
||||
"Create a new file in the current codebase. Only use this when generating new code, NOT when showing existing code from the project.".to_string()
|
||||
}
|
||||
|
||||
fn view(&self, cx: &mut WindowContext) -> View<Self::View> {
|
||||
@@ -61,11 +61,11 @@ pub struct CreateBufferView {
|
||||
|
||||
impl Render for CreateBufferView {
|
||||
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
ui::Label::new("Opening a buffer")
|
||||
div().child("Opening a buffer")
|
||||
}
|
||||
}
|
||||
|
||||
impl ToolView for CreateBufferView {
|
||||
impl ToolOutput for CreateBufferView {
|
||||
type Input = CreateBufferInput;
|
||||
|
||||
type SerializedState = ();
|
||||
@@ -81,9 +81,8 @@ impl ToolView for CreateBufferView {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_input(&mut self, input: Self::Input, cx: &mut ViewContext<Self>) {
|
||||
fn set_input(&mut self, input: Self::Input, _cx: &mut ViewContext<Self>) {
|
||||
self.input = Some(input);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn execute(&mut self, cx: &mut ViewContext<Self>) -> Task<Result<()>> {
|
||||
|
||||
@@ -1,19 +1,12 @@
|
||||
use anyhow::Result;
|
||||
use assistant_tooling::{LanguageModelTool, ToolView};
|
||||
use assistant_tooling::{LanguageModelTool, ToolOutput};
|
||||
use collections::BTreeMap;
|
||||
use file_icons::FileIcons;
|
||||
use gpui::{prelude::*, AnyElement, Model, Task};
|
||||
use gpui::{prelude::*, Model, Task};
|
||||
use project::ProjectPath;
|
||||
use schemars::JsonSchema;
|
||||
use semantic_index::{ProjectIndex, Status};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
fmt::Write as _,
|
||||
ops::Range,
|
||||
path::{Path, PathBuf},
|
||||
str::FromStr as _,
|
||||
sync::Arc,
|
||||
};
|
||||
use std::{fmt::Write as _, ops::Range, path::Path, sync::Arc};
|
||||
use ui::{prelude::*, CollapsibleContainer, Color, Icon, IconName, Label, WindowContext};
|
||||
|
||||
const DEFAULT_SEARCH_LIMIT: usize = 20;
|
||||
@@ -45,48 +38,8 @@ pub struct ProjectIndexView {
|
||||
pub struct CodebaseQuery {
|
||||
/// Semantic search query
|
||||
query: String,
|
||||
/// Criteria to include results
|
||||
includes: Option<SearchFilter>,
|
||||
/// Criteria to exclude results
|
||||
excludes: Option<SearchFilter>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, JsonSchema, Clone, Default)]
|
||||
pub struct SearchFilter {
|
||||
/// Filter by file path prefix
|
||||
prefix_path: Option<String>,
|
||||
/// Filter by file extension
|
||||
extension: Option<String>,
|
||||
// Note: we possibly can't do content filtering very easily given the project context handling
|
||||
// the final results, so we're leaving out direct string matches for now
|
||||
}
|
||||
|
||||
fn project_starts_with(prefix_path: Option<String>, project_path: ProjectPath) -> bool {
|
||||
if let Some(path) = &prefix_path {
|
||||
if let Some(path) = PathBuf::from_str(path).ok() {
|
||||
return project_path.path.starts_with(path);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
impl SearchFilter {
|
||||
fn matches(&self, project_path: &ProjectPath) -> bool {
|
||||
let path_match = project_starts_with(self.prefix_path.clone(), project_path.clone());
|
||||
|
||||
path_match
|
||||
&& (if let Some(extension) = &self.extension {
|
||||
project_path
|
||||
.path
|
||||
.extension()
|
||||
.and_then(|ext| ext.to_str())
|
||||
.map(|ext| ext == extension)
|
||||
.unwrap_or(false)
|
||||
} else {
|
||||
true
|
||||
})
|
||||
}
|
||||
/// Maximum number of results to return, defaults to 20
|
||||
limit: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@@ -106,56 +59,6 @@ impl ProjectIndexView {
|
||||
self.expanded_header = !self.expanded_header;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn render_filter_section(
|
||||
&mut self,
|
||||
heading: &str,
|
||||
filter: Option<SearchFilter>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<AnyElement> {
|
||||
let filter = match filter {
|
||||
Some(filter) => filter,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
// Any of the filter fields can be empty. We'll show nothing if they're all empty.
|
||||
let path = filter.prefix_path.as_ref().map(|path| {
|
||||
let icon_path = FileIcons::get_icon(Path::new(path), cx)
|
||||
.map(SharedString::from)
|
||||
.unwrap_or_else(|| SharedString::from("icons/file_icons/file.svg"));
|
||||
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.child("Paths: ")
|
||||
.child(Icon::from_path(icon_path))
|
||||
.child(ui::Label::new(path.clone()).color(Color::Muted))
|
||||
});
|
||||
|
||||
let extension = filter.extension.as_ref().map(|extension| {
|
||||
let icon_path = FileIcons::get_icon(Path::new(extension), cx)
|
||||
.map(SharedString::from)
|
||||
.unwrap_or_else(|| SharedString::from("icons/file_icons/file.svg"));
|
||||
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.child("Extensions: ")
|
||||
.child(Icon::from_path(icon_path))
|
||||
.child(ui::Label::new(extension.clone()).color(Color::Muted))
|
||||
});
|
||||
|
||||
if path.is_none() && extension.is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(
|
||||
v_flex()
|
||||
.child(ui::Label::new(heading.to_string()))
|
||||
.gap_1()
|
||||
.children(path)
|
||||
.children(extension)
|
||||
.into_any_element(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for ProjectIndexView {
|
||||
@@ -172,23 +75,19 @@ impl Render for ProjectIndexView {
|
||||
ProjectIndexToolState::Finished { excerpts, .. } => {
|
||||
let file_count = excerpts.len();
|
||||
|
||||
if excerpts.is_empty() {
|
||||
("No results found".to_string(), div())
|
||||
} else {
|
||||
let header_text = format!(
|
||||
"Read {} {}",
|
||||
file_count,
|
||||
if file_count == 1 { "file" } else { "files" }
|
||||
);
|
||||
let header_text = format!(
|
||||
"Read {} {}",
|
||||
file_count,
|
||||
if file_count == 1 { "file" } else { "files" }
|
||||
);
|
||||
|
||||
let el = v_flex().gap_2().children(excerpts.keys().map(|path| {
|
||||
h_flex().gap_2().child(Icon::new(IconName::File)).child(
|
||||
Label::new(path.path.to_string_lossy().to_string()).color(Color::Muted),
|
||||
)
|
||||
}));
|
||||
let el = v_flex().gap_2().children(excerpts.keys().map(|path| {
|
||||
h_flex().gap_2().child(Icon::new(IconName::File)).child(
|
||||
Label::new(path.path.to_string_lossy().to_string()).color(Color::Muted),
|
||||
)
|
||||
}));
|
||||
|
||||
(header_text, el)
|
||||
}
|
||||
(header_text, el)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -215,16 +114,6 @@ impl Render for ProjectIndexView {
|
||||
.child(Icon::new(IconName::MagnifyingGlass))
|
||||
.child(Label::new(format!("`{}`", query)).color(Color::Muted)),
|
||||
)
|
||||
.children(self.render_filter_section(
|
||||
"Includes",
|
||||
self.input.includes.clone(),
|
||||
cx,
|
||||
))
|
||||
.children(self.render_filter_section(
|
||||
"Excludes",
|
||||
self.input.excludes.clone(),
|
||||
cx,
|
||||
))
|
||||
.child(content),
|
||||
),
|
||||
)
|
||||
@@ -232,7 +121,7 @@ impl Render for ProjectIndexView {
|
||||
}
|
||||
}
|
||||
|
||||
impl ToolView for ProjectIndexView {
|
||||
impl ToolOutput for ProjectIndexView {
|
||||
type Input = CodebaseQuery;
|
||||
type SerializedState = SerializedState;
|
||||
|
||||
@@ -276,13 +165,11 @@ impl ToolView for ProjectIndexView {
|
||||
|
||||
let project_index = self.project_index.read(cx);
|
||||
let index_status = project_index.status();
|
||||
|
||||
// TODO: wire the filters into the search here instead of processing after.
|
||||
// Otherwise we'll get zero results sometimes.
|
||||
let search = project_index.search(self.input.query.clone(), DEFAULT_SEARCH_LIMIT, cx);
|
||||
|
||||
let includes = self.input.includes.clone();
|
||||
let excludes = self.input.excludes.clone();
|
||||
let search = project_index.search(
|
||||
self.input.query.clone(),
|
||||
self.input.limit.unwrap_or(DEFAULT_SEARCH_LIMIT),
|
||||
cx,
|
||||
);
|
||||
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
let search_result = search.await;
|
||||
@@ -295,17 +182,6 @@ impl ToolView for ProjectIndexView {
|
||||
worktree_id: search_result.worktree.read(cx).id(),
|
||||
path: search_result.path,
|
||||
};
|
||||
|
||||
if let Some(includes) = &includes {
|
||||
if !includes.matches(&project_path) {
|
||||
continue;
|
||||
}
|
||||
} else if let Some(excludes) = &excludes {
|
||||
if excludes.matches(&project_path) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
excerpts
|
||||
.entry(project_path)
|
||||
.or_default()
|
||||
@@ -401,20 +277,11 @@ impl LanguageModelTool for ProjectIndexTool {
|
||||
type View = ProjectIndexView;
|
||||
|
||||
fn name(&self) -> String {
|
||||
"semantic_search_codebase".to_string()
|
||||
"query_codebase".to_string()
|
||||
}
|
||||
|
||||
fn description(&self) -> String {
|
||||
unindent::unindent(
|
||||
r#"This search tool uses a semantic index to perform search queries across your codebase, identifying and returning excerpts of text and code possibly related to the query.
|
||||
|
||||
Ideal for:
|
||||
- Discovering implementations of similar logic within the project
|
||||
- Finding usage examples of functions, classes/structures, libraries, and other code elements
|
||||
- Developing understanding of the codebase's architecture and design
|
||||
|
||||
Note: The search's effectiveness is directly related to the current state of the codebase and the specificity of your query. It is recommended that you use snippets of code that are similar to the code you wish to find."#,
|
||||
)
|
||||
"Semantic search against the user's current codebase, returning excerpts related to the query by computing a dot product against embeddings of code chunks in the code base and an embedding of the query.".to_string()
|
||||
}
|
||||
|
||||
fn view(&self, cx: &mut WindowContext) -> gpui::View<Self::View> {
|
||||
|
||||
@@ -119,7 +119,7 @@ impl RenderOnce for ChatMessage {
|
||||
)
|
||||
.when(self.messages.len() > 0, |el| {
|
||||
el.child(
|
||||
h_flex().w_full().child(
|
||||
h_flex().child(
|
||||
v_flex()
|
||||
.relative()
|
||||
.overflow_hidden()
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
# Assistant Tooling
|
||||
|
||||
Bringing Language Model tool calling to GPUI.
|
||||
Bringing OpenAI compatible tool calling to GPUI.
|
||||
|
||||
This unlocks:
|
||||
|
||||
- **Structured Extraction** of model responses
|
||||
- **Validation** of model inputs
|
||||
- **Execution** of chosen tools
|
||||
- **Execution** of chosen toolsn
|
||||
|
||||
## Overview
|
||||
|
||||
Language Models can produce structured outputs that are perfect for calling functions. The most famous of these is OpenAI's tool calling. When making a chat completion you can pass a list of tools available to the model. The model will choose `0..n` tools to help them complete a user's task. It's up to _you_ to create the tools that the model can call.
|
||||
Language Models can produce structured outputs that are perfect for calling functions. The most famous of these is OpenAI's tool calling. When make a chat completion you can pass a list of tools available to the model. The model will choose `0..n` tools to help them complete a user's task. It's up to _you_ to create the tools that the model can call.
|
||||
|
||||
> **User**: "Hey I need help with implementing a collapsible panel in GPUI"
|
||||
>
|
||||
@@ -22,64 +22,187 @@ Language Models can produce structured outputs that are perfect for calling func
|
||||
>
|
||||
> **Assistant**: "Here are some excerpts from the GPUI codebase that might help you."
|
||||
|
||||
This library is designed to facilitate this interaction mode by allowing you to go from `struct` to `tool` with two simple traits, `LanguageModelTool` and `ToolView`.
|
||||
This library is designed to facilitate this interaction mode by allowing you to go from `struct` to `tool` with a simple trait, `LanguageModelTool`.
|
||||
|
||||
## Using the Tool Registry
|
||||
## Example
|
||||
|
||||
Let's expose querying a semantic index directly by the model. First, we'll set up some _necessary_ imports
|
||||
|
||||
```rust
|
||||
let mut tool_registry = ToolRegistry::new();
|
||||
tool_registry
|
||||
.register(WeatherTool { api_client },
|
||||
})
|
||||
.unwrap(); // You can only register one tool per name
|
||||
use anyhow::Result;
|
||||
use assistant_tooling::{LanguageModelTool, ToolRegistry};
|
||||
use gpui::{App, AppContext, Task};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde_json::json;
|
||||
```
|
||||
|
||||
let completion = cx.update(|cx| {
|
||||
CompletionProvider::get(cx).complete(
|
||||
model_name,
|
||||
messages,
|
||||
Vec::new(),
|
||||
1.0,
|
||||
// The definitions get passed directly to OpenAI when you want
|
||||
// the model to be able to call your tool
|
||||
tool_registry.definitions(),
|
||||
)
|
||||
});
|
||||
Then we'll define the query structure the model must fill in. This _must_ derive `Deserialize` from `serde` and `JsonSchema` from the `schemars` crate.
|
||||
|
||||
let mut stream = completion?.await?;
|
||||
```rust
|
||||
#[derive(Deserialize, JsonSchema)]
|
||||
struct CodebaseQuery {
|
||||
query: String,
|
||||
}
|
||||
```
|
||||
|
||||
let mut message = AssistantMessage::new();
|
||||
After that we can define our tool, with the expectation that it will need a `ProjectIndex` to search against. For this example, the index uses the same interface as `semantic_index::ProjectIndex`.
|
||||
|
||||
while let Some(delta) = stream.next().await {
|
||||
// As messages stream in, you'll get both assistant content
|
||||
if let Some(content) = &delta.content {
|
||||
message
|
||||
.body
|
||||
.update(cx, |message, cx| message.append(&content, cx));
|
||||
```rust
|
||||
struct ProjectIndex {}
|
||||
|
||||
impl ProjectIndex {
|
||||
fn new() -> Self {
|
||||
ProjectIndex {}
|
||||
}
|
||||
|
||||
// And tool calls!
|
||||
for tool_call_delta in delta.tool_calls {
|
||||
let index = tool_call_delta.index as usize;
|
||||
if index >= message.tool_calls.len() {
|
||||
message.tool_calls.resize_with(index + 1, Default::default);
|
||||
fn search(&self, _query: &str, _limit: usize, _cx: &AppContext) -> Task<Result<Vec<String>>> {
|
||||
// Instead of hooking up a real index, we're going to fake it
|
||||
if _query.contains("gpui") {
|
||||
return Task::ready(Ok(vec![r#"// crates/gpui/src/gpui.rs
|
||||
//! # Welcome to GPUI!
|
||||
//!
|
||||
//! GPUI is a hybrid immediate and retained mode, GPU accelerated, UI framework
|
||||
//! for Rust, designed to support a wide variety of applications
|
||||
"#
|
||||
.to_string()]));
|
||||
}
|
||||
let tool_call = &mut message.tool_calls[index];
|
||||
return Task::ready(Ok(vec![]));
|
||||
}
|
||||
}
|
||||
|
||||
// Build up an ID
|
||||
if let Some(id) = &tool_call_delta.id {
|
||||
tool_call.id.push_str(id);
|
||||
}
|
||||
struct ProjectIndexTool {
|
||||
project_index: ProjectIndex,
|
||||
}
|
||||
```
|
||||
|
||||
tool_registry.update_tool_call(
|
||||
tool_call,
|
||||
tool_call_delta.name.as_deref(),
|
||||
tool_call_delta.arguments.as_deref(),
|
||||
cx,
|
||||
);
|
||||
Now we can implement the `LanguageModelTool` trait for our tool by:
|
||||
|
||||
- Defining the `Input` from the model, which is `CodebaseQuery`
|
||||
- Defining the `Output`
|
||||
- Implementing the `name` and `description` functions to provide the model information when it's choosing a tool
|
||||
- Implementing the `execute` function to run the tool
|
||||
|
||||
```rust
|
||||
impl LanguageModelTool for ProjectIndexTool {
|
||||
type Input = CodebaseQuery;
|
||||
type Output = String;
|
||||
|
||||
fn name(&self) -> String {
|
||||
"query_codebase".to_string()
|
||||
}
|
||||
|
||||
fn description(&self) -> String {
|
||||
"Executes a query against the codebase, returning excerpts related to the query".to_string()
|
||||
}
|
||||
|
||||
fn execute(&self, query: Self::Input, cx: &AppContext) -> Task<Result<Self::Output>> {
|
||||
let results = self.project_index.search(query.query.as_str(), 10, cx);
|
||||
|
||||
cx.spawn(|_cx| async move {
|
||||
let results = results.await?;
|
||||
|
||||
if !results.is_empty() {
|
||||
Ok(results.join("\n"))
|
||||
} else {
|
||||
Ok("No results".to_string())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Once the stream of tokens is complete, you can exexute the tool call by calling `tool_registry.execute_tool_call(tool_call, cx)`, which returns a `Task<Result<()>>`.
|
||||
For the sake of this example, let's look at the types that OpenAI will be passing to us
|
||||
|
||||
As the tokens stream in and tool calls are executed, your `ToolView` will get updates. Render each tool call by passing that `tool_call` in to `tool_registry.render_tool_call(tool_call, cx)`. The final message for the model can be pulled by calling `self.tool_registry.content_for_tool_call( tool_call, &mut project_context, cx, )`.
|
||||
```rust
|
||||
// OpenAI definitions, shown here for demonstration
|
||||
#[derive(Deserialize)]
|
||||
struct FunctionCall {
|
||||
name: String,
|
||||
args: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Eq, PartialEq)]
|
||||
enum ToolCallType {
|
||||
#[serde(rename = "function")]
|
||||
Function,
|
||||
Other,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||
struct ToolCallId(String);
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
enum ToolCall {
|
||||
Function {
|
||||
#[allow(dead_code)]
|
||||
id: ToolCallId,
|
||||
function: FunctionCall,
|
||||
},
|
||||
Other {
|
||||
#[allow(dead_code)]
|
||||
id: ToolCallId,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct AssistantMessage {
|
||||
role: String,
|
||||
content: Option<String>,
|
||||
tool_calls: Option<Vec<ToolCall>>,
|
||||
}
|
||||
```
|
||||
|
||||
When the model wants to call tools, it will pass a list of `ToolCall`s. When those are `function`s that we can handle, we'll pass them to our `ToolRegistry` to get a future that we can await.
|
||||
|
||||
```rust
|
||||
// Inside `fn main()`
|
||||
App::new().run(|cx: &mut AppContext| {
|
||||
let tool = ProjectIndexTool {
|
||||
project_index: ProjectIndex::new(),
|
||||
};
|
||||
|
||||
let mut registry = ToolRegistry::new();
|
||||
let registered = registry.register(tool);
|
||||
assert!(registered.is_ok());
|
||||
```
|
||||
|
||||
Let's pretend the model sent us back a message requesting
|
||||
|
||||
```rust
|
||||
let model_response = json!({
|
||||
"role": "assistant",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_1",
|
||||
"function": {
|
||||
"name": "query_codebase",
|
||||
"args": r#"{"query":"GPUI Task background_executor"}"#
|
||||
},
|
||||
"type": "function"
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
let message: AssistantMessage = serde_json::from_value(model_response).unwrap();
|
||||
|
||||
// We know there's a tool call, so let's skip straight to it for this example
|
||||
let tool_calls = message.tool_calls.as_ref().unwrap();
|
||||
let tool_call = tool_calls.get(0).unwrap();
|
||||
```
|
||||
|
||||
We can now use our registry to call the tool.
|
||||
|
||||
```rust
|
||||
let task = registry.call(
|
||||
tool_call.name,
|
||||
tool_call.args,
|
||||
);
|
||||
|
||||
cx.spawn(|_cx| async move {
|
||||
let result = task.await?;
|
||||
println!("{}", result.unwrap());
|
||||
Ok(())
|
||||
})
|
||||
```
|
||||
|
||||
@@ -8,6 +8,6 @@ pub use attachment_registry::{
|
||||
};
|
||||
pub use project_context::ProjectContext;
|
||||
pub use tool_registry::{
|
||||
LanguageModelTool, SavedToolFunctionCall, ToolFunctionCall, ToolFunctionDefinition,
|
||||
ToolRegistry, ToolView,
|
||||
LanguageModelTool, SavedToolFunctionCall, ToolFunctionCall, ToolFunctionDefinition, ToolOutput,
|
||||
ToolRegistry,
|
||||
};
|
||||
|
||||
@@ -31,11 +31,11 @@ enum ToolFunctionCallState {
|
||||
#[default]
|
||||
Initializing,
|
||||
NoSuchTool,
|
||||
KnownTool(Box<dyn InternalToolView>),
|
||||
ExecutedTool(Box<dyn InternalToolView>),
|
||||
KnownTool(Box<dyn ToolView>),
|
||||
ExecutedTool(Box<dyn ToolView>),
|
||||
}
|
||||
|
||||
trait InternalToolView {
|
||||
trait ToolView {
|
||||
fn view(&self) -> AnyView;
|
||||
fn generate(&self, project: &mut ProjectContext, cx: &mut WindowContext) -> String;
|
||||
fn try_set_input(&self, input: &str, cx: &mut WindowContext);
|
||||
@@ -69,7 +69,7 @@ pub struct ToolFunctionDefinition {
|
||||
}
|
||||
|
||||
pub trait LanguageModelTool {
|
||||
type View: ToolView;
|
||||
type View: ToolOutput;
|
||||
|
||||
/// Returns the name of the tool.
|
||||
///
|
||||
@@ -85,7 +85,7 @@ pub trait LanguageModelTool {
|
||||
|
||||
/// Returns the OpenAI Function definition for the tool, for direct use with OpenAI's API.
|
||||
fn definition(&self) -> ToolFunctionDefinition {
|
||||
let root_schema = schema_for!(<Self::View as ToolView>::Input);
|
||||
let root_schema = schema_for!(<Self::View as ToolOutput>::Input);
|
||||
|
||||
ToolFunctionDefinition {
|
||||
name: self.name(),
|
||||
@@ -98,7 +98,7 @@ pub trait LanguageModelTool {
|
||||
fn view(&self, cx: &mut WindowContext) -> View<Self::View>;
|
||||
}
|
||||
|
||||
pub trait ToolView: Render {
|
||||
pub trait ToolOutput: Render {
|
||||
/// The input type that will be passed in to `execute` when the tool is called
|
||||
/// by the language model.
|
||||
type Input: DeserializeOwned + JsonSchema;
|
||||
@@ -121,7 +121,7 @@ pub trait ToolView: Render {
|
||||
struct RegisteredTool {
|
||||
enabled: AtomicBool,
|
||||
type_id: TypeId,
|
||||
build_view: Box<dyn Fn(&mut WindowContext) -> Box<dyn InternalToolView>>,
|
||||
build_view: Box<dyn Fn(&mut WindowContext) -> Box<dyn ToolView>>,
|
||||
definition: ToolFunctionDefinition,
|
||||
}
|
||||
|
||||
@@ -304,7 +304,7 @@ impl ToolRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToolView> InternalToolView for View<T> {
|
||||
impl<T: ToolOutput> ToolView for View<T> {
|
||||
fn view(&self) -> AnyView {
|
||||
self.clone().into()
|
||||
}
|
||||
@@ -404,7 +404,7 @@ mod test {
|
||||
}
|
||||
}
|
||||
|
||||
impl ToolView for WeatherView {
|
||||
impl ToolOutput for WeatherView {
|
||||
type Input = WeatherQuery;
|
||||
|
||||
type SerializedState = WeatherResult;
|
||||
|
||||
@@ -18,7 +18,6 @@ client.workspace = true
|
||||
db.workspace = true
|
||||
editor.workspace = true
|
||||
gpui.workspace = true
|
||||
http.workspace = true
|
||||
isahc.workspace = true
|
||||
log.workspace = true
|
||||
markdown_preview.workspace = true
|
||||
|
||||
@@ -20,7 +20,6 @@ use smol::{fs, io::AsyncReadExt};
|
||||
use settings::{Settings, SettingsSources, SettingsStore};
|
||||
use smol::{fs::File, process::Command};
|
||||
|
||||
use http::{HttpClient, HttpClientWithUrl};
|
||||
use release_channel::{AppCommitSha, AppVersion, ReleaseChannel};
|
||||
use std::{
|
||||
env::consts::{ARCH, OS},
|
||||
@@ -30,7 +29,10 @@ use std::{
|
||||
time::Duration,
|
||||
};
|
||||
use update_notification::UpdateNotification;
|
||||
use util::ResultExt;
|
||||
use util::{
|
||||
http::{HttpClient, HttpClientWithUrl},
|
||||
ResultExt,
|
||||
};
|
||||
use workspace::notifications::NotificationId;
|
||||
use workspace::Workspace;
|
||||
|
||||
|
||||
@@ -50,4 +50,3 @@ language = { workspace = true, features = ["test-support"] }
|
||||
live_kit_client = { workspace = true, features = ["test-support"] }
|
||||
project = { workspace = true, features = ["test-support"] }
|
||||
util = { workspace = true, features = ["test-support"] }
|
||||
http = { workspace = true, features = ["test-support"] }
|
||||
|
||||
@@ -40,4 +40,3 @@ rpc = { workspace = true, features = ["test-support"] }
|
||||
client = { workspace = true, features = ["test-support"] }
|
||||
settings = { workspace = true, features = ["test-support"] }
|
||||
util = { workspace = true, features = ["test-support"] }
|
||||
http = { workspace = true, features = ["test-support"] }
|
||||
|
||||
@@ -4,9 +4,9 @@ use super::*;
|
||||
use client::{test::FakeServer, Client, UserStore};
|
||||
use clock::FakeSystemClock;
|
||||
use gpui::{AppContext, Context, Model, TestAppContext};
|
||||
use http::FakeHttpClient;
|
||||
use rpc::proto::{self};
|
||||
use settings::SettingsStore;
|
||||
use util::http::FakeHttpClient;
|
||||
|
||||
#[gpui::test]
|
||||
fn test_update_channels(cx: &mut AppContext) {
|
||||
|
||||
@@ -4,10 +4,8 @@ use anyhow::{Context, Result};
|
||||
use clap::Parser;
|
||||
use cli::{ipc::IpcOneShotServer, CliRequest, CliResponse, IpcHandshake};
|
||||
use std::{
|
||||
env, fs, io,
|
||||
env, fs,
|
||||
path::{Path, PathBuf},
|
||||
process::ExitStatus,
|
||||
thread::{self, JoinHandle},
|
||||
};
|
||||
use util::paths::PathLikeWithPosition;
|
||||
|
||||
@@ -16,7 +14,6 @@ struct Detect;
|
||||
trait InstalledApp {
|
||||
fn zed_version_string(&self) -> String;
|
||||
fn launch(&self, ipc_url: String) -> anyhow::Result<()>;
|
||||
fn run_foreground(&self, ipc_url: String) -> io::Result<ExitStatus>;
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
@@ -40,9 +37,6 @@ struct Args {
|
||||
/// Print Zed's version and the app path.
|
||||
#[arg(short, long)]
|
||||
version: bool,
|
||||
/// Run zed in the foreground (useful for debugging)
|
||||
#[arg(long)]
|
||||
foreground: bool,
|
||||
/// Custom path to Zed.app or the zed binary
|
||||
#[arg(long)]
|
||||
zed: Option<PathBuf>,
|
||||
@@ -105,6 +99,10 @@ fn main() -> Result<()> {
|
||||
IpcOneShotServer::<IpcHandshake>::new().context("Handshake before Zed spawn")?;
|
||||
let url = format!("zed-cli://{server_name}");
|
||||
|
||||
app.launch(url)?;
|
||||
let (_, handshake) = server.accept().context("Handshake after Zed spawn")?;
|
||||
let (tx, rx) = (handshake.requests, handshake.responses);
|
||||
|
||||
let open_new_workspace = if args.new {
|
||||
Some(true)
|
||||
} else if args.add {
|
||||
@@ -113,33 +111,20 @@ fn main() -> Result<()> {
|
||||
None
|
||||
};
|
||||
|
||||
let sender: JoinHandle<anyhow::Result<()>> = thread::spawn(move || {
|
||||
let (_, handshake) = server.accept().context("Handshake after Zed spawn")?;
|
||||
let (tx, rx) = (handshake.requests, handshake.responses);
|
||||
tx.send(CliRequest::Open {
|
||||
paths,
|
||||
wait: args.wait,
|
||||
open_new_workspace,
|
||||
dev_server_token: args.dev_server_token,
|
||||
})?;
|
||||
tx.send(CliRequest::Open {
|
||||
paths,
|
||||
wait: args.wait,
|
||||
open_new_workspace,
|
||||
dev_server_token: args.dev_server_token,
|
||||
})?;
|
||||
|
||||
while let Ok(response) = rx.recv() {
|
||||
match response {
|
||||
CliResponse::Ping => {}
|
||||
CliResponse::Stdout { message } => println!("{message}"),
|
||||
CliResponse::Stderr { message } => eprintln!("{message}"),
|
||||
CliResponse::Exit { status } => std::process::exit(status),
|
||||
}
|
||||
while let Ok(response) = rx.recv() {
|
||||
match response {
|
||||
CliResponse::Ping => {}
|
||||
CliResponse::Stdout { message } => println!("{message}"),
|
||||
CliResponse::Stderr { message } => eprintln!("{message}"),
|
||||
CliResponse::Exit { status } => std::process::exit(status),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
if args.foreground {
|
||||
app.run_foreground(url)?;
|
||||
} else {
|
||||
app.launch(url)?;
|
||||
sender.join().unwrap()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -156,8 +141,7 @@ mod linux {
|
||||
unix::net::{SocketAddr, UnixDatagram},
|
||||
},
|
||||
path::{Path, PathBuf},
|
||||
process::{self, ExitStatus},
|
||||
thread,
|
||||
process, thread,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
@@ -224,12 +208,6 @@ mod linux {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_foreground(&self, ipc_url: String) -> io::Result<ExitStatus> {
|
||||
std::process::Command::new(self.0.clone())
|
||||
.arg(ipc_url)
|
||||
.status()
|
||||
}
|
||||
}
|
||||
|
||||
impl App {
|
||||
@@ -279,9 +257,7 @@ mod linux {
|
||||
#[cfg(target_os = "windows")]
|
||||
mod windows {
|
||||
use crate::{Detect, InstalledApp};
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use std::process::ExitStatus;
|
||||
|
||||
struct App;
|
||||
impl InstalledApp for App {
|
||||
@@ -291,9 +267,6 @@ mod windows {
|
||||
fn launch(&self, _ipc_url: String) -> anyhow::Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn run_foreground(&self, _ipc_url: String) -> io::Result<ExitStatus> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Detect {
|
||||
@@ -315,9 +288,9 @@ mod mac_os {
|
||||
use serde::Deserialize;
|
||||
use std::{
|
||||
ffi::OsStr,
|
||||
fs, io,
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
process::{Command, ExitStatus},
|
||||
process::Command,
|
||||
ptr,
|
||||
};
|
||||
|
||||
@@ -469,15 +442,6 @@ mod mac_os {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_foreground(&self, ipc_url: String) -> io::Result<ExitStatus> {
|
||||
let path = match self {
|
||||
Bundle::App { app_bundle, .. } => app_bundle.join("Contents/MacOS/zed"),
|
||||
Bundle::LocalPath { executable, .. } => executable.clone(),
|
||||
};
|
||||
|
||||
std::process::Command::new(path).arg(ipc_url).status()
|
||||
}
|
||||
}
|
||||
|
||||
impl Bundle {
|
||||
|
||||
@@ -19,14 +19,12 @@ test-support = ["clock/test-support", "collections/test-support", "gpui/test-sup
|
||||
anyhow.workspace = true
|
||||
async-recursion = "0.3"
|
||||
async-tungstenite = { version = "0.16", features = ["async-std", "async-native-tls"] }
|
||||
async-native-tls = { version = "0.5.0", features = ["vendored"] }
|
||||
chrono = { workspace = true, features = ["serde"] }
|
||||
clock.workspace = true
|
||||
collections.workspace = true
|
||||
feature_flags.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
http.workspace = true
|
||||
lazy_static.workspace = true
|
||||
log.workspace = true
|
||||
once_cell.workspace = true
|
||||
@@ -58,11 +56,3 @@ gpui = { workspace = true, features = ["test-support"] }
|
||||
rpc = { workspace = true, features = ["test-support"] }
|
||||
settings = { workspace = true, features = ["test-support"] }
|
||||
util = { workspace = true, features = ["test-support"] }
|
||||
http = { workspace = true, features = ["test-support"] }
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
async-native-tls = {"version" = "0.5.0", features = ["vendored"]}
|
||||
# This is an indirect dependency of async-tungstenite that is included
|
||||
# here so we can vendor libssl with the feature flag.
|
||||
[package.metadata.cargo-machete]
|
||||
ignored = ["async-native-tls"]
|
||||
|
||||
@@ -20,7 +20,6 @@ use gpui::{
|
||||
actions, AnyModel, AnyWeakModel, AppContext, AsyncAppContext, BorrowAppContext, Global, Model,
|
||||
Task, WeakModel,
|
||||
};
|
||||
use http::{HttpClient, HttpClientWithUrl};
|
||||
use lazy_static::lazy_static;
|
||||
use parking_lot::RwLock;
|
||||
use postage::watch;
|
||||
@@ -48,6 +47,7 @@ use std::{
|
||||
use telemetry::Telemetry;
|
||||
use thiserror::Error;
|
||||
use url::Url;
|
||||
use util::http::{HttpClient, HttpClientWithUrl};
|
||||
use util::{ResultExt, TryFutureExt};
|
||||
|
||||
pub use rpc::*;
|
||||
@@ -204,7 +204,7 @@ pub enum EstablishConnectionError {
|
||||
#[error("{0}")]
|
||||
Other(#[from] anyhow::Error),
|
||||
#[error("{0}")]
|
||||
Http(#[from] http::Error),
|
||||
Http(#[from] util::http::Error),
|
||||
#[error("{0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
#[error("{0}")]
|
||||
@@ -1679,10 +1679,10 @@ mod tests {
|
||||
|
||||
use clock::FakeSystemClock;
|
||||
use gpui::{BackgroundExecutor, Context, TestAppContext};
|
||||
use http::FakeHttpClient;
|
||||
use parking_lot::Mutex;
|
||||
use settings::SettingsStore;
|
||||
use std::future;
|
||||
use util::http::FakeHttpClient;
|
||||
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_reconnection(cx: &mut TestAppContext) {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -5,7 +5,6 @@ use chrono::{DateTime, Utc};
|
||||
use clock::SystemClock;
|
||||
use futures::Future;
|
||||
use gpui::{AppContext, AppMetadata, BackgroundExecutor, Task};
|
||||
use http::{self, HttpClient, HttpClientWithUrl, Method};
|
||||
use once_cell::sync::Lazy;
|
||||
use parking_lot::Mutex;
|
||||
use release_channel::ReleaseChannel;
|
||||
@@ -20,6 +19,7 @@ use telemetry_events::{
|
||||
SettingEvent,
|
||||
};
|
||||
use tempfile::NamedTempFile;
|
||||
use util::http::{self, HttpClient, HttpClientWithUrl, Method};
|
||||
#[cfg(not(debug_assertions))]
|
||||
use util::ResultExt;
|
||||
use util::TryFutureExt;
|
||||
@@ -261,15 +261,11 @@ impl Telemetry {
|
||||
conversation_id: Option<String>,
|
||||
kind: AssistantKind,
|
||||
model: String,
|
||||
response_latency: Option<Duration>,
|
||||
error_message: Option<String>,
|
||||
) {
|
||||
let event = Event::Assistant(AssistantEvent {
|
||||
conversation_id,
|
||||
kind,
|
||||
model: model.to_string(),
|
||||
response_latency,
|
||||
error_message,
|
||||
});
|
||||
|
||||
self.report_event(event)
|
||||
@@ -501,7 +497,7 @@ mod tests {
|
||||
use chrono::TimeZone;
|
||||
use clock::FakeSystemClock;
|
||||
use gpui::TestAppContext;
|
||||
use http::FakeHttpClient;
|
||||
use util::http::FakeHttpClient;
|
||||
|
||||
#[gpui::test]
|
||||
fn test_telemetry_flush_on_max_queue_size(cx: &mut TestAppContext) {
|
||||
|
||||
@@ -35,7 +35,6 @@ envy = "0.4.2"
|
||||
futures.workspace = true
|
||||
google_ai.workspace = true
|
||||
hex.workspace = true
|
||||
http.workspace = true
|
||||
live_kit_server.workspace = true
|
||||
log.workspace = true
|
||||
nanoid.workspace = true
|
||||
@@ -91,7 +90,6 @@ language = { workspace = true, features = ["test-support"] }
|
||||
live_kit_client = { workspace = true, features = ["test-support"] }
|
||||
lsp = { workspace = true, features = ["test-support"] }
|
||||
menu.workspace = true
|
||||
multi_buffer = { workspace = true, features = ["test-support"] }
|
||||
node_runtime.workspace = true
|
||||
notifications = { workspace = true, features = ["test-support"] }
|
||||
pretty_assertions.workspace = true
|
||||
|
||||
@@ -785,8 +785,6 @@ pub struct AssistantEventRow {
|
||||
conversation_id: String,
|
||||
kind: String,
|
||||
model: String,
|
||||
response_latency_in_ms: Option<i64>,
|
||||
error_message: Option<String>,
|
||||
}
|
||||
|
||||
impl AssistantEventRow {
|
||||
@@ -813,10 +811,6 @@ impl AssistantEventRow {
|
||||
conversation_id: event.conversation_id.unwrap_or_default(),
|
||||
kind: event.kind.to_string(),
|
||||
model: event.model,
|
||||
response_latency_in_ms: event
|
||||
.response_latency
|
||||
.map(|latency| latency.as_millis() as i64),
|
||||
error_message: event.error_message,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,9 +48,6 @@ impl Database {
|
||||
|
||||
/// Returns all users by ID. There are no access checks here, so this should only be used internally.
|
||||
pub async fn get_users_by_ids(&self, ids: Vec<UserId>) -> Result<Vec<user::Model>> {
|
||||
if ids.len() >= 10000_usize {
|
||||
return Err(anyhow!("too many users"))?;
|
||||
}
|
||||
self.transaction(|tx| async {
|
||||
let tx = tx;
|
||||
Ok(user::Entity::find()
|
||||
|
||||
@@ -42,7 +42,6 @@ use futures::{
|
||||
stream::FuturesUnordered,
|
||||
FutureExt, SinkExt, StreamExt, TryStreamExt,
|
||||
};
|
||||
use http::IsahcHttpClient;
|
||||
use prometheus::{register_int_gauge, IntGauge};
|
||||
use rpc::{
|
||||
proto::{
|
||||
@@ -74,6 +73,7 @@ use tracing::{
|
||||
field::{self},
|
||||
info_span, instrument, Instrument,
|
||||
};
|
||||
use util::http::IsahcHttpClient;
|
||||
|
||||
use self::connection_pool::VersionedMessage;
|
||||
|
||||
@@ -4344,7 +4344,6 @@ async fn complete_with_open_ai(
|
||||
OPEN_AI_API_URL,
|
||||
&api_key,
|
||||
crate::ai::language_model_request_to_open_ai(request)?,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("open_ai::stream_completion request failed within collab")?;
|
||||
@@ -4489,8 +4488,8 @@ async fn complete_with_anthropic(
|
||||
.collect();
|
||||
|
||||
let mut stream = anthropic::stream_completion(
|
||||
session.http_client.as_ref(),
|
||||
anthropic::ANTHROPIC_API_URL,
|
||||
session.http_client.clone(),
|
||||
"https://api.anthropic.com",
|
||||
&api_key,
|
||||
anthropic::Request {
|
||||
model,
|
||||
@@ -4499,7 +4498,6 @@ async fn complete_with_anthropic(
|
||||
system: system_message,
|
||||
max_tokens: 4092,
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ use editor::{
|
||||
ConfirmCodeAction, ConfirmCompletion, ConfirmRename, ContextMenuFirst, Redo, Rename,
|
||||
RevertSelectedHunks, ToggleCodeActions, Undo,
|
||||
},
|
||||
display_map::DisplayRow,
|
||||
test::{
|
||||
editor_hunks,
|
||||
editor_test_context::{AssertionContextManager, EditorTestContext},
|
||||
@@ -25,7 +24,6 @@ use language::{
|
||||
language_settings::{AllLanguageSettings, InlayHintSettings},
|
||||
FakeLspAdapter,
|
||||
};
|
||||
use multi_buffer::MultiBufferRow;
|
||||
use project::{
|
||||
project_settings::{InlineBlameSettings, ProjectSettings},
|
||||
SERVER_PROGRESS_DEBOUNCE_TIMEOUT,
|
||||
@@ -2116,30 +2114,14 @@ struct Row10;"#};
|
||||
assert_eq!(
|
||||
all_hunks,
|
||||
vec![
|
||||
(
|
||||
"".to_string(),
|
||||
DiffHunkStatus::Added,
|
||||
DisplayRow(1)..DisplayRow(3)
|
||||
),
|
||||
(
|
||||
"struct Row2;\n".to_string(),
|
||||
DiffHunkStatus::Removed,
|
||||
DisplayRow(4)..DisplayRow(4)
|
||||
),
|
||||
(
|
||||
"struct Row5;\n".to_string(),
|
||||
DiffHunkStatus::Modified,
|
||||
DisplayRow(6)..DisplayRow(7)
|
||||
),
|
||||
(
|
||||
"struct Row8;\n".to_string(),
|
||||
DiffHunkStatus::Removed,
|
||||
DisplayRow(9)..DisplayRow(9)
|
||||
),
|
||||
("".to_string(), DiffHunkStatus::Added, 1..3),
|
||||
("struct Row2;\n".to_string(), DiffHunkStatus::Removed, 4..4),
|
||||
("struct Row5;\n".to_string(), DiffHunkStatus::Modified, 6..7),
|
||||
("struct Row8;\n".to_string(), DiffHunkStatus::Removed, 9..9),
|
||||
(
|
||||
"struct Row10;".to_string(),
|
||||
DiffHunkStatus::Modified,
|
||||
DisplayRow(10)..DisplayRow(10),
|
||||
10..10,
|
||||
),
|
||||
]
|
||||
);
|
||||
@@ -2151,35 +2133,23 @@ struct Row10;"#};
|
||||
let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
|
||||
assert_eq!(
|
||||
expanded_hunks_background_highlights(editor, cx),
|
||||
vec![DisplayRow(1)..=DisplayRow(2), DisplayRow(8)..=DisplayRow(8)],
|
||||
vec![1..=2, 8..=8],
|
||||
);
|
||||
assert_eq!(
|
||||
all_hunks,
|
||||
vec![
|
||||
(
|
||||
"".to_string(),
|
||||
DiffHunkStatus::Added,
|
||||
DisplayRow(1)..DisplayRow(3)
|
||||
),
|
||||
(
|
||||
"struct Row2;\n".to_string(),
|
||||
DiffHunkStatus::Removed,
|
||||
DisplayRow(5)..DisplayRow(5)
|
||||
),
|
||||
(
|
||||
"struct Row5;\n".to_string(),
|
||||
DiffHunkStatus::Modified,
|
||||
DisplayRow(8)..DisplayRow(9)
|
||||
),
|
||||
("".to_string(), DiffHunkStatus::Added, 1..3),
|
||||
("struct Row2;\n".to_string(), DiffHunkStatus::Removed, 5..5),
|
||||
("struct Row5;\n".to_string(), DiffHunkStatus::Modified, 8..9),
|
||||
(
|
||||
"struct Row8;\n".to_string(),
|
||||
DiffHunkStatus::Removed,
|
||||
DisplayRow(12)..DisplayRow(12)
|
||||
12..12
|
||||
),
|
||||
(
|
||||
"struct Row10;".to_string(),
|
||||
DiffHunkStatus::Modified,
|
||||
DisplayRow(13)..DisplayRow(13),
|
||||
13..13,
|
||||
),
|
||||
]
|
||||
);
|
||||
@@ -2203,7 +2173,7 @@ struct Row10;"#};
|
||||
vec![(
|
||||
"struct Row10;".to_string(),
|
||||
DiffHunkStatus::Modified,
|
||||
DisplayRow(10)..DisplayRow(10),
|
||||
10..10,
|
||||
)]
|
||||
);
|
||||
assert_eq!(all_expanded_hunks, Vec::new());
|
||||
@@ -2214,14 +2184,14 @@ struct Row10;"#};
|
||||
let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
|
||||
assert_eq!(
|
||||
expanded_hunks_background_highlights(editor, cx),
|
||||
vec![DisplayRow(5)..=DisplayRow(5)]
|
||||
vec![5..=5]
|
||||
);
|
||||
assert_eq!(
|
||||
all_hunks,
|
||||
vec![(
|
||||
"struct Row10;".to_string(),
|
||||
DiffHunkStatus::Modified,
|
||||
DisplayRow(10)..DisplayRow(10),
|
||||
10..10,
|
||||
)]
|
||||
);
|
||||
assert_eq!(all_expanded_hunks, Vec::new());
|
||||
@@ -2360,7 +2330,7 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
|
||||
let blame = editor_b.blame().expect("editor_b should have blame now");
|
||||
let entries = blame.update(cx, |blame, cx| {
|
||||
blame
|
||||
.blame_for_rows((0..4).map(MultiBufferRow).map(Some), cx)
|
||||
.blame_for_rows((0..4).map(Some), cx)
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
|
||||
@@ -2399,7 +2369,7 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
|
||||
let blame = editor_b.blame().expect("editor_b should have blame now");
|
||||
let entries = blame.update(cx, |blame, cx| {
|
||||
blame
|
||||
.blame_for_rows((0..4).map(MultiBufferRow).map(Some), cx)
|
||||
.blame_for_rows((0..4).map(Some), cx)
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
|
||||
@@ -2426,7 +2396,7 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
|
||||
let blame = editor_b.blame().expect("editor_b should have blame now");
|
||||
let entries = blame.update(cx, |blame, cx| {
|
||||
blame
|
||||
.blame_for_rows((0..4).map(MultiBufferRow).map(Some), cx)
|
||||
.blame_for_rows((0..4).map(Some), cx)
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ use gpui::{
|
||||
MouseDownEvent, TestAppContext,
|
||||
};
|
||||
use language::{
|
||||
language_settings::{AllLanguageSettings, Formatter, PrettierSettings},
|
||||
language_settings::{AllLanguageSettings, Formatter},
|
||||
tree_sitter_rust, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language, LanguageConfig,
|
||||
LanguageMatcher, LineEnding, OffsetRangeExt, Point, Rope,
|
||||
};
|
||||
@@ -4445,17 +4445,18 @@ async fn test_prettier_formatting_buffer(
|
||||
|
||||
client_a.language_registry().add(Arc::new(Language::new(
|
||||
LanguageConfig {
|
||||
name: "TypeScript".into(),
|
||||
name: "Rust".into(),
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["ts".to_string()],
|
||||
path_suffixes: vec!["rs".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
prettier_parser_name: Some("test_parser".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
Some(tree_sitter_rust::language()),
|
||||
)));
|
||||
let mut fake_language_servers = client_a.language_registry().register_fake_lsp_adapter(
|
||||
"TypeScript",
|
||||
"Rust",
|
||||
FakeLspAdapter {
|
||||
prettier_plugins: vec![test_plugin],
|
||||
..Default::default()
|
||||
@@ -4469,11 +4470,11 @@ async fn test_prettier_formatting_buffer(
|
||||
let buffer_text = "let one = \"two\"";
|
||||
client_a
|
||||
.fs()
|
||||
.insert_tree(&directory, json!({ "a.ts": buffer_text }))
|
||||
.insert_tree(&directory, json!({ "a.rs": buffer_text }))
|
||||
.await;
|
||||
let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
|
||||
let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
|
||||
let open_buffer = project_a.update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.ts"), cx));
|
||||
let open_buffer = project_a.update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
|
||||
let buffer_a = cx_a.executor().spawn(open_buffer).await.unwrap();
|
||||
|
||||
let project_id = active_call_a
|
||||
@@ -4481,17 +4482,13 @@ async fn test_prettier_formatting_buffer(
|
||||
.await
|
||||
.unwrap();
|
||||
let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
|
||||
let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.ts"), cx));
|
||||
let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx));
|
||||
let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap();
|
||||
|
||||
cx_a.update(|cx| {
|
||||
cx.update_global(|store: &mut SettingsStore, cx| {
|
||||
store.update_user_settings::<AllLanguageSettings>(cx, |file| {
|
||||
file.defaults.formatter = Some(Formatter::Auto);
|
||||
file.defaults.prettier = Some(PrettierSettings {
|
||||
allowed: true,
|
||||
..PrettierSettings::default()
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -4499,10 +4496,6 @@ async fn test_prettier_formatting_buffer(
|
||||
cx.update_global(|store: &mut SettingsStore, cx| {
|
||||
store.update_user_settings::<AllLanguageSettings>(cx, |file| {
|
||||
file.defaults.formatter = Some(Formatter::LanguageServer);
|
||||
file.defaults.prettier = Some(PrettierSettings {
|
||||
allowed: true,
|
||||
..PrettierSettings::default()
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,7 +19,6 @@ use fs::FakeFs;
|
||||
use futures::{channel::oneshot, StreamExt as _};
|
||||
use git::GitHostingProviderRegistry;
|
||||
use gpui::{BackgroundExecutor, Context, Model, Task, TestAppContext, View, VisualTestContext};
|
||||
use http::FakeHttpClient;
|
||||
use language::LanguageRegistry;
|
||||
use node_runtime::FakeNodeRuntime;
|
||||
use notifications::NotificationStore;
|
||||
@@ -42,6 +41,7 @@ use std::{
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
use util::http::FakeHttpClient;
|
||||
use workspace::{Workspace, WorkspaceId, WorkspaceStore};
|
||||
|
||||
pub struct TestServer {
|
||||
|
||||
@@ -25,7 +25,6 @@ test-support = [
|
||||
"settings/test-support",
|
||||
"util/test-support",
|
||||
"workspace/test-support",
|
||||
"http/test-support",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
@@ -85,5 +84,4 @@ rpc = { workspace = true, features = ["test-support"] }
|
||||
settings = { workspace = true, features = ["test-support"] }
|
||||
tree-sitter-markdown.workspace = true
|
||||
util = { workspace = true, features = ["test-support"] }
|
||||
http = { workspace = true, features = ["test-support"] }
|
||||
workspace = { workspace = true, features = ["test-support"] }
|
||||
|
||||
@@ -557,12 +557,11 @@ mod tests {
|
||||
use client::{Client, User, UserStore};
|
||||
use clock::FakeSystemClock;
|
||||
use gpui::TestAppContext;
|
||||
use http::FakeHttpClient;
|
||||
use language::{Language, LanguageConfig};
|
||||
use project::Project;
|
||||
use rpc::proto;
|
||||
use settings::SettingsStore;
|
||||
use util::test::marked_text_ranges;
|
||||
use util::{http::FakeHttpClient, test::marked_text_ranges};
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_message_editor(cx: &mut TestAppContext) {
|
||||
|
||||
@@ -58,7 +58,7 @@ impl Render for CollabTitlebarItem {
|
||||
let project_id = self.project.read(cx).remote_id();
|
||||
let workspace = self.workspace.upgrade();
|
||||
|
||||
TitleBar::new("collab-titlebar", Box::new(workspace::CloseWindow))
|
||||
TitleBar::new("collab-titlebar")
|
||||
// note: on windows titlebar behaviour is handled by the platform implementation
|
||||
.when(cfg!(not(windows)), |this| {
|
||||
this.on_click(|event, cx| {
|
||||
@@ -73,8 +73,7 @@ impl Render for CollabTitlebarItem {
|
||||
.gap_1()
|
||||
.children(self.render_project_host(cx))
|
||||
.child(self.render_project_name(cx))
|
||||
.children(self.render_project_branch(cx))
|
||||
.on_mouse_move(|_, cx| cx.stop_propagation()),
|
||||
.children(self.render_project_branch(cx)),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
@@ -106,7 +105,6 @@ impl Render for CollabTitlebarItem {
|
||||
|
||||
this.children(current_user_face_pile.map(|face_pile| {
|
||||
v_flex()
|
||||
.on_mouse_move(|_, cx| cx.stop_propagation())
|
||||
.child(face_pile)
|
||||
.child(render_color_ribbon(player_colors.local().cursor))
|
||||
}))
|
||||
@@ -169,7 +167,6 @@ impl Render for CollabTitlebarItem {
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.pr_1()
|
||||
.on_mouse_move(|_, cx| cx.stop_propagation())
|
||||
.when_some(room, |this, room| {
|
||||
let room = room.read(cx);
|
||||
let project = self.project.read(cx);
|
||||
|
||||
@@ -32,7 +32,6 @@ command_palette_hooks.workspace = true
|
||||
editor.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
http.workspace = true
|
||||
language.workspace = true
|
||||
lsp.workspace = true
|
||||
menu.workspace = true
|
||||
@@ -64,4 +63,3 @@ rpc = { workspace = true, features = ["test-support"] }
|
||||
settings = { workspace = true, features = ["test-support"] }
|
||||
theme = { workspace = true, features = ["test-support"] }
|
||||
util = { workspace = true, features = ["test-support"] }
|
||||
http = { workspace = true, features = ["test-support"] }
|
||||
|
||||
@@ -12,8 +12,6 @@ use gpui::{
|
||||
actions, AppContext, AsyncAppContext, Context, Entity, EntityId, EventEmitter, Global, Model,
|
||||
ModelContext, Task, WeakModel,
|
||||
};
|
||||
use http::github::latest_github_release;
|
||||
use http::HttpClient;
|
||||
use language::{
|
||||
language_settings::{all_language_settings, language_settings, InlineCompletionProvider},
|
||||
point_from_lsp, point_to_lsp, Anchor, Bias, Buffer, BufferSnapshot, Language, PointUtf16,
|
||||
@@ -33,7 +31,9 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use util::{fs::remove_matching, maybe, paths, ResultExt};
|
||||
use util::{
|
||||
fs::remove_matching, github::latest_github_release, http::HttpClient, maybe, paths, ResultExt,
|
||||
};
|
||||
|
||||
pub use copilot_completion_provider::CopilotCompletionProvider;
|
||||
pub use sign_in::CopilotCodeVerification;
|
||||
@@ -393,7 +393,7 @@ impl Copilot {
|
||||
Default::default(),
|
||||
cx.to_async(),
|
||||
);
|
||||
let http = http::FakeHttpClient::create(|_| async { unreachable!() });
|
||||
let http = util::http::FakeHttpClient::create(|_| async { unreachable!() });
|
||||
let node_runtime = FakeNodeRuntime::new();
|
||||
let this = cx.new_model(|cx| Self {
|
||||
server_id: LanguageServerId(0),
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../../LICENSE-GPL
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::*;
|
||||
use collections::HashMap;
|
||||
use editor::{
|
||||
display_map::{BlockContext, DisplayRow, TransformBlock},
|
||||
display_map::{BlockContext, TransformBlock},
|
||||
DisplayPoint, GutterDimensions,
|
||||
};
|
||||
use gpui::{px, AvailableSpace, Stateful, TestAppContext, VisualTestContext};
|
||||
@@ -158,11 +158,11 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
|
||||
assert_eq!(
|
||||
editor_blocks(&editor, cx),
|
||||
[
|
||||
(DisplayRow(0), "path header block".into()),
|
||||
(DisplayRow(2), "diagnostic header".into()),
|
||||
(DisplayRow(15), "collapsed context".into()),
|
||||
(DisplayRow(16), "diagnostic header".into()),
|
||||
(DisplayRow(25), "collapsed context".into()),
|
||||
(0, "path header block".into()),
|
||||
(2, "diagnostic header".into()),
|
||||
(15, "collapsed context".into()),
|
||||
(16, "diagnostic header".into()),
|
||||
(25, "collapsed context".into()),
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -210,7 +210,7 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
|
||||
editor.update(cx, |editor, cx| {
|
||||
assert_eq!(
|
||||
editor.selections.display_ranges(cx),
|
||||
[DisplayPoint::new(DisplayRow(12), 6)..DisplayPoint::new(DisplayRow(12), 6)]
|
||||
[DisplayPoint::new(12, 6)..DisplayPoint::new(12, 6)]
|
||||
);
|
||||
});
|
||||
|
||||
@@ -243,13 +243,13 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
|
||||
assert_eq!(
|
||||
editor_blocks(&editor, cx),
|
||||
[
|
||||
(DisplayRow(0), "path header block".into()),
|
||||
(DisplayRow(2), "diagnostic header".into()),
|
||||
(DisplayRow(7), "path header block".into()),
|
||||
(DisplayRow(9), "diagnostic header".into()),
|
||||
(DisplayRow(22), "collapsed context".into()),
|
||||
(DisplayRow(23), "diagnostic header".into()),
|
||||
(DisplayRow(32), "collapsed context".into()),
|
||||
(0, "path header block".into()),
|
||||
(2, "diagnostic header".into()),
|
||||
(7, "path header block".into()),
|
||||
(9, "diagnostic header".into()),
|
||||
(22, "collapsed context".into()),
|
||||
(23, "diagnostic header".into()),
|
||||
(32, "collapsed context".into()),
|
||||
]
|
||||
);
|
||||
|
||||
@@ -309,7 +309,7 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
|
||||
editor.update(cx, |editor, cx| {
|
||||
assert_eq!(
|
||||
editor.selections.display_ranges(cx),
|
||||
[DisplayPoint::new(DisplayRow(19), 6)..DisplayPoint::new(DisplayRow(19), 6)]
|
||||
[DisplayPoint::new(19, 6)..DisplayPoint::new(19, 6)]
|
||||
);
|
||||
});
|
||||
|
||||
@@ -355,15 +355,15 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
|
||||
assert_eq!(
|
||||
editor_blocks(&editor, cx),
|
||||
[
|
||||
(DisplayRow(0), "path header block".into()),
|
||||
(DisplayRow(2), "diagnostic header".into()),
|
||||
(DisplayRow(7), "collapsed context".into()),
|
||||
(DisplayRow(8), "diagnostic header".into()),
|
||||
(DisplayRow(13), "path header block".into()),
|
||||
(DisplayRow(15), "diagnostic header".into()),
|
||||
(DisplayRow(28), "collapsed context".into()),
|
||||
(DisplayRow(29), "diagnostic header".into()),
|
||||
(DisplayRow(38), "collapsed context".into()),
|
||||
(0, "path header block".into()),
|
||||
(2, "diagnostic header".into()),
|
||||
(7, "collapsed context".into()),
|
||||
(8, "diagnostic header".into()),
|
||||
(13, "path header block".into()),
|
||||
(15, "diagnostic header".into()),
|
||||
(28, "collapsed context".into()),
|
||||
(29, "diagnostic header".into()),
|
||||
(38, "collapsed context".into()),
|
||||
]
|
||||
);
|
||||
|
||||
@@ -493,8 +493,8 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
|
||||
assert_eq!(
|
||||
editor_blocks(&editor, cx),
|
||||
[
|
||||
(DisplayRow(0), "path header block".into()),
|
||||
(DisplayRow(2), "diagnostic header".into()),
|
||||
(0, "path header block".into()),
|
||||
(2, "diagnostic header".into()),
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -539,10 +539,10 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
|
||||
assert_eq!(
|
||||
editor_blocks(&editor, cx),
|
||||
[
|
||||
(DisplayRow(0), "path header block".into()),
|
||||
(DisplayRow(2), "diagnostic header".into()),
|
||||
(DisplayRow(6), "collapsed context".into()),
|
||||
(DisplayRow(7), "diagnostic header".into()),
|
||||
(0, "path header block".into()),
|
||||
(2, "diagnostic header".into()),
|
||||
(6, "collapsed context".into()),
|
||||
(7, "diagnostic header".into()),
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -605,10 +605,10 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
|
||||
assert_eq!(
|
||||
editor_blocks(&editor, cx),
|
||||
[
|
||||
(DisplayRow(0), "path header block".into()),
|
||||
(DisplayRow(2), "diagnostic header".into()),
|
||||
(DisplayRow(7), "collapsed context".into()),
|
||||
(DisplayRow(8), "diagnostic header".into()),
|
||||
(0, "path header block".into()),
|
||||
(2, "diagnostic header".into()),
|
||||
(7, "collapsed context".into()),
|
||||
(8, "diagnostic header".into()),
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -661,10 +661,10 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
|
||||
assert_eq!(
|
||||
editor_blocks(&editor, cx),
|
||||
[
|
||||
(DisplayRow(0), "path header block".into()),
|
||||
(DisplayRow(2), "diagnostic header".into()),
|
||||
(DisplayRow(7), "collapsed context".into()),
|
||||
(DisplayRow(8), "diagnostic header".into()),
|
||||
(0, "path header block".into()),
|
||||
(2, "diagnostic header".into()),
|
||||
(7, "collapsed context".into()),
|
||||
(8, "diagnostic header".into()),
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -958,17 +958,14 @@ fn random_diagnostic(
|
||||
}
|
||||
}
|
||||
|
||||
fn editor_blocks(
|
||||
editor: &View<Editor>,
|
||||
cx: &mut VisualTestContext,
|
||||
) -> Vec<(DisplayRow, SharedString)> {
|
||||
fn editor_blocks(editor: &View<Editor>, cx: &mut VisualTestContext) -> Vec<(u32, SharedString)> {
|
||||
let mut blocks = Vec::new();
|
||||
cx.draw(gpui::Point::default(), AvailableSpace::min_size(), |cx| {
|
||||
editor.update(cx, |editor, cx| {
|
||||
let snapshot = editor.snapshot(cx);
|
||||
blocks.extend(
|
||||
snapshot
|
||||
.blocks_in_range(DisplayRow(0)..snapshot.max_point().row())
|
||||
.blocks_in_range(0..snapshot.max_point().row())
|
||||
.enumerate()
|
||||
.filter_map(|(ix, (row, block))| {
|
||||
let name: SharedString = match block {
|
||||
|
||||
@@ -39,7 +39,6 @@ futures.workspace = true
|
||||
fuzzy.workspace = true
|
||||
git.workspace = true
|
||||
gpui.workspace = true
|
||||
http.workspace = true
|
||||
indoc.workspace = true
|
||||
itertools.workspace = true
|
||||
language.workspace = true
|
||||
@@ -92,4 +91,3 @@ tree-sitter-typescript.workspace = true
|
||||
unindent.workspace = true
|
||||
util = { workspace = true, features = ["test-support"] }
|
||||
workspace = { workspace = true, features = ["test-support"] }
|
||||
http = { workspace = true, features = ["test-support"] }
|
||||
|
||||
@@ -54,7 +54,7 @@ pub struct SelectToEndOfLine {
|
||||
pub struct ToggleCodeActions {
|
||||
// Display row from which the action was deployed.
|
||||
#[serde(default)]
|
||||
pub deployed_from_indicator: Option<DisplayRow>,
|
||||
pub deployed_from_indicator: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default)]
|
||||
@@ -77,12 +77,12 @@ pub struct ToggleComments {
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default)]
|
||||
pub struct FoldAt {
|
||||
pub buffer_row: MultiBufferRow,
|
||||
pub buffer_row: u32,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default)]
|
||||
pub struct UnfoldAt {
|
||||
pub buffer_row: MultiBufferRow,
|
||||
pub buffer_row: u32,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default)]
|
||||
|
||||
@@ -23,8 +23,8 @@ mod inlay_map;
|
||||
mod tab_map;
|
||||
mod wrap_map;
|
||||
|
||||
use crate::EditorStyle;
|
||||
use crate::{hover_links::InlayHighlight, movement::TextLayoutDetails, InlayId};
|
||||
use crate::{EditorStyle, RowExt};
|
||||
pub use block_map::{BlockMap, BlockPoint};
|
||||
use collections::{HashMap, HashSet};
|
||||
use fold_map::FoldMap;
|
||||
@@ -34,11 +34,7 @@ use language::{
|
||||
language_settings::language_settings, OffsetUtf16, Point, Subscription as BufferSubscription,
|
||||
};
|
||||
use lsp::DiagnosticSeverity;
|
||||
use multi_buffer::{
|
||||
Anchor, AnchorRangeExt, MultiBuffer, MultiBufferPoint, MultiBufferRow, MultiBufferSnapshot,
|
||||
ToOffset, ToPoint,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use multi_buffer::{Anchor, AnchorRangeExt, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint};
|
||||
use std::{any::TypeId, borrow::Cow, fmt::Debug, num::NonZeroU32, ops::Range, sync::Arc};
|
||||
use sum_tree::{Bias, TreeMap};
|
||||
use tab_map::TabMap;
|
||||
@@ -46,11 +42,10 @@ use tab_map::TabMap;
|
||||
use wrap_map::WrapMap;
|
||||
|
||||
pub use block_map::{
|
||||
BlockBufferRows, BlockChunks as DisplayChunks, BlockContext, BlockDisposition, BlockId,
|
||||
BlockProperties, BlockStyle, RenderBlock, TransformBlock,
|
||||
BlockBufferRows as DisplayBufferRows, BlockChunks as DisplayChunks, BlockContext,
|
||||
BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock, TransformBlock,
|
||||
};
|
||||
|
||||
use self::block_map::BlockRow;
|
||||
pub use self::fold_map::{Fold, FoldId, FoldPoint};
|
||||
pub use self::inlay_map::{InlayOffset, InlayPoint};
|
||||
pub(crate) use inlay_map::Inlay;
|
||||
@@ -387,20 +382,15 @@ impl DisplaySnapshot {
|
||||
self.buffer_snapshot.len() == 0
|
||||
}
|
||||
|
||||
pub fn buffer_rows(
|
||||
&self,
|
||||
start_row: DisplayRow,
|
||||
) -> impl Iterator<Item = Option<MultiBufferRow>> + '_ {
|
||||
self.block_snapshot
|
||||
.buffer_rows(BlockRow(start_row.0))
|
||||
.map(|row| row.map(|row| MultiBufferRow(row.0)))
|
||||
pub fn buffer_rows(&self, start_row: u32) -> DisplayBufferRows {
|
||||
self.block_snapshot.buffer_rows(start_row)
|
||||
}
|
||||
|
||||
pub fn max_buffer_row(&self) -> MultiBufferRow {
|
||||
pub fn max_buffer_row(&self) -> u32 {
|
||||
self.buffer_snapshot.max_buffer_row()
|
||||
}
|
||||
|
||||
pub fn prev_line_boundary(&self, mut point: MultiBufferPoint) -> (Point, DisplayPoint) {
|
||||
pub fn prev_line_boundary(&self, mut point: Point) -> (Point, DisplayPoint) {
|
||||
loop {
|
||||
let mut inlay_point = self.inlay_snapshot.to_inlay_point(point);
|
||||
let mut fold_point = self.fold_snapshot.to_fold_point(inlay_point, Bias::Left);
|
||||
@@ -418,7 +408,7 @@ impl DisplaySnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_line_boundary(&self, mut point: MultiBufferPoint) -> (Point, DisplayPoint) {
|
||||
pub fn next_line_boundary(&self, mut point: Point) -> (Point, DisplayPoint) {
|
||||
loop {
|
||||
let mut inlay_point = self.inlay_snapshot.to_inlay_point(point);
|
||||
let mut fold_point = self.fold_snapshot.to_fold_point(inlay_point, Bias::Right);
|
||||
@@ -439,14 +429,13 @@ impl DisplaySnapshot {
|
||||
// used by line_mode selections and tries to match vim behaviour
|
||||
pub fn expand_to_line(&self, range: Range<Point>) -> Range<Point> {
|
||||
let new_start = if range.start.row == 0 {
|
||||
MultiBufferPoint::new(0, 0)
|
||||
} else if range.start.row == self.max_buffer_row().0
|
||||
|| (range.end.column > 0 && range.end.row == self.max_buffer_row().0)
|
||||
Point::new(0, 0)
|
||||
} else if range.start.row == self.max_buffer_row()
|
||||
|| (range.end.column > 0 && range.end.row == self.max_buffer_row())
|
||||
{
|
||||
MultiBufferPoint::new(
|
||||
Point::new(
|
||||
range.start.row - 1,
|
||||
self.buffer_snapshot
|
||||
.line_len(MultiBufferRow(range.start.row - 1)),
|
||||
self.buffer_snapshot.line_len(range.start.row - 1),
|
||||
)
|
||||
} else {
|
||||
self.prev_line_boundary(range.start).0
|
||||
@@ -454,9 +443,9 @@ impl DisplaySnapshot {
|
||||
|
||||
let new_end = if range.end.column == 0 {
|
||||
range.end
|
||||
} else if range.end.row < self.max_buffer_row().0 {
|
||||
} else if range.end.row < self.max_buffer_row() {
|
||||
self.buffer_snapshot
|
||||
.clip_point(MultiBufferPoint::new(range.end.row + 1, 0), Bias::Left)
|
||||
.clip_point(Point::new(range.end.row + 1, 0), Bias::Left)
|
||||
} else {
|
||||
self.buffer_snapshot.max_point()
|
||||
};
|
||||
@@ -464,7 +453,7 @@ impl DisplaySnapshot {
|
||||
new_start..new_end
|
||||
}
|
||||
|
||||
fn point_to_display_point(&self, point: MultiBufferPoint, bias: Bias) -> DisplayPoint {
|
||||
fn point_to_display_point(&self, point: Point, bias: Bias) -> DisplayPoint {
|
||||
let inlay_point = self.inlay_snapshot.to_inlay_point(point);
|
||||
let fold_point = self.fold_snapshot.to_fold_point(inlay_point, bias);
|
||||
let tab_point = self.tab_snapshot.to_tab_point(fold_point);
|
||||
@@ -520,10 +509,10 @@ impl DisplaySnapshot {
|
||||
}
|
||||
|
||||
/// Returns text chunks starting at the given display row until the end of the file
|
||||
pub fn text_chunks(&self, display_row: DisplayRow) -> impl Iterator<Item = &str> {
|
||||
pub fn text_chunks(&self, display_row: u32) -> impl Iterator<Item = &str> {
|
||||
self.block_snapshot
|
||||
.chunks(
|
||||
display_row.0..self.max_point().row().next_row().0,
|
||||
display_row..self.max_point().row() + 1,
|
||||
false,
|
||||
Highlights::default(),
|
||||
)
|
||||
@@ -531,8 +520,8 @@ impl DisplaySnapshot {
|
||||
}
|
||||
|
||||
/// Returns text chunks starting at the end of the given display row in reverse until the start of the file
|
||||
pub fn reverse_text_chunks(&self, display_row: DisplayRow) -> impl Iterator<Item = &str> {
|
||||
(0..=display_row.0).rev().flat_map(|row| {
|
||||
pub fn reverse_text_chunks(&self, display_row: u32) -> impl Iterator<Item = &str> {
|
||||
(0..=display_row).rev().flat_map(|row| {
|
||||
self.block_snapshot
|
||||
.chunks(row..row + 1, false, Highlights::default())
|
||||
.map(|h| h.text)
|
||||
@@ -544,12 +533,12 @@ impl DisplaySnapshot {
|
||||
|
||||
pub fn chunks(
|
||||
&self,
|
||||
display_rows: Range<DisplayRow>,
|
||||
display_rows: Range<u32>,
|
||||
language_aware: bool,
|
||||
highlight_styles: HighlightStyles,
|
||||
) -> DisplayChunks<'_> {
|
||||
self.block_snapshot.chunks(
|
||||
display_rows.start.0..display_rows.end.0,
|
||||
display_rows,
|
||||
language_aware,
|
||||
Highlights {
|
||||
text_highlights: Some(&self.text_highlights),
|
||||
@@ -561,7 +550,7 @@ impl DisplaySnapshot {
|
||||
|
||||
pub fn highlighted_chunks<'a>(
|
||||
&'a self,
|
||||
display_rows: Range<DisplayRow>,
|
||||
display_rows: Range<u32>,
|
||||
language_aware: bool,
|
||||
editor_style: &'a EditorStyle,
|
||||
) -> impl Iterator<Item = HighlightedChunk<'a>> {
|
||||
@@ -621,7 +610,7 @@ impl DisplaySnapshot {
|
||||
|
||||
pub fn layout_row(
|
||||
&self,
|
||||
display_row: DisplayRow,
|
||||
display_row: u32,
|
||||
TextLayoutDetails {
|
||||
text_system,
|
||||
editor_style,
|
||||
@@ -634,7 +623,7 @@ impl DisplaySnapshot {
|
||||
let mut runs = Vec::new();
|
||||
let mut line = String::new();
|
||||
|
||||
let range = display_row..display_row.next_row();
|
||||
let range = display_row..display_row + 1;
|
||||
for chunk in self.highlighted_chunks(range, false, &editor_style) {
|
||||
line.push_str(chunk.chunk);
|
||||
|
||||
@@ -674,7 +663,7 @@ impl DisplaySnapshot {
|
||||
|
||||
pub fn display_column_for_x(
|
||||
&self,
|
||||
display_row: DisplayRow,
|
||||
display_row: u32,
|
||||
x: Pixels,
|
||||
details: &TextLayoutDetails,
|
||||
) -> u32 {
|
||||
@@ -743,7 +732,7 @@ impl DisplaySnapshot {
|
||||
|
||||
pub fn clip_at_line_end(&self, point: DisplayPoint) -> DisplayPoint {
|
||||
let mut point = point.0;
|
||||
if point.column == self.line_len(DisplayRow(point.row)) {
|
||||
if point.column == self.line_len(point.row) {
|
||||
point.column = point.column.saturating_sub(1);
|
||||
point = self.block_snapshot.clip_point(point, Bias::Left);
|
||||
}
|
||||
@@ -759,38 +748,36 @@ impl DisplaySnapshot {
|
||||
|
||||
pub fn blocks_in_range(
|
||||
&self,
|
||||
rows: Range<DisplayRow>,
|
||||
) -> impl Iterator<Item = (DisplayRow, &TransformBlock)> {
|
||||
self.block_snapshot
|
||||
.blocks_in_range(rows.start.0..rows.end.0)
|
||||
.map(|(row, block)| (DisplayRow(row), block))
|
||||
rows: Range<u32>,
|
||||
) -> impl Iterator<Item = (u32, &TransformBlock)> {
|
||||
self.block_snapshot.blocks_in_range(rows)
|
||||
}
|
||||
|
||||
pub fn intersects_fold<T: ToOffset>(&self, offset: T) -> bool {
|
||||
self.fold_snapshot.intersects_fold(offset)
|
||||
}
|
||||
|
||||
pub fn is_line_folded(&self, buffer_row: MultiBufferRow) -> bool {
|
||||
pub fn is_line_folded(&self, buffer_row: u32) -> bool {
|
||||
self.fold_snapshot.is_line_folded(buffer_row)
|
||||
}
|
||||
|
||||
pub fn is_block_line(&self, display_row: DisplayRow) -> bool {
|
||||
self.block_snapshot.is_block_line(BlockRow(display_row.0))
|
||||
pub fn is_block_line(&self, display_row: u32) -> bool {
|
||||
self.block_snapshot.is_block_line(display_row)
|
||||
}
|
||||
|
||||
pub fn soft_wrap_indent(&self, display_row: DisplayRow) -> Option<u32> {
|
||||
pub fn soft_wrap_indent(&self, display_row: u32) -> Option<u32> {
|
||||
let wrap_row = self
|
||||
.block_snapshot
|
||||
.to_wrap_point(BlockPoint::new(display_row.0, 0))
|
||||
.to_wrap_point(BlockPoint::new(display_row, 0))
|
||||
.row();
|
||||
self.wrap_snapshot.soft_wrap_indent(wrap_row)
|
||||
}
|
||||
|
||||
pub fn text(&self) -> String {
|
||||
self.text_chunks(DisplayRow(0)).collect()
|
||||
self.text_chunks(0).collect()
|
||||
}
|
||||
|
||||
pub fn line(&self, display_row: DisplayRow) -> String {
|
||||
pub fn line(&self, display_row: u32) -> String {
|
||||
let mut result = String::new();
|
||||
for chunk in self.text_chunks(display_row) {
|
||||
if let Some(ix) = chunk.find('\n') {
|
||||
@@ -803,7 +790,7 @@ impl DisplaySnapshot {
|
||||
result
|
||||
}
|
||||
|
||||
pub fn line_indent_for_buffer_row(&self, buffer_row: MultiBufferRow) -> (u32, bool) {
|
||||
pub fn line_indent_for_buffer_row(&self, buffer_row: u32) -> (u32, bool) {
|
||||
let (buffer, range) = self
|
||||
.buffer_snapshot
|
||||
.buffer_line_for_row(buffer_row)
|
||||
@@ -825,15 +812,15 @@ impl DisplaySnapshot {
|
||||
(indent_size, is_blank)
|
||||
}
|
||||
|
||||
pub fn line_len(&self, row: DisplayRow) -> u32 {
|
||||
self.block_snapshot.line_len(BlockRow(row.0))
|
||||
pub fn line_len(&self, row: u32) -> u32 {
|
||||
self.block_snapshot.line_len(row)
|
||||
}
|
||||
|
||||
pub fn longest_row(&self) -> DisplayRow {
|
||||
DisplayRow(self.block_snapshot.longest_row())
|
||||
pub fn longest_row(&self) -> u32 {
|
||||
self.block_snapshot.longest_row()
|
||||
}
|
||||
|
||||
pub fn fold_for_line(&self, buffer_row: MultiBufferRow) -> Option<FoldStatus> {
|
||||
pub fn fold_for_line(&self, buffer_row: u32) -> Option<FoldStatus> {
|
||||
if self.is_line_folded(buffer_row) {
|
||||
Some(FoldStatus::Folded)
|
||||
} else if self.is_foldable(buffer_row) {
|
||||
@@ -843,7 +830,7 @@ impl DisplaySnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_foldable(&self, buffer_row: MultiBufferRow) -> bool {
|
||||
pub fn is_foldable(&self, buffer_row: u32) -> bool {
|
||||
let max_row = self.buffer_snapshot.max_buffer_row();
|
||||
if buffer_row >= max_row {
|
||||
return false;
|
||||
@@ -854,9 +841,8 @@ impl DisplaySnapshot {
|
||||
return false;
|
||||
}
|
||||
|
||||
for next_row in (buffer_row.0 + 1)..=max_row.0 {
|
||||
let (next_indent_size, next_line_is_blank) =
|
||||
self.line_indent_for_buffer_row(MultiBufferRow(next_row));
|
||||
for next_row in (buffer_row + 1)..=max_row {
|
||||
let (next_indent_size, next_line_is_blank) = self.line_indent_for_buffer_row(next_row);
|
||||
if next_indent_size > indent_size {
|
||||
return true;
|
||||
} else if !next_line_is_blank {
|
||||
@@ -867,22 +853,20 @@ impl DisplaySnapshot {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn foldable_range(&self, buffer_row: MultiBufferRow) -> Option<Range<Point>> {
|
||||
let start = MultiBufferPoint::new(buffer_row.0, self.buffer_snapshot.line_len(buffer_row));
|
||||
if self.is_foldable(MultiBufferRow(start.row))
|
||||
&& !self.is_line_folded(MultiBufferRow(start.row))
|
||||
{
|
||||
pub fn foldable_range(&self, buffer_row: u32) -> Option<Range<Point>> {
|
||||
let start = Point::new(buffer_row, self.buffer_snapshot.line_len(buffer_row));
|
||||
if self.is_foldable(start.row) && !self.is_line_folded(start.row) {
|
||||
let (start_indent, _) = self.line_indent_for_buffer_row(buffer_row);
|
||||
let max_point = self.buffer_snapshot.max_point();
|
||||
let mut end = None;
|
||||
|
||||
for row in (buffer_row.0 + 1)..=max_point.row {
|
||||
let (indent, is_blank) = self.line_indent_for_buffer_row(MultiBufferRow(row));
|
||||
for row in (buffer_row + 1)..=max_point.row {
|
||||
let (indent, is_blank) = self.line_indent_for_buffer_row(row);
|
||||
if !is_blank && indent <= start_indent {
|
||||
let prev_row = row - 1;
|
||||
end = Some(Point::new(
|
||||
prev_row,
|
||||
self.buffer_snapshot.line_len(MultiBufferRow(prev_row)),
|
||||
self.buffer_snapshot.line_len(prev_row),
|
||||
));
|
||||
break;
|
||||
}
|
||||
@@ -919,31 +903,27 @@ impl Debug for DisplayPoint {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_fmt(format_args!(
|
||||
"DisplayPoint({}, {})",
|
||||
self.row().0,
|
||||
self.row(),
|
||||
self.column()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Default, Eq, Ord, PartialOrd, PartialEq, Deserialize, Hash)]
|
||||
#[serde(transparent)]
|
||||
pub struct DisplayRow(pub u32);
|
||||
|
||||
impl DisplayPoint {
|
||||
pub fn new(row: DisplayRow, column: u32) -> Self {
|
||||
Self(BlockPoint(Point::new(row.0, column)))
|
||||
pub fn new(row: u32, column: u32) -> Self {
|
||||
Self(BlockPoint(Point::new(row, column)))
|
||||
}
|
||||
|
||||
pub fn zero() -> Self {
|
||||
Self::new(DisplayRow(0), 0)
|
||||
Self::new(0, 0)
|
||||
}
|
||||
|
||||
pub fn is_zero(&self) -> bool {
|
||||
self.0.is_zero()
|
||||
}
|
||||
|
||||
pub fn row(self) -> DisplayRow {
|
||||
DisplayRow(self.0.row)
|
||||
pub fn row(self) -> u32 {
|
||||
self.0.row
|
||||
}
|
||||
|
||||
pub fn column(self) -> u32 {
|
||||
@@ -1191,7 +1171,7 @@ pub mod tests {
|
||||
let buffer = &snapshot.buffer_snapshot;
|
||||
for _ in 0..5 {
|
||||
let row = rng.gen_range(0..=buffer.max_point().row);
|
||||
let column = rng.gen_range(0..=buffer.line_len(MultiBufferRow(row)));
|
||||
let column = rng.gen_range(0..=buffer.line_len(row));
|
||||
let point = buffer.clip_point(Point::new(row, column), Left);
|
||||
|
||||
let (prev_buffer_bound, prev_display_bound) = snapshot.prev_line_boundary(point);
|
||||
@@ -1236,12 +1216,12 @@ pub mod tests {
|
||||
}
|
||||
|
||||
// Movement
|
||||
let min_point = snapshot.clip_point(DisplayPoint::new(DisplayRow(0), 0), Left);
|
||||
let min_point = snapshot.clip_point(DisplayPoint::new(0, 0), Left);
|
||||
let max_point = snapshot.clip_point(snapshot.max_point(), Right);
|
||||
for _ in 0..5 {
|
||||
let row = rng.gen_range(0..=snapshot.max_point().row().0);
|
||||
let column = rng.gen_range(0..=snapshot.line_len(DisplayRow(row)));
|
||||
let point = snapshot.clip_point(DisplayPoint::new(DisplayRow(row), column), Left);
|
||||
let row = rng.gen_range(0..=snapshot.max_point().row());
|
||||
let column = rng.gen_range(0..=snapshot.line_len(row));
|
||||
let point = snapshot.clip_point(DisplayPoint::new(row, column), Left);
|
||||
|
||||
log::info!("Moving from point {:?}", point);
|
||||
|
||||
@@ -1308,64 +1288,63 @@ pub mod tests {
|
||||
|
||||
let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
|
||||
assert_eq!(
|
||||
snapshot.text_chunks(DisplayRow(0)).collect::<String>(),
|
||||
snapshot.text_chunks(0).collect::<String>(),
|
||||
"one two \nthree four \nfive\nsix seven \neight"
|
||||
);
|
||||
assert_eq!(
|
||||
snapshot.clip_point(DisplayPoint::new(DisplayRow(0), 8), Bias::Left),
|
||||
DisplayPoint::new(DisplayRow(0), 7)
|
||||
snapshot.clip_point(DisplayPoint::new(0, 8), Bias::Left),
|
||||
DisplayPoint::new(0, 7)
|
||||
);
|
||||
assert_eq!(
|
||||
snapshot.clip_point(DisplayPoint::new(DisplayRow(0), 8), Bias::Right),
|
||||
DisplayPoint::new(DisplayRow(1), 0)
|
||||
snapshot.clip_point(DisplayPoint::new(0, 8), Bias::Right),
|
||||
DisplayPoint::new(1, 0)
|
||||
);
|
||||
assert_eq!(
|
||||
movement::right(&snapshot, DisplayPoint::new(DisplayRow(0), 7)),
|
||||
DisplayPoint::new(DisplayRow(1), 0)
|
||||
movement::right(&snapshot, DisplayPoint::new(0, 7)),
|
||||
DisplayPoint::new(1, 0)
|
||||
);
|
||||
assert_eq!(
|
||||
movement::left(&snapshot, DisplayPoint::new(DisplayRow(1), 0)),
|
||||
DisplayPoint::new(DisplayRow(0), 7)
|
||||
movement::left(&snapshot, DisplayPoint::new(1, 0)),
|
||||
DisplayPoint::new(0, 7)
|
||||
);
|
||||
|
||||
let x = snapshot
|
||||
.x_for_display_point(DisplayPoint::new(DisplayRow(1), 10), &text_layout_details);
|
||||
let x = snapshot.x_for_display_point(DisplayPoint::new(1, 10), &text_layout_details);
|
||||
assert_eq!(
|
||||
movement::up(
|
||||
&snapshot,
|
||||
DisplayPoint::new(DisplayRow(1), 10),
|
||||
DisplayPoint::new(1, 10),
|
||||
SelectionGoal::None,
|
||||
false,
|
||||
&text_layout_details,
|
||||
),
|
||||
(
|
||||
DisplayPoint::new(DisplayRow(0), 7),
|
||||
DisplayPoint::new(0, 7),
|
||||
SelectionGoal::HorizontalPosition(x.0)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
movement::down(
|
||||
&snapshot,
|
||||
DisplayPoint::new(DisplayRow(0), 7),
|
||||
DisplayPoint::new(0, 7),
|
||||
SelectionGoal::HorizontalPosition(x.0),
|
||||
false,
|
||||
&text_layout_details
|
||||
),
|
||||
(
|
||||
DisplayPoint::new(DisplayRow(1), 10),
|
||||
DisplayPoint::new(1, 10),
|
||||
SelectionGoal::HorizontalPosition(x.0)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
movement::down(
|
||||
&snapshot,
|
||||
DisplayPoint::new(DisplayRow(1), 10),
|
||||
DisplayPoint::new(1, 10),
|
||||
SelectionGoal::HorizontalPosition(x.0),
|
||||
false,
|
||||
&text_layout_details
|
||||
),
|
||||
(
|
||||
DisplayPoint::new(DisplayRow(2), 4),
|
||||
DisplayPoint::new(2, 4),
|
||||
SelectionGoal::HorizontalPosition(x.0)
|
||||
)
|
||||
);
|
||||
@@ -1377,7 +1356,7 @@ pub mod tests {
|
||||
|
||||
let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
|
||||
assert_eq!(
|
||||
snapshot.text_chunks(DisplayRow(1)).collect::<String>(),
|
||||
snapshot.text_chunks(1).collect::<String>(),
|
||||
"three four \nfive\nsix and \nseven eight"
|
||||
);
|
||||
|
||||
@@ -1388,7 +1367,7 @@ pub mod tests {
|
||||
|
||||
let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
|
||||
assert_eq!(
|
||||
snapshot.text_chunks(DisplayRow(1)).collect::<String>(),
|
||||
snapshot.text_chunks(1).collect::<String>(),
|
||||
"three \nfour five\nsix and \nseven \neight"
|
||||
)
|
||||
});
|
||||
@@ -1409,18 +1388,9 @@ pub mod tests {
|
||||
buffer.update(cx, |buffer, cx| {
|
||||
buffer.edit(
|
||||
vec![
|
||||
(
|
||||
MultiBufferPoint::new(1, 0)..MultiBufferPoint::new(1, 0),
|
||||
"\t",
|
||||
),
|
||||
(
|
||||
MultiBufferPoint::new(1, 1)..MultiBufferPoint::new(1, 1),
|
||||
"\t",
|
||||
),
|
||||
(
|
||||
MultiBufferPoint::new(2, 1)..MultiBufferPoint::new(2, 1),
|
||||
"\t",
|
||||
),
|
||||
(Point::new(1, 0)..Point::new(1, 0), "\t"),
|
||||
(Point::new(1, 1)..Point::new(1, 1), "\t"),
|
||||
(Point::new(2, 1)..Point::new(2, 1), "\t"),
|
||||
],
|
||||
None,
|
||||
cx,
|
||||
@@ -1429,7 +1399,7 @@ pub mod tests {
|
||||
|
||||
assert_eq!(
|
||||
map.update(cx, |map, cx| map.snapshot(cx))
|
||||
.text_chunks(DisplayRow(1))
|
||||
.text_chunks(1)
|
||||
.collect::<String>()
|
||||
.lines()
|
||||
.next(),
|
||||
@@ -1437,7 +1407,7 @@ pub mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
map.update(cx, |map, cx| map.snapshot(cx))
|
||||
.text_chunks(DisplayRow(2))
|
||||
.text_chunks(2)
|
||||
.collect::<String>()
|
||||
.lines()
|
||||
.next(),
|
||||
@@ -1492,7 +1462,7 @@ pub mod tests {
|
||||
let map = cx
|
||||
.new_model(|cx| DisplayMap::new(buffer, font("Helvetica"), font_size, None, 1, 1, cx));
|
||||
assert_eq!(
|
||||
cx.update(|cx| syntax_chunks(DisplayRow(0)..DisplayRow(5), &map, &theme, cx)),
|
||||
cx.update(|cx| syntax_chunks(0..5, &map, &theme, cx)),
|
||||
vec![
|
||||
("fn ".to_string(), None),
|
||||
("outer".to_string(), Some(Hsla::blue())),
|
||||
@@ -1503,7 +1473,7 @@ pub mod tests {
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
cx.update(|cx| syntax_chunks(DisplayRow(3)..DisplayRow(5), &map, &theme, cx)),
|
||||
cx.update(|cx| syntax_chunks(3..5, &map, &theme, cx)),
|
||||
vec![
|
||||
(" fn ".to_string(), Some(Hsla::red())),
|
||||
("inner".to_string(), Some(Hsla::blue())),
|
||||
@@ -1512,13 +1482,10 @@ pub mod tests {
|
||||
);
|
||||
|
||||
map.update(cx, |map, cx| {
|
||||
map.fold(
|
||||
vec![MultiBufferPoint::new(0, 6)..MultiBufferPoint::new(3, 2)],
|
||||
cx,
|
||||
)
|
||||
map.fold(vec![Point::new(0, 6)..Point::new(3, 2)], cx)
|
||||
});
|
||||
assert_eq!(
|
||||
cx.update(|cx| syntax_chunks(DisplayRow(0)..DisplayRow(2), &map, &theme, cx)),
|
||||
cx.update(|cx| syntax_chunks(0..2, &map, &theme, cx)),
|
||||
vec![
|
||||
("fn ".to_string(), None),
|
||||
("out".to_string(), Some(Hsla::blue())),
|
||||
@@ -1581,7 +1548,7 @@ pub mod tests {
|
||||
DisplayMap::new(buffer, font("Courier"), font_size, Some(px(40.0)), 1, 1, cx)
|
||||
});
|
||||
assert_eq!(
|
||||
cx.update(|cx| syntax_chunks(DisplayRow(0)..DisplayRow(5), &map, &theme, cx)),
|
||||
cx.update(|cx| syntax_chunks(0..5, &map, &theme, cx)),
|
||||
[
|
||||
("fn \n".to_string(), None),
|
||||
("oute\nr".to_string(), Some(Hsla::blue())),
|
||||
@@ -1589,18 +1556,15 @@ pub mod tests {
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
cx.update(|cx| syntax_chunks(DisplayRow(3)..DisplayRow(5), &map, &theme, cx)),
|
||||
cx.update(|cx| syntax_chunks(3..5, &map, &theme, cx)),
|
||||
[("{}\n\n".to_string(), None)]
|
||||
);
|
||||
|
||||
map.update(cx, |map, cx| {
|
||||
map.fold(
|
||||
vec![MultiBufferPoint::new(0, 6)..MultiBufferPoint::new(3, 2)],
|
||||
cx,
|
||||
)
|
||||
map.fold(vec![Point::new(0, 6)..Point::new(3, 2)], cx)
|
||||
});
|
||||
assert_eq!(
|
||||
cx.update(|cx| syntax_chunks(DisplayRow(1)..DisplayRow(4), &map, &theme, cx)),
|
||||
cx.update(|cx| syntax_chunks(1..4, &map, &theme, cx)),
|
||||
[
|
||||
("out".to_string(), Some(Hsla::blue())),
|
||||
("⋯\n".to_string(), None),
|
||||
@@ -1672,7 +1636,7 @@ pub mod tests {
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
cx.update(|cx| chunks(DisplayRow(0)..DisplayRow(10), &map, &theme, cx)),
|
||||
cx.update(|cx| chunks(0..10, &map, &theme, cx)),
|
||||
[
|
||||
("const ".to_string(), None, None),
|
||||
("a".to_string(), None, Some(Hsla::blue())),
|
||||
@@ -1768,57 +1732,45 @@ pub mod tests {
|
||||
let map = map.update(cx, |map, cx| map.snapshot(cx));
|
||||
assert_eq!(map.text(), "✅ α\nβ \n🏀β γ");
|
||||
assert_eq!(
|
||||
map.text_chunks(DisplayRow(0)).collect::<String>(),
|
||||
map.text_chunks(0).collect::<String>(),
|
||||
"✅ α\nβ \n🏀β γ"
|
||||
);
|
||||
assert_eq!(
|
||||
map.text_chunks(DisplayRow(1)).collect::<String>(),
|
||||
"β \n🏀β γ"
|
||||
);
|
||||
assert_eq!(
|
||||
map.text_chunks(DisplayRow(2)).collect::<String>(),
|
||||
"🏀β γ"
|
||||
);
|
||||
assert_eq!(map.text_chunks(1).collect::<String>(), "β \n🏀β γ");
|
||||
assert_eq!(map.text_chunks(2).collect::<String>(), "🏀β γ");
|
||||
|
||||
let point = MultiBufferPoint::new(0, "✅\t\t".len() as u32);
|
||||
let display_point = DisplayPoint::new(DisplayRow(0), "✅ ".len() as u32);
|
||||
let point = Point::new(0, "✅\t\t".len() as u32);
|
||||
let display_point = DisplayPoint::new(0, "✅ ".len() as u32);
|
||||
assert_eq!(point.to_display_point(&map), display_point);
|
||||
assert_eq!(display_point.to_point(&map), point);
|
||||
|
||||
let point = MultiBufferPoint::new(1, "β\t".len() as u32);
|
||||
let display_point = DisplayPoint::new(DisplayRow(1), "β ".len() as u32);
|
||||
let point = Point::new(1, "β\t".len() as u32);
|
||||
let display_point = DisplayPoint::new(1, "β ".len() as u32);
|
||||
assert_eq!(point.to_display_point(&map), display_point);
|
||||
assert_eq!(display_point.to_point(&map), point,);
|
||||
|
||||
let point = MultiBufferPoint::new(2, "🏀β\t\t".len() as u32);
|
||||
let display_point = DisplayPoint::new(DisplayRow(2), "🏀β ".len() as u32);
|
||||
let point = Point::new(2, "🏀β\t\t".len() as u32);
|
||||
let display_point = DisplayPoint::new(2, "🏀β ".len() as u32);
|
||||
assert_eq!(point.to_display_point(&map), display_point);
|
||||
assert_eq!(display_point.to_point(&map), point,);
|
||||
|
||||
// Display points inside of expanded tabs
|
||||
assert_eq!(
|
||||
DisplayPoint::new(DisplayRow(0), "✅ ".len() as u32).to_point(&map),
|
||||
MultiBufferPoint::new(0, "✅\t".len() as u32),
|
||||
DisplayPoint::new(0, "✅ ".len() as u32).to_point(&map),
|
||||
Point::new(0, "✅\t".len() as u32),
|
||||
);
|
||||
assert_eq!(
|
||||
DisplayPoint::new(DisplayRow(0), "✅ ".len() as u32).to_point(&map),
|
||||
MultiBufferPoint::new(0, "✅".len() as u32),
|
||||
DisplayPoint::new(0, "✅ ".len() as u32).to_point(&map),
|
||||
Point::new(0, "✅".len() as u32),
|
||||
);
|
||||
|
||||
// Clipping display points inside of multi-byte characters
|
||||
assert_eq!(
|
||||
map.clip_point(
|
||||
DisplayPoint::new(DisplayRow(0), "✅".len() as u32 - 1),
|
||||
Left
|
||||
),
|
||||
DisplayPoint::new(DisplayRow(0), 0)
|
||||
map.clip_point(DisplayPoint::new(0, "✅".len() as u32 - 1), Left),
|
||||
DisplayPoint::new(0, 0)
|
||||
);
|
||||
assert_eq!(
|
||||
map.clip_point(
|
||||
DisplayPoint::new(DisplayRow(0), "✅".len() as u32 - 1),
|
||||
Bias::Right
|
||||
),
|
||||
DisplayPoint::new(DisplayRow(0), "✅".len() as u32)
|
||||
map.clip_point(DisplayPoint::new(0, "✅".len() as u32 - 1), Bias::Right),
|
||||
DisplayPoint::new(0, "✅".len() as u32)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1833,12 +1785,12 @@ pub mod tests {
|
||||
});
|
||||
assert_eq!(
|
||||
map.update(cx, |map, cx| map.snapshot(cx)).max_point(),
|
||||
DisplayPoint::new(DisplayRow(1), 11)
|
||||
DisplayPoint::new(1, 11)
|
||||
)
|
||||
}
|
||||
|
||||
fn syntax_chunks(
|
||||
rows: Range<DisplayRow>,
|
||||
rows: Range<u32>,
|
||||
map: &Model<DisplayMap>,
|
||||
theme: &SyntaxTheme,
|
||||
cx: &mut AppContext,
|
||||
@@ -1850,7 +1802,7 @@ pub mod tests {
|
||||
}
|
||||
|
||||
fn chunks(
|
||||
rows: Range<DisplayRow>,
|
||||
rows: Range<u32>,
|
||||
map: &Model<DisplayMap>,
|
||||
theme: &SyntaxTheme,
|
||||
cx: &mut AppContext,
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::{EditorStyle, GutterDimensions};
|
||||
use collections::{Bound, HashMap, HashSet};
|
||||
use gpui::{AnyElement, Pixels, WindowContext};
|
||||
use language::{BufferSnapshot, Chunk, Patch, Point};
|
||||
use multi_buffer::{Anchor, ExcerptId, ExcerptRange, MultiBufferRow, ToPoint as _};
|
||||
use multi_buffer::{Anchor, ExcerptId, ExcerptRange, ToPoint as _};
|
||||
use parking_lot::Mutex;
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
@@ -50,7 +50,7 @@ pub struct BlockId(usize);
|
||||
pub struct BlockPoint(pub Point);
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
|
||||
pub struct BlockRow(pub(super) u32);
|
||||
struct BlockRow(u32);
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
|
||||
struct WrapRow(u32);
|
||||
@@ -163,7 +163,7 @@ pub struct BlockChunks<'a> {
|
||||
pub struct BlockBufferRows<'a> {
|
||||
transforms: sum_tree::Cursor<'a, Transform, (BlockRow, WrapRow)>,
|
||||
input_buffer_rows: wrap_map::WrapBufferRows<'a>,
|
||||
output_row: BlockRow,
|
||||
output_row: u32,
|
||||
started: bool,
|
||||
}
|
||||
|
||||
@@ -357,7 +357,7 @@ impl BlockMap {
|
||||
match block.disposition {
|
||||
BlockDisposition::Above => position.column = 0,
|
||||
BlockDisposition::Below => {
|
||||
position.column = buffer.line_len(MultiBufferRow(position.row))
|
||||
position.column = buffer.line_len(position.row)
|
||||
}
|
||||
}
|
||||
let position = wrap_snapshot.make_wrap_point(position, Bias::Left);
|
||||
@@ -372,7 +372,7 @@ impl BlockMap {
|
||||
(
|
||||
wrap_snapshot
|
||||
.make_wrap_point(
|
||||
Point::new(excerpt_boundary.row.0, 0),
|
||||
Point::new(excerpt_boundary.row, 0),
|
||||
Bias::Left,
|
||||
)
|
||||
.row(),
|
||||
@@ -633,12 +633,12 @@ impl BlockSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn buffer_rows(&self, start_row: BlockRow) -> BlockBufferRows {
|
||||
pub fn buffer_rows(&self, start_row: u32) -> BlockBufferRows {
|
||||
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
|
||||
cursor.seek(&start_row, Bias::Right, &());
|
||||
cursor.seek(&BlockRow(start_row), Bias::Right, &());
|
||||
let (output_start, input_start) = cursor.start();
|
||||
let overshoot = if cursor.item().map_or(false, |t| t.is_isomorphic()) {
|
||||
start_row.0 - output_start.0
|
||||
start_row - output_start.0
|
||||
} else {
|
||||
0
|
||||
};
|
||||
@@ -676,7 +676,7 @@ impl BlockSnapshot {
|
||||
|
||||
pub fn max_point(&self) -> BlockPoint {
|
||||
let row = self.transforms.summary().output_rows - 1;
|
||||
BlockPoint::new(row, self.line_len(BlockRow(row)))
|
||||
BlockPoint::new(row, self.line_len(row))
|
||||
}
|
||||
|
||||
pub fn longest_row(&self) -> u32 {
|
||||
@@ -684,12 +684,12 @@ impl BlockSnapshot {
|
||||
self.to_block_point(WrapPoint::new(input_row, 0)).row
|
||||
}
|
||||
|
||||
pub(super) fn line_len(&self, row: BlockRow) -> u32 {
|
||||
pub fn line_len(&self, row: u32) -> u32 {
|
||||
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
|
||||
cursor.seek(&BlockRow(row.0), Bias::Right, &());
|
||||
cursor.seek(&BlockRow(row), Bias::Right, &());
|
||||
if let Some(transform) = cursor.item() {
|
||||
let (output_start, input_start) = cursor.start();
|
||||
let overshoot = row.0 - output_start.0;
|
||||
let overshoot = row - output_start.0;
|
||||
if transform.block.is_some() {
|
||||
0
|
||||
} else {
|
||||
@@ -700,9 +700,9 @@ impl BlockSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn is_block_line(&self, row: BlockRow) -> bool {
|
||||
pub fn is_block_line(&self, row: u32) -> bool {
|
||||
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
|
||||
cursor.seek(&row, Bias::Right, &());
|
||||
cursor.seek(&BlockRow(row), Bias::Right, &());
|
||||
cursor.item().map_or(false, |t| t.block.is_some())
|
||||
}
|
||||
|
||||
@@ -881,16 +881,16 @@ impl<'a> Iterator for BlockChunks<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Iterator for BlockBufferRows<'a> {
|
||||
type Item = Option<BlockRow>;
|
||||
type Item = Option<u32>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.started {
|
||||
self.output_row.0 += 1;
|
||||
self.output_row += 1;
|
||||
} else {
|
||||
self.started = true;
|
||||
}
|
||||
|
||||
if self.output_row.0 >= self.transforms.end(&()).0 .0 {
|
||||
if self.output_row >= self.transforms.end(&()).0 .0 {
|
||||
self.transforms.next(&());
|
||||
}
|
||||
|
||||
@@ -898,7 +898,7 @@ impl<'a> Iterator for BlockBufferRows<'a> {
|
||||
if transform.block.is_some() {
|
||||
Some(None)
|
||||
} else {
|
||||
Some(self.input_buffer_rows.next().unwrap().map(BlockRow))
|
||||
Some(self.input_buffer_rows.next().unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1154,10 +1154,7 @@ mod tests {
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
snapshot
|
||||
.buffer_rows(BlockRow(0))
|
||||
.map(|row| row.map(|r| r.0))
|
||||
.collect::<Vec<_>>(),
|
||||
snapshot.buffer_rows(0).collect::<Vec<_>>(),
|
||||
&[
|
||||
Some(0),
|
||||
None,
|
||||
@@ -1397,7 +1394,7 @@ mod tests {
|
||||
position.column = 0;
|
||||
}
|
||||
BlockDisposition::Below => {
|
||||
position.column = buffer_snapshot.line_len(MultiBufferRow(position.row));
|
||||
position.column = buffer_snapshot.line_len(position.row);
|
||||
}
|
||||
};
|
||||
let row = wraps_snapshot.make_wrap_point(position, Bias::Left).row();
|
||||
@@ -1413,7 +1410,7 @@ mod tests {
|
||||
expected_blocks.extend(buffer_snapshot.excerpt_boundaries_in_range(0..).map(
|
||||
|boundary| {
|
||||
let position =
|
||||
wraps_snapshot.make_wrap_point(Point::new(boundary.row.0, 0), Bias::Left);
|
||||
wraps_snapshot.make_wrap_point(Point::new(boundary.row, 0), Bias::Left);
|
||||
(
|
||||
position.row(),
|
||||
ExpectedBlock::ExcerptHeader {
|
||||
@@ -1430,9 +1427,7 @@ mod tests {
|
||||
expected_blocks.sort_unstable();
|
||||
let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
|
||||
|
||||
let input_buffer_rows = buffer_snapshot
|
||||
.buffer_rows(MultiBufferRow(0))
|
||||
.collect::<Vec<_>>();
|
||||
let input_buffer_rows = buffer_snapshot.buffer_rows(0).collect::<Vec<_>>();
|
||||
let mut expected_buffer_rows = Vec::new();
|
||||
let mut expected_text = String::new();
|
||||
let mut expected_block_positions = Vec::new();
|
||||
@@ -1503,8 +1498,7 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
blocks_snapshot
|
||||
.buffer_rows(BlockRow(start_row as u32))
|
||||
.map(|row| row.map(|r| r.0))
|
||||
.buffer_rows(start_row as u32)
|
||||
.collect::<Vec<_>>(),
|
||||
&expected_buffer_rows[start_row..]
|
||||
);
|
||||
@@ -1524,7 +1518,7 @@ mod tests {
|
||||
let row = row as u32;
|
||||
|
||||
assert_eq!(
|
||||
blocks_snapshot.line_len(BlockRow(row)),
|
||||
blocks_snapshot.line_len(row),
|
||||
line.len() as u32,
|
||||
"invalid line len for row {}",
|
||||
row
|
||||
|
||||
@@ -4,7 +4,7 @@ use super::{
|
||||
};
|
||||
use gpui::{ElementId, HighlightStyle, Hsla};
|
||||
use language::{Chunk, Edit, Point, TextSummary};
|
||||
use multi_buffer::{Anchor, AnchorRangeExt, MultiBufferRow, MultiBufferSnapshot, ToOffset};
|
||||
use multi_buffer::{Anchor, AnchorRangeExt, MultiBufferSnapshot, ToOffset};
|
||||
use std::{
|
||||
cmp::{self, Ordering},
|
||||
iter,
|
||||
@@ -629,17 +629,17 @@ impl FoldSnapshot {
|
||||
cursor.item().map_or(false, |t| t.output_text.is_some())
|
||||
}
|
||||
|
||||
pub fn is_line_folded(&self, buffer_row: MultiBufferRow) -> bool {
|
||||
pub fn is_line_folded(&self, buffer_row: u32) -> bool {
|
||||
let mut inlay_point = self
|
||||
.inlay_snapshot
|
||||
.to_inlay_point(Point::new(buffer_row.0, 0));
|
||||
.to_inlay_point(Point::new(buffer_row, 0));
|
||||
let mut cursor = self.transforms.cursor::<InlayPoint>();
|
||||
cursor.seek(&inlay_point, Bias::Right, &());
|
||||
loop {
|
||||
match cursor.item() {
|
||||
Some(transform) => {
|
||||
let buffer_point = self.inlay_snapshot.to_buffer_point(inlay_point);
|
||||
if buffer_point.row != buffer_row.0 {
|
||||
if buffer_point.row != buffer_row {
|
||||
return false;
|
||||
} else if transform.output_text.is_some() {
|
||||
return true;
|
||||
@@ -1549,7 +1549,7 @@ mod tests {
|
||||
.collect::<HashSet<_>>();
|
||||
for row in 0..=buffer_snapshot.max_point().row {
|
||||
assert_eq!(
|
||||
snapshot.is_line_folded(MultiBufferRow(row)),
|
||||
snapshot.is_line_folded(row),
|
||||
folded_buffer_rows.contains(&row),
|
||||
"expected buffer row {}{} to be folded",
|
||||
row,
|
||||
|
||||
@@ -2,9 +2,7 @@ use crate::{HighlightStyles, InlayId};
|
||||
use collections::{BTreeMap, BTreeSet};
|
||||
use gpui::HighlightStyle;
|
||||
use language::{Chunk, Edit, Point, TextSummary};
|
||||
use multi_buffer::{
|
||||
Anchor, MultiBufferChunks, MultiBufferRow, MultiBufferRows, MultiBufferSnapshot, ToOffset,
|
||||
};
|
||||
use multi_buffer::{Anchor, MultiBufferChunks, MultiBufferRows, MultiBufferSnapshot, ToOffset};
|
||||
use std::{
|
||||
any::TypeId,
|
||||
cmp,
|
||||
@@ -184,7 +182,7 @@ pub struct InlayBufferRows<'a> {
|
||||
transforms: Cursor<'a, Transform, (InlayPoint, Point)>,
|
||||
buffer_rows: MultiBufferRows<'a>,
|
||||
inlay_row: u32,
|
||||
max_buffer_row: MultiBufferRow,
|
||||
max_buffer_row: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
@@ -377,7 +375,7 @@ impl<'a> InlayBufferRows<'a> {
|
||||
self.transforms.seek(&inlay_point, Bias::Left, &());
|
||||
|
||||
let mut buffer_point = self.transforms.start().1;
|
||||
let buffer_row = MultiBufferRow(if row == 0 {
|
||||
let buffer_row = if row == 0 {
|
||||
0
|
||||
} else {
|
||||
match self.transforms.item() {
|
||||
@@ -385,9 +383,9 @@ impl<'a> InlayBufferRows<'a> {
|
||||
buffer_point += inlay_point.0 - self.transforms.start().0 .0;
|
||||
buffer_point.row
|
||||
}
|
||||
_ => cmp::min(buffer_point.row + 1, self.max_buffer_row.0),
|
||||
_ => cmp::min(buffer_point.row + 1, self.max_buffer_row),
|
||||
}
|
||||
});
|
||||
};
|
||||
self.inlay_row = inlay_point.row();
|
||||
self.buffer_rows.seek(buffer_row);
|
||||
}
|
||||
@@ -988,17 +986,17 @@ impl InlaySnapshot {
|
||||
let inlay_point = InlayPoint::new(row, 0);
|
||||
cursor.seek(&inlay_point, Bias::Left, &());
|
||||
|
||||
let max_buffer_row = MultiBufferRow(self.buffer.max_point().row);
|
||||
let max_buffer_row = self.buffer.max_point().row;
|
||||
let mut buffer_point = cursor.start().1;
|
||||
let buffer_row = if row == 0 {
|
||||
MultiBufferRow(0)
|
||||
0
|
||||
} else {
|
||||
match cursor.item() {
|
||||
Some(Transform::Isomorphic(_)) => {
|
||||
buffer_point += inlay_point.0 - cursor.start().0 .0;
|
||||
MultiBufferRow(buffer_point.row)
|
||||
buffer_point.row
|
||||
}
|
||||
_ => cmp::min(MultiBufferRow(buffer_point.row + 1), max_buffer_row),
|
||||
_ => cmp::min(buffer_point.row + 1, max_buffer_row),
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,6 @@ use settings::{Settings, SettingsSources};
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct EditorSettings {
|
||||
pub cursor_blink: bool,
|
||||
pub current_line_highlight: CurrentLineHighlight,
|
||||
pub hover_popover_enabled: bool,
|
||||
pub show_completions_on_input: bool,
|
||||
pub show_completion_documentation: bool,
|
||||
@@ -25,19 +24,6 @@ pub struct EditorSettings {
|
||||
pub double_click_in_multibuffer: DoubleClickInMultibuffer,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum CurrentLineHighlight {
|
||||
// Don't highlight the current line.
|
||||
None,
|
||||
// Highlight the gutter area.
|
||||
Gutter,
|
||||
// Highlight the editor area.
|
||||
Line,
|
||||
// Highlight the full line.
|
||||
All,
|
||||
}
|
||||
|
||||
/// When to populate a new search's query based on the text under the cursor.
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
@@ -119,10 +105,6 @@ pub struct EditorSettingsContent {
|
||||
///
|
||||
/// Default: true
|
||||
pub cursor_blink: Option<bool>,
|
||||
/// How to highlight the current line in the editor.
|
||||
///
|
||||
/// Default: all
|
||||
pub current_line_highlight: Option<CurrentLineHighlight>,
|
||||
/// Whether to show the informational hover box when moving the mouse
|
||||
/// over symbols in the editor.
|
||||
///
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -4,29 +4,29 @@ use std::ops::Range;
|
||||
|
||||
use git::diff::{DiffHunk, DiffHunkStatus};
|
||||
use language::Point;
|
||||
use multi_buffer::{Anchor, MultiBufferRow};
|
||||
use multi_buffer::Anchor;
|
||||
|
||||
use crate::{
|
||||
display_map::{DisplaySnapshot, ToDisplayPoint},
|
||||
hunk_status, AnchorRangeExt, DisplayRow,
|
||||
AnchorRangeExt,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum DisplayDiffHunk {
|
||||
Folded {
|
||||
display_row: DisplayRow,
|
||||
display_row: u32,
|
||||
},
|
||||
|
||||
Unfolded {
|
||||
diff_base_byte_range: Range<usize>,
|
||||
display_row_range: Range<DisplayRow>,
|
||||
display_row_range: Range<u32>,
|
||||
multi_buffer_range: Range<Anchor>,
|
||||
status: DiffHunkStatus,
|
||||
},
|
||||
}
|
||||
|
||||
impl DisplayDiffHunk {
|
||||
pub fn start_display_row(&self) -> DisplayRow {
|
||||
pub fn start_display_row(&self) -> u32 {
|
||||
match self {
|
||||
&DisplayDiffHunk::Folded { display_row } => display_row,
|
||||
DisplayDiffHunk::Unfolded {
|
||||
@@ -35,7 +35,7 @@ impl DisplayDiffHunk {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains_display_row(&self, display_row: DisplayRow) -> bool {
|
||||
pub fn contains_display_row(&self, display_row: u32) -> bool {
|
||||
let range = match self {
|
||||
&DisplayDiffHunk::Folded { display_row } => display_row..=display_row,
|
||||
|
||||
@@ -48,26 +48,21 @@ impl DisplayDiffHunk {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn diff_hunk_to_display(
|
||||
hunk: &DiffHunk<MultiBufferRow>,
|
||||
snapshot: &DisplaySnapshot,
|
||||
) -> DisplayDiffHunk {
|
||||
let hunk_start_point = Point::new(hunk.associated_range.start.0, 0);
|
||||
let hunk_start_point_sub = Point::new(hunk.associated_range.start.0.saturating_sub(1), 0);
|
||||
pub fn diff_hunk_to_display(hunk: &DiffHunk<u32>, snapshot: &DisplaySnapshot) -> DisplayDiffHunk {
|
||||
let hunk_start_point = Point::new(hunk.associated_range.start, 0);
|
||||
let hunk_start_point_sub = Point::new(hunk.associated_range.start.saturating_sub(1), 0);
|
||||
let hunk_end_point_sub = Point::new(
|
||||
hunk.associated_range
|
||||
.end
|
||||
.0
|
||||
.saturating_sub(1)
|
||||
.max(hunk.associated_range.start.0),
|
||||
.max(hunk.associated_range.start),
|
||||
0,
|
||||
);
|
||||
|
||||
let status = hunk_status(hunk);
|
||||
let is_removal = status == DiffHunkStatus::Removed;
|
||||
let is_removal = hunk.status() == DiffHunkStatus::Removed;
|
||||
|
||||
let folds_start = Point::new(hunk.associated_range.start.0.saturating_sub(2), 0);
|
||||
let folds_end = Point::new(hunk.associated_range.end.0 + 2, 0);
|
||||
let folds_start = Point::new(hunk.associated_range.start.saturating_sub(2), 0);
|
||||
let folds_end = Point::new(hunk.associated_range.end + 2, 0);
|
||||
let folds_range = folds_start..folds_end;
|
||||
|
||||
let containing_fold = snapshot.folds_in_range(folds_range).find(|fold| {
|
||||
@@ -88,7 +83,7 @@ pub fn diff_hunk_to_display(
|
||||
let start = hunk_start_point.to_display_point(snapshot).row();
|
||||
|
||||
let hunk_end_row = hunk.associated_range.end.max(hunk.associated_range.start);
|
||||
let hunk_end_point = Point::new(hunk_end_row.0, 0);
|
||||
let hunk_end_point = Point::new(hunk_end_row, 0);
|
||||
|
||||
let multi_buffer_start = snapshot.buffer_snapshot.anchor_after(hunk_start_point);
|
||||
let multi_buffer_end = snapshot.buffer_snapshot.anchor_before(hunk_end_point);
|
||||
@@ -97,7 +92,7 @@ pub fn diff_hunk_to_display(
|
||||
DisplayDiffHunk::Unfolded {
|
||||
display_row_range: start..end,
|
||||
multi_buffer_range: multi_buffer_start..multi_buffer_end,
|
||||
status,
|
||||
status: hunk.status(),
|
||||
diff_base_byte_range: hunk.diff_base_byte_range.clone(),
|
||||
}
|
||||
}
|
||||
@@ -105,11 +100,11 @@ pub fn diff_hunk_to_display(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::editor_tests::init_test;
|
||||
use crate::Point;
|
||||
use crate::{editor_tests::init_test, hunk_status};
|
||||
use gpui::{Context, TestAppContext};
|
||||
use language::Capability::ReadWrite;
|
||||
use multi_buffer::{ExcerptRange, MultiBuffer, MultiBufferRow};
|
||||
use multi_buffer::{ExcerptRange, MultiBuffer};
|
||||
use project::{FakeFs, Project};
|
||||
use unindent::Unindent;
|
||||
#[gpui::test]
|
||||
@@ -262,41 +257,26 @@ mod tests {
|
||||
);
|
||||
|
||||
let expected = [
|
||||
(
|
||||
DiffHunkStatus::Modified,
|
||||
MultiBufferRow(1)..MultiBufferRow(2),
|
||||
),
|
||||
(
|
||||
DiffHunkStatus::Modified,
|
||||
MultiBufferRow(2)..MultiBufferRow(3),
|
||||
),
|
||||
(DiffHunkStatus::Modified, 1..2),
|
||||
(DiffHunkStatus::Modified, 2..3),
|
||||
//TODO: Define better when and where removed hunks show up at range extremities
|
||||
(
|
||||
DiffHunkStatus::Removed,
|
||||
MultiBufferRow(6)..MultiBufferRow(6),
|
||||
),
|
||||
(
|
||||
DiffHunkStatus::Removed,
|
||||
MultiBufferRow(8)..MultiBufferRow(8),
|
||||
),
|
||||
(
|
||||
DiffHunkStatus::Added,
|
||||
MultiBufferRow(10)..MultiBufferRow(11),
|
||||
),
|
||||
(DiffHunkStatus::Removed, 6..6),
|
||||
(DiffHunkStatus::Removed, 8..8),
|
||||
(DiffHunkStatus::Added, 10..11),
|
||||
];
|
||||
|
||||
assert_eq!(
|
||||
snapshot
|
||||
.git_diff_hunks_in_range(MultiBufferRow(0)..MultiBufferRow(12))
|
||||
.map(|hunk| (hunk_status(&hunk), hunk.associated_range))
|
||||
.git_diff_hunks_in_range(0..12)
|
||||
.map(|hunk| (hunk.status(), hunk.associated_range))
|
||||
.collect::<Vec<_>>(),
|
||||
&expected,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
snapshot
|
||||
.git_diff_hunks_in_range_rev(MultiBufferRow(0)..MultiBufferRow(12))
|
||||
.map(|hunk| (hunk_status(&hunk), hunk.associated_range))
|
||||
.git_diff_hunks_in_range_rev(0..12)
|
||||
.map(|hunk| (hunk.status(), hunk.associated_range))
|
||||
.collect::<Vec<_>>(),
|
||||
expected
|
||||
.iter()
|
||||
|
||||
@@ -7,13 +7,12 @@ use git::{
|
||||
parse_git_remote_url, GitHostingProvider, GitHostingProviderRegistry, Oid, PullRequest,
|
||||
};
|
||||
use gpui::{Model, ModelContext, Subscription, Task};
|
||||
use http::HttpClient;
|
||||
use language::{markdown, Bias, Buffer, BufferSnapshot, Edit, LanguageRegistry, ParsedMarkdown};
|
||||
use multi_buffer::MultiBufferRow;
|
||||
use project::{Item, Project};
|
||||
use smallvec::SmallVec;
|
||||
use sum_tree::SumTree;
|
||||
use url::Url;
|
||||
use util::http::HttpClient;
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct GitBlameEntry {
|
||||
@@ -186,7 +185,7 @@ impl GitBlame {
|
||||
|
||||
pub fn blame_for_rows<'a>(
|
||||
&'a mut self,
|
||||
rows: impl 'a + IntoIterator<Item = Option<MultiBufferRow>>,
|
||||
rows: impl 'a + IntoIterator<Item = Option<u32>>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> impl 'a + Iterator<Item = Option<BlameEntry>> {
|
||||
self.sync(cx);
|
||||
@@ -194,7 +193,7 @@ impl GitBlame {
|
||||
let mut cursor = self.entries.cursor::<u32>();
|
||||
rows.into_iter().map(move |row| {
|
||||
let row = row?;
|
||||
cursor.seek_forward(&row.0, Bias::Right, &());
|
||||
cursor.seek_forward(&row, Bias::Right, &());
|
||||
cursor.item()?.blame.clone()
|
||||
})
|
||||
}
|
||||
@@ -533,7 +532,7 @@ mod tests {
|
||||
($blame:expr, $rows:expr, $expected:expr, $cx:expr) => {
|
||||
assert_eq!(
|
||||
$blame
|
||||
.blame_for_rows($rows.map(MultiBufferRow).map(Some), $cx)
|
||||
.blame_for_rows($rows.map(Some), $cx)
|
||||
.collect::<Vec<_>>(),
|
||||
$expected
|
||||
);
|
||||
@@ -598,7 +597,7 @@ mod tests {
|
||||
blame.update(cx, |blame, cx| {
|
||||
assert_eq!(
|
||||
blame
|
||||
.blame_for_rows((0..1).map(MultiBufferRow).map(Some), cx)
|
||||
.blame_for_rows((0..1).map(Some), cx)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![None]
|
||||
);
|
||||
@@ -662,7 +661,7 @@ mod tests {
|
||||
// All lines
|
||||
assert_eq!(
|
||||
blame
|
||||
.blame_for_rows((0..8).map(MultiBufferRow).map(Some), cx)
|
||||
.blame_for_rows((0..8).map(Some), cx)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
Some(blame_entry("1b1b1b", 0..1)),
|
||||
@@ -678,7 +677,7 @@ mod tests {
|
||||
// Subset of lines
|
||||
assert_eq!(
|
||||
blame
|
||||
.blame_for_rows((1..4).map(MultiBufferRow).map(Some), cx)
|
||||
.blame_for_rows((1..4).map(Some), cx)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
Some(blame_entry("0d0d0d", 1..2)),
|
||||
@@ -689,7 +688,7 @@ mod tests {
|
||||
// Subset of lines, with some not displayed
|
||||
assert_eq!(
|
||||
blame
|
||||
.blame_for_rows(vec![Some(MultiBufferRow(1)), None, None], cx)
|
||||
.blame_for_rows(vec![Some(1), None, None], cx)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![Some(blame_entry("0d0d0d", 1..2)), None, None]
|
||||
);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::{
|
||||
display_map::{InlayOffset, ToDisplayPoint},
|
||||
hover_links::{InlayHighlight, RangeInEditor},
|
||||
Anchor, AnchorRangeExt, DisplayPoint, DisplayRow, Editor, EditorSettings, EditorSnapshot,
|
||||
EditorStyle, ExcerptId, Hover, RangeToAnchorExt,
|
||||
Anchor, AnchorRangeExt, DisplayPoint, Editor, EditorSettings, EditorSnapshot, EditorStyle,
|
||||
ExcerptId, Hover, RangeToAnchorExt,
|
||||
};
|
||||
use futures::{stream::FuturesUnordered, FutureExt};
|
||||
use gpui::{
|
||||
@@ -440,7 +440,7 @@ impl HoverState {
|
||||
&mut self,
|
||||
snapshot: &EditorSnapshot,
|
||||
style: &EditorStyle,
|
||||
visible_rows: Range<DisplayRow>,
|
||||
visible_rows: Range<u32>,
|
||||
max_size: Size<Pixels>,
|
||||
workspace: Option<WeakView<Workspace>>,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
|
||||
@@ -7,10 +7,7 @@ use collections::{hash_map, HashMap, HashSet};
|
||||
use git::diff::{DiffHunk, DiffHunkStatus};
|
||||
use gpui::{AppContext, Hsla, Model, Task, View};
|
||||
use language::Buffer;
|
||||
use multi_buffer::{
|
||||
Anchor, ExcerptRange, MultiBuffer, MultiBufferRow, MultiBufferSnapshot, ToPoint,
|
||||
};
|
||||
use settings::{Settings, SettingsStore};
|
||||
use multi_buffer::{Anchor, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToPoint};
|
||||
use text::{BufferId, Point};
|
||||
use ui::{
|
||||
div, ActiveTheme, Context as _, IntoElement, ParentElement, Styled, ViewContext, VisualContext,
|
||||
@@ -18,11 +15,10 @@ use ui::{
|
||||
use util::{debug_panic, RangeExt};
|
||||
|
||||
use crate::{
|
||||
editor_settings::CurrentLineHighlight,
|
||||
git::{diff_hunk_to_display, DisplayDiffHunk},
|
||||
hunk_status, hunks_for_selections, BlockDisposition, BlockId, BlockProperties, BlockStyle,
|
||||
DiffRowHighlight, Editor, EditorSettings, EditorSnapshot, ExpandAllHunkDiffs, RangeToAnchorExt,
|
||||
RevertSelectedHunks, ToDisplayPoint, ToggleHunkDiff,
|
||||
hunks_for_selections, BlockDisposition, BlockId, BlockProperties, BlockStyle, DiffRowHighlight,
|
||||
Editor, EditorSnapshot, ExpandAllHunkDiffs, RangeToAnchorExt, RevertSelectedHunks,
|
||||
ToDisplayPoint, ToggleHunkDiff,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -94,11 +90,11 @@ impl Editor {
|
||||
let hunks = snapshot
|
||||
.display_snapshot
|
||||
.buffer_snapshot
|
||||
.git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
|
||||
.git_diff_hunks_in_range(0..u32::MAX)
|
||||
.filter(|hunk| {
|
||||
let hunk_display_row_range = Point::new(hunk.associated_range.start.0, 0)
|
||||
let hunk_display_row_range = Point::new(hunk.associated_range.start, 0)
|
||||
.to_display_point(&snapshot.display_snapshot)
|
||||
..Point::new(hunk.associated_range.end.0, 0)
|
||||
..Point::new(hunk.associated_range.end, 0)
|
||||
.to_display_point(&snapshot.display_snapshot);
|
||||
let row_range_end =
|
||||
display_rows_with_expanded_hunks.get(&hunk_display_row_range.start.row());
|
||||
@@ -109,7 +105,7 @@ impl Editor {
|
||||
|
||||
fn toggle_hunks_expanded(
|
||||
&mut self,
|
||||
hunks_to_toggle: Vec<DiffHunk<MultiBufferRow>>,
|
||||
hunks_to_toggle: Vec<DiffHunk<u32>>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
let previous_toggle_task = self.expanded_hunks.hunk_update_tasks.remove(&None);
|
||||
@@ -180,10 +176,10 @@ impl Editor {
|
||||
});
|
||||
for remaining_hunk in hunks_to_toggle {
|
||||
let remaining_hunk_point_range =
|
||||
Point::new(remaining_hunk.associated_range.start.0, 0)
|
||||
..Point::new(remaining_hunk.associated_range.end.0, 0);
|
||||
Point::new(remaining_hunk.associated_range.start, 0)
|
||||
..Point::new(remaining_hunk.associated_range.end, 0);
|
||||
hunks_to_expand.push(HunkToExpand {
|
||||
status: hunk_status(&remaining_hunk),
|
||||
status: remaining_hunk.status(),
|
||||
multi_buffer_range: remaining_hunk_point_range
|
||||
.to_anchors(&snapshot.buffer_snapshot),
|
||||
diff_base_byte_range: remaining_hunk.diff_base_byte_range.clone(),
|
||||
@@ -378,10 +374,9 @@ impl Editor {
|
||||
}
|
||||
|
||||
let snapshot = editor.snapshot(cx);
|
||||
let mut recalculated_hunks = snapshot
|
||||
.buffer_snapshot
|
||||
.git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
|
||||
.filter(|hunk| hunk.buffer_id == buffer_id)
|
||||
let buffer_snapshot = buffer.read(cx).snapshot();
|
||||
let mut recalculated_hunks = buffer_snapshot
|
||||
.git_diff_hunks_in_row_range(0..u32::MAX)
|
||||
.fuse()
|
||||
.peekable();
|
||||
let mut highlights_to_remove =
|
||||
@@ -407,7 +402,7 @@ impl Editor {
|
||||
.to_display_point(&snapshot)
|
||||
.row();
|
||||
while let Some(buffer_hunk) = recalculated_hunks.peek() {
|
||||
match diff_hunk_to_display(&buffer_hunk, &snapshot) {
|
||||
match diff_hunk_to_display(buffer_hunk, &snapshot) {
|
||||
DisplayDiffHunk::Folded { display_row } => {
|
||||
recalculated_hunks.next();
|
||||
if !expanded_hunk.folded
|
||||
@@ -446,7 +441,7 @@ impl Editor {
|
||||
} else {
|
||||
if !expanded_hunk.folded
|
||||
&& expanded_hunk_display_range == hunk_display_range
|
||||
&& expanded_hunk.status == hunk_status(buffer_hunk)
|
||||
&& expanded_hunk.status == buffer_hunk.status()
|
||||
&& expanded_hunk.diff_base_byte_range
|
||||
== buffer_hunk.diff_base_byte_range
|
||||
{
|
||||
@@ -582,32 +577,12 @@ fn editor_with_deleted_text(
|
||||
.anchor_after(editor.buffer.read(cx).len(cx));
|
||||
|
||||
editor.highlight_rows::<DiffRowHighlight>(start..=end, Some(deleted_color), cx);
|
||||
|
||||
let subscription_editor = parent_editor.clone();
|
||||
editor._subscriptions.extend([
|
||||
cx.on_blur(&editor.focus_handle, |editor, cx| {
|
||||
editor.set_current_line_highlight(CurrentLineHighlight::None);
|
||||
editor.change_selections(None, cx, |s| {
|
||||
s.try_cancel();
|
||||
});
|
||||
cx.notify();
|
||||
}),
|
||||
cx.on_focus(&editor.focus_handle, move |editor, cx| {
|
||||
let restored_highlight = if let Some(parent_editor) = subscription_editor.upgrade()
|
||||
{
|
||||
parent_editor.read(cx).current_line_highlight
|
||||
} else {
|
||||
EditorSettings::get_global(cx).current_line_highlight
|
||||
};
|
||||
editor.set_current_line_highlight(restored_highlight);
|
||||
cx.notify();
|
||||
}),
|
||||
cx.observe_global::<SettingsStore>(|editor, cx| {
|
||||
if !editor.is_focused(cx) {
|
||||
editor.set_current_line_highlight(CurrentLineHighlight::None);
|
||||
}
|
||||
}),
|
||||
]);
|
||||
let hunk_related_subscription = cx.on_blur(&editor.focus_handle, |editor, cx| {
|
||||
editor.change_selections(None, cx, |s| {
|
||||
s.try_cancel();
|
||||
});
|
||||
});
|
||||
editor._subscriptions.push(hunk_related_subscription);
|
||||
let original_multi_buffer_range = hunk.multi_buffer_range.clone();
|
||||
let diff_base_range = hunk.diff_base_byte_range.clone();
|
||||
editor.register_action::<RevertSelectedHunks>(move |_, cx| {
|
||||
@@ -639,17 +614,15 @@ fn editor_with_deleted_text(
|
||||
editor
|
||||
});
|
||||
|
||||
let editor_height = editor.update(cx, |editor, cx| editor.max_point(cx).row().0 as u8);
|
||||
let editor_height = editor.update(cx, |editor, cx| editor.max_point(cx).row() as u8);
|
||||
(editor_height, editor)
|
||||
}
|
||||
|
||||
fn buffer_diff_hunk(
|
||||
buffer_snapshot: &MultiBufferSnapshot,
|
||||
row_range: Range<Point>,
|
||||
) -> Option<DiffHunk<MultiBufferRow>> {
|
||||
let mut hunks = buffer_snapshot.git_diff_hunks_in_range(
|
||||
MultiBufferRow(row_range.start.row)..MultiBufferRow(row_range.end.row),
|
||||
);
|
||||
) -> Option<DiffHunk<u32>> {
|
||||
let mut hunks = buffer_snapshot.git_diff_hunks_in_range(row_range.start.row..row_range.end.row);
|
||||
let hunk = hunks.next()?;
|
||||
let second_hunk = hunks.next();
|
||||
if second_hunk.is_none() {
|
||||
|
||||
@@ -70,10 +70,8 @@ pub fn deploy_context_menu(
|
||||
s.set_pending_display_range(point..point, SelectMode::Character);
|
||||
});
|
||||
|
||||
let focus = cx.focused();
|
||||
ui::ContextMenu::build(cx, |menu, _cx| {
|
||||
let builder = menu
|
||||
.action("Rename Symbol", Box::new(Rename))
|
||||
menu.action("Rename Symbol", Box::new(Rename))
|
||||
.action("Go to Definition", Box::new(GoToDefinition))
|
||||
.action("Go to Type Definition", Box::new(GoToTypeDefinition))
|
||||
.action("Go to Implementation", Box::new(GoToImplementation))
|
||||
@@ -86,11 +84,7 @@ pub fn deploy_context_menu(
|
||||
)
|
||||
.separator()
|
||||
.action("Reveal in Finder", Box::new(RevealInFinder))
|
||||
.action("Open in Terminal", Box::new(OpenInTerminal));
|
||||
match focus {
|
||||
Some(focus) => builder.context(focus),
|
||||
None => builder,
|
||||
}
|
||||
.action("Open in Terminal", Box::new(OpenInTerminal))
|
||||
})
|
||||
};
|
||||
let mouse_context_menu = MouseContextMenu::new(position, context_menu, cx);
|
||||
|
||||
@@ -2,12 +2,10 @@
|
||||
//! in editor given a given motion (e.g. it handles converting a "move left" command into coordinates in editor). It is exposed mostly for use by vim crate.
|
||||
|
||||
use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint};
|
||||
use crate::{
|
||||
char_kind, scroll::ScrollAnchor, CharKind, DisplayRow, EditorStyle, RowExt, ToOffset, ToPoint,
|
||||
};
|
||||
use crate::{char_kind, scroll::ScrollAnchor, CharKind, EditorStyle, ToOffset, ToPoint};
|
||||
use gpui::{px, Pixels, WindowTextSystem};
|
||||
use language::Point;
|
||||
use multi_buffer::{MultiBufferRow, MultiBufferSnapshot};
|
||||
use multi_buffer::MultiBufferSnapshot;
|
||||
use serde::Deserialize;
|
||||
|
||||
use std::{ops::Range, sync::Arc};
|
||||
@@ -37,7 +35,7 @@ pub struct TextLayoutDetails {
|
||||
pub fn left(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint {
|
||||
if point.column() > 0 {
|
||||
*point.column_mut() -= 1;
|
||||
} else if point.row().0 > 0 {
|
||||
} else if point.row() > 0 {
|
||||
*point.row_mut() -= 1;
|
||||
*point.column_mut() = map.line_len(point.row());
|
||||
}
|
||||
@@ -129,7 +127,7 @@ pub(crate) fn up_by_rows(
|
||||
_ => map.x_for_display_point(start, text_layout_details),
|
||||
};
|
||||
|
||||
let prev_row = DisplayRow(start.row().0.saturating_sub(row_count));
|
||||
let prev_row = start.row().saturating_sub(row_count);
|
||||
let mut point = map.clip_point(
|
||||
DisplayPoint::new(prev_row, map.line_len(prev_row)),
|
||||
Bias::Left,
|
||||
@@ -139,7 +137,7 @@ pub(crate) fn up_by_rows(
|
||||
} else if preserve_column_at_start {
|
||||
return (start, goal);
|
||||
} else {
|
||||
point = DisplayPoint::new(DisplayRow(0), 0);
|
||||
point = DisplayPoint::new(0, 0);
|
||||
goal_x = px(0.);
|
||||
}
|
||||
|
||||
@@ -168,7 +166,7 @@ pub(crate) fn down_by_rows(
|
||||
_ => map.x_for_display_point(start, text_layout_details),
|
||||
};
|
||||
|
||||
let new_row = DisplayRow(start.row().0 + row_count);
|
||||
let new_row = start.row() + row_count;
|
||||
let mut point = map.clip_point(DisplayPoint::new(new_row, 0), Bias::Right);
|
||||
if point.row() > start.row() {
|
||||
*point.column_mut() = map.display_column_for_x(point.row(), goal_x, text_layout_details)
|
||||
@@ -222,9 +220,7 @@ pub fn indented_line_beginning(
|
||||
let soft_line_start = map.clip_point(DisplayPoint::new(display_point.row(), 0), Bias::Right);
|
||||
let indent_start = Point::new(
|
||||
point.row,
|
||||
map.buffer_snapshot
|
||||
.indent_size_for_line(MultiBufferRow(point.row))
|
||||
.len,
|
||||
map.buffer_snapshot.indent_size_for_line(point.row).len,
|
||||
)
|
||||
.to_display_point(map);
|
||||
let line_start = map.prev_line_boundary(point).1;
|
||||
@@ -330,7 +326,7 @@ pub fn start_of_paragraph(
|
||||
|
||||
let mut found_non_blank_line = false;
|
||||
for row in (0..point.row + 1).rev() {
|
||||
let blank = map.buffer_snapshot.is_line_blank(MultiBufferRow(row));
|
||||
let blank = map.buffer_snapshot.is_line_blank(row);
|
||||
if found_non_blank_line && blank {
|
||||
if count <= 1 {
|
||||
return Point::new(row, 0).to_display_point(map);
|
||||
@@ -353,13 +349,13 @@ pub fn end_of_paragraph(
|
||||
mut count: usize,
|
||||
) -> DisplayPoint {
|
||||
let point = display_point.to_point(map);
|
||||
if point.row == map.max_buffer_row().0 {
|
||||
if point.row == map.max_buffer_row() {
|
||||
return map.max_point();
|
||||
}
|
||||
|
||||
let mut found_non_blank_line = false;
|
||||
for row in point.row..map.max_buffer_row().next_row().0 {
|
||||
let blank = map.buffer_snapshot.is_line_blank(MultiBufferRow(row));
|
||||
for row in point.row..map.max_buffer_row() + 1 {
|
||||
let blank = map.buffer_snapshot.is_line_blank(row);
|
||||
if found_non_blank_line && blank {
|
||||
if count <= 1 {
|
||||
return Point::new(row, 0).to_display_point(map);
|
||||
@@ -553,16 +549,13 @@ pub fn split_display_range_by_lines(
|
||||
|
||||
let mut start = range.start;
|
||||
// Loop over all the covered rows until the one containing the range end
|
||||
for row in range.start.row().0..range.end.row().0 {
|
||||
let row_end_column = map.line_len(DisplayRow(row));
|
||||
let end = map.clip_point(
|
||||
DisplayPoint::new(DisplayRow(row), row_end_column),
|
||||
Bias::Left,
|
||||
);
|
||||
for row in range.start.row()..range.end.row() {
|
||||
let row_end_column = map.line_len(row);
|
||||
let end = map.clip_point(DisplayPoint::new(row, row_end_column), Bias::Left);
|
||||
if start != end {
|
||||
result.push(start..end);
|
||||
}
|
||||
start = map.clip_point(DisplayPoint::new(DisplayRow(row + 1), 0), Bias::Left);
|
||||
start = map.clip_point(DisplayPoint::new(row + 1, 0), Bias::Left);
|
||||
}
|
||||
|
||||
// Add the final range from the start of the last end to the original range end.
|
||||
@@ -577,7 +570,7 @@ mod tests {
|
||||
use crate::{
|
||||
display_map::Inlay,
|
||||
test::{editor_test_context::EditorTestContext, marked_display_snapshot},
|
||||
Buffer, DisplayMap, DisplayRow, ExcerptRange, InlayId, MultiBuffer,
|
||||
Buffer, DisplayMap, ExcerptRange, InlayId, MultiBuffer,
|
||||
};
|
||||
use gpui::{font, Context as _};
|
||||
use language::Capability;
|
||||
@@ -907,126 +900,126 @@ mod tests {
|
||||
|
||||
assert_eq!(snapshot.text(), "\n\nabc\ndefg\n\n\nhijkl\nmn");
|
||||
|
||||
let col_2_x = snapshot
|
||||
.x_for_display_point(DisplayPoint::new(DisplayRow(2), 2), &text_layout_details);
|
||||
let col_2_x =
|
||||
snapshot.x_for_display_point(DisplayPoint::new(2, 2), &text_layout_details);
|
||||
|
||||
// Can't move up into the first excerpt's header
|
||||
assert_eq!(
|
||||
up(
|
||||
&snapshot,
|
||||
DisplayPoint::new(DisplayRow(2), 2),
|
||||
DisplayPoint::new(2, 2),
|
||||
SelectionGoal::HorizontalPosition(col_2_x.0),
|
||||
false,
|
||||
&text_layout_details
|
||||
),
|
||||
(
|
||||
DisplayPoint::new(DisplayRow(2), 0),
|
||||
DisplayPoint::new(2, 0),
|
||||
SelectionGoal::HorizontalPosition(0.0)
|
||||
),
|
||||
);
|
||||
assert_eq!(
|
||||
up(
|
||||
&snapshot,
|
||||
DisplayPoint::new(DisplayRow(2), 0),
|
||||
DisplayPoint::new(2, 0),
|
||||
SelectionGoal::None,
|
||||
false,
|
||||
&text_layout_details
|
||||
),
|
||||
(
|
||||
DisplayPoint::new(DisplayRow(2), 0),
|
||||
DisplayPoint::new(2, 0),
|
||||
SelectionGoal::HorizontalPosition(0.0)
|
||||
),
|
||||
);
|
||||
|
||||
let col_4_x = snapshot
|
||||
.x_for_display_point(DisplayPoint::new(DisplayRow(3), 4), &text_layout_details);
|
||||
let col_4_x =
|
||||
snapshot.x_for_display_point(DisplayPoint::new(3, 4), &text_layout_details);
|
||||
|
||||
// Move up and down within first excerpt
|
||||
assert_eq!(
|
||||
up(
|
||||
&snapshot,
|
||||
DisplayPoint::new(DisplayRow(3), 4),
|
||||
DisplayPoint::new(3, 4),
|
||||
SelectionGoal::HorizontalPosition(col_4_x.0),
|
||||
false,
|
||||
&text_layout_details
|
||||
),
|
||||
(
|
||||
DisplayPoint::new(DisplayRow(2), 3),
|
||||
DisplayPoint::new(2, 3),
|
||||
SelectionGoal::HorizontalPosition(col_4_x.0)
|
||||
),
|
||||
);
|
||||
assert_eq!(
|
||||
down(
|
||||
&snapshot,
|
||||
DisplayPoint::new(DisplayRow(2), 3),
|
||||
DisplayPoint::new(2, 3),
|
||||
SelectionGoal::HorizontalPosition(col_4_x.0),
|
||||
false,
|
||||
&text_layout_details
|
||||
),
|
||||
(
|
||||
DisplayPoint::new(DisplayRow(3), 4),
|
||||
DisplayPoint::new(3, 4),
|
||||
SelectionGoal::HorizontalPosition(col_4_x.0)
|
||||
),
|
||||
);
|
||||
|
||||
let col_5_x = snapshot
|
||||
.x_for_display_point(DisplayPoint::new(DisplayRow(6), 5), &text_layout_details);
|
||||
let col_5_x =
|
||||
snapshot.x_for_display_point(DisplayPoint::new(6, 5), &text_layout_details);
|
||||
|
||||
// Move up and down across second excerpt's header
|
||||
assert_eq!(
|
||||
up(
|
||||
&snapshot,
|
||||
DisplayPoint::new(DisplayRow(6), 5),
|
||||
DisplayPoint::new(6, 5),
|
||||
SelectionGoal::HorizontalPosition(col_5_x.0),
|
||||
false,
|
||||
&text_layout_details
|
||||
),
|
||||
(
|
||||
DisplayPoint::new(DisplayRow(3), 4),
|
||||
DisplayPoint::new(3, 4),
|
||||
SelectionGoal::HorizontalPosition(col_5_x.0)
|
||||
),
|
||||
);
|
||||
assert_eq!(
|
||||
down(
|
||||
&snapshot,
|
||||
DisplayPoint::new(DisplayRow(3), 4),
|
||||
DisplayPoint::new(3, 4),
|
||||
SelectionGoal::HorizontalPosition(col_5_x.0),
|
||||
false,
|
||||
&text_layout_details
|
||||
),
|
||||
(
|
||||
DisplayPoint::new(DisplayRow(6), 5),
|
||||
DisplayPoint::new(6, 5),
|
||||
SelectionGoal::HorizontalPosition(col_5_x.0)
|
||||
),
|
||||
);
|
||||
|
||||
let max_point_x = snapshot
|
||||
.x_for_display_point(DisplayPoint::new(DisplayRow(7), 2), &text_layout_details);
|
||||
let max_point_x =
|
||||
snapshot.x_for_display_point(DisplayPoint::new(7, 2), &text_layout_details);
|
||||
|
||||
// Can't move down off the end
|
||||
assert_eq!(
|
||||
down(
|
||||
&snapshot,
|
||||
DisplayPoint::new(DisplayRow(7), 0),
|
||||
DisplayPoint::new(7, 0),
|
||||
SelectionGoal::HorizontalPosition(0.0),
|
||||
false,
|
||||
&text_layout_details
|
||||
),
|
||||
(
|
||||
DisplayPoint::new(DisplayRow(7), 2),
|
||||
DisplayPoint::new(7, 2),
|
||||
SelectionGoal::HorizontalPosition(max_point_x.0)
|
||||
),
|
||||
);
|
||||
assert_eq!(
|
||||
down(
|
||||
&snapshot,
|
||||
DisplayPoint::new(DisplayRow(7), 2),
|
||||
DisplayPoint::new(7, 2),
|
||||
SelectionGoal::HorizontalPosition(max_point_x.0),
|
||||
false,
|
||||
&text_layout_details
|
||||
),
|
||||
(
|
||||
DisplayPoint::new(DisplayRow(7), 2),
|
||||
DisplayPoint::new(7, 2),
|
||||
SelectionGoal::HorizontalPosition(max_point_x.0)
|
||||
),
|
||||
);
|
||||
|
||||
@@ -6,8 +6,8 @@ use crate::{
|
||||
display_map::{DisplaySnapshot, ToDisplayPoint},
|
||||
hover_popover::hide_hover,
|
||||
persistence::DB,
|
||||
Anchor, DisplayPoint, DisplayRow, Editor, EditorEvent, EditorMode, EditorSettings,
|
||||
InlayHintRefreshReason, MultiBufferSnapshot, RowExt, ToPoint,
|
||||
Anchor, DisplayPoint, Editor, EditorEvent, EditorMode, EditorSettings, InlayHintRefreshReason,
|
||||
MultiBufferSnapshot, ToPoint,
|
||||
};
|
||||
pub use autoscroll::{Autoscroll, AutoscrollStrategy};
|
||||
use gpui::{point, px, AppContext, Entity, Global, Pixels, Task, ViewContext, WindowContext};
|
||||
@@ -48,7 +48,7 @@ impl ScrollAnchor {
|
||||
if self.anchor == Anchor::min() {
|
||||
scroll_position.y = 0.;
|
||||
} else {
|
||||
let scroll_top = self.anchor.to_display_point(snapshot).row().as_f32();
|
||||
let scroll_top = self.anchor.to_display_point(snapshot).row() as f32;
|
||||
scroll_position.y = scroll_top + scroll_position.y;
|
||||
}
|
||||
scroll_position
|
||||
@@ -200,7 +200,7 @@ impl ScrollManager {
|
||||
)
|
||||
} else {
|
||||
let scroll_top_buffer_point =
|
||||
DisplayPoint::new(DisplayRow(scroll_position.y as u32), 0).to_point(&map);
|
||||
DisplayPoint::new(scroll_position.y as u32, 0).to_point(&map);
|
||||
let top_anchor = map
|
||||
.buffer_snapshot
|
||||
.anchor_at(scroll_top_buffer_point, Bias::Right);
|
||||
@@ -210,7 +210,7 @@ impl ScrollManager {
|
||||
anchor: top_anchor,
|
||||
offset: point(
|
||||
scroll_position.x.max(0.),
|
||||
scroll_position.y - top_anchor.to_display_point(&map).row().as_f32(),
|
||||
scroll_position.y - top_anchor.to_display_point(&map).row() as f32,
|
||||
),
|
||||
},
|
||||
scroll_top_buffer_point.row,
|
||||
@@ -472,7 +472,7 @@ impl Editor {
|
||||
}
|
||||
|
||||
if let Some(visible_lines) = self.visible_line_count() {
|
||||
if newest_head.row() < DisplayRow(screen_top.row().0 + visible_lines as u32) {
|
||||
if newest_head.row() < screen_top.row() + visible_lines as u32 {
|
||||
return Ordering::Equal;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ impl Editor {
|
||||
let scroll_margin_rows = self.vertical_scroll_margin() as u32;
|
||||
|
||||
let mut new_screen_top = self.selections.newest_display(cx).head();
|
||||
*new_screen_top.row_mut() = new_screen_top.row().0.saturating_sub(scroll_margin_rows);
|
||||
*new_screen_top.row_mut() = new_screen_top.row().saturating_sub(scroll_margin_rows);
|
||||
*new_screen_top.column_mut() = 0;
|
||||
let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
|
||||
let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top);
|
||||
@@ -60,7 +60,7 @@ impl Editor {
|
||||
};
|
||||
|
||||
let mut new_screen_top = self.selections.newest_display(cx).head();
|
||||
*new_screen_top.row_mut() = new_screen_top.row().0.saturating_sub(visible_rows / 2);
|
||||
*new_screen_top.row_mut() = new_screen_top.row().saturating_sub(visible_rows / 2);
|
||||
*new_screen_top.column_mut() = 0;
|
||||
let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
|
||||
let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top);
|
||||
@@ -86,7 +86,6 @@ impl Editor {
|
||||
let mut new_screen_top = self.selections.newest_display(cx).head();
|
||||
*new_screen_top.row_mut() = new_screen_top
|
||||
.row()
|
||||
.0
|
||||
.saturating_sub(visible_rows.saturating_sub(scroll_margin_rows));
|
||||
*new_screen_top.column_mut() = 0;
|
||||
let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
|
||||
|
||||
@@ -5,8 +5,7 @@ use gpui::{px, Bounds, Pixels, ViewContext};
|
||||
use language::Point;
|
||||
|
||||
use crate::{
|
||||
display_map::ToDisplayPoint, DiffRowHighlight, DisplayRow, Editor, EditorMode,
|
||||
LineWithInvisibles, RowExt,
|
||||
display_map::ToDisplayPoint, DiffRowHighlight, Editor, EditorMode, LineWithInvisibles,
|
||||
};
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
@@ -89,9 +88,9 @@ impl Editor {
|
||||
}
|
||||
}
|
||||
let max_scroll_top = if matches!(self.mode, EditorMode::AutoHeight { .. }) {
|
||||
(display_map.max_point().row().as_f32() - visible_lines + 1.).max(0.)
|
||||
(display_map.max_point().row() as f32 - visible_lines + 1.).max(0.)
|
||||
} else {
|
||||
display_map.max_point().row().as_f32()
|
||||
display_map.max_point().row() as f32
|
||||
};
|
||||
if scroll_position.y > max_scroll_top {
|
||||
scroll_position.y = max_scroll_top;
|
||||
@@ -114,7 +113,7 @@ impl Editor {
|
||||
)
|
||||
.first_entry()
|
||||
{
|
||||
target_top = first_highlighted_row.key().as_f32();
|
||||
target_top = *first_highlighted_row.key() as f32;
|
||||
target_bottom = target_top + 1.;
|
||||
} else {
|
||||
let selections = self.selections.all::<Point>(cx);
|
||||
@@ -123,16 +122,14 @@ impl Editor {
|
||||
.unwrap()
|
||||
.head()
|
||||
.to_display_point(&display_map)
|
||||
.row()
|
||||
.as_f32();
|
||||
.row() as f32;
|
||||
target_bottom = selections
|
||||
.last()
|
||||
.unwrap()
|
||||
.head()
|
||||
.to_display_point(&display_map)
|
||||
.row()
|
||||
.next_row()
|
||||
.as_f32();
|
||||
.row() as f32
|
||||
+ 1.0;
|
||||
|
||||
// If the selections can't all fit on screen, scroll to the newest.
|
||||
if autoscroll == Autoscroll::newest()
|
||||
@@ -144,8 +141,7 @@ impl Editor {
|
||||
.unwrap()
|
||||
.head()
|
||||
.to_display_point(&display_map)
|
||||
.row()
|
||||
.as_f32();
|
||||
.row() as f32;
|
||||
target_top = newest_selection_top;
|
||||
target_bottom = newest_selection_top + 1.;
|
||||
}
|
||||
@@ -231,7 +227,7 @@ impl Editor {
|
||||
|
||||
pub(crate) fn autoscroll_horizontally(
|
||||
&mut self,
|
||||
start_row: DisplayRow,
|
||||
start_row: u32,
|
||||
viewport_width: Pixels,
|
||||
scroll_width: Pixels,
|
||||
max_glyph_width: Pixels,
|
||||
@@ -249,18 +245,16 @@ impl Editor {
|
||||
target_right = px(0.);
|
||||
for selection in selections {
|
||||
let head = selection.head().to_display_point(&display_map);
|
||||
if head.row() >= start_row
|
||||
&& head.row() < DisplayRow(start_row.0 + layouts.len() as u32)
|
||||
{
|
||||
if head.row() >= start_row && head.row() < start_row + layouts.len() as u32 {
|
||||
let start_column = head.column().saturating_sub(3);
|
||||
let end_column = cmp::min(display_map.line_len(head.row()), head.column() + 3);
|
||||
target_left = target_left.min(
|
||||
layouts[head.row().minus(start_row) as usize]
|
||||
layouts[(head.row() - start_row) as usize]
|
||||
.line
|
||||
.x_for_index(start_column as usize),
|
||||
);
|
||||
target_right = target_right.max(
|
||||
layouts[head.row().minus(start_row) as usize]
|
||||
layouts[(head.row() - start_row) as usize]
|
||||
.line
|
||||
.x_for_index(end_column as usize)
|
||||
+ max_glyph_width,
|
||||
|
||||
@@ -14,8 +14,7 @@ use util::post_inc;
|
||||
use crate::{
|
||||
display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint},
|
||||
movement::TextLayoutDetails,
|
||||
Anchor, DisplayPoint, DisplayRow, ExcerptId, MultiBuffer, MultiBufferSnapshot, SelectMode,
|
||||
ToOffset,
|
||||
Anchor, DisplayPoint, ExcerptId, MultiBuffer, MultiBufferSnapshot, SelectMode, ToOffset,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -309,7 +308,7 @@ impl SelectionsCollection {
|
||||
pub fn build_columnar_selection(
|
||||
&mut self,
|
||||
display_map: &DisplaySnapshot,
|
||||
row: DisplayRow,
|
||||
row: u32,
|
||||
positions: &Range<Pixels>,
|
||||
reversed: bool,
|
||||
text_layout_details: &TextLayoutDetails,
|
||||
|
||||
@@ -81,30 +81,23 @@ pub fn editor_hunks(
|
||||
editor: &Editor,
|
||||
snapshot: &DisplaySnapshot,
|
||||
cx: &mut ViewContext<'_, Editor>,
|
||||
) -> Vec<(
|
||||
String,
|
||||
git::diff::DiffHunkStatus,
|
||||
std::ops::Range<crate::DisplayRow>,
|
||||
)> {
|
||||
use multi_buffer::MultiBufferRow;
|
||||
) -> Vec<(String, git::diff::DiffHunkStatus, core::ops::Range<u32>)> {
|
||||
use text::Point;
|
||||
|
||||
use crate::hunk_status;
|
||||
|
||||
snapshot
|
||||
.buffer_snapshot
|
||||
.git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
|
||||
.git_diff_hunks_in_range(0..u32::MAX)
|
||||
.map(|hunk| {
|
||||
let display_range = Point::new(hunk.associated_range.start.0, 0)
|
||||
let display_range = Point::new(hunk.associated_range.start, 0)
|
||||
.to_display_point(snapshot)
|
||||
.row()
|
||||
..Point::new(hunk.associated_range.end.0, 0)
|
||||
..Point::new(hunk.associated_range.end, 0)
|
||||
.to_display_point(snapshot)
|
||||
.row();
|
||||
let (_, buffer, _) = editor
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.excerpt_containing(Point::new(hunk.associated_range.start.0, 0), cx)
|
||||
.excerpt_containing(Point::new(hunk.associated_range.start, 0), cx)
|
||||
.expect("no excerpt for expanded buffer's hunk start");
|
||||
let diff_base = buffer
|
||||
.read(cx)
|
||||
@@ -112,7 +105,7 @@ pub fn editor_hunks(
|
||||
.expect("should have a diff base for expanded hunk")
|
||||
.slice(hunk.diff_base_byte_range.clone())
|
||||
.to_string();
|
||||
(diff_base, hunk_status(&hunk), display_range)
|
||||
(diff_base, hunk.status(), display_range)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@@ -122,11 +115,7 @@ pub fn expanded_hunks(
|
||||
editor: &Editor,
|
||||
snapshot: &DisplaySnapshot,
|
||||
cx: &mut ViewContext<'_, Editor>,
|
||||
) -> Vec<(
|
||||
String,
|
||||
git::diff::DiffHunkStatus,
|
||||
std::ops::Range<crate::DisplayRow>,
|
||||
)> {
|
||||
) -> Vec<(String, git::diff::DiffHunkStatus, core::ops::Range<u32>)> {
|
||||
editor
|
||||
.expanded_hunks
|
||||
.hunks(false)
|
||||
@@ -161,9 +150,7 @@ pub fn expanded_hunks(
|
||||
pub fn expanded_hunks_background_highlights(
|
||||
editor: &mut Editor,
|
||||
cx: &mut gpui::WindowContext,
|
||||
) -> Vec<std::ops::RangeInclusive<crate::DisplayRow>> {
|
||||
use crate::DisplayRow;
|
||||
|
||||
) -> Vec<std::ops::RangeInclusive<u32>> {
|
||||
let mut highlights = Vec::new();
|
||||
|
||||
let mut range_start = 0;
|
||||
@@ -172,19 +159,19 @@ pub fn expanded_hunks_background_highlights(
|
||||
{
|
||||
match previous_highlighted_row {
|
||||
Some(previous_row) => {
|
||||
if previous_row + 1 != highlighted_row.0 {
|
||||
highlights.push(DisplayRow(range_start)..=DisplayRow(previous_row));
|
||||
range_start = highlighted_row.0;
|
||||
if previous_row + 1 != highlighted_row {
|
||||
highlights.push(range_start..=previous_row);
|
||||
range_start = highlighted_row;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
range_start = highlighted_row.0;
|
||||
range_start = highlighted_row;
|
||||
}
|
||||
}
|
||||
previous_highlighted_row = Some(highlighted_row.0);
|
||||
previous_highlighted_row = Some(highlighted_row);
|
||||
}
|
||||
if let Some(previous_row) = previous_highlighted_row {
|
||||
highlights.push(DisplayRow(range_start)..=DisplayRow(previous_row));
|
||||
highlights.push(range_start..=previous_row);
|
||||
}
|
||||
|
||||
highlights
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::{
|
||||
display_map::ToDisplayPoint, AnchorRangeExt, Autoscroll, DisplayPoint, Editor, MultiBuffer,
|
||||
RowExt,
|
||||
};
|
||||
use collections::BTreeMap;
|
||||
use futures::Future;
|
||||
@@ -193,11 +192,32 @@ impl EditorTestContext {
|
||||
self.assertion_cx.context()
|
||||
}
|
||||
|
||||
// unlike cx.simulate_keystrokes(), this does not run_until_parked
|
||||
// so you can use it to test detailed timing
|
||||
pub fn simulate_keystroke(&mut self, keystroke_text: &str) {
|
||||
pub fn simulate_keystroke(&mut self, keystroke_text: &str) -> ContextHandle {
|
||||
let keystroke_under_test_handle =
|
||||
self.add_assertion_context(format!("Simulated Keystroke: {:?}", keystroke_text));
|
||||
let keystroke = Keystroke::parse(keystroke_text).unwrap();
|
||||
|
||||
self.cx.dispatch_keystroke(self.window, keystroke);
|
||||
|
||||
keystroke_under_test_handle
|
||||
}
|
||||
|
||||
pub fn simulate_keystrokes<const COUNT: usize>(
|
||||
&mut self,
|
||||
keystroke_texts: [&str; COUNT],
|
||||
) -> ContextHandle {
|
||||
let keystrokes_under_test_handle =
|
||||
self.add_assertion_context(format!("Simulated Keystrokes: {:?}", keystroke_texts));
|
||||
for keystroke_text in keystroke_texts.into_iter() {
|
||||
self.simulate_keystroke(keystroke_text);
|
||||
}
|
||||
// it is common for keyboard shortcuts to kick off async actions, so this ensures that they are complete
|
||||
// before returning.
|
||||
// NOTE: we don't do this in simulate_keystroke() because a possible cause of bugs is that typing too
|
||||
// quickly races with async actions.
|
||||
self.cx.background_executor.run_until_parked();
|
||||
|
||||
keystrokes_under_test_handle
|
||||
}
|
||||
|
||||
pub fn run_until_parked(&mut self) {
|
||||
@@ -236,7 +256,7 @@ impl EditorTestContext {
|
||||
let details = editor.text_layout_details(cx);
|
||||
|
||||
let y = pixel_position.y
|
||||
+ line_height * (display_point.row().as_f32() - newest_point.row().as_f32());
|
||||
+ line_height * (display_point.row() as f32 - newest_point.row() as f32);
|
||||
let x = pixel_position.x + snapshot.x_for_display_point(display_point, &details)
|
||||
- snapshot.x_for_display_point(newest_point, &details);
|
||||
Point::new(x, y)
|
||||
|
||||
@@ -23,7 +23,6 @@ collections.workspace = true
|
||||
fs.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
http.workspace = true
|
||||
isahc.workspace = true
|
||||
language.workspace = true
|
||||
log.workspace = true
|
||||
|
||||
@@ -6,7 +6,6 @@ use async_compression::futures::bufread::GzipDecoder;
|
||||
use async_tar::Archive;
|
||||
use futures::io::BufReader;
|
||||
use futures::AsyncReadExt;
|
||||
use http::{self, AsyncBody, HttpClient};
|
||||
use serde::Deserialize;
|
||||
use std::{
|
||||
env, fs, mem,
|
||||
@@ -14,6 +13,7 @@ use std::{
|
||||
process::{Command, Stdio},
|
||||
sync::Arc,
|
||||
};
|
||||
use util::http::{self, AsyncBody, HttpClient};
|
||||
use wasm_encoder::{ComponentSectionId, Encode as _, RawSection, Section as _};
|
||||
use wasmparser::Parser;
|
||||
use wit_component::ComponentEncoder;
|
||||
|
||||
@@ -28,7 +28,6 @@ use gpui::{
|
||||
actions, AppContext, AsyncAppContext, Context, EventEmitter, Global, Model, ModelContext, Task,
|
||||
WeakModel,
|
||||
};
|
||||
use http::{AsyncBody, HttpClient, HttpClientWithUrl};
|
||||
use language::{
|
||||
ContextProviderWithTasks, LanguageConfig, LanguageMatcher, LanguageQueries, LanguageRegistry,
|
||||
QUERY_FILENAME_PREFIXES,
|
||||
@@ -47,7 +46,12 @@ use std::{
|
||||
};
|
||||
use theme::{ThemeRegistry, ThemeSettings};
|
||||
use url::Url;
|
||||
use util::{maybe, paths::EXTENSIONS_DIR, ResultExt};
|
||||
use util::{
|
||||
http::{AsyncBody, HttpClient, HttpClientWithUrl},
|
||||
maybe,
|
||||
paths::EXTENSIONS_DIR,
|
||||
ResultExt,
|
||||
};
|
||||
use wasm_host::{
|
||||
wit::{is_supported_wasm_api_version, wasm_api_version_range},
|
||||
WasmExtension, WasmHost,
|
||||
|
||||
@@ -10,7 +10,6 @@ use collections::BTreeMap;
|
||||
use fs::{FakeFs, Fs, RealFs};
|
||||
use futures::{io::BufReader, AsyncReadExt, StreamExt};
|
||||
use gpui::{Context, TestAppContext};
|
||||
use http::{FakeHttpClient, Response};
|
||||
use language::{LanguageMatcher, LanguageRegistry, LanguageServerBinaryStatus, LanguageServerName};
|
||||
use node_runtime::FakeNodeRuntime;
|
||||
use parking_lot::Mutex;
|
||||
@@ -23,7 +22,10 @@ use std::{
|
||||
sync::Arc,
|
||||
};
|
||||
use theme::ThemeRegistry;
|
||||
use util::test::temp_tree;
|
||||
use util::{
|
||||
http::{FakeHttpClient, Response},
|
||||
test::temp_tree,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
#[ctor::ctor]
|
||||
|
||||
@@ -13,7 +13,6 @@ use futures::{
|
||||
Future, FutureExt, StreamExt as _,
|
||||
};
|
||||
use gpui::{AppContext, AsyncAppContext, BackgroundExecutor, Task};
|
||||
use http::HttpClient;
|
||||
use language::LanguageRegistry;
|
||||
use node_runtime::NodeRuntime;
|
||||
use semantic_version::SemanticVersion;
|
||||
@@ -21,6 +20,7 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::{Arc, OnceLock},
|
||||
};
|
||||
use util::http::HttpClient;
|
||||
use wasmtime::{
|
||||
component::{Component, ResourceTable},
|
||||
Engine, Store,
|
||||
|
||||
@@ -155,7 +155,7 @@ impl github::Host for WasmState {
|
||||
options: github::GithubReleaseOptions,
|
||||
) -> wasmtime::Result<Result<github::GithubRelease, String>> {
|
||||
maybe!(async {
|
||||
let release = http::github::latest_github_release(
|
||||
let release = util::github::latest_github_release(
|
||||
&repo,
|
||||
options.require_assets,
|
||||
options.pre_release,
|
||||
|
||||
@@ -58,7 +58,6 @@ const SUGGESTIONS_BY_EXTENSION_ID: &[(&str, &[&str])] = &[
|
||||
("r", &["r", "R"]),
|
||||
("racket", &["rkt"]),
|
||||
("rescript", &["res", "resi"]),
|
||||
("ruby", &["rb", "erb"]),
|
||||
("scheme", &["scm"]),
|
||||
("scss", &["scss"]),
|
||||
("sql", &["sql"]),
|
||||
|
||||
@@ -24,7 +24,6 @@ futures.workspace = true
|
||||
gpui.workspace = true
|
||||
human_bytes = "0.4.1"
|
||||
isahc.workspace = true
|
||||
http.workspace = true
|
||||
language.workspace = true
|
||||
log.workspace = true
|
||||
menu.workspace = true
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user