Merge branch 'main' into kvark-linux

This commit is contained in:
Mikayla 2024-02-07 11:49:49 -08:00
commit 67555ee5b4
No known key found for this signature in database
238 changed files with 6624 additions and 3524 deletions

View File

@ -2,29 +2,29 @@ name: "Check formatting"
description: "Checks code formatting use cargo fmt"
runs:
using: "composite"
steps:
- name: cargo fmt
shell: bash -euxo pipefail {0}
run: cargo fmt --all -- --check
using: "composite"
steps:
- name: cargo fmt
shell: bash -euxo pipefail {0}
run: cargo fmt --all -- --check
- name: cargo clippy
shell: bash -euxo pipefail {0}
# clippy.toml is not currently supporting specifying allowed lints
# so specify those here, and disable the rest until Zed's workspace
# will have more fixes & suppression for the standard lint set
run: |
cargo clippy --release --workspace --all-features --all-targets -- -A clippy::all -D clippy::dbg_macro -D clippy::todo
cargo clippy -p gpui
- name: cargo clippy
shell: bash -euxo pipefail {0}
# clippy.toml is not currently supporting specifying allowed lints
# so specify those here, and disable the rest until Zed's workspace
# will have more fixes & suppression for the standard lint set
run: |
cargo clippy --release --workspace --all-features --all-targets -- -A clippy::all -D clippy::dbg_macro -D clippy::todo
cargo clippy -p gpui
- name: Find modified migrations
shell: bash -euxo pipefail {0}
run: |
export SQUAWK_GITHUB_TOKEN=${{ github.token }}
. ./script/squawk
- name: Find modified migrations
shell: bash -euxo pipefail {0}
run: |
export SQUAWK_GITHUB_TOKEN=${{ github.token }}
. ./script/squawk
- uses: bufbuild/buf-setup-action@v1
- uses: bufbuild/buf-breaking-action@v1
with:
input: "crates/rpc/proto/"
against: "https://github.com/${GITHUB_REPOSITORY}.git#branch=main,subdir=crates/rpc/proto/"
- uses: bufbuild/buf-setup-action@v1
- uses: bufbuild/buf-breaking-action@v1
with:
input: "crates/rpc/proto/"
against: "https://github.com/${GITHUB_REPOSITORY}.git#branch=main,subdir=crates/rpc/proto/"

View File

@ -10,7 +10,7 @@ runs:
cargo install cargo-nextest
- name: Install Node
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: "18"

View File

@ -2,4 +2,8 @@
Release Notes:
- (Added|Fixed|Improved) ... ([#<public_issue_number_if_exists>](https://github.com/zed-industries/zed/issues/<public_issue_number_if_exists>)).
- Added/Fixed/Improved ... ([#<public_issue_number_if_exists>](https://github.com/zed-industries/zed/issues/<public_issue_number_if_exists>)).
**or**
- N/A

View File

@ -1,149 +1,149 @@
name: CI
on:
push:
branches:
- main
- "v[0-9]+.[0-9]+.x"
tags:
- "v*"
pull_request:
branches:
- "**"
push:
branches:
- main
- "v[0-9]+.[0-9]+.x"
tags:
- "v*"
pull_request:
branches:
- "**"
concurrency:
# Allow only one workflow per any non-`main` branch.
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }}
cancel-in-progress: true
# Allow only one workflow per any non-`main` branch.
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: 1
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: 1
jobs:
style:
name: Check formatting, Clippy lints, and spelling
runs-on:
- self-hosted
- test
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
fetch-depth: 0
style:
name: Check formatting, Clippy lints, and spelling
runs-on:
- self-hosted
- test
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
fetch-depth: 0
- name: Set up default .cargo/config.toml
run: cp ./.cargo/ci-config.toml ~/.cargo/config.toml
- name: Set up default .cargo/config.toml
run: cp ./.cargo/ci-config.toml ~/.cargo/config.toml
- name: Check spelling
run: |
if ! which typos > /dev/null; then
cargo install typos-cli
fi
typos
- name: Check spelling
run: |
if ! which typos > /dev/null; then
cargo install typos-cli
fi
typos
- name: Run style checks
uses: ./.github/actions/check_style
- name: Run style checks
uses: ./.github/actions/check_style
tests:
name: Run tests
runs-on:
- self-hosted
- test
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
tests:
name: Run tests
runs-on:
- self-hosted
- test
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
- name: Run tests
uses: ./.github/actions/run_tests
- name: Run tests
uses: ./.github/actions/run_tests
- name: Build collab
run: cargo build -p collab
- name: Build collab
run: cargo build -p collab
- name: Build other binaries
run: cargo build --workspace --bins --all-features
- name: Build other binaries
run: cargo build --workspace --bins --all-features
bundle:
name: Bundle app
runs-on:
- self-hosted
- bundle
if: ${{ startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-build-dmg') }}
needs: tests
env:
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
APPLE_NOTARIZATION_USERNAME: ${{ secrets.APPLE_NOTARIZATION_USERNAME }}
APPLE_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_NOTARIZATION_PASSWORD }}
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
steps:
- name: Install Node
uses: actions/setup-node@v3
with:
node-version: "18"
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
- name: Limit target directory size
run: script/clear-target-dir-if-larger-than 100
- name: Determine version and release channel
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
run: |
set -eu
version=$(script/get-crate-version zed)
channel=$(cat crates/zed/RELEASE_CHANNEL)
echo "Publishing version: ${version} on release channel ${channel}"
echo "RELEASE_CHANNEL=${channel}" >> $GITHUB_ENV
expected_tag_name=""
case ${channel} in
stable)
expected_tag_name="v${version}";;
preview)
expected_tag_name="v${version}-pre";;
nightly)
expected_tag_name="v${version}-nightly";;
*)
echo "can't publish a release on channel ${channel}"
exit 1;;
esac
if [[ $GITHUB_REF_NAME != $expected_tag_name ]]; then
echo "invalid release tag ${GITHUB_REF_NAME}. expected ${expected_tag_name}"
exit 1
fi
- name: Generate license file
run: script/generate-licenses
- name: Create app bundle
run: script/bundle
- name: Upload app bundle to workflow run if main branch or specific label
uses: actions/upload-artifact@v3
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-build-dmg') }}
with:
name: Zed_${{ github.event.pull_request.head.sha || github.sha }}.dmg
path: target/release/Zed.dmg
- uses: softprops/action-gh-release@v1
name: Upload app bundle to release
if: ${{ env.RELEASE_CHANNEL == 'preview' || env.RELEASE_CHANNEL == 'stable' }}
with:
draft: true
prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}
files: target/release/Zed.dmg
body: ""
bundle:
name: Bundle app
runs-on:
- self-hosted
- bundle
if: ${{ startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-build-dmg') }}
needs: tests
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
APPLE_NOTARIZATION_USERNAME: ${{ secrets.APPLE_NOTARIZATION_USERNAME }}
APPLE_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_NOTARIZATION_PASSWORD }}
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
steps:
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "18"
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
- name: Limit target directory size
run: script/clear-target-dir-if-larger-than 100
- name: Determine version and release channel
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
run: |
set -eu
version=$(script/get-crate-version zed)
channel=$(cat crates/zed/RELEASE_CHANNEL)
echo "Publishing version: ${version} on release channel ${channel}"
echo "RELEASE_CHANNEL=${channel}" >> $GITHUB_ENV
expected_tag_name=""
case ${channel} in
stable)
expected_tag_name="v${version}";;
preview)
expected_tag_name="v${version}-pre";;
nightly)
expected_tag_name="v${version}-nightly";;
*)
echo "can't publish a release on channel ${channel}"
exit 1;;
esac
if [[ $GITHUB_REF_NAME != $expected_tag_name ]]; then
echo "invalid release tag ${GITHUB_REF_NAME}. expected ${expected_tag_name}"
exit 1
fi
- name: Generate license file
run: script/generate-licenses
- name: Create app bundle
run: script/bundle
- name: Upload app bundle to workflow run if main branch or specific label
uses: actions/upload-artifact@v3
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-build-dmg') }}
with:
name: Zed_${{ github.event.pull_request.head.sha || github.sha }}.dmg
path: target/release/Zed.dmg
- uses: softprops/action-gh-release@v1
name: Upload app bundle to release
if: ${{ env.RELEASE_CHANNEL == 'preview' || env.RELEASE_CHANNEL == 'stable' }}
with:
draft: true
prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}
files: target/release/Zed.dmg
body: ""
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,35 +1,35 @@
name: Danger
on:
pull_request:
branches: [main]
types:
- opened
- synchronize
- reopened
- edited
pull_request:
branches: [main]
types:
- opened
- synchronize
- reopened
- edited
jobs:
danger:
runs-on: ubuntu-latest
danger:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2.2.4
with:
version: 8
- uses: pnpm/action-setup@v2.2.4
with:
version: 8
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "pnpm"
cache-dependency-path: "script/danger/pnpm-lock.yaml"
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "pnpm"
cache-dependency-path: "script/danger/pnpm-lock.yaml"
- run: pnpm install --dir script/danger
- run: pnpm install --dir script/danger
- name: Run Danger
run: pnpm run --dir script/danger danger ci
env:
GITHUB_TOKEN: ${{ github.token }}
- name: Run Danger
run: pnpm run --dir script/danger danger ci
env:
GITHUB_TOKEN: ${{ github.token }}

View File

@ -3,35 +3,35 @@ name: Randomized Tests
concurrency: randomized-tests
on:
push:
branches:
- randomized-tests-runner
# schedule:
# - cron: '0 * * * *'
push:
branches:
- randomized-tests-runner
# schedule:
# - cron: '0 * * * *'
env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: 1
ZED_SERVER_URL: https://zed.dev
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: 1
ZED_SERVER_URL: https://zed.dev
jobs:
tests:
name: Run randomized tests
runs-on:
- self-hosted
- randomized-tests
steps:
- name: Install Node
uses: actions/setup-node@v3
with:
node-version: "18"
tests:
name: Run randomized tests
runs-on:
- self-hosted
- randomized-tests
steps:
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "18"
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
- name: Run randomized tests
run: script/randomized-test-ci
- name: Run randomized tests
run: script/randomized-test-ci

View File

@ -16,6 +16,7 @@ env:
jobs:
style:
name: Check formatting and Clippy lints
if: github.repository_owner == 'zed-industries'
runs-on:
- self-hosted
- test
@ -32,6 +33,7 @@ jobs:
tests:
name: Run tests
if: github.repository_owner == 'zed-industries'
runs-on:
- self-hosted
- test
@ -48,6 +50,7 @@ jobs:
bundle:
name: Bundle app
if: github.repository_owner == 'zed-industries'
runs-on:
- self-hosted
- bundle
@ -62,7 +65,7 @@ jobs:
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
steps:
- name: Install Node
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: "18"

View File

@ -6,6 +6,7 @@ on:
jobs:
update_top_ranking_issues:
runs-on: ubuntu-latest
if: github.repository_owner == 'zed-industries'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4

View File

@ -6,6 +6,7 @@ on:
jobs:
update_top_ranking_issues:
runs-on: ubuntu-latest
if: github.repository_owner == 'zed-industries'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4

137
Cargo.lock generated
View File

@ -103,9 +103,9 @@ dependencies = [
[[package]]
name = "alacritty_terminal"
version = "0.21.0"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35229555d7cc7e83392dfc27c96bec560b1076d756184893296cd60125f4a264"
checksum = "cc7ceabf6fc76511f616ca216b51398a2511f19ba9f71bcbd977999edff1b0d1"
dependencies = [
"base64 0.21.4",
"bitflags 2.4.1",
@ -467,6 +467,18 @@ dependencies = [
"event-listener",
]
[[package]]
name = "async-native-tls"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e9e7a929bd34c68a82d58a4de7f86fffdaf97fb2af850162a7bb19dd7269b33"
dependencies = [
"async-std",
"native-tls",
"thiserror",
"url",
]
[[package]]
name = "async-net"
version = "1.7.0"
@ -596,19 +608,6 @@ version = "4.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799"
[[package]]
name = "async-tls"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f23d769dbf1838d5df5156e7b1ad404f4c463d1ac2c6aeb6cd943630f8a8400"
dependencies = [
"futures-core",
"futures-io",
"rustls 0.19.1",
"webpki",
"webpki-roots 0.21.1",
]
[[package]]
name = "async-trait"
version = "0.1.73"
@ -626,7 +625,8 @@ version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5682ea0913e5c20780fe5785abacb85a411e7437bf52a1bedb93ddb3972cb8dd"
dependencies = [
"async-tls",
"async-native-tls",
"async-std",
"futures-io",
"futures-util",
"log",
@ -2507,6 +2507,7 @@ dependencies = [
"itertools 0.10.5",
"language",
"lazy_static",
"linkify",
"log",
"lsp",
"multi_buffer",
@ -4026,12 +4027,6 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "json_comments"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dbbfed4e59ba9750e15ba154fdfd9329cee16ff3df539c2666b70f58cc32105"
[[package]]
name = "jwt"
version = "0.16.0"
@ -4310,6 +4305,15 @@ dependencies = [
"safemem",
]
[[package]]
name = "linkify"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1dfa36d52c581e9ec783a7ce2a5e0143da6237be5811a0b3153fedfdbe9f780"
dependencies = [
"memchr",
]
[[package]]
name = "linkme"
version = "0.3.17"
@ -6283,6 +6287,7 @@ dependencies = [
"editor",
"gpui",
"search",
"settings",
"ui",
"workspace",
]
@ -6965,19 +6970,6 @@ dependencies = [
"rustix 0.38.30",
]
[[package]]
name = "rustls"
version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7"
dependencies = [
"base64 0.13.1",
"log",
"ring",
"sct 0.6.1",
"webpki",
]
[[package]]
name = "rustls"
version = "0.21.7"
@ -6986,7 +6978,7 @@ checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8"
dependencies = [
"ring",
"rustls-webpki",
"sct 0.7.0",
"sct",
]
[[package]]
@ -7129,16 +7121,6 @@ dependencies = [
"sha2 0.9.9",
]
[[package]]
name = "sct"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "sct"
version = "0.7.0"
@ -7792,6 +7774,7 @@ name = "sqlez"
version = "0.1.0"
dependencies = [
"anyhow",
"collections",
"futures 0.3.28",
"indoc",
"lazy_static",
@ -7870,7 +7853,7 @@ dependencies = [
"paste",
"percent-encoding",
"rust_decimal",
"rustls 0.21.7",
"rustls",
"rustls-pemfile",
"serde",
"serde_json",
@ -7884,7 +7867,7 @@ dependencies = [
"tracing",
"url",
"uuid 1.4.1",
"webpki-roots 0.24.0",
"webpki-roots",
]
[[package]]
@ -8367,6 +8350,7 @@ version = "0.1.0"
dependencies = [
"alacritty_terminal",
"anyhow",
"collections",
"db",
"dirs 4.0.0",
"futures 0.3.28",
@ -8397,6 +8381,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"client",
"collections",
"db",
"dirs 4.0.0",
"editor",
@ -8461,6 +8446,7 @@ name = "theme"
version = "0.1.0"
dependencies = [
"anyhow",
"collections",
"color",
"derive_more",
"fs",
@ -8495,7 +8481,6 @@ dependencies = [
"gpui",
"indexmap 1.9.3",
"indoc",
"json_comments",
"log",
"palette",
"pathfinder_color",
@ -8503,6 +8488,7 @@ dependencies = [
"schemars",
"serde",
"serde_json",
"serde_json_lenient",
"simplelog",
"strum",
"theme",
@ -8991,6 +8977,15 @@ dependencies = [
"tree-sitter",
]
[[package]]
name = "tree-sitter-beancount"
version = "2.2.0"
source = "git+https://github.com/polarmutex/tree-sitter-beancount?rev=da1bf8c6eb0ae7a97588affde7227630bcd678b6#da1bf8c6eb0ae7a97588affde7227630bcd678b6"
dependencies = [
"cc",
"tree-sitter",
]
[[package]]
name = "tree-sitter-c"
version = "0.20.6"
@ -9129,6 +9124,15 @@ dependencies = [
"tree-sitter",
]
[[package]]
name = "tree-sitter-hcl"
version = "0.0.1"
source = "git+https://github.com/MichaHoffmann/tree-sitter-hcl?rev=v1.1.0#636dbe70301ecbab8f353c8c78b3406fe4f185f5"
dependencies = [
"cc",
"tree-sitter",
]
[[package]]
name = "tree-sitter-heex"
version = "0.0.1"
@ -9204,6 +9208,15 @@ dependencies = [
"tree-sitter",
]
[[package]]
name = "tree-sitter-ocaml"
version = "0.20.4"
source = "git+https://github.com/tree-sitter/tree-sitter-ocaml?rev=4abfdc1c7af2c6c77a370aee974627be1c285b3b#4abfdc1c7af2c6c77a370aee974627be1c285b3b"
dependencies = [
"cc",
"tree-sitter",
]
[[package]]
name = "tree-sitter-php"
version = "0.21.1"
@ -9373,6 +9386,7 @@ dependencies = [
"http",
"httparse",
"log",
"native-tls",
"rand 0.8.5",
"sha-1 0.9.8",
"thiserror",
@ -9582,6 +9596,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"backtrace",
"collections",
"dirs 3.0.2",
"futures 0.3.28",
"git2",
@ -10086,25 +10101,6 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "webpki"
version = "0.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "webpki-roots"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940"
dependencies = [
"webpki",
]
[[package]]
name = "webpki-roots"
version = "0.24.0"
@ -10563,7 +10559,7 @@ dependencies = [
[[package]]
name = "zed"
version = "0.122.0"
version = "0.123.0"
dependencies = [
"activity_indicator",
"ai",
@ -10658,6 +10654,7 @@ dependencies = [
"toml",
"tree-sitter",
"tree-sitter-bash",
"tree-sitter-beancount",
"tree-sitter-c",
"tree-sitter-c-sharp",
"tree-sitter-cpp",
@ -10673,6 +10670,7 @@ dependencies = [
"tree-sitter-gomod",
"tree-sitter-gowork",
"tree-sitter-haskell",
"tree-sitter-hcl",
"tree-sitter-heex",
"tree-sitter-html",
"tree-sitter-json 0.20.0",
@ -10680,6 +10678,7 @@ dependencies = [
"tree-sitter-markdown",
"tree-sitter-nix",
"tree-sitter-nu",
"tree-sitter-ocaml",
"tree-sitter-php",
"tree-sitter-proto",
"tree-sitter-purescript",

View File

@ -92,6 +92,90 @@ default-members = ["crates/zed"]
resolver = "2"
[workspace.dependencies]
activity_indicator = { path = "crates/activity_indicator" }
ai = { path = "crates/ai" }
assets = { path = "crates/assets" }
assistant = { path = "crates/assistant" }
audio = { path = "crates/audio" }
auto_update = { path = "crates/auto_update" }
breadcrumbs = { path = "crates/breadcrumbs" }
call = { path = "crates/call" }
channel = { path = "crates/channel" }
cli = { path = "crates/cli" }
client = { path = "crates/client" }
clock = { path = "crates/clock" }
collab = { path = "crates/collab" }
collab_ui = { path = "crates/collab_ui" }
collections = { path = "crates/collections" }
color = { path = "crates/color" }
command_palette = { path = "crates/command_palette" }
copilot = { path = "crates/copilot" }
copilot_ui = { path = "crates/copilot_ui" }
db = { path = "crates/db" }
diagnostics = { path = "crates/diagnostics" }
editor = { path = "crates/editor" }
feature_flags = { path = "crates/feature_flags" }
feedback = { path = "crates/feedback" }
file_finder = { path = "crates/file_finder" }
fs = { path = "crates/fs" }
fsevent = { path = "crates/fsevent" }
fuzzy = { path = "crates/fuzzy" }
git = { path = "crates/git" }
go_to_line = { path = "crates/go_to_line" }
gpui = { path = "crates/gpui" }
gpui_macros = { path = "crates/gpui_macros" }
install_cli = { path = "crates/install_cli" }
journal = { path = "crates/journal" }
language = { path = "crates/language" }
language_selector = { path = "crates/language_selector" }
language_tools = { path = "crates/language_tools" }
live_kit_client = { path = "crates/live_kit_client" }
live_kit_server = { path = "crates/live_kit_server" }
lsp = { path = "crates/lsp" }
markdown_preview = { path = "crates/markdown_preview" }
media = { path = "crates/media" }
menu = { path = "crates/menu" }
multi_buffer = { path = "crates/multi_buffer" }
node_runtime = { path = "crates/node_runtime" }
notifications = { path = "crates/notifications" }
outline = { path = "crates/outline" }
picker = { path = "crates/picker" }
plugin = { path = "crates/plugin" }
plugin_macros = { path = "crates/plugin_macros" }
prettier = { path = "crates/prettier" }
project = { path = "crates/project" }
project_panel = { path = "crates/project_panel" }
project_symbols = { path = "crates/project_symbols" }
quick_action_bar = { path = "crates/quick_action_bar" }
recent_projects = { path = "crates/recent_projects" }
release_channel = { path = "crates/release_channel" }
rich_text = { path = "crates/rich_text" }
rope = { path = "crates/rope" }
rpc = { path = "crates/rpc" }
search = { path = "crates/search" }
semantic_index = { path = "crates/semantic_index" }
settings = { path = "crates/settings" }
snippet = { path = "crates/snippet" }
sqlez = { path = "crates/sqlez" }
sqlez_macros = { path = "crates/sqlez_macros" }
story = { path = "crates/story" }
storybook = { path = "crates/storybook" }
sum_tree = { path = "crates/sum_tree" }
terminal = { path = "crates/terminal" }
terminal_view = { path = "crates/terminal_view" }
text = { path = "crates/text" }
theme = { path = "crates/theme" }
theme_importer = { path = "crates/theme_importer" }
theme_selector = { path = "crates/theme_selector" }
ui = { path = "crates/ui" }
util = { path = "crates/util" }
vcs_menu = { path = "crates/vcs_menu" }
vim = { path = "crates/vim" }
welcome = { path = "crates/welcome" }
workspace = { path = "crates/workspace" }
zed = { path = "crates/zed" }
zed_actions = { path = "crates/zed_actions" }
anyhow = "1.0.57"
async-compression = { version = "0.4", features = ["gzip", "futures-io"] }
async-trait = "0.1"
@ -100,11 +184,14 @@ ctor = "0.2.6"
derive_more = "0.99.17"
env_logger = "0.9"
futures = "0.3"
git2 = { version = "0.15", default-features = false}
git2 = { version = "0.15", default-features = false }
globset = "0.4"
indoc = "1"
# We explicitly disable a http2 support in isahc.
isahc = { version = "1.7.2", default-features = false, features = ["static-curl", "text-decoding"] }
isahc = { version = "1.7.2", default-features = false, features = [
"static-curl",
"text-decoding",
] }
lazy_static = "1.4.0"
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
ordered-float = "2.1.1"
@ -122,7 +209,10 @@ schemars = "0.8"
serde = { version = "1.0", features = ["derive", "rc"] }
serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
serde_json = { version = "1.0", features = ["preserve_order", "raw_value"] }
serde_json_lenient = { version = "0.1", features = ["preserve_order", "raw_value"] }
serde_json_lenient = { version = "0.1", features = [
"preserve_order",
"raw_value",
] }
serde_repr = "0.1"
smallvec = { version = "1.6", features = ["union"] }
smol = "1.2"
@ -135,9 +225,10 @@ time = { version = "0.3", features = ["serde", "serde-well-known"] }
toml = "0.5"
tree-sitter = { version = "0.20", features = ["wasm"] }
tree-sitter-bash = { git = "https://github.com/tree-sitter/tree-sitter-bash", rev = "7331995b19b8f8aba2d5e26deb51d2195c18bc94" }
tree-sitter-beancount = { git = "https://github.com/polarmutex/tree-sitter-beancount", rev = "da1bf8c6eb0ae7a97588affde7227630bcd678b6" }
tree-sitter-c = "0.20.1"
tree-sitter-c-sharp = { git = "https://github.com/tree-sitter/tree-sitter-c-sharp", rev = "dd5e59721a5f8dae34604060833902b882023aaf" }
tree-sitter-cpp = { git = "https://github.com/tree-sitter/tree-sitter-cpp", rev="f44509141e7e483323d2ec178f2d2e6c0fc041c1" }
tree-sitter-cpp = { git = "https://github.com/tree-sitter/tree-sitter-cpp", rev = "f44509141e7e483323d2ec178f2d2e6c0fc041c1" }
tree-sitter-css = { git = "https://github.com/tree-sitter/tree-sitter-css", rev = "769203d0f9abe1a9a691ac2b9fe4bb4397a73c51" }
tree-sitter-elixir = { git = "https://github.com/elixir-lang/tree-sitter-elixir", rev = "a2861e88a730287a60c11ea9299c033c7d076e30" }
tree-sitter-elm = { git = "https://github.com/elm-tooling/tree-sitter-elm", rev = "692c50c0b961364c40299e73c1306aecb5d20f40" }
@ -150,6 +241,7 @@ tree-sitter-go = { git = "https://github.com/tree-sitter/tree-sitter-go", rev =
tree-sitter-gomod = { git = "https://github.com/camdencheek/tree-sitter-go-mod" }
tree-sitter-gowork = { git = "https://github.com/d1y/tree-sitter-go-work" }
tree-sitter-haskell = { git = "https://github.com/tree-sitter/tree-sitter-haskell", rev = "cf98de23e4285b8e6bcb57b050ef2326e2cc284b" }
tree-sitter-hcl = { git = "https://github.com/MichaHoffmann/tree-sitter-hcl", rev = "v1.1.0" }
tree-sitter-heex = { git = "https://github.com/phoenixframework/tree-sitter-heex", rev = "2e1348c3cf2c9323e87c2744796cf3f3868aa82a" }
tree-sitter-html = "0.19.0"
tree-sitter-json = { git = "https://github.com/tree-sitter/tree-sitter-json", rev = "40a81c01a40ac48744e0c8ccabbaba1920441199" }
@ -157,8 +249,9 @@ tree-sitter-lua = "0.0.14"
tree-sitter-markdown = { git = "https://github.com/MDeiml/tree-sitter-markdown", rev = "330ecab87a3e3a7211ac69bbadc19eabecdb1cca" }
tree-sitter-nix = { git = "https://github.com/nix-community/tree-sitter-nix", rev = "66e3e9ce9180ae08fc57372061006ef83f0abde7" }
tree-sitter-nu = { git = "https://github.com/nushell/tree-sitter-nu", rev = "26bbaecda0039df4067861ab38ea8ea169f7f5aa" }
tree-sitter-ocaml = { git = "https://github.com/tree-sitter/tree-sitter-ocaml", rev = "4abfdc1c7af2c6c77a370aee974627be1c285b3b" }
tree-sitter-php = "0.21.1"
tree-sitter-proto = {git = "https://github.com/rewinfrey/tree-sitter-proto", rev = "36d54f288aee112f13a67b550ad32634d0c2cb52" }
tree-sitter-proto = { git = "https://github.com/rewinfrey/tree-sitter-proto", rev = "36d54f288aee112f13a67b550ad32634d0c2cb52" }
tree-sitter-purescript = { git = "https://github.com/ivanmoreau/tree-sitter-purescript", rev = "a37140f0c7034977b90faa73c94fcb8a5e45ed08" }
tree-sitter-python = "0.20.2"
tree-sitter-racket = { git = "https://github.com/zed-industries/tree-sitter-racket", rev = "eb010cf2c674c6fd9a6316a84e28ef90190fe51a" }

View File

@ -0,0 +1,9 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.7295 12.1981L7.17677 7.64544C7.07914 7.5478 6.92085 7.5478 6.82322 7.64544L2.27053 12.1981C2.11304 12.3556 2.22458 12.6249 2.4473 12.6249H11.5527C11.7754 12.6249 11.887 12.3556 11.7295 12.1981Z" fill="black" fill-opacity="0.9"/>
<path d="M1.80178 11.7294L6.35447 7.17668C6.4521 7.07905 6.4521 6.92076 6.35447 6.82312L1.80178 2.27043C1.64429 2.11294 1.375 2.22448 1.375 2.44721L1.375 11.5526C1.375 11.7753 1.64428 11.8869 1.80178 11.7294Z" fill="black" fill-opacity="0.9"/>
<path d="M9.98928 9.16694L11.9794 7.17751C12.0771 7.0799 12.0771 6.92161 11.9795 6.82396L9.98928 4.833C9.89165 4.73534 9.73333 4.73532 9.63569 4.83297L7.64553 6.82313C7.5479 6.92076 7.5479 7.07905 7.64553 7.17668L9.63575 9.16691C9.73337 9.26453 9.89164 9.26455 9.98928 9.16694Z" fill="black" fill-opacity="0.9"/>
<path d="M7.89553 1.80168L12.1982 6.10438C12.3557 6.26187 12.625 6.15033 12.625 5.9276V2.37491C12.625 1.82262 12.1773 1.37491 11.625 1.37491H8.0723C7.84958 1.37491 7.73804 1.64419 7.89553 1.80168Z" fill="black" fill-opacity="0.6"/>
<path d="M8.73976 4.18772L5.25981 4.1895C5.03708 4.18962 4.92567 4.45896 5.08325 4.61637L6.82322 6.35456C6.92087 6.45211 7.07909 6.45207 7.17669 6.35447L8.91666 4.61449C9.0742 4.45696 8.96255 4.1876 8.73976 4.18772Z" fill="black" fill-opacity="0.6"/>
<path d="M8.1147 3.55936L4.13431 3.55936C4.06801 3.55936 4.00442 3.53302 3.95753 3.48614L2.27057 1.79918C2.11308 1.64169 2.22462 1.37241 2.44735 1.37241L6.42774 1.37241C6.49405 1.37241 6.55763 1.39874 6.60452 1.44563L8.29148 3.13258C8.44897 3.29007 8.33743 3.55936 8.1147 3.55936Z" fill="black" fill-opacity="0.6"/>
<path d="M12.625 8.07221V11.5526C12.625 11.7753 12.3557 11.8869 12.1982 11.7294L10.458 9.98918C10.3604 9.89155 10.3604 9.73326 10.458 9.63563L12.1982 7.89544C12.3557 7.73794 12.625 7.84949 12.625 8.07221Z" fill="black" fill-opacity="0.6"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -25,6 +25,7 @@
"doc": "document",
"docx": "document",
"eex": "elixir",
"elm": "elm",
"erl": "erlang",
"escript": "erlang",
"eslintrc": "eslint",
@ -69,6 +70,8 @@
"mdb": "storage",
"mdf": "storage",
"mdx": "document",
"ml": "ocaml",
"mli": "ocaml",
"mp3": "audio",
"mp4": "video",
"myd": "storage",
@ -85,6 +88,7 @@
"pptx": "document",
"prettierignore": "prettier",
"prettierrc": "prettier",
"prisma": "prisma",
"profile": "terminal",
"ps1": "terminal",
"psd": "image",
@ -116,8 +120,8 @@
"xlsx": "document",
"xml": "template",
"xrl": "erlang",
"yaml": "yaml",
"yml": "yaml",
"yaml": "settings",
"yml": "settings",
"yrl": "erlang",
"zlogin": "terminal",
"zsh": "terminal",
@ -152,6 +156,9 @@
"elixir": {
"icon": "icons/file_icons/elixir.svg"
},
"elm": {
"icon": "icons/file_icons/elm.svg"
},
"erlang": {
"icon": "icons/file_icons/erlang.svg"
},
@ -179,18 +186,21 @@
"log": {
"icon": "icons/file_icons/info.svg"
},
"ocaml": {
"icon": "icons/file_icons/ocaml.svg"
},
"phoenix": {
"icon": "icons/file_icons/phoenix.svg"
},
"php": {
"icon": "icons/file_icons/php.svg"
},
"yaml": {
"icon": "icons/file_icons/yaml.svg"
},
"prettier": {
"icon": "icons/file_icons/prettier.svg"
},
"prisma": {
"icon": "icons/file_icons/prisma.svg"
},
"python": {
"icon": "icons/file_icons/python.svg"
},

View File

@ -0,0 +1,5 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.73843 11.709C6.70584 11.6334 6.60683 11.4367 6.55712 11.3736C6.44917 11.2362 6.42396 11.2258 6.39221 11.0523C6.33703 10.7501 6.19094 10.202 6.01879 9.82381C5.92987 9.62863 5.78201 9.46467 5.64665 9.32312C5.52847 9.19895 5.26214 8.99002 5.2157 9.00037C4.7807 9.09487 4.64576 9.55902 4.44115 9.92673C4.32795 10.1301 4.208 10.3031 4.1188 10.5195C4.03641 10.7184 4.04373 10.9387 3.90268 11.1095C3.75801 11.2849 3.66398 11.4715 3.59311 11.6981C3.57968 11.7412 3.54148 12.1939 3.5 12.3006L4.14649 12.2511C4.74896 12.2958 4.57496 12.547 5.51526 12.4922L7 12.4423C6.95398 12.2942 6.89056 12.1228 6.86613 12.067C6.82472 11.9732 6.77267 11.7897 6.73843 11.709Z" fill="black"/>
<path d="M8.72454 8.42043C8.61775 8.50889 8.40904 8.72165 7.95506 8.8021C7.75133 8.83825 7.56076 8.84122 7.35158 8.82925C7.24918 8.8236 7.15263 8.81758 7.04997 8.81605C6.9895 8.81552 6.78663 8.80812 6.79667 8.83039L6.77408 8.89506C6.7776 8.91633 6.78497 8.96949 6.78703 8.98237C6.79534 9.03461 6.79766 9.07617 6.79939 9.12421C6.80252 9.22297 6.79228 9.32592 6.79667 9.42559C6.80577 9.63232 6.87262 9.82076 6.88106 10.0293C6.89029 10.2615 6.99037 10.5072 7.08718 10.6969C7.12393 10.7691 7.17988 10.7773 7.20426 10.8663C7.23284 10.9681 7.20579 11.0762 7.21968 11.1848C7.27417 11.6058 7.37982 12.0459 7.54501 12.4259C7.54621 12.4291 7.54747 12.4325 7.54893 12.4354C7.75293 12.3961 7.95732 12.3119 8.22239 12.2669C8.70839 12.1841 9.3843 12.2268 9.81848 12.1801C10.9171 12.0616 11.5133 12.6972 12.5 12.4367V3.09052C12.5 2.21217 11.8798 1.5 11.1142 1.5H2.88578C2.1205 1.5 1.5 2.21217 1.5 3.09052V6.5608C1.69828 6.47851 1.98348 5.99435 2.07285 5.87661C2.2292 5.67071 2.25758 5.40808 2.33546 5.24267C2.51281 4.86596 2.54331 4.60691 2.94645 4.60691C3.13436 4.60691 3.20899 4.65663 3.3361 4.85238C3.42454 4.98851 3.57731 5.24 3.64881 5.40815C3.73134 5.60215 3.86583 5.86464 3.92497 5.91763C3.96876 5.95698 4.01221 5.9865 4.05275 6.00396C4.11813 6.0321 4.17222 5.98047 4.21595 5.94051C4.27176 5.8895 4.29582 5.78556 4.34751 5.64692C4.422 5.44689 4.5032 5.20721 4.54938 5.12356C4.62932 4.97897 4.65656 4.80739 4.74288 4.72427C4.8702 4.60172 5.03632 4.59311 5.08203 4.58274C5.33779 4.52478 5.45408 4.72419 5.58006 4.85315C5.66253 4.93764 5.77522 5.10785 5.85523 5.33594C5.91776 5.51408 5.99736 5.67887 6.03065 5.78174C6.06281 5.88103 6.14222 6.04018 6.18926 6.23098C6.23199 6.40424 6.34635 6.537 6.3898 6.61936C6.3898 6.61936 6.45632 6.83319 6.86079 7.02864C6.9485 7.07104 7.12579 7.13998 7.23157 7.18413C7.40733 7.25741 7.57757 7.24788 7.79432 7.21807C7.94888 7.21807 8.03261 6.96123 8.10284 6.75556C8.14437 6.634 8.18418 6.28566 8.21129 6.18675C8.23754 6.09051 8.17614 6.01608 8.22843 5.93174C8.28956 5.83329 8.32591 5.82796 8.3612 5.69961C8.43701 5.42478 8.87524 5.4109 9.12156 5.4109C9.32689 5.4109 9.30078 5.63967 9.6491 5.56143C9.84858 5.51652 10.0408 5.59094 10.2526 5.65531C10.4309 5.7096 10.5986 5.77145 10.699 5.90642C10.764 5.99382 10.9254 6.43169 10.761 6.45037C10.7768 6.47257 10.7884 6.5126 10.8179 6.53456C10.7813 6.69974 10.622 6.58207 10.5335 6.56087C10.4142 6.5325 10.33 6.56514 10.2133 6.6244C10.0138 6.72635 9.72206 6.71446 9.5483 6.88055C9.40085 7.02132 9.40111 7.33558 9.33234 7.51166C9.33234 7.51166 9.14137 8.07551 8.72454 8.42043Z" fill="black"/>
<path d="M3.6514 8.80413C3.57333 8.79137 3.50083 8.77702 3.425 8.75238C3.28339 8.70644 3.12867 8.66165 2.9892 8.60788C2.90451 8.57488 2.62239 8.41409 2.5611 8.36877C2.41737 8.26211 2.32191 7.97231 2.20955 8.00214C2.13782 8.02098 2.06795 8.06058 2.02333 8.17701C1.98692 8.27196 1.97457 8.43521 1.94936 8.54469C1.92011 8.67177 1.86959 8.7904 1.82536 8.91149C1.74401 9.13362 1.59759 9.33453 1.5345 9.55093C1.52181 9.59546 1.51055 9.64527 1.5 9.6972V10.5274V11.9637V12.1713C1.57359 12.1915 1.65057 12.2164 1.73674 12.2535C2.37264 12.5265 2.52781 12.5497 3.15152 12.4348L3.21002 12.4223C3.25775 12.2625 3.2946 11.718 3.32555 11.5494C3.34966 11.4202 3.38279 11.3173 3.39537 11.1853C3.40723 11.06 3.39427 10.9406 3.3876 10.8267C3.37011 10.5414 3.51669 10.4395 3.58667 10.1945C3.64976 9.97283 3.68617 9.7206 3.73839 9.49399C3.78847 9.27653 3.86665 8.96922 4 8.85975C3.98382 8.82938 3.72144 8.81539 3.6514 8.80413Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1,6 @@
<svg width="14" height="14" viewBox="
0 0 425 512" xmlns="http://www.w3.org/2000/svg" fill="#000000
">
<path
d="m381.38934 405.88714-229.67062 67.92744c-7.01651 2.07778-13.74132-3.99173-12.2669-11.07217l82.04834-392.9335c1.53436-7.352147 11.69152-8.514905 14.89609-1.710173l151.9177 322.59543c2.86494 6.08949-.40357 13.26702-6.92461 15.19297zm39.38512-16.02808-175.89887-373.53306c-11.59465-21.691431-39.0351-20.904032-49.75484-2.749064l-190.77231 308.99c-5.9096786 9.63371-5.7938027 21.50903.3356409 31.01887l93.252489 144.4589c9.615412 11.46292 18.506512 16.87006 33.692012 12.37878l270.68561-80.05849c18.03265-5.40039 26.72265-22.82202 18.46027-40.50593z" />
</svg>

After

Width:  |  Height:  |  Size: 680 B

View File

@ -1 +0,0 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="457px" height="512px"><polygon points="342.0159302,0 457,0 114.9831009,512 0,512 171.0082092,256 0,0 114.9831009,0 228.4997559,169.9342041 "/></svg>

Before

Width:  |  Height:  |  Size: 209 B

View File

@ -415,7 +415,15 @@
"cmd-?": "assistant::ToggleFocus",
"cmd-alt-s": "workspace::SaveAll",
"cmd-k m": "language_selector::Toggle",
"escape": "workspace::Unfollow"
"escape": "workspace::Unfollow",
"cmd-k cmd-left": ["workspace::ActivatePaneInDirection", "Left"],
"cmd-k cmd-right": ["workspace::ActivatePaneInDirection", "Right"],
"cmd-k cmd-up": ["workspace::ActivatePaneInDirection", "Up"],
"cmd-k cmd-down": ["workspace::ActivatePaneInDirection", "Down"],
"cmd-k shift-left": ["workspace::SwapPaneInDirection", "Left"],
"cmd-k shift-right": ["workspace::SwapPaneInDirection", "Right"],
"cmd-k shift-up": ["workspace::SwapPaneInDirection", "Up"],
"cmd-k shift-down": ["workspace::SwapPaneInDirection", "Down"]
}
},
// Bindings from Sublime Text
@ -441,18 +449,6 @@
"ctrl-alt-shift-f": "editor::SelectToNextSubwordEnd"
}
},
{
"bindings": {
"cmd-k cmd-left": ["workspace::ActivatePaneInDirection", "Left"],
"cmd-k cmd-right": ["workspace::ActivatePaneInDirection", "Right"],
"cmd-k cmd-up": ["workspace::ActivatePaneInDirection", "Up"],
"cmd-k cmd-down": ["workspace::ActivatePaneInDirection", "Down"],
"cmd-k shift-left": ["workspace::SwapPaneInDirection", "Left"],
"cmd-k shift-right": ["workspace::SwapPaneInDirection", "Right"],
"cmd-k shift-up": ["workspace::SwapPaneInDirection", "Up"],
"cmd-k shift-down": ["workspace::SwapPaneInDirection", "Down"]
}
},
// Bindings from Atom
{
"context": "Pane",

View File

@ -42,6 +42,7 @@
"shift-alt-up": "editor::MoveLineUp",
"shift-alt-down": "editor::MoveLineDown",
"cmd-alt-l": "editor::Format",
"shift-f6": "editor::Rename",
"cmd-[": "pane::GoBack",
"cmd-]": "pane::GoForward",
"alt-f7": "editor::FindAllReferences",
@ -80,10 +81,18 @@
"cmd-6": "diagnostics::Deploy"
}
},
{
"context": "Pane",
"bindings": {
"cmd-alt-left": "pane::GoBack",
"cmd-alt-right": "pane::GoForward"
}
},
{
"context": "ProjectPanel",
"bindings": {
"enter": "project_panel::Open"
"enter": "project_panel::Open",
"shift-f6": "project_panel::Rename"
}
}
]

View File

@ -284,7 +284,8 @@
"ctrl-w o": "workspace::CloseInactiveTabsAndPanes",
"ctrl-w ctrl-o": "workspace::CloseInactiveTabsAndPanes",
"ctrl-w n": ["workspace::NewFileInDirection", "Up"],
"ctrl-w ctrl-n": ["workspace::NewFileInDirection", "Up"]
"ctrl-w ctrl-n": ["workspace::NewFileInDirection", "Up"],
"-": "pane::RevealInProjectPanel"
}
},
{
@ -502,18 +503,5 @@
"enter": "vim::SearchSubmit",
"escape": "buffer_search::Dismiss"
}
},
{
"context": "Dock",
"bindings": {
"ctrl-w h": ["workspace::ActivatePaneInDirection", "Left"],
"ctrl-w l": ["workspace::ActivatePaneInDirection", "Right"],
"ctrl-w k": ["workspace::ActivatePaneInDirection", "Up"],
"ctrl-w j": ["workspace::ActivatePaneInDirection", "Down"],
"ctrl-w ctrl-h": ["workspace::ActivatePaneInDirection", "Left"],
"ctrl-w ctrl-l": ["workspace::ActivatePaneInDirection", "Right"],
"ctrl-w ctrl-k": ["workspace::ActivatePaneInDirection", "Up"],
"ctrl-w ctrl-j": ["workspace::ActivatePaneInDirection", "Down"]
}
}
]

View File

@ -104,8 +104,17 @@
"show_whitespaces": "selection",
// Settings related to calls in Zed
"calls": {
// Join calls with the microphone muted by default
"mute_on_join": false
// Join calls with the microphone live by default
"mute_on_join": false,
// Share your project when you are the first to join a channel
"share_on_join": true
},
// Toolbar related settings
"toolbar": {
// Whether to show breadcrumbs.
"breadcrumbs": true,
// Whether to show quick action buttons.
"quick_actions": true
},
// Scrollbar related settings
"scrollbar": {
@ -127,8 +136,12 @@
// Whether to show selections in the scrollbar.
"selections": true,
// Whether to show symbols selections in the scrollbar.
"symbols_selections": true
"symbols_selections": true,
// Whether to show diagnostic indicators in the scrollbar.
"diagnostics": true
},
// The number of lines to keep above/below the cursor when scrolling.
"vertical_scroll_margin": 3,
"relative_line_numbers": false,
// When to populate a new search's query based on the text under the cursor.
// This setting can take the following three values:
@ -485,6 +498,9 @@
"JavaScript": {
"tab_size": 2
},
"Terraform": {
"tab_size": 2
},
"TypeScript": {
"tab_size": 2
},
@ -496,6 +512,12 @@
},
"JSON": {
"tab_size": 2
},
"OCaml": {
"tab_size": 2
},
"OCaml Interface": {
"tab_size": 2
}
},
// Zed's Prettier integration settings.

View File

@ -42,7 +42,7 @@
"tab_bar.background": "#21242bff",
"tab.inactive_background": "#21242bff",
"tab.active_background": "#1e2025ff",
"search.match_background": null,
"search.match_background": "#11a79366",
"panel.background": "#21242bff",
"panel.focused_border": null,
"pane.focused_border": null,

View File

@ -42,7 +42,7 @@
"tab_bar.background": "#221f26ff",
"tab.inactive_background": "#221f26ff",
"tab.active_background": "#19171cff",
"search.match_background": null,
"search.match_background": "#576dda66",
"panel.background": "#221f26ff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -426,7 +426,7 @@
"tab_bar.background": "#e6e3ebff",
"tab.inactive_background": "#e6e3ebff",
"tab.active_background": "#efecf4ff",
"search.match_background": null,
"search.match_background": "#586dda66",
"panel.background": "#e6e3ebff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -810,7 +810,7 @@
"tab_bar.background": "#262622ff",
"tab.inactive_background": "#262622ff",
"tab.active_background": "#20201dff",
"search.match_background": null,
"search.match_background": "#6684e066",
"panel.background": "#262622ff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -1194,7 +1194,7 @@
"tab_bar.background": "#eeebd7ff",
"tab.inactive_background": "#eeebd7ff",
"tab.active_background": "#fefbecff",
"search.match_background": null,
"search.match_background": "#6784e066",
"panel.background": "#eeebd7ff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -1578,7 +1578,7 @@
"tab_bar.background": "#2c2b23ff",
"tab.inactive_background": "#2c2b23ff",
"tab.active_background": "#22221bff",
"search.match_background": null,
"search.match_background": "#37a16666",
"panel.background": "#2c2b23ff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -1962,7 +1962,7 @@
"tab_bar.background": "#ebeae3ff",
"tab.inactive_background": "#ebeae3ff",
"tab.active_background": "#f4f3ecff",
"search.match_background": null,
"search.match_background": "#38a16666",
"panel.background": "#ebeae3ff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -2346,7 +2346,7 @@
"tab_bar.background": "#27211eff",
"tab.inactive_background": "#27211eff",
"tab.active_background": "#1b1918ff",
"search.match_background": null,
"search.match_background": "#417ee666",
"panel.background": "#27211eff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -2730,7 +2730,7 @@
"tab_bar.background": "#e9e6e4ff",
"tab.inactive_background": "#e9e6e4ff",
"tab.active_background": "#f0eeedff",
"search.match_background": null,
"search.match_background": "#417ee666",
"panel.background": "#e9e6e4ff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -3114,7 +3114,7 @@
"tab_bar.background": "#252025ff",
"tab.inactive_background": "#252025ff",
"tab.active_background": "#1b181bff",
"search.match_background": null,
"search.match_background": "#526aeb66",
"panel.background": "#252025ff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -3498,7 +3498,7 @@
"tab_bar.background": "#e0d5e0ff",
"tab.inactive_background": "#e0d5e0ff",
"tab.active_background": "#f7f3f7ff",
"search.match_background": null,
"search.match_background": "#526aeb66",
"panel.background": "#e0d5e0ff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -3882,7 +3882,7 @@
"tab_bar.background": "#1c2529ff",
"tab.inactive_background": "#1c2529ff",
"tab.active_background": "#161b1dff",
"search.match_background": null,
"search.match_background": "#277fad66",
"panel.background": "#1c2529ff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -4266,7 +4266,7 @@
"tab_bar.background": "#cdeaf9ff",
"tab.inactive_background": "#cdeaf9ff",
"tab.active_background": "#ebf8ffff",
"search.match_background": null,
"search.match_background": "#277fad66",
"panel.background": "#cdeaf9ff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -4650,7 +4650,7 @@
"tab_bar.background": "#252020ff",
"tab.inactive_background": "#252020ff",
"tab.active_background": "#1b1818ff",
"search.match_background": null,
"search.match_background": "#7272ca66",
"panel.background": "#252020ff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -5034,7 +5034,7 @@
"tab_bar.background": "#ebe3e3ff",
"tab.inactive_background": "#ebe3e3ff",
"tab.active_background": "#f4ececff",
"search.match_background": null,
"search.match_background": "#7372ca66",
"panel.background": "#ebe3e3ff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -5418,7 +5418,7 @@
"tab_bar.background": "#1f2621ff",
"tab.inactive_background": "#1f2621ff",
"tab.active_background": "#171c19ff",
"search.match_background": null,
"search.match_background": "#478c9066",
"panel.background": "#1f2621ff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -5802,7 +5802,7 @@
"tab_bar.background": "#e3ebe6ff",
"tab.inactive_background": "#e3ebe6ff",
"tab.active_background": "#ecf4eeff",
"search.match_background": null,
"search.match_background": "#488c9066",
"panel.background": "#e3ebe6ff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -6186,7 +6186,7 @@
"tab_bar.background": "#1f231fff",
"tab.inactive_background": "#1f231fff",
"tab.active_background": "#131513ff",
"search.match_background": null,
"search.match_background": "#3e62f466",
"panel.background": "#1f231fff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -6570,7 +6570,7 @@
"tab_bar.background": "#daeedaff",
"tab.inactive_background": "#daeedaff",
"tab.active_background": "#f3faf3ff",
"search.match_background": null,
"search.match_background": "#3f62f466",
"panel.background": "#daeedaff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -6954,7 +6954,7 @@
"tab_bar.background": "#262f51ff",
"tab.inactive_background": "#262f51ff",
"tab.active_background": "#202646ff",
"search.match_background": null,
"search.match_background": "#3e8fd066",
"panel.background": "#262f51ff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -7338,7 +7338,7 @@
"tab_bar.background": "#e5e8f5ff",
"tab.inactive_background": "#e5e8f5ff",
"tab.active_background": "#f5f7ffff",
"search.match_background": null,
"search.match_background": "#3f8fd066",
"panel.background": "#e5e8f5ff",
"panel.focused_border": null,
"pane.focused_border": null,

View File

@ -42,7 +42,7 @@
"tab_bar.background": "#1f2127ff",
"tab.inactive_background": "#1f2127ff",
"tab.active_background": "#0d1016ff",
"search.match_background": null,
"search.match_background": "#5ac2fe66",
"panel.background": "#1f2127ff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -411,7 +411,7 @@
"tab_bar.background": "#ececedff",
"tab.inactive_background": "#ececedff",
"tab.active_background": "#fcfcfcff",
"search.match_background": null,
"search.match_background": "#3b9ee566",
"panel.background": "#ececedff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -780,7 +780,7 @@
"tab_bar.background": "#353944ff",
"tab.inactive_background": "#353944ff",
"tab.active_background": "#242835ff",
"search.match_background": null,
"search.match_background": "#73cffe66",
"panel.background": "#353944ff",
"panel.focused_border": null,
"pane.focused_border": null,

View File

@ -42,7 +42,7 @@
"tab_bar.background": "#3a3735ff",
"tab.inactive_background": "#3a3735ff",
"tab.active_background": "#282828ff",
"search.match_background": null,
"search.match_background": "#83a59866",
"panel.background": "#3a3735ff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -416,7 +416,7 @@
"tab_bar.background": "#393634ff",
"tab.inactive_background": "#393634ff",
"tab.active_background": "#1d2021ff",
"search.match_background": null,
"search.match_background": "#83a59866",
"panel.background": "#393634ff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -790,7 +790,7 @@
"tab_bar.background": "#3b3735ff",
"tab.inactive_background": "#3b3735ff",
"tab.active_background": "#32302fff",
"search.match_background": null,
"search.match_background": "#83a59866",
"panel.background": "#3b3735ff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -1164,7 +1164,7 @@
"tab_bar.background": "#ecddb4ff",
"tab.inactive_background": "#ecddb4ff",
"tab.active_background": "#fbf1c7ff",
"search.match_background": null,
"search.match_background": "#0b667866",
"panel.background": "#ecddb4ff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -1538,7 +1538,7 @@
"tab_bar.background": "#ecddb5ff",
"tab.inactive_background": "#ecddb5ff",
"tab.active_background": "#f9f5d7ff",
"search.match_background": null,
"search.match_background": "#0b667866",
"panel.background": "#ecddb5ff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -1912,7 +1912,7 @@
"tab_bar.background": "#ecdcb3ff",
"tab.inactive_background": "#ecdcb3ff",
"tab.active_background": "#f2e5bcff",
"search.match_background": null,
"search.match_background": "#0b667866",
"panel.background": "#ecdcb3ff",
"panel.focused_border": null,
"pane.focused_border": null,

View File

@ -42,7 +42,7 @@
"tab_bar.background": "#2f343eff",
"tab.inactive_background": "#2f343eff",
"tab.active_background": "#282c33ff",
"search.match_background": null,
"search.match_background": "#74ade866",
"panel.background": "#2f343eff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -416,7 +416,7 @@
"tab_bar.background": "#ebebecff",
"tab.inactive_background": "#ebebecff",
"tab.active_background": "#fafafaff",
"search.match_background": null,
"search.match_background": "#5c79e266",
"panel.background": "#ebebecff",
"panel.focused_border": null,
"pane.focused_border": null,

View File

@ -42,7 +42,7 @@
"tab_bar.background": "#1c1b2aff",
"tab.inactive_background": "#1c1b2aff",
"tab.active_background": "#191724ff",
"search.match_background": null,
"search.match_background": "#57949f66",
"panel.background": "#1c1b2aff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -421,7 +421,7 @@
"tab_bar.background": "#fef9f2ff",
"tab.inactive_background": "#fef9f2ff",
"tab.active_background": "#faf4edff",
"search.match_background": null,
"search.match_background": "#9cced766",
"panel.background": "#fef9f2ff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -800,7 +800,7 @@
"tab_bar.background": "#28253cff",
"tab.inactive_background": "#28253cff",
"tab.active_background": "#232136ff",
"search.match_background": null,
"search.match_background": "#9cced766",
"panel.background": "#28253cff",
"panel.focused_border": null,
"pane.focused_border": null,

View File

@ -42,7 +42,7 @@
"tab_bar.background": "#2b3038ff",
"tab.inactive_background": "#2b3038ff",
"tab.active_background": "#282c33ff",
"search.match_background": null,
"search.match_background": "#528b8b66",
"panel.background": "#2b3038ff",
"panel.focused_border": null,
"pane.focused_border": null,

View File

@ -42,7 +42,7 @@
"tab_bar.background": "#04313bff",
"tab.inactive_background": "#04313bff",
"tab.active_background": "#002a35ff",
"search.match_background": null,
"search.match_background": "#288bd166",
"panel.background": "#04313bff",
"panel.focused_border": null,
"pane.focused_border": null,
@ -411,7 +411,7 @@
"tab_bar.background": "#f3eddaff",
"tab.inactive_background": "#f3eddaff",
"tab.active_background": "#fdf6e3ff",
"search.match_background": null,
"search.match_background": "#298bd166",
"panel.background": "#f3eddaff",
"panel.focused_border": null,
"pane.focused_border": null,

View File

@ -42,7 +42,7 @@
"tab_bar.background": "#231f16ff",
"tab.inactive_background": "#231f16ff",
"tab.active_background": "#1b1810ff",
"search.match_background": null,
"search.match_background": "#499bef66",
"panel.background": "#231f16ff",
"panel.focused_border": null,
"pane.focused_border": null,

View File

@ -11,18 +11,18 @@ doctest = false
[dependencies]
anyhow.workspace = true
auto_update = { path = "../auto_update" }
editor = { path = "../editor" }
auto_update.workspace = true
editor.workspace = true
futures.workspace = true
gpui = { path = "../gpui" }
language = { path = "../language" }
project = { path = "../project" }
settings = { path = "../settings" }
gpui.workspace = true
language.workspace = true
project.workspace = true
settings.workspace = true
smallvec.workspace = true
theme = { path = "../theme" }
ui = { path = "../ui" }
util = { path = "../util" }
workspace = { path = "../workspace", package = "workspace" }
theme.workspace = true
ui.workspace = true
util.workspace = true
workspace.workspace = true
[dev-dependencies]
editor = { path = "../editor", features = ["test-support"] }
editor = { workspace = true, features = ["test-support"] }

View File

@ -17,9 +17,9 @@ anyhow.workspace = true
async-trait.workspace = true
bincode = "1.3.3"
futures.workspace = true
gpui = { path = "../gpui" }
gpui.workspace = true
isahc.workspace = true
language = { path = "../language" }
language.workspace = true
lazy_static.workspace = true
log.workspace = true
matrixmultiply = "0.3.7"
@ -33,7 +33,7 @@ rusqlite = { version = "0.29.0", features = ["blob", "array", "modern_sqlite"] }
serde.workspace = true
serde_json.workspace = true
tiktoken-rs.workspace = true
util = { path = "../util" }
util.workspace = true
[dev-dependencies]
gpui = { path = "../gpui", features = ["test-support"] }
gpui = { workspace = true, features = ["test-support"] }

View File

@ -134,7 +134,7 @@ impl OpenAiEmbeddingProvider {
spans: Vec<&str>,
request_timeout: u64,
) -> Result<Response<AsyncBody>> {
let request = Request::post("https://api.openai.com/v1/embeddings")
let request = Request::post(format!("{OPEN_AI_API_URL}/embeddings"))
.redirect_policy(isahc::config::RedirectPolicy::Follow)
.timeout(Duration::from_secs(request_timeout))
.header("Content-Type", "application/json")

View File

@ -7,5 +7,5 @@ license = "GPL-3.0-or-later"
[dependencies]
anyhow.workspace = true
gpui = { path = "../gpui" }
gpui.workspace = true
rust-embed.workspace = true

View File

@ -10,44 +10,44 @@ path = "src/assistant.rs"
doctest = false
[dependencies]
ai = { path = "../ai" }
ai.workspace = true
anyhow.workspace = true
chrono.workspace = true
client = { path = "../client" }
collections = { path = "../collections" }
editor = { path = "../editor" }
fs = { path = "../fs" }
client.workspace = true
collections.workspace = true
editor.workspace = true
fs.workspace = true
futures.workspace = true
gpui = { path = "../gpui" }
gpui.workspace = true
indoc.workspace = true
isahc.workspace = true
language = { path = "../language" }
language.workspace = true
log.workspace = true
menu = { path = "../menu" }
multi_buffer = { path = "../multi_buffer" }
menu.workspace = true
multi_buffer.workspace = true
ordered-float.workspace = true
parking_lot.workspace = true
project = { path = "../project" }
project.workspace = true
regex.workspace = true
schemars.workspace = true
search = { path = "../search" }
semantic_index = { path = "../semantic_index" }
search.workspace = true
semantic_index.workspace = true
serde.workspace = true
serde_json.workspace = true
settings = { path = "../settings" }
settings.workspace = true
smol.workspace = true
theme = { path = "../theme" }
theme.workspace = true
tiktoken-rs.workspace = true
ui = { path = "../ui" }
util = { path = "../util" }
ui.workspace = true
util.workspace = true
uuid.workspace = true
workspace = { path = "../workspace" }
workspace.workspace = true
[dev-dependencies]
ai = { path = "../ai", features = ["test-support"] }
ai = { workspace = true, features = ["test-support"] }
ctor.workspace = true
editor = { path = "../editor", features = ["test-support"] }
editor = { workspace = true, features = ["test-support"] }
env_logger.workspace = true
log.workspace = true
project = { path = "../project", features = ["test-support"] }
project = { workspace = true, features = ["test-support"] }
rand.workspace = true

View File

@ -962,6 +962,7 @@ impl AssistantPanel {
line_height: relative(1.3).into(),
background_color: None,
underline: None,
strikethrough: None,
white_space: WhiteSpace::Normal,
};
EditorElement::new(
@ -3166,6 +3167,7 @@ impl InlineAssistant {
line_height: relative(1.3).into(),
background_color: None,
underline: None,
strikethrough: None,
white_space: WhiteSpace::Normal,
};
EditorElement::new(

View File

@ -11,11 +11,11 @@ doctest = false
[dependencies]
anyhow.workspace = true
collections = { path = "../collections" }
collections.workspace = true
derive_more.workspace = true
futures.workspace = true
gpui = { path = "../gpui" }
gpui.workspace = true
log.workspace = true
parking_lot.workspace = true
rodio = { version = "0.17.1", default-features = false, features = ["wav"] }
util = { path = "../util" }
util.workspace = true

View File

@ -11,22 +11,22 @@ doctest = false
[dependencies]
anyhow.workspace = true
client = { path = "../client" }
db = { path = "../db" }
gpui = { path = "../gpui" }
client.workspace = true
db.workspace = true
gpui.workspace = true
isahc.workspace = true
lazy_static.workspace = true
log.workspace = true
menu = { path = "../menu" }
project = { path = "../project" }
release_channel = { path = "../release_channel" }
menu.workspace = true
project.workspace = true
release_channel.workspace = true
schemars.workspace = true
serde.workspace = true
serde_derive.workspace = true
serde_json.workspace = true
settings = { path = "../settings" }
settings.workspace = true
smol.workspace = true
tempfile.workspace = true
theme = { path = "../theme" }
util = { path = "../util" }
workspace = { path = "../workspace" }
theme.workspace = true
util.workspace = true
workspace.workspace = true

View File

@ -40,10 +40,11 @@ impl Render for UpdateNotification {
.id("notes")
.child(Label::new("View the release notes"))
.cursor_pointer()
.on_click(|_, cx| {
.on_click(cx.listener(|this, _, cx| {
crate::view_release_notes(&Default::default(), cx);
}),
)
this.dismiss(&menu::Cancel, cx)
})),
);
}
}

View File

@ -10,20 +10,20 @@ path = "src/breadcrumbs.rs"
doctest = false
[dependencies]
collections = { path = "../collections" }
editor = { path = "../editor" }
gpui = { path = "../gpui" }
collections.workspace = true
editor.workspace = true
gpui.workspace = true
itertools = "0.10"
language = { path = "../language" }
outline = { path = "../outline" }
project = { path = "../project" }
search = { path = "../search" }
settings = { path = "../settings" }
theme = { path = "../theme" }
ui = { path = "../ui" }
workspace = { path = "../workspace" }
language.workspace = true
outline.workspace = true
project.workspace = true
search.workspace = true
settings.workspace = true
theme.workspace = true
ui.workspace = true
workspace.workspace = true
[dev-dependencies]
editor = { path = "../editor", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
workspace = { path = "../workspace", features = ["test-support"] }
editor = { workspace = true, features = ["test-support"] }
gpui = { workspace = true, features = ["test-support"] }
workspace = { workspace = true, features = ["test-support"] }

View File

@ -22,33 +22,33 @@ test-support = [
[dependencies]
anyhow.workspace = true
async-broadcast = "0.4"
audio = { path = "../audio" }
client = { path = "../client" }
collections = { path = "../collections" }
fs = { path = "../fs" }
audio.workspace = true
client.workspace = true
collections.workspace = true
fs.workspace = true
futures.workspace = true
gpui = { path = "../gpui" }
gpui.workspace = true
image = "0.23"
language = { path = "../language" }
live_kit_client = { path = "../live_kit_client" }
language.workspace = true
live_kit_client.workspace = true
log.workspace = true
media = { path = "../media" }
media.workspace = true
postage.workspace = true
project = { path = "../project" }
project.workspace = true
schemars.workspace = true
serde.workspace = true
serde_derive.workspace = true
serde_json.workspace = true
settings = { path = "../settings" }
settings.workspace = true
smallvec.workspace = true
util = { path = "../util" }
util.workspace = true
[dev-dependencies]
client = { path = "../client", features = ["test-support"] }
collections = { path = "../collections", features = ["test-support"] }
fs = { path = "../fs", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
language = { path = "../language", features = ["test-support"] }
live_kit_client = { path = "../live_kit_client", features = ["test-support"] }
project = { path = "../project", features = ["test-support"] }
util = { path = "../util", features = ["test-support"] }
client = { workspace = true, features = ["test-support"] }
collections = { workspace = true, features = ["test-support"] }
fs = { workspace = true, features = ["test-support"] }
gpui = { workspace = true, features = ["test-support"] }
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"] }

View File

@ -7,6 +7,7 @@ use settings::Settings;
#[derive(Deserialize, Debug)]
pub struct CallSettings {
pub mute_on_join: bool,
pub share_on_join: bool,
}
/// Configuration of voice calls in Zed.
@ -16,6 +17,11 @@ pub struct CallSettingsContent {
///
/// Default: false
pub mute_on_join: Option<bool>,
/// Whether your current project should be shared when joining an empty channel.
///
/// Default: true
pub share_on_join: Option<bool>,
}
impl Settings for CallSettings {

View File

@ -617,6 +617,10 @@ impl Room {
self.local_participant.role == proto::ChannelRole::Admin
}
pub fn local_participant_is_guest(&self) -> bool {
self.local_participant.role == proto::ChannelRole::Guest
}
pub fn set_participant_role(
&mut self,
user_id: u64,
@ -1202,7 +1206,7 @@ impl Room {
})
}
pub(crate) fn share_project(
pub fn share_project(
&mut self,
project: Model<Project>,
cx: &mut ModelContext<Self>,

View File

@ -14,42 +14,42 @@ test-support = ["collections/test-support", "gpui/test-support", "rpc/test-suppo
[dependencies]
anyhow.workspace = true
client = { path = "../client" }
clock = { path = "../clock" }
collections = { path = "../collections" }
db = { path = "../db" }
feature_flags = { path = "../feature_flags" }
client.workspace = true
clock.workspace = true
collections.workspace = true
db.workspace = true
feature_flags.workspace = true
futures.workspace = true
gpui = { path = "../gpui" }
gpui.workspace = true
image = "0.23"
language = { path = "../language" }
language.workspace = true
lazy_static.workspace = true
log.workspace = true
parking_lot.workspace = true
postage.workspace = true
rand.workspace = true
release_channel = { path = "../release_channel" }
rpc = { path = "../rpc" }
release_channel.workspace = true
rpc.workspace = true
schemars.workspace = true
serde.workspace = true
serde_derive.workspace = true
settings = { path = "../settings" }
settings.workspace = true
smallvec.workspace = true
smol.workspace = true
sum_tree = { path = "../sum_tree" }
sum_tree.workspace = true
tempfile.workspace = true
text = { path = "../text" }
text.workspace = true
thiserror.workspace = true
time.workspace = true
tiny_http = "0.8"
url.workspace = true
util = { path = "../util" }
util.workspace = true
uuid.workspace = true
[dev-dependencies]
collections = { path = "../collections", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
rpc = { path = "../rpc", features = ["test-support"] }
client = { path = "../client", features = ["test-support"] }
settings = { path = "../settings", features = ["test-support"] }
util = { path = "../util", features = ["test-support"] }
collections = { workspace = true, features = ["test-support"] }
gpui = { workspace = true, features = ["test-support"] }
rpc = { workspace = true, features = ["test-support"] }
client = { workspace = true, features = ["test-support"] }
settings = { workspace = true, features = ["test-support"] }
util = { workspace = true, features = ["test-support"] }

View File

@ -5,12 +5,13 @@ use client::{
user::{User, UserStore},
Client, Subscription, TypedEnvelope, UserId,
};
use collections::HashSet;
use futures::lock::Mutex;
use gpui::{AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Task};
use gpui::{
AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Task, WeakModel,
};
use rand::prelude::*;
use std::{
collections::HashSet,
mem,
ops::{ControlFlow, Range},
sync::Arc,
};
@ -26,6 +27,7 @@ pub struct ChannelChat {
loaded_all_messages: bool,
last_acknowledged_id: Option<u64>,
next_pending_message_id: usize,
first_loaded_message_id: Option<u64>,
user_store: Model<UserStore>,
rpc: Arc<Client>,
outgoing_messages_lock: Arc<Mutex<()>>,
@ -37,6 +39,7 @@ pub struct ChannelChat {
pub struct MessageParams {
pub text: String,
pub mentions: Vec<(Range<usize>, UserId)>,
pub reply_to_message_id: Option<u64>,
}
#[derive(Clone, Debug)]
@ -47,6 +50,7 @@ pub struct ChannelMessage {
pub sender: Arc<User>,
pub nonce: u128,
pub mentions: Vec<(Range<usize>, UserId)>,
pub reply_to_message_id: Option<u64>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -55,6 +59,15 @@ pub enum ChannelMessageId {
Pending(usize),
}
impl Into<Option<u64>> for ChannelMessageId {
fn into(self) -> Option<u64> {
match self {
ChannelMessageId::Saved(id) => Some(id),
ChannelMessageId::Pending(_) => None,
}
}
}
#[derive(Clone, Debug, Default)]
pub struct ChannelMessageSummary {
max_id: ChannelMessageId,
@ -96,28 +109,35 @@ impl ChannelChat {
let response = client
.request(proto::JoinChannelChat { channel_id })
.await?;
let messages = messages_from_proto(response.messages, &user_store, &mut cx).await?;
let loaded_all_messages = response.done;
Ok(cx.new_model(|cx| {
let handle = cx.new_model(|cx| {
cx.on_release(Self::release).detach();
let mut this = Self {
Self {
channel_id: channel.id,
user_store,
user_store: user_store.clone(),
channel_store,
rpc: client,
rpc: client.clone(),
outgoing_messages_lock: Default::default(),
messages: Default::default(),
acknowledged_message_ids: Default::default(),
loaded_all_messages,
loaded_all_messages: false,
next_pending_message_id: 0,
last_acknowledged_id: None,
rng: StdRng::from_entropy(),
first_loaded_message_id: None,
_subscription: subscription.set_model(&cx.handle(), &mut cx.to_async()),
};
this.insert_messages(messages, cx);
this
})?)
}
})?;
Self::handle_loaded_messages(
handle.downgrade(),
user_store,
client,
response.messages,
response.done,
&mut cx,
)
.await?;
Ok(handle)
}
fn release(&mut self, _: &mut AppContext) {
@ -166,6 +186,7 @@ impl ChannelChat {
timestamp: OffsetDateTime::now_utc(),
mentions: message.mentions.clone(),
nonce,
reply_to_message_id: message.reply_to_message_id,
},
&(),
),
@ -183,6 +204,7 @@ impl ChannelChat {
body: message.text,
nonce: Some(nonce.into()),
mentions: mentions_to_proto(&message.mentions),
reply_to_message_id: message.reply_to_message_id,
});
let response = request.await?;
drop(outgoing_message_guard);
@ -227,12 +249,16 @@ impl ChannelChat {
before_message_id,
})
.await?;
let loaded_all_messages = response.done;
let messages = messages_from_proto(response.messages, &user_store, &mut cx).await?;
this.update(&mut cx, |this, cx| {
this.loaded_all_messages = loaded_all_messages;
this.insert_messages(messages, cx);
})?;
Self::handle_loaded_messages(
this,
user_store,
rpc,
response.messages,
response.done,
&mut cx,
)
.await?;
anyhow::Ok(())
}
.log_err()
@ -240,9 +266,14 @@ impl ChannelChat {
}
pub fn first_loaded_message_id(&mut self) -> Option<u64> {
self.messages.first().and_then(|message| match message.id {
ChannelMessageId::Saved(id) => Some(id),
ChannelMessageId::Pending(_) => None,
self.first_loaded_message_id
}
/// Load a message by its id, if it's already stored locally.
pub fn find_loaded_message(&self, id: u64) -> Option<&ChannelMessage> {
self.messages.iter().find(|message| match message.id {
ChannelMessageId::Saved(message_id) => message_id == id,
ChannelMessageId::Pending(_) => false,
})
}
@ -304,6 +335,66 @@ impl ChannelChat {
}
}
async fn handle_loaded_messages(
this: WeakModel<Self>,
user_store: Model<UserStore>,
rpc: Arc<Client>,
proto_messages: Vec<proto::ChannelMessage>,
loaded_all_messages: bool,
cx: &mut AsyncAppContext,
) -> Result<()> {
let loaded_messages = messages_from_proto(proto_messages, &user_store, cx).await?;
let first_loaded_message_id = loaded_messages.first().map(|m| m.id);
let loaded_message_ids = this.update(cx, |this, _| {
let mut loaded_message_ids: HashSet<u64> = HashSet::default();
for message in loaded_messages.iter() {
if let Some(saved_message_id) = message.id.into() {
loaded_message_ids.insert(saved_message_id);
}
}
for message in this.messages.iter() {
if let Some(saved_message_id) = message.id.into() {
loaded_message_ids.insert(saved_message_id);
}
}
loaded_message_ids
})?;
let missing_ancestors = loaded_messages
.iter()
.filter_map(|message| {
if let Some(ancestor_id) = message.reply_to_message_id {
if !loaded_message_ids.contains(&ancestor_id) {
return Some(ancestor_id);
}
}
None
})
.collect::<Vec<_>>();
let loaded_ancestors = if missing_ancestors.is_empty() {
None
} else {
let response = rpc
.request(proto::GetChannelMessagesById {
message_ids: missing_ancestors,
})
.await?;
Some(messages_from_proto(response.messages, &user_store, cx).await?)
};
this.update(cx, |this, cx| {
this.first_loaded_message_id = first_loaded_message_id.and_then(|msg_id| msg_id.into());
this.loaded_all_messages = loaded_all_messages;
this.insert_messages(loaded_messages, cx);
if let Some(loaded_ancestors) = loaded_ancestors {
this.insert_messages(loaded_ancestors, cx);
}
})?;
Ok(())
}
pub fn rejoin(&mut self, cx: &mut ModelContext<Self>) {
let user_store = self.user_store.clone();
let rpc = self.rpc.clone();
@ -311,28 +402,17 @@ impl ChannelChat {
cx.spawn(move |this, mut cx| {
async move {
let response = rpc.request(proto::JoinChannelChat { channel_id }).await?;
let messages = messages_from_proto(response.messages, &user_store, &mut cx).await?;
let loaded_all_messages = response.done;
let pending_messages = this.update(&mut cx, |this, cx| {
if let Some((first_new_message, last_old_message)) =
messages.first().zip(this.messages.last())
{
if first_new_message.id > last_old_message.id {
let old_messages = mem::take(&mut this.messages);
cx.emit(ChannelChatEvent::MessagesUpdated {
old_range: 0..old_messages.summary().count,
new_count: 0,
});
this.loaded_all_messages = loaded_all_messages;
}
}
this.insert_messages(messages, cx);
if loaded_all_messages {
this.loaded_all_messages = loaded_all_messages;
}
Self::handle_loaded_messages(
this.clone(),
user_store.clone(),
rpc.clone(),
response.messages,
response.done,
&mut cx,
)
.await?;
let pending_messages = this.update(&mut cx, |this, _| {
this.pending_messages().cloned().collect::<Vec<_>>()
})?;
@ -342,6 +422,7 @@ impl ChannelChat {
body: pending_message.body,
mentions: mentions_to_proto(&pending_message.mentions),
nonce: Some(pending_message.nonce.into()),
reply_to_message_id: pending_message.reply_to_message_id,
});
let response = request.await?;
let message = ChannelMessage::from_proto(
@ -553,6 +634,7 @@ impl ChannelMessage {
.nonce
.ok_or_else(|| anyhow!("nonce is required"))?
.into(),
reply_to_message_id: message.reply_to_message_id,
})
}
@ -642,6 +724,7 @@ impl<'a> From<&'a str> for MessageParams {
Self {
text: value.into(),
mentions: Vec::new(),
reply_to_message_id: None,
}
}
}

View File

@ -184,6 +184,7 @@ async fn test_channel_messages(cx: &mut TestAppContext) {
sender_id: 5,
mentions: vec![],
nonce: Some(1.into()),
reply_to_message_id: None,
},
proto::ChannelMessage {
id: 11,
@ -192,6 +193,7 @@ async fn test_channel_messages(cx: &mut TestAppContext) {
sender_id: 6,
mentions: vec![],
nonce: Some(2.into()),
reply_to_message_id: None,
},
],
done: false,
@ -239,6 +241,7 @@ async fn test_channel_messages(cx: &mut TestAppContext) {
sender_id: 7,
mentions: vec![],
nonce: Some(3.into()),
reply_to_message_id: None,
}),
});
@ -292,6 +295,7 @@ async fn test_channel_messages(cx: &mut TestAppContext) {
sender_id: 5,
nonce: Some(4.into()),
mentions: vec![],
reply_to_message_id: None,
},
proto::ChannelMessage {
id: 9,
@ -300,6 +304,7 @@ async fn test_channel_messages(cx: &mut TestAppContext) {
sender_id: 6,
nonce: Some(5.into()),
mentions: vec![],
reply_to_message_id: None,
},
],
},

View File

@ -20,7 +20,7 @@ dirs = "3.0"
ipc-channel = "0.16"
serde.workspace = true
serde_derive.workspace = true
util = { path = "../util" }
util.workspace = true
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.9"

View File

@ -14,20 +14,20 @@ test-support = ["collections/test-support", "gpui/test-support", "rpc/test-suppo
[dependencies]
chrono = { version = "0.4", features = ["serde"] }
collections = { path = "../collections" }
db = { path = "../db" }
gpui = { path = "../gpui" }
util = { path = "../util" }
release_channel = { path = "../release_channel" }
rpc = { path = "../rpc" }
text = { path = "../text" }
settings = { path = "../settings" }
feature_flags = { path = "../feature_flags" }
sum_tree = { path = "../sum_tree" }
collections.workspace = true
db.workspace = true
gpui.workspace = true
util.workspace = true
release_channel.workspace = true
rpc.workspace = true
text.workspace = true
settings.workspace = true
feature_flags.workspace = true
sum_tree.workspace = true
anyhow.workspace = true
async-recursion = "0.3"
async-tungstenite = { version = "0.16", features = ["async-tls"] }
async-tungstenite = { version = "0.16", features = ["async-std", "async-native-tls"] }
futures.workspace = true
image = "0.23"
lazy_static.workspace = true
@ -51,8 +51,8 @@ uuid.workspace = true
url.workspace = true
[dev-dependencies]
collections = { path = "../collections", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
rpc = { path = "../rpc", features = ["test-support"] }
settings = { path = "../settings", features = ["test-support"] }
util = { path = "../util", features = ["test-support"] }
collections = { workspace = true, features = ["test-support"] }
gpui = { workspace = true, features = ["test-support"] }
rpc = { workspace = true, features = ["test-support"] }
settings = { workspace = true, features = ["test-support"] }
util = { workspace = true, features = ["test-support"] }

View File

@ -10,6 +10,7 @@ use async_tungstenite::tungstenite::{
error::Error as WebsocketError,
http::{Request, StatusCode},
};
use collections::HashMap;
use futures::{
channel::oneshot, future::LocalBoxFuture, AsyncReadExt, FutureExt, SinkExt, StreamExt,
TryFutureExt as _, TryStreamExt,
@ -29,7 +30,6 @@ use serde_json;
use settings::{Settings, SettingsStore};
use std::{
any::TypeId,
collections::HashMap,
convert::TryFrom,
fmt::Write as _,
future::Future,
@ -1040,7 +1040,7 @@ impl Client {
rpc_url.set_scheme("wss").unwrap();
let request = request.uri(rpc_url.as_str()).body(())?;
let (stream, _) =
async_tungstenite::async_tls::client_async_tls(request, stream).await?;
async_tungstenite::async_std::client_async_tls(request, stream).await?;
Ok(Connection::new(
stream
.map_err(|error| anyhow!(error))

View File

@ -145,11 +145,14 @@ const FLUSH_INTERVAL: Duration = Duration::from_secs(1);
#[cfg(not(debug_assertions))]
const FLUSH_INTERVAL: Duration = Duration::from_secs(60 * 5);
static ZED_CLIENT_CHECKSUM_SEED: Lazy<Vec<u8>> = Lazy::new(|| {
static ZED_CLIENT_CHECKSUM_SEED: Lazy<Option<Vec<u8>>> = Lazy::new(|| {
option_env!("ZED_CLIENT_CHECKSUM_SEED")
.unwrap_or("development-checksum-seed")
.as_bytes()
.into()
.map(|s| s.as_bytes().into())
.or_else(|| {
env::var("ZED_CLIENT_CHECKSUM_SEED")
.ok()
.map(|s| s.as_bytes().into())
})
});
impl Telemetry {
@ -510,6 +513,10 @@ impl Telemetry {
return;
}
let Some(checksum_seed) = &*ZED_CLIENT_CHECKSUM_SEED else {
return;
};
let this = self.clone();
self.executor
.spawn(
@ -551,9 +558,9 @@ impl Telemetry {
}
let mut summer = Sha256::new();
summer.update(&*ZED_CLIENT_CHECKSUM_SEED);
summer.update(checksum_seed);
summer.update(&json_bytes);
summer.update(&*ZED_CLIENT_CHECKSUM_SEED);
summer.update(checksum_seed);
let mut checksum = String::new();
for byte in summer.finalize().as_slice() {
use std::fmt::Write;

View File

@ -22,15 +22,15 @@ axum-extra = { version = "0.3", features = ["erased-json"] }
base64 = "0.13"
chrono.workspace = true
clap = { version = "3.1", features = ["derive"], optional = true }
clock = { path = "../clock" }
collections = { path = "../collections" }
clock.workspace = true
collections.workspace = true
dashmap = "5.4"
envy = "0.4.2"
futures.workspace = true
hyper = "0.14"
lazy_static.workspace = true
lipsum = { version = "0.8", optional = true }
live_kit_server = { path = "../live_kit_server" }
live_kit_server.workspace = true
log.workspace = true
nanoid = "0.4"
parking_lot.workspace = true
@ -38,7 +38,7 @@ prometheus = "0.13"
prost.workspace = true
rand.workspace = true
reqwest = { version = "0.11", features = ["json"], optional = true }
rpc = { path = "../rpc" }
rpc.workspace = true
scrypt = "0.7"
sea-orm = { version = "0.12.x", features = ["sqlx-postgres", "postgres-array", "runtime-tokio-rustls", "with-uuid"] }
serde.workspace = true
@ -47,7 +47,7 @@ serde_json.workspace = true
sha-1 = "0.9"
smallvec.workspace = true
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres", "json", "time", "uuid", "any"] }
text = { path = "../text" }
text.workspace = true
time.workspace = true
tokio = { version = "1", features = ["full"] }
tokio-tungstenite = "0.17"
@ -57,44 +57,44 @@ tower = "0.4"
tracing = "0.1.34"
tracing-log = "0.1.3"
tracing-subscriber = { version = "0.3.11", features = ["env-filter", "json"] }
util = { path = "../util" }
util.workspace = true
uuid.workspace = true
[dev-dependencies]
release_channel = { path = "../release_channel" }
release_channel.workspace = true
async-trait.workspace = true
audio = { path = "../audio" }
call = { path = "../call", features = ["test-support"] }
channel = { path = "../channel" }
client = { path = "../client", features = ["test-support"] }
collab_ui = { path = "../collab_ui", features = ["test-support"] }
collections = { path = "../collections", features = ["test-support"] }
audio.workspace = true
call = { workspace = true, features = ["test-support"] }
channel.workspace = true
client = { workspace = true, features = ["test-support"] }
collab_ui = { workspace = true, features = ["test-support"] }
collections = { workspace = true, features = ["test-support"] }
ctor.workspace = true
editor = { path = "../editor", features = ["test-support"] }
editor = { workspace = true, features = ["test-support"] }
env_logger.workspace = true
file_finder = { path = "../file_finder" }
fs = { path = "../fs", features = ["test-support"] }
git = { path = "../git", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
file_finder.workspace = true
fs = { workspace = true, features = ["test-support"] }
git = { workspace = true, features = ["test-support"] }
gpui = { workspace = true, features = ["test-support"] }
indoc.workspace = true
language = { path = "../language", features = ["test-support"] }
language = { workspace = true, features = ["test-support"] }
lazy_static.workspace = true
live_kit_client = { path = "../live_kit_client", features = ["test-support"] }
lsp = { path = "../lsp", features = ["test-support"] }
menu = { path = "../menu" }
node_runtime = { path = "../node_runtime" }
notifications = { path = "../notifications", features = ["test-support"] }
live_kit_client = { workspace = true, features = ["test-support"] }
lsp = { workspace = true, features = ["test-support"] }
menu.workspace = true
node_runtime.workspace = true
notifications = { workspace = true, features = ["test-support"] }
pretty_assertions.workspace = true
project = { path = "../project", features = ["test-support"] }
rpc = { path = "../rpc", features = ["test-support"] }
project = { workspace = true, features = ["test-support"] }
rpc = { workspace = true, features = ["test-support"] }
sea-orm = { version = "0.12.x", features = ["sqlx-sqlite"] }
serde_json.workspace = true
settings = { path = "../settings", features = ["test-support"] }
settings = { workspace = true, features = ["test-support"] }
sqlx = { version = "0.7", features = ["sqlite"] }
theme = { path = "../theme" }
theme.workspace = true
unindent.workspace = true
util = { path = "../util" }
workspace = { path = "../workspace", features = ["test-support"] }
util.workspace = true
workspace = { workspace = true, features = ["test-support"] }
[features]
seed-support = ["clap", "lipsum", "reqwest"]

View File

@ -217,7 +217,8 @@ CREATE TABLE IF NOT EXISTS "channel_messages" (
"sender_id" INTEGER NOT NULL REFERENCES users (id),
"body" TEXT NOT NULL,
"sent_at" TIMESTAMP,
"nonce" BLOB NOT NULL
"nonce" BLOB NOT NULL,
"reply_to_message_id" INTEGER DEFAULT NULL
);
CREATE INDEX "index_channel_messages_on_channel_id" ON "channel_messages" ("channel_id");
CREATE UNIQUE INDEX "index_channel_messages_on_sender_id_nonce" ON "channel_messages" ("sender_id", "nonce");

View File

@ -0,0 +1 @@
ALTER TABLE channel_messages ADD reply_to_message_id INTEGER DEFAULT NULL

View File

@ -692,7 +692,7 @@ impl ProjectCollaborator {
pub struct LeftProject {
pub id: ProjectId,
pub host_user_id: UserId,
pub host_connection_id: ConnectionId,
pub host_connection_id: Option<ConnectionId>,
pub connection_ids: Vec<ConnectionId>,
}

View File

@ -161,6 +161,7 @@ impl Database {
upper_half: nonce.0,
lower_half: nonce.1,
}),
reply_to_message_id: row.reply_to_message_id.map(|id| id.to_proto()),
}
})
.collect::<Vec<_>>();
@ -207,6 +208,7 @@ impl Database {
mentions: &[proto::ChatMention],
timestamp: OffsetDateTime,
nonce: u128,
reply_to_message_id: Option<MessageId>,
) -> Result<CreatedChannelMessage> {
self.transaction(|tx| async move {
let channel = self.get_channel_internal(channel_id, &*tx).await?;
@ -245,6 +247,7 @@ impl Database {
sent_at: ActiveValue::Set(timestamp),
nonce: ActiveValue::Set(Uuid::from_u128(nonce)),
id: ActiveValue::NotSet,
reply_to_message_id: ActiveValue::Set(reply_to_message_id),
})
.on_conflict(
OnConflict::columns([

View File

@ -778,7 +778,7 @@ impl Database {
let left_project = LeftProject {
id: project_id,
host_user_id: project.host_user_id,
host_connection_id: project.host_connection()?,
host_connection_id: Some(project.host_connection()?),
connection_ids,
};
Ok((room, left_project))

View File

@ -862,7 +862,7 @@ impl Database {
id: collaborator.project_id,
host_user_id: Default::default(),
connection_ids: Default::default(),
host_connection_id: Default::default(),
host_connection_id: None,
});
let collaborator_connection_id = collaborator.connection();
@ -872,7 +872,7 @@ impl Database {
if collaborator.is_host {
left_project.host_user_id = collaborator.user_id;
left_project.host_connection_id = collaborator_connection_id;
left_project.host_connection_id = Some(collaborator_connection_id);
}
}
drop(collaborators);

View File

@ -12,6 +12,7 @@ pub struct Model {
pub body: String,
pub sent_at: PrimitiveDateTime,
pub nonce: Uuid,
pub reply_to_message_id: Option<MessageId>,
}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -32,6 +32,7 @@ async fn test_channel_message_retrieval(db: &Arc<Database>) {
&[],
OffsetDateTime::now_utc(),
i,
None,
)
.await
.unwrap()
@ -106,6 +107,7 @@ async fn test_channel_message_nonces(db: &Arc<Database>) {
&mentions_to_proto(&[(3..10, user_b.to_proto())]),
OffsetDateTime::now_utc(),
100,
None,
)
.await
.unwrap()
@ -118,6 +120,7 @@ async fn test_channel_message_nonces(db: &Arc<Database>) {
&mentions_to_proto(&[]),
OffsetDateTime::now_utc(),
200,
None,
)
.await
.unwrap()
@ -130,6 +133,7 @@ async fn test_channel_message_nonces(db: &Arc<Database>) {
&mentions_to_proto(&[(4..11, user_c.to_proto())]),
OffsetDateTime::now_utc(),
100,
None,
)
.await
.unwrap()
@ -142,6 +146,7 @@ async fn test_channel_message_nonces(db: &Arc<Database>) {
&mentions_to_proto(&[]),
OffsetDateTime::now_utc(),
200,
None,
)
.await
.unwrap()
@ -157,6 +162,7 @@ async fn test_channel_message_nonces(db: &Arc<Database>) {
&mentions_to_proto(&[(4..11, user_a.to_proto())]),
OffsetDateTime::now_utc(),
100,
None,
)
.await
.unwrap()
@ -231,17 +237,41 @@ async fn test_unseen_channel_messages(db: &Arc<Database>) {
.unwrap();
let _ = db
.create_channel_message(channel_1, user, "1_1", &[], OffsetDateTime::now_utc(), 1)
.create_channel_message(
channel_1,
user,
"1_1",
&[],
OffsetDateTime::now_utc(),
1,
None,
)
.await
.unwrap();
let _ = db
.create_channel_message(channel_1, user, "1_2", &[], OffsetDateTime::now_utc(), 2)
.create_channel_message(
channel_1,
user,
"1_2",
&[],
OffsetDateTime::now_utc(),
2,
None,
)
.await
.unwrap();
let third_message = db
.create_channel_message(channel_1, user, "1_3", &[], OffsetDateTime::now_utc(), 3)
.create_channel_message(
channel_1,
user,
"1_3",
&[],
OffsetDateTime::now_utc(),
3,
None,
)
.await
.unwrap()
.message_id;
@ -251,7 +281,15 @@ async fn test_unseen_channel_messages(db: &Arc<Database>) {
.unwrap();
let fourth_message = db
.create_channel_message(channel_2, user, "2_1", &[], OffsetDateTime::now_utc(), 4)
.create_channel_message(
channel_2,
user,
"2_1",
&[],
OffsetDateTime::now_utc(),
4,
None,
)
.await
.unwrap()
.message_id;
@ -317,6 +355,7 @@ async fn test_channel_message_mentions(db: &Arc<Database>) {
&mentions_to_proto(&[(3..10, user_b.to_proto()), (15..22, user_c.to_proto())]),
OffsetDateTime::now_utc(),
1,
None,
)
.await
.unwrap();
@ -327,6 +366,7 @@ async fn test_channel_message_mentions(db: &Arc<Database>) {
&mentions_to_proto(&[(4..11, user_c.to_proto())]),
OffsetDateTime::now_utc(),
2,
None,
)
.await
.unwrap();
@ -337,6 +377,7 @@ async fn test_channel_message_mentions(db: &Arc<Database>) {
&mentions_to_proto(&[]),
OffsetDateTime::now_utc(),
3,
None,
)
.await
.unwrap();
@ -347,6 +388,7 @@ async fn test_channel_message_mentions(db: &Arc<Database>) {
&mentions_to_proto(&[(0..7, user_b.to_proto())]),
OffsetDateTime::now_utc(),
4,
None,
)
.await
.unwrap();

View File

@ -1691,7 +1691,7 @@ async fn leave_project(request: proto::LeaveProject, session: Session) -> Result
tracing::info!(
%project_id,
host_user_id = %project.host_user_id,
host_connection_id = %project.host_connection_id,
host_connection_id = ?project.host_connection_id,
"leave project"
);
@ -3019,6 +3019,10 @@ async fn send_channel_message(
&request.mentions,
timestamp,
nonce.clone().into(),
match request.reply_to_message_id {
Some(reply_to_message_id) => Some(MessageId::from_proto(reply_to_message_id)),
None => None,
},
)
.await?;
let message = proto::ChannelMessage {
@ -3028,6 +3032,7 @@ async fn send_channel_message(
mentions: request.mentions,
timestamp: timestamp.unix_timestamp() as u64,
nonce: Some(nonce),
reply_to_message_id: request.reply_to_message_id,
};
broadcast(
Some(session.connection_id),

View File

@ -43,6 +43,7 @@ async fn test_basic_channel_messages(
MessageParams {
text: "hi @user_c!".into(),
mentions: vec![(3..10, client_c.id())],
reply_to_message_id: None,
},
cx,
)
@ -402,3 +403,66 @@ async fn test_channel_message_changes(
assert!(b_has_messages);
}
#[gpui::test]
async fn test_chat_replies(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
let mut server = TestServer::start(cx_a.executor()).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
let channel_id = server
.make_channel(
"the-channel",
None,
(&client_a, cx_a),
&mut [(&client_b, cx_b)],
)
.await;
// Client A sends a message, client B should see that there is a new message.
let channel_chat_a = client_a
.channel_store()
.update(cx_a, |store, cx| store.open_channel_chat(channel_id, cx))
.await
.unwrap();
let channel_chat_b = client_b
.channel_store()
.update(cx_b, |store, cx| store.open_channel_chat(channel_id, cx))
.await
.unwrap();
let msg_id = channel_chat_a
.update(cx_a, |c, cx| c.send_message("one".into(), cx).unwrap())
.await
.unwrap();
cx_a.run_until_parked();
let reply_id = channel_chat_b
.update(cx_b, |c, cx| {
c.send_message(
MessageParams {
text: "reply".into(),
reply_to_message_id: Some(msg_id),
mentions: Vec::new(),
},
cx,
)
.unwrap()
})
.await
.unwrap();
cx_a.run_until_parked();
channel_chat_a.update(cx_a, |channel_chat, _| {
assert_eq!(
channel_chat
.find_loaded_message(reply_id)
.unwrap()
.reply_to_message_id,
Some(msg_id),
)
});
}

View File

@ -22,6 +22,8 @@ use workspace::{
SplitDirection, Workspace,
};
use super::TestClient;
#[gpui::test(iterations = 10)]
async fn test_basic_following(
cx_a: &mut TestAppContext,
@ -1996,3 +1998,82 @@ async fn test_following_to_channel_notes_without_a_shared_project(
);
});
}
async fn join_channel(
channel_id: u64,
client: &TestClient,
cx: &mut TestAppContext,
) -> anyhow::Result<()> {
cx.update(|cx| workspace::join_channel(channel_id, client.app_state.clone(), None, cx))
.await
}
async fn share_workspace(
workspace: &View<Workspace>,
cx: &mut VisualTestContext,
) -> anyhow::Result<u64> {
let project = workspace.update(cx, |workspace, _| workspace.project().clone());
cx.read(ActiveCall::global)
.update(cx, |call, cx| call.share_project(project, cx))
.await
}
#[gpui::test]
async fn test_following_to_channel_notes_other_workspace(
cx_a: &mut TestAppContext,
cx_b: &mut TestAppContext,
) {
let (_, client_a, client_b, channel) = TestServer::start2(cx_a, cx_b).await;
let mut cx_a2 = cx_a.clone();
let (workspace_a, cx_a) = client_a.build_test_workspace(cx_a).await;
join_channel(channel, &client_a, cx_a).await.unwrap();
share_workspace(&workspace_a, cx_a).await.unwrap();
// a opens 1.txt
cx_a.simulate_keystrokes("cmd-p 1 enter");
cx_a.run_until_parked();
workspace_a.update(cx_a, |workspace, cx| {
let editor = workspace.active_item(cx).unwrap();
assert_eq!(editor.tab_description(0, cx).unwrap(), "1.txt");
});
// b joins channel and is following a
join_channel(channel, &client_b, cx_b).await.unwrap();
cx_b.run_until_parked();
let (workspace_b, cx_b) = client_b.active_workspace(cx_b);
workspace_b.update(cx_b, |workspace, cx| {
let editor = workspace.active_item(cx).unwrap();
assert_eq!(editor.tab_description(0, cx).unwrap(), "1.txt");
});
// a opens a second workspace and the channel notes
let (workspace_a2, cx_a2) = client_a.build_test_workspace(&mut cx_a2).await;
cx_a2.update(|cx| cx.activate_window());
cx_a2
.update(|cx| ChannelView::open(channel, None, workspace_a2, cx))
.await
.unwrap();
cx_a2.run_until_parked();
// b should follow a to the channel notes
workspace_b.update(cx_b, |workspace, cx| {
let editor = workspace.active_item_as::<ChannelView>(cx).unwrap();
assert_eq!(editor.read(cx).channel(cx).unwrap().id, channel);
});
// a returns to the shared project
cx_a.update(|cx| cx.activate_window());
cx_a.run_until_parked();
workspace_a.update(cx_a, |workspace, cx| {
let editor = workspace.active_item(cx).unwrap();
assert_eq!(editor.tab_description(0, cx).unwrap(), "1.txt");
});
// b should follow a back
workspace_b.update(cx_b, |workspace, cx| {
let editor = workspace.active_item_as::<Editor>(cx).unwrap();
assert_eq!(editor.tab_description(0, cx).unwrap(), "1.txt");
});
}

View File

@ -5967,6 +5967,6 @@ async fn test_cmd_k_left(cx: &mut TestAppContext) {
cx.executor().advance_clock(Duration::from_secs(2));
cx.simulate_keystrokes("left");
workspace.update(cx, |workspace, cx| {
assert!(workspace.items(cx).collect::<Vec<_>>().len() == 3);
assert!(workspace.items(cx).collect::<Vec<_>>().len() == 2);
});
}

View File

@ -123,7 +123,12 @@ impl TestServer {
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
let channel_id = server
.make_channel("a", None, (&client_a, cx_a), &mut [(&client_b, cx_b)])
.make_channel(
"test-channel",
None,
(&client_a, cx_a),
&mut [(&client_b, cx_b)],
)
.await;
cx_a.run_until_parked();

View File

@ -26,58 +26,58 @@ test-support = [
[dependencies]
anyhow.workspace = true
auto_update = { path = "../auto_update" }
call = { path = "../call" }
channel = { path = "../channel" }
client = { path = "../client" }
clock = { path = "../clock" }
collections = { path = "../collections" }
db = { path = "../db" }
editor = { path = "../editor" }
feature_flags = { path = "../feature_flags" }
feedback = { path = "../feedback" }
auto_update.workspace = true
call.workspace = true
channel.workspace = true
client.workspace = true
clock.workspace = true
collections.workspace = true
db.workspace = true
editor.workspace = true
feature_flags.workspace = true
feedback.workspace = true
futures.workspace = true
fuzzy = { path = "../fuzzy" }
gpui = { path = "../gpui" }
language = { path = "../language" }
fuzzy.workspace = true
gpui.workspace = true
language.workspace = true
lazy_static.workspace = true
log.workspace = true
menu = { path = "../menu" }
notifications = { path = "../notifications" }
menu.workspace = true
notifications.workspace = true
parking_lot.workspace = true
picker = { path = "../picker" }
picker.workspace = true
postage.workspace = true
project = { path = "../project" }
recent_projects = { path = "../recent_projects" }
rich_text = { path = "../rich_text" }
rpc = { path = "../rpc" }
project.workspace = true
recent_projects.workspace = true
rich_text.workspace = true
rpc.workspace = true
schemars.workspace = true
serde.workspace = true
serde_derive.workspace = true
serde_json.workspace = true
settings = { path = "../settings" }
settings.workspace = true
smallvec.workspace = true
story = { path = "../story", optional = true }
theme = { path = "../theme" }
theme_selector = { path = "../theme_selector" }
story = { workspace = true, optional = true }
theme.workspace = true
theme_selector.workspace = true
time.workspace = true
ui = { path = "../ui" }
util = { path = "../util" }
vcs_menu = { path = "../vcs_menu" }
workspace = { path = "../workspace" }
zed_actions = { path = "../zed_actions" }
ui.workspace = true
util.workspace = true
vcs_menu.workspace = true
workspace.workspace = true
zed_actions.workspace = true
[dev-dependencies]
call = { path = "../call", features = ["test-support"] }
client = { path = "../client", features = ["test-support"] }
collections = { path = "../collections", features = ["test-support"] }
editor = { path = "../editor", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
notifications = { path = "../notifications", features = ["test-support"] }
call = { workspace = true, features = ["test-support"] }
client = { workspace = true, features = ["test-support"] }
collections = { workspace = true, features = ["test-support"] }
editor = { workspace = true, features = ["test-support"] }
gpui = { workspace = true, features = ["test-support"] }
notifications = { workspace = true, features = ["test-support"] }
pretty_assertions.workspace = true
project = { path = "../project", features = ["test-support"] }
rpc = { path = "../rpc", features = ["test-support"] }
settings = { path = "../settings", features = ["test-support"] }
project = { workspace = true, features = ["test-support"] }
rpc = { workspace = true, features = ["test-support"] }
settings = { workspace = true, features = ["test-support"] }
tree-sitter-markdown.workspace = true
util = { path = "../util", features = ["test-support"] }
workspace = { path = "../workspace", features = ["test-support"] }
util = { workspace = true, features = ["test-support"] }
workspace = { workspace = true, features = ["test-support"] }

View File

@ -1,16 +1,16 @@
use crate::{collab_panel, ChatPanelSettings};
use anyhow::Result;
use call::{room, ActiveCall};
use channel::{ChannelChat, ChannelChatEvent, ChannelMessageId, ChannelStore};
use channel::{ChannelChat, ChannelChatEvent, ChannelMessage, ChannelMessageId, ChannelStore};
use client::Client;
use collections::HashMap;
use db::kvp::KEY_VALUE_STORE;
use editor::Editor;
use gpui::{
actions, div, list, prelude::*, px, Action, AppContext, AsyncWindowContext, DismissEvent,
ElementId, EventEmitter, Fill, FocusHandle, FocusableView, FontWeight, ListOffset,
ListScrollEvent, ListState, Model, Render, Subscription, Task, View, ViewContext,
VisualContext, WeakView,
actions, div, list, prelude::*, px, Action, AppContext, AsyncWindowContext, CursorStyle,
DismissEvent, ElementId, EventEmitter, FocusHandle, FocusableView, FontStyle, FontWeight,
HighlightStyle, ListOffset, ListScrollEvent, ListState, Model, Render, StyledText,
Subscription, Task, View, ViewContext, VisualContext, WeakView,
};
use language::LanguageRegistry;
use menu::Confirm;
@ -23,7 +23,7 @@ use std::{sync::Arc, time::Duration};
use time::{OffsetDateTime, UtcOffset};
use ui::{
popover_menu, prelude::*, Avatar, Button, ContextMenu, IconButton, IconName, KeyBinding, Label,
TabBar,
TabBar, Tooltip,
};
use util::{ResultExt, TryFutureExt};
use workspace::{
@ -62,6 +62,7 @@ pub struct ChatPanel {
markdown_data: HashMap<ChannelMessageId, RichText>,
focus_handle: FocusHandle,
open_context_menu: Option<(u64, Subscription)>,
highlighted_message: Option<(u64, Task<()>)>,
}
#[derive(Serialize, Deserialize)]
@ -124,6 +125,7 @@ impl ChatPanel {
markdown_data: Default::default(),
focus_handle: cx.focus_handle(),
open_context_menu: None,
highlighted_message: None,
};
if let Some(channel_id) = ActiveCall::global(cx)
@ -236,6 +238,7 @@ impl ChatPanel {
let channel_name = chat.channel(cx).map(|channel| channel.name.clone());
self.message_editor.update(cx, |editor, cx| {
editor.set_channel(channel_id, channel_name, cx);
editor.clear_reply_to_message_id();
});
};
let subscription = cx.subscribe(&chat, Self::channel_did_change);
@ -285,6 +288,99 @@ impl ChatPanel {
}
}
fn render_replied_to_message(
&mut self,
message_id: Option<ChannelMessageId>,
reply_to_message: &ChannelMessage,
cx: &mut ViewContext<Self>,
) -> impl IntoElement {
let body_element_id: ElementId = match message_id {
Some(ChannelMessageId::Saved(id)) => ("reply-to-saved-message", id).into(),
Some(ChannelMessageId::Pending(id)) => ("reply-to-pending-message", id).into(), // This should never happen
None => ("composing-reply").into(),
};
let message_element_id: ElementId = match message_id {
Some(ChannelMessageId::Saved(id)) => ("reply-to-saved-message-container", id).into(),
Some(ChannelMessageId::Pending(id)) => {
("reply-to-pending-message-container", id).into()
} // This should never happen
None => ("composing-reply-container").into(),
};
let current_channel_id = self.channel_id(cx);
let reply_to_message_id = reply_to_message.id;
let reply_to_message_body = self
.markdown_data
.entry(reply_to_message.id)
.or_insert_with(|| {
Self::render_markdown_with_mentions(
&self.languages,
self.client.id(),
reply_to_message,
)
});
const REPLY_TO_PREFIX: &str = "Reply to @";
div().flex_grow().child(
v_flex()
.id(message_element_id)
.text_ui_xs()
.child(
h_flex()
.gap_x_1()
.items_center()
.justify_start()
.overflow_x_hidden()
.whitespace_nowrap()
.child(
StyledText::new(format!(
"{}{}",
REPLY_TO_PREFIX,
reply_to_message.sender.github_login.clone()
))
.with_highlights(
&cx.text_style(),
vec![(
(REPLY_TO_PREFIX.len() - 1)
..(reply_to_message.sender.github_login.len()
+ REPLY_TO_PREFIX.len()),
HighlightStyle {
font_weight: Some(FontWeight::BOLD),
..Default::default()
},
)],
),
),
)
.child(
div()
.border_l_2()
.border_color(cx.theme().colors().border)
.px_1()
.py_0p5()
.mb_1()
.overflow_hidden()
.child(
div()
.max_h_12()
.child(reply_to_message_body.element(body_element_id, cx)),
),
)
.cursor(CursorStyle::PointingHand)
.tooltip(|cx| Tooltip::text("Go to message", cx))
.on_click(cx.listener(move |chat_panel, _, cx| {
if let Some(channel_id) = current_channel_id {
chat_panel
.select_channel(channel_id, reply_to_message_id.into(), cx)
.detach_and_log_err(cx)
}
})),
)
}
fn render_message(&mut self, ix: usize, cx: &mut ViewContext<Self>) -> impl IntoElement {
let active_chat = &self.active_chat.as_ref().unwrap().0;
let (message, is_continuation_from_previous, is_admin) =
@ -317,18 +413,9 @@ impl ChatPanel {
});
let _is_pending = message.is_pending();
let text = self.markdown_data.entry(message.id).or_insert_with(|| {
Self::render_markdown_with_mentions(&self.languages, self.client.id(), &message)
});
let belongs_to_user = Some(message.sender.id) == self.client.user_id();
let message_id_to_remove = if let (ChannelMessageId::Saved(id), true) =
(message.id, belongs_to_user || is_admin)
{
Some(id)
} else {
None
};
let can_delete_message = belongs_to_user || is_admin;
let element_id: ElementId = match message.id {
ChannelMessageId::Saved(id) => ("saved-message", id).into(),
@ -341,19 +428,41 @@ impl ChatPanel {
.iter()
.any(|m| Some(m.1) == self.client.user_id());
let message_id = match message.id {
ChannelMessageId::Saved(id) => Some(id),
ChannelMessageId::Pending(_) => None,
};
let reply_to_message = message
.reply_to_message_id
.map(|id| active_chat.read(cx).find_loaded_message(id))
.flatten()
.cloned();
let replied_to_you =
reply_to_message.as_ref().map(|m| m.sender.id) == self.client.user_id();
let is_highlighted_message = self
.highlighted_message
.as_ref()
.is_some_and(|(id, _)| Some(id) == message_id.as_ref());
let background = if is_highlighted_message {
cx.theme().status().info_background
} else if mentioning_you || replied_to_you {
cx.theme().colors().background
} else {
cx.theme().colors().panel_background
};
v_flex().w_full().relative().child(
div()
.bg(if mentioning_you {
Fill::from(cx.theme().colors().background)
} else {
Fill::default()
})
.bg(background)
.rounded_md()
.overflow_hidden()
.px_1()
.py_0p5()
.when(!is_continuation_from_previous, |this| {
this.mt_1().child(
this.mt_2().child(
h_flex()
.text_ui_sm()
.child(div().absolute().child(
@ -377,36 +486,86 @@ impl ChatPanel {
),
)
})
.when(mentioning_you, |this| this.mt_1())
.child(
v_flex()
.w_full()
.text_ui_sm()
.id(element_id)
.group("")
.child(text.element("body".into(), cx))
.child(
.when(
message.reply_to_message_id.is_some() && reply_to_message.is_none(),
|this| {
const MESSAGE_DELETED: &str = "Message has been deleted";
let body_text = StyledText::new(MESSAGE_DELETED).with_highlights(
&cx.text_style(),
vec![(
0..MESSAGE_DELETED.len(),
HighlightStyle {
font_style: Some(FontStyle::Italic),
..Default::default()
},
)],
);
this.child(
div()
.absolute()
.z_index(1)
.right_0()
.w_6()
.bg(cx.theme().colors().panel_background)
.when(!self.has_open_menu(message_id_to_remove), |el| {
el.visible_on_hover("")
})
.children(message_id_to_remove.map(|message_id| {
popover_menu(("menu", message_id))
.trigger(IconButton::new(
("trigger", message_id),
IconName::Ellipsis,
))
.menu(move |cx| {
Some(Self::render_message_menu(&this, message_id, cx))
})
})),
),
),
.border_l_2()
.text_ui_xs()
.border_color(cx.theme().colors().border)
.px_1()
.py_0p5()
.child(body_text),
)
},
)
.when_some(reply_to_message, |el, reply_to_message| {
el.child(self.render_replied_to_message(
Some(message.id),
&reply_to_message,
cx,
))
})
.when(mentioning_you || replied_to_you, |this| this.my_0p5())
.map(|el| {
let text = self.markdown_data.entry(message.id).or_insert_with(|| {
Self::render_markdown_with_mentions(
&self.languages,
self.client.id(),
&message,
)
});
el.child(
v_flex()
.w_full()
.text_ui_sm()
.id(element_id)
.group("")
.child(text.element("body".into(), cx))
.child(
div()
.absolute()
.z_index(1)
.right_0()
.w_6()
.bg(background)
.when(!self.has_open_menu(message_id), |el| {
el.visible_on_hover("")
})
.when_some(message_id, |el, message_id| {
el.child(
popover_menu(("menu", message_id))
.trigger(IconButton::new(
("trigger", message_id),
IconName::Ellipsis,
))
.menu(move |cx| {
Some(Self::render_message_menu(
&this,
message_id,
can_delete_message,
cx,
))
}),
)
}),
),
)
}),
)
}
@ -420,13 +579,27 @@ impl ChatPanel {
fn render_message_menu(
this: &View<Self>,
message_id: u64,
can_delete_message: bool,
cx: &mut WindowContext,
) -> View<ContextMenu> {
let menu = {
let this = this.clone();
ContextMenu::build(cx, move |menu, _| {
menu.entry("Delete message", None, move |cx| {
this.update(cx, |this, cx| this.remove_message(message_id, cx))
ContextMenu::build(cx, move |menu, cx| {
menu.entry(
"Reply to message",
None,
cx.handler_for(&this, move |this, cx| {
this.message_editor.update(cx, |editor, cx| {
editor.set_reply_to_message_id(message_id);
editor.focus_handle(cx).focus(cx);
})
}),
)
.when(can_delete_message, move |menu| {
menu.entry(
"Delete message",
None,
cx.handler_for(&this, move |this, cx| this.remove_message(message_id, cx)),
)
})
})
};
@ -517,7 +690,21 @@ impl ChatPanel {
ChannelChat::load_history_since_message(chat.clone(), message_id, (*cx).clone())
.await
{
let task = cx.spawn({
let this = this.clone();
|mut cx| async move {
cx.background_executor().timer(Duration::from_secs(2)).await;
this.update(&mut cx, |this, cx| {
this.highlighted_message.take();
cx.notify();
})
.ok();
}
});
this.update(&mut cx, |this, cx| {
this.highlighted_message = Some((message_id, task));
if this.active_chat.as_ref().map_or(false, |(c, _)| *c == chat) {
this.message_list.scroll_to(ListOffset {
item_ix,
@ -536,6 +723,8 @@ impl ChatPanel {
impl Render for ChatPanel {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let reply_to_message_id = self.message_editor.read(cx).reply_to_message_id();
v_flex()
.track_focus(&self.focus_handle)
.full()
@ -558,7 +747,7 @@ impl Render for ChatPanel {
),
),
)
.child(div().flex_grow().px_2().pt_1().map(|this| {
.child(div().flex_grow().px_2().map(|this| {
if self.active_chat.is_some() {
this.child(list(self.message_list.clone()).full())
} else {
@ -589,25 +778,58 @@ impl Render for ChatPanel {
)
}
}))
.child(
h_flex()
.when(!self.is_scrolled_to_bottom, |el| {
el.border_t_1().border_color(cx.theme().colors().border)
.when_some(reply_to_message_id, |el, reply_to_message_id| {
let reply_message = self
.active_chat()
.map(|active_chat| {
active_chat.read(cx).messages().iter().find_map(|m| {
if m.id == ChannelMessageId::Saved(reply_to_message_id) {
Some(m)
} else {
None
}
})
})
.p_2()
.map(|el| {
if self.active_chat.is_some() {
el.child(self.message_editor.clone())
} else {
el.child(
div()
.rounded_md()
.h_6()
.w_full()
.bg(cx.theme().colors().editor_background),
)
}
}),
.flatten()
.cloned();
el.when_some(reply_message, |el, reply_message| {
el.child(
div()
.when(!self.is_scrolled_to_bottom, |el| {
el.border_t_1().border_color(cx.theme().colors().border)
})
.flex()
.w_full()
.items_start()
.overflow_hidden()
.py_1()
.px_2()
.bg(cx.theme().colors().background)
.child(self.render_replied_to_message(None, &reply_message, cx))
.child(
IconButton::new("close-reply-preview", IconName::Close)
.shape(ui::IconButtonShape::Square)
.on_click(cx.listener(move |this, _, cx| {
this.message_editor.update(cx, |editor, _| {
editor.clear_reply_to_message_id()
});
})),
),
)
})
})
.children(
Some(
h_flex()
.when(
!self.is_scrolled_to_bottom && reply_to_message_id.is_none(),
|el| el.border_t_1().border_color(cx.theme().colors().border),
)
.p_2()
.map(|el| el.child(self.message_editor.clone())),
)
.filter(|_| self.active_chat.is_some()),
)
.into_any()
}
@ -747,6 +969,7 @@ mod tests {
}),
nonce: 5,
mentions: vec![(ranges[0].clone(), 101), (ranges[1].clone(), 102)],
reply_to_message_id: None,
};
let message = ChatPanel::render_markdown_with_mentions(&language_registry, 102, &message);

View File

@ -34,6 +34,7 @@ pub struct MessageEditor {
mentions: Vec<UserId>,
mentions_task: Option<Task<()>>,
channel_id: Option<ChannelId>,
reply_to_message_id: Option<u64>,
}
struct MessageEditorCompletionProvider(WeakView<MessageEditor>);
@ -112,9 +113,22 @@ impl MessageEditor {
channel_id: None,
mentions: Vec::new(),
mentions_task: None,
reply_to_message_id: None,
}
}
pub fn reply_to_message_id(&self) -> Option<u64> {
self.reply_to_message_id
}
pub fn set_reply_to_message_id(&mut self, reply_to_message_id: u64) {
self.reply_to_message_id = Some(reply_to_message_id);
}
pub fn clear_reply_to_message_id(&mut self) {
self.reply_to_message_id = None;
}
pub fn set_channel(
&mut self,
channel_id: u64,
@ -172,8 +186,13 @@ impl MessageEditor {
editor.clear(cx);
self.mentions.clear();
let reply_to_message_id = std::mem::take(&mut self.reply_to_message_id);
MessageParams { text, mentions }
MessageParams {
text,
mentions,
reply_to_message_id,
}
})
}
@ -341,6 +360,7 @@ impl Render for MessageEditor {
line_height: relative(1.3).into(),
background_color: None,
underline: None,
strikethrough: None,
white_space: WhiteSpace::Normal,
};
@ -424,6 +444,7 @@ mod tests {
MessageParams {
text,
mentions: vec![(ranges[0].clone(), 101), (ranges[1].clone(), 102)],
reply_to_message_id: None
}
);
});

View File

@ -40,7 +40,7 @@ use util::{maybe, ResultExt, TryFutureExt};
use workspace::{
dock::{DockPosition, Panel, PanelEvent},
notifications::{DetachAndPromptErr, NotifyResultExt, NotifyTaskExt},
Workspace,
OpenChannelNotes, Workspace,
};
actions!(
@ -69,6 +69,19 @@ pub fn init(cx: &mut AppContext) {
workspace.register_action(|workspace, _: &ToggleFocus, cx| {
workspace.toggle_panel_focus::<CollabPanel>(cx);
});
workspace.register_action(|_, _: &OpenChannelNotes, cx| {
let channel_id = ActiveCall::global(cx)
.read(cx)
.room()
.and_then(|room| room.read(cx).channel_id());
if let Some(channel_id) = channel_id {
let workspace = cx.view().clone();
cx.window_context().defer(move |cx| {
ChannelView::open(channel_id, None, workspace, cx).detach_and_log_err(cx)
});
}
});
})
.detach();
}
@ -957,7 +970,7 @@ impl CollabPanel {
.child(render_tree_branch(false, true, cx))
.child(IconButton::new(0, IconName::File)),
)
.child(div().h_7().w_full().child(Label::new("notes")))
.child(Label::new("notes"))
.tooltip(move |cx| Tooltip::text("Open Channel Notes", cx))
}
@ -2055,6 +2068,7 @@ impl CollabPanel {
line_height: relative(1.3).into(),
background_color: None,
underline: None,
strikethrough: None,
white_space: WhiteSpace::Normal,
};

View File

@ -562,14 +562,23 @@ impl CollabTitlebarItem {
}
fn window_activation_changed(&mut self, cx: &mut ViewContext<Self>) {
let project = if cx.is_window_active() {
Some(self.project.clone())
} else {
None
};
ActiveCall::global(cx)
.update(cx, |call, cx| call.set_location(project.as_ref(), cx))
.detach_and_log_err(cx);
if cx.is_window_active() {
ActiveCall::global(cx)
.update(cx, |call, cx| call.set_location(Some(&self.project), cx))
.detach_and_log_err(cx);
return;
}
if cx.active_window().is_none() {
ActiveCall::global(cx)
.update(cx, |call, cx| call.set_location(None, cx))
.detach_and_log_err(cx);
}
self.workspace
.update(cx, |workspace, cx| {
workspace.update_active_view_for_followers(cx);
})
.ok();
}
fn active_call_changed(&mut self, cx: &mut ViewContext<Self>) {

View File

@ -16,4 +16,4 @@ doctest = true
[dependencies]
itertools = { version = "0.11.0", optional = true }
palette = "0.7.3"
story = { path = "../story", optional = true }
story = { workspace = true, optional = true }

View File

@ -11,32 +11,32 @@ doctest = false
[dependencies]
anyhow.workspace = true
client = { path = "../client" }
collections = { path = "../collections" }
client.workspace = true
collections.workspace = true
# HACK: We're only depending on `copilot` here for `CommandPaletteFilter`. See the attached comment on that type.
copilot = { path = "../copilot" }
editor = { path = "../editor" }
fuzzy = { path = "../fuzzy" }
gpui = { path = "../gpui" }
picker = { path = "../picker" }
project = { path = "../project" }
release_channel = { path = "../release_channel" }
copilot.workspace = true
editor.workspace = true
fuzzy.workspace = true
gpui.workspace = true
picker.workspace = true
project.workspace = true
release_channel.workspace = true
serde.workspace = true
settings = { path = "../settings" }
theme = { path = "../theme" }
ui = { path = "../ui" }
util = { path = "../util" }
workspace = { path = "../workspace" }
zed_actions = { path = "../zed_actions" }
settings.workspace = true
theme.workspace = true
ui.workspace = true
util.workspace = true
workspace.workspace = true
zed_actions.workspace = true
[dev-dependencies]
ctor.workspace = true
editor = { path = "../editor", features = ["test-support"] }
editor = { workspace = true, features = ["test-support"] }
env_logger.workspace = true
go_to_line = { path = "../go_to_line" }
gpui = { path = "../gpui", features = ["test-support"] }
language = { path = "../language", features = ["test-support"] }
menu = { path = "../menu" }
project = { path = "../project", features = ["test-support"] }
go_to_line.workspace = true
gpui = { workspace = true, features = ["test-support"] }
language = { workspace = true, features = ["test-support"] }
menu.workspace = true
project = { workspace = true, features = ["test-support"] }
serde_json.workspace = true
workspace = { path = "../workspace", features = ["test-support"] }
workspace = { workspace = true, features = ["test-support"] }

View File

@ -23,28 +23,28 @@ test-support = [
anyhow.workspace = true
async-compression.workspace = true
async-tar = "0.4.2"
collections = { path = "../collections" }
collections.workspace = true
futures.workspace = true
gpui = { path = "../gpui" }
language = { path = "../language" }
gpui.workspace = true
language.workspace = true
log.workspace = true
lsp = { path = "../lsp" }
node_runtime = { path = "../node_runtime" }
lsp.workspace = true
node_runtime.workspace = true
parking_lot.workspace = true
serde.workspace = true
serde_derive.workspace = true
settings = { path = "../settings" }
settings.workspace = true
smol.workspace = true
theme = { path = "../theme" }
util = { path = "../util" }
theme.workspace = true
util.workspace = true
[dev-dependencies]
clock = { path = "../clock" }
collections = { path = "../collections", features = ["test-support"] }
fs = { path = "../fs", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
language = { path = "../language", features = ["test-support"] }
lsp = { path = "../lsp", features = ["test-support"] }
rpc = { path = "../rpc", features = ["test-support"] }
settings = { path = "../settings", features = ["test-support"] }
util = { path = "../util", features = ["test-support"] }
clock.workspace = true
collections = { workspace = true, features = ["test-support"] }
fs = { workspace = true, features = ["test-support"] }
gpui = { workspace = true, features = ["test-support"] }
language = { workspace = true, features = ["test-support"] }
lsp = { workspace = true, features = ["test-support"] }
rpc = { workspace = true, features = ["test-support"] }
settings = { workspace = true, features = ["test-support"] }
util = { workspace = true, features = ["test-support"] }

View File

@ -11,19 +11,19 @@ doctest = false
[dependencies]
anyhow.workspace = true
copilot = { path = "../copilot" }
editor = { path = "../editor" }
fs = { path = "../fs" }
copilot.workspace = true
editor.workspace = true
fs.workspace = true
futures.workspace = true
gpui = { path = "../gpui" }
language = { path = "../language" }
settings = { path = "../settings" }
gpui.workspace = true
language.workspace = true
settings.workspace = true
smol.workspace = true
theme = { path = "../theme" }
ui = { path = "../ui" }
util = { path = "../util" }
workspace = { path = "../workspace" }
zed_actions = { path = "../zed_actions" }
theme.workspace = true
ui.workspace = true
util.workspace = true
workspace.workspace = true
zed_actions.workspace = true
[dev-dependencies]
editor = { path = "../editor", features = ["test-support"] }
editor = { workspace = true, features = ["test-support"] }

View File

@ -15,21 +15,21 @@ test-support = []
[dependencies]
anyhow.workspace = true
async-trait.workspace = true
collections = { path = "../collections" }
gpui = { path = "../gpui" }
collections.workspace = true
gpui.workspace = true
indoc.workspace = true
lazy_static.workspace = true
log.workspace = true
parking_lot.workspace = true
release_channel = { path = "../release_channel" }
release_channel.workspace = true
serde.workspace = true
serde_derive.workspace = true
smol.workspace = true
sqlez = { path = "../sqlez" }
sqlez_macros = { path = "../sqlez_macros" }
util = { path = "../util" }
sqlez.workspace = true
sqlez_macros.workspace = true
util.workspace = true
[dev-dependencies]
env_logger.workspace = true
gpui = { path = "../gpui", features = ["test-support"] }
gpui = { workspace = true, features = ["test-support"] }
tempfile.workspace = true

View File

@ -11,32 +11,32 @@ doctest = false
[dependencies]
anyhow.workspace = true
collections = { path = "../collections" }
editor = { path = "../editor" }
collections.workspace = true
editor.workspace = true
futures.workspace = true
gpui = { path = "../gpui" }
language = { path = "../language" }
gpui.workspace = true
language.workspace = true
log.workspace = true
lsp = { path = "../lsp" }
lsp.workspace = true
postage.workspace = true
project = { path = "../project" }
project.workspace = true
schemars.workspace = true
serde.workspace = true
serde_derive.workspace = true
settings = { path = "../settings" }
settings.workspace = true
smallvec.workspace = true
theme = { path = "../theme" }
ui = { path = "../ui" }
util = { path = "../util" }
workspace = { path = "../workspace" }
theme.workspace = true
ui.workspace = true
util.workspace = true
workspace.workspace = true
[dev-dependencies]
client = { path = "../client", features = ["test-support"] }
editor = { path = "../editor", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
language = { path = "../language", features = ["test-support"] }
lsp = { path = "../lsp", features = ["test-support"] }
client = { workspace = true, features = ["test-support"] }
editor = { workspace = true, features = ["test-support"] }
gpui = { workspace = true, features = ["test-support"] }
language = { workspace = true, features = ["test-support"] }
lsp = { workspace = true, features = ["test-support"] }
serde_json.workspace = true
theme = { path = "../theme", features = ["test-support"] }
theme = { workspace = true, features = ["test-support"] }
unindent.workspace = true
workspace = { path = "../workspace", features = ["test-support"] }
workspace = { workspace = true, features = ["test-support"] }

View File

@ -20,73 +20,74 @@ test-support = [
"util/test-support",
"workspace/test-support",
"tree-sitter-rust",
"tree-sitter-typescript"
"tree-sitter-typescript",
]
[dependencies]
aho-corasick = "1.1"
anyhow.workspace = true
client = { path = "../client" }
clock = { path = "../clock" }
collections = { path = "../collections" }
client.workspace = true
clock.workspace = true
collections.workspace = true
convert_case = "0.6.0"
copilot = { path = "../copilot" }
db = { path = "../db" }
copilot.workspace = true
db.workspace = true
futures.workspace = true
fuzzy = { path = "../fuzzy" }
git = { path = "../git" }
gpui = { path = "../gpui" }
fuzzy.workspace = true
git.workspace = true
gpui.workspace = true
indoc = "1.0.4"
itertools = "0.10"
language = { path = "../language" }
language.workspace = true
lazy_static.workspace = true
linkify = "0.10.0"
log.workspace = true
lsp = { path = "../lsp" }
multi_buffer = { path = "../multi_buffer" }
lsp.workspace = true
multi_buffer.workspace = true
ordered-float.workspace = true
parking_lot.workspace = true
postage.workspace = true
project = { path = "../project" }
project.workspace = true
rand.workspace = true
rich_text = { path = "../rich_text" }
rpc = { path = "../rpc" }
rich_text.workspace = true
rpc.workspace = true
schemars.workspace = true
serde.workspace = true
serde_derive.workspace = true
serde_json.workspace = true
settings = { path = "../settings" }
settings.workspace = true
smallvec.workspace = true
smol.workspace = true
snippet = { path = "../snippet" }
sqlez = { path = "../sqlez" }
sum_tree = { path = "../sum_tree" }
text = { path = "../text" }
theme = { path = "../theme" }
snippet.workspace = true
sqlez.workspace = true
sum_tree.workspace = true
text.workspace = true
theme.workspace = true
tree-sitter-html = { workspace = true, optional = true }
tree-sitter-rust = { workspace = true, optional = true }
tree-sitter-typescript = { workspace = true, optional = true }
ui = { path = "../ui" }
ui.workspace = true
url.workspace = true
util = { path = "../util" }
workspace = { path = "../workspace" }
util.workspace = true
workspace.workspace = true
[dev-dependencies]
copilot = { path = "../copilot", features = ["test-support"] }
copilot = { workspace = true, features = ["test-support"] }
ctor.workspace = true
env_logger.workspace = true
gpui = { path = "../gpui", features = ["test-support"] }
language = { path = "../language", features = ["test-support"] }
lsp = { path = "../lsp", features = ["test-support"] }
multi_buffer = { path = "../multi_buffer", features = ["test-support"] }
project = { path = "../project", features = ["test-support"] }
release_channel = { path = "../release_channel" }
gpui = { workspace = true, features = ["test-support"] }
language = { workspace = true, features = ["test-support"] }
lsp = { workspace = true, features = ["test-support"] }
multi_buffer = { workspace = true, features = ["test-support"] }
project = { workspace = true, features = ["test-support"] }
release_channel.workspace = true
rand.workspace = true
settings = { path = "../settings", features = ["test-support"] }
text = { path = "../text", features = ["test-support"] }
settings = { workspace = true, features = ["test-support"] }
text = { workspace = true, features = ["test-support"] }
tree-sitter-html.workspace = true
tree-sitter-rust.workspace = true
tree-sitter-typescript.workspace = true
tree-sitter.workspace = true
unindent.workspace = true
util = { path = "../util", features = ["test-support"] }
workspace = { path = "../workspace", features = ["test-support"] }
util = { workspace = true, features = ["test-support"] }
workspace = { workspace = true, features = ["test-support"] }

View File

@ -70,6 +70,30 @@ pub struct FoldAt {
pub struct UnfoldAt {
pub buffer_row: u32,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
pub struct MoveUpByLines {
#[serde(default)]
pub(super) lines: u32,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
pub struct MoveDownByLines {
#[serde(default)]
pub(super) lines: u32,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
pub struct SelectUpByLines {
#[serde(default)]
pub(super) lines: u32,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
pub struct SelectDownByLines {
#[serde(default)]
pub(super) lines: u32,
}
impl_actions!(
editor,
[
@ -84,7 +108,11 @@ impl_actions!(
ConfirmCodeAction,
ToggleComments,
FoldAt,
UnfoldAt
UnfoldAt,
MoveUpByLines,
MoveDownByLines,
SelectUpByLines,
SelectDownByLines
]
);

View File

@ -25,8 +25,8 @@ mod wrap_map;
use crate::EditorStyle;
use crate::{
link_go_to_definition::InlayHighlight, movement::TextLayoutDetails, Anchor, AnchorRangeExt,
InlayId, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint,
hover_links::InlayHighlight, movement::TextLayoutDetails, Anchor, AnchorRangeExt, InlayId,
MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint,
};
pub use block_map::{BlockMap, BlockPoint};
use collections::{BTreeMap, HashMap, HashSet};
@ -586,8 +586,9 @@ impl DisplaySnapshot {
text_system,
editor_style,
rem_size,
anchor: _,
scroll_anchor: _,
visible_rows: _,
vertical_scroll_margin: _,
}: &TextLayoutDetails,
) -> Arc<LineLayout> {
let mut runs = Vec::new();

View File

@ -1168,7 +1168,7 @@ mod tests {
use super::*;
use crate::{
display_map::{InlayHighlights, TextHighlights},
link_go_to_definition::InlayHighlight,
hover_links::InlayHighlight,
InlayId, MultiBuffer,
};
use gpui::AppContext;

View File

@ -22,9 +22,9 @@ mod inlay_hint_cache;
mod debounced_delay;
mod git;
mod highlight_matching_bracket;
mod hover_links;
mod hover_popover;
pub mod items;
mod link_go_to_definition;
mod mouse_context_menu;
pub mod movement;
mod persistence;
@ -61,8 +61,8 @@ use gpui::{
DispatchPhase, ElementId, EventEmitter, FocusHandle, FocusableView, FontId, FontStyle,
FontWeight, HighlightStyle, Hsla, InteractiveText, KeyContext, Model, MouseButton,
ParentElement, Pixels, Render, SharedString, Styled, StyledText, Subscription, Task, TextStyle,
UniformListScrollHandle, View, ViewContext, ViewInputHandler, VisualContext, WeakView,
WhiteSpace, WindowContext,
UnderlineStyle, UniformListScrollHandle, View, ViewContext, ViewInputHandler, VisualContext,
WeakView, WhiteSpace, WindowContext,
};
use highlight_matching_bracket::refresh_matching_bracket_highlights;
use hover_popover::{hide_hover, HoverState};
@ -77,7 +77,7 @@ use language::{
Language, OffsetRangeExt, Point, Selection, SelectionGoal, TransactionId,
};
use link_go_to_definition::{GoToDefinitionLink, InlayHighlight, LinkGoToDefinitionState};
use hover_links::{HoverLink, HoveredLinkState, InlayHighlight};
use lsp::{DiagnosticSeverity, LanguageServerId};
use mouse_context_menu::MouseContextMenu;
use movement::TextLayoutDetails;
@ -120,6 +120,7 @@ use ui::{
Tooltip,
};
use util::{maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
use workspace::Toast;
use workspace::{searchable::SearchEvent, ItemNavHistory, Pane, SplitDirection, ViewId, Workspace};
const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
@ -374,6 +375,7 @@ pub struct Editor {
hovered_cursors: HashMap<HoveredCursor, Task<()>>,
pub show_local_selections: bool,
mode: EditorMode,
show_breadcrumbs: bool,
show_gutter: bool,
show_wrap_guides: Option<bool>,
placeholder_text: Option<Arc<str>>,
@ -402,7 +404,7 @@ pub struct Editor {
remote_id: Option<ViewId>,
hover_state: HoverState,
gutter_hovered: bool,
link_go_to_definition_state: LinkGoToDefinitionState,
hovered_link_state: Option<HoveredLinkState>,
copilot_state: CopilotState,
inlay_hint_cache: InlayHintCache,
next_inlay_id: usize,
@ -851,15 +853,21 @@ impl CompletionsMenu {
let selected_item = self.selected_item;
let style = style.clone();
let multiline_docs = {
let multiline_docs = if show_completion_documentation {
let mat = &self.matches[selected_item];
let multiline_docs = match &self.completions.read()[mat.candidate_id].documentation {
Some(Documentation::MultiLinePlainText(text)) => {
Some(div().child(SharedString::from(text.clone())))
}
Some(Documentation::MultiLineMarkdown(parsed)) => Some(div().child(
render_parsed_markdown("completions_markdown", parsed, &style, workspace, cx),
)),
Some(Documentation::MultiLineMarkdown(parsed)) if !parsed.text.is_empty() => {
Some(div().child(render_parsed_markdown(
"completions_markdown",
parsed,
&style,
workspace,
cx,
)))
}
_ => None,
};
multiline_docs.map(|div| {
@ -876,6 +884,8 @@ impl CompletionsMenu {
// because that would move the cursor.
.on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
})
} else {
None
};
let list = uniform_list(
@ -1424,7 +1434,7 @@ impl Editor {
buffer: buffer.clone(),
display_map: display_map.clone(),
selections,
scroll_manager: ScrollManager::new(),
scroll_manager: ScrollManager::new(cx),
columnar_selection_tail: None,
add_selections_state: None,
select_next_state: None,
@ -1442,6 +1452,7 @@ impl Editor {
blink_manager: blink_manager.clone(),
show_local_selections: true,
mode,
show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
show_gutter: mode == EditorMode::Full,
show_wrap_guides: None,
placeholder_text: None,
@ -1471,7 +1482,7 @@ impl Editor {
leader_peer_id: None,
remote_id: None,
hover_state: Default::default(),
link_go_to_definition_state: Default::default(),
hovered_link_state: Default::default(),
copilot_state: Default::default(),
inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
gutter_hovered: false,
@ -3080,8 +3091,9 @@ impl Editor {
text_system: cx.text_system().clone(),
editor_style: self.style.clone().unwrap(),
rem_size: cx.rem_size(),
anchor: self.scroll_manager.anchor().anchor,
scroll_anchor: self.scroll_manager.anchor(),
visible_rows: self.visible_line_count(),
vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
}
}
@ -5373,6 +5385,86 @@ impl Editor {
})
}
pub fn move_up_by_lines(&mut self, action: &MoveUpByLines, cx: &mut ViewContext<Self>) {
if self.take_rename(true, cx).is_some() {
return;
}
if matches!(self.mode, EditorMode::SingleLine) {
cx.propagate();
return;
}
let text_layout_details = &self.text_layout_details(cx);
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
let line_mode = s.line_mode;
s.move_with(|map, selection| {
if !selection.is_empty() && !line_mode {
selection.goal = SelectionGoal::None;
}
let (cursor, goal) = movement::up_by_rows(
map,
selection.start,
action.lines,
selection.goal,
false,
&text_layout_details,
);
selection.collapse_to(cursor, goal);
});
})
}
pub fn move_down_by_lines(&mut self, action: &MoveDownByLines, cx: &mut ViewContext<Self>) {
if self.take_rename(true, cx).is_some() {
return;
}
if matches!(self.mode, EditorMode::SingleLine) {
cx.propagate();
return;
}
let text_layout_details = &self.text_layout_details(cx);
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
let line_mode = s.line_mode;
s.move_with(|map, selection| {
if !selection.is_empty() && !line_mode {
selection.goal = SelectionGoal::None;
}
let (cursor, goal) = movement::down_by_rows(
map,
selection.start,
action.lines,
selection.goal,
false,
&text_layout_details,
);
selection.collapse_to(cursor, goal);
});
})
}
pub fn select_down_by_lines(&mut self, action: &SelectDownByLines, cx: &mut ViewContext<Self>) {
let text_layout_details = &self.text_layout_details(cx);
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.move_heads_with(|map, head, goal| {
movement::down_by_rows(map, head, action.lines, goal, false, &text_layout_details)
})
})
}
pub fn select_up_by_lines(&mut self, action: &SelectUpByLines, cx: &mut ViewContext<Self>) {
let text_layout_details = &self.text_layout_details(cx);
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.move_heads_with(|map, head, goal| {
movement::up_by_rows(map, head, action.lines, goal, false, &text_layout_details)
})
})
}
pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
if self.take_rename(true, cx).is_some() {
return;
@ -7156,11 +7248,8 @@ impl Editor {
cx.spawn(|editor, mut cx| async move {
let definitions = definitions.await?;
editor.update(&mut cx, |editor, cx| {
editor.navigate_to_definitions(
definitions
.into_iter()
.map(GoToDefinitionLink::Text)
.collect(),
editor.navigate_to_hover_links(
definitions.into_iter().map(HoverLink::Text).collect(),
split,
cx,
);
@ -7170,29 +7259,34 @@ impl Editor {
.detach_and_log_err(cx);
}
pub fn navigate_to_definitions(
pub fn navigate_to_hover_links(
&mut self,
mut definitions: Vec<GoToDefinitionLink>,
mut definitions: Vec<HoverLink>,
split: bool,
cx: &mut ViewContext<Editor>,
) {
let Some(workspace) = self.workspace() else {
return;
};
let pane = workspace.read(cx).active_pane().clone();
// If there is one definition, just open it directly
if definitions.len() == 1 {
let definition = definitions.pop().unwrap();
let target_task = match definition {
GoToDefinitionLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
HoverLink::InlayHint(lsp_location, server_id) => {
self.compute_target_location(lsp_location, server_id, cx)
}
HoverLink::Url(url) => {
cx.open_url(&url);
Task::ready(Ok(None))
}
};
cx.spawn(|editor, mut cx| async move {
let target = target_task.await.context("target resolution task")?;
if let Some(target) = target {
editor.update(&mut cx, |editor, cx| {
let Some(workspace) = editor.workspace() else {
return;
};
let pane = workspace.read(cx).active_pane().clone();
let range = target.range.to_offset(target.buffer.read(cx));
let range = editor.range_for_match(&range);
if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
@ -7233,37 +7327,35 @@ impl Editor {
} else if !definitions.is_empty() {
let replica_id = self.replica_id(cx);
cx.spawn(|editor, mut cx| async move {
let (title, location_tasks) = editor
let (title, location_tasks, workspace) = editor
.update(&mut cx, |editor, cx| {
let title = definitions
.iter()
.find_map(|definition| match definition {
GoToDefinitionLink::Text(link) => {
link.origin.as_ref().map(|origin| {
let buffer = origin.buffer.read(cx);
format!(
"Definitions for {}",
buffer
.text_for_range(origin.range.clone())
.collect::<String>()
)
})
}
GoToDefinitionLink::InlayHint(_, _) => None,
HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
let buffer = origin.buffer.read(cx);
format!(
"Definitions for {}",
buffer
.text_for_range(origin.range.clone())
.collect::<String>()
)
}),
HoverLink::InlayHint(_, _) => None,
HoverLink::Url(_) => None,
})
.unwrap_or("Definitions".to_string());
let location_tasks = definitions
.into_iter()
.map(|definition| match definition {
GoToDefinitionLink::Text(link) => {
Task::Ready(Some(Ok(Some(link.target))))
}
GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
HoverLink::InlayHint(lsp_location, server_id) => {
editor.compute_target_location(lsp_location, server_id, cx)
}
HoverLink::Url(_) => Task::ready(Ok(None)),
})
.collect::<Vec<_>>();
(title, location_tasks)
(title, location_tasks, editor.workspace().clone())
})
.context("location tasks preparation")?;
@ -7273,6 +7365,10 @@ impl Editor {
.filter_map(|location| location.transpose())
.collect::<Result<_>>()
.context("location tasks")?;
let Some(workspace) = workspace else {
return Ok(());
};
workspace
.update(&mut cx, |workspace, cx| {
Self::open_locations_in_multibuffer(
@ -7811,7 +7907,7 @@ impl Editor {
.insert_blocks(
diagnostic_group.iter().map(|entry| {
let diagnostic = entry.diagnostic.clone();
let message_height = diagnostic.message.lines().count() as u8;
let message_height = diagnostic.message.matches('\n').count() as u8 + 1;
BlockProperties {
style: BlockStyle::Fixed,
position: buffer.anchor_after(entry.range.start),
@ -8259,21 +8355,37 @@ impl Editor {
use git::permalink::{build_permalink, BuildPermalinkParams};
let permalink = maybe!({
let project = self.project.clone()?;
let project = self.project.clone().ok_or_else(|| anyhow!("no project"))?;
let project = project.read(cx);
let worktree = project.visible_worktrees(cx).next()?;
let worktree = project
.visible_worktrees(cx)
.next()
.ok_or_else(|| anyhow!("no worktree"))?;
let mut cwd = worktree.read(cx).abs_path().to_path_buf();
cwd.push(".git");
let repo = project.fs().open_repo(&cwd)?;
let origin_url = repo.lock().remote_url("origin")?;
let sha = repo.lock().head_sha()?;
const REMOTE_NAME: &'static str = "origin";
let repo = project
.fs()
.open_repo(&cwd)
.ok_or_else(|| anyhow!("no Git repo"))?;
let origin_url = repo
.lock()
.remote_url(REMOTE_NAME)
.ok_or_else(|| anyhow!("remote \"{REMOTE_NAME}\" not found"))?;
let sha = repo
.lock()
.head_sha()
.ok_or_else(|| anyhow!("failed to read HEAD SHA"))?;
let buffer = self.buffer().read(cx).as_singleton()?;
let file = buffer.read(cx).file().and_then(|f| f.as_local())?;
let path = file.path().to_str().map(|path| path.to_string())?;
let path = maybe!({
let buffer = self.buffer().read(cx).as_singleton()?;
let file = buffer.read(cx).file().and_then(|f| f.as_local())?;
file.path().to_str().map(|path| path.to_string())
})
.ok_or_else(|| anyhow!("failed to determine file path"))?;
let selections = self.selections.all::<Point>(cx);
let selection = selections.iter().peekable().next();
@ -8284,11 +8396,23 @@ impl Editor {
path: &path,
selection: selection.map(|selection| selection.range()),
})
.log_err()
});
if let Some(permalink) = permalink {
cx.write_to_clipboard(ClipboardItem::new(permalink.to_string()));
match permalink {
Ok(permalink) => {
cx.write_to_clipboard(ClipboardItem::new(permalink.to_string()));
}
Err(err) => {
let message = format!("Failed to copy permalink: {err}");
Err::<(), anyhow::Error>(err).log_err();
if let Some(workspace) = self.workspace() {
workspace.update(cx, |workspace, cx| {
workspace.show_toast(Toast::new(0x156a5f9ee, message), cx)
})
}
}
}
}
@ -8676,6 +8800,9 @@ impl Editor {
)),
cx,
);
let editor_settings = EditorSettings::get_global(cx);
self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
cx.notify();
}
@ -9368,6 +9495,7 @@ impl Render for Editor {
line_height: relative(settings.buffer_line_height.value()),
background_color: None,
underline: None,
strikethrough: None,
white_space: WhiteSpace::Normal,
},
@ -9381,6 +9509,7 @@ impl Render for Editor {
line_height: relative(settings.buffer_line_height.value()),
background_color: None,
underline: None,
strikethrough: None,
white_space: WhiteSpace::Normal,
},
};
@ -9587,7 +9716,14 @@ impl ViewInputHandler for Editor {
} else {
this.highlight_text::<InputComposition>(
marked_ranges.clone(),
HighlightStyle::default(), // todo!() this.style(cx).composition_mark,
HighlightStyle {
underline: Some(UnderlineStyle {
thickness: px(1.),
color: None,
wavy: false,
}),
..Default::default()
},
cx,
);
}

View File

@ -10,7 +10,9 @@ pub struct EditorSettings {
pub show_completion_documentation: bool,
pub completion_documentation_secondary_query_debounce: u64,
pub use_on_type_format: bool,
pub toolbar: Toolbar,
pub scrollbar: Scrollbar,
pub vertical_scroll_margin: f32,
pub relative_line_numbers: bool,
pub seed_search_query_from_cursor: SeedQuerySetting,
pub redact_private_values: bool,
@ -28,12 +30,19 @@ pub enum SeedQuerySetting {
Never,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub struct Toolbar {
pub breadcrumbs: bool,
pub quick_actions: bool,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub struct Scrollbar {
pub show: ShowScrollbar,
pub git_diff: bool,
pub selections: bool,
pub symbols_selections: bool,
pub diagnostics: bool,
}
/// When to show the scrollbar in the editor.
@ -84,8 +93,15 @@ pub struct EditorSettingsContent {
///
/// Default: true
pub use_on_type_format: Option<bool>,
/// Toolbar related settings
pub toolbar: Option<ToolbarContent>,
/// Scrollbar related settings
pub scrollbar: Option<ScrollbarContent>,
/// The number of lines to keep above/below the cursor when auto-scrolling.
///
/// Default: 3.
pub vertical_scroll_margin: Option<f32>,
/// Whether the line numbers on editors gutter are relative or not.
///
/// Default: false
@ -103,6 +119,19 @@ pub struct EditorSettingsContent {
pub redact_private_values: Option<bool>,
}
// Toolbar related settings
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub struct ToolbarContent {
/// Whether to display breadcrumbs in the editor toolbar.
///
/// Default: true
pub breadcrumbs: Option<bool>,
/// Whether to display quik action buttons in the editor toolbar.
///
/// Default: true
pub quick_actions: Option<bool>,
}
/// Scrollbar related settings
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub struct ScrollbarContent {
@ -122,6 +151,10 @@ pub struct ScrollbarContent {
///
/// Default: true
pub symbols_selections: Option<bool>,
/// Whether to show diagnostic indicators in the scrollbar.
///
/// Default: true
pub diagnostics: Option<bool>,
}
impl Settings for EditorSettings {

View File

@ -9,11 +9,6 @@ use crate::{
self, hover_at, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH, MIN_POPOVER_LINE_HEIGHT,
},
items::BufferSearchHighlights,
link_go_to_definition::{
go_to_fetched_definition, go_to_fetched_type_definition, show_link_definition,
update_go_to_definition_link, update_inlay_link_and_hover_points, GoToDefinitionTrigger,
LinkGoToDefinitionState,
},
mouse_context_menu,
scroll::scroll_amount::ScrollAmount,
CursorShape, DisplayPoint, DocumentHighlightRead, DocumentHighlightWrite, Editor, EditorMode,
@ -35,6 +30,7 @@ use gpui::{
};
use itertools::Itertools;
use language::language_settings::ShowWhitespaceSetting;
use lsp::DiagnosticSeverity;
use multi_buffer::Anchor;
use project::{
project_settings::{GitGutterSetting, ProjectSettings},
@ -146,7 +142,11 @@ impl EditorElement {
register_action(view, cx, Editor::move_left);
register_action(view, cx, Editor::move_right);
register_action(view, cx, Editor::move_down);
register_action(view, cx, Editor::move_down_by_lines);
register_action(view, cx, Editor::select_down_by_lines);
register_action(view, cx, Editor::move_up);
register_action(view, cx, Editor::move_up_by_lines);
register_action(view, cx, Editor::select_up_by_lines);
register_action(view, cx, Editor::cancel);
register_action(view, cx, Editor::newline);
register_action(view, cx, Editor::newline_above);
@ -332,7 +332,14 @@ impl EditorElement {
register_action(view, cx, Editor::display_cursor_names);
}
fn register_key_listeners(&self, cx: &mut ElementContext) {
fn register_key_listeners(
&self,
cx: &mut ElementContext,
text_bounds: Bounds<Pixels>,
layout: &LayoutState,
) {
let position_map = layout.position_map.clone();
let stacking_order = cx.stacking_order().clone();
cx.on_key_event({
let editor = self.editor.clone();
move |event: &ModifiersChangedEvent, phase, cx| {
@ -340,46 +347,41 @@ impl EditorElement {
return;
}
if editor.update(cx, |editor, cx| Self::modifiers_changed(editor, event, cx)) {
cx.stop_propagation();
}
editor.update(cx, |editor, cx| {
Self::modifiers_changed(
editor,
event,
&position_map,
text_bounds,
&stacking_order,
cx,
)
})
}
});
}
pub(crate) fn modifiers_changed(
fn modifiers_changed(
editor: &mut Editor,
event: &ModifiersChangedEvent,
position_map: &PositionMap,
text_bounds: Bounds<Pixels>,
stacking_order: &StackingOrder,
cx: &mut ViewContext<Editor>,
) -> bool {
let pending_selection = editor.has_pending_selection();
if let Some(point) = &editor.link_go_to_definition_state.last_trigger_point {
if event.command && !pending_selection {
let point = point.clone();
let snapshot = editor.snapshot(cx);
let kind = point.definition_kind(event.shift);
show_link_definition(kind, editor, point, snapshot, cx);
return false;
}
}
) {
let mouse_position = cx.mouse_position();
if !text_bounds.contains(&mouse_position)
|| !cx.was_top_layer(&mouse_position, stacking_order)
{
if editor.link_go_to_definition_state.symbol_range.is_some()
|| !editor.link_go_to_definition_state.definitions.is_empty()
{
editor.link_go_to_definition_state.symbol_range.take();
editor.link_go_to_definition_state.definitions.clear();
cx.notify();
}
editor.link_go_to_definition_state.task = None;
editor.clear_highlights::<LinkGoToDefinitionState>(cx);
return;
}
false
editor.update_hovered_link(
position_map.point_for_position(text_bounds, mouse_position),
&position_map.snapshot,
event.modifiers,
cx,
)
}
fn mouse_left_down(
@ -480,13 +482,7 @@ impl EditorElement {
&& cx.was_top_layer(&event.position, stacking_order)
{
let point = position_map.point_for_position(text_bounds, event.position);
let could_be_inlay = point.as_valid().is_none();
let split = event.modifiers.alt;
if event.modifiers.shift || could_be_inlay {
go_to_fetched_type_definition(editor, point, split, cx);
} else {
go_to_fetched_definition(editor, point, split, cx);
}
editor.handle_click_hovered_link(point, event.modifiers, cx);
cx.stop_propagation();
} else if end_selection {
@ -559,31 +555,14 @@ impl EditorElement {
if text_hovered && was_top {
let point_for_position = position_map.point_for_position(text_bounds, event.position);
match point_for_position.as_valid() {
Some(point) => {
update_go_to_definition_link(
editor,
Some(GoToDefinitionTrigger::Text(point)),
modifiers.command,
modifiers.shift,
cx,
);
hover_at(editor, Some(point), cx);
Self::update_visible_cursor(editor, point, position_map, cx);
}
None => {
update_inlay_link_and_hover_points(
&position_map.snapshot,
point_for_position,
editor,
modifiers.command,
modifiers.shift,
cx,
);
}
editor.update_hovered_link(point_for_position, &position_map.snapshot, modifiers, cx);
if let Some(point) = point_for_position.as_valid() {
hover_at(editor, Some(point), cx);
Self::update_visible_cursor(editor, point, position_map, cx);
}
} else {
update_go_to_definition_link(editor, None, modifiers.command, modifiers.shift, cx);
editor.hide_hovered_link(cx);
hover_at(editor, None, cx);
if gutter_hovered && was_top {
cx.stop_propagation();
@ -925,13 +904,13 @@ impl EditorElement {
if self
.editor
.read(cx)
.link_go_to_definition_state
.definitions
.is_empty()
.hovered_link_state
.as_ref()
.is_some_and(|hovered_link_state| !hovered_link_state.links.is_empty())
{
cx.set_cursor_style(CursorStyle::IBeam);
} else {
cx.set_cursor_style(CursorStyle::PointingHand);
} else {
cx.set_cursor_style(CursorStyle::IBeam);
}
}
@ -1094,6 +1073,7 @@ impl EditorElement {
font: self.style.text.font(),
color: self.style.background,
background_color: None,
strikethrough: None,
underline: None,
}],
)
@ -1477,6 +1457,64 @@ impl EditorElement {
}
}
if layout.is_singleton && scrollbar_settings.diagnostics {
let max_point = layout
.position_map
.snapshot
.display_snapshot
.buffer_snapshot
.max_point();
let diagnostics = layout
.position_map
.snapshot
.buffer_snapshot
.diagnostics_in_range::<_, Point>(Point::zero()..max_point, false)
// We want to sort by severity, in order to paint the most severe diagnostics last.
.sorted_by_key(|diagnostic| std::cmp::Reverse(diagnostic.diagnostic.severity));
for diagnostic in diagnostics {
let start_display = diagnostic
.range
.start
.to_display_point(&layout.position_map.snapshot.display_snapshot);
let end_display = diagnostic
.range
.end
.to_display_point(&layout.position_map.snapshot.display_snapshot);
let start_y = y_for_row(start_display.row() as f32);
let mut end_y = if diagnostic.range.start == diagnostic.range.end {
y_for_row((end_display.row() + 1) as f32)
} else {
y_for_row((end_display.row()) as f32)
};
if end_y - start_y < px(1.) {
end_y = start_y + px(1.);
}
let bounds = Bounds::from_corners(point(left, start_y), point(right, end_y));
let color = match diagnostic.diagnostic.severity {
DiagnosticSeverity::ERROR => cx.theme().status().error,
DiagnosticSeverity::WARNING => cx.theme().status().warning,
DiagnosticSeverity::INFORMATION => cx.theme().status().info,
_ => cx.theme().status().hint,
};
cx.paint_quad(quad(
bounds,
Corners::default(),
color,
Edges {
top: Pixels::ZERO,
right: px(1.),
bottom: Pixels::ZERO,
left: px(1.),
},
cx.theme().colors().scrollbar_thumb_border,
));
}
}
cx.paint_quad(quad(
thumb_bounds,
Corners::default(),
@ -1676,6 +1714,7 @@ impl EditorElement {
color: Hsla::default(),
background_color: None,
underline: None,
strikethrough: None,
}],
)
.unwrap();
@ -1812,6 +1851,7 @@ impl EditorElement {
color,
background_color: None,
underline: None,
strikethrough: None,
};
let shaped_line = cx
.text_system()
@ -1869,6 +1909,7 @@ impl EditorElement {
color: placeholder_color,
background_color: None,
underline: Default::default(),
strikethrough: None,
};
cx.text_system()
.shape_line(line.to_string().into(), font_size, &[run])
@ -2106,6 +2147,9 @@ impl EditorElement {
// Symbols Selections
(is_singleton && scrollbar_settings.symbols_selections && (editor.has_background_highlights::<DocumentHighlightRead>() || editor.has_background_highlights::<DocumentHighlightWrite>()))
||
// Diagnostics
(is_singleton && scrollbar_settings.diagnostics && snapshot.buffer_snapshot.has_diagnostics())
||
// Scrollmanager
editor.scroll_manager.scrollbars_visible()
}
@ -2281,6 +2325,7 @@ impl EditorElement {
color: cx.theme().colors().editor_invisible,
background_color: None,
underline: None,
strikethrough: None,
}],
)
.unwrap();
@ -2295,6 +2340,7 @@ impl EditorElement {
color: cx.theme().colors().editor_invisible,
background_color: None,
underline: None,
strikethrough: None,
}],
)
.unwrap();
@ -2828,6 +2874,7 @@ impl LineWithInvisibles {
color: text_style.color,
background_color: text_style.background_color,
underline: text_style.underline,
strikethrough: text_style.strikethrough,
});
if editor_mode == EditorMode::Full {
@ -3039,9 +3086,9 @@ impl Element for EditorElement {
let key_context = self.editor.read(cx).key_context(cx);
cx.with_key_dispatch(Some(key_context), Some(focus_handle.clone()), |_, cx| {
self.register_actions(cx);
self.register_key_listeners(cx);
cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
self.register_key_listeners(cx, text_bounds, &layout);
cx.handle_input(
&focus_handle,
ElementInputHandler::new(bounds, self.editor.clone()),
@ -3158,16 +3205,6 @@ pub struct PointForPosition {
}
impl PointForPosition {
#[cfg(test)]
pub fn valid(valid: DisplayPoint) -> Self {
Self {
previous_valid: valid,
next_valid: valid,
exact_unclipped: valid,
column_overshoot_after_line_end: 0,
}
}
pub fn as_valid(&self) -> Option<DisplayPoint> {
if self.previous_valid == self.exact_unclipped && self.next_valid == self.exact_unclipped {
Some(self.previous_valid)
@ -3251,6 +3288,7 @@ fn layout_line(
color: Hsla::default(),
background_color: None,
underline: None,
strikethrough: None,
}],
)
}

View File

@ -1,6 +1,6 @@
use crate::{
display_map::{InlayOffset, ToDisplayPoint},
link_go_to_definition::{InlayHighlight, RangeInEditor},
hover_links::{InlayHighlight, RangeInEditor},
Anchor, AnchorRangeExt, DisplayPoint, Editor, EditorSettings, EditorSnapshot, EditorStyle,
ExcerptId, Hover, RangeToAnchorExt,
};
@ -605,8 +605,8 @@ mod tests {
use crate::{
editor_tests::init_test,
element::PointForPosition,
hover_links::update_inlay_link_and_hover_points,
inlay_hint_cache::tests::{cached_hint_labels, visible_hint_labels},
link_go_to_definition::update_inlay_link_and_hover_points,
test::editor_lsp_test_context::EditorLspTestContext,
InlayId,
};

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
use crate::{
editor_settings::SeedQuerySetting, link_go_to_definition::hide_link_definition,
persistence::DB, scroll::ScrollAnchor, Anchor, Autoscroll, Editor, EditorEvent, EditorSettings,
ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, NavigationData, ToPoint as _,
editor_settings::SeedQuerySetting, persistence::DB, scroll::ScrollAnchor, Anchor, Autoscroll,
Editor, EditorEvent, EditorSettings, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot,
NavigationData, ToPoint as _,
};
use anyhow::{anyhow, Context as _, Result};
use collections::HashSet;
@ -682,8 +682,7 @@ impl Item for Editor {
}
fn workspace_deactivated(&mut self, cx: &mut ViewContext<Self>) {
hide_link_definition(self, cx);
self.link_go_to_definition_state.last_trigger_point = None;
self.hide_hovered_link(cx);
}
fn is_dirty(&self, cx: &AppContext) -> bool {
@ -801,7 +800,11 @@ impl Item for Editor {
}
fn breadcrumb_location(&self) -> ToolbarItemLocation {
ToolbarItemLocation::PrimaryLeft
if self.show_breadcrumbs {
ToolbarItemLocation::PrimaryLeft
} else {
ToolbarItemLocation::Hidden
}
}
fn breadcrumbs(&self, variant: &Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {

View File

@ -2,14 +2,12 @@
//! 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, CharKind, EditorStyle, ToOffset, ToPoint};
use gpui::{px, Pixels, TextSystem};
use crate::{char_kind, scroll::ScrollAnchor, CharKind, EditorStyle, ToOffset, ToPoint};
use gpui::{px, Pixels, WindowTextSystem};
use language::Point;
use std::{ops::Range, sync::Arc};
use multi_buffer::Anchor;
/// Defines search strategy for items in `movement` module.
/// `FindRange::SingeLine` only looks for a match on a single line at a time, whereas
/// `FindRange::MultiLine` keeps going until the end of a string.
@ -22,11 +20,12 @@ pub enum FindRange {
/// TextLayoutDetails encompasses everything we need to move vertically
/// taking into account variable width characters.
pub struct TextLayoutDetails {
pub(crate) text_system: Arc<TextSystem>,
pub(crate) text_system: Arc<WindowTextSystem>,
pub(crate) editor_style: EditorStyle,
pub(crate) rem_size: Pixels,
pub anchor: Anchor,
pub scroll_anchor: ScrollAnchor,
pub visible_rows: Option<f32>,
pub vertical_scroll_margin: f32,
}
/// Returns a column to the left of the current point, wrapping
@ -396,14 +395,17 @@ pub fn find_preceding_boundary(
/// Scans for a boundary following the given start point until a boundary is found, indicated by the
/// given predicate returning true. The predicate is called with the character to the left and right
/// of the candidate boundary location, and will be called with `\n` characters indicating the start
/// or end of a line.
pub fn find_boundary(
/// or end of a line. The function supports optionally returning the point just before the boundary
/// is found via return_point_before_boundary.
pub fn find_boundary_point(
map: &DisplaySnapshot,
from: DisplayPoint,
find_range: FindRange,
mut is_boundary: impl FnMut(char, char) -> bool,
return_point_before_boundary: bool,
) -> DisplayPoint {
let mut offset = from.to_offset(&map, Bias::Right);
let mut prev_offset = offset;
let mut prev_ch = None;
for ch in map.buffer_snapshot.chars_at(offset) {
@ -412,16 +414,38 @@ pub fn find_boundary(
}
if let Some(prev_ch) = prev_ch {
if is_boundary(prev_ch, ch) {
break;
if return_point_before_boundary {
return map.clip_point(prev_offset.to_display_point(map), Bias::Right);
} else {
break;
}
}
}
prev_offset = offset;
offset += ch.len_utf8();
prev_ch = Some(ch);
}
map.clip_point(offset.to_display_point(map), Bias::Right)
}
pub fn find_boundary(
map: &DisplaySnapshot,
from: DisplayPoint,
find_range: FindRange,
is_boundary: impl FnMut(char, char) -> bool,
) -> DisplayPoint {
return find_boundary_point(map, from, find_range, is_boundary, false);
}
pub fn find_boundary_exclusive(
map: &DisplaySnapshot,
from: DisplayPoint,
find_range: FindRange,
is_boundary: impl FnMut(char, char) -> bool,
) -> DisplayPoint {
return find_boundary_point(map, from, find_range, is_boundary, true);
}
/// Returns an iterator over the characters following a given offset in the [`DisplaySnapshot`].
/// The returned value also contains a range of the start/end of a returned character in
/// the [`DisplaySnapshot`]. The offsets are relative to the start of a buffer.
@ -764,7 +788,7 @@ mod tests {
&snapshot,
display_points[0],
FindRange::MultiLine,
is_boundary
is_boundary,
),
display_points[1]
);

View File

@ -6,13 +6,14 @@ use crate::{
display_map::{DisplaySnapshot, ToDisplayPoint},
hover_popover::hide_hover,
persistence::DB,
Anchor, DisplayPoint, Editor, EditorEvent, EditorMode, InlayHintRefreshReason,
Anchor, DisplayPoint, Editor, EditorEvent, EditorMode, EditorSettings, InlayHintRefreshReason,
MultiBufferSnapshot, ToPoint,
};
pub use autoscroll::{Autoscroll, AutoscrollStrategy};
use gpui::{point, px, AppContext, Entity, Global, Pixels, Task, ViewContext};
use gpui::{point, px, AppContext, Entity, Global, Pixels, Task, ViewContext, WindowContext};
use language::{Bias, Point};
pub use scroll_amount::ScrollAmount;
use settings::Settings;
use std::{
cmp::Ordering,
time::{Duration, Instant},
@ -21,7 +22,6 @@ use util::ResultExt;
use workspace::{ItemId, WorkspaceId};
pub const SCROLL_EVENT_SEPARATION: Duration = Duration::from_millis(28);
pub const VERTICAL_SCROLL_MARGIN: f32 = 3.;
const SCROLLBAR_SHOW_INTERVAL: Duration = Duration::from_secs(1);
#[derive(Default)]
@ -128,7 +128,7 @@ impl OngoingScroll {
}
pub struct ScrollManager {
vertical_scroll_margin: f32,
pub(crate) vertical_scroll_margin: f32,
anchor: ScrollAnchor,
ongoing: OngoingScroll,
autoscroll_request: Option<(Autoscroll, bool)>,
@ -140,9 +140,9 @@ pub struct ScrollManager {
}
impl ScrollManager {
pub fn new() -> Self {
pub fn new(cx: &mut WindowContext) -> Self {
ScrollManager {
vertical_scroll_margin: VERTICAL_SCROLL_MARGIN,
vertical_scroll_margin: EditorSettings::get_global(cx).vertical_scroll_margin,
anchor: ScrollAnchor::new(),
ongoing: OngoingScroll::new(),
autoscroll_request: None,

View File

@ -4,7 +4,8 @@ use crate::{
use collections::BTreeMap;
use futures::Future;
use gpui::{
AnyWindowHandle, AppContext, Keystroke, ModelContext, View, ViewContext, VisualTestContext,
AnyWindowHandle, AppContext, Keystroke, ModelContext, Pixels, Point, View, ViewContext,
VisualTestContext,
};
use indoc::indoc;
use itertools::Itertools;
@ -187,6 +188,31 @@ impl EditorTestContext {
ranges[0].start.to_display_point(&snapshot)
}
pub fn pixel_position(&mut self, marked_text: &str) -> Point<Pixels> {
let display_point = self.display_point(marked_text);
self.pixel_position_for(display_point)
}
pub fn pixel_position_for(&mut self, display_point: DisplayPoint) -> Point<Pixels> {
self.update_editor(|editor, cx| {
let newest_point = editor.selections.newest_display(cx).head();
let pixel_position = editor.pixel_position_of_newest_cursor.unwrap();
let line_height = editor
.style()
.unwrap()
.text
.line_height_in_pixels(cx.rem_size());
let snapshot = editor.snapshot(cx);
let details = editor.text_layout_details(cx);
let y = pixel_position.y
+ 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)
})
}
// Returns anchors for the current buffer using `«` and `»`
pub fn text_anchor_range(&mut self, marked_text: &str) -> Range<language::Anchor> {
let ranges = self.ranges(marked_text);
@ -343,7 +369,7 @@ impl EditorTestContext {
}
impl Deref for EditorTestContext {
type Target = gpui::TestAppContext;
type Target = gpui::VisualTestContext;
fn deref(&self) -> &Self::Target {
&self.cx

View File

@ -10,4 +10,4 @@ path = "src/feature_flags.rs"
[dependencies]
anyhow.workspace = true
gpui = { path = "../gpui" }
gpui.workspace = true

View File

@ -14,34 +14,34 @@ test-support = []
[dependencies]
anyhow.workspace = true
bitflags = "2.4.1"
client = { path = "../client" }
db = { path = "../db" }
editor = { path = "../editor" }
client.workspace = true
db.workspace = true
editor.workspace = true
futures.workspace = true
gpui = { path = "../gpui" }
gpui.workspace = true
human_bytes = "0.4.1"
isahc.workspace = true
language = { path = "../language" }
language.workspace = true
lazy_static.workspace = true
log.workspace = true
menu = { path = "../menu" }
menu.workspace = true
postage.workspace = true
project = { path = "../project" }
project.workspace = true
regex.workspace = true
release_channel = { path = "../release_channel" }
release_channel.workspace = true
serde.workspace = true
serde_derive.workspace = true
serde_json.workspace = true
settings = { path = "../settings" }
settings.workspace = true
smallvec.workspace = true
smol.workspace = true
sysinfo.workspace = true
theme = { path = "../theme" }
theme.workspace = true
tree-sitter-markdown = { git = "https://github.com/MDeiml/tree-sitter-markdown", rev = "330ecab87a3e3a7211ac69bbadc19eabecdb1cca" }
ui = { path = "../ui" }
ui.workspace = true
urlencoding = "2.1.2"
util = { path = "../util" }
workspace = { path = "../workspace" }
util.workspace = true
workspace.workspace = true
[dev-dependencies]
editor = { path = "../editor", features = ["test-support"] }
editor = { workspace = true, features = ["test-support"] }

View File

@ -11,29 +11,29 @@ doctest = false
[dependencies]
anyhow.workspace = true
collections = { path = "../collections" }
editor = { path = "../editor" }
fuzzy = { path = "../fuzzy" }
gpui = { path = "../gpui" }
collections.workspace = true
editor.workspace = true
fuzzy.workspace = true
gpui.workspace = true
itertools = "0.11"
menu = { path = "../menu" }
picker = { path = "../picker" }
menu.workspace = true
picker.workspace = true
postage.workspace = true
project = { path = "../project" }
project.workspace = true
serde.workspace = true
settings = { path = "../settings" }
text = { path = "../text" }
theme = { path = "../theme" }
ui = { path = "../ui" }
util = { path = "../util" }
workspace = { path = "../workspace" }
settings.workspace = true
text.workspace = true
theme.workspace = true
ui.workspace = true
util.workspace = true
workspace.workspace = true
[dev-dependencies]
ctor.workspace = true
editor = { path = "../editor", features = ["test-support"] }
editor = { workspace = true, features = ["test-support"] }
env_logger.workspace = true
gpui = { path = "../gpui", features = ["test-support"] }
language = { path = "../language", features = ["test-support"] }
gpui = { workspace = true, features = ["test-support"] }
language = { workspace = true, features = ["test-support"] }
serde_json.workspace = true
theme = { path = "../theme", features = ["test-support"] }
workspace = { path = "../workspace", features = ["test-support"] }
theme = { workspace = true, features = ["test-support"] }
workspace = { workspace = true, features = ["test-support"] }

View File

@ -566,7 +566,7 @@ impl FileFinderDelegate {
let path = &path_match.path;
let path_string = path.to_string_lossy();
let full_path = [path_match.path_prefix.as_ref(), path_string.as_ref()].join("");
let path_positions = path_match.positions.clone();
let mut path_positions = path_match.positions.clone();
let file_name = path.file_name().map_or_else(
|| path_match.path_prefix.to_string(),
@ -584,6 +584,9 @@ impl FileFinderDelegate {
})
.collect();
let full_path = full_path.trim_end_matches(&file_name).to_string();
path_positions.retain(|idx| *idx < full_path.len());
(file_name, file_name_positions, full_path, path_positions)
}
@ -868,9 +871,14 @@ impl PickerDelegate for FileFinderDelegate {
.inset(true)
.selected(selected)
.child(
v_flex()
h_flex()
.gap_2()
.child(HighlightedLabel::new(file_name, file_name_positions))
.child(HighlightedLabel::new(full_path, full_path_positions)),
.child(
HighlightedLabel::new(full_path, full_path_positions)
.size(LabelSize::Small)
.color(Color::Muted),
),
),
)
}

View File

@ -490,8 +490,8 @@ async fn test_single_file_worktrees(cx: &mut TestAppContext) {
delegate.labels_for_path_match(&matches[0].0);
assert_eq!(file_name, "the-file");
assert_eq!(file_name_positions, &[0, 1, 4]);
assert_eq!(full_path, "the-file");
assert_eq!(full_path_positions, &[0, 1, 4]);
assert_eq!(full_path, "");
assert_eq!(full_path_positions, &[0; 0]);
});
// Since the worktree root is a file, searching for its name followed by a slash does

View File

@ -9,11 +9,11 @@ license = "GPL-3.0-or-later"
path = "src/fs.rs"
[dependencies]
collections = { path = "../collections" }
rope = { path = "../rope" }
text = { path = "../text" }
util = { path = "../util" }
sum_tree = { path = "../sum_tree" }
collections.workspace = true
rope.workspace = true
text.workspace = true
util.workspace = true
sum_tree.workspace = true
anyhow.workspace = true
async-trait.workspace = true
@ -31,16 +31,16 @@ log.workspace = true
libc = "0.2"
time.workspace = true
gpui = { path = "../gpui", optional = true}
gpui = { workspace = true, optional = true}
[target.'cfg(target_os = "macos")'.dependencies]
fsevent = { path = "../fsevent" }
fsevent.workspace = true
[target.'cfg(not(target_os = "macos"))'.dependencies]
notify = "6.1.1"
[dev-dependencies]
gpui = { path = "../gpui", features = ["test-support"] }
gpui = { workspace = true, features = ["test-support"] }
[features]
test-support = ["gpui/test-support"]

View File

@ -5,14 +5,12 @@ use parking_lot::Mutex;
use serde_derive::{Deserialize, Serialize};
use std::{
cmp::Ordering,
ffi::OsStr,
os::unix::prelude::OsStrExt,
path::{Component, Path, PathBuf},
sync::Arc,
time::SystemTime,
};
use sum_tree::{MapSeekTarget, TreeMap};
use util::ResultExt;
use util::{paths::PathExt, ResultExt};
pub use git2::Repository as LibGitRepository;
@ -119,7 +117,7 @@ impl GitRepository for LibGitRepository {
if let Some(statuses) = self.statuses(Some(&mut options)).log_err() {
for status in statuses.iter() {
let path = RepoPath(PathBuf::from(OsStr::from_bytes(status.path_bytes())));
let path = RepoPath(PathBuf::try_from_bytes(status.path_bytes()).unwrap());
let status = status.status();
if !status.contains(git2::Status::IGNORED) {
if let Some(status) = read_status(status) {

View File

@ -10,5 +10,5 @@ path = "src/fuzzy.rs"
doctest = false
[dependencies]
gpui = { path = "../gpui" }
util = { path = "../util" }
gpui.workspace = true
util.workspace = true

View File

@ -11,17 +11,17 @@ path = "src/git.rs"
[dependencies]
anyhow.workspace = true
async-trait.workspace = true
clock = { path = "../clock" }
collections = { path = "../collections" }
clock.workspace = true
collections.workspace = true
futures.workspace = true
git2.workspace = true
lazy_static.workspace = true
log.workspace = true
parking_lot.workspace = true
smol.workspace = true
sum_tree = { path = "../sum_tree" }
text = { path = "../text" }
util = { path = "../util" }
sum_tree.workspace = true
text.workspace = true
util.workspace = true
[dev-dependencies]
unindent.workspace = true

View File

@ -10,17 +10,17 @@ path = "src/go_to_line.rs"
doctest = false
[dependencies]
editor = { path = "../editor" }
gpui = { path = "../gpui" }
menu = { path = "../menu" }
editor.workspace = true
gpui.workspace = true
menu.workspace = true
postage.workspace = true
serde.workspace = true
settings = { path = "../settings" }
text = { path = "../text" }
theme = { path = "../theme" }
ui = { path = "../ui" }
util = { path = "../util" }
workspace = { path = "../workspace" }
settings.workspace = true
text.workspace = true
theme.workspace = true
ui.workspace = true
util.workspace = true
workspace.workspace = true
[dev-dependencies]
editor = { path = "../editor", features = ["test-support"] }
editor = { workspace = true, features = ["test-support"] }

View File

@ -26,15 +26,15 @@ anyhow.workspace = true
async-task = "4.7"
backtrace = { version = "0.3", optional = true }
bitflags = "2.4.0"
collections = { path = "../collections" }
collections.workspace = true
ctor.workspace = true
derive_more.workspace = true
dhat = { version = "0.3", optional = true }
env_logger = { version = "0.9", optional = true }
etagere = "0.2"
futures.workspace = true
gpui_macros = { path = "../gpui_macros" }
font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "d97147f" }
gpui_macros.workspace = true
image = "0.23"
itertools = "0.10"
lazy_static.workspace = true
@ -59,24 +59,24 @@ serde_json.workspace = true
slotmap = "1.0.6"
smallvec.workspace = true
smol.workspace = true
sum_tree = { path = "../sum_tree" }
sum_tree.workspace = true
taffy = { git = "https://github.com/DioxusLabs/taffy", rev = "1876f72bee5e376023eaa518aa7b8a34c769bd1b" }
thiserror.workspace = true
time.workspace = true
tiny-skia = "0.5"
usvg = { version = "0.14", features = [] }
util = { path = "../util" }
util.workspace = true
uuid = { version = "1.1.2", features = ["v4"] }
waker-fn = "1.1.0"
[dev-dependencies]
backtrace = "0.3"
collections = { path = "../collections", features = ["test-support"] }
collections = { workspace = true, features = ["test-support"] }
dhat = "0.3"
env_logger.workspace = true
png = "0.16"
simplelog = "0.9"
util = { path = "../util", features = ["test-support"] }
util = { workspace = true, features = ["test-support"] }
[build-dependencies]
bindgen = "0.65.1"
@ -90,7 +90,7 @@ core-graphics = "0.22.3"
core-text = "19.2"
foreign-types = "0.3"
log.workspace = true
media = { path = "../media" }
media.workspace = true
metal = "0.21.0"
objc = "0.2"

View File

@ -28,6 +28,7 @@ fn generate_dispatch_bindings() {
.header("src/platform/mac/dispatch.h")
.allowlist_var("_dispatch_main_q")
.allowlist_var("DISPATCH_QUEUE_PRIORITY_DEFAULT")
.allowlist_var("DISPATCH_QUEUE_PRIORITY_HIGH")
.allowlist_var("DISPATCH_TIME_NOW")
.allowlist_function("dispatch_get_global_queue")
.allowlist_function("dispatch_async_f")

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