Compare commits

...

121 Commits

Author SHA1 Message Date
Conrad Irwin
ada0523043 Fix mouse selection
Co-Authored-By: WindSoilder <WindSoilder@outlook.com>
2024-02-02 16:36:51 -07:00
WindSoilder
aeb8535468 Merge branch 'main' into vim_click 2024-02-01 06:59:01 +08:00
Marshall Bowers
a588a7dd4d Fix some typos in comments (#7169)
This PR fixes a couple typos I found in some comments/doc comments.

Release Notes:

- N/A
2024-01-31 16:21:33 -05:00
Conrad Irwin
dcca48482b disallow opening private files (#7165)
- Disallow sharing gitignored files through collab
- Show errors when failing to open files
- Show a warning to followers when view is unshared

/cc @mikaylamaki, let's update this to use your `private_files` config
before merge.


Release Notes:

- Added the ability to prevent sharing private files over collab.

---------

Co-authored-by: Piotr <piotr@zed.dev>
Co-authored-by: Mikayla <mikayla@zed.dev>
2024-01-31 12:05:51 -08:00
Joseph T. Lyons
c983c9b6df v0.122.x dev 2024-01-31 14:44:24 -05:00
Mikayla Maki
f98d636203 WIP: Add a setting to visually redact enviroment variables (#7124)
Release Notes:

- Added bash syntax highlighting to `.env` files. 
- Added a `private_files` setting for configuring which files should be
considered to contain environment variables or other sensitive
information.
- Added a `redact_private_values` setting to add or remove censor bars
over variable values in files matching the `private_files` patterns.
-(internal) added a new `redactions.scm` query to our language support,
allowing different config file formats to indicate where environment
variable values can be identified in the syntax tree, added this query
to `bash`, `json`, `toml`, and `yaml` files.

---------

Co-authored-by: Nathan <nathan@zed.dev>
2024-01-31 11:42:09 -08:00
Ben Hamment
5333eff0e4 Improve file finder by ignoring spaces in query (#7068)
Release Notes:

- Changed file finder to ignore spaces in queries ([#5324
](https://github.com/zed-industries/zed/issues/5324)).


![image](https://github.com/zed-industries/zed/assets/7274458/14f3d511-129d-4e73-b9d3-12ce1aaa892f)

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-01-31 14:18:21 -05:00
Conrad Irwin
135bca262c vim: Make H/M/L work in visual mode (#7166)
Release notes:
- N/A
2024-01-31 12:13:35 -07:00
d1y
5d85801d1f Add highlighting for go.work (#7142)
<img width="617" alt="image"
src="https://github.com/zed-industries/zed/assets/45585937/ecb28152-db02-450e-bc81-395abd1c1eef">

Release Notes:

- Added  highlighting for go.work
2024-01-31 12:11:03 -07:00
Vishal Bhavsar
ebdabb907a vim: Support counts for H and L motions (#7149)
Release Notes:

- Added support for counts to `H` and `L` motions
([#4941](https://github.com/zed-industries/zed/issues/4941)).
2024-01-31 11:50:08 -07:00
Conrad Irwin
689d43047d Don't panic when collaborating with older Zed versions (#7162)
Older Zed versions may send a buffer id of 0, which is no-longer
supported. (as of #6993)

This doesn't fix that, but it does ensure that we don't panic in the
workspace by maintaining the invariant that from_proto_state returns
Some(Task) if the variant matches.

It also converts the panic to an error should something similar happen
again in the future.


Release Notes:

- N/A
2024-01-31 11:46:03 -07:00
Ares Andrew
59f77d316f Use mimalloc as default allocator (#7140)
From https://github.com/microsoft/mimalloc:
> In our benchmarks (see
[below](https://github.com/microsoft/mimalloc#performance)), mimalloc
outperforms other leading allocators (jemalloc, tcmalloc, Hoard, etc),
and often uses less memory. A nice property is that it does consistently
well over a wide range of benchmarks. There is also good huge OS page
support for larger server programs.


Release Notes:

- Changed default allocator to mimalloc.
2024-01-31 09:50:37 -08:00
d1y
b7ced3943e Add highlighting for git_commit (#7147)
https://github.com/zed-industries/zed/assets/45585937/32cf5622-e960-4775-986d-bcfd30c81098

Release Notes:

- Added highlighting for git_commit
2024-01-31 09:41:19 -08:00
Marshall Bowers
39200ec9f7 Adjust heading levels in docs (#7163)
This PR adjusts the heading levels in the docs, as some of them weren't
following the right hierarchy.

I also formatted all of the docs with Prettier.

Release Notes:

- N/A
2024-01-31 12:34:31 -05:00
Pyae Sone Aung
6e443ac298 Add PHP file type icon (#7159)
Add PHP file type icon from
[file-icons/icons](https://github.com/file-icons/icons)


[https://github.com/file-icons/icons/blob/master/svg/PHP.svg](https://github.com/file-icons/icons/blob/master/svg/PHP.svg)

<img width="408" alt="Screenshot 2024-01-31 at 23 14 55"
src="https://github.com/zed-industries/zed/assets/44226349/26c3d19d-3a5d-4fc6-b551-f5351ba62b7d">


Release Notes:

- Added PHP file type icon.
2024-01-31 12:14:28 -05:00
Piotr Osiewicz
5941102aac gpui: Add runtime-shaders feature so that Xcode.app is no longer necessary for Nix-based workflows (#7148)
Release Notes:

- N/A

Co-authored-by: Niklas <niklas@niklaskorz.de>
2024-01-31 17:37:16 +01:00
Marshall Bowers
8c8a5ad275 Make theme parsing more lenient (#7154)
This PR improves the theme parsing to be a bit more lenient, allowing
things like comments and trailing commas in theme files.

Release Notes:

- N/A
2024-01-31 11:05:22 -05:00
Julia
7cb97e57f9 Add debounce for re-querying completion documentation 2024-01-31 09:50:26 -05:00
Andrey Kuzmin
634fe99fa5 Add LSP support for Elm (#7116)
Closes #4595

Release Notes:

- Added LSP support for Elm
([#4595](https://github.com/zed-industries/zed/issues/4595)).

---------

Co-authored-by: Jared M. Smith <absynce@gmail.com>
2024-01-31 11:05:38 +02:00
d1y
c3d4fa4336 Permalink add Gitee host support (#7134)
China's largest git code hosting platform
About Gitee: https://gitee.com/about_us

Release Notes:

- Added Gitee host support   with Git-Permalink
2024-01-31 08:54:03 +01:00
Derrick Laird
ba91adf48a languages: add highlighting for go.mod (#7137)
Release Notes:

- Added syntax highlighting for go.mod files. Fixes #7133 

<img width="863" alt="image"
src="https://github.com/zed-industries/zed/assets/8725798/dc521a02-c53a-44aa-b0c1-eebf31835679">
2024-01-31 08:46:13 +01:00
Marshall Bowers
e5fe811d7a theme_importer: Add ability to print theme JSON schema (#7129)
This PR adds a quick subcommand to the `theme_importer` to facilitate
printing out the JSON schema for a theme.

Note that you do need to pass a `<PATH>` to the subcommand still, even
though it will be ignored. I'll rework the CLI to this at some point.

The JSON schema for the current version of the theme can also be found
at
[`https://zed.dev/schema/themes/v0.1.0.json`](https://zed.dev/schema/themes/v0.1.0.json).

Release Notes:

- N/A
2024-01-30 23:33:54 -05:00
Max Brunsfeld
9459394ea0 Re-enable language plugin functionality with some fixes (#7105)
Part of https://github.com/zed-industries/zed/issues/7096

* [x] Load all queries for language plugins, not just highlight query
* [x] Auto-reload languages when changing the `plugins` directory
* [x] Bump Tree-sitter for language loading and unloading fixes
* [x] Figure out code signing

Release Notes:

- N/A

---------

Co-authored-by: Antonio <antonio@zed.dev>
Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-01-30 19:59:13 -08:00
Conrad Irwin
db99d4fef1 No more nightly/preview collab anymore (#7112)
Release Notes:

- N/A
2024-01-30 20:11:06 -07:00
Conrad Irwin
aee0f65b26 Attempt to fix a panic in worktree scanning (#7128)
Somehow (and this should be investigated separately) we're ending up
with paths that look like: /path/to/project/../../path/to/dependency,
these pass the Ok(repo_path) = path.strip_prefix(), but then fail.

Release Notes:

- Fixed (hopefully) a panic that could occur due to path confusing in
git status
2024-01-30 20:10:52 -07:00
Marshall Bowers
dbb5fad147 Fix some formatting issues in Cargo.toml files (#7127)
This PR fixes some formatting issues in some of the `Cargo.toml` files.

I tried to fix most of these in #7126, but there were a few that I
missed.

Release Notes:

- N/A
2024-01-30 22:01:35 -05:00
Zongle Wang
28f875f5f9 Note installation step via Homebrew (#7053)
https://formulae.brew.sh/cask/zed
2024-01-30 21:47:30 -05:00
Marshall Bowers
e338f34097 Sort dependencies in Cargo.toml files (#7126)
This PR sorts the dependency lists in our `Cargo.toml` files so that
they are in alphabetical order.

This should make them easier to visually scan when looking for a
dependency.

Apologies in advance for any merge conflicts 🙈 

Release Notes:

- N/A
2024-01-30 21:41:29 -05:00
Marshall Bowers
d97e780135 Restrict access to global Audio (#7122)
This PR restricts access to the `Audio` global to force consumers to go
through the `Audio` public interface to interact with it.

Release Notes:

- N/A
2024-01-30 20:49:58 -05:00
Marshall Bowers
176f63e86e Add ability to copy a permalink to a line (#7119)
This PR adds the ability to copy the permalink to a line from within
Zed.

This functionality is available through the `editor: copy permalink to
line` action in the command palette:

<img width="589" alt="Screenshot 2024-01-30 at 7 07 46 PM"
src="https://github.com/zed-industries/zed/assets/1486634/332282cb-211f-4f16-9eb1-415bcfee9b7b">

Executing this action will create a permalink to the currently selected
line(s) and copy it to the clipboard.

Here is an example line:

```
56c80e8011/src/lib.rs (L25)
```

Currently, both GitHub and GitLab are supported.

### Notes and known limitations

- In order to determine where to permalink to, we read the URL of the
`origin` remote in Git. This feature will not work if the `origin`
remote is not present.
- Attempting to permalink to a ref that is not pushed to the origin will
result in the link 404ing.
- Attempting to permalink when Git is in a dirty state may not generate
the right link.
- For instance, modifying a file (e.g., adding new lines) and grabbing a
permalink to it will result in incorrect line numbers.

Release Notes:

- Added the ability to copy a permalink to a line
([#6777](https://github.com/zed-industries/zed/issues/6777)).
- Available via the `editor: copy permalink to line` action in the
command palette.
2024-01-30 19:20:15 -05:00
Felix Salazar
cbcaca4153 Show highlighted symbol in the scrollbar (#7029)
Release Notes:

- Added highlighted symbols to the scrollbar; partially mentioned in:
  - https://github.com/zed-industries/zed/issues/5308
  - https://github.com/zed-industries/zed/issues/4866
2024-01-30 13:57:42 -08:00
Conrad Irwin
871b8525b4 Fix per-env settings override (#7114)
Due to a misplaced .trim(), the RELEASE_CHANNEL_NAME included the
trailing newline.

Release Notes:

- N/A
2024-01-30 14:38:51 -07:00
Derrick Laird
a5826e22f5 Add Go file icon (#7110)
![IMG_4664](https://github.com/zed-industries/zed/assets/8725798/75436116-7c7e-4ae6-b76c-13f21c52bee8)

Release Notes:

- Added icon to `.go` files
2024-01-30 15:48:33 -05:00
Conrad Irwin
7f66e366b4 Release version of clippy? (#7107)
Release Notes:

- N/A
2024-01-30 13:29:39 -07:00
Conrad Irwin
3075e58729 collab 0.43.0 2024-01-30 13:22:54 -07:00
Conrad Irwin
911b4b5ae8 Migrate automatically on service start (#7103)
This avoids a forgettable manual step in deploying collab

Release Notes:

- N/A
2024-01-30 13:19:35 -07:00
Marshall Bowers
2e7f9c48bb Use fully-qualified name to avoid an unused import (#7104)
This PR adjusts how we implement `Global` conditionally to avoid an
unused import when compiling in release mode.

Release Notes:

- N/A
2024-01-30 14:57:54 -05:00
Mikayla Maki
a54eaaecee Add raw window handle implementations to GPUI (#7101)
This is in preparation for experiments with wgpu. This should have no
external effect.

Release Notes:

- N/A
2024-01-30 11:42:28 -08:00
Marshall Bowers
1d794dbb37 Only impl Global for DebugBelow when compiling with debug_assertions (#7102)
This PR fixes this error when compiling a release build:

<img width="504" alt="Screenshot 2024-01-30 at 2 30 38 PM"
src="https://github.com/zed-industries/zed/assets/1486634/96470735-2b9e-4945-b4c3-c86ef0168b8c">

`DebugBelow` only exists when compiling with `debug_assertions`, so we
only want to implement it using that same criterion.

Release Notes:

- N/A
2024-01-30 14:37:29 -05:00
Piotr Osiewicz
e756602610 log: Use local timezone in log timestamps (#7079)
I'm gonna let it sit for a day in case anybody has any objections to
that change.

Release Notes:

- Logs now use local timestamps instead of UTC-based timestamps

---------

Co-authored-by: Beniamin <beniamin@zagan.be>
2024-01-30 20:18:39 +01:00
Piotr Osiewicz
e6ebe7974d gpui: Add Global marker trait (#7095)
This should prevent a class of bugs where one queries the wrong type of
global, which results in oddities at runtime.

Release Notes:

- N/A

---------

Co-authored-by: Marshall <marshall@zed.dev>
Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-01-30 14:08:20 -05:00
Derrick Laird
7bfa584eb6 Add protobuf support (#6748)
Release Notes:

- Added protobuf syntax highlighting
([#5160](https://github.com/zed-industries/zed/issues/5160)).
2024-01-30 21:08:10 +02:00
Conrad Irwin
dfbcaf36fc nightly url setting (#7037)
Release Notes:

- Added the ability to set settings per-release stage
- Added a `"server_url"` setting
2024-01-30 11:35:07 -07:00
Conrad Irwin
c07355265f Add logging for the font_descriptor panic (#7097)
Release Notes:

- Fixed a panic caused by an inconsistency in font metrics.
2024-01-30 11:16:43 -07:00
白山風露
631f885900 Ensure sqlez build succeeds on Windows (#7072)
On Windows, `OsStr` must be a valid
[WTF-8](https://simonsapin.github.io/wtf-8/) sequence, and there are no
safety ways converting from bytes to OsStr in std. So I added
`PathExt::try_from_bytes` and use it in `sqlez`.
2024-01-30 10:07:46 -08:00
Remco Smits
30b9cef5ba Improve mention visibility by adding a background color (#7014)
When the chat if going fast, It's hard to see who is mentioning you, so
this feature will make it more clear by the UI instead of needing to
read all the messages.

<img width="242" alt="Screenshot 2024-01-29 at 21 19 07"
src="https://github.com/zed-industries/zed/assets/62463826/65ec307d-5027-4ead-9568-854fc746c822">

Release Notes:

- Added background to messages that mention you.
2024-01-30 09:58:36 -08:00
fminkowski
e5c4c8522b C# Support: Add treesitter and OmniSharp LSP support (#6908)
This PR adds the C# tree-sitter grammar. It also adds OmniSharp-Roslyn
for LSP support.

Resolves issue
[#5299](https://github.com/zed-industries/zed/issues/5299)

Release Notes:

- Added C# support

## VSCode
<img width="984" alt="vscode"
src="https://github.com/zed-industries/zed/assets/6967829/1f6b4cb7-4e00-4d61-8e58-2867dc5c8ecf">

## Zed
<img width="1722" alt="zed"
src="https://github.com/zed-industries/zed/assets/6967829/88436c78-93de-4e26-be15-b0dea6590c55">
2024-01-30 09:54:39 -08:00
Marshall Bowers
2980f0508c Rework loading images from files (#7088)
This PR is a follow-up to #7084, where I noted that I wasn't satisfied
with using `SharedUri` to represent both URIs and paths on the local
filesystem:

> I'm still not entirely happy with this naming, as the file paths that
we can store in here are not _really_ URIs, as they are lacking a
protocol.
>
> I want to explore changing `SharedUri` / `SharedUrl` back to alway
storing a URL and treat local filepaths differently, as it seems we're
conflating two different concerns under the same umbrella, at the
moment.

`SharedUri` has now been reverted to just containing a `SharedString`
with a URI.

`ImageSource` now has a new `File` variant that is used to load an image
from a `PathBuf`.

Release Notes:

- N/A
2024-01-30 11:26:02 -05:00
Thorsten Ball
6d4fe8098b Fix panic in fuzzy-finder for unicode characters (#7080)
This fixes a panic in the fuzzy finder which someone ran into when
typing in a query that contained the lower-case version of a unicode
character that has more chars than its upper-case version.

It also fixes another problem which was that we didn't find a match if
both candidates and query contained upper-case characters whose
lower-case version had more chars.


Release Notes:

- Fixed a panic in fuzzy-finder that could occur when matching with
queries containing upper-case unicode characters whose lower-case
version has more chars.

Co-authored-by: bennetbo <bennetbo@gmx.de>
2024-01-30 16:10:35 +01:00
Marshall Bowers
6c7893db35 Rename SharedUrl to SharedUri (#7084)
This PR renames `SharedUrl` to `SharedUri` to better reflect its intent.

I'm still not entirely happy with this naming, as the file paths that we
can store in here are not _really_ URIs, as they are lacking a protocol.

I want to explore changing `SharedUri` / `SharedUrl` back to alway
storing a URL and treat local filepaths differently, as it seems we're
conflating two different concerns under the same umbrella, at the
moment.

Release Notes:

- N/A
2024-01-30 09:54:23 -05:00
Noritada Kobayashi
d18c0d9df0 Make minor documentation corrections and improvements (#7070)
This PR makes minor documentation corrections and improvements.

Release Notes:

- N/A
2024-01-30 12:20:05 +01:00
d1y
a0582d02b9 Make avif/heif/webp files to use image icon (#7063)
The Wikipedia Link:

- https://en.wikipedia.org/wiki/AVIF
- https://en.wikipedia.org/wiki/High_Efficiency_Image_File_Format
- https://en.wikipedia.org/wiki/WebP

Release Notes:

- Made avif/heif/webp files to use an image icon
2024-01-30 11:58:56 +02:00
Ammar Arif
fb9eb6a0fc Add taplo toml LSP (#7034)
Release Notes:

- Added `toml` LSP using [taplo](https://taplo.tamasfe.dev/)
2024-01-30 11:50:15 +02:00
Thorsten Ball
7b8bd97652 vim: implement <space> in normal mode (#7011)
This fixes #6815 by implementing `<space>` in normal mode in Vim. Turns
out that `<space>` behaves like a reverse `<backspace>` (which we
already had): it goes to the right and, if at end of line, to the next
line.

That means I had to touch `movement::right`, which is used in a few
places, but it's documentation said that it would go to the next line,
which it did *not*. So I changed the behaviour.

But I would love another pair of eyes on this, because I don't want to
break non-Vim behaviour.

Release Notes:

- Added support for `<space>` in Vim normal mode: `<space>` goes to the
right and to next line if at end of line.
([#6815](https://github.com/zed-industries/zed/issues/6815)).
2024-01-30 10:47:39 +01:00
Hans
4af542567c Fix a spelling typo (#7059)
Release notes: N/A
2024-01-30 11:40:33 +02:00
d1y
dc5f4c8719 Add CSS file type icon (#7062)
The SVG icon RAW link: https://www.svgrepo.com/svg/473577/css3
The Licensing https://www.svgrepo.com/page/licensing/#CC0

<img width="309" alt="image"
src="https://github.com/zed-industries/zed/assets/45585937/4b1fa935-144f-4c02-a378-b0974e4d0b39">


Release Notes:

- Added a CSS file type icon
2024-01-30 11:37:48 +02:00
Thorsten Ball
cddc0fbf92 vim: fix t not being repeatable with , (#7007)
This fixes `t` not being repeatable with `,` and `;` in normal mode.

Release Notes:

- Fixed `t` in Vim mode not being repeatable with `,` or `;`.

---------

Co-authored-by: Conrad <conrad@zed.dev>
2024-01-30 09:17:19 +01:00
Hans
843916d585 Fix two typos (#7056) 2024-01-30 10:17:06 +02:00
Hans
561cd37c85 Spell adjust (#7050) 2024-01-30 02:23:10 -05:00
Bennet Bo Fenner
dd74643993 gpui: Support loading image from filesystem (#6978)
This PR implements support for loading and displaying images from a
local file using gpui's `img` element.

API Changes:
- Changed `SharedUrl` to `SharedUrl::File`, `SharedUrl::Network`

Usage:
```rust
// load from network
img(SharedUrl::network(...)) // previously img(SharedUrl(...)

// load from filesystem
img(SharedUrl::file(...))
```

This will be useful when implementing markdown image support, because we
need to be able to render images from the filesystem (relative/absolute
path), e.g. when implementing markdown preview #5064.

I also added an example `image` to the gpui crate, let me know if this
is useful. Showcase:
<img width="872" alt="image"
src="https://github.com/zed-industries/zed/assets/53836821/b4310a26-db81-44fa-9a7b-61e7d0ad4349">

**Note**: The example is fetching images from [Lorem
Picsum](https://picsum.photos) ([Github
Repo](https://github.com/DMarby/picsum-photos)), which is a free
resource for fetching images in a specific size. Please let me know if
you're okay with using this in the example.
2024-01-29 21:56:51 -08:00
Robert Clover
b865db455d Fix terminal line background being reset on each line (#7040)
Release Notes:

- Fixed #5027
- Fixed #5079

Info:

The terminal draws a rectangle for the background color of cells, but
this was being reset on every line. The effect of this was that tools
like Vim, Helix, and git-delta would only have one line with a
background color:

<img width="979" alt="Screenshot 2024-01-30 at 2 48 13 pm"
src="https://github.com/zed-industries/zed/assets/52195359/ab1873d2-0653-44c6-9406-bc2a277d9a2f">

After this change:

<img width="1011" alt="Screenshot 2024-01-30 at 2 49 45 pm"
src="https://github.com/zed-industries/zed/assets/52195359/6e4d2a70-590b-48b0-a464-4c827f55622e">
2024-01-29 21:01:02 -08:00
Marshall Bowers
372bc427bd Fix casing of OpenZedUrl action (#7045)
This PR updates the casing of the `OpenZedUrl` action to match the [Rust
naming
guidelines](https://rust-lang.github.io/api-guidelines/naming.html).

Release Notes:

- N/A
2024-01-29 23:50:31 -05:00
Marshall Bowers
0cb8b0e451 Clean up Cargo.toml files (#7044)
This PR cleans up some inconsistencies in the `Cargo.toml` files that
were driving me crazy.

Release Notes:

- N/A
2024-01-29 23:47:20 -05:00
Conrad Irwin
3736ce9d9d protobuf linter? (#7019)
Release Notes:

- N/A
2024-01-29 21:39:18 -07:00
Marshall Bowers
9f9bef4175 Prevent GitHub from displaying comments within JSON files as errors (#7043)
This PR adds a `.gitattributes` rule to prevent GitHub from displaying
comments within JSON files as errors.

We have a number of JSON files (e.g., settings) that make use of
comments, and having a bunch of red in a diff is annoying.

Release Notes:

- N/A
2024-01-29 23:11:25 -05:00
Vishal Bhavsar
31e9526544 vim: Add support for moving to first, middle and last visible lines (H, L, M) (#6919)
This change implements the vim
[motion](https://github.com/vim/vim/blob/master/runtime/doc/motion.txt)
commands to move the cursor to the top, middle and bottom of the visible
view. This feature is requested in
https://github.com/zed-industries/zed/issues/4941.

This change takes inspiration from
[crates/vim/src/normal/scroll.rs](https://github.com/zed-industries/zed/blob/main/crates/vim/src/normal/scroll.rs).

A note on the behavior of these commands: Because
`NeovimBackedTestContext` requires compatibility with nvim, the current
implementation causes slightly non-standard behavior: it causes the
editor to scroll a few lines. The standard behavior causes no scrolling.
It is easy enough to account for the margin by adding
`VERTICAL_SCROLL_MARGIN`. However, doing so will cause test failures due
to the disparity between nvim and zed states. Perhaps
`NeovimBackedTestContext` should have a switch to be more tolerant for
such cases.

Release Notes:

- Added support for moving to top, middle and bottom of the screen in
vim mode (`H`, `M`, and `L`)
([#4941](https://github.com/zed-industries/zed/issues/4941)).
2024-01-29 20:58:24 -07:00
Todsaporn Banjerdkit
1ab49fdbe6 Use fallback BPE if the language model doesn't have one (#6848)
Release Notes:

- Added a fallback BPE if the language model doesn't have one.

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-01-29 22:42:03 -05:00
Marshall Bowers
9cb5a84b8d Add support for loading user themes (#7027)
This PR adds support for loading user themes in Zed.

Themes are loaded from the `themes` directory under the Zed config:
`~/.config/zed/themes`. This directory should contain JSON files
containing a `ThemeFamilyContent`.

Here's an example of the general structure of a theme family file:

```jsonc
{
  "name": "Vitesse",
  "author": "Anthony Fu",
  "themes": [
    {
      "name": "Vitesse Dark Soft",
      "appearance": "dark",
      "style": {
        "border": "#252525",
        // ...
      }
    }
  ]
}
```

Themes placed in this directory will be loaded and available in the
theme selector.

Release Notes:

- Added support for loading user themes from `~/.config/zed/themes`.
2024-01-29 21:32:45 -05:00
d1y
5f4dd36a1a Add ability to expand/collapse directories using the project_panel::Open action (#6914)
#6910

I changed the `open_file` symbol to `open`, because this is more
consistent with the original intention

Release Notes:

- Added the ability to expand/collapse directories using the
`project_panel::Open` action.
2024-01-29 18:21:23 -08:00
Amin Yahyaabadi
2c834c24a3 Build media and live-kit in test-mode on non-MacOS (#6859)
Build media and live-kit in test-mode on non-MacOS (Related to
https://github.com/zed-industries/zed/issues/5391
https://github.com/zed-industries/zed/issues/5395
https://github.com/zed-industries/zed/issues/5394)

This makes it possible to build the media and live-kit crates on
non-MacOS

Release Notes:

- N/A
2024-01-29 18:04:15 -08:00
Marshall Bowers
e69e6f5586 Add a newtype wrapper around the global ThemeRegistry (#7025)
This PR adds a newtype wrapper around the global `ThemeRegistry`.

This allows us to limit where the `ThemeRegistry` can be accessed
directly via `cx.global` et al., without going through the dedicated
methods on the `ThemeRegistry`.

Release Notes:

- N/A
2024-01-29 18:09:37 -05:00
Marshall Bowers
7656096814 Rework access to ThemeRegistry global (#7023)
This PR reworks how we access the `ThemeRegistry` global.

Previously we were making calls directly on the context, like
`cx.global::<ThemeRegistry>`. However, one problem with this is that it
spreads out the knowledge of exactly what type is stored in the global.

In order to make it easier to adjust the type we store in the context
(e.g., wrapping the `ThemeRegistry` in an `Arc`), we now call methods on
the `ThemeRegistry` itself for accessing the global.

It would also be interesting to see how we could prevent access to the
`ThemeRegistry` without going through one of these dedicated methods 🤔

Release Notes:

- N/A
2024-01-29 17:51:34 -05:00
Ben Hamment
2a2cf45e90 Improve file types list for Ruby syntax (#7022)
### Improved list of file types that require Ruby syntax highlighting

- `.ru` files: These are Rackup files and are used for configuring Rack
applications.
- `.thor` files: Used for defining Thor tasks. Thor is a toolkit for
building powerful command-line interfaces in Ruby.
- `.cap, .capfile, and Capfile`: These files are used for Capistrano
deployment configuration.
- `.jbuilder` files: Used for creating JSON responses in a Rails
application.
- `.rabl files`: RABL (Ruby API Builder Language) files are used for
defining JSON templates.
- `.rxml` files: RXML files are used for creating XML responses.
- `.builder` files: Builder templates, which are used for generating XML
and other formats.
- `.gemspec` files: These files define metadata for a RubyGem.
- `.rdoc` files: RDoc documentation files.
- `.thor` files: These files are used for defining Thor tasks.
- `.pryrc` files: Configuration files for the Pry REPL (Read-Eval-Print
Loop).
- `.simplecov`: Configuration file for SimpleCov, a code coverage
analysis tool.

#### Some examples
- <img width="393" alt="image"
src="https://github.com/zed-industries/zed/assets/7274458/4e30b03a-ae21-4bae-ab11-48aa46b4bd0b">
- <img width="501" alt="image"
src="https://github.com/zed-industries/zed/assets/7274458/07251529-bcde-42d2-8973-341679e5cbc4">
- <img width="365" alt="image"
src="https://github.com/zed-industries/zed/assets/7274458/8c9c116c-2b51-4062-b83b-83f60838d28a">
- <img width="396" alt="image"
src="https://github.com/zed-industries/zed/assets/7274458/02d591af-88df-41f0-ba52-b11fa370ba07">
- <img width="432" alt="image"
src="https://github.com/zed-industries/zed/assets/7274458/91cd760f-f56a-488a-9736-bc03a44b27cf">
- <img width="296" alt="image"
src="https://github.com/zed-industries/zed/assets/7274458/282c331e-8896-49d4-b3b6-b3edbf0c1cc5">
- <img width="316" alt="image"
src="https://github.com/zed-industries/zed/assets/7274458/a2ddfccf-43d4-4657-a764-9e8d62aa4467">
- <img width="536" alt="image"
src="https://github.com/zed-industries/zed/assets/7274458/b0fce8db-4a9e-4615-bf5d-0b6c523bcbc8">

- `path_suffixes` Also new lines the array for readability especially
for diffs.

Release Notes:

- Associated `.ru`, `.thor`, `.cap`, `.capfile`, `Capfile`, `.jbuilder`,
`.rabl`, `.rxml`, `.builder`, `.gemspec`, `.rdoc`, `.thor`, `.pryrc`,
and `.simplecov` files with Ruby.
2024-01-29 17:41:57 -05:00
Max Brunsfeld
3728da1165 collab 0.42.1 2024-01-29 13:46:45 -08:00
Conrad Irwin
c008c78e87 Fix slow query for fetching descendants of channels (#7008)
Release Notes:

- N/A

---------

Co-authored-by: Max <max@zed.dev>
2024-01-29 13:24:59 -08:00
Amin Yahyaabadi
1313402a6b Introduce cross-platform file-watching (#6855)
This adds cross-platform file-watching via the
[Notify](https://github.com/notify-rs/notify) crate. The previous
fs-events implementation is now only used on MacOS, and on other
platforms Notify is used. The watching function interface is the same.

Related to #5391 #5395 #5394.

Release Notes:

- N/A
2024-01-29 22:18:10 +02:00
Piotr Osiewicz
b29f45ea68 Change underlying type of BufferId to NonZeroU64. 2024-01-29 20:00:47 +01:00
Piotr Osiewicz
5ab715aac9 text: Wrap BufferId into a newtype 2024-01-29 20:00:47 +01:00
Julia
941e838be9 Prevent z-index id shuffle when number of z-indicies in the scene change 2024-01-29 13:08:40 -05:00
Julia
849a1324f7 Fix hovering over elements nested inside their parents
Co-Authored-By: Conrad Irwin <conrad@zed.dev>
2024-01-29 13:08:40 -05:00
Marshall Bowers
d60ef81ede Setup Danger (#6994)
This PR sets up [Danger](https://danger.systems/js/) to help us codify
some of our PR rules.

As an initial rule, Danger will check to ensure that every PR has a
`Release Notes` section:

<img width="943" alt="Screenshot 2024-01-29 at 11 50 12 AM"
src="https://github.com/zed-industries/zed/assets/1486634/4d56e759-e72f-4bc0-8e74-42c55e2e6888">

Release Notes:

- N/A
2024-01-29 11:58:24 -05:00
Pseudomata
d694017d05 Add Haskell file type icon (#6995)
Small change to add the Haskell logo as a file icon. Screenshot below
shows what this looks like.

![CleanShot 2024-01-29 at 11 21
30@2x](https://github.com/zed-industries/zed/assets/132238190/b484c679-965a-4e73-88dc-ebb670a0f390)
2024-01-29 11:32:35 -05:00
Marshall Bowers
d1a6003033 Use Git status color for icons in project panel (#6992)
This PR updates the icons in the file tree in the project panel to also
use the Git status color:

<img width="267" alt="Screenshot 2024-01-29 at 10 21 41 AM"
src="https://github.com/zed-industries/zed/assets/1486634/2f4f6e9e-8050-4f5b-851a-e407aec823a0">

Release Notes:

- Updated icons in project panel to reflect Git status
([#6991](https://github.com/zed-industries/zed/issues/6991)).
2024-01-29 10:26:30 -05:00
bestgopher
d2968866b2 rust: Update rust-analyzer repo address (#6986)
I am so sorry that I closed the
https://github.com/zed-industries/zed/pull/6980 misoperationally. Here
is a new pr.
2024-01-29 09:55:31 -05:00
WindSoilder
a082b93260 single click convert from virual mode to normal mode 2024-01-29 20:41:36 +08:00
Abel Chalier
a827d0dac6 fixed zig config on line_comments (#6979)
On a .zig files , the `CMD-/` keybinding don't work,
In the language config its the only language with '//' type comment that
is written in singular without array.
This fixed the problem, so i assume it was just a typo

![image](https://github.com/zed-industries/zed/assets/6186996/d7f2381a-8b21-4f50-8910-6685b81a5aec)

Release notes:
- Fixed line comment continuations and Editor::ToggleComments for Zig, Haskell and PureScript.
2024-01-29 13:34:26 +01:00
liruifengv
843919dd14 docs: Update configuring_zed__key_bindings.md (#6974)
Release notes: N/A
2024-01-29 14:08:12 +02:00
d1y
8263da02bd Make .jsonc icon use storage icon (#6972)
Jsonc is a simplified json format which allows comments and unquoted
values delimited by whitespace. A jsonc formatted file can be
unambiguously transformed to a json file. Comments will be stripped out
and quotes added.

Release Notes:

- Added an icon for .jsonc files
2024-01-29 13:47:23 +02:00
George Munyoro
49542757fd Correctly check existence of target directory in copy_recursive function (#6875)
Fixes https://github.com/zed-industries/zed/issues/6778

Release Notes:

- Fixed issue where copy-paste for folders was not working in the
project panel
([#6778](https://github.com/zed-industries/zed/issues/6778)).
2024-01-29 12:06:02 +02:00
Piotr Osiewicz
9ef830e9bb update: Add arch and os to release query parameters. (#6976)
This is no-op right now, but we need it for multi-platform support and
(potentially) to split the Universal Binary on mac into two
non-universal targets.
Related to: #6837 
Release Notes:
- N/A
2024-01-29 10:48:54 +01:00
Piotr Osiewicz
8fbc88b708 Load embedded fonts directly from .rdata instead of cloning (#6932)
The fonts we embed in Zed binary (Zed Sans & Zed Mono) weigh about 30Mb in total and we are cloning them several times during startup and loading of embedded assets (once explicitly in Zed and then under the hood in font-kit). Moreover, after loading we have at least 2 copies of each font in our program; one in .rdata and the other on the heap for use by font-kit.
This commit does away with that distinction (we're no longer allocating the font data) and slightly relaxes the interface of `TextSystem::add_fonts` by expecting one to pass `Cow<[u8]>` instead of `Arc<Vec<u8>>`. Additionally, `AssetSource::get` now returns `Cow<'static, [u8]>` instead of `Cow<'self, [u8]>`; all existing implementations conform with that change.

Note that this optimization takes effect only in Release builds, as the library we use for asset embedding - rust-embed - embeds the assets only in Release mode and in Dev builds it simply loads data from disk. Thus it returns `Cow<[u8]>` in it's interface. Therefore, we still copy that memory around in Dev builds, but that's not really an issue. 
This patch makes no assumptions about the build profile we're running under, that's just an intrinsic property of rust-embed.

Tl;dr: this should shave off about 30Mb of memory usage and a fair chunk (~30ms) of startup time.

Release Notes:
- Improved startup time and memory usage.
2024-01-29 10:06:57 +01:00
Joseph T. Lyons
e22ffb6740 Run weekly update each day 2024-01-29 02:47:25 -05:00
Kieran Gill
00a6ac6141 docs: Consistent shortcut style (#6961)
Hey all. This is a follow-up to my last PR
https://github.com/zed-industries/zed/pull/6821. I went through and
applied the same style changes to more pages so your docs are more
consistent.

<img width="1330" alt="image"
src="https://github.com/zed-industries/zed/assets/18583882/b9adbb38-b98d-47c6-b585-7e298ffae854">


Note that some pages from your old docs repo haven't been ported to
`zed-industries/zed` yet.

Release Notes:

- Improved documentation consistency.
2024-01-28 22:33:07 -05:00
Brian Ginsburg
6cd306e4c9 docs: Add initial language settings documentation (#6957)
This pull request implements the following documentation changes:

- [x] Copy existing language settings docs from old docs repo
- [x] Add new pages for Zig, Haskell, Gleam, Deno and PureScript
- [x] Add `rust-analyzer` target directory section to Rust language page

Release Notes:

- Added initial language settings documentation
([#4264](https://github.com/zed-industries/zed/issues/4264)).
2024-01-28 22:07:27 -05:00
Marshall Bowers
5d0c144ce7 theme_importer: Define more colors in VsCodeTheme (#6960)
This PR extends the `VsCodeTheme` struct with more of the colors
available on a VS Code theme.

Release Notes:

- N/A
2024-01-28 21:55:40 -05:00
Conrad Irwin
331b6e7e6e Support "dtx" vim key combination when "x" is immediately to the right of the cursor (#6830)
Closes #4358

The bug originates on this line:
5db7e8f89e/crates/vim/src/motion.rs (L451)

- When running "dtx" on "ˇabcx", the range to delete is 0 -> 2 ("abc")
- When running "dtx" on "abˇcx", the range to delete is 2 -> 2 ("c"), so
`new_point == point` and the function incorrectly returns `None` and "c"
is not deleted
- We need to disambiguate between the "not found" case and the "found
immediately to the right" case
- This bug does not apply to the backwards case ("dTx")

Release Notes:
- Fixed "dtx" vim key combination when "x" is immediately to the right
of the cursor (#4358)
2024-01-28 19:46:14 -07:00
Conrad Irwin
4ab1d8af62 Add support for u and U in vim visual mode (#6948)
This pull request adds support for upper / lower casing a visual
selection with `u` or `U` respectively, and resolves #6901.

I took an approach to split out the logic of the existing `ChangeCase`
action into something that could be reused for the two new actions. One
area that I could use feedback on is whether or not vim binding updates
make sense. Judging purely from the way things work from a clean install
of Vim / Neovim, I'm pretty sure that it's a bug that `u` applies an
undo in visual mode, so I think it's safe to make this change, but it
changes a behavior that some users may be used to by now in Zed.

Release Notes:

- Added support for `u` and `U` in Vim visual mode
([#6901](https://github.com/zed-industries/zed/issues/6901)).
2024-01-28 19:37:46 -07:00
Joseph T. Lyons
7c93f6244d Reinstate requirements.txt 2024-01-28 18:30:47 -05:00
Joseph T. Lyons
92911def96 Update top ranking issues script to take in a day interval (#6951)
Switch to `poetry`
Update script to take in a `query-day-interval` argument, so we can make
a report from all issues or just n last days.

Release Notes:

- N/A
2024-01-28 18:26:01 -05:00
Joseph T. Lyons
80cdd60d4c Add a weekly report 2024-01-28 18:22:39 -05:00
Joseph T. Lyons
6305761064 Update top ranking issues script to take in a day interval 2024-01-28 18:09:11 -05:00
Iván Molina Rebolledo
db68fc5130 Add PureScript LSP/Highlighting support (#6911)
This PR adds basic support for the language using the
`purescript-language-server`.

Release Notes:

- Added support for PureScript.
2024-01-28 17:44:50 -05:00
Kirill Bulatov
7767062c09 Add missing workspace edit capabilities (#6950)
Closes https://github.com/zed-industries/zed/issues/6916

Specification:
https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspaceEdit
Adds a safe minimum of the capabilities that Zed supports already,
allowing rust-analyzer to send file edits on rename.

Release Notes:

- Fixed rust module rename from editor not renaming the FS entries
([6916](https://github.com/zed-industries/zed/issues/6916))
2024-01-28 23:46:10 +02:00
Marshall Bowers
444f918e51 Retrieve credentials for saved conversations (#6949)
This PR fixes an issue where credentials for saved conversations weren't
being fully retrieved, which was preventing continuing a conversation
with the assistant in a saved conversation.

Release Notes:

- Fixed continuing a saved conversation with the Zed assistant.
2024-01-28 15:25:57 -05:00
Brooks Swinnerton
52f7f2d770 Update name of function to match other implementation 2024-01-28 15:20:50 -05:00
Brooks Swinnerton
c0c0abae56 Add support for u and U in vim visual mode 2024-01-28 15:13:09 -05:00
Julia
8e523d812f Fix #4628: npm install to a wrong location (#6742)
By default `npm install` will walk up the folder tree checking for a
folder that contains either a package.json file, or a node_modules
folder. If such a thing is found, then that is treated as the effective
"current directory" for the purpose of running npm commands.
This caused npm dependencies for language servers sometimes to be
installed in the wrong folder, described in #4628.
Adding `--prefix ./` to `npm install` forces
node_runtime.npm_install_packages to install packages in provided path
even if node_modules dir exists somewhere up the filesystem tree from
installation path.
2024-01-28 14:10:10 -05:00
Marshall Bowers
1f82a2aca1 Remove obviated comment in zed-licenses.toml (#6946)
This PR removes a comment from `zed-licenses.toml`, as it no longer
applies now that we don't have `buildLicenses.ts` anymore.

Release Notes:

- N/A
2024-01-28 14:07:53 -05:00
Ikko Eltociear Ashimine
b213458803 Fix typo in copilot.rs (#6933)
specifcially -> specifically

Release Notes:

- N/A
2024-01-28 19:56:50 +02:00
d1y
4fc01163da Add Vue file icon (#6930)
https://github.com/vuejs/art/blob/master/white-on-dark-logo.svg
<img width="222" alt="image"
src="https://github.com/zed-industries/zed/assets/45585937/2839dd7e-ea25-4f9b-aac9-2437f1c3df23">

Release Notes:

- Added icon for `.vue` files.
2024-01-28 12:32:43 -05:00
ge
42e605a766 fix: adding prefix arg to npm subcommands 2024-01-28 19:09:50 +02:00
Marshall Bowers
027f055841 Update casing of "OpenAI" in identifiers to match Rust conventions (#6940)
This PR updates the casing of "OpenAI" when used in Rust identifiers to
match the [Rust naming
guidelines](https://rust-lang.github.io/api-guidelines/naming.html):

> In `UpperCamelCase`, acronyms and contractions of compound words count
as one word: use `Uuid` rather than `UUID`, `Usize` rather than `USize`
or `Stdin` rather than `StdIn`.

Release Notes:

- N/A
2024-01-28 12:01:10 -05:00
Marshall Bowers
e8bf06fc42 Style crate names in CONTRIBUTING.md (#6939)
This PR styles the crate names in `CONTRIBUTING.md` using inline code.

Release Notes:

- N/A
2024-01-28 11:07:37 -05:00
Marshall Bowers
5dec9f7163 Link to the Zed roadmap in CONTRIBUTING.md (#6937)
This PR updates `CONTRIBUTING.md` to link to the public Zed roadmap.

Release Notes:

- N/A
2024-01-28 10:48:38 -05:00
Piotr Osiewicz
f7b6bfefe6 chore: Remove rerun-if-changed clause that might cause spurious rebuilds (#4193)
Something is stomping over the timestamp of generated scene.h right
after the build script finishes:
`Dirty gpui v0.1.0 (/Users/someonetoignore/work/zed/zed/crates/gpui):
the file target/debug/build/gpui-ca04eedfe8d0e13c/out/scene.h has
changed (1705922004.637000680s, 1s after last build at
1705922003.507431315s)`
^ That's an output of `-v` flag added to either `cargo run` or `cargo
build`.
This comes up when you do `cargo build` followed by `cargo run` or
another `cargo build`; the artifact is not getting reused, even though
it should be possible?
Release Notes:
- N/A
2024-01-28 15:35:53 +01:00
d1y
e38489196d Make .gitkeep icon use VCS icon (#6931)
This should be a standard recognized by everyone

```bash
mkdir todo
touch todo/.gitkeep # just placeholder
git add todo
git commit
```

Release Notes:

- Added icon for `.gitkeep` files.
2024-01-28 16:30:30 +02:00
Noritada Kobayashi
3493e71ad4 Add Ruby file icon (#6922)
This icon is designed based on [Ruby's official logo], to harmonize with
the other icons.
It is deformed and simplified to be human-recognizable, even at letter
size.

[Ruby's official logo]: https://www.ruby-lang.org/en/about/logo/

Release Notes:

- Added Ruby file icon.
2024-01-28 13:46:32 +02:00
Marshall Bowers
3eb0a2c3c7 theme_importer: Map more colors (#6913)
This PR extends the `theme_importer` to map more colors from VS Code
themes.

Release Notes:

- N/A
2024-01-27 23:38:07 -05:00
Marshall Bowers
ff76b2ec4d Replace IIFE with maybe! (#6909)
This PR replaces a usage of an IIFE with `maybe!`.

Release Notes:

- N/A
2024-01-27 22:34:37 -05:00
Brian Strauch
5b9f15e075 add test_data 2024-01-26 20:02:59 -08:00
Brian Strauch
f369e9a48b vim: fix dtx when x is immediately to the right 2024-01-26 16:58:48 -08:00
356 changed files with 9873 additions and 4465 deletions

2
.gitattributes vendored Normal file
View File

@@ -0,0 +1,2 @@
# Prevent GitHub from displaying comments within JSON files as errors.
*.json linguist-language=JSON-with-Comments

View File

@@ -14,7 +14,7 @@ runs:
# so specify those here, and disable the rest until Zed's workspace
# will have more fixes & suppression for the standard lint set
run: |
cargo clippy --workspace --all-features --all-targets -- -A clippy::all -D clippy::dbg_macro -D clippy::todo
cargo clippy --release --workspace --all-features --all-targets -- -A clippy::all -D clippy::dbg_macro -D clippy::todo
cargo clippy -p gpui
- name: Find modified migrations
@@ -22,3 +22,9 @@ runs:
run: |
export SQUAWK_GITHUB_TOKEN=${{ github.token }}
. ./script/squawk
- uses: bufbuild/buf-setup-action@v1
- uses: bufbuild/buf-breaking-action@v1
with:
input: "crates/rpc/proto/"
against: "https://github.com/${GITHUB_REPOSITORY}.git#branch=main,subdir=crates/rpc/proto/"

35
.github/workflows/danger.yml vendored Normal file
View File

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

View File

@@ -14,4 +14,4 @@ jobs:
architecture: "x64"
cache: "pip"
- run: pip install -r script/update_top_ranking_issues/requirements.txt
- run: python script/update_top_ranking_issues/main.py --github-token ${{ secrets.GITHUB_TOKEN }} --prod
- run: python script/update_top_ranking_issues/main.py 5393 --github-token ${{ secrets.GITHUB_TOKEN }} --prod

View File

@@ -0,0 +1,17 @@
on:
schedule:
- cron: "0 15 * * *"
workflow_dispatch:
jobs:
update_top_ranking_issues:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: "3.10.5"
architecture: "x64"
cache: "pip"
- run: pip install -r script/update_top_ranking_issues/requirements.txt
- run: python script/update_top_ranking_issues/main.py 6952 --github-token ${{ secrets.GITHUB_TOKEN }} --prod --query-day-interval 7

View File

@@ -1,6 +1,6 @@
{
"JSON": {
"tab_size": 4
},
"formatter": "auto"
"JSON": {
"tab_size": 4
},
"formatter": "auto"
}

View File

@@ -10,7 +10,7 @@ All activity in Zed forums is subject to our [Code of Conduct](https://zed.dev/d
If you're looking for ideas about what to work on, check out:
- Our public roadmap (link coming soon!) contains a rough outline of our near-term priorities for Zed.
- Our [public roadmap](https://zed.dev/roadmap) contains a rough outline of our near-term priorities for Zed.
- Our [top-ranking issues](https://github.com/zed-industries/zed/issues/5393) based on votes by the community.
Outside of a handful of extremely popular languages and themes, we are generally not looking to extend Zed's language or theme support by directly building them into Zed. We really want to build a plugin system to handle making the editor extensible going forward. If you are passionate about shipping new languages or themes we suggest contributing to the extension system to help us get there faster.
@@ -19,7 +19,7 @@ Outside of a handful of extremely popular languages and themes, we are generally
The best way to propose a change is to [start a discussion on our GitHub repository](https://github.com/zed-industries/zed/discussions).
First, write a short **problem statement**, which *clearly* and *briefly* describes the problem you want to solve independently from any specific solution. It doesn't need to be long or formal, but it's difficult to consider a solution in absence of a clear understanding of the problem.
First, write a short **problem statement**, which _clearly_ and _briefly_ describes the problem you want to solve independently from any specific solution. It doesn't need to be long or formal, but it's difficult to consider a solution in absence of a clear understanding of the problem.
Next, write a short **solution proposal**. How can the problem (or set of problems) you have stated above be addressed? What are the pros and cons of your approach? Again, keep it brief and informal. This isn't a specification, but rather a starting point for a conversation.
@@ -43,14 +43,14 @@ We plan to set aside time each week to pair program with contributors on promisi
Zed is made up of several smaller crates - let's go over those you're most likely to interact with:
- [gpui](/crates/gpui) is a GPU-accelerated UI framework which provides all of the building blocks for Zed. **We recommend familiarizing yourself with the root level GPUI documentation**
- [editor](/crates/editor) contains the core `Editor` type that drives both the code editor and all various input fields within Zed. It also handles a display layer for LSP features such as Inlay Hints or code completions.
- [project](/crates/project) manages files and navigation within the filetree. It is also Zed's side of communication with LSP.
- [workspace](/crates/workspace) handles local state serialization and groups projects together.
- [vim](/crates/vim) is a thin implementation of Vim workflow over `editor`.
- [lsp](/crates/lsp) handles communication with external LSP server.
- [language](/crates/language) drives `editor`'s understanding of language - from providing a list of symbols to the syntax map.
- [collab](/crates/collab) is the collaboration server itself, driving the collaboration features such as project sharing.
- [rpc](/crates/rpc) defines messages to be exchanged with collaboration server.
- [theme](/crates/theme) defines the theme system and provides a default theme.
- [ui](/crates/ui) is a collection of UI components and common patterns used throughout Zed.
- [`gpui`](/crates/gpui) is a GPU-accelerated UI framework which provides all of the building blocks for Zed. **We recommend familiarizing yourself with the root level GPUI documentation**
- [`editor`](/crates/editor) contains the core `Editor` type that drives both the code editor and all various input fields within Zed. It also handles a display layer for LSP features such as Inlay Hints or code completions.
- [`project`](/crates/project) manages files and navigation within the filetree. It is also Zed's side of communication with LSP.
- [`workspace`](/crates/workspace) handles local state serialization and groups projects together.
- [`vim`](/crates/vim) is a thin implementation of Vim workflow over `editor`.
- [`lsp`](/crates/lsp) handles communication with external LSP server.
- [`language`](/crates/language) drives `editor`'s understanding of language - from providing a list of symbols to the syntax map.
- [`collab`](/crates/collab) is the collaboration server itself, driving the collaboration features such as project sharing.
- [`rpc`](/crates/rpc) defines messages to be exchanged with collaboration server.
- [`theme`](/crates/theme) defines the theme system and provides a default theme.
- [`ui`](/crates/ui) is a collection of UI components and common patterns used throughout Zed.

731
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,8 @@
[workspace]
members = [
"crates/assets",
"crates/activity_indicator",
"crates/ai",
"crates/assets",
"crates/assistant",
"crates/audio",
"crates/auto_update",
@@ -19,8 +19,6 @@ members = [
"crates/copilot",
"crates/copilot_ui",
"crates/db",
"crates/refineable",
"crates/refineable/derive_refineable",
"crates/diagnostics",
"crates/editor",
"crates/feature_flags",
@@ -32,9 +30,9 @@ members = [
"crates/git",
"crates/go_to_line",
"crates/gpui",
"crates/gpui_macros",
"crates/gpui",
"crates/gpui_macros",
"crates/gpui_macros",
"crates/install_cli",
"crates/journal",
"crates/journal",
@@ -59,6 +57,10 @@ members = [
"crates/project_symbols",
"crates/quick_action_bar",
"crates/recent_projects",
"crates/refineable",
"crates/refineable/derive_refineable",
"crates/release_channel",
"crates/rich_text",
"crates/rope",
"crates/rpc",
"crates/search",
@@ -67,7 +69,7 @@ members = [
"crates/snippet",
"crates/sqlez",
"crates/sqlez_macros",
"crates/rich_text",
"crates/story",
"crates/storybook",
"crates/sum_tree",
"crates/terminal",
@@ -78,11 +80,10 @@ members = [
"crates/theme_selector",
"crates/ui",
"crates/util",
"crates/story",
"crates/vim",
"crates/vcs_menu",
"crates/workspace",
"crates/vim",
"crates/welcome",
"crates/workspace",
"crates/zed",
"crates/zed_actions",
]
@@ -90,84 +91,92 @@ default-members = ["crates/zed"]
resolver = "2"
[workspace.dependencies]
anyhow = { version = "1.0.57" }
async-trait = { version = "0.1" }
anyhow = "1.0.57"
async-compression = { version = "0.4", features = ["gzip", "futures-io"] }
async-trait = "0.1"
chrono = { version = "0.4", features = ["serde"] }
ctor = "0.2.6"
derive_more = { version = "0.99.17" }
env_logger = { version = "0.9" }
futures = { version = "0.3" }
globset = { version = "0.4" }
derive_more = "0.99.17"
env_logger = "0.9"
futures = "0.3"
git2 = { version = "0.15", default-features = false}
globset = "0.4"
indoc = "1"
# We explicitly disable a http2 support in isahc.
isahc = { version = "1.7.2", default-features = false, features = ["static-curl", "text-decoding"] }
lazy_static = { version = "1.4.0" }
lazy_static = "1.4.0"
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
ordered-float = { version = "2.1.1" }
parking_lot = { version = "0.11.1" }
ordered-float = "2.1.1"
parking_lot = "0.11.1"
postage = { version = "0.5", features = ["futures-traits"] }
prost = { version = "0.8" }
rand = { version = "0.8.5" }
pretty_assertions = "1.3.0"
prost = "0.8"
rand = "0.8.5"
refineable = { path = "./crates/refineable" }
regex = { version = "1.5" }
rust-embed = { version = "8.0", features = ["include-exclude"] }
regex = "1.5"
rusqlite = { version = "0.29.0", features = ["blob", "array", "modern_sqlite"] }
schemars = { version = "0.8" }
rust-embed = { version = "8.0", features = ["include-exclude"] }
schemars = "0.8"
serde = { version = "1.0", features = ["derive", "rc"] }
serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
serde_json = { version = "1.0", features = ["preserve_order", "raw_value"] }
serde_json_lenient = { version = "0.1", features = ["preserve_order", "raw_value"] }
serde_repr = "0.1"
smallvec = { version = "1.6", features = ["union"] }
smol = { version = "1.2" }
smol = "1.2"
strum = { version = "0.25.0", features = ["derive"] }
sysinfo = "0.29.10"
tempfile = { version = "3.9.0" }
thiserror = { version = "1.0.29" }
time = { version = "0.3", features = ["serde", "serde-well-known"] }
toml = { version = "0.5" }
tempfile = "3.9.0"
thiserror = "1.0.29"
tiktoken-rs = "0.5.7"
tree-sitter = { version = "0.20" }
unindent = { version = "0.1.7" }
pretty_assertions = "1.3.0"
git2 = { version = "0.15", default-features = false}
uuid = { version = "1.1.2", features = ["v4"] }
time = { version = "0.3", features = ["serde", "serde-well-known"] }
toml = "0.5"
tree-sitter = { version = "0.20", features = ["wasm"] }
tree-sitter-bash = { git = "https://github.com/tree-sitter/tree-sitter-bash", rev = "7331995b19b8f8aba2d5e26deb51d2195c18bc94" }
tree-sitter-c = "0.20.1"
tree-sitter-c-sharp = { git = "https://github.com/tree-sitter/tree-sitter-c-sharp", rev = "dd5e59721a5f8dae34604060833902b882023aaf" }
tree-sitter-cpp = { git = "https://github.com/tree-sitter/tree-sitter-cpp", rev="f44509141e7e483323d2ec178f2d2e6c0fc041c1" }
tree-sitter-css = { git = "https://github.com/tree-sitter/tree-sitter-css", rev = "769203d0f9abe1a9a691ac2b9fe4bb4397a73c51" }
tree-sitter-elixir = { git = "https://github.com/elixir-lang/tree-sitter-elixir", rev = "a2861e88a730287a60c11ea9299c033c7d076e30" }
tree-sitter-elm = { git = "https://github.com/elm-tooling/tree-sitter-elm", rev = "692c50c0b961364c40299e73c1306aecb5d20f40"}
tree-sitter-elm = { git = "https://github.com/elm-tooling/tree-sitter-elm", rev = "692c50c0b961364c40299e73c1306aecb5d20f40" }
tree-sitter-embedded-template = "0.20.0"
tree-sitter-glsl = { git = "https://github.com/theHamsta/tree-sitter-glsl", rev = "2a56fb7bc8bb03a1892b4741279dd0a8758b7fb3" }
tree-sitter-gitcommit = { git = "https://github.com/gbprod/tree-sitter-gitcommit" }
tree-sitter-gleam = { git = "https://github.com/gleam-lang/tree-sitter-gleam", rev = "58b7cac8fc14c92b0677c542610d8738c373fa81" }
tree-sitter-glsl = { git = "https://github.com/theHamsta/tree-sitter-glsl", rev = "2a56fb7bc8bb03a1892b4741279dd0a8758b7fb3" }
tree-sitter-go = { git = "https://github.com/tree-sitter/tree-sitter-go", rev = "aeb2f33b366fd78d5789ff104956ce23508b85db" }
tree-sitter-heex = { git = "https://github.com/phoenixframework/tree-sitter-heex", rev = "2e1348c3cf2c9323e87c2744796cf3f3868aa82a" }
tree-sitter-json = { git = "https://github.com/tree-sitter/tree-sitter-json", rev = "40a81c01a40ac48744e0c8ccabbaba1920441199" }
tree-sitter-rust = "0.20.3"
tree-sitter-markdown = { git = "https://github.com/MDeiml/tree-sitter-markdown", rev = "330ecab87a3e3a7211ac69bbadc19eabecdb1cca" }
tree-sitter-php = "0.21.1"
tree-sitter-python = "0.20.2"
tree-sitter-toml = { git = "https://github.com/tree-sitter/tree-sitter-toml", rev = "342d9be207c2dba869b9967124c679b5e6fd0ebe" }
tree-sitter-typescript = { git = "https://github.com/tree-sitter/tree-sitter-typescript", rev = "5d20856f34315b068c41edaee2ac8a100081d259" }
tree-sitter-ruby = "0.20.0"
tree-sitter-gomod = { git = "https://github.com/camdencheek/tree-sitter-go-mod" }
tree-sitter-gowork = { git = "https://github.com/d1y/tree-sitter-go-work" }
tree-sitter-haskell = { git = "https://github.com/tree-sitter/tree-sitter-haskell", rev = "cf98de23e4285b8e6bcb57b050ef2326e2cc284b" }
tree-sitter-heex = { git = "https://github.com/phoenixframework/tree-sitter-heex", rev = "2e1348c3cf2c9323e87c2744796cf3f3868aa82a" }
tree-sitter-html = "0.19.0"
tree-sitter-scheme = { git = "https://github.com/6cdh/tree-sitter-scheme", rev = "af0fd1fa452cb2562dc7b5c8a8c55551c39273b9" }
tree-sitter-svelte = { git = "https://github.com/Himujjal/tree-sitter-svelte", rev = "697bb515471871e85ff799ea57a76298a71a9cca" }
tree-sitter-racket = { git = "https://github.com/zed-industries/tree-sitter-racket", rev = "eb010cf2c674c6fd9a6316a84e28ef90190fe51a" }
tree-sitter-yaml = { git = "https://github.com/zed-industries/tree-sitter-yaml", rev = "f545a41f57502e1b5ddf2a6668896c1b0620f930" }
tree-sitter-json = { git = "https://github.com/tree-sitter/tree-sitter-json", rev = "40a81c01a40ac48744e0c8ccabbaba1920441199" }
tree-sitter-lua = "0.0.14"
tree-sitter-markdown = { git = "https://github.com/MDeiml/tree-sitter-markdown", rev = "330ecab87a3e3a7211ac69bbadc19eabecdb1cca" }
tree-sitter-nix = { git = "https://github.com/nix-community/tree-sitter-nix", rev = "66e3e9ce9180ae08fc57372061006ef83f0abde7" }
tree-sitter-nu = { git = "https://github.com/nushell/tree-sitter-nu", rev = "26bbaecda0039df4067861ab38ea8ea169f7f5aa" }
tree-sitter-vue = { git = "https://github.com/zed-industries/tree-sitter-vue", rev = "6608d9d60c386f19d80af7d8132322fa11199c42" }
tree-sitter-php = "0.21.1"
tree-sitter-proto = {git = "https://github.com/rewinfrey/tree-sitter-proto", rev = "36d54f288aee112f13a67b550ad32634d0c2cb52" }
tree-sitter-purescript = { git = "https://github.com/ivanmoreau/tree-sitter-purescript", rev = "a37140f0c7034977b90faa73c94fcb8a5e45ed08" }
tree-sitter-python = "0.20.2"
tree-sitter-racket = { git = "https://github.com/zed-industries/tree-sitter-racket", rev = "eb010cf2c674c6fd9a6316a84e28ef90190fe51a" }
tree-sitter-ruby = "0.20.0"
tree-sitter-rust = "0.20.3"
tree-sitter-scheme = { git = "https://github.com/6cdh/tree-sitter-scheme", rev = "af0fd1fa452cb2562dc7b5c8a8c55551c39273b9" }
tree-sitter-svelte = { git = "https://github.com/Himujjal/tree-sitter-svelte", rev = "697bb515471871e85ff799ea57a76298a71a9cca" }
tree-sitter-toml = { git = "https://github.com/tree-sitter/tree-sitter-toml", rev = "342d9be207c2dba869b9967124c679b5e6fd0ebe" }
tree-sitter-typescript = { git = "https://github.com/tree-sitter/tree-sitter-typescript", rev = "5d20856f34315b068c41edaee2ac8a100081d259" }
tree-sitter-uiua = { git = "https://github.com/shnarazk/tree-sitter-uiua", rev = "9260f11be5900beda4ee6d1a24ab8ddfaf5a19b2" }
tree-sitter-vue = { git = "https://github.com/zed-industries/tree-sitter-vue", rev = "6608d9d60c386f19d80af7d8132322fa11199c42" }
tree-sitter-yaml = { git = "https://github.com/zed-industries/tree-sitter-yaml", rev = "f545a41f57502e1b5ddf2a6668896c1b0620f930" }
tree-sitter-zig = { git = "https://github.com/maxxnino/tree-sitter-zig", rev = "0d08703e4c3f426ec61695d7617415fff97029bd" }
unindent = "0.1.7"
url = "2.2"
uuid = { version = "1.1.2", features = ["v4"] }
wasmtime = "16"
[patch.crates-io]
tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "31c40449749c4263a91a43593831b82229049a4c" }
# wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", rev = "v16.0.0" }
tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "1d8975319c2d5de1bf710e7e21db25b0eee4bc66" }
wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", rev = "v16.0.0" }
[profile.dev]
split-debuginfo = "unpacked"

View File

@@ -14,6 +14,11 @@ Support for additional platforms is on our [roadmap](https://zed.dev/roadmap):
- Windows ([tracking issue](https://github.com/zed-industries/zed/issues/5394))
- Web ([tracking issue](https://github.com/zed-industries/zed/issues/5396))
For macOS users, you can also install Zed from Homebrew:
```sh
brew install zed
```
## Developing Zed
- [Building Zed](./docs/src/developing_zed__building_zed.md)

View File

@@ -0,0 +1,4 @@
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg fill="#000000" width="800px" height="800px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M24.235 6.519l-16.47-0.004 0.266 3.277 12.653 0.002-0.319 3.394h-8.298l0.3 3.215h7.725l-0.457 4.403-3.636 1.005-3.694-1.012-0.235-2.637h-3.262l0.362 4.817 6.829 2.128 6.714-1.912 1.521-16.675zM2.879 1.004h26.242l-2.387 26.946-10.763 3.045-10.703-3.047z"></path>
</svg>

After

Width:  |  Height:  |  Size: 482 B

View File

@@ -2,6 +2,7 @@
"suffixes": {
"aac": "audio",
"accdb": "storage",
"avif": "image",
"bak": "backup",
"bash": "terminal",
"bash_aliases": "terminal",
@@ -13,7 +14,7 @@
"cc": "code",
"conf": "settings",
"cpp": "code",
"css": "code",
"css": "css",
"csv": "storage",
"dat": "storage",
"db": "storage",
@@ -37,13 +38,16 @@
"gitattributes": "vcs",
"gitignore": "vcs",
"gitmodules": "vcs",
"go": "code",
"gitkeep": "vcs",
"go": "go",
"h": "code",
"handlebars": "code",
"hbs": "template",
"heex": "elixir",
"heif": "image",
"htm": "template",
"html": "template",
"hs": "haskell",
"ib": "storage",
"ico": "image",
"ini": "settings",
@@ -52,6 +56,7 @@
"jpg": "image",
"js": "code",
"json": "storage",
"jsonc": "storage",
"ldf": "storage",
"lock": "lock",
"log": "log",
@@ -69,7 +74,7 @@
"ogg": "video",
"pdb": "storage",
"pdf": "document",
"php": "code",
"php": "php",
"png": "image",
"ppt": "document",
"pptx": "document",
@@ -79,7 +84,7 @@
"ps1": "terminal",
"psd": "image",
"py": "python",
"rb": "code",
"rb": "ruby",
"rkt": "code",
"rs": "rust",
"rtf": "document",
@@ -97,7 +102,9 @@
"tsv": "storage",
"tsx": "code",
"txt": "document",
"vue": "vue",
"wav": "audio",
"webp": "image",
"webm": "video",
"xls": "document",
"xlsx": "document",
@@ -125,6 +132,9 @@
"collapsed_folder": {
"icon": "icons/file_icons/folder.svg"
},
"css": {
"icon": "icons/file_icons/css.svg"
},
"default": {
"icon": "icons/file_icons/file.svg"
},
@@ -143,6 +153,12 @@
"expanded_folder": {
"icon": "icons/file_icons/folder_open.svg"
},
"haskell": {
"icon": "icons/file_icons/haskell.svg"
},
"go": {
"icon": "icons/file_icons/go.svg"
},
"image": {
"icon": "icons/file_icons/image.svg"
},
@@ -155,12 +171,18 @@
"phoenix": {
"icon": "icons/file_icons/phoenix.svg"
},
"php": {
"icon": "icons/file_icons/php.svg"
},
"prettier": {
"icon": "icons/file_icons/prettier.svg"
},
"python": {
"icon": "icons/file_icons/python.svg"
},
"ruby": {
"icon": "icons/file_icons/ruby.svg"
},
"rust": {
"icon": "icons/file_icons/rust.svg"
},
@@ -187,6 +209,9 @@
},
"video": {
"icon": "icons/file_icons/video.svg"
},
"vue": {
"icon": "icons/file_icons/vue.svg"
}
}
}

View File

@@ -0,0 +1 @@
<svg height="14" viewBox="0 0 207 78" width="14" xmlns="http://www.w3.org/2000/svg"><g fill="#000000"><path d="m16.2 24.1c-.4 0-.5-.2-.3-.5l2.1-2.7c.2-.3.7-.5 1.1-.5h35.7c.4 0 .5.3.3.6l-1.7 2.6c-.2.3-.7.6-1 .6z"/><path d="m1.1 33.3c-.4 0-.5-.2-.3-.5l2.1-2.7c.2-.3.7-.5 1.1-.5h45.6c.4 0 .6.3.5.6l-.8 2.4c-.1.4-.5.6-.9.6z"/><path d="m25.3 42.5c-.4 0-.5-.3-.3-.6l1.4-2.5c.2-.3.6-.6 1-.6h20c.4 0 .6.3.6.7l-.2 2.4c0 .4-.4.7-.7.7z"/><g transform="translate(55)"><path d="m74.1 22.3c-6.3 1.6-10.6 2.8-16.8 4.4-1.5.4-1.6.5-2.9-1-1.5-1.7-2.6-2.8-4.7-3.8-6.3-3.1-12.4-2.2-18.1 1.5-6.8 4.4-10.3 10.9-10.2 19 .1 8 5.6 14.6 13.5 15.7 6.8.9 12.5-1.5 17-6.6.9-1.1 1.7-2.3 2.7-3.7-3.6 0-8.1 0-19.3 0-2.1 0-2.6-1.3-1.9-3 1.3-3.1 3.7-8.3 5.1-10.9.3-.6 1-1.6 2.5-1.6h36.4c-.2 2.7-.2 5.4-.6 8.1-1.1 7.2-3.8 13.8-8.2 19.6-7.2 9.5-16.6 15.4-28.5 17-9.8 1.3-18.9-.6-26.9-6.6-7.4-5.6-11.6-13-12.7-22.2-1.3-10.9 1.9-20.7 8.5-29.3 7.1-9.3 16.5-15.2 28-17.3 9.4-1.7 18.4-.6 26.5 4.9 5.3 3.5 9.1 8.3 11.6 14.1.6.9.2 1.4-1 1.7z"/><path d="m107.2 77.6c-9.1-.2-17.4-2.8-24.4-8.8-5.9-5.1-9.6-11.6-10.8-19.3-1.8-11.3 1.3-21.3 8.1-30.2 7.3-9.6 16.1-14.6 28-16.7 10.2-1.8 19.8-.8 28.5 5.1 7.9 5.4 12.8 12.7 14.1 22.3 1.7 13.5-2.2 24.5-11.5 33.9-6.6 6.7-14.7 10.9-24 12.8-2.7.5-5.4.6-8 .9zm23.8-40.4c-.1-1.3-.1-2.3-.3-3.3-1.8-9.9-10.9-15.5-20.4-13.3-9.3 2.1-15.3 8-17.5 17.4-1.8 7.8 2 15.7 9.2 18.9 5.5 2.4 11 2.1 16.3-.6 7.9-4.1 12.2-10.5 12.7-19.1z" fill-rule="nonzero"/></g></g></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 32 32"
version="1.1"
id="svg977"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs981" />
<path
id="path973"
d="M 10.699219 8.9003906 L 10.699219 9 C 12.199219 11.3 13.800781 13.600391 15.300781 15.900391 L 15.300781 16 C 13.800781 18.3 12.199219 20.600391 10.699219 22.900391 L 10.699219 23 L 14.199219 23 L 14.300781 22.900391 C 15.200781 21.500391 16.199609 20.099219 17.099609 18.699219 C 17.199609 18.599219 17.099219 18.599219 17.199219 18.699219 C 18.099219 20.099219 19.1 21.500391 20 22.900391 L 20.099609 23 L 23.599609 23 C 21.699609 20 19.699219 17.099609 17.699219 14.099609 C 16.499219 12.399609 15.399219 10.600391 14.199219 8.9003906 L 10.699219 8.9003906 z M 6 9 C 7.6 11.3 9.0996094 13.6 10.599609 16 L 10.599609 16.099609 C 9.4996094 17.799609 8.4007813 19.399609 7.3007812 21.099609 C 6.8007813 21.699609 6.4 22.4 6 23 L 6 23.099609 L 9.5 23.099609 C 11.1 20.699609 12.699219 18.399609 14.199219 16.099609 L 14.199219 16 C 13.499219 14.8 12.700391 13.7 11.900391 12.5 C 11.100391 11.4 10.399609 10.199609 9.5996094 9.0996094 L 9.5 9 L 6 9 z M 18.199219 13 L 18.199219 13.099609 C 18.699219 13.899609 19.199219 14.600391 19.699219 15.400391 L 26 15.400391 L 26 13 L 18.199219 13 z M 20.5 16.599609 L 20.5 16.699219 C 21 17.499219 21.5 18.2 22 19 L 26 19 L 26 16.599609 L 20.5 16.599609 z " />
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="512" height="512"><path d="M170.322 349.808c-2.4-15.66-9-28.38-25.020-34.531-6.27-2.4-11.7-6.78-17.88-9.54-7.020-3.15-14.16-6.15-21.57-8.1-5.61-1.5-10.83 1.020-14.16 5.94-3.15 4.62-0.87 8.97 1.77 12.84 2.97 4.35 6.27 8.49 9.6 12.57 5.52 6.78 11.37 13.29 16.74 20.161 5.13 6.57 9.51 13.86 8.76 22.56-1.65 19.080-10.29 34.891-24.21 47.76-1.53 1.38-4.23 2.37-6.21 2.19-8.88-0.96-16.95-4.32-23.46-10.53-7.47-7.11-6.33-15.48 2.61-20.67 2.13-1.23 4.35-2.37 6.3-3.87 5.46-4.11 7.29-11.13 4.32-17.22-1.41-2.94-3-6.12-5.34-8.25-11.43-10.41-22.651-21.151-34.891-30.63-29.671-23.041-44.91-53.52-47.251-90.421-2.64-40.981 6.87-79.231 28.5-114.242 8.19-13.29 17.73-25.951 32.37-32.52 9.96-4.47 20.88-6.99 31.531-9.78 29.311-7.71 58.89-13.5 89.401-8.34 26.28 4.41 45.511 17.94 54.331 43.77 5.79 16.89 7.17 34.35 5.37 52.231-3.54 35.131-29.49 66.541-63.331 75.841-14.67 4.020-22.68 1.77-31.5-10.44-6.33-8.79-11.58-18.36-17.25-27.631-0.84-1.38-1.44-2.97-2.16-4.44-0.69-1.47-1.44-2.88-2.16-4.35 2.13 15.24 5.67 29.911 13.98 42.99 4.5 7.11 10.5 12.36 19.29 13.14 32.34 2.91 59.641-7.71 79.021-33.721 21.69-29.101 26.461-62.581 20.19-97.831-1.23-6.96-3.3-13.77-4.77-20.7-0.99-4.47 0.78-7.77 5.19-9.33 2.040-0.69 4.14-1.26 6.18-1.68 26.461-5.7 53.221-7.59 80.191-4.86 30.601 3.060 59.551 11.46 85.441 28.471 40.531 26.67 65.641 64.621 79.291 110.522 1.98 6.66 2.28 13.95 2.46 20.971 0.12 4.68-2.88 5.91-6.45 2.97-3.93-3.21-7.53-6.87-10.92-10.65-3.15-3.57-5.67-7.65-8.73-11.4-2.37-2.94-4.44-2.49-5.58 1.17-0.72 2.22-1.35 4.41-1.98 6.63-7.080 25.26-18.24 48.3-36.33 67.711-2.52 2.73-4.77 6.78-5.070 10.38-0.78 9.96-1.35 20.13-0.39 30.060 1.98 21.331 5.070 42.57 7.47 63.871 1.35 12.030-2.52 19.11-13.83 23.281-7.95 2.91-16.47 5.040-24.87 5.64-13.38 0.93-26.88 0.27-40.32 0.27-0.36-15 0.93-29.731-13.17-37.771 2.73-11.13 5.88-21.69 7.77-32.49 1.56-8.97 0.24-17.79-6.060-25.14-5.91-6.93-13.32-8.82-20.101-4.86-20.43 11.91-41.671 11.97-63.301 4.17-9.93-3.6-16.86-1.56-22.351 7.5-5.91 9.75-8.4 20.7-7.74 31.771 0.84 13.95 3.27 27.75 5.13 41.64 1.020 7.77 0.15 9.78-7.56 11.76-17.13 4.35-34.56 4.83-52.081 3.42-0.93-0.090-1.86-0.48-2.46-0.63-0.87-14.55 0.66-29.671-16.68-37.411 7.68-16.29 6.63-33.18 3.99-50.070l-0.060-0.15zM66.761 292.718c2.55-2.4 4.59-6.15 5.31-9.6 1.8-8.64-4.68-20.22-12.18-23.43-3.99-1.74-7.47-1.11-10.29 2.070-6.87 7.77-13.65 15.63-20.401 23.521-1.14 1.35-2.16 2.94-2.97 4.53-2.7 5.19-1.11 8.97 4.65 10.38 3.48 0.87 7.080 1.050 10.65 1.56 9.3-0.9 18.3-2.46 25.23-9v-0.030zM67.541 206.347c-0.030-6.18-5.19-11.34-11.28-11.37-6.27-0.030-11.67 5.58-11.46 11.76 0.27 6.21 5.43 11.19 11.61 11.070 6.24-0.090 11.22-5.19 11.16-11.43l-0.030-0.030z"></path></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,7 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5 7.9 4.8 12C6.1 12 8.5 10.5 9.5 9.5Z" fill="black" fill-opacity="0.6"/>
<path d="M12 4.8 7.9 5 9.5 9.5 12 12Z" fill="black" fill-opacity="0.6"/>
<path d="M12 4.8V12H4.8" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2.3 7.2 2 10C2 11.8 3.6 12 4.8 12 6.5 12 8.5 10.5 9.5 9.5 10.5 8.5 12 6.5 12 4.8 12 3.6 11.8 2 10 2L7.2 2.3" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<ellipse rx="1.9" ry="3.8" cx="7.5" cy="0" transform="rotate(45)" stroke="black" stroke-width="1.25"/>
</svg>

After

Width:  |  Height:  |  Size: 674 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 14">
<path d="M7 8.135 3.578 2.212l-.18-.312H1l6 10.392L13 1.9h-2.4L7 8.135Z"/>
<path d="M7 3.675 5.972 1.9H1l.18.312h4.615L7 4.3l1.205-2.088h4.615L13 1.9H8.028L7 3.675Z"/>
</svg>

After

Width:  |  Height:  |  Size: 240 B

View File

@@ -31,6 +31,7 @@
"up": "vim::Up",
"l": "vim::Right",
"right": "vim::Right",
"space": "vim::Space",
"$": "vim::EndOfLine",
"^": "vim::FirstNonWhitespace",
"_": "vim::StartOfLineDownward",
@@ -206,6 +207,9 @@
"displayLines": true
}
],
"shift-h": "vim::WindowTop",
"shift-m": "vim::WindowMiddle",
"shift-l": "vim::WindowBottom",
// z commands
"z t": "editor::ScrollCursorTop",
"z z": "editor::ScrollCursorCenter",
@@ -330,12 +334,7 @@
"*": "vim::MoveToNext",
"#": "vim::MoveToPrev",
";": "vim::RepeatFind",
",": [
"vim::RepeatFind",
{
"backwards": true
}
],
",": "vim::RepeatFindReversed",
"r": ["vim::PushOperator", "Replace"],
"s": "vim::Substitute",
"shift-s": "vim::SubstituteLine",
@@ -400,7 +399,8 @@
{
"context": "Editor && vim_mode == visual && !VimWaiting && !VimObject",
"bindings": {
"u": "editor::Undo",
"u": "vim::ConvertToLowerCase",
"U": "vim::ConvertToUpperCase",
"o": "vim::OtherEnd",
"shift-o": "vim::OtherEnd",
"d": "vim::VisualDelete",

View File

@@ -62,6 +62,9 @@
// Whether to display inline and alongside documentation for items in the
// completions menu
"show_completion_documentation": true,
// The debounce delay before re-querying the language server for completion
// documentation when not included in original completion list.
"completion_documentation_secondary_query_debounce": 300,
// Whether to show wrap guides in the editor. Setting this to true will
// show a guide at the 'preferred_line_length' value if softwrap is set to
// 'preferred_line_length', and will show any additional guides as specified
@@ -69,6 +72,17 @@
"show_wrap_guides": true,
// Character counts at which to show wrap guides in the editor.
"wrap_guides": [],
// Hide the values of in variables from visual display in private files
"redact_private_values": false,
// Globs to match against file paths to determine if a file is private.
"private_files": [
"**/.env*",
"**/*.pem",
"**/*.key",
"**/*.cert",
"**/*.crt",
"**/secrets.yml"
],
// Whether to use additional LSP queries to format (and amend) the code after
// every "trigger" symbol input, defined by LSP server capabilities.
"use_on_type_format": true,
@@ -111,7 +125,9 @@
// Whether to show git diff indicators in the scrollbar.
"git_diff": true,
// Whether to show selections in the scrollbar.
"selections": true
"selections": true,
// Whether to show symbols selections in the scrollbar.
"symbols_selections": true
},
"relative_line_numbers": false,
// When to populate a new search's query based on the text under the cursor.
@@ -503,5 +519,28 @@
// }
// }
// }
},
// The server to connect to. If the environment variable
// ZED_SERVER_URL is set, it will override this setting.
"server_url": "https://zed.dev",
// Settings overrides to use when using Zed Preview.
// Mostly useful for developers who are managing multiple instances of Zed.
"preview": {
// "theme": "Andromeda"
},
// Settings overrides to use when using Zed Nightly.
// Mostly useful for developers who are managing multiple instances of Zed.
"nightly": {
// "theme": "Andromeda"
},
// Settings overrides to use when using Zed Stable.
// Mostly useful for developers who are managing multiple instances of Zed.
"stable": {
// "theme": "Andromeda"
},
// Settings overrides to use when using Zed Stable.
// Mostly useful for developers who are managing multiple instances of Zed.
"dev": {
// "theme": "Andromeda"
}
}

View File

@@ -5,26 +5,24 @@ edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lib]
path = "src/activity_indicator.rs"
doctest = false
[dependencies]
anyhow.workspace = true
auto_update = { path = "../auto_update" }
editor = { path = "../editor" }
language = { path = "../language" }
futures.workspace = true
gpui = { path = "../gpui" }
language = { path = "../language" }
project = { path = "../project" }
settings = { path = "../settings" }
smallvec.workspace = true
theme = { path = "../theme" }
ui = { path = "../ui" }
util = { path = "../util" }
theme = { path = "../theme" }
workspace = { path = "../workspace", package = "workspace" }
anyhow.workspace = true
futures.workspace = true
smallvec.workspace = true
[dev-dependencies]
editor = { path = "../editor", features = ["test-support"] }

View File

@@ -5,7 +5,6 @@ edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lib]
path = "src/ai.rs"
doctest = false
@@ -14,27 +13,27 @@ doctest = false
test-support = []
[dependencies]
gpui = { path = "../gpui" }
util = { path = "../util" }
language = { path = "../language" }
async-trait.workspace = true
anyhow.workspace = true
async-trait.workspace = true
bincode = "1.3.3"
futures.workspace = true
gpui = { path = "../gpui" }
isahc.workspace = true
language = { path = "../language" }
lazy_static.workspace = true
log.workspace = true
matrixmultiply = "0.3.7"
ordered-float.workspace = true
parking_lot.workspace = true
isahc.workspace = true
regex.workspace = true
serde.workspace = true
serde_json.workspace = true
parse_duration = "2.1.1"
postage.workspace = true
rand.workspace = true
log.workspace = true
parse_duration = "2.1.1"
tiktoken-rs.workspace = true
matrixmultiply = "0.3.7"
regex.workspace = true
rusqlite = { version = "0.29.0", features = ["blob", "array", "modern_sqlite"] }
bincode = "1.3.3"
serde.workspace = true
serde_json.workspace = true
tiktoken-rs.workspace = true
util = { path = "../util" }
[dev-dependencies]
gpui = { path = "../gpui", features = ["test-support"] }

View File

@@ -0,0 +1,9 @@
pub mod completion;
pub mod embedding;
pub mod model;
pub use completion::*;
pub use embedding::*;
pub use model::OpenAiLanguageModel;
pub const OPEN_AI_API_URL: &'static str = "https://api.openai.com/v1";

View File

@@ -21,7 +21,7 @@ use crate::{
models::LanguageModel,
};
use crate::providers::open_ai::{OpenAILanguageModel, OPENAI_API_URL};
use crate::providers::open_ai::{OpenAiLanguageModel, OPEN_AI_API_URL};
#[derive(Clone, Copy, Serialize, Deserialize, Debug, Eq, PartialEq)]
#[serde(rename_all = "lowercase")]
@@ -58,7 +58,7 @@ pub struct RequestMessage {
}
#[derive(Debug, Default, Serialize)]
pub struct OpenAIRequest {
pub struct OpenAiRequest {
pub model: String,
pub messages: Vec<RequestMessage>,
pub stream: bool,
@@ -66,7 +66,7 @@ pub struct OpenAIRequest {
pub temperature: f32,
}
impl CompletionRequest for OpenAIRequest {
impl CompletionRequest for OpenAiRequest {
fn data(&self) -> serde_json::Result<String> {
serde_json::to_string(self)
}
@@ -79,7 +79,7 @@ pub struct ResponseMessage {
}
#[derive(Deserialize, Debug)]
pub struct OpenAIUsage {
pub struct OpenAiUsage {
pub prompt_tokens: u32,
pub completion_tokens: u32,
pub total_tokens: u32,
@@ -93,20 +93,20 @@ pub struct ChatChoiceDelta {
}
#[derive(Deserialize, Debug)]
pub struct OpenAIResponseStreamEvent {
pub struct OpenAiResponseStreamEvent {
pub id: Option<String>,
pub object: String,
pub created: u32,
pub model: String,
pub choices: Vec<ChatChoiceDelta>,
pub usage: Option<OpenAIUsage>,
pub usage: Option<OpenAiUsage>,
}
pub async fn stream_completion(
credential: ProviderCredential,
executor: BackgroundExecutor,
request: Box<dyn CompletionRequest>,
) -> Result<impl Stream<Item = Result<OpenAIResponseStreamEvent>>> {
) -> Result<impl Stream<Item = Result<OpenAiResponseStreamEvent>>> {
let api_key = match credential {
ProviderCredential::Credentials { api_key } => api_key,
_ => {
@@ -114,10 +114,10 @@ pub async fn stream_completion(
}
};
let (tx, rx) = futures::channel::mpsc::unbounded::<Result<OpenAIResponseStreamEvent>>();
let (tx, rx) = futures::channel::mpsc::unbounded::<Result<OpenAiResponseStreamEvent>>();
let json_data = request.data()?;
let mut response = Request::post(format!("{OPENAI_API_URL}/chat/completions"))
let mut response = Request::post(format!("{OPEN_AI_API_URL}/chat/completions"))
.header("Content-Type", "application/json")
.header("Authorization", format!("Bearer {}", api_key))
.body(json_data)?
@@ -132,7 +132,7 @@ pub async fn stream_completion(
fn parse_line(
line: Result<String, io::Error>,
) -> Result<Option<OpenAIResponseStreamEvent>> {
) -> Result<Option<OpenAiResponseStreamEvent>> {
if let Some(data) = line?.strip_prefix("data: ") {
let event = serde_json::from_str(data)?;
Ok(Some(event))
@@ -169,16 +169,16 @@ pub async fn stream_completion(
response.body_mut().read_to_string(&mut body).await?;
#[derive(Deserialize)]
struct OpenAIResponse {
error: OpenAIError,
struct OpenAiResponse {
error: OpenAiError,
}
#[derive(Deserialize)]
struct OpenAIError {
struct OpenAiError {
message: String,
}
match serde_json::from_str::<OpenAIResponse>(&body) {
match serde_json::from_str::<OpenAiResponse>(&body) {
Ok(response) if !response.error.message.is_empty() => Err(anyhow!(
"Failed to connect to OpenAI API: {}",
response.error.message,
@@ -194,16 +194,16 @@ pub async fn stream_completion(
}
#[derive(Clone)]
pub struct OpenAICompletionProvider {
model: OpenAILanguageModel,
pub struct OpenAiCompletionProvider {
model: OpenAiLanguageModel,
credential: Arc<RwLock<ProviderCredential>>,
executor: BackgroundExecutor,
}
impl OpenAICompletionProvider {
impl OpenAiCompletionProvider {
pub async fn new(model_name: String, executor: BackgroundExecutor) -> Self {
let model = executor
.spawn(async move { OpenAILanguageModel::load(&model_name) })
.spawn(async move { OpenAiLanguageModel::load(&model_name) })
.await;
let credential = Arc::new(RwLock::new(ProviderCredential::NoCredentials));
Self {
@@ -214,7 +214,7 @@ impl OpenAICompletionProvider {
}
}
impl CredentialProvider for OpenAICompletionProvider {
impl CredentialProvider for OpenAiCompletionProvider {
fn has_credentials(&self) -> bool {
match *self.credential.read() {
ProviderCredential::Credentials { .. } => true,
@@ -232,7 +232,7 @@ impl CredentialProvider for OpenAICompletionProvider {
if let Some(api_key) = env::var("OPENAI_API_KEY").log_err() {
async move { ProviderCredential::Credentials { api_key } }.boxed()
} else {
let credentials = cx.read_credentials(OPENAI_API_URL);
let credentials = cx.read_credentials(OPEN_AI_API_URL);
async move {
if let Some(Some((_, api_key))) = credentials.await.log_err() {
if let Some(api_key) = String::from_utf8(api_key).log_err() {
@@ -266,7 +266,7 @@ impl CredentialProvider for OpenAICompletionProvider {
let credential = credential.clone();
let write_credentials = match credential {
ProviderCredential::Credentials { api_key } => {
Some(cx.write_credentials(OPENAI_API_URL, "Bearer", api_key.as_bytes()))
Some(cx.write_credentials(OPEN_AI_API_URL, "Bearer", api_key.as_bytes()))
}
_ => None,
};
@@ -281,7 +281,7 @@ impl CredentialProvider for OpenAICompletionProvider {
fn delete_credentials(&self, cx: &mut AppContext) -> BoxFuture<()> {
*self.credential.write() = ProviderCredential::NoCredentials;
let delete_credentials = cx.delete_credentials(OPENAI_API_URL);
let delete_credentials = cx.delete_credentials(OPEN_AI_API_URL);
async move {
delete_credentials.await.log_err();
}
@@ -289,7 +289,7 @@ impl CredentialProvider for OpenAICompletionProvider {
}
}
impl CompletionProvider for OpenAICompletionProvider {
impl CompletionProvider for OpenAiCompletionProvider {
fn base_model(&self) -> Box<dyn LanguageModel> {
let model: Box<dyn LanguageModel> = Box::new(self.model.clone());
model

View File

@@ -25,17 +25,17 @@ use util::ResultExt;
use crate::auth::{CredentialProvider, ProviderCredential};
use crate::embedding::{Embedding, EmbeddingProvider};
use crate::models::LanguageModel;
use crate::providers::open_ai::OpenAILanguageModel;
use crate::providers::open_ai::OpenAiLanguageModel;
use crate::providers::open_ai::OPENAI_API_URL;
use crate::providers::open_ai::OPEN_AI_API_URL;
lazy_static! {
static ref OPENAI_BPE_TOKENIZER: CoreBPE = cl100k_base().unwrap();
pub(crate) static ref OPEN_AI_BPE_TOKENIZER: CoreBPE = cl100k_base().unwrap();
}
#[derive(Clone)]
pub struct OpenAIEmbeddingProvider {
model: OpenAILanguageModel,
pub struct OpenAiEmbeddingProvider {
model: OpenAiLanguageModel,
credential: Arc<RwLock<ProviderCredential>>,
pub client: Arc<dyn HttpClient>,
pub executor: BackgroundExecutor,
@@ -44,42 +44,42 @@ pub struct OpenAIEmbeddingProvider {
}
#[derive(Serialize)]
struct OpenAIEmbeddingRequest<'a> {
struct OpenAiEmbeddingRequest<'a> {
model: &'static str,
input: Vec<&'a str>,
}
#[derive(Deserialize)]
struct OpenAIEmbeddingResponse {
data: Vec<OpenAIEmbedding>,
usage: OpenAIEmbeddingUsage,
struct OpenAiEmbeddingResponse {
data: Vec<OpenAiEmbedding>,
usage: OpenAiEmbeddingUsage,
}
#[derive(Debug, Deserialize)]
struct OpenAIEmbedding {
struct OpenAiEmbedding {
embedding: Vec<f32>,
index: usize,
object: String,
}
#[derive(Deserialize)]
struct OpenAIEmbeddingUsage {
struct OpenAiEmbeddingUsage {
prompt_tokens: usize,
total_tokens: usize,
}
impl OpenAIEmbeddingProvider {
impl OpenAiEmbeddingProvider {
pub async fn new(client: Arc<dyn HttpClient>, executor: BackgroundExecutor) -> Self {
let (rate_limit_count_tx, rate_limit_count_rx) = watch::channel_with(None);
let rate_limit_count_tx = Arc::new(Mutex::new(rate_limit_count_tx));
// Loading the model is expensive, so ensure this runs off the main thread.
let model = executor
.spawn(async move { OpenAILanguageModel::load("text-embedding-ada-002") })
.spawn(async move { OpenAiLanguageModel::load("text-embedding-ada-002") })
.await;
let credential = Arc::new(RwLock::new(ProviderCredential::NoCredentials));
OpenAIEmbeddingProvider {
OpenAiEmbeddingProvider {
model,
credential,
client,
@@ -140,7 +140,7 @@ impl OpenAIEmbeddingProvider {
.header("Content-Type", "application/json")
.header("Authorization", format!("Bearer {}", api_key))
.body(
serde_json::to_string(&OpenAIEmbeddingRequest {
serde_json::to_string(&OpenAiEmbeddingRequest {
input: spans.clone(),
model: "text-embedding-ada-002",
})
@@ -152,7 +152,7 @@ impl OpenAIEmbeddingProvider {
}
}
impl CredentialProvider for OpenAIEmbeddingProvider {
impl CredentialProvider for OpenAiEmbeddingProvider {
fn has_credentials(&self) -> bool {
match *self.credential.read() {
ProviderCredential::Credentials { .. } => true,
@@ -170,7 +170,7 @@ impl CredentialProvider for OpenAIEmbeddingProvider {
if let Some(api_key) = env::var("OPENAI_API_KEY").log_err() {
async move { ProviderCredential::Credentials { api_key } }.boxed()
} else {
let credentials = cx.read_credentials(OPENAI_API_URL);
let credentials = cx.read_credentials(OPEN_AI_API_URL);
async move {
if let Some(Some((_, api_key))) = credentials.await.log_err() {
if let Some(api_key) = String::from_utf8(api_key).log_err() {
@@ -204,7 +204,7 @@ impl CredentialProvider for OpenAIEmbeddingProvider {
let credential = credential.clone();
let write_credentials = match credential {
ProviderCredential::Credentials { api_key } => {
Some(cx.write_credentials(OPENAI_API_URL, "Bearer", api_key.as_bytes()))
Some(cx.write_credentials(OPEN_AI_API_URL, "Bearer", api_key.as_bytes()))
}
_ => None,
};
@@ -219,7 +219,7 @@ impl CredentialProvider for OpenAIEmbeddingProvider {
fn delete_credentials(&self, cx: &mut AppContext) -> BoxFuture<()> {
*self.credential.write() = ProviderCredential::NoCredentials;
let delete_credentials = cx.delete_credentials(OPENAI_API_URL);
let delete_credentials = cx.delete_credentials(OPEN_AI_API_URL);
async move {
delete_credentials.await.log_err();
}
@@ -228,7 +228,7 @@ impl CredentialProvider for OpenAIEmbeddingProvider {
}
#[async_trait]
impl EmbeddingProvider for OpenAIEmbeddingProvider {
impl EmbeddingProvider for OpenAiEmbeddingProvider {
fn base_model(&self) -> Box<dyn LanguageModel> {
let model: Box<dyn LanguageModel> = Box::new(self.model.clone());
model
@@ -270,7 +270,7 @@ impl EmbeddingProvider for OpenAIEmbeddingProvider {
StatusCode::OK => {
let mut body = String::new();
response.body_mut().read_to_string(&mut body).await?;
let response: OpenAIEmbeddingResponse = serde_json::from_str(&body)?;
let response: OpenAiEmbeddingResponse = serde_json::from_str(&body)?;
log::trace!(
"openai embedding completed. tokens: {:?}",

View File

@@ -1,9 +0,0 @@
pub mod completion;
pub mod embedding;
pub mod model;
pub use completion::*;
pub use embedding::*;
pub use model::OpenAILanguageModel;
pub const OPENAI_API_URL: &'static str = "https://api.openai.com/v1";

View File

@@ -1,26 +1,28 @@
use anyhow::anyhow;
use tiktoken_rs::CoreBPE;
use util::ResultExt;
use crate::models::{LanguageModel, TruncationDirection};
use super::OPEN_AI_BPE_TOKENIZER;
#[derive(Clone)]
pub struct OpenAILanguageModel {
pub struct OpenAiLanguageModel {
name: String,
bpe: Option<CoreBPE>,
}
impl OpenAILanguageModel {
impl OpenAiLanguageModel {
pub fn load(model_name: &str) -> Self {
let bpe = tiktoken_rs::get_bpe_from_model(model_name).log_err();
OpenAILanguageModel {
let bpe =
tiktoken_rs::get_bpe_from_model(model_name).unwrap_or(OPEN_AI_BPE_TOKENIZER.to_owned());
OpenAiLanguageModel {
name: model_name.to_string(),
bpe,
bpe: Some(bpe),
}
}
}
impl LanguageModel for OpenAILanguageModel {
impl LanguageModel for OpenAiLanguageModel {
fn name(&self) -> String {
self.name.clone()
}

View File

@@ -1,11 +0,0 @@
pub trait LanguageModel {
fn name(&self) -> String;
fn count_tokens(&self, content: &str) -> anyhow::Result<usize>;
fn truncate(
&self,
content: &str,
length: usize,
direction: TruncationDirection,
) -> anyhow::Result<String>;
fn capacity(&self) -> anyhow::Result<usize>;
}

View File

@@ -5,10 +5,7 @@ edition = "2021"
publish = false
license = "GPL-3.0-or-later"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
gpui = {path = "../gpui"}
rust-embed.workspace = true
anyhow.workspace = true
gpui = { path = "../gpui" }
rust-embed.workspace = true

View File

@@ -16,7 +16,7 @@ use rust_embed::RustEmbed;
pub struct Assets;
impl AssetSource for Assets {
fn load(&self, path: &str) -> Result<std::borrow::Cow<[u8]>> {
fn load(&self, path: &str) -> Result<std::borrow::Cow<'static, [u8]>> {
Self::get(path)
.map(|f| f.data)
.ok_or_else(|| anyhow!("could not find asset at path \"{}\"", path))

View File

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

View File

@@ -7,7 +7,7 @@ mod streaming_diff;
use ai::providers::open_ai::Role;
use anyhow::Result;
pub use assistant_panel::AssistantPanel;
use assistant_settings::OpenAIModel;
use assistant_settings::OpenAiModel;
use chrono::{DateTime, Local};
use collections::HashMap;
use fs::Fs;
@@ -68,7 +68,7 @@ struct SavedConversation {
messages: Vec<SavedMessage>,
message_metadata: HashMap<MessageId, MessageMetadata>,
summary: String,
model: OpenAIModel,
model: OpenAiModel,
}
impl SavedConversation {

View File

@@ -1,5 +1,5 @@
use crate::{
assistant_settings::{AssistantDockPosition, AssistantSettings, OpenAIModel},
assistant_settings::{AssistantDockPosition, AssistantSettings, OpenAiModel},
codegen::{self, Codegen, CodegenKind},
prompts::generate_content_prompt,
Assist, CycleMessageRole, InlineAssist, MessageId, MessageMetadata, MessageStatus,
@@ -10,7 +10,7 @@ use ai::prompts::repository_context::PromptCodeSnippet;
use ai::{
auth::ProviderCredential,
completion::{CompletionProvider, CompletionRequest},
providers::open_ai::{OpenAICompletionProvider, OpenAIRequest, RequestMessage},
providers::open_ai::{OpenAiCompletionProvider, OpenAiRequest, RequestMessage},
};
use anyhow::{anyhow, Result};
use chrono::{DateTime, Local};
@@ -35,7 +35,7 @@ use gpui::{
StatefulInteractiveElement, Styled, Subscription, Task, TextStyle, UniformListScrollHandle,
View, ViewContext, VisualContext, WeakModel, WeakView, WhiteSpace, WindowContext,
};
use language::{language_settings::SoftWrap, Buffer, LanguageRegistry, ToOffset as _};
use language::{language_settings::SoftWrap, Buffer, BufferId, LanguageRegistry, ToOffset as _};
use project::Project;
use search::{buffer_search::DivRegistrar, BufferSearchBar};
use semantic_index::{SemanticIndex, SemanticIndexStatus};
@@ -123,7 +123,7 @@ impl AssistantPanel {
.unwrap_or_default();
// Defaulting currently to GPT4, allow for this to be set via config.
let completion_provider =
OpenAICompletionProvider::new("gpt-4".into(), cx.background_executor().clone())
OpenAiCompletionProvider::new("gpt-4".into(), cx.background_executor().clone())
.await;
// TODO: deserialize state.
@@ -717,7 +717,7 @@ impl AssistantPanel {
content: prompt,
});
let request = Box::new(OpenAIRequest {
let request = Box::new(OpenAiRequest {
model: model.full_name().into(),
messages,
stream: true,
@@ -1096,6 +1096,7 @@ impl AssistantPanel {
let conversation =
Conversation::deserialize(saved_conversation, path.clone(), languages, &mut cx)
.await?;
this.update(&mut cx, |this, cx| {
// If, by the time we've loaded the conversation, the user has already opened
// the same conversation, we don't want to open it again.
@@ -1393,7 +1394,7 @@ struct Conversation {
pending_summary: Task<Option<()>>,
completion_count: usize,
pending_completions: Vec<PendingCompletion>,
model: OpenAIModel,
model: OpenAiModel,
token_count: Option<usize>,
max_token_count: usize,
pending_token_count: Task<Option<()>>,
@@ -1413,7 +1414,7 @@ impl Conversation {
) -> Self {
let markdown = language_registry.language_for_name("Markdown");
let buffer = cx.new_model(|cx| {
let mut buffer = Buffer::new(0, cx.entity_id().as_u64(), "");
let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "");
buffer.set_language_registry(language_registry);
cx.spawn(|buffer, mut cx| async move {
let markdown = markdown.await?;
@@ -1501,18 +1502,24 @@ impl Conversation {
};
let model = saved_conversation.model;
let completion_provider: Arc<dyn CompletionProvider> = Arc::new(
OpenAICompletionProvider::new(
OpenAiCompletionProvider::new(
model.full_name().into(),
cx.background_executor().clone(),
)
.await,
);
cx.update(|cx| completion_provider.retrieve_credentials(cx))?;
cx.update(|cx| completion_provider.retrieve_credentials(cx))?
.await;
let markdown = language_registry.language_for_name("Markdown");
let mut message_anchors = Vec::new();
let mut next_message_id = MessageId(0);
let buffer = cx.new_model(|cx| {
let mut buffer = Buffer::new(0, cx.entity_id().as_u64(), saved_conversation.text);
let mut buffer = Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
saved_conversation.text,
);
for message in saved_conversation.messages {
message_anchors.push(MessageAnchor {
id: message.id,
@@ -1626,7 +1633,7 @@ impl Conversation {
Some(self.max_token_count as isize - self.token_count? as isize)
}
fn set_model(&mut self, model: OpenAIModel, cx: &mut ModelContext<Self>) {
fn set_model(&mut self, model: OpenAiModel, cx: &mut ModelContext<Self>) {
self.model = model;
self.count_remaining_tokens(cx);
cx.notify();
@@ -1676,10 +1683,11 @@ impl Conversation {
if should_assist {
if !self.completion_provider.has_credentials() {
log::info!("completion provider has no credentials");
return Default::default();
}
let request: Box<dyn CompletionRequest> = Box::new(OpenAIRequest {
let request: Box<dyn CompletionRequest> = Box::new(OpenAiRequest {
model: self.model.full_name().to_string(),
messages: self
.messages(cx)
@@ -1962,7 +1970,7 @@ impl Conversation {
content: "Summarize the conversation into a short title without punctuation"
.into(),
}));
let request: Box<dyn CompletionRequest> = Box::new(OpenAIRequest {
let request: Box<dyn CompletionRequest> = Box::new(OpenAiRequest {
model: self.model.full_name().to_string(),
messages: messages.collect(),
stream: true,

View File

@@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
use settings::Settings;
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
pub enum OpenAIModel {
pub enum OpenAiModel {
#[serde(rename = "gpt-3.5-turbo-0613")]
ThreePointFiveTurbo,
#[serde(rename = "gpt-4-0613")]
@@ -14,28 +14,28 @@ pub enum OpenAIModel {
FourTurbo,
}
impl OpenAIModel {
impl OpenAiModel {
pub fn full_name(&self) -> &'static str {
match self {
OpenAIModel::ThreePointFiveTurbo => "gpt-3.5-turbo-0613",
OpenAIModel::Four => "gpt-4-0613",
OpenAIModel::FourTurbo => "gpt-4-1106-preview",
OpenAiModel::ThreePointFiveTurbo => "gpt-3.5-turbo-0613",
OpenAiModel::Four => "gpt-4-0613",
OpenAiModel::FourTurbo => "gpt-4-1106-preview",
}
}
pub fn short_name(&self) -> &'static str {
match self {
OpenAIModel::ThreePointFiveTurbo => "gpt-3.5-turbo",
OpenAIModel::Four => "gpt-4",
OpenAIModel::FourTurbo => "gpt-4-turbo",
OpenAiModel::ThreePointFiveTurbo => "gpt-3.5-turbo",
OpenAiModel::Four => "gpt-4",
OpenAiModel::FourTurbo => "gpt-4-turbo",
}
}
pub fn cycle(&self) -> Self {
match self {
OpenAIModel::ThreePointFiveTurbo => OpenAIModel::Four,
OpenAIModel::Four => OpenAIModel::FourTurbo,
OpenAIModel::FourTurbo => OpenAIModel::ThreePointFiveTurbo,
OpenAiModel::ThreePointFiveTurbo => OpenAiModel::Four,
OpenAiModel::Four => OpenAiModel::FourTurbo,
OpenAiModel::FourTurbo => OpenAiModel::ThreePointFiveTurbo,
}
}
}
@@ -54,7 +54,7 @@ pub struct AssistantSettings {
pub dock: AssistantDockPosition,
pub default_width: Pixels,
pub default_height: Pixels,
pub default_open_ai_model: OpenAIModel,
pub default_open_ai_model: OpenAiModel,
}
/// Assistant panel settings
@@ -79,7 +79,7 @@ pub struct AssistantSettingsContent {
/// The default OpenAI model to use when starting new conversations.
///
/// Default: gpt-4-1106-preview
pub default_open_ai_model: Option<OpenAIModel>,
pub default_open_ai_model: Option<OpenAiModel>,
}
impl Settings for AssistantSettings {

View File

@@ -365,7 +365,9 @@ mod tests {
use futures::stream::{self};
use gpui::{Context, TestAppContext};
use indoc::indoc;
use language::{language_settings, tree_sitter_rust, Buffer, Language, LanguageConfig, Point};
use language::{
language_settings, tree_sitter_rust, Buffer, BufferId, Language, LanguageConfig, Point,
};
use rand::prelude::*;
use serde::Serialize;
use settings::SettingsStore;
@@ -394,8 +396,9 @@ mod tests {
}
}
"};
let buffer =
cx.new_model(|cx| Buffer::new(0, 0, text).with_language(Arc::new(rust_lang()), cx));
let buffer = cx.new_model(|cx| {
Buffer::new(0, BufferId::new(1).unwrap(), text).with_language(Arc::new(rust_lang()), cx)
});
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
let range = buffer.read_with(cx, |buffer, cx| {
let snapshot = buffer.snapshot(cx);
@@ -460,8 +463,9 @@ mod tests {
le
}
"};
let buffer =
cx.new_model(|cx| Buffer::new(0, 0, text).with_language(Arc::new(rust_lang()), cx));
let buffer = cx.new_model(|cx| {
Buffer::new(0, BufferId::new(1).unwrap(), text).with_language(Arc::new(rust_lang()), cx)
});
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
let position = buffer.read_with(cx, |buffer, cx| {
let snapshot = buffer.snapshot(cx);
@@ -525,8 +529,9 @@ mod tests {
" \n",
"}\n" //
);
let buffer =
cx.new_model(|cx| Buffer::new(0, 0, text).with_language(Arc::new(rust_lang()), cx));
let buffer = cx.new_model(|cx| {
Buffer::new(0, BufferId::new(1).unwrap(), text).with_language(Arc::new(rust_lang()), cx)
});
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
let position = buffer.read_with(cx, |buffer, cx| {
let snapshot = buffer.snapshot(cx);

View File

@@ -4,7 +4,7 @@ use ai::prompts::file_context::FileContext;
use ai::prompts::generate::GenerateInlineContent;
use ai::prompts::preamble::EngineerPreamble;
use ai::prompts::repository_context::{PromptCodeSnippet, RepositoryContext};
use ai::providers::open_ai::OpenAILanguageModel;
use ai::providers::open_ai::OpenAiLanguageModel;
use language::{BufferSnapshot, OffsetRangeExt, ToOffset};
use std::cmp::{self, Reverse};
use std::ops::Range;
@@ -131,7 +131,7 @@ pub fn generate_content_prompt(
project_name: Option<String>,
) -> anyhow::Result<String> {
// Using new Prompt Templates
let openai_model: Arc<dyn LanguageModel> = Arc::new(OpenAILanguageModel::load(model));
let openai_model: Arc<dyn LanguageModel> = Arc::new(OpenAiLanguageModel::load(model));
let lang_name = if let Some(language_name) = language_name {
Some(language_name.to_string())
} else {
@@ -178,7 +178,9 @@ pub(crate) mod tests {
use gpui::{AppContext, Context};
use indoc::indoc;
use language::{language_settings, tree_sitter_rust, Buffer, Language, LanguageConfig, Point};
use language::{
language_settings, tree_sitter_rust, Buffer, BufferId, Language, LanguageConfig, Point,
};
use settings::SettingsStore;
pub(crate) fn rust_lang() -> Language {
@@ -253,8 +255,9 @@ pub(crate) mod tests {
}
}
"};
let buffer =
cx.new_model(|cx| Buffer::new(0, 0, text).with_language(Arc::new(rust_lang()), cx));
let buffer = cx.new_model(|cx| {
Buffer::new(0, BufferId::new(1).unwrap(), text).with_language(Arc::new(rust_lang()), cx)
});
let snapshot = buffer.read(cx).snapshot();
assert_eq!(

View File

@@ -5,20 +5,17 @@ edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lib]
path = "src/audio.rs"
doctest = false
[dependencies]
gpui = { path = "../gpui" }
collections = { path = "../collections" }
util = { path = "../util" }
rodio ={version = "0.17.1", default-features=false, features = ["wav"]}
log.workspace = true
futures.workspace = true
anyhow.workspace = true
collections = { path = "../collections" }
derive_more.workspace = true
futures.workspace = true
gpui = { path = "../gpui" }
log.workspace = true
parking_lot.workspace = true
rodio = { version = "0.17.1", default-features = false, features = ["wav"] }
util = { path = "../util" }

View File

@@ -2,7 +2,7 @@ use std::{io::Cursor, sync::Arc};
use anyhow::Result;
use collections::HashMap;
use gpui::{AppContext, AssetSource};
use gpui::{AppContext, AssetSource, Global};
use rodio::{
source::{Buffered, SamplesConverter},
Decoder, Source,
@@ -15,6 +15,10 @@ pub struct SoundRegistry {
assets: Box<dyn AssetSource>,
}
struct GlobalSoundRegistry(Arc<SoundRegistry>);
impl Global for GlobalSoundRegistry {}
impl SoundRegistry {
pub fn new(source: impl AssetSource) -> Arc<Self> {
Arc::new(Self {
@@ -24,7 +28,11 @@ impl SoundRegistry {
}
pub fn global(cx: &AppContext) -> Arc<Self> {
cx.global::<Arc<Self>>().clone()
cx.global::<GlobalSoundRegistry>().0.clone()
}
pub(crate) fn set_global(source: impl AssetSource, cx: &mut AppContext) {
cx.set_global(GlobalSoundRegistry(SoundRegistry::new(source)));
}
pub fn get(&self, name: &str) -> Result<impl Source<Item = f32>> {

View File

@@ -1,13 +1,14 @@
use assets::SoundRegistry;
use gpui::{AppContext, AssetSource};
use derive_more::{Deref, DerefMut};
use gpui::{AppContext, AssetSource, Global};
use rodio::{OutputStream, OutputStreamHandle};
use util::ResultExt;
mod assets;
pub fn init(source: impl AssetSource, cx: &mut AppContext) {
cx.set_global(SoundRegistry::new(source));
cx.set_global(Audio::new());
SoundRegistry::set_global(source, cx);
cx.set_global(GlobalAudio(Audio::new()));
}
pub enum Sound {
@@ -37,6 +38,11 @@ pub struct Audio {
output_handle: Option<OutputStreamHandle>,
}
#[derive(Deref, DerefMut)]
struct GlobalAudio(Audio);
impl Global for GlobalAudio {}
impl Audio {
pub fn new() -> Self {
Self {
@@ -56,11 +62,11 @@ impl Audio {
}
pub fn play_sound(sound: Sound, cx: &mut AppContext) {
if !cx.has_global::<Self>() {
if !cx.has_global::<GlobalAudio>() {
return;
}
cx.update_global::<Self, _>(|this, cx| {
cx.update_global::<GlobalAudio, _>(|this, cx| {
let output_handle = this.ensure_output_exists()?;
let source = SoundRegistry::global(cx).get(sound.file()).log_err()?;
output_handle.play_raw(source).log_err()?;
@@ -69,11 +75,11 @@ impl Audio {
}
pub fn end_call(cx: &mut AppContext) {
if !cx.has_global::<Self>() {
if !cx.has_global::<GlobalAudio>() {
return;
}
cx.update_global::<Self, _>(|this, _| {
cx.update_global::<GlobalAudio, _>(|this, _| {
this._output_stream.take();
this.output_handle.take();
});

View File

@@ -5,28 +5,28 @@ edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lib]
path = "src/auto_update.rs"
doctest = false
[dependencies]
db = { path = "../db" }
client = { path = "../client" }
gpui = { path = "../gpui" }
menu = { path = "../menu" }
project = { path = "../project" }
settings = { path = "../settings" }
theme = { path = "../theme" }
workspace = { path = "../workspace" }
util = { path = "../util" }
anyhow.workspace = true
client = { path = "../client" }
db = { path = "../db" }
gpui = { path = "../gpui" }
isahc.workspace = true
lazy_static.workspace = true
log.workspace = true
menu = { path = "../menu" }
project = { path = "../project" }
release_channel = { path = "../release_channel" }
schemars.workspace = true
serde.workspace = true
serde_derive.workspace = true
serde_json.workspace = true
settings = { path = "../settings" }
smol.workspace = true
tempfile.workspace = true
theme = { path = "../theme" }
util = { path = "../util" }
workspace = { path = "../workspace" }

View File

@@ -5,8 +5,8 @@ use client::{Client, TelemetrySettings, ZED_APP_PATH, ZED_APP_VERSION};
use db::kvp::KEY_VALUE_STORE;
use db::RELEASE_CHANNEL;
use gpui::{
actions, AppContext, AsyncAppContext, Context as _, Model, ModelContext, SemanticVersion, Task,
ViewContext, VisualContext, WindowContext,
actions, AppContext, AsyncAppContext, Context as _, Global, Model, ModelContext,
SemanticVersion, Task, ViewContext, VisualContext, WindowContext,
};
use isahc::AsyncBody;
@@ -18,10 +18,15 @@ use smol::io::AsyncReadExt;
use settings::{Settings, SettingsStore};
use smol::{fs::File, process::Command};
use std::{ffi::OsString, sync::Arc, time::Duration};
use release_channel::{AppCommitSha, ReleaseChannel};
use std::{
env::consts::{ARCH, OS},
ffi::OsString,
sync::Arc,
time::Duration,
};
use update_notification::UpdateNotification;
use util::channel::{AppCommitSha, ReleaseChannel};
use util::http::HttpClient;
use util::http::{HttpClient, ZedHttpClient};
use workspace::Workspace;
const SHOULD_SHOW_UPDATE_NOTIFICATION_KEY: &str = "auto-updater-should-show-updated-notification";
@@ -49,9 +54,8 @@ pub enum AutoUpdateStatus {
pub struct AutoUpdater {
status: AutoUpdateStatus,
current_version: SemanticVersion,
http_client: Arc<dyn HttpClient>,
http_client: Arc<ZedHttpClient>,
pending_poll: Option<Task<Option<()>>>,
server_url: String,
}
#[derive(Deserialize)]
@@ -87,7 +91,12 @@ impl Settings for AutoUpdateSetting {
}
}
pub fn init(http_client: Arc<dyn HttpClient>, server_url: String, cx: &mut AppContext) {
#[derive(Default)]
struct GlobalAutoUpdate(Option<Model<AutoUpdater>>);
impl Global for GlobalAutoUpdate {}
pub fn init(http_client: Arc<ZedHttpClient>, cx: &mut AppContext) {
AutoUpdateSetting::register(cx);
cx.observe_new_views(|workspace: &mut Workspace, _cx| {
@@ -101,7 +110,7 @@ pub fn init(http_client: Arc<dyn HttpClient>, server_url: String, cx: &mut AppCo
if let Some(version) = ZED_APP_VERSION.or_else(|| cx.app_metadata().app_version) {
let auto_updater = cx.new_model(|cx| {
let updater = AutoUpdater::new(version, http_client, server_url);
let updater = AutoUpdater::new(version, http_client);
let mut update_subscription = AutoUpdateSetting::get_global(cx)
.0
@@ -120,7 +129,7 @@ pub fn init(http_client: Arc<dyn HttpClient>, server_url: String, cx: &mut AppCo
updater
});
cx.set_global(Some(auto_updater));
cx.set_global(GlobalAutoUpdate(Some(auto_updater)));
}
}
@@ -139,17 +148,18 @@ pub fn check(_: &Check, cx: &mut WindowContext) {
pub fn view_release_notes(_: &ViewReleaseNotes, cx: &mut AppContext) -> Option<()> {
let auto_updater = AutoUpdater::get(cx)?;
let release_channel = cx.try_global::<ReleaseChannel>()?;
let release_channel = ReleaseChannel::try_global(cx)?;
if matches!(
release_channel,
ReleaseChannel::Stable | ReleaseChannel::Preview
) {
let auto_updater = auto_updater.read(cx);
let server_url = &auto_updater.server_url;
let release_channel = release_channel.dev_name();
let current_version = auto_updater.current_version;
let url = format!("{server_url}/releases/{release_channel}/{current_version}");
let url = &auto_updater
.http_client
.zed_url(&format!("/releases/{release_channel}/{current_version}"));
cx.open_url(&url);
}
@@ -183,19 +193,14 @@ pub fn notify_of_any_new_update(cx: &mut ViewContext<Workspace>) -> Option<()> {
impl AutoUpdater {
pub fn get(cx: &mut AppContext) -> Option<Model<Self>> {
cx.default_global::<Option<Model<Self>>>().clone()
cx.default_global::<GlobalAutoUpdate>().0.clone()
}
fn new(
current_version: SemanticVersion,
http_client: Arc<dyn HttpClient>,
server_url: String,
) -> Self {
fn new(current_version: SemanticVersion, http_client: Arc<ZedHttpClient>) -> Self {
Self {
status: AutoUpdateStatus::Idle,
current_version,
http_client,
server_url,
pending_poll: None,
}
}
@@ -241,18 +246,16 @@ impl AutoUpdater {
}
async fn update(this: Model<Self>, mut cx: AsyncAppContext) -> Result<()> {
let (client, server_url, current_version) = this.read_with(&cx, |this, _| {
(
this.http_client.clone(),
this.server_url.clone(),
this.current_version,
)
let (client, current_version) = this.read_with(&cx, |this, _| {
(this.http_client.clone(), this.current_version)
})?;
let mut url_string = format!("{server_url}/api/releases/latest?asset=Zed.dmg");
let mut url_string = client.zed_url(&format!(
"/api/releases/latest?asset=Zed.dmg&os={}&arch={}",
OS, ARCH
));
cx.update(|cx| {
if let Some(param) = cx
.try_global::<ReleaseChannel>()
if let Some(param) = ReleaseChannel::try_global(cx)
.map(|release_channel| release_channel.release_query_param())
.flatten()
{
@@ -274,7 +277,9 @@ impl AutoUpdater {
let should_download = match *RELEASE_CHANNEL {
ReleaseChannel::Nightly => cx
.try_read_global::<AppCommitSha, _>(|sha, _| release.version != sha.0)
.update(|cx| AppCommitSha::try_global(cx).map(|sha| release.version != sha.0))
.ok()
.flatten()
.unwrap_or(true),
_ => release.version.parse::<SemanticVersion>()? > current_version,
};
@@ -309,9 +314,8 @@ impl AutoUpdater {
let mut dmg_file = File::create(&dmg_path).await?;
let (installation_id, release_channel, telemetry) = cx.update(|cx| {
let installation_id = cx.global::<Arc<Client>>().telemetry().installation_id();
let release_channel = cx
.try_global::<ReleaseChannel>()
let installation_id = Client::global(cx).telemetry().installation_id();
let release_channel = ReleaseChannel::try_global(cx)
.map(|release_channel| release_channel.display_name());
let telemetry = TelemetrySettings::get_global(cx).metrics;

View File

@@ -3,7 +3,7 @@ use gpui::{
SemanticVersion, StatefulInteractiveElement, Styled, ViewContext,
};
use menu::Cancel;
use util::channel::ReleaseChannel;
use release_channel::ReleaseChannel;
use workspace::ui::{h_flex, v_flex, Icon, IconName, Label, StyledExt};
pub struct UpdateNotification {
@@ -14,7 +14,7 @@ impl EventEmitter<DismissEvent> for UpdateNotification {}
impl Render for UpdateNotification {
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {
let app_name = cx.global::<ReleaseChannel>().display_name();
let app_name = ReleaseChannel::global(cx).display_name();
v_flex()
.on_action(cx.listener(UpdateNotification::dismiss))

View File

@@ -5,7 +5,6 @@ edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lib]
path = "src/breadcrumbs.rs"
doctest = false
@@ -14,15 +13,15 @@ doctest = false
collections = { path = "../collections" }
editor = { path = "../editor" }
gpui = { path = "../gpui" }
ui = { path = "../ui" }
itertools = "0.10"
language = { path = "../language" }
outline = { path = "../outline" }
project = { path = "../project" }
search = { path = "../search" }
settings = { path = "../settings" }
theme = { path = "../theme" }
ui = { path = "../ui" }
workspace = { path = "../workspace" }
outline = { path = "../outline" }
itertools = "0.10"
[dev-dependencies]
editor = { path = "../editor", features = ["test-support"] }

View File

@@ -5,7 +5,6 @@ edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lib]
path = "src/call.rs"
doctest = false
@@ -21,36 +20,35 @@ test-support = [
]
[dependencies]
anyhow.workspace = true
async-broadcast = "0.4"
audio = { path = "../audio" }
client = { path = "../client" }
collections = { path = "../collections" }
gpui = { path = "../gpui" }
log.workspace = true
live_kit_client = { path = "../live_kit_client" }
fs = { path = "../fs" }
language = { path = "../language" }
media = { path = "../media" }
project = { path = "../project" }
settings = { path = "../settings" }
util = { path = "../util" }
anyhow.workspace = true
async-broadcast = "0.4"
futures.workspace = true
gpui = { path = "../gpui" }
image = "0.23"
language = { path = "../language" }
live_kit_client = { path = "../live_kit_client" }
log.workspace = true
media = { path = "../media" }
postage.workspace = true
project = { path = "../project" }
schemars.workspace = true
serde.workspace = true
serde_json.workspace = true
serde_derive.workspace = true
serde_json.workspace = true
settings = { path = "../settings" }
smallvec.workspace = true
util = { path = "../util" }
[dev-dependencies]
client = { path = "../client", features = ["test-support"] }
fs = { path = "../fs", features = ["test-support"] }
language = { path = "../language", features = ["test-support"] }
collections = { path = "../collections", features = ["test-support"] }
fs = { path = "../fs", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
language = { path = "../language", features = ["test-support"] }
live_kit_client = { path = "../live_kit_client", features = ["test-support"] }
project = { path = "../project", features = ["test-support"] }
util = { path = "../util", features = ["test-support"] }

View File

@@ -9,8 +9,8 @@ use client::{proto, Client, TypedEnvelope, User, UserStore, ZED_ALWAYS_ACTIVE};
use collections::HashSet;
use futures::{channel::oneshot, future::Shared, Future, FutureExt};
use gpui::{
AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Subscription, Task,
WeakModel,
AppContext, AsyncAppContext, Context, EventEmitter, Global, Model, ModelContext, Subscription,
Task, WeakModel,
};
use postage::watch;
use project::Project;
@@ -21,11 +21,15 @@ use std::sync::Arc;
pub use participant::ParticipantLocation;
pub use room::Room;
struct GlobalActiveCall(Model<ActiveCall>);
impl Global for GlobalActiveCall {}
pub fn init(client: Arc<Client>, user_store: Model<UserStore>, cx: &mut AppContext) {
CallSettings::register(cx);
let active_call = cx.new_model(|cx| ActiveCall::new(client, user_store, cx));
cx.set_global(active_call);
cx.set_global(GlobalActiveCall(active_call));
}
pub struct OneAtATime {
@@ -154,7 +158,12 @@ impl ActiveCall {
}
pub fn global(cx: &AppContext) -> Model<Self> {
cx.global::<Model<Self>>().clone()
cx.global::<GlobalActiveCall>().0.clone()
}
pub fn try_global(cx: &AppContext) -> Option<Model<Self>> {
cx.try_global::<GlobalActiveCall>()
.map(|call| call.0.clone())
}
pub fn invite(

View File

@@ -5,7 +5,6 @@ edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lib]
path = "src/channel.rs"
doctest = false
@@ -14,38 +13,38 @@ doctest = false
test-support = ["collections/test-support", "gpui/test-support", "rpc/test-support"]
[dependencies]
anyhow.workspace = true
client = { path = "../client" }
clock = { path = "../clock" }
collections = { path = "../collections" }
db = { path = "../db" }
gpui = { path = "../gpui" }
util = { path = "../util" }
rpc = { path = "../rpc" }
text = { path = "../text" }
language = { path = "../language" }
settings = { path = "../settings" }
feature_flags = { path = "../feature_flags" }
sum_tree = { path = "../sum_tree" }
clock = { path = "../clock" }
anyhow.workspace = true
futures.workspace = true
gpui = { path = "../gpui" }
image = "0.23"
language = { path = "../language" }
lazy_static.workspace = true
smallvec.workspace = true
log.workspace = true
parking_lot.workspace = true
postage.workspace = true
rand.workspace = true
release_channel = { path = "../release_channel" }
rpc = { path = "../rpc" }
schemars.workspace = true
serde.workspace = true
serde_derive.workspace = true
settings = { path = "../settings" }
smallvec.workspace = true
smol.workspace = true
sum_tree = { path = "../sum_tree" }
tempfile.workspace = true
text = { path = "../text" }
thiserror.workspace = true
time.workspace = true
tiny_http = "0.8"
url.workspace = true
util = { path = "../util" }
uuid.workspace = true
url = "2.2"
serde.workspace = true
serde_derive.workspace = true
tempfile = "3"
[dev-dependencies]
collections = { path = "../collections", features = ["test-support"] }

View File

@@ -9,6 +9,7 @@ use rpc::{
TypedEnvelope,
};
use std::{sync::Arc, time::Duration};
use text::BufferId;
use util::ResultExt;
pub const ACKNOWLEDGE_DEBOUNCE_INTERVAL: Duration = Duration::from_millis(250);
@@ -53,7 +54,7 @@ impl ChannelBuffer {
channel_id: channel.id,
})
.await?;
let buffer_id = BufferId::new(response.buffer_id)?;
let base_text = response.base_text;
let operations = response
.operations
@@ -63,12 +64,7 @@ impl ChannelBuffer {
let buffer = cx.new_model(|cx| {
let capability = channel_store.read(cx).channel_capability(channel.id);
language::Buffer::remote(
response.buffer_id,
response.replica_id as u16,
capability,
base_text,
)
language::Buffer::remote(buffer_id, response.replica_id as u16, capability, base_text)
})?;
buffer.update(&mut cx, |buffer, cx| buffer.apply_ops(operations, cx))??;
@@ -107,7 +103,7 @@ impl ChannelBuffer {
}
}
pub fn remote_id(&self, cx: &AppContext) -> u64 {
pub fn remote_id(&self, cx: &AppContext) -> BufferId {
self.buffer.read(cx).remote_id()
}
@@ -210,7 +206,7 @@ impl ChannelBuffer {
pub fn acknowledge_buffer_version(&mut self, cx: &mut ModelContext<'_, ChannelBuffer>) {
let buffer = self.buffer.read(cx);
let version = buffer.version();
let buffer_id = buffer.remote_id();
let buffer_id = buffer.remote_id().into();
let client = self.client.clone();
let epoch = self.epoch();

View File

@@ -5,13 +5,13 @@ use anyhow::{anyhow, Result};
use channel_index::ChannelIndex;
use client::{Client, Subscription, User, UserId, UserStore};
use collections::{hash_map, HashMap, HashSet};
use db::RELEASE_CHANNEL;
use futures::{channel::mpsc, future::Shared, Future, FutureExt, StreamExt};
use gpui::{
AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, SharedString, Task,
WeakModel,
AppContext, AsyncAppContext, Context, EventEmitter, Global, Model, ModelContext, SharedString,
Task, WeakModel,
};
use language::Capability;
use release_channel::RELEASE_CHANNEL;
use rpc::{
proto::{self, ChannelRole, ChannelVisibility},
TypedEnvelope,
@@ -22,7 +22,7 @@ use util::{async_maybe, maybe, ResultExt};
pub fn init(client: &Arc<Client>, user_store: Model<UserStore>, cx: &mut AppContext) {
let channel_store =
cx.new_model(|cx| ChannelStore::new(client.clone(), user_store.clone(), cx));
cx.set_global(channel_store);
cx.set_global(GlobalChannelStore(channel_store));
}
pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30);
@@ -143,9 +143,13 @@ enum OpenedModelHandle<E> {
Loading(Shared<Task<Result<Model<E>, Arc<anyhow::Error>>>>),
}
struct GlobalChannelStore(Model<ChannelStore>);
impl Global for GlobalChannelStore {}
impl ChannelStore {
pub fn global(cx: &AppContext) -> Model<Self> {
cx.global::<Model<Self>>().clone()
cx.global::<GlobalChannelStore>().0.clone()
}
pub fn new(

View File

@@ -329,6 +329,7 @@ async fn test_channel_messages(cx: &mut TestAppContext) {
fn init_test(cx: &mut AppContext) -> Model<ChannelStore> {
let settings_store = SettingsStore::test(cx);
cx.set_global(settings_store);
client::init_settings(cx);
let http = FakeHttpClient::with_404_response();
let client = Client::new(http.clone(), cx);

View File

@@ -5,7 +5,6 @@ edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lib]
path = "src/cli.rs"
doctest = false

View File

@@ -5,7 +5,6 @@ edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lib]
path = "src/client.rs"
doctest = false
@@ -19,6 +18,7 @@ collections = { path = "../collections" }
db = { path = "../db" }
gpui = { path = "../gpui" }
util = { path = "../util" }
release_channel = { path = "../release_channel" }
rpc = { path = "../rpc" }
text = { path = "../text" }
settings = { path = "../settings" }
@@ -41,12 +41,12 @@ serde_derive.workspace = true
serde_json.workspace = true
smol.workspace = true
sysinfo.workspace = true
tempfile = "3"
tempfile.workspace = true
thiserror.workspace = true
time.workspace = true
tiny_http = "0.8"
uuid.workspace = true
url = "2.2"
url.workspace = true
[dev-dependencies]
collections = { path = "../collections", features = ["test-support"] }

View File

@@ -15,18 +15,19 @@ use futures::{
TryFutureExt as _, TryStreamExt,
};
use gpui::{
actions, AnyModel, AnyWeakModel, AppContext, AsyncAppContext, Model, SemanticVersion, Task,
WeakModel,
actions, AnyModel, AnyWeakModel, AppContext, AsyncAppContext, Global, Model, SemanticVersion,
Task, WeakModel,
};
use lazy_static::lazy_static;
use parking_lot::RwLock;
use postage::watch;
use rand::prelude::*;
use release_channel::ReleaseChannel;
use rpc::proto::{AnyTypedEnvelope, EntityMessage, EnvelopedMessage, PeerId, RequestMessage};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_json;
use settings::Settings;
use settings::{Settings, SettingsStore};
use std::{
any::TypeId,
collections::HashMap,
@@ -41,8 +42,7 @@ use std::{
use telemetry::Telemetry;
use thiserror::Error;
use url::Url;
use util::channel::ReleaseChannel;
use util::http::HttpClient;
use util::http::{HttpClient, ZedHttpClient};
use util::{ResultExt, TryFutureExt};
pub use rpc::*;
@@ -50,9 +50,8 @@ pub use telemetry::Event;
pub use user::*;
lazy_static! {
pub static ref ZED_SERVER_URL: String =
std::env::var("ZED_SERVER_URL").unwrap_or_else(|_| "https://zed.dev".to_string());
pub static ref ZED_RPC_URL: Option<String> = std::env::var("ZED_RPC_URL").ok();
static ref ZED_SERVER_URL: Option<String> = std::env::var("ZED_SERVER_URL").ok();
static ref ZED_RPC_URL: Option<String> = std::env::var("ZED_RPC_URL").ok();
pub static ref IMPERSONATE_LOGIN: Option<String> = std::env::var("ZED_IMPERSONATE")
.ok()
.and_then(|s| if s.is_empty() { None } else { Some(s) });
@@ -73,13 +72,45 @@ pub const CONNECTION_TIMEOUT: Duration = Duration::from_secs(5);
actions!(client, [SignIn, SignOut, Reconnect]);
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
pub struct ClientSettingsContent {
server_url: Option<String>,
}
#[derive(Deserialize)]
pub struct ClientSettings {
pub server_url: String,
}
impl Settings for ClientSettings {
const KEY: Option<&'static str> = None;
type FileContent = ClientSettingsContent;
fn load(
default_value: &Self::FileContent,
user_values: &[&Self::FileContent],
_: &mut AppContext,
) -> Result<Self>
where
Self: Sized,
{
let mut result = Self::load_via_json_merge(default_value, user_values)?;
if let Some(server_url) = &*ZED_SERVER_URL {
result.server_url = server_url.clone()
}
Ok(result)
}
}
pub fn init_settings(cx: &mut AppContext) {
TelemetrySettings::register(cx);
cx.update_global(|store: &mut SettingsStore, cx| {
store.register_setting::<ClientSettings>(cx);
});
}
pub fn init(client: &Arc<Client>, cx: &mut AppContext) {
init_settings(cx);
let client = Arc::downgrade(client);
cx.on_action({
let client = client.clone();
@@ -118,10 +149,14 @@ pub fn init(client: &Arc<Client>, cx: &mut AppContext) {
});
}
struct GlobalClient(Arc<Client>);
impl Global for GlobalClient {}
pub struct Client {
id: AtomicU64,
peer: Arc<Peer>,
http: Arc<dyn HttpClient>,
http: Arc<ZedHttpClient>,
telemetry: Arc<Telemetry>,
state: RwLock<ClientState>,
@@ -390,8 +425,8 @@ impl settings::Settings for TelemetrySettings {
}
impl Client {
pub fn new(http: Arc<dyn HttpClient>, cx: &mut AppContext) -> Arc<Self> {
Arc::new(Self {
pub fn new(http: Arc<ZedHttpClient>, cx: &mut AppContext) -> Arc<Self> {
let client = Arc::new(Self {
id: AtomicU64::new(0),
peer: Peer::new(0),
telemetry: Telemetry::new(http.clone(), cx),
@@ -402,14 +437,16 @@ impl Client {
authenticate: Default::default(),
#[cfg(any(test, feature = "test-support"))]
establish_connection: Default::default(),
})
});
client
}
pub fn id(&self) -> u64 {
self.id.load(std::sync::atomic::Ordering::SeqCst)
}
pub fn http_client(&self) -> Arc<dyn HttpClient> {
pub fn http_client(&self) -> Arc<ZedHttpClient> {
self.http.clone()
}
@@ -450,6 +487,13 @@ impl Client {
self
}
pub fn global(cx: &AppContext) -> Arc<Self> {
cx.global::<GlobalClient>().0.clone()
}
pub fn set_global(client: Arc<Client>, cx: &mut AppContext) {
cx.set_global(GlobalClient(client))
}
pub fn user_id(&self) -> Option<u64> {
self.state
.read()
@@ -925,14 +969,14 @@ impl Client {
}
async fn get_rpc_url(
http: Arc<dyn HttpClient>,
http: Arc<ZedHttpClient>,
release_channel: Option<ReleaseChannel>,
) -> Result<Url> {
if let Some(url) = &*ZED_RPC_URL {
return Url::parse(url).context("invalid rpc url");
}
let mut url = format!("{}/rpc", *ZED_SERVER_URL);
let mut url = http.zed_url("/rpc");
if let Some(preview_param) =
release_channel.and_then(|channel| channel.release_query_param())
{
@@ -963,7 +1007,10 @@ impl Client {
credentials: &Credentials,
cx: &AsyncAppContext,
) -> Task<Result<Connection, EstablishConnectionError>> {
let release_channel = cx.try_read_global(|channel: &ReleaseChannel, _| *channel);
let release_channel = cx
.update(|cx| ReleaseChannel::try_global(cx))
.ok()
.flatten();
let request = Request::builder()
.header(
@@ -1053,10 +1100,10 @@ impl Client {
// Open the Zed sign-in page in the user's browser, with query parameters that indicate
// that the user is signing in from a Zed app running on the same device.
let mut url = format!(
"{}/native_app_signin?native_app_port={}&native_app_public_key={}",
*ZED_SERVER_URL, port, public_key_string
);
let mut url = http.zed_url(&format!(
"/native_app_signin?native_app_port={}&native_app_public_key={}",
port, public_key_string
));
if let Some(impersonate_login) = IMPERSONATE_LOGIN.as_ref() {
log::info!("impersonating user @{}", impersonate_login);
@@ -1088,7 +1135,7 @@ impl Client {
}
let post_auth_url =
format!("{}/native_app_signin_succeeded", *ZED_SERVER_URL);
http.zed_url("/native_app_signin_succeeded");
req.respond(
tiny_http::Response::empty(302).with_header(
tiny_http::Header::from_bytes(
@@ -1130,7 +1177,7 @@ impl Client {
}
async fn authenticate_as_admin(
http: Arc<dyn HttpClient>,
http: Arc<ZedHttpClient>,
login: String,
mut api_token: String,
) -> Result<Credentials> {
@@ -1351,7 +1398,7 @@ async fn read_credentials_from_keychain(cx: &AsyncAppContext) -> Option<Credenti
}
let (user_id, access_token) = cx
.update(|cx| cx.read_credentials(&ZED_SERVER_URL))
.update(|cx| cx.read_credentials(&ClientSettings::get_global(cx).server_url))
.log_err()?
.await
.log_err()??;
@@ -1368,7 +1415,7 @@ async fn write_credentials_to_keychain(
) -> Result<()> {
cx.update(move |cx| {
cx.write_credentials(
&ZED_SERVER_URL,
&ClientSettings::get_global(cx).server_url,
&credentials.user_id.to_string(),
credentials.access_token.as_bytes(),
)
@@ -1377,7 +1424,7 @@ async fn write_credentials_to_keychain(
}
async fn delete_credentials_from_keychain(cx: &AsyncAppContext) -> Result<()> {
cx.update(move |cx| cx.delete_credentials(&ZED_SERVER_URL))?
cx.update(move |cx| cx.delete_credentials(&ClientSettings::get_global(cx).server_url))?
.await
}
@@ -1684,6 +1731,7 @@ mod tests {
cx.update(|cx| {
let settings_store = SettingsStore::test(cx);
cx.set_global(settings_store);
init_settings(cx);
});
}
}

View File

@@ -1,11 +1,11 @@
mod event_coalescer;
use crate::{TelemetrySettings, ZED_SERVER_URL};
use crate::TelemetrySettings;
use chrono::{DateTime, Utc};
use futures::Future;
use gpui::{AppContext, AppMetadata, BackgroundExecutor, Task};
use lazy_static::lazy_static;
use parking_lot::Mutex;
use release_channel::ReleaseChannel;
use serde::Serialize;
use settings::{Settings, SettingsStore};
use std::{env, io::Write, mem, path::PathBuf, sync::Arc, time::Duration};
@@ -13,15 +13,15 @@ use sysinfo::{
CpuRefreshKind, Pid, PidExt, ProcessExt, ProcessRefreshKind, RefreshKind, System, SystemExt,
};
use tempfile::NamedTempFile;
use util::http::HttpClient;
use util::http::{HttpClient, ZedHttpClient};
#[cfg(not(debug_assertions))]
use util::ResultExt;
use util::{channel::ReleaseChannel, TryFutureExt};
use util::TryFutureExt;
use self::event_coalescer::EventCoalescer;
pub struct Telemetry {
http_client: Arc<dyn HttpClient>,
http_client: Arc<ZedHttpClient>,
executor: BackgroundExecutor,
state: Arc<Mutex<TelemetryState>>,
}
@@ -43,12 +43,6 @@ struct TelemetryState {
max_queue_size: usize,
}
const EVENTS_URL_PATH: &'static str = "/api/events";
lazy_static! {
static ref EVENTS_URL: String = format!("{}{}", *ZED_SERVER_URL, EVENTS_URL_PATH);
}
#[derive(Serialize, Debug)]
struct EventRequestBody {
installation_id: Option<Arc<str>>,
@@ -149,10 +143,9 @@ const FLUSH_INTERVAL: Duration = Duration::from_secs(1);
const FLUSH_INTERVAL: Duration = Duration::from_secs(60 * 5);
impl Telemetry {
pub fn new(client: Arc<dyn HttpClient>, cx: &mut AppContext) -> Arc<Self> {
let release_channel = cx
.try_global::<ReleaseChannel>()
.map(|release_channel| release_channel.display_name());
pub fn new(client: Arc<ZedHttpClient>, cx: &mut AppContext) -> Arc<Self> {
let release_channel =
ReleaseChannel::try_global(cx).map(|release_channel| release_channel.display_name());
TelemetrySettings::register(cx);
@@ -548,7 +541,7 @@ impl Telemetry {
}
this.http_client
.post_json(EVENTS_URL.as_str(), json_bytes.into())
.post_json(&this.http_client.zed_url("/api/events"), json_bytes.into())
.await?;
anyhow::Ok(())
}

View File

@@ -4,7 +4,7 @@ use collections::{hash_map::Entry, HashMap, HashSet};
use feature_flags::FeatureFlagAppExt;
use futures::{channel::mpsc, Future, StreamExt};
use gpui::{
AppContext, AsyncAppContext, EventEmitter, Model, ModelContext, SharedString, SharedUrl, Task,
AppContext, AsyncAppContext, EventEmitter, Model, ModelContext, SharedString, SharedUri, Task,
WeakModel,
};
use postage::{sink::Sink, watch};
@@ -22,7 +22,7 @@ pub struct ParticipantIndex(pub u32);
pub struct User {
pub id: UserId,
pub github_login: String,
pub avatar_uri: SharedUrl,
pub avatar_uri: SharedUri,
}
#[derive(Clone, Debug, PartialEq, Eq)]

View File

@@ -5,7 +5,6 @@ edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lib]
path = "src/clock.rs"
doctest = false

View File

@@ -3,11 +3,10 @@ authors = ["Nathan Sobo <nathan@zed.dev>"]
default-run = "collab"
edition = "2021"
name = "collab"
version = "0.42.0"
version = "0.43.0"
publish = false
license = "AGPL-3.0-or-later"
[[bin]]
name = "collab"
@@ -16,26 +15,22 @@ name = "seed"
required-features = ["seed-support"]
[dependencies]
clock = { path = "../clock" }
collections = { path = "../collections" }
live_kit_server = { path = "../live_kit_server" }
text = { path = "../text" }
rpc = { path = "../rpc" }
util = { path = "../util" }
anyhow.workspace = true
async-tungstenite = "0.16"
axum = { version = "0.5", features = ["json", "headers", "ws"] }
axum-extra = { version = "0.3", features = ["erased-json"] }
base64 = "0.13"
clap = { version = "3.1", features = ["derive"], optional = true }
chrono.workspace = true
clap = { version = "3.1", features = ["derive"], optional = true }
clock = { path = "../clock" }
collections = { path = "../collections" }
dashmap = "5.4"
envy = "0.4.2"
futures.workspace = true
hyper = "0.14"
lazy_static.workspace = true
lipsum = { version = "0.8", optional = true }
live_kit_server = { path = "../live_kit_server" }
log.workspace = true
nanoid = "0.4"
parking_lot.workspace = true
@@ -43,62 +38,62 @@ prometheus = "0.13"
prost.workspace = true
rand.workspace = true
reqwest = { version = "0.11", features = ["json"], optional = true }
rpc = { path = "../rpc" }
scrypt = "0.7"
smallvec.workspace = true
sea-orm = { version = "0.12.x", features = ["sqlx-postgres", "postgres-array", "runtime-tokio-rustls", "with-uuid"] }
serde.workspace = true
serde_derive.workspace = true
serde_json.workspace = true
sha-1 = "0.9"
smallvec.workspace = true
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres", "json", "time", "uuid", "any"] }
text = { path = "../text" }
time.workspace = true
tokio = { version = "1", features = ["full"] }
tokio-tungstenite = "0.17"
toml.workspace = true
tonic = "0.6"
tower = "0.4"
toml.workspace = true
tracing = "0.1.34"
tracing-log = "0.1.3"
tracing-subscriber = { version = "0.3.11", features = ["env-filter", "json"] }
util = { path = "../util" }
uuid.workspace = true
[dev-dependencies]
async-trait.workspace = true
audio = { path = "../audio" }
collections = { path = "../collections", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
call = { path = "../call", features = ["test-support"] }
client = { path = "../client", features = ["test-support"] }
channel = { path = "../channel" }
client = { path = "../client", features = ["test-support"] }
collab_ui = { path = "../collab_ui", features = ["test-support"] }
collections = { path = "../collections", features = ["test-support"] }
ctor.workspace = true
editor = { path = "../editor", features = ["test-support"] }
language = { path = "../language", features = ["test-support"] }
env_logger.workspace = true
file_finder = { path = "../file_finder" }
fs = { path = "../fs", features = ["test-support"] }
git = { path = "../git", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
indoc.workspace = true
language = { path = "../language", features = ["test-support"] }
lazy_static.workspace = true
live_kit_client = { path = "../live_kit_client", features = ["test-support"] }
lsp = { path = "../lsp", features = ["test-support"] }
menu = { path = "../menu" }
node_runtime = { path = "../node_runtime" }
notifications = { path = "../notifications", features = ["test-support"] }
file_finder = { path = "../file_finder"}
menu = { path = "../menu"}
pretty_assertions.workspace = true
project = { path = "../project", features = ["test-support"] }
rpc = { path = "../rpc", features = ["test-support"] }
settings = { path = "../settings", features = ["test-support"] }
theme = { path = "../theme" }
workspace = { path = "../workspace", features = ["test-support"] }
collab_ui = { path = "../collab_ui", features = ["test-support"] }
async-trait.workspace = true
pretty_assertions.workspace = true
ctor.workspace = true
env_logger.workspace = true
indoc.workspace = true
util = { path = "../util" }
lazy_static.workspace = true
sea-orm = { version = "0.12.x", features = ["sqlx-sqlite"] }
serde_json.workspace = true
settings = { path = "../settings", features = ["test-support"] }
sqlx = { version = "0.7", features = ["sqlite"] }
theme = { path = "../theme" }
unindent.workspace = true
util = { path = "../util" }
workspace = { path = "../workspace", features = ["test-support"] }
[features]
seed-support = ["clap", "lipsum", "reqwest"]

View File

@@ -1,4 +0,0 @@
ZED_ENVIRONMENT=nightly
RUST_LOG=info
INVITE_LINK_PREFIX=https://zed.dev/invites/
DATABASE_MAX_CONNECTIONS=10

View File

@@ -1,4 +0,0 @@
ZED_ENVIRONMENT=preview
RUST_LOG=info
INVITE_LINK_PREFIX=https://zed.dev/invites/
DATABASE_MAX_CONNECTIONS=10

View File

@@ -0,0 +1,4 @@
-- Add migration script here
DROP INDEX index_channels_on_parent_path;
CREATE INDEX index_channels_on_parent_path ON channels (parent_path text_pattern_ops);

View File

@@ -693,7 +693,7 @@ impl Database {
return Ok(());
}
let mut text_buffer = text::Buffer::new(0, 0, base_text);
let mut text_buffer = text::Buffer::new(0, text::BufferId::new(1).unwrap(), base_text);
text_buffer
.apply_ops(operations.into_iter().filter_map(operation_from_wire))
.unwrap();

View File

@@ -173,7 +173,7 @@ impl Database {
.await
}
/// Sets the visibiltity of the given channel.
/// Sets the visibility of the given channel.
pub async fn set_channel_visibility(
&self,
channel_id: ChannelId,
@@ -197,12 +197,10 @@ impl Database {
}
} else if visibility == ChannelVisibility::Members {
if self
.get_channel_descendants_including_self(vec![channel_id], &*tx)
.get_channel_descendants_excluding_self([&channel], &*tx)
.await?
.into_iter()
.any(|channel| {
channel.id != channel_id && channel.visibility == ChannelVisibility::Public
})
.any(|channel| channel.visibility == ChannelVisibility::Public)
{
Err(ErrorCode::BadPublicNesting
.with_tag("direction", "children")
@@ -261,10 +259,11 @@ impl Database {
.await?;
let channels_to_remove = self
.get_channel_descendants_including_self(vec![channel.id], &*tx)
.get_channel_descendants_excluding_self([&channel], &*tx)
.await?
.into_iter()
.map(|channel| channel.id)
.chain(Some(channel_id))
.collect::<Vec<_>>();
channel::Entity::delete_many()
@@ -445,16 +444,12 @@ impl Database {
) -> Result<MembershipUpdated> {
let new_channels = self.get_user_channels(user_id, Some(channel), &*tx).await?;
let removed_channels = self
.get_channel_descendants_including_self(vec![channel.id], &*tx)
.get_channel_descendants_excluding_self([channel], &*tx)
.await?
.into_iter()
.filter_map(|channel| {
if !new_channels.channels.iter().any(|c| c.id == channel.id) {
Some(channel.id)
} else {
None
}
})
.map(|channel| channel.id)
.chain([channel.id])
.filter(|channel_id| !new_channels.channels.iter().any(|c| c.id == *channel_id))
.collect::<Vec<_>>();
Ok(MembershipUpdated {
@@ -545,26 +540,6 @@ impl Database {
.await
}
pub async fn get_channel_memberships(
&self,
user_id: UserId,
) -> Result<(Vec<channel_member::Model>, Vec<channel::Model>)> {
self.transaction(|tx| async move {
let memberships = channel_member::Entity::find()
.filter(channel_member::Column::UserId.eq(user_id))
.all(&*tx)
.await?;
let channels = self
.get_channel_descendants_including_self(
memberships.iter().map(|m| m.channel_id),
&*tx,
)
.await?;
Ok((memberships, channels))
})
.await
}
/// Returns all channels for the user with the given ID.
pub async fn get_channels_for_user(&self, user_id: UserId) -> Result<ChannelsForUser> {
self.transaction(|tx| async move {
@@ -596,13 +571,21 @@ impl Database {
.all(&*tx)
.await?;
let descendants = self
.get_channel_descendants_including_self(
channel_memberships.iter().map(|m| m.channel_id),
&*tx,
)
let channels = channel::Entity::find()
.filter(channel::Column::Id.is_in(channel_memberships.iter().map(|m| m.channel_id)))
.all(&*tx)
.await?;
let mut descendants = self
.get_channel_descendants_excluding_self(channels.iter(), &*tx)
.await?;
for channel in channels {
if let Err(ix) = descendants.binary_search_by_key(&channel.path(), |c| c.path()) {
descendants.insert(ix, channel);
}
}
let roles_by_channel_id = channel_memberships
.iter()
.map(|membership| (membership.channel_id, membership.role))
@@ -880,46 +863,23 @@ impl Database {
// Get the descendants of the given set if channels, ordered by their
// path.
async fn get_channel_descendants_including_self(
pub(crate) async fn get_channel_descendants_excluding_self(
&self,
channel_ids: impl IntoIterator<Item = ChannelId>,
channels: impl IntoIterator<Item = &channel::Model>,
tx: &DatabaseTransaction,
) -> Result<Vec<channel::Model>> {
let mut values = String::new();
for id in channel_ids {
if !values.is_empty() {
values.push_str(", ");
}
write!(&mut values, "({})", id).unwrap();
let mut filter = Condition::any();
for channel in channels.into_iter() {
filter = filter.add(channel::Column::ParentPath.like(channel.descendant_path_filter()));
}
if values.is_empty() {
if filter.is_empty() {
return Ok(vec![]);
}
let sql = format!(
r#"
SELECT DISTINCT
descendant_channels.*,
descendant_channels.parent_path || descendant_channels.id as full_path
FROM
channels parent_channels, channels descendant_channels
WHERE
descendant_channels.id IN ({values}) OR
(
parent_channels.id IN ({values}) AND
descendant_channels.parent_path LIKE (parent_channels.parent_path || parent_channels.id || '/%')
)
ORDER BY
full_path ASC
"#
);
Ok(channel::Entity::find()
.from_raw_sql(Statement::from_string(
self.pool.get_database_backend(),
sql,
))
.filter(filter)
.order_by_asc(Expr::cust("parent_path || id || '/'"))
.all(tx)
.await?)
}

View File

@@ -39,6 +39,10 @@ impl Model {
pub fn path(&self) -> String {
format!("{}{}/", self.parent_path, self.id)
}
pub fn descendant_path_filter(&self) -> String {
format!("{}{}/%", self.parent_path, self.id)
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -67,7 +67,7 @@ async fn test_channel_buffers(db: &Arc<Database>) {
.await
.unwrap();
let mut buffer_a = Buffer::new(0, 0, "".to_string());
let mut buffer_a = Buffer::new(0, text::BufferId::new(1).unwrap(), "".to_string());
let mut operations = Vec::new();
operations.push(buffer_a.edit([(0..0, "hello world")]));
operations.push(buffer_a.edit([(5..5, ", cruel")]));
@@ -90,7 +90,11 @@ async fn test_channel_buffers(db: &Arc<Database>) {
.await
.unwrap();
let mut buffer_b = Buffer::new(0, 0, buffer_response_b.base_text);
let mut buffer_b = Buffer::new(
0,
text::BufferId::new(1).unwrap(),
buffer_response_b.base_text,
);
buffer_b
.apply_ops(buffer_response_b.operations.into_iter().map(|operation| {
let operation = proto::deserialize_operation(operation).unwrap();
@@ -223,7 +227,11 @@ async fn test_channel_buffers_last_operations(db: &Database) {
.unwrap(),
);
text_buffers.push(Buffer::new(0, 0, "".to_string()));
text_buffers.push(Buffer::new(
0,
text::BufferId::new(1).unwrap(),
"".to_string(),
));
}
let operations = db
@@ -270,7 +278,7 @@ async fn test_channel_buffers_last_operations(db: &Database) {
db.join_channel_buffer(buffers[1].channel_id, user_id, connection_id)
.await
.unwrap();
text_buffers[1] = Buffer::new(1, 0, "def".to_string());
text_buffers[1] = Buffer::new(1, text::BufferId::new(1).unwrap(), "def".to_string());
update_buffer(
buffers[1].channel_id,
user_id,

View File

@@ -29,48 +29,13 @@ async fn main() -> Result<()> {
println!("collab v{VERSION}");
}
Some("migrate") => {
let config = envy::from_env::<MigrateConfig>().expect("error loading config");
let mut db_options = db::ConnectOptions::new(config.database_url.clone());
db_options.max_connections(5);
let db = Database::new(db_options, Executor::Production).await?;
let migrations_path = config
.migrations_path
.as_deref()
.unwrap_or_else(|| Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/migrations")));
let migrations = db.migrate(&migrations_path, false).await?;
for (migration, duration) in migrations {
println!(
"Ran {} {} {:?}",
migration.version, migration.description, duration
);
}
return Ok(());
run_migrations().await?;
}
Some("serve") => {
let config = envy::from_env::<Config>().expect("error loading config");
init_tracing(&config);
if config.is_development() {
// sanity check database url so even if we deploy a busted ZED_ENVIRONMENT to production
// we do not run
if config.database_url != "postgres://postgres@localhost/zed" {
panic!("about to run development migrations on a non-development database?")
}
let migrations_path = Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/migrations"));
let db_options = db::ConnectOptions::new(config.database_url.clone());
let db = Database::new(db_options, Executor::Production).await?;
let migrations = db.migrate(&migrations_path, false).await?;
for (migration, duration) in migrations {
println!(
"Ran {} {} {:?}",
migration.version, migration.description, duration
);
}
}
run_migrations().await?;
let state = AppState::new(config).await?;
@@ -116,6 +81,29 @@ async fn main() -> Result<()> {
Ok(())
}
async fn run_migrations() -> Result<()> {
let config = envy::from_env::<MigrateConfig>().expect("error loading config");
let db_options = db::ConnectOptions::new(config.database_url.clone());
let db = Database::new(db_options, Executor::Production).await?;
let migrations_path = config
.migrations_path
.as_deref()
.unwrap_or_else(|| Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/migrations")));
let migrations = db.migrate(&migrations_path, false).await?;
for (migration, duration) in migrations {
log::info!(
"Migrated {} {} {:?}",
migration.version,
migration.description,
duration
);
}
return Ok(());
}
async fn handle_root() -> String {
format!("collab v{VERSION}")
}

View File

@@ -1263,7 +1263,7 @@ async fn rejoin_room(
Ok(())
}
/// leave room disonnects from the room.
/// leave room disconnects from the room.
async fn leave_room(
_: proto::LeaveRoom,
response: Response<proto::LeaveRoom>,

View File

@@ -153,6 +153,7 @@ impl TestServer {
}
let settings = SettingsStore::test(cx);
cx.set_global(settings);
client::init_settings(cx);
});
let http = FakeHttpClient::with_404_response();

View File

@@ -5,7 +5,6 @@ edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lib]
path = "src/collab_ui.rs"
doctest = false
@@ -26,50 +25,47 @@ test-support = [
]
[dependencies]
anyhow.workspace = true
auto_update = { path = "../auto_update" }
db = { path = "../db" }
call = { path = "../call" }
client = { path = "../client" }
channel = { path = "../channel" }
client = { path = "../client" }
clock = { path = "../clock" }
collections = { path = "../collections" }
# context_menu = { path = "../context_menu" }
# drag_and_drop = { path = "../drag_and_drop" }
db = { path = "../db" }
editor = { path = "../editor" }
feature_flags = { path = "../feature_flags" }
feedback = { path = "../feedback" }
fuzzy = { path = "../fuzzy" }
futures.workspace = true
fuzzy = { path = "../fuzzy" }
gpui = { path = "../gpui" }
language = { path = "../language" }
menu = { path = "../menu" }
notifications = { path = "../notifications" }
rich_text = { path = "../rich_text" }
picker = { path = "../picker" }
project = { path = "../project" }
recent_projects = { path = "../recent_projects" }
rpc = { path = "../rpc" }
settings = { path = "../settings" }
story = { path = "../story", optional = true }
feature_flags = { path = "../feature_flags"}
theme = { path = "../theme" }
theme_selector = { path = "../theme_selector" }
vcs_menu = { path = "../vcs_menu" }
ui = { path = "../ui" }
util = { path = "../util" }
workspace = { path = "../workspace" }
zed_actions = { path = "../zed_actions"}
anyhow.workspace = true
futures.workspace = true
lazy_static.workspace = true
log.workspace = true
menu = { path = "../menu" }
notifications = { path = "../notifications" }
parking_lot.workspace = true
schemars.workspace = true
picker = { path = "../picker" }
postage.workspace = true
project = { path = "../project" }
recent_projects = { path = "../recent_projects" }
rich_text = { path = "../rich_text" }
rpc = { path = "../rpc" }
schemars.workspace = true
serde.workspace = true
serde_derive.workspace = true
serde_json.workspace = true
time.workspace = true
settings = { path = "../settings" }
smallvec.workspace = true
story = { path = "../story", optional = true }
theme = { path = "../theme" }
theme_selector = { path = "../theme_selector" }
time.workspace = true
ui = { path = "../ui" }
util = { path = "../util" }
vcs_menu = { path = "../vcs_menu" }
workspace = { path = "../workspace" }
zed_actions = { path = "../zed_actions" }
[dev-dependencies]
call = { path = "../call", features = ["test-support"] }
@@ -78,11 +74,10 @@ collections = { path = "../collections", features = ["test-support"] }
editor = { path = "../editor", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
notifications = { path = "../notifications", features = ["test-support"] }
pretty_assertions.workspace = true
project = { path = "../project", features = ["test-support"] }
rpc = { path = "../rpc", features = ["test-support"] }
settings = { path = "../settings", features = ["test-support"] }
tree-sitter-markdown.workspace = true
util = { path = "../util", features = ["test-support"] }
workspace = { path = "../workspace", features = ["test-support"] }
pretty_assertions.workspace = true
tree-sitter-markdown.workspace = true

View File

@@ -8,8 +8,9 @@ use db::kvp::KEY_VALUE_STORE;
use editor::Editor;
use gpui::{
actions, div, list, prelude::*, px, Action, AppContext, AsyncWindowContext, DismissEvent,
ElementId, EventEmitter, FocusHandle, FocusableView, FontWeight, ListOffset, ListScrollEvent,
ListState, Model, Render, Subscription, Task, View, ViewContext, VisualContext, WeakView,
ElementId, EventEmitter, Fill, FocusHandle, FocusableView, FontWeight, ListOffset,
ListScrollEvent, ListState, Model, Render, Subscription, Task, View, ViewContext,
VisualContext, WeakView,
};
use language::LanguageRegistry;
use menu::Confirm;
@@ -335,67 +336,78 @@ impl ChatPanel {
};
let this = cx.view().clone();
v_flex()
.w_full()
.relative()
.overflow_hidden()
.when(!is_continuation_from_previous, |this| {
this.pt_3().child(
h_flex()
.text_ui_sm()
.child(
div().absolute().child(
let mentioning_you = message
.mentions
.iter()
.any(|m| Some(m.1) == self.client.user_id());
v_flex().w_full().relative().child(
div()
.bg(if mentioning_you {
Fill::from(cx.theme().colors().background)
} else {
Fill::default()
})
.rounded_md()
.overflow_hidden()
.px_1()
.py_0p5()
.when(!is_continuation_from_previous, |this| {
this.mt_1().child(
h_flex()
.text_ui_sm()
.child(div().absolute().child(
Avatar::new(message.sender.avatar_uri.clone()).size(rems(1.)),
))
.child(
div()
.pl(cx.rem_size() + px(6.0))
.pr(px(8.0))
.font_weight(FontWeight::BOLD)
.child(Label::new(message.sender.github_login.clone())),
)
.child(
Label::new(format_timestamp(
OffsetDateTime::now_utc(),
message.timestamp,
self.local_timezone,
))
.size(LabelSize::Small)
.color(Color::Muted),
),
)
)
})
.when(mentioning_you, |this| this.mt_1())
.child(
v_flex()
.w_full()
.text_ui_sm()
.id(element_id)
.group("")
.child(text.element("body".into(), cx))
.child(
div()
.pl(cx.rem_size() + px(6.0))
.pr(px(8.0))
.font_weight(FontWeight::BOLD)
.child(Label::new(message.sender.github_login.clone())),
)
.child(
Label::new(format_timestamp(
OffsetDateTime::now_utc(),
message.timestamp,
self.local_timezone,
))
.size(LabelSize::Small)
.color(Color::Muted),
.absolute()
.z_index(1)
.right_0()
.w_6()
.bg(cx.theme().colors().panel_background)
.when(!self.has_open_menu(message_id_to_remove), |el| {
el.visible_on_hover("")
})
.children(message_id_to_remove.map(|message_id| {
popover_menu(("menu", message_id))
.trigger(IconButton::new(
("trigger", message_id),
IconName::Ellipsis,
))
.menu(move |cx| {
Some(Self::render_message_menu(&this, message_id, cx))
})
})),
),
)
})
.when(is_continuation_from_previous, |this| this.pt_1())
.child(
v_flex()
.w_full()
.text_ui_sm()
.id(element_id)
.group("")
.child(text.element("body".into(), cx))
.child(
div()
.absolute()
.z_index(1)
.right_0()
.w_6()
.bg(cx.theme().colors().panel_background)
.when(!self.has_open_menu(message_id_to_remove), |el| {
el.visible_on_hover("")
})
.children(message_id_to_remove.map(|message_id| {
popover_menu(("menu", message_id))
.trigger(IconButton::new(
("trigger", message_id),
IconName::Ellipsis,
))
.menu(move |cx| {
Some(Self::render_message_menu(&this, message_id, cx))
})
})),
),
)
),
)
}
fn has_open_menu(&self, message_id: Option<u64>) -> bool {

View File

@@ -2495,9 +2495,8 @@ impl CollabPanel {
h_flex()
.absolute()
.right(rems(0.))
.z_index(1)
.h_full()
// HACK: Without this the channel name clips on top of the icons, but I'm not sure why.
.z_index(10)
.child(
h_flex()
.h_full()

View File

@@ -1,10 +1,10 @@
use gpui::{img, prelude::*, AnyElement, SharedUrl};
use gpui::{img, prelude::*, AnyElement, SharedUri};
use smallvec::SmallVec;
use ui::prelude::*;
#[derive(IntoElement)]
pub struct CollabNotification {
avatar_uri: SharedUrl,
avatar_uri: SharedUri,
accept_button: Button,
dismiss_button: Button,
children: SmallVec<[AnyElement; 2]>,
@@ -12,7 +12,7 @@ pub struct CollabNotification {
impl CollabNotification {
pub fn new(
avatar_uri: impl Into<SharedUrl>,
avatar_uri: impl Into<SharedUri>,
accept_button: Button,
dismiss_button: Button,
) -> Self {

View File

@@ -5,7 +5,6 @@ edition = "2021"
publish = false
license = "Apache-2.0"
[lib]
path = "src/collections.rs"
doctest = false

View File

@@ -11,13 +11,4 @@ pub type HashMap<K, V> = std::collections::HashMap<K, V>;
pub type HashSet<T> = std::collections::HashSet<T>;
pub use rustc_hash::{FxHashMap, FxHashSet};
use std::any::TypeId;
pub use std::collections::*;
// NEW TYPES
#[derive(Default)]
pub struct CommandPaletteFilter {
pub hidden_namespaces: HashSet<&'static str>,
pub hidden_action_types: HashSet<TypeId>,
}

View File

@@ -14,6 +14,6 @@ path = "src/color.rs"
doctest = true
[dependencies]
story = { path = "../story", optional = true }
itertools = { version = "0.11.0", optional = true }
palette = "0.7.3"
story = { path = "../story", optional = true }

View File

@@ -5,36 +5,38 @@ edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lib]
path = "src/command_palette.rs"
doctest = false
[dependencies]
anyhow.workspace = true
client = { path = "../client" }
collections = { path = "../collections" }
# HACK: We're only depending on `copilot` here for `CommandPaletteFilter`. See the attached comment on that type.
copilot = { path = "../copilot" }
editor = { path = "../editor" }
fuzzy = { path = "../fuzzy" }
gpui = { path = "../gpui" }
picker = { path = "../picker" }
project = { path = "../project" }
release_channel = { path = "../release_channel" }
serde.workspace = true
settings = { path = "../settings" }
theme = { path = "../theme" }
ui = { path = "../ui" }
util = { path = "../util" }
workspace = { path = "../workspace" }
zed_actions = { path = "../zed_actions" }
anyhow.workspace = true
serde.workspace = true
[dev-dependencies]
gpui = { path = "../gpui", features = ["test-support"] }
ctor.workspace = true
editor = { path = "../editor", features = ["test-support"] }
language = { path = "../language", features = ["test-support"] }
project = { path = "../project", features = ["test-support"] }
menu = { path = "../menu" }
env_logger.workspace = true
go_to_line = { path = "../go_to_line" }
gpui = { path = "../gpui", features = ["test-support"] }
language = { path = "../language", features = ["test-support"] }
menu = { path = "../menu" }
project = { path = "../project", features = ["test-support"] }
serde_json.workspace = true
workspace = { path = "../workspace", features = ["test-support"] }
ctor.workspace = true
env_logger.workspace = true

View File

@@ -4,21 +4,20 @@ use std::{
};
use client::telemetry::Telemetry;
use collections::{CommandPaletteFilter, HashMap};
use collections::HashMap;
use copilot::CommandPaletteFilter;
use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{
actions, Action, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView,
actions, Action, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Global,
ParentElement, Render, Styled, View, ViewContext, VisualContext, WeakView,
};
use picker::{Picker, PickerDelegate};
use release_channel::{parse_zed_link, ReleaseChannel};
use ui::{h_flex, prelude::*, v_flex, HighlightedLabel, KeyBinding, ListItem, ListItemSpacing};
use util::{
channel::{parse_zed_link, ReleaseChannel, RELEASE_CHANNEL},
ResultExt,
};
use util::ResultExt;
use workspace::{ModalView, Workspace};
use zed_actions::OpenZedURL;
use zed_actions::OpenZedUrl;
actions!(command_palette, [Toggle]);
@@ -100,8 +99,11 @@ impl Render for CommandPalette {
}
}
pub type CommandPaletteInterceptor =
Box<dyn Fn(&str, &AppContext) -> Option<CommandInterceptResult>>;
pub struct CommandPaletteInterceptor(
pub Box<dyn Fn(&str, &AppContext) -> Option<CommandInterceptResult>>,
);
impl Global for CommandPaletteInterceptor {}
pub struct CommandInterceptResult {
pub action: Box<dyn Action>,
@@ -135,10 +137,12 @@ impl Clone for Command {
/// Hit count for each command in the palette.
/// We only account for commands triggered directly via command palette and not by e.g. keystrokes because
/// if an user already knows a keystroke for a command, they are unlikely to use a command palette to look for it.
/// if a user already knows a keystroke for a command, they are unlikely to use a command palette to look for it.
#[derive(Default)]
struct HitCounts(HashMap<String, usize>);
impl Global for HitCounts {}
impl CommandPaletteDelegate {
fn new(
command_palette: WeakView<CommandPalette>,
@@ -229,14 +233,17 @@ impl PickerDelegate for CommandPaletteDelegate {
let mut intercept_result = cx
.try_read_global(|interceptor: &CommandPaletteInterceptor, cx| {
(interceptor)(&query, cx)
(interceptor.0)(&query, cx)
})
.flatten();
if *RELEASE_CHANNEL == ReleaseChannel::Dev {
let release_channel = cx
.update(|cx| ReleaseChannel::try_global(cx))
.ok()
.flatten();
if release_channel == Some(ReleaseChannel::Dev) {
if parse_zed_link(&query).is_some() {
intercept_result = Some(CommandInterceptResult {
action: OpenZedURL { url: query.clone() }.boxed_clone(),
action: OpenZedUrl { url: query.clone() }.boxed_clone(),
string: query.clone(),
positions: vec![],
})

View File

@@ -5,7 +5,6 @@ edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lib]
path = "src/copilot.rs"
doctest = false
@@ -21,24 +20,23 @@ test-support = [
]
[dependencies]
collections = { path = "../collections" }
# context_menu = { path = "../context_menu" }
gpui = { path = "../gpui" }
language = { path = "../language" }
settings = { path = "../settings" }
theme = { path = "../theme" }
lsp = { path = "../lsp" }
node_runtime = { path = "../node_runtime"}
util = { path = "../util" }
anyhow.workspace = true
async-compression.workspace = true
async-tar = "0.4.2"
anyhow.workspace = true
collections = { path = "../collections" }
futures.workspace = true
gpui = { path = "../gpui" }
language = { path = "../language" }
log.workspace = true
lsp = { path = "../lsp" }
node_runtime = { path = "../node_runtime" }
parking_lot.workspace = true
serde.workspace = true
serde_derive.workspace = true
settings = { path = "../settings" }
smol.workspace = true
futures.workspace = true
parking_lot.workspace = true
theme = { path = "../theme" }
util = { path = "../util" }
[dev-dependencies]
clock = { path = "../clock" }

View File

@@ -5,7 +5,7 @@ use async_tar::Archive;
use collections::{HashMap, HashSet};
use futures::{channel::oneshot, future::Shared, Future, FutureExt, TryFutureExt};
use gpui::{
actions, AppContext, AsyncAppContext, Context, Entity, EntityId, EventEmitter, Model,
actions, AppContext, AsyncAppContext, Context, Entity, EntityId, EventEmitter, Global, Model,
ModelContext, Task, WeakModel,
};
use language::{
@@ -32,6 +32,17 @@ use util::{
ResultExt,
};
// HACK: This type is only defined in `copilot` since it is the earliest ancestor
// of the crates that use it.
//
// This is not great. Let's find a better place for it to live.
#[derive(Default)]
pub struct CommandPaletteFilter {
pub hidden_namespaces: HashSet<&'static str>,
pub hidden_action_types: HashSet<TypeId>,
}
impl Global for CommandPaletteFilter {}
actions!(
copilot,
[
@@ -54,7 +65,7 @@ pub fn init(
let node_runtime = node_runtime.clone();
move |cx| Copilot::start(new_server_id, http, node_runtime, cx)
});
cx.set_global(copilot.clone());
Copilot::set_global(copilot.clone(), cx);
cx.observe(&copilot, |handle, cx| {
let copilot_action_types = [
TypeId::of::<Suggest>(),
@@ -65,7 +76,7 @@ pub fn init(
let copilot_auth_action_types = [TypeId::of::<SignOut>()];
let copilot_no_auth_action_types = [TypeId::of::<SignIn>()];
let status = handle.read(cx).status();
let filter = cx.default_global::<collections::CommandPaletteFilter>();
let filter = cx.default_global::<CommandPaletteFilter>();
match status {
Status::Disabled => {
@@ -307,9 +318,18 @@ pub enum Event {
impl EventEmitter<Event> for Copilot {}
struct GlobalCopilot(Model<Copilot>);
impl Global for GlobalCopilot {}
impl Copilot {
pub fn global(cx: &AppContext) -> Option<Model<Self>> {
cx.try_global::<Model<Self>>().map(|model| model.clone())
cx.try_global::<GlobalCopilot>()
.map(|model| model.0.clone())
}
pub fn set_global(copilot: Model<Self>, cx: &mut AppContext) {
cx.set_global(GlobalCopilot(copilot));
}
fn start(
@@ -964,7 +984,7 @@ async fn get_copilot_lsp(http: Arc<dyn HttpClient>) -> anyhow::Result<PathBuf> {
let server_path = version_dir.join(SERVER_PATH);
if fs::metadata(&server_path).await.is_err() {
// Copilot LSP looks for this dist dir specifcially, so lets add it in.
// Copilot LSP looks for this dist dir specifically, so lets add it in.
let dist_dir = version_dir.join("dist");
fs::create_dir_all(dist_dir.as_path()).await?;
@@ -1023,12 +1043,15 @@ async fn get_copilot_lsp(http: Arc<dyn HttpClient>) -> anyhow::Result<PathBuf> {
mod tests {
use super::*;
use gpui::TestAppContext;
use language::BufferId;
#[gpui::test(iterations = 10)]
async fn test_buffer_management(cx: &mut TestAppContext) {
let (copilot, mut lsp) = Copilot::fake(cx);
let buffer_1 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "Hello"));
let buffer_1 = cx.new_model(|cx| {
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "Hello")
});
let buffer_1_uri: lsp::Url = format!("buffer://{}", buffer_1.entity_id().as_u64())
.parse()
.unwrap();
@@ -1046,7 +1069,13 @@ mod tests {
}
);
let buffer_2 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "Goodbye"));
let buffer_2 = cx.new_model(|cx| {
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
"Goodbye",
)
});
let buffer_2_uri: lsp::Url = format!("buffer://{}", buffer_2.entity_id().as_u64())
.parse()
.unwrap();
@@ -1222,6 +1251,10 @@ mod tests {
fn worktree_id(&self) -> usize {
0
}
fn is_private(&self) -> bool {
false
}
}
impl language::LocalFile for File {
@@ -1235,7 +1268,7 @@ mod tests {
fn buffer_reloaded(
&self,
_: u64,
_: BufferId,
_: &clock::Global,
_: language::RopeFingerprint,
_: language::LineEnding,

View File

@@ -5,26 +5,25 @@ edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lib]
path = "src/copilot_ui.rs"
doctest = false
[dependencies]
anyhow.workspace = true
copilot = { path = "../copilot" }
editor = { path = "../editor" }
fs = { path = "../fs" }
zed_actions = { path = "../zed_actions"}
futures.workspace = true
gpui = { path = "../gpui" }
language = { path = "../language" }
settings = { path = "../settings" }
smol.workspace = true
theme = { path = "../theme" }
ui = { path = "../ui" }
util = { path = "../util" }
workspace = {path = "../workspace" }
anyhow.workspace = true
smol.workspace = true
futures.workspace = true
workspace = { path = "../workspace" }
zed_actions = { path = "../zed_actions" }
[dev-dependencies]
editor = { path = "../editor", features = ["test-support"] }

View File

@@ -225,8 +225,9 @@ impl CopilotButton {
let file = snapshot.file_at(suggestion_anchor).cloned();
self.editor_enabled = Some(
all_language_settings(self.file.as_ref(), cx)
.copilot_enabled(language, file.as_ref().map(|file| file.path().as_ref())),
file.as_ref().map(|file| !file.is_private()).unwrap_or(true)
&& all_language_settings(self.file.as_ref(), cx)
.copilot_enabled(language, file.as_ref().map(|file| file.path().as_ref())),
);
self.language = language.cloned();
self.file = file;

View File

@@ -5,7 +5,6 @@ edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lib]
path = "src/db.rs"
doctest = false
@@ -14,22 +13,23 @@ doctest = false
test-support = []
[dependencies]
anyhow.workspace = true
async-trait.workspace = true
collections = { path = "../collections" }
gpui = { path = "../gpui" }
sqlez = { path = "../sqlez" }
sqlez_macros = { path = "../sqlez_macros" }
util = { path = "../util" }
anyhow.workspace = true
indoc.workspace = true
async-trait.workspace = true
lazy_static.workspace = true
log.workspace = true
parking_lot.workspace = true
release_channel = { path = "../release_channel" }
serde.workspace = true
serde_derive.workspace = true
smol.workspace = true
sqlez = { path = "../sqlez" }
sqlez_macros = { path = "../sqlez_macros" }
util = { path = "../util" }
[dev-dependencies]
gpui = { path = "../gpui", features = ["test-support"] }
env_logger.workspace = true
gpui = { path = "../gpui", features = ["test-support"] }
tempfile.workspace = true

View File

@@ -10,16 +10,16 @@ pub use lazy_static;
pub use smol;
pub use sqlez;
pub use sqlez_macros;
pub use util::channel::{RELEASE_CHANNEL, RELEASE_CHANNEL_NAME};
pub use util::paths::DB_DIR;
use release_channel::ReleaseChannel;
pub use release_channel::RELEASE_CHANNEL;
use sqlez::domain::Migrator;
use sqlez::thread_safe_connection::ThreadSafeConnection;
use sqlez_macros::sql;
use std::future::Future;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, Ordering};
use util::channel::ReleaseChannel;
use util::{async_maybe, ResultExt};
const CONNECTION_INITIALIZE_QUERY: &'static str = sql!(
@@ -223,7 +223,7 @@ mod tests {
.prefix("DbTests")
.tempdir()
.unwrap();
let _bad_db = open_db::<BadDB>(tempdir.path(), &util::channel::ReleaseChannel::Dev).await;
let _bad_db = open_db::<BadDB>(tempdir.path(), &release_channel::ReleaseChannel::Dev).await;
}
/// Test that DB exists but corrupted (causing recreate)
@@ -261,11 +261,12 @@ mod tests {
.unwrap();
{
let corrupt_db =
open_db::<CorruptedDB>(tempdir.path(), &util::channel::ReleaseChannel::Dev).await;
open_db::<CorruptedDB>(tempdir.path(), &release_channel::ReleaseChannel::Dev).await;
assert!(corrupt_db.persistent());
}
let good_db = open_db::<GoodDB>(tempdir.path(), &util::channel::ReleaseChannel::Dev).await;
let good_db =
open_db::<GoodDB>(tempdir.path(), &release_channel::ReleaseChannel::Dev).await;
assert!(
good_db.select_row::<usize>("SELECT * FROM test2").unwrap()()
.unwrap()
@@ -309,7 +310,7 @@ mod tests {
{
// Setup the bad database
let corrupt_db =
open_db::<CorruptedDB>(tempdir.path(), &util::channel::ReleaseChannel::Dev).await;
open_db::<CorruptedDB>(tempdir.path(), &release_channel::ReleaseChannel::Dev).await;
assert!(corrupt_db.persistent());
}
@@ -320,7 +321,7 @@ mod tests {
let guard = thread::spawn(move || {
let good_db = smol::block_on(open_db::<GoodDB>(
tmp_path.as_path(),
&util::channel::ReleaseChannel::Dev,
&release_channel::ReleaseChannel::Dev,
));
assert!(
good_db.select_row::<usize>("SELECT * FROM test2").unwrap()()

View File

@@ -5,41 +5,38 @@ edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lib]
path = "src/diagnostics.rs"
doctest = false
[dependencies]
anyhow.workspace = true
collections = { path = "../collections" }
editor = { path = "../editor" }
gpui = { path = "../gpui" }
ui = { path = "../ui" }
language = { path = "../language" }
lsp = { path = "../lsp" }
project = { path = "../project" }
settings = { path = "../settings" }
theme = { path = "../theme" }
util = { path = "../util" }
workspace = {path = "../workspace" }
log.workspace = true
anyhow.workspace = true
futures.workspace = true
gpui = { path = "../gpui" }
language = { path = "../language" }
log.workspace = true
lsp = { path = "../lsp" }
postage.workspace = true
project = { path = "../project" }
schemars.workspace = true
serde.workspace = true
serde_derive.workspace = true
settings = { path = "../settings" }
smallvec.workspace = true
postage.workspace = true
theme = { path = "../theme" }
ui = { path = "../ui" }
util = { path = "../util" }
workspace = { path = "../workspace" }
[dev-dependencies]
client = { path = "../client", features = ["test-support"] }
editor = { path = "../editor", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
language = { path = "../language", features = ["test-support"] }
lsp = { path = "../lsp", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
workspace = {path = "../workspace", features = ["test-support"] }
theme = { path = "../theme", features = ["test-support"] }
serde_json.workspace = true
theme = { path = "../theme", features = ["test-support"] }
unindent.workspace = true
workspace = { path = "../workspace", features = ["test-support"] }

View File

@@ -5,7 +5,6 @@ edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lib]
path = "src/editor.rs"
doctest = false
@@ -25,71 +24,68 @@ test-support = [
]
[dependencies]
aho-corasick = "1.1"
anyhow.workspace = true
client = { path = "../client" }
clock = { path = "../clock" }
collections = { path = "../collections" }
convert_case = "0.6.0"
copilot = { path = "../copilot" }
db = { path = "../db" }
collections = { path = "../collections" }
# context_menu = { path = "../context_menu" }
futures.workspace = true
fuzzy = { path = "../fuzzy" }
git = { path = "../git" }
gpui = { path = "../gpui" }
language = { path = "../language" }
lsp = { path = "../lsp" }
multi_buffer = { path = "../multi_buffer" }
project = { path = "../project" }
rpc = { path = "../rpc" }
rich_text = { path = "../rich_text" }
settings = { path = "../settings" }
snippet = { path = "../snippet" }
sum_tree = { path = "../sum_tree" }
text = { path = "../text" }
theme = { path = "../theme" }
ui = { path = "../ui" }
util = { path = "../util" }
sqlez = { path = "../sqlez" }
workspace = { path = "../workspace" }
aho-corasick = "1.1"
anyhow.workspace = true
convert_case = "0.6.0"
futures.workspace = true
indoc = "1.0.4"
itertools = "0.10"
language = { path = "../language" }
lazy_static.workspace = true
log.workspace = true
lsp = { path = "../lsp" }
multi_buffer = { path = "../multi_buffer" }
ordered-float.workspace = true
parking_lot.workspace = true
postage.workspace = true
project = { path = "../project" }
rand.workspace = true
rich_text = { path = "../rich_text" }
rpc = { path = "../rpc" }
schemars.workspace = true
serde.workspace = true
serde_json.workspace = true
serde_derive.workspace = true
serde_json.workspace = true
settings = { path = "../settings" }
smallvec.workspace = true
smol.workspace = true
tree-sitter-rust = { workspace = true, optional = true }
snippet = { path = "../snippet" }
sqlez = { path = "../sqlez" }
sum_tree = { path = "../sum_tree" }
text = { path = "../text" }
theme = { path = "../theme" }
tree-sitter-html = { workspace = true, optional = true }
tree-sitter-rust = { workspace = true, optional = true }
tree-sitter-typescript = { workspace = true, optional = true }
ui = { path = "../ui" }
url.workspace = true
util = { path = "../util" }
workspace = { path = "../workspace" }
[dev-dependencies]
copilot = { path = "../copilot", features = ["test-support"] }
text = { path = "../text", features = ["test-support"] }
language = { path = "../language", features = ["test-support"] }
lsp = { path = "../lsp", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
util = { path = "../util", features = ["test-support"] }
project = { path = "../project", features = ["test-support"] }
settings = { path = "../settings", features = ["test-support"] }
workspace = { path = "../workspace", features = ["test-support"] }
multi_buffer = { path = "../multi_buffer", features = ["test-support"] }
ctor.workspace = true
env_logger.workspace = true
gpui = { path = "../gpui", features = ["test-support"] }
language = { path = "../language", features = ["test-support"] }
lsp = { path = "../lsp", features = ["test-support"] }
multi_buffer = { path = "../multi_buffer", features = ["test-support"] }
project = { path = "../project", features = ["test-support"] }
rand.workspace = true
unindent.workspace = true
tree-sitter.workspace = true
tree-sitter-rust.workspace = true
settings = { path = "../settings", features = ["test-support"] }
text = { path = "../text", features = ["test-support"] }
tree-sitter-html.workspace = true
tree-sitter-rust.workspace = true
tree-sitter-typescript.workspace = true
tree-sitter.workspace = true
unindent.workspace = true
util = { path = "../util", features = ["test-support"] }
workspace = { path = "../workspace", features = ["test-support"] }

View File

@@ -110,6 +110,7 @@ gpui::actions!(
Copy,
CopyHighlightJson,
CopyPath,
CopyPermalinkToLine,
CopyRelativePath,
Cut,
CutToEndOfLine,

View File

@@ -0,0 +1,49 @@
use std::time::Duration;
use futures::{channel::oneshot, FutureExt};
use gpui::{Task, ViewContext};
use crate::Editor;
pub struct DebouncedDelay {
task: Option<Task<()>>,
cancel_channel: Option<oneshot::Sender<()>>,
}
impl DebouncedDelay {
pub fn new() -> DebouncedDelay {
DebouncedDelay {
task: None,
cancel_channel: None,
}
}
pub fn fire_new<F>(&mut self, delay: Duration, cx: &mut ViewContext<Editor>, func: F)
where
F: 'static + Send + FnOnce(&mut Editor, &mut ViewContext<Editor>) -> Task<()>,
{
if let Some(channel) = self.cancel_channel.take() {
_ = channel.send(());
}
let (sender, mut receiver) = oneshot::channel::<()>();
self.cancel_channel = Some(sender);
let previous_task = self.task.take();
self.task = Some(cx.spawn(move |model, mut cx| async move {
let mut timer = cx.background_executor().timer(delay).fuse();
if let Some(previous_task) = previous_task {
previous_task.await;
}
futures::select_biased! {
_ = receiver => return,
_ = timer => {}
}
if let Ok(task) = model.update(&mut cx, |project, cx| (func)(project, cx)) {
task.await;
}
}));
}
}

View File

@@ -586,6 +586,8 @@ impl DisplaySnapshot {
text_system,
editor_style,
rem_size,
anchor: _,
visible_rows: _,
}: &TextLayoutDetails,
) -> Arc<LineLayout> {
let mut runs = Vec::new();
@@ -1007,6 +1009,7 @@ pub mod tests {
use settings::SettingsStore;
use smol::stream::StreamExt;
use std::{env, sync::Arc};
use text::BufferId;
use theme::{LoadThemes, SyntaxTheme};
use util::test::{marked_text_ranges, sample_text};
use Bias::*;
@@ -1467,7 +1470,8 @@ pub mod tests {
cx.update(|cx| init_test(cx, |s| s.defaults.tab_size = Some(2.try_into().unwrap())));
let buffer = cx.new_model(|cx| {
Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
.with_language(language, cx)
});
cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
@@ -1553,7 +1557,8 @@ pub mod tests {
cx.update(|cx| init_test(cx, |_| {}));
let buffer = cx.new_model(|cx| {
Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
.with_language(language, cx)
});
cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
@@ -1620,7 +1625,8 @@ pub mod tests {
let (text, highlighted_ranges) = marked_text_ranges(r#"constˇ «a»: B = "c «d»""#, false);
let buffer = cx.new_model(|cx| {
Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
.with_language(language, cx)
});
cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;

View File

@@ -19,6 +19,7 @@ mod editor_settings;
mod element;
mod inlay_hint_cache;
mod debounced_delay;
mod git;
mod highlight_matching_bracket;
mod hover_popover;
@@ -45,6 +46,7 @@ use clock::ReplicaId;
use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
use convert_case::{Case, Casing};
use copilot::Copilot;
use debounced_delay::DebouncedDelay;
pub use display_map::DisplayPoint;
use display_map::*;
pub use editor_settings::EditorSettings;
@@ -85,7 +87,7 @@ pub use multi_buffer::{
ToPoint,
};
use ordered_float::OrderedFloat;
use parking_lot::RwLock;
use parking_lot::{Mutex, RwLock};
use project::{FormatTrigger, Location, Project, ProjectPath, ProjectTransaction};
use rand::prelude::*;
use rpc::proto::*;
@@ -104,12 +106,11 @@ use std::{
ops::{ControlFlow, Deref, DerefMut, Range, RangeInclusive},
path::Path,
sync::Arc,
sync::Weak,
time::{Duration, Instant},
};
pub use sum_tree::Bias;
use sum_tree::TreeMap;
use text::{OffsetUtf16, Rope};
use text::{BufferId, OffsetUtf16, Rope};
use theme::{
observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
ThemeColors, ThemeSettings,
@@ -118,7 +119,7 @@ use ui::{
h_flex, prelude::*, ButtonSize, ButtonStyle, IconButton, IconName, IconSize, ListItem, Popover,
Tooltip,
};
use util::{post_inc, RangeExt, ResultExt, TryFutureExt};
use util::{maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
use workspace::{searchable::SearchEvent, ItemNavHistory, Pane, SplitDirection, ViewId, Workspace};
const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
@@ -241,7 +242,7 @@ pub fn init(cx: &mut AppContext) {
.detach();
cx.on_action(move |_: &workspace::NewFile, cx| {
let app_state = cx.global::<Weak<workspace::AppState>>();
let app_state = workspace::AppState::global(cx);
if let Some(app_state) = app_state.upgrade() {
workspace::open_new(&app_state, cx, |workspace, cx| {
Editor::new_file(workspace, &Default::default(), cx)
@@ -250,7 +251,7 @@ pub fn init(cx: &mut AppContext) {
}
});
cx.on_action(move |_: &workspace::NewWindow, cx| {
let app_state = cx.global::<Weak<workspace::AppState>>();
let app_state = workspace::AppState::global(cx);
if let Some(app_state) = app_state.upgrade() {
workspace::open_new(&app_state, cx, |workspace, cx| {
Editor::new_file(workspace, &Default::default(), cx)
@@ -384,6 +385,7 @@ pub struct Editor {
mouse_context_menu: Option<MouseContextMenu>,
completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
next_completion_id: CompletionId,
completion_documentation_pre_resolve_debounce: DebouncedDelay,
available_code_actions: Option<(Model<Buffer>, Arc<[CodeAction]>)>,
code_actions_task: Option<Task<()>>,
document_highlights_task: Option<Task<()>>,
@@ -702,6 +704,7 @@ struct CompletionsMenu {
matches: Arc<[StringMatch]>,
selected_item: usize,
scroll_handle: UniformListScrollHandle,
selected_completion_documentation_resolve_debounce: Arc<Mutex<DebouncedDelay>>,
}
impl CompletionsMenu {
@@ -742,30 +745,31 @@ impl CompletionsMenu {
}
fn pre_resolve_completion_documentation(
&self,
completions: Arc<RwLock<Box<[Completion]>>>,
matches: Arc<[StringMatch]>,
editor: &Editor,
cx: &mut ViewContext<Editor>,
) -> Option<Task<()>> {
) -> Task<()> {
let settings = EditorSettings::get_global(cx);
if !settings.show_completion_documentation {
return None;
return Task::ready(());
}
let Some(provider) = editor.completion_provider.as_ref() else {
return None;
return Task::ready(());
};
let resolve_task = provider.resolve_completions(
self.matches.iter().map(|m| m.candidate_id).collect(),
self.completions.clone(),
matches.iter().map(|m| m.candidate_id).collect(),
completions.clone(),
cx,
);
return Some(cx.spawn(move |this, mut cx| async move {
return cx.spawn(move |this, mut cx| async move {
if let Some(true) = resolve_task.await.log_err() {
this.update(&mut cx, |_, cx| cx.notify()).ok();
}
}));
});
}
fn attempt_resolve_selected_completion_documentation(
@@ -786,12 +790,20 @@ impl CompletionsMenu {
let resolve_task = project.update(cx, |project, cx| {
project.resolve_completions(vec![completion_index], self.completions.clone(), cx)
});
cx.spawn(move |this, mut cx| async move {
if let Some(true) = resolve_task.await.log_err() {
this.update(&mut cx, |_, cx| cx.notify()).ok();
}
})
.detach();
let delay_ms =
EditorSettings::get_global(cx).completion_documentation_secondary_query_debounce;
let delay = Duration::from_millis(delay_ms);
self.selected_completion_documentation_resolve_debounce
.lock()
.fire_new(delay, cx, |_, cx| {
cx.spawn(move |this, mut cx| async move {
if let Some(true) = resolve_task.await.log_err() {
this.update(&mut cx, |_, cx| cx.notify()).ok();
}
})
});
}
fn visible(&self) -> bool {
@@ -1289,19 +1301,37 @@ impl InlayHintRefreshReason {
impl Editor {
pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), String::new()));
let buffer = cx.new_model(|cx| {
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
String::new(),
)
});
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
Self::new(EditorMode::SingleLine, buffer, None, cx)
}
pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), String::new()));
let buffer = cx.new_model(|cx| {
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
String::new(),
)
});
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
Self::new(EditorMode::Full, buffer, None, cx)
}
pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), String::new()));
let buffer = cx.new_model(|cx| {
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
String::new(),
)
});
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
Self::new(EditorMode::AutoHeight { max_lines }, buffer, None, cx)
}
@@ -1417,6 +1447,7 @@ impl Editor {
mouse_context_menu: None,
completion_tasks: Default::default(),
next_completion_id: 0,
completion_documentation_pre_resolve_debounce: DebouncedDelay::new(),
next_inlay_id: 0,
available_code_actions: Default::default(),
code_actions_task: Default::default(),
@@ -2279,7 +2310,7 @@ impl Editor {
let mut bracket_pair = None;
let mut is_bracket_pair_start = false;
if !text.is_empty() {
// `text` can be empty when an user is using IME (e.g. Chinese Wubi Simplified)
// `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
// and they are removing the character that triggered IME popup.
for (pair, enabled) in scope.brackets() {
if enabled && pair.close && pair.start.ends_with(text.as_ref()) {
@@ -3034,6 +3065,8 @@ impl Editor {
text_system: cx.text_system().clone(),
editor_style: self.style.clone().unwrap(),
rem_size: cx.rem_size(),
anchor: self.scroll_manager.anchor().anchor,
visible_rows: self.visible_line_count(),
}
}
@@ -3124,7 +3157,7 @@ impl Editor {
let task = cx.spawn(|this, mut cx| {
async move {
let completions = completions.await.log_err();
let (menu, pre_resolve_task) = if let Some(completions) = completions {
let menu = if let Some(completions) = completions {
let mut menu = CompletionsMenu {
id,
initial_position: position,
@@ -3144,23 +3177,40 @@ impl Editor {
matches: Vec::new().into(),
selected_item: 0,
scroll_handle: UniformListScrollHandle::new(),
selected_completion_documentation_resolve_debounce: Arc::new(Mutex::new(
DebouncedDelay::new(),
)),
};
menu.filter(query.as_deref(), cx.background_executor().clone())
.await;
if menu.matches.is_empty() {
(None, None)
None
} else {
let pre_resolve_task = this
.update(&mut cx, |editor, cx| {
menu.pre_resolve_completion_documentation(editor, cx)
})
.ok()
.flatten();
(Some(menu), pre_resolve_task)
this.update(&mut cx, |editor, cx| {
let completions = menu.completions.clone();
let matches = menu.matches.clone();
let delay_ms = EditorSettings::get_global(cx)
.completion_documentation_secondary_query_debounce;
let delay = Duration::from_millis(delay_ms);
editor
.completion_documentation_pre_resolve_debounce
.fire_new(delay, cx, |editor, cx| {
CompletionsMenu::pre_resolve_completion_documentation(
completions,
matches,
editor,
cx,
)
});
})
.ok();
Some(menu)
}
} else {
(None, None)
None
};
this.update(&mut cx, |this, cx| {
@@ -3196,10 +3246,6 @@ impl Editor {
}
})?;
if let Some(pre_resolve_task) = pre_resolve_task {
pre_resolve_task.await;
}
Ok::<_, anyhow::Error>(())
}
.log_err()
@@ -8196,6 +8242,43 @@ impl Editor {
}
}
pub fn copy_permalink_to_line(&mut self, _: &CopyPermalinkToLine, cx: &mut ViewContext<Self>) {
use git::permalink::{build_permalink, BuildPermalinkParams};
let permalink = maybe!({
let project = self.project.clone()?;
let project = project.read(cx);
let worktree = project.visible_worktrees(cx).next()?;
let mut cwd = worktree.read(cx).abs_path().to_path_buf();
cwd.push(".git");
let repo = project.fs().open_repo(&cwd)?;
let origin_url = repo.lock().remote_url("origin")?;
let sha = repo.lock().head_sha()?;
let buffer = self.buffer().read(cx).as_singleton()?;
let file = buffer.read(cx).file().and_then(|f| f.as_local())?;
let path = file.path().to_str().map(|path| path.to_string())?;
let selections = self.selections.all::<Point>(cx);
let selection = selections.iter().peekable().next();
build_permalink(BuildPermalinkParams {
remote_url: &origin_url,
sha: &sha,
path: &path,
selection: selection.map(|selection| selection.range()),
})
.log_err()
});
if let Some(permalink) = permalink {
cx.write_to_clipboard(ClipboardItem::new(permalink.to_string()));
}
}
pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
self.highlighted_rows = rows;
}
@@ -8410,6 +8493,31 @@ impl Editor {
results
}
/// Get the text ranges corresponding to the redaction query
pub fn redacted_ranges(
&self,
search_range: Range<Anchor>,
display_snapshot: &DisplaySnapshot,
cx: &mut ViewContext<Self>,
) -> Vec<Range<DisplayPoint>> {
display_snapshot
.buffer_snapshot
.redacted_ranges(search_range, |file| {
if let Some(file) = file {
file.is_private()
&& EditorSettings::get(Some((file.worktree_id(), file.path())), cx)
.redact_private_values
} else {
false
}
})
.map(|range| {
range.start.to_display_point(display_snapshot)
..range.end.to_display_point(display_snapshot)
})
.collect()
}
pub fn highlight_text<T: 'static>(
&mut self,
ranges: Vec<Range<Anchor>>,

View File

@@ -8,10 +8,12 @@ pub struct EditorSettings {
pub hover_popover_enabled: bool,
pub show_completions_on_input: bool,
pub show_completion_documentation: bool,
pub completion_documentation_secondary_query_debounce: u64,
pub use_on_type_format: bool,
pub scrollbar: Scrollbar,
pub relative_line_numbers: bool,
pub seed_search_query_from_cursor: SeedQuerySetting,
pub redact_private_values: bool,
}
/// When to populate a new search's query based on the text under the cursor.
@@ -31,6 +33,7 @@ pub struct Scrollbar {
pub show: ShowScrollbar,
pub git_diff: bool,
pub selections: bool,
pub symbols_selections: bool,
}
/// When to show the scrollbar in the editor.
@@ -71,6 +74,11 @@ pub struct EditorSettingsContent {
///
/// Default: true
pub show_completion_documentation: Option<bool>,
/// The debounce delay before re-querying the language server for completion
/// documentation when not included in original completion list.
///
/// Default: 300 ms
pub completion_documentation_secondary_query_debounce: Option<u64>,
/// Whether to use additional LSP queries to format (and amend) the code after
/// every "trigger" symbol input, defined by LSP server capabilities.
///
@@ -86,6 +94,13 @@ pub struct EditorSettingsContent {
///
/// Default: always
pub seed_search_query_from_cursor: Option<SeedQuerySetting>,
/// Hide the values of variables in `private` files, as defined by the
/// private_files setting. This only changes the visual representation,
/// the values are still present in the file and can be selected / copied / pasted
///
/// Default: false
pub redact_private_values: Option<bool>,
}
/// Scrollbar related settings
@@ -103,6 +118,10 @@ pub struct ScrollbarContent {
///
/// Default: true
pub selections: Option<bool>,
/// Whether to show symbols highlighted markers in the scrollbar.
///
/// Default: true
pub symbols_selections: Option<bool>,
}
impl Settings for EditorSettings {

View File

@@ -39,7 +39,8 @@ fn test_edit_events(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let buffer = cx.new_model(|cx| {
let mut buffer = language::Buffer::new(0, cx.entity_id().as_u64(), "123456");
let mut buffer =
language::Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "123456");
buffer.set_group_interval(Duration::from_secs(1));
buffer
});
@@ -154,7 +155,9 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let mut now = Instant::now();
let buffer = cx.new_model(|cx| language::Buffer::new(0, cx.entity_id().as_u64(), "123456"));
let buffer = cx.new_model(|cx| {
language::Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "123456")
});
let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval());
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
@@ -225,7 +228,8 @@ fn test_ime_composition(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let buffer = cx.new_model(|cx| {
let mut buffer = language::Buffer::new(0, cx.entity_id().as_u64(), "abcde");
let mut buffer =
language::Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "abcde");
// Ensure automatic grouping doesn't occur.
buffer.set_group_interval(Duration::ZERO);
buffer
@@ -629,7 +633,7 @@ async fn test_navigation_history(cx: &mut TestAppContext) {
// Ensure we don't panic when navigation data contains invalid anchors *and* points.
let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
invalid_anchor.text_anchor.buffer_id = Some(999);
invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
let invalid_point = Point::new(9999, 0);
editor.navigate(
Box::new(NavigationData {
@@ -2342,11 +2346,20 @@ fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
));
let toml_buffer = cx.new_model(|cx| {
Buffer::new(0, cx.entity_id().as_u64(), "a = 1\nb = 2\n").with_language(toml_language, cx)
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
"a = 1\nb = 2\n",
)
.with_language(toml_language, cx)
});
let rust_buffer = cx.new_model(|cx| {
Buffer::new(0, cx.entity_id().as_u64(), "const c: usize = 3;\n")
.with_language(rust_language, cx)
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
"const c: usize = 3;\n",
)
.with_language(rust_language, cx)
});
let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0, ReadWrite);
@@ -3984,8 +3997,10 @@ async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
"#
.unindent();
let buffer = cx
.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx));
let buffer = cx.new_model(|cx| {
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
.with_language(language, cx)
});
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
@@ -4149,8 +4164,10 @@ async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
let text = "fn a() {}";
let buffer = cx
.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx));
let buffer = cx.new_model(|cx| {
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
.with_language(language, cx)
});
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
editor
@@ -4713,8 +4730,10 @@ async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
"#
.unindent();
let buffer = cx
.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx));
let buffer = cx.new_model(|cx| {
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
.with_language(language, cx)
});
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
@@ -4862,8 +4881,10 @@ async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
"#
.unindent();
let buffer = cx
.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx));
let buffer = cx.new_model(|cx| {
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
.with_language(language, cx)
});
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
editor
@@ -6095,7 +6116,13 @@ async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(3, 4, 'a')));
let buffer = cx.new_model(|cx| {
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
sample_text(3, 4, 'a'),
)
});
let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0, ReadWrite);
multibuffer.push_excerpts(
@@ -6179,7 +6206,13 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
primary: None,
}
});
let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), initial_text));
let buffer = cx.new_model(|cx| {
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
initial_text,
)
});
let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0, ReadWrite);
multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
@@ -6237,7 +6270,13 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
fn test_refresh_selections(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(3, 4, 'a')));
let buffer = cx.new_model(|cx| {
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
sample_text(3, 4, 'a'),
)
});
let mut excerpt1_id = None;
let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0, ReadWrite);
@@ -6322,7 +6361,13 @@ fn test_refresh_selections(cx: &mut TestAppContext) {
fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(3, 4, 'a')));
let buffer = cx.new_model(|cx| {
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
sample_text(3, 4, 'a'),
)
});
let mut excerpt1_id = None;
let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0, ReadWrite);
@@ -6417,8 +6462,10 @@ async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
"{{} }\n", //
);
let buffer = cx
.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx));
let buffer = cx.new_model(|cx| {
Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
.with_language(language, cx)
});
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
@@ -7179,7 +7226,7 @@ async fn test_copilot(executor: BackgroundExecutor, cx: &mut gpui::TestAppContex
init_test(cx, |_| {});
let (copilot, copilot_lsp) = Copilot::fake(cx);
_ = cx.update(|cx| cx.set_global(copilot));
_ = cx.update(|cx| Copilot::set_global(copilot, cx));
let mut cx = EditorLspTestContext::new_rust(
lsp::ServerCapabilities {
completion_provider: Some(lsp::CompletionOptions {
@@ -7432,7 +7479,7 @@ async fn test_copilot_completion_invalidation(
init_test(cx, |_| {});
let (copilot, copilot_lsp) = Copilot::fake(cx);
_ = cx.update(|cx| cx.set_global(copilot));
_ = cx.update(|cx| Copilot::set_global(copilot, cx));
let mut cx = EditorLspTestContext::new_rust(
lsp::ServerCapabilities {
completion_provider: Some(lsp::CompletionOptions {
@@ -7496,10 +7543,22 @@ async fn test_copilot_multibuffer(executor: BackgroundExecutor, cx: &mut gpui::T
init_test(cx, |_| {});
let (copilot, copilot_lsp) = Copilot::fake(cx);
_ = cx.update(|cx| cx.set_global(copilot));
_ = cx.update(|cx| Copilot::set_global(copilot, cx));
let buffer_1 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "a = 1\nb = 2\n"));
let buffer_2 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "c = 3\nd = 4\n"));
let buffer_1 = cx.new_model(|cx| {
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
"a = 1\nb = 2\n",
)
});
let buffer_2 = cx.new_model(|cx| {
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
"c = 3\nd = 4\n",
)
});
let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0, ReadWrite);
multibuffer.push_excerpts(
@@ -7601,7 +7660,7 @@ async fn test_copilot_disabled_globs(executor: BackgroundExecutor, cx: &mut gpui
});
let (copilot, copilot_lsp) = Copilot::fake(cx);
_ = cx.update(|cx| cx.set_global(copilot));
_ = cx.update(|cx| Copilot::set_global(copilot, cx));
let fs = FakeFs::new(cx.executor());
fs.insert_tree(

View File

@@ -16,9 +16,10 @@ use crate::{
},
mouse_context_menu,
scroll::scroll_amount::ScrollAmount,
CursorShape, DisplayPoint, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle,
HalfPageDown, HalfPageUp, HoveredCursor, LineDown, LineUp, OpenExcerpts, PageDown, PageUp,
Point, SelectPhase, Selection, SoftWrap, ToPoint, CURSORS_VISIBLE_FOR, MAX_LINE_LEN,
CursorShape, DisplayPoint, DocumentHighlightRead, DocumentHighlightWrite, Editor, EditorMode,
EditorSettings, EditorSnapshot, EditorStyle, HalfPageDown, HalfPageUp, HoveredCursor, LineDown,
LineUp, OpenExcerpts, PageDown, PageUp, Point, SelectPhase, Selection, SoftWrap, ToPoint,
CURSORS_VISIBLE_FOR, MAX_LINE_LEN,
};
use anyhow::Result;
use collections::{BTreeMap, HashMap};
@@ -276,6 +277,7 @@ impl EditorElement {
register_action(view, cx, Editor::copy_path);
register_action(view, cx, Editor::copy_relative_path);
register_action(view, cx, Editor::copy_highlight_json);
register_action(view, cx, Editor::copy_permalink_to_line);
register_action(view, cx, |editor, action, cx| {
if let Some(task) = editor.format(action, cx) {
task.detach_and_log_err(cx);
@@ -1151,7 +1153,9 @@ impl EditorElement {
)
}
cx.with_z_index(0, |cx| {
cx.with_z_index(0, |cx| self.paint_redactions(text_bounds, &layout, cx));
cx.with_z_index(1, |cx| {
for cursor in cursors {
cursor.paint(content_origin, cx);
}
@@ -1160,6 +1164,32 @@ impl EditorElement {
)
}
fn paint_redactions(
&mut self,
text_bounds: Bounds<Pixels>,
layout: &LayoutState,
cx: &mut ElementContext,
) {
let content_origin = text_bounds.origin + point(layout.gutter_margin, Pixels::ZERO);
let line_end_overshoot = layout.line_end_overshoot();
// A softer than perfect black
let redaction_color = gpui::rgb(0x0e1111);
for range in layout.redacted_ranges.iter() {
self.paint_highlighted_range(
range.clone(),
redaction_color.into(),
Pixels::ZERO,
line_end_overshoot,
layout,
content_origin,
text_bounds,
cx,
);
}
}
fn paint_overlays(
&mut self,
text_bounds: Bounds<Pixels>,
@@ -1366,6 +1396,44 @@ impl EditorElement {
}
}
if layout.is_singleton && scrollbar_settings.symbols_selections {
let selection_ranges = self.editor.read(cx).background_highlights_in_range(
Anchor::min()..Anchor::max(),
&layout.position_map.snapshot,
cx.theme().colors(),
);
for hunk in selection_ranges {
let start_display = Point::new(hunk.0.start.row(), 0)
.to_display_point(&layout.position_map.snapshot.display_snapshot);
let end_display = Point::new(hunk.0.end.row(), 0)
.to_display_point(&layout.position_map.snapshot.display_snapshot);
let start_y = y_for_row(start_display.row() as f32);
let mut end_y = if hunk.0.start == hunk.0.end {
y_for_row((end_display.row() + 1) as f32)
} else {
y_for_row((end_display.row()) as f32)
};
if end_y - start_y < px(1.) {
end_y = start_y + px(1.);
}
let bounds = Bounds::from_corners(point(left, start_y), point(right, end_y));
cx.paint_quad(quad(
bounds,
Corners::default(),
cx.theme().status().info,
Edges {
top: Pixels::ZERO,
right: px(1.),
bottom: Pixels::ZERO,
left: px(1.),
},
cx.theme().colors().scrollbar_thumb_border,
));
}
}
if layout.is_singleton && scrollbar_settings.git_diff {
for hunk in layout
.position_map
@@ -1917,6 +1985,8 @@ impl EditorElement {
cx.theme().colors(),
);
let redacted_ranges = editor.redacted_ranges(start_anchor..end_anchor, &snapshot.display_snapshot, cx);
let mut newest_selection_head = None;
if editor.show_local_selections {
@@ -2032,8 +2102,12 @@ impl EditorElement {
||
// Selections
(is_singleton && scrollbar_settings.selections && editor.has_background_highlights::<BufferSearchHighlights>())
||
// Symbols Selections
(is_singleton && scrollbar_settings.symbols_selections && (editor.has_background_highlights::<DocumentHighlightRead>() || editor.has_background_highlights::<DocumentHighlightWrite>()))
||
// Scrollmanager
|| editor.scroll_manager.scrollbars_visible()
editor.scroll_manager.scrollbars_visible()
}
ShowScrollbar::System => editor.scroll_manager.scrollbars_visible(),
ShowScrollbar::Always => true,
@@ -2254,6 +2328,7 @@ impl EditorElement {
active_rows,
highlighted_rows,
highlighted_ranges,
redacted_ranges,
line_numbers,
display_hunks,
blocks,
@@ -3038,6 +3113,7 @@ pub struct LayoutState {
display_hunks: Vec<DisplayDiffHunk>,
blocks: Vec<BlockLayout>,
highlighted_ranges: Vec<(Range<DisplayPoint>, Hsla)>,
redacted_ranges: Vec<Range<DisplayPoint>>,
selections: Vec<(PlayerColor, Vec<SelectionLayout>)>,
scrollbar_row_range: Range<f32>,
show_scrollbars: bool,
@@ -3051,6 +3127,12 @@ pub struct LayoutState {
space_invisible: ShapedLine,
}
impl LayoutState {
fn line_end_overshoot(&self) -> Pixels {
0.15 * self.position_map.line_height
}
}
struct CodeActionsIndicator {
row: u32,
button: IconButton,

View File

@@ -1,3 +1,5 @@
pub mod permalink;
use std::ops::Range;
use git::diff::{DiffHunk, DiffHunkStatus};

View File

@@ -0,0 +1,390 @@
use std::ops::Range;
use anyhow::{anyhow, Result};
use language::Point;
use url::Url;
enum GitHostingProvider {
Github,
Gitlab,
Gitee,
}
impl GitHostingProvider {
fn base_url(&self) -> Url {
let base_url = match self {
Self::Github => "https://github.com",
Self::Gitlab => "https://gitlab.com",
Self::Gitee => "https://gitee.com",
};
Url::parse(&base_url).unwrap()
}
/// Returns the fragment portion of the URL for the selected lines in
/// the representation the [`GitHostingProvider`] expects.
fn line_fragment(&self, selection: &Range<Point>) -> String {
if selection.start.row == selection.end.row {
let line = selection.start.row + 1;
match self {
Self::Github | Self::Gitlab | Self::Gitee => format!("L{}", line),
}
} else {
let start_line = selection.start.row + 1;
let end_line = selection.end.row + 1;
match self {
Self::Github => format!("L{}-L{}", start_line, end_line),
Self::Gitlab => format!("L{}-{}", start_line, end_line),
Self::Gitee => format!("L{}-{}", start_line, end_line),
}
}
}
}
pub struct BuildPermalinkParams<'a> {
pub remote_url: &'a str,
pub sha: &'a str,
pub path: &'a str,
pub selection: Option<Range<Point>>,
}
pub fn build_permalink(params: BuildPermalinkParams) -> Result<Url> {
let BuildPermalinkParams {
remote_url,
sha,
path,
selection,
} = params;
let ParsedGitRemote {
provider,
owner,
repo,
} = parse_git_remote_url(remote_url)
.ok_or_else(|| anyhow!("failed to parse Git remote URL"))?;
let path = match provider {
GitHostingProvider::Github => format!("{owner}/{repo}/blob/{sha}/{path}"),
GitHostingProvider::Gitlab => format!("{owner}/{repo}/-/blob/{sha}/{path}"),
GitHostingProvider::Gitee => format!("{owner}/{repo}/blob/{sha}/{path}"),
};
let line_fragment = selection.map(|selection| provider.line_fragment(&selection));
let mut permalink = provider.base_url().join(&path).unwrap();
permalink.set_fragment(line_fragment.as_deref());
Ok(permalink)
}
struct ParsedGitRemote<'a> {
pub provider: GitHostingProvider,
pub owner: &'a str,
pub repo: &'a str,
}
fn parse_git_remote_url(url: &str) -> Option<ParsedGitRemote> {
if url.starts_with("git@github.com:") || url.starts_with("https://github.com/") {
let repo_with_owner = url
.trim_start_matches("git@github.com:")
.trim_start_matches("https://github.com/")
.trim_end_matches(".git");
let (owner, repo) = repo_with_owner.split_once("/")?;
return Some(ParsedGitRemote {
provider: GitHostingProvider::Github,
owner,
repo,
});
}
if url.starts_with("git@gitlab.com:") || url.starts_with("https://gitlab.com/") {
let repo_with_owner = url
.trim_start_matches("git@gitlab.com:")
.trim_start_matches("https://gitlab.com/")
.trim_end_matches(".git");
let (owner, repo) = repo_with_owner.split_once("/")?;
return Some(ParsedGitRemote {
provider: GitHostingProvider::Gitlab,
owner,
repo,
});
}
if url.starts_with("git@gitee.com:") || url.starts_with("https://gitee.com/") {
let repo_with_owner = url
.trim_start_matches("git@gitee.com:")
.trim_start_matches("https://gitee.com/")
.trim_end_matches(".git");
let (owner, repo) = repo_with_owner.split_once("/")?;
return Some(ParsedGitRemote {
provider: GitHostingProvider::Gitee,
owner,
repo,
});
}
None
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_build_github_permalink_from_ssh_url() {
let permalink = build_permalink(BuildPermalinkParams {
remote_url: "git@github.com:zed-industries/zed.git",
sha: "e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7",
path: "crates/editor/src/git/permalink.rs",
selection: None,
})
.unwrap();
let expected_url = "https://github.com/zed-industries/zed/blob/e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7/crates/editor/src/git/permalink.rs";
assert_eq!(permalink.to_string(), expected_url.to_string())
}
#[test]
fn test_build_github_permalink_from_ssh_url_single_line_selection() {
let permalink = build_permalink(BuildPermalinkParams {
remote_url: "git@github.com:zed-industries/zed.git",
sha: "e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7",
path: "crates/editor/src/git/permalink.rs",
selection: Some(Point::new(6, 1)..Point::new(6, 10)),
})
.unwrap();
let expected_url = "https://github.com/zed-industries/zed/blob/e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7/crates/editor/src/git/permalink.rs#L7";
assert_eq!(permalink.to_string(), expected_url.to_string())
}
#[test]
fn test_build_github_permalink_from_ssh_url_multi_line_selection() {
let permalink = build_permalink(BuildPermalinkParams {
remote_url: "git@github.com:zed-industries/zed.git",
sha: "e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7",
path: "crates/editor/src/git/permalink.rs",
selection: Some(Point::new(23, 1)..Point::new(47, 10)),
})
.unwrap();
let expected_url = "https://github.com/zed-industries/zed/blob/e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7/crates/editor/src/git/permalink.rs#L24-L48";
assert_eq!(permalink.to_string(), expected_url.to_string())
}
#[test]
fn test_build_github_permalink_from_https_url() {
let permalink = build_permalink(BuildPermalinkParams {
remote_url: "https://github.com/zed-industries/zed.git",
sha: "b2efec9824c45fcc90c9a7eb107a50d1772a60aa",
path: "crates/zed/src/main.rs",
selection: None,
})
.unwrap();
let expected_url = "https://github.com/zed-industries/zed/blob/b2efec9824c45fcc90c9a7eb107a50d1772a60aa/crates/zed/src/main.rs";
assert_eq!(permalink.to_string(), expected_url.to_string())
}
#[test]
fn test_build_github_permalink_from_https_url_single_line_selection() {
let permalink = build_permalink(BuildPermalinkParams {
remote_url: "https://github.com/zed-industries/zed.git",
sha: "b2efec9824c45fcc90c9a7eb107a50d1772a60aa",
path: "crates/zed/src/main.rs",
selection: Some(Point::new(6, 1)..Point::new(6, 10)),
})
.unwrap();
let expected_url = "https://github.com/zed-industries/zed/blob/b2efec9824c45fcc90c9a7eb107a50d1772a60aa/crates/zed/src/main.rs#L7";
assert_eq!(permalink.to_string(), expected_url.to_string())
}
#[test]
fn test_build_github_permalink_from_https_url_multi_line_selection() {
let permalink = build_permalink(BuildPermalinkParams {
remote_url: "https://github.com/zed-industries/zed.git",
sha: "b2efec9824c45fcc90c9a7eb107a50d1772a60aa",
path: "crates/zed/src/main.rs",
selection: Some(Point::new(23, 1)..Point::new(47, 10)),
})
.unwrap();
let expected_url = "https://github.com/zed-industries/zed/blob/b2efec9824c45fcc90c9a7eb107a50d1772a60aa/crates/zed/src/main.rs#L24-L48";
assert_eq!(permalink.to_string(), expected_url.to_string())
}
#[test]
fn test_build_gitlab_permalink_from_ssh_url() {
let permalink = build_permalink(BuildPermalinkParams {
remote_url: "git@gitlab.com:zed-industries/zed.git",
sha: "e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7",
path: "crates/editor/src/git/permalink.rs",
selection: None,
})
.unwrap();
let expected_url = "https://gitlab.com/zed-industries/zed/-/blob/e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7/crates/editor/src/git/permalink.rs";
assert_eq!(permalink.to_string(), expected_url.to_string())
}
#[test]
fn test_build_gitlab_permalink_from_ssh_url_single_line_selection() {
let permalink = build_permalink(BuildPermalinkParams {
remote_url: "git@gitlab.com:zed-industries/zed.git",
sha: "e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7",
path: "crates/editor/src/git/permalink.rs",
selection: Some(Point::new(6, 1)..Point::new(6, 10)),
})
.unwrap();
let expected_url = "https://gitlab.com/zed-industries/zed/-/blob/e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7/crates/editor/src/git/permalink.rs#L7";
assert_eq!(permalink.to_string(), expected_url.to_string())
}
#[test]
fn test_build_gitlab_permalink_from_ssh_url_multi_line_selection() {
let permalink = build_permalink(BuildPermalinkParams {
remote_url: "git@gitlab.com:zed-industries/zed.git",
sha: "e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7",
path: "crates/editor/src/git/permalink.rs",
selection: Some(Point::new(23, 1)..Point::new(47, 10)),
})
.unwrap();
let expected_url = "https://gitlab.com/zed-industries/zed/-/blob/e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7/crates/editor/src/git/permalink.rs#L24-48";
assert_eq!(permalink.to_string(), expected_url.to_string())
}
#[test]
fn test_build_gitlab_permalink_from_https_url() {
let permalink = build_permalink(BuildPermalinkParams {
remote_url: "https://gitlab.com/zed-industries/zed.git",
sha: "b2efec9824c45fcc90c9a7eb107a50d1772a60aa",
path: "crates/zed/src/main.rs",
selection: None,
})
.unwrap();
let expected_url = "https://gitlab.com/zed-industries/zed/-/blob/b2efec9824c45fcc90c9a7eb107a50d1772a60aa/crates/zed/src/main.rs";
assert_eq!(permalink.to_string(), expected_url.to_string())
}
#[test]
fn test_build_gitlab_permalink_from_https_url_single_line_selection() {
let permalink = build_permalink(BuildPermalinkParams {
remote_url: "https://gitlab.com/zed-industries/zed.git",
sha: "b2efec9824c45fcc90c9a7eb107a50d1772a60aa",
path: "crates/zed/src/main.rs",
selection: Some(Point::new(6, 1)..Point::new(6, 10)),
})
.unwrap();
let expected_url = "https://gitlab.com/zed-industries/zed/-/blob/b2efec9824c45fcc90c9a7eb107a50d1772a60aa/crates/zed/src/main.rs#L7";
assert_eq!(permalink.to_string(), expected_url.to_string())
}
#[test]
fn test_build_gitlab_permalink_from_https_url_multi_line_selection() {
let permalink = build_permalink(BuildPermalinkParams {
remote_url: "https://gitlab.com/zed-industries/zed.git",
sha: "b2efec9824c45fcc90c9a7eb107a50d1772a60aa",
path: "crates/zed/src/main.rs",
selection: Some(Point::new(23, 1)..Point::new(47, 10)),
})
.unwrap();
let expected_url = "https://gitlab.com/zed-industries/zed/-/blob/b2efec9824c45fcc90c9a7eb107a50d1772a60aa/crates/zed/src/main.rs#L24-48";
assert_eq!(permalink.to_string(), expected_url.to_string())
}
#[test]
fn test_build_gitee_permalink_from_ssh_url() {
let permalink = build_permalink(BuildPermalinkParams {
remote_url: "git@gitee.com:libkitten/zed.git",
sha: "e5fe811d7ad0fc26934edd76f891d20bdc3bb194",
path: "crates/editor/src/git/permalink.rs",
selection: None,
})
.unwrap();
let expected_url = "https://gitee.com/libkitten/zed/blob/e5fe811d7ad0fc26934edd76f891d20bdc3bb194/crates/editor/src/git/permalink.rs";
assert_eq!(permalink.to_string(), expected_url.to_string())
}
#[test]
fn test_build_gitee_permalink_from_ssh_url_single_line_selection() {
let permalink = build_permalink(BuildPermalinkParams {
remote_url: "git@gitee.com:libkitten/zed.git",
sha: "e5fe811d7ad0fc26934edd76f891d20bdc3bb194",
path: "crates/editor/src/git/permalink.rs",
selection: Some(Point::new(6, 1)..Point::new(6, 10)),
})
.unwrap();
let expected_url = "https://gitee.com/libkitten/zed/blob/e5fe811d7ad0fc26934edd76f891d20bdc3bb194/crates/editor/src/git/permalink.rs#L7";
assert_eq!(permalink.to_string(), expected_url.to_string())
}
#[test]
fn test_build_gitee_permalink_from_ssh_url_multi_line_selection() {
let permalink = build_permalink(BuildPermalinkParams {
remote_url: "git@gitee.com:libkitten/zed.git",
sha: "e5fe811d7ad0fc26934edd76f891d20bdc3bb194",
path: "crates/editor/src/git/permalink.rs",
selection: Some(Point::new(23, 1)..Point::new(47, 10)),
})
.unwrap();
let expected_url = "https://gitee.com/libkitten/zed/blob/e5fe811d7ad0fc26934edd76f891d20bdc3bb194/crates/editor/src/git/permalink.rs#L24-48";
assert_eq!(permalink.to_string(), expected_url.to_string())
}
#[test]
fn test_build_gitee_permalink_from_https_url() {
let permalink = build_permalink(BuildPermalinkParams {
remote_url: "https://gitee.com/libkitten/zed.git",
sha: "e5fe811d7ad0fc26934edd76f891d20bdc3bb194",
path: "crates/zed/src/main.rs",
selection: None,
})
.unwrap();
let expected_url = "https://gitee.com/libkitten/zed/blob/e5fe811d7ad0fc26934edd76f891d20bdc3bb194/crates/zed/src/main.rs";
assert_eq!(permalink.to_string(), expected_url.to_string())
}
#[test]
fn test_build_gitee_permalink_from_https_url_single_line_selection() {
let permalink = build_permalink(BuildPermalinkParams {
remote_url: "https://gitee.com/libkitten/zed.git",
sha: "e5fe811d7ad0fc26934edd76f891d20bdc3bb194",
path: "crates/zed/src/main.rs",
selection: Some(Point::new(6, 1)..Point::new(6, 10)),
})
.unwrap();
let expected_url = "https://gitee.com/libkitten/zed/blob/e5fe811d7ad0fc26934edd76f891d20bdc3bb194/crates/zed/src/main.rs#L7";
assert_eq!(permalink.to_string(), expected_url.to_string())
}
#[test]
fn test_build_gitee_permalink_from_https_url_multi_line_selection() {
let permalink = build_permalink(BuildPermalinkParams {
remote_url: "https://gitee.com/libkitten/zed.git",
sha: "e5fe811d7ad0fc26934edd76f891d20bdc3bb194",
path: "crates/zed/src/main.rs",
selection: Some(Point::new(23, 1)..Point::new(47, 10)),
})
.unwrap();
let expected_url = "https://gitee.com/libkitten/zed/blob/e5fe811d7ad0fc26934edd76f891d20bdc3bb194/crates/zed/src/main.rs#L24-48";
assert_eq!(permalink.to_string(), expected_url.to_string())
}
}

View File

@@ -28,7 +28,7 @@ use collections::{hash_map, HashMap, HashSet};
use language::language_settings::InlayHintSettings;
use smol::lock::Semaphore;
use sum_tree::Bias;
use text::{ToOffset, ToPoint};
use text::{BufferId, ToOffset, ToPoint};
use util::post_inc;
pub struct InlayHintCache {
@@ -50,7 +50,7 @@ struct TasksForRanges {
struct CachedExcerptHints {
version: usize,
buffer_version: Global,
buffer_id: u64,
buffer_id: BufferId,
ordered_hints: Vec<InlayId>,
hints_by_id: HashMap<InlayId, InlayHint>,
}
@@ -93,7 +93,7 @@ struct ExcerptHintsUpdate {
#[derive(Debug, Clone, Copy)]
struct ExcerptQuery {
buffer_id: u64,
buffer_id: BufferId,
excerpt_id: ExcerptId,
cache_version: usize,
invalidate: InvalidationStrategy,
@@ -553,7 +553,7 @@ impl InlayHintCache {
/// Queries a certain hint from the cache for extra data via the LSP <a href="https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#inlayHint_resolve">resolve</a> request.
pub(super) fn spawn_hint_resolve(
&self,
buffer_id: u64,
buffer_id: BufferId,
excerpt_id: ExcerptId,
id: InlayId,
cx: &mut ViewContext<'_, Editor>,

View File

@@ -30,7 +30,7 @@ use std::{
path::{Path, PathBuf},
sync::Arc,
};
use text::Selection;
use text::{BufferId, Selection};
use theme::Theme;
use ui::{h_flex, prelude::*, Label};
use util::{paths::PathExt, paths::FILE_ROW_COLUMN_DELIMITER, ResultExt, TryFutureExt};
@@ -76,13 +76,13 @@ impl FollowableItem for Editor {
let buffers = project.update(cx, |project, cx| {
buffer_ids
.iter()
.map(|id| project.open_buffer_by_id(*id, cx))
.collect::<Vec<_>>()
.map(|id| BufferId::new(*id).map(|id| project.open_buffer_by_id(id, cx)))
.collect::<Result<Vec<_>>>()
});
let pane = pane.downgrade();
Some(cx.spawn(|mut cx| async move {
let mut buffers = futures::future::try_join_all(buffers)
let mut buffers = futures::future::try_join_all(buffers?)
.await
.debug_assert_ok("leaders don't share views for unshared buffers")?;
let editor = pane.update(&mut cx, |pane, cx| {
@@ -109,10 +109,12 @@ impl FollowableItem for Editor {
MultiBuffer::new(replica_id, project.read(cx).capability());
let mut excerpts = state.excerpts.into_iter().peekable();
while let Some(excerpt) = excerpts.peek() {
let buffer_id = excerpt.buffer_id;
let Ok(buffer_id) = BufferId::new(excerpt.buffer_id) else {
continue;
};
let buffer_excerpts = iter::from_fn(|| {
let excerpt = excerpts.peek()?;
(excerpt.buffer_id == buffer_id)
(excerpt.buffer_id == u64::from(buffer_id))
.then(|| excerpts.next().unwrap())
});
let buffer =
@@ -183,13 +185,21 @@ impl FollowableItem for Editor {
fn to_state_proto(&self, cx: &WindowContext) -> Option<proto::view::Variant> {
let buffer = self.buffer.read(cx);
if buffer
.as_singleton()
.and_then(|buffer| buffer.read(cx).file())
.map_or(false, |file| file.is_private())
{
return None;
}
let scroll_anchor = self.scroll_manager.anchor();
let excerpts = buffer
.read(cx)
.excerpts()
.map(|(id, buffer, range)| proto::Excerpt {
id: id.to_proto(),
buffer_id: buffer.remote_id(),
buffer_id: buffer.remote_id().into(),
context_start: Some(serialize_text_anchor(&range.context.start)),
context_end: Some(serialize_text_anchor(&range.context.end)),
primary_start: range
@@ -336,9 +346,9 @@ async fn update_editor_from_message(
let inserted_excerpt_buffers = project.update(cx, |project, cx| {
inserted_excerpt_buffer_ids
.into_iter()
.map(|id| project.open_buffer_by_id(id, cx))
.collect::<Vec<_>>()
})?;
.map(|id| BufferId::new(id).map(|id| project.open_buffer_by_id(id, cx)))
.collect::<Result<Vec<_>>>()
})??;
let _inserted_excerpt_buffers = try_join_all(inserted_excerpt_buffers).await?;
// Update the editor's excerpts.
@@ -362,7 +372,7 @@ async fn update_editor_from_message(
let Some(previous_excerpt_id) = insertion.previous_excerpt_id else {
continue;
};
let buffer_id = excerpt.buffer_id;
let buffer_id = BufferId::new(excerpt.buffer_id)?;
let Some(buffer) = project.read(cx).buffer_for_id(buffer_id) else {
continue;
};
@@ -370,7 +380,7 @@ async fn update_editor_from_message(
let adjacent_excerpts = iter::from_fn(|| {
let insertion = insertions.peek()?;
if insertion.previous_excerpt_id.is_none()
&& insertion.excerpt.as_ref()?.buffer_id == buffer_id
&& insertion.excerpt.as_ref()?.buffer_id == u64::from(buffer_id)
{
insertions.next()?.excerpt
} else {
@@ -395,8 +405,9 @@ async fn update_editor_from_message(
}
multibuffer.remove_excerpts(removed_excerpt_ids, cx);
});
})?;
Result::<(), anyhow::Error>::Ok(())
})
})??;
// Deserialize the editor state.
let (selections, pending_selection, scroll_top_anchor) = this.update(cx, |editor, cx| {
@@ -450,13 +461,13 @@ async fn update_editor_from_message(
}
fn serialize_excerpt(
buffer_id: u64,
buffer_id: BufferId,
id: &ExcerptId,
range: &ExcerptRange<language::Anchor>,
) -> Option<proto::Excerpt> {
Some(proto::Excerpt {
id: id.to_proto(),
buffer_id,
buffer_id: buffer_id.into(),
context_start: Some(serialize_text_anchor(&range.context.start)),
context_end: Some(serialize_text_anchor(&range.context.end)),
primary_start: range
@@ -1361,5 +1372,9 @@ mod tests {
fn to_proto(&self) -> rpc::proto::File {
unimplemented!()
}
fn is_private(&self) -> bool {
false
}
}
}

View File

@@ -8,6 +8,8 @@ use language::Point;
use std::{ops::Range, sync::Arc};
use multi_buffer::Anchor;
/// Defines search strategy for items in `movement` module.
/// `FindRange::SingeLine` only looks for a match on a single line at a time, whereas
/// `FindRange::MultiLine` keeps going until the end of a string.
@@ -23,6 +25,8 @@ pub struct TextLayoutDetails {
pub(crate) text_system: Arc<TextSystem>,
pub(crate) editor_style: EditorStyle,
pub(crate) rem_size: Pixels,
pub anchor: Anchor,
pub visible_rows: Option<f32>,
}
/// Returns a column to the left of the current point, wrapping
@@ -46,11 +50,10 @@ pub fn saturating_left(map: &DisplaySnapshot, mut point: DisplayPoint) -> Displa
map.clip_point(point, Bias::Left)
}
/// Returns a column to the right of the current point, wrapping
/// to the next line if that point is at the end of line.
/// Returns a column to the right of the current point, doing nothing
// if that point is at the end of the line.
pub fn right(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint {
let max_column = map.line_len(point.row());
if point.column() < max_column {
if point.column() < map.line_len(point.row()) {
*point.column_mut() += 1;
} else if point.row() < map.max_point().row() {
*point.row_mut() += 1;
@@ -522,6 +525,7 @@ mod tests {
use language::Capability;
use project::Project;
use settings::SettingsStore;
use text::BufferId;
use util::post_inc;
#[gpui::test]
@@ -822,8 +826,13 @@ mod tests {
let font = font("Helvetica");
let buffer =
cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abc\ndefg\nhijkl\nmn"));
let buffer = cx.new_model(|cx| {
Buffer::new(
0,
BufferId::new(cx.entity_id().as_u64()).unwrap(),
"abc\ndefg\nhijkl\nmn",
)
});
let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0, Capability::ReadWrite);
multibuffer.push_excerpts(

View File

@@ -10,7 +10,7 @@ use crate::{
MultiBufferSnapshot, ToPoint,
};
pub use autoscroll::{Autoscroll, AutoscrollStrategy};
use gpui::{point, px, AppContext, Entity, Pixels, Task, ViewContext};
use gpui::{point, px, AppContext, Entity, Global, Pixels, Task, ViewContext};
use language::{Bias, Point};
pub use scroll_amount::ScrollAmount;
use std::{
@@ -27,6 +27,8 @@ const SCROLLBAR_SHOW_INTERVAL: Duration = Duration::from_secs(1);
#[derive(Default)]
pub struct ScrollbarAutoHide(pub bool);
impl Global for ScrollbarAutoHide {}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ScrollAnchor {
pub offset: gpui::Point<f32>,
@@ -299,7 +301,7 @@ impl ScrollManager {
}
impl Editor {
pub fn vertical_scroll_margin(&mut self) -> usize {
pub fn vertical_scroll_margin(&self) -> usize {
self.scroll_manager.vertical_scroll_margin as usize
}

View File

@@ -5,10 +5,9 @@ edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lib]
path = "src/feature_flags.rs"
[dependencies]
gpui = { path = "../gpui" }
anyhow.workspace = true
gpui = { path = "../gpui" }

View File

@@ -1,4 +1,4 @@
use gpui::{AppContext, Subscription, ViewContext};
use gpui::{AppContext, Global, Subscription, ViewContext};
#[derive(Default)]
struct FeatureFlags {
@@ -12,6 +12,8 @@ impl FeatureFlags {
}
}
impl Global for FeatureFlags {}
pub trait FeatureFlag {
const NAME: &'static str;
}

View File

@@ -5,7 +5,6 @@ edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lib]
path = "src/feedback.rs"
@@ -13,37 +12,36 @@ path = "src/feedback.rs"
test-support = []
[dependencies]
anyhow.workspace = true
bitflags = "2.4.1"
client = { path = "../client" }
db = { path = "../db" }
editor = { path = "../editor" }
gpui = { path = "../gpui" }
language = { path = "../language" }
menu = { path = "../menu" }
project = { path = "../project" }
settings = { path = "../settings" }
theme = { path = "../theme" }
ui = { path = "../ui" }
util = { path = "../util" }
workspace = { path = "../workspace"}
bitflags = "2.4.1"
human_bytes = "0.4.1"
anyhow.workspace = true
futures.workspace = true
gpui = { path = "../gpui" }
human_bytes = "0.4.1"
isahc.workspace = true
language = { path = "../language" }
lazy_static.workspace = true
log.workspace = true
menu = { path = "../menu" }
postage.workspace = true
project = { path = "../project" }
regex.workspace = true
release_channel = { path = "../release_channel" }
serde.workspace = true
serde_derive.workspace = true
serde_json.workspace = true
settings = { path = "../settings" }
smallvec.workspace = true
smol.workspace = true
sysinfo.workspace = true
theme = { path = "../theme" }
tree-sitter-markdown = { git = "https://github.com/MDeiml/tree-sitter-markdown", rev = "330ecab87a3e3a7211ac69bbadc19eabecdb1cca" }
ui = { path = "../ui" }
urlencoding = "2.1.2"
util = { path = "../util" }
workspace = { path = "../workspace" }
[dev-dependencies]
editor = { path = "../editor", features = ["test-support"] }

View File

@@ -2,7 +2,7 @@ use std::{ops::RangeInclusive, sync::Arc, time::Duration};
use anyhow::{anyhow, bail};
use bitflags::bitflags;
use client::{Client, ZED_SERVER_URL};
use client::Client;
use db::kvp::KEY_VALUE_STORE;
use editor::{Editor, EditorEvent};
use futures::AsyncReadExt;
@@ -16,7 +16,7 @@ use project::Project;
use regex::Regex;
use serde_derive::Serialize;
use ui::{prelude::*, Button, ButtonStyle, IconPosition, Tooltip};
use util::ResultExt;
use util::{http::HttpClient, ResultExt};
use workspace::{ModalView, Toast, Workspace};
use crate::{system_specs::SystemSpecs, GiveFeedback, OpenZedRepo};
@@ -225,7 +225,7 @@ impl FeedbackModal {
None,
&["Yes, Submit!", "No"],
);
let client = cx.global::<Arc<Client>>().clone();
let client = Client::global(cx).clone();
let specs = self.system_specs.clone();
cx.spawn(|this, mut cx| async move {
let answer = answer.await.ok();
@@ -293,12 +293,12 @@ impl FeedbackModal {
}
}
let feedback_endpoint = format!("{}/api/feedback", *ZED_SERVER_URL);
let telemetry = zed_client.telemetry();
let metrics_id = telemetry.metrics_id();
let installation_id = telemetry.installation_id();
let is_staff = telemetry.is_staff();
let http_client = zed_client.http_client();
let feedback_endpoint = http_client.zed_url("/api/feedback");
let request = FeedbackRequestBody {
feedback_text: &feedback_text,
email,

View File

@@ -1,10 +1,10 @@
use client::ZED_APP_VERSION;
use gpui::AppContext;
use human_bytes::human_bytes;
use release_channel::ReleaseChannel;
use serde::Serialize;
use std::{env, fmt::Display};
use sysinfo::{RefreshKind, System, SystemExt};
use util::channel::ReleaseChannel;
#[derive(Clone, Debug, Serialize)]
pub struct SystemSpecs {
@@ -21,7 +21,7 @@ impl SystemSpecs {
let app_version = ZED_APP_VERSION
.or_else(|| cx.app_metadata().app_version)
.map(|v| v.to_string());
let release_channel = cx.global::<ReleaseChannel>().display_name();
let release_channel = ReleaseChannel::global(cx).display_name();
let os_name = cx.app_metadata().os_name;
let system = System::new_with_specifics(RefreshKind::new().with_memory());
let memory = system.total_memory();

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