Compare commits
45 Commits
hover-copy
...
bash-timeo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bdb1768c33 | ||
|
|
f7c3c533a3 | ||
|
|
c05bf096f8 | ||
|
|
b15ee1b1cc | ||
|
|
0459b1d303 | ||
|
|
246013cfc2 | ||
|
|
47eaf274d6 | ||
|
|
ef4b5b0698 | ||
|
|
39c98ce882 | ||
|
|
763cc6dba3 | ||
|
|
0b75c13034 | ||
|
|
38ec45008c | ||
|
|
97641c3298 | ||
|
|
ca8f6e8a3f | ||
|
|
db53da49e1 | ||
|
|
df94dcdea6 | ||
|
|
1c85901440 | ||
|
|
9fb77ad176 | ||
|
|
feafad2f9d | ||
|
|
86ef00054b | ||
|
|
9e8afa8daa | ||
|
|
698cdc4d1a | ||
|
|
85c5d8af3a | ||
|
|
1774cad933 | ||
|
|
ee7b1ec7f2 | ||
|
|
14b43d573c | ||
|
|
d39e1e03b8 | ||
|
|
9e504a1ed9 | ||
|
|
ca4cc4764b | ||
|
|
ea33d78ae4 | ||
|
|
e36a2f2739 | ||
|
|
cbd9b4cc39 | ||
|
|
64f8b1e739 | ||
|
|
4f936d8100 | ||
|
|
97abf21a28 | ||
|
|
5fb1411e4d | ||
|
|
b04f7a4c7c | ||
|
|
61b7a05792 | ||
|
|
cc15598e09 | ||
|
|
7ee9109ade | ||
|
|
c21fdd212b | ||
|
|
a28929592e | ||
|
|
3b787e85a4 | ||
|
|
1264e7a200 | ||
|
|
bfe08e449f |
36
.github/ISSUE_TEMPLATE/01_bug_agent.yml
vendored
Normal file
36
.github/ISSUE_TEMPLATE/01_bug_agent.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: Bug Report (Agent Panel)
|
||||
description: Zed Agent Panel Bugs
|
||||
type: "Bug"
|
||||
labels: ["agent", "ai"]
|
||||
title: "Agent Panel: <a short description of the Agent Panel bug>"
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Summary
|
||||
description: Describe the bug with a one line summary, and provide detailed reproduction steps
|
||||
value: |
|
||||
<!-- Please insert a one line summary of the issue below -->
|
||||
SUMMARY_SENTENCE_HERE
|
||||
|
||||
### Description
|
||||
<!-- Describe with sufficient detail to reproduce from a clean Zed install. -->
|
||||
<!-- Please include the LLM provider and model name you are using -->
|
||||
Steps to trigger the problem:
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
Actual Behavior:
|
||||
Expected Behavior:
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: environment
|
||||
attributes:
|
||||
label: Zed Version and System Specs
|
||||
description: 'Open Zed, and in the command palette select "zed: Copy System Specs Into Clipboard"'
|
||||
placeholder: |
|
||||
Output of "zed: Copy System Specs Into Clipboard"
|
||||
validations:
|
||||
required: true
|
||||
51
.github/ISSUE_TEMPLATE/01_git_bug_report.yml
vendored
51
.github/ISSUE_TEMPLATE/01_git_bug_report.yml
vendored
@@ -1,51 +0,0 @@
|
||||
name: Git Bug Report
|
||||
description: There is a bug related to Git features in Zed
|
||||
type: "Bug"
|
||||
labels: ["git"]
|
||||
title: "Git: <a short description of the Git bug>"
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Summary
|
||||
description: Describe the bug with a one line summary, and provide detailed reproduction steps
|
||||
value: |
|
||||
<!-- Please insert a one line summary of the issue below -->
|
||||
|
||||
<!-- Include all steps necessary to reproduce from a clean Zed installation. Be verbose -->
|
||||
Steps to trigger the problem:
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
Actual Behavior:
|
||||
|
||||
Expected Behavior:
|
||||
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: environment
|
||||
attributes:
|
||||
label: Zed Version and System Specs
|
||||
description: 'Open Zed, and in the command palette select "zed: Copy System Specs Into Clipboard"'
|
||||
placeholder: |
|
||||
Output of "zed: Copy System Specs Into Clipboard"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: If applicable, attach your `~/Library/Logs/Zed/Zed.log` file to this issue.
|
||||
description: |
|
||||
macOS: `~/Library/Logs/Zed/Zed.log`
|
||||
Linux: `~/.local/share/zed/logs/Zed.log` or $XDG_DATA_HOME
|
||||
If you only need the most recent lines, you can run the `zed: open log` command palette action to see the last 1000.
|
||||
value: |
|
||||
<details><summary>Zed.log</summary>
|
||||
|
||||
<!-- Click below this line and paste or drag-and-drop your log-->
|
||||
```
|
||||
|
||||
```
|
||||
<!-- Click above this line and paste or drag-and-drop your log--></details>
|
||||
validations:
|
||||
required: false
|
||||
51
.github/ISSUE_TEMPLATE/02_agent_bug_report.yml
vendored
51
.github/ISSUE_TEMPLATE/02_agent_bug_report.yml
vendored
@@ -1,51 +0,0 @@
|
||||
name: Agent Panel Bug Report
|
||||
description: There is a bug related to the Agent Panel in Zed
|
||||
type: "Bug"
|
||||
labels: ["agent", "ai"]
|
||||
title: "Agent Panel: <a short description of the Agent Panel bug>"
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Summary
|
||||
description: Describe the bug with a one line summary, and provide detailed reproduction steps
|
||||
value: |
|
||||
<!-- Please insert a one line summary of the issue below -->
|
||||
|
||||
<!-- Include all steps necessary to reproduce from a clean Zed installation. Be verbose -->
|
||||
Steps to trigger the problem:
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
Actual Behavior:
|
||||
|
||||
Expected Behavior:
|
||||
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: environment
|
||||
attributes:
|
||||
label: Zed Version and System Specs
|
||||
description: 'Open Zed, and in the command palette select "zed: Copy System Specs Into Clipboard"'
|
||||
placeholder: |
|
||||
Output of "zed: Copy System Specs Into Clipboard"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: If applicable, attach your `~/Library/Logs/Zed/Zed.log` file to this issue.
|
||||
description: |
|
||||
macOS: `~/Library/Logs/Zed/Zed.log`
|
||||
Linux: `~/.local/share/zed/logs/Zed.log` or $XDG_DATA_HOME
|
||||
If you only need the most recent lines, you can run the `zed: open log` command palette action to see the last 1000.
|
||||
value: |
|
||||
<details><summary>Zed.log</summary>
|
||||
|
||||
<!-- Click below this line and paste or drag-and-drop your log-->
|
||||
```
|
||||
|
||||
```
|
||||
<!-- Click above this line and paste or drag-and-drop your log--></details>
|
||||
validations:
|
||||
required: false
|
||||
@@ -1,5 +1,5 @@
|
||||
name: Edit Predictions Bug Report
|
||||
description: There is a bug related to Edit Predictions in Zed
|
||||
name: Bug Report (Edit Predictions)
|
||||
description: Zed Edit Predictions bugs
|
||||
type: "Bug"
|
||||
labels: ["ai", "inline completion", "zeta"]
|
||||
title: "Edit Predictions: <a short description of the Edit Prediction bug>"
|
||||
@@ -10,19 +10,21 @@ body:
|
||||
description: Describe the bug with a one line summary, and provide detailed reproduction steps
|
||||
value: |
|
||||
<!-- Please insert a one line summary of the issue below -->
|
||||
SUMMARY_SENTENCE_HERE
|
||||
|
||||
<!-- Include all steps necessary to reproduce from a clean Zed installation. Be verbose -->
|
||||
### Description
|
||||
<!-- Describe with sufficient detail to reproduce from a clean Zed install. -->
|
||||
<!-- Please include the LLM provider and model name you are using -->
|
||||
Steps to trigger the problem:
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
Actual Behavior:
|
||||
|
||||
Expected Behavior:
|
||||
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: environment
|
||||
attributes:
|
||||
@@ -32,20 +34,3 @@ body:
|
||||
Output of "zed: Copy System Specs Into Clipboard"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: If applicable, attach your `~/Library/Logs/Zed/Zed.log` file to this issue.
|
||||
description: |
|
||||
macOS: `~/Library/Logs/Zed/Zed.log`
|
||||
Linux: `~/.local/share/zed/logs/Zed.log` or $XDG_DATA_HOME
|
||||
If you only need the most recent lines, you can run the `zed: open log` command palette action to see the last 1000.
|
||||
value: |
|
||||
<details><summary>Zed.log</summary>
|
||||
|
||||
<!-- Click below this line and paste or drag-and-drop your log-->
|
||||
```
|
||||
|
||||
```
|
||||
<!-- Click above this line and paste or drag-and-drop your log--></details>
|
||||
validations:
|
||||
required: false
|
||||
35
.github/ISSUE_TEMPLATE/03_bug_git.yml
vendored
Normal file
35
.github/ISSUE_TEMPLATE/03_bug_git.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
name: Bug Report (Git)
|
||||
description: Zed Git-Related Bugs
|
||||
type: "Bug"
|
||||
labels: ["git"]
|
||||
title: "Git: <a short description of the Git bug>"
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Summary
|
||||
description: Describe the bug with a one line summary, and provide detailed reproduction steps
|
||||
value: |
|
||||
<!-- Please insert a one line summary of the issue below -->
|
||||
SUMMARY_SENTENCE_HERE
|
||||
|
||||
### Description
|
||||
<!-- Describe with sufficient detail to reproduce from a clean Zed install. -->
|
||||
Steps to trigger the problem:
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
Actual Behavior:
|
||||
Expected Behavior:
|
||||
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: environment
|
||||
attributes:
|
||||
label: Zed Version and System Specs
|
||||
description: 'Open Zed, and in the command palette select "zed: Copy System Specs Into Clipboard"'
|
||||
placeholder: |
|
||||
Output of "zed: Copy System Specs Into Clipboard"
|
||||
validations:
|
||||
required: true
|
||||
47
.github/ISSUE_TEMPLATE/10_bug_report.yml
vendored
47
.github/ISSUE_TEMPLATE/10_bug_report.yml
vendored
@@ -1,46 +1,44 @@
|
||||
name: Bug Report
|
||||
name: Bug Report (Other)
|
||||
description: |
|
||||
Something is broken in Zed (exclude crashing).
|
||||
Something else is broken in Zed (exclude crashing).
|
||||
type: "Bug"
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Summary
|
||||
description: Describe the bug with a one line summary, and provide detailed reproduction steps
|
||||
description: Provide a one sentence summary and detailed reproduction steps
|
||||
value: |
|
||||
<!-- Please insert a one line summary of the issue below -->
|
||||
|
||||
<!-- Begin your issue with a one sentence summary -->
|
||||
SUMMARY_SENTENCE_HERE
|
||||
|
||||
<!-- Be verbose: Include all steps necessary to reproduce from a clean Zed installation. -->
|
||||
<!-- Code snippets are better than images, a repository link that reproduces the issue is ideal. -->
|
||||
### Description
|
||||
<!-- Describe with sufficient detail to reproduce from a clean Zed install.
|
||||
- Any code must be sufficient to reproduce (include context!)
|
||||
- Code must as text, not just as a screenshot.
|
||||
- Issues with insufficient detail may be summarily closed.
|
||||
-->
|
||||
|
||||
Steps to trigger the problem:
|
||||
Steps to reproduce:
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
Expected Behavior:
|
||||
Actual Behavior:
|
||||
|
||||
Expected Behavior:
|
||||
<!-- Before Submitting, did you:
|
||||
1. Include settings.json, keymap.json, .editorconfig if relevant?
|
||||
2. Check your Zed.log for relevant errors? (please include!)
|
||||
3. Click Preview to ensure everything looks right?
|
||||
4. Hide videos, large images and logs in ``` inside collapsible blocks:
|
||||
|
||||
<!--
|
||||
Is there anything additional necessary to reproduce this issue?
|
||||
- settings.json, keymap.json, .editorconfig etc?
|
||||
- Does it happen intermittently or only with specific projects / file types?
|
||||
- Have you found a workaround?
|
||||
<details><summary>click to expand</summary>
|
||||
|
||||
Did you check your Zed.log to see if there is any relevant details there?
|
||||
- When including large items (videos, screenshots, logs, configs) please wrap with:
|
||||
```json
|
||||
|
||||
<details><summary>See inside for XXXXYYY</summary>
|
||||
|
||||
```shell
|
||||
code
|
||||
```
|
||||
|
||||
</details>
|
||||
```
|
||||
</details>
|
||||
-->
|
||||
|
||||
validations:
|
||||
@@ -50,7 +48,8 @@ body:
|
||||
id: environment
|
||||
attributes:
|
||||
label: Zed Version and System Specs
|
||||
description: 'Open Zed, and in the command palette select "zed: Copy System Specs Into Clipboard"'
|
||||
description: |
|
||||
Open Zed, from the command palette select "zed: Copy System Specs Into Clipboard"
|
||||
placeholder: |
|
||||
Output of "zed: Copy System Specs Into Clipboard"
|
||||
validations:
|
||||
|
||||
14
.github/ISSUE_TEMPLATE/11_crash_report.yml
vendored
14
.github/ISSUE_TEMPLATE/11_crash_report.yml
vendored
@@ -5,10 +5,12 @@ body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Summary
|
||||
description: Describe the bug with a one line summary, and provide detailed reproduction steps
|
||||
description: Summarize the issue with detailed reproduction steps
|
||||
value: |
|
||||
<!-- Please insert a one line summary of the issue below -->
|
||||
<!-- Begin your issue with a one sentence summary -->
|
||||
SUMMARY_SENTENCE_HERE
|
||||
|
||||
### Description
|
||||
<!-- Include all steps necessary to reproduce from a clean Zed installation. Be verbose -->
|
||||
Steps to trigger the problem:
|
||||
1.
|
||||
@@ -16,7 +18,6 @@ body:
|
||||
3.
|
||||
|
||||
Actual Behavior:
|
||||
|
||||
Expected Behavior:
|
||||
|
||||
validations:
|
||||
@@ -40,10 +41,11 @@ body:
|
||||
value: |
|
||||
<details><summary>Zed.log</summary>
|
||||
|
||||
<!-- Click below this line and paste or drag-and-drop your log-->
|
||||
```
|
||||
<!-- Paste your log inside the code block. -->
|
||||
```log
|
||||
|
||||
```
|
||||
<!-- Click above this line and paste or drag-and-drop your log--></details>
|
||||
|
||||
</details>
|
||||
validations:
|
||||
required: false
|
||||
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
5
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -4,9 +4,6 @@ contact_links:
|
||||
- name: Feature Request
|
||||
url: https://github.com/zed-industries/zed/discussions/new/choose
|
||||
about: To request a feature, open a new Discussion in one of the appropriate Discussion categories
|
||||
- name: Zed Discussion Forum
|
||||
url: https://github.com/zed-industries/zed/discussions
|
||||
about: A community discussion forum
|
||||
- name: "Zed Discord: #Support Channel"
|
||||
- name: "Zed Discord"
|
||||
url: https://zed.dev/community-links
|
||||
about: Real-time discussion and user support
|
||||
|
||||
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@@ -114,7 +114,9 @@ jobs:
|
||||
timeout-minutes: 60
|
||||
name: Check workspace-hack crate
|
||||
needs: [job_spec]
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
if: |
|
||||
github.repository_owner == 'zed-industries' &&
|
||||
needs.job_spec.outputs.run_tests == 'true'
|
||||
runs-on:
|
||||
- buildjet-8vcpu-ubuntu-2204
|
||||
steps:
|
||||
|
||||
195
Cargo.lock
generated
195
Cargo.lock
generated
@@ -746,7 +746,6 @@ dependencies = [
|
||||
"itertools 0.14.0",
|
||||
"language",
|
||||
"language_model",
|
||||
"lsp",
|
||||
"open",
|
||||
"project",
|
||||
"rand 0.8.5",
|
||||
@@ -913,18 +912,6 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-native-tls"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9343dc5acf07e79ff82d0c37899f079db3534d99f189a1837c8e549c99405bec"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"native-tls",
|
||||
"thiserror 1.0.69",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-net"
|
||||
version = "2.0.0"
|
||||
@@ -1094,18 +1081,6 @@ version = "4.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
|
||||
|
||||
[[package]]
|
||||
name = "async-tls"
|
||||
version = "0.13.0"
|
||||
source = "git+https://github.com/zed-industries/async-tls?rev=1e759a4b5e370f87dc15e40756ac4f8815b61d9d#1e759a4b5e370f87dc15e40756ac4f8815b61d9d"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"rustls 0.23.25",
|
||||
"rustls-pemfile 2.2.0",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.88"
|
||||
@@ -1119,12 +1094,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-tungstenite"
|
||||
version = "0.28.2"
|
||||
version = "0.29.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c348fb0b6d132c596eca3dcd941df48fb597aafcb07a738ec41c004b087dc99"
|
||||
checksum = "ef0f7efedeac57d9b26170f72965ecfd31473ca52ca7a64e925b0b6f5f079886"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"async-tls",
|
||||
"atomic-waker",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
@@ -1132,7 +1105,10 @@ dependencies = [
|
||||
"futures-util",
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
"tungstenite 0.24.0",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
"tokio-rustls 0.26.2",
|
||||
"tungstenite 0.26.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2838,7 +2814,6 @@ name = "client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-native-tls",
|
||||
"async-recursion 0.3.2",
|
||||
"async-tungstenite",
|
||||
"chrono",
|
||||
@@ -2849,6 +2824,7 @@ dependencies = [
|
||||
"feature_flags",
|
||||
"futures 0.3.31",
|
||||
"gpui",
|
||||
"gpui_tokio",
|
||||
"http_client",
|
||||
"http_client_tls",
|
||||
"log",
|
||||
@@ -2870,6 +2846,7 @@ dependencies = [
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
"tiny_http",
|
||||
"tokio",
|
||||
"tokio-socks",
|
||||
"url",
|
||||
"util",
|
||||
@@ -3352,6 +3329,15 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.8.0"
|
||||
@@ -4484,6 +4470,32 @@ dependencies = [
|
||||
"workspace-hack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "documented"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc6db32f0995bc4553d2de888999075acd0dbeef75ba923503f6a724263dc6f3"
|
||||
dependencies = [
|
||||
"documented-macros",
|
||||
"phf",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "documented-macros"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a394bb35929b58f9a5fd418f7c6b17a4b616efcc1e53e6995ca123948f87e5fa"
|
||||
dependencies = [
|
||||
"convert_case 0.6.0",
|
||||
"itertools 0.13.0",
|
||||
"optfield",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strum",
|
||||
"syn 2.0.100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dotenvy"
|
||||
version = "0.15.7"
|
||||
@@ -7452,8 +7464,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "jupyter-protocol"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9ae6296f9476658b3550293c113996daf75fa542cd8d078abb4c60207bded14"
|
||||
source = "git+https://github.com/ConradIrwin/runtimed?rev=7130c804216b6914355d15d0b91ea91f6babd734#7130c804216b6914355d15d0b91ea91f6babd734"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -7468,8 +7479,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "jupyter-websocket-client"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49c1ba895c5271ff8dcae51c347fd3588905ba0025a57e20955fd231fe1228cc"
|
||||
source = "git+https://github.com/ConradIrwin/runtimed?rev=7130c804216b6914355d15d0b91ea91f6babd734#7130c804216b6914355d15d0b91ea91f6babd734"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -7900,7 +7910,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.48.5",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8776,8 +8786,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "nbformat"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "244c1673f02b4d5f3c51b6f8ed28d57182cb166a50a6dbf651a3d53e23dc81c0"
|
||||
source = "git+https://github.com/ConradIrwin/runtimed?rev=7130c804216b6914355d15d0b91ea91f6babd734#7130c804216b6914355d15d0b91ea91f6babd734"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@@ -9556,15 +9565,6 @@ version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "300.4.2+3.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "168ce4e058f975fe43e89d9ccf78ca668601887ae736090aacc23ae353c298e2"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.107"
|
||||
@@ -9573,11 +9573,21 @@ checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"openssl-src",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "optfield"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa59f025cde9c698fcb4fcb3533db4621795374065bee908215263488f2d2a1d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "option-ext"
|
||||
version = "0.2.0"
|
||||
@@ -9997,7 +10007,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0#1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=845945b830297a50de0e24020b980a65e4820559#845945b830297a50de0e24020b980a65e4820559"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"env_logger 0.10.2",
|
||||
@@ -10034,7 +10044,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-conda"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0#1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=845945b830297a50de0e24020b980a65e4820559#845945b830297a50de0e24020b980a65e4820559"
|
||||
dependencies = [
|
||||
"env_logger 0.10.2",
|
||||
"lazy_static",
|
||||
@@ -10053,7 +10063,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-core"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0#1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=845945b830297a50de0e24020b980a65e4820559#845945b830297a50de0e24020b980a65e4820559"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"lazy_static",
|
||||
@@ -10068,7 +10078,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-env-var-path"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0#1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=845945b830297a50de0e24020b980a65e4820559#845945b830297a50de0e24020b980a65e4820559"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
@@ -10084,7 +10094,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-fs"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0#1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=845945b830297a50de0e24020b980a65e4820559#845945b830297a50de0e24020b980a65e4820559"
|
||||
dependencies = [
|
||||
"log",
|
||||
"msvc_spectre_libs",
|
||||
@@ -10093,7 +10103,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-global-virtualenvs"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0#1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=845945b830297a50de0e24020b980a65e4820559#845945b830297a50de0e24020b980a65e4820559"
|
||||
dependencies = [
|
||||
"log",
|
||||
"msvc_spectre_libs",
|
||||
@@ -10106,7 +10116,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-homebrew"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0#1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=845945b830297a50de0e24020b980a65e4820559#845945b830297a50de0e24020b980a65e4820559"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
@@ -10124,7 +10134,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-jsonrpc"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0#1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=845945b830297a50de0e24020b980a65e4820559#845945b830297a50de0e24020b980a65e4820559"
|
||||
dependencies = [
|
||||
"env_logger 0.10.2",
|
||||
"log",
|
||||
@@ -10137,7 +10147,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-linux-global-python"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0#1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=845945b830297a50de0e24020b980a65e4820559#845945b830297a50de0e24020b980a65e4820559"
|
||||
dependencies = [
|
||||
"log",
|
||||
"msvc_spectre_libs",
|
||||
@@ -10150,7 +10160,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-mac-commandlinetools"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0#1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=845945b830297a50de0e24020b980a65e4820559#845945b830297a50de0e24020b980a65e4820559"
|
||||
dependencies = [
|
||||
"log",
|
||||
"msvc_spectre_libs",
|
||||
@@ -10163,7 +10173,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-mac-python-org"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0#1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=845945b830297a50de0e24020b980a65e4820559#845945b830297a50de0e24020b980a65e4820559"
|
||||
dependencies = [
|
||||
"log",
|
||||
"msvc_spectre_libs",
|
||||
@@ -10176,7 +10186,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-mac-xcode"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0#1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=845945b830297a50de0e24020b980a65e4820559#845945b830297a50de0e24020b980a65e4820559"
|
||||
dependencies = [
|
||||
"log",
|
||||
"msvc_spectre_libs",
|
||||
@@ -10189,7 +10199,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-pipenv"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0#1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=845945b830297a50de0e24020b980a65e4820559#845945b830297a50de0e24020b980a65e4820559"
|
||||
dependencies = [
|
||||
"log",
|
||||
"msvc_spectre_libs",
|
||||
@@ -10202,7 +10212,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-pixi"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0#1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=845945b830297a50de0e24020b980a65e4820559#845945b830297a50de0e24020b980a65e4820559"
|
||||
dependencies = [
|
||||
"log",
|
||||
"msvc_spectre_libs",
|
||||
@@ -10214,7 +10224,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-poetry"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0#1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=845945b830297a50de0e24020b980a65e4820559#845945b830297a50de0e24020b980a65e4820559"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"lazy_static",
|
||||
@@ -10235,7 +10245,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-pyenv"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0#1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=845945b830297a50de0e24020b980a65e4820559#845945b830297a50de0e24020b980a65e4820559"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
@@ -10253,7 +10263,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-python-utils"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0#1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=845945b830297a50de0e24020b980a65e4820559#845945b830297a50de0e24020b980a65e4820559"
|
||||
dependencies = [
|
||||
"env_logger 0.10.2",
|
||||
"lazy_static",
|
||||
@@ -10270,7 +10280,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-reporter"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0#1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=845945b830297a50de0e24020b980a65e4820559#845945b830297a50de0e24020b980a65e4820559"
|
||||
dependencies = [
|
||||
"env_logger 0.10.2",
|
||||
"log",
|
||||
@@ -10284,7 +10294,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-telemetry"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0#1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=845945b830297a50de0e24020b980a65e4820559#845945b830297a50de0e24020b980a65e4820559"
|
||||
dependencies = [
|
||||
"env_logger 0.10.2",
|
||||
"lazy_static",
|
||||
@@ -10299,7 +10309,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-venv"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0#1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=845945b830297a50de0e24020b980a65e4820559#845945b830297a50de0e24020b980a65e4820559"
|
||||
dependencies = [
|
||||
"log",
|
||||
"msvc_spectre_libs",
|
||||
@@ -10311,7 +10321,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-virtualenv"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0#1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=845945b830297a50de0e24020b980a65e4820559#845945b830297a50de0e24020b980a65e4820559"
|
||||
dependencies = [
|
||||
"log",
|
||||
"msvc_spectre_libs",
|
||||
@@ -10323,7 +10333,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-virtualenvwrapper"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0#1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=845945b830297a50de0e24020b980a65e4820559#845945b830297a50de0e24020b980a65e4820559"
|
||||
dependencies = [
|
||||
"log",
|
||||
"msvc_spectre_libs",
|
||||
@@ -10336,7 +10346,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-windows-registry"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0#1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=845945b830297a50de0e24020b980a65e4820559#845945b830297a50de0e24020b980a65e4820559"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
@@ -10348,13 +10358,13 @@ dependencies = [
|
||||
"pet-virtualenv",
|
||||
"pet-windows-store",
|
||||
"regex",
|
||||
"winreg 0.52.0",
|
||||
"winreg 0.55.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pet-windows-store"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0#1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=845945b830297a50de0e24020b980a65e4820559#845945b830297a50de0e24020b980a65e4820559"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
@@ -10364,7 +10374,7 @@ dependencies = [
|
||||
"pet-python-utils",
|
||||
"pet-virtualenv",
|
||||
"regex",
|
||||
"winreg 0.52.0",
|
||||
"winreg 0.55.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -12102,8 +12112,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "runtimelib"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9af6ed9fd10d7ee940676945510c197c2a472806bb652096a713985c44ffd643"
|
||||
source = "git+https://github.com/ConradIrwin/runtimed?rev=7130c804216b6914355d15d0b91ea91f6babd734#7130c804216b6914355d15d0b91ea91f6babd734"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-dispatcher",
|
||||
@@ -14164,12 +14173,14 @@ name = "tasks_ui"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collections",
|
||||
"debugger_ui",
|
||||
"editor",
|
||||
"feature_flags",
|
||||
"file_icons",
|
||||
"fuzzy",
|
||||
"gpui",
|
||||
"itertools 0.14.0",
|
||||
"language",
|
||||
"menu",
|
||||
"picker",
|
||||
@@ -15330,24 +15341,6 @@ dependencies = [
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"bytes 1.10.1",
|
||||
"data-encoding",
|
||||
"http 1.3.1",
|
||||
"httparse",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"sha1",
|
||||
"thiserror 1.0.69",
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.26.2"
|
||||
@@ -15402,6 +15395,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"component",
|
||||
"documented",
|
||||
"gpui",
|
||||
"icons",
|
||||
"itertools 0.14.0",
|
||||
@@ -17251,6 +17245,16 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.55.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winresource"
|
||||
version = "0.1.20"
|
||||
@@ -17657,6 +17661,7 @@ dependencies = [
|
||||
"indexmap",
|
||||
"inout",
|
||||
"itertools 0.12.1",
|
||||
"itertools 0.13.0",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"libsqlite3-sys",
|
||||
@@ -17701,7 +17706,6 @@ dependencies = [
|
||||
"scopeguard",
|
||||
"sea-orm",
|
||||
"sea-query-binder",
|
||||
"security-framework 2.11.1",
|
||||
"security-framework 3.2.0",
|
||||
"security-framework-sys",
|
||||
"semver",
|
||||
@@ -17732,6 +17736,7 @@ dependencies = [
|
||||
"toml_edit",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
"tungstenite 0.26.2",
|
||||
"unicode-properties",
|
||||
"url",
|
||||
"uuid",
|
||||
|
||||
25
Cargo.toml
25
Cargo.toml
@@ -396,7 +396,7 @@ async-pipe = { git = "https://github.com/zed-industries/async-pipe-rs", rev = "8
|
||||
async-recursion = "1.0.0"
|
||||
async-tar = "0.5.0"
|
||||
async-trait = "0.1"
|
||||
async-tungstenite = "0.28"
|
||||
async-tungstenite = "0.29.1"
|
||||
async-watch = "0.3.1"
|
||||
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
|
||||
aws-config = { version = "1.6.1", features = ["behavior-version-latest"] }
|
||||
@@ -453,8 +453,8 @@ indoc = "2"
|
||||
inventory = "0.3.19"
|
||||
itertools = "0.14.0"
|
||||
jsonwebtoken = "9.3"
|
||||
jupyter-protocol = { version = "0.6.0" }
|
||||
jupyter-websocket-client = { version = "0.9.0" }
|
||||
jupyter-protocol = { git = "https://github.com/ConradIrwin/runtimed", rev = "7130c804216b6914355d15d0b91ea91f6babd734" }
|
||||
jupyter-websocket-client = { git = "https://github.com/ConradIrwin/runtimed" ,rev = "7130c804216b6914355d15d0b91ea91f6babd734" }
|
||||
libc = "0.2"
|
||||
libsqlite3-sys = { version = "0.30.1", features = ["bundled"] }
|
||||
linkify = "0.10.0"
|
||||
@@ -463,7 +463,7 @@ log = { version = "0.4.16", features = ["kv_unstable_serde", "serde"] }
|
||||
markup5ever_rcdom = "0.3.0"
|
||||
mlua = { version = "0.10", features = ["lua54", "vendored", "async", "send"] }
|
||||
nanoid = "0.4"
|
||||
nbformat = { version = "0.10.0" }
|
||||
nbformat = { git = "https://github.com/ConradIrwin/runtimed", rev = "7130c804216b6914355d15d0b91ea91f6babd734" }
|
||||
nix = "0.29"
|
||||
objc = "0.2"
|
||||
open = "5.0.0"
|
||||
@@ -472,13 +472,13 @@ ordered-float = "2.1.1"
|
||||
palette = { version = "0.7.5", default-features = false, features = ["std"] }
|
||||
parking_lot = "0.12.1"
|
||||
pathdiff = "0.2"
|
||||
pet = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0" }
|
||||
pet-fs = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0" }
|
||||
pet-pixi = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0" }
|
||||
pet-conda = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0" }
|
||||
pet-core = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0" }
|
||||
pet-poetry = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0" }
|
||||
pet-reporter = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0" }
|
||||
pet = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
|
||||
pet-fs = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
|
||||
pet-pixi = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
|
||||
pet-conda = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
|
||||
pet-core = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
|
||||
pet-poetry = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
|
||||
pet-reporter = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
|
||||
postage = { version = "0.5", features = ["futures-traits"] }
|
||||
pretty_assertions = { version = "1.3.0", features = ["unstable"] }
|
||||
proc-macro2 = "1.0.93"
|
||||
@@ -501,7 +501,7 @@ reqwest = { git = "https://github.com/zed-industries/reqwest.git", rev = "fd110f
|
||||
"stream",
|
||||
] }
|
||||
rsa = "0.9.6"
|
||||
runtimelib = { version = "0.25.0", default-features = false, features = [
|
||||
runtimelib = { git = "https://github.com/ConradIrwin/runtimed", rev = "7130c804216b6914355d15d0b91ea91f6babd734", default-features = false, features = [
|
||||
"async-dispatcher-runtime",
|
||||
] }
|
||||
rustc-demangle = "0.1.23"
|
||||
@@ -661,7 +661,6 @@ features = [
|
||||
# TODO livekit https://github.com/RustAudio/cpal/pull/891
|
||||
[patch.crates-io]
|
||||
cpal = { git = "https://github.com/zed-industries/cpal", rev = "fd8bc2fd39f1f5fdee5a0690656caff9a26d9d50" }
|
||||
real-async-tls = { git = "https://github.com/zed-industries/async-tls", rev = "1e759a4b5e370f87dc15e40756ac4f8815b61d9d", package = "async-tls" }
|
||||
notify = { git = "https://github.com/zed-industries/notify.git", rev = "bbb9ea5ae52b253e095737847e367c30653a2e96" }
|
||||
notify-types = { git = "https://github.com/zed-industries/notify.git", rev = "bbb9ea5ae52b253e095737847e367c30653a2e96" }
|
||||
|
||||
|
||||
@@ -532,6 +532,7 @@
|
||||
"context": "Editor && showing_completions",
|
||||
"bindings": {
|
||||
"enter": "editor::ConfirmCompletion",
|
||||
"shift-enter": "editor::ConfirmCompletionReplace",
|
||||
"tab": "editor::ComposeCompletion"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -525,7 +525,7 @@
|
||||
"cmd-k cmd-9": ["editor::FoldAtLevel", 9],
|
||||
"cmd-k cmd-0": "editor::FoldAll",
|
||||
"cmd-k cmd-j": "editor::UnfoldAll",
|
||||
// Using `ctrl-space` in Zed requires disabling the macOS global shortcut.
|
||||
// Using `ctrl-space` / `ctrl-shift-space` in Zed requires disabling the macOS global shortcut.
|
||||
// System Preferences->Keyboard->Keyboard Shortcuts->Input Sources->Select the previous input source (uncheck)
|
||||
"ctrl-space": "editor::ShowCompletions",
|
||||
"ctrl-shift-space": "editor::ShowWordCompletions",
|
||||
@@ -681,6 +681,7 @@
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"enter": "editor::ConfirmCompletion",
|
||||
"shift-enter": "editor::ConfirmCompletionReplace",
|
||||
"tab": "editor::ComposeCompletion"
|
||||
}
|
||||
},
|
||||
@@ -1003,6 +1004,8 @@
|
||||
"cmd-home": "terminal::ScrollToTop",
|
||||
"shift-end": "terminal::ScrollToBottom",
|
||||
"cmd-end": "terminal::ScrollToBottom",
|
||||
// Using `ctrl-shift-space` in Zed requires disabling the macOS global shortcut.
|
||||
// System Preferences->Keyboard->Keyboard Shortcuts->Input Sources->Select the previous input source (uncheck)
|
||||
"ctrl-shift-space": "terminal::ToggleViMode",
|
||||
"ctrl-k up": "pane::SplitUp",
|
||||
"ctrl-k down": "pane::SplitDown",
|
||||
|
||||
@@ -44,6 +44,12 @@
|
||||
"[ /": "vim::PreviousComment",
|
||||
"] *": "vim::NextComment",
|
||||
"] /": "vim::NextComment",
|
||||
"[ -": "vim::PreviousLesserIndent",
|
||||
"[ +": "vim::PreviousGreaterIndent",
|
||||
"[ =": "vim::PreviousSameIndent",
|
||||
"] -": "vim::NextLesserIndent",
|
||||
"] +": "vim::NextGreaterIndent",
|
||||
"] =": "vim::NextSameIndent",
|
||||
// Word motions
|
||||
"w": "vim::NextWordStart",
|
||||
"e": "vim::NextWordEnd",
|
||||
|
||||
@@ -6,9 +6,18 @@ You are an AI assistant integrated into a code editor. You have the programming
|
||||
It will be up to you to decide which of these you are doing based on what the user has told you. When unclear, ask clarifying questions to understand the user's intent before proceeding.
|
||||
|
||||
You should only perform actions that modify the user's system if explicitly requested by the user:
|
||||
- If the user asks a question about how to accomplish a task, provide guidance or information, and use read-only tools (e.g., search) to assist. You may suggest potential actions, but do not directly modify the user’s system without explicit instruction.
|
||||
- If the user asks a question about how to accomplish a task, provide guidance or information, and use read-only tools (e.g., search) to assist. You may suggest potential actions, but do not directly modify the user's system without explicit instruction.
|
||||
- If the user clearly requests that you perform an action, carry out the action directly without explaining why you are doing so.
|
||||
|
||||
When answering questions, it's okay to give incomplete examples containing comments about what would go there in a real version. When being asked to directly perform tasks on the code base, you must ALWAYS make fully working code. You may never "simplify" the code by omitting or deleting functionality you know the user has requested, and you must NEVER write comments like "in a full version, this would..." - instead, you must actually implement the real version. Don't be lazy!
|
||||
|
||||
Note that project files are automatically backed up. The user can always get them back later if anything goes wrong, so there's
|
||||
no need to create backup files (e.g. `.bak` files) because these files will just take up unnecessary space on the user's disk.
|
||||
|
||||
When attempting to resolve issues around failing tests, never simply remove the failing tests. Unless the user explicitly asks you to remove tests, ALWAYS attempt to fix the code causing the tests to fail.
|
||||
|
||||
Ignore "TODO"-type comments unless they're relevant to the user's explicit request or the user specifically asks you to address them. It is, however, okay to include them in codebase summaries.
|
||||
|
||||
<style>
|
||||
Editing code:
|
||||
- Make sure to take previous edits into account.
|
||||
|
||||
@@ -1136,7 +1136,8 @@
|
||||
"code_actions_on_format": {},
|
||||
// Settings related to running tasks.
|
||||
"tasks": {
|
||||
"variables": {}
|
||||
"variables": {},
|
||||
"enabled": true
|
||||
},
|
||||
// An object whose keys are language names, and whose values
|
||||
// are arrays of filenames or extensions of files that should
|
||||
@@ -1456,6 +1457,8 @@
|
||||
"lsp": {
|
||||
// Specify the LSP name as a key here.
|
||||
// "rust-analyzer": {
|
||||
// // A special flag for rust-analyzer integration, to use server-provided tasks
|
||||
// enable_lsp_tasks": true,
|
||||
// // These initialization options are merged into Zed's defaults
|
||||
// "initialization_options": {
|
||||
// "check": {
|
||||
|
||||
@@ -25,6 +25,7 @@ use language_model::{ConfiguredModel, LanguageModelRegistry, LanguageModelToolUs
|
||||
use markdown::parser::CodeBlockKind;
|
||||
use markdown::{Markdown, MarkdownElement, MarkdownStyle, ParsedMarkdown, without_fences};
|
||||
use project::ProjectItem as _;
|
||||
use rope::Point;
|
||||
use settings::{Settings as _, update_settings_file};
|
||||
use std::ops::Range;
|
||||
use std::path::Path;
|
||||
@@ -58,7 +59,7 @@ pub struct ActiveThread {
|
||||
expanded_thinking_segments: HashMap<(MessageId, usize), bool>,
|
||||
last_error: Option<ThreadError>,
|
||||
notifications: Vec<WindowHandle<AgentNotification>>,
|
||||
copied_code_block_ids: HashSet<usize>,
|
||||
copied_code_block_ids: HashSet<(MessageId, usize)>,
|
||||
_subscriptions: Vec<Subscription>,
|
||||
notification_subscriptions: HashMap<WindowHandle<AgentNotification>, Vec<Subscription>>,
|
||||
feedback_message_editor: Option<Entity<Editor>>,
|
||||
@@ -289,7 +290,8 @@ fn tool_use_markdown_style(window: &Window, cx: &mut App) -> MarkdownStyle {
|
||||
}
|
||||
|
||||
fn render_markdown_code_block(
|
||||
id: usize,
|
||||
message_id: MessageId,
|
||||
ix: usize,
|
||||
kind: &CodeBlockKind,
|
||||
parsed_markdown: &ParsedMarkdown,
|
||||
codeblock_range: Range<usize>,
|
||||
@@ -366,7 +368,7 @@ fn render_markdown_code_block(
|
||||
};
|
||||
|
||||
h_flex()
|
||||
.id(("code-block-header-label", id))
|
||||
.id(("code-block-header-label", ix))
|
||||
.w_full()
|
||||
.max_w_full()
|
||||
.px_1()
|
||||
@@ -397,8 +399,40 @@ fn render_markdown_code_block(
|
||||
.read(cx)
|
||||
.find_project_path(&path_range.path, cx)
|
||||
{
|
||||
workspace
|
||||
.open_path(project_path, None, true, window, cx)
|
||||
let target = path_range.range.as_ref().map(|range| {
|
||||
Point::new(
|
||||
// Line number is 1-based
|
||||
range.start.line.saturating_sub(1),
|
||||
range.start.col.unwrap_or(0),
|
||||
)
|
||||
});
|
||||
let open_task = workspace.open_path(
|
||||
project_path,
|
||||
None,
|
||||
true,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
window
|
||||
.spawn(cx, async move |cx| {
|
||||
let item = open_task.await?;
|
||||
if let Some(target) = target {
|
||||
if let Some(active_editor) =
|
||||
item.downcast::<Editor>()
|
||||
{
|
||||
active_editor
|
||||
.downgrade()
|
||||
.update_in(cx, |editor, window, cx| {
|
||||
editor
|
||||
.go_to_singleton_buffer_point(
|
||||
target, window, cx,
|
||||
);
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
}
|
||||
@@ -416,7 +450,10 @@ fn render_markdown_code_block(
|
||||
.element_background
|
||||
.blend(cx.theme().colors().editor_foreground.opacity(0.01));
|
||||
|
||||
let codeblock_was_copied = active_thread.read(cx).copied_code_block_ids.contains(&id);
|
||||
let codeblock_was_copied = active_thread
|
||||
.read(cx)
|
||||
.copied_code_block_ids
|
||||
.contains(&(message_id, ix));
|
||||
|
||||
let codeblock_header = h_flex()
|
||||
.p_1()
|
||||
@@ -429,7 +466,7 @@ fn render_markdown_code_block(
|
||||
.children(label)
|
||||
.child(
|
||||
IconButton::new(
|
||||
("copy-markdown-code", id),
|
||||
("copy-markdown-code", ix),
|
||||
if codeblock_was_copied {
|
||||
IconName::Check
|
||||
} else {
|
||||
@@ -444,7 +481,7 @@ fn render_markdown_code_block(
|
||||
let parsed_markdown = parsed_markdown.clone();
|
||||
move |_event, _window, cx| {
|
||||
active_thread.update(cx, |this, cx| {
|
||||
this.copied_code_block_ids.insert(id);
|
||||
this.copied_code_block_ids.insert((message_id, ix));
|
||||
|
||||
let code =
|
||||
without_fences(&parsed_markdown.source()[codeblock_range.clone()])
|
||||
@@ -457,7 +494,7 @@ fn render_markdown_code_block(
|
||||
|
||||
cx.update(|cx| {
|
||||
this.update(cx, |this, cx| {
|
||||
this.copied_code_block_ids.remove(&id);
|
||||
this.copied_code_block_ids.remove(&(message_id, ix));
|
||||
cx.notify();
|
||||
})
|
||||
})
|
||||
@@ -1162,16 +1199,62 @@ impl ActiveThread {
|
||||
|
||||
let context_store = self.context_store.clone();
|
||||
let workspace = self.workspace.clone();
|
||||
|
||||
let thread = self.thread.read(cx);
|
||||
|
||||
// Get all the data we need from thread before we start using it in closures
|
||||
let checkpoint = thread.checkpoint_for_message(message_id);
|
||||
let context = thread.context_for_message(message_id).collect::<Vec<_>>();
|
||||
|
||||
let tool_uses = thread.tool_uses_for_message(message_id, cx);
|
||||
let has_tool_uses = !tool_uses.is_empty();
|
||||
let is_generating = thread.is_generating();
|
||||
|
||||
let is_first_message = ix == 0;
|
||||
let is_last_message = ix == self.messages.len() - 1;
|
||||
let show_feedback = is_last_message && message.role != Role::User;
|
||||
|
||||
let needs_confirmation = tool_uses.iter().any(|tool_use| tool_use.needs_confirmation);
|
||||
|
||||
let generating_label = (is_generating && is_last_message).then(|| {
|
||||
Label::new("Generating")
|
||||
.color(Color::Muted)
|
||||
.size(LabelSize::Small)
|
||||
.with_animation(
|
||||
"generating-label",
|
||||
Animation::new(Duration::from_secs(1)).repeat(),
|
||||
|mut label, delta| {
|
||||
let text = match delta {
|
||||
d if d < 0.25 => "Generating",
|
||||
d if d < 0.5 => "Generating.",
|
||||
d if d < 0.75 => "Generating..",
|
||||
_ => "Generating...",
|
||||
};
|
||||
label.set_text(text);
|
||||
label
|
||||
},
|
||||
)
|
||||
.with_animation(
|
||||
"pulsating-label",
|
||||
Animation::new(Duration::from_secs(2))
|
||||
.repeat()
|
||||
.with_easing(pulsating_between(0.6, 1.)),
|
||||
|label, delta| label.map_element(|label| label.alpha(delta)),
|
||||
)
|
||||
});
|
||||
|
||||
// Don't render user messages that are just there for returning tool results.
|
||||
if message.role == Role::User && thread.message_has_tool_results(message_id) {
|
||||
if let Some(generating_label) = generating_label {
|
||||
return h_flex()
|
||||
.w_full()
|
||||
.h_10()
|
||||
.py_1p5()
|
||||
.pl_4()
|
||||
.pb_3()
|
||||
.child(generating_label)
|
||||
.into_any_element();
|
||||
}
|
||||
|
||||
return Empty.into_any();
|
||||
}
|
||||
|
||||
@@ -1183,9 +1266,6 @@ impl ActiveThread {
|
||||
.filter(|(id, _)| *id == message_id)
|
||||
.map(|(_, state)| state.editor.clone());
|
||||
|
||||
let first_message = ix == 0;
|
||||
let show_feedback = ix == self.messages.len() - 1 && message.role != Role::User;
|
||||
|
||||
let colors = cx.theme().colors();
|
||||
let active_color = colors.element_active;
|
||||
let editor_bg_color = colors.editor_background;
|
||||
@@ -1354,7 +1434,7 @@ impl ActiveThread {
|
||||
Role::User => v_flex()
|
||||
.id(("message-container", ix))
|
||||
.map(|this| {
|
||||
if first_message {
|
||||
if is_first_message {
|
||||
this.pt_2()
|
||||
} else {
|
||||
this.pt_4()
|
||||
@@ -1472,15 +1552,11 @@ impl ActiveThread {
|
||||
.border_l_1()
|
||||
.border_color(cx.theme().colors().border_variant)
|
||||
.children(message_content)
|
||||
.gap_2p5()
|
||||
.pb_2p5()
|
||||
.when(!tool_uses.is_empty(), |parent| {
|
||||
parent.child(
|
||||
div().children(
|
||||
tool_uses
|
||||
.into_iter()
|
||||
.map(|tool_use| self.render_tool_use(tool_use, window, cx)),
|
||||
),
|
||||
.when(has_tool_uses, |parent| {
|
||||
parent.children(
|
||||
tool_uses
|
||||
.into_iter()
|
||||
.map(|tool_use| self.render_tool_use(tool_use, window, cx)),
|
||||
)
|
||||
}),
|
||||
Role::System => div().id(("message-container", ix)).py_1().px_2().child(
|
||||
@@ -1493,9 +1569,6 @@ impl ActiveThread {
|
||||
|
||||
v_flex()
|
||||
.w_full()
|
||||
.when(first_message, |parent| {
|
||||
parent.child(self.render_rules_item(cx))
|
||||
})
|
||||
.when_some(checkpoint, |parent, checkpoint| {
|
||||
let mut is_pending = false;
|
||||
let mut error = None;
|
||||
@@ -1565,65 +1638,56 @@ impl ActiveThread {
|
||||
.child(ui::Divider::horizontal()),
|
||||
)
|
||||
})
|
||||
.when(is_first_message, |parent| {
|
||||
parent.child(self.render_rules_item(cx))
|
||||
})
|
||||
.child(styled_message)
|
||||
.when(
|
||||
show_feedback && !self.thread.read(cx).is_generating(),
|
||||
|parent| {
|
||||
parent.child(feedback_items).when_some(
|
||||
self.feedback_message_editor.clone(),
|
||||
|parent, feedback_editor| {
|
||||
let focus_handle = feedback_editor.focus_handle(cx);
|
||||
parent.child(
|
||||
v_flex()
|
||||
.key_context("AgentFeedbackMessageEditor")
|
||||
.on_action(cx.listener(|this, _: &menu::Cancel, _, cx| {
|
||||
this.feedback_message_editor = None;
|
||||
cx.notify();
|
||||
}))
|
||||
.on_action(cx.listener(|this, _: &menu::Confirm, _, cx| {
|
||||
this.submit_feedback_message(cx);
|
||||
cx.notify();
|
||||
}))
|
||||
.on_action(cx.listener(Self::confirm_editing_message))
|
||||
.mx_4()
|
||||
.mb_3()
|
||||
.p_2()
|
||||
.rounded_md()
|
||||
.border_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.child(feedback_editor)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.justify_end()
|
||||
.child(
|
||||
Button::new("dismiss-feedback-message", "Cancel")
|
||||
.label_size(LabelSize::Small)
|
||||
.key_binding(
|
||||
KeyBinding::for_action_in(
|
||||
&menu::Cancel,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
.map(|kb| kb.size(rems_from_px(10.))),
|
||||
)
|
||||
.on_click(cx.listener(|this, _, _, cx| {
|
||||
this.feedback_message_editor = None;
|
||||
cx.notify();
|
||||
})),
|
||||
)
|
||||
.child(
|
||||
Button::new(
|
||||
"submit-feedback-message",
|
||||
"Share Feedback",
|
||||
)
|
||||
.style(ButtonStyle::Tinted(ui::TintColor::Accent))
|
||||
.when(!needs_confirmation && generating_label.is_some(), |this| {
|
||||
this.child(
|
||||
h_flex()
|
||||
.h_8()
|
||||
.mt_2()
|
||||
.mb_4()
|
||||
.ml_4()
|
||||
.py_1p5()
|
||||
.child(generating_label.unwrap()),
|
||||
)
|
||||
})
|
||||
.when(show_feedback && !is_generating, |parent| {
|
||||
parent.child(feedback_items).when_some(
|
||||
self.feedback_message_editor.clone(),
|
||||
|parent, feedback_editor| {
|
||||
let focus_handle = feedback_editor.focus_handle(cx);
|
||||
parent.child(
|
||||
v_flex()
|
||||
.key_context("AgentFeedbackMessageEditor")
|
||||
.on_action(cx.listener(|this, _: &menu::Cancel, _, cx| {
|
||||
this.feedback_message_editor = None;
|
||||
cx.notify();
|
||||
}))
|
||||
.on_action(cx.listener(|this, _: &menu::Confirm, _, cx| {
|
||||
this.submit_feedback_message(cx);
|
||||
cx.notify();
|
||||
}))
|
||||
.on_action(cx.listener(Self::confirm_editing_message))
|
||||
.my_3()
|
||||
.mx_4()
|
||||
.p_2()
|
||||
.rounded_md()
|
||||
.border_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.child(feedback_editor)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.justify_end()
|
||||
.child(
|
||||
Button::new("dismiss-feedback-message", "Cancel")
|
||||
.label_size(LabelSize::Small)
|
||||
.key_binding(
|
||||
KeyBinding::for_action_in(
|
||||
&menu::Confirm,
|
||||
&menu::Cancel,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
@@ -1631,16 +1695,38 @@ impl ActiveThread {
|
||||
.map(|kb| kb.size(rems_from_px(10.))),
|
||||
)
|
||||
.on_click(cx.listener(|this, _, _, cx| {
|
||||
this.submit_feedback_message(cx);
|
||||
this.feedback_message_editor = None;
|
||||
cx.notify();
|
||||
})),
|
||||
)
|
||||
.child(
|
||||
Button::new(
|
||||
"submit-feedback-message",
|
||||
"Share Feedback",
|
||||
)
|
||||
.style(ButtonStyle::Tinted(ui::TintColor::Accent))
|
||||
.label_size(LabelSize::Small)
|
||||
.key_binding(
|
||||
KeyBinding::for_action_in(
|
||||
&menu::Confirm,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
.map(|kb| kb.size(rems_from_px(10.))),
|
||||
)
|
||||
.on_click(
|
||||
cx.listener(|this, _, _, cx| {
|
||||
this.submit_feedback_message(cx);
|
||||
cx.notify();
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
),
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
})
|
||||
.into_any()
|
||||
}
|
||||
|
||||
@@ -1699,6 +1785,7 @@ impl ActiveThread {
|
||||
let active_thread = cx.entity();
|
||||
move |id, kind, parsed_markdown, range, window, cx| {
|
||||
render_markdown_code_block(
|
||||
message_id,
|
||||
id,
|
||||
kind,
|
||||
parsed_markdown,
|
||||
@@ -2122,6 +2209,7 @@ impl ActiveThread {
|
||||
if !tool_use.needs_confirmation {
|
||||
element.child(
|
||||
v_flex()
|
||||
.my_1p5()
|
||||
.child(
|
||||
h_flex()
|
||||
.group("disclosure-header")
|
||||
@@ -2193,6 +2281,7 @@ impl ActiveThread {
|
||||
)
|
||||
} else {
|
||||
v_flex()
|
||||
.my_3()
|
||||
.rounded_lg()
|
||||
.border_1()
|
||||
.border_color(self.tool_card_border_color(cx))
|
||||
@@ -2295,7 +2384,32 @@ impl ActiveThread {
|
||||
.border_t_1()
|
||||
.border_color(self.tool_card_border_color(cx))
|
||||
.rounded_b_lg()
|
||||
.child(Label::new("Action Confirmation").color(Color::Muted).size(LabelSize::Small))
|
||||
.child(
|
||||
Label::new("Waiting for Confirmation…")
|
||||
.color(Color::Muted)
|
||||
.size(LabelSize::Small)
|
||||
.with_animation(
|
||||
"generating-label",
|
||||
Animation::new(Duration::from_secs(1)).repeat(),
|
||||
|mut label, delta| {
|
||||
let text = match delta {
|
||||
d if d < 0.25 => "Waiting for Confirmation",
|
||||
d if d < 0.5 => "Waiting for Confirmation.",
|
||||
d if d < 0.75 => "Waiting for Confirmation..",
|
||||
_ => "Waiting for Confirmation...",
|
||||
};
|
||||
label.set_text(text);
|
||||
label
|
||||
},
|
||||
)
|
||||
.with_animation(
|
||||
"pulsating-label",
|
||||
Animation::new(Duration::from_secs(2))
|
||||
.repeat()
|
||||
.with_easing(pulsating_between(0.6, 1.)),
|
||||
|label, delta| label.map_element(|label| label.alpha(delta)),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_0p5()
|
||||
@@ -2410,7 +2524,7 @@ impl ActiveThread {
|
||||
};
|
||||
|
||||
div()
|
||||
.pt_1()
|
||||
.pt_2()
|
||||
.px_2p5()
|
||||
.child(
|
||||
h_flex()
|
||||
|
||||
@@ -106,7 +106,7 @@ impl ContextPickerCompletionProvider {
|
||||
.iter()
|
||||
.map(|mode| {
|
||||
Completion {
|
||||
old_range: source_range.clone(),
|
||||
replace_range: source_range.clone(),
|
||||
new_text: format!("@{} ", mode.mention_prefix()),
|
||||
label: CodeLabel::plain(mode.label().to_string(), None),
|
||||
icon_path: Some(mode.icon().path().into()),
|
||||
@@ -160,7 +160,7 @@ impl ContextPickerCompletionProvider {
|
||||
let new_text = MentionLink::for_thread(&thread_entry);
|
||||
let new_text_len = new_text.len();
|
||||
Completion {
|
||||
old_range: source_range.clone(),
|
||||
replace_range: source_range.clone(),
|
||||
new_text,
|
||||
label: CodeLabel::plain(thread_entry.summary.to_string(), None),
|
||||
documentation: None,
|
||||
@@ -205,7 +205,7 @@ impl ContextPickerCompletionProvider {
|
||||
let new_text = MentionLink::for_fetch(&url_to_fetch);
|
||||
let new_text_len = new_text.len();
|
||||
Completion {
|
||||
old_range: source_range.clone(),
|
||||
replace_range: source_range.clone(),
|
||||
new_text,
|
||||
label: CodeLabel::plain(url_to_fetch.to_string(), None),
|
||||
documentation: None,
|
||||
@@ -287,7 +287,7 @@ impl ContextPickerCompletionProvider {
|
||||
let new_text = MentionLink::for_file(&file_name, &full_path);
|
||||
let new_text_len = new_text.len();
|
||||
Completion {
|
||||
old_range: source_range.clone(),
|
||||
replace_range: source_range.clone(),
|
||||
new_text,
|
||||
label,
|
||||
documentation: None,
|
||||
@@ -350,7 +350,7 @@ impl ContextPickerCompletionProvider {
|
||||
let new_text = MentionLink::for_symbol(&symbol.name, &full_path);
|
||||
let new_text_len = new_text.len();
|
||||
Some(Completion {
|
||||
old_range: source_range.clone(),
|
||||
replace_range: source_range.clone(),
|
||||
new_text,
|
||||
label,
|
||||
documentation: None,
|
||||
|
||||
@@ -1414,7 +1414,7 @@ impl Thread {
|
||||
|
||||
for tool_use in pending_tool_uses.iter() {
|
||||
if let Some(tool) = self.tools.tool(&tool_use.name, cx) {
|
||||
if tool.needs_confirmation()
|
||||
if tool.needs_confirmation(&tool_use.input, cx)
|
||||
&& !AssistantSettings::get_global(cx).always_allow_tool_actions
|
||||
{
|
||||
self.tool_use.confirm_tool_use(
|
||||
@@ -1487,6 +1487,7 @@ impl Thread {
|
||||
tool_use_id.clone(),
|
||||
tool_name,
|
||||
output,
|
||||
cx,
|
||||
);
|
||||
|
||||
cx.emit(ThreadEvent::ToolFinished {
|
||||
@@ -1831,7 +1832,7 @@ impl Thread {
|
||||
));
|
||||
|
||||
self.tool_use
|
||||
.insert_tool_output(tool_use_id.clone(), tool_name, err);
|
||||
.insert_tool_output(tool_use_id.clone(), tool_name, err, cx);
|
||||
|
||||
cx.emit(ThreadEvent::ToolFinished {
|
||||
tool_use_id,
|
||||
|
||||
@@ -7,10 +7,11 @@ use futures::FutureExt as _;
|
||||
use futures::future::Shared;
|
||||
use gpui::{App, SharedString, Task};
|
||||
use language_model::{
|
||||
LanguageModelRequestMessage, LanguageModelToolResult, LanguageModelToolUse,
|
||||
LanguageModelToolUseId, MessageContent, Role,
|
||||
LanguageModelRegistry, LanguageModelRequestMessage, LanguageModelToolResult,
|
||||
LanguageModelToolUse, LanguageModelToolUseId, MessageContent, Role,
|
||||
};
|
||||
use ui::IconName;
|
||||
use util::truncate_lines_to_byte_limit;
|
||||
|
||||
use crate::thread::MessageId;
|
||||
use crate::thread_store::SerializedMessage;
|
||||
@@ -200,7 +201,7 @@ impl ToolUseState {
|
||||
|
||||
let (icon, needs_confirmation) = if let Some(tool) = self.tools.tool(&tool_use.name, cx)
|
||||
{
|
||||
(tool.icon(), tool.needs_confirmation())
|
||||
(tool.icon(), tool.needs_confirmation(&tool_use.input, cx))
|
||||
} else {
|
||||
(IconName::Cog, false)
|
||||
};
|
||||
@@ -331,9 +332,32 @@ impl ToolUseState {
|
||||
tool_use_id: LanguageModelToolUseId,
|
||||
tool_name: Arc<str>,
|
||||
output: Result<String>,
|
||||
cx: &App,
|
||||
) -> Option<PendingToolUse> {
|
||||
match output {
|
||||
Ok(tool_result) => {
|
||||
let model_registry = LanguageModelRegistry::read_global(cx);
|
||||
|
||||
const BYTES_PER_TOKEN_ESTIMATE: usize = 3;
|
||||
|
||||
// Protect from clearly large output
|
||||
let tool_output_limit = model_registry
|
||||
.default_model()
|
||||
.map(|model| model.model.max_token_count() * BYTES_PER_TOKEN_ESTIMATE)
|
||||
.unwrap_or(usize::MAX);
|
||||
|
||||
let tool_result = if tool_result.len() <= tool_output_limit {
|
||||
tool_result
|
||||
} else {
|
||||
let truncated = truncate_lines_to_byte_limit(&tool_result, tool_output_limit);
|
||||
|
||||
format!(
|
||||
"Tool result too long. The first {} bytes:\n\n{}",
|
||||
truncated.len(),
|
||||
truncated
|
||||
)
|
||||
};
|
||||
|
||||
self.tool_results.insert(
|
||||
tool_use_id.clone(),
|
||||
LanguageModelToolResult {
|
||||
|
||||
@@ -120,7 +120,7 @@ impl SlashCommandCompletionProvider {
|
||||
) as Arc<_>
|
||||
});
|
||||
Some(project::Completion {
|
||||
old_range: name_range.clone(),
|
||||
replace_range: name_range.clone(),
|
||||
documentation: Some(CompletionDocumentation::SingleLine(
|
||||
command.description().into(),
|
||||
)),
|
||||
@@ -219,7 +219,7 @@ impl SlashCommandCompletionProvider {
|
||||
}
|
||||
|
||||
project::Completion {
|
||||
old_range: if new_argument.replace_previous_arguments {
|
||||
replace_range: if new_argument.replace_previous_arguments {
|
||||
argument_range.clone()
|
||||
} else {
|
||||
last_argument_range.clone()
|
||||
|
||||
@@ -48,7 +48,7 @@ pub trait Tool: 'static + Send + Sync {
|
||||
|
||||
/// Returns true iff the tool needs the users's confirmation
|
||||
/// before having permission to run.
|
||||
fn needs_confirmation(&self) -> bool;
|
||||
fn needs_confirmation(&self, input: &serde_json::Value, cx: &App) -> bool;
|
||||
|
||||
/// Returns the JSON schema that describes the tool's input.
|
||||
fn input_schema(&self, _: LanguageModelToolSchemaFormat) -> serde_json::Value {
|
||||
|
||||
@@ -23,7 +23,6 @@ http_client.workspace = true
|
||||
itertools.workspace = true
|
||||
language.workspace = true
|
||||
language_model.workspace = true
|
||||
lsp.workspace = true
|
||||
project.workspace = true
|
||||
regex.workspace = true
|
||||
schemars.workspace = true
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
mod bash_tool;
|
||||
mod batch_tool;
|
||||
mod code_symbol_iter;
|
||||
mod code_symbols_tool;
|
||||
mod copy_path_tool;
|
||||
mod create_directory_tool;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use crate::schema::json_schema_for;
|
||||
use anyhow::{Context as _, Result, anyhow};
|
||||
use assistant_tool::{ActionLog, Tool};
|
||||
use futures::io::BufReader;
|
||||
use futures::{AsyncBufReadExt, AsyncReadExt, FutureExt};
|
||||
use gpui::{App, Entity, Task};
|
||||
use language_model::{LanguageModelRequestMessage, LanguageModelToolSchemaFormat};
|
||||
use project::Project;
|
||||
@@ -14,7 +16,7 @@ use util::markdown::MarkdownString;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct BashToolInput {
|
||||
/// The bash command to execute as a one-liner.
|
||||
/// The bash one-liner command to execute.
|
||||
command: String,
|
||||
/// Working directory for the command. This must be one of the root directories of the project.
|
||||
cd: String,
|
||||
@@ -27,7 +29,7 @@ impl Tool for BashTool {
|
||||
"bash".to_string()
|
||||
}
|
||||
|
||||
fn needs_confirmation(&self) -> bool {
|
||||
fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
@@ -121,33 +123,113 @@ impl Tool for BashTool {
|
||||
worktree.read(cx).abs_path()
|
||||
};
|
||||
|
||||
cx.spawn(async move |_| {
|
||||
cx.spawn(async move |cx| {
|
||||
// Add 2>&1 to merge stderr into stdout for proper interleaving.
|
||||
let command = format!("({}) 2>&1", input.command);
|
||||
|
||||
let output = new_smol_command("bash")
|
||||
let mut cmd = new_smol_command("bash")
|
||||
.arg("-c")
|
||||
.arg(&command)
|
||||
.current_dir(working_dir)
|
||||
.output()
|
||||
.await
|
||||
.stdout(std::process::Stdio::piped())
|
||||
.spawn()
|
||||
.context("Failed to execute bash command")?;
|
||||
|
||||
let output_string = String::from_utf8_lossy(&output.stdout).to_string();
|
||||
// Capture stdout with a limit
|
||||
let stdout = cmd.stdout.take().unwrap();
|
||||
let mut reader = BufReader::new(stdout);
|
||||
|
||||
if output.status.success() {
|
||||
const MESSAGE_1: &str = "Command output too long. The first ";
|
||||
const MESSAGE_2: &str = " bytes:\n\n";
|
||||
const ERR_MESSAGE_1: &str = "Command failed with exit code ";
|
||||
const ERR_MESSAGE_2: &str = "\n\n";
|
||||
|
||||
const STDOUT_LIMIT: usize = 8192;
|
||||
|
||||
const LIMIT: usize = STDOUT_LIMIT
|
||||
- (MESSAGE_1.len()
|
||||
+ (STDOUT_LIMIT.ilog10() as usize + 1) // byte count
|
||||
+ MESSAGE_2.len()
|
||||
+ ERR_MESSAGE_1.len()
|
||||
+ 3 // status code
|
||||
+ ERR_MESSAGE_2.len());
|
||||
|
||||
// Read one more byte to determine whether the output was truncated
|
||||
let mut buffer = vec![0; LIMIT + 1];
|
||||
let bytes_read = reader.read(&mut buffer).await?;
|
||||
|
||||
let mut timer = cx
|
||||
.background_executor()
|
||||
.timer(std::time::Duration::from_secs(10))
|
||||
.fuse();
|
||||
|
||||
// Repeatedly fill the output reader's buffer without copying it.
|
||||
loop {
|
||||
let mut skipped_bytes = reader.fill_buf().fuse();
|
||||
|
||||
futures::select! {
|
||||
skipped_bytes = skipped_bytes => {
|
||||
let skipped_bytes = skipped_bytes?;
|
||||
if skipped_bytes.is_empty() {
|
||||
break;
|
||||
}
|
||||
let skipped_bytes_len = skipped_bytes.len();
|
||||
reader.consume_unpin(skipped_bytes_len);
|
||||
|
||||
timer = cx
|
||||
.background_executor()
|
||||
.timer(std::time::Duration::from_secs(10))
|
||||
.fuse();
|
||||
}
|
||||
_ = timer => {
|
||||
return Err(anyhow!("Command timed out. Output so far:\n{}", String::from_utf8_lossy(&buffer[..bytes_read])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let output_bytes = &buffer[..bytes_read];
|
||||
|
||||
// Let the process continue running
|
||||
let status = cmd.status().await.context("Failed to get command status")?;
|
||||
|
||||
let output_string = if bytes_read > LIMIT {
|
||||
// Valid to find `\n` in UTF-8 since 0-127 ASCII characters are not used in
|
||||
// multi-byte characters.
|
||||
let last_line_ix = output_bytes.iter().rposition(|b| *b == b'\n');
|
||||
let output_string = String::from_utf8_lossy(
|
||||
&output_bytes[..last_line_ix.unwrap_or(output_bytes.len())],
|
||||
);
|
||||
|
||||
format!(
|
||||
"{}{}{}{}",
|
||||
MESSAGE_1,
|
||||
output_string.len(),
|
||||
MESSAGE_2,
|
||||
output_string
|
||||
)
|
||||
} else {
|
||||
String::from_utf8_lossy(&output_bytes).into()
|
||||
};
|
||||
|
||||
let output_with_status = if status.success() {
|
||||
if output_string.is_empty() {
|
||||
Ok("Command executed successfully.".to_string())
|
||||
"Command executed successfully.".to_string()
|
||||
} else {
|
||||
Ok(output_string)
|
||||
output_string.to_string()
|
||||
}
|
||||
} else {
|
||||
Ok(format!(
|
||||
"Command failed with exit code {}\n{}",
|
||||
output.status.code().unwrap_or(-1),
|
||||
&output_string
|
||||
))
|
||||
}
|
||||
format!(
|
||||
"{}{}{}{}",
|
||||
ERR_MESSAGE_1,
|
||||
status.code().unwrap_or(-1),
|
||||
ERR_MESSAGE_2,
|
||||
output_string,
|
||||
)
|
||||
};
|
||||
|
||||
debug_assert!(output_with_status.len() <= STDOUT_LIMIT);
|
||||
|
||||
Ok(output_with_status)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,8 +151,17 @@ impl Tool for BatchTool {
|
||||
"batch_tool".into()
|
||||
}
|
||||
|
||||
fn needs_confirmation(&self) -> bool {
|
||||
true
|
||||
fn needs_confirmation(&self, input: &serde_json::Value, cx: &App) -> bool {
|
||||
serde_json::from_value::<BatchToolInput>(input.clone())
|
||||
.map(|input| {
|
||||
let working_set = ToolWorkingSet::default();
|
||||
input.invocations.iter().any(|invocation| {
|
||||
working_set
|
||||
.tool(&invocation.name, cx)
|
||||
.map_or(false, |tool| tool.needs_confirmation(&invocation.input, cx))
|
||||
})
|
||||
})
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn description(&self) -> String {
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
use project::DocumentSymbol;
|
||||
use regex::Regex;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Entry {
|
||||
pub name: String,
|
||||
pub kind: lsp::SymbolKind,
|
||||
pub depth: u32,
|
||||
pub start_line: usize,
|
||||
pub end_line: usize,
|
||||
}
|
||||
|
||||
/// An iterator that filters document symbols based on a regex pattern.
|
||||
/// This iterator recursively traverses the document symbol tree, incrementing depth for child symbols.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CodeSymbolIterator<'a> {
|
||||
symbols: &'a [DocumentSymbol],
|
||||
regex: Option<Regex>,
|
||||
// Stack of (symbol, depth) pairs to process
|
||||
pending_symbols: Vec<(&'a DocumentSymbol, u32)>,
|
||||
current_index: usize,
|
||||
current_depth: u32,
|
||||
}
|
||||
|
||||
impl<'a> CodeSymbolIterator<'a> {
|
||||
pub fn new(symbols: &'a [DocumentSymbol], regex: Option<Regex>) -> Self {
|
||||
Self {
|
||||
symbols,
|
||||
regex,
|
||||
pending_symbols: Vec::new(),
|
||||
current_index: 0,
|
||||
current_depth: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for CodeSymbolIterator<'_> {
|
||||
type Item = Entry;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some((symbol, depth)) = self.pending_symbols.pop() {
|
||||
for child in symbol.children.iter().rev() {
|
||||
self.pending_symbols.push((child, depth + 1));
|
||||
}
|
||||
|
||||
return Some(Entry {
|
||||
name: symbol.name.clone(),
|
||||
kind: symbol.kind,
|
||||
depth,
|
||||
start_line: symbol.range.start.0.row as usize,
|
||||
end_line: symbol.range.end.0.row as usize,
|
||||
});
|
||||
}
|
||||
|
||||
while self.current_index < self.symbols.len() {
|
||||
let regex = self.regex.as_ref();
|
||||
let symbol = &self.symbols[self.current_index];
|
||||
self.current_index += 1;
|
||||
|
||||
if regex.is_none_or(|regex| regex.is_match(&symbol.name)) {
|
||||
// Push in reverse order to maintain traversal order
|
||||
for child in symbol.children.iter().rev() {
|
||||
self.pending_symbols.push((child, self.current_depth + 1));
|
||||
}
|
||||
|
||||
return Some(Entry {
|
||||
name: symbol.name.clone(),
|
||||
kind: symbol.kind,
|
||||
depth: self.current_depth,
|
||||
start_line: symbol.range.start.0.row as usize,
|
||||
end_line: symbol.range.end.0.row as usize,
|
||||
});
|
||||
} else {
|
||||
// Even if parent doesn't match, push children to check them later
|
||||
for child in symbol.children.iter().rev() {
|
||||
self.pending_symbols.push((child, self.current_depth + 1));
|
||||
}
|
||||
|
||||
// Check if any pending children match our criteria
|
||||
if let Some(result) = self.next() {
|
||||
return Some(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,21 @@
|
||||
use std::fmt::{self, Write};
|
||||
use std::fmt::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::schema::json_schema_for;
|
||||
use anyhow::{Result, anyhow};
|
||||
use assistant_tool::{ActionLog, Tool};
|
||||
use collections::IndexMap;
|
||||
use gpui::{App, AsyncApp, Entity, Task};
|
||||
use language::{CodeLabel, Language, LanguageRegistry};
|
||||
use language::{OutlineItem, ParseStatus, Point};
|
||||
use language_model::{LanguageModelRequestMessage, LanguageModelToolSchemaFormat};
|
||||
use lsp::SymbolKind;
|
||||
use project::{DocumentSymbol, Project, Symbol};
|
||||
use project::{Project, Symbol};
|
||||
use regex::{Regex, RegexBuilder};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ui::IconName;
|
||||
use util::markdown::MarkdownString;
|
||||
|
||||
use crate::code_symbol_iter::{CodeSymbolIterator, Entry};
|
||||
use crate::schema::json_schema_for;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct CodeSymbolsInput {
|
||||
/// The relative path of the source code file to read and get the symbols for.
|
||||
@@ -82,7 +79,7 @@ impl Tool for CodeSymbolsTool {
|
||||
"code_symbols".into()
|
||||
}
|
||||
|
||||
fn needs_confirmation(&self) -> bool {
|
||||
fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
@@ -180,24 +177,28 @@ pub async fn file_outline(
|
||||
action_log.buffer_read(buffer.clone(), cx);
|
||||
})?;
|
||||
|
||||
let symbols = project
|
||||
.update(cx, |project, cx| project.document_symbols(&buffer, cx))?
|
||||
.await?;
|
||||
// Wait until the buffer has been fully parsed, so that we can read its outline.
|
||||
let mut parse_status = buffer.read_with(cx, |buffer, _| buffer.parse_status())?;
|
||||
while parse_status
|
||||
.recv()
|
||||
.await
|
||||
.map_or(false, |status| status != ParseStatus::Idle)
|
||||
{}
|
||||
|
||||
if symbols.is_empty() {
|
||||
return Err(
|
||||
if buffer.read_with(cx, |buffer, _| buffer.snapshot().is_empty())? {
|
||||
anyhow!("This file is empty.")
|
||||
} else {
|
||||
anyhow!("No outline information available for this file.")
|
||||
},
|
||||
);
|
||||
}
|
||||
let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot())?;
|
||||
let Some(outline) = snapshot.outline(None) else {
|
||||
return Err(anyhow!("No outline information available for this file."));
|
||||
};
|
||||
|
||||
let language = buffer.read_with(cx, |buffer, _| buffer.language().cloned())?;
|
||||
let language_registry = project.read_with(cx, |project, _| project.languages().clone())?;
|
||||
|
||||
render_outline(&symbols, language, language_registry, regex, offset).await
|
||||
render_outline(
|
||||
outline
|
||||
.items
|
||||
.into_iter()
|
||||
.map(|item| item.to_point(&snapshot)),
|
||||
regex,
|
||||
offset,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn project_symbols(
|
||||
@@ -292,61 +293,27 @@ async fn project_symbols(
|
||||
}
|
||||
|
||||
async fn render_outline(
|
||||
symbols: &[DocumentSymbol],
|
||||
language: Option<Arc<Language>>,
|
||||
registry: Arc<LanguageRegistry>,
|
||||
items: impl IntoIterator<Item = OutlineItem<Point>>,
|
||||
regex: Option<Regex>,
|
||||
offset: u32,
|
||||
) -> Result<String> {
|
||||
const RESULTS_PER_PAGE_USIZE: usize = RESULTS_PER_PAGE as usize;
|
||||
let entries = CodeSymbolIterator::new(symbols, regex.clone())
|
||||
.skip(offset as usize)
|
||||
// Take 1 more than RESULTS_PER_PAGE so we can tell if there are more results.
|
||||
.take(RESULTS_PER_PAGE_USIZE.saturating_add(1))
|
||||
.collect::<Vec<Entry>>();
|
||||
let has_more = entries.len() > RESULTS_PER_PAGE_USIZE;
|
||||
|
||||
// Get language-specific labels, if available
|
||||
let labels = match &language {
|
||||
Some(lang) => {
|
||||
let entries_for_labels: Vec<(String, SymbolKind)> = entries
|
||||
.iter()
|
||||
.take(RESULTS_PER_PAGE_USIZE)
|
||||
.map(|entry| (entry.name.clone(), entry.kind))
|
||||
.collect();
|
||||
let mut items = items.into_iter().skip(offset as usize);
|
||||
|
||||
let lang_name = lang.name();
|
||||
if let Some(lsp_adapter) = registry.lsp_adapters(&lang_name).first().cloned() {
|
||||
lsp_adapter
|
||||
.labels_for_symbols(&entries_for_labels, lang)
|
||||
.await
|
||||
.ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
let entries = items
|
||||
.by_ref()
|
||||
.filter(|item| {
|
||||
regex
|
||||
.as_ref()
|
||||
.is_none_or(|regex| regex.is_match(&item.text))
|
||||
})
|
||||
.take(RESULTS_PER_PAGE_USIZE)
|
||||
.collect::<Vec<_>>();
|
||||
let has_more = items.next().is_some();
|
||||
|
||||
let mut output = String::new();
|
||||
|
||||
let entries_rendered = match &labels {
|
||||
Some(label_list) => render_entries(
|
||||
&mut output,
|
||||
entries
|
||||
.into_iter()
|
||||
.take(RESULTS_PER_PAGE_USIZE)
|
||||
.zip(label_list.iter())
|
||||
.map(|(entry, label)| (entry, label.as_ref())),
|
||||
),
|
||||
None => render_entries(
|
||||
&mut output,
|
||||
entries
|
||||
.into_iter()
|
||||
.take(RESULTS_PER_PAGE_USIZE)
|
||||
.map(|entry| (entry, None)),
|
||||
),
|
||||
};
|
||||
let entries_rendered = render_entries(&mut output, entries);
|
||||
|
||||
// Calculate pagination information
|
||||
let page_start = offset + 1;
|
||||
@@ -372,31 +339,19 @@ async fn render_outline(
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
fn render_entries<'a>(
|
||||
output: &mut String,
|
||||
entries: impl IntoIterator<Item = (Entry, Option<&'a CodeLabel>)>,
|
||||
) -> u32 {
|
||||
fn render_entries(output: &mut String, items: impl IntoIterator<Item = OutlineItem<Point>>) -> u32 {
|
||||
let mut entries_rendered = 0;
|
||||
|
||||
for (entry, label) in entries {
|
||||
for item in items {
|
||||
// Indent based on depth ("" for level 0, " " for level 1, etc.)
|
||||
for _ in 0..entry.depth {
|
||||
output.push_str(" ");
|
||||
}
|
||||
|
||||
match label {
|
||||
Some(label) => {
|
||||
output.push_str(label.text());
|
||||
}
|
||||
None => {
|
||||
write_symbol_kind(output, entry.kind).ok();
|
||||
output.push_str(&entry.name);
|
||||
}
|
||||
for _ in 0..item.depth {
|
||||
output.push(' ');
|
||||
}
|
||||
output.push_str(&item.text);
|
||||
|
||||
// Add position information - convert to 1-based line numbers for display
|
||||
let start_line = entry.start_line + 1;
|
||||
let end_line = entry.end_line + 1;
|
||||
let start_line = item.range.start.row + 1;
|
||||
let end_line = item.range.end.row + 1;
|
||||
|
||||
if start_line == end_line {
|
||||
writeln!(output, " [L{}]", start_line).ok();
|
||||
@@ -408,38 +363,3 @@ fn render_entries<'a>(
|
||||
|
||||
entries_rendered
|
||||
}
|
||||
|
||||
// We may not have a language server adapter to have language-specific
|
||||
// ways to translate SymbolKnd into a string. In that situation,
|
||||
// fall back on some reasonable default strings to render.
|
||||
fn write_symbol_kind(buf: &mut String, kind: SymbolKind) -> Result<(), fmt::Error> {
|
||||
match kind {
|
||||
SymbolKind::FILE => write!(buf, "file "),
|
||||
SymbolKind::MODULE => write!(buf, "module "),
|
||||
SymbolKind::NAMESPACE => write!(buf, "namespace "),
|
||||
SymbolKind::PACKAGE => write!(buf, "package "),
|
||||
SymbolKind::CLASS => write!(buf, "class "),
|
||||
SymbolKind::METHOD => write!(buf, "method "),
|
||||
SymbolKind::PROPERTY => write!(buf, "property "),
|
||||
SymbolKind::FIELD => write!(buf, "field "),
|
||||
SymbolKind::CONSTRUCTOR => write!(buf, "constructor "),
|
||||
SymbolKind::ENUM => write!(buf, "enum "),
|
||||
SymbolKind::INTERFACE => write!(buf, "interface "),
|
||||
SymbolKind::FUNCTION => write!(buf, "function "),
|
||||
SymbolKind::VARIABLE => write!(buf, "variable "),
|
||||
SymbolKind::CONSTANT => write!(buf, "constant "),
|
||||
SymbolKind::STRING => write!(buf, "string "),
|
||||
SymbolKind::NUMBER => write!(buf, "number "),
|
||||
SymbolKind::BOOLEAN => write!(buf, "boolean "),
|
||||
SymbolKind::ARRAY => write!(buf, "array "),
|
||||
SymbolKind::OBJECT => write!(buf, "object "),
|
||||
SymbolKind::KEY => write!(buf, "key "),
|
||||
SymbolKind::NULL => write!(buf, "null "),
|
||||
SymbolKind::ENUM_MEMBER => write!(buf, "enum member "),
|
||||
SymbolKind::STRUCT => write!(buf, "struct "),
|
||||
SymbolKind::EVENT => write!(buf, "event "),
|
||||
SymbolKind::OPERATOR => write!(buf, "operator "),
|
||||
SymbolKind::TYPE_PARAMETER => write!(buf, "type parameter "),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ impl Tool for CopyPathTool {
|
||||
"copy_path".into()
|
||||
}
|
||||
|
||||
fn needs_confirmation(&self) -> bool {
|
||||
fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ impl Tool for CreateDirectoryTool {
|
||||
"create_directory".into()
|
||||
}
|
||||
|
||||
fn needs_confirmation(&self) -> bool {
|
||||
fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ impl Tool for CreateFileTool {
|
||||
"create_file".into()
|
||||
}
|
||||
|
||||
fn needs_confirmation(&self) -> bool {
|
||||
fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ impl Tool for DeletePathTool {
|
||||
"delete_path".into()
|
||||
}
|
||||
|
||||
fn needs_confirmation(&self) -> bool {
|
||||
fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ impl Tool for DiagnosticsTool {
|
||||
"diagnostics".into()
|
||||
}
|
||||
|
||||
fn needs_confirmation(&self) -> bool {
|
||||
fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ impl Tool for FetchTool {
|
||||
"fetch".to_string()
|
||||
}
|
||||
|
||||
fn needs_confirmation(&self) -> bool {
|
||||
fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ impl Tool for FindReplaceFileTool {
|
||||
"find_replace_file".into()
|
||||
}
|
||||
|
||||
fn needs_confirmation(&self) -> bool {
|
||||
fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ impl Tool for ListDirectoryTool {
|
||||
"list_directory".into()
|
||||
}
|
||||
|
||||
fn needs_confirmation(&self) -> bool {
|
||||
fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ impl Tool for MovePathTool {
|
||||
"move_path".into()
|
||||
}
|
||||
|
||||
fn needs_confirmation(&self) -> bool {
|
||||
fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ impl Tool for NowTool {
|
||||
"now".into()
|
||||
}
|
||||
|
||||
fn needs_confirmation(&self) -> bool {
|
||||
fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ impl Tool for OpenTool {
|
||||
"open".to_string()
|
||||
}
|
||||
|
||||
fn needs_confirmation(&self) -> bool {
|
||||
fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ impl Tool for PathSearchTool {
|
||||
"path_search".into()
|
||||
}
|
||||
|
||||
fn needs_confirmation(&self) -> bool {
|
||||
fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::code_symbols_tool::file_outline;
|
||||
use crate::schema::json_schema_for;
|
||||
use crate::{code_symbols_tool::file_outline, schema::json_schema_for};
|
||||
use anyhow::{Result, anyhow};
|
||||
use assistant_tool::{ActionLog, Tool};
|
||||
use gpui::{App, Entity, Task};
|
||||
@@ -16,7 +15,7 @@ use util::markdown::MarkdownString;
|
||||
/// If the model requests to read a file whose size exceeds this, then
|
||||
/// the tool will return an error along with the model's symbol outline,
|
||||
/// and suggest trying again using line ranges from the outline.
|
||||
const MAX_FILE_SIZE_TO_READ: usize = 4096;
|
||||
const MAX_FILE_SIZE_TO_READ: usize = 16384;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct ReadFileToolInput {
|
||||
@@ -52,7 +51,7 @@ impl Tool for ReadFileTool {
|
||||
"read_file".into()
|
||||
}
|
||||
|
||||
fn needs_confirmation(&self) -> bool {
|
||||
fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ impl Tool for RegexSearchTool {
|
||||
"regex_search".into()
|
||||
}
|
||||
|
||||
fn needs_confirmation(&self) -> bool {
|
||||
fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ impl Tool for SymbolInfoTool {
|
||||
"symbol_info".into()
|
||||
}
|
||||
|
||||
fn needs_confirmation(&self) -> bool {
|
||||
fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ impl Tool for ThinkingTool {
|
||||
"thinking".to_string()
|
||||
}
|
||||
|
||||
fn needs_confirmation(&self) -> bool {
|
||||
fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
|
||||
@@ -944,8 +944,8 @@ impl Room {
|
||||
)
|
||||
})?;
|
||||
if self.live_kit.as_ref().map_or(true, |kit| kit.deafened) {
|
||||
if matches!(track, livekit_client::RemoteTrack::Audio(_)) {
|
||||
track.set_enabled(false, cx);
|
||||
if publication.is_audio() {
|
||||
publication.set_enabled(false, cx);
|
||||
}
|
||||
}
|
||||
match track {
|
||||
|
||||
@@ -18,7 +18,7 @@ test-support = ["clock/test-support", "collections/test-support", "gpui/test-sup
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
async-recursion = "0.3"
|
||||
async-tungstenite = { workspace = true, features = ["async-std", "async-tls"] }
|
||||
async-tungstenite = { workspace = true, features = ["tokio", "tokio-rustls-manual-roots"] }
|
||||
chrono = { workspace = true, features = ["serde"] }
|
||||
clock.workspace = true
|
||||
collections.workspace = true
|
||||
@@ -26,6 +26,7 @@ credentials_provider.workspace = true
|
||||
feature_flags.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
gpui_tokio.workspace = true
|
||||
http_client.workspace = true
|
||||
http_client_tls.workspace = true
|
||||
log.workspace = true
|
||||
@@ -51,6 +52,7 @@ url.workspace = true
|
||||
util.workspace = true
|
||||
worktree.workspace = true
|
||||
telemetry.workspace = true
|
||||
tokio.workspace = true
|
||||
workspace-hack.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
@@ -67,4 +69,3 @@ windows.workspace = true
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
cocoa.workspace = true
|
||||
async-native-tls = { version = "0.5.0", features = ["vendored"] }
|
||||
|
||||
@@ -20,7 +20,7 @@ use futures::{
|
||||
AsyncReadExt, FutureExt, SinkExt, Stream, StreamExt, TryFutureExt as _, TryStreamExt,
|
||||
channel::oneshot, future::BoxFuture,
|
||||
};
|
||||
use gpui::{App, AppContext as _, AsyncApp, Entity, Global, Task, WeakEntity, actions};
|
||||
use gpui::{App, AsyncApp, Entity, Global, Task, WeakEntity, actions};
|
||||
use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
|
||||
use parking_lot::RwLock;
|
||||
use postage::watch;
|
||||
@@ -1086,7 +1086,7 @@ impl Client {
|
||||
let rpc_url = self.rpc_url(http, release_channel);
|
||||
let system_id = self.telemetry.system_id();
|
||||
let metrics_id = self.telemetry.metrics_id();
|
||||
cx.background_spawn(async move {
|
||||
cx.spawn(async move |cx| {
|
||||
use HttpOrHttps::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -1105,7 +1105,12 @@ impl Client {
|
||||
.host_str()
|
||||
.zip(rpc_url.port_or_known_default())
|
||||
.ok_or_else(|| anyhow!("missing host in rpc url"))?;
|
||||
let stream = connect_socks_proxy_stream(proxy.as_ref(), rpc_host).await?;
|
||||
|
||||
let stream = {
|
||||
let handle = cx.update(|cx| gpui_tokio::Tokio::handle(cx)).ok().unwrap();
|
||||
let _guard = handle.enter();
|
||||
connect_socks_proxy_stream(proxy.as_ref(), rpc_host).await?
|
||||
};
|
||||
|
||||
log::info!("connected to rpc endpoint {}", rpc_url);
|
||||
|
||||
@@ -1144,30 +1149,19 @@ impl Client {
|
||||
request_headers.insert("x-zed-metrics-id", HeaderValue::from_str(&metrics_id)?);
|
||||
}
|
||||
|
||||
match url_scheme {
|
||||
Https => {
|
||||
let (stream, _) =
|
||||
async_tungstenite::async_tls::client_async_tls_with_connector(
|
||||
request,
|
||||
stream,
|
||||
Some(http_client_tls::tls_config().into()),
|
||||
)
|
||||
.await?;
|
||||
Ok(Connection::new(
|
||||
stream
|
||||
.map_err(|error| anyhow!(error))
|
||||
.sink_map_err(|error| anyhow!(error)),
|
||||
))
|
||||
}
|
||||
Http => {
|
||||
let (stream, _) = async_tungstenite::client_async(request, stream).await?;
|
||||
Ok(Connection::new(
|
||||
stream
|
||||
.map_err(|error| anyhow!(error))
|
||||
.sink_map_err(|error| anyhow!(error)),
|
||||
))
|
||||
}
|
||||
}
|
||||
let (stream, _) = async_tungstenite::tokio::client_async_tls_with_connector_and_config(
|
||||
request,
|
||||
stream,
|
||||
Some(Arc::new(http_client_tls::tls_config()).into()),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(Connection::new(
|
||||
stream
|
||||
.map_err(|error| anyhow!(error))
|
||||
.sink_map_err(|error| anyhow!(error)),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1639,7 +1633,7 @@ mod tests {
|
||||
use crate::test::FakeServer;
|
||||
|
||||
use clock::FakeSystemClock;
|
||||
use gpui::{BackgroundExecutor, TestAppContext};
|
||||
use gpui::{AppContext as _, BackgroundExecutor, TestAppContext};
|
||||
use http_client::FakeHttpClient;
|
||||
use parking_lot::Mutex;
|
||||
use proto::TypedEnvelope;
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
//! socks proxy
|
||||
use anyhow::{Result, anyhow};
|
||||
use futures::io::{AsyncRead, AsyncWrite};
|
||||
use http_client::Uri;
|
||||
use tokio_socks::{
|
||||
io::Compat,
|
||||
tcp::{Socks4Stream, Socks5Stream},
|
||||
};
|
||||
use tokio_socks::tcp::{Socks4Stream, Socks5Stream};
|
||||
|
||||
pub(crate) async fn connect_socks_proxy_stream(
|
||||
proxy: Option<&Uri>,
|
||||
@@ -14,7 +10,7 @@ pub(crate) async fn connect_socks_proxy_stream(
|
||||
let stream = match parse_socks_proxy(proxy) {
|
||||
Some((socks_proxy, SocksVersion::V4)) => {
|
||||
let stream = Socks4Stream::connect_with_socket(
|
||||
Compat::new(smol::net::TcpStream::connect(socks_proxy).await?),
|
||||
tokio::net::TcpStream::connect(socks_proxy).await?,
|
||||
rpc_host,
|
||||
)
|
||||
.await
|
||||
@@ -23,13 +19,15 @@ pub(crate) async fn connect_socks_proxy_stream(
|
||||
}
|
||||
Some((socks_proxy, SocksVersion::V5)) => Box::new(
|
||||
Socks5Stream::connect_with_socket(
|
||||
Compat::new(smol::net::TcpStream::connect(socks_proxy).await?),
|
||||
tokio::net::TcpStream::connect(socks_proxy).await?,
|
||||
rpc_host,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| anyhow!("error connecting to socks {}", err))?,
|
||||
) as Box<dyn AsyncReadWrite>,
|
||||
None => Box::new(smol::net::TcpStream::connect(rpc_host).await?) as Box<dyn AsyncReadWrite>,
|
||||
None => {
|
||||
Box::new(tokio::net::TcpStream::connect(rpc_host).await?) as Box<dyn AsyncReadWrite>
|
||||
}
|
||||
};
|
||||
Ok(stream)
|
||||
}
|
||||
@@ -60,5 +58,11 @@ enum SocksVersion {
|
||||
V5,
|
||||
}
|
||||
|
||||
pub(crate) trait AsyncReadWrite: AsyncRead + AsyncWrite + Unpin + Send + 'static {}
|
||||
impl<T: AsyncRead + AsyncWrite + Unpin + Send + 'static> AsyncReadWrite for T {}
|
||||
pub(crate) trait AsyncReadWrite:
|
||||
tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + 'static
|
||||
{
|
||||
}
|
||||
impl<T: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + 'static> AsyncReadWrite
|
||||
for T
|
||||
{
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ clock.workspace = true
|
||||
collections.workspace = true
|
||||
dashmap.workspace = true
|
||||
derive_more.workspace = true
|
||||
buffer_diff.workspace = true
|
||||
envy = "0.4.2"
|
||||
futures.workspace = true
|
||||
google_ai.workspace = true
|
||||
@@ -85,6 +84,7 @@ assistant_slash_command.workspace = true
|
||||
assistant_tool.workspace = true
|
||||
async-trait.workspace = true
|
||||
audio.workspace = true
|
||||
buffer_diff.workspace = true
|
||||
call = { workspace = true, features = ["test-support"] }
|
||||
channel.workspace = true
|
||||
client = { workspace = true, features = ["test-support"] }
|
||||
|
||||
@@ -318,6 +318,7 @@ impl Server {
|
||||
.add_request_handler(forward_read_only_project_request::<proto::OpenUncommittedDiff>)
|
||||
.add_request_handler(forward_read_only_project_request::<proto::LspExtExpandMacro>)
|
||||
.add_request_handler(forward_read_only_project_request::<proto::LspExtOpenDocs>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::LspExtRunnables>)
|
||||
.add_request_handler(
|
||||
forward_read_only_project_request::<proto::LspExtSwitchSourceHeader>,
|
||||
)
|
||||
@@ -4153,13 +4154,13 @@ async fn get_llm_api_token(
|
||||
|
||||
fn to_axum_message(message: TungsteniteMessage) -> anyhow::Result<AxumMessage> {
|
||||
let message = match message {
|
||||
TungsteniteMessage::Text(payload) => AxumMessage::Text(payload),
|
||||
TungsteniteMessage::Binary(payload) => AxumMessage::Binary(payload),
|
||||
TungsteniteMessage::Ping(payload) => AxumMessage::Ping(payload),
|
||||
TungsteniteMessage::Pong(payload) => AxumMessage::Pong(payload),
|
||||
TungsteniteMessage::Text(payload) => AxumMessage::Text(payload.as_str().to_string()),
|
||||
TungsteniteMessage::Binary(payload) => AxumMessage::Binary(payload.into()),
|
||||
TungsteniteMessage::Ping(payload) => AxumMessage::Ping(payload.into()),
|
||||
TungsteniteMessage::Pong(payload) => AxumMessage::Pong(payload.into()),
|
||||
TungsteniteMessage::Close(frame) => AxumMessage::Close(frame.map(|frame| AxumCloseFrame {
|
||||
code: frame.code.into(),
|
||||
reason: frame.reason,
|
||||
reason: frame.reason.as_str().to_owned().into(),
|
||||
})),
|
||||
// We should never receive a frame while reading the message, according
|
||||
// to the `tungstenite` maintainers:
|
||||
@@ -4179,14 +4180,14 @@ fn to_axum_message(message: TungsteniteMessage) -> anyhow::Result<AxumMessage> {
|
||||
|
||||
fn to_tungstenite_message(message: AxumMessage) -> TungsteniteMessage {
|
||||
match message {
|
||||
AxumMessage::Text(payload) => TungsteniteMessage::Text(payload),
|
||||
AxumMessage::Binary(payload) => TungsteniteMessage::Binary(payload),
|
||||
AxumMessage::Ping(payload) => TungsteniteMessage::Ping(payload),
|
||||
AxumMessage::Pong(payload) => TungsteniteMessage::Pong(payload),
|
||||
AxumMessage::Text(payload) => TungsteniteMessage::Text(payload.into()),
|
||||
AxumMessage::Binary(payload) => TungsteniteMessage::Binary(payload.into()),
|
||||
AxumMessage::Ping(payload) => TungsteniteMessage::Ping(payload.into()),
|
||||
AxumMessage::Pong(payload) => TungsteniteMessage::Pong(payload.into()),
|
||||
AxumMessage::Close(frame) => {
|
||||
TungsteniteMessage::Close(frame.map(|frame| TungsteniteCloseFrame {
|
||||
code: frame.code.into(),
|
||||
reason: frame.reason,
|
||||
reason: frame.reason.as_ref().into(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,7 +309,7 @@ impl MessageEditor {
|
||||
.map(|mat| {
|
||||
let (new_text, label) = completion_fn(&mat);
|
||||
Completion {
|
||||
old_range: range.clone(),
|
||||
replace_range: range.clone(),
|
||||
new_text,
|
||||
label,
|
||||
icon_path: None,
|
||||
|
||||
@@ -3,37 +3,62 @@ use std::ops::{Deref, DerefMut};
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use collections::HashMap;
|
||||
use gpui::{AnyElement, App, IntoElement, RenderOnce, SharedString, Window, div, prelude::*, px};
|
||||
use gpui::{
|
||||
AnyElement, App, IntoElement, RenderOnce, SharedString, Window, div, pattern_slash, prelude::*,
|
||||
px, rems,
|
||||
};
|
||||
use linkme::distributed_slice;
|
||||
use parking_lot::RwLock;
|
||||
use theme::ActiveTheme;
|
||||
|
||||
pub trait Component {
|
||||
fn scope() -> Option<ComponentScope>;
|
||||
fn scope() -> ComponentScope {
|
||||
ComponentScope::None
|
||||
}
|
||||
fn name() -> &'static str {
|
||||
std::any::type_name::<Self>()
|
||||
}
|
||||
/// Returns a name that the component should be sorted by.
|
||||
///
|
||||
/// Implement this if the component should be sorted in an alternate order than its name.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// For example, to group related components together when sorted:
|
||||
///
|
||||
/// - Button -> ButtonA
|
||||
/// - IconButton -> ButtonBIcon
|
||||
/// - ToggleButton -> ButtonCToggle
|
||||
///
|
||||
/// This naming scheme keeps these components together and allows them to /// be sorted in a logical order.
|
||||
fn sort_name() -> &'static str {
|
||||
Self::name()
|
||||
}
|
||||
fn description() -> Option<&'static str> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ComponentPreview: Component {
|
||||
fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement;
|
||||
fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[distributed_slice]
|
||||
pub static __ALL_COMPONENTS: [fn()] = [..];
|
||||
|
||||
#[distributed_slice]
|
||||
pub static __ALL_PREVIEWS: [fn()] = [..];
|
||||
|
||||
pub static COMPONENT_DATA: LazyLock<RwLock<ComponentRegistry>> =
|
||||
LazyLock::new(|| RwLock::new(ComponentRegistry::new()));
|
||||
|
||||
pub struct ComponentRegistry {
|
||||
components: Vec<(Option<ComponentScope>, &'static str, Option<&'static str>)>,
|
||||
previews: HashMap<&'static str, fn(&mut Window, &mut App) -> AnyElement>,
|
||||
components: Vec<(
|
||||
ComponentScope,
|
||||
// name
|
||||
&'static str,
|
||||
// sort name
|
||||
&'static str,
|
||||
// description
|
||||
Option<&'static str>,
|
||||
)>,
|
||||
previews: HashMap<&'static str, fn(&mut Window, &mut App) -> Option<AnyElement>>,
|
||||
}
|
||||
|
||||
impl ComponentRegistry {
|
||||
@@ -47,30 +72,16 @@ impl ComponentRegistry {
|
||||
|
||||
pub fn init() {
|
||||
let component_fns: Vec<_> = __ALL_COMPONENTS.iter().cloned().collect();
|
||||
let preview_fns: Vec<_> = __ALL_PREVIEWS.iter().cloned().collect();
|
||||
|
||||
for f in component_fns {
|
||||
f();
|
||||
}
|
||||
for f in preview_fns {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_component<T: Component>() {
|
||||
let component_data = (T::scope(), T::name(), T::description());
|
||||
COMPONENT_DATA.write().components.push(component_data);
|
||||
}
|
||||
|
||||
pub fn register_preview<T: ComponentPreview>() {
|
||||
let preview_data = (
|
||||
T::name(),
|
||||
T::preview as fn(&mut Window, &mut App) -> AnyElement,
|
||||
);
|
||||
COMPONENT_DATA
|
||||
.write()
|
||||
.previews
|
||||
.insert(preview_data.0, preview_data.1);
|
||||
let component_data = (T::scope(), T::name(), T::sort_name(), T::description());
|
||||
let mut data = COMPONENT_DATA.write();
|
||||
data.components.push(component_data);
|
||||
data.previews.insert(T::name(), T::preview);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
@@ -80,29 +91,41 @@ pub struct ComponentId(pub &'static str);
|
||||
pub struct ComponentMetadata {
|
||||
id: ComponentId,
|
||||
name: SharedString,
|
||||
scope: Option<ComponentScope>,
|
||||
sort_name: SharedString,
|
||||
scope: ComponentScope,
|
||||
description: Option<SharedString>,
|
||||
preview: Option<fn(&mut Window, &mut App) -> AnyElement>,
|
||||
preview: Option<fn(&mut Window, &mut App) -> Option<AnyElement>>,
|
||||
}
|
||||
|
||||
impl ComponentMetadata {
|
||||
pub fn id(&self) -> ComponentId {
|
||||
self.id.clone()
|
||||
}
|
||||
|
||||
pub fn name(&self) -> SharedString {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
pub fn scope(&self) -> Option<ComponentScope> {
|
||||
self.scope.clone()
|
||||
pub fn sort_name(&self) -> SharedString {
|
||||
self.sort_name.clone()
|
||||
}
|
||||
|
||||
pub fn scopeless_name(&self) -> SharedString {
|
||||
self.name
|
||||
.clone()
|
||||
.split("::")
|
||||
.last()
|
||||
.unwrap_or(&self.name)
|
||||
.to_string()
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn scope(&self) -> ComponentScope {
|
||||
self.scope.clone()
|
||||
}
|
||||
pub fn description(&self) -> Option<SharedString> {
|
||||
self.description.clone()
|
||||
}
|
||||
|
||||
pub fn preview(&self) -> Option<fn(&mut Window, &mut App) -> AnyElement> {
|
||||
pub fn preview(&self) -> Option<fn(&mut Window, &mut App) -> Option<AnyElement>> {
|
||||
self.preview
|
||||
}
|
||||
}
|
||||
@@ -113,26 +136,18 @@ impl AllComponents {
|
||||
pub fn new() -> Self {
|
||||
AllComponents(HashMap::default())
|
||||
}
|
||||
|
||||
/// Returns all components with previews
|
||||
pub fn all_previews(&self) -> Vec<&ComponentMetadata> {
|
||||
self.0.values().filter(|c| c.preview.is_some()).collect()
|
||||
}
|
||||
|
||||
/// Returns all components with previews sorted by name
|
||||
pub fn all_previews_sorted(&self) -> Vec<ComponentMetadata> {
|
||||
let mut previews: Vec<ComponentMetadata> =
|
||||
self.all_previews().into_iter().cloned().collect();
|
||||
previews.sort_by_key(|a| a.name());
|
||||
previews
|
||||
}
|
||||
|
||||
/// Returns all components
|
||||
pub fn all(&self) -> Vec<&ComponentMetadata> {
|
||||
self.0.values().collect()
|
||||
}
|
||||
|
||||
/// Returns all components sorted by name
|
||||
pub fn all_sorted(&self) -> Vec<ComponentMetadata> {
|
||||
let mut components: Vec<ComponentMetadata> = self.all().into_iter().cloned().collect();
|
||||
components.sort_by_key(|a| a.name());
|
||||
@@ -142,7 +157,6 @@ impl AllComponents {
|
||||
|
||||
impl Deref for AllComponents {
|
||||
type Target = HashMap<ComponentId, ComponentMetadata>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
@@ -157,139 +171,127 @@ impl DerefMut for AllComponents {
|
||||
pub fn components() -> AllComponents {
|
||||
let data = COMPONENT_DATA.read();
|
||||
let mut all_components = AllComponents::new();
|
||||
|
||||
for (scope, name, description) in &data.components {
|
||||
for (scope, name, sort_name, description) in &data.components {
|
||||
let preview = data.previews.get(name).cloned();
|
||||
let component_name = SharedString::new_static(name);
|
||||
let sort_name = SharedString::new_static(sort_name);
|
||||
let id = ComponentId(name);
|
||||
all_components.insert(
|
||||
id.clone(),
|
||||
ComponentMetadata {
|
||||
id,
|
||||
name: component_name,
|
||||
sort_name,
|
||||
scope: scope.clone(),
|
||||
description: description.map(Into::into),
|
||||
preview,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
all_components
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum ComponentScope {
|
||||
Layout,
|
||||
Input,
|
||||
Notification,
|
||||
Editor,
|
||||
Collaboration,
|
||||
DataDisplay,
|
||||
Editor,
|
||||
Images,
|
||||
Input,
|
||||
Layout,
|
||||
Loading,
|
||||
Navigation,
|
||||
None,
|
||||
Notification,
|
||||
Overlays,
|
||||
Status,
|
||||
Typography,
|
||||
VersionControl,
|
||||
Unknown(SharedString),
|
||||
}
|
||||
|
||||
impl Display for ComponentScope {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ComponentScope::Layout => write!(f, "Layout"),
|
||||
ComponentScope::Input => write!(f, "Input"),
|
||||
ComponentScope::Notification => write!(f, "Notification"),
|
||||
ComponentScope::Editor => write!(f, "Editor"),
|
||||
ComponentScope::Collaboration => write!(f, "Collaboration"),
|
||||
ComponentScope::DataDisplay => write!(f, "Data Display"),
|
||||
ComponentScope::Editor => write!(f, "Editor"),
|
||||
ComponentScope::Images => write!(f, "Images & Icons"),
|
||||
ComponentScope::Input => write!(f, "Forms & Input"),
|
||||
ComponentScope::Layout => write!(f, "Layout & Structure"),
|
||||
ComponentScope::Loading => write!(f, "Loading & Progress"),
|
||||
ComponentScope::Navigation => write!(f, "Navigation"),
|
||||
ComponentScope::None => write!(f, "Unsorted"),
|
||||
ComponentScope::Notification => write!(f, "Notification"),
|
||||
ComponentScope::Overlays => write!(f, "Overlays & Layering"),
|
||||
ComponentScope::Status => write!(f, "Status"),
|
||||
ComponentScope::Typography => write!(f, "Typography"),
|
||||
ComponentScope::VersionControl => write!(f, "Version Control"),
|
||||
ComponentScope::Unknown(name) => write!(f, "Unknown: {}", name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for ComponentScope {
|
||||
fn from(value: &str) -> Self {
|
||||
match value {
|
||||
"Layout" => ComponentScope::Layout,
|
||||
"Input" => ComponentScope::Input,
|
||||
"Notification" => ComponentScope::Notification,
|
||||
"Editor" => ComponentScope::Editor,
|
||||
"Collaboration" => ComponentScope::Collaboration,
|
||||
"Version Control" | "VersionControl" => ComponentScope::VersionControl,
|
||||
_ => ComponentScope::Unknown(SharedString::new(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for ComponentScope {
|
||||
fn from(value: String) -> Self {
|
||||
match value.as_str() {
|
||||
"Layout" => ComponentScope::Layout,
|
||||
"Input" => ComponentScope::Input,
|
||||
"Notification" => ComponentScope::Notification,
|
||||
"Editor" => ComponentScope::Editor,
|
||||
"Collaboration" => ComponentScope::Collaboration,
|
||||
"Version Control" | "VersionControl" => ComponentScope::VersionControl,
|
||||
_ => ComponentScope::Unknown(SharedString::new(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Which side of the preview to show labels on
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ExampleLabelSide {
|
||||
/// Left side
|
||||
Left,
|
||||
/// Right side
|
||||
Right,
|
||||
/// Top side
|
||||
#[default]
|
||||
Top,
|
||||
/// Bottom side
|
||||
Bottom,
|
||||
}
|
||||
|
||||
/// A single example of a component.
|
||||
#[derive(IntoElement)]
|
||||
pub struct ComponentExample {
|
||||
variant_name: SharedString,
|
||||
element: AnyElement,
|
||||
label_side: ExampleLabelSide,
|
||||
grow: bool,
|
||||
pub variant_name: SharedString,
|
||||
pub description: Option<SharedString>,
|
||||
pub element: AnyElement,
|
||||
}
|
||||
|
||||
impl RenderOnce for ComponentExample {
|
||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let base = div().flex();
|
||||
|
||||
let base = match self.label_side {
|
||||
ExampleLabelSide::Right => base.flex_row(),
|
||||
ExampleLabelSide::Left => base.flex_row_reverse(),
|
||||
ExampleLabelSide::Bottom => base.flex_col(),
|
||||
ExampleLabelSide::Top => base.flex_col_reverse(),
|
||||
};
|
||||
|
||||
base.gap_2()
|
||||
.p_2()
|
||||
.text_size(px(10.))
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
.when(self.grow, |this| this.flex_1())
|
||||
.when(!self.grow, |this| this.flex_none())
|
||||
.child(self.element)
|
||||
.child(self.variant_name)
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
.child(
|
||||
div()
|
||||
.child(self.variant_name.clone())
|
||||
.text_size(rems(1.25))
|
||||
.text_color(cx.theme().colors().text),
|
||||
)
|
||||
.when_some(self.description, |this, description| {
|
||||
this.child(
|
||||
div()
|
||||
.text_size(rems(0.9375))
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
.child(description.clone()),
|
||||
)
|
||||
})
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.w_full()
|
||||
.rounded_xl()
|
||||
.min_h(px(100.))
|
||||
.justify_center()
|
||||
.p_8()
|
||||
.border_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.bg(pattern_slash(
|
||||
cx.theme().colors().surface_background.opacity(0.5),
|
||||
24.0,
|
||||
24.0,
|
||||
))
|
||||
.shadow_sm()
|
||||
.child(self.element),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentExample {
|
||||
/// Create a new example with the given variant name and example value.
|
||||
pub fn new(variant_name: impl Into<SharedString>, element: AnyElement) -> Self {
|
||||
Self {
|
||||
variant_name: variant_name.into(),
|
||||
element,
|
||||
label_side: ExampleLabelSide::default(),
|
||||
grow: false,
|
||||
description: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the example to grow to fill the available horizontal space.
|
||||
pub fn grow(mut self) -> Self {
|
||||
self.grow = true;
|
||||
pub fn description(mut self, description: impl Into<SharedString>) -> Self {
|
||||
self.description = Some(description.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -309,7 +311,7 @@ impl RenderOnce for ComponentExampleGroup {
|
||||
.flex_col()
|
||||
.text_sm()
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
.when(self.grow, |this| this.w_full().flex_1())
|
||||
.w_full()
|
||||
.when_some(self.title, |this, title| {
|
||||
this.gap_4().child(
|
||||
div()
|
||||
@@ -336,7 +338,7 @@ impl RenderOnce for ComponentExampleGroup {
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.when(self.vertical, |this| this.flex_col())
|
||||
.flex_col()
|
||||
.items_start()
|
||||
.w_full()
|
||||
.gap_6()
|
||||
@@ -348,7 +350,6 @@ impl RenderOnce for ComponentExampleGroup {
|
||||
}
|
||||
|
||||
impl ComponentExampleGroup {
|
||||
/// Create a new group of examples with the given title.
|
||||
pub fn new(examples: Vec<ComponentExample>) -> Self {
|
||||
Self {
|
||||
title: None,
|
||||
@@ -357,8 +358,6 @@ impl ComponentExampleGroup {
|
||||
vertical: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new group of examples with the given title.
|
||||
pub fn with_title(title: impl Into<SharedString>, examples: Vec<ComponentExample>) -> Self {
|
||||
Self {
|
||||
title: Some(title.into()),
|
||||
@@ -367,21 +366,16 @@ impl ComponentExampleGroup {
|
||||
vertical: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the group to grow to fill the available horizontal space.
|
||||
pub fn grow(mut self) -> Self {
|
||||
self.grow = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Lay the group out vertically.
|
||||
pub fn vertical(mut self) -> Self {
|
||||
self.vertical = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a single example
|
||||
pub fn single_example(
|
||||
variant_name: impl Into<SharedString>,
|
||||
example: AnyElement,
|
||||
@@ -389,12 +383,10 @@ pub fn single_example(
|
||||
ComponentExample::new(variant_name, example)
|
||||
}
|
||||
|
||||
/// Create a group of examples without a title
|
||||
pub fn example_group(examples: Vec<ComponentExample>) -> ComponentExampleGroup {
|
||||
ComponentExampleGroup::new(examples)
|
||||
}
|
||||
|
||||
/// Create a group of examples with a title
|
||||
pub fn example_group_with_title(
|
||||
title: impl Into<SharedString>,
|
||||
examples: Vec<ComponentExample>,
|
||||
|
||||
@@ -43,6 +43,7 @@ pub fn init(app_state: Arc<AppState>, cx: &mut App) {
|
||||
language_registry,
|
||||
user_store,
|
||||
None,
|
||||
None,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
@@ -106,10 +107,12 @@ impl ComponentPreview {
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
user_store: Entity<UserStore>,
|
||||
selected_index: impl Into<Option<usize>>,
|
||||
active_page: Option<PreviewPage>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let sorted_components = components().all_sorted();
|
||||
let selected_index = selected_index.into().unwrap_or(0);
|
||||
let active_page = active_page.unwrap_or(PreviewPage::AllComponents);
|
||||
|
||||
let component_list = ListState::new(
|
||||
sorted_components.len(),
|
||||
@@ -135,7 +138,7 @@ impl ComponentPreview {
|
||||
language_registry,
|
||||
user_store,
|
||||
workspace,
|
||||
active_page: PreviewPage::AllComponents,
|
||||
active_page,
|
||||
component_map: components().0,
|
||||
components: sorted_components,
|
||||
component_list,
|
||||
@@ -169,8 +172,7 @@ impl ComponentPreview {
|
||||
fn scope_ordered_entries(&self) -> Vec<PreviewEntry> {
|
||||
use std::collections::HashMap;
|
||||
|
||||
let mut scope_groups: HashMap<Option<ComponentScope>, Vec<ComponentMetadata>> =
|
||||
HashMap::default();
|
||||
let mut scope_groups: HashMap<ComponentScope, Vec<ComponentMetadata>> = HashMap::default();
|
||||
|
||||
for component in &self.components {
|
||||
scope_groups
|
||||
@@ -192,6 +194,7 @@ impl ComponentPreview {
|
||||
ComponentScope::Notification,
|
||||
ComponentScope::Collaboration,
|
||||
ComponentScope::VersionControl,
|
||||
ComponentScope::None,
|
||||
];
|
||||
|
||||
// Always show all components first
|
||||
@@ -199,38 +202,27 @@ impl ComponentPreview {
|
||||
entries.push(PreviewEntry::Separator);
|
||||
|
||||
for scope in known_scopes.iter() {
|
||||
let scope_key = Some(scope.clone());
|
||||
if let Some(components) = scope_groups.remove(&scope_key) {
|
||||
if let Some(components) = scope_groups.remove(scope) {
|
||||
if !components.is_empty() {
|
||||
entries.push(PreviewEntry::SectionHeader(scope.to_string().into()));
|
||||
let mut sorted_components = components;
|
||||
sorted_components.sort_by_key(|component| component.sort_name());
|
||||
|
||||
for component in components {
|
||||
for component in sorted_components {
|
||||
entries.push(PreviewEntry::Component(component));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (scope, components) in &scope_groups {
|
||||
if let Some(ComponentScope::Unknown(_)) = scope {
|
||||
if !components.is_empty() {
|
||||
if let Some(scope_value) = scope {
|
||||
entries.push(PreviewEntry::SectionHeader(scope_value.to_string().into()));
|
||||
}
|
||||
|
||||
for component in components {
|
||||
entries.push(PreviewEntry::Component(component.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(components) = scope_groups.get(&None) {
|
||||
if let Some(components) = scope_groups.get(&ComponentScope::None) {
|
||||
if !components.is_empty() {
|
||||
entries.push(PreviewEntry::Separator);
|
||||
entries.push(PreviewEntry::SectionHeader("Uncategorized".into()));
|
||||
let mut sorted_components = components.clone();
|
||||
sorted_components.sort_by_key(|c| c.sort_name());
|
||||
|
||||
for component in components {
|
||||
for component in sorted_components {
|
||||
entries.push(PreviewEntry::Component(component.clone()));
|
||||
}
|
||||
}
|
||||
@@ -250,7 +242,10 @@ impl ComponentPreview {
|
||||
let id = component_metadata.id();
|
||||
let selected = self.active_page == PreviewPage::Component(id.clone());
|
||||
ListItem::new(ix)
|
||||
.child(Label::new(component_metadata.name().clone()).color(Color::Default))
|
||||
.child(
|
||||
Label::new(component_metadata.scopeless_name().clone())
|
||||
.color(Color::Default),
|
||||
)
|
||||
.selectable(true)
|
||||
.toggle_state(selected)
|
||||
.inset(true)
|
||||
@@ -333,7 +328,7 @@ impl ComponentPreview {
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> impl IntoElement {
|
||||
let name = component.name();
|
||||
let name = component.scopeless_name();
|
||||
let scope = component.scope();
|
||||
|
||||
let description = component.description();
|
||||
@@ -354,13 +349,12 @@ impl ComponentPreview {
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.text_xl()
|
||||
.child(div().child(name))
|
||||
.when_some(scope, |this, scope| {
|
||||
h_flex().gap_1().text_xl().child(div().child(name)).when(
|
||||
!matches!(scope, ComponentScope::None),
|
||||
|this| {
|
||||
this.child(div().opacity(0.5).child(format!("({})", scope)))
|
||||
}),
|
||||
},
|
||||
),
|
||||
)
|
||||
.when_some(description, |this, description| {
|
||||
this.child(
|
||||
@@ -373,7 +367,7 @@ impl ComponentPreview {
|
||||
}),
|
||||
)
|
||||
.when_some(component.preview(), |this, preview| {
|
||||
this.child(preview(window, cx))
|
||||
this.children(preview(window, cx))
|
||||
}),
|
||||
)
|
||||
.into_any_element()
|
||||
@@ -395,17 +389,16 @@ impl ComponentPreview {
|
||||
fn render_component_page(
|
||||
&mut self,
|
||||
component_id: &ComponentId,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
_window: &mut Window,
|
||||
_cx: &mut Context<Self>,
|
||||
) -> impl IntoElement {
|
||||
let component = self.component_map.get(&component_id);
|
||||
|
||||
if let Some(component) = component {
|
||||
v_flex()
|
||||
.w_full()
|
||||
.flex_initial()
|
||||
.min_h_full()
|
||||
.child(self.render_preview(component, window, cx))
|
||||
.id("render-component-page")
|
||||
.size_full()
|
||||
.child(ComponentPreviewPage::new(component.clone()))
|
||||
.into_any_element()
|
||||
} else {
|
||||
v_flex()
|
||||
@@ -445,10 +438,11 @@ impl Render for ComponentPreview {
|
||||
.overflow_hidden()
|
||||
.size_full()
|
||||
.track_focus(&self.focus_handle)
|
||||
.px_2()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.child(
|
||||
v_flex()
|
||||
.border_r_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.h_full()
|
||||
.child(
|
||||
uniform_list(
|
||||
@@ -465,6 +459,7 @@ impl Render for ComponentPreview {
|
||||
)
|
||||
.track_scroll(self.nav_scroll_handle.clone())
|
||||
.pt_4()
|
||||
.px_4()
|
||||
.w(px(240.))
|
||||
.h_full()
|
||||
.flex_1(),
|
||||
@@ -527,6 +522,7 @@ impl Item for ComponentPreview {
|
||||
let user_store = self.user_store.clone();
|
||||
let weak_workspace = self.workspace.clone();
|
||||
let selected_index = self.cursor_index;
|
||||
let active_page = self.active_page.clone();
|
||||
|
||||
Some(cx.new(|cx| {
|
||||
Self::new(
|
||||
@@ -534,6 +530,7 @@ impl Item for ComponentPreview {
|
||||
language_registry,
|
||||
user_store,
|
||||
selected_index,
|
||||
Some(active_page),
|
||||
cx,
|
||||
)
|
||||
}))
|
||||
@@ -566,7 +563,14 @@ impl SerializableItem for ComponentPreview {
|
||||
let weak_workspace = workspace.clone();
|
||||
cx.update(|_, cx| {
|
||||
Ok(cx.new(|cx| {
|
||||
ComponentPreview::new(weak_workspace, language_registry, user_store, None, cx)
|
||||
ComponentPreview::new(
|
||||
weak_workspace,
|
||||
language_registry,
|
||||
user_store,
|
||||
None,
|
||||
None,
|
||||
cx,
|
||||
)
|
||||
}))
|
||||
})?
|
||||
})
|
||||
@@ -600,3 +604,76 @@ impl SerializableItem for ComponentPreview {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct ComponentPreviewPage {
|
||||
// languages: Arc<LanguageRegistry>,
|
||||
component: ComponentMetadata,
|
||||
}
|
||||
|
||||
impl ComponentPreviewPage {
|
||||
pub fn new(
|
||||
component: ComponentMetadata,
|
||||
// languages: Arc<LanguageRegistry>
|
||||
) -> Self {
|
||||
Self {
|
||||
// languages,
|
||||
component,
|
||||
}
|
||||
}
|
||||
|
||||
fn render_header(&self, _: &Window, cx: &App) -> impl IntoElement {
|
||||
v_flex()
|
||||
.px_12()
|
||||
.pt_16()
|
||||
.pb_12()
|
||||
.gap_6()
|
||||
.bg(cx.theme().colors().surface_background)
|
||||
.border_b_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_0p5()
|
||||
.child(
|
||||
Label::new(self.component.scope().to_string())
|
||||
.size(LabelSize::Small)
|
||||
.color(Color::Muted),
|
||||
)
|
||||
.child(
|
||||
Headline::new(self.component.scopeless_name()).size(HeadlineSize::XLarge),
|
||||
),
|
||||
)
|
||||
.when_some(self.component.description(), |this, description| {
|
||||
this.child(div().text_sm().child(description))
|
||||
})
|
||||
}
|
||||
|
||||
fn render_preview(&self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
v_flex()
|
||||
.flex_1()
|
||||
.px_12()
|
||||
.py_6()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.child(if let Some(preview) = self.component.preview() {
|
||||
preview(window, cx).unwrap_or_else(|| {
|
||||
div()
|
||||
.child("Failed to load preview. This path should be unreachable")
|
||||
.into_any_element()
|
||||
})
|
||||
} else {
|
||||
div().child("No preview available").into_any_element()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for ComponentPreviewPage {
|
||||
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
v_flex()
|
||||
.id("component-preview-page")
|
||||
.overflow_y_scroll()
|
||||
.overflow_x_hidden()
|
||||
.w_full()
|
||||
.child(self.render_header(window, cx))
|
||||
.child(self.render_preview(window, cx))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ impl Tool for ContextServerTool {
|
||||
}
|
||||
}
|
||||
|
||||
fn needs_confirmation(&self) -> bool {
|
||||
fn needs_confirmation(&self, _: &serde_json::Value, _: &App) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
|
||||
@@ -1280,10 +1280,6 @@ mod tests {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn to_proto(&self, _: &App) -> rpc::proto::File {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ use dap::{
|
||||
};
|
||||
use futures::{SinkExt as _, channel::mpsc};
|
||||
use gpui::{
|
||||
Action, App, AsyncWindowContext, Context, Entity, EventEmitter, FocusHandle, Focusable,
|
||||
Subscription, Task, WeakEntity, actions,
|
||||
Action, App, AsyncWindowContext, Context, Entity, EntityId, EventEmitter, FocusHandle,
|
||||
Focusable, Subscription, Task, WeakEntity, actions,
|
||||
};
|
||||
use project::{
|
||||
Project,
|
||||
@@ -336,6 +336,95 @@ impl DebugPanel {
|
||||
})
|
||||
}
|
||||
|
||||
fn close_session(&mut self, entity_id: EntityId, cx: &mut Context<Self>) {
|
||||
let Some(session) = self
|
||||
.sessions
|
||||
.iter()
|
||||
.find(|other| entity_id == other.entity_id())
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
session.update(cx, |session, cx| session.shutdown(cx));
|
||||
|
||||
self.sessions.retain(|other| entity_id != other.entity_id());
|
||||
|
||||
if let Some(active_session_id) = self
|
||||
.active_session
|
||||
.as_ref()
|
||||
.map(|session| session.entity_id())
|
||||
{
|
||||
if active_session_id == entity_id {
|
||||
self.active_session = self.sessions.first().cloned();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn sessions_drop_down_menu(
|
||||
&self,
|
||||
active_session: &Entity<DebugSession>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> DropdownMenu {
|
||||
let sessions = self.sessions.clone();
|
||||
let weak = cx.weak_entity();
|
||||
let label = active_session.read(cx).label_element(cx);
|
||||
|
||||
DropdownMenu::new_with_element(
|
||||
"debugger-session-list",
|
||||
label,
|
||||
ContextMenu::build(window, cx, move |mut this, _, _| {
|
||||
for session in sessions.into_iter() {
|
||||
let weak_session = session.downgrade();
|
||||
let weak_id = weak_session.entity_id();
|
||||
|
||||
this = this.custom_entry(
|
||||
{
|
||||
let weak = weak.clone();
|
||||
move |_, cx| {
|
||||
weak_session
|
||||
.read_with(cx, |session, cx| {
|
||||
h_flex()
|
||||
.w_full()
|
||||
.justify_between()
|
||||
.child(session.label_element(cx))
|
||||
.child(
|
||||
IconButton::new(
|
||||
"close-debug-session",
|
||||
IconName::Close,
|
||||
)
|
||||
.icon_size(IconSize::Small)
|
||||
.on_click({
|
||||
let weak = weak.clone();
|
||||
move |_, _, cx| {
|
||||
weak.update(cx, |panel, cx| {
|
||||
panel.close_session(weak_id, cx);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}),
|
||||
)
|
||||
.into_any_element()
|
||||
})
|
||||
.unwrap_or_else(|_| div().into_any_element())
|
||||
}
|
||||
},
|
||||
{
|
||||
let weak = weak.clone();
|
||||
move |window, cx| {
|
||||
weak.update(cx, |panel, cx| {
|
||||
panel.activate_session(session.clone(), window, cx);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
this
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fn top_controls_strip(&self, window: &mut Window, cx: &mut Context<Self>) -> Option<Div> {
|
||||
let active_session = self.active_session.clone();
|
||||
|
||||
@@ -529,34 +618,8 @@ impl DebugPanel {
|
||||
},
|
||||
)
|
||||
.when_some(active_session.as_ref(), |this, session| {
|
||||
let sessions = self.sessions.clone();
|
||||
let weak = cx.weak_entity();
|
||||
let label = session.read(cx).label(cx);
|
||||
this.child(DropdownMenu::new(
|
||||
"debugger-session-list",
|
||||
label,
|
||||
ContextMenu::build(window, cx, move |mut this, _, cx| {
|
||||
for item in sessions {
|
||||
let weak = weak.clone();
|
||||
this = this.entry(
|
||||
session.read(cx).label(cx),
|
||||
None,
|
||||
move |window, cx| {
|
||||
weak.update(cx, |panel, cx| {
|
||||
panel.activate_session(
|
||||
item.clone(),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
})
|
||||
.ok();
|
||||
},
|
||||
);
|
||||
}
|
||||
this
|
||||
}),
|
||||
))
|
||||
.child(Divider::vertical())
|
||||
let context_menu = self.sessions_drop_down_menu(session, window, cx);
|
||||
this.child(context_menu).child(Divider::vertical())
|
||||
})
|
||||
.child(
|
||||
IconButton::new("debug-new-session", IconName::Plus)
|
||||
|
||||
@@ -31,6 +31,7 @@ pub(super) struct NewSessionModal {
|
||||
debug_panel: WeakEntity<DebugPanel>,
|
||||
mode: NewSessionMode,
|
||||
stop_on_entry: ToggleState,
|
||||
initialize_args: Option<serde_json::Value>,
|
||||
debugger: Option<SharedString>,
|
||||
last_selected_profile_name: Option<SharedString>,
|
||||
}
|
||||
@@ -82,17 +83,17 @@ impl NewSessionModal {
|
||||
.map(Into::into)
|
||||
.unwrap_or(ToggleState::Unselected),
|
||||
last_selected_profile_name: None,
|
||||
initialize_args: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn debug_config(&self, cx: &App) -> Option<DebugTaskDefinition> {
|
||||
let request = self.mode.debug_task(cx);
|
||||
|
||||
Some(DebugTaskDefinition {
|
||||
adapter: self.debugger.clone()?.to_string(),
|
||||
label: suggested_label(&request, self.debugger.as_deref()?),
|
||||
request,
|
||||
initialize_args: None,
|
||||
initialize_args: self.initialize_args.clone(),
|
||||
tcp_connection: None,
|
||||
locator: None,
|
||||
stop_on_entry: match self.stop_on_entry {
|
||||
@@ -228,7 +229,7 @@ impl NewSessionModal {
|
||||
weak.update(cx, |this, cx| {
|
||||
this.last_selected_profile_name = Some(SharedString::from(&task.label));
|
||||
this.debugger = Some(task.adapter.clone().into());
|
||||
|
||||
this.initialize_args = task.initialize_args.clone();
|
||||
match &task.request {
|
||||
DebugRequestType::Launch(launch_config) => {
|
||||
this.mode = NewSessionMode::launch(
|
||||
|
||||
@@ -7,7 +7,7 @@ use project::debugger::{dap_store::DapStore, session::Session};
|
||||
use project::worktree_store::WorktreeStore;
|
||||
use rpc::proto::{self, PeerId};
|
||||
use running::RunningState;
|
||||
use ui::prelude::*;
|
||||
use ui::{Indicator, prelude::*};
|
||||
use workspace::{
|
||||
FollowableItem, ViewId, Workspace,
|
||||
item::{self, Item},
|
||||
@@ -81,7 +81,6 @@ impl DebugSession {
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(unused)]
|
||||
pub(crate) fn shutdown(&mut self, cx: &mut Context<Self>) {
|
||||
match &self.mode {
|
||||
DebugSessionState::Running(state) => state.update(cx, |state, cx| state.shutdown(cx)),
|
||||
@@ -108,6 +107,33 @@ impl DebugSession {
|
||||
.expect("Remote Debug Sessions are not implemented yet")
|
||||
.label()
|
||||
}
|
||||
|
||||
pub(crate) fn label_element(&self, cx: &App) -> AnyElement {
|
||||
let label = self.label(cx);
|
||||
|
||||
let (icon, color) = match &self.mode {
|
||||
DebugSessionState::Running(state) => {
|
||||
if state.read(cx).session().read(cx).is_terminated() {
|
||||
(Some(Indicator::dot().color(Color::Error)), Color::Error)
|
||||
} else {
|
||||
match state.read(cx).thread_status(cx).unwrap_or_default() {
|
||||
project::debugger::session::ThreadStatus::Stopped => (
|
||||
Some(Indicator::dot().color(Color::Conflict)),
|
||||
Color::Conflict,
|
||||
),
|
||||
_ => (Some(Indicator::dot().color(Color::Success)), Color::Success),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.when_some(icon, |this, indicator| this.child(indicator))
|
||||
.justify_between()
|
||||
.child(Label::new(label).color(color))
|
||||
.into_any_element()
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<DebugPanelItemEvent> for DebugSession {}
|
||||
|
||||
@@ -242,6 +242,7 @@ fn new_debugger_pane(
|
||||
})));
|
||||
pane.display_nav_history_buttons(None);
|
||||
pane.set_custom_drop_handle(cx, custom_drop_handle);
|
||||
pane.set_render_tab_bar_buttons(cx, |_, _, _| (None, None));
|
||||
pane
|
||||
});
|
||||
|
||||
@@ -343,12 +344,13 @@ impl RunningState {
|
||||
SharedString::new_static("Modules"),
|
||||
cx,
|
||||
)),
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
this.activate_item(0, false, false, window, cx);
|
||||
});
|
||||
let rightmost_pane = new_debugger_pane(workspace.clone(), project.clone(), window, cx);
|
||||
rightmost_pane.update(cx, |this, cx| {
|
||||
@@ -446,7 +448,7 @@ impl RunningState {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn activate_variable_list(&self, window: &mut Window, cx: &mut App) {
|
||||
pub(crate) fn activate_modules_list(&self, window: &mut Window, cx: &mut App) {
|
||||
let (variable_list_position, pane) = self
|
||||
.panes
|
||||
.panes()
|
||||
@@ -454,7 +456,7 @@ impl RunningState {
|
||||
.find_map(|pane| {
|
||||
pane.read(cx)
|
||||
.items_of_type::<SubView>()
|
||||
.position(|view| view.read(cx).tab_name == *"Variables")
|
||||
.position(|view| view.read(cx).tab_name == *"Modules")
|
||||
.map(|view| (view, pane))
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
@@ -8,7 +8,7 @@ use dap::OutputEvent;
|
||||
use editor::{CompletionProvider, Editor, EditorElement, EditorStyle, ExcerptId};
|
||||
use fuzzy::StringMatchCandidate;
|
||||
use gpui::{Context, Entity, Render, Subscription, Task, TextStyle, WeakEntity};
|
||||
use language::{Buffer, CodeLabel};
|
||||
use language::{Buffer, CodeLabel, ToOffset};
|
||||
use menu::Confirm;
|
||||
use project::{
|
||||
Completion,
|
||||
@@ -356,7 +356,7 @@ impl ConsoleQueryBarCompletionProvider {
|
||||
let variable_value = variables.get(&string_match.string)?;
|
||||
|
||||
Some(project::Completion {
|
||||
old_range: buffer_position..buffer_position,
|
||||
replace_range: buffer_position..buffer_position,
|
||||
new_text: string_match.string.clone(),
|
||||
label: CodeLabel {
|
||||
filter_range: 0..string_match.string.len(),
|
||||
@@ -392,25 +392,61 @@ impl ConsoleQueryBarCompletionProvider {
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
let snapshot = buffer.read(cx).text_snapshot();
|
||||
cx.background_executor().spawn(async move {
|
||||
let completions = completion_task.await?;
|
||||
|
||||
Ok(Some(
|
||||
completion_task
|
||||
.await?
|
||||
.iter()
|
||||
.map(|completion| project::Completion {
|
||||
old_range: buffer_position..buffer_position, // TODO(debugger): change this
|
||||
new_text: completion.text.clone().unwrap_or(completion.label.clone()),
|
||||
label: CodeLabel {
|
||||
filter_range: 0..completion.label.len(),
|
||||
text: completion.label.clone(),
|
||||
runs: Vec::new(),
|
||||
},
|
||||
icon_path: None,
|
||||
documentation: None,
|
||||
confirm: None,
|
||||
source: project::CompletionSource::Custom,
|
||||
insert_text_mode: None,
|
||||
completions
|
||||
.into_iter()
|
||||
.map(|completion| {
|
||||
let new_text = completion
|
||||
.text
|
||||
.as_ref()
|
||||
.unwrap_or(&completion.label)
|
||||
.to_owned();
|
||||
let mut word_bytes_length = 0;
|
||||
for chunk in snapshot
|
||||
.reversed_chunks_in_range(language::Anchor::MIN..buffer_position)
|
||||
{
|
||||
let mut processed_bytes = 0;
|
||||
if let Some(_) = chunk.chars().rfind(|c| {
|
||||
let is_whitespace = c.is_whitespace();
|
||||
if !is_whitespace {
|
||||
processed_bytes += c.len_utf8();
|
||||
}
|
||||
|
||||
is_whitespace
|
||||
}) {
|
||||
word_bytes_length += processed_bytes;
|
||||
break;
|
||||
} else {
|
||||
word_bytes_length += chunk.len();
|
||||
}
|
||||
}
|
||||
|
||||
let buffer_offset = buffer_position.to_offset(&snapshot);
|
||||
let start = buffer_offset - word_bytes_length;
|
||||
let start = snapshot.anchor_before(start);
|
||||
let replace_range = start..buffer_position;
|
||||
|
||||
project::Completion {
|
||||
replace_range,
|
||||
new_text,
|
||||
label: CodeLabel {
|
||||
filter_range: 0..completion.label.len(),
|
||||
text: completion.label,
|
||||
runs: Vec::new(),
|
||||
},
|
||||
icon_path: None,
|
||||
documentation: None,
|
||||
confirm: None,
|
||||
source: project::CompletionSource::BufferWord {
|
||||
word_range: buffer_position..language::Anchor::MAX,
|
||||
resolved: false,
|
||||
},
|
||||
insert_text_mode: None,
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
))
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
use anyhow::anyhow;
|
||||
use gpui::{
|
||||
AnyElement, Empty, Entity, FocusHandle, Focusable, ListState, Subscription, WeakEntity, list,
|
||||
AnyElement, Empty, Entity, FocusHandle, Focusable, ListState, MouseButton, Stateful,
|
||||
Subscription, WeakEntity, list,
|
||||
};
|
||||
use project::{
|
||||
ProjectItem as _, ProjectPath,
|
||||
debugger::session::{Session, SessionEvent},
|
||||
};
|
||||
use std::{path::Path, sync::Arc};
|
||||
use ui::prelude::*;
|
||||
use ui::{Scrollbar, ScrollbarState, prelude::*};
|
||||
use util::maybe;
|
||||
use workspace::Workspace;
|
||||
|
||||
@@ -17,6 +18,7 @@ pub struct ModuleList {
|
||||
session: Entity<Session>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
focus_handle: FocusHandle,
|
||||
scrollbar_state: ScrollbarState,
|
||||
_subscription: Subscription,
|
||||
}
|
||||
|
||||
@@ -50,6 +52,7 @@ impl ModuleList {
|
||||
});
|
||||
|
||||
Self {
|
||||
scrollbar_state: ScrollbarState::new(list.clone()),
|
||||
list,
|
||||
session,
|
||||
workspace,
|
||||
@@ -153,6 +156,38 @@ impl ModuleList {
|
||||
self.session
|
||||
.update(cx, |session, cx| session.modules(cx).to_vec())
|
||||
}
|
||||
fn render_vertical_scrollbar(&self, cx: &mut Context<Self>) -> Stateful<Div> {
|
||||
div()
|
||||
.occlude()
|
||||
.id("module-list-vertical-scrollbar")
|
||||
.on_mouse_move(cx.listener(|_, _, _, cx| {
|
||||
cx.notify();
|
||||
cx.stop_propagation()
|
||||
}))
|
||||
.on_hover(|_, _, cx| {
|
||||
cx.stop_propagation();
|
||||
})
|
||||
.on_any_mouse_down(|_, _, cx| {
|
||||
cx.stop_propagation();
|
||||
})
|
||||
.on_mouse_up(
|
||||
MouseButton::Left,
|
||||
cx.listener(|_, _, _, cx| {
|
||||
cx.stop_propagation();
|
||||
}),
|
||||
)
|
||||
.on_scroll_wheel(cx.listener(|_, _, _, cx| {
|
||||
cx.notify();
|
||||
}))
|
||||
.h_full()
|
||||
.absolute()
|
||||
.right_1()
|
||||
.top_1()
|
||||
.bottom_0()
|
||||
.w(px(12.))
|
||||
.cursor_default()
|
||||
.children(Scrollbar::vertical(self.scrollbar_state.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Focusable for ModuleList {
|
||||
@@ -177,5 +212,6 @@ impl Render for ModuleList {
|
||||
.size_full()
|
||||
.p_1()
|
||||
.child(list(self.list.clone()).size_full())
|
||||
.child(self.render_vertical_scrollbar(cx))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,14 @@ use std::sync::Arc;
|
||||
use anyhow::{Result, anyhow};
|
||||
use dap::StackFrameId;
|
||||
use gpui::{
|
||||
AnyElement, Entity, EventEmitter, FocusHandle, Focusable, ListState, Subscription, Task,
|
||||
WeakEntity, list,
|
||||
AnyElement, Entity, EventEmitter, FocusHandle, Focusable, ListState, MouseButton, Stateful,
|
||||
Subscription, Task, WeakEntity, list,
|
||||
};
|
||||
|
||||
use language::PointUtf16;
|
||||
use project::debugger::session::{Session, SessionEvent, StackFrame};
|
||||
use project::{ProjectItem, ProjectPath};
|
||||
use ui::{Tooltip, prelude::*};
|
||||
use ui::{Scrollbar, ScrollbarState, Tooltip, prelude::*};
|
||||
use util::ResultExt;
|
||||
use workspace::Workspace;
|
||||
|
||||
@@ -32,6 +32,7 @@ pub struct StackFrameList {
|
||||
entries: Vec<StackFrameEntry>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
current_stack_frame_id: Option<StackFrameId>,
|
||||
scrollbar_state: ScrollbarState,
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
@@ -75,6 +76,7 @@ impl StackFrameList {
|
||||
});
|
||||
|
||||
Self {
|
||||
scrollbar_state: ScrollbarState::new(list.clone()),
|
||||
list,
|
||||
session,
|
||||
workspace,
|
||||
@@ -493,6 +495,39 @@ impl StackFrameList {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn render_vertical_scrollbar(&self, cx: &mut Context<Self>) -> Stateful<Div> {
|
||||
div()
|
||||
.occlude()
|
||||
.id("stack-frame-list-vertical-scrollbar")
|
||||
.on_mouse_move(cx.listener(|_, _, _, cx| {
|
||||
cx.notify();
|
||||
cx.stop_propagation()
|
||||
}))
|
||||
.on_hover(|_, _, cx| {
|
||||
cx.stop_propagation();
|
||||
})
|
||||
.on_any_mouse_down(|_, _, cx| {
|
||||
cx.stop_propagation();
|
||||
})
|
||||
.on_mouse_up(
|
||||
MouseButton::Left,
|
||||
cx.listener(|_, _, _, cx| {
|
||||
cx.stop_propagation();
|
||||
}),
|
||||
)
|
||||
.on_scroll_wheel(cx.listener(|_, _, _, cx| {
|
||||
cx.notify();
|
||||
}))
|
||||
.h_full()
|
||||
.absolute()
|
||||
.right_1()
|
||||
.top_1()
|
||||
.bottom_0()
|
||||
.w(px(12.))
|
||||
.cursor_default()
|
||||
.children(Scrollbar::vertical(self.scrollbar_state.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for StackFrameList {
|
||||
@@ -507,6 +542,7 @@ impl Render for StackFrameList {
|
||||
.size_full()
|
||||
.p_1()
|
||||
.child(list(self.list.clone()).size_full())
|
||||
.child(self.render_vertical_scrollbar(cx))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -138,7 +138,8 @@ async fn test_module_list(executor: BackgroundExecutor, cx: &mut TestAppContext)
|
||||
.clone()
|
||||
});
|
||||
|
||||
running_state.update(cx, |_, cx| {
|
||||
running_state.update_in(cx, |this, window, cx| {
|
||||
this.activate_modules_list(window, cx);
|
||||
cx.refresh_windows();
|
||||
});
|
||||
|
||||
|
||||
@@ -207,9 +207,6 @@ async fn test_basic_fetch_initial_scope_and_variables(
|
||||
.expect("Session should be running by this point")
|
||||
.clone()
|
||||
});
|
||||
running_state.update_in(cx, |this, window, cx| {
|
||||
this.activate_variable_list(window, cx);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
|
||||
running_state.update(cx, |running_state, cx| {
|
||||
@@ -481,9 +478,6 @@ async fn test_fetch_variables_for_multiple_scopes(
|
||||
.expect("Session should be running by this point")
|
||||
.clone()
|
||||
});
|
||||
running_state.update_in(cx, |this, window, cx| {
|
||||
this.activate_variable_list(window, cx);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
|
||||
running_state.update(cx, |running_state, cx| {
|
||||
@@ -800,10 +794,6 @@ async fn test_keyboard_navigation(executor: BackgroundExecutor, cx: &mut TestApp
|
||||
variable_list.update(cx, |_, cx| cx.focus_self(window));
|
||||
running
|
||||
});
|
||||
running_state.update_in(cx, |this, window, cx| {
|
||||
this.activate_variable_list(window, cx);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
cx.dispatch_action(SelectFirst);
|
||||
cx.dispatch_action(SelectFirst);
|
||||
cx.run_until_parked();
|
||||
@@ -1572,21 +1562,6 @@ async fn test_variable_list_only_sends_requests_when_rendering(
|
||||
|
||||
cx.run_until_parked();
|
||||
|
||||
// We shouldn't make any variable requests unless we're rendering the variable list
|
||||
running_state.update_in(cx, |running_state, window, cx| {
|
||||
let variable_list = running_state.variable_list().read(cx);
|
||||
let empty: Vec<dap::Variable> = vec![];
|
||||
|
||||
assert_eq!(empty, variable_list.variables());
|
||||
assert!(!made_scopes_request.load(Ordering::SeqCst));
|
||||
|
||||
cx.focus_self(window);
|
||||
});
|
||||
running_state.update_in(cx, |this, window, cx| {
|
||||
this.activate_variable_list(window, cx);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
|
||||
running_state.update(cx, |running_state, cx| {
|
||||
let (stack_frame_list, stack_frame_id) =
|
||||
running_state.stack_frame_list().update(cx, |list, _| {
|
||||
@@ -1898,10 +1873,6 @@ async fn test_it_fetches_scopes_variables_when_you_select_a_stack_frame(
|
||||
.expect("Session should be running by this point")
|
||||
.clone()
|
||||
});
|
||||
running_state.update_in(cx, |this, window, cx| {
|
||||
this.activate_variable_list(window, cx);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
|
||||
running_state.update(cx, |running_state, cx| {
|
||||
let (stack_frame_list, stack_frame_id) =
|
||||
|
||||
@@ -3,6 +3,7 @@ use super::*;
|
||||
use gpui::{action_as, action_with_deprecated_aliases, actions};
|
||||
use schemars::JsonSchema;
|
||||
use util::serde::default_true;
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct SelectNext {
|
||||
@@ -262,6 +263,8 @@ actions!(
|
||||
Cancel,
|
||||
CancelLanguageServerWork,
|
||||
ConfirmRename,
|
||||
ConfirmCompletionInsert,
|
||||
ConfirmCompletionReplace,
|
||||
ContextMenuFirst,
|
||||
ContextMenuLast,
|
||||
ContextMenuNext,
|
||||
|
||||
@@ -230,7 +230,7 @@ impl CompletionsMenu {
|
||||
let completions = choices
|
||||
.iter()
|
||||
.map(|choice| Completion {
|
||||
old_range: selection.start.text_anchor..selection.end.text_anchor,
|
||||
replace_range: selection.start.text_anchor..selection.end.text_anchor,
|
||||
new_text: choice.to_string(),
|
||||
label: CodeLabel {
|
||||
text: choice.to_string(),
|
||||
|
||||
@@ -109,8 +109,8 @@ use language::{
|
||||
IndentKind, IndentSize, Language, OffsetRangeExt, Point, Selection, SelectionGoal, TextObject,
|
||||
TransactionId, TreeSitterOptions, WordsQuery,
|
||||
language_settings::{
|
||||
self, InlayHintSettings, RewrapBehavior, WordsCompletionMode, all_language_settings,
|
||||
language_settings,
|
||||
self, InlayHintSettings, LspInsertMode, RewrapBehavior, WordsCompletionMode,
|
||||
all_language_settings, language_settings,
|
||||
},
|
||||
point_from_lsp, text_diff_with_options,
|
||||
};
|
||||
@@ -131,7 +131,7 @@ pub use proposed_changes_editor::{
|
||||
};
|
||||
use smallvec::smallvec;
|
||||
use std::{cell::OnceCell, iter::Peekable};
|
||||
use task::{ResolvedTask, TaskTemplate, TaskVariables};
|
||||
use task::{ResolvedTask, RunnableTag, TaskTemplate, TaskVariables};
|
||||
|
||||
pub use lsp::CompletionContext;
|
||||
use lsp::{
|
||||
@@ -140,6 +140,7 @@ use lsp::{
|
||||
};
|
||||
|
||||
use language::BufferSnapshot;
|
||||
pub use lsp_ext::lsp_tasks;
|
||||
use movement::TextLayoutDetails;
|
||||
pub use multi_buffer::{
|
||||
Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, RowInfo,
|
||||
@@ -1384,7 +1385,9 @@ impl Editor {
|
||||
window,
|
||||
|editor, _, event, window, cx| match event {
|
||||
BreakpointStoreEvent::ActiveDebugLineChanged => {
|
||||
editor.go_to_active_debug_line(window, cx);
|
||||
if editor.go_to_active_debug_line(window, cx) {
|
||||
cx.stop_propagation();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
@@ -1607,17 +1610,25 @@ impl Editor {
|
||||
}
|
||||
this.tasks_update_task = Some(this.refresh_runnables(window, cx));
|
||||
this._subscriptions.extend(project_subscriptions);
|
||||
this._subscriptions
|
||||
.push(cx.subscribe_self(|editor, e: &EditorEvent, cx| {
|
||||
|
||||
this._subscriptions.push(cx.subscribe_in(
|
||||
&cx.entity(),
|
||||
window,
|
||||
|editor, _, e: &EditorEvent, window, cx| {
|
||||
if let EditorEvent::SelectionsChanged { local } = e {
|
||||
if *local {
|
||||
let new_anchor = editor.scroll_manager.anchor();
|
||||
let snapshot = editor.snapshot(window, cx);
|
||||
editor.update_restoration_data(cx, move |data| {
|
||||
data.scroll_anchor = new_anchor;
|
||||
data.scroll_position = (
|
||||
new_anchor.top_row(&snapshot.buffer_snapshot),
|
||||
new_anchor.offset,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}));
|
||||
},
|
||||
));
|
||||
|
||||
this.end_selection(window, cx);
|
||||
this.scroll_manager.show_scrollbars(window, cx);
|
||||
@@ -2367,22 +2378,30 @@ impl Editor {
|
||||
if selections.len() == 1 {
|
||||
cx.emit(SearchEvent::ActiveMatchChanged)
|
||||
}
|
||||
if local && self.is_singleton(cx) {
|
||||
let inmemory_selections = selections.iter().map(|s| s.range()).collect();
|
||||
self.update_restoration_data(cx, |data| {
|
||||
data.selections = inmemory_selections;
|
||||
});
|
||||
if local {
|
||||
if let Some((_, _, buffer_snapshot)) = buffer.as_singleton() {
|
||||
let inmemory_selections = selections
|
||||
.iter()
|
||||
.map(|s| {
|
||||
text::ToPoint::to_point(&s.range().start.text_anchor, buffer_snapshot)
|
||||
..text::ToPoint::to_point(&s.range().end.text_anchor, buffer_snapshot)
|
||||
})
|
||||
.collect();
|
||||
self.update_restoration_data(cx, |data| {
|
||||
data.selections = inmemory_selections;
|
||||
});
|
||||
|
||||
if WorkspaceSettings::get(None, cx).restore_on_startup != RestoreOnStartupBehavior::None
|
||||
{
|
||||
if let Some(workspace_id) =
|
||||
self.workspace.as_ref().and_then(|workspace| workspace.1)
|
||||
if WorkspaceSettings::get(None, cx).restore_on_startup
|
||||
!= RestoreOnStartupBehavior::None
|
||||
{
|
||||
let snapshot = self.buffer().read(cx).snapshot(cx);
|
||||
let selections = selections.clone();
|
||||
let background_executor = cx.background_executor().clone();
|
||||
let editor_id = cx.entity().entity_id().as_u64() as ItemId;
|
||||
self.serialize_selections = cx.background_spawn(async move {
|
||||
if let Some(workspace_id) =
|
||||
self.workspace.as_ref().and_then(|workspace| workspace.1)
|
||||
{
|
||||
let snapshot = self.buffer().read(cx).snapshot(cx);
|
||||
let selections = selections.clone();
|
||||
let background_executor = cx.background_executor().clone();
|
||||
let editor_id = cx.entity().entity_id().as_u64() as ItemId;
|
||||
self.serialize_selections = cx.background_spawn(async move {
|
||||
background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
|
||||
let db_selections = selections
|
||||
.iter()
|
||||
@@ -2399,6 +2418,7 @@ impl Editor {
|
||||
.with_context(|| format!("persisting editor selections for editor {editor_id}, workspace {workspace_id:?}"))
|
||||
.log_err();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2407,18 +2427,27 @@ impl Editor {
|
||||
}
|
||||
|
||||
fn folds_did_change(&mut self, cx: &mut Context<Self>) {
|
||||
if !self.is_singleton(cx)
|
||||
|| WorkspaceSettings::get(None, cx).restore_on_startup == RestoreOnStartupBehavior::None
|
||||
{
|
||||
use text::ToOffset as _;
|
||||
use text::ToPoint as _;
|
||||
|
||||
if WorkspaceSettings::get(None, cx).restore_on_startup == RestoreOnStartupBehavior::None {
|
||||
return;
|
||||
}
|
||||
|
||||
let snapshot = self.buffer().read(cx).snapshot(cx);
|
||||
let Some(singleton) = self.buffer().read(cx).as_singleton() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let snapshot = singleton.read(cx).snapshot();
|
||||
let inmemory_folds = self.display_map.update(cx, |display_map, cx| {
|
||||
display_map
|
||||
.snapshot(cx)
|
||||
.folds_in_range(0..snapshot.len())
|
||||
.map(|fold| fold.range.deref().clone())
|
||||
let display_snapshot = display_map.snapshot(cx);
|
||||
|
||||
display_snapshot
|
||||
.folds_in_range(0..display_snapshot.buffer_snapshot.len())
|
||||
.map(|fold| {
|
||||
fold.range.start.text_anchor.to_point(&snapshot)
|
||||
..fold.range.end.text_anchor.to_point(&snapshot)
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
self.update_restoration_data(cx, |data| {
|
||||
@@ -2436,8 +2465,8 @@ impl Editor {
|
||||
.folds_in_range(0..snapshot.len())
|
||||
.map(|fold| {
|
||||
(
|
||||
fold.range.start.to_offset(&snapshot),
|
||||
fold.range.end.to_offset(&snapshot),
|
||||
fold.range.start.text_anchor.to_offset(&snapshot),
|
||||
fold.range.end.text_anchor.to_offset(&snapshot),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
@@ -4433,7 +4462,7 @@ impl Editor {
|
||||
words.remove(&lsp_completion.new_text);
|
||||
}
|
||||
completions.extend(words.into_iter().map(|(word, word_range)| Completion {
|
||||
old_range: old_range.clone(),
|
||||
replace_range: old_range.clone(),
|
||||
new_text: word.clone(),
|
||||
label: CodeLabel::plain(word, None),
|
||||
icon_path: None,
|
||||
@@ -4540,6 +4569,26 @@ impl Editor {
|
||||
self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
|
||||
}
|
||||
|
||||
pub fn confirm_completion_insert(
|
||||
&mut self,
|
||||
_: &ConfirmCompletionInsert,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Option<Task<Result<()>>> {
|
||||
self.hide_mouse_cursor(&HideMouseCursorOrigin::TypingAction);
|
||||
self.do_completion(None, CompletionIntent::CompleteWithInsert, window, cx)
|
||||
}
|
||||
|
||||
pub fn confirm_completion_replace(
|
||||
&mut self,
|
||||
_: &ConfirmCompletionReplace,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Option<Task<Result<()>>> {
|
||||
self.hide_mouse_cursor(&HideMouseCursorOrigin::TypingAction);
|
||||
self.do_completion(None, CompletionIntent::CompleteWithReplace, window, cx)
|
||||
}
|
||||
|
||||
pub fn compose_completion(
|
||||
&mut self,
|
||||
action: &ComposeCompletion,
|
||||
@@ -4559,12 +4608,10 @@ impl Editor {
|
||||
) -> Option<Task<Result<()>>> {
|
||||
use language::ToOffset as _;
|
||||
|
||||
let completions_menu =
|
||||
if let CodeContextMenu::Completions(menu) = self.hide_context_menu(window, cx)? {
|
||||
menu
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
let CodeContextMenu::Completions(completions_menu) = self.hide_context_menu(window, cx)?
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let candidate_id = {
|
||||
let entries = completions_menu.entries.borrow();
|
||||
@@ -4593,9 +4640,12 @@ impl Editor {
|
||||
new_text = completion.new_text.clone();
|
||||
};
|
||||
let selections = self.selections.all::<usize>(cx);
|
||||
|
||||
let replace_range = choose_completion_range(&completion, intent, &buffer_handle, cx);
|
||||
let buffer = buffer_handle.read(cx);
|
||||
let old_range = completion.old_range.to_offset(buffer);
|
||||
let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
|
||||
let old_text = buffer
|
||||
.text_for_range(replace_range.clone())
|
||||
.collect::<String>();
|
||||
|
||||
let newest_selection = self.selections.newest_anchor();
|
||||
if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
|
||||
@@ -4606,8 +4656,8 @@ impl Editor {
|
||||
.start
|
||||
.text_anchor
|
||||
.to_offset(buffer)
|
||||
.saturating_sub(old_range.start);
|
||||
let lookahead = old_range
|
||||
.saturating_sub(replace_range.start);
|
||||
let lookahead = replace_range
|
||||
.end
|
||||
.saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
|
||||
let mut common_prefix_len = 0;
|
||||
@@ -4636,8 +4686,8 @@ impl Editor {
|
||||
ranges.clear();
|
||||
ranges.extend(selections.iter().map(|s| {
|
||||
if s.id == newest_selection.id {
|
||||
range_to_replace = Some(old_range.clone());
|
||||
old_range.clone()
|
||||
range_to_replace = Some(replace_range.clone());
|
||||
replace_range.clone()
|
||||
} else {
|
||||
s.start..s.end
|
||||
}
|
||||
@@ -12421,12 +12471,13 @@ impl Editor {
|
||||
return Task::ready(());
|
||||
}
|
||||
let project = self.project.as_ref().map(Entity::downgrade);
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
let task_sources = self.lsp_task_sources(cx);
|
||||
cx.spawn_in(window, async move |editor, cx| {
|
||||
cx.background_executor().timer(UPDATE_DEBOUNCE).await;
|
||||
let Some(project) = project.and_then(|p| p.upgrade()) else {
|
||||
return;
|
||||
};
|
||||
let Ok(display_snapshot) = this.update(cx, |this, cx| {
|
||||
let Ok(display_snapshot) = editor.update(cx, |this, cx| {
|
||||
this.display_map.update(cx, |map, cx| map.snapshot(cx))
|
||||
}) else {
|
||||
return;
|
||||
@@ -12449,15 +12500,77 @@ impl Editor {
|
||||
}
|
||||
})
|
||||
.await;
|
||||
let Ok(lsp_tasks) =
|
||||
cx.update(|_, cx| crate::lsp_tasks(project.clone(), &task_sources, None, cx))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let lsp_tasks = lsp_tasks.await;
|
||||
|
||||
let Ok(mut lsp_tasks_by_rows) = cx.update(|_, cx| {
|
||||
lsp_tasks
|
||||
.into_iter()
|
||||
.flat_map(|(kind, tasks)| {
|
||||
tasks.into_iter().filter_map(move |(location, task)| {
|
||||
Some((kind.clone(), location?, task))
|
||||
})
|
||||
})
|
||||
.fold(HashMap::default(), |mut acc, (kind, location, task)| {
|
||||
let buffer = location.target.buffer;
|
||||
let buffer_snapshot = buffer.read(cx).snapshot();
|
||||
let offset = display_snapshot.buffer_snapshot.excerpts().find_map(
|
||||
|(excerpt_id, snapshot, _)| {
|
||||
if snapshot.remote_id() == buffer_snapshot.remote_id() {
|
||||
display_snapshot
|
||||
.buffer_snapshot
|
||||
.anchor_in_excerpt(excerpt_id, location.target.range.start)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
);
|
||||
if let Some(offset) = offset {
|
||||
let task_buffer_range =
|
||||
location.target.range.to_point(&buffer_snapshot);
|
||||
let context_buffer_range =
|
||||
task_buffer_range.to_offset(&buffer_snapshot);
|
||||
let context_range = BufferOffset(context_buffer_range.start)
|
||||
..BufferOffset(context_buffer_range.end);
|
||||
|
||||
acc.entry((buffer_snapshot.remote_id(), task_buffer_range.start.row))
|
||||
.or_insert_with(|| RunnableTasks {
|
||||
templates: Vec::new(),
|
||||
offset,
|
||||
column: task_buffer_range.start.column,
|
||||
extra_variables: HashMap::default(),
|
||||
context_range,
|
||||
})
|
||||
.templates
|
||||
.push((kind, task.original_task().clone()));
|
||||
}
|
||||
|
||||
acc
|
||||
})
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
|
||||
this.update(cx, |this, _| {
|
||||
this.clear_tasks();
|
||||
for (key, value) in rows {
|
||||
this.insert_tasks(key, value);
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
editor
|
||||
.update(cx, |editor, _| {
|
||||
editor.clear_tasks();
|
||||
for (key, mut value) in rows {
|
||||
if let Some(lsp_tasks) = lsp_tasks_by_rows.remove(&key) {
|
||||
value.templates.extend(lsp_tasks.templates);
|
||||
}
|
||||
|
||||
editor.insert_tasks(key, value);
|
||||
}
|
||||
for (key, value) in lsp_tasks_by_rows {
|
||||
editor.insert_tasks(key, value);
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
}
|
||||
fn fetch_runnable_ranges(
|
||||
@@ -12472,7 +12585,7 @@ impl Editor {
|
||||
snapshot: DisplaySnapshot,
|
||||
runnable_ranges: Vec<RunnableRange>,
|
||||
mut cx: AsyncWindowContext,
|
||||
) -> Vec<((BufferId, u32), RunnableTasks)> {
|
||||
) -> Vec<((BufferId, BufferRow), RunnableTasks)> {
|
||||
runnable_ranges
|
||||
.into_iter()
|
||||
.filter_map(|mut runnable| {
|
||||
@@ -12529,11 +12642,9 @@ impl Editor {
|
||||
)
|
||||
});
|
||||
|
||||
let tags = mem::take(&mut runnable.tags);
|
||||
let mut tags: Vec<_> = tags
|
||||
let mut templates_with_tags = mem::take(&mut runnable.tags)
|
||||
.into_iter()
|
||||
.flat_map(|tag| {
|
||||
let tag = tag.0.clone();
|
||||
.flat_map(|RunnableTag(tag)| {
|
||||
inventory
|
||||
.as_ref()
|
||||
.into_iter()
|
||||
@@ -12550,20 +12661,20 @@ impl Editor {
|
||||
})
|
||||
})
|
||||
.sorted_by_key(|(kind, _)| kind.to_owned())
|
||||
.collect();
|
||||
if let Some((leading_tag_source, _)) = tags.first() {
|
||||
.collect::<Vec<_>>();
|
||||
if let Some((leading_tag_source, _)) = templates_with_tags.first() {
|
||||
// Strongest source wins; if we have worktree tag binding, prefer that to
|
||||
// global and language bindings;
|
||||
// if we have a global binding, prefer that to language binding.
|
||||
let first_mismatch = tags
|
||||
let first_mismatch = templates_with_tags
|
||||
.iter()
|
||||
.position(|(tag_source, _)| tag_source != leading_tag_source);
|
||||
if let Some(index) = first_mismatch {
|
||||
tags.truncate(index);
|
||||
templates_with_tags.truncate(index);
|
||||
}
|
||||
}
|
||||
|
||||
tags
|
||||
templates_with_tags
|
||||
}
|
||||
|
||||
pub fn move_to_enclosing_bracket(
|
||||
@@ -15884,8 +15995,9 @@ impl Editor {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let _ = maybe!({
|
||||
// Returns true if the editor handled a go-to-line request
|
||||
pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
|
||||
maybe!({
|
||||
let breakpoint_store = self.breakpoint_store.as_ref()?;
|
||||
|
||||
let Some((_, _, active_position)) =
|
||||
@@ -15903,6 +16015,7 @@ impl Editor {
|
||||
.read(cx)
|
||||
.snapshot();
|
||||
|
||||
let mut handled = false;
|
||||
for (id, ExcerptRange { context, .. }) in self
|
||||
.buffer
|
||||
.read(cx)
|
||||
@@ -15916,6 +16029,7 @@ impl Editor {
|
||||
let snapshot = self.buffer.read(cx).snapshot(cx);
|
||||
let multibuffer_anchor = snapshot.anchor_in_excerpt(id, active_position)?;
|
||||
|
||||
handled = true;
|
||||
self.clear_row_highlights::<DebugCurrentRowHighlight>();
|
||||
self.go_to_line::<DebugCurrentRowHighlight>(
|
||||
multibuffer_anchor,
|
||||
@@ -15926,9 +16040,9 @@ impl Editor {
|
||||
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
Some(())
|
||||
});
|
||||
handled.then_some(())
|
||||
})
|
||||
.is_some()
|
||||
}
|
||||
|
||||
pub fn copy_file_name_without_extension(
|
||||
@@ -17887,6 +18001,81 @@ impl Editor {
|
||||
}
|
||||
}
|
||||
|
||||
// Consider user intent and default settings
|
||||
fn choose_completion_range(
|
||||
completion: &Completion,
|
||||
intent: CompletionIntent,
|
||||
buffer: &Entity<Buffer>,
|
||||
cx: &mut Context<Editor>,
|
||||
) -> Range<usize> {
|
||||
fn should_replace(
|
||||
completion: &Completion,
|
||||
insert_range: &Range<text::Anchor>,
|
||||
intent: CompletionIntent,
|
||||
completion_mode_setting: LspInsertMode,
|
||||
buffer: &Buffer,
|
||||
) -> bool {
|
||||
// specific actions take precedence over settings
|
||||
match intent {
|
||||
CompletionIntent::CompleteWithInsert => return false,
|
||||
CompletionIntent::CompleteWithReplace => return true,
|
||||
CompletionIntent::Complete | CompletionIntent::Compose => {}
|
||||
}
|
||||
|
||||
match completion_mode_setting {
|
||||
LspInsertMode::Insert => false,
|
||||
LspInsertMode::Replace => true,
|
||||
LspInsertMode::ReplaceSubsequence => {
|
||||
let mut text_to_replace = buffer.chars_for_range(
|
||||
buffer.anchor_before(completion.replace_range.start)
|
||||
..buffer.anchor_after(completion.replace_range.end),
|
||||
);
|
||||
let mut completion_text = completion.new_text.chars();
|
||||
|
||||
// is `text_to_replace` a subsequence of `completion_text`
|
||||
text_to_replace
|
||||
.all(|needle_ch| completion_text.any(|haystack_ch| haystack_ch == needle_ch))
|
||||
}
|
||||
LspInsertMode::ReplaceSuffix => {
|
||||
let range_after_cursor = insert_range.end..completion.replace_range.end;
|
||||
|
||||
let text_after_cursor = buffer
|
||||
.text_for_range(
|
||||
buffer.anchor_before(range_after_cursor.start)
|
||||
..buffer.anchor_after(range_after_cursor.end),
|
||||
)
|
||||
.collect::<String>();
|
||||
completion.new_text.ends_with(&text_after_cursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let buffer = buffer.read(cx);
|
||||
|
||||
if let CompletionSource::Lsp {
|
||||
insert_range: Some(insert_range),
|
||||
..
|
||||
} = &completion.source
|
||||
{
|
||||
let completion_mode_setting =
|
||||
language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx)
|
||||
.completions
|
||||
.lsp_insert_mode;
|
||||
|
||||
if !should_replace(
|
||||
completion,
|
||||
&insert_range,
|
||||
intent,
|
||||
completion_mode_setting,
|
||||
buffer,
|
||||
) {
|
||||
return insert_range.to_offset(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
completion.replace_range.to_offset(buffer)
|
||||
}
|
||||
|
||||
fn insert_extra_newline_brackets(
|
||||
buffer: &MultiBufferSnapshot,
|
||||
range: Range<usize>,
|
||||
@@ -18608,9 +18797,10 @@ fn snippet_completions(
|
||||
end: lsp_end,
|
||||
};
|
||||
Some(Completion {
|
||||
old_range: range,
|
||||
replace_range: range,
|
||||
new_text: snippet.body.clone(),
|
||||
source: CompletionSource::Lsp {
|
||||
insert_range: None,
|
||||
server_id: LanguageServerId(usize::MAX),
|
||||
resolved: true,
|
||||
lsp_completion: Box::new(lsp::CompletionItem {
|
||||
|
||||
@@ -9218,7 +9218,7 @@ async fn test_completion_mode(cx: &mut TestAppContext) {
|
||||
initial_state: String,
|
||||
buffer_marked_text: String,
|
||||
completion_text: &'static str,
|
||||
expected_with_insertion_mode: String,
|
||||
expected_with_insert_mode: String,
|
||||
expected_with_replace_mode: String,
|
||||
expected_with_replace_subsequence_mode: String,
|
||||
expected_with_replace_suffix_mode: String,
|
||||
@@ -9230,7 +9230,7 @@ async fn test_completion_mode(cx: &mut TestAppContext) {
|
||||
initial_state: "before ediˇ after".into(),
|
||||
buffer_marked_text: "before <edi|> after".into(),
|
||||
completion_text: "editor",
|
||||
expected_with_insertion_mode: "before editorˇ after".into(),
|
||||
expected_with_insert_mode: "before editorˇ after".into(),
|
||||
expected_with_replace_mode: "before editorˇ after".into(),
|
||||
expected_with_replace_subsequence_mode: "before editorˇ after".into(),
|
||||
expected_with_replace_suffix_mode: "before editorˇ after".into(),
|
||||
@@ -9240,7 +9240,7 @@ async fn test_completion_mode(cx: &mut TestAppContext) {
|
||||
initial_state: "before ediˇtor after".into(),
|
||||
buffer_marked_text: "before <edi|tor> after".into(),
|
||||
completion_text: "editor",
|
||||
expected_with_insertion_mode: "before editorˇtor after".into(),
|
||||
expected_with_insert_mode: "before editorˇtor after".into(),
|
||||
expected_with_replace_mode: "before ediˇtor after".into(),
|
||||
expected_with_replace_subsequence_mode: "before ediˇtor after".into(),
|
||||
expected_with_replace_suffix_mode: "before ediˇtor after".into(),
|
||||
@@ -9250,7 +9250,7 @@ async fn test_completion_mode(cx: &mut TestAppContext) {
|
||||
initial_state: "before torˇ after".into(),
|
||||
buffer_marked_text: "before <tor|> after".into(),
|
||||
completion_text: "editor",
|
||||
expected_with_insertion_mode: "before editorˇ after".into(),
|
||||
expected_with_insert_mode: "before editorˇ after".into(),
|
||||
expected_with_replace_mode: "before editorˇ after".into(),
|
||||
expected_with_replace_subsequence_mode: "before editorˇ after".into(),
|
||||
expected_with_replace_suffix_mode: "before editorˇ after".into(),
|
||||
@@ -9260,7 +9260,7 @@ async fn test_completion_mode(cx: &mut TestAppContext) {
|
||||
initial_state: "before ˇtor after".into(),
|
||||
buffer_marked_text: "before <|tor> after".into(),
|
||||
completion_text: "editor",
|
||||
expected_with_insertion_mode: "before editorˇtor after".into(),
|
||||
expected_with_insert_mode: "before editorˇtor after".into(),
|
||||
expected_with_replace_mode: "before editorˇ after".into(),
|
||||
expected_with_replace_subsequence_mode: "before editorˇ after".into(),
|
||||
expected_with_replace_suffix_mode: "before editorˇ after".into(),
|
||||
@@ -9270,7 +9270,7 @@ async fn test_completion_mode(cx: &mut TestAppContext) {
|
||||
initial_state: "pˇfield: bool".into(),
|
||||
buffer_marked_text: "<p|field>: bool".into(),
|
||||
completion_text: "pub ",
|
||||
expected_with_insertion_mode: "pub ˇfield: bool".into(),
|
||||
expected_with_insert_mode: "pub ˇfield: bool".into(),
|
||||
expected_with_replace_mode: "pub ˇ: bool".into(),
|
||||
expected_with_replace_subsequence_mode: "pub ˇfield: bool".into(),
|
||||
expected_with_replace_suffix_mode: "pub ˇfield: bool".into(),
|
||||
@@ -9280,7 +9280,7 @@ async fn test_completion_mode(cx: &mut TestAppContext) {
|
||||
initial_state: "[element_ˇelement_2]".into(),
|
||||
buffer_marked_text: "[<element_|element_2>]".into(),
|
||||
completion_text: "element_1",
|
||||
expected_with_insertion_mode: "[element_1ˇelement_2]".into(),
|
||||
expected_with_insert_mode: "[element_1ˇelement_2]".into(),
|
||||
expected_with_replace_mode: "[element_1ˇ]".into(),
|
||||
expected_with_replace_subsequence_mode: "[element_1ˇelement_2]".into(),
|
||||
expected_with_replace_suffix_mode: "[element_1ˇelement_2]".into(),
|
||||
@@ -9290,7 +9290,7 @@ async fn test_completion_mode(cx: &mut TestAppContext) {
|
||||
initial_state: "[elˇelement]".into(),
|
||||
buffer_marked_text: "[<el|element>]".into(),
|
||||
completion_text: "element",
|
||||
expected_with_insertion_mode: "[elementˇelement]".into(),
|
||||
expected_with_insert_mode: "[elementˇelement]".into(),
|
||||
expected_with_replace_mode: "[elˇement]".into(),
|
||||
expected_with_replace_subsequence_mode: "[elementˇelement]".into(),
|
||||
expected_with_replace_suffix_mode: "[elˇement]".into(),
|
||||
@@ -9300,7 +9300,7 @@ async fn test_completion_mode(cx: &mut TestAppContext) {
|
||||
initial_state: "SubˇError".into(),
|
||||
buffer_marked_text: "<Sub|Error>".into(),
|
||||
completion_text: "SubscriptionError",
|
||||
expected_with_insertion_mode: "SubscriptionErrorˇError".into(),
|
||||
expected_with_insert_mode: "SubscriptionErrorˇError".into(),
|
||||
expected_with_replace_mode: "SubscriptionErrorˇ".into(),
|
||||
expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
|
||||
expected_with_replace_suffix_mode: "SubscriptionErrorˇ".into(),
|
||||
@@ -9310,7 +9310,7 @@ async fn test_completion_mode(cx: &mut TestAppContext) {
|
||||
initial_state: "SubˇErr".into(),
|
||||
buffer_marked_text: "<Sub|Err>".into(),
|
||||
completion_text: "SubscriptionError",
|
||||
expected_with_insertion_mode: "SubscriptionErrorˇErr".into(),
|
||||
expected_with_insert_mode: "SubscriptionErrorˇErr".into(),
|
||||
expected_with_replace_mode: "SubscriptionErrorˇ".into(),
|
||||
expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
|
||||
expected_with_replace_suffix_mode: "SubscriptionErrorˇErr".into(),
|
||||
@@ -9320,7 +9320,7 @@ async fn test_completion_mode(cx: &mut TestAppContext) {
|
||||
initial_state: "Suˇscrirr".into(),
|
||||
buffer_marked_text: "<Su|scrirr>".into(),
|
||||
completion_text: "SubscriptionError",
|
||||
expected_with_insertion_mode: "SubscriptionErrorˇscrirr".into(),
|
||||
expected_with_insert_mode: "SubscriptionErrorˇscrirr".into(),
|
||||
expected_with_replace_mode: "SubscriptionErrorˇ".into(),
|
||||
expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
|
||||
expected_with_replace_suffix_mode: "SubscriptionErrorˇscrirr".into(),
|
||||
@@ -9330,7 +9330,7 @@ async fn test_completion_mode(cx: &mut TestAppContext) {
|
||||
initial_state: "foo(indˇix)".into(),
|
||||
buffer_marked_text: "foo(<ind|ix>)".into(),
|
||||
completion_text: "node_index",
|
||||
expected_with_insertion_mode: "foo(node_indexˇix)".into(),
|
||||
expected_with_insert_mode: "foo(node_indexˇix)".into(),
|
||||
expected_with_replace_mode: "foo(node_indexˇ)".into(),
|
||||
expected_with_replace_subsequence_mode: "foo(node_indexˇix)".into(),
|
||||
expected_with_replace_suffix_mode: "foo(node_indexˇix)".into(),
|
||||
@@ -9339,7 +9339,7 @@ async fn test_completion_mode(cx: &mut TestAppContext) {
|
||||
|
||||
for run in runs {
|
||||
let run_variations = [
|
||||
(LspInsertMode::Insert, run.expected_with_insertion_mode),
|
||||
(LspInsertMode::Insert, run.expected_with_insert_mode),
|
||||
(LspInsertMode::Replace, run.expected_with_replace_mode),
|
||||
(
|
||||
LspInsertMode::ReplaceSubsequence,
|
||||
@@ -9395,6 +9395,98 @@ async fn test_completion_mode(cx: &mut TestAppContext) {
|
||||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
lsp::ServerCapabilities {
|
||||
completion_provider: Some(lsp::CompletionOptions {
|
||||
resolve_provider: Some(true),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
cx,
|
||||
)
|
||||
.await;
|
||||
|
||||
let initial_state = "SubˇError";
|
||||
let buffer_marked_text = "<Sub|Error>";
|
||||
let completion_text = "SubscriptionError";
|
||||
let expected_with_insert_mode = "SubscriptionErrorˇError";
|
||||
let expected_with_replace_mode = "SubscriptionErrorˇ";
|
||||
|
||||
update_test_language_settings(&mut cx, |settings| {
|
||||
settings.defaults.completions = Some(CompletionSettings {
|
||||
words: WordsCompletionMode::Disabled,
|
||||
// set the opposite here to ensure that the action is overriding the default behavior
|
||||
lsp_insert_mode: LspInsertMode::Insert,
|
||||
lsp: true,
|
||||
lsp_fetch_timeout_ms: 0,
|
||||
});
|
||||
});
|
||||
|
||||
cx.set_state(initial_state);
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
|
||||
});
|
||||
|
||||
let counter = Arc::new(AtomicUsize::new(0));
|
||||
handle_completion_request_with_insert_and_replace(
|
||||
&mut cx,
|
||||
&buffer_marked_text,
|
||||
vec![completion_text],
|
||||
counter.clone(),
|
||||
)
|
||||
.await;
|
||||
cx.condition(|editor, _| editor.context_menu_visible())
|
||||
.await;
|
||||
assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
|
||||
|
||||
let apply_additional_edits = cx.update_editor(|editor, window, cx| {
|
||||
editor
|
||||
.confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
|
||||
.unwrap()
|
||||
});
|
||||
cx.assert_editor_state(&expected_with_replace_mode);
|
||||
handle_resolve_completion_request(&mut cx, None).await;
|
||||
apply_additional_edits.await.unwrap();
|
||||
|
||||
update_test_language_settings(&mut cx, |settings| {
|
||||
settings.defaults.completions = Some(CompletionSettings {
|
||||
words: WordsCompletionMode::Disabled,
|
||||
// set the opposite here to ensure that the action is overriding the default behavior
|
||||
lsp_insert_mode: LspInsertMode::Replace,
|
||||
lsp: true,
|
||||
lsp_fetch_timeout_ms: 0,
|
||||
});
|
||||
});
|
||||
|
||||
cx.set_state(initial_state);
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
|
||||
});
|
||||
handle_completion_request_with_insert_and_replace(
|
||||
&mut cx,
|
||||
&buffer_marked_text,
|
||||
vec![completion_text],
|
||||
counter.clone(),
|
||||
)
|
||||
.await;
|
||||
cx.condition(|editor, _| editor.context_menu_visible())
|
||||
.await;
|
||||
assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
|
||||
|
||||
let apply_additional_edits = cx.update_editor(|editor, window, cx| {
|
||||
editor
|
||||
.confirm_completion_insert(&ConfirmCompletionInsert, window, cx)
|
||||
.unwrap()
|
||||
});
|
||||
cx.assert_editor_state(&expected_with_insert_mode);
|
||||
handle_resolve_completion_request(&mut cx, None).await;
|
||||
apply_additional_edits.await.unwrap();
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_completion(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
@@ -12539,6 +12631,7 @@ async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppCon
|
||||
initialization_options: Some(json!({
|
||||
"some other init value": false
|
||||
})),
|
||||
enable_lsp_tasks: false,
|
||||
},
|
||||
);
|
||||
});
|
||||
@@ -12558,6 +12651,7 @@ async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppCon
|
||||
initialization_options: Some(json!({
|
||||
"anotherInitValue": false
|
||||
})),
|
||||
enable_lsp_tasks: false,
|
||||
},
|
||||
);
|
||||
});
|
||||
@@ -12577,6 +12671,7 @@ async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppCon
|
||||
initialization_options: Some(json!({
|
||||
"anotherInitValue": false
|
||||
})),
|
||||
enable_lsp_tasks: false,
|
||||
},
|
||||
);
|
||||
});
|
||||
@@ -12594,6 +12689,7 @@ async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppCon
|
||||
binary: None,
|
||||
settings: None,
|
||||
initialization_options: None,
|
||||
enable_lsp_tasks: false,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -461,6 +461,20 @@ impl EditorElement {
|
||||
cx.propagate();
|
||||
}
|
||||
});
|
||||
register_action(editor, window, |editor, action, window, cx| {
|
||||
if let Some(task) = editor.confirm_completion_replace(action, window, cx) {
|
||||
task.detach_and_notify_err(window, cx);
|
||||
} else {
|
||||
cx.propagate();
|
||||
}
|
||||
});
|
||||
register_action(editor, window, |editor, action, window, cx| {
|
||||
if let Some(task) = editor.confirm_completion_insert(action, window, cx) {
|
||||
task.detach_and_notify_err(window, cx);
|
||||
} else {
|
||||
cx.propagate();
|
||||
}
|
||||
});
|
||||
register_action(editor, window, |editor, action, window, cx| {
|
||||
if let Some(task) = editor.compose_completion(action, window, cx) {
|
||||
task.detach_and_notify_err(window, cx);
|
||||
@@ -8404,7 +8418,7 @@ enum CursorPopoverType {
|
||||
}
|
||||
|
||||
pub fn scale_vertical_mouse_autoscroll_delta(delta: Pixels) -> f32 {
|
||||
(delta.pow(1.5) / 100.0).into()
|
||||
(delta.pow(1.2) / 100.0).min(px(3.0)).into()
|
||||
}
|
||||
|
||||
fn scale_horizontal_mouse_autoscroll_delta(delta: Pixels) -> f32 {
|
||||
|
||||
@@ -6,7 +6,6 @@ use crate::{
|
||||
scroll::ScrollAnchor,
|
||||
};
|
||||
use anyhow::{Context as _, Result, anyhow};
|
||||
use clock::Global;
|
||||
use collections::{HashMap, HashSet};
|
||||
use file_icons::FileIcons;
|
||||
use futures::future::try_join_all;
|
||||
@@ -16,12 +15,12 @@ use gpui::{
|
||||
ParentElement, Pixels, SharedString, Styled, Task, WeakEntity, Window, point,
|
||||
};
|
||||
use language::{
|
||||
Bias, Buffer, CharKind, DiskState, Point, SelectionGoal,
|
||||
Bias, Buffer, BufferRow, CharKind, DiskState, LocalFile, Point, SelectionGoal,
|
||||
proto::serialize_anchor as serialize_text_anchor,
|
||||
};
|
||||
use lsp::DiagnosticSeverity;
|
||||
use project::{
|
||||
Project, ProjectEntryId, ProjectItem as _, ProjectPath, lsp_store::FormatTrigger,
|
||||
Project, ProjectItem as _, ProjectPath, lsp_store::FormatTrigger,
|
||||
project_settings::ProjectSettings, search::SearchQuery,
|
||||
};
|
||||
use rpc::proto::{self, PeerId, update_view};
|
||||
@@ -30,13 +29,12 @@ use std::{
|
||||
any::TypeId,
|
||||
borrow::Cow,
|
||||
cmp::{self, Ordering},
|
||||
collections::hash_map,
|
||||
iter,
|
||||
ops::Range,
|
||||
path::Path,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use text::{BufferId, Selection};
|
||||
use text::{BufferId, BufferSnapshot, Selection};
|
||||
use theme::{Theme, ThemeSettings};
|
||||
use ui::{IconDecorationKind, prelude::*};
|
||||
use util::{ResultExt, TryFutureExt, paths::PathExt};
|
||||
@@ -1243,26 +1241,14 @@ impl SerializableItem for Editor {
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct EditorRestorationData {
|
||||
entries: HashMap<ProjectEntryId, RestorationData>,
|
||||
entries: HashMap<PathBuf, RestorationData>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Default, Debug)]
|
||||
pub struct RestorationData {
|
||||
pub scroll_anchor: ScrollAnchor,
|
||||
pub folds: Vec<Range<Anchor>>,
|
||||
pub selections: Vec<Range<Anchor>>,
|
||||
pub buffer_version: Global,
|
||||
}
|
||||
|
||||
impl Default for RestorationData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
scroll_anchor: ScrollAnchor::new(),
|
||||
folds: Vec::new(),
|
||||
selections: Vec::new(),
|
||||
buffer_version: Global::default(),
|
||||
}
|
||||
}
|
||||
pub scroll_position: (BufferRow, gpui::Point<f32>),
|
||||
pub folds: Vec<Range<Point>>,
|
||||
pub selections: Vec<Range<Point>>,
|
||||
}
|
||||
|
||||
impl ProjectItem for Editor {
|
||||
@@ -1280,21 +1266,37 @@ impl ProjectItem for Editor {
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let mut editor = Self::for_buffer(buffer.clone(), Some(project), window, cx);
|
||||
|
||||
if WorkspaceSettings::get(None, cx).restore_on_file_reopen {
|
||||
if let Some(restoration_data) = Self::project_item_kind()
|
||||
.and_then(|kind| pane.project_item_restoration_data.get(&kind))
|
||||
.and_then(|data| data.downcast_ref::<EditorRestorationData>())
|
||||
.and_then(|data| data.entries.get(&buffer.read(cx).entry_id(cx)?))
|
||||
.filter(|data| !buffer.read(cx).version.changed_since(&data.buffer_version))
|
||||
{
|
||||
editor.fold_ranges(restoration_data.folds.clone(), false, window, cx);
|
||||
if !restoration_data.selections.is_empty() {
|
||||
editor.change_selections(None, window, cx, |s| {
|
||||
s.select_ranges(restoration_data.selections.clone());
|
||||
});
|
||||
if let Some((excerpt_id, buffer_id, snapshot)) =
|
||||
editor.buffer().read(cx).snapshot(cx).as_singleton()
|
||||
{
|
||||
if WorkspaceSettings::get(None, cx).restore_on_file_reopen {
|
||||
if let Some(restoration_data) = Self::project_item_kind()
|
||||
.and_then(|kind| pane.project_item_restoration_data.get(&kind))
|
||||
.and_then(|data| data.downcast_ref::<EditorRestorationData>())
|
||||
.and_then(|data| {
|
||||
let file = project::File::from_dyn(buffer.read(cx).file())?;
|
||||
data.entries.get(&file.abs_path(cx))
|
||||
})
|
||||
{
|
||||
editor.fold_ranges(
|
||||
clip_ranges(&restoration_data.folds, &snapshot),
|
||||
false,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
if !restoration_data.selections.is_empty() {
|
||||
editor.change_selections(None, window, cx, |s| {
|
||||
s.select_ranges(clip_ranges(&restoration_data.selections, &snapshot));
|
||||
});
|
||||
}
|
||||
let (top_row, offset) = restoration_data.scroll_position;
|
||||
let anchor = Anchor::in_buffer(
|
||||
*excerpt_id,
|
||||
buffer_id,
|
||||
snapshot.anchor_before(Point::new(top_row, 0)),
|
||||
);
|
||||
editor.set_scroll_anchor(ScrollAnchor { anchor, offset }, window, cx);
|
||||
}
|
||||
editor.set_scroll_anchor(restoration_data.scroll_anchor, window, cx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1302,6 +1304,19 @@ impl ProjectItem for Editor {
|
||||
}
|
||||
}
|
||||
|
||||
fn clip_ranges<'a>(
|
||||
original: impl IntoIterator<Item = &'a Range<Point>> + 'a,
|
||||
snapshot: &'a BufferSnapshot,
|
||||
) -> Vec<Range<Point>> {
|
||||
original
|
||||
.into_iter()
|
||||
.map(|range| {
|
||||
snapshot.clip_point(range.start, Bias::Left)
|
||||
..snapshot.clip_point(range.end, Bias::Right)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
impl EventEmitter<SearchEvent> for Editor {}
|
||||
|
||||
impl Editor {
|
||||
@@ -1320,8 +1335,7 @@ impl Editor {
|
||||
let kind = Editor::project_item_kind()?;
|
||||
let pane = editor.workspace()?.read(cx).pane_for(&cx.entity())?;
|
||||
let buffer = editor.buffer().read(cx).as_singleton()?;
|
||||
let entry_id = buffer.read(cx).entry_id(cx)?;
|
||||
let buffer_version = buffer.read(cx).version();
|
||||
let file_abs_path = project::File::from_dyn(buffer.read(cx).file())?.abs_path(cx);
|
||||
pane.update(cx, |pane, _| {
|
||||
let data = pane
|
||||
.project_item_restoration_data
|
||||
@@ -1336,17 +1350,8 @@ impl Editor {
|
||||
}
|
||||
};
|
||||
|
||||
let data = match data.entries.entry(entry_id) {
|
||||
hash_map::Entry::Occupied(o) => {
|
||||
if buffer_version.changed_since(&o.get().buffer_version) {
|
||||
return None;
|
||||
}
|
||||
o.into_mut()
|
||||
}
|
||||
hash_map::Entry::Vacant(v) => v.insert(RestorationData::default()),
|
||||
};
|
||||
let data = data.entries.entry(file_abs_path).or_default();
|
||||
write(data);
|
||||
data.buffer_version = buffer_version;
|
||||
Some(())
|
||||
})
|
||||
});
|
||||
|
||||
@@ -1,12 +1,25 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::Editor;
|
||||
use collections::HashMap;
|
||||
use futures::stream::FuturesUnordered;
|
||||
use gpui::{App, AppContext as _, Entity, Task};
|
||||
use itertools::Itertools;
|
||||
use language::Buffer;
|
||||
use language::Language;
|
||||
use lsp::LanguageServerId;
|
||||
use lsp::LanguageServerName;
|
||||
use multi_buffer::Anchor;
|
||||
use project::LanguageServerToQuery;
|
||||
use project::LocationLink;
|
||||
use project::Project;
|
||||
use project::TaskSourceKind;
|
||||
use project::lsp_store::lsp_ext_command::GetLspRunnables;
|
||||
use smol::stream::StreamExt;
|
||||
use task::ResolvedTask;
|
||||
use task::TaskContext;
|
||||
use text::BufferId;
|
||||
use util::ResultExt as _;
|
||||
|
||||
pub(crate) fn find_specific_language_server_in_selection<F>(
|
||||
editor: &Editor,
|
||||
@@ -60,3 +73,83 @@ where
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn lsp_tasks(
|
||||
project: Entity<Project>,
|
||||
task_sources: &HashMap<LanguageServerName, Vec<BufferId>>,
|
||||
for_position: Option<text::Anchor>,
|
||||
cx: &mut App,
|
||||
) -> Task<Vec<(TaskSourceKind, Vec<(Option<LocationLink>, ResolvedTask)>)>> {
|
||||
let mut lsp_task_sources = task_sources
|
||||
.iter()
|
||||
.map(|(name, buffer_ids)| {
|
||||
let buffers = buffer_ids
|
||||
.iter()
|
||||
.filter_map(|&buffer_id| project.read(cx).buffer_for_id(buffer_id, cx))
|
||||
.collect::<Vec<_>>();
|
||||
language_server_for_buffers(project.clone(), name.clone(), buffers, cx)
|
||||
})
|
||||
.collect::<FuturesUnordered<_>>();
|
||||
|
||||
cx.spawn(async move |cx| {
|
||||
let mut lsp_tasks = Vec::new();
|
||||
let lsp_task_context = TaskContext::default();
|
||||
while let Some(server_to_query) = lsp_task_sources.next().await {
|
||||
if let Some((server_id, buffers)) = server_to_query {
|
||||
let source_kind = TaskSourceKind::Lsp(server_id);
|
||||
let id_base = source_kind.to_id_base();
|
||||
let mut new_lsp_tasks = Vec::new();
|
||||
for buffer in buffers {
|
||||
if let Ok(runnables_task) = project.update(cx, |project, cx| {
|
||||
let buffer_id = buffer.read(cx).remote_id();
|
||||
project.request_lsp(
|
||||
buffer,
|
||||
LanguageServerToQuery::Other(server_id),
|
||||
GetLspRunnables {
|
||||
buffer_id,
|
||||
position: for_position,
|
||||
},
|
||||
cx,
|
||||
)
|
||||
}) {
|
||||
if let Some(new_runnables) = runnables_task.await.log_err() {
|
||||
new_lsp_tasks.extend(new_runnables.runnables.into_iter().filter_map(
|
||||
|(location, runnable)| {
|
||||
let resolved_task =
|
||||
runnable.resolve_task(&id_base, &lsp_task_context)?;
|
||||
Some((location, resolved_task))
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
lsp_tasks.push((source_kind, new_lsp_tasks));
|
||||
}
|
||||
}
|
||||
lsp_tasks
|
||||
})
|
||||
}
|
||||
|
||||
fn language_server_for_buffers(
|
||||
project: Entity<Project>,
|
||||
name: LanguageServerName,
|
||||
candidates: Vec<Entity<Buffer>>,
|
||||
cx: &mut App,
|
||||
) -> Task<Option<(LanguageServerId, Vec<Entity<Buffer>>)>> {
|
||||
cx.spawn(async move |cx| {
|
||||
for buffer in &candidates {
|
||||
let server_id = buffer
|
||||
.update(cx, |buffer, cx| {
|
||||
project.update(cx, |project, cx| {
|
||||
project.language_server_id_for_name(buffer, &name.0, cx)
|
||||
})
|
||||
})
|
||||
.ok()?
|
||||
.await;
|
||||
if let Some(server_id) = server_id {
|
||||
return Some((server_id, candidates));
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
use crate::Editor;
|
||||
|
||||
use collections::HashMap;
|
||||
use gpui::{App, Task, Window};
|
||||
use project::Location;
|
||||
use lsp::LanguageServerName;
|
||||
use project::{Location, project_settings::ProjectSettings};
|
||||
use settings::Settings as _;
|
||||
use task::{TaskContext, TaskVariables, VariableName};
|
||||
use text::{ToOffset, ToPoint};
|
||||
use text::{BufferId, ToOffset, ToPoint};
|
||||
|
||||
impl Editor {
|
||||
pub fn task_context(&self, window: &mut Window, cx: &mut App) -> Task<Option<TaskContext>> {
|
||||
@@ -70,4 +73,38 @@ impl Editor {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn lsp_task_sources(&self, cx: &App) -> HashMap<LanguageServerName, Vec<BufferId>> {
|
||||
let lsp_settings = &ProjectSettings::get_global(cx).lsp;
|
||||
|
||||
self.buffer()
|
||||
.read(cx)
|
||||
.all_buffers()
|
||||
.into_iter()
|
||||
.filter_map(|buffer| {
|
||||
let lsp_tasks_source = buffer
|
||||
.read(cx)
|
||||
.language()?
|
||||
.context_provider()?
|
||||
.lsp_task_source()?;
|
||||
if lsp_settings
|
||||
.get(&lsp_tasks_source)
|
||||
.map_or(true, |s| s.enable_lsp_tasks)
|
||||
{
|
||||
let buffer_id = buffer.read(cx).remote_id();
|
||||
Some((lsp_tasks_source, buffer_id))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.fold(
|
||||
HashMap::default(),
|
||||
|mut acc, (lsp_task_source, buffer_id)| {
|
||||
acc.entry(lsp_task_source)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(buffer_id);
|
||||
acc
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -606,7 +606,7 @@ impl GitRepository for RealGitRepository {
|
||||
};
|
||||
|
||||
let content = repo.find_blob(oid)?.content().to_owned();
|
||||
Ok(Some(String::from_utf8(content)?))
|
||||
Ok(String::from_utf8(content).ok())
|
||||
}
|
||||
|
||||
match logic(&repo.lock(), &path) {
|
||||
@@ -629,8 +629,7 @@ impl GitRepository for RealGitRepository {
|
||||
return None;
|
||||
}
|
||||
let content = repo.find_blob(entry.id()).log_err()?.content().to_owned();
|
||||
let content = String::from_utf8(content).log_err()?;
|
||||
Some(content)
|
||||
String::from_utf8(content).ok()
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
|
||||
@@ -244,10 +244,6 @@ impl language::File for GitBlob {
|
||||
self.worktree_id
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn to_proto(&self, _cx: &App) -> language::proto::File {
|
||||
unimplemented!()
|
||||
}
|
||||
@@ -282,10 +278,6 @@ impl language::File for CommitMetadataFile {
|
||||
self.worktree_id
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn to_proto(&self, _: &App) -> language::proto::File {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
@@ -3953,8 +3953,7 @@ impl Render for GitPanelMessageTooltip {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(IntoElement, IntoComponent)]
|
||||
#[component(scope = "Version Control")]
|
||||
#[derive(IntoElement, RegisterComponent)]
|
||||
pub struct PanelRepoFooter {
|
||||
active_repository: SharedString,
|
||||
branch: Option<Branch>,
|
||||
@@ -4134,8 +4133,12 @@ impl RenderOnce for PanelRepoFooter {
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentPreview for PanelRepoFooter {
|
||||
fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
|
||||
impl Component for PanelRepoFooter {
|
||||
fn scope() -> ComponentScope {
|
||||
ComponentScope::VersionControl
|
||||
}
|
||||
|
||||
fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
|
||||
let unknown_upstream = None;
|
||||
let no_remote_upstream = Some(UpstreamTracking::Gone);
|
||||
let ahead_of_upstream = Some(
|
||||
@@ -4207,192 +4210,180 @@ impl ComponentPreview for PanelRepoFooter {
|
||||
}
|
||||
|
||||
let example_width = px(340.);
|
||||
|
||||
v_flex()
|
||||
.gap_6()
|
||||
.w_full()
|
||||
.flex_none()
|
||||
.children(vec![
|
||||
example_group_with_title(
|
||||
"Action Button States",
|
||||
vec![
|
||||
single_example(
|
||||
"No Branch",
|
||||
div()
|
||||
.w(example_width)
|
||||
.overflow_hidden()
|
||||
.child(PanelRepoFooter::new_preview(
|
||||
active_repository(1).clone(),
|
||||
None,
|
||||
))
|
||||
.into_any_element(),
|
||||
)
|
||||
.grow(),
|
||||
single_example(
|
||||
"Remote status unknown",
|
||||
div()
|
||||
.w(example_width)
|
||||
.overflow_hidden()
|
||||
.child(PanelRepoFooter::new_preview(
|
||||
active_repository(2).clone(),
|
||||
Some(branch(unknown_upstream)),
|
||||
))
|
||||
.into_any_element(),
|
||||
)
|
||||
.grow(),
|
||||
single_example(
|
||||
"No Remote Upstream",
|
||||
div()
|
||||
.w(example_width)
|
||||
.overflow_hidden()
|
||||
.child(PanelRepoFooter::new_preview(
|
||||
active_repository(3).clone(),
|
||||
Some(branch(no_remote_upstream)),
|
||||
))
|
||||
.into_any_element(),
|
||||
)
|
||||
.grow(),
|
||||
single_example(
|
||||
"Not Ahead or Behind",
|
||||
div()
|
||||
.w(example_width)
|
||||
.overflow_hidden()
|
||||
.child(PanelRepoFooter::new_preview(
|
||||
active_repository(4).clone(),
|
||||
Some(branch(not_ahead_or_behind_upstream)),
|
||||
))
|
||||
.into_any_element(),
|
||||
)
|
||||
.grow(),
|
||||
single_example(
|
||||
"Behind remote",
|
||||
div()
|
||||
.w(example_width)
|
||||
.overflow_hidden()
|
||||
.child(PanelRepoFooter::new_preview(
|
||||
active_repository(5).clone(),
|
||||
Some(branch(behind_upstream)),
|
||||
))
|
||||
.into_any_element(),
|
||||
)
|
||||
.grow(),
|
||||
single_example(
|
||||
"Ahead of remote",
|
||||
div()
|
||||
.w(example_width)
|
||||
.overflow_hidden()
|
||||
.child(PanelRepoFooter::new_preview(
|
||||
active_repository(6).clone(),
|
||||
Some(branch(ahead_of_upstream)),
|
||||
))
|
||||
.into_any_element(),
|
||||
)
|
||||
.grow(),
|
||||
single_example(
|
||||
"Ahead and behind remote",
|
||||
div()
|
||||
.w(example_width)
|
||||
.overflow_hidden()
|
||||
.child(PanelRepoFooter::new_preview(
|
||||
active_repository(7).clone(),
|
||||
Some(branch(ahead_and_behind_upstream)),
|
||||
))
|
||||
.into_any_element(),
|
||||
)
|
||||
.grow(),
|
||||
],
|
||||
)
|
||||
.grow()
|
||||
.vertical(),
|
||||
])
|
||||
.children(vec![
|
||||
example_group_with_title(
|
||||
"Labels",
|
||||
vec![
|
||||
single_example(
|
||||
"Short Branch & Repo",
|
||||
div()
|
||||
.w(example_width)
|
||||
.overflow_hidden()
|
||||
.child(PanelRepoFooter::new_preview(
|
||||
SharedString::from("zed"),
|
||||
Some(custom("main", behind_upstream)),
|
||||
))
|
||||
.into_any_element(),
|
||||
)
|
||||
.grow(),
|
||||
single_example(
|
||||
"Long Branch",
|
||||
div()
|
||||
.w(example_width)
|
||||
.overflow_hidden()
|
||||
.child(PanelRepoFooter::new_preview(
|
||||
SharedString::from("zed"),
|
||||
Some(custom(
|
||||
"redesign-and-update-git-ui-list-entry-style",
|
||||
behind_upstream,
|
||||
)),
|
||||
))
|
||||
.into_any_element(),
|
||||
)
|
||||
.grow(),
|
||||
single_example(
|
||||
"Long Repo",
|
||||
div()
|
||||
.w(example_width)
|
||||
.overflow_hidden()
|
||||
.child(PanelRepoFooter::new_preview(
|
||||
SharedString::from("zed-industries-community-examples"),
|
||||
Some(custom("gpui", ahead_of_upstream)),
|
||||
))
|
||||
.into_any_element(),
|
||||
)
|
||||
.grow(),
|
||||
single_example(
|
||||
"Long Repo & Branch",
|
||||
div()
|
||||
.w(example_width)
|
||||
.overflow_hidden()
|
||||
.child(PanelRepoFooter::new_preview(
|
||||
SharedString::from("zed-industries-community-examples"),
|
||||
Some(custom(
|
||||
"redesign-and-update-git-ui-list-entry-style",
|
||||
behind_upstream,
|
||||
)),
|
||||
))
|
||||
.into_any_element(),
|
||||
)
|
||||
.grow(),
|
||||
single_example(
|
||||
"Uppercase Repo",
|
||||
div()
|
||||
.w(example_width)
|
||||
.overflow_hidden()
|
||||
.child(PanelRepoFooter::new_preview(
|
||||
SharedString::from("LICENSES"),
|
||||
Some(custom("main", ahead_of_upstream)),
|
||||
))
|
||||
.into_any_element(),
|
||||
)
|
||||
.grow(),
|
||||
single_example(
|
||||
"Uppercase Branch",
|
||||
div()
|
||||
.w(example_width)
|
||||
.overflow_hidden()
|
||||
.child(PanelRepoFooter::new_preview(
|
||||
SharedString::from("zed"),
|
||||
Some(custom("update-README", behind_upstream)),
|
||||
))
|
||||
.into_any_element(),
|
||||
)
|
||||
.grow(),
|
||||
],
|
||||
)
|
||||
.grow()
|
||||
.vertical(),
|
||||
])
|
||||
.into_any_element()
|
||||
Some(
|
||||
v_flex()
|
||||
.gap_6()
|
||||
.w_full()
|
||||
.flex_none()
|
||||
.children(vec![
|
||||
example_group_with_title(
|
||||
"Action Button States",
|
||||
vec![
|
||||
single_example(
|
||||
"No Branch",
|
||||
div()
|
||||
.w(example_width)
|
||||
.overflow_hidden()
|
||||
.child(PanelRepoFooter::new_preview(
|
||||
active_repository(1).clone(),
|
||||
None,
|
||||
))
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"Remote status unknown",
|
||||
div()
|
||||
.w(example_width)
|
||||
.overflow_hidden()
|
||||
.child(PanelRepoFooter::new_preview(
|
||||
active_repository(2).clone(),
|
||||
Some(branch(unknown_upstream)),
|
||||
))
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"No Remote Upstream",
|
||||
div()
|
||||
.w(example_width)
|
||||
.overflow_hidden()
|
||||
.child(PanelRepoFooter::new_preview(
|
||||
active_repository(3).clone(),
|
||||
Some(branch(no_remote_upstream)),
|
||||
))
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"Not Ahead or Behind",
|
||||
div()
|
||||
.w(example_width)
|
||||
.overflow_hidden()
|
||||
.child(PanelRepoFooter::new_preview(
|
||||
active_repository(4).clone(),
|
||||
Some(branch(not_ahead_or_behind_upstream)),
|
||||
))
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"Behind remote",
|
||||
div()
|
||||
.w(example_width)
|
||||
.overflow_hidden()
|
||||
.child(PanelRepoFooter::new_preview(
|
||||
active_repository(5).clone(),
|
||||
Some(branch(behind_upstream)),
|
||||
))
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"Ahead of remote",
|
||||
div()
|
||||
.w(example_width)
|
||||
.overflow_hidden()
|
||||
.child(PanelRepoFooter::new_preview(
|
||||
active_repository(6).clone(),
|
||||
Some(branch(ahead_of_upstream)),
|
||||
))
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"Ahead and behind remote",
|
||||
div()
|
||||
.w(example_width)
|
||||
.overflow_hidden()
|
||||
.child(PanelRepoFooter::new_preview(
|
||||
active_repository(7).clone(),
|
||||
Some(branch(ahead_and_behind_upstream)),
|
||||
))
|
||||
.into_any_element(),
|
||||
),
|
||||
],
|
||||
)
|
||||
.grow()
|
||||
.vertical(),
|
||||
])
|
||||
.children(vec![
|
||||
example_group_with_title(
|
||||
"Labels",
|
||||
vec![
|
||||
single_example(
|
||||
"Short Branch & Repo",
|
||||
div()
|
||||
.w(example_width)
|
||||
.overflow_hidden()
|
||||
.child(PanelRepoFooter::new_preview(
|
||||
SharedString::from("zed"),
|
||||
Some(custom("main", behind_upstream)),
|
||||
))
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"Long Branch",
|
||||
div()
|
||||
.w(example_width)
|
||||
.overflow_hidden()
|
||||
.child(PanelRepoFooter::new_preview(
|
||||
SharedString::from("zed"),
|
||||
Some(custom(
|
||||
"redesign-and-update-git-ui-list-entry-style",
|
||||
behind_upstream,
|
||||
)),
|
||||
))
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"Long Repo",
|
||||
div()
|
||||
.w(example_width)
|
||||
.overflow_hidden()
|
||||
.child(PanelRepoFooter::new_preview(
|
||||
SharedString::from("zed-industries-community-examples"),
|
||||
Some(custom("gpui", ahead_of_upstream)),
|
||||
))
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"Long Repo & Branch",
|
||||
div()
|
||||
.w(example_width)
|
||||
.overflow_hidden()
|
||||
.child(PanelRepoFooter::new_preview(
|
||||
SharedString::from("zed-industries-community-examples"),
|
||||
Some(custom(
|
||||
"redesign-and-update-git-ui-list-entry-style",
|
||||
behind_upstream,
|
||||
)),
|
||||
))
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"Uppercase Repo",
|
||||
div()
|
||||
.w(example_width)
|
||||
.overflow_hidden()
|
||||
.child(PanelRepoFooter::new_preview(
|
||||
SharedString::from("LICENSES"),
|
||||
Some(custom("main", ahead_of_upstream)),
|
||||
))
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"Uppercase Branch",
|
||||
div()
|
||||
.w(example_width)
|
||||
.overflow_hidden()
|
||||
.child(PanelRepoFooter::new_preview(
|
||||
SharedString::from("zed"),
|
||||
Some(custom("update-README", behind_upstream)),
|
||||
))
|
||||
.into_any_element(),
|
||||
),
|
||||
],
|
||||
)
|
||||
.grow()
|
||||
.vertical(),
|
||||
])
|
||||
.into_any_element(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -441,8 +441,8 @@ mod remote_button {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(IntoElement, IntoComponent)]
|
||||
#[component(scope = "Version Control")]
|
||||
/// A visual representation of a file's Git status.
|
||||
#[derive(IntoElement, RegisterComponent)]
|
||||
pub struct GitStatusIcon {
|
||||
status: FileStatus,
|
||||
}
|
||||
@@ -484,8 +484,12 @@ impl RenderOnce for GitStatusIcon {
|
||||
}
|
||||
|
||||
// View this component preview using `workspace: open component-preview`
|
||||
impl ComponentPreview for GitStatusIcon {
|
||||
fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
|
||||
impl Component for GitStatusIcon {
|
||||
fn scope() -> ComponentScope {
|
||||
ComponentScope::VersionControl
|
||||
}
|
||||
|
||||
fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
|
||||
fn tracked_file_status(code: StatusCode) -> FileStatus {
|
||||
FileStatus::Tracked(git::status::TrackedStatus {
|
||||
index_status: code,
|
||||
@@ -502,17 +506,19 @@ impl ComponentPreview for GitStatusIcon {
|
||||
}
|
||||
.into();
|
||||
|
||||
v_flex()
|
||||
.gap_6()
|
||||
.children(vec![example_group(vec![
|
||||
single_example("Modified", GitStatusIcon::new(modified).into_any_element()),
|
||||
single_example("Added", GitStatusIcon::new(added).into_any_element()),
|
||||
single_example("Deleted", GitStatusIcon::new(deleted).into_any_element()),
|
||||
single_example(
|
||||
"Conflicted",
|
||||
GitStatusIcon::new(conflict).into_any_element(),
|
||||
),
|
||||
])])
|
||||
.into_any_element()
|
||||
Some(
|
||||
v_flex()
|
||||
.gap_6()
|
||||
.children(vec![example_group(vec![
|
||||
single_example("Modified", GitStatusIcon::new(modified).into_any_element()),
|
||||
single_example("Added", GitStatusIcon::new(added).into_any_element()),
|
||||
single_example("Deleted", GitStatusIcon::new(deleted).into_any_element()),
|
||||
single_example(
|
||||
"Conflicted",
|
||||
GitStatusIcon::new(conflict).into_any_element(),
|
||||
),
|
||||
])])
|
||||
.into_any_element(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1005,8 +1005,7 @@ impl Render for ProjectDiffToolbar {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(IntoElement, IntoComponent)]
|
||||
#[component(scope = "Version Control")]
|
||||
#[derive(IntoElement, RegisterComponent)]
|
||||
pub struct ProjectDiffEmptyState {
|
||||
pub no_repo: bool,
|
||||
pub can_push_and_pull: bool,
|
||||
@@ -1178,8 +1177,12 @@ mod preview {
|
||||
use super::ProjectDiffEmptyState;
|
||||
|
||||
// View this component preview using `workspace: open component-preview`
|
||||
impl ComponentPreview for ProjectDiffEmptyState {
|
||||
fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
|
||||
impl Component for ProjectDiffEmptyState {
|
||||
fn scope() -> ComponentScope {
|
||||
ComponentScope::VersionControl
|
||||
}
|
||||
|
||||
fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
|
||||
let unknown_upstream: Option<UpstreamTracking> = None;
|
||||
let ahead_of_upstream: Option<UpstreamTracking> = Some(
|
||||
UpstreamTrackingStatus {
|
||||
@@ -1244,46 +1247,48 @@ mod preview {
|
||||
|
||||
let (width, height) = (px(480.), px(320.));
|
||||
|
||||
v_flex()
|
||||
.gap_6()
|
||||
.children(vec![
|
||||
example_group(vec![
|
||||
single_example(
|
||||
"No Repo",
|
||||
div()
|
||||
.w(width)
|
||||
.h(height)
|
||||
.child(no_repo_state)
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"No Changes",
|
||||
div()
|
||||
.w(width)
|
||||
.h(height)
|
||||
.child(no_changes_state)
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"Unknown Upstream",
|
||||
div()
|
||||
.w(width)
|
||||
.h(height)
|
||||
.child(unknown_upstream_state)
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"Ahead of Remote",
|
||||
div()
|
||||
.w(width)
|
||||
.h(height)
|
||||
.child(ahead_of_upstream_state)
|
||||
.into_any_element(),
|
||||
),
|
||||
Some(
|
||||
v_flex()
|
||||
.gap_6()
|
||||
.children(vec![
|
||||
example_group(vec![
|
||||
single_example(
|
||||
"No Repo",
|
||||
div()
|
||||
.w(width)
|
||||
.h(height)
|
||||
.child(no_repo_state)
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"No Changes",
|
||||
div()
|
||||
.w(width)
|
||||
.h(height)
|
||||
.child(no_changes_state)
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"Unknown Upstream",
|
||||
div()
|
||||
.w(width)
|
||||
.h(height)
|
||||
.child(unknown_upstream_state)
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"Ahead of Remote",
|
||||
div()
|
||||
.w(width)
|
||||
.h(height)
|
||||
.child(ahead_of_upstream_state)
|
||||
.into_any_element(),
|
||||
),
|
||||
])
|
||||
.vertical(),
|
||||
])
|
||||
.vertical(),
|
||||
])
|
||||
.into_any_element()
|
||||
.into_any_element(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,6 +393,8 @@ pub enum Model {
|
||||
Gemini20FlashLite,
|
||||
#[serde(rename = "gemini-2.5-pro-exp-03-25")]
|
||||
Gemini25ProExp0325,
|
||||
#[serde(rename = "gemini-2.5-pro-preview-03-25")]
|
||||
Gemini25ProPreview0325,
|
||||
#[serde(rename = "custom")]
|
||||
Custom {
|
||||
name: String,
|
||||
@@ -412,6 +414,7 @@ impl Model {
|
||||
Model::Gemini20FlashThinking => "gemini-2.0-flash-thinking-exp",
|
||||
Model::Gemini20FlashLite => "gemini-2.0-flash-lite-preview",
|
||||
Model::Gemini25ProExp0325 => "gemini-2.5-pro-exp-03-25",
|
||||
Model::Gemini25ProPreview0325 => "gemini-2.5-pro-preview-03-25",
|
||||
Model::Custom { name, .. } => name,
|
||||
}
|
||||
}
|
||||
@@ -425,6 +428,7 @@ impl Model {
|
||||
Model::Gemini20FlashThinking => "Gemini 2.0 Flash Thinking",
|
||||
Model::Gemini20FlashLite => "Gemini 2.0 Flash Lite",
|
||||
Model::Gemini25ProExp0325 => "Gemini 2.5 Pro Exp",
|
||||
Model::Gemini25ProPreview0325 => "Gemini 2.5 Pro Preview",
|
||||
Self::Custom {
|
||||
name, display_name, ..
|
||||
} => display_name.as_ref().unwrap_or(name),
|
||||
@@ -440,6 +444,7 @@ impl Model {
|
||||
Model::Gemini20FlashThinking => 1_000_000,
|
||||
Model::Gemini20FlashLite => 1_000_000,
|
||||
Model::Gemini25ProExp0325 => 1_000_000,
|
||||
Model::Gemini25ProPreview0325 => 1_000_000,
|
||||
Model::Custom { max_tokens, .. } => *max_tokens,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ etagere = "0.2"
|
||||
futures.workspace = true
|
||||
gpui_macros.workspace = true
|
||||
http_client = { optional = true, workspace = true }
|
||||
image = "0.25.1"
|
||||
image.workspace = true
|
||||
inventory.workspace = true
|
||||
itertools.workspace = true
|
||||
log.workspace = true
|
||||
|
||||
@@ -42,13 +42,10 @@ use std::{
|
||||
/// }
|
||||
/// register_action!(Paste);
|
||||
/// ```
|
||||
pub trait Action: 'static + Send {
|
||||
pub trait Action: Any + Send {
|
||||
/// Clone the action into a new box
|
||||
fn boxed_clone(&self) -> Box<dyn Action>;
|
||||
|
||||
/// Cast the action to the any type
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
|
||||
/// Do a partial equality check on this action and the other
|
||||
fn partial_eq(&self, action: &dyn Action) -> bool;
|
||||
|
||||
@@ -94,9 +91,9 @@ impl std::fmt::Debug for dyn Action {
|
||||
}
|
||||
|
||||
impl dyn Action {
|
||||
/// Get the type id of this action
|
||||
pub fn type_id(&self) -> TypeId {
|
||||
self.as_any().type_id()
|
||||
/// Type-erase Action type.
|
||||
pub fn as_any(&self) -> &dyn Any {
|
||||
self as &dyn Any
|
||||
}
|
||||
}
|
||||
|
||||
@@ -557,9 +554,6 @@ macro_rules! __impl_action {
|
||||
::std::boxed::Box::new(self.clone())
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn ::std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
$($items)*
|
||||
}
|
||||
|
||||
@@ -597,10 +597,6 @@ mod tests {
|
||||
Box::new(TestAction)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn ::std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn build(_value: serde_json::Value) -> anyhow::Result<Box<dyn Action>>
|
||||
where
|
||||
Self: Sized,
|
||||
|
||||
@@ -513,9 +513,8 @@ fn fs_quad(input: QuadVarying) -> @location(0) vec4<f32> {
|
||||
let point = input.position.xy - quad.bounds.origin;
|
||||
let center_to_point = point - half_size;
|
||||
|
||||
// Signed distance field threshold for inclusion of pixels. Use of 0.5
|
||||
// instead of 1.0 causes the width of rounded borders to appear more
|
||||
// consistent with straight borders.
|
||||
// Signed distance field threshold for inclusion of pixels. 0.5 is the
|
||||
// minimum distance between the center of the pixel and the edge.
|
||||
let antialias_threshold = 0.5;
|
||||
|
||||
// Radius of the nearest corner
|
||||
@@ -612,24 +611,29 @@ fn fs_quad(input: QuadVarying) -> @location(0) vec4<f32> {
|
||||
|
||||
// Dashed border logic when border_style == 1
|
||||
if (quad.border_style == 1) {
|
||||
// Position in "dash space", where each dash period has length 1
|
||||
// Position along the perimeter in "dash space", where each dash
|
||||
// period has length 1
|
||||
var t = 0.0;
|
||||
|
||||
// Total number of dash periods, so that the dash spacing can be
|
||||
// adjusted to evenly divide it
|
||||
var max_t = 0.0;
|
||||
|
||||
// Since border width affects the dash size, the density of dashes
|
||||
// varies, and this is indicated by dash_velocity. It has units
|
||||
// (dash period / pixel). So a dash velocity of (1 / 10) is 1 dash
|
||||
// every 10 pixels.
|
||||
var dash_velocity = 0.0;
|
||||
|
||||
// Border width is proportional to dash size. This is the behavior
|
||||
// used by browsers, but also avoids dashes from different segments
|
||||
// overlapping when dash size is smaller than the border width.
|
||||
//
|
||||
// Dash pattern: (2 * border width) dash, (1 * border width) gap
|
||||
let dash_length_per_width = 2.0;
|
||||
let dash_gap_per_width = 1.0;
|
||||
let dash_period_per_width = dash_length_per_width + dash_gap_per_width;
|
||||
|
||||
// Since the dash size is determined by border width, the density of
|
||||
// dashes varies. Multiplying a pixel distance by this returns a
|
||||
// position in dash space - it has units (dash period / pixels). So
|
||||
// a dash velocity of (1 / 10) is 1 dash every 10 pixels.
|
||||
var dash_velocity = 0.0;
|
||||
|
||||
// Dividing this by the border width gives the dash velocity
|
||||
let dv_numerator = 1.0 / dash_period_per_width;
|
||||
|
||||
@@ -645,8 +649,8 @@ fn fs_quad(input: QuadVarying) -> @location(0) vec4<f32> {
|
||||
t = select(point.y, point.x, is_horizontal) * dash_velocity;
|
||||
max_t = select(size.y, size.x, is_horizontal) * dash_velocity;
|
||||
} else {
|
||||
// When corners are rounded, the dashes are laid out around the
|
||||
// whole perimeter.
|
||||
// When corners are rounded, the dashes are laid out clockwise
|
||||
// around the whole perimeter.
|
||||
|
||||
let r_tr = quad.corner_radii.top_right;
|
||||
let r_br = quad.corner_radii.bottom_right;
|
||||
@@ -694,23 +698,34 @@ fn fs_quad(input: QuadVarying) -> @location(0) vec4<f32> {
|
||||
if (is_near_rounded_corner) {
|
||||
let radians = atan2(corner_center_to_point.y,
|
||||
corner_center_to_point.x);
|
||||
let corner_t = radians * corner_radius;
|
||||
let corner_t = radians * corner_radius * dash_velocity;
|
||||
|
||||
if (center_to_point.x >= 0.0) {
|
||||
if (center_to_point.y < 0.0) {
|
||||
dash_velocity = corner_dash_velocity_tr;
|
||||
t = upto_r - corner_t * dash_velocity;
|
||||
// Subtracted because radians is pi/2 to 0 when
|
||||
// going clockwise around the top right corner,
|
||||
// since the y axis has been flipped
|
||||
t = upto_r - corner_t;
|
||||
} else {
|
||||
dash_velocity = corner_dash_velocity_br;
|
||||
t = upto_br + corner_t * dash_velocity;
|
||||
// Added because radians is 0 to pi/2 when going
|
||||
// clockwise around the bottom-right corner
|
||||
t = upto_br + corner_t;
|
||||
}
|
||||
} else {
|
||||
if (center_to_point.y >= 0.0) {
|
||||
dash_velocity = corner_dash_velocity_bl;
|
||||
t = upto_l - corner_t * dash_velocity;
|
||||
// Subtracted because radians is pi/2 to 0 when
|
||||
// going clockwise around the bottom-left corner,
|
||||
// since the x axis has been flipped
|
||||
t = upto_l - corner_t;
|
||||
} else {
|
||||
dash_velocity = corner_dash_velocity_tl;
|
||||
t = upto_tl + corner_t * dash_velocity;
|
||||
// Added because radians is 0 to pi/2 when going
|
||||
// clockwise around the top-left corner, since both
|
||||
// axis were flipped
|
||||
t = upto_tl + corner_t;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -106,7 +106,17 @@ impl PlatformDispatcher for LinuxDispatcher {
|
||||
}
|
||||
|
||||
fn dispatch_on_main_thread(&self, runnable: Runnable) {
|
||||
self.main_sender.send(runnable).ok();
|
||||
self.main_sender.send(runnable).unwrap_or_else(|runnable| {
|
||||
// NOTE: Runnable may wrap a Future that is !Send.
|
||||
//
|
||||
// This is usually safe because we only poll it on the main thread.
|
||||
// However if the send fails, we know that:
|
||||
// 1. main_receiver has been dropped (which implies the app is shutting down)
|
||||
// 2. we are on a background thread.
|
||||
// It is not safe to drop something !Send on the wrong thread, and
|
||||
// the app will exit soon anyway, so we must forget the runnable.
|
||||
std::mem::forget(runnable);
|
||||
});
|
||||
}
|
||||
|
||||
fn dispatch_after(&self, duration: Duration, runnable: Runnable) {
|
||||
|
||||
@@ -121,7 +121,8 @@ fragment float4 quad_fragment(QuadFragmentInput input [[stage_in]],
|
||||
float2 point = input.position.xy - float2(quad.bounds.origin.x, quad.bounds.origin.y);
|
||||
float2 center_to_point = point - half_size;
|
||||
|
||||
// Signed distance field threshold for inclusion of pixels
|
||||
// Signed distance field threshold for inclusion of pixels. 0.5 is the
|
||||
// minimum distance between the center of the pixel and the edge.
|
||||
const float antialias_threshold = 0.5;
|
||||
|
||||
// Radius of the nearest corner
|
||||
@@ -211,24 +212,29 @@ fragment float4 quad_fragment(QuadFragmentInput input [[stage_in]],
|
||||
|
||||
// Dashed border logic when border_style == 1
|
||||
if (quad.border_style == 1) {
|
||||
// Position in "dash space", where each dash period has length 1
|
||||
// Position along the perimeter in "dash space", where each dash
|
||||
// period has length 1
|
||||
float t = 0.0;
|
||||
|
||||
// Total number of dash periods, so that the dash spacing can be
|
||||
// adjusted to evenly divide it
|
||||
float max_t = 0.0;
|
||||
|
||||
// Since border width affects the dash size, the density of dashes
|
||||
// varies, and this is indicated by dash_velocity. It has units
|
||||
// (dash period / pixel). So a dash velocity of (1 / 10) is 1 dash
|
||||
// every 10 pixels.
|
||||
float dash_velocity = 0.0;
|
||||
|
||||
// Border width is proportional to dash size. This is the behavior
|
||||
// used by browsers, but also avoids dashes from different segments
|
||||
// overlapping when dash size is smaller than the border width.
|
||||
//
|
||||
// Dash pattern: (2 * border width) dash, (1 * border width) gap
|
||||
const float dash_length_per_width = 2.0;
|
||||
const float dash_gap_per_width = 1.0;
|
||||
const float dash_period_per_width = dash_length_per_width + dash_gap_per_width;
|
||||
|
||||
// Since the dash size is determined by border width, the density of
|
||||
// dashes varies. Multiplying a pixel distance by this returns a
|
||||
// position in dash space - it has units (dash period / pixels). So
|
||||
// a dash velocity of (1 / 10) is 1 dash every 10 pixels.
|
||||
float dash_velocity = 0.0;
|
||||
|
||||
// Dividing this by the border width gives the dash velocity
|
||||
const float dv_numerator = 1.0 / dash_period_per_width;
|
||||
|
||||
@@ -244,8 +250,8 @@ fragment float4 quad_fragment(QuadFragmentInput input [[stage_in]],
|
||||
max_t = is_horizontal ? size.x : size.y;
|
||||
max_t *= dash_velocity;
|
||||
} else {
|
||||
// When corners are rounded, the dashes are laid out around the
|
||||
// whole perimeter.
|
||||
// When corners are rounded, the dashes are laid out clockwise
|
||||
// around the whole perimeter.
|
||||
|
||||
float r_tr = quad.corner_radii.top_right;
|
||||
float r_br = quad.corner_radii.bottom_right;
|
||||
@@ -292,23 +298,34 @@ fragment float4 quad_fragment(QuadFragmentInput input [[stage_in]],
|
||||
|
||||
if (is_near_rounded_corner) {
|
||||
float radians = atan2(corner_center_to_point.y, corner_center_to_point.x);
|
||||
float corner_t = radians * corner_radius;
|
||||
float corner_t = radians * corner_radius * dash_velocity;
|
||||
|
||||
if (center_to_point.x >= 0.0) {
|
||||
if (center_to_point.y < 0.0) {
|
||||
dash_velocity = corner_dash_velocity_tr;
|
||||
t = upto_r - corner_t * dash_velocity;
|
||||
// Subtracted because radians is pi/2 to 0 when
|
||||
// going clockwise around the top right corner,
|
||||
// since the y axis has been flipped
|
||||
t = upto_r - corner_t;
|
||||
} else {
|
||||
dash_velocity = corner_dash_velocity_br;
|
||||
t = upto_br + corner_t * dash_velocity;
|
||||
// Added because radians is 0 to pi/2 when going
|
||||
// clockwise around the bottom-right corner
|
||||
t = upto_br + corner_t;
|
||||
}
|
||||
} else {
|
||||
if (center_to_point.y >= 0.0) {
|
||||
dash_velocity = corner_dash_velocity_bl;
|
||||
t = upto_l - corner_t * dash_velocity;
|
||||
// Subtracted because radians is pi/1 to 0 when
|
||||
// going clockwise around the bottom-left corner,
|
||||
// since the x axis has been flipped
|
||||
t = upto_l - corner_t;
|
||||
} else {
|
||||
dash_velocity = corner_dash_velocity_tl;
|
||||
t = upto_tl + corner_t * dash_velocity;
|
||||
// Added because radians is 0 to pi/2 when going
|
||||
// clockwise around the top-left corner, since both
|
||||
// axis were flipped
|
||||
t = upto_tl + corner_t;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -3,7 +3,6 @@ use std::{
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::Context as _;
|
||||
use async_task::Runnable;
|
||||
use flume::Sender;
|
||||
use parking::Parker;
|
||||
@@ -95,14 +94,8 @@ impl PlatformDispatcher for WindowsDispatcher {
|
||||
}
|
||||
|
||||
fn dispatch_on_main_thread(&self, runnable: Runnable) {
|
||||
if self
|
||||
.main_sender
|
||||
.send(runnable)
|
||||
.context("Dispatch on main thread failed")
|
||||
.log_err()
|
||||
.is_some()
|
||||
{
|
||||
unsafe {
|
||||
match self.main_sender.send(runnable) {
|
||||
Ok(_) => unsafe {
|
||||
PostThreadMessageW(
|
||||
self.main_thread_id_win32,
|
||||
WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD,
|
||||
@@ -110,6 +103,17 @@ impl PlatformDispatcher for WindowsDispatcher {
|
||||
LPARAM(0),
|
||||
)
|
||||
.log_err();
|
||||
},
|
||||
Err(runnable) => {
|
||||
// NOTE: Runnable may wrap a Future that is !Send.
|
||||
//
|
||||
// This is usually safe because we only poll it on the main thread.
|
||||
// However if the send fails, we know that:
|
||||
// 1. main_receiver has been dropped (which implies the app is shutting down)
|
||||
// 2. we are on a background thread.
|
||||
// It is not safe to drop something !Send on the wrong thread, and
|
||||
// the app will exit soon anyway, so we must forget the runnable.
|
||||
std::mem::forget(runnable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,10 +22,6 @@ fn test_action_macros() {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn partial_eq(&self, _action: &dyn gpui::Action) -> bool {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
@@ -306,7 +306,7 @@ pub enum BufferEvent {
|
||||
}
|
||||
|
||||
/// The file associated with a buffer.
|
||||
pub trait File: Send + Sync {
|
||||
pub trait File: Send + Sync + Any {
|
||||
/// Returns the [`LocalFile`] associated with this file, if the
|
||||
/// file is local.
|
||||
fn as_local(&self) -> Option<&dyn LocalFile>;
|
||||
@@ -336,9 +336,6 @@ pub trait File: Send + Sync {
|
||||
/// This is needed for looking up project-specific settings.
|
||||
fn worktree_id(&self, cx: &App) -> WorktreeId;
|
||||
|
||||
/// Converts this file into an [`Any`] trait object.
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
|
||||
/// Converts this file into a protobuf message.
|
||||
fn to_proto(&self, cx: &App) -> rpc::proto::File;
|
||||
|
||||
@@ -4610,10 +4607,6 @@ impl File for TestFile {
|
||||
WorktreeId::from_usize(0)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn to_proto(&self, _: &App) -> rpc::proto::File {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
@@ -572,7 +572,11 @@ pub trait LspAdapter: 'static + Send + Sync {
|
||||
}
|
||||
|
||||
/// Support custom initialize params.
|
||||
fn prepare_initialize_params(&self, original: InitializeParams) -> Result<InitializeParams> {
|
||||
fn prepare_initialize_params(
|
||||
&self,
|
||||
original: InitializeParams,
|
||||
_: &App,
|
||||
) -> Result<InitializeParams> {
|
||||
Ok(original)
|
||||
}
|
||||
|
||||
|
||||
@@ -370,7 +370,7 @@ fn default_words_completion_mode() -> WordsCompletionMode {
|
||||
}
|
||||
|
||||
fn default_lsp_insert_mode() -> LspInsertMode {
|
||||
LspInsertMode::Insert
|
||||
LspInsertMode::ReplaceSuffix
|
||||
}
|
||||
|
||||
fn default_lsp_fetch_timeout_ms() -> u64 {
|
||||
@@ -1029,7 +1029,10 @@ fn scroll_debounce_ms() -> u64 {
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct LanguageTaskConfig {
|
||||
/// Extra task variables to set for a particular language.
|
||||
#[serde(default)]
|
||||
pub variables: HashMap<String, String>,
|
||||
#[serde(default = "default_true")]
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
impl InlayHintSettings {
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::{LanguageToolchainStore, Location, Runnable};
|
||||
use anyhow::Result;
|
||||
use collections::HashMap;
|
||||
use gpui::{App, Task};
|
||||
use lsp::LanguageServerName;
|
||||
use task::{TaskTemplates, TaskVariables};
|
||||
use text::BufferId;
|
||||
|
||||
@@ -15,6 +16,7 @@ pub struct RunnableRange {
|
||||
pub runnable: Runnable,
|
||||
pub extra_captures: HashMap<String, String>,
|
||||
}
|
||||
|
||||
/// Language Contexts are used by Zed tasks to extract information about the source file where the tasks are supposed to be scheduled from.
|
||||
/// Multiple context providers may be used together: by default, Zed provides a base [`BasicContextProvider`] context that fills all non-custom [`VariableName`] variants.
|
||||
///
|
||||
@@ -40,4 +42,9 @@ pub trait ContextProvider: Send + Sync {
|
||||
) -> Option<TaskTemplates> {
|
||||
None
|
||||
}
|
||||
|
||||
/// A language server name, that can return tasks using LSP (ext) for this language.
|
||||
fn lsp_task_source(&self) -> Option<LanguageServerName> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,6 +107,7 @@ impl CloudModel {
|
||||
| google_ai::Model::Gemini20FlashThinking
|
||||
| google_ai::Model::Gemini20FlashLite
|
||||
| google_ai::Model::Gemini25ProExp0325
|
||||
| google_ai::Model::Gemini25ProPreview0325
|
||||
| google_ai::Model::Custom { .. } => {
|
||||
LanguageModelAvailability::RequiresPlan(Plan::ZedPro)
|
||||
}
|
||||
|
||||
@@ -504,10 +504,14 @@ impl CloudLanguageModel {
|
||||
let mut retry_delay = Duration::from_secs(1);
|
||||
|
||||
loop {
|
||||
let request_builder = http_client::Request::builder();
|
||||
let request_builder = http_client::Request::builder().method(Method::POST);
|
||||
let request_builder = if let Ok(completions_url) = std::env::var("ZED_COMPLETIONS_URL")
|
||||
{
|
||||
request_builder.uri(completions_url)
|
||||
} else {
|
||||
request_builder.uri(http_client.build_zed_llm_url("/completion", &[])?.as_ref())
|
||||
};
|
||||
let request = request_builder
|
||||
.method(Method::POST)
|
||||
.uri(http_client.build_zed_llm_url("/completion", &[])?.as_ref())
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Authorization", format!("Bearer {token}"))
|
||||
.body(serde_json::to_string(&body)?.into())?;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use anyhow::{Context, Result, anyhow, bail};
|
||||
use async_trait::async_trait;
|
||||
use futures::StreamExt;
|
||||
use gpui::AsyncApp;
|
||||
use gpui::{App, AsyncApp};
|
||||
use http_client::github::{GitHubLspBinaryVersion, latest_github_release};
|
||||
pub use language::*;
|
||||
use lsp::{DiagnosticTag, InitializeParams, LanguageServerBinary, LanguageServerName};
|
||||
@@ -273,6 +273,7 @@ impl super::LspAdapter for CLspAdapter {
|
||||
fn prepare_initialize_params(
|
||||
&self,
|
||||
mut original: InitializeParams,
|
||||
_: &App,
|
||||
) -> Result<InitializeParams> {
|
||||
let experimental = json!({
|
||||
"textDocument": {
|
||||
|
||||
@@ -991,6 +991,7 @@ impl LspAdapter for PyLspAdapter {
|
||||
util::command::new_smol_command(pip_path.as_path())
|
||||
.arg("install")
|
||||
.arg("python-lsp-server")
|
||||
.arg("-U")
|
||||
.output()
|
||||
.await?
|
||||
.status
|
||||
@@ -1001,6 +1002,7 @@ impl LspAdapter for PyLspAdapter {
|
||||
util::command::new_smol_command(pip_path.as_path())
|
||||
.arg("install")
|
||||
.arg("python-lsp-server[all]")
|
||||
.arg("-U")
|
||||
.output()
|
||||
.await?
|
||||
.status
|
||||
@@ -1011,6 +1013,7 @@ impl LspAdapter for PyLspAdapter {
|
||||
util::command::new_smol_command(pip_path)
|
||||
.arg("install")
|
||||
.arg("pylsp-mypy")
|
||||
.arg("-U")
|
||||
.output()
|
||||
.await?
|
||||
.status
|
||||
|
||||
@@ -7,8 +7,11 @@ use gpui::{App, AsyncApp, SharedString, Task};
|
||||
use http_client::github::AssetKind;
|
||||
use http_client::github::{GitHubLspBinaryVersion, latest_github_release};
|
||||
pub use language::*;
|
||||
use lsp::LanguageServerBinary;
|
||||
use lsp::{InitializeParams, LanguageServerBinary};
|
||||
use project::project_settings::ProjectSettings;
|
||||
use regex::Regex;
|
||||
use serde_json::json;
|
||||
use settings::Settings as _;
|
||||
use smol::fs::{self};
|
||||
use std::fmt::Display;
|
||||
use std::{
|
||||
@@ -18,6 +21,7 @@ use std::{
|
||||
sync::{Arc, LazyLock},
|
||||
};
|
||||
use task::{TaskTemplate, TaskTemplates, TaskType, TaskVariables, VariableName};
|
||||
use util::merge_json_value_into;
|
||||
use util::{ResultExt, fs::remove_matching, maybe};
|
||||
|
||||
use crate::language_settings::language_settings;
|
||||
@@ -48,9 +52,9 @@ impl RustLspAdapter {
|
||||
const ARCH_SERVER_NAME: &str = "pc-windows-msvc";
|
||||
}
|
||||
|
||||
impl RustLspAdapter {
|
||||
const SERVER_NAME: LanguageServerName = LanguageServerName::new_static("rust-analyzer");
|
||||
const SERVER_NAME: LanguageServerName = LanguageServerName::new_static("rust-analyzer");
|
||||
|
||||
impl RustLspAdapter {
|
||||
fn build_asset_name() -> String {
|
||||
let extension = match Self::GITHUB_ASSET_KIND {
|
||||
AssetKind::TarGz => "tar.gz",
|
||||
@@ -60,7 +64,7 @@ impl RustLspAdapter {
|
||||
|
||||
format!(
|
||||
"{}-{}-{}.{}",
|
||||
Self::SERVER_NAME,
|
||||
SERVER_NAME,
|
||||
std::env::consts::ARCH,
|
||||
Self::ARCH_SERVER_NAME,
|
||||
extension
|
||||
@@ -98,7 +102,7 @@ impl ManifestProvider for CargoManifestProvider {
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for RustLspAdapter {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
Self::SERVER_NAME.clone()
|
||||
SERVER_NAME.clone()
|
||||
}
|
||||
|
||||
fn manifest_name(&self) -> Option<ManifestName> {
|
||||
@@ -473,6 +477,30 @@ impl LspAdapter for RustLspAdapter {
|
||||
filter_range,
|
||||
})
|
||||
}
|
||||
|
||||
fn prepare_initialize_params(
|
||||
&self,
|
||||
mut original: InitializeParams,
|
||||
cx: &App,
|
||||
) -> Result<InitializeParams> {
|
||||
let enable_lsp_tasks = ProjectSettings::get_global(cx)
|
||||
.lsp
|
||||
.get(&SERVER_NAME)
|
||||
.map_or(false, |s| s.enable_lsp_tasks);
|
||||
if enable_lsp_tasks {
|
||||
let experimental = json!({
|
||||
"runnables": {
|
||||
"kinds": [ "cargo", "shell" ],
|
||||
},
|
||||
});
|
||||
if let Some(ref mut original_experimental) = original.capabilities.experimental {
|
||||
merge_json_value_into(experimental, original_experimental);
|
||||
} else {
|
||||
original.capabilities.experimental = Some(experimental);
|
||||
}
|
||||
}
|
||||
Ok(original)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RustContextProvider;
|
||||
@@ -776,6 +804,10 @@ impl ContextProvider for RustContextProvider {
|
||||
|
||||
Some(TaskTemplates(task_templates))
|
||||
}
|
||||
|
||||
fn lsp_task_source(&self) -> Option<LanguageServerName> {
|
||||
Some(SERVER_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
/// Part of the data structure of Cargo metadata
|
||||
|
||||
@@ -255,23 +255,6 @@ impl RemoteTrackPublication {
|
||||
}
|
||||
}
|
||||
|
||||
impl RemoteTrack {
|
||||
pub fn set_enabled(&self, enabled: bool, cx: &App) {
|
||||
let this = self.clone();
|
||||
Tokio::spawn(cx, async move {
|
||||
match this {
|
||||
RemoteTrack::Audio(remote_audio_track) => {
|
||||
remote_audio_track.0.rtc_track().set_enabled(enabled)
|
||||
}
|
||||
RemoteTrack::Video(remote_video_track) => {
|
||||
remote_video_track.0.rtc_track().set_enabled(enabled)
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
|
||||
impl Participant {
|
||||
pub fn identity(&self) -> ParticipantIdentity {
|
||||
match self {
|
||||
|
||||
@@ -89,12 +89,3 @@ impl RemoteTrackPublication {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RemoteTrack {
|
||||
pub fn set_enabled(&self, enabled: bool, _cx: &App) {
|
||||
match self {
|
||||
RemoteTrack::Audio(remote_audio_track) => remote_audio_track.set_enabled(enabled),
|
||||
RemoteTrack::Video(remote_video_track) => remote_video_track.set_enabled(enabled),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,23 +43,6 @@ impl RemoteAudioTrack {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_enabled(&self, enabled: bool) {
|
||||
let Some(room) = self.room.upgrade() else {
|
||||
return;
|
||||
};
|
||||
if enabled {
|
||||
room.0
|
||||
.lock()
|
||||
.paused_audio_tracks
|
||||
.remove(&self.server_track.sid);
|
||||
} else {
|
||||
room.0
|
||||
.lock()
|
||||
.paused_audio_tracks
|
||||
.insert(self.server_track.sid.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RemoteVideoTrack {
|
||||
@@ -70,6 +53,4 @@ impl RemoteVideoTrack {
|
||||
pub fn publisher_id(&self) -> ParticipantIdentity {
|
||||
self.server_track.publisher_id.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn set_enabled(&self, _enabled: bool) {}
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ pub enum MarkdownEvent {
|
||||
Start(MarkdownTag),
|
||||
/// End of a tagged element.
|
||||
End(MarkdownTagEnd),
|
||||
/// Text that uses the associated range from the mardown source.
|
||||
/// Text that uses the associated range from the markdown source.
|
||||
Text,
|
||||
/// Text that differs from the markdown source - typically due to substitution of HTML entities
|
||||
/// and smart punctuation.
|
||||
|
||||
@@ -1718,21 +1718,25 @@ impl MultiBuffer {
|
||||
(None, None) => break,
|
||||
(None, Some(_)) => {
|
||||
let existing_id = existing_iter.next().unwrap();
|
||||
let locator = snapshot.excerpt_locator_for_id(existing_id);
|
||||
let existing_excerpt = excerpts_cursor.item().unwrap();
|
||||
excerpts_cursor.seek_forward(&Some(locator), Bias::Left, &());
|
||||
let existing_end = existing_excerpt
|
||||
.range
|
||||
.context
|
||||
.end
|
||||
.to_point(&buffer_snapshot);
|
||||
if let Some((new_id, last)) = to_insert.last() {
|
||||
if existing_end <= last.context.end {
|
||||
self.snapshot
|
||||
.borrow_mut()
|
||||
.replaced_excerpts
|
||||
.insert(existing_id, *new_id);
|
||||
}
|
||||
let locator = snapshot.excerpt_locator_for_id(existing_id);
|
||||
excerpts_cursor.seek_forward(&Some(locator), Bias::Left, &());
|
||||
if let Some(existing_excerpt) = excerpts_cursor
|
||||
.item()
|
||||
.filter(|e| e.buffer_id == buffer_snapshot.remote_id())
|
||||
{
|
||||
let existing_end = existing_excerpt
|
||||
.range
|
||||
.context
|
||||
.end
|
||||
.to_point(&buffer_snapshot);
|
||||
if existing_end <= last.context.end {
|
||||
self.snapshot
|
||||
.borrow_mut()
|
||||
.replaced_excerpts
|
||||
.insert(existing_id, *new_id);
|
||||
}
|
||||
};
|
||||
}
|
||||
to_remove.push(existing_id);
|
||||
continue;
|
||||
@@ -1745,16 +1749,14 @@ impl MultiBuffer {
|
||||
};
|
||||
let locator = snapshot.excerpt_locator_for_id(*existing);
|
||||
excerpts_cursor.seek_forward(&Some(locator), Bias::Left, &());
|
||||
let Some(existing_excerpt) = excerpts_cursor.item() else {
|
||||
let Some(existing_excerpt) = excerpts_cursor
|
||||
.item()
|
||||
.filter(|e| e.buffer_id == buffer_snapshot.remote_id())
|
||||
else {
|
||||
to_remove.push(existing_iter.next().unwrap());
|
||||
to_insert.push((next_excerpt_id(), new_iter.next().unwrap()));
|
||||
continue;
|
||||
};
|
||||
if existing_excerpt.buffer_id != buffer_snapshot.remote_id() {
|
||||
to_remove.push(existing_iter.next().unwrap());
|
||||
to_insert.push((next_excerpt_id(), new_iter.next().unwrap()));
|
||||
continue;
|
||||
}
|
||||
|
||||
let existing_start = existing_excerpt
|
||||
.range
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user