Compare commits

..

1 Commits

Author SHA1 Message Date
Nathan Sobo
809776f6bc WIP 2024-02-02 14:46:44 -07:00
432 changed files with 6867 additions and 19656 deletions

View File

@@ -2,14 +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: Find modified migrations
shell: bash -euxo pipefail {0}
run: |
export SQUAWK_GITHUB_TOKEN=${{ github.token }}
. ./script/squawk
- 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
- 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@v4
uses: actions/setup-node@v3
with:
node-version: "18"

View File

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

View File

@@ -1,206 +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 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
- name: Ensure fresh merge
shell: bash -euxo pipefail {0}
run: |
if [ -z "$GITHUB_BASE_REF" ];
then
echo "BUF_BASE_BRANCH=$(git merge-base origin/main HEAD)" >> $GITHUB_ENV
else
git checkout -B temp
git merge -q origin/$GITHUB_BASE_REF -m "merge main into temp"
echo "BUF_BASE_BRANCH=$GITHUB_BASE_REF" >> $GITHUB_ENV
fi
tests:
name: Run tests
runs-on:
- self-hosted
- test
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
- 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=${BUF_BASE_BRANCH},subdir=crates/rpc/proto/"
- name: Run tests
uses: ./.github/actions/run_tests
macos_tests:
name: (macOS) Run Clippy and tests
runs-on:
- self-hosted
- test
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
- name: Build collab
run: cargo build -p collab
- name: cargo clippy
shell: bash -euxo pipefail {0}
run: script/clippy
- name: Build other binaries
run: cargo build --workspace --bins --all-features
- name: Run tests
uses: ./.github/actions/run_tests
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: Build collab
run: cargo build -p collab
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
- name: Build other binaries
run: cargo build --workspace --bins --all-features
- name: Limit target directory size
run: script/clear-target-dir-if-larger-than 100
# todo!(linux): Actually run the tests
linux_tests:
name: (Linux) Run Clippy and tests
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
- name: Determine version and release channel
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
run: |
set -eu
- name: Restore from cache
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/rust-toolchain.toml') }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-${{ hashFiles('**/rust-toolchain.toml') }}-
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
- name: configure linux
shell: bash -euxo pipefail {0}
run: script/linux
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: cargo clippy
shell: bash -euxo pipefail {0}
run: script/clippy
- name: Generate license file
run: script/generate-licenses
- name: Build Zed
run: cargo build -p zed
bundle:
name: Bundle macOS app
runs-on:
- self-hosted
- bundle
if: ${{ startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-build-dmg') }}
needs: [macos_tests]
- 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:
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 }}
DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}
DIGITALOCEAN_SPACES_SECRET_KEY: ${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }}
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 }}
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@v3
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

@@ -27,10 +27,6 @@ jobs:
- name: Run style checks
uses: ./.github/actions/check_style
- name: Run clippy
shell: bash -euxo pipefail {0}
run: script/clippy
tests:
name: Run tests
runs-on:

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@v4
with:
node-version: "18"
tests:
name: Run randomized tests
runs-on:
- self-hosted
- randomized-tests
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: 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,7 +16,6 @@ env:
jobs:
style:
name: Check formatting and Clippy lints
if: github.repository_owner == 'zed-industries'
runs-on:
- self-hosted
- test
@@ -31,12 +30,8 @@ jobs:
- name: Run style checks
uses: ./.github/actions/check_style
- name: Run clippy
shell: bash -euxo pipefail {0}
run: script/clippy
tests:
name: Run tests
if: github.repository_owner == 'zed-industries'
runs-on:
- self-hosted
- test
@@ -53,7 +48,6 @@ jobs:
bundle:
name: Bundle app
if: github.repository_owner == 'zed-industries'
runs-on:
- self-hosted
- bundle
@@ -65,10 +59,9 @@ jobs:
APPLE_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_NOTARIZATION_PASSWORD }}
DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}
DIGITALOCEAN_SPACES_SECRET_KEY: ${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }}
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
steps:
- name: Install Node
uses: actions/setup-node@v4
uses: actions/setup-node@v3
with:
node-version: "18"

View File

@@ -6,7 +6,6 @@ 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,7 +6,6 @@ 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

@@ -13,8 +13,6 @@ Antonio Scandurra <me@as-cii.com>
Antonio Scandurra <me@as-cii.com> <antonio@zed.dev>
Conrad Irwin <conrad@zed.dev>
Conrad Irwin <conrad@zed.dev> <conrad.irwin@gmail.com>
Greg Morenz <greg-morenz@droid.cafe>
Greg Morenz <greg-morenz@droid.cafe> <morenzg@gmail.com>
Joseph T. Lyons <JosephTLyons@gmail.com>
Joseph T. Lyons <JosephTLyons@gmail.com> <JosephTLyons@users.noreply.github.com>
Julia <floc@unpromptedtirade.com>
@@ -41,8 +39,6 @@ Nathan Sobo <nathan@zed.dev> <nathan@warp.dev>
Nathan Sobo <nathan@zed.dev> <nathansobo@gmail.com>
Piotr Osiewicz <piotr@zed.dev>
Piotr Osiewicz <piotr@zed.dev> <24362066+osiewicz@users.noreply.github.com>
Robert Clover <git@clo4.net>
Robert Clover <git@clo4.net> <robert@clover.gdn>
Thorsten Ball <thorsten@zed.dev>
Thorsten Ball <thorsten@zed.dev> <me@thorstenball.com>
Thorsten Ball <thorsten@zed.dev> <mrnugget@gmail.com>

707
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -21,8 +21,6 @@ members = [
"crates/db",
"crates/diagnostics",
"crates/editor",
"crates/extension",
"crates/extensions_ui",
"crates/feature_flags",
"crates/feedback",
"crates/file_finder",
@@ -32,9 +30,12 @@ members = [
"crates/git",
"crates/go_to_line",
"crates/gpui",
"crates/gpui",
"crates/gpui_macros",
"crates/gpui_macros",
"crates/install_cli",
"crates/journal",
"crates/journal",
"crates/language",
"crates/language_selector",
"crates/language_tools",
@@ -91,109 +92,19 @@ 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" }
extension = { path = "crates/extension" }
extensions_ui = { path = "crates/extensions_ui" }
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-tar = "0.4.2"
async-trait = "0.1"
chrono = { version = "0.4", features = ["serde"] }
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"
@@ -211,10 +122,7 @@ 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"
@@ -226,13 +134,10 @@ tiktoken-rs = "0.5.7"
time = { version = "0.3", features = ["serde", "serde-well-known"] }
toml = "0.5"
tree-sitter = { version = "0.20", features = ["wasm"] }
tree-sitter-astro = { git = "https://github.com/virchau13/tree-sitter-astro.git", rev = "e924787e12e8a03194f36a113290ac11d6dc10f3" }
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-clojure = { git = "https://github.com/prcastro/tree-sitter-clojure", branch = "update-ts" }
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" }
@@ -245,7 +150,6 @@ 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" }
@@ -253,10 +157,8 @@ 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-prisma-io = { git = "https://github.com/victorhqc/tree-sitter-prisma" }
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" }
@@ -278,18 +180,11 @@ wasmtime = "16"
[patch.crates-io]
tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "1d8975319c2d5de1bf710e7e21db25b0eee4bc66" }
wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", rev = "v16.0.0" }
# Workaround for a broken nightly build of gpui: See #7644 and revisit once 0.5.3 is released.
pathfinder_simd = { git = "https://github.com/servo/pathfinder.git", rev = "e4fcda0d5259d0acf902aee6de7d2501f2bd6629" }
[profile.dev]
split-debuginfo = "unpacked"
debug = "limited"
# todo!(linux) - Remove this
[profile.dev.package.blade-graphics]
split-debuginfo = "off"
debug = "full"
[profile.dev.package.taffy]
opt-level = 3

View File

@@ -1,6 +1,6 @@
# syntax = docker/dockerfile:1.2
FROM rust:1.76-bullseye as builder
FROM rust:1.75-bullseye as builder
WORKDIR app
COPY . .

View File

@@ -1,4 +0,0 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.32668 11.5331C4.78215 10.9852 4.62318 9.834 4.85006 9C5.24343 9.52582 5.78848 9.69239 6.35304 9.78642C7.2246 9.93151 8.08055 9.87724 8.89019 9.43877C8.98281 9.38857 9.06841 9.32182 9.16962 9.25421C9.24559 9.49681 9.26536 9.74172 9.23882 9.99099C9.1743 10.5981 8.89982 11.067 8.46326 11.4225C8.28869 11.5647 8.10397 11.6918 7.92367 11.8259C7.36978 12.2379 7.21992 12.7211 7.42805 13.4239C7.433 13.4411 7.43742 13.4582 7.44861 13.5C7.1658 13.3606 6.95923 13.1578 6.80182 12.8911C6.63558 12.6097 6.55649 12.2983 6.55233 11.9614C6.55025 11.7974 6.55025 11.632 6.53022 11.4704C6.4813 11.0763 6.31323 10.8999 5.99661 10.8897C5.67166 10.8793 5.41462 11.1004 5.34646 11.4486C5.34125 11.4753 5.33371 11.5017 5.32616 11.5328L5.32668 11.5331Z" fill="#17191E" fill-opacity="0.6"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.30826 8.62679L6.73279 4.5906C6.82176 4.33854 7.17823 4.33854 7.26719 4.5906L8.69173 8.62679C8.69652 8.64035 8.70626 8.65153 8.7189 8.65814C8.80645 8.6531 8.89466 8.65055 8.98347 8.65055C10.1418 8.65055 11.1986 9.08493 12 9.79967L8.83801 1.36772C8.75507 1.14653 8.54362 1 8.30739 1H5.6926C5.45637 1 5.24492 1.14653 5.16198 1.36772L1.99999 9.79968C2.80137 9.08493 3.85822 8.65055 5.01652 8.65055C5.10533 8.65055 5.19355 8.6531 5.28109 8.65814C5.29373 8.65153 5.30347 8.64035 5.30826 8.62679Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -1,9 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -1,11 +1,9 @@
{
"suffixes": {
"astro": "astro",
"Emakefile": "erlang",
"aac": "audio",
"accdb": "storage",
"app.src": "erlang",
"avi": "video",
"avif": "image",
"bak": "backup",
"bash": "terminal",
@@ -27,7 +25,6 @@
"doc": "document",
"docx": "document",
"eex": "elixir",
"elm": "elm",
"erl": "erlang",
"escript": "erlang",
"eslintrc": "eslint",
@@ -52,7 +49,6 @@
"hbs": "template",
"heex": "elixir",
"heif": "image",
"heic": "image",
"hrl": "erlang",
"hs": "haskell",
"htm": "template",
@@ -60,30 +56,19 @@
"ib": "storage",
"ico": "image",
"ini": "settings",
"j2k": "image",
"java": "code",
"jfif": "image",
"jp2": "image",
"jpeg": "image",
"jpg": "image",
"js": "code",
"json": "storage",
"jsonc": "storage",
"jxl": "image",
"ldf": "storage",
"lock": "lock",
"log": "log",
"m4a": "audio",
"m4v": "video",
"md": "document",
"mdb": "storage",
"mdf": "storage",
"mdx": "document",
"mkv": "video",
"mka": "audio",
"ml": "ocaml",
"mli": "ocaml",
"mov": "video",
"mp3": "audio",
"mp4": "video",
"myd": "storage",
@@ -91,8 +76,7 @@
"odp": "document",
"ods": "document",
"odt": "document",
"ogg": "audio",
"opus": "audio",
"ogg": "video",
"pdb": "storage",
"pdf": "document",
"php": "php",
@@ -101,12 +85,10 @@
"pptx": "document",
"prettierignore": "prettier",
"prettierrc": "prettier",
"prisma": "prisma",
"profile": "terminal",
"ps1": "terminal",
"psd": "image",
"py": "python",
"qoi": "image",
"rb": "ruby",
"rebar.config": "erlang",
"rkt": "code",
@@ -130,15 +112,12 @@
"wav": "audio",
"webm": "video",
"webp": "image",
"wma": "audio",
"wmv": "video",
"wv": "audio",
"xls": "document",
"xlsx": "document",
"xml": "template",
"xrl": "erlang",
"yaml": "settings",
"yml": "settings",
"yaml": "yaml",
"yml": "yaml",
"yrl": "erlang",
"zlogin": "terminal",
"zsh": "terminal",
@@ -149,9 +128,6 @@
"zshrc": "terminal"
},
"types": {
"astro": {
"icon": "icons/file_icons/astro.svg"
},
"audio": {
"icon": "icons/file_icons/audio.svg"
},
@@ -176,9 +152,6 @@
"elixir": {
"icon": "icons/file_icons/elixir.svg"
},
"elm": {
"icon": "icons/file_icons/elm.svg"
},
"erlang": {
"icon": "icons/file_icons/erlang.svg"
},
@@ -206,21 +179,18 @@
"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

@@ -1,5 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -1,6 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 680 B

View File

@@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 209 B

View File

@@ -415,15 +415,7 @@
"cmd-?": "assistant::ToggleFocus",
"cmd-alt-s": "workspace::SaveAll",
"cmd-k m": "language_selector::Toggle",
"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"]
"escape": "workspace::Unfollow"
}
},
// Bindings from Sublime Text
@@ -449,6 +441,18 @@
"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",
@@ -565,12 +569,6 @@
"tab": "channel_modal::ToggleMode"
}
},
{
"context": "ChatPanel > MessageEditor",
"bindings": {
"escape": "chat_panel::CloseReplyPreview"
}
},
{
"context": "Terminal",
"bindings": {

View File

@@ -42,7 +42,6 @@
"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",
@@ -81,18 +80,10 @@
"cmd-6": "diagnostics::Deploy"
}
},
{
"context": "Pane",
"bindings": {
"cmd-alt-left": "pane::GoBack",
"cmd-alt-right": "pane::GoForward"
}
},
{
"context": "ProjectPanel",
"bindings": {
"enter": "project_panel::Open",
"shift-f6": "project_panel::Rename"
"enter": "project_panel::Open"
}
}
]

View File

@@ -284,8 +284,7 @@
"ctrl-w o": "workspace::CloseInactiveTabsAndPanes",
"ctrl-w ctrl-o": "workspace::CloseInactiveTabsAndPanes",
"ctrl-w n": ["workspace::NewFileInDirection", "Up"],
"ctrl-w ctrl-n": ["workspace::NewFileInDirection", "Up"],
"-": "pane::RevealInProjectPanel"
"ctrl-w ctrl-n": ["workspace::NewFileInDirection", "Up"]
}
},
{
@@ -485,9 +484,7 @@
"ctrl-x ctrl-a": "assistant::InlineAssist", // zed specific
"ctrl-x ctrl-c": "copilot::Suggest", // zed specific
"ctrl-x ctrl-l": "editor::ToggleCodeActions", // zed specific
"ctrl-x ctrl-z": "editor::Cancel",
"ctrl-w": "editor::DeleteToPreviousWordStart",
"ctrl-u": "editor::DeleteToBeginningOfLine"
"ctrl-x ctrl-z": "editor::Cancel"
}
},
{
@@ -500,10 +497,23 @@
}
},
{
"context": "BufferSearchBar && !in_replace",
"context": "BufferSearchBar && !in_replace > VimEnabled",
"bindings": {
"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,17 +104,8 @@
"show_whitespaces": "selection",
// Settings related to calls in Zed
"calls": {
// 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
// Join calls with the microphone muted by default
"mute_on_join": false
},
// Scrollbar related settings
"scrollbar": {
@@ -136,12 +127,8 @@
// Whether to show selections in the scrollbar.
"selections": true,
// Whether to show symbols selections in the scrollbar.
"symbols_selections": true,
// Whether to show diagnostic indicators in the scrollbar.
"diagnostics": true
"symbols_selections": 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:
@@ -214,8 +201,6 @@
"default_width": 640,
// Default height when the assistant is docked to the bottom.
"default_height": 320,
// The default OpenAI API endpoint to use when starting new conversations.
"openai_api_url": "https://api.openai.com/v1",
// The default OpenAI model to use when starting new conversations. This
// setting can take three values:
//
@@ -447,7 +432,7 @@
}
// Set the terminal's font size. If this option is not included,
// the terminal will default to matching the buffer's font size.
// "font_size": 15,
// "font_size": "15",
// Set the terminal's font family. If this option is not included,
// the terminal will default to matching the buffer's font family.
// "font_family": "Zed Mono",
@@ -500,9 +485,6 @@
"JavaScript": {
"tab_size": 2
},
"Terraform": {
"tab_size": 2
},
"TypeScript": {
"tab_size": 2
},
@@ -514,12 +496,6 @@
},
"JSON": {
"tab_size": 2
},
"OCaml": {
"tab_size": 2
},
"OCaml Interface": {
"tab_size": 2
}
},
// Zed's Prettier integration settings.
@@ -562,7 +538,7 @@
"stable": {
// "theme": "Andromeda"
},
// Settings overrides to use when using Zed Dev.
// Settings overrides to use when using Zed Stable.
// Mostly useful for developers who are managing multiple instances of Zed.
"dev": {
// "theme": "Andromeda"

View File

@@ -42,7 +42,7 @@
"tab_bar.background": "#21242bff",
"tab.inactive_background": "#21242bff",
"tab.active_background": "#1e2025ff",
"search.match_background": "#11a79366",
"search.match_background": null,
"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": "#576dda66",
"search.match_background": null,
"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": "#586dda66",
"search.match_background": null,
"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": "#6684e066",
"search.match_background": null,
"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": "#6784e066",
"search.match_background": null,
"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": "#37a16666",
"search.match_background": null,
"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": "#38a16666",
"search.match_background": null,
"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": "#417ee666",
"search.match_background": null,
"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": "#417ee666",
"search.match_background": null,
"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": "#526aeb66",
"search.match_background": null,
"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": "#526aeb66",
"search.match_background": null,
"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": "#277fad66",
"search.match_background": null,
"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": "#277fad66",
"search.match_background": null,
"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": "#7272ca66",
"search.match_background": null,
"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": "#7372ca66",
"search.match_background": null,
"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": "#478c9066",
"search.match_background": null,
"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": "#488c9066",
"search.match_background": null,
"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": "#3e62f466",
"search.match_background": null,
"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": "#3f62f466",
"search.match_background": null,
"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": "#3e8fd066",
"search.match_background": null,
"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": "#3f8fd066",
"search.match_background": null,
"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": "#5ac2fe66",
"search.match_background": null,
"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": "#3b9ee566",
"search.match_background": null,
"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": "#73cffe66",
"search.match_background": null,
"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": "#83a59866",
"search.match_background": null,
"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": "#83a59866",
"search.match_background": null,
"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": "#83a59866",
"search.match_background": null,
"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": "#0b667866",
"search.match_background": null,
"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": "#0b667866",
"search.match_background": null,
"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": "#0b667866",
"search.match_background": null,
"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": "#74ade866",
"search.match_background": null,
"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": "#5c79e266",
"search.match_background": null,
"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": "#57949f66",
"search.match_background": null,
"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": "#9cced766",
"search.match_background": null,
"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": "#9cced766",
"search.match_background": null,
"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": "#528b8b66",
"search.match_background": null,
"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": "#288bd166",
"search.match_background": null,
"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": "#298bd166",
"search.match_background": null,
"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": "#499bef66",
"search.match_background": null,
"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.workspace = true
editor.workspace = true
auto_update = { path = "../auto_update" }
editor = { path = "../editor" }
futures.workspace = true
gpui.workspace = true
language.workspace = true
project.workspace = true
settings.workspace = true
gpui = { path = "../gpui" }
language = { path = "../language" }
project = { path = "../project" }
settings = { path = "../settings" }
smallvec.workspace = true
theme.workspace = true
ui.workspace = true
util.workspace = true
workspace.workspace = true
theme = { path = "../theme" }
ui = { path = "../ui" }
util = { path = "../util" }
workspace = { path = "../workspace", package = "workspace" }
[dev-dependencies]
editor = { workspace = true, features = ["test-support"] }
editor = { path = "../editor", features = ["test-support"] }

View File

@@ -17,9 +17,9 @@ anyhow.workspace = true
async-trait.workspace = true
bincode = "1.3.3"
futures.workspace = true
gpui.workspace = true
gpui = { path = "../gpui" }
isahc.workspace = true
language.workspace = true
language = { path = "../language" }
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.workspace = true
util = { path = "../util" }
[dev-dependencies]
gpui = { workspace = true, features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }

View File

@@ -103,7 +103,6 @@ pub struct OpenAiResponseStreamEvent {
}
pub async fn stream_completion(
api_url: String,
credential: ProviderCredential,
executor: BackgroundExecutor,
request: Box<dyn CompletionRequest>,
@@ -118,7 +117,7 @@ pub async fn stream_completion(
let (tx, rx) = futures::channel::mpsc::unbounded::<Result<OpenAiResponseStreamEvent>>();
let json_data = request.data()?;
let mut response = Request::post(format!("{api_url}/chat/completions"))
let mut response = Request::post(format!("{OPEN_AI_API_URL}/chat/completions"))
.header("Content-Type", "application/json")
.header("Authorization", format!("Bearer {}", api_key))
.body(json_data)?
@@ -196,20 +195,18 @@ pub async fn stream_completion(
#[derive(Clone)]
pub struct OpenAiCompletionProvider {
api_url: String,
model: OpenAiLanguageModel,
credential: Arc<RwLock<ProviderCredential>>,
executor: BackgroundExecutor,
}
impl OpenAiCompletionProvider {
pub async fn new(api_url: String, model_name: String, executor: BackgroundExecutor) -> Self {
pub async fn new(model_name: String, executor: BackgroundExecutor) -> Self {
let model = executor
.spawn(async move { OpenAiLanguageModel::load(&model_name) })
.await;
let credential = Arc::new(RwLock::new(ProviderCredential::NoCredentials));
Self {
api_url,
model,
credential,
executor,
@@ -306,8 +303,7 @@ impl CompletionProvider for OpenAiCompletionProvider {
// which is currently model based, due to the language model.
// At some point in the future we should rectify this.
let credential = self.credential.read().clone();
let api_url = self.api_url.clone();
let request = stream_completion(api_url, credential, self.executor.clone(), prompt);
let request = stream_completion(credential, self.executor.clone(), prompt);
async move {
let response = request.await?;
let stream = response

View File

@@ -35,7 +35,6 @@ lazy_static! {
#[derive(Clone)]
pub struct OpenAiEmbeddingProvider {
api_url: String,
model: OpenAiLanguageModel,
credential: Arc<RwLock<ProviderCredential>>,
pub client: Arc<dyn HttpClient>,
@@ -70,11 +69,7 @@ struct OpenAiEmbeddingUsage {
}
impl OpenAiEmbeddingProvider {
pub async fn new(
api_url: String,
client: Arc<dyn HttpClient>,
executor: BackgroundExecutor,
) -> Self {
pub async fn new(client: Arc<dyn HttpClient>, executor: BackgroundExecutor) -> Self {
let (rate_limit_count_tx, rate_limit_count_rx) = watch::channel_with(None);
let rate_limit_count_tx = Arc::new(Mutex::new(rate_limit_count_tx));
@@ -85,7 +80,6 @@ impl OpenAiEmbeddingProvider {
let credential = Arc::new(RwLock::new(ProviderCredential::NoCredentials));
OpenAiEmbeddingProvider {
api_url,
model,
credential,
client,
@@ -136,12 +130,11 @@ impl OpenAiEmbeddingProvider {
}
async fn send_request(
&self,
api_url: &str,
api_key: &str,
spans: Vec<&str>,
request_timeout: u64,
) -> Result<Response<AsyncBody>> {
let request = Request::post(format!("{api_url}/embeddings"))
let request = Request::post("https://api.openai.com/v1/embeddings")
.redirect_policy(isahc::config::RedirectPolicy::Follow)
.timeout(Duration::from_secs(request_timeout))
.header("Content-Type", "application/json")
@@ -253,7 +246,6 @@ impl EmbeddingProvider for OpenAiEmbeddingProvider {
const BACKOFF_SECONDS: [usize; 4] = [3, 5, 15, 45];
const MAX_RETRIES: usize = 4;
let api_url = self.api_url.as_str();
let api_key = self.get_api_key()?;
let mut request_number = 0;
@@ -263,7 +255,6 @@ impl EmbeddingProvider for OpenAiEmbeddingProvider {
while request_number < MAX_RETRIES {
response = self
.send_request(
&api_url,
&api_key,
spans.iter().map(|x| &**x).collect(),
request_timeout,

View File

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

View File

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

View File

@@ -68,7 +68,6 @@ struct SavedConversation {
messages: Vec<SavedMessage>,
message_metadata: HashMap<MessageId, MessageMetadata>,
summary: String,
api_url: Option<String>,
model: OpenAiModel,
}

View File

@@ -7,7 +7,6 @@ use crate::{
SavedMessage, Split, ToggleFocus, ToggleIncludeConversation, ToggleRetrieveContext,
};
use ai::prompts::repository_context::PromptCodeSnippet;
use ai::providers::open_ai::OPEN_AI_API_URL;
use ai::{
auth::ProviderCredential,
completion::{CompletionProvider, CompletionRequest},
@@ -122,22 +121,10 @@ impl AssistantPanel {
.await
.log_err()
.unwrap_or_default();
let (api_url, model_name) = cx
.update(|cx| {
let settings = AssistantSettings::get_global(cx);
(
settings.openai_api_url.clone(),
settings.default_open_ai_model.full_name().to_string(),
)
})
.log_err()
.unwrap();
let completion_provider = OpenAiCompletionProvider::new(
api_url,
model_name,
cx.background_executor().clone(),
)
.await;
// Defaulting currently to GPT4, allow for this to be set via config.
let completion_provider =
OpenAiCompletionProvider::new("gpt-4".into(), cx.background_executor().clone())
.await;
// TODO: deserialize state.
let workspace_handle = workspace.clone();
@@ -975,7 +962,6 @@ impl AssistantPanel {
line_height: relative(1.3).into(),
background_color: None,
underline: None,
strikethrough: None,
white_space: WhiteSpace::Normal,
};
EditorElement::new(
@@ -1420,7 +1406,6 @@ struct Conversation {
completion_count: usize,
pending_completions: Vec<PendingCompletion>,
model: OpenAiModel,
api_url: Option<String>,
token_count: Option<usize>,
max_token_count: usize,
pending_token_count: Task<Option<()>>,
@@ -1455,7 +1440,6 @@ impl Conversation {
let settings = AssistantSettings::get_global(cx);
let model = settings.default_open_ai_model.clone();
let api_url = settings.openai_api_url.clone();
let mut this = Self {
id: Some(Uuid::new_v4().to_string()),
@@ -1469,7 +1453,6 @@ impl Conversation {
token_count: None,
max_token_count: tiktoken_rs::model::get_context_size(&model.full_name()),
pending_token_count: Task::ready(None),
api_url: Some(api_url),
model: model.clone(),
_subscriptions: vec![cx.subscribe(&buffer, Self::handle_buffer_event)],
pending_save: Task::ready(Ok(())),
@@ -1515,7 +1498,6 @@ impl Conversation {
.map(|summary| summary.text.clone())
.unwrap_or_default(),
model: self.model.clone(),
api_url: self.api_url.clone(),
}
}
@@ -1530,12 +1512,8 @@ impl Conversation {
None => Some(Uuid::new_v4().to_string()),
};
let model = saved_conversation.model;
let api_url = saved_conversation.api_url;
let completion_provider: Arc<dyn CompletionProvider> = Arc::new(
OpenAiCompletionProvider::new(
api_url
.clone()
.unwrap_or_else(|| OPEN_AI_API_URL.to_string()),
model.full_name().into(),
cx.background_executor().clone(),
)
@@ -1588,7 +1566,6 @@ impl Conversation {
token_count: None,
max_token_count: tiktoken_rs::model::get_context_size(&model.full_name()),
pending_token_count: Task::ready(None),
api_url,
model,
_subscriptions: vec![cx.subscribe(&buffer, Self::handle_buffer_event)],
pending_save: Task::ready(Ok(())),
@@ -3189,7 +3166,6 @@ impl InlineAssistant {
line_height: relative(1.3).into(),
background_color: None,
underline: None,
strikethrough: None,
white_space: WhiteSpace::Normal,
};
EditorElement::new(

View File

@@ -55,7 +55,6 @@ pub struct AssistantSettings {
pub default_width: Pixels,
pub default_height: Pixels,
pub default_open_ai_model: OpenAiModel,
pub openai_api_url: String,
}
/// Assistant panel settings
@@ -81,10 +80,6 @@ pub struct AssistantSettingsContent {
///
/// Default: gpt-4-1106-preview
pub default_open_ai_model: Option<OpenAiModel>,
/// OpenAI API base URL to use when starting new conversations.
///
/// Default: https://api.openai.com/v1
pub openai_api_url: Option<String>,
}
impl Settings for AssistantSettings {

View File

@@ -366,8 +366,7 @@ mod tests {
use gpui::{Context, TestAppContext};
use indoc::indoc;
use language::{
language_settings, tree_sitter_rust, Buffer, BufferId, Language, LanguageConfig,
LanguageMatcher, Point,
language_settings, tree_sitter_rust, Buffer, BufferId, Language, LanguageConfig, Point,
};
use rand::prelude::*;
use serde::Serialize;
@@ -676,10 +675,7 @@ mod tests {
Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),

View File

@@ -172,24 +172,22 @@ pub fn generate_content_prompt(
#[cfg(test)]
pub(crate) mod tests {
use super::*;
use std::sync::Arc;
use gpui::{AppContext, Context};
use indoc::indoc;
use language::{
language_settings, tree_sitter_rust, Buffer, BufferId, Language, LanguageConfig,
LanguageMatcher, Point,
language_settings, tree_sitter_rust, Buffer, BufferId, Language, LanguageConfig, Point,
};
use settings::SettingsStore;
use std::sync::Arc;
pub(crate) fn rust_lang() -> Language {
Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),

View File

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

View File

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

View File

@@ -40,11 +40,10 @@ impl Render for UpdateNotification {
.id("notes")
.child(Label::new("View the release notes"))
.cursor_pointer()
.on_click(cx.listener(|this, _, cx| {
.on_click(|_, 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.workspace = true
editor.workspace = true
gpui.workspace = true
collections = { path = "../collections" }
editor = { path = "../editor" }
gpui = { path = "../gpui" }
itertools = "0.10"
language.workspace = true
outline.workspace = true
project.workspace = true
search.workspace = true
settings.workspace = true
theme.workspace = true
ui.workspace = true
workspace.workspace = true
language = { path = "../language" }
outline = { path = "../outline" }
project = { path = "../project" }
search = { path = "../search" }
settings = { path = "../settings" }
theme = { path = "../theme" }
ui = { path = "../ui" }
workspace = { path = "../workspace" }
[dev-dependencies]
editor = { workspace = true, features = ["test-support"] }
gpui = { workspace = true, features = ["test-support"] }
workspace = { workspace = true, features = ["test-support"] }
editor = { path = "../editor", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
workspace = { path = "../workspace", features = ["test-support"] }

View File

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

View File

@@ -7,7 +7,6 @@ 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.
@@ -17,11 +16,6 @@ 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,10 +617,6 @@ 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,
@@ -1206,7 +1202,7 @@ impl Room {
})
}
pub fn share_project(
pub(crate) 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.workspace = true
clock.workspace = true
collections.workspace = true
db.workspace = true
feature_flags.workspace = true
client = { path = "../client" }
clock = { path = "../clock" }
collections = { path = "../collections" }
db = { path = "../db" }
feature_flags = { path = "../feature_flags" }
futures.workspace = true
gpui.workspace = true
gpui = { path = "../gpui" }
image = "0.23"
language.workspace = true
language = { path = "../language" }
lazy_static.workspace = true
log.workspace = true
parking_lot.workspace = true
postage.workspace = true
rand.workspace = true
release_channel.workspace = true
rpc.workspace = true
release_channel = { path = "../release_channel" }
rpc = { path = "../rpc" }
schemars.workspace = true
serde.workspace = true
serde_derive.workspace = true
settings.workspace = true
settings = { path = "../settings" }
smallvec.workspace = true
smol.workspace = true
sum_tree.workspace = true
sum_tree = { path = "../sum_tree" }
tempfile.workspace = true
text.workspace = true
text = { path = "../text" }
thiserror.workspace = true
time.workspace = true
tiny_http = "0.8"
url.workspace = true
util.workspace = true
util = { path = "../util" }
uuid.workspace = true
[dev-dependencies]
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"] }
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"] }

View File

@@ -5,13 +5,12 @@ 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, WeakModel,
};
use gpui::{AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Task};
use rand::prelude::*;
use std::{
collections::HashSet,
mem,
ops::{ControlFlow, Range},
sync::Arc,
};
@@ -27,7 +26,6 @@ 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<()>>,
@@ -39,7 +37,6 @@ 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)]
@@ -50,7 +47,6 @@ 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)]
@@ -59,15 +55,6 @@ 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,
@@ -109,35 +96,28 @@ 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;
let handle = cx.new_model(|cx| {
Ok(cx.new_model(|cx| {
cx.on_release(Self::release).detach();
Self {
let mut this = Self {
channel_id: channel.id,
user_store: user_store.clone(),
user_store,
channel_store,
rpc: client.clone(),
rpc: client,
outgoing_messages_lock: Default::default(),
messages: Default::default(),
acknowledged_message_ids: Default::default(),
loaded_all_messages: false,
loaded_all_messages,
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()),
}
})?;
Self::handle_loaded_messages(
handle.downgrade(),
user_store,
client,
response.messages,
response.done,
&mut cx,
)
.await?;
Ok(handle)
};
this.insert_messages(messages, cx);
this
})?)
}
fn release(&mut self, _: &mut AppContext) {
@@ -186,7 +166,6 @@ impl ChannelChat {
timestamp: OffsetDateTime::now_utc(),
mentions: message.mentions.clone(),
nonce,
reply_to_message_id: message.reply_to_message_id,
},
&(),
),
@@ -204,7 +183,6 @@ 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);
@@ -249,16 +227,12 @@ impl ChannelChat {
before_message_id,
})
.await?;
Self::handle_loaded_messages(
this,
user_store,
rpc,
response.messages,
response.done,
&mut cx,
)
.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);
})?;
anyhow::Ok(())
}
.log_err()
@@ -266,14 +240,9 @@ impl ChannelChat {
}
pub fn first_loaded_message_id(&mut self) -> Option<u64> {
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,
self.messages.first().and_then(|message| match message.id {
ChannelMessageId::Saved(id) => Some(id),
ChannelMessageId::Pending(_) => None,
})
}
@@ -335,66 +304,6 @@ 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();
@@ -402,17 +311,28 @@ impl ChannelChat {
cx.spawn(move |this, mut cx| {
async move {
let response = rpc.request(proto::JoinChannelChat { channel_id }).await?;
Self::handle_loaded_messages(
this.clone(),
user_store.clone(),
rpc.clone(),
response.messages,
response.done,
&mut cx,
)
.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;
}
let pending_messages = this.update(&mut cx, |this, _| {
this.pending_messages().cloned().collect::<Vec<_>>()
})?;
@@ -422,7 +342,6 @@ 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(
@@ -634,7 +553,6 @@ impl ChannelMessage {
.nonce
.ok_or_else(|| anyhow!("nonce is required"))?
.into(),
reply_to_message_id: message.reply_to_message_id,
})
}
@@ -724,7 +642,6 @@ impl<'a> From<&'a str> for MessageParams {
Self {
text: value.into(),
mentions: Vec::new(),
reply_to_message_id: None,
}
}
}

View File

@@ -74,19 +74,11 @@ impl Channel {
pub fn link(&self) -> String {
RELEASE_CHANNEL.link_prefix().to_owned()
+ "channel/"
+ &Self::slug(&self.name)
+ &self.slug()
+ "-"
+ &self.id.to_string()
}
pub fn notes_link(&self, heading: Option<String>) -> String {
self.link()
+ "/notes"
+ &heading
.map(|h| format!("#{}", Self::slug(&h)))
.unwrap_or_default()
}
pub fn is_root_channel(&self) -> bool {
self.parent_path.is_empty()
}
@@ -98,8 +90,9 @@ impl Channel {
.unwrap_or(self.id)
}
pub fn slug(str: &str) -> String {
let slug: String = str
pub fn slug(&self) -> String {
let slug: String = self
.name
.chars()
.map(|c| if c.is_alphanumeric() { c } else { '-' })
.collect();
@@ -1131,10 +1124,9 @@ impl ChannelState {
if let Some(latest_version) = &self.latest_notes_versions {
if let Some(observed_version) = &self.observed_notes_versions {
latest_version.epoch > observed_version.epoch
|| (latest_version.epoch == observed_version.epoch
&& latest_version
.version
.changed_since(&observed_version.version))
|| latest_version
.version
.changed_since(&observed_version.version)
} else {
true
}

View File

@@ -184,7 +184,6 @@ 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,
@@ -193,7 +192,6 @@ async fn test_channel_messages(cx: &mut TestAppContext) {
sender_id: 6,
mentions: vec![],
nonce: Some(2.into()),
reply_to_message_id: None,
},
],
done: false,
@@ -241,7 +239,6 @@ async fn test_channel_messages(cx: &mut TestAppContext) {
sender_id: 7,
mentions: vec![],
nonce: Some(3.into()),
reply_to_message_id: None,
}),
});
@@ -295,7 +292,6 @@ 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,
@@ -304,7 +300,6 @@ 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.workspace = true
util = { path = "../util" }
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.9"

View File

@@ -1,14 +1,20 @@
#![cfg_attr(target_os = "linux", allow(dead_code))]
use anyhow::{anyhow, Context, Result};
use clap::Parser;
use cli::{CliRequest, CliResponse};
use cli::{CliRequest, CliResponse, IpcHandshake, FORCE_CLI_MODE_ENV_VAR_NAME};
use core_foundation::{
array::{CFArray, CFIndex},
string::kCFStringEncodingUTF8,
url::{CFURLCreateWithBytes, CFURL},
};
use core_services::{kLSLaunchDefaults, LSLaunchURLSpec, LSOpenFromURLSpec, TCFType};
use ipc_channel::ipc::{IpcOneShotServer, IpcReceiver, IpcSender};
use serde::Deserialize;
use std::{
ffi::OsStr,
fs::{self, OpenOptions},
io,
path::{Path, PathBuf},
ptr,
};
use util::paths::PathLikeWithPosition;
@@ -106,6 +112,136 @@ enum Bundle {
},
}
impl Bundle {
fn detect(args_bundle_path: Option<&Path>) -> anyhow::Result<Self> {
let bundle_path = if let Some(bundle_path) = args_bundle_path {
bundle_path
.canonicalize()
.with_context(|| format!("Args bundle path {bundle_path:?} canonicalization"))?
} else {
locate_bundle().context("bundle autodiscovery")?
};
match bundle_path.extension().and_then(|ext| ext.to_str()) {
Some("app") => {
let plist_path = bundle_path.join("Contents/Info.plist");
let plist = plist::from_file::<_, InfoPlist>(&plist_path).with_context(|| {
format!("Reading *.app bundle plist file at {plist_path:?}")
})?;
Ok(Self::App {
app_bundle: bundle_path,
plist,
})
}
_ => {
println!("Bundle path {bundle_path:?} has no *.app extension, attempting to locate a dev build");
let plist_path = bundle_path
.parent()
.with_context(|| format!("Bundle path {bundle_path:?} has no parent"))?
.join("WebRTC.framework/Resources/Info.plist");
let plist = plist::from_file::<_, InfoPlist>(&plist_path)
.with_context(|| format!("Reading dev bundle plist file at {plist_path:?}"))?;
Ok(Self::LocalPath {
executable: bundle_path,
plist,
})
}
}
}
fn plist(&self) -> &InfoPlist {
match self {
Self::App { plist, .. } => plist,
Self::LocalPath { plist, .. } => plist,
}
}
fn path(&self) -> &Path {
match self {
Self::App { app_bundle, .. } => app_bundle,
Self::LocalPath { executable, .. } => executable,
}
}
fn launch(&self) -> anyhow::Result<(IpcSender<CliRequest>, IpcReceiver<CliResponse>)> {
let (server, server_name) =
IpcOneShotServer::<IpcHandshake>::new().context("Handshake before Zed spawn")?;
let url = format!("zed-cli://{server_name}");
match self {
Self::App { app_bundle, .. } => {
let app_path = app_bundle;
let status = unsafe {
let app_url = CFURL::from_path(app_path, true)
.with_context(|| format!("invalid app path {app_path:?}"))?;
let url_to_open = CFURL::wrap_under_create_rule(CFURLCreateWithBytes(
ptr::null(),
url.as_ptr(),
url.len() as CFIndex,
kCFStringEncodingUTF8,
ptr::null(),
));
// equivalent to: open zed-cli:... -a /Applications/Zed\ Preview.app
let urls_to_open = CFArray::from_copyable(&[url_to_open.as_concrete_TypeRef()]);
LSOpenFromURLSpec(
&LSLaunchURLSpec {
appURL: app_url.as_concrete_TypeRef(),
itemURLs: urls_to_open.as_concrete_TypeRef(),
passThruParams: ptr::null(),
launchFlags: kLSLaunchDefaults,
asyncRefCon: ptr::null_mut(),
},
ptr::null_mut(),
)
};
anyhow::ensure!(
status == 0,
"cannot start app bundle {}",
self.zed_version_string()
);
}
Self::LocalPath { executable, .. } => {
let executable_parent = executable
.parent()
.with_context(|| format!("Executable {executable:?} path has no parent"))?;
let subprocess_stdout_file =
fs::File::create(executable_parent.join("zed_dev.log"))
.with_context(|| format!("Log file creation in {executable_parent:?}"))?;
let subprocess_stdin_file =
subprocess_stdout_file.try_clone().with_context(|| {
format!("Cloning descriptor for file {subprocess_stdout_file:?}")
})?;
let mut command = std::process::Command::new(executable);
let command = command
.env(FORCE_CLI_MODE_ENV_VAR_NAME, "")
.stderr(subprocess_stdout_file)
.stdout(subprocess_stdin_file)
.arg(url);
command
.spawn()
.with_context(|| format!("Spawning {command:?}"))?;
}
}
let (_, handshake) = server.accept().context("Handshake after Zed spawn")?;
Ok((handshake.requests, handshake.responses))
}
fn zed_version_string(&self) -> String {
let is_dev = matches!(self, Self::LocalPath { .. });
format!(
"Zed {}{} {}",
self.plist().bundle_short_version_string,
if is_dev { " (dev)" } else { "" },
self.path().display(),
)
}
}
fn touch(path: &Path) -> io::Result<()> {
match OpenOptions::new().create(true).write(true).open(path) {
Ok(_) => Ok(()),
@@ -123,187 +259,3 @@ fn locate_bundle() -> Result<PathBuf> {
}
Ok(app_path)
}
#[cfg(target_os = "linux")]
mod linux {
use std::path::Path;
use cli::{CliRequest, CliResponse};
use ipc_channel::ipc::{IpcReceiver, IpcSender};
use crate::{Bundle, InfoPlist};
impl Bundle {
pub fn detect(_args_bundle_path: Option<&Path>) -> anyhow::Result<Self> {
unimplemented!()
}
pub fn plist(&self) -> &InfoPlist {
unimplemented!()
}
pub fn path(&self) -> &Path {
unimplemented!()
}
pub fn launch(&self) -> anyhow::Result<(IpcSender<CliRequest>, IpcReceiver<CliResponse>)> {
unimplemented!()
}
pub fn zed_version_string(&self) -> String {
unimplemented!()
}
}
}
#[cfg(target_os = "macos")]
mod mac_os {
use anyhow::Context;
use core_foundation::{
array::{CFArray, CFIndex},
string::kCFStringEncodingUTF8,
url::{CFURLCreateWithBytes, CFURL},
};
use core_services::{kLSLaunchDefaults, LSLaunchURLSpec, LSOpenFromURLSpec, TCFType};
use std::{fs, path::Path, ptr};
use cli::{CliRequest, CliResponse, IpcHandshake, FORCE_CLI_MODE_ENV_VAR_NAME};
use ipc_channel::ipc::{IpcOneShotServer, IpcReceiver, IpcSender};
use crate::{locate_bundle, Bundle, InfoPlist};
impl Bundle {
pub fn detect(args_bundle_path: Option<&Path>) -> anyhow::Result<Self> {
let bundle_path = if let Some(bundle_path) = args_bundle_path {
bundle_path
.canonicalize()
.with_context(|| format!("Args bundle path {bundle_path:?} canonicalization"))?
} else {
locate_bundle().context("bundle autodiscovery")?
};
match bundle_path.extension().and_then(|ext| ext.to_str()) {
Some("app") => {
let plist_path = bundle_path.join("Contents/Info.plist");
let plist =
plist::from_file::<_, InfoPlist>(&plist_path).with_context(|| {
format!("Reading *.app bundle plist file at {plist_path:?}")
})?;
Ok(Self::App {
app_bundle: bundle_path,
plist,
})
}
_ => {
println!("Bundle path {bundle_path:?} has no *.app extension, attempting to locate a dev build");
let plist_path = bundle_path
.parent()
.with_context(|| format!("Bundle path {bundle_path:?} has no parent"))?
.join("WebRTC.framework/Resources/Info.plist");
let plist =
plist::from_file::<_, InfoPlist>(&plist_path).with_context(|| {
format!("Reading dev bundle plist file at {plist_path:?}")
})?;
Ok(Self::LocalPath {
executable: bundle_path,
plist,
})
}
}
}
fn plist(&self) -> &InfoPlist {
match self {
Self::App { plist, .. } => plist,
Self::LocalPath { plist, .. } => plist,
}
}
fn path(&self) -> &Path {
match self {
Self::App { app_bundle, .. } => app_bundle,
Self::LocalPath { executable, .. } => executable,
}
}
pub fn launch(&self) -> anyhow::Result<(IpcSender<CliRequest>, IpcReceiver<CliResponse>)> {
let (server, server_name) =
IpcOneShotServer::<IpcHandshake>::new().context("Handshake before Zed spawn")?;
let url = format!("zed-cli://{server_name}");
match self {
Self::App { app_bundle, .. } => {
let app_path = app_bundle;
let status = unsafe {
let app_url = CFURL::from_path(app_path, true)
.with_context(|| format!("invalid app path {app_path:?}"))?;
let url_to_open = CFURL::wrap_under_create_rule(CFURLCreateWithBytes(
ptr::null(),
url.as_ptr(),
url.len() as CFIndex,
kCFStringEncodingUTF8,
ptr::null(),
));
// equivalent to: open zed-cli:... -a /Applications/Zed\ Preview.app
let urls_to_open =
CFArray::from_copyable(&[url_to_open.as_concrete_TypeRef()]);
LSOpenFromURLSpec(
&LSLaunchURLSpec {
appURL: app_url.as_concrete_TypeRef(),
itemURLs: urls_to_open.as_concrete_TypeRef(),
passThruParams: ptr::null(),
launchFlags: kLSLaunchDefaults,
asyncRefCon: ptr::null_mut(),
},
ptr::null_mut(),
)
};
anyhow::ensure!(
status == 0,
"cannot start app bundle {}",
self.zed_version_string()
);
}
Self::LocalPath { executable, .. } => {
let executable_parent = executable
.parent()
.with_context(|| format!("Executable {executable:?} path has no parent"))?;
let subprocess_stdout_file = fs::File::create(
executable_parent.join("zed_dev.log"),
)
.with_context(|| format!("Log file creation in {executable_parent:?}"))?;
let subprocess_stdin_file =
subprocess_stdout_file.try_clone().with_context(|| {
format!("Cloning descriptor for file {subprocess_stdout_file:?}")
})?;
let mut command = std::process::Command::new(executable);
let command = command
.env(FORCE_CLI_MODE_ENV_VAR_NAME, "")
.stderr(subprocess_stdout_file)
.stdout(subprocess_stdin_file)
.arg(url);
command
.spawn()
.with_context(|| format!("Spawning {command:?}"))?;
}
}
let (_, handshake) = server.accept().context("Handshake after Zed spawn")?;
Ok((handshake.requests, handshake.responses))
}
pub fn zed_version_string(&self) -> String {
let is_dev = matches!(self, Self::LocalPath { .. });
format!(
"Zed {}{} {}",
self.plist().bundle_short_version_string,
if is_dev { " (dev)" } else { "" },
self.path().display(),
)
}
}
}

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.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
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" }
anyhow.workspace = true
async-recursion = "0.3"
async-tungstenite = { version = "0.16", features = ["async-std", "async-native-tls"] }
async-tungstenite = { version = "0.16", features = ["async-tls"] }
futures.workspace = true
image = "0.23"
lazy_static.workspace = true
@@ -51,8 +51,8 @@ uuid.workspace = true
url.workspace = true
[dev-dependencies]
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"] }
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"] }

View File

@@ -10,7 +10,6 @@ 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,
@@ -30,6 +29,7 @@ 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_std::client_async_tls(request, stream).await?;
async_tungstenite::async_tls::client_async_tls(request, stream).await?;
Ok(Connection::new(
stream
.map_err(|error| anyhow!(error))

View File

@@ -145,14 +145,11 @@ 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<Option<Vec<u8>>> = Lazy::new(|| {
static ZED_CLIENT_CHECKSUM_SEED: Lazy<Vec<u8>> = Lazy::new(|| {
option_env!("ZED_CLIENT_CHECKSUM_SEED")
.map(|s| s.as_bytes().into())
.or_else(|| {
env::var("ZED_CLIENT_CHECKSUM_SEED")
.ok()
.map(|s| s.as_bytes().into())
})
.unwrap_or("development-checksum-seed")
.as_bytes()
.into()
});
impl Telemetry {
@@ -513,10 +510,6 @@ impl Telemetry {
return;
}
let Some(checksum_seed) = &*ZED_CLIENT_CHECKSUM_SEED else {
return;
};
let this = self.clone();
self.executor
.spawn(
@@ -558,9 +551,9 @@ impl Telemetry {
}
let mut summer = Sha256::new();
summer.update(checksum_seed);
summer.update(&*ZED_CLIENT_CHECKSUM_SEED);
summer.update(&json_bytes);
summer.update(checksum_seed);
summer.update(&*ZED_CLIENT_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.workspace = true
collections.workspace = true
clock = { path = "../clock" }
collections = { path = "../collections" }
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.workspace = true
live_kit_server = { path = "../live_kit_server" }
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.workspace = true
rpc = { path = "../rpc" }
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.workspace = true
text = { path = "../text" }
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.workspace = true
util = { path = "../util" }
uuid.workspace = true
[dev-dependencies]
release_channel.workspace = true
release_channel = { path = "../release_channel" }
async-trait.workspace = true
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"] }
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"] }
ctor.workspace = true
editor = { workspace = true, features = ["test-support"] }
editor = { path = "../editor", features = ["test-support"] }
env_logger.workspace = true
file_finder.workspace = true
fs = { workspace = true, features = ["test-support"] }
git = { workspace = true, features = ["test-support"] }
gpui = { workspace = true, features = ["test-support"] }
file_finder = { path = "../file_finder" }
fs = { path = "../fs", features = ["test-support"] }
git = { path = "../git", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
indoc.workspace = true
language = { workspace = true, features = ["test-support"] }
language = { path = "../language", features = ["test-support"] }
lazy_static.workspace = true
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"] }
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"] }
pretty_assertions.workspace = true
project = { workspace = true, features = ["test-support"] }
rpc = { workspace = true, features = ["test-support"] }
project = { path = "../project", features = ["test-support"] }
rpc = { path = "../rpc", features = ["test-support"] }
sea-orm = { version = "0.12.x", features = ["sqlx-sqlite"] }
serde_json.workspace = true
settings = { workspace = true, features = ["test-support"] }
settings = { path = "../settings", features = ["test-support"] }
sqlx = { version = "0.7", features = ["sqlite"] }
theme.workspace = true
theme = { path = "../theme" }
unindent.workspace = true
util.workspace = true
workspace = { workspace = true, features = ["test-support"] }
util = { path = "../util" }
workspace = { path = "../workspace", features = ["test-support"] }
[features]
seed-support = ["clap", "lipsum", "reqwest"]

View File

@@ -163,8 +163,7 @@ CREATE TABLE "room_participants" (
"calling_connection_id" INTEGER NOT NULL,
"calling_connection_server_id" INTEGER REFERENCES servers (id) ON DELETE SET NULL,
"participant_index" INTEGER,
"role" TEXT,
"in_call" BOOLEAN NOT NULL DEFAULT FALSE
"role" TEXT
);
CREATE UNIQUE INDEX "index_room_participants_on_user_id" ON "room_participants" ("user_id");
CREATE INDEX "index_room_participants_on_room_id" ON "room_participants" ("room_id");
@@ -218,8 +217,7 @@ 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,
"reply_to_message_id" INTEGER DEFAULT NULL
"nonce" BLOB NOT 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

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

View File

@@ -1,3 +0,0 @@
-- Add migration script here
ALTER TABLE room_participants ADD COLUMN in_call BOOL NOT NULL DEFAULT FALSE;

View File

@@ -587,9 +587,6 @@ pub struct ChannelsForUser {
pub channels: Vec<Channel>,
pub channel_memberships: Vec<channel_member::Model>,
pub channel_participants: HashMap<ChannelId, Vec<UserId>>,
pub observed_buffer_versions: Vec<proto::ChannelBufferVersion>,
pub observed_channel_messages: Vec<proto::ChannelMessageId>,
pub latest_buffer_versions: Vec<proto::ChannelBufferVersion>,
pub latest_channel_messages: Vec<proto::ChannelMessageId>,
}
@@ -695,7 +692,7 @@ impl ProjectCollaborator {
pub struct LeftProject {
pub id: ProjectId,
pub host_user_id: UserId,
pub host_connection_id: Option<ConnectionId>,
pub host_connection_id: ConnectionId,
pub connection_ids: Vec<ConnectionId>,
}

View File

@@ -561,6 +561,7 @@ impl Database {
tx: &DatabaseTransaction,
) -> Result<()> {
use observed_buffer_edits::Column;
observed_buffer_edits::Entity::insert(observed_buffer_edits::ActiveModel {
user_id: ActiveValue::Set(user_id),
buffer_id: ActiveValue::Set(buffer_id),
@@ -670,7 +671,7 @@ impl Database {
buffer_id: row.buffer_id,
epoch: row.epoch,
lamport_timestamp: row.lamport_timestamp,
replica_id: row.replica_id,
replica_id: row.lamport_timestamp,
value: Default::default(),
});
operations.push(proto::Operation {
@@ -749,9 +750,20 @@ impl Database {
pub async fn latest_channel_buffer_changes(
&self,
channel_ids_by_buffer_id: &HashMap<BufferId, ChannelId>,
channel_ids: &[ChannelId],
tx: &DatabaseTransaction,
) -> Result<Vec<proto::ChannelBufferVersion>> {
let mut channel_ids_by_buffer_id = HashMap::default();
let mut rows = buffer::Entity::find()
.filter(buffer::Column::ChannelId.is_in(channel_ids.iter().copied()))
.stream(&*tx)
.await?;
while let Some(row) = rows.next().await {
let row = row?;
channel_ids_by_buffer_id.insert(row.id, row.channel_id);
}
drop(rows);
let latest_operations = self
.get_latest_operations_for_buffers(channel_ids_by_buffer_id.keys().copied(), &*tx)
.await?;
@@ -771,36 +783,6 @@ impl Database {
.collect())
}
pub async fn observed_channel_buffer_changes(
&self,
channel_ids_by_buffer_id: &HashMap<BufferId, ChannelId>,
user_id: UserId,
tx: &DatabaseTransaction,
) -> Result<Vec<proto::ChannelBufferVersion>> {
let observed_operations = observed_buffer_edits::Entity::find()
.filter(observed_buffer_edits::Column::UserId.eq(user_id))
.filter(
observed_buffer_edits::Column::BufferId
.is_in(channel_ids_by_buffer_id.keys().copied()),
)
.all(&*tx)
.await?;
Ok(observed_operations
.iter()
.flat_map(|op| {
Some(proto::ChannelBufferVersion {
channel_id: channel_ids_by_buffer_id.get(&op.buffer_id)?.to_proto(),
epoch: op.epoch as u64,
version: vec![proto::VectorClockEntry {
replica_id: op.replica_id as u32,
timestamp: op.lamport_timestamp as u32,
}],
})
})
.collect())
}
/// Returns the latest operations for the buffers with the specified IDs.
pub async fn get_latest_operations_for_buffers(
&self,

View File

@@ -103,6 +103,7 @@ impl Database {
channel_id: ChannelId,
user_id: UserId,
connection: ConnectionId,
environment: &str,
) -> Result<(JoinRoom, Option<MembershipUpdated>, ChannelRole)> {
self.transaction(move |tx| async move {
let channel = self.get_channel_internal(channel_id, &*tx).await?;
@@ -162,7 +163,7 @@ impl Database {
let live_kit_room = format!("channel-{}", nanoid::nanoid!(30));
let room_id = self
.get_or_create_channel_room(channel_id, &live_kit_room, &*tx)
.get_or_create_channel_room(channel_id, &live_kit_room, environment, &*tx)
.await?;
self.join_channel_room_internal(room_id, user_id, connection, role, &*tx)
@@ -626,40 +627,18 @@ impl Database {
}
let channel_ids = channels.iter().map(|c| c.id).collect::<Vec<_>>();
let mut channel_ids_by_buffer_id = HashMap::default();
let mut rows = buffer::Entity::find()
.filter(buffer::Column::ChannelId.is_in(channel_ids.iter().copied()))
.stream(&*tx)
.await?;
while let Some(row) = rows.next().await {
let row = row?;
channel_ids_by_buffer_id.insert(row.id, row.channel_id);
}
drop(rows);
let latest_buffer_versions = self
.latest_channel_buffer_changes(&channel_ids_by_buffer_id, &*tx)
.latest_channel_buffer_changes(&channel_ids, &*tx)
.await?;
let latest_channel_messages = self.latest_channel_messages(&channel_ids, &*tx).await?;
let observed_buffer_versions = self
.observed_channel_buffer_changes(&channel_ids_by_buffer_id, user_id, &*tx)
.await?;
let observed_channel_messages = self
.observed_channel_messages(&channel_ids, user_id, &*tx)
.await?;
let latest_messages = self.latest_channel_messages(&channel_ids, &*tx).await?;
Ok(ChannelsForUser {
channel_memberships,
channels,
channel_participants,
latest_buffer_versions,
latest_channel_messages,
observed_buffer_versions,
observed_channel_messages,
latest_channel_messages: latest_messages,
})
}
@@ -932,6 +911,7 @@ impl Database {
&self,
channel_id: ChannelId,
live_kit_room: &str,
environment: &str,
tx: &DatabaseTransaction,
) -> Result<RoomId> {
let room = room::Entity::find()
@@ -940,11 +920,19 @@ impl Database {
.await?;
let room_id = if let Some(room) = room {
if let Some(env) = room.environment {
if &env != environment {
Err(ErrorCode::WrongReleaseChannel
.with_tag("required", &env)
.anyhow())?;
}
}
room.id
} else {
let result = room::Entity::insert(room::ActiveModel {
channel_id: ActiveValue::Set(Some(channel_id)),
live_kit_room: ActiveValue::Set(live_kit_room.to_string()),
environment: ActiveValue::Set(Some(environment.to_string())),
..Default::default()
})
.exec(&*tx)

View File

@@ -161,7 +161,6 @@ 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<_>>();
@@ -208,7 +207,6 @@ 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?;
@@ -247,7 +245,6 @@ 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([
@@ -388,30 +385,6 @@ impl Database {
Ok(())
}
pub async fn observed_channel_messages(
&self,
channel_ids: &[ChannelId],
user_id: UserId,
tx: &DatabaseTransaction,
) -> Result<Vec<proto::ChannelMessageId>> {
let rows = observed_channel_messages::Entity::find()
.filter(observed_channel_messages::Column::UserId.eq(user_id))
.filter(
observed_channel_messages::Column::ChannelId
.is_in(channel_ids.iter().map(|id| id.0)),
)
.all(&*tx)
.await?;
Ok(rows
.into_iter()
.map(|message| proto::ChannelMessageId {
channel_id: message.channel_id.to_proto(),
message_id: message.channel_message_id.to_proto(),
})
.collect())
}
pub async fn latest_channel_messages(
&self,
channel_ids: &[ChannelId],

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: Some(project.host_connection()?),
host_connection_id: project.host_connection()?,
connection_ids,
};
Ok((room, left_project))

View File

@@ -110,10 +110,12 @@ impl Database {
user_id: UserId,
connection: ConnectionId,
live_kit_room: &str,
release_channel: &str,
) -> Result<proto::Room> {
self.transaction(|tx| async move {
let room = room::ActiveModel {
live_kit_room: ActiveValue::set(live_kit_room.into()),
environment: ActiveValue::set(Some(release_channel.to_string())),
..Default::default()
}
.insert(&*tx)
@@ -300,21 +302,31 @@ impl Database {
room_id: RoomId,
user_id: UserId,
connection: ConnectionId,
environment: &str,
) -> Result<RoomGuard<JoinRoom>> {
self.room_transaction(room_id, |tx| async move {
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
enum QueryChannelId {
enum QueryChannelIdAndEnvironment {
ChannelId,
Environment,
}
let channel_id: Option<ChannelId> = room::Entity::find()
.select_only()
.column(room::Column::ChannelId)
.filter(room::Column::Id.eq(room_id))
.into_values::<_, QueryChannelId>()
.one(&*tx)
.await?
.ok_or_else(|| anyhow!("no such room"))?;
let (channel_id, release_channel): (Option<ChannelId>, Option<String>) =
room::Entity::find()
.select_only()
.column(room::Column::ChannelId)
.column(room::Column::Environment)
.filter(room::Column::Id.eq(room_id))
.into_values::<_, QueryChannelIdAndEnvironment>()
.one(&*tx)
.await?
.ok_or_else(|| anyhow!("no such room"))?;
if let Some(release_channel) = release_channel {
if &release_channel != environment {
Err(anyhow!("must join using the {} release", release_channel))?;
}
}
if channel_id.is_some() {
Err(anyhow!("tried to join channel call directly"))?
@@ -850,7 +862,7 @@ impl Database {
id: collaborator.project_id,
host_user_id: Default::default(),
connection_ids: Default::default(),
host_connection_id: None,
host_connection_id: Default::default(),
});
let collaborator_connection_id = collaborator.connection();
@@ -860,7 +872,7 @@ impl Database {
if collaborator.is_host {
left_project.host_user_id = collaborator.user_id;
left_project.host_connection_id = Some(collaborator_connection_id);
left_project.host_connection_id = collaborator_connection_id;
}
}
drop(collaborators);

View File

@@ -12,7 +12,6 @@ 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

@@ -8,6 +8,7 @@ pub struct Model {
pub id: RoomId,
pub live_kit_room: String,
pub channel_id: Option<ChannelId>,
pub environment: Option<String>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]

View File

@@ -15,6 +15,8 @@ use std::sync::{
Arc,
};
const TEST_RELEASE_CHANNEL: &'static str = "test";
pub struct TestDb {
pub db: Option<Arc<Database>>,
pub connection: Option<sqlx::AnyConnection>,

View File

@@ -337,12 +337,17 @@ async fn test_channel_buffers_last_operations(db: &Database) {
let buffer_changes = db
.transaction(|tx| {
let buffers = &buffers;
let mut hash = HashMap::default();
hash.insert(buffers[0].id, buffers[0].channel_id);
hash.insert(buffers[1].id, buffers[1].channel_id);
hash.insert(buffers[2].id, buffers[2].channel_id);
async move { db.latest_channel_buffer_changes(&hash, &*tx).await }
async move {
db.latest_channel_buffer_changes(
&[
buffers[0].channel_id,
buffers[1].channel_id,
buffers[2].channel_id,
],
&*tx,
)
.await
}
})
.await
.unwrap();

View File

@@ -1,6 +1,6 @@
use crate::{
db::{
tests::{channel_tree, new_test_connection, new_test_user},
tests::{channel_tree, new_test_connection, new_test_user, TEST_RELEASE_CHANNEL},
Channel, ChannelId, ChannelRole, Database, NewUserParams, RoomId,
},
test_both_dbs,
@@ -135,7 +135,12 @@ async fn test_joining_channels(db: &Arc<Database>) {
// can join a room with membership to its channel
let (joined_room, _, _) = db
.join_channel(channel_1, user_1, ConnectionId { owner_id, id: 1 })
.join_channel(
channel_1,
user_1,
ConnectionId { owner_id, id: 1 },
TEST_RELEASE_CHANNEL,
)
.await
.unwrap();
assert_eq!(joined_room.room.participants.len(), 1);
@@ -144,7 +149,12 @@ async fn test_joining_channels(db: &Arc<Database>) {
drop(joined_room);
// cannot join a room without membership to its channel
assert!(db
.join_room(room_id, user_2, ConnectionId { owner_id, id: 1 },)
.join_room(
room_id,
user_2,
ConnectionId { owner_id, id: 1 },
TEST_RELEASE_CHANNEL
)
.await
.is_err());
}
@@ -722,7 +732,7 @@ async fn test_guest_access(db: &Arc<Database>) {
.await
.is_err());
db.join_channel(zed_channel, guest, guest_connection)
db.join_channel(zed_channel, guest, guest_connection, TEST_RELEASE_CHANNEL)
.await
.unwrap();

View File

@@ -517,7 +517,7 @@ async fn test_project_count(db: &Arc<Database>) {
.unwrap();
let room_id = RoomId::from_proto(
db.create_room(user1.user_id, ConnectionId { owner_id, id: 0 }, "")
db.create_room(user1.user_id, ConnectionId { owner_id, id: 0 }, "", "test")
.await
.unwrap()
.id,
@@ -531,9 +531,14 @@ async fn test_project_count(db: &Arc<Database>) {
)
.await
.unwrap();
db.join_room(room_id, user2.user_id, ConnectionId { owner_id, id: 1 })
.await
.unwrap();
db.join_room(
room_id,
user2.user_id,
ConnectionId { owner_id, id: 1 },
"test",
)
.await
.unwrap();
assert_eq!(db.project_count_excluding_admins().await.unwrap(), 0);
db.share_project(room_id, ConnectionId { owner_id, id: 1 }, &[])
@@ -611,3 +616,80 @@ async fn test_fuzzy_search_users(cx: &mut TestAppContext) {
.collect::<Vec<_>>()
}
}
test_both_dbs!(
test_non_matching_release_channels,
test_non_matching_release_channels_postgres,
test_non_matching_release_channels_sqlite
);
async fn test_non_matching_release_channels(db: &Arc<Database>) {
let owner_id = db.create_server("test").await.unwrap().0 as u32;
let user1 = db
.create_user(
&format!("admin@example.com"),
true,
NewUserParams {
github_login: "admin".into(),
github_user_id: 0,
},
)
.await
.unwrap();
let user2 = db
.create_user(
&format!("user@example.com"),
false,
NewUserParams {
github_login: "user".into(),
github_user_id: 1,
},
)
.await
.unwrap();
let room = db
.create_room(
user1.user_id,
ConnectionId { owner_id, id: 0 },
"",
"stable",
)
.await
.unwrap();
db.call(
RoomId::from_proto(room.id),
user1.user_id,
ConnectionId { owner_id, id: 0 },
user2.user_id,
None,
)
.await
.unwrap();
// User attempts to join from preview
let result = db
.join_room(
RoomId::from_proto(room.id),
user2.user_id,
ConnectionId { owner_id, id: 1 },
"preview",
)
.await;
assert!(result.is_err());
// User switches to stable
let result = db
.join_room(
RoomId::from_proto(room.id),
user2.user_id,
ConnectionId { owner_id, id: 1 },
"stable",
)
.await;
assert!(result.is_ok())
}

View File

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

@@ -102,6 +102,7 @@ impl<R: RequestMessage> Response<R> {
#[derive(Clone)]
struct Session {
zed_environment: Arc<str>,
user_id: UserId,
connection_id: ConnectionId,
db: Arc<tokio::sync::Mutex<DbHandle>>,
@@ -601,7 +602,7 @@ impl Server {
let mut pool = this.connection_pool.lock();
pool.add_connection(connection_id, user_id, user.admin);
this.peer.send(connection_id, build_initial_contacts_update(contacts, &pool))?;
this.peer.send(connection_id, build_update_user_channels(&channels_for_user))?;
this.peer.send(connection_id, build_update_user_channels(&channels_for_user.channel_memberships))?;
this.peer.send(connection_id, build_channels_update(
channels_for_user,
channel_invites
@@ -616,6 +617,7 @@ impl Server {
user_id,
connection_id,
db: Arc::new(tokio::sync::Mutex::new(DbHandle(this.app_state.db.clone()))),
zed_environment: this.app_state.config.zed_environment.clone(),
peer: this.peer.clone(),
connection_pool: this.connection_pool.clone(),
live_kit_client: this.app_state.live_kit_client.clone(),
@@ -864,7 +866,7 @@ pub fn routes(server: Arc<Server>) -> Router<Body> {
pub async fn handle_websocket_request(
TypedHeader(ProtocolVersion(protocol_version)): TypedHeader<ProtocolVersion>,
app_version_header: Option<TypedHeader<AppVersionHeader>>,
_app_version_header: Option<TypedHeader<AppVersionHeader>>,
ConnectInfo(socket_address): ConnectInfo<SocketAddr>,
Extension(server): Extension<Arc<Server>>,
Extension(user): Extension<User>,
@@ -879,19 +881,6 @@ pub async fn handle_websocket_request(
.into_response();
}
// the first version of zed that sent this header was 0.121.x
if let Some(version) = app_version_header.map(|header| header.0 .0) {
// 0.123.0 was a nightly version with incompatible collab changes
// that were reverted.
if version == "0.123.0".parse().unwrap() {
return (
StatusCode::UPGRADE_REQUIRED,
"client must be upgraded".to_string(),
)
.into_response();
}
}
let socket_address = socket_address.to_string();
ws.on_upgrade(move |socket| {
use util::ResultExt;
@@ -1020,7 +1009,12 @@ async fn create_room(
let room = session
.db()
.await
.create_room(session.user_id, session.connection_id, &live_kit_room)
.create_room(
session.user_id,
session.connection_id,
&live_kit_room,
&session.zed_environment,
)
.await?;
response.send(proto::CreateRoomResponse {
@@ -1050,7 +1044,12 @@ async fn join_room(
let room = session
.db()
.await
.join_room(room_id, session.user_id, session.connection_id)
.join_room(
room_id,
session.user_id,
session.connection_id,
session.zed_environment.as_ref(),
)
.await?;
room_updated(&room.room, &session.peer);
room.into_inner()
@@ -1692,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"
);
@@ -2735,7 +2734,12 @@ async fn join_channel_internal(
let db = session.db().await;
let (joined_room, membership_updated, role) = db
.join_channel(channel_id, session.user_id, session.connection_id)
.join_channel(
channel_id,
session.user_id,
session.connection_id,
session.zed_environment.as_ref(),
)
.await?;
let live_kit_connection_info = session.live_kit_client.as_ref().and_then(|live_kit| {
@@ -3015,10 +3019,6 @@ 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,7 +3028,6 @@ 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),
@@ -3339,18 +3338,17 @@ fn notify_membership_updated(
}
}
fn build_update_user_channels(channels: &ChannelsForUser) -> proto::UpdateUserChannels {
fn build_update_user_channels(
memberships: &Vec<db::channel_member::Model>,
) -> proto::UpdateUserChannels {
proto::UpdateUserChannels {
channel_memberships: channels
.channel_memberships
channel_memberships: memberships
.iter()
.map(|m| proto::ChannelMembership {
channel_id: m.channel_id.to_proto(),
role: m.role.into(),
})
.collect(),
observed_channel_buffer_version: channels.observed_buffer_versions.clone(),
observed_channel_message_id: channels.observed_channel_messages.clone(),
..Default::default()
}
}

View File

@@ -1,6 +1,6 @@
use crate::{
rpc::{CLEANUP_TIMEOUT, RECONNECT_TIMEOUT},
tests::{test_server::open_channel_notes, TestServer},
tests::TestServer,
};
use call::ActiveCall;
use channel::ACKNOWLEDGE_DEBOUNCE_INTERVAL;
@@ -161,15 +161,15 @@ async fn test_channel_notes_participant_indices(
// Clients A, B, and C open the channel notes
let channel_view_a = cx_a
.update(|cx| ChannelView::open(channel_id, None, workspace_a.clone(), cx))
.update(|cx| ChannelView::open(channel_id, workspace_a.clone(), cx))
.await
.unwrap();
let channel_view_b = cx_b
.update(|cx| ChannelView::open(channel_id, None, workspace_b.clone(), cx))
.update(|cx| ChannelView::open(channel_id, workspace_b.clone(), cx))
.await
.unwrap();
let channel_view_c = cx_c
.update(|cx| ChannelView::open(channel_id, None, workspace_c.clone(), cx))
.update(|cx| ChannelView::open(channel_id, workspace_c.clone(), cx))
.await
.unwrap();
@@ -605,75 +605,113 @@ async fn test_channel_buffer_changes(
cx_a: &mut TestAppContext,
cx_b: &mut TestAppContext,
) {
let (server, client_a, client_b, channel_id) = TestServer::start2(cx_a, cx_b).await;
let (_, cx_a) = client_a.build_test_workspace(cx_a).await;
let (workspace_b, cx_b) = client_b.build_test_workspace(cx_b).await;
let channel_store_b = client_b.channel_store().clone();
let mut server = TestServer::start(deterministic.clone()).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
// Editing the channel notes should set them to dirty
open_channel_notes(channel_id, cx_a).await.unwrap();
cx_a.simulate_keystrokes("1");
channel_store_b.read_with(cx_b, |channel_store, _| {
assert!(channel_store.has_channel_buffer_changed(channel_id))
let channel_id = server
.make_channel(
"the-channel",
None,
(&client_a, cx_a),
&mut [(&client_b, cx_b)],
)
.await;
let channel_buffer_a = client_a
.channel_store()
.update(cx_a, |store, cx| store.open_channel_buffer(channel_id, cx))
.await
.unwrap();
// Client A makes an edit, and client B should see that the note has changed.
channel_buffer_a.update(cx_a, |buffer, cx| {
buffer.buffer().update(cx, |buffer, cx| {
buffer.edit([(0..0, "1")], None, cx);
})
});
deterministic.run_until_parked();
let has_buffer_changed = cx_b.update(|cx| {
client_b
.channel_store()
.read(cx)
.has_channel_buffer_changed(channel_id)
});
assert!(has_buffer_changed);
// Opening the buffer should clear the changed flag.
open_channel_notes(channel_id, cx_b).await.unwrap();
channel_store_b.read_with(cx_b, |channel_store, _| {
assert!(!channel_store.has_channel_buffer_changed(channel_id))
let project_b = client_b.build_empty_local_project(cx_b);
let (workspace_b, cx_b) = client_b.build_workspace(&project_b, cx_b);
let channel_view_b = cx_b
.update(|cx| ChannelView::open(channel_id, workspace_b.clone(), cx))
.await
.unwrap();
deterministic.run_until_parked();
let has_buffer_changed = cx_b.update(|cx| {
client_b
.channel_store()
.read(cx)
.has_channel_buffer_changed(channel_id)
});
assert!(!has_buffer_changed);
// Editing the channel while the buffer is open should not show that the buffer has changed.
cx_a.simulate_keystrokes("2");
channel_store_b.read_with(cx_b, |channel_store, _| {
assert!(!channel_store.has_channel_buffer_changed(channel_id))
channel_buffer_a.update(cx_a, |buffer, cx| {
buffer.buffer().update(cx, |buffer, cx| {
buffer.edit([(0..0, "2")], None, cx);
})
});
deterministic.run_until_parked();
let has_buffer_changed = cx_b.read(|cx| {
client_b
.channel_store()
.read(cx)
.has_channel_buffer_changed(channel_id)
});
assert!(!has_buffer_changed);
deterministic.advance_clock(ACKNOWLEDGE_DEBOUNCE_INTERVAL);
// Test that the server is tracking things correctly, and we retain our 'not changed'
// state across a disconnect
deterministic.advance_clock(ACKNOWLEDGE_DEBOUNCE_INTERVAL);
server
.simulate_long_connection_interruption(client_b.peer_id().unwrap(), deterministic.clone());
channel_store_b.read_with(cx_b, |channel_store, _| {
assert!(!channel_store.has_channel_buffer_changed(channel_id))
let has_buffer_changed = cx_b.read(|cx| {
client_b
.channel_store()
.read(cx)
.has_channel_buffer_changed(channel_id)
});
assert!(!has_buffer_changed);
// Closing the buffer should re-enable change tracking
cx_b.update(|cx| {
workspace_b.update(cx, |workspace, cx| {
workspace.close_all_items_and_panes(&Default::default(), cx)
});
drop(channel_view_b)
});
deterministic.run_until_parked();
channel_buffer_a.update(cx_a, |buffer, cx| {
buffer.buffer().update(cx, |buffer, cx| {
buffer.edit([(0..0, "3")], None, cx);
})
});
deterministic.run_until_parked();
cx_a.simulate_keystrokes("3");
channel_store_b.read_with(cx_b, |channel_store, _| {
assert!(channel_store.has_channel_buffer_changed(channel_id))
});
}
#[gpui::test]
async fn test_channel_buffer_changes_persist(
cx_a: &mut TestAppContext,
cx_b: &mut TestAppContext,
cx_b2: &mut TestAppContext,
) {
let (mut server, client_a, client_b, channel_id) = TestServer::start2(cx_a, cx_b).await;
let (_, cx_a) = client_a.build_test_workspace(cx_a).await;
let (_, cx_b) = client_b.build_test_workspace(cx_b).await;
// a) edits the notes
open_channel_notes(channel_id, cx_a).await.unwrap();
cx_a.simulate_keystrokes("1");
// b) opens them to observe the current version
open_channel_notes(channel_id, cx_b).await.unwrap();
// On boot the client should get the correct state.
let client_b2 = server.create_client(cx_b2, "user_b").await;
let channel_store_b2 = client_b2.channel_store().clone();
channel_store_b2.read_with(cx_b2, |channel_store, _| {
assert!(!channel_store.has_channel_buffer_changed(channel_id))
let has_buffer_changed = cx_b.read(|cx| {
client_b
.channel_store()
.read(cx)
.has_channel_buffer_changed(channel_id)
});
assert!(has_buffer_changed);
}
#[track_caller]

View File

@@ -43,7 +43,6 @@ 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,
)
@@ -403,66 +402,3 @@ 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

@@ -19,7 +19,7 @@ use gpui::{TestAppContext, VisualContext, VisualTestContext};
use indoc::indoc;
use language::{
language_settings::{AllLanguageSettings, InlayHintSettings},
tree_sitter_rust, FakeLspAdapter, Language, LanguageConfig, LanguageMatcher,
tree_sitter_rust, FakeLspAdapter, Language, LanguageConfig,
};
use rpc::RECEIVE_TIMEOUT;
use serde_json::json;
@@ -269,10 +269,7 @@ async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mu
let mut language = Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
@@ -458,10 +455,7 @@ async fn test_collaborating_with_code_actions(
let mut language = Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
@@ -674,10 +668,7 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T
let mut language = Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
@@ -862,10 +853,7 @@ async fn test_language_server_statuses(cx_a: &mut TestAppContext, cx_b: &mut Tes
let mut language = Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
@@ -1156,10 +1144,7 @@ async fn test_on_input_format_from_host_to_guest(
let mut language = Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
@@ -1287,10 +1272,7 @@ async fn test_on_input_format_from_guest_to_host(
let mut language = Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
@@ -1449,10 +1431,7 @@ async fn test_mutual_editor_inlay_hint_cache_update(
let mut language = Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
@@ -1718,10 +1697,7 @@ async fn test_inlay_hint_refresh_is_forwarded(
let mut language = Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),

View File

@@ -22,8 +22,6 @@ use workspace::{
SplitDirection, Workspace,
};
use super::TestClient;
#[gpui::test(iterations = 10)]
async fn test_basic_following(
cx_a: &mut TestAppContext,
@@ -1907,7 +1905,7 @@ async fn test_following_to_channel_notes_without_a_shared_project(
// Client A opens the notes for channel 1.
let channel_notes_1_a = cx_a
.update(|cx| ChannelView::open(channel_1_id, None, workspace_a.clone(), cx))
.update(|cx| ChannelView::open(channel_1_id, workspace_a.clone(), cx))
.await
.unwrap();
channel_notes_1_a.update(cx_a, |notes, cx| {
@@ -1953,7 +1951,7 @@ async fn test_following_to_channel_notes_without_a_shared_project(
// Client A opens the notes for channel 2.
let channel_notes_2_a = cx_a
.update(|cx| ChannelView::open(channel_2_id, None, workspace_a.clone(), cx))
.update(|cx| ChannelView::open(channel_2_id, workspace_a.clone(), cx))
.await
.unwrap();
channel_notes_2_a.update(cx_a, |notes, cx| {
@@ -1998,82 +1996,3 @@ 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

@@ -14,7 +14,7 @@ use gpui::{
use language::{
language_settings::{AllLanguageSettings, Formatter},
tree_sitter_rust, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language, LanguageConfig,
LanguageMatcher, LineEnding, OffsetRangeExt, Point, Rope,
LineEnding, OffsetRangeExt, Point, Rope,
};
use live_kit_client::MacOSDisplay;
use lsp::LanguageServerId;
@@ -2246,10 +2246,7 @@ async fn test_propagate_saves_and_fs_changes(
let rust = Arc::new(Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
@@ -2257,10 +2254,7 @@ async fn test_propagate_saves_and_fs_changes(
let javascript = Arc::new(Language::new(
LanguageConfig {
name: "JavaScript".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["js".to_string()],
..Default::default()
},
path_suffixes: vec!["js".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
@@ -3789,10 +3783,7 @@ async fn test_collaborating_with_diagnostics(
let mut language = Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
@@ -4070,10 +4061,7 @@ async fn test_collaborating_with_lsp_progress_updates_and_diagnostics_ordering(
let mut language = Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
@@ -4302,10 +4290,7 @@ async fn test_formatting_buffer(
let mut language = Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
@@ -4410,10 +4395,7 @@ async fn test_prettier_formatting_buffer(
let mut language = Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
path_suffixes: vec!["rs".to_string()],
prettier_parser_name: Some("test_parser".to_string()),
..Default::default()
},
@@ -4529,10 +4511,7 @@ async fn test_definition(
let mut language = Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
@@ -4676,10 +4655,7 @@ async fn test_references(
let mut language = Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
@@ -4876,10 +4852,7 @@ async fn test_document_highlights(
let mut language = Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
@@ -4982,10 +4955,7 @@ async fn test_lsp_hover(
let mut language = Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
@@ -5081,10 +5051,7 @@ async fn test_project_symbols(
let mut language = Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
@@ -5193,10 +5160,7 @@ async fn test_open_buffer_while_getting_definition_pointing_to_it(
let mut language = Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
@@ -6003,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() == 2);
assert!(workspace.items(cx).collect::<Vec<_>>().len() == 3);
});
}

View File

@@ -8,9 +8,7 @@ use editor::Bias;
use fs::{repository::GitFileStatus, FakeFs, Fs as _};
use futures::StreamExt;
use gpui::{BackgroundExecutor, Model, TestAppContext};
use language::{
range_to_lsp, FakeLspAdapter, Language, LanguageConfig, LanguageMatcher, PointUtf16,
};
use language::{range_to_lsp, FakeLspAdapter, Language, LanguageConfig, PointUtf16};
use lsp::FakeLanguageServer;
use pretty_assertions::assert_eq;
use project::{search::SearchQuery, Project, ProjectPath};
@@ -1024,10 +1022,7 @@ impl RandomizedTest for ProjectCollaborationTest {
let mut language = Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
None,

View File

@@ -10,11 +10,10 @@ use channel::{ChannelBuffer, ChannelStore};
use client::{
self, proto::PeerId, Client, Connection, Credentials, EstablishConnectionError, UserStore,
};
use collab_ui::channel_view::ChannelView;
use collections::{HashMap, HashSet};
use fs::FakeFs;
use futures::{channel::oneshot, StreamExt as _};
use gpui::{BackgroundExecutor, Context, Model, Task, TestAppContext, View, VisualTestContext};
use gpui::{BackgroundExecutor, Context, Model, TestAppContext, View, VisualTestContext};
use language::LanguageRegistry;
use node_runtime::FakeNodeRuntime;
@@ -124,12 +123,7 @@ 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(
"test-channel",
None,
(&client_a, cx_a),
&mut [(&client_b, cx_b)],
)
.make_channel("a", None, (&client_a, cx_a), &mut [(&client_b, cx_b)])
.await;
cx_a.run_until_parked();
@@ -761,16 +755,6 @@ impl TestClient {
}
}
pub fn open_channel_notes(
channel_id: u64,
cx: &mut VisualTestContext,
) -> Task<anyhow::Result<View<ChannelView>>> {
let window = cx.update(|cx| cx.active_window().unwrap().downcast::<Workspace>().unwrap());
let view = window.root_view(cx).unwrap();
cx.update(|cx| ChannelView::open(channel_id, None, view.clone(), cx))
}
impl Drop for TestClient {
fn drop(&mut self) {
self.app_state.client.teardown();

View File

@@ -26,58 +26,58 @@ test-support = [
[dependencies]
anyhow.workspace = true
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
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" }
futures.workspace = true
fuzzy.workspace = true
gpui.workspace = true
language.workspace = true
fuzzy = { path = "../fuzzy" }
gpui = { path = "../gpui" }
language = { path = "../language" }
lazy_static.workspace = true
log.workspace = true
menu.workspace = true
notifications.workspace = true
menu = { path = "../menu" }
notifications = { path = "../notifications" }
parking_lot.workspace = true
picker.workspace = true
picker = { path = "../picker" }
postage.workspace = true
project.workspace = true
recent_projects.workspace = true
rich_text.workspace = true
rpc.workspace = true
project = { path = "../project" }
recent_projects = { path = "../recent_projects" }
rich_text = { path = "../rich_text" }
rpc = { path = "../rpc" }
schemars.workspace = true
serde.workspace = true
serde_derive.workspace = true
serde_json.workspace = true
settings.workspace = true
settings = { path = "../settings" }
smallvec.workspace = true
story = { workspace = true, optional = true }
theme.workspace = true
theme_selector.workspace = true
story = { path = "../story", optional = true }
theme = { path = "../theme" }
theme_selector = { path = "../theme_selector" }
time.workspace = true
ui.workspace = true
util.workspace = true
vcs_menu.workspace = true
workspace.workspace = true
zed_actions.workspace = true
ui = { path = "../ui" }
util = { path = "../util" }
vcs_menu = { path = "../vcs_menu" }
workspace = { path = "../workspace" }
zed_actions = { path = "../zed_actions" }
[dev-dependencies]
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"] }
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"] }
pretty_assertions.workspace = true
project = { workspace = true, features = ["test-support"] }
rpc = { workspace = true, features = ["test-support"] }
settings = { workspace = true, features = ["test-support"] }
project = { path = "../project", features = ["test-support"] }
rpc = { path = "../rpc", features = ["test-support"] }
settings = { path = "../settings", features = ["test-support"] }
tree-sitter-markdown.workspace = true
util = { workspace = true, features = ["test-support"] }
workspace = { workspace = true, features = ["test-support"] }
util = { path = "../util", features = ["test-support"] }
workspace = { path = "../workspace", features = ["test-support"] }

View File

@@ -6,14 +6,11 @@ use client::{
Collaborator, ParticipantIndex,
};
use collections::HashMap;
use editor::{
display_map::ToDisplayPoint, scroll::Autoscroll, CollaborationHub, DisplayPoint, Editor,
EditorEvent,
};
use editor::{CollaborationHub, Editor, EditorEvent};
use gpui::{
actions, AnyElement, AnyView, AppContext, ClipboardItem, Entity as _, EventEmitter,
FocusableView, IntoElement as _, Model, Pixels, Point, Render, Subscription, Task, View,
ViewContext, VisualContext as _, WeakView, WindowContext,
actions, AnyElement, AnyView, AppContext, Entity as _, EventEmitter, FocusableView,
IntoElement as _, Model, Pixels, Point, Render, Subscription, Task, View, ViewContext,
VisualContext as _, WindowContext,
};
use project::Project;
use std::{
@@ -26,10 +23,10 @@ use workspace::{
item::{FollowableItem, Item, ItemEvent, ItemHandle},
register_followable_item,
searchable::SearchableItemHandle,
ItemNavHistory, Pane, SaveIntent, Toast, ViewId, Workspace, WorkspaceId,
ItemNavHistory, Pane, SaveIntent, ViewId, Workspace, WorkspaceId,
};
actions!(collab, [CopyLink]);
actions!(collab, [Deploy]);
pub fn init(cx: &mut AppContext) {
register_followable_item::<ChannelView>(cx)
@@ -37,30 +34,21 @@ pub fn init(cx: &mut AppContext) {
pub struct ChannelView {
pub editor: View<Editor>,
workspace: WeakView<Workspace>,
project: Model<Project>,
channel_store: Model<ChannelStore>,
channel_buffer: Model<ChannelBuffer>,
remote_id: Option<ViewId>,
_editor_event_subscription: Subscription,
_reparse_subscription: Option<Subscription>,
}
impl ChannelView {
pub fn open(
channel_id: ChannelId,
link_position: Option<String>,
workspace: View<Workspace>,
cx: &mut WindowContext,
) -> Task<Result<View<Self>>> {
let pane = workspace.read(cx).active_pane().clone();
let channel_view = Self::open_in_pane(
channel_id,
link_position,
pane.clone(),
workspace.clone(),
cx,
);
let channel_view = Self::open_in_pane(channel_id, pane.clone(), workspace.clone(), cx);
cx.spawn(|mut cx| async move {
let channel_view = channel_view.await?;
pane.update(&mut cx, |pane, cx| {
@@ -78,12 +66,10 @@ impl ChannelView {
pub fn open_in_pane(
channel_id: ChannelId,
link_position: Option<String>,
pane: View<Pane>,
workspace: View<Workspace>,
cx: &mut WindowContext,
) -> Task<Result<View<Self>>> {
let weak_workspace = workspace.downgrade();
let workspace = workspace.read(cx);
let project = workspace.project().to_owned();
let channel_store = ChannelStore::global(cx);
@@ -96,13 +82,12 @@ impl ChannelView {
let channel_buffer = channel_buffer.await?;
let markdown = markdown.await.log_err();
channel_buffer.update(&mut cx, |channel_buffer, cx| {
channel_buffer.buffer().update(cx, |buffer, cx| {
channel_buffer.update(&mut cx, |buffer, cx| {
buffer.buffer().update(cx, |buffer, cx| {
buffer.set_language_registry(language_registry);
let Some(markdown) = markdown else {
return;
};
buffer.set_language(Some(markdown), cx);
if let Some(markdown) = markdown {
buffer.set_language(Some(markdown), cx);
}
})
})?;
@@ -116,18 +101,12 @@ impl ChannelView {
// If this channel buffer is already open in this pane, just return it.
if let Some(existing_view) = existing_view.clone() {
if existing_view.read(cx).channel_buffer == channel_buffer {
if let Some(link_position) = link_position {
existing_view.update(cx, |channel_view, cx| {
channel_view.focus_position_from_link(link_position, true, cx)
});
}
return existing_view;
}
}
let view = cx.new_view(|cx| {
let mut this =
Self::new(project, weak_workspace, channel_store, channel_buffer, cx);
let mut this = Self::new(project, channel_store, channel_buffer, cx);
this.acknowledge_buffer_version(cx);
this
});
@@ -142,12 +121,6 @@ impl ChannelView {
}
}
if let Some(link_position) = link_position {
view.update(cx, |channel_view, cx| {
channel_view.focus_position_from_link(link_position, true, cx)
});
}
view
})
})
@@ -155,29 +128,16 @@ impl ChannelView {
pub fn new(
project: Model<Project>,
workspace: WeakView<Workspace>,
channel_store: Model<ChannelStore>,
channel_buffer: Model<ChannelBuffer>,
cx: &mut ViewContext<Self>,
) -> Self {
let buffer = channel_buffer.read(cx).buffer();
let this = cx.view().downgrade();
let editor = cx.new_view(|cx| {
let mut editor = Editor::for_buffer(buffer, None, cx);
editor.set_collaboration_hub(Box::new(ChannelBufferCollaborationHub(
channel_buffer.clone(),
)));
editor.set_custom_context_menu(move |_, position, cx| {
let this = this.clone();
Some(ui::ContextMenu::build(cx, move |menu, _| {
menu.entry("Copy link to section", None, move |cx| {
this.update(cx, |this, cx| {
this.copy_link_for_position(position.clone(), cx)
})
.ok();
})
}))
});
editor
});
let _editor_event_subscription =
@@ -188,94 +148,14 @@ impl ChannelView {
Self {
editor,
workspace,
project,
channel_store,
channel_buffer,
remote_id: None,
_editor_event_subscription,
_reparse_subscription: None,
}
}
fn focus_position_from_link(
&mut self,
position: String,
first_attempt: bool,
cx: &mut ViewContext<Self>,
) {
let position = Channel::slug(&position).to_lowercase();
let snapshot = self.editor.update(cx, |editor, cx| editor.snapshot(cx));
if let Some(outline) = snapshot.buffer_snapshot.outline(None) {
if let Some(item) = outline
.items
.iter()
.find(|item| &Channel::slug(&item.text).to_lowercase() == &position)
{
self.editor.update(cx, |editor, cx| {
editor.change_selections(Some(Autoscroll::focused()), cx, |s| {
s.replace_cursors_with(|map| vec![item.range.start.to_display_point(&map)])
})
});
return;
}
}
if !first_attempt {
return;
}
self._reparse_subscription = Some(cx.subscribe(
&self.editor,
move |this, _, e: &EditorEvent, cx| {
match e {
EditorEvent::Reparsed => {
this.focus_position_from_link(position.clone(), false, cx);
this._reparse_subscription.take();
}
EditorEvent::Edited | EditorEvent::SelectionsChanged { local: true } => {
this._reparse_subscription.take();
}
_ => {}
};
},
));
}
fn copy_link(&mut self, _: &CopyLink, cx: &mut ViewContext<Self>) {
let position = self
.editor
.update(cx, |editor, cx| editor.selections.newest_display(cx).start);
self.copy_link_for_position(position, cx)
}
fn copy_link_for_position(&self, position: DisplayPoint, cx: &mut ViewContext<Self>) {
let snapshot = self.editor.update(cx, |editor, cx| editor.snapshot(cx));
let mut closest_heading = None;
if let Some(outline) = snapshot.buffer_snapshot.outline(None) {
for item in outline.items {
if item.range.start.to_display_point(&snapshot) > position {
break;
}
closest_heading = Some(item);
}
}
let Some(channel) = self.channel(cx) else {
return;
};
let link = channel.notes_link(closest_heading.map(|heading| heading.text));
cx.write_to_clipboard(ClipboardItem::new(link));
self.workspace
.update(cx, |workspace, cx| {
workspace.show_toast(Toast::new(0, "Link copied to clipboard"), cx);
})
.ok();
}
pub fn channel(&self, cx: &AppContext) -> Option<Arc<Channel>> {
self.channel_buffer.read(cx).channel(cx)
}
@@ -335,11 +215,8 @@ impl ChannelView {
impl EventEmitter<EditorEvent> for ChannelView {}
impl Render for ChannelView {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
div()
.size_full()
.on_action(cx.listener(Self::copy_link))
.child(self.editor.clone())
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
self.editor.clone()
}
}
@@ -397,7 +274,6 @@ impl Item for ChannelView {
Some(cx.new_view(|cx| {
Self::new(
self.project.clone(),
self.workspace.clone(),
self.channel_store.clone(),
self.channel_buffer.clone(),
cx,
@@ -480,7 +356,7 @@ impl FollowableItem for ChannelView {
unreachable!()
};
let open = ChannelView::open_in_pane(state.channel_id, None, pane, workspace, cx);
let open = ChannelView::open_in_pane(state.channel_id, pane, workspace, cx);
Some(cx.spawn(|mut cx| async move {
let this = open.await?;

View File

@@ -1,16 +1,16 @@
use crate::{collab_panel, ChatPanelSettings};
use anyhow::Result;
use call::{room, ActiveCall};
use channel::{ChannelChat, ChannelChatEvent, ChannelMessage, ChannelMessageId, ChannelStore};
use channel::{ChannelChat, ChannelChatEvent, 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, CursorStyle,
DismissEvent, ElementId, EventEmitter, FocusHandle, FocusableView, FontStyle, FontWeight,
HighlightStyle, ListOffset, ListScrollEvent, ListState, Model, Render, StyledText,
Subscription, Task, View, ViewContext, VisualContext, WeakView,
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,
};
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, Tooltip,
TabBar,
};
use util::{ResultExt, TryFutureExt};
use workspace::{
@@ -62,7 +62,6 @@ 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)]
@@ -70,7 +69,7 @@ struct SerializedChatPanel {
width: Option<Pixels>,
}
actions!(chat_panel, [ToggleFocus, CloseReplyPreview]);
actions!(chat_panel, [ToggleFocus]);
impl ChatPanel {
pub fn new(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) -> View<Self> {
@@ -125,7 +124,6 @@ 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)
@@ -238,7 +236,6 @@ 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);
@@ -288,99 +285,6 @@ 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) =
@@ -413,9 +317,18 @@ 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 can_delete_message = belongs_to_user || is_admin;
let message_id_to_remove = if let (ChannelMessageId::Saved(id), true) =
(message.id, belongs_to_user || is_admin)
{
Some(id)
} else {
None
};
let element_id: ElementId = match message.id {
ChannelMessageId::Saved(id) => ("saved-message", id).into(),
@@ -428,41 +341,19 @@ 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(background)
.bg(if mentioning_you {
Fill::from(cx.theme().colors().background)
} else {
Fill::default()
})
.rounded_md()
.overflow_hidden()
.px_1()
.py_0p5()
.when(!is_continuation_from_previous, |this| {
this.mt_2().child(
this.mt_1().child(
h_flex()
.text_ui_sm()
.child(div().absolute().child(
@@ -486,86 +377,36 @@ impl ChatPanel {
),
)
})
.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(
.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(
div()
.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,
))
}),
)
}),
),
)
}),
.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))
})
})),
),
),
)
}
@@ -579,27 +420,13 @@ impl ChatPanel {
fn render_message_menu(
this: &View<Self>,
message_id: u64,
can_delete_message: bool,
cx: &mut WindowContext,
) -> View<ContextMenu> {
let menu = {
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)),
)
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))
})
})
};
@@ -690,21 +517,7 @@ 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,
@@ -719,19 +532,11 @@ impl ChatPanel {
Ok(())
})
}
fn close_reply_preview(&mut self, _: &CloseReplyPreview, cx: &mut ViewContext<Self>) {
self.message_editor
.update(cx, |editor, _| editor.clear_reply_to_message_id());
}
}
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()
.key_context("ChatPanel")
.track_focus(&self.focus_handle)
.full()
.on_action(cx.listener(Self::send))
@@ -753,7 +558,7 @@ impl Render for ChatPanel {
),
),
)
.child(div().flex_grow().px_2().map(|this| {
.child(div().flex_grow().px_2().pt_1().map(|this| {
if self.active_chat.is_some() {
this.child(list(self.message_list.clone()).full())
} else {
@@ -784,65 +589,25 @@ impl Render for ChatPanel {
)
}
}))
.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
}
})
.child(
h_flex()
.when(!self.is_scrolled_to_bottom, |el| {
el.border_t_1().border_color(cx.theme().colors().border)
})
.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)
.tooltip(|cx| {
Tooltip::for_action(
"Close reply preview",
&CloseReplyPreview,
cx,
)
})
.on_click(cx.listener(move |_, _, cx| {
cx.dispatch_action(CloseReplyPreview.boxed_clone())
})),
),
)
})
})
.children(
Some(
h_flex()
.key_context("MessageEditor")
.on_action(cx.listener(ChatPanel::close_reply_preview))
.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()),
.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),
)
}
}),
)
.into_any()
}
@@ -982,7 +747,6 @@ 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,7 +34,6 @@ pub struct MessageEditor {
mentions: Vec<UserId>,
mentions_task: Option<Task<()>>,
channel_id: Option<ChannelId>,
reply_to_message_id: Option<u64>,
}
struct MessageEditorCompletionProvider(WeakView<MessageEditor>);
@@ -113,22 +112,9 @@ 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,
@@ -186,13 +172,8 @@ 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,
reply_to_message_id,
}
MessageParams { text, mentions }
})
}
@@ -360,7 +341,6 @@ impl Render for MessageEditor {
line_height: relative(1.3).into(),
background_color: None,
underline: None,
strikethrough: None,
white_space: WhiteSpace::Normal,
};
@@ -444,7 +424,6 @@ 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},
OpenChannelNotes, Workspace,
Workspace,
};
actions!(
@@ -69,19 +69,6 @@ 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();
}
@@ -970,7 +957,7 @@ impl CollabPanel {
.child(render_tree_branch(false, true, cx))
.child(IconButton::new(0, IconName::File)),
)
.child(Label::new("notes"))
.child(div().h_7().w_full().child(Label::new("notes")))
.tooltip(move |cx| Tooltip::text("Open Channel Notes", cx))
}
@@ -1691,7 +1678,7 @@ impl CollabPanel {
fn open_channel_notes(&mut self, channel_id: ChannelId, cx: &mut ViewContext<Self>) {
if let Some(workspace) = self.workspace.upgrade() {
ChannelView::open(channel_id, None, workspace, cx).detach();
ChannelView::open(channel_id, workspace, cx).detach();
}
}
@@ -2068,7 +2055,6 @@ impl CollabPanel {
line_height: relative(1.3).into(),
background_color: None,
underline: None,
strikethrough: None,
white_space: WhiteSpace::Normal,
};

View File

@@ -562,23 +562,14 @@ impl CollabTitlebarItem {
}
fn window_activation_changed(&mut self, cx: &mut ViewContext<Self>) {
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();
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);
}
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 = { workspace = true, optional = true }
story = { path = "../story", optional = true }

View File

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

View File

@@ -317,8 +317,10 @@ impl PickerDelegate for CommandPaletteDelegate {
});
let action = command.action;
cx.focus(&self.previous_focus_handle);
cx.window_context()
.spawn(move |mut cx| async move { cx.update(|cx| cx.dispatch_action(action)) })
.detach_and_log_err(cx);
self.dismissed(cx);
cx.dispatch_action(action);
}
fn render_match(

View File

@@ -23,28 +23,28 @@ test-support = [
anyhow.workspace = true
async-compression.workspace = true
async-tar = "0.4.2"
collections.workspace = true
collections = { path = "../collections" }
futures.workspace = true
gpui.workspace = true
language.workspace = true
gpui = { path = "../gpui" }
language = { path = "../language" }
log.workspace = true
lsp.workspace = true
node_runtime.workspace = true
lsp = { path = "../lsp" }
node_runtime = { path = "../node_runtime" }
parking_lot.workspace = true
serde.workspace = true
serde_derive.workspace = true
settings.workspace = true
settings = { path = "../settings" }
smol.workspace = true
theme.workspace = true
util.workspace = true
theme = { path = "../theme" }
util = { path = "../util" }
[dev-dependencies]
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"] }
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"] }

View File

@@ -444,6 +444,7 @@ impl Copilot {
|_, _| { /* Silence the notification */ },
)
.detach();
let server = cx.update(|cx| server.initialize(None, cx))?.await?;
let status = server
@@ -975,10 +976,9 @@ async fn get_copilot_lsp(http: Arc<dyn HttpClient>) -> anyhow::Result<PathBuf> {
///Check for the latest copilot language server and download it if we haven't already
async fn fetch_latest(http: Arc<dyn HttpClient>) -> anyhow::Result<PathBuf> {
let release =
latest_github_release("zed-industries/copilot", true, false, http.clone()).await?;
let release = latest_github_release("zed-industries/copilot", false, http.clone()).await?;
let version_dir = &*paths::COPILOT_DIR.join(format!("copilot-{}", release.tag_name));
let version_dir = &*paths::COPILOT_DIR.join(format!("copilot-{}", release.name));
fs::create_dir_all(version_dir).await?;
let server_path = version_dir.join(SERVER_PATH);
@@ -997,7 +997,7 @@ async fn get_copilot_lsp(http: Arc<dyn HttpClient>) -> anyhow::Result<PathBuf> {
let mut response = http
.get(url, Default::default(), true)
.await
.context("error downloading copilot release")?;
.map_err(|err| anyhow!("error downloading copilot release: {}", err))?;
let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut()));
let archive = Archive::new(decompressed_bytes);
archive.unpack(dist_dir).await?;

View File

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

View File

@@ -144,12 +144,6 @@ impl CopilotCodeVerification {
.on_click(|_, cx| cx.open_url(COPILOT_SIGN_UP_URL)),
)
}
fn render_disabled_modal() -> impl Element {
v_flex()
.child(Headline::new("Copilot is disabled").size(HeadlineSize::Large))
.child(Label::new("You can enable Copilot in your settings."))
}
}
impl Render for CopilotCodeVerification {
@@ -166,10 +160,6 @@ impl Render for CopilotCodeVerification {
self.connect_clicked = false;
Self::render_enabled_modal(cx).into_any_element()
}
Status::Disabled => {
self.connect_clicked = false;
Self::render_disabled_modal().into_any_element()
}
_ => div().into_any_element(),
};

View File

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

View File

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

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