Compare commits
11 Commits
dynamic-ru
...
fix-hover-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30239b3cc6 | ||
|
|
180421fe5a | ||
|
|
1a13995b8f | ||
|
|
b6379a9177 | ||
|
|
2cb041504b | ||
|
|
4d8dc79d7e | ||
|
|
474c806331 | ||
|
|
2db6ccd803 | ||
|
|
2d6a227258 | ||
|
|
a3ce933b04 | ||
|
|
816c48b7d6 |
38
.github/ISSUE_TEMPLATE/0_feature_request.yml
vendored
@@ -2,23 +2,23 @@ name: Feature Request
|
||||
description: "Tip: open this issue template from within Zed with the `request feature` command palette action"
|
||||
labels: ["admin read", "triage", "enhancement"]
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Check for existing issues
|
||||
description: Check the backlog of issues to reduce the chances of creating duplicates; if an issue already exists, place a `+1` (👍) on it.
|
||||
options:
|
||||
- label: Completed
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the feature
|
||||
description: A clear and concise description of what you want to happen.
|
||||
validations:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Check for existing issues
|
||||
description: Check the backlog of issues to reduce the chances of creating duplicates; if an issue already exists, place a `+1` (👍) on it.
|
||||
options:
|
||||
- label: Completed
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: |
|
||||
If applicable, add mockups / screenshots to help present your vision of the feature
|
||||
description: Drag images into the text input below
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the feature
|
||||
description: A clear and concise description of what you want to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: |
|
||||
If applicable, add mockups / screenshots to help present your vision of the feature
|
||||
description: Drag images into the text input below
|
||||
validations:
|
||||
required: false
|
||||
|
||||
40
.github/ISSUE_TEMPLATE/1_bug_report.yml
vendored
@@ -1,40 +0,0 @@
|
||||
name: Bug Report
|
||||
description: |
|
||||
Use this template for **non-crash-related** bug reports.
|
||||
Tip: open this issue template from within Zed with the `file bug report` command palette action.
|
||||
labels: ["admin read", "triage", "defect"]
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Check for existing issues
|
||||
description: Check the backlog of issues to reduce the chances of creating duplicates; if an issue already exists, place a `+1` (👍) on it.
|
||||
options:
|
||||
- label: Completed
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the bug / provide steps to reproduce it
|
||||
description: A clear and concise description of what the bug is.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: environment
|
||||
attributes:
|
||||
label: Environment
|
||||
description: Run the `copy system specs into clipboard` command palette action and paste the output in the field below.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: If applicable, add mockups / screenshots to help explain present your vision of the feature
|
||||
description: Drag issues into the text input below
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: |
|
||||
If applicable, attach your `~/Library/Logs/Zed/Zed.log` file to this issue.
|
||||
If you only need the most recent lines, you can run the `zed: open log` command palette action to see the last 1000.
|
||||
description: Drag Zed.log into the text input below
|
||||
validations:
|
||||
required: false
|
||||
47
.github/ISSUE_TEMPLATE/1_language_support.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
name: Language Support
|
||||
description: Request language support
|
||||
title: "<name_of_language> support"
|
||||
labels:
|
||||
[
|
||||
"admin read",
|
||||
"triage",
|
||||
"enhancement",
|
||||
"language",
|
||||
"unsupported language",
|
||||
"potential extension",
|
||||
]
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Check for existing issues
|
||||
description: Check the backlog of issues to reduce the chances of creating duplicates; if an issue already exists, place a `+1` (👍) on it.
|
||||
options:
|
||||
- label: Completed
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Language
|
||||
description: What language do you want support for?
|
||||
placeholder: HTML
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Tree Sitter parser link
|
||||
description: If applicable, provide a link to the appropriate tree sitter parser. Look here first - https://tree-sitter.github.io/tree-sitter/#available-parsers
|
||||
placeholder: https://github.com/tree-sitter/tree-sitter-html
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
attributes:
|
||||
label: Language server link
|
||||
description: If applicable, provide a link to the appropriate language server. Look here first - https://microsoft.github.io/language-server-protocol/implementors/servers/
|
||||
placeholder: https://github.com/Microsoft/vscode/tree/main/extensions/html-language-features/server
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Misc notes
|
||||
description: Provide any additional things the team should consider when adding support for this language
|
||||
validations:
|
||||
required: false
|
||||
38
.github/ISSUE_TEMPLATE/2_bug_report.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: Bug Report
|
||||
description: "Tip: open this issue template from within Zed with the `file bug report` command palette action"
|
||||
labels: ["admin read", "triage", "defect"]
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Check for existing issues
|
||||
description: Check the backlog of issues to reduce the chances of creating duplicates; if an issue already exists, place a `+1` (👍) on it.
|
||||
options:
|
||||
- label: Completed
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the bug / provide steps to reproduce it
|
||||
description: A clear and concise description of what the bug is.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: environment
|
||||
attributes:
|
||||
label: Environment
|
||||
description: Run the `copy system specs into clipboard` command palette action and paste the output in the field below.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: If applicable, add mockups / screenshots to help explain present your vision of the feature
|
||||
description: Drag issues into the text input below
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: |
|
||||
If applicable, attach your `~/Library/Logs/Zed/Zed.log` file to this issue.
|
||||
If you only need the most recent lines, you can run the `zed: open log` command palette action to see the last 1000.
|
||||
description: Drag Zed.log into the text input below
|
||||
validations:
|
||||
required: false
|
||||
39
.github/ISSUE_TEMPLATE/2_crash_report.yml
vendored
@@ -1,39 +0,0 @@
|
||||
name: Crash Report
|
||||
description: |
|
||||
Use this template for crash reports.
|
||||
labels: ["admin read", "triage", "defect", "panic / crash"]
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Check for existing issues
|
||||
description: Check the backlog of issues to reduce the chances of creating duplicates; if an issue already exists, place a `+1` (👍) on it.
|
||||
options:
|
||||
- label: Completed
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the bug / provide steps to reproduce it
|
||||
description: A clear and concise description of what the bug is.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: environment
|
||||
attributes:
|
||||
label: Environment
|
||||
description: Run the `copy system specs into clipboard` command palette action and paste the output in the field below.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: If applicable, add mockups / screenshots to help explain present your vision of the feature
|
||||
description: Drag issues into the text input below
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: |
|
||||
If applicable, attach your `~/Library/Logs/Zed/Zed.log` file to this issue.
|
||||
If you only need the most recent lines, you can run the `zed: open log` command palette action to see the last 1000.
|
||||
description: Drag Zed.log into the text input below
|
||||
validations:
|
||||
required: false
|
||||
27
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,16 +1,13 @@
|
||||
contact_links:
|
||||
- name: Language Request
|
||||
url: https://github.com/zed-industries/extensions/issues/new?assignees=&labels=language&projects=&template=1_language_request.yml&title=%3Cname_of_language%3E
|
||||
about: Request a language in the extensions repository
|
||||
- name: Theme Request
|
||||
url: https://github.com/zed-industries/extensions/issues/new?assignees=&labels=theme&projects=&template=0_theme_request.yml&title=%3Cname_of_theme%3E+theme
|
||||
about: Request a theme in the extensions repository
|
||||
- name: Top-Ranking Issues
|
||||
url: https://github.com/zed-industries/zed/issues/5393
|
||||
about: See an overview of the most popular Zed issues
|
||||
- name: Platform Support
|
||||
url: https://github.com/zed-industries/zed/issues/5391
|
||||
about: A quick note on platform support
|
||||
- name: Positive Feedback
|
||||
url: https://github.com/zed-industries/zed/discussions/5397
|
||||
about: A central location for kind words about Zed
|
||||
- name: Theme Request
|
||||
url: https://github.com/zed-industries/extensions/issues/new/choose
|
||||
about: Request a theme in the extensions repository
|
||||
- name: Top-Ranking Issues
|
||||
url: https://github.com/zed-industries/zed/issues/5393
|
||||
about: See an overview of the most popular Zed issues
|
||||
- name: Platform Support
|
||||
url: https://github.com/zed-industries/zed/issues/5391
|
||||
about: A quick note on platform support
|
||||
- name: Positive Feedback
|
||||
url: https://github.com/zed-industries/zed/discussions/5397
|
||||
about: A central location for kind words about Zed
|
||||
|
||||
4
.github/pull_request_template.md
vendored
@@ -4,8 +4,8 @@ Release Notes:
|
||||
|
||||
- Added/Fixed/Improved ... ([#<public_issue_number_if_exists>](https://github.com/zed-industries/zed/issues/<public_issue_number_if_exists>)).
|
||||
|
||||
Optionally, include screenshots / media showcasing your addition that can be included in the release notes.
|
||||
|
||||
**or**
|
||||
|
||||
- N/A
|
||||
|
||||
Optionally, include screenshots / media showcasing your addition that can be included in the release notes.
|
||||
|
||||
35
.github/workflows/ci.yml
vendored
@@ -51,9 +51,6 @@ jobs:
|
||||
- name: Run style checks
|
||||
uses: ./.github/actions/check_style
|
||||
|
||||
- name: Check unused dependencies
|
||||
uses: bnjbvr/cargo-machete@main
|
||||
|
||||
- name: Ensure fresh merge
|
||||
shell: bash -euxo pipefail {0}
|
||||
run: |
|
||||
@@ -110,10 +107,16 @@ jobs:
|
||||
clean: false
|
||||
submodules: "recursive"
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: swatinem/rust-cache@v2
|
||||
- name: Restore from cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/rust-toolchain.toml') }}-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- name: configure linux
|
||||
shell: bash -euxo pipefail {0}
|
||||
@@ -137,14 +140,20 @@ jobs:
|
||||
clean: false
|
||||
submodules: "recursive"
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: swatinem/rust-cache@v2
|
||||
- name: Restore from cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
|
||||
- name: cargo clippy
|
||||
shell: bash -euxo pipefail {0}
|
||||
run: script/clippy
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/rust-toolchain.toml') }}-${{ hashFiles('**/Cargo.lock') }}
|
||||
# todo!(windows): Actually run clippy
|
||||
#- name: cargo clippy
|
||||
# shell: bash -euxo pipefail {0}
|
||||
# run: script/clippy
|
||||
|
||||
- name: Build Zed
|
||||
run: cargo build -p zed
|
||||
|
||||
10
.github/workflows/deploy_collab.yml
vendored
@@ -120,12 +120,6 @@ jobs:
|
||||
export ZED_DO_CERTIFICATE_ID=$(doctl compute certificate list --format ID --no-header)
|
||||
export ZED_IMAGE_ID="registry.digitalocean.com/zed/collab:${GITHUB_SHA}"
|
||||
|
||||
export ZED_SERVICE_NAME=collab
|
||||
envsubst < crates/collab/k8s/collab.template.yml | kubectl apply -f -
|
||||
kubectl -n "$ZED_KUBE_NAMESPACE" rollout status deployment/$ZED_SERVICE_NAME --watch
|
||||
echo "deployed ${ZED_SERVICE_NAME} to ${ZED_KUBE_NAMESPACE}"
|
||||
|
||||
export ZED_SERVICE_NAME=api
|
||||
envsubst < crates/collab/k8s/collab.template.yml | kubectl apply -f -
|
||||
kubectl -n "$ZED_KUBE_NAMESPACE" rollout status deployment/$ZED_SERVICE_NAME --watch
|
||||
echo "deployed ${ZED_SERVICE_NAME} to ${ZED_KUBE_NAMESPACE}"
|
||||
kubectl -n "$ZED_KUBE_NAMESPACE" rollout status deployment/collab --watch
|
||||
echo "deployed collab.template.yml to ${ZED_KUBE_NAMESPACE}"
|
||||
|
||||
@@ -9,11 +9,6 @@
|
||||
"format_on_save": "off"
|
||||
},
|
||||
"YAML": {
|
||||
"tab_size": 2,
|
||||
"formatter": "prettier"
|
||||
},
|
||||
"JSON": {
|
||||
"tab_size": 2,
|
||||
"formatter": "prettier"
|
||||
}
|
||||
},
|
||||
|
||||
1296
Cargo.lock
generated
40
Cargo.toml
@@ -16,7 +16,6 @@ members = [
|
||||
"crates/collab_ui",
|
||||
"crates/collections",
|
||||
"crates/command_palette",
|
||||
"crates/command_palette_hooks",
|
||||
"crates/copilot",
|
||||
"crates/copilot_ui",
|
||||
"crates/db",
|
||||
@@ -39,7 +38,6 @@ members = [
|
||||
"crates/language",
|
||||
"crates/language_selector",
|
||||
"crates/language_tools",
|
||||
"crates/languages",
|
||||
"crates/live_kit_client",
|
||||
"crates/live_kit_server",
|
||||
"crates/lsp",
|
||||
@@ -51,9 +49,10 @@ members = [
|
||||
"crates/notifications",
|
||||
"crates/outline",
|
||||
"crates/picker",
|
||||
"crates/plugin",
|
||||
"crates/plugin_macros",
|
||||
"crates/prettier",
|
||||
"crates/project",
|
||||
"crates/project_core",
|
||||
"crates/project_panel",
|
||||
"crates/project_symbols",
|
||||
"crates/quick_action_bar",
|
||||
@@ -81,8 +80,6 @@ members = [
|
||||
"crates/theme",
|
||||
"crates/theme_importer",
|
||||
"crates/theme_selector",
|
||||
"crates/telemetry_events",
|
||||
"crates/time_format",
|
||||
"crates/ui",
|
||||
"crates/util",
|
||||
"crates/vcs_menu",
|
||||
@@ -113,7 +110,6 @@ collab_ui = { path = "crates/collab_ui" }
|
||||
collections = { path = "crates/collections" }
|
||||
color = { path = "crates/color" }
|
||||
command_palette = { path = "crates/command_palette" }
|
||||
command_palette_hooks = { path = "crates/command_palette_hooks" }
|
||||
copilot = { path = "crates/copilot" }
|
||||
copilot_ui = { path = "crates/copilot_ui" }
|
||||
db = { path = "crates/db" }
|
||||
@@ -136,7 +132,6 @@ journal = { path = "crates/journal" }
|
||||
language = { path = "crates/language" }
|
||||
language_selector = { path = "crates/language_selector" }
|
||||
language_tools = { path = "crates/language_tools" }
|
||||
languages = { path = "crates/languages" }
|
||||
live_kit_client = { path = "crates/live_kit_client" }
|
||||
live_kit_server = { path = "crates/live_kit_server" }
|
||||
lsp = { path = "crates/lsp" }
|
||||
@@ -152,7 +147,6 @@ plugin = { path = "crates/plugin" }
|
||||
plugin_macros = { path = "crates/plugin_macros" }
|
||||
prettier = { path = "crates/prettier" }
|
||||
project = { path = "crates/project" }
|
||||
project_core = { path = "crates/project_core" }
|
||||
project_panel = { path = "crates/project_panel" }
|
||||
project_symbols = { path = "crates/project_symbols" }
|
||||
quick_action_bar = { path = "crates/quick_action_bar" }
|
||||
@@ -178,8 +172,6 @@ text = { path = "crates/text" }
|
||||
theme = { path = "crates/theme" }
|
||||
theme_importer = { path = "crates/theme_importer" }
|
||||
theme_selector = { path = "crates/theme_selector" }
|
||||
telemetry_events = { path = "crates/telemetry_events" }
|
||||
time_format = { path = "crates/time_format" }
|
||||
ui = { path = "crates/ui" }
|
||||
util = { path = "crates/util" }
|
||||
vcs_menu = { path = "crates/vcs_menu" }
|
||||
@@ -197,32 +189,25 @@ blade-graphics = { git = "https://github.com/kvark/blade", rev = "e9d93a4d41f394
|
||||
blade-macros = { git = "https://github.com/kvark/blade", rev = "e9d93a4d41f3946a03ffb76136290d6ccf7f2b80" }
|
||||
blade-rwh = { package = "raw-window-handle", version = "0.5" }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
clickhouse = { version = "0.11.6" }
|
||||
ctor = "0.2.6"
|
||||
core-foundation = { version = "0.9.3" }
|
||||
core-foundation-sys = "0.8.6"
|
||||
derive_more = "0.99.17"
|
||||
env_logger = "0.9"
|
||||
futures = "0.3"
|
||||
git2 = { version = "0.15", default-features = false }
|
||||
globset = "0.4"
|
||||
hex = "0.4.3"
|
||||
ignore = "0.4.22"
|
||||
indoc = "1"
|
||||
# We explicitly disable http2 support in isahc.
|
||||
# We explicitly disable a http2 support in isahc.
|
||||
isahc = { version = "1.7.2", default-features = false, features = ["static-curl", "text-decoding"] }
|
||||
itertools = "0.11.0"
|
||||
lazy_static = "1.4.0"
|
||||
linkify = "0.10.0"
|
||||
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
|
||||
ordered-float = "2.1.1"
|
||||
palette = { version = "0.7.5", default-features = false, features = ["std"] }
|
||||
parking_lot = "0.11.1"
|
||||
profiling = "1"
|
||||
postage = { version = "0.5", features = ["futures-traits"] }
|
||||
pretty_assertions = "1.3.0"
|
||||
prost = "0.8"
|
||||
pulldown-cmark = { version = "0.10.0", default-features = false }
|
||||
pulldown-cmark = { version = "0.9.2", default-features = false }
|
||||
rand = "0.8.5"
|
||||
refineable = { path = "./crates/refineable" }
|
||||
regex = "1.5"
|
||||
@@ -235,8 +220,6 @@ 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_repr = "0.1"
|
||||
sha2 = "0.10"
|
||||
shellexpand = "2.1.0"
|
||||
smallvec = { version = "1.6", features = ["union"] }
|
||||
smol = "1.2"
|
||||
strum = { version = "0.25.0", features = ["derive"] }
|
||||
@@ -246,7 +229,6 @@ thiserror = "1.0.29"
|
||||
tiktoken-rs = "0.5.7"
|
||||
time = { version = "0.3", features = ["serde", "serde-well-known", "formatting"] }
|
||||
toml = "0.8"
|
||||
tower-http = "0.4.4"
|
||||
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" }
|
||||
@@ -296,12 +278,13 @@ tree-sitter-zig = { git = "https://github.com/maxxnino/tree-sitter-zig", rev = "
|
||||
unindent = "0.1.7"
|
||||
url = "2.2"
|
||||
uuid = { version = "1.1.2", features = ["v4"] }
|
||||
wasmtime = "18.0"
|
||||
wasmtime = "16"
|
||||
which = "6.0.0"
|
||||
sys-locale = "0.3.1"
|
||||
|
||||
[patch.crates-io]
|
||||
tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "e4a23971ec3071a09c1e84816954c98f96e98e52" }
|
||||
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" }
|
||||
|
||||
@@ -314,15 +297,10 @@ debug = "limited"
|
||||
split-debuginfo = "off"
|
||||
debug = "full"
|
||||
|
||||
[profile.dev.package]
|
||||
taffy = { opt-level = 3 }
|
||||
cranelift-codegen = { opt-level = 3 }
|
||||
wasmtime-cranelift = { opt-level = 3 }
|
||||
[profile.dev.package.taffy]
|
||||
opt-level = 3
|
||||
|
||||
[profile.release]
|
||||
debug = "limited"
|
||||
lto = "thin"
|
||||
codegen-units = 1
|
||||
|
||||
[workspace.metadata.cargo-machete]
|
||||
ignored = ["bindgen", "cbindgen", "prost_build", "serde"]
|
||||
|
||||
2
Procfile
@@ -1,3 +1,3 @@
|
||||
collab: RUST_LOG=${RUST_LOG:-warn,tower_http=info,collab=info} cargo run --package=collab serve
|
||||
collab: RUST_LOG=${RUST_LOG:-warn,collab=info} cargo run --package=collab serve
|
||||
livekit: livekit-server --dev
|
||||
blob_store: MINIO_ROOT_USER=the-blob-store-access-key MINIO_ROOT_PASSWORD=the-blob-store-secret-key minio server .blob_store
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 22.596c6.628 0 12-4.338 12-9.688 0-3.318-2.057-6.248-5.219-7.986-1.286-.715-2.297-1.357-3.139-1.89C14.058 2.025 13.08 1.404 12 1.404c-1.097 0-2.334.785-3.966 1.821a49.92 49.92 0 0 1-2.816 1.697C2.057 6.66 0 9.59 0 12.908c0 5.35 5.372 9.687 12 9.687v.001ZM10.599 4.715c.334-.759.503-1.58.498-2.409 0-.145.202-.187.23-.029.658 2.783-.902 4.162-2.057 4.624-.124.048-.199-.121-.103-.209a5.763 5.763 0 0 0 1.432-1.977Zm2.058-.102a5.82 5.82 0 0 0-.782-2.306v-.016c-.069-.123.086-.263.185-.172 1.962 2.111 1.307 4.067.556 5.051-.082.103-.23-.003-.189-.126a5.85 5.85 0 0 0 .23-2.431Zm1.776-.561a5.727 5.727 0 0 0-1.612-1.806v-.014c-.112-.085-.024-.274.114-.218 2.595 1.087 2.774 3.18 2.459 4.407a.116.116 0 0 1-.049.071.11.11 0 0 1-.153-.026.122.122 0 0 1-.022-.083 5.891 5.891 0 0 0-.737-2.331Zm-5.087.561c-.617.546-1.282.76-2.063 1-.117 0-.195-.078-.156-.181 1.752-.909 2.376-1.649 2.999-2.778 0 0 .155-.118.188.085 0 .304-.349 1.329-.968 1.874Zm4.945 11.237a2.957 2.957 0 0 1-.937 1.553c-.346.346-.8.565-1.286.62a2.178 2.178 0 0 1-1.327-.62 2.955 2.955 0 0 1-.925-1.553.244.244 0 0 1 .064-.198.234.234 0 0 1 .193-.069h3.965a.226.226 0 0 1 .19.07c.05.053.073.125.063.197Zm-5.458-2.176a1.862 1.862 0 0 1-2.384-.245 1.98 1.98 0 0 1-.233-2.447c.207-.319.503-.566.848-.713a1.84 1.84 0 0 1 1.092-.11c.366.075.703.261.967.531a1.98 1.98 0 0 1 .408 2.114 1.931 1.931 0 0 1-.698.869v.001Zm8.495.005a1.86 1.86 0 0 1-2.381-.253 1.964 1.964 0 0 1-.547-1.366c0-.384.11-.76.32-1.079.207-.319.503-.567.849-.713a1.844 1.844 0 0 1 1.093-.108c.367.076.704.262.968.534a1.98 1.98 0 0 1 .4 2.117 1.932 1.932 0 0 1-.702.868Z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14px" height="14px" viewBox="0 0 14 14" version="1.1">
|
||||
<g id="surface1">
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 9.203125 3.410156 C 8.792969 3 8.378906 2.597656 7.957031 2.207031 C 7.835938 2.078125 7.664062 1.996094 7.476562 1.996094 C 7.472656 1.996094 7.472656 1.996094 7.46875 1.996094 C 7.324219 2.011719 7.1875 2.042969 7.0625 2.089844 L 7.074219 2.085938 L 4.425781 3.40625 Z M 3.71875 3.71875 L 3.71875 9.078125 C 3.714844 9.109375 3.710938 9.144531 3.710938 9.179688 C 3.710938 9.40625 3.800781 9.613281 3.945312 9.765625 L 6.183594 12.003906 L 10.066406 12.003906 L 10.066406 10.066406 Z M 3.40625 3.40625 C 3.40625 3.40625 5.707031 2.257812 6.855469 1.683594 C 7.039062 1.59375 7.25 1.539062 7.476562 1.539062 C 7.496094 1.539062 7.515625 1.539062 7.53125 1.539062 C 7.824219 1.597656 8.082031 1.722656 8.296875 1.902344 L 8.292969 1.898438 L 12.460938 6.066406 L 12.460938 10.519531 L 10.519531 10.519531 L 10.519531 12.460938 L 5.996094 12.460938 L 1.898438 8.367188 C 1.683594 8.140625 1.546875 7.839844 1.542969 7.503906 C 1.558594 7.316406 1.609375 7.148438 1.6875 6.992188 L 1.683594 7 Z M 3.40625 3.40625 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14px" height="14px" viewBox="0 0 14 14" version="1.1">
|
||||
<g id="surface1">
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 13.421875 5.847656 C 13.152344 5.671875 12.824219 5.566406 12.46875 5.566406 C 12.429688 5.566406 12.390625 5.566406 12.351562 5.570312 L 12.355469 5.570312 C 12.160156 5.570312 11.96875 5.585938 11.785156 5.621094 L 11.804688 5.617188 C 11.699219 5.023438 11.355469 4.523438 10.875 4.21875 L 10.867188 4.214844 L 10.679688 4.105469 L 10.554688 4.285156 C 10.410156 4.507812 10.292969 4.769531 10.226562 5.050781 L 10.222656 5.066406 C 10.183594 5.207031 10.160156 5.371094 10.160156 5.539062 C 10.160156 5.902344 10.265625 6.242188 10.445312 6.527344 L 10.441406 6.519531 C 10.164062 6.648438 9.839844 6.734375 9.496094 6.75 L 0.859375 6.75 C 0.632812 6.75 0.449219 6.929688 0.449219 7.15625 C 0.449219 7.179688 0.449219 7.210938 0.449219 7.238281 C 0.449219 8.003906 0.585938 8.738281 0.839844 9.417969 L 0.828125 9.375 C 1.070312 10.089844 1.53125 10.675781 2.128906 11.070312 L 2.144531 11.078125 C 2.90625 11.476562 3.808594 11.707031 4.765625 11.707031 C 4.855469 11.707031 4.945312 11.703125 5.035156 11.699219 L 5.023438 11.699219 C 5.03125 11.699219 5.039062 11.699219 5.050781 11.699219 C 5.605469 11.699219 6.148438 11.648438 6.675781 11.546875 L 6.621094 11.554688 C 7.40625 11.410156 8.109375 11.144531 8.742188 10.78125 L 8.710938 10.796875 C 9.261719 10.476562 9.730469 10.085938 10.128906 9.636719 L 10.136719 9.628906 C 10.714844 8.949219 11.1875 8.152344 11.511719 7.28125 L 11.527344 7.226562 L 11.648438 7.226562 C 11.671875 7.230469 11.703125 7.230469 11.730469 7.230469 C 12.265625 7.230469 12.753906 7.019531 13.113281 6.675781 C 13.277344 6.519531 13.410156 6.332031 13.496094 6.117188 L 13.5 6.105469 L 13.550781 5.949219 Z M 1.65625 6.496094 L 2.816406 6.496094 C 2.871094 6.496094 2.917969 6.449219 2.917969 6.394531 L 2.917969 5.363281 C 2.917969 5.308594 2.871094 5.265625 2.816406 5.261719 L 1.65625 5.261719 C 1.601562 5.265625 1.558594 5.308594 1.558594 5.363281 L 1.558594 6.394531 C 1.558594 6.453125 1.601562 6.496094 1.65625 6.496094 C 1.65625 6.496094 1.65625 6.496094 1.660156 6.496094 Z M 3.253906 6.496094 L 4.410156 6.496094 C 4.464844 6.496094 4.511719 6.453125 4.511719 6.394531 L 4.511719 5.363281 C 4.511719 5.308594 4.464844 5.265625 4.410156 5.261719 L 3.25 5.261719 C 3.195312 5.261719 3.152344 5.308594 3.152344 5.363281 L 3.152344 6.394531 C 3.152344 6.453125 3.195312 6.496094 3.25 6.496094 Z M 4.871094 6.496094 L 6.027344 6.496094 C 6.082031 6.496094 6.128906 6.449219 6.128906 6.394531 L 6.128906 5.363281 C 6.128906 5.308594 6.082031 5.265625 6.027344 5.261719 L 4.871094 5.261719 C 4.816406 5.265625 4.769531 5.308594 4.769531 5.363281 L 4.769531 6.394531 C 4.769531 6.449219 4.816406 6.496094 4.871094 6.496094 Z M 6.46875 6.496094 L 7.625 6.496094 C 7.683594 6.496094 7.726562 6.453125 7.726562 6.394531 L 7.726562 5.363281 C 7.726562 5.308594 7.683594 5.261719 7.625 5.261719 L 6.46875 5.261719 C 6.414062 5.261719 6.367188 5.308594 6.367188 5.363281 L 6.367188 6.394531 C 6.367188 6.453125 6.414062 6.496094 6.46875 6.496094 Z M 3.253906 5.015625 L 4.410156 5.015625 C 4.464844 5.015625 4.511719 4.96875 4.511719 4.914062 L 4.511719 3.882812 C 4.511719 3.828125 4.464844 3.78125 4.410156 3.78125 L 3.253906 3.78125 C 3.195312 3.78125 3.152344 3.828125 3.152344 3.882812 L 3.152344 4.914062 C 3.152344 4.96875 3.195312 5.015625 3.253906 5.015625 Z M 4.871094 5.015625 L 6.027344 5.015625 C 6.082031 5.015625 6.128906 4.96875 6.128906 4.914062 L 6.128906 3.882812 C 6.128906 3.828125 6.082031 3.78125 6.027344 3.78125 L 4.871094 3.78125 C 4.816406 3.78125 4.769531 3.828125 4.769531 3.882812 L 4.769531 4.914062 C 4.769531 4.96875 4.816406 5.015625 4.871094 5.015625 Z M 6.46875 5.015625 L 7.625 5.015625 C 7.683594 5.015625 7.726562 4.96875 7.726562 4.914062 L 7.726562 3.882812 C 7.726562 3.828125 7.683594 3.78125 7.625 3.78125 L 6.46875 3.78125 C 6.414062 3.78125 6.367188 3.828125 6.367188 3.882812 L 6.367188 4.914062 C 6.367188 4.96875 6.414062 5.015625 6.46875 5.015625 Z M 6.46875 3.53125 L 7.625 3.53125 C 7.683594 3.53125 7.726562 3.488281 7.726562 3.429688 L 7.726562 2.398438 C 7.726562 2.34375 7.683594 2.296875 7.625 2.296875 L 6.46875 2.296875 C 6.414062 2.296875 6.367188 2.34375 6.367188 2.398438 L 6.367188 3.429688 C 6.367188 3.488281 6.414062 3.53125 6.46875 3.53125 Z M 8.082031 6.496094 L 9.238281 6.496094 C 9.296875 6.496094 9.339844 6.453125 9.339844 6.394531 L 9.339844 5.363281 C 9.339844 5.308594 9.296875 5.261719 9.238281 5.261719 L 8.082031 5.261719 C 8.027344 5.261719 7.980469 5.308594 7.980469 5.363281 L 7.980469 6.394531 C 7.980469 6.449219 8.027344 6.496094 8.082031 6.496094 Z M 8.082031 6.496094 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.8 KiB |
@@ -1,319 +1,269 @@
|
||||
{
|
||||
"stems": {
|
||||
"Podfile": "ruby",
|
||||
"Procfile": "heroku",
|
||||
"Dockerfile": "docker"
|
||||
},
|
||||
"suffixes": {
|
||||
"astro": "astro",
|
||||
"Emakefile": "erlang",
|
||||
"aac": "audio",
|
||||
"accdb": "storage",
|
||||
"app.src": "erlang",
|
||||
"avi": "video",
|
||||
"avif": "image",
|
||||
"bak": "backup",
|
||||
"bash": "terminal",
|
||||
"bash_aliases": "terminal",
|
||||
"bash_logout": "terminal",
|
||||
"bash_profile": "terminal",
|
||||
"bashrc": "terminal",
|
||||
"bmp": "image",
|
||||
"c": "code",
|
||||
"cc": "code",
|
||||
"cjs": "code",
|
||||
"conf": "settings",
|
||||
"cpp": "code",
|
||||
"css": "css",
|
||||
"csv": "storage",
|
||||
"cts": "typescript",
|
||||
"dart": "dart",
|
||||
"dat": "storage",
|
||||
"db": "storage",
|
||||
"dbf": "storage",
|
||||
"dll": "storage",
|
||||
"doc": "document",
|
||||
"docx": "document",
|
||||
"eex": "elixir",
|
||||
"elm": "elm",
|
||||
"erl": "erlang",
|
||||
"escript": "erlang",
|
||||
"eslintrc": "eslint",
|
||||
"eslintrc.js": "eslint",
|
||||
"eslintrc.json": "eslint",
|
||||
"ex": "elixir",
|
||||
"exs": "elixir",
|
||||
"fish": "terminal",
|
||||
"flac": "audio",
|
||||
"fmp": "storage",
|
||||
"fp7": "storage",
|
||||
"frm": "storage",
|
||||
"gdb": "storage",
|
||||
"gif": "image",
|
||||
"gitattributes": "vcs",
|
||||
"gitignore": "vcs",
|
||||
"gitkeep": "vcs",
|
||||
"gitmodules": "vcs",
|
||||
"go": "go",
|
||||
"graphql": "graphql",
|
||||
"h": "code",
|
||||
"handlebars": "code",
|
||||
"hbs": "template",
|
||||
"heex": "elixir",
|
||||
"heif": "image",
|
||||
"heic": "image",
|
||||
"hrl": "erlang",
|
||||
"hs": "haskell",
|
||||
"htm": "template",
|
||||
"html": "template",
|
||||
"ib": "storage",
|
||||
"ico": "image",
|
||||
"ini": "settings",
|
||||
"j2k": "image",
|
||||
"java": "java",
|
||||
"jfif": "image",
|
||||
"jp2": "image",
|
||||
"jpeg": "image",
|
||||
"jpg": "image",
|
||||
"js": "code",
|
||||
"json": "storage",
|
||||
"jsonc": "storage",
|
||||
"jxl": "image",
|
||||
"kt": "kotlin",
|
||||
"ldf": "storage",
|
||||
"lock": "lock",
|
||||
"lockb": "bun",
|
||||
"log": "log",
|
||||
"lua": "lua",
|
||||
"m4a": "audio",
|
||||
"m4v": "video",
|
||||
"md": "document",
|
||||
"mdb": "storage",
|
||||
"mdf": "storage",
|
||||
"mdx": "document",
|
||||
"metadata": "code",
|
||||
"mkv": "video",
|
||||
"mjs": "code",
|
||||
"mka": "audio",
|
||||
"ml": "ocaml",
|
||||
"mli": "ocaml",
|
||||
"mov": "video",
|
||||
"mp3": "audio",
|
||||
"mp4": "video",
|
||||
"mts": "typescript",
|
||||
"myd": "storage",
|
||||
"myi": "storage",
|
||||
"nu": "terminal",
|
||||
"odp": "document",
|
||||
"ods": "document",
|
||||
"odt": "document",
|
||||
"ogg": "audio",
|
||||
"opus": "audio",
|
||||
"otf": "font",
|
||||
"pdb": "storage",
|
||||
"pdf": "document",
|
||||
"php": "php",
|
||||
"plist": "template",
|
||||
"png": "image",
|
||||
"ppt": "document",
|
||||
"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",
|
||||
"rs": "rust",
|
||||
"r": "r",
|
||||
"rtf": "document",
|
||||
"sav": "storage",
|
||||
"scm": "code",
|
||||
"sdf": "storage",
|
||||
"sh": "terminal",
|
||||
"sqlite": "storage",
|
||||
"svelte": "template",
|
||||
"svg": "image",
|
||||
"swift": "swift",
|
||||
"tf": "terraform",
|
||||
"tfvars": "terraform",
|
||||
"tiff": "image",
|
||||
"toml": "toml",
|
||||
"ts": "typescript",
|
||||
"tsv": "storage",
|
||||
"ttf": "font",
|
||||
"tsx": "code",
|
||||
"txt": "document",
|
||||
"vue": "vue",
|
||||
"wav": "audio",
|
||||
"webm": "video",
|
||||
"webp": "image",
|
||||
"wma": "audio",
|
||||
"wmv": "video",
|
||||
"wv": "audio",
|
||||
"xls": "document",
|
||||
"xlsx": "document",
|
||||
"xml": "template",
|
||||
"xrl": "erlang",
|
||||
"yaml": "settings",
|
||||
"yml": "settings",
|
||||
"yrl": "erlang",
|
||||
"zlogin": "terminal",
|
||||
"zsh": "terminal",
|
||||
"zsh_aliases": "terminal",
|
||||
"zsh_histfile": "terminal",
|
||||
"zsh_profile": "terminal",
|
||||
"zshenv": "terminal",
|
||||
"zshrc": "terminal"
|
||||
},
|
||||
"types": {
|
||||
"astro": {
|
||||
"icon": "icons/file_icons/astro.svg"
|
||||
"suffixes": {
|
||||
"astro": "astro",
|
||||
"Emakefile": "erlang",
|
||||
"aac": "audio",
|
||||
"accdb": "storage",
|
||||
"app.src": "erlang",
|
||||
"avi": "video",
|
||||
"avif": "image",
|
||||
"bak": "backup",
|
||||
"bash": "terminal",
|
||||
"bash_aliases": "terminal",
|
||||
"bash_logout": "terminal",
|
||||
"bash_profile": "terminal",
|
||||
"bashrc": "terminal",
|
||||
"bmp": "image",
|
||||
"c": "code",
|
||||
"cc": "code",
|
||||
"cjs": "code",
|
||||
"conf": "settings",
|
||||
"cpp": "code",
|
||||
"css": "css",
|
||||
"csv": "storage",
|
||||
"cts": "typescript",
|
||||
"dat": "storage",
|
||||
"db": "storage",
|
||||
"dbf": "storage",
|
||||
"dll": "storage",
|
||||
"doc": "document",
|
||||
"docx": "document",
|
||||
"eex": "elixir",
|
||||
"elm": "elm",
|
||||
"erl": "erlang",
|
||||
"escript": "erlang",
|
||||
"eslintrc": "eslint",
|
||||
"eslintrc.js": "eslint",
|
||||
"eslintrc.json": "eslint",
|
||||
"ex": "elixir",
|
||||
"exs": "elixir",
|
||||
"fish": "terminal",
|
||||
"flac": "audio",
|
||||
"fmp": "storage",
|
||||
"fp7": "storage",
|
||||
"frm": "storage",
|
||||
"gdb": "storage",
|
||||
"gif": "image",
|
||||
"gitattributes": "vcs",
|
||||
"gitignore": "vcs",
|
||||
"gitkeep": "vcs",
|
||||
"gitmodules": "vcs",
|
||||
"go": "go",
|
||||
"h": "code",
|
||||
"handlebars": "code",
|
||||
"hbs": "template",
|
||||
"heex": "elixir",
|
||||
"heif": "image",
|
||||
"heic": "image",
|
||||
"hrl": "erlang",
|
||||
"hs": "haskell",
|
||||
"htm": "template",
|
||||
"html": "template",
|
||||
"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",
|
||||
"lua": "lua",
|
||||
"m4a": "audio",
|
||||
"m4v": "video",
|
||||
"md": "document",
|
||||
"mdb": "storage",
|
||||
"mdf": "storage",
|
||||
"mdx": "document",
|
||||
"mkv": "video",
|
||||
"mjs": "code",
|
||||
"mka": "audio",
|
||||
"ml": "ocaml",
|
||||
"mli": "ocaml",
|
||||
"mov": "video",
|
||||
"mp3": "audio",
|
||||
"mp4": "video",
|
||||
"mts": "typescript",
|
||||
"myd": "storage",
|
||||
"myi": "storage",
|
||||
"odp": "document",
|
||||
"ods": "document",
|
||||
"odt": "document",
|
||||
"ogg": "audio",
|
||||
"opus": "audio",
|
||||
"pdb": "storage",
|
||||
"pdf": "document",
|
||||
"php": "php",
|
||||
"png": "image",
|
||||
"ppt": "document",
|
||||
"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",
|
||||
"rs": "rust",
|
||||
"rtf": "document",
|
||||
"sav": "storage",
|
||||
"scm": "code",
|
||||
"sdf": "storage",
|
||||
"sh": "terminal",
|
||||
"sqlite": "storage",
|
||||
"svelte": "template",
|
||||
"svg": "image",
|
||||
"swift": "code",
|
||||
"tiff": "image",
|
||||
"toml": "toml",
|
||||
"ts": "typescript",
|
||||
"tsv": "storage",
|
||||
"tsx": "code",
|
||||
"txt": "document",
|
||||
"vue": "vue",
|
||||
"wav": "audio",
|
||||
"webm": "video",
|
||||
"webp": "image",
|
||||
"wma": "audio",
|
||||
"wmv": "video",
|
||||
"wv": "audio",
|
||||
"xls": "document",
|
||||
"xlsx": "document",
|
||||
"xml": "template",
|
||||
"xrl": "erlang",
|
||||
"yaml": "settings",
|
||||
"yml": "settings",
|
||||
"yrl": "erlang",
|
||||
"zlogin": "terminal",
|
||||
"zsh": "terminal",
|
||||
"zsh_aliases": "terminal",
|
||||
"zsh_histfile": "terminal",
|
||||
"zsh_profile": "terminal",
|
||||
"zshenv": "terminal",
|
||||
"zshrc": "terminal"
|
||||
},
|
||||
"audio": {
|
||||
"icon": "icons/file_icons/audio.svg"
|
||||
},
|
||||
"code": {
|
||||
"icon": "icons/file_icons/code.svg"
|
||||
},
|
||||
"collapsed_chevron": {
|
||||
"icon": "icons/file_icons/chevron_right.svg"
|
||||
},
|
||||
"collapsed_folder": {
|
||||
"icon": "icons/file_icons/folder.svg"
|
||||
},
|
||||
"css": {
|
||||
"icon": "icons/file_icons/css.svg"
|
||||
},
|
||||
"dart": {
|
||||
"icon": "icons/file_icons/dart.svg"
|
||||
},
|
||||
"default": {
|
||||
"icon": "icons/file_icons/file.svg"
|
||||
},
|
||||
"docker": {
|
||||
"icon": "icons/file_icons/docker.svg"
|
||||
},
|
||||
"document": {
|
||||
"icon": "icons/file_icons/book.svg"
|
||||
},
|
||||
"elixir": {
|
||||
"icon": "icons/file_icons/elixir.svg"
|
||||
},
|
||||
"elm": {
|
||||
"icon": "icons/file_icons/elm.svg"
|
||||
},
|
||||
"erlang": {
|
||||
"icon": "icons/file_icons/erlang.svg"
|
||||
},
|
||||
"eslint": {
|
||||
"icon": "icons/file_icons/eslint.svg"
|
||||
},
|
||||
"expanded_chevron": {
|
||||
"icon": "icons/file_icons/chevron_down.svg"
|
||||
},
|
||||
"expanded_folder": {
|
||||
"icon": "icons/file_icons/folder_open.svg"
|
||||
},
|
||||
"font": {
|
||||
"icon": "icons/file_icons/font.svg"
|
||||
},
|
||||
"haskell": {
|
||||
"icon": "icons/file_icons/haskell.svg"
|
||||
},
|
||||
"heroku": {
|
||||
"icon": "icons/file_icons/heroku.svg"
|
||||
},
|
||||
"go": {
|
||||
"icon": "icons/file_icons/go.svg"
|
||||
},
|
||||
"graphql": {
|
||||
"icon": "icons/file_icons/graphql.svg"
|
||||
},
|
||||
"image": {
|
||||
"icon": "icons/file_icons/image.svg"
|
||||
},
|
||||
"java": {
|
||||
"icon": "icons/file_icons/java.svg"
|
||||
},
|
||||
"kotlin": {
|
||||
"icon": "icons/file_icons/kotlin.svg"
|
||||
},
|
||||
"lock": {
|
||||
"icon": "icons/file_icons/lock.svg"
|
||||
},
|
||||
"bun": {
|
||||
"icon": "icons/file_icons/bun.svg"
|
||||
},
|
||||
"log": {
|
||||
"icon": "icons/file_icons/info.svg"
|
||||
},
|
||||
"lua": {
|
||||
"icon": "icons/file_icons/lua.svg"
|
||||
},
|
||||
"ocaml": {
|
||||
"icon": "icons/file_icons/ocaml.svg"
|
||||
},
|
||||
"phoenix": {
|
||||
"icon": "icons/file_icons/phoenix.svg"
|
||||
},
|
||||
"php": {
|
||||
"icon": "icons/file_icons/php.svg"
|
||||
},
|
||||
"prettier": {
|
||||
"icon": "icons/file_icons/prettier.svg"
|
||||
},
|
||||
"prisma": {
|
||||
"icon": "icons/file_icons/prisma.svg"
|
||||
},
|
||||
"python": {
|
||||
"icon": "icons/file_icons/python.svg"
|
||||
},
|
||||
"ruby": {
|
||||
"icon": "icons/file_icons/ruby.svg"
|
||||
},
|
||||
"rust": {
|
||||
"icon": "icons/file_icons/rust.svg"
|
||||
},
|
||||
"r": {
|
||||
"icon": "icons/file_icons/r.svg"
|
||||
},
|
||||
"settings": {
|
||||
"icon": "icons/file_icons/settings.svg"
|
||||
},
|
||||
"storage": {
|
||||
"icon": "icons/file_icons/database.svg"
|
||||
},
|
||||
"swift": {
|
||||
"icon": "icons/file_icons/swift.svg"
|
||||
},
|
||||
"template": {
|
||||
"icon": "icons/file_icons/html.svg"
|
||||
},
|
||||
"terraform": {
|
||||
"icon": "icons/file_icons/terraform.svg"
|
||||
},
|
||||
"terminal": {
|
||||
"icon": "icons/file_icons/terminal.svg"
|
||||
},
|
||||
"toml": {
|
||||
"icon": "icons/file_icons/toml.svg"
|
||||
},
|
||||
"typescript": {
|
||||
"icon": "icons/file_icons/typescript.svg"
|
||||
},
|
||||
"vcs": {
|
||||
"icon": "icons/file_icons/git.svg"
|
||||
},
|
||||
"video": {
|
||||
"icon": "icons/file_icons/video.svg"
|
||||
},
|
||||
"vue": {
|
||||
"icon": "icons/file_icons/vue.svg"
|
||||
"types": {
|
||||
"astro": {
|
||||
"icon": "icons/file_icons/astro.svg"
|
||||
},
|
||||
"audio": {
|
||||
"icon": "icons/file_icons/audio.svg"
|
||||
},
|
||||
"code": {
|
||||
"icon": "icons/file_icons/code.svg"
|
||||
},
|
||||
"collapsed_chevron": {
|
||||
"icon": "icons/file_icons/chevron_right.svg"
|
||||
},
|
||||
"collapsed_folder": {
|
||||
"icon": "icons/file_icons/folder.svg"
|
||||
},
|
||||
"css": {
|
||||
"icon": "icons/file_icons/css.svg"
|
||||
},
|
||||
"default": {
|
||||
"icon": "icons/file_icons/file.svg"
|
||||
},
|
||||
"document": {
|
||||
"icon": "icons/file_icons/book.svg"
|
||||
},
|
||||
"elixir": {
|
||||
"icon": "icons/file_icons/elixir.svg"
|
||||
},
|
||||
"elm": {
|
||||
"icon": "icons/file_icons/elm.svg"
|
||||
},
|
||||
"erlang": {
|
||||
"icon": "icons/file_icons/erlang.svg"
|
||||
},
|
||||
"eslint": {
|
||||
"icon": "icons/file_icons/eslint.svg"
|
||||
},
|
||||
"expanded_chevron": {
|
||||
"icon": "icons/file_icons/chevron_down.svg"
|
||||
},
|
||||
"expanded_folder": {
|
||||
"icon": "icons/file_icons/folder_open.svg"
|
||||
},
|
||||
"haskell": {
|
||||
"icon": "icons/file_icons/haskell.svg"
|
||||
},
|
||||
"go": {
|
||||
"icon": "icons/file_icons/go.svg"
|
||||
},
|
||||
"image": {
|
||||
"icon": "icons/file_icons/image.svg"
|
||||
},
|
||||
"lock": {
|
||||
"icon": "icons/file_icons/lock.svg"
|
||||
},
|
||||
"log": {
|
||||
"icon": "icons/file_icons/info.svg"
|
||||
},
|
||||
"lua": {
|
||||
"icon": "icons/file_icons/lua.svg"
|
||||
},
|
||||
"ocaml": {
|
||||
"icon": "icons/file_icons/ocaml.svg"
|
||||
},
|
||||
"phoenix": {
|
||||
"icon": "icons/file_icons/phoenix.svg"
|
||||
},
|
||||
"php": {
|
||||
"icon": "icons/file_icons/php.svg"
|
||||
},
|
||||
"prettier": {
|
||||
"icon": "icons/file_icons/prettier.svg"
|
||||
},
|
||||
"prisma": {
|
||||
"icon": "icons/file_icons/prisma.svg"
|
||||
},
|
||||
"python": {
|
||||
"icon": "icons/file_icons/python.svg"
|
||||
},
|
||||
"ruby": {
|
||||
"icon": "icons/file_icons/ruby.svg"
|
||||
},
|
||||
"rust": {
|
||||
"icon": "icons/file_icons/rust.svg"
|
||||
},
|
||||
"settings": {
|
||||
"icon": "icons/file_icons/settings.svg"
|
||||
},
|
||||
"storage": {
|
||||
"icon": "icons/file_icons/database.svg"
|
||||
},
|
||||
"template": {
|
||||
"icon": "icons/file_icons/html.svg"
|
||||
},
|
||||
"terminal": {
|
||||
"icon": "icons/file_icons/terminal.svg"
|
||||
},
|
||||
"toml": {
|
||||
"icon": "icons/file_icons/toml.svg"
|
||||
},
|
||||
"typescript": {
|
||||
"icon": "icons/file_icons/typescript.svg"
|
||||
},
|
||||
"vcs": {
|
||||
"icon": "icons/file_icons/git.svg"
|
||||
},
|
||||
"video": {
|
||||
"icon": "icons/file_icons/video.svg"
|
||||
},
|
||||
"vue": {
|
||||
"icon": "icons/file_icons/vue.svg"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14px" height="14px" viewBox="0 0 14 14" version="1.1">
|
||||
<g id="surface1">
|
||||
<path style="fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;" d="M 16.001786 21 L 19.497321 21 M 5.997321 21 L 12 3 L 18.002679 21 M 4.502679 21 L 7.998214 21 M 14.997321 14.000893 L 9.002679 14.000893 " transform="matrix(0.486111,0,0,0.486111,1.166667,1.166667)"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 559 B |
@@ -1 +0,0 @@
|
||||
<svg width="14px" height="14px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="#000000"><path fill-rule="evenodd" clip-rule="evenodd" d="M50 6.90308L87.323 28.4515V71.5484L50 93.0968L12.677 71.5484V28.4515L50 6.90308ZM16.8647 30.8693V62.5251L44.2795 15.0414L16.8647 30.8693ZM50 13.5086L18.3975 68.2457H81.6025L50 13.5086ZM77.4148 72.4334H22.5852L50 88.2613L77.4148 72.4334ZM83.1353 62.5251L55.7205 15.0414L83.1353 30.8693V62.5251Z"/><circle cx="50" cy="9.3209" r="8.82"/><circle cx="85.2292" cy="29.6605" r="8.82"/><circle cx="85.2292" cy="70.3396" r="8.82"/><circle cx="50" cy="90.6791" r="8.82"/><circle cx="14.7659" cy="70.3396" r="8.82"/><circle cx="14.7659" cy="29.6605" r="8.82"/></svg>
|
||||
|
Before Width: | Height: | Size: 709 B |
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14px" height="14px" viewBox="0 0 14 14" version="1.1">
|
||||
<g id="surface1">
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 12.023438 0 L 1.976562 0 C 1.277344 0 0.71875 0.558594 0.71875 1.261719 L 0.71875 12.738281 C 0.71875 13.441406 1.277344 14 1.976562 14 L 12.023438 14 C 12.722656 14 13.28125 13.441406 13.28125 12.738281 L 13.28125 1.261719 C 13.28125 0.558594 12.722656 0 12.023438 0 Z M 12.582031 12.738281 C 12.582031 13.054688 12.335938 13.300781 12.023438 13.300781 L 1.976562 13.300781 C 1.664062 13.300781 1.417969 13.054688 1.417969 12.738281 L 1.417969 1.261719 C 1.417969 0.945312 1.664062 0.699219 1.976562 0.699219 L 12.023438 0.699219 C 12.335938 0.699219 12.582031 0.945312 12.582031 1.261719 Z M 3.867188 11.898438 L 5.441406 10.5 L 3.867188 9.101562 Z M 9.539062 6.230469 C 9.257812 5.949219 8.734375 5.601562 7.859375 5.601562 C 6.914062 5.601562 5.933594 5.84375 5.234375 6.089844 L 5.234375 2.101562 L 3.832031 2.101562 L 3.832031 8.15625 L 4.8125 7.699219 C 4.8125 7.699219 6.421875 6.964844 7.824219 6.964844 C 8.523438 6.964844 8.699219 7.351562 8.699219 7.699219 L 8.699219 11.898438 L 10.097656 11.898438 L 10.097656 7.699219 C 10.132812 7.59375 10.132812 6.824219 9.539062 6.230469 Z M 7.683594 4.375 L 9.082031 4.375 C 9.710938 3.640625 10.027344 2.90625 10.132812 2.101562 L 8.734375 2.101562 C 8.59375 2.90625 8.242188 3.640625 7.683594 4.375 Z M 7.683594 4.375 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14px" height="14px" viewBox="0 0 14 14" version="1.1">
|
||||
<g id="surface1">
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 10.644531 9.734375 C 9.640625 9.734375 8.824219 8.917969 8.824219 7.910156 C 8.824219 6.90625 9.640625 6.089844 10.644531 6.089844 C 11.652344 6.089844 12.46875 6.90625 12.46875 7.910156 C 12.46875 8.917969 11.652344 9.734375 10.644531 9.734375 Z M 10.644531 7 C 10.144531 7 9.734375 7.410156 9.734375 7.910156 C 9.734375 8.414062 10.144531 8.824219 10.644531 8.824219 C 11.148438 8.824219 11.558594 8.414062 11.558594 7.910156 C 11.558594 7.410156 11.148438 7 10.644531 7 Z M 7.457031 4.265625 L 6.542969 4.265625 L 6.542969 2.441406 L 7.457031 2.441406 Z M 6.089844 4.265625 L 5.175781 4.265625 L 5.175781 1.53125 L 6.089844 1.53125 Z M 4.722656 4.265625 L 3.808594 4.265625 L 3.808594 2.441406 L 4.722656 2.441406 Z M 4.722656 4.265625 "/>
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 5.632812 12.011719 C 3.617188 12.011719 1.988281 10.382812 1.988281 8.367188 L 1.988281 5.632812 L 9.277344 5.632812 L 9.277344 8.367188 C 9.277344 10.382812 7.648438 12.011719 5.632812 12.011719 Z M 5.632812 12.011719 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14px" height="14px" viewBox="0 0 14 14" version="1.1">
|
||||
<g id="surface1">
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 12.125 12.125 L 1.875 12.125 L 1.875 1.875 L 12.125 1.875 L 7 7 Z M 12.125 12.125 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 385 B |
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="15px" height="14px" viewBox="0 0 15 14" version="1.1">
|
||||
<g id="surface1">
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 14.0625 6.46875 C 14.0625 4.203125 11.125 2.367188 7.5 2.367188 C 3.875 2.367188 0.9375 4.203125 0.9375 6.46875 C 0.9375 8.488281 3.273438 10.160156 6.34375 10.503906 L 6.34375 11.8125 L 8.582031 11.8125 L 8.582031 10.511719 C 9.113281 10.457031 9.636719 10.359375 10.148438 10.21875 L 11.058594 11.8125 L 13.589844 11.8125 L 12.0625 9.410156 C 13.292969 8.667969 14.0625 7.625 14.0625 6.46875 Z M 3.515625 6.773438 C 3.515625 5.226562 5.75 3.96875 8.503906 3.96875 C 11.257812 3.96875 13.292969 4.828125 13.292969 6.773438 C 13.304688 7.757812 12.671875 8.644531 11.699219 9.015625 C 11.65625 8.988281 11.609375 8.964844 11.558594 8.941406 C 11.355469 8.851562 11.144531 8.78125 10.933594 8.71875 C 10.933594 8.71875 12.886719 8.582031 12.886719 6.765625 C 12.886719 4.949219 10.839844 4.914062 10.839844 4.914062 L 6.34375 4.914062 L 6.34375 9.300781 C 4.671875 8.847656 3.515625 7.886719 3.515625 6.773438 Z M 9.957031 7.582031 L 8.601562 7.582031 L 8.601562 6.410156 L 9.957031 6.410156 C 10.125 6.398438 10.292969 6.453125 10.410156 6.5625 C 10.53125 6.675781 10.597656 6.828125 10.585938 6.984375 C 10.589844 7.144531 10.527344 7.296875 10.410156 7.410156 C 10.289062 7.519531 10.128906 7.582031 9.957031 7.582031 Z M 8.582031 9.109375 L 9.183594 9.109375 C 9.300781 9.113281 9.40625 9.15625 9.484375 9.238281 C 9.578125 9.320312 9.65625 9.414062 9.722656 9.511719 C 9.34375 9.554688 8.964844 9.574219 8.582031 9.578125 Z M 8.582031 9.109375 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14px" height="14px" viewBox="0 0 14 14" version="1.1">
|
||||
<g id="surface1">
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 8.269531 1.53125 C 13.078125 4.824219 11.523438 8.457031 11.523438 8.457031 C 11.523438 8.457031 12.890625 10.015625 12.335938 11.375 C 12.335938 11.375 11.773438 10.421875 10.828125 10.421875 C 9.914062 10.421875 9.378906 11.375 7.546875 11.375 C 3.460938 11.375 1.53125 7.9375 1.53125 7.9375 C 5.210938 10.375 7.722656 8.648438 7.722656 8.648438 C 6.066406 7.679688 2.539062 3.039062 2.539062 3.039062 C 5.609375 5.675781 6.9375 6.371094 6.9375 6.371094 C 6.144531 5.710938 3.921875 2.484375 3.921875 2.484375 C 5.699219 4.296875 9.230469 6.828125 9.230469 6.828125 C 10.234375 4.027344 8.269531 1.53125 8.269531 1.53125 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 926 B |
@@ -1,6 +0,0 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.5066 8.01531L19.2375 12.1073V20.2894L12.5066 16.1992V8.01531Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.0294 12.1073V20.2894L27.1563 16.1992V8.01531L20.0294 12.1073Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.58781 3.66V11.5787L11.7147 15.5381V7.61937L4.58781 3.66Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.5066 25.04L19.2375 29V21.1348V21.0818L12.5066 17.1219V25.04Z" fill="black"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 620 B |
@@ -55,14 +55,38 @@
|
||||
"bindings": {
|
||||
"alt-cmd-/": "search::ToggleRegex",
|
||||
"ctrl-0": "project_panel::ToggleFocus",
|
||||
"cmd-1": ["pane::ActivateItem", 0],
|
||||
"cmd-2": ["pane::ActivateItem", 1],
|
||||
"cmd-3": ["pane::ActivateItem", 2],
|
||||
"cmd-4": ["pane::ActivateItem", 3],
|
||||
"cmd-5": ["pane::ActivateItem", 4],
|
||||
"cmd-6": ["pane::ActivateItem", 5],
|
||||
"cmd-7": ["pane::ActivateItem", 6],
|
||||
"cmd-8": ["pane::ActivateItem", 7],
|
||||
"cmd-1": [
|
||||
"pane::ActivateItem",
|
||||
0
|
||||
],
|
||||
"cmd-2": [
|
||||
"pane::ActivateItem",
|
||||
1
|
||||
],
|
||||
"cmd-3": [
|
||||
"pane::ActivateItem",
|
||||
2
|
||||
],
|
||||
"cmd-4": [
|
||||
"pane::ActivateItem",
|
||||
3
|
||||
],
|
||||
"cmd-5": [
|
||||
"pane::ActivateItem",
|
||||
4
|
||||
],
|
||||
"cmd-6": [
|
||||
"pane::ActivateItem",
|
||||
5
|
||||
],
|
||||
"cmd-7": [
|
||||
"pane::ActivateItem",
|
||||
6
|
||||
],
|
||||
"cmd-8": [
|
||||
"pane::ActivateItem",
|
||||
7
|
||||
],
|
||||
"cmd-9": "pane::ActivateLastItem"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -101,8 +101,14 @@
|
||||
"ctrl-o": "pane::GoBack",
|
||||
"ctrl-i": "pane::GoForward",
|
||||
"ctrl-]": "editor::GoToDefinition",
|
||||
"escape": ["vim::SwitchMode", "Normal"],
|
||||
"ctrl-[": ["vim::SwitchMode", "Normal"],
|
||||
"escape": [
|
||||
"vim::SwitchMode",
|
||||
"Normal"
|
||||
],
|
||||
"ctrl-[": [
|
||||
"vim::SwitchMode",
|
||||
"Normal"
|
||||
],
|
||||
"v": "vim::ToggleVisual",
|
||||
"shift-v": "vim::ToggleVisualLine",
|
||||
"ctrl-v": "vim::ToggleVisualBlock",
|
||||
@@ -235,36 +241,123 @@
|
||||
}
|
||||
],
|
||||
// Count support
|
||||
"1": ["vim::Number", 1],
|
||||
"2": ["vim::Number", 2],
|
||||
"3": ["vim::Number", 3],
|
||||
"4": ["vim::Number", 4],
|
||||
"5": ["vim::Number", 5],
|
||||
"6": ["vim::Number", 6],
|
||||
"7": ["vim::Number", 7],
|
||||
"8": ["vim::Number", 8],
|
||||
"9": ["vim::Number", 9],
|
||||
"1": [
|
||||
"vim::Number",
|
||||
1
|
||||
],
|
||||
"2": [
|
||||
"vim::Number",
|
||||
2
|
||||
],
|
||||
"3": [
|
||||
"vim::Number",
|
||||
3
|
||||
],
|
||||
"4": [
|
||||
"vim::Number",
|
||||
4
|
||||
],
|
||||
"5": [
|
||||
"vim::Number",
|
||||
5
|
||||
],
|
||||
"6": [
|
||||
"vim::Number",
|
||||
6
|
||||
],
|
||||
"7": [
|
||||
"vim::Number",
|
||||
7
|
||||
],
|
||||
"8": [
|
||||
"vim::Number",
|
||||
8
|
||||
],
|
||||
"9": [
|
||||
"vim::Number",
|
||||
9
|
||||
],
|
||||
// window related commands (ctrl-w X)
|
||||
"ctrl-w left": ["workspace::ActivatePaneInDirection", "Left"],
|
||||
"ctrl-w right": ["workspace::ActivatePaneInDirection", "Right"],
|
||||
"ctrl-w up": ["workspace::ActivatePaneInDirection", "Up"],
|
||||
"ctrl-w down": ["workspace::ActivatePaneInDirection", "Down"],
|
||||
"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"],
|
||||
"ctrl-w shift-left": ["workspace::SwapPaneInDirection", "Left"],
|
||||
"ctrl-w shift-right": ["workspace::SwapPaneInDirection", "Right"],
|
||||
"ctrl-w shift-up": ["workspace::SwapPaneInDirection", "Up"],
|
||||
"ctrl-w shift-down": ["workspace::SwapPaneInDirection", "Down"],
|
||||
"ctrl-w shift-h": ["workspace::SwapPaneInDirection", "Left"],
|
||||
"ctrl-w shift-l": ["workspace::SwapPaneInDirection", "Right"],
|
||||
"ctrl-w shift-k": ["workspace::SwapPaneInDirection", "Up"],
|
||||
"ctrl-w shift-j": ["workspace::SwapPaneInDirection", "Down"],
|
||||
"ctrl-w left": [
|
||||
"workspace::ActivatePaneInDirection",
|
||||
"Left"
|
||||
],
|
||||
"ctrl-w right": [
|
||||
"workspace::ActivatePaneInDirection",
|
||||
"Right"
|
||||
],
|
||||
"ctrl-w up": [
|
||||
"workspace::ActivatePaneInDirection",
|
||||
"Up"
|
||||
],
|
||||
"ctrl-w down": [
|
||||
"workspace::ActivatePaneInDirection",
|
||||
"Down"
|
||||
],
|
||||
"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"
|
||||
],
|
||||
"ctrl-w shift-left": [
|
||||
"workspace::SwapPaneInDirection",
|
||||
"Left"
|
||||
],
|
||||
"ctrl-w shift-right": [
|
||||
"workspace::SwapPaneInDirection",
|
||||
"Right"
|
||||
],
|
||||
"ctrl-w shift-up": [
|
||||
"workspace::SwapPaneInDirection",
|
||||
"Up"
|
||||
],
|
||||
"ctrl-w shift-down": [
|
||||
"workspace::SwapPaneInDirection",
|
||||
"Down"
|
||||
],
|
||||
"ctrl-w shift-h": [
|
||||
"workspace::SwapPaneInDirection",
|
||||
"Left"
|
||||
],
|
||||
"ctrl-w shift-l": [
|
||||
"workspace::SwapPaneInDirection",
|
||||
"Right"
|
||||
],
|
||||
"ctrl-w shift-k": [
|
||||
"workspace::SwapPaneInDirection",
|
||||
"Up"
|
||||
],
|
||||
"ctrl-w shift-j": [
|
||||
"workspace::SwapPaneInDirection",
|
||||
"Down"
|
||||
],
|
||||
"ctrl-w g t": "pane::ActivateNextItem",
|
||||
"ctrl-w ctrl-g t": "pane::ActivateNextItem",
|
||||
"ctrl-w g shift-t": "pane::ActivatePrevItem",
|
||||
@@ -286,8 +379,14 @@
|
||||
"ctrl-w ctrl-q": "pane::CloseAllItems",
|
||||
"ctrl-w o": "workspace::CloseInactiveTabsAndPanes",
|
||||
"ctrl-w ctrl-o": "workspace::CloseInactiveTabsAndPanes",
|
||||
"ctrl-w n": ["workspace::NewFileInDirection", "Up"],
|
||||
"ctrl-w ctrl-n": ["workspace::NewFileInDirection", "Up"],
|
||||
"ctrl-w n": [
|
||||
"workspace::NewFileInDirection",
|
||||
"Up"
|
||||
],
|
||||
"ctrl-w ctrl-n": [
|
||||
"workspace::NewFileInDirection",
|
||||
"Up"
|
||||
],
|
||||
"-": "pane::RevealInProjectPanel"
|
||||
}
|
||||
},
|
||||
@@ -303,12 +402,21 @@
|
||||
"context": "Editor && vim_mode == normal && vim_operator == none && !VimWaiting",
|
||||
"bindings": {
|
||||
".": "vim::Repeat",
|
||||
"c": ["vim::PushOperator", "Change"],
|
||||
"c": [
|
||||
"vim::PushOperator",
|
||||
"Change"
|
||||
],
|
||||
"shift-c": "vim::ChangeToEndOfLine",
|
||||
"d": ["vim::PushOperator", "Delete"],
|
||||
"d": [
|
||||
"vim::PushOperator",
|
||||
"Delete"
|
||||
],
|
||||
"shift-d": "vim::DeleteToEndOfLine",
|
||||
"shift-j": "vim::JoinLines",
|
||||
"y": ["vim::PushOperator", "Yank"],
|
||||
"y": [
|
||||
"vim::PushOperator",
|
||||
"Yank"
|
||||
],
|
||||
"shift-y": "vim::YankLine",
|
||||
"i": "vim::InsertBefore",
|
||||
"shift-i": "vim::InsertFirstNonWhitespace",
|
||||
@@ -339,11 +447,14 @@
|
||||
],
|
||||
"*": "vim::MoveToNext",
|
||||
"#": "vim::MoveToPrev",
|
||||
"r": ["vim::PushOperator", "Replace"],
|
||||
"r": [
|
||||
"vim::PushOperator",
|
||||
"Replace"
|
||||
],
|
||||
"s": "vim::Substitute",
|
||||
"shift-s": "vim::SubstituteLine",
|
||||
"> >": "vim::Indent",
|
||||
"< <": "vim::Outdent",
|
||||
"> >": "editor::Indent",
|
||||
"< <": "editor::Outdent",
|
||||
"ctrl-pagedown": "pane::ActivateNextItem",
|
||||
"ctrl-pageup": "pane::ActivatePrevItem"
|
||||
}
|
||||
@@ -351,7 +462,10 @@
|
||||
{
|
||||
"context": "Editor && VimCount",
|
||||
"bindings": {
|
||||
"0": ["vim::Number", 0]
|
||||
"0": [
|
||||
"vim::Number",
|
||||
0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -383,7 +497,6 @@
|
||||
"ignorePunctuation": true
|
||||
}
|
||||
],
|
||||
"t": "vim::Tag",
|
||||
"s": "vim::Sentence",
|
||||
"'": "vim::Quotes",
|
||||
"`": "vim::BackQuotes",
|
||||
@@ -398,8 +511,7 @@
|
||||
"}": "vim::CurlyBrackets",
|
||||
"shift-b": "vim::CurlyBrackets",
|
||||
"<": "vim::AngleBrackets",
|
||||
">": "vim::AngleBrackets",
|
||||
"a": "vim::Argument"
|
||||
">": "vim::AngleBrackets"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -456,12 +568,24 @@
|
||||
"shift-i": "vim::InsertBefore",
|
||||
"shift-a": "vim::InsertAfter",
|
||||
"shift-j": "vim::JoinLines",
|
||||
"r": ["vim::PushOperator", "Replace"],
|
||||
"ctrl-c": ["vim::SwitchMode", "Normal"],
|
||||
"escape": ["vim::SwitchMode", "Normal"],
|
||||
"ctrl-[": ["vim::SwitchMode", "Normal"],
|
||||
">": "vim::Indent",
|
||||
"<": "vim::Outdent",
|
||||
"r": [
|
||||
"vim::PushOperator",
|
||||
"Replace"
|
||||
],
|
||||
"ctrl-c": [
|
||||
"vim::SwitchMode",
|
||||
"Normal"
|
||||
],
|
||||
"escape": [
|
||||
"vim::SwitchMode",
|
||||
"Normal"
|
||||
],
|
||||
"ctrl-[": [
|
||||
"vim::SwitchMode",
|
||||
"Normal"
|
||||
],
|
||||
">": "editor::Indent",
|
||||
"<": "editor::Outdent",
|
||||
"i": [
|
||||
"vim::PushOperator",
|
||||
{
|
||||
@@ -492,9 +616,7 @@
|
||||
"ctrl-x ctrl-l": "editor::ToggleCodeActions", // zed specific
|
||||
"ctrl-x ctrl-z": "editor::Cancel",
|
||||
"ctrl-w": "editor::DeleteToPreviousWordStart",
|
||||
"ctrl-u": "editor::DeleteToBeginningOfLine",
|
||||
"ctrl-t": "vim::Indent",
|
||||
"ctrl-d": "vim::Outdent"
|
||||
"ctrl-u": "editor::DeleteToBeginningOfLine"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -502,8 +624,14 @@
|
||||
"bindings": {
|
||||
"tab": "vim::Tab",
|
||||
"enter": "vim::Enter",
|
||||
"escape": ["vim::SwitchMode", "Normal"],
|
||||
"ctrl-[": ["vim::SwitchMode", "Normal"]
|
||||
"escape": [
|
||||
"vim::SwitchMode",
|
||||
"Normal"
|
||||
],
|
||||
"ctrl-[": [
|
||||
"vim::SwitchMode",
|
||||
"Normal"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -169,13 +169,7 @@
|
||||
"show_type_hints": true,
|
||||
"show_parameter_hints": true,
|
||||
// Corresponds to null/None LSP hint type value.
|
||||
"show_other_hints": true,
|
||||
// Time to wait after editing the buffer, before requesting the hints,
|
||||
// set to 0 to disable debouncing.
|
||||
"edit_debounce_ms": 700,
|
||||
// Time to wait after scrolling the buffer, before requesting the hints,
|
||||
// set to 0 to disable debouncing.
|
||||
"scroll_debounce_ms": 50
|
||||
"show_other_hints": true
|
||||
},
|
||||
"project_panel": {
|
||||
// Default width of the project panel.
|
||||
@@ -345,7 +339,9 @@
|
||||
"copilot": {
|
||||
// The set of glob patterns for which copilot should be disabled
|
||||
// in any matching file.
|
||||
"disabled_globs": [".env"]
|
||||
"disabled_globs": [
|
||||
".env"
|
||||
]
|
||||
},
|
||||
// Settings specific to journaling
|
||||
"journal": {
|
||||
@@ -454,7 +450,12 @@
|
||||
// Default directories to search for virtual environments, relative
|
||||
// to the current working directory. We recommend overriding this
|
||||
// in your project's settings, rather than globally.
|
||||
"directories": [".env", "env", ".venv", "venv"],
|
||||
"directories": [
|
||||
".env",
|
||||
"env",
|
||||
".venv",
|
||||
"venv"
|
||||
],
|
||||
// Can also be 'csh', 'fish', and `nushell`
|
||||
"activate_script": "default"
|
||||
}
|
||||
@@ -571,8 +572,7 @@
|
||||
},
|
||||
// Vim settings
|
||||
"vim": {
|
||||
"use_system_clipboard": "always",
|
||||
"use_multiline_find": false
|
||||
"use_system_clipboard": "always"
|
||||
},
|
||||
// The server to connect to. If the environment variable
|
||||
// ZED_SERVER_URL is set, it will override this setting.
|
||||
|
||||
@@ -482,7 +482,7 @@
|
||||
"hidden": "#998b78ff",
|
||||
"hidden.background": "#4c4642ff",
|
||||
"hidden.border": "#544c48ff",
|
||||
"hint": "#6a695bff",
|
||||
"hint": "#8c957dff",
|
||||
"hint.background": "#1e2321ff",
|
||||
"hint.border": "#303a36ff",
|
||||
"ignored": "#c5b597ff",
|
||||
|
||||
@@ -17,7 +17,9 @@ futures.workspace = true
|
||||
gpui.workspace = true
|
||||
language.workspace = true
|
||||
project.workspace = true
|
||||
settings.workspace = true
|
||||
smallvec.workspace = true
|
||||
theme.workspace = true
|
||||
ui.workspace = true
|
||||
util.workspace = true
|
||||
workspace.workspace = true
|
||||
|
||||
@@ -28,6 +28,7 @@ parking_lot.workspace = true
|
||||
parse_duration = "2.1.1"
|
||||
postage.workspace = true
|
||||
rand.workspace = true
|
||||
regex.workspace = true
|
||||
rusqlite = { version = "0.29.0", features = ["blob", "array", "modern_sqlite"] }
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
@@ -13,17 +13,20 @@ doctest = false
|
||||
ai.workspace = true
|
||||
anyhow.workspace = true
|
||||
chrono.workspace = true
|
||||
client.workspace = true
|
||||
collections.workspace = true
|
||||
editor.workspace = true
|
||||
fs.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
indoc.workspace = true
|
||||
isahc.workspace = true
|
||||
language.workspace = true
|
||||
log.workspace = true
|
||||
menu.workspace = true
|
||||
multi_buffer.workspace = true
|
||||
ordered-float.workspace = true
|
||||
parking_lot.workspace = true
|
||||
project.workspace = true
|
||||
regex.workspace = true
|
||||
schemars.workspace = true
|
||||
@@ -33,7 +36,6 @@ serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
settings.workspace = true
|
||||
smol.workspace = true
|
||||
telemetry_events.workspace = true
|
||||
theme.workspace = true
|
||||
tiktoken-rs.workspace = true
|
||||
ui.workspace = true
|
||||
|
||||
@@ -15,6 +15,7 @@ use ai::{
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use chrono::{DateTime, Local};
|
||||
use client::telemetry::AssistantKind;
|
||||
use collections::{hash_map, HashMap, HashSet, VecDeque};
|
||||
use editor::{
|
||||
actions::{MoveDown, MoveUp},
|
||||
@@ -51,7 +52,6 @@ use std::{
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use telemetry_events::AssistantKind;
|
||||
use theme::ThemeSettings;
|
||||
use ui::{
|
||||
prelude::*,
|
||||
|
||||
@@ -13,7 +13,9 @@ doctest = false
|
||||
anyhow.workspace = true
|
||||
collections.workspace = true
|
||||
derive_more.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
log.workspace = true
|
||||
parking_lot.workspace = true
|
||||
rodio = { version = "0.17.1", default-features = false, features = ["wav"] }
|
||||
util.workspace = true
|
||||
|
||||
@@ -16,9 +16,11 @@ db.workspace = true
|
||||
editor.workspace = true
|
||||
gpui.workspace = true
|
||||
isahc.workspace = true
|
||||
lazy_static.workspace = true
|
||||
log.workspace = true
|
||||
markdown_preview.workspace = true
|
||||
menu.workspace = true
|
||||
project.workspace = true
|
||||
release_channel.workspace = true
|
||||
schemars.workspace = true
|
||||
serde.workspace = true
|
||||
@@ -27,5 +29,6 @@ serde_json.workspace = true
|
||||
settings.workspace = true
|
||||
smol.workspace = true
|
||||
tempfile.workspace = true
|
||||
theme.workspace = true
|
||||
util.workspace = true
|
||||
workspace.workspace = true
|
||||
|
||||
@@ -29,7 +29,7 @@ use std::{
|
||||
};
|
||||
use update_notification::UpdateNotification;
|
||||
use util::{
|
||||
http::{HttpClient, HttpClientWithUrl},
|
||||
http::{HttpClient, ZedHttpClient},
|
||||
ResultExt,
|
||||
};
|
||||
use workspace::Workspace;
|
||||
@@ -67,7 +67,7 @@ pub enum AutoUpdateStatus {
|
||||
pub struct AutoUpdater {
|
||||
status: AutoUpdateStatus,
|
||||
current_version: SemanticVersion,
|
||||
http_client: Arc<HttpClientWithUrl>,
|
||||
http_client: Arc<ZedHttpClient>,
|
||||
pending_poll: Option<Task<Option<()>>>,
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ struct ReleaseNotesBody {
|
||||
release_notes: String,
|
||||
}
|
||||
|
||||
pub fn init(http_client: Arc<HttpClientWithUrl>, cx: &mut AppContext) {
|
||||
pub fn init(http_client: Arc<ZedHttpClient>, cx: &mut AppContext) {
|
||||
AutoUpdateSetting::register(cx);
|
||||
|
||||
cx.observe_new_views(|workspace: &mut Workspace, _cx| {
|
||||
@@ -181,7 +181,7 @@ pub fn view_release_notes(_: &ViewReleaseNotes, cx: &mut AppContext) -> Option<(
|
||||
let current_version = auto_updater.current_version;
|
||||
let url = &auto_updater
|
||||
.http_client
|
||||
.build_url(&format!("/releases/{release_channel}/{current_version}"));
|
||||
.zed_url(&format!("/releases/{release_channel}/{current_version}"));
|
||||
cx.open_url(&url);
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ fn view_release_notes_locally(workspace: &mut Workspace, cx: &mut ViewContext<Wo
|
||||
let version = env!("CARGO_PKG_VERSION");
|
||||
|
||||
let client = client::Client::global(cx).http_client();
|
||||
let url = client.build_url(&format!(
|
||||
let url = client.zed_url(&format!(
|
||||
"/api/release_notes/{}/{}",
|
||||
release_channel.dev_name(),
|
||||
version
|
||||
@@ -283,7 +283,7 @@ impl AutoUpdater {
|
||||
cx.default_global::<GlobalAutoUpdate>().0.clone()
|
||||
}
|
||||
|
||||
fn new(current_version: SemanticVersion, http_client: Arc<HttpClientWithUrl>) -> Self {
|
||||
fn new(current_version: SemanticVersion, http_client: Arc<ZedHttpClient>) -> Self {
|
||||
Self {
|
||||
status: AutoUpdateStatus::Idle,
|
||||
current_version,
|
||||
@@ -337,7 +337,7 @@ impl AutoUpdater {
|
||||
(this.http_client.clone(), this.current_version)
|
||||
})?;
|
||||
|
||||
let mut url_string = client.build_url(&format!(
|
||||
let mut url_string = client.zed_url(&format!(
|
||||
"/api/releases/latest?asset=Zed.dmg&os={}&arch={}",
|
||||
OS, ARCH
|
||||
));
|
||||
|
||||
@@ -10,10 +10,15 @@ path = "src/breadcrumbs.rs"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
collections.workspace = true
|
||||
editor.workspace = true
|
||||
gpui.workspace = true
|
||||
itertools.workspace = true
|
||||
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
|
||||
|
||||
@@ -21,21 +21,26 @@ test-support = [
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
async-broadcast = "0.4"
|
||||
audio.workspace = true
|
||||
client.workspace = true
|
||||
collections.workspace = true
|
||||
fs.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
image = "0.23"
|
||||
language.workspace = true
|
||||
live_kit_client.workspace = true
|
||||
log.workspace = true
|
||||
media.workspace = true
|
||||
postage.workspace = true
|
||||
project.workspace = true
|
||||
schemars.workspace = true
|
||||
serde.workspace = true
|
||||
serde_derive.workspace = true
|
||||
serde_json.workspace = true
|
||||
settings.workspace = true
|
||||
smallvec.workspace = true
|
||||
util.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -5,7 +5,7 @@ pub mod room;
|
||||
use anyhow::{anyhow, Result};
|
||||
use audio::Audio;
|
||||
use call_settings::CallSettings;
|
||||
use client::{proto, ChannelId, Client, TypedEnvelope, User, UserStore, ZED_ALWAYS_ACTIVE};
|
||||
use client::{proto, Client, TypedEnvelope, User, UserStore, ZED_ALWAYS_ACTIVE};
|
||||
use collections::HashSet;
|
||||
use futures::{channel::oneshot, future::Shared, Future, FutureExt};
|
||||
use gpui::{
|
||||
@@ -107,7 +107,7 @@ impl ActiveCall {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn channel_id(&self, cx: &AppContext) -> Option<ChannelId> {
|
||||
pub fn channel_id(&self, cx: &AppContext) -> Option<u64> {
|
||||
self.room()?.read(cx).channel_id()
|
||||
}
|
||||
|
||||
@@ -336,7 +336,7 @@ impl ActiveCall {
|
||||
|
||||
pub fn join_channel(
|
||||
&mut self,
|
||||
channel_id: ChannelId,
|
||||
channel_id: u64,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<Option<Model<Room>>>> {
|
||||
if let Some(room) = self.room().cloned() {
|
||||
@@ -487,7 +487,7 @@ impl ActiveCall {
|
||||
pub fn report_call_event_for_room(
|
||||
operation: &'static str,
|
||||
room_id: u64,
|
||||
channel_id: Option<ChannelId>,
|
||||
channel_id: Option<u64>,
|
||||
client: &Arc<Client>,
|
||||
) {
|
||||
let telemetry = client.telemetry();
|
||||
@@ -497,7 +497,7 @@ pub fn report_call_event_for_room(
|
||||
|
||||
pub fn report_call_event_for_channel(
|
||||
operation: &'static str,
|
||||
channel_id: ChannelId,
|
||||
channel_id: u64,
|
||||
client: &Arc<Client>,
|
||||
cx: &AppContext,
|
||||
) {
|
||||
|
||||
@@ -6,7 +6,7 @@ use anyhow::{anyhow, Result};
|
||||
use audio::{Audio, Sound};
|
||||
use client::{
|
||||
proto::{self, PeerId},
|
||||
ChannelId, Client, ParticipantIndex, TypedEnvelope, User, UserStore,
|
||||
Client, ParticipantIndex, TypedEnvelope, User, UserStore,
|
||||
};
|
||||
use collections::{BTreeMap, HashMap, HashSet};
|
||||
use fs::Fs;
|
||||
@@ -27,7 +27,7 @@ pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Event {
|
||||
RoomJoined {
|
||||
channel_id: Option<ChannelId>,
|
||||
channel_id: Option<u64>,
|
||||
},
|
||||
ParticipantLocationChanged {
|
||||
participant_id: proto::PeerId,
|
||||
@@ -53,13 +53,13 @@ pub enum Event {
|
||||
project_id: u64,
|
||||
},
|
||||
Left {
|
||||
channel_id: Option<ChannelId>,
|
||||
channel_id: Option<u64>,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct Room {
|
||||
id: u64,
|
||||
channel_id: Option<ChannelId>,
|
||||
channel_id: Option<u64>,
|
||||
live_kit: Option<LiveKitRoom>,
|
||||
status: RoomStatus,
|
||||
shared_projects: HashSet<WeakModel<Project>>,
|
||||
@@ -84,7 +84,7 @@ pub struct Room {
|
||||
impl EventEmitter<Event> for Room {}
|
||||
|
||||
impl Room {
|
||||
pub fn channel_id(&self) -> Option<ChannelId> {
|
||||
pub fn channel_id(&self) -> Option<u64> {
|
||||
self.channel_id
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ impl Room {
|
||||
|
||||
fn new(
|
||||
id: u64,
|
||||
channel_id: Option<ChannelId>,
|
||||
channel_id: Option<u64>,
|
||||
live_kit_connection_info: Option<proto::LiveKitConnectionInfo>,
|
||||
client: Arc<Client>,
|
||||
user_store: Model<UserStore>,
|
||||
@@ -273,17 +273,13 @@ impl Room {
|
||||
}
|
||||
|
||||
pub(crate) async fn join_channel(
|
||||
channel_id: ChannelId,
|
||||
channel_id: u64,
|
||||
client: Arc<Client>,
|
||||
user_store: Model<UserStore>,
|
||||
cx: AsyncAppContext,
|
||||
) -> Result<Model<Self>> {
|
||||
Self::from_join_response(
|
||||
client
|
||||
.request(proto::JoinChannel {
|
||||
channel_id: channel_id.0,
|
||||
})
|
||||
.await?,
|
||||
client.request(proto::JoinChannel { channel_id }).await?,
|
||||
client,
|
||||
user_store,
|
||||
cx,
|
||||
@@ -341,7 +337,7 @@ impl Room {
|
||||
let room = cx.new_model(|cx| {
|
||||
Self::new(
|
||||
room_proto.id,
|
||||
response.channel_id.map(ChannelId),
|
||||
response.channel_id,
|
||||
response.live_kit_connection_info,
|
||||
client,
|
||||
user_store,
|
||||
|
||||
@@ -17,18 +17,34 @@ anyhow.workspace = true
|
||||
client.workspace = true
|
||||
clock.workspace = true
|
||||
collections.workspace = true
|
||||
db.workspace = true
|
||||
feature_flags.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
image = "0.23"
|
||||
language.workspace = true
|
||||
lazy_static.workspace = true
|
||||
log.workspace = true
|
||||
parking_lot.workspace = true
|
||||
postage.workspace = true
|
||||
rand.workspace = true
|
||||
release_channel.workspace = true
|
||||
rpc.workspace = true
|
||||
schemars.workspace = true
|
||||
serde.workspace = true
|
||||
serde_derive.workspace = true
|
||||
settings.workspace = true
|
||||
smallvec.workspace = true
|
||||
smol.workspace = true
|
||||
sum_tree.workspace = true
|
||||
tempfile.workspace = true
|
||||
text.workspace = true
|
||||
thiserror.workspace = true
|
||||
time.workspace = true
|
||||
tiny_http = "0.8"
|
||||
url.workspace = true
|
||||
util.workspace = true
|
||||
uuid.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
collections = { workspace = true, features = ["test-support"] }
|
||||
|
||||
@@ -11,7 +11,7 @@ pub use channel_chat::{
|
||||
mentions_to_proto, ChannelChat, ChannelChatEvent, ChannelMessage, ChannelMessageId,
|
||||
MessageParams,
|
||||
};
|
||||
pub use channel_store::{Channel, ChannelEvent, ChannelMembership, ChannelStore, HostedProjectId};
|
||||
pub use channel_store::{Channel, ChannelEvent, ChannelId, ChannelMembership, ChannelStore};
|
||||
|
||||
#[cfg(test)]
|
||||
mod channel_store_tests;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{Channel, ChannelStore};
|
||||
use crate::{Channel, ChannelId, ChannelStore};
|
||||
use anyhow::Result;
|
||||
use client::{ChannelId, Client, Collaborator, UserStore, ZED_ALWAYS_ACTIVE};
|
||||
use client::{Client, Collaborator, UserStore, ZED_ALWAYS_ACTIVE};
|
||||
use collections::HashMap;
|
||||
use gpui::{AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Task};
|
||||
use language::proto::serialize_version;
|
||||
@@ -51,7 +51,7 @@ impl ChannelBuffer {
|
||||
) -> Result<Model<Self>> {
|
||||
let response = client
|
||||
.request(proto::JoinChannelBuffer {
|
||||
channel_id: channel.id.0,
|
||||
channel_id: channel.id,
|
||||
})
|
||||
.await?;
|
||||
let buffer_id = BufferId::new(response.buffer_id)?;
|
||||
@@ -68,7 +68,7 @@ impl ChannelBuffer {
|
||||
})?;
|
||||
buffer.update(&mut cx, |buffer, cx| buffer.apply_ops(operations, cx))??;
|
||||
|
||||
let subscription = client.subscribe_to_entity(channel.id.0)?;
|
||||
let subscription = client.subscribe_to_entity(channel.id)?;
|
||||
|
||||
anyhow::Ok(cx.new_model(|cx| {
|
||||
cx.subscribe(&buffer, Self::on_buffer_update).detach();
|
||||
@@ -97,7 +97,7 @@ impl ChannelBuffer {
|
||||
}
|
||||
self.client
|
||||
.send(proto::LeaveChannelBuffer {
|
||||
channel_id: self.channel_id.0,
|
||||
channel_id: self.channel_id,
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
@@ -191,7 +191,7 @@ impl ChannelBuffer {
|
||||
let operation = language::proto::serialize_operation(operation);
|
||||
self.client
|
||||
.send(proto::UpdateChannelBuffer {
|
||||
channel_id: self.channel_id.0,
|
||||
channel_id: self.channel_id,
|
||||
operations: vec![operation],
|
||||
})
|
||||
.log_err();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::{Channel, ChannelStore};
|
||||
use crate::{Channel, ChannelId, ChannelStore};
|
||||
use anyhow::{anyhow, Result};
|
||||
use client::{
|
||||
proto,
|
||||
user::{User, UserStore},
|
||||
ChannelId, Client, Subscription, TypedEnvelope, UserId,
|
||||
Client, Subscription, TypedEnvelope, UserId,
|
||||
};
|
||||
use collections::HashSet;
|
||||
use futures::lock::Mutex;
|
||||
@@ -104,12 +104,10 @@ impl ChannelChat {
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Result<Model<Self>> {
|
||||
let channel_id = channel.id;
|
||||
let subscription = client.subscribe_to_entity(channel_id.0).unwrap();
|
||||
let subscription = client.subscribe_to_entity(channel_id).unwrap();
|
||||
|
||||
let response = client
|
||||
.request(proto::JoinChannelChat {
|
||||
channel_id: channel_id.0,
|
||||
})
|
||||
.request(proto::JoinChannelChat { channel_id })
|
||||
.await?;
|
||||
|
||||
let handle = cx.new_model(|cx| {
|
||||
@@ -145,7 +143,7 @@ impl ChannelChat {
|
||||
fn release(&mut self, _: &mut AppContext) {
|
||||
self.rpc
|
||||
.send(proto::LeaveChannelChat {
|
||||
channel_id: self.channel_id.0,
|
||||
channel_id: self.channel_id,
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
@@ -202,7 +200,7 @@ impl ChannelChat {
|
||||
Ok(cx.spawn(move |this, mut cx| async move {
|
||||
let outgoing_message_guard = outgoing_messages_lock.lock().await;
|
||||
let request = rpc.request(proto::SendChannelMessage {
|
||||
channel_id: channel_id.0,
|
||||
channel_id,
|
||||
body: message.text,
|
||||
nonce: Some(nonce.into()),
|
||||
mentions: mentions_to_proto(&message.mentions),
|
||||
@@ -222,7 +220,7 @@ impl ChannelChat {
|
||||
|
||||
pub fn remove_message(&mut self, id: u64, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
|
||||
let response = self.rpc.request(proto::RemoveChannelMessage {
|
||||
channel_id: self.channel_id.0,
|
||||
channel_id: self.channel_id,
|
||||
message_id: id,
|
||||
});
|
||||
cx.spawn(move |this, mut cx| async move {
|
||||
@@ -247,7 +245,7 @@ impl ChannelChat {
|
||||
async move {
|
||||
let response = rpc
|
||||
.request(proto::GetChannelMessages {
|
||||
channel_id: channel_id.0,
|
||||
channel_id,
|
||||
before_message_id,
|
||||
})
|
||||
.await?;
|
||||
@@ -325,7 +323,7 @@ impl ChannelChat {
|
||||
{
|
||||
self.rpc
|
||||
.send(proto::AckChannelMessage {
|
||||
channel_id: self.channel_id.0,
|
||||
channel_id: self.channel_id,
|
||||
message_id: latest_message_id,
|
||||
})
|
||||
.ok();
|
||||
@@ -403,11 +401,7 @@ impl ChannelChat {
|
||||
let channel_id = self.channel_id;
|
||||
cx.spawn(move |this, mut cx| {
|
||||
async move {
|
||||
let response = rpc
|
||||
.request(proto::JoinChannelChat {
|
||||
channel_id: channel_id.0,
|
||||
})
|
||||
.await?;
|
||||
let response = rpc.request(proto::JoinChannelChat { channel_id }).await?;
|
||||
Self::handle_loaded_messages(
|
||||
this.clone(),
|
||||
user_store.clone(),
|
||||
@@ -424,7 +418,7 @@ impl ChannelChat {
|
||||
|
||||
for pending_message in pending_messages {
|
||||
let request = rpc.request(proto::SendChannelMessage {
|
||||
channel_id: channel_id.0,
|
||||
channel_id,
|
||||
body: pending_message.body,
|
||||
mentions: mentions_to_proto(&pending_message.mentions),
|
||||
nonce: Some(pending_message.nonce.into()),
|
||||
@@ -467,7 +461,7 @@ impl ChannelChat {
|
||||
if self.acknowledged_message_ids.insert(id) {
|
||||
self.rpc
|
||||
.send(proto::AckChannelMessage {
|
||||
channel_id: self.channel_id.0,
|
||||
channel_id: self.channel_id,
|
||||
message_id: id,
|
||||
})
|
||||
.ok();
|
||||
|
||||
@@ -3,7 +3,7 @@ mod channel_index;
|
||||
use crate::{channel_buffer::ChannelBuffer, channel_chat::ChannelChat, ChannelMessage};
|
||||
use anyhow::{anyhow, Result};
|
||||
use channel_index::ChannelIndex;
|
||||
use client::{ChannelId, Client, Subscription, User, UserId, UserStore};
|
||||
use client::{Client, Subscription, User, UserId, UserStore};
|
||||
use collections::{hash_map, HashMap, HashSet};
|
||||
use futures::{channel::mpsc, future::Shared, Future, FutureExt, StreamExt};
|
||||
use gpui::{
|
||||
@@ -19,16 +19,15 @@ use rpc::{
|
||||
use std::{mem, sync::Arc, time::Duration};
|
||||
use util::{async_maybe, maybe, ResultExt};
|
||||
|
||||
pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
|
||||
pub fn init(client: &Arc<Client>, user_store: Model<UserStore>, cx: &mut AppContext) {
|
||||
let channel_store =
|
||||
cx.new_model(|cx| ChannelStore::new(client.clone(), user_store.clone(), cx));
|
||||
cx.set_global(GlobalChannelStore(channel_store));
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||
pub struct HostedProjectId(pub u64);
|
||||
pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
|
||||
pub type ChannelId = u64;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
struct NotesVersion {
|
||||
@@ -36,31 +35,11 @@ struct NotesVersion {
|
||||
version: clock::Global,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct HostedProject {
|
||||
id: HostedProjectId,
|
||||
channel_id: ChannelId,
|
||||
name: SharedString,
|
||||
_visibility: proto::ChannelVisibility,
|
||||
}
|
||||
|
||||
impl From<proto::HostedProject> for HostedProject {
|
||||
fn from(project: proto::HostedProject) -> Self {
|
||||
Self {
|
||||
id: HostedProjectId(project.id),
|
||||
channel_id: ChannelId(project.channel_id),
|
||||
_visibility: project.visibility(),
|
||||
name: project.name.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ChannelStore {
|
||||
pub channel_index: ChannelIndex,
|
||||
channel_invitations: Vec<Arc<Channel>>,
|
||||
channel_participants: HashMap<ChannelId, Vec<Arc<User>>>,
|
||||
channel_states: HashMap<ChannelId, ChannelState>,
|
||||
hosted_projects: HashMap<HostedProjectId, HostedProject>,
|
||||
|
||||
outgoing_invites: HashSet<(ChannelId, UserId)>,
|
||||
update_channels_tx: mpsc::UnboundedSender<proto::UpdateChannels>,
|
||||
@@ -79,7 +58,7 @@ pub struct Channel {
|
||||
pub id: ChannelId,
|
||||
pub name: SharedString,
|
||||
pub visibility: proto::ChannelVisibility,
|
||||
pub parent_path: Vec<ChannelId>,
|
||||
pub parent_path: Vec<u64>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -89,7 +68,6 @@ pub struct ChannelState {
|
||||
observed_chat_message: Option<u64>,
|
||||
observed_notes_versions: Option<NotesVersion>,
|
||||
role: Option<ChannelRole>,
|
||||
projects: HashSet<HostedProjectId>,
|
||||
}
|
||||
|
||||
impl Channel {
|
||||
@@ -114,7 +92,10 @@ impl Channel {
|
||||
}
|
||||
|
||||
pub fn root_id(&self) -> ChannelId {
|
||||
self.parent_path.first().copied().unwrap_or(self.id)
|
||||
self.parent_path
|
||||
.first()
|
||||
.map(|id| *id as ChannelId)
|
||||
.unwrap_or(self.id)
|
||||
}
|
||||
|
||||
pub fn slug(str: &str) -> String {
|
||||
@@ -218,7 +199,6 @@ impl ChannelStore {
|
||||
channel_invitations: Vec::default(),
|
||||
channel_index: ChannelIndex::default(),
|
||||
channel_participants: Default::default(),
|
||||
hosted_projects: Default::default(),
|
||||
outgoing_invites: Default::default(),
|
||||
opened_buffers: Default::default(),
|
||||
opened_chats: Default::default(),
|
||||
@@ -305,19 +285,6 @@ impl ChannelStore {
|
||||
self.channel_index.by_id().get(&channel_id)
|
||||
}
|
||||
|
||||
pub fn projects_for_id(&self, channel_id: ChannelId) -> Vec<(SharedString, HostedProjectId)> {
|
||||
let mut projects: Vec<(SharedString, HostedProjectId)> = self
|
||||
.channel_states
|
||||
.get(&channel_id)
|
||||
.map(|state| state.projects.clone())
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.flat_map(|id| Some((self.hosted_projects.get(&id)?.name.clone(), id)))
|
||||
.collect();
|
||||
projects.sort();
|
||||
projects
|
||||
}
|
||||
|
||||
pub fn has_open_channel_buffer(&self, channel_id: ChannelId, _cx: &AppContext) -> bool {
|
||||
if let Some(buffer) = self.opened_buffers.get(&channel_id) {
|
||||
if let OpenedModelHandle::Open(buffer) = buffer {
|
||||
@@ -595,16 +562,13 @@ impl ChannelStore {
|
||||
let name = name.trim_start_matches("#").to_owned();
|
||||
cx.spawn(move |this, mut cx| async move {
|
||||
let response = client
|
||||
.request(proto::CreateChannel {
|
||||
name,
|
||||
parent_id: parent_id.map(|cid| cid.0),
|
||||
})
|
||||
.request(proto::CreateChannel { name, parent_id })
|
||||
.await?;
|
||||
|
||||
let channel = response
|
||||
.channel
|
||||
.ok_or_else(|| anyhow!("missing channel in response"))?;
|
||||
let channel_id = ChannelId(channel.id);
|
||||
let channel_id = channel.id;
|
||||
|
||||
this.update(&mut cx, |this, cx| {
|
||||
let task = this.update_channels(
|
||||
@@ -636,10 +600,7 @@ impl ChannelStore {
|
||||
let client = self.client.clone();
|
||||
cx.spawn(move |_, _| async move {
|
||||
let _ = client
|
||||
.request(proto::MoveChannel {
|
||||
channel_id: channel_id.0,
|
||||
to: to.0,
|
||||
})
|
||||
.request(proto::MoveChannel { channel_id, to })
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
@@ -656,7 +617,7 @@ impl ChannelStore {
|
||||
cx.spawn(move |_, _| async move {
|
||||
let _ = client
|
||||
.request(proto::SetChannelVisibility {
|
||||
channel_id: channel_id.0,
|
||||
channel_id,
|
||||
visibility: visibility.into(),
|
||||
})
|
||||
.await?;
|
||||
@@ -681,7 +642,7 @@ impl ChannelStore {
|
||||
cx.spawn(move |this, mut cx| async move {
|
||||
let result = client
|
||||
.request(proto::InviteChannelMember {
|
||||
channel_id: channel_id.0,
|
||||
channel_id,
|
||||
user_id,
|
||||
role: role.into(),
|
||||
})
|
||||
@@ -713,7 +674,7 @@ impl ChannelStore {
|
||||
cx.spawn(move |this, mut cx| async move {
|
||||
let result = client
|
||||
.request(proto::RemoveChannelMember {
|
||||
channel_id: channel_id.0,
|
||||
channel_id,
|
||||
user_id,
|
||||
})
|
||||
.await;
|
||||
@@ -743,7 +704,7 @@ impl ChannelStore {
|
||||
cx.spawn(move |this, mut cx| async move {
|
||||
let result = client
|
||||
.request(proto::SetChannelMemberRole {
|
||||
channel_id: channel_id.0,
|
||||
channel_id,
|
||||
user_id,
|
||||
role: role.into(),
|
||||
})
|
||||
@@ -769,10 +730,7 @@ impl ChannelStore {
|
||||
let name = new_name.to_string();
|
||||
cx.spawn(move |this, mut cx| async move {
|
||||
let channel = client
|
||||
.request(proto::RenameChannel {
|
||||
channel_id: channel_id.0,
|
||||
name,
|
||||
})
|
||||
.request(proto::RenameChannel { channel_id, name })
|
||||
.await?
|
||||
.channel
|
||||
.ok_or_else(|| anyhow!("missing channel in response"))?;
|
||||
@@ -805,10 +763,7 @@ impl ChannelStore {
|
||||
let client = self.client.clone();
|
||||
cx.background_executor().spawn(async move {
|
||||
client
|
||||
.request(proto::RespondToChannelInvite {
|
||||
channel_id: channel_id.0,
|
||||
accept,
|
||||
})
|
||||
.request(proto::RespondToChannelInvite { channel_id, accept })
|
||||
.await?;
|
||||
Ok(())
|
||||
})
|
||||
@@ -823,9 +778,7 @@ impl ChannelStore {
|
||||
let user_store = self.user_store.downgrade();
|
||||
cx.spawn(move |_, mut cx| async move {
|
||||
let response = client
|
||||
.request(proto::GetChannelMembers {
|
||||
channel_id: channel_id.0,
|
||||
})
|
||||
.request(proto::GetChannelMembers { channel_id })
|
||||
.await?;
|
||||
|
||||
let user_ids = response.members.iter().map(|m| m.user_id).collect();
|
||||
@@ -853,11 +806,7 @@ impl ChannelStore {
|
||||
pub fn remove_channel(&self, channel_id: ChannelId) -> impl Future<Output = Result<()>> {
|
||||
let client = self.client.clone();
|
||||
async move {
|
||||
client
|
||||
.request(proto::DeleteChannel {
|
||||
channel_id: channel_id.0,
|
||||
})
|
||||
.await?;
|
||||
client.request(proto::DeleteChannel { channel_id }).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -894,23 +843,19 @@ impl ChannelStore {
|
||||
for buffer_version in message.payload.observed_channel_buffer_version {
|
||||
let version = language::proto::deserialize_version(&buffer_version.version);
|
||||
this.acknowledge_notes_version(
|
||||
ChannelId(buffer_version.channel_id),
|
||||
buffer_version.channel_id,
|
||||
buffer_version.epoch,
|
||||
&version,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
for message_id in message.payload.observed_channel_message_id {
|
||||
this.acknowledge_message_id(
|
||||
ChannelId(message_id.channel_id),
|
||||
message_id.message_id,
|
||||
cx,
|
||||
);
|
||||
this.acknowledge_message_id(message_id.channel_id, message_id.message_id, cx);
|
||||
}
|
||||
for membership in message.payload.channel_memberships {
|
||||
if let Some(role) = ChannelRole::from_i32(membership.role) {
|
||||
this.channel_states
|
||||
.entry(ChannelId(membership.channel_id))
|
||||
.entry(membership.channel_id)
|
||||
.or_insert_with(|| ChannelState::default())
|
||||
.set_role(role)
|
||||
}
|
||||
@@ -943,7 +888,7 @@ impl ChannelStore {
|
||||
let channel_buffer = buffer.read(cx);
|
||||
let buffer = channel_buffer.buffer().read(cx);
|
||||
buffer_versions.push(proto::ChannelBufferVersion {
|
||||
channel_id: channel_buffer.channel_id.0,
|
||||
channel_id: channel_buffer.channel_id,
|
||||
epoch: channel_buffer.epoch(),
|
||||
version: language::proto::serialize_version(&buffer.version()),
|
||||
});
|
||||
@@ -974,7 +919,7 @@ impl ChannelStore {
|
||||
if let Some(remote_buffer) = response
|
||||
.buffers
|
||||
.iter_mut()
|
||||
.find(|buffer| buffer.channel_id == channel_id.0)
|
||||
.find(|buffer| buffer.channel_id == channel_id)
|
||||
{
|
||||
let channel_id = channel_buffer.channel_id;
|
||||
let remote_version =
|
||||
@@ -1010,7 +955,7 @@ impl ChannelStore {
|
||||
{
|
||||
client
|
||||
.send(proto::UpdateChannelBuffer {
|
||||
channel_id: channel_id.0,
|
||||
channel_id,
|
||||
operations: chunk,
|
||||
})
|
||||
.ok();
|
||||
@@ -1065,12 +1010,12 @@ impl ChannelStore {
|
||||
) -> Option<Task<Result<()>>> {
|
||||
if !payload.remove_channel_invitations.is_empty() {
|
||||
self.channel_invitations
|
||||
.retain(|channel| !payload.remove_channel_invitations.contains(&channel.id.0));
|
||||
.retain(|channel| !payload.remove_channel_invitations.contains(&channel.id));
|
||||
}
|
||||
for channel in payload.channel_invitations {
|
||||
match self
|
||||
.channel_invitations
|
||||
.binary_search_by_key(&channel.id, |c| c.id.0)
|
||||
.binary_search_by_key(&channel.id, |c| c.id)
|
||||
{
|
||||
Ok(ix) => {
|
||||
Arc::make_mut(&mut self.channel_invitations[ix]).name = channel.name.into()
|
||||
@@ -1078,14 +1023,10 @@ impl ChannelStore {
|
||||
Err(ix) => self.channel_invitations.insert(
|
||||
ix,
|
||||
Arc::new(Channel {
|
||||
id: ChannelId(channel.id),
|
||||
id: channel.id,
|
||||
visibility: channel.visibility(),
|
||||
name: channel.name.into(),
|
||||
parent_path: channel
|
||||
.parent_path
|
||||
.into_iter()
|
||||
.map(|cid| ChannelId(cid))
|
||||
.collect(),
|
||||
parent_path: channel.parent_path,
|
||||
}),
|
||||
),
|
||||
}
|
||||
@@ -1094,27 +1035,20 @@ impl ChannelStore {
|
||||
let channels_changed = !payload.channels.is_empty()
|
||||
|| !payload.delete_channels.is_empty()
|
||||
|| !payload.latest_channel_message_ids.is_empty()
|
||||
|| !payload.latest_channel_buffer_versions.is_empty()
|
||||
|| !payload.hosted_projects.is_empty()
|
||||
|| !payload.deleted_hosted_projects.is_empty();
|
||||
|| !payload.latest_channel_buffer_versions.is_empty();
|
||||
|
||||
if channels_changed {
|
||||
if !payload.delete_channels.is_empty() {
|
||||
let delete_channels: Vec<ChannelId> = payload
|
||||
.delete_channels
|
||||
.into_iter()
|
||||
.map(|cid| ChannelId(cid))
|
||||
.collect();
|
||||
self.channel_index.delete_channels(&delete_channels);
|
||||
self.channel_index.delete_channels(&payload.delete_channels);
|
||||
self.channel_participants
|
||||
.retain(|channel_id, _| !delete_channels.contains(&channel_id));
|
||||
.retain(|channel_id, _| !&payload.delete_channels.contains(channel_id));
|
||||
|
||||
for channel_id in &delete_channels {
|
||||
for channel_id in &payload.delete_channels {
|
||||
let channel_id = *channel_id;
|
||||
if payload
|
||||
.channels
|
||||
.iter()
|
||||
.any(|channel| channel.id == channel_id.0)
|
||||
.any(|channel| channel.id == channel_id)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -1130,7 +1064,7 @@ impl ChannelStore {
|
||||
|
||||
let mut index = self.channel_index.bulk_insert();
|
||||
for channel in payload.channels {
|
||||
let id = ChannelId(channel.id);
|
||||
let id = channel.id;
|
||||
let channel_changed = index.insert(channel);
|
||||
|
||||
if channel_changed {
|
||||
@@ -1145,45 +1079,17 @@ impl ChannelStore {
|
||||
for latest_buffer_version in payload.latest_channel_buffer_versions {
|
||||
let version = language::proto::deserialize_version(&latest_buffer_version.version);
|
||||
self.channel_states
|
||||
.entry(ChannelId(latest_buffer_version.channel_id))
|
||||
.entry(latest_buffer_version.channel_id)
|
||||
.or_default()
|
||||
.update_latest_notes_version(latest_buffer_version.epoch, &version)
|
||||
}
|
||||
|
||||
for latest_channel_message in payload.latest_channel_message_ids {
|
||||
self.channel_states
|
||||
.entry(ChannelId(latest_channel_message.channel_id))
|
||||
.entry(latest_channel_message.channel_id)
|
||||
.or_default()
|
||||
.update_latest_message_id(latest_channel_message.message_id);
|
||||
}
|
||||
|
||||
for hosted_project in payload.hosted_projects {
|
||||
let hosted_project: HostedProject = hosted_project.into();
|
||||
if let Some(old_project) = self
|
||||
.hosted_projects
|
||||
.insert(hosted_project.id, hosted_project.clone())
|
||||
{
|
||||
self.channel_states
|
||||
.entry(old_project.channel_id)
|
||||
.or_default()
|
||||
.remove_hosted_project(old_project.id);
|
||||
}
|
||||
self.channel_states
|
||||
.entry(hosted_project.channel_id)
|
||||
.or_default()
|
||||
.add_hosted_project(hosted_project.id);
|
||||
}
|
||||
|
||||
for hosted_project_id in payload.deleted_hosted_projects {
|
||||
let hosted_project_id = HostedProjectId(hosted_project_id);
|
||||
|
||||
if let Some(old_project) = self.hosted_projects.remove(&hosted_project_id) {
|
||||
self.channel_states
|
||||
.entry(old_project.channel_id)
|
||||
.or_default()
|
||||
.remove_hosted_project(old_project.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cx.notify();
|
||||
@@ -1223,7 +1129,7 @@ impl ChannelStore {
|
||||
participants.sort_by_key(|u| u.id);
|
||||
|
||||
this.channel_participants
|
||||
.insert(ChannelId(entry.channel_id), participants);
|
||||
.insert(entry.channel_id, participants);
|
||||
}
|
||||
|
||||
cx.notify();
|
||||
@@ -1301,12 +1207,4 @@ impl ChannelState {
|
||||
version: version.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
fn add_hosted_project(&mut self, project_id: HostedProjectId) {
|
||||
self.projects.insert(project_id);
|
||||
}
|
||||
|
||||
fn remove_hosted_project(&mut self, project_id: HostedProjectId) {
|
||||
self.projects.remove(&project_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::Channel;
|
||||
use client::ChannelId;
|
||||
use crate::{Channel, ChannelId};
|
||||
use collections::BTreeMap;
|
||||
use rpc::proto;
|
||||
use std::sync::Arc;
|
||||
@@ -51,32 +50,27 @@ pub struct ChannelPathsInsertGuard<'a> {
|
||||
impl<'a> ChannelPathsInsertGuard<'a> {
|
||||
pub fn insert(&mut self, channel_proto: proto::Channel) -> bool {
|
||||
let mut ret = false;
|
||||
let parent_path = channel_proto
|
||||
.parent_path
|
||||
.iter()
|
||||
.map(|cid| ChannelId(*cid))
|
||||
.collect();
|
||||
if let Some(existing_channel) = self.channels_by_id.get_mut(&ChannelId(channel_proto.id)) {
|
||||
if let Some(existing_channel) = self.channels_by_id.get_mut(&channel_proto.id) {
|
||||
let existing_channel = Arc::make_mut(existing_channel);
|
||||
|
||||
ret = existing_channel.visibility != channel_proto.visibility()
|
||||
|| existing_channel.name != channel_proto.name
|
||||
|| existing_channel.parent_path != parent_path;
|
||||
|| existing_channel.parent_path != channel_proto.parent_path;
|
||||
|
||||
existing_channel.visibility = channel_proto.visibility();
|
||||
existing_channel.name = channel_proto.name.into();
|
||||
existing_channel.parent_path = parent_path;
|
||||
existing_channel.parent_path = channel_proto.parent_path.into();
|
||||
} else {
|
||||
self.channels_by_id.insert(
|
||||
ChannelId(channel_proto.id),
|
||||
channel_proto.id,
|
||||
Arc::new(Channel {
|
||||
id: ChannelId(channel_proto.id),
|
||||
id: channel_proto.id,
|
||||
visibility: channel_proto.visibility(),
|
||||
name: channel_proto.name.into(),
|
||||
parent_path,
|
||||
parent_path: channel_proto.parent_path,
|
||||
}),
|
||||
);
|
||||
self.insert_root(ChannelId(channel_proto.id));
|
||||
self.insert_root(channel_proto.id);
|
||||
}
|
||||
ret
|
||||
}
|
||||
@@ -100,17 +94,14 @@ impl<'a> Drop for ChannelPathsInsertGuard<'a> {
|
||||
fn channel_path_sorting_key<'a>(
|
||||
id: ChannelId,
|
||||
channels_by_id: &'a BTreeMap<ChannelId, Arc<Channel>>,
|
||||
) -> impl Iterator<Item = (&str, ChannelId)> {
|
||||
) -> impl Iterator<Item = &str> {
|
||||
let (parent_path, name) = channels_by_id
|
||||
.get(&id)
|
||||
.map_or((&[] as &[_], None), |channel| {
|
||||
(
|
||||
channel.parent_path.as_slice(),
|
||||
Some((channel.name.as_ref(), channel.id)),
|
||||
)
|
||||
(channel.parent_path.as_slice(), Some(channel.name.as_ref()))
|
||||
});
|
||||
parent_path
|
||||
.iter()
|
||||
.filter_map(|id| Some((channels_by_id.get(id)?.name.as_ref(), *id)))
|
||||
.filter_map(|id| Some(channels_by_id.get(id)?.name.as_ref()))
|
||||
.chain(name)
|
||||
}
|
||||
|
||||
@@ -16,11 +16,13 @@ path = "src/main.rs"
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
clap = { version = "3.1", features = ["derive"] }
|
||||
dirs = "3.0"
|
||||
ipc-channel = "0.16"
|
||||
serde.workspace = true
|
||||
serde_derive.workspace = true
|
||||
util.workspace = true
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
core-foundation.workspace = true
|
||||
core-foundation = "0.9"
|
||||
core-services = "0.2"
|
||||
plist = "1.3"
|
||||
|
||||
@@ -156,39 +156,6 @@ mod linux {
|
||||
}
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
#[cfg(target_os = "windows")]
|
||||
mod windows {
|
||||
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;
|
||||
|
||||
@@ -13,9 +13,10 @@ doctest = false
|
||||
test-support = ["clock/test-support", "collections/test-support", "gpui/test-support", "rpc/test-support"]
|
||||
|
||||
[dependencies]
|
||||
chrono = { workspace = true, features = ["serde"] }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
clock.workspace = true
|
||||
collections.workspace = true
|
||||
db.workspace = true
|
||||
gpui.workspace = true
|
||||
util.workspace = true
|
||||
release_channel.workspace = true
|
||||
@@ -23,11 +24,13 @@ rpc.workspace = true
|
||||
text.workspace = true
|
||||
settings.workspace = true
|
||||
feature_flags.workspace = true
|
||||
sum_tree.workspace = true
|
||||
|
||||
anyhow.workspace = true
|
||||
async-recursion = "0.3"
|
||||
async-tungstenite = { version = "0.16", features = ["async-std", "async-native-tls"] }
|
||||
futures.workspace = true
|
||||
image = "0.23"
|
||||
lazy_static.workspace = true
|
||||
log.workspace = true
|
||||
once_cell = "1.19.0"
|
||||
@@ -36,15 +39,16 @@ postage.workspace = true
|
||||
rand.workspace = true
|
||||
schemars.workspace = true
|
||||
serde.workspace = true
|
||||
serde_derive.workspace = true
|
||||
serde_json.workspace = true
|
||||
sha2.workspace = true
|
||||
sha2 = "0.10"
|
||||
smol.workspace = true
|
||||
sysinfo.workspace = true
|
||||
telemetry_events.workspace = true
|
||||
tempfile.workspace = true
|
||||
thiserror.workspace = true
|
||||
time.workspace = true
|
||||
tiny_http = "0.8"
|
||||
uuid.workspace = true
|
||||
url.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -42,11 +42,11 @@ use std::{
|
||||
use telemetry::Telemetry;
|
||||
use thiserror::Error;
|
||||
use url::Url;
|
||||
use util::http::{HttpClient, HttpClientWithUrl};
|
||||
use util::http::{HttpClient, ZedHttpClient};
|
||||
use util::{ResultExt, TryFutureExt};
|
||||
|
||||
pub use rpc::*;
|
||||
pub use telemetry_events::Event;
|
||||
pub use telemetry::Event;
|
||||
pub use user::*;
|
||||
|
||||
lazy_static! {
|
||||
@@ -153,7 +153,7 @@ impl Global for GlobalClient {}
|
||||
pub struct Client {
|
||||
id: AtomicU64,
|
||||
peer: Arc<Peer>,
|
||||
http: Arc<HttpClientWithUrl>,
|
||||
http: Arc<ZedHttpClient>,
|
||||
telemetry: Arc<Telemetry>,
|
||||
state: RwLock<ClientState>,
|
||||
|
||||
@@ -424,7 +424,7 @@ impl settings::Settings for TelemetrySettings {
|
||||
impl Client {
|
||||
pub fn new(
|
||||
clock: Arc<dyn SystemClock>,
|
||||
http: Arc<HttpClientWithUrl>,
|
||||
http: Arc<ZedHttpClient>,
|
||||
cx: &mut AppContext,
|
||||
) -> Arc<Self> {
|
||||
let client = Arc::new(Self {
|
||||
@@ -447,7 +447,7 @@ impl Client {
|
||||
self.id.load(std::sync::atomic::Ordering::SeqCst)
|
||||
}
|
||||
|
||||
pub fn http_client(&self) -> Arc<HttpClientWithUrl> {
|
||||
pub fn http_client(&self) -> Arc<ZedHttpClient> {
|
||||
self.http.clone()
|
||||
}
|
||||
|
||||
@@ -970,14 +970,14 @@ impl Client {
|
||||
}
|
||||
|
||||
async fn get_rpc_url(
|
||||
http: Arc<HttpClientWithUrl>,
|
||||
http: Arc<ZedHttpClient>,
|
||||
release_channel: Option<ReleaseChannel>,
|
||||
) -> Result<Url> {
|
||||
if let Some(url) = &*ZED_RPC_URL {
|
||||
return Url::parse(url).context("invalid rpc url");
|
||||
}
|
||||
|
||||
let mut url = http.build_url("/rpc");
|
||||
let mut url = http.zed_url("/rpc");
|
||||
if let Some(preview_param) =
|
||||
release_channel.and_then(|channel| channel.release_query_param())
|
||||
{
|
||||
@@ -1110,7 +1110,7 @@ impl Client {
|
||||
|
||||
// Open the Zed sign-in page in the user's browser, with query parameters that indicate
|
||||
// that the user is signing in from a Zed app running on the same device.
|
||||
let mut url = http.build_url(&format!(
|
||||
let mut url = http.zed_url(&format!(
|
||||
"/native_app_signin?native_app_port={}&native_app_public_key={}",
|
||||
port, public_key_string
|
||||
));
|
||||
@@ -1145,7 +1145,7 @@ impl Client {
|
||||
}
|
||||
|
||||
let post_auth_url =
|
||||
http.build_url("/native_app_signin_succeeded");
|
||||
http.zed_url("/native_app_signin_succeeded");
|
||||
req.respond(
|
||||
tiny_http::Response::empty(302).with_header(
|
||||
tiny_http::Header::from_bytes(
|
||||
@@ -1187,7 +1187,7 @@ impl Client {
|
||||
}
|
||||
|
||||
async fn authenticate_as_admin(
|
||||
http: Arc<HttpClientWithUrl>,
|
||||
http: Arc<ZedHttpClient>,
|
||||
login: String,
|
||||
mut api_token: String,
|
||||
) -> Result<Credentials> {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
mod event_coalescer;
|
||||
|
||||
use crate::{ChannelId, TelemetrySettings};
|
||||
use crate::TelemetrySettings;
|
||||
use chrono::{DateTime, Utc};
|
||||
use clock::SystemClock;
|
||||
use futures::Future;
|
||||
@@ -8,6 +8,7 @@ use gpui::{AppContext, AppMetadata, BackgroundExecutor, Task};
|
||||
use once_cell::sync::Lazy;
|
||||
use parking_lot::Mutex;
|
||||
use release_channel::ReleaseChannel;
|
||||
use serde::Serialize;
|
||||
use settings::{Settings, SettingsStore};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::io::Write;
|
||||
@@ -15,12 +16,8 @@ use std::{env, mem, path::PathBuf, sync::Arc, time::Duration};
|
||||
use sysinfo::{
|
||||
CpuRefreshKind, Pid, PidExt, ProcessExt, ProcessRefreshKind, RefreshKind, System, SystemExt,
|
||||
};
|
||||
use telemetry_events::{
|
||||
ActionEvent, AppEvent, AssistantEvent, AssistantKind, CallEvent, CopilotEvent, CpuEvent,
|
||||
EditEvent, EditorEvent, Event, EventRequestBody, EventWrapper, MemoryEvent, SettingEvent,
|
||||
};
|
||||
use tempfile::NamedTempFile;
|
||||
use util::http::{self, HttpClient, HttpClientWithUrl, Method};
|
||||
use util::http::{self, HttpClient, Method, ZedHttpClient};
|
||||
#[cfg(not(debug_assertions))]
|
||||
use util::ResultExt;
|
||||
use util::TryFutureExt;
|
||||
@@ -29,7 +26,7 @@ use self::event_coalescer::EventCoalescer;
|
||||
|
||||
pub struct Telemetry {
|
||||
clock: Arc<dyn SystemClock>,
|
||||
http_client: Arc<HttpClientWithUrl>,
|
||||
http_client: Arc<ZedHttpClient>,
|
||||
executor: BackgroundExecutor,
|
||||
state: Arc<Mutex<TelemetryState>>,
|
||||
}
|
||||
@@ -38,7 +35,7 @@ struct TelemetryState {
|
||||
settings: TelemetrySettings,
|
||||
metrics_id: Option<Arc<str>>, // Per logged-in user
|
||||
installation_id: Option<Arc<str>>, // Per app installation (different for dev, nightly, preview, and stable)
|
||||
session_id: Option<String>, // Per app launch
|
||||
session_id: Option<Arc<str>>, // Per app launch
|
||||
release_channel: Option<&'static str>,
|
||||
app_metadata: AppMetadata,
|
||||
architecture: &'static str,
|
||||
@@ -51,6 +48,93 @@ struct TelemetryState {
|
||||
max_queue_size: usize,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
struct EventRequestBody {
|
||||
installation_id: Option<Arc<str>>,
|
||||
session_id: Option<Arc<str>>,
|
||||
is_staff: Option<bool>,
|
||||
app_version: Option<String>,
|
||||
os_name: &'static str,
|
||||
os_version: Option<String>,
|
||||
architecture: &'static str,
|
||||
release_channel: Option<&'static str>,
|
||||
events: Vec<EventWrapper>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
struct EventWrapper {
|
||||
signed_in: bool,
|
||||
#[serde(flatten)]
|
||||
event: Event,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum AssistantKind {
|
||||
Panel,
|
||||
Inline,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum Event {
|
||||
Editor {
|
||||
operation: &'static str,
|
||||
file_extension: Option<String>,
|
||||
vim_mode: bool,
|
||||
copilot_enabled: bool,
|
||||
copilot_enabled_for_language: bool,
|
||||
milliseconds_since_first_event: i64,
|
||||
},
|
||||
Copilot {
|
||||
suggestion_id: Option<String>,
|
||||
suggestion_accepted: bool,
|
||||
file_extension: Option<String>,
|
||||
milliseconds_since_first_event: i64,
|
||||
},
|
||||
Call {
|
||||
operation: &'static str,
|
||||
room_id: Option<u64>,
|
||||
channel_id: Option<u64>,
|
||||
milliseconds_since_first_event: i64,
|
||||
},
|
||||
Assistant {
|
||||
conversation_id: Option<String>,
|
||||
kind: AssistantKind,
|
||||
model: &'static str,
|
||||
milliseconds_since_first_event: i64,
|
||||
},
|
||||
Cpu {
|
||||
usage_as_percentage: f32,
|
||||
core_count: u32,
|
||||
milliseconds_since_first_event: i64,
|
||||
},
|
||||
Memory {
|
||||
memory_in_bytes: u64,
|
||||
virtual_memory_in_bytes: u64,
|
||||
milliseconds_since_first_event: i64,
|
||||
},
|
||||
App {
|
||||
operation: String,
|
||||
milliseconds_since_first_event: i64,
|
||||
},
|
||||
Setting {
|
||||
setting: &'static str,
|
||||
value: String,
|
||||
milliseconds_since_first_event: i64,
|
||||
},
|
||||
Edit {
|
||||
duration: i64,
|
||||
environment: &'static str,
|
||||
milliseconds_since_first_event: i64,
|
||||
},
|
||||
Action {
|
||||
source: &'static str,
|
||||
action: String,
|
||||
milliseconds_since_first_event: i64,
|
||||
},
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
const MAX_QUEUE_LEN: usize = 5;
|
||||
|
||||
@@ -62,6 +146,7 @@ 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(|| {
|
||||
option_env!("ZED_CLIENT_CHECKSUM_SEED")
|
||||
.map(|s| s.as_bytes().into())
|
||||
@@ -75,7 +160,7 @@ static ZED_CLIENT_CHECKSUM_SEED: Lazy<Option<Vec<u8>>> = Lazy::new(|| {
|
||||
impl Telemetry {
|
||||
pub fn new(
|
||||
clock: Arc<dyn SystemClock>,
|
||||
client: Arc<HttpClientWithUrl>,
|
||||
client: Arc<ZedHttpClient>,
|
||||
cx: &mut AppContext,
|
||||
) -> Arc<Self> {
|
||||
let release_channel =
|
||||
@@ -96,7 +181,7 @@ impl Telemetry {
|
||||
log_file: None,
|
||||
is_staff: None,
|
||||
first_event_date_time: None,
|
||||
event_coalescer: EventCoalescer::new(clock.clone()),
|
||||
event_coalescer: EventCoalescer::new(),
|
||||
max_queue_size: MAX_QUEUE_LEN,
|
||||
}));
|
||||
|
||||
@@ -233,13 +318,15 @@ impl Telemetry {
|
||||
copilot_enabled: bool,
|
||||
copilot_enabled_for_language: bool,
|
||||
) {
|
||||
let event = Event::Editor(EditorEvent {
|
||||
let event = Event::Editor {
|
||||
file_extension,
|
||||
vim_mode,
|
||||
operation: operation.into(),
|
||||
operation,
|
||||
copilot_enabled,
|
||||
copilot_enabled_for_language,
|
||||
});
|
||||
milliseconds_since_first_event: self
|
||||
.milliseconds_since_first_event(self.clock.utc_now()),
|
||||
};
|
||||
|
||||
self.report_event(event)
|
||||
}
|
||||
@@ -250,11 +337,13 @@ impl Telemetry {
|
||||
suggestion_accepted: bool,
|
||||
file_extension: Option<String>,
|
||||
) {
|
||||
let event = Event::Copilot(CopilotEvent {
|
||||
let event = Event::Copilot {
|
||||
suggestion_id,
|
||||
suggestion_accepted,
|
||||
file_extension,
|
||||
});
|
||||
milliseconds_since_first_event: self
|
||||
.milliseconds_since_first_event(self.clock.utc_now()),
|
||||
};
|
||||
|
||||
self.report_event(event)
|
||||
}
|
||||
@@ -265,11 +354,13 @@ impl Telemetry {
|
||||
kind: AssistantKind,
|
||||
model: &'static str,
|
||||
) {
|
||||
let event = Event::Assistant(AssistantEvent {
|
||||
let event = Event::Assistant {
|
||||
conversation_id,
|
||||
kind,
|
||||
model: model.to_string(),
|
||||
});
|
||||
model,
|
||||
milliseconds_since_first_event: self
|
||||
.milliseconds_since_first_event(self.clock.utc_now()),
|
||||
};
|
||||
|
||||
self.report_event(event)
|
||||
}
|
||||
@@ -278,22 +369,26 @@ impl Telemetry {
|
||||
self: &Arc<Self>,
|
||||
operation: &'static str,
|
||||
room_id: Option<u64>,
|
||||
channel_id: Option<ChannelId>,
|
||||
channel_id: Option<u64>,
|
||||
) {
|
||||
let event = Event::Call(CallEvent {
|
||||
operation: operation.to_string(),
|
||||
let event = Event::Call {
|
||||
operation,
|
||||
room_id,
|
||||
channel_id: channel_id.map(|cid| cid.0),
|
||||
});
|
||||
channel_id,
|
||||
milliseconds_since_first_event: self
|
||||
.milliseconds_since_first_event(self.clock.utc_now()),
|
||||
};
|
||||
|
||||
self.report_event(event)
|
||||
}
|
||||
|
||||
pub fn report_cpu_event(self: &Arc<Self>, usage_as_percentage: f32, core_count: u32) {
|
||||
let event = Event::Cpu(CpuEvent {
|
||||
let event = Event::Cpu {
|
||||
usage_as_percentage,
|
||||
core_count,
|
||||
});
|
||||
milliseconds_since_first_event: self
|
||||
.milliseconds_since_first_event(self.clock.utc_now()),
|
||||
};
|
||||
|
||||
self.report_event(event)
|
||||
}
|
||||
@@ -303,16 +398,22 @@ impl Telemetry {
|
||||
memory_in_bytes: u64,
|
||||
virtual_memory_in_bytes: u64,
|
||||
) {
|
||||
let event = Event::Memory(MemoryEvent {
|
||||
let event = Event::Memory {
|
||||
memory_in_bytes,
|
||||
virtual_memory_in_bytes,
|
||||
});
|
||||
milliseconds_since_first_event: self
|
||||
.milliseconds_since_first_event(self.clock.utc_now()),
|
||||
};
|
||||
|
||||
self.report_event(event)
|
||||
}
|
||||
|
||||
pub fn report_app_event(self: &Arc<Self>, operation: String) -> Event {
|
||||
let event = Event::App(AppEvent { operation });
|
||||
let event = Event::App {
|
||||
operation,
|
||||
milliseconds_since_first_event: self
|
||||
.milliseconds_since_first_event(self.clock.utc_now()),
|
||||
};
|
||||
|
||||
self.report_event(event.clone());
|
||||
|
||||
@@ -320,10 +421,12 @@ impl Telemetry {
|
||||
}
|
||||
|
||||
pub fn report_setting_event(self: &Arc<Self>, setting: &'static str, value: String) {
|
||||
let event = Event::Setting(SettingEvent {
|
||||
setting: setting.to_string(),
|
||||
let event = Event::Setting {
|
||||
setting,
|
||||
value,
|
||||
});
|
||||
milliseconds_since_first_event: self
|
||||
.milliseconds_since_first_event(self.clock.utc_now()),
|
||||
};
|
||||
|
||||
self.report_event(event)
|
||||
}
|
||||
@@ -334,24 +437,42 @@ impl Telemetry {
|
||||
drop(state);
|
||||
|
||||
if let Some((start, end, environment)) = period_data {
|
||||
let event = Event::Edit(EditEvent {
|
||||
let event = Event::Edit {
|
||||
duration: end.timestamp_millis() - start.timestamp_millis(),
|
||||
environment: environment.to_string(),
|
||||
});
|
||||
environment,
|
||||
milliseconds_since_first_event: self
|
||||
.milliseconds_since_first_event(self.clock.utc_now()),
|
||||
};
|
||||
|
||||
self.report_event(event);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn report_action_event(self: &Arc<Self>, source: &'static str, action: String) {
|
||||
let event = Event::Action(ActionEvent {
|
||||
source: source.to_string(),
|
||||
let event = Event::Action {
|
||||
source,
|
||||
action,
|
||||
});
|
||||
milliseconds_since_first_event: self
|
||||
.milliseconds_since_first_event(self.clock.utc_now()),
|
||||
};
|
||||
|
||||
self.report_event(event)
|
||||
}
|
||||
|
||||
fn milliseconds_since_first_event(self: &Arc<Self>, date_time: DateTime<Utc>) -> i64 {
|
||||
let mut state = self.state.lock();
|
||||
|
||||
match state.first_event_date_time {
|
||||
Some(first_event_date_time) => {
|
||||
date_time.timestamp_millis() - first_event_date_time.timestamp_millis()
|
||||
}
|
||||
None => {
|
||||
state.first_event_date_time = Some(date_time);
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn report_event(self: &Arc<Self>, event: Event) {
|
||||
let mut state = self.state.lock();
|
||||
|
||||
@@ -368,24 +489,8 @@ impl Telemetry {
|
||||
}));
|
||||
}
|
||||
|
||||
let date_time = self.clock.utc_now();
|
||||
|
||||
let milliseconds_since_first_event = match state.first_event_date_time {
|
||||
Some(first_event_date_time) => {
|
||||
date_time.timestamp_millis() - first_event_date_time.timestamp_millis()
|
||||
}
|
||||
None => {
|
||||
state.first_event_date_time = Some(date_time);
|
||||
0
|
||||
}
|
||||
};
|
||||
|
||||
let signed_in = state.metrics_id.is_some();
|
||||
state.events_queue.push(EventWrapper {
|
||||
signed_in,
|
||||
milliseconds_since_first_event,
|
||||
event,
|
||||
});
|
||||
state.events_queue.push(EventWrapper { signed_in, event });
|
||||
|
||||
if state.installation_id.is_some() {
|
||||
if state.events_queue.len() >= state.max_queue_size {
|
||||
@@ -440,22 +545,21 @@ impl Telemetry {
|
||||
{
|
||||
let state = this.state.lock();
|
||||
let request_body = EventRequestBody {
|
||||
installation_id: state.installation_id.as_deref().map(Into::into),
|
||||
installation_id: state.installation_id.clone(),
|
||||
session_id: state.session_id.clone(),
|
||||
is_staff: state.is_staff.clone(),
|
||||
app_version: state
|
||||
.app_metadata
|
||||
.app_version
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
os_name: state.app_metadata.os_name.to_string(),
|
||||
.map(|version| version.to_string()),
|
||||
os_name: state.app_metadata.os_name,
|
||||
os_version: state
|
||||
.app_metadata
|
||||
.os_version
|
||||
.map(|version| version.to_string()),
|
||||
architecture: state.architecture.to_string(),
|
||||
architecture: state.architecture,
|
||||
|
||||
release_channel: state.release_channel.map(Into::into),
|
||||
release_channel: state.release_channel,
|
||||
events,
|
||||
};
|
||||
json_bytes.clear();
|
||||
@@ -474,7 +578,7 @@ impl Telemetry {
|
||||
|
||||
let request = http::Request::builder()
|
||||
.method(Method::POST)
|
||||
.uri(this.http_client.build_zed_api_url("/telemetry/events"))
|
||||
.uri(&this.http_client.zed_url("/api/events"))
|
||||
.header("Content-Type", "text/plain")
|
||||
.header("x-zed-checksum", checksum)
|
||||
.body(json_bytes.into());
|
||||
@@ -523,9 +627,10 @@ mod tests {
|
||||
let event = telemetry.report_app_event(operation.clone());
|
||||
assert_eq!(
|
||||
event,
|
||||
Event::App(AppEvent {
|
||||
Event::App {
|
||||
operation: operation.clone(),
|
||||
})
|
||||
milliseconds_since_first_event: 0
|
||||
}
|
||||
);
|
||||
assert_eq!(telemetry.state.lock().events_queue.len(), 1);
|
||||
assert!(telemetry.state.lock().flush_events_task.is_some());
|
||||
@@ -539,9 +644,10 @@ mod tests {
|
||||
let event = telemetry.report_app_event(operation.clone());
|
||||
assert_eq!(
|
||||
event,
|
||||
Event::App(AppEvent {
|
||||
Event::App {
|
||||
operation: operation.clone(),
|
||||
})
|
||||
milliseconds_since_first_event: 100
|
||||
}
|
||||
);
|
||||
assert_eq!(telemetry.state.lock().events_queue.len(), 2);
|
||||
assert!(telemetry.state.lock().flush_events_task.is_some());
|
||||
@@ -555,9 +661,10 @@ mod tests {
|
||||
let event = telemetry.report_app_event(operation.clone());
|
||||
assert_eq!(
|
||||
event,
|
||||
Event::App(AppEvent {
|
||||
Event::App {
|
||||
operation: operation.clone(),
|
||||
})
|
||||
milliseconds_since_first_event: 200
|
||||
}
|
||||
);
|
||||
assert_eq!(telemetry.state.lock().events_queue.len(), 3);
|
||||
assert!(telemetry.state.lock().flush_events_task.is_some());
|
||||
@@ -572,9 +679,10 @@ mod tests {
|
||||
let event = telemetry.report_app_event(operation.clone());
|
||||
assert_eq!(
|
||||
event,
|
||||
Event::App(AppEvent {
|
||||
Event::App {
|
||||
operation: operation.clone(),
|
||||
})
|
||||
milliseconds_since_first_event: 300
|
||||
}
|
||||
);
|
||||
|
||||
assert!(is_empty_state(&telemetry));
|
||||
@@ -604,9 +712,10 @@ mod tests {
|
||||
let event = telemetry.report_app_event(operation.clone());
|
||||
assert_eq!(
|
||||
event,
|
||||
Event::App(AppEvent {
|
||||
Event::App {
|
||||
operation: operation.clone(),
|
||||
})
|
||||
milliseconds_since_first_event: 0
|
||||
}
|
||||
);
|
||||
assert_eq!(telemetry.state.lock().events_queue.len(), 1);
|
||||
assert!(telemetry.state.lock().flush_events_task.is_some());
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
use std::sync::Arc;
|
||||
use std::time;
|
||||
|
||||
use chrono::{DateTime, Duration, Utc};
|
||||
use clock::SystemClock;
|
||||
use std::time;
|
||||
|
||||
const COALESCE_TIMEOUT: time::Duration = time::Duration::from_secs(20);
|
||||
const SIMULATED_DURATION_FOR_SINGLE_EVENT: time::Duration = time::Duration::from_millis(1);
|
||||
@@ -15,20 +12,30 @@ struct PeriodData {
|
||||
}
|
||||
|
||||
pub struct EventCoalescer {
|
||||
clock: Arc<dyn SystemClock>,
|
||||
state: Option<PeriodData>,
|
||||
}
|
||||
|
||||
impl EventCoalescer {
|
||||
pub fn new(clock: Arc<dyn SystemClock>) -> Self {
|
||||
Self { clock, state: None }
|
||||
pub fn new() -> Self {
|
||||
Self { state: None }
|
||||
}
|
||||
|
||||
pub fn log_event(
|
||||
&mut self,
|
||||
environment: &'static str,
|
||||
) -> Option<(DateTime<Utc>, DateTime<Utc>, &'static str)> {
|
||||
let log_time = self.clock.utc_now();
|
||||
self.log_event_with_time(Utc::now(), environment)
|
||||
}
|
||||
|
||||
// pub fn close_current_period(&mut self) -> Option<(DateTime<Utc>, DateTime<Utc>)> {
|
||||
// self.environment.map(|env| self.log_event(env)).flatten()
|
||||
// }
|
||||
|
||||
fn log_event_with_time(
|
||||
&mut self,
|
||||
log_time: DateTime<Utc>,
|
||||
environment: &'static str,
|
||||
) -> Option<(DateTime<Utc>, DateTime<Utc>, &'static str)> {
|
||||
let coalesce_timeout = Duration::from_std(COALESCE_TIMEOUT).unwrap();
|
||||
|
||||
let Some(state) = &mut self.state else {
|
||||
@@ -71,22 +78,18 @@ impl EventCoalescer {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use chrono::TimeZone;
|
||||
use clock::FakeSystemClock;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_same_context_exceeding_timeout() {
|
||||
let clock = Arc::new(FakeSystemClock::new(
|
||||
Utc.with_ymd_and_hms(1990, 4, 12, 0, 0, 0).unwrap(),
|
||||
));
|
||||
let environment_1 = "environment_1";
|
||||
let mut event_coalescer = EventCoalescer::new(clock.clone());
|
||||
let mut event_coalescer = EventCoalescer::new();
|
||||
|
||||
assert_eq!(event_coalescer.state, None);
|
||||
|
||||
let period_start = clock.utc_now();
|
||||
let period_data = event_coalescer.log_event(environment_1);
|
||||
let period_start = Utc.with_ymd_and_hms(1990, 4, 12, 0, 0, 0).unwrap();
|
||||
let period_data = event_coalescer.log_event_with_time(period_start, environment_1);
|
||||
|
||||
assert_eq!(period_data, None);
|
||||
assert_eq!(
|
||||
@@ -99,12 +102,12 @@ mod tests {
|
||||
);
|
||||
|
||||
let within_timeout_adjustment = Duration::from_std(COALESCE_TIMEOUT / 2).unwrap();
|
||||
let mut period_end = period_start;
|
||||
|
||||
// Ensure that many calls within the timeout don't start a new period
|
||||
for _ in 0..100 {
|
||||
clock.advance(within_timeout_adjustment);
|
||||
let period_data = event_coalescer.log_event(environment_1);
|
||||
let period_end = clock.utc_now();
|
||||
period_end += within_timeout_adjustment;
|
||||
let period_data = event_coalescer.log_event_with_time(period_end, environment_1);
|
||||
|
||||
assert_eq!(period_data, None);
|
||||
assert_eq!(
|
||||
@@ -117,12 +120,10 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
let period_end = clock.utc_now();
|
||||
let exceed_timeout_adjustment = Duration::from_std(COALESCE_TIMEOUT * 2).unwrap();
|
||||
// Logging an event exceeding the timeout should start a new period
|
||||
clock.advance(exceed_timeout_adjustment);
|
||||
let new_period_start = clock.utc_now();
|
||||
let period_data = event_coalescer.log_event(environment_1);
|
||||
let new_period_start = period_end + exceed_timeout_adjustment;
|
||||
let period_data = event_coalescer.log_event_with_time(new_period_start, environment_1);
|
||||
|
||||
assert_eq!(period_data, Some((period_start, period_end, environment_1)));
|
||||
assert_eq!(
|
||||
@@ -137,16 +138,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_different_environment_under_timeout() {
|
||||
let clock = Arc::new(FakeSystemClock::new(
|
||||
Utc.with_ymd_and_hms(1990, 4, 12, 0, 0, 0).unwrap(),
|
||||
));
|
||||
let environment_1 = "environment_1";
|
||||
let mut event_coalescer = EventCoalescer::new(clock.clone());
|
||||
let mut event_coalescer = EventCoalescer::new();
|
||||
|
||||
assert_eq!(event_coalescer.state, None);
|
||||
|
||||
let period_start = clock.utc_now();
|
||||
let period_data = event_coalescer.log_event(environment_1);
|
||||
let period_start = Utc.with_ymd_and_hms(1990, 4, 12, 0, 0, 0).unwrap();
|
||||
let period_data = event_coalescer.log_event_with_time(period_start, environment_1);
|
||||
|
||||
assert_eq!(period_data, None);
|
||||
assert_eq!(
|
||||
@@ -159,9 +157,8 @@ mod tests {
|
||||
);
|
||||
|
||||
let within_timeout_adjustment = Duration::from_std(COALESCE_TIMEOUT / 2).unwrap();
|
||||
clock.advance(within_timeout_adjustment);
|
||||
let period_end = clock.utc_now();
|
||||
let period_data = event_coalescer.log_event(environment_1);
|
||||
let period_end = period_start + within_timeout_adjustment;
|
||||
let period_data = event_coalescer.log_event_with_time(period_end, environment_1);
|
||||
|
||||
assert_eq!(period_data, None);
|
||||
assert_eq!(
|
||||
@@ -173,12 +170,10 @@ mod tests {
|
||||
})
|
||||
);
|
||||
|
||||
clock.advance(within_timeout_adjustment);
|
||||
|
||||
// Logging an event within the timeout but with a different environment should start a new period
|
||||
let period_end = clock.utc_now();
|
||||
let period_end = period_end + within_timeout_adjustment;
|
||||
let environment_2 = "environment_2";
|
||||
let period_data = event_coalescer.log_event(environment_2);
|
||||
let period_data = event_coalescer.log_event_with_time(period_end, environment_2);
|
||||
|
||||
assert_eq!(period_data, Some((period_start, period_end, environment_1)));
|
||||
assert_eq!(
|
||||
@@ -193,16 +188,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_switching_environment_while_within_timeout() {
|
||||
let clock = Arc::new(FakeSystemClock::new(
|
||||
Utc.with_ymd_and_hms(1990, 4, 12, 0, 0, 0).unwrap(),
|
||||
));
|
||||
let environment_1 = "environment_1";
|
||||
let mut event_coalescer = EventCoalescer::new(clock.clone());
|
||||
let mut event_coalescer = EventCoalescer::new();
|
||||
|
||||
assert_eq!(event_coalescer.state, None);
|
||||
|
||||
let period_start = clock.utc_now();
|
||||
let period_data = event_coalescer.log_event(environment_1);
|
||||
let period_start = Utc.with_ymd_and_hms(1990, 4, 12, 0, 0, 0).unwrap();
|
||||
let period_data = event_coalescer.log_event_with_time(period_start, environment_1);
|
||||
|
||||
assert_eq!(period_data, None);
|
||||
assert_eq!(
|
||||
@@ -215,10 +207,9 @@ mod tests {
|
||||
);
|
||||
|
||||
let within_timeout_adjustment = Duration::from_std(COALESCE_TIMEOUT / 2).unwrap();
|
||||
clock.advance(within_timeout_adjustment);
|
||||
let period_end = clock.utc_now();
|
||||
let period_end = period_start + within_timeout_adjustment;
|
||||
let environment_2 = "environment_2";
|
||||
let period_data = event_coalescer.log_event(environment_2);
|
||||
let period_data = event_coalescer.log_event_with_time(period_end, environment_2);
|
||||
|
||||
assert_eq!(period_data, Some((period_start, period_end, environment_1)));
|
||||
assert_eq!(
|
||||
@@ -230,26 +221,22 @@ mod tests {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// 0 20 40 60
|
||||
// |-------------------|-------------------|-------------------|-------------------
|
||||
// |--------|----------env change
|
||||
// |-------------------
|
||||
// |period_start |period_end
|
||||
// |new_period_start
|
||||
// // 0 20 40 60
|
||||
// // |-------------------|-------------------|-------------------|-------------------
|
||||
// // |--------|----------env change
|
||||
// // |-------------------
|
||||
// // |period_start |period_end
|
||||
// // |new_period_start
|
||||
|
||||
#[test]
|
||||
fn test_switching_environment_while_exceeding_timeout() {
|
||||
let clock = Arc::new(FakeSystemClock::new(
|
||||
Utc.with_ymd_and_hms(1990, 4, 12, 0, 0, 0).unwrap(),
|
||||
));
|
||||
let environment_1 = "environment_1";
|
||||
let mut event_coalescer = EventCoalescer::new(clock.clone());
|
||||
let mut event_coalescer = EventCoalescer::new();
|
||||
|
||||
assert_eq!(event_coalescer.state, None);
|
||||
|
||||
let period_start = clock.utc_now();
|
||||
let period_data = event_coalescer.log_event(environment_1);
|
||||
let period_start = Utc.with_ymd_and_hms(1990, 4, 12, 0, 0, 0).unwrap();
|
||||
let period_data = event_coalescer.log_event_with_time(period_start, environment_1);
|
||||
|
||||
assert_eq!(period_data, None);
|
||||
assert_eq!(
|
||||
@@ -262,10 +249,9 @@ mod tests {
|
||||
);
|
||||
|
||||
let exceed_timeout_adjustment = Duration::from_std(COALESCE_TIMEOUT * 2).unwrap();
|
||||
clock.advance(exceed_timeout_adjustment);
|
||||
let period_end = clock.utc_now();
|
||||
let period_end = period_start + exceed_timeout_adjustment;
|
||||
let environment_2 = "environment_2";
|
||||
let period_data = event_coalescer.log_event(environment_2);
|
||||
let period_data = event_coalescer.log_event_with_time(period_end, environment_2);
|
||||
|
||||
assert_eq!(
|
||||
period_data,
|
||||
@@ -284,7 +270,6 @@ mod tests {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// 0 20 40 60
|
||||
// |-------------------|-------------------|-------------------|-------------------
|
||||
// |--------|----------------------------------------env change
|
||||
|
||||
@@ -15,15 +15,6 @@ use util::TryFutureExt as _;
|
||||
|
||||
pub type UserId = u64;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||
pub struct ChannelId(pub u64);
|
||||
|
||||
impl std::fmt::Display for ChannelId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct ParticipantIndex(pub u32);
|
||||
|
||||
|
||||
@@ -12,12 +12,6 @@ BLOB_STORE_SECRET_KEY = "the-blob-store-secret-key"
|
||||
BLOB_STORE_BUCKET = "the-extensions-bucket"
|
||||
BLOB_STORE_URL = "http://127.0.0.1:9000"
|
||||
BLOB_STORE_REGION = "the-region"
|
||||
ZED_CLIENT_CHECKSUM_SEED = "development-checksum-seed"
|
||||
|
||||
# CLICKHOUSE_URL = ""
|
||||
# CLICKHOUSE_USER = "default"
|
||||
# CLICKHOUSE_PASSWORD = ""
|
||||
# CLICKHOUSE_DATABASE = "default"
|
||||
|
||||
# RUST_LOG=info
|
||||
# LOG_JSON=true
|
||||
|
||||
@@ -7,9 +7,6 @@ version = "0.44.0"
|
||||
publish = false
|
||||
license = "AGPL-3.0-or-later"
|
||||
|
||||
[features]
|
||||
seed-support = ["reqwest"]
|
||||
|
||||
[[bin]]
|
||||
name = "collab"
|
||||
|
||||
@@ -24,16 +21,17 @@ aws-config = { version = "1.1.5" }
|
||||
aws-sdk-s3 = { version = "1.15.0" }
|
||||
axum = { version = "0.5", features = ["json", "headers", "ws"] }
|
||||
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
|
||||
clickhouse.workspace = true
|
||||
collections.workspace = true
|
||||
dashmap = "5.4"
|
||||
envy = "0.4.2"
|
||||
futures.workspace = true
|
||||
hex.workspace = true
|
||||
hyper = "0.14"
|
||||
lazy_static.workspace = true
|
||||
lipsum = { version = "0.8", optional = true }
|
||||
live_kit_server.workspace = true
|
||||
log.workspace = true
|
||||
nanoid = "0.4"
|
||||
@@ -49,15 +47,16 @@ semver.workspace = true
|
||||
serde.workspace = true
|
||||
serde_derive.workspace = true
|
||||
serde_json.workspace = true
|
||||
sha2.workspace = true
|
||||
sha-1 = "0.9"
|
||||
smallvec.workspace = true
|
||||
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres", "json", "time", "uuid", "any"] }
|
||||
telemetry_events.workspace = true
|
||||
text.workspace = true
|
||||
time.workspace = true
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-tungstenite = "0.17"
|
||||
toml.workspace = true
|
||||
tonic = "0.6"
|
||||
tower = "0.4"
|
||||
tower-http = { workspace = true, features = ["trace"] }
|
||||
tracing = "0.1.34"
|
||||
tracing-log = "0.1.3"
|
||||
tracing-subscriber = { version = "0.3.11", features = ["env-filter", "json"] }
|
||||
@@ -99,3 +98,6 @@ theme.workspace = true
|
||||
unindent.workspace = true
|
||||
util.workspace = true
|
||||
workspace = { workspace = true, features = ["test-support"] }
|
||||
|
||||
[features]
|
||||
seed-support = ["clap", "lipsum", "reqwest"]
|
||||
|
||||
@@ -9,7 +9,7 @@ kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
namespace: ${ZED_KUBE_NAMESPACE}
|
||||
name: ${ZED_SERVICE_NAME}
|
||||
name: collab
|
||||
annotations:
|
||||
service.beta.kubernetes.io/do-loadbalancer-tls-ports: "443"
|
||||
service.beta.kubernetes.io/do-loadbalancer-certificate-id: ${ZED_DO_CERTIFICATE_ID}
|
||||
@@ -17,7 +17,7 @@ metadata:
|
||||
spec:
|
||||
type: LoadBalancer
|
||||
selector:
|
||||
app: ${ZED_SERVICE_NAME}
|
||||
app: collab
|
||||
ports:
|
||||
- name: web
|
||||
protocol: TCP
|
||||
@@ -29,17 +29,17 @@ apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
namespace: ${ZED_KUBE_NAMESPACE}
|
||||
name: ${ZED_SERVICE_NAME}
|
||||
name: collab
|
||||
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: ${ZED_SERVICE_NAME}
|
||||
app: collab
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: ${ZED_SERVICE_NAME}
|
||||
app: collab
|
||||
annotations:
|
||||
ad.datadoghq.com/collab.check_names: |
|
||||
["openmetrics"]
|
||||
@@ -55,11 +55,10 @@ spec:
|
||||
]
|
||||
spec:
|
||||
containers:
|
||||
- name: ${ZED_SERVICE_NAME}
|
||||
- name: collab
|
||||
image: "${ZED_IMAGE_ID}"
|
||||
args:
|
||||
- serve
|
||||
- ${ZED_SERVICE_NAME}
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
@@ -91,11 +90,6 @@ spec:
|
||||
secretKeyRef:
|
||||
name: api
|
||||
key: token
|
||||
- name: ZED_CLIENT_CHECKSUM_SEED
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: zed-client
|
||||
key: checksum-seed
|
||||
- name: LIVE_KIT_SERVER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
@@ -136,26 +130,6 @@ spec:
|
||||
secretKeyRef:
|
||||
name: blob-store
|
||||
key: bucket
|
||||
- name: CLICKHOUSE_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: clickhouse
|
||||
key: url
|
||||
- name: CLICKHOUSE_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: clickhouse
|
||||
key: user
|
||||
- name: CLICKHOUSE_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: clickhouse
|
||||
key: password
|
||||
- name: CLICKHOUSE_DATABASE
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: clickhouse
|
||||
key: database
|
||||
- name: INVITE_LINK_PREFIX
|
||||
value: ${INVITE_LINK_PREFIX}
|
||||
- name: RUST_BACKTRACE
|
||||
|
||||
@@ -375,13 +375,3 @@ CREATE TABLE extension_versions (
|
||||
|
||||
CREATE UNIQUE INDEX "index_extensions_external_id" ON "extensions" ("external_id");
|
||||
CREATE INDEX "index_extensions_total_download_count" ON "extensions" ("total_download_count");
|
||||
|
||||
CREATE TABLE hosted_projects (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
channel_id INTEGER NOT NULL REFERENCES channels(id),
|
||||
name TEXT NOT NULL,
|
||||
visibility TEXT NOT NULL,
|
||||
deleted_at TIMESTAMP NULL
|
||||
);
|
||||
CREATE INDEX idx_hosted_projects_on_channel_id ON hosted_projects (channel_id);
|
||||
CREATE UNIQUE INDEX uix_hosted_projects_on_channel_id_and_name ON hosted_projects (channel_id, name) WHERE (deleted_at IS NULL);
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
-- Add migration script here
|
||||
|
||||
CREATE TABLE hosted_projects (
|
||||
id INT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
|
||||
channel_id INT NOT NULL REFERENCES channels(id),
|
||||
name TEXT NOT NULL,
|
||||
visibility TEXT NOT NULL,
|
||||
deleted_at TIMESTAMP NULL
|
||||
);
|
||||
CREATE INDEX idx_hosted_projects_on_channel_id ON hosted_projects (channel_id);
|
||||
CREATE UNIQUE INDEX uix_hosted_projects_on_channel_id_and_name ON hosted_projects (channel_id, name) WHERE (deleted_at IS NULL);
|
||||
@@ -1,3 +0,0 @@
|
||||
-- Add migration script here
|
||||
|
||||
CREATE UNIQUE INDEX uix_channels_parent_path_name ON channels(parent_path, name) WHERE (parent_path IS NOT NULL AND parent_path != '');
|
||||
@@ -1,5 +1,4 @@
|
||||
pub mod events;
|
||||
pub mod extensions;
|
||||
mod extensions;
|
||||
|
||||
use crate::{
|
||||
auth,
|
||||
@@ -25,7 +24,7 @@ use tracing::instrument;
|
||||
|
||||
pub use extensions::fetch_extensions_from_blob_store_periodically;
|
||||
|
||||
pub fn routes(rpc_server: Option<Arc<rpc::Server>>, state: Arc<AppState>) -> Router<Body> {
|
||||
pub fn routes(rpc_server: Arc<rpc::Server>, state: Arc<AppState>) -> Router<Body> {
|
||||
Router::new()
|
||||
.route("/user", get(get_authenticated_user))
|
||||
.route("/users/:id/access_tokens", post(create_access_token))
|
||||
@@ -33,6 +32,7 @@ pub fn routes(rpc_server: Option<Arc<rpc::Server>>, state: Arc<AppState>) -> Rou
|
||||
.route("/rpc_server_snapshot", get(get_rpc_server_snapshot))
|
||||
.route("/contributors", get(get_contributors).post(add_contributor))
|
||||
.route("/contributor", get(check_is_contributor))
|
||||
.merge(extensions::router())
|
||||
.layer(
|
||||
ServiceBuilder::new()
|
||||
.layer(Extension(state))
|
||||
@@ -135,12 +135,8 @@ async fn trace_panic(panic: Json<Panic>) -> Result<()> {
|
||||
}
|
||||
|
||||
async fn get_rpc_server_snapshot(
|
||||
Extension(rpc_server): Extension<Option<Arc<rpc::Server>>>,
|
||||
Extension(rpc_server): Extension<Arc<rpc::Server>>,
|
||||
) -> Result<ErasedJson> {
|
||||
let Some(rpc_server) = rpc_server else {
|
||||
return Err(Error::Internal(anyhow!("rpc server is not available")));
|
||||
};
|
||||
|
||||
Ok(ErasedJson::pretty(rpc_server.snapshot().await))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,797 +0,0 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use axum::{
|
||||
body::Bytes, headers::Header, http::HeaderName, routing::post, Extension, Router, TypedHeader,
|
||||
};
|
||||
use hyper::StatusCode;
|
||||
use lazy_static::lazy_static;
|
||||
use serde::{Serialize, Serializer};
|
||||
use sha2::{Digest, Sha256};
|
||||
use telemetry_events::{
|
||||
ActionEvent, AppEvent, AssistantEvent, CallEvent, CopilotEvent, CpuEvent, EditEvent,
|
||||
EditorEvent, Event, EventRequestBody, EventWrapper, MemoryEvent, SettingEvent,
|
||||
};
|
||||
|
||||
use crate::{AppState, Error, Result};
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new().route("/telemetry/events", post(post_events))
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref ZED_CHECKSUM_HEADER: HeaderName = HeaderName::from_static("x-zed-checksum");
|
||||
static ref CLOUDFLARE_IP_COUNTRY_HEADER: HeaderName = HeaderName::from_static("cf-ipcountry");
|
||||
}
|
||||
|
||||
pub struct ZedChecksumHeader(Vec<u8>);
|
||||
|
||||
impl Header for ZedChecksumHeader {
|
||||
fn name() -> &'static HeaderName {
|
||||
&ZED_CHECKSUM_HEADER
|
||||
}
|
||||
|
||||
fn decode<'i, I>(values: &mut I) -> Result<Self, axum::headers::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
I: Iterator<Item = &'i axum::http::HeaderValue>,
|
||||
{
|
||||
let checksum = values
|
||||
.next()
|
||||
.ok_or_else(axum::headers::Error::invalid)?
|
||||
.to_str()
|
||||
.map_err(|_| axum::headers::Error::invalid())?;
|
||||
|
||||
let bytes = hex::decode(checksum).map_err(|_| axum::headers::Error::invalid())?;
|
||||
Ok(Self(bytes))
|
||||
}
|
||||
|
||||
fn encode<E: Extend<axum::http::HeaderValue>>(&self, _values: &mut E) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CloudflareIpCountryHeader(String);
|
||||
|
||||
impl Header for CloudflareIpCountryHeader {
|
||||
fn name() -> &'static HeaderName {
|
||||
&CLOUDFLARE_IP_COUNTRY_HEADER
|
||||
}
|
||||
|
||||
fn decode<'i, I>(values: &mut I) -> Result<Self, axum::headers::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
I: Iterator<Item = &'i axum::http::HeaderValue>,
|
||||
{
|
||||
let country_code = values
|
||||
.next()
|
||||
.ok_or_else(axum::headers::Error::invalid)?
|
||||
.to_str()
|
||||
.map_err(|_| axum::headers::Error::invalid())?;
|
||||
|
||||
Ok(Self(country_code.to_string()))
|
||||
}
|
||||
|
||||
fn encode<E: Extend<axum::http::HeaderValue>>(&self, _values: &mut E) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn post_events(
|
||||
Extension(app): Extension<Arc<AppState>>,
|
||||
TypedHeader(ZedChecksumHeader(checksum)): TypedHeader<ZedChecksumHeader>,
|
||||
country_code_header: Option<TypedHeader<CloudflareIpCountryHeader>>,
|
||||
body: Bytes,
|
||||
) -> Result<()> {
|
||||
let Some(clickhouse_client) = app.clickhouse_client.clone() else {
|
||||
Err(Error::Http(
|
||||
StatusCode::NOT_IMPLEMENTED,
|
||||
"not supported".into(),
|
||||
))?
|
||||
};
|
||||
|
||||
let Some(checksum_seed) = app.config.zed_client_checksum_seed.as_ref() else {
|
||||
return Err(Error::Http(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"events not enabled".into(),
|
||||
))?;
|
||||
};
|
||||
|
||||
let mut summer = Sha256::new();
|
||||
summer.update(checksum_seed);
|
||||
summer.update(&body);
|
||||
summer.update(checksum_seed);
|
||||
|
||||
if &checksum[..] != &summer.finalize()[..] {
|
||||
return Err(Error::Http(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"invalid checksum".into(),
|
||||
))?;
|
||||
}
|
||||
|
||||
let request_body: telemetry_events::EventRequestBody =
|
||||
serde_json::from_slice(&body).map_err(|err| {
|
||||
log::error!("can't parse event json: {err}");
|
||||
Error::Internal(anyhow!(err))
|
||||
})?;
|
||||
|
||||
let mut to_upload = ToUpload::default();
|
||||
let Some(last_event) = request_body.events.last() else {
|
||||
return Err(Error::Http(StatusCode::BAD_REQUEST, "no events".into()))?;
|
||||
};
|
||||
let country_code = country_code_header.map(|h| h.0 .0);
|
||||
|
||||
let first_event_at = chrono::Utc::now()
|
||||
- chrono::Duration::milliseconds(last_event.milliseconds_since_first_event);
|
||||
|
||||
for wrapper in &request_body.events {
|
||||
match &wrapper.event {
|
||||
Event::Editor(event) => to_upload.editor_events.push(EditorEventRow::from_event(
|
||||
event.clone(),
|
||||
&wrapper,
|
||||
&request_body,
|
||||
first_event_at,
|
||||
country_code.clone(),
|
||||
)),
|
||||
Event::Copilot(event) => to_upload.copilot_events.push(CopilotEventRow::from_event(
|
||||
event.clone(),
|
||||
&wrapper,
|
||||
&request_body,
|
||||
first_event_at,
|
||||
country_code.clone(),
|
||||
)),
|
||||
Event::Call(event) => to_upload.call_events.push(CallEventRow::from_event(
|
||||
event.clone(),
|
||||
&wrapper,
|
||||
&request_body,
|
||||
first_event_at,
|
||||
)),
|
||||
Event::Assistant(event) => {
|
||||
to_upload
|
||||
.assistant_events
|
||||
.push(AssistantEventRow::from_event(
|
||||
event.clone(),
|
||||
&wrapper,
|
||||
&request_body,
|
||||
first_event_at,
|
||||
))
|
||||
}
|
||||
Event::Cpu(event) => to_upload.cpu_events.push(CpuEventRow::from_event(
|
||||
event.clone(),
|
||||
&wrapper,
|
||||
&request_body,
|
||||
first_event_at,
|
||||
)),
|
||||
Event::Memory(event) => to_upload.memory_events.push(MemoryEventRow::from_event(
|
||||
event.clone(),
|
||||
&wrapper,
|
||||
&request_body,
|
||||
first_event_at,
|
||||
)),
|
||||
Event::App(event) => to_upload.app_events.push(AppEventRow::from_event(
|
||||
event.clone(),
|
||||
&wrapper,
|
||||
&request_body,
|
||||
first_event_at,
|
||||
)),
|
||||
Event::Setting(event) => to_upload.setting_events.push(SettingEventRow::from_event(
|
||||
event.clone(),
|
||||
&wrapper,
|
||||
&request_body,
|
||||
first_event_at,
|
||||
)),
|
||||
Event::Edit(event) => to_upload.edit_events.push(EditEventRow::from_event(
|
||||
event.clone(),
|
||||
&wrapper,
|
||||
&request_body,
|
||||
first_event_at,
|
||||
)),
|
||||
Event::Action(event) => to_upload.action_events.push(ActionEventRow::from_event(
|
||||
event.clone(),
|
||||
&wrapper,
|
||||
&request_body,
|
||||
first_event_at,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
to_upload
|
||||
.upload(&clickhouse_client)
|
||||
.await
|
||||
.map_err(|err| Error::Internal(anyhow!(err)))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ToUpload {
|
||||
editor_events: Vec<EditorEventRow>,
|
||||
copilot_events: Vec<CopilotEventRow>,
|
||||
assistant_events: Vec<AssistantEventRow>,
|
||||
call_events: Vec<CallEventRow>,
|
||||
cpu_events: Vec<CpuEventRow>,
|
||||
memory_events: Vec<MemoryEventRow>,
|
||||
app_events: Vec<AppEventRow>,
|
||||
setting_events: Vec<SettingEventRow>,
|
||||
edit_events: Vec<EditEventRow>,
|
||||
action_events: Vec<ActionEventRow>,
|
||||
}
|
||||
|
||||
impl ToUpload {
|
||||
pub async fn upload(&self, clickhouse_client: &clickhouse::Client) -> anyhow::Result<()> {
|
||||
Self::upload_to_table("editor_events", &self.editor_events, clickhouse_client)
|
||||
.await
|
||||
.with_context(|| format!("failed to upload to table 'editor_events'"))?;
|
||||
Self::upload_to_table("copilot_events", &self.copilot_events, clickhouse_client)
|
||||
.await
|
||||
.with_context(|| format!("failed to upload to table 'copilot_events'"))?;
|
||||
Self::upload_to_table(
|
||||
"assistant_events",
|
||||
&self.assistant_events,
|
||||
clickhouse_client,
|
||||
)
|
||||
.await
|
||||
.with_context(|| format!("failed to upload to table 'assistant_events'"))?;
|
||||
Self::upload_to_table("call_events", &self.call_events, clickhouse_client)
|
||||
.await
|
||||
.with_context(|| format!("failed to upload to table 'call_events'"))?;
|
||||
Self::upload_to_table("cpu_events", &self.cpu_events, clickhouse_client)
|
||||
.await
|
||||
.with_context(|| format!("failed to upload to table 'cpu_events'"))?;
|
||||
Self::upload_to_table("memory_events", &self.memory_events, clickhouse_client)
|
||||
.await
|
||||
.with_context(|| format!("failed to upload to table 'memory_events'"))?;
|
||||
Self::upload_to_table("app_events", &self.app_events, clickhouse_client)
|
||||
.await
|
||||
.with_context(|| format!("failed to upload to table 'app_events'"))?;
|
||||
Self::upload_to_table("setting_events", &self.setting_events, clickhouse_client)
|
||||
.await
|
||||
.with_context(|| format!("failed to upload to table 'setting_events'"))?;
|
||||
Self::upload_to_table("edit_events", &self.edit_events, clickhouse_client)
|
||||
.await
|
||||
.with_context(|| format!("failed to upload to table 'edit_events'"))?;
|
||||
Self::upload_to_table("action_events", &self.action_events, clickhouse_client)
|
||||
.await
|
||||
.with_context(|| format!("failed to upload to table 'action_events'"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn upload_to_table<T: clickhouse::Row + Serialize + std::fmt::Debug>(
|
||||
table: &str,
|
||||
rows: &[T],
|
||||
clickhouse_client: &clickhouse::Client,
|
||||
) -> anyhow::Result<()> {
|
||||
if !rows.is_empty() {
|
||||
let mut insert = clickhouse_client.insert(table)?;
|
||||
|
||||
for event in rows {
|
||||
insert.write(event).await?;
|
||||
}
|
||||
|
||||
insert.end().await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn serialize_country_code<S>(country_code: &str, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
if country_code.len() != 2 {
|
||||
use serde::ser::Error;
|
||||
return Err(S::Error::custom(
|
||||
"country_code must be exactly 2 characters",
|
||||
));
|
||||
}
|
||||
|
||||
let country_code = country_code.as_bytes();
|
||||
|
||||
serializer.serialize_u16(((country_code[0] as u16) << 8) + country_code[1] as u16)
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, clickhouse::Row)]
|
||||
pub struct EditorEventRow {
|
||||
pub installation_id: String,
|
||||
pub operation: String,
|
||||
pub app_version: String,
|
||||
pub file_extension: String,
|
||||
pub os_name: String,
|
||||
pub os_version: String,
|
||||
pub release_channel: String,
|
||||
pub signed_in: bool,
|
||||
pub vim_mode: bool,
|
||||
#[serde(serialize_with = "serialize_country_code")]
|
||||
pub country_code: String,
|
||||
pub region_code: String,
|
||||
pub city: String,
|
||||
pub time: i64,
|
||||
pub copilot_enabled: bool,
|
||||
pub copilot_enabled_for_language: bool,
|
||||
pub historical_event: bool,
|
||||
pub architecture: String,
|
||||
pub is_staff: Option<bool>,
|
||||
pub session_id: Option<String>,
|
||||
pub major: Option<i32>,
|
||||
pub minor: Option<i32>,
|
||||
pub patch: Option<i32>,
|
||||
}
|
||||
|
||||
impl EditorEventRow {
|
||||
fn from_event(
|
||||
event: EditorEvent,
|
||||
wrapper: &EventWrapper,
|
||||
body: &EventRequestBody,
|
||||
first_event_at: chrono::DateTime<chrono::Utc>,
|
||||
country_code: Option<String>,
|
||||
) -> Self {
|
||||
let semver = body.semver();
|
||||
let time =
|
||||
first_event_at + chrono::Duration::milliseconds(wrapper.milliseconds_since_first_event);
|
||||
|
||||
Self {
|
||||
app_version: body.app_version.clone(),
|
||||
major: semver.map(|s| s.major as i32),
|
||||
minor: semver.map(|s| s.minor as i32),
|
||||
patch: semver.map(|s| s.patch as i32),
|
||||
release_channel: body.release_channel.clone().unwrap_or_default(),
|
||||
os_name: body.os_name.clone(),
|
||||
os_version: body.os_version.clone().unwrap_or_default(),
|
||||
architecture: body.architecture.clone(),
|
||||
installation_id: body.installation_id.clone().unwrap_or_default(),
|
||||
session_id: body.session_id.clone(),
|
||||
is_staff: body.is_staff,
|
||||
time: time.timestamp_millis(),
|
||||
operation: event.operation,
|
||||
file_extension: event.file_extension.unwrap_or_default(),
|
||||
signed_in: wrapper.signed_in,
|
||||
vim_mode: event.vim_mode,
|
||||
copilot_enabled: event.copilot_enabled,
|
||||
copilot_enabled_for_language: event.copilot_enabled_for_language,
|
||||
country_code: country_code.unwrap_or("XX".to_string()),
|
||||
region_code: "".to_string(),
|
||||
city: "".to_string(),
|
||||
historical_event: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, clickhouse::Row)]
|
||||
pub struct CopilotEventRow {
|
||||
pub installation_id: String,
|
||||
pub suggestion_id: String,
|
||||
pub suggestion_accepted: bool,
|
||||
pub app_version: String,
|
||||
pub file_extension: String,
|
||||
pub os_name: String,
|
||||
pub os_version: String,
|
||||
pub release_channel: String,
|
||||
pub signed_in: bool,
|
||||
#[serde(serialize_with = "serialize_country_code")]
|
||||
pub country_code: String,
|
||||
pub region_code: String,
|
||||
pub city: String,
|
||||
pub time: i64,
|
||||
pub is_staff: Option<bool>,
|
||||
pub session_id: Option<String>,
|
||||
pub major: Option<i32>,
|
||||
pub minor: Option<i32>,
|
||||
pub patch: Option<i32>,
|
||||
}
|
||||
|
||||
impl CopilotEventRow {
|
||||
fn from_event(
|
||||
event: CopilotEvent,
|
||||
wrapper: &EventWrapper,
|
||||
body: &EventRequestBody,
|
||||
first_event_at: chrono::DateTime<chrono::Utc>,
|
||||
country_code: Option<String>,
|
||||
) -> Self {
|
||||
let semver = body.semver();
|
||||
let time =
|
||||
first_event_at + chrono::Duration::milliseconds(wrapper.milliseconds_since_first_event);
|
||||
|
||||
Self {
|
||||
app_version: body.app_version.clone(),
|
||||
major: semver.map(|s| s.major as i32),
|
||||
minor: semver.map(|s| s.minor as i32),
|
||||
patch: semver.map(|s| s.patch as i32),
|
||||
release_channel: body.release_channel.clone().unwrap_or_default(),
|
||||
os_name: body.os_name.clone(),
|
||||
os_version: body.os_version.clone().unwrap_or_default(),
|
||||
installation_id: body.installation_id.clone().unwrap_or_default(),
|
||||
session_id: body.session_id.clone(),
|
||||
is_staff: body.is_staff,
|
||||
time: time.timestamp_millis(),
|
||||
file_extension: event.file_extension.unwrap_or_default(),
|
||||
signed_in: wrapper.signed_in,
|
||||
country_code: country_code.unwrap_or("XX".to_string()),
|
||||
region_code: "".to_string(),
|
||||
city: "".to_string(),
|
||||
suggestion_id: event.suggestion_id.unwrap_or_default(),
|
||||
suggestion_accepted: event.suggestion_accepted,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, clickhouse::Row)]
|
||||
pub struct CallEventRow {
|
||||
// AppInfoBase
|
||||
app_version: String,
|
||||
major: Option<i32>,
|
||||
minor: Option<i32>,
|
||||
patch: Option<i32>,
|
||||
release_channel: String,
|
||||
|
||||
// ClientEventBase
|
||||
installation_id: String,
|
||||
session_id: Option<String>,
|
||||
is_staff: Option<bool>,
|
||||
time: i64,
|
||||
|
||||
// CallEventRow
|
||||
operation: String,
|
||||
room_id: Option<u64>,
|
||||
channel_id: Option<u64>,
|
||||
}
|
||||
|
||||
impl CallEventRow {
|
||||
fn from_event(
|
||||
event: CallEvent,
|
||||
wrapper: &EventWrapper,
|
||||
body: &EventRequestBody,
|
||||
first_event_at: chrono::DateTime<chrono::Utc>,
|
||||
) -> Self {
|
||||
let semver = body.semver();
|
||||
let time =
|
||||
first_event_at + chrono::Duration::milliseconds(wrapper.milliseconds_since_first_event);
|
||||
|
||||
Self {
|
||||
app_version: body.app_version.clone(),
|
||||
major: semver.map(|s| s.major as i32),
|
||||
minor: semver.map(|s| s.minor as i32),
|
||||
patch: semver.map(|s| s.patch as i32),
|
||||
release_channel: body.release_channel.clone().unwrap_or_default(),
|
||||
installation_id: body.installation_id.clone().unwrap_or_default(),
|
||||
session_id: body.session_id.clone(),
|
||||
is_staff: body.is_staff,
|
||||
time: time.timestamp_millis(),
|
||||
operation: event.operation,
|
||||
room_id: event.room_id,
|
||||
channel_id: event.channel_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, clickhouse::Row)]
|
||||
pub struct AssistantEventRow {
|
||||
// AppInfoBase
|
||||
app_version: String,
|
||||
major: Option<i32>,
|
||||
minor: Option<i32>,
|
||||
patch: Option<i32>,
|
||||
release_channel: String,
|
||||
|
||||
// ClientEventBase
|
||||
installation_id: Option<String>,
|
||||
session_id: Option<String>,
|
||||
is_staff: Option<bool>,
|
||||
time: i64,
|
||||
|
||||
// AssistantEventRow
|
||||
conversation_id: String,
|
||||
kind: String,
|
||||
model: String,
|
||||
}
|
||||
|
||||
impl AssistantEventRow {
|
||||
fn from_event(
|
||||
event: AssistantEvent,
|
||||
wrapper: &EventWrapper,
|
||||
body: &EventRequestBody,
|
||||
first_event_at: chrono::DateTime<chrono::Utc>,
|
||||
) -> Self {
|
||||
let semver = body.semver();
|
||||
let time =
|
||||
first_event_at + chrono::Duration::milliseconds(wrapper.milliseconds_since_first_event);
|
||||
|
||||
Self {
|
||||
app_version: body.app_version.clone(),
|
||||
major: semver.map(|s| s.major as i32),
|
||||
minor: semver.map(|s| s.minor as i32),
|
||||
patch: semver.map(|s| s.patch as i32),
|
||||
release_channel: body.release_channel.clone().unwrap_or_default(),
|
||||
installation_id: body.installation_id.clone(),
|
||||
session_id: body.session_id.clone(),
|
||||
is_staff: body.is_staff,
|
||||
time: time.timestamp_millis(),
|
||||
conversation_id: event.conversation_id.unwrap_or_default(),
|
||||
kind: event.kind.to_string(),
|
||||
model: event.model,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, clickhouse::Row, Serialize)]
|
||||
pub struct CpuEventRow {
|
||||
pub installation_id: Option<String>,
|
||||
pub is_staff: Option<bool>,
|
||||
pub usage_as_percentage: f32,
|
||||
pub core_count: u32,
|
||||
pub app_version: String,
|
||||
pub release_channel: String,
|
||||
pub time: i64,
|
||||
pub session_id: Option<String>,
|
||||
// pub normalized_cpu_usage: f64, MATERIALIZED
|
||||
pub major: Option<i32>,
|
||||
pub minor: Option<i32>,
|
||||
pub patch: Option<i32>,
|
||||
}
|
||||
|
||||
impl CpuEventRow {
|
||||
fn from_event(
|
||||
event: CpuEvent,
|
||||
wrapper: &EventWrapper,
|
||||
body: &EventRequestBody,
|
||||
first_event_at: chrono::DateTime<chrono::Utc>,
|
||||
) -> Self {
|
||||
let semver = body.semver();
|
||||
let time =
|
||||
first_event_at + chrono::Duration::milliseconds(wrapper.milliseconds_since_first_event);
|
||||
|
||||
Self {
|
||||
app_version: body.app_version.clone(),
|
||||
major: semver.map(|s| s.major as i32),
|
||||
minor: semver.map(|s| s.minor as i32),
|
||||
patch: semver.map(|s| s.patch as i32),
|
||||
release_channel: body.release_channel.clone().unwrap_or_default(),
|
||||
installation_id: body.installation_id.clone(),
|
||||
session_id: body.session_id.clone(),
|
||||
is_staff: body.is_staff,
|
||||
time: time.timestamp_millis(),
|
||||
usage_as_percentage: event.usage_as_percentage,
|
||||
core_count: event.core_count,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, clickhouse::Row)]
|
||||
pub struct MemoryEventRow {
|
||||
// AppInfoBase
|
||||
app_version: String,
|
||||
major: Option<i32>,
|
||||
minor: Option<i32>,
|
||||
patch: Option<i32>,
|
||||
release_channel: String,
|
||||
|
||||
// ClientEventBase
|
||||
installation_id: Option<String>,
|
||||
session_id: Option<String>,
|
||||
is_staff: Option<bool>,
|
||||
time: i64,
|
||||
|
||||
// MemoryEventRow
|
||||
memory_in_bytes: u64,
|
||||
virtual_memory_in_bytes: u64,
|
||||
}
|
||||
|
||||
impl MemoryEventRow {
|
||||
fn from_event(
|
||||
event: MemoryEvent,
|
||||
wrapper: &EventWrapper,
|
||||
body: &EventRequestBody,
|
||||
first_event_at: chrono::DateTime<chrono::Utc>,
|
||||
) -> Self {
|
||||
let semver = body.semver();
|
||||
let time =
|
||||
first_event_at + chrono::Duration::milliseconds(wrapper.milliseconds_since_first_event);
|
||||
|
||||
Self {
|
||||
app_version: body.app_version.clone(),
|
||||
major: semver.map(|s| s.major as i32),
|
||||
minor: semver.map(|s| s.minor as i32),
|
||||
patch: semver.map(|s| s.patch as i32),
|
||||
release_channel: body.release_channel.clone().unwrap_or_default(),
|
||||
installation_id: body.installation_id.clone(),
|
||||
session_id: body.session_id.clone(),
|
||||
is_staff: body.is_staff,
|
||||
time: time.timestamp_millis(),
|
||||
memory_in_bytes: event.memory_in_bytes,
|
||||
virtual_memory_in_bytes: event.virtual_memory_in_bytes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, clickhouse::Row)]
|
||||
pub struct AppEventRow {
|
||||
// AppInfoBase
|
||||
app_version: String,
|
||||
major: Option<i32>,
|
||||
minor: Option<i32>,
|
||||
patch: Option<i32>,
|
||||
release_channel: String,
|
||||
|
||||
// ClientEventBase
|
||||
installation_id: Option<String>,
|
||||
session_id: Option<String>,
|
||||
is_staff: Option<bool>,
|
||||
time: i64,
|
||||
|
||||
// AppEventRow
|
||||
operation: String,
|
||||
}
|
||||
|
||||
impl AppEventRow {
|
||||
fn from_event(
|
||||
event: AppEvent,
|
||||
wrapper: &EventWrapper,
|
||||
body: &EventRequestBody,
|
||||
first_event_at: chrono::DateTime<chrono::Utc>,
|
||||
) -> Self {
|
||||
let semver = body.semver();
|
||||
let time =
|
||||
first_event_at + chrono::Duration::milliseconds(wrapper.milliseconds_since_first_event);
|
||||
|
||||
Self {
|
||||
app_version: body.app_version.clone(),
|
||||
major: semver.map(|s| s.major as i32),
|
||||
minor: semver.map(|s| s.minor as i32),
|
||||
patch: semver.map(|s| s.patch as i32),
|
||||
release_channel: body.release_channel.clone().unwrap_or_default(),
|
||||
installation_id: body.installation_id.clone(),
|
||||
session_id: body.session_id.clone(),
|
||||
is_staff: body.is_staff,
|
||||
time: time.timestamp_millis(),
|
||||
operation: event.operation,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, clickhouse::Row)]
|
||||
pub struct SettingEventRow {
|
||||
// AppInfoBase
|
||||
app_version: String,
|
||||
major: Option<i32>,
|
||||
minor: Option<i32>,
|
||||
patch: Option<i32>,
|
||||
release_channel: String,
|
||||
|
||||
// ClientEventBase
|
||||
installation_id: Option<String>,
|
||||
session_id: Option<String>,
|
||||
is_staff: Option<bool>,
|
||||
time: i64,
|
||||
// SettingEventRow
|
||||
setting: String,
|
||||
value: String,
|
||||
}
|
||||
|
||||
impl SettingEventRow {
|
||||
fn from_event(
|
||||
event: SettingEvent,
|
||||
wrapper: &EventWrapper,
|
||||
body: &EventRequestBody,
|
||||
first_event_at: chrono::DateTime<chrono::Utc>,
|
||||
) -> Self {
|
||||
let semver = body.semver();
|
||||
let time =
|
||||
first_event_at + chrono::Duration::milliseconds(wrapper.milliseconds_since_first_event);
|
||||
|
||||
Self {
|
||||
app_version: body.app_version.clone(),
|
||||
major: semver.map(|s| s.major as i32),
|
||||
minor: semver.map(|s| s.minor as i32),
|
||||
patch: semver.map(|s| s.patch as i32),
|
||||
release_channel: body.release_channel.clone().unwrap_or_default(),
|
||||
installation_id: body.installation_id.clone(),
|
||||
session_id: body.session_id.clone(),
|
||||
is_staff: body.is_staff,
|
||||
time: time.timestamp_millis(),
|
||||
setting: event.setting,
|
||||
value: event.value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, clickhouse::Row)]
|
||||
pub struct EditEventRow {
|
||||
// AppInfoBase
|
||||
app_version: String,
|
||||
major: Option<i32>,
|
||||
minor: Option<i32>,
|
||||
patch: Option<i32>,
|
||||
release_channel: String,
|
||||
|
||||
// ClientEventBase
|
||||
installation_id: Option<String>,
|
||||
// Note: This column name has a typo in the ClickHouse table.
|
||||
#[serde(rename = "sesssion_id")]
|
||||
session_id: Option<String>,
|
||||
is_staff: Option<bool>,
|
||||
time: i64,
|
||||
|
||||
// EditEventRow
|
||||
period_start: i64,
|
||||
period_end: i64,
|
||||
environment: String,
|
||||
}
|
||||
|
||||
impl EditEventRow {
|
||||
fn from_event(
|
||||
event: EditEvent,
|
||||
wrapper: &EventWrapper,
|
||||
body: &EventRequestBody,
|
||||
first_event_at: chrono::DateTime<chrono::Utc>,
|
||||
) -> Self {
|
||||
let semver = body.semver();
|
||||
let time =
|
||||
first_event_at + chrono::Duration::milliseconds(wrapper.milliseconds_since_first_event);
|
||||
|
||||
let period_start = time - chrono::Duration::milliseconds(event.duration);
|
||||
let period_end = time;
|
||||
|
||||
Self {
|
||||
app_version: body.app_version.clone(),
|
||||
major: semver.map(|s| s.major as i32),
|
||||
minor: semver.map(|s| s.minor as i32),
|
||||
patch: semver.map(|s| s.patch as i32),
|
||||
release_channel: body.release_channel.clone().unwrap_or_default(),
|
||||
installation_id: body.installation_id.clone(),
|
||||
session_id: body.session_id.clone(),
|
||||
is_staff: body.is_staff,
|
||||
time: time.timestamp_millis(),
|
||||
period_start: period_start.timestamp_millis(),
|
||||
period_end: period_end.timestamp_millis(),
|
||||
environment: event.environment,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, clickhouse::Row)]
|
||||
pub struct ActionEventRow {
|
||||
// AppInfoBase
|
||||
app_version: String,
|
||||
major: Option<i32>,
|
||||
minor: Option<i32>,
|
||||
patch: Option<i32>,
|
||||
release_channel: String,
|
||||
|
||||
// ClientEventBase
|
||||
installation_id: Option<String>,
|
||||
// Note: This column name has a typo in the ClickHouse table.
|
||||
#[serde(rename = "sesssion_id")]
|
||||
session_id: Option<String>,
|
||||
is_staff: Option<bool>,
|
||||
time: i64,
|
||||
// ActionEventRow
|
||||
source: String,
|
||||
action: String,
|
||||
}
|
||||
|
||||
impl ActionEventRow {
|
||||
fn from_event(
|
||||
event: ActionEvent,
|
||||
wrapper: &EventWrapper,
|
||||
body: &EventRequestBody,
|
||||
first_event_at: chrono::DateTime<chrono::Utc>,
|
||||
) -> Self {
|
||||
let semver = body.semver();
|
||||
let time =
|
||||
first_event_at + chrono::Duration::milliseconds(wrapper.milliseconds_since_first_event);
|
||||
|
||||
Self {
|
||||
app_version: body.app_version.clone(),
|
||||
major: semver.map(|s| s.major as i32),
|
||||
minor: semver.map(|s| s.minor as i32),
|
||||
patch: semver.map(|s| s.patch as i32),
|
||||
release_channel: body.release_channel.clone().unwrap_or_default(),
|
||||
installation_id: body.installation_id.clone(),
|
||||
session_id: body.session_id.clone(),
|
||||
is_staff: body.is_staff,
|
||||
time: time.timestamp_millis(),
|
||||
source: event.source,
|
||||
action: event.action,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -587,7 +587,6 @@ pub struct ChannelsForUser {
|
||||
pub channels: Vec<Channel>,
|
||||
pub channel_memberships: Vec<channel_member::Model>,
|
||||
pub channel_participants: HashMap<ChannelId, Vec<UserId>>,
|
||||
pub hosted_projects: Vec<proto::HostedProject>,
|
||||
|
||||
pub observed_buffer_versions: Vec<proto::ChannelBufferVersion>,
|
||||
pub observed_channel_messages: Vec<proto::ChannelMessageId>,
|
||||
|
||||
@@ -88,7 +88,6 @@ id_type!(FlagId);
|
||||
id_type!(ExtensionId);
|
||||
id_type!(NotificationId);
|
||||
id_type!(NotificationKindId);
|
||||
id_type!(HostedProjectId);
|
||||
|
||||
/// ChannelRole gives you permissions for both channels and calls.
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug, EnumIter, DeriveActiveEnum, Default, Hash)]
|
||||
|
||||
@@ -6,7 +6,6 @@ pub mod channels;
|
||||
pub mod contacts;
|
||||
pub mod contributors;
|
||||
pub mod extensions;
|
||||
pub mod hosted_projects;
|
||||
pub mod messages;
|
||||
pub mod notifications;
|
||||
pub mod projects;
|
||||
|
||||
@@ -652,14 +652,9 @@ impl Database {
|
||||
.observed_channel_messages(&channel_ids, user_id, &*tx)
|
||||
.await?;
|
||||
|
||||
let hosted_projects = self
|
||||
.get_hosted_projects(&channel_ids, &roles_by_channel_id, &*tx)
|
||||
.await?;
|
||||
|
||||
Ok(ChannelsForUser {
|
||||
channel_memberships,
|
||||
channels,
|
||||
hosted_projects,
|
||||
channel_participants,
|
||||
latest_buffer_versions,
|
||||
latest_channel_messages,
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
use rpc::proto;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl Database {
|
||||
pub async fn get_hosted_projects(
|
||||
&self,
|
||||
channel_ids: &Vec<ChannelId>,
|
||||
roles: &HashMap<ChannelId, ChannelRole>,
|
||||
tx: &DatabaseTransaction,
|
||||
) -> Result<Vec<proto::HostedProject>> {
|
||||
Ok(hosted_project::Entity::find()
|
||||
.filter(hosted_project::Column::ChannelId.is_in(channel_ids.iter().map(|id| id.0)))
|
||||
.all(&*tx)
|
||||
.await?
|
||||
.into_iter()
|
||||
.flat_map(|project| {
|
||||
if project.deleted_at.is_some() {
|
||||
return None;
|
||||
}
|
||||
match project.visibility {
|
||||
ChannelVisibility::Public => {}
|
||||
ChannelVisibility::Members => {
|
||||
let is_visible = roles
|
||||
.get(&project.channel_id)
|
||||
.map(|role| role.can_see_all_descendants())
|
||||
.unwrap_or(false);
|
||||
if !is_visible {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
};
|
||||
Some(proto::HostedProject {
|
||||
id: project.id.to_proto(),
|
||||
channel_id: project.channel_id.to_proto(),
|
||||
name: project.name.clone(),
|
||||
visibility: project.visibility.into(),
|
||||
})
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@ pub mod extension;
|
||||
pub mod extension_version;
|
||||
pub mod feature_flag;
|
||||
pub mod follower;
|
||||
pub mod hosted_project;
|
||||
pub mod language_server;
|
||||
pub mod notification;
|
||||
pub mod notification_kind;
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
use crate::db::{ChannelId, ChannelVisibility, HostedProjectId};
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "hosted_projects")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: HostedProjectId,
|
||||
pub channel_id: ChannelId,
|
||||
pub name: String,
|
||||
pub visibility: ChannelVisibility,
|
||||
pub deleted_at: Option<DateTime>,
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {}
|
||||
@@ -58,24 +58,11 @@ impl From<serde_json::Error> for Error {
|
||||
impl IntoResponse for Error {
|
||||
fn into_response(self) -> axum::response::Response {
|
||||
match self {
|
||||
Error::Http(code, message) => {
|
||||
log::error!("HTTP error {}: {}", code, &message);
|
||||
(code, message).into_response()
|
||||
}
|
||||
Error::Http(code, message) => (code, message).into_response(),
|
||||
Error::Database(error) => {
|
||||
log::error!(
|
||||
"HTTP error {}: {:?}",
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
&error
|
||||
);
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, format!("{}", &error)).into_response()
|
||||
}
|
||||
Error::Internal(error) => {
|
||||
log::error!(
|
||||
"HTTP error {}: {:?}",
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
&error
|
||||
);
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, format!("{}", &error)).into_response()
|
||||
}
|
||||
}
|
||||
@@ -110,10 +97,6 @@ pub struct Config {
|
||||
pub database_url: String,
|
||||
pub database_max_connections: u32,
|
||||
pub api_token: String,
|
||||
pub clickhouse_url: Option<String>,
|
||||
pub clickhouse_user: Option<String>,
|
||||
pub clickhouse_password: Option<String>,
|
||||
pub clickhouse_database: Option<String>,
|
||||
pub invite_link_prefix: String,
|
||||
pub live_kit_server: Option<String>,
|
||||
pub live_kit_key: Option<String>,
|
||||
@@ -126,7 +109,6 @@ pub struct Config {
|
||||
pub blob_store_secret_key: Option<String>,
|
||||
pub blob_store_bucket: Option<String>,
|
||||
pub zed_environment: Arc<str>,
|
||||
pub zed_client_checksum_seed: Option<String>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@@ -145,7 +127,6 @@ pub struct AppState {
|
||||
pub db: Arc<Database>,
|
||||
pub live_kit_client: Option<Arc<dyn live_kit_server::api::Client>>,
|
||||
pub blob_store_client: Option<aws_sdk_s3::Client>,
|
||||
pub clickhouse_client: Option<clickhouse::Client>,
|
||||
pub config: Config,
|
||||
}
|
||||
|
||||
@@ -175,7 +156,6 @@ impl AppState {
|
||||
db: Arc::new(db),
|
||||
live_kit_client,
|
||||
blob_store_client: build_blob_store_client(&config).await.log_err(),
|
||||
clickhouse_client: build_clickhouse_client(&config).log_err(),
|
||||
config,
|
||||
};
|
||||
Ok(Arc::new(this))
|
||||
@@ -216,31 +196,3 @@ async fn build_blob_store_client(config: &Config) -> anyhow::Result<aws_sdk_s3::
|
||||
|
||||
Ok(aws_sdk_s3::Client::new(&s3_config))
|
||||
}
|
||||
|
||||
fn build_clickhouse_client(config: &Config) -> anyhow::Result<clickhouse::Client> {
|
||||
Ok(clickhouse::Client::default()
|
||||
.with_url(
|
||||
config
|
||||
.clickhouse_url
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow!("missing clickhouse_url"))?,
|
||||
)
|
||||
.with_user(
|
||||
config
|
||||
.clickhouse_user
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow!("missing clickhouse_user"))?,
|
||||
)
|
||||
.with_password(
|
||||
config
|
||||
.clickhouse_password
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow!("missing clickhouse_password"))?,
|
||||
)
|
||||
.with_database(
|
||||
config
|
||||
.clickhouse_database
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow!("missing clickhouse_database"))?,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
use anyhow::anyhow;
|
||||
use axum::{extract::MatchedPath, routing::get, Extension, Router};
|
||||
use axum::{routing::get, Extension, Router};
|
||||
use collab::{
|
||||
api::fetch_extensions_from_blob_store_periodically, db, env, executor::Executor, AppState,
|
||||
Config, MigrateConfig, Result,
|
||||
};
|
||||
use db::Database;
|
||||
use hyper::Request;
|
||||
use std::{
|
||||
env::args,
|
||||
net::{SocketAddr, TcpListener},
|
||||
path::Path,
|
||||
sync::Arc,
|
||||
};
|
||||
#[cfg(unix)]
|
||||
use tokio::signal::unix::SignalKind;
|
||||
use tower_http::trace::{self, TraceLayer};
|
||||
use tracing::Level;
|
||||
use tracing_log::LogTracer;
|
||||
use tracing_subscriber::{filter::EnvFilter, fmt::format::JsonFields, Layer};
|
||||
use util::ResultExt;
|
||||
@@ -32,8 +28,7 @@ async fn main() -> Result<()> {
|
||||
);
|
||||
}
|
||||
|
||||
let mut args = args().skip(1);
|
||||
match args.next().as_deref() {
|
||||
match args().skip(1).next().as_deref() {
|
||||
Some("version") => {
|
||||
println!("collab v{} ({})", VERSION, REVISION.unwrap_or("unknown"));
|
||||
}
|
||||
@@ -41,17 +36,6 @@ async fn main() -> Result<()> {
|
||||
run_migrations().await?;
|
||||
}
|
||||
Some("serve") => {
|
||||
let (is_api, is_collab) = if let Some(next) = args.next() {
|
||||
(next == "api", next == "collab")
|
||||
} else {
|
||||
(true, true)
|
||||
};
|
||||
if !is_api && !is_collab {
|
||||
Err(anyhow!(
|
||||
"usage: collab <version | migrate | serve [api|collab]>"
|
||||
))?;
|
||||
}
|
||||
|
||||
let config = envy::from_env::<Config>().expect("error loading config");
|
||||
init_tracing(&config);
|
||||
|
||||
@@ -62,55 +46,24 @@ async fn main() -> Result<()> {
|
||||
let listener = TcpListener::bind(&format!("0.0.0.0:{}", state.config.http_port))
|
||||
.expect("failed to bind TCP listener");
|
||||
|
||||
let rpc_server = if is_collab {
|
||||
let epoch = state
|
||||
.db
|
||||
.create_server(&state.config.zed_environment)
|
||||
.await?;
|
||||
let rpc_server =
|
||||
collab::rpc::Server::new(epoch, state.clone(), Executor::Production);
|
||||
rpc_server.start().await?;
|
||||
let epoch = state
|
||||
.db
|
||||
.create_server(&state.config.zed_environment)
|
||||
.await?;
|
||||
let rpc_server = collab::rpc::Server::new(epoch, state.clone(), Executor::Production);
|
||||
rpc_server.start().await?;
|
||||
|
||||
Some(rpc_server)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
fetch_extensions_from_blob_store_periodically(state.clone(), Executor::Production);
|
||||
|
||||
if is_api {
|
||||
fetch_extensions_from_blob_store_periodically(state.clone(), Executor::Production);
|
||||
}
|
||||
|
||||
let mut app = collab::api::routes(rpc_server.clone(), state.clone());
|
||||
if let Some(rpc_server) = rpc_server.clone() {
|
||||
app = app.merge(collab::rpc::routes(rpc_server))
|
||||
}
|
||||
app = app
|
||||
let app = collab::api::routes(rpc_server.clone(), state.clone())
|
||||
.merge(collab::rpc::routes(rpc_server.clone()))
|
||||
.merge(
|
||||
Router::new()
|
||||
.route("/", get(handle_root))
|
||||
.route("/healthz", get(handle_liveness_probe))
|
||||
.merge(collab::api::extensions::router())
|
||||
.merge(collab::api::events::router())
|
||||
.layer(Extension(state.clone())),
|
||||
)
|
||||
.layer(
|
||||
TraceLayer::new_for_http()
|
||||
.make_span_with(|request: &Request<_>| {
|
||||
let matched_path = request
|
||||
.extensions()
|
||||
.get::<MatchedPath>()
|
||||
.map(MatchedPath::as_str);
|
||||
|
||||
tracing::info_span!(
|
||||
"http_request",
|
||||
method = ?request.method(),
|
||||
matched_path,
|
||||
)
|
||||
})
|
||||
.on_response(trace::DefaultOnResponse::new().level(Level::INFO)),
|
||||
);
|
||||
|
||||
#[cfg(unix)]
|
||||
axum::Server::from_tcp(listener)?
|
||||
.serve(app.into_make_service_with_connect_info::<SocketAddr>())
|
||||
.with_graceful_shutdown(async move {
|
||||
@@ -123,21 +76,12 @@ async fn main() -> Result<()> {
|
||||
futures::pin_mut!(sigterm, sigint);
|
||||
futures::future::select(sigterm, sigint).await;
|
||||
tracing::info!("Received interrupt signal");
|
||||
|
||||
if let Some(rpc_server) = rpc_server {
|
||||
rpc_server.teardown();
|
||||
}
|
||||
rpc_server.teardown();
|
||||
})
|
||||
.await?;
|
||||
|
||||
// todo!("windows")
|
||||
#[cfg(windows)]
|
||||
unimplemented!();
|
||||
}
|
||||
_ => {
|
||||
Err(anyhow!(
|
||||
"usage: collab <version | migrate | serve [api|collab]>"
|
||||
))?;
|
||||
Err(anyhow!("usage: collab <version | migrate | serve>"))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -3396,9 +3396,6 @@ fn build_channels_update(
|
||||
for channel in channel_invites {
|
||||
update.channel_invitations.push(channel.to_proto());
|
||||
}
|
||||
for project in channels.hosted_projects {
|
||||
update.hosted_projects.push(project);
|
||||
}
|
||||
|
||||
update
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use call::Room;
|
||||
use client::ChannelId;
|
||||
use gpui::{Model, TestAppContext};
|
||||
|
||||
mod channel_buffer_tests;
|
||||
@@ -44,6 +43,6 @@ fn room_participants(room: &Model<Room>, cx: &mut TestAppContext) -> RoomPartici
|
||||
})
|
||||
}
|
||||
|
||||
fn channel_id(room: &Model<Room>, cx: &mut TestAppContext) -> Option<ChannelId> {
|
||||
fn channel_id(room: &Model<Room>, cx: &mut TestAppContext) -> Option<u64> {
|
||||
cx.read(|cx| room.read(cx).channel_id())
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ async fn test_channel_requires_zed_cla(cx_a: &mut TestAppContext, cx_b: &mut Tes
|
||||
server
|
||||
.app_state
|
||||
.db
|
||||
.set_channel_requires_zed_cla(ChannelId::from_proto(parent_channel_id.0), true)
|
||||
.set_channel_requires_zed_cla(ChannelId::from_proto(parent_channel_id), true)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -100,13 +100,13 @@ async fn test_basic_channel_messages(
|
||||
Notification::ChannelMessageMention {
|
||||
message_id,
|
||||
sender_id: client_a.id(),
|
||||
channel_id: channel_id.0,
|
||||
channel_id,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
store.notification_at(1).unwrap().notification,
|
||||
Notification::ChannelInvitation {
|
||||
channel_id: channel_id.0,
|
||||
channel_id,
|
||||
channel_name: "the-channel".to_string(),
|
||||
inviter_id: client_a.id()
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ use crate::{
|
||||
tests::{room_participants, RoomParticipants, TestServer},
|
||||
};
|
||||
use call::ActiveCall;
|
||||
use channel::{ChannelMembership, ChannelStore};
|
||||
use client::{ChannelId, User};
|
||||
use channel::{ChannelId, ChannelMembership, ChannelStore};
|
||||
use client::User;
|
||||
use futures::future::try_join_all;
|
||||
use gpui::{BackgroundExecutor, Model, SharedString, TestAppContext};
|
||||
use rpc::{
|
||||
@@ -281,7 +281,7 @@ async fn test_core_channels(
|
||||
.app_state
|
||||
.db
|
||||
.rename_channel(
|
||||
db::ChannelId::from_proto(channel_a_id.0),
|
||||
db::ChannelId::from_proto(channel_a_id),
|
||||
UserId::from_proto(client_a.id()),
|
||||
"channel-a-renamed",
|
||||
)
|
||||
@@ -1444,7 +1444,7 @@ fn assert_channels(
|
||||
fn assert_channels_list_shape(
|
||||
channel_store: &Model<ChannelStore>,
|
||||
cx: &TestAppContext,
|
||||
expected_channels: &[(ChannelId, usize)],
|
||||
expected_channels: &[(u64, usize)],
|
||||
) {
|
||||
let actual = cx.read(|cx| {
|
||||
channel_store.read_with(cx, |store, _| {
|
||||
|
||||
@@ -1426,8 +1426,6 @@ async fn test_mutual_editor_inlay_hint_cache_update(
|
||||
store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
|
||||
settings.defaults.inlay_hints = Some(InlayHintSettings {
|
||||
enabled: true,
|
||||
edit_debounce_ms: 0,
|
||||
scroll_debounce_ms: 0,
|
||||
show_type_hints: true,
|
||||
show_parameter_hints: false,
|
||||
show_other_hints: true,
|
||||
@@ -1440,8 +1438,6 @@ async fn test_mutual_editor_inlay_hint_cache_update(
|
||||
store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
|
||||
settings.defaults.inlay_hints = Some(InlayHintSettings {
|
||||
enabled: true,
|
||||
edit_debounce_ms: 0,
|
||||
scroll_debounce_ms: 0,
|
||||
show_type_hints: true,
|
||||
show_parameter_hints: false,
|
||||
show_other_hints: true,
|
||||
@@ -1699,8 +1695,6 @@ async fn test_inlay_hint_refresh_is_forwarded(
|
||||
store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
|
||||
settings.defaults.inlay_hints = Some(InlayHintSettings {
|
||||
enabled: false,
|
||||
edit_debounce_ms: 0,
|
||||
scroll_debounce_ms: 0,
|
||||
show_type_hints: false,
|
||||
show_parameter_hints: false,
|
||||
show_other_hints: false,
|
||||
@@ -1713,8 +1707,6 @@ async fn test_inlay_hint_refresh_is_forwarded(
|
||||
store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
|
||||
settings.defaults.inlay_hints = Some(InlayHintSettings {
|
||||
enabled: true,
|
||||
edit_debounce_ms: 0,
|
||||
scroll_debounce_ms: 0,
|
||||
show_type_hints: true,
|
||||
show_parameter_hints: true,
|
||||
show_other_hints: true,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::{rpc::RECONNECT_TIMEOUT, tests::TestServer};
|
||||
use call::{ActiveCall, ParticipantLocation};
|
||||
use client::ChannelId;
|
||||
use collab_ui::{
|
||||
channel_view::ChannelView,
|
||||
notifications::project_shared_notification::ProjectSharedNotification,
|
||||
@@ -2001,7 +2000,7 @@ async fn test_following_to_channel_notes_without_a_shared_project(
|
||||
}
|
||||
|
||||
async fn join_channel(
|
||||
channel_id: ChannelId,
|
||||
channel_id: u64,
|
||||
client: &TestClient,
|
||||
cx: &mut TestAppContext,
|
||||
) -> anyhow::Result<()> {
|
||||
|
||||
@@ -137,7 +137,7 @@ async fn test_notifications(
|
||||
assert_eq!(
|
||||
entry.notification,
|
||||
Notification::ChannelInvitation {
|
||||
channel_id: channel_id.0,
|
||||
channel_id,
|
||||
channel_name: "the-channel".to_string(),
|
||||
inviter_id: client_a.id()
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@ impl RandomizedTest for RandomChannelBufferTest {
|
||||
.channel_buffers()
|
||||
.deref()
|
||||
.iter()
|
||||
.find(|b| b.read(cx).channel_id.0 == channel_id.to_proto())
|
||||
.find(|b| b.read(cx).channel_id == channel_id.to_proto())
|
||||
{
|
||||
let channel_buffer = channel_buffer.read(cx);
|
||||
|
||||
|
||||
@@ -8,8 +8,7 @@ use anyhow::anyhow;
|
||||
use call::ActiveCall;
|
||||
use channel::{ChannelBuffer, ChannelStore};
|
||||
use client::{
|
||||
self, proto::PeerId, ChannelId, Client, Connection, Credentials, EstablishConnectionError,
|
||||
UserStore,
|
||||
self, proto::PeerId, Client, Connection, Credentials, EstablishConnectionError, UserStore,
|
||||
};
|
||||
use clock::FakeSystemClock;
|
||||
use collab_ui::channel_view::ChannelView;
|
||||
@@ -121,7 +120,7 @@ impl TestServer {
|
||||
pub async fn start2(
|
||||
cx_a: &mut TestAppContext,
|
||||
cx_b: &mut TestAppContext,
|
||||
) -> (TestServer, TestClient, TestClient, ChannelId) {
|
||||
) -> (TestServer, TestClient, TestClient, u64) {
|
||||
let mut server = Self::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;
|
||||
@@ -354,10 +353,10 @@ impl TestServer {
|
||||
pub async fn make_channel(
|
||||
&self,
|
||||
channel: &str,
|
||||
parent: Option<ChannelId>,
|
||||
parent: Option<u64>,
|
||||
admin: (&TestClient, &mut TestAppContext),
|
||||
members: &mut [(&TestClient, &mut TestAppContext)],
|
||||
) -> ChannelId {
|
||||
) -> u64 {
|
||||
let (_, admin_cx) = admin;
|
||||
let channel_id = admin_cx
|
||||
.read(ChannelStore::global)
|
||||
@@ -400,7 +399,7 @@ impl TestServer {
|
||||
channel: &str,
|
||||
client: &TestClient,
|
||||
cx: &mut TestAppContext,
|
||||
) -> ChannelId {
|
||||
) -> u64 {
|
||||
let channel_id = self
|
||||
.make_channel(channel, None, (client, cx), &mut [])
|
||||
.await;
|
||||
@@ -424,7 +423,7 @@ impl TestServer {
|
||||
&self,
|
||||
channels: &[(&str, Option<&str>)],
|
||||
creator: (&TestClient, &mut TestAppContext),
|
||||
) -> Vec<ChannelId> {
|
||||
) -> Vec<u64> {
|
||||
let mut observed_channels = HashMap::default();
|
||||
let mut result = Vec::new();
|
||||
for (channel, parent) in channels {
|
||||
@@ -484,7 +483,6 @@ impl TestServer {
|
||||
db: test_db.db().clone(),
|
||||
live_kit_client: Some(Arc::new(fake_server.create_api_client())),
|
||||
blob_store_client: None,
|
||||
clickhouse_client: None,
|
||||
config: Config {
|
||||
http_port: 0,
|
||||
database_url: "".into(),
|
||||
@@ -502,11 +500,6 @@ impl TestServer {
|
||||
blob_store_access_key: None,
|
||||
blob_store_secret_key: None,
|
||||
blob_store_bucket: None,
|
||||
clickhouse_url: None,
|
||||
clickhouse_user: None,
|
||||
clickhouse_password: None,
|
||||
clickhouse_database: None,
|
||||
zed_client_checksum_seed: None,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -678,7 +671,7 @@ impl TestClient {
|
||||
pub async fn host_workspace(
|
||||
&self,
|
||||
workspace: &View<Workspace>,
|
||||
channel_id: ChannelId,
|
||||
channel_id: u64,
|
||||
cx: &mut VisualTestContext,
|
||||
) {
|
||||
cx.update(|cx| {
|
||||
@@ -699,7 +692,7 @@ impl TestClient {
|
||||
|
||||
pub async fn join_workspace<'a>(
|
||||
&'a self,
|
||||
channel_id: ChannelId,
|
||||
channel_id: u64,
|
||||
cx: &'a mut TestAppContext,
|
||||
) -> (View<Workspace>, &'a mut VisualTestContext) {
|
||||
cx.update(|cx| workspace::join_channel(channel_id, self.app_state.clone(), None, cx))
|
||||
@@ -778,7 +771,7 @@ impl TestClient {
|
||||
}
|
||||
|
||||
pub fn open_channel_notes(
|
||||
channel_id: ChannelId,
|
||||
channel_id: u64,
|
||||
cx: &mut VisualTestContext,
|
||||
) -> Task<anyhow::Result<View<ChannelView>>> {
|
||||
let window = cx.update(|cx| cx.active_window().unwrap().downcast::<Workspace>().unwrap());
|
||||
|
||||
@@ -35,16 +35,19 @@ collections.workspace = true
|
||||
db.workspace = true
|
||||
editor.workspace = true
|
||||
extensions_ui.workspace = true
|
||||
feature_flags.workspace = true
|
||||
feedback.workspace = true
|
||||
futures.workspace = true
|
||||
fuzzy.workspace = true
|
||||
gpui.workspace = true
|
||||
language.workspace = true
|
||||
lazy_static.workspace = true
|
||||
log.workspace = true
|
||||
menu.workspace = true
|
||||
notifications.workspace = true
|
||||
parking_lot.workspace = true
|
||||
picker.workspace = true
|
||||
postage.workspace = true
|
||||
project.workspace = true
|
||||
recent_projects.workspace = true
|
||||
rich_text.workspace = true
|
||||
@@ -58,13 +61,13 @@ smallvec.workspace = true
|
||||
story = { workspace = true, optional = true }
|
||||
theme.workspace = true
|
||||
theme_selector.workspace = true
|
||||
time_format.workspace = true
|
||||
time.workspace = true
|
||||
ui.workspace = true
|
||||
util.workspace = true
|
||||
vcs_menu.workspace = true
|
||||
workspace.workspace = true
|
||||
zed_actions.workspace = true
|
||||
sys-locale.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
call = { workspace = true, features = ["test-support"] }
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use anyhow::Result;
|
||||
use call::report_call_event_for_channel;
|
||||
use channel::{Channel, ChannelBuffer, ChannelBufferEvent, ChannelStore};
|
||||
use channel::{Channel, ChannelBuffer, ChannelBufferEvent, ChannelId, ChannelStore};
|
||||
use client::{
|
||||
proto::{self, PeerId},
|
||||
ChannelId, Collaborator, ParticipantIndex,
|
||||
Collaborator, ParticipantIndex,
|
||||
};
|
||||
use collections::HashMap;
|
||||
use editor::{
|
||||
@@ -454,7 +454,7 @@ impl FollowableItem for ChannelView {
|
||||
|
||||
Some(proto::view::Variant::ChannelView(
|
||||
proto::view::ChannelView {
|
||||
channel_id: channel_buffer.channel_id.0,
|
||||
channel_id: channel_buffer.channel_id,
|
||||
editor: if let Some(proto::view::Variant::Editor(proto)) =
|
||||
self.editor.read(cx).to_state_proto(cx)
|
||||
{
|
||||
@@ -480,8 +480,7 @@ impl FollowableItem for ChannelView {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let open =
|
||||
ChannelView::open_in_pane(ChannelId(state.channel_id), None, pane, workspace, cx);
|
||||
let open = ChannelView::open_in_pane(state.channel_id, None, pane, workspace, cx);
|
||||
|
||||
Some(cx.spawn(|mut cx| async move {
|
||||
let this = open.await?;
|
||||
|
||||
@@ -2,14 +2,14 @@ use crate::{collab_panel, ChatPanelSettings};
|
||||
use anyhow::Result;
|
||||
use call::{room, ActiveCall};
|
||||
use channel::{ChannelChat, ChannelChatEvent, ChannelMessage, ChannelMessageId, ChannelStore};
|
||||
use client::{ChannelId, Client};
|
||||
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, ClipboardItem,
|
||||
CursorStyle, DismissEvent, ElementId, EventEmitter, FocusHandle, FocusableView, FontStyle,
|
||||
FontWeight, HighlightStyle, ListOffset, ListScrollEvent, ListState, Model, Render, StyledText,
|
||||
actions, div, list, prelude::*, px, Action, AppContext, AsyncWindowContext, CursorStyle,
|
||||
DismissEvent, ElementId, EventEmitter, FocusHandle, FocusableView, FontStyle, FontWeight,
|
||||
HighlightStyle, ListOffset, ListScrollEvent, ListState, Model, Render, StyledText,
|
||||
Subscription, Task, View, ViewContext, VisualContext, WeakView,
|
||||
};
|
||||
use language::LanguageRegistry;
|
||||
@@ -169,7 +169,7 @@ impl ChatPanel {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn channel_id(&self, cx: &AppContext) -> Option<ChannelId> {
|
||||
pub fn channel_id(&self, cx: &AppContext) -> Option<u64> {
|
||||
self.active_chat
|
||||
.as_ref()
|
||||
.map(|(chat, _)| chat.read(cx).channel_id)
|
||||
@@ -488,10 +488,11 @@ impl ChatPanel {
|
||||
.child(Label::new(message.sender.github_login.clone())),
|
||||
)
|
||||
.child(
|
||||
Label::new(time_format::format_localized_timestamp(
|
||||
Label::new(format_timestamp(
|
||||
OffsetDateTime::now_utc(),
|
||||
message.timestamp,
|
||||
self.local_timezone,
|
||||
None,
|
||||
))
|
||||
.size(LabelSize::Small)
|
||||
.color(Color::Muted),
|
||||
@@ -628,20 +629,6 @@ impl ChatPanel {
|
||||
})
|
||||
}),
|
||||
)
|
||||
.entry(
|
||||
"Copy message text",
|
||||
None,
|
||||
cx.handler_for(&this, move |this, cx| {
|
||||
this.active_chat().map(|active_chat| {
|
||||
if let Some(message) =
|
||||
active_chat.read(cx).find_loaded_message(message_id)
|
||||
{
|
||||
let text = message.body.clone();
|
||||
cx.write_to_clipboard(ClipboardItem::new(text))
|
||||
}
|
||||
});
|
||||
}),
|
||||
)
|
||||
.when(can_delete_message, move |menu| {
|
||||
menu.entry(
|
||||
"Delete message",
|
||||
@@ -710,7 +697,7 @@ impl ChatPanel {
|
||||
|
||||
pub fn select_channel(
|
||||
&mut self,
|
||||
selected_channel_id: ChannelId,
|
||||
selected_channel_id: u64,
|
||||
scroll_to_message_id: Option<u64>,
|
||||
cx: &mut ViewContext<ChatPanel>,
|
||||
) -> Task<Result<()>> {
|
||||
@@ -972,13 +959,94 @@ impl Panel for ChatPanel {
|
||||
|
||||
impl EventEmitter<PanelEvent> for ChatPanel {}
|
||||
|
||||
fn is_12_hour_clock(locale: String) -> bool {
|
||||
[
|
||||
"es-MX", "es-CO", "es-SV", "es-NI",
|
||||
"es-HN", // Mexico, Colombia, El Salvador, Nicaragua, Honduras
|
||||
"en-US", "en-CA", "en-AU", "en-NZ", // U.S, Canada, Australia, New Zealand
|
||||
"ar-SA", "ar-EG", "ar-JO", // Saudi Arabia, Egypt, Jordan
|
||||
"en-IN", "hi-IN", // India, Hindu
|
||||
"en-PK", "ur-PK", // Pakistan, Urdu
|
||||
"en-PH", "fil-PH", // Philippines, Filipino
|
||||
"bn-BD", "ccp-BD", // Bangladesh, Chakma
|
||||
"en-IE", "ga-IE", // Ireland, Irish
|
||||
"en-MY", "ms-MY", // Malaysia, Malay
|
||||
]
|
||||
.contains(&locale.as_str())
|
||||
}
|
||||
|
||||
fn format_timestamp(
|
||||
reference: OffsetDateTime,
|
||||
timestamp: OffsetDateTime,
|
||||
timezone: UtcOffset,
|
||||
locale: Option<String>,
|
||||
) -> String {
|
||||
let locale = match locale {
|
||||
Some(locale) => locale,
|
||||
None => sys_locale::get_locale().unwrap_or_else(|| String::from("en-US")),
|
||||
};
|
||||
let timestamp_local = timestamp.to_offset(timezone);
|
||||
let timestamp_local_hour = timestamp_local.hour();
|
||||
let timestamp_local_minute = timestamp_local.minute();
|
||||
|
||||
let (hour, meridiem) = if is_12_hour_clock(locale) {
|
||||
let meridiem = if timestamp_local_hour >= 12 {
|
||||
"pm"
|
||||
} else {
|
||||
"am"
|
||||
};
|
||||
|
||||
let hour_12 = match timestamp_local_hour {
|
||||
0 => 12, // Midnight
|
||||
13..=23 => timestamp_local_hour - 12, // PM hours
|
||||
_ => timestamp_local_hour, // AM hours
|
||||
};
|
||||
|
||||
(hour_12, Some(meridiem))
|
||||
} else {
|
||||
(timestamp_local_hour, None)
|
||||
};
|
||||
|
||||
let formatted_time = match meridiem {
|
||||
Some(meridiem) => format!("{:02}:{:02} {}", hour, timestamp_local_minute, meridiem),
|
||||
None => format!("{:02}:{:02}", hour, timestamp_local_minute),
|
||||
};
|
||||
|
||||
let reference_local = reference.to_offset(timezone);
|
||||
let reference_local_date = reference_local.date();
|
||||
let timestamp_local_date = timestamp_local.date();
|
||||
|
||||
if timestamp_local_date == reference_local_date {
|
||||
return formatted_time;
|
||||
}
|
||||
|
||||
if reference_local_date.previous_day() == Some(timestamp_local_date) {
|
||||
return format!("yesterday at {}", formatted_time);
|
||||
}
|
||||
|
||||
match meridiem {
|
||||
Some(_) => format!(
|
||||
"{:02}/{:02}/{}",
|
||||
timestamp_local_date.month() as u32,
|
||||
timestamp_local_date.day(),
|
||||
timestamp_local_date.year()
|
||||
),
|
||||
None => format!(
|
||||
"{:02}/{:02}/{}",
|
||||
timestamp_local_date.day(),
|
||||
timestamp_local_date.month() as u32,
|
||||
timestamp_local_date.year()
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use gpui::HighlightStyle;
|
||||
use pretty_assertions::assert_eq;
|
||||
use rich_text::Highlight;
|
||||
use time::OffsetDateTime;
|
||||
use time::{Date, OffsetDateTime, Time, UtcOffset};
|
||||
use util::test::marked_text_ranges;
|
||||
|
||||
#[gpui::test]
|
||||
@@ -1129,4 +1197,150 @@ mod tests {
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_locale() {
|
||||
let reference = create_offset_datetime(1990, 4, 12, 16, 45, 0);
|
||||
let timestamp = create_offset_datetime(1990, 4, 12, 15, 30, 0);
|
||||
|
||||
assert_eq!(
|
||||
format_timestamp(
|
||||
reference,
|
||||
timestamp,
|
||||
test_timezone(),
|
||||
Some(String::from("en-GB"))
|
||||
),
|
||||
"15:30"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_today() {
|
||||
let reference = create_offset_datetime(1990, 4, 12, 16, 45, 0);
|
||||
let timestamp = create_offset_datetime(1990, 4, 12, 15, 30, 0);
|
||||
|
||||
assert_eq!(
|
||||
format_timestamp(
|
||||
reference,
|
||||
timestamp,
|
||||
test_timezone(),
|
||||
Some(String::from("en-US"))
|
||||
),
|
||||
"03:30 pm"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_yesterday() {
|
||||
let reference = create_offset_datetime(1990, 4, 12, 10, 30, 0);
|
||||
let timestamp = create_offset_datetime(1990, 4, 11, 9, 0, 0);
|
||||
|
||||
assert_eq!(
|
||||
format_timestamp(
|
||||
reference,
|
||||
timestamp,
|
||||
test_timezone(),
|
||||
Some(String::from("en-US"))
|
||||
),
|
||||
"yesterday at 09:00 am"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_yesterday_less_than_24_hours_ago() {
|
||||
let reference = create_offset_datetime(1990, 4, 12, 19, 59, 0);
|
||||
let timestamp = create_offset_datetime(1990, 4, 11, 20, 0, 0);
|
||||
|
||||
assert_eq!(
|
||||
format_timestamp(
|
||||
reference,
|
||||
timestamp,
|
||||
test_timezone(),
|
||||
Some(String::from("en-US"))
|
||||
),
|
||||
"yesterday at 08:00 pm"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_yesterday_more_than_24_hours_ago() {
|
||||
let reference = create_offset_datetime(1990, 4, 12, 19, 59, 0);
|
||||
let timestamp = create_offset_datetime(1990, 4, 11, 18, 0, 0);
|
||||
|
||||
assert_eq!(
|
||||
format_timestamp(
|
||||
reference,
|
||||
timestamp,
|
||||
test_timezone(),
|
||||
Some(String::from("en-US"))
|
||||
),
|
||||
"yesterday at 06:00 pm"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_yesterday_over_midnight() {
|
||||
let reference = create_offset_datetime(1990, 4, 12, 0, 5, 0);
|
||||
let timestamp = create_offset_datetime(1990, 4, 11, 23, 55, 0);
|
||||
|
||||
assert_eq!(
|
||||
format_timestamp(
|
||||
reference,
|
||||
timestamp,
|
||||
test_timezone(),
|
||||
Some(String::from("en-US"))
|
||||
),
|
||||
"yesterday at 11:55 pm"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_yesterday_over_month() {
|
||||
let reference = create_offset_datetime(1990, 4, 2, 9, 0, 0);
|
||||
let timestamp = create_offset_datetime(1990, 4, 1, 20, 0, 0);
|
||||
|
||||
assert_eq!(
|
||||
format_timestamp(
|
||||
reference,
|
||||
timestamp,
|
||||
test_timezone(),
|
||||
Some(String::from("en-US"))
|
||||
),
|
||||
"yesterday at 08:00 pm"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_before_yesterday() {
|
||||
let reference = create_offset_datetime(1990, 4, 12, 10, 30, 0);
|
||||
let timestamp = create_offset_datetime(1990, 4, 10, 20, 20, 0);
|
||||
|
||||
assert_eq!(
|
||||
format_timestamp(
|
||||
reference,
|
||||
timestamp,
|
||||
test_timezone(),
|
||||
Some(String::from("en-US"))
|
||||
),
|
||||
"04/10/1990"
|
||||
);
|
||||
}
|
||||
|
||||
fn test_timezone() -> UtcOffset {
|
||||
UtcOffset::from_hms(0, 0, 0).expect("Valid timezone offset")
|
||||
}
|
||||
|
||||
fn create_offset_datetime(
|
||||
year: i32,
|
||||
month: u8,
|
||||
day: u8,
|
||||
hour: u8,
|
||||
minute: u8,
|
||||
second: u8,
|
||||
) -> OffsetDateTime {
|
||||
let date =
|
||||
Date::from_calendar_date(year, time::Month::try_from(month).unwrap(), day).unwrap();
|
||||
let time = Time::from_hms(hour, minute, second).unwrap();
|
||||
date.with_time(time).assume_utc() // Assume UTC for simplicity
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use anyhow::Result;
|
||||
use channel::{ChannelMembership, ChannelStore, MessageParams};
|
||||
use client::{ChannelId, UserId};
|
||||
use channel::{ChannelId, ChannelMembership, ChannelStore, MessageParams};
|
||||
use client::UserId;
|
||||
use collections::{HashMap, HashSet};
|
||||
use editor::{AnchorRangeExt, CompletionProvider, Editor, EditorElement, EditorStyle};
|
||||
use fuzzy::StringMatchCandidate;
|
||||
@@ -131,7 +131,7 @@ impl MessageEditor {
|
||||
|
||||
pub fn set_channel(
|
||||
&mut self,
|
||||
channel_id: ChannelId,
|
||||
channel_id: u64,
|
||||
channel_name: Option<SharedString>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
|
||||
@@ -7,8 +7,8 @@ use crate::{
|
||||
CollaborationPanelSettings,
|
||||
};
|
||||
use call::ActiveCall;
|
||||
use channel::{Channel, ChannelEvent, ChannelStore, HostedProjectId};
|
||||
use client::{ChannelId, Client, Contact, User, UserStore};
|
||||
use channel::{Channel, ChannelEvent, ChannelId, ChannelStore};
|
||||
use client::{Client, Contact, User, UserStore};
|
||||
use contact_finder::ContactFinder;
|
||||
use db::kvp::KEY_VALUE_STORE;
|
||||
use editor::{Editor, EditorElement, EditorStyle};
|
||||
@@ -184,10 +184,6 @@ enum ListEntry {
|
||||
ChannelEditor {
|
||||
depth: usize,
|
||||
},
|
||||
HostedProject {
|
||||
id: HostedProjectId,
|
||||
name: SharedString,
|
||||
},
|
||||
Contact {
|
||||
contact: Arc<Contact>,
|
||||
calling: bool,
|
||||
@@ -330,10 +326,7 @@ impl CollabPanel {
|
||||
panel.width = serialized_panel.width;
|
||||
panel.collapsed_channels = serialized_panel
|
||||
.collapsed_channels
|
||||
.unwrap_or_else(|| Vec::new())
|
||||
.iter()
|
||||
.map(|cid| ChannelId(*cid))
|
||||
.collect();
|
||||
.unwrap_or_else(|| Vec::new());
|
||||
cx.notify();
|
||||
});
|
||||
}
|
||||
@@ -351,9 +344,7 @@ impl CollabPanel {
|
||||
COLLABORATION_PANEL_KEY.into(),
|
||||
serde_json::to_string(&SerializedCollabPanel {
|
||||
width,
|
||||
collapsed_channels: Some(
|
||||
collapsed_channels.iter().map(|cid| cid.0).collect(),
|
||||
),
|
||||
collapsed_channels: Some(collapsed_channels),
|
||||
})?,
|
||||
)
|
||||
.await?;
|
||||
@@ -572,7 +563,6 @@ impl CollabPanel {
|
||||
}
|
||||
}
|
||||
|
||||
let hosted_projects = channel_store.projects_for_id(channel.id);
|
||||
let has_children = channel_store
|
||||
.channel_at_index(mat.candidate_id + 1)
|
||||
.map_or(false, |next_channel| {
|
||||
@@ -606,10 +596,6 @@ impl CollabPanel {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (name, id) in hosted_projects {
|
||||
self.entries.push(ListEntry::HostedProject { id, name })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1037,33 +1023,6 @@ impl CollabPanel {
|
||||
.tooltip(move |cx| Tooltip::text("Open Chat", cx))
|
||||
}
|
||||
|
||||
fn render_channel_project(
|
||||
&self,
|
||||
id: HostedProjectId,
|
||||
name: &SharedString,
|
||||
is_selected: bool,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> impl IntoElement {
|
||||
ListItem::new(ElementId::NamedInteger(
|
||||
"channel-project".into(),
|
||||
id.0 as usize,
|
||||
))
|
||||
.indent_level(2)
|
||||
.indent_step_size(px(20.))
|
||||
.selected(is_selected)
|
||||
.on_click(cx.listener(move |_this, _, _cx| {
|
||||
// todo!()
|
||||
}))
|
||||
.start_slot(
|
||||
h_flex()
|
||||
.relative()
|
||||
.gap_1()
|
||||
.child(IconButton::new(0, IconName::FileTree)),
|
||||
)
|
||||
.child(Label::new(name.clone()))
|
||||
.tooltip(move |cx| Tooltip::text("Open Project", cx))
|
||||
}
|
||||
|
||||
fn has_subchannels(&self, ix: usize) -> bool {
|
||||
self.entries.get(ix).map_or(false, |entry| {
|
||||
if let ListEntry::Channel { has_children, .. } = entry {
|
||||
@@ -1527,12 +1486,6 @@ impl CollabPanel {
|
||||
ListEntry::ChannelChat { channel_id } => {
|
||||
self.join_channel_chat(*channel_id, cx)
|
||||
}
|
||||
ListEntry::HostedProject {
|
||||
id: _id,
|
||||
name: _name,
|
||||
} => {
|
||||
// todo!()
|
||||
}
|
||||
|
||||
ListEntry::OutgoingRequest(_) => {}
|
||||
ListEntry::ChannelEditor { .. } => {}
|
||||
@@ -1970,7 +1923,7 @@ impl CollabPanel {
|
||||
|
||||
fn respond_to_channel_invite(
|
||||
&mut self,
|
||||
channel_id: ChannelId,
|
||||
channel_id: u64,
|
||||
accept: bool,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
@@ -1989,7 +1942,7 @@ impl CollabPanel {
|
||||
.detach_and_prompt_err("Call failed", cx, |_, _| None);
|
||||
}
|
||||
|
||||
fn join_channel(&self, channel_id: ChannelId, cx: &mut ViewContext<Self>) {
|
||||
fn join_channel(&self, channel_id: u64, cx: &mut ViewContext<Self>) {
|
||||
let Some(workspace) = self.workspace.upgrade() else {
|
||||
return;
|
||||
};
|
||||
@@ -2136,10 +2089,6 @@ impl CollabPanel {
|
||||
ListEntry::ChannelChat { channel_id } => self
|
||||
.render_channel_chat(*channel_id, is_selected, cx)
|
||||
.into_any_element(),
|
||||
|
||||
ListEntry::HostedProject { id, name } => self
|
||||
.render_channel_project(*id, name, is_selected, cx)
|
||||
.into_any_element(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2456,7 +2405,7 @@ impl CollabPanel {
|
||||
.tooltip(|cx| Tooltip::text("Accept invite", cx)),
|
||||
];
|
||||
|
||||
ListItem::new(("channel-invite", channel.id.0 as usize))
|
||||
ListItem::new(("channel-invite", channel.id as usize))
|
||||
.selected(is_selected)
|
||||
.child(
|
||||
h_flex()
|
||||
@@ -2548,7 +2497,7 @@ impl CollabPanel {
|
||||
|
||||
div()
|
||||
.h_6()
|
||||
.id(channel_id.0 as usize)
|
||||
.id(channel_id as usize)
|
||||
.group("")
|
||||
.flex()
|
||||
.w_full()
|
||||
@@ -2576,7 +2525,7 @@ impl CollabPanel {
|
||||
this.move_channel(dragged_channel.id, channel_id, cx);
|
||||
}))
|
||||
.child(
|
||||
ListItem::new(channel_id.0 as usize)
|
||||
ListItem::new(channel_id as usize)
|
||||
// Add one level of depth for the disclosure arrow.
|
||||
.indent_level(depth + 1)
|
||||
.indent_step_size(px(20.))
|
||||
@@ -2623,7 +2572,7 @@ impl CollabPanel {
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.id(channel_id.0 as usize)
|
||||
.id(channel_id as usize)
|
||||
.child(Label::new(channel.name.clone()))
|
||||
.children(face_pile.map(|face_pile| face_pile.p_1())),
|
||||
),
|
||||
@@ -2723,24 +2672,32 @@ fn render_tree_branch(is_last: bool, overdraw: bool, cx: &mut WindowContext) ->
|
||||
let right = bounds.right();
|
||||
let top = bounds.top();
|
||||
|
||||
cx.paint_quad(fill(
|
||||
Bounds::from_corners(
|
||||
point(start_x, top),
|
||||
point(
|
||||
start_x + thickness,
|
||||
if is_last {
|
||||
start_y
|
||||
} else {
|
||||
bounds.bottom() + if overdraw { px(1.) } else { px(0.) }
|
||||
},
|
||||
cx.paint_quad(
|
||||
fill(
|
||||
Bounds::from_corners(
|
||||
point(start_x, top),
|
||||
point(
|
||||
start_x + thickness,
|
||||
if is_last {
|
||||
start_y
|
||||
} else {
|
||||
bounds.bottom() + if overdraw { px(1.) } else { px(0.) }
|
||||
},
|
||||
),
|
||||
),
|
||||
color,
|
||||
),
|
||||
color,
|
||||
));
|
||||
cx.paint_quad(fill(
|
||||
Bounds::from_corners(point(start_x, start_y), point(right, start_y + thickness)),
|
||||
color,
|
||||
));
|
||||
None,
|
||||
None,
|
||||
);
|
||||
cx.paint_quad(
|
||||
fill(
|
||||
Bounds::from_corners(point(start_x, start_y), point(right, start_y + thickness)),
|
||||
color,
|
||||
),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
})
|
||||
.w(width)
|
||||
.h(line_height)
|
||||
@@ -2877,11 +2834,6 @@ impl PartialEq for ListEntry {
|
||||
return channel_1.id == channel_2.id;
|
||||
}
|
||||
}
|
||||
ListEntry::HostedProject { id, .. } => {
|
||||
if let ListEntry::HostedProject { id: other_id, .. } = other {
|
||||
return id == other_id;
|
||||
}
|
||||
}
|
||||
ListEntry::ChannelNotes { channel_id } => {
|
||||
if let ListEntry::ChannelNotes {
|
||||
channel_id: other_id,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use channel::{ChannelMembership, ChannelStore};
|
||||
use channel::{ChannelId, ChannelMembership, ChannelStore};
|
||||
use client::{
|
||||
proto::{self, ChannelRole, ChannelVisibility},
|
||||
ChannelId, User, UserId, UserStore,
|
||||
User, UserId, UserStore,
|
||||
};
|
||||
use fuzzy::{match_strings, StringMatchCandidate};
|
||||
use gpui::{
|
||||
@@ -266,7 +266,7 @@ pub struct ChannelModalDelegate {
|
||||
impl PickerDelegate for ChannelModalDelegate {
|
||||
type ListItem = ListItem;
|
||||
|
||||
fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
|
||||
fn placeholder_text(&self) -> Arc<str> {
|
||||
"Search collaborator by username...".into()
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ impl PickerDelegate for ContactFinderDelegate {
|
||||
self.selected_index = ix;
|
||||
}
|
||||
|
||||
fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
|
||||
fn placeholder_text(&self) -> Arc<str> {
|
||||
"Search collaborator by username...".into()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{chat_panel::ChatPanel, NotificationPanelSettings};
|
||||
use anyhow::Result;
|
||||
use channel::ChannelStore;
|
||||
use client::{ChannelId, Client, Notification, User, UserStore};
|
||||
use client::{Client, Notification, User, UserStore};
|
||||
use collections::HashMap;
|
||||
use db::kvp::KEY_VALUE_STORE;
|
||||
use futures::StreamExt;
|
||||
@@ -357,7 +357,7 @@ impl NotificationPanel {
|
||||
"{} invited you to join the #{channel_name} channel",
|
||||
inviter.github_login
|
||||
),
|
||||
needs_response: channel_store.has_channel_invitation(ChannelId(channel_id)),
|
||||
needs_response: channel_store.has_channel_invitation(channel_id),
|
||||
actor: Some(inviter),
|
||||
can_navigate: false,
|
||||
})
|
||||
@@ -368,7 +368,7 @@ impl NotificationPanel {
|
||||
message_id,
|
||||
} => {
|
||||
let sender = user_store.get_cached_user(sender_id)?;
|
||||
let channel = channel_store.channel_for_id(ChannelId(channel_id))?;
|
||||
let channel = channel_store.channel_for_id(channel_id)?;
|
||||
let message = self
|
||||
.notification_store
|
||||
.read(cx)
|
||||
@@ -432,7 +432,7 @@ impl NotificationPanel {
|
||||
if let Some(panel) = workspace.focus_panel::<ChatPanel>(cx) {
|
||||
panel.update(cx, |panel, cx| {
|
||||
panel
|
||||
.select_channel(ChannelId(channel_id), Some(message_id), cx)
|
||||
.select_channel(channel_id, Some(message_id), cx)
|
||||
.detach_and_log_err(cx);
|
||||
});
|
||||
}
|
||||
@@ -454,7 +454,7 @@ impl NotificationPanel {
|
||||
panel.is_scrolled_to_bottom()
|
||||
&& panel
|
||||
.active_chat()
|
||||
.map_or(false, |chat| chat.read(cx).channel_id.0 == *channel_id)
|
||||
.map_or(false, |chat| chat.read(cx).channel_id == *channel_id)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
@@ -7,10 +7,13 @@ license = "GPL-3.0-or-later"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
stories = ["dep:itertools", "dep:story"]
|
||||
|
||||
[lib]
|
||||
path = "src/color.rs"
|
||||
doctest = true
|
||||
|
||||
[dependencies]
|
||||
palette.workspace = true
|
||||
itertools = { version = "0.11.0", optional = true }
|
||||
palette = "0.7.3"
|
||||
story = { workspace = true, optional = true }
|
||||
|
||||
@@ -10,13 +10,15 @@ path = "src/command_palette.rs"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
client.workspace = true
|
||||
collections.workspace = true
|
||||
command_palette_hooks.workspace = true
|
||||
# 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
|
||||
postage.workspace = true
|
||||
project.workspace = true
|
||||
release_channel.workspace = true
|
||||
serde.workspace = true
|
||||
@@ -26,6 +28,7 @@ ui.workspace = true
|
||||
util.workspace = true
|
||||
workspace.workspace = true
|
||||
zed_actions.workspace = true
|
||||
postage.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
ctor.workspace = true
|
||||
|
||||
@@ -6,9 +6,7 @@ use std::{
|
||||
|
||||
use client::telemetry::Telemetry;
|
||||
use collections::HashMap;
|
||||
use command_palette_hooks::{
|
||||
CommandInterceptResult, CommandPaletteFilter, CommandPaletteInterceptor,
|
||||
};
|
||||
use copilot::CommandPaletteFilter;
|
||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||
use gpui::{
|
||||
actions, Action, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Global,
|
||||
@@ -103,6 +101,18 @@ impl Render for CommandPalette {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CommandPaletteInterceptor(
|
||||
pub Box<dyn Fn(&str, &AppContext) -> Option<CommandInterceptResult>>,
|
||||
);
|
||||
|
||||
impl Global for CommandPaletteInterceptor {}
|
||||
|
||||
pub struct CommandInterceptResult {
|
||||
pub action: Box<dyn Action>,
|
||||
pub string: String,
|
||||
pub positions: Vec<usize>,
|
||||
}
|
||||
|
||||
pub struct CommandPaletteDelegate {
|
||||
command_palette: WeakView<CommandPalette>,
|
||||
all_commands: Vec<Command>,
|
||||
@@ -221,7 +231,7 @@ impl CommandPaletteDelegate {
|
||||
impl PickerDelegate for CommandPaletteDelegate {
|
||||
type ListItem = ListItem;
|
||||
|
||||
fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
|
||||
fn placeholder_text(&self) -> Arc<str> {
|
||||
"Execute a command...".into()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
[package]
|
||||
name = "command_palette_hooks"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
[lib]
|
||||
path = "src/command_palette_hooks.rs"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
collections.workspace = true
|
||||
gpui.workspace = true
|
||||
@@ -1,24 +0,0 @@
|
||||
use std::any::TypeId;
|
||||
|
||||
use collections::HashSet;
|
||||
use gpui::{Action, AppContext, Global};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CommandPaletteFilter {
|
||||
pub hidden_namespaces: HashSet<&'static str>,
|
||||
pub hidden_action_types: HashSet<TypeId>,
|
||||
}
|
||||
|
||||
impl Global for CommandPaletteFilter {}
|
||||
|
||||
pub struct CommandPaletteInterceptor(
|
||||
pub Box<dyn Fn(&str, &AppContext) -> Option<CommandInterceptResult>>,
|
||||
);
|
||||
|
||||
impl Global for CommandPaletteInterceptor {}
|
||||
|
||||
pub struct CommandInterceptResult {
|
||||
pub action: Box<dyn Action>,
|
||||
pub string: String,
|
||||
pub positions: Vec<usize>,
|
||||
}
|
||||
@@ -22,18 +22,20 @@ test-support = [
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
async-compression.workspace = true
|
||||
async-tar.workspace = true
|
||||
async-tar = "0.4.2"
|
||||
collections.workspace = true
|
||||
command_palette_hooks.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
language.workspace = true
|
||||
log.workspace = true
|
||||
lsp.workspace = true
|
||||
node_runtime.workspace = true
|
||||
parking_lot.workspace = true
|
||||
serde.workspace = true
|
||||
serde_derive.workspace = true
|
||||
settings.workspace = true
|
||||
smol.workspace = true
|
||||
theme.workspace = true
|
||||
util.workspace = true
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
|
||||
@@ -3,7 +3,6 @@ use anyhow::{anyhow, Context as _, Result};
|
||||
use async_compression::futures::bufread::GzipDecoder;
|
||||
use async_tar::Archive;
|
||||
use collections::{HashMap, HashSet};
|
||||
use command_palette_hooks::CommandPaletteFilter;
|
||||
use futures::{channel::oneshot, future::Shared, Future, FutureExt, TryFutureExt};
|
||||
use gpui::{
|
||||
actions, AppContext, AsyncAppContext, Context, Entity, EntityId, EventEmitter, Global, Model,
|
||||
@@ -33,6 +32,17 @@ use util::{
|
||||
ResultExt,
|
||||
};
|
||||
|
||||
// HACK: This type is only defined in `copilot` since it is the earliest ancestor
|
||||
// of the crates that use it.
|
||||
//
|
||||
// This is not great. Let's find a better place for it to live.
|
||||
#[derive(Default)]
|
||||
pub struct CommandPaletteFilter {
|
||||
pub hidden_namespaces: HashSet<&'static str>,
|
||||
pub hidden_action_types: HashSet<TypeId>,
|
||||
}
|
||||
|
||||
impl Global for CommandPaletteFilter {}
|
||||
actions!(
|
||||
copilot,
|
||||
[
|
||||
|
||||
@@ -14,9 +14,12 @@ anyhow.workspace = true
|
||||
copilot.workspace = true
|
||||
editor.workspace = true
|
||||
fs.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
language.workspace = true
|
||||
settings.workspace = true
|
||||
smol.workspace = true
|
||||
theme.workspace = true
|
||||
ui.workspace = true
|
||||
util.workspace = true
|
||||
workspace.workspace = true
|
||||
|
||||
@@ -14,16 +14,22 @@ test-support = []
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
async-trait.workspace = true
|
||||
collections.workspace = true
|
||||
gpui.workspace = true
|
||||
indoc.workspace = true
|
||||
lazy_static.workspace = true
|
||||
log.workspace = true
|
||||
parking_lot.workspace = true
|
||||
release_channel.workspace = true
|
||||
serde.workspace = true
|
||||
serde_derive.workspace = true
|
||||
smol.workspace = true
|
||||
sqlez.workspace = true
|
||||
sqlez_macros.workspace = true
|
||||
util.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger.workspace = true
|
||||
gpui = { workspace = true, features = ["test-support"] }
|
||||
tempfile.workspace = true
|
||||
|
||||
@@ -18,10 +18,13 @@ gpui.workspace = true
|
||||
language.workspace = true
|
||||
log.workspace = true
|
||||
lsp.workspace = true
|
||||
postage.workspace = true
|
||||
project.workspace = true
|
||||
schemars.workspace = true
|
||||
serde.workspace = true
|
||||
serde_derive.workspace = true
|
||||
settings.workspace = true
|
||||
smallvec.workspace = true
|
||||
theme.workspace = true
|
||||
ui.workspace = true
|
||||
util.workspace = true
|
||||
|
||||
@@ -1584,6 +1584,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn editor_blocks(editor: &View<Editor>, cx: &mut WindowContext) -> Vec<(u32, SharedString)> {
|
||||
let editor_view = editor.clone();
|
||||
editor.update(cx, |editor, cx| {
|
||||
let snapshot = editor.snapshot(cx);
|
||||
snapshot
|
||||
@@ -1592,6 +1593,7 @@ mod tests {
|
||||
.filter_map(|(ix, (row, block))| {
|
||||
let name: SharedString = match block {
|
||||
TransformBlock::Custom(block) => cx.with_element_context({
|
||||
let editor_view = editor_view.clone();
|
||||
|cx| -> Option<SharedString> {
|
||||
block
|
||||
.render(&mut BlockContext {
|
||||
@@ -1602,6 +1604,7 @@ mod tests {
|
||||
em_width: px(0.),
|
||||
max_width: px(0.),
|
||||
block_id: ix,
|
||||
view: editor_view,
|
||||
editor_style: &editor::EditorStyle::default(),
|
||||
})
|
||||
.inner_id()?
|
||||
|
||||