Compare commits

...

146 Commits

Author SHA1 Message Date
Conrad Irwin
bd317eea49 zed 0.124.2 2024-02-23 08:34:08 -07:00
Conrad Irwin
77cdc280c2 Fix a panic in the assistant panel (#8244)
Release Notes:

- Fixed a panic in the assistant panel when the app is shutting down.
2024-02-23 08:33:04 -07:00
Conrad Irwin
1ba33763a4 fix vim panics (#8245)
Release Notes:

- vim: Fixed a panic when using H/M/L when scrolled beyond the end of
the buffer
2024-02-23 08:33:04 -07:00
Thorsten Ball
292d32eb70 Pick up more home dir shell env when spawning (#8273)
Release Notes:

- Improved how Zed picks up shell environment when spawned.
2024-02-23 15:20:54 +01:00
Thorsten Ball
10df9dfca1 Detect and possibly use user-installed gopls / zls language servers (#8188)
After a lot of back-and-forth, this is a small attempt to implement
solutions (1) and (3) in
https://github.com/zed-industries/zed/issues/7902. The goal is to have a
minimal change that helps users get started with Zed, until we have
extensions ready.

Release Notes:

- Added detection of user-installed `gopls` to Go language server
adapter. If a user has `gopls` in `$PATH` when opening a worktree, it
will be used.
- Added detection of user-installed `zls` to Zig language server
adapter. If a user has `zls` in `$PATH` when opening a worktree, it will
be used.

Example:

I don't have `go` installed globally, but I do have `gopls`:

```
~ $ which go
go not found
~ $ which gopls
/Users/thorstenball/code/go/bin/gopls
```

But I do have `go` in a project's directory:

```
~/tmp/go-testing φ which go
/Users/thorstenball/.local/share/mise/installs/go/1.21.5/go/bin/go
~/tmp/go-testing φ which gopls
/Users/thorstenball/code/go/bin/gopls
```

With current Zed when I run `zed ~/tmp/go-testing`, I'd get the dreaded
error:

![screenshot-2024-02-23-11 14
08@2x](https://github.com/zed-industries/zed/assets/1185253/822ea59b-c63e-4102-a50e-75501cc4e0e3)

But with the changes in this PR, it works:

```
[2024-02-23T11:14:42+01:00 INFO  language::language_registry] starting language server "gopls", path: "/Users/thorstenball/tmp/go-testing", id: 1
[2024-02-23T11:14:42+01:00 INFO  language::language_registry] found user-installed language server for Go. path: "/Users/thorstenball/code/go/bin/gopls", arguments: ["-mode=stdio"]
[2024-02-23T11:14:42+01:00 INFO  lsp] starting language server. binary path: "/Users/thorstenball/code/go/bin/gopls", working directory: "/Users/thorstenball/tmp/go-testing", args: ["-mode=stdio"]
```

---------

Co-authored-by: Antonio <antonio@zed.dev>
2024-02-23 14:01:22 +01:00
Kirill Bulatov
57426b925e Ensure default prettier installs correctly when certain FS entries are missing (#8261)
Fixes https://github.com/zed-industries/zed/issues/7865

* bind default prettier (re)installation decision to
`prettier_server.js` existence
* ensure the `prettier_server.js` file is created last, after all
default prettier packages installed
* ensure that default prettier directory exists before installing the
packages
* reinstall default prettier if the `prettier_server.js` file is
different from what Zed expects

Release Notes:

- Fixed incorrect default prettier installation process
2024-02-23 12:30:08 +02:00
Kirill Bulatov
e12d617264 zed 0.124.1 2024-02-22 17:10:37 +02:00
Kirill Bulatov
ca1a95e6e2 Require prerelease eslint version (#8197)
Fixes https://github.com/zed-industries/zed/issues/7650

Release Notes:

- Fixed eslint diagnostics not showing up due to old eslint version used
2024-02-22 16:38:31 +02:00
Joseph T. Lyons
bc8e7e0cc7 v0.124.x preview 2024-02-21 12:22:24 -05:00
Kirill Bulatov
f895d66d1c Make language server id more explicit in unhandled message logs (#8131)
Before:
```
[2024-02-21T18:55:55+02:00 INFO  language::language_registry] starting language server "eslint", path: "/Users/someonetoignore/Downloads/eslint-configs-demo", id: 2
[2024-02-21T18:55:56+02:00 INFO  lsp] 2 unhandled notification window/logMessage:
{
  "type": 3,
  "message": "ESLint server running in node v18.15.0"
}
[2024-02-21T18:55:56+02:00 INFO  lsp] 2 unhandled notification eslint/confirmESLintExecution:
{
  "scope": "local",
  "uri": "file:///Users/someonetoignore/Downloads/eslint-configs-demo/index.js",
  "libraryPath": "/Users/someonetoignore/Downloads/eslint-configs-demo/node_modules/eslint/lib/api.js"
}
```

After:
```
[2024-02-21T18:57:31+02:00 INFO  language::language_registry] starting language server "eslint", path: "/Users/someonetoignore/Downloads/eslint-configs-demo", id: 2
[2024-02-21T18:57:32+02:00 INFO  lsp] Language server with id 2 sent unhandled notification window/logMessage:
{
  "type": 3,
  "message": "ESLint server running in node v18.15.0"
}
[2024-02-21T18:57:32+02:00 INFO  project::prettier_support] Fetching default prettier and plugins: [("prettier-plugin-tailwindcss", "0.5.11"), ("prettier", "3.2.5")]
[2024-02-21T18:57:32+02:00 INFO  lsp] Language server with id 2 sent unhandled notification eslint/confirmESLintExecution:
{
  "scope": "local",
  "uri": "file:///Users/someonetoignore/Downloads/eslint-configs-demo/index.js",
  "libraryPath": "/Users/someonetoignore/Downloads/eslint-configs-demo/node_modules/eslint/lib/api.js"
}
```

We have to pass a name there too, but the problem here is that the
unhandled message callback is created very early, along with the binary,
but the server name is received from the LSP initialize response, which
is a totally separate piece of code.
I plan to refactor that code next, but so far, improve the logs at least
slightly.

Release Notes:

- N/A
2024-02-21 19:11:23 +02:00
Ivan Buryak
7bf16f263e Fix a bug when extension loading is failed after it's folder is viewed by MacOS finder (#8111)
Fixes #8096

# Bug description

I was experimenting with adding extensions and almost went crazy trying
to make my demo extension work. It appeared that I was copying files
with Finder that creates hidden `.DS_Store` files which interfered with
Zed's loading logic. It assumes that `languages/` directory contains
only directories and never files and so it crashes when meets
`.DS_Store`. This makes any extension stop working after it has been
viewed via Finder

# Change

Check if path is directory when loading extension languages (so it will
skip .DS_Store files)
2024-02-21 08:46:58 -08:00
Kyber
d3745a3931 Document new theme options (#7899)
Added documentation for
[#4970](https://github.com/zed-industries/zed/issues/4970), a feature
added in the latest update. Will need to modify `Default Settings` to
reflect the new default theme example.

Release Notes:

- N/A
2024-02-21 11:12:10 -05:00
Kirill Bulatov
0c939e5dfc Add task docs and default keybindings (#8123)
Also group task source modules together

Release Notes:

- N/A

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
2024-02-21 16:43:56 +02:00
Piotr Osiewicz
b9151b9506 Runnables: remove version field from the format (#8118)
This changes the format of runnables slightly (the top-level object is
now a sequence, not a map).
The 2nd commit pulls in aliases from .zshrc and co.
Release Notes:

- N/A
2024-02-21 14:30:16 +01:00
Kirill Bulatov
2679457b02 Rename runnables into tasks (#8119)
Release Notes:

- N/A
2024-02-21 14:56:43 +02:00
Thorsten Ball
45e2c01773 Copilot: handle "ok" status message when no user is set (#8116)
In #6954 a user has trouble using copilot. We haven't gotten to the
bottom of the problem, but one problem is that apparently sometimes (I'm
going to find out when) copilot sends an `"OK"` status message without a
username. This is from the user's logs:

2024-02-20T15:28:41-03:00 [ERROR] failed to deserialize response from
language server: missing field `user`. Response from language server:
"{\"status\":\"OK\"}"

The official `copilot.vim` plugin handles this as if the user is not
authenticated (!= authorized):


1a284014d2/autoload/copilot.vim (L574-L579)

So that's what I'm doing here too.

Release Notes:

- Fixed wrong handling of Copilot sign-in status in rare cases.
2024-02-21 11:39:43 +01:00
Thorsten Ball
fd9823898f Tiny change: use consistent casing in log message (#8115)
Release Notes:

- N/A
2024-02-21 10:23:05 +01:00
Thorsten Ball
d5aba2795b Log when starting language servers (#8075)
This should help us debug more failures because we can now see what
exactly was started.

Release Notes:

- N/A

Co-authored-by: Nathan <nathan@zed.dev>
Co-authored-by: Max <max@zed.dev>
2024-02-21 10:11:41 +01:00
Joseph T. Lyons
92b2e5608b Fix crash when closing last zed window (#8102)
Fixes: #8100

Release Notes:

- N/A
2024-02-21 00:48:42 -05:00
Joseph T. Lyons
c58d72ea2b Improve automatic indentation in Gleam code files (#8098)
Release Notes:

- Improved automatic indentation in Gleam code files
([#7295](https://github.com/zed-industries/zed/issues/7295)).
2024-02-20 23:55:42 -05:00
Joseph T. Lyons
58a5a1eb8f Automatically indent the cursor when adding a newline after a { in Gleam code files (#8097)
Fixes: https://github.com/zed-industries/zed/issues/7295

Release Notes:

- Fixed a bug where adding a newline after a `{` would not automatically
indent the cursor in Gleam code files
([#7295](https://github.com/zed-industries/zed/issues/7295)).
2024-02-20 23:37:15 -05:00
gmorenz
cd640a87a9 Improve key handling on x11, sharing wayland implementation (#8094)
Makes keyboard shortcuts work on x11.

Release Notes:

- N/A
2024-02-20 16:04:52 -08:00
Kirill Bulatov
c97ecc7326 Add initial CI job for Windows target (#8088)
Clippy is disabled for now, due to many warnings in both `gpui` and
other code, see
https://github.com/zed-industries/zed/actions/runs/7980269779/job/21789529800
for more details.

Also, due to `#!/usr/bin/env bash` shebang in the `script/clippy`, it
starts in Windows CI with `shell: C:\Program Files\Git\bin\bash.EXE
-euxo pipefail {0}`

https://github.com/zed-industries/zed/actions/runs/7980269779/job/21789529800#step:4:3
It seems more appropriate to use PowerShell instead.

See `todo!("windows")` for all stubbed places currently.

Release Notes:

- N/A
2024-02-21 00:35:29 +02:00
Marshall Bowers
48f0f387f8 Update docs for building Zed (#8092)
This PR updates the docs for building Zed to fix the links in the
sidebar after the addition of the Linux-specific docs in #8083.

Release Notes:

- N/A
2024-02-20 17:34:13 -05:00
Piotr Osiewicz
2ec910f772 Runnables: Add oneshot runnables (#8061)
/cc @SomeoneToIgnore 
Fixes #7460 and partially addresses #7108 
Release Notes:

- N/A
2024-02-20 23:13:09 +01:00
N
8a73bc4c7d Vim: enable sending multiple keystrokes from custom keybinding (#7965)
Release Notes:

- Added `workspace::SendKeystrokes` to enable mapping from one key to a
sequence of others
([#7033](https://github.com/zed-industries/zed/issues/7033)).

Improves #7033. Big thank you to @ConradIrwin who did most of the heavy
lifting on this one.

This PR allows the user to send multiple keystrokes via custom
keybinding. For example, the following keybinding would go down four
lines and then right four characters.

```json
[
  {
    "context": "Editor && VimControl && !VimWaiting && !menu",
    "bindings": {
      "g z": [
        "workspace::SendKeystrokes",
        "j j j j l l l l"
      ],
    }
  }
]
```

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2024-02-20 15:01:45 -07:00
gmorenz
8f5d7db875 First pass at making a linux keymap (#8082)
Undoubtedly not perfect, but this should be something we can work off
of.

Note that matching keybindings with ctrl in them is currently broken on
linux (or at least x11). This keymap might just manage to be less useful
than using the macos one on linux until that is fixed... the proximate
cause of this is that the `key` field of the `Keystroke` struct looks
like `"\u{e}"` instead of `"n"` when `ctrl-n` is pressed.

Release Notes:

- N/A
2024-02-20 13:51:54 -08:00
Gabriel Dinner-David
389d26d974 Linux(Wayland): translate enter and pageup/down from keysym (#8089)
enter and pagedown/pageup weren't working now they do
Release Notes:
- N/A
2024-02-20 13:48:14 -08:00
Marshall Bowers
e580e2ff0a Update Cargo.lock (#8085)
This PR updates `Cargo.lock`, since it was missed in #8059.

Release Notes:

- N/A
2024-02-20 15:43:48 -05:00
Conrad Irwin
3d9503a454 Fix cx.windows() to return borrowed windows (#8086)
Fixes #8068

Release Notes:

- Fixed an error message when joining a project twice
([#8068](https://github.com/zed-industries/zed/issues/8068)).
2024-02-20 13:42:11 -07:00
Mikayla Maki
5c7cec9f85 Add linux to readme (#8083)
Release Notes:

- N/A
2024-02-20 12:02:51 -08:00
Dzmitry Malyshau
b028231aea linux/x11: disable Vulkan validation in Debug (#8044)
Turns out this validation requirement is confusing new users.

Release Notes:
- N/A
2024-02-20 11:14:13 -08:00
Andrew Lygin
f7d2cb1818 Project search bar layout improvements (#7963)
The PR matches project search layout with the recent changes in the
buffer project layout.

https://github.com/zed-industries/zed/assets/2101250/91b905ea-aed8-4740-9e60-67f3052885e2


Release Notes:

- Improve project search bar layout, match it with the buffer search bar ([7722](https://github.com/zed-industries/zed/issues/7722))
2024-02-20 21:07:01 +02:00
Robin Pfäffle
78dcd72790 Fix display of links in lists (markdown_preview) (#8073)
![markdown_preview](https://github.com/zed-industries/zed/assets/67913738/d8e4800f-d549-42e7-90b4-001d98aa39d2)

Release Notes:

- Fixed display of long links in lists not fully visible in markdown
preview.
2024-02-20 11:30:40 -07:00
Dzmitry Malyshau
d51a0b60be linux/x11: send XCB requests asynchronously (#8045)
With `send_and_check_request` we'd be blocking both the main loop and
the caller. `send_request` is only going to be blocking on the main loop
when processing the request.

Release Notes:
- N/A

Based on a flamegraph from `perf`/`hotspot`, we are spending 40% of time
redrawing, another 40% of time downloading stuff (i.e. rust toolchain),
and the rest on text rendering, layout and such. This is with Vulkan
Validation (see https://github.com/zed-industries/zed/pull/8044).

I'm also wondering if it would be better with #7758, but regardless we
should have no problem rendering at 60-120 fps and processing user
input. More follow-ups are expected here.
2024-02-20 10:20:49 -08:00
Marshall Bowers
8178d347b6 Change default Markdown tab size (#8080)
Following up to #8079, this PR changes the default Markdown tab size to
2 spaces.

This should produce less surprising formatting for lists when using
Prettier.

Release Notes:

- Changed default Markdown tab size to 2 spaces.
2024-02-20 13:18:40 -05:00
Marshall Bowers
33ecb424af Adjust tab size for Markdown (#8079)
This PR sets the `tab_size` for Markdown to 2 spaces.

This should prevent Prettier from adding a bunch of leading whitespace
when formatting Markdown lists.

Release Notes:

- N/A
2024-02-20 13:15:14 -05:00
Darren Schroeder
91b97387b6 bump tree-sitter-nu to latest (#8059)
This PR bumps the tree-sitter-nu commit to the latest supported by the
nushell team. It also includes the latest highlights.scm

Release Notes:

Bumped `nu` tree sitter dependency and highlights.scm
2024-02-20 12:05:09 -05:00
Julia
0a40a21c74 Timeout while waiting for server to shutdown and kill it 2024-02-20 11:47:13 -05:00
Conrad Irwin
b14d576349 Follower simplification (#8026)
Release Notes:

- Improved reliability of following
2024-02-20 09:41:37 -07:00
Philipp Schaffrath
db0eaca2e5 Rename scrollbar_thumb to be consistent with other style properties (#8004)
This small inconsistency was mentioned on the discord. This fixes it.

Release Notes:

- Themes: Renamed `scrollbar_thumb.background` to
`scrollbar.thumb.background` to be consistent with other style
properties.

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-02-20 11:26:09 -05:00
Thorsten Ball
80db468720 go: better logging if go install gopls fails (#8060)
Release Notes:

- Improved logging if installing `gopls` fails
2024-02-20 15:56:52 +01:00
Kirill Bulatov
0d2ad67b27 Add settings to configure terminal scroll limit (#8063)
Fixes https://github.com/zed-industries/zed/issues/7550
Also set maximum allowed to runnables' terminals.


Release Notes:

- Added settings to configure terminal scroll limit
([7550](https://github.com/zed-industries/zed/issues/7550))
2024-02-20 16:14:59 +02:00
Kirill Bulatov
7065d6c46d Use proper template for initial runnables config contents (#8064)
Release Notes:

- N/A
2024-02-20 16:14:50 +02:00
Hourann
6c714c13b3 Fix markdown preview heading overflows no wrap (#8052)
![Kapture 2024-02-20 at 18 27
15](https://github.com/zed-industries/zed/assets/8416130/87d4dcea-e2f0-44ba-88a4-06829dbb0e89)

Release Notes:

- Improved markdown preview wrapping ([#8047](https://github.com/zed-industries/zed/issues/8047)).
2024-02-20 15:18:42 +02:00
Kirill Bulatov
c54d6aff6c Properly ignore missing/empty runnables config 2024-02-20 15:02:35 +02:00
Kirill Bulatov
48a6fb9e84 Fix runnables-related hickups (#8058)
* never error on absent/empty runnables file
* always activate terminal tab on runnable (re)schedule

Release Notes:

- N/A
2024-02-20 14:54:19 +02:00
Ali Servet Donmez
e9f400a8bd rust-analyzer check command is check and not checkOnSave (#8054)
Reference: https://rust-analyzer.github.io/manual.html#configuration

Release Notes:

- N/A
2024-02-20 14:06:07 +02:00
Thorsten Ball
fc101c1fb3 Log when failed to deserialize response from language server (#8046)
This should probably help us debug when language servers don't start up
properly.

Release Notes:

- N/A
2024-02-20 10:16:00 +01:00
bbb651
4616d66e1d Download right language server binary for OS (#8040)
Release Notes:

- Download right language server binary for OS
2024-02-20 09:53:03 +02:00
Bennet Bo Fenner
3ef8a9910d chat: auto detect links (#8028)
@ConradIrwin here's our current implementation for auto detecting links
in the chat.
We also fixed an edge case where the close reply to preview button was
cut off (rendered off screen).

Release Notes:

- Added auto detection for links in the chat panel.

---------

Co-authored-by: Remco Smits <62463826+RemcoSmitsDev@users.noreply.github.com>
2024-02-19 21:49:47 -07:00
Ben Hamment
1e44bac418 Add Ruby method visibility in outline view (#7954)
Release Notes:

- Improved ([#7849
](https://github.com/zed-industries/zed/issues/7849)).

<img width="897" alt="image"
src="https://github.com/zed-industries/zed/assets/7274458/a2b0db84-1971-45c0-a5a2-68de651e342b">
2024-02-19 19:26:04 -07:00
Remco Smits
aad7761038 Add an indicator to the channel chat to see all the messages that you missed (#7781)
This pull requests add the following features:
- Show indicator before first unseen message
- Scroll to last unseen message

<img width="241" alt="Screenshot 2024-02-14 at 18 10 35"
src="https://github.com/zed-industries/zed/assets/62463826/ca396daf-7102-4eac-ae50-7d0b5ba9b6d5">


https://github.com/zed-industries/zed/assets/62463826/3a5c4afb-aea7-4e7b-98f6-515c027ef83b

### Questions: 
1. Should we hide the indicator after a couple of seconds? Now the
indicator will hide when you close/reopen the channel chat, because when
the last unseen channel message ID is not smaller than the last message
we will not show it.

Release Notes:
- Added unseen messages indicator for the channel chat.
2024-02-19 19:20:02 -07:00
bbb651
0422d43798 Linux: Add support for MouseButton::Navigate in GPUI (wayland and x11) (#7996)
Release Notes:

- N/A

Based on wgpu implementation (which I wrote).

---------

Co-authored-by: Mikayla Maki <mikayla@zed.dev>
2024-02-19 18:09:53 -08:00
Dzmitry Malyshau
b00b65b330 linux/x11: implement window focus (#8002)
Release Notes:
- N/A
2024-02-19 17:54:54 -08:00
Janrupf
fddb778e5f Enable server side decorations on wayland (#8037)
This PR enables server side decorations on Wayland if possible. This is
stopgap solution, so that the window can be moved, resized and dragged
on Wayland sessions at all.


![image](https://github.com/zed-industries/zed/assets/25827180/3dc9af53-76c0-4664-8746-ed6a6e5eafe7)

Since Wayland compositors can decide to force either mode (as in,
forcing server or client side decorations), this requires additional
handling in zed. Since zed doesn't provide any of that handling as of
now, as a temporary solution server side decorations are always
requested.
2024-02-19 17:53:31 -08:00
白山風露
77974a4367 Stubbing unix-dependent values on Windows (#8036)
Release Notes:

- N/A
2024-02-19 17:41:35 -08:00
白山風露
0037f0b2fd Avoid dependencies build errors on Windows (#7827)
This is a compilation of fixes for errors that appeared in dependent
crates in Windows.

- wezterm (zed-industries/wezterm#1)
- tree-sitter-svelte (Himujjal/tree-sitter-svelte#54)
- tree-sitter-uiua (shnarazk/tree-sitter-uiua#25)
- tree-sitter-haskell (I sent a PR, but upstream source is regenerated
and no longer errors.)

Release Notes:

- N/A
2024-02-19 16:44:24 -08:00
Kirill Bulatov
37f6a706cc Invalidate Linux build caches more agressively (#8031)
We run Linux CI on regular GitHub Action runners, which have ~30GB of
disk space. This is nothing for Rust builds and, due to Cargo.lock
perturbations, we tend to accumulate enough artifacts to fill the disk
entirely since `restore-keys` alowed to keep the cache for different
lockfiles.

Instead, try to invalidate the cache more aggressively (which will cost
us more frequent ~30min Linux CI runs) to see how this will work in
comparison.

Release Notes:

- N/A
2024-02-19 14:16:39 -08:00
Abdullah Alsigar
f4bafd5899 Dart support (#7220)
This is my first contribution, feedback is welcome.

Release Notes:

- Added Dart language support
([#5343](https://github.com/zed-industries/zed/issues/5343)).
2024-02-19 11:10:08 -08:00
Piotr Osiewicz
b3d3a00a14 chore: Add missing LICENSE-GPL files 2024-02-19 18:40:41 +01:00
Thorsten Ball
0a5df7d597 Fix jk not working in Vim bindings (#8023)
Fixes #8006.

Release Notes:

- Fixed two-character bindings in Vim insert mode (e.g. `j k` or `j j`)
not working.
([#8006](https://github.com/zed-industries/zed/issues/8006))

Co-authored-by: Conrad <conrad@zed.dev>
2024-02-19 10:21:07 -07:00
Conrad Irwin
4d1585b917 Don't drop key bindings (#8019)
Fixes: #7748



Release Notes:

- Fixed a bug where keystrokes could be lost after focus changes
([#7748](https://github.com/zed-industries/zed/issues/7748)).

Co-authored-by: Antonio <as-cii@zed.dev>
2024-02-19 10:20:51 -07:00
Kirill Bulatov
99559f3975 Add another runnables_ui/Cargo.toml field to satisfy license checks 2024-02-19 19:14:00 +02:00
Kirill Bulatov
5783497c21 Add missing license field to runnables_ui 2024-02-19 19:04:40 +02:00
Kirill Bulatov
e27c2fc946 Fix seed-db script by passing it the correct admin file path (#8022)
Release Notes:

- N/A
2024-02-19 18:53:17 +02:00
Piotr Osiewicz
f17d0b5729 Add static Runnables (#8009)
Part of #7108

This PR includes just the static runnables part. We went with **not**
having a dedicated panel for runnables.
This is just a 1st PR out of N, as we want to start exploring the
dynamic runnables front. Still, all that work is going to happen once
this gets merged.

Release Notes:

- Added initial, static Runnables support to Zed. Such runnables are defined in
`runnables.json` file (accessible via `zed: open runnables` action) and
they can be spawned with `runnables: spawn` action.

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
Co-authored-by: Pitor <pitor@zed.dev>
Co-authored-by: Beniamin <beniamin@zagan.be>
2024-02-19 18:41:43 +02:00
Antonio Scandurra
ca251babcd Drop Box<dyn PlatformWindow> when the OS closes the native window (#8016)
Closes #7973 

This fixes a leak in GPUI when the user didn't override
`on_should_close_window`.

Release Notes:

- N/A

---------

Co-authored-by: Thorsten <thorsten@zed.dev>
2024-02-19 15:38:50 +01:00
Bennet Bo Fenner
c33efe8cd0 recent projects: cleanup ui (#7528)
As the ui for the file finder was recently changed in #7364, I think it
makes sense to also update the ui of the recent projects overlay.

Before:

![image](https://github.com/zed-industries/zed/assets/53836821/8a0f5bef-9b37-40f3-a974-9dfd7833cc71)

After:

![image](https://github.com/zed-industries/zed/assets/53836821/7e9f934a-1ac3-4716-b7b6-67a7435f3bde)


Release Notes:

- Improved UI of recent project overlay
2024-02-19 14:37:52 +02:00
Tadeo Kondrak
2b56c43f2d Wayland: Keyboard input improvements (#7989)
Release Notes:

- N/A

---

Right now the Wayland backend is using `xkb::State::key_get_utf8` as the
`key`, when it should be used as the `ime_key`. It also manages
pressing/releasing modifiers manually when this should be managed by the
display server.

This allows modifier combinations to work in more cases, making it an
alternative to https://github.com/zed-industries/zed/pull/7975, which
interprets what is now only used as the `ime_key` value as a `key`
value.
2024-02-18 21:25:42 -08:00
Roman
bd137b01ad Wayland fractional scaling (#7961)
This PR adds support for fractional scaling on Wayland.

Release Notes:

- N/A
2024-02-18 21:22:03 -08:00
Dzmitry Malyshau
4e1e26b696 blade: Fix initialization of atlas textures used for path rasterization (#8000)
Generally the BladeAtlas logic has been deferring all the texture
initializations and updates till `begin_frame`. This doesn't work for
path rasterization, since a texture needs to be allocated after
`begin_frame` and used immediately.

Fixed validation error:
> UNASSIGNED-CoreValidation-DrawState-InvalidImageLayout(ERROR / SPEC):
msgNum: 1303270965 - Validation Error: [
UNASSIGNED-CoreValidation-DrawState-InvalidImageLayout ] Object 0:
handle = 0x60ce301b9010, name = main, type =
VK_OBJECT_TYPE_COMMAND_BUFFER; Object 1: handle = 0x51820000000007b,
name = atlas, type = VK_OBJECT_TYPE_IMAGE; | MessageID = 0x4dae5635 |
vkQueueSubmit(): pSubmits[0].pCommandBuffers[0] command buffer
VkCommandBuffer 0x60ce301b9010[main] expects VkImage
0x51820000000007b[atlas] (subresource: aspectMask 0x1 array layer 0, mip
level 0) to be in layout VK_IMAGE_LAYOUT_GENERAL--instead, current
layout is VK_IMAGE_LAYOUT_UNDEFINED.
    Objects: 2
        [0] 0x60ce301b9010, type: 6, name: main
        [1] 0x51820000000007b, type: 10, name: atlas

Release Notes:
- N/A
2024-02-18 21:18:59 -08:00
d1y
12b12ba17a Add syntax highlighting and LSP for Dockerfiles(#6905) (#7977)
Release Notes:

- Added Dockerfile syntax highlighting and LSP support

---------

Co-authored-by: Bryce Palmer <bpalmer@redhat.com>
Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
2024-02-18 20:44:54 +02:00
d1y
8acd4d122e Fix git_commit grammar name typo (#7982)
Release Notes:

- Fixed git_commit highlights not working
2024-02-18 20:40:56 +02:00
Thorsten Ball
251218954d Add editor::OpenUrl action and bind to gx in Vim mode (#7972)
This adds one feature I've been missing a lot in Vim mode: `gx` to open
the URL under the cursor.

Technically, in Vim, `gx` opens more "paths", not just URLs, but I think
this is a good start.

Release Notes:

- Added `gx` to Vim mode to open the URL under the cursor.

Demo:


https://github.com/zed-industries/zed/assets/1185253/6a19490d-b61d-40b7-93e8-4819599f6977
2024-02-18 18:52:50 +01:00
Victor
3ca6f7572f Add more documentation about base keymaps (#7953)
Release Notes:

- Clarify base keymap settings better in the docs
2024-02-18 17:33:46 +02:00
Marshall Bowers
b91d6da6b6 Remove Beancount as a built-in language (#7934)
This PR removes Beancount as a built-in language, as it is now available
as an [extension](https://github.com/zed-extensions/beancount).

Release Notes:

- Removed built-in support for Beancount, as it is now provided by an
[extension](https://github.com/zed-extensions/beancount).
2024-02-18 09:58:12 -05:00
Aleksei Trifonov
a041e07c99 Hide Inline Assist button if assistant.button is disabled (#7932)
This PR adds check for `assistant.button` setting in quick bar, to hide
it when the setting is set to false. It seems that the setting can be a
separate one, I would be happy to add it if needed.

Release Notes:

- Improved `assistant.button` setting so that `Inline Assist` button in
editor quick bar is also hidden
([#4500](https://github.com/zed-industries/zed/issues/4500)).
2024-02-18 08:14:08 +02:00
Andrew Lygin
6d9b8cc595 Clear search results on invalid query input (#7958)
Fixes a bug in the buffer search bar: Clears results of a previous
successfull search when the user enters invalid search request.

Steps to reproduce the bug:
1. Switch to Regex search mode.
2. Enter a valid search query that produces several matches.
3. Add a symbol to the search query that makes it an invalid regexp.
4. Switch to the editor and walk through the code back and forth.

Expected result: All the match highlightings after step 2 are cleared,
search bar indicates absence of the last search matches.

Actual: The results from the last valid search are highlighted, search
bar indicates presence of matches.

Potentially, the same effect may occur when searching in the simple text
mode, or when clearing the search query in some circumstances, so I made
the fix for all those cases, though I wasn't able to reproduce them
manually.

The bug:


https://github.com/zed-industries/zed/assets/2101250/1c50b98c-ae8e-4a9c-8ff5-1e5c63027ec3

After the fix:


https://github.com/zed-industries/zed/assets/2101250/e3eedf8c-2e3e-41ee-81cc-c2c9d919fba3

Release Notes:
- Clear search results on invalid query input
2024-02-18 08:01:06 +02:00
Conrad Irwin
4c781b6455 vim netrw (#7962)
- Tidy up vim netrw bindings (c.f.
https://github.com/zed-industries/zed/issues/4270,
https://github.com/zed-industries/zed/pull/7757)
- Add vim commands for panels

Release Notes:

- vim: add commands to toggle panels `:E[xplore]`, `:C[ollab]`,
`:Ch[at]`, `:N[otification]`, `:A[I]`, `:te[rm]` (or `:T[erm]`).
2024-02-17 13:36:08 -07:00
vultix
8aa5319210 Add documentation to many core editor types (#7919)
Hopefully this makes it a bit easier for new contributors to dive into
the codebase :)

Release Notes:

- Improved documentation for many core editor types

---------

Co-authored-by: Nathan Sobo <nathan@zed.dev>
2024-02-17 09:03:05 -07:00
Jack T
f19378135a Add Lua file icon (#7926)
Release Notes: 

- Added Lua file icon and association ([7925](https://github.com/zed-industries/zed/issues/7925))
2024-02-17 17:59:25 +02:00
Xing Liu
7804be0286 Add missing character "-" for "Go back" in key_bindings.md (#7942)
Release Notes:
- Added "Go back" binding docs in the `key_bindings.md`
2024-02-17 17:51:14 +02:00
evrsen
98ffdca32e Add locale-based time formatting in the chat panel (#7945)
This commit addresses the issue of time formats displayed in the chat
panel. Previously, the time was always displayed in a 12-hour format,
regardless of the user's locale. The `format_timestamp` function has
been updated to check the system locale and format the time accordingly.


Release Notes:
- Fixed time formatting in the chat panel to be locale based.
2024-02-17 08:44:28 -07:00
claytonrcarter
cd4d2f7900 Add Prettier support for Vue, Markdown and PHP (#7904)
Current prettier support w/i Zed leaves out a few languages that are
officially supported by prettier. In particular, Vue and Markdown are
supported by the core prettier project, and PHP is supported via an
official plugin. I didn't see any open issues for this, but I have been
wondering for months why `"formatter": "prettier"` wasn't working on my
PHP files. Now that Zed is open source, I was able to find out why, and
fix it. 😄

I have been using this with PHP files daily for a week+ now, and I have
also used it successfully with Vue and Markdown files, though not as
extensively. I looked around and did not see any tests for specific
prettier language integrations, but if I missed them please let me know
and I'll add some tests.

**Notes**
- I did not add support for Ruby (which has an official prettier plugin)
because it seems to require some external dependencies (notably, Rudy
and some Gems). When those are present on the system and `$PATH`,
prettier will will work just fine on Ruby files if the plugin is set up
similar to how the PHP plugin is set up (I tried it), and I can add that
in here, if desired. The PHP plugin is pure JS (as I recall) and doesn't
have this issue.
- I did *not* add support for languages that have "community" plugins,
though I do note that Zed already ships with prettier support for svelte
enabled, which – if I understand correctly – is powered by a community
plugin. If desired, I could look at adding support/configuration to
enable prettier support for things like elm, erb, glsl, bash, toml.
Bash, in particular, *I* would find useful. 😄

Release Notes:

- Added prettier support for Vue, Markdown and PHP
2024-02-17 11:35:31 +02:00
Robin Pfäffle
43a845cbbf Add default settings to display Svelte inlay hints (#7943)
Fixes: #7913.

Release Notes:

- Added default settings for Svelte language server to display inlay
hints ([#7913](https://github.com/zed-industries/zed/issues/7913)).
2024-02-17 11:33:05 +02:00
Marshall Bowers
3cbc18895a Upgrade toml to v0.8 (#7931)
This PR upgrades our `toml` dependency to v0.8.

I noticed that our current version of `toml` wasn't able to parse
certain kinds of documents involving enums, whereas the newer version
can.

Release Notes:

- N/A
2024-02-16 17:43:40 -05:00
Roman
f82b2741cd Wayland input handling (#7857)
Adds initial keyboard and mouse input for Wayland (thanks to @gabydd and
@kvark for reference).


Release Notes:

- N/A

---------

Co-authored-by: Mikayla Maki <mikayla@zed.dev>
2024-02-16 13:49:34 -08:00
Dzmitry Malyshau
9ad1862f2f Enable Blade on MacOS via "macos-blade" feature (#7669)
Depends on https://github.com/zed-industries/font-kit/pull/2 and
https://github.com/kvark/blade/pull/77

This change enables Blade to be also used on MacOS. It will also make it
easier to use it on Windows.

What works: most of the things. Zed loads as fast and appears equally
responsive to the current renderer.
<img width="306" alt="Screenshot 2024-02-11 at 12 09 15 AM"
src="https://github.com/zed-industries/zed/assets/107301/66d82f45-5ea2-4e2b-86c6-5b3ed333c827">

Things missing:
- [x] video streaming. ~~Requires a bit of plumbing on both Blade and
Zed sides, but all fairly straightforward.~~
  -  verified with a local setup
- [x] resize. ~~Not sure where exactly to hook up the reaction on the
window size change. Once we know where, the fix is one line.~~
- [ ] fine-tune CA Layer
- this isn't a blocker for merging the PR, but it would be a blocker if
we wanted to switch to the new path by default
- [ ] rebase on latest, get the dependency merged (need review/merge of
https://github.com/zed-industries/font-kit/pull/2!)

Update: I implemented resize support as well as "surface" rendering on
the Blade path (which will be useful on Linux/Windows later on). I
haven't tested the latter though - not sure how to get something
streaming. Would appreciate some help! I don't think this should be a
blocker to this PR, anyway.

The only little piece that's missing for the Blade on MacOS path to be
full-featured is fine-tuning the CALayer configuration. Zed does a lot
of careful logic in configuring the layer, such as switching the
"present with transaction" on/off intermittently, which Blade path
doesn't have yet.

Release Notes:
- N/A

---------

Co-authored-by: Mikayla <mikayla@zed.dev>
2024-02-16 13:39:40 -08:00
Mikayla Maki
1c361ac579 Remove comment (#7922)
Per https://github.com/zed-industries/zed/pull/7814, this is more
trouble than it's worth. As these functions are never exposed to the
user of GPUI, we can just manually audit and enforce the relevant rules.

Release Notes:

- N/A
2024-02-16 10:39:50 -08:00
Rom Grk
bea36918f4 Linux: file dialogs (#7852)
This PR implements linux file dialogs and open/reveal actions.

| Open folder | Reveal path |
| --- | --- |
| ![Screenshot from 2024-02-15
16-50-49](https://github.com/zed-industries/zed/assets/1423607/b4260574-d841-4ded-821d-521f507916d1)
| ![Screenshot from 2024-02-15
16-51-36](https://github.com/zed-industries/zed/assets/1423607/1f32f451-7def-423a-9d69-de2876285b60)
|

---------

Co-authored-by: Mikayla Maki <mikayla@zed.dev>
2024-02-16 10:34:54 -08:00
Conrad Irwin
43e8fdbe82 Fix a missing follower update on re-activate (#7918)
This could cause following to get into a bad state temporarily

Release Notes:

- Fixed a bug around following if the follow started while the workspace
was inactive.
2024-02-16 11:33:49 -07:00
Antonio Scandurra
5df1318e75 Don't use stale layout when view cache is invalidated in GPUI (#7914)
When a view is invalidated, we want to participate in Taffy layout with
an accurate style rather than the dummy style we use when a view is
cached. Previously, we only detected invalidation during paint. This
adds logic to layout as well to avoid using the dummy style when dirty.

Release Notes:

- N/A

---------

Co-authored-by: Nathan <nathan@zed.dev>
2024-02-16 19:06:11 +01:00
Joseph T. Lyons
577b244b03 Add button link to extension repository (#7880)
Fixes: https://github.com/zed-industries/zed/issues/7873

<img width="1165" alt="image"
src="https://github.com/zed-industries/zed/assets/19867440/2338519c-bd48-4716-9f88-ed155b0dad67">

Release Notes:

- Added a button to link to an extension's repository
2024-02-16 11:49:05 -05:00
Max Brunsfeld
2e0d18ee76 Don't support cloning the extensions view (#7875)
Fixes https://github.com/zed-industries/zed/issues/7840

We could support this later, but for now, I don't think we need to.

Release Notes:

- N/A

Co-authored-by: Nathan <nathan@zed.dev>
2024-02-16 11:48:47 -05:00
Marshall Bowers
c5a23faf7c Fall back to One themes if the selected theme doesn't exist (#7911)
This PR makes it so the One themes—One Dark and One Light—are used as a
fallback when trying to reload a theme that no longer exists in the
registry.

This makes it so when an extension providing the current theme is
removed, the active theme will change to either One Dark or One Light
(based on the system appearance) instead of retaining a cached version
of the theme.

Release Notes:

- Changed the behavior when uninstalling a theme to default to One Dark
or One Light (based on system appearance) rather than keeping a cached
version of the old theme.
2024-02-16 11:28:34 -05:00
Marshall Bowers
86f81c4db3 Mention Docker Compose in the collab docs (#7908)
This PR adds a note about using Docker Compose to run the `collab`
dependencies.

Release Notes:

- N/A
2024-02-16 11:02:24 -05:00
Marshall Bowers
07501d9cfa Add LiveKit server to Docker Compose (#7907)
This PR adds the LiveKit server to the Docker Compose setup.

All of the dependencies needed to run the collab server are now
encapsulated within Docker Compose.

Release Notes:

- N/A
2024-02-16 10:49:48 -05:00
Subash Chandra Sapkota
07c7778cff Add cancel button on GitHub Copilot actions (#7850)
Release Notes:

- Added cancel button on Copilot actions
([#6878](https://github.com/zed-industries/zed/issues/6878)).

Here is the screenshot of the UI change:

<img width="545" alt="Screenshot 2024-02-15 at 13 00 53"
src="https://github.com/zed-industries/zed/assets/29421465/7a4e1641-1822-47ad-819e-f3d83bc3cc74">
2024-02-16 10:45:55 -05:00
Antonio Scandurra
aa6926e57a Revert "Upgrade to Taffy 0.4" (#7896)
Reverts zed-industries/zed#7868

@nicoburns: this seems to break text input in the chat panel, so
reverting this for now.

<img width="365" alt="image"
src="https://github.com/zed-industries/zed/assets/482957/fc47eee9-6049-49c2-be2c-fb91f7d35caf">

It would be great to upgrade to Taffy 0.4 though, so we should figure
out why that particular input breaks.

Release notes:

- N/A
2024-02-16 14:30:31 +01:00
Thorsten Ball
ae577c9d5c Highlight escape sequences in TypeScript/JavaScript (#7892)
Ran into this while hacking on TypeScript/React/TSX...

Release Notes:


- N/A

![screenshot-2024-02-16-07 03
56@2x](https://github.com/zed-industries/zed/assets/1185253/e6725a1e-7653-4316-abd0-280ea8818d66)
2024-02-16 09:12:39 +01:00
Marshall Bowers
f4f72a1136 Add blob store to Docker Compose (#7889)
This PR adds the local blob store—backed by
[MinIO](https://github.com/minio/minio)—to the Docker Compose setup.

This allows running the blob store locally all within a container.

Release Notes:

- N/A
2024-02-15 23:19:03 -05:00
Marshall Bowers
4310b0b8de Replace full with size_full (#7888)
This PR removes the `full` style method and replaces it with
`size_full`, as the two do the same thing.

This is the generated code for `size_full`:

```rs
#[doc = "Sets the width and height of the element.\n\n100%"]
fn size_full(mut self) -> Self {
    let style = self.style();
    style.size.width = Some((gpui::relative(1.)).into());
    style.size.height = Some((gpui::relative(1.)).into());
    self
}
```

Release Notes:

- N/A
2024-02-15 22:26:49 -05:00
Marshall Bowers
a161a7d0c9 Format YAML files (#7887)
This PR formats the YAML files in the repo with Prettier.

Release Notes:

- N/A
2024-02-15 22:04:57 -05:00
Marshall Bowers
ab6b9196e1 Fix Cargo.toml formatting (#7886)
This PR disables the formatting for `.toml` files within the Zed repo,
as the formatter provided by the TOML language server messes things up.

Release Notes:

- N/A
2024-02-15 21:54:43 -05:00
Marshall Bowers
ef551cedef Add CheckboxWithLabel component (#7881)
This PR builds on top of #7878 by adding a general-purpose
`CheckboxWithLabel` component to use for checkboxes that have attached
labels.

This component encompasses the functionality of allowing to click on the
label to toggle the value of the checkbox.

There was only one other occurrence of a checkbox with a label—the
"Public" checkbox in the channel management modal—and this has been
updated to use `CheckboxWithLabel`.

Resolves #7794.

Release Notes:

- Added support for clicking the label of the "Public" checkbox in the
channel management modal to toggle the value
([#7794](https://github.com/zed-industries/zed/issues/7794)).
2024-02-15 21:00:30 -05:00
Marshall Bowers
9ef83a2557 Make the labels of the checkboxes on the welcome screen clickable (#7878)
This PR makes the labels of the checkboxes on the welcome screen
clickable.

Release Notes:

- Added support for clicking the labels of the checkboxes on the welcome
screen to toggle the value
([#7794](https://github.com/zed-industries/zed/issues/7794)).
2024-02-15 20:37:31 -05:00
Joseph T. Lyons
32fdff0285 Update issue template configuration (again) 2024-02-15 19:35:23 -05:00
Joseph T. Lyons
4094562321 Update issue template configuration 2024-02-15 18:47:01 -05:00
Marshall Bowers
6869b62af3 Update .mailmap (#7871)
This PR updates the `.mailmap` file to merge some commit authors using
multiple emails.

Release Notes:

- N/A
2024-02-15 18:35:43 -05:00
Joseph T. Lyons
21a7421ee0 Update 1_language_support.yml 2024-02-15 18:25:13 -05:00
Vishal Bhavsar
96dcc385dd vim: Implement Go To Previous Word End (#7505)
Activated by keystrokes g-e.



Release Notes:

- vim: Added `ge` and `gE` for go to Previous Word End.

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2024-02-15 16:15:31 -07:00
Marshall Bowers
e6766e102e Return descriptions for extensions (#7869)
This PR updates the extensions API to return descriptions for
extensions.

Release Notes:

- N/A
2024-02-15 17:43:47 -05:00
Nico Burns
694e18417e Upgrade to Taffy 0.4 (#7868)
Upgraded Taffy to v0.4.0 from crates.io (previously using prerelease
version from git).

Code changes required were minor as gpui was already using a recent
version of Taffy.

Release Notes:

- N/A
2024-02-15 14:30:39 -08:00
Joey Smith
94426c4393 Better logic for copying themed player colors into registry (#7867)
Release Notes:

- Fixed a potential panic when themes did not contain enough player
colors ([#7733](https://github.com/zed-industries/zed/issues/7733)).

Thanks to @maxdeviant for the code review and improvements!
2024-02-15 17:22:23 -05:00
Marshall Bowers
bf1bcd027c Secondarily sort by extension name instead of ID (#7866)
This PR makes it so extensions are secondarily sorted by their name
(instead of by ID) after we sort them by their download count.

Release Notes:

- N/A
2024-02-15 16:51:41 -05:00
Marshall Bowers
23132b5ab1 Display extension download counts (#7864)
This PR adds the download count for extensions to the extensions view.

Release Notes:

- Added download counts for extensions to the extensions view.
2024-02-15 16:41:54 -05:00
Conrad Irwin
a8d5864524 Fix panic when loading hover state. (#7861)
Release Notes:

- Fixed a panic when hovering over an identifier in the editor
2024-02-15 14:20:10 -07:00
Conrad Irwin
ea322e1d1c Add "code_actions_on_format" (#7860)
This lets Go programmers configure `"code_actions_on_format": {
  "source.organizeImports": true,
}` so that they don't have to manage their imports manually

I landed on `code_actions_on_format` instead of `code_actions_on_save`
(the
VSCode version of this) because I want to run these when I explicitly
format
(and not if `format_on_save` is disabled).

Co-Authored-By: Thorsten <thorsten@zed.dev>

Release Notes:

- Added `"code_actions_on_format"` to control additional formatting
steps on format/save
([#5232](https://github.com/zed-industries/zed/issues/5232)).
- Added a `"code_actions_on_format"` of `"source.organizeImports"` for
Go ([#4886](https://github.com/zed-industries/zed/issues/4886)).

Co-authored-by: Thorsten <thorsten@zed.dev>
2024-02-15 14:19:57 -07:00
Max Brunsfeld
e1ae0d46da Add an extensions API to the collaboration server (#7807)
This PR adds a REST API to the collab server for searching and
downloading extensions. Previously, we had implemented this API in
zed.dev directly, but this implementation is better, because we use the
collab database to store the download counts for extensions.

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
Co-authored-by: Marshall <marshall@zed.dev>
Co-authored-by: Conrad <conrad@zed.dev>
2024-02-15 12:53:57 -08:00
Kirill Bulatov
bdc2558eac Add default language server settings to display inlay hints for Go and TypeScript (#7854)
Hints are still disabled by default in Zed, but when those get enabled,
the language server settings allow to display those instantly without
further server configuration, which might be not obvious. Also add the
documentation enties for those settings and their defaults in Zed.

Closes https://github.com/zed-industries/zed/issues/7821

Release Notes:

- Added default settings for TypeScript and Go LSP servers to enable
inlay hints when those are turned on in Zed
([7821](https://github.com/zed-industries/zed/issues/7821))
2024-02-15 22:01:49 +02:00
Dzmitry Malyshau
a41fb29e01 Linux/x11 input handling (#7811)
Implements the basics of keyboard and mouse handling.
Some keys will need special treatment, like Backspace/Delete. In this
PR, all keys are treated as append-only. Leaving this for a follow-up.

I used @gabydd 's branch as a reference (thank you!) as well as
https://github.com/xkbcommon/libxkbcommon/blob/master/doc/quick-guide.md
For future work, I'll also use
https://github.com/xkbcommon/libxkbcommon/blob/master/tools/interactive-x11.c

All commits are separately compileable and reviewable.

Release Notes:
- N/A

---------

Co-authored-by: Mikayla Maki <mikayla@zed.dev>
2024-02-15 11:58:47 -08:00
Andrew Lygin
aa319ccfd0 Fix buffer search invalid regexp indicator (#7795)
This PR fixes the issue with invalid regexp highlighting (red border)
when switching to the simple text searching mode (#7658).

Implementation details:

- `update_matches()` always relied on the caller to reset the
`query_contains_error` flag, which wasn't always the case. Now, it
resets the flag itself.

Release Notes:

- Fix issue with switching between invalid regexp and simple text buffer
search (#7658).

How it works now:


https://github.com/zed-industries/zed/assets/2101250/ac868a5d-5e2f-49a0-90fc-00e62a1d5ee8
2024-02-15 13:33:26 -05:00
Michal Příhoda
f01763a1fa Allow both integer and string request IDs in LSP (#7662)
Zed's LSP support expects request messages to have integer ID, Metals
LSP uses string. According to specification, both is acceptable:

interface RequestMessage extends Message {

	/**
	 * The request id.
	 */
	id: integer | string;

...
This pull requests modifies the types and serialization/deserialization
so that string IDs are accepted.

Release Notes:

- Make Zed LSP request ids compliant to the LSP specification
2024-02-15 20:26:23 +02:00
Thorsten Ball
2dffc5f6e1 Fix case-only renaming of files in project panel (#7835)
This is a follow-up to #7768 but now also fixes #5211.

Explanation is relatively simple: case-only renames previously failed
because while Zed would think that `foobar` and `FOOBAR` are different,
the filesystem would give us an file-already-exists error when renaming.

So what we're doing here is to check whether we're on a case-insensitive
filesystem and if so, we overwrite the old file.

Release Notes:

- Fixed case-only renaming of files in project panel.
([#5211](https://github.com/zed-industries/zed/issues/5211)).

Proof:



https://github.com/zed-industries/zed/assets/1185253/57d5063f-09d9-47b1-a2df-3d7edefca97d
2024-02-15 19:07:10 +01:00
Thorsten Ball
ed791c4fc1 Improve ruby highlighting (#7829)
Release Notes:

- Improved syntax-highlighting of identifiers in Ruby.

Before:

![screenshot-2024-02-15-14 40
19@2x](https://github.com/zed-industries/zed/assets/1185253/29fca0eb-7c0c-4aee-9f31-a8a3c6680cb9)

After:

![screenshot-2024-02-15-14 40
56@2x](https://github.com/zed-industries/zed/assets/1185253/2ce0e0c9-8689-4ff8-9f40-2ea5f6ccc2f6)
2024-02-15 15:12:41 +01:00
Kirill Bulatov
7c6b34cb73 Close modals and menus before dispathing actions (#7830)
Fixes https://github.com/zed-industries/zed/issues/7799 by forcing the
modal to close before dispatching the action.
While not needed specifically for this case, changed the context menus
to do the same, to be uniform — context menu actions seem to work
properly after this change too.

Release Notes:

- Fixed markdown preview action not working
([7799](https://github.com/zed-industries/zed/issues/7799))
2024-02-15 15:57:32 +02:00
Thorsten Ball
3921259b6c Extend Ruby word characters to include valid identifier chars (#7824)
In Ruby `_$=@!:?` can all be part of an identifier. If we don't have
them in this list, autocomplete doesn't work as expected.

See: https://gist.github.com/misfo/1072693

This fixes one part of #7819 but not the whole ticket.

Release Notes:

- Fixed completions in Ruby not working for identifiers that start or
end with special characters (e.g.: `@`)
2024-02-15 14:25:18 +01:00
Manohar_nalluri
e93dca5ec3 Incorrect file icons for .mjs, .mts, .cjs, .cts #7804 (#7815)
Release Notes:

- Added file extensions ([7804](https://github.com/zed-industries/zed/issues/7804))
2024-02-15 15:13:46 +02:00
Antonio Scandurra
c6626627c2 Remove existing gzip files before compressing dSYMs (#7818)
This should fix the build.

Release Notes:

- N/A
2024-02-15 11:32:08 +01:00
Antonio Scandurra
db86f4006e Staple notarization ticket to .dmg and .app bundle (#7775)
This should eliminate a pretty significant (multiple seconds) slowdown
that new users (or users after restarting their OS) have been
experiencing.

Previously, we would just notarize the application, which meant that
every user of the application had to perform an integrity check against
Apple's servers to ensure the app wasn't malicious.

With this commit, we are now using `xcrun stapler staple`, which
attaches the notarization ticket to both the app bundle as well as the
DMG. This should prevent users from needing to reach out to Apple's
notarization service in order to verify the app's integrity.

You can confirm the quarantine status of the application by running `ls
-l@` in `Terminal.app`:

    ls -l@ /Applications/Zed.app/Contents/MacOS/zed

Release Notes:

- Improved startup time when opening Zed for the first time or after
restarting the operating system.

Co-authored-by: Thorsten <thorsten@zed.dev>
Co-authored-by: bennetbo <bennetbo@gmx.de>
Co-authored-by: Martin Palma <m@palma.bz>
Co-authored-by: evrsen <146845123+evrsen@users.noreply.github.com>
2024-02-15 06:47:12 +01:00
Roman
41372a96ed Linux: Fix x11 crash (#7805)
Release Notes:

- N/A
2024-02-14 16:03:03 -08:00
Roman
f62baeda64 gpui: Add Wayland support (#7664)
This PR adds Wayland support to gpui using
[wayland-rs](https://github.com/Smithay/wayland-rs). It is based on
[#7598](https://github.com/zed-industries/zed/pull/7598).

It detects Wayland support at runtime by checking the existence of the
`WAYLAND_DISPLAY` environment variable. If it does not exist or is
empty, the X11 backend will be used. To use the X11 backend in a Wayland
session (for development purposes), you just need to unset
WAYLAND_DISPLAY (`WAYLAND_DISPLAY= cargo run ...`).

At the moment it only creates the window and renders the initial content
provided by `BladeRenderer`, so it can run "Hello world" example.


![image](https://github.com/zed-industries/zed/assets/40907255/1655bc64-4d36-4178-9851-bfe42f03f716)

Todo:
- [x] Add basic Wayland support.
- [x] Add window resizing.
- [x] Add window closing.
- [x] Add window updating.
- [ ] Implement input handling, fractional scaling, and support other
Wayland protocols.
- [ ] Implement all unimplemented todo!(linux).
- [ ] Add window decorations or use custom decorations (like on MacOS).
- [ ] Address other missing functionality.

Release Notes:
- N/A

---------

Co-authored-by: gabydd <gabydinnerdavid@gmail.com>
Co-authored-by: Mikayla Maki <mikayla@zed.dev>
2024-02-14 14:50:11 -08:00
gmorenz
6e6ae0ef21 Ignore 'DROP COLUMN enviroment' in typo checker (#7803)
In dropping a misspelled "enviroment" column in #7742, a new "typo" was
introduced breaking CI. This fixes that.

Release Notes:

- N/A
2024-02-14 15:34:58 -07:00
gmorenz
a3300aed31 Don't reinstall dependencies on arch linux (#7801)
Very minor quality of life update, passing the --needed flag to pacman
to skip reinstalling up to date dependencies.

Release Notes:

- N/A
2024-02-14 14:28:29 -08:00
Conrad Irwin
cbbc8cad84 Try and make collab deploys faster (#7746)
Use github machines and build on host instead of in container

Release Notes:

- N/A
2024-02-14 15:11:57 -07:00
Conrad Irwin
8e52cf1495 Add netrw bindings for vim (#7757)
This is now possible after #7647

Release Notes:

- Added vim bindings for project panel
([#4270](https://github.com/zed-industries/zed/issues/4270)).
2024-02-14 14:38:07 -07:00
Conrad Irwin
65a1938e52 panics (#7793)
Release Notes:

- Fix a panic in rename
([#7509](https://github.com/zed-industries/zed/issues/7509)).

---------

Co-authored-by: Max <max@zed.dev>
2024-02-14 14:36:40 -07:00
Conrad Irwin
855acb948c drop columns (#7742)
Blocked on: #7741

Release Notes:

- N/A
2024-02-14 14:30:48 -07:00
Leon
54f82eb166 Add double quote wrap in activate_python_virtual_environment (#7787)
Added double quote wrap in activate_python_virtual_environment to handle
spaces in path.
Would fail to find venv activate script when spaces exist.


Release Notes:

- Fixed venv_detection activation not finding directory if space is in
the path

![image](https://github.com/zed-industries/zed/assets/52263845/9bf95b92-c6d4-4e2c-9158-d91a41c10216)


- With changes

![image](https://github.com/zed-industries/zed/assets/52263845/93602ae7-d991-494b-89c3-5afcce556888)
2024-02-14 12:42:06 -08:00
Joseph T. Lyons
ac59b9b02f Add a line instructing users to include media screenshots (#7790)
Making media up for release notes / tweets is becoming very time
consuming. I spoke to Max and suggested we ask users to submit media for
their features, to reduce what we need to produce for tweets and such. I
dont know if this is the best way to signal it; I don't like adding more
to the PR template, but I'm not sure of a better way at the moment.


Release Notes:

- N/a
2024-02-14 15:36:56 -05:00
Dzmitry Malyshau
8f7a26f397 X11: Continuous Presentation (#7762)
Alternative to #7758, which doesn't involve adding a new trait method
`request_draw`.
Somehow, my whole screen goes blinking black with this when moving the
window, so not ready for landing.

Release Notes:
- N/A

---------

Co-authored-by: Mikayla Maki <mikayla@zed.dev>
2024-02-14 12:24:12 -08:00
Conrad Irwin
181f556269 Fewer nightlys (#7784)
Remove an extraneous /nightly/ from our dSYM paths

Release Notes:

- N/A
2024-02-14 11:40:02 -07:00
Adrian Garcia Badaracco
a02bdd0a9f Fix link in python.md (#7735) 2024-02-14 10:30:30 -08:00
Joseph T. Lyons
6876ea44ac Remove 0-patch requirement on main in bump-zed-minor-versions 2024-02-14 13:13:44 -05:00
Joseph T. Lyons
16849f48e6 v0.124.x dev 2024-02-14 13:10:07 -05:00
295 changed files with 12006 additions and 2969 deletions

View File

@@ -2,23 +2,23 @@ name: Feature Request
description: "Tip: open this issue template from within Zed with the `request feature` command palette action"
labels: ["admin read", "triage", "enhancement"]
body:
- type: checkboxes
attributes:
label: Check for existing issues
description: Check the backlog of issues to reduce the chances of creating duplicates; if an issue already exists, place a `+1` (👍) on it.
options:
- label: Completed
required: true
- type: textarea
attributes:
label: Describe the feature
description: A clear and concise description of what you want to happen.
validations:
- type: checkboxes
attributes:
label: Check for existing issues
description: Check the backlog of issues to reduce the chances of creating duplicates; if an issue already exists, place a `+1` (👍) on it.
options:
- label: Completed
required: true
- type: textarea
attributes:
label: |
If applicable, add mockups / screenshots to help present your vision of the feature
description: Drag images into the text input below
validations:
required: false
- type: textarea
attributes:
label: Describe the feature
description: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
attributes:
label: |
If applicable, add mockups / screenshots to help present your vision of the feature
description: Drag images into the text input below
validations:
required: false

View File

@@ -2,46 +2,46 @@ name: Language Support
description: Request language support
title: "<name_of_language> support"
labels:
[
"admin read",
"triage",
"enhancement",
"language",
"unsupported language",
"potential plugin",
]
[
"admin read",
"triage",
"enhancement",
"language",
"unsupported language",
"potential extension",
]
body:
- type: checkboxes
attributes:
label: Check for existing issues
description: Check the backlog of issues to reduce the chances of creating duplicates; if an issue already exists, place a `+1` (👍) on it.
options:
- label: Completed
required: true
- type: input
attributes:
label: Language
description: What language do you want support for?
placeholder: HTML
validations:
- type: checkboxes
attributes:
label: Check for existing issues
description: Check the backlog of issues to reduce the chances of creating duplicates; if an issue already exists, place a `+1` (👍) on it.
options:
- label: Completed
required: true
- type: input
attributes:
label: Tree Sitter parser link
description: If applicable, provide a link to the appropriate tree sitter parser. Look here first - https://tree-sitter.github.io/tree-sitter/#available-parsers
placeholder: https://github.com/tree-sitter/tree-sitter-html
validations:
required: false
- type: input
attributes:
label: Language server link
description: If applicable, provide a link to the appropriate language server. Look here first - https://microsoft.github.io/language-server-protocol/implementors/servers/
placeholder: https://github.com/Microsoft/vscode/tree/main/extensions/html-language-features/server
validations:
required: false
- type: textarea
attributes:
label: Misc notes
description: Provide any additional things the team should consider when adding support for this language
validations:
required: false
- type: input
attributes:
label: Language
description: What language do you want support for?
placeholder: HTML
validations:
required: true
- type: input
attributes:
label: Tree Sitter parser link
description: If applicable, provide a link to the appropriate tree sitter parser. Look here first - https://tree-sitter.github.io/tree-sitter/#available-parsers
placeholder: https://github.com/tree-sitter/tree-sitter-html
validations:
required: false
- type: input
attributes:
label: Language server link
description: If applicable, provide a link to the appropriate language server. Look here first - https://microsoft.github.io/language-server-protocol/implementors/servers/
placeholder: https://github.com/Microsoft/vscode/tree/main/extensions/html-language-features/server
validations:
required: false
- type: textarea
attributes:
label: Misc notes
description: Provide any additional things the team should consider when adding support for this language
validations:
required: false

View File

@@ -2,37 +2,37 @@ name: Bug Report
description: "Tip: open this issue template from within Zed with the `file bug report` command palette action"
labels: ["admin read", "triage", "defect"]
body:
- type: checkboxes
attributes:
label: Check for existing issues
description: Check the backlog of issues to reduce the chances of creating duplicates; if an issue already exists, place a `+1` (👍) on it.
options:
- label: Completed
required: true
- type: textarea
attributes:
label: Describe the bug / provide steps to reproduce it
description: A clear and concise description of what the bug is.
validations:
- type: checkboxes
attributes:
label: Check for existing issues
description: Check the backlog of issues to reduce the chances of creating duplicates; if an issue already exists, place a `+1` (👍) on it.
options:
- label: Completed
required: true
- type: textarea
id: environment
attributes:
label: Environment
description: Run the `copy system specs into clipboard` command palette action and paste the output in the field below.
validations:
required: true
- type: textarea
attributes:
label: If applicable, add mockups / screenshots to help explain present your vision of the feature
description: Drag issues into the text input below
validations:
required: false
- type: textarea
attributes:
label: |
If applicable, attach your `~/Library/Logs/Zed/Zed.log` file to this issue.
If you only need the most recent lines, you can run the `zed: open log` command palette action to see the last 1000.
description: Drag Zed.log into the text input below
validations:
required: false
- type: textarea
attributes:
label: Describe the bug / provide steps to reproduce it
description: A clear and concise description of what the bug is.
validations:
required: true
- type: textarea
id: environment
attributes:
label: Environment
description: Run the `copy system specs into clipboard` command palette action and paste the output in the field below.
validations:
required: true
- type: textarea
attributes:
label: If applicable, add mockups / screenshots to help explain present your vision of the feature
description: Drag issues into the text input below
validations:
required: false
- type: textarea
attributes:
label: |
If applicable, attach your `~/Library/Logs/Zed/Zed.log` file to this issue.
If you only need the most recent lines, you can run the `zed: open log` command palette action to see the last 1000.
description: Drag Zed.log into the text input below
validations:
required: false

View File

@@ -1,10 +1,13 @@
contact_links:
- name: Top-Ranking Issues
url: https://github.com/zed-industries/zed/issues/5393
about: See an overview of the most popular Zed issues
- name: Platform Support
url: https://github.com/zed-industries/zed/issues/5391
about: A quick note on platform support
- name: Positive Feedback
url: https://github.com/zed-industries/zed/discussions/5397
about: A central location for kind words about Zed
- name: Theme Request
url: https://github.com/zed-industries/extensions/issues/new/choose
about: Request a theme in the extensions repository
- name: Top-Ranking Issues
url: https://github.com/zed-industries/zed/issues/5393
about: See an overview of the most popular Zed issues
- name: Platform Support
url: https://github.com/zed-industries/zed/issues/5391
about: A quick note on platform support
- name: Positive Feedback
url: https://github.com/zed-industries/zed/discussions/5397
about: A central location for kind words about Zed

View File

@@ -2,14 +2,14 @@ name: "Check formatting"
description: "Checks code formatting use cargo fmt"
runs:
using: "composite"
steps:
- name: cargo fmt
shell: bash -euxo pipefail {0}
run: cargo fmt --all -- --check
using: "composite"
steps:
- name: cargo fmt
shell: bash -euxo pipefail {0}
run: cargo fmt --all -- --check
- name: Find modified migrations
shell: bash -euxo pipefail {0}
run: |
export SQUAWK_GITHUB_TOKEN=${{ github.token }}
. ./script/squawk
- name: Find modified migrations
shell: bash -euxo pipefail {0}
run: |
export SQUAWK_GITHUB_TOKEN=${{ github.token }}
. ./script/squawk

View File

@@ -2,22 +2,22 @@ name: "Run tests"
description: "Runs the tests"
runs:
using: "composite"
steps:
- name: Install Rust
shell: bash -euxo pipefail {0}
run: |
cargo install cargo-nextest
using: "composite"
steps:
- name: Install Rust
shell: bash -euxo pipefail {0}
run: |
cargo install cargo-nextest
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "18"
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "18"
- name: Limit target directory size
shell: bash -euxo pipefail {0}
run: script/clear-target-dir-if-larger-than 100
- name: Limit target directory size
shell: bash -euxo pipefail {0}
run: script/clear-target-dir-if-larger-than 100
- name: Run tests
shell: bash -euxo pipefail {0}
run: cargo nextest run --workspace --no-fail-fast
- name: Run tests
shell: bash -euxo pipefail {0}
run: cargo nextest run --workspace --no-fail-fast

View File

@@ -7,3 +7,5 @@ Release Notes:
**or**
- N/A
Optionally, include screenshots / media showcasing your addition that can be included in the release notes.

View File

@@ -1,206 +1,238 @@
name: CI
on:
push:
branches:
- main
- "v[0-9]+.[0-9]+.x"
tags:
- "v*"
pull_request:
branches:
- "**"
push:
branches:
- main
- "v[0-9]+.[0-9]+.x"
tags:
- "v*"
pull_request:
branches:
- "**"
concurrency:
# Allow only one workflow per any non-`main` branch.
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }}
cancel-in-progress: true
# Allow only one workflow per any non-`main` branch.
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: 1
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: 1
jobs:
style:
name: Check formatting and spelling
runs-on:
- self-hosted
- test
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
fetch-depth: 0
style:
name: Check formatting and spelling
runs-on:
- self-hosted
- test
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
fetch-depth: 0
- name: Set up default .cargo/config.toml
run: cp ./.cargo/ci-config.toml ~/.cargo/config.toml
- name: Remove untracked files
run: git clean -df
- name: Check spelling
run: |
if ! which typos > /dev/null; then
cargo install typos-cli
fi
typos
- name: Set up default .cargo/config.toml
run: cp ./.cargo/ci-config.toml ~/.cargo/config.toml
- name: Run style checks
uses: ./.github/actions/check_style
- name: Check spelling
run: |
if ! which typos > /dev/null; then
cargo install typos-cli
fi
typos
- name: Ensure fresh merge
shell: bash -euxo pipefail {0}
run: |
if [ -z "$GITHUB_BASE_REF" ];
then
echo "BUF_BASE_BRANCH=$(git merge-base origin/main HEAD)" >> $GITHUB_ENV
else
git checkout -B temp
git merge -q origin/$GITHUB_BASE_REF -m "merge main into temp"
echo "BUF_BASE_BRANCH=$GITHUB_BASE_REF" >> $GITHUB_ENV
fi
- name: Run style checks
uses: ./.github/actions/check_style
- uses: bufbuild/buf-setup-action@v1
- uses: bufbuild/buf-breaking-action@v1
with:
input: "crates/rpc/proto/"
against: "https://github.com/${GITHUB_REPOSITORY}.git#branch=${BUF_BASE_BRANCH},subdir=crates/rpc/proto/"
- name: Ensure fresh merge
shell: bash -euxo pipefail {0}
run: |
if [ -z "$GITHUB_BASE_REF" ];
then
echo "BUF_BASE_BRANCH=$(git merge-base origin/main HEAD)" >> $GITHUB_ENV
else
git checkout -B temp
git merge -q origin/$GITHUB_BASE_REF -m "merge main into temp"
echo "BUF_BASE_BRANCH=$GITHUB_BASE_REF" >> $GITHUB_ENV
fi
macos_tests:
name: (macOS) Run Clippy and tests
runs-on:
- self-hosted
- test
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
- uses: bufbuild/buf-setup-action@v1
- uses: bufbuild/buf-breaking-action@v1
with:
input: "crates/rpc/proto/"
against: "https://github.com/${GITHUB_REPOSITORY}.git#branch=${BUF_BASE_BRANCH},subdir=crates/rpc/proto/"
- name: cargo clippy
shell: bash -euxo pipefail {0}
run: script/clippy
macos_tests:
name: (macOS) Run Clippy and tests
runs-on:
- self-hosted
- test
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
- name: Run tests
uses: ./.github/actions/run_tests
- name: cargo clippy
shell: bash -euxo pipefail {0}
run: script/clippy
- name: Build collab
run: cargo build -p collab
- name: Run tests
uses: ./.github/actions/run_tests
- name: Build other binaries
run: cargo build --workspace --bins --all-features
- name: Build collab
run: cargo build -p collab
# todo!(linux): Actually run the tests
linux_tests:
name: (Linux) Run Clippy and tests
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
- name: Build other binaries and features
run: cargo build --workspace --bins --all-features; cargo check -p gpui --features "macos-blade"
- name: Restore from cache
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/rust-toolchain.toml') }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-${{ hashFiles('**/rust-toolchain.toml') }}-
# todo!(linux): Actually run the tests
linux_tests:
name: (Linux) Run Clippy and tests
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
- name: configure linux
shell: bash -euxo pipefail {0}
run: script/linux
- name: Restore from cache
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/rust-toolchain.toml') }}-${{ hashFiles('**/Cargo.lock') }}
- name: cargo clippy
shell: bash -euxo pipefail {0}
run: script/clippy
- name: configure linux
shell: bash -euxo pipefail {0}
run: script/linux
- name: Build Zed
run: cargo build -p zed
bundle:
name: Bundle app
runs-on:
- self-hosted
- bundle
if: ${{ startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-build-dmg') }}
needs: [macos_tests, linux_tests]
- name: cargo clippy
shell: bash -euxo pipefail {0}
run: script/clippy
- name: Build Zed
run: cargo build -p zed
# todo!(windows): Actually run the tests
windows_tests:
name: (Windows) Run Clippy and tests
runs-on: windows-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
- name: Restore from cache
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/rust-toolchain.toml') }}-${{ hashFiles('**/Cargo.lock') }}
# todo!(windows): Actually run clippy
#- name: cargo clippy
# shell: bash -euxo pipefail {0}
# run: script/clippy
- name: Build Zed
run: cargo build -p zed
bundle:
name: Bundle macOS app
runs-on:
- self-hosted
- bundle
if: ${{ startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-build-dmg') }}
needs: [macos_tests]
env:
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
APPLE_NOTARIZATION_USERNAME: ${{ secrets.APPLE_NOTARIZATION_USERNAME }}
APPLE_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_NOTARIZATION_PASSWORD }}
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}
DIGITALOCEAN_SPACES_SECRET_KEY: ${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }}
steps:
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "18"
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
- name: Limit target directory size
run: script/clear-target-dir-if-larger-than 100
- name: Determine version and release channel
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
run: |
set -eu
version=$(script/get-crate-version zed)
channel=$(cat crates/zed/RELEASE_CHANNEL)
echo "Publishing version: ${version} on release channel ${channel}"
echo "RELEASE_CHANNEL=${channel}" >> $GITHUB_ENV
expected_tag_name=""
case ${channel} in
stable)
expected_tag_name="v${version}";;
preview)
expected_tag_name="v${version}-pre";;
nightly)
expected_tag_name="v${version}-nightly";;
*)
echo "can't publish a release on channel ${channel}"
exit 1;;
esac
if [[ $GITHUB_REF_NAME != $expected_tag_name ]]; then
echo "invalid release tag ${GITHUB_REF_NAME}. expected ${expected_tag_name}"
exit 1
fi
- name: Generate license file
run: script/generate-licenses
- name: Create app bundle
run: script/bundle
- name: Upload app bundle to workflow run if main branch or specific label
uses: actions/upload-artifact@v3
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-build-dmg') }}
with:
name: Zed_${{ github.event.pull_request.head.sha || github.sha }}.dmg
path: target/release/Zed.dmg
- uses: softprops/action-gh-release@v1
name: Upload app bundle to release
if: ${{ env.RELEASE_CHANNEL == 'preview' || env.RELEASE_CHANNEL == 'stable' }}
with:
draft: true
prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}
files: target/release/Zed.dmg
body: ""
env:
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
APPLE_NOTARIZATION_USERNAME: ${{ secrets.APPLE_NOTARIZATION_USERNAME }}
APPLE_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_NOTARIZATION_PASSWORD }}
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}
DIGITALOCEAN_SPACES_SECRET_KEY: ${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }}
steps:
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "18"
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
- name: Limit target directory size
run: script/clear-target-dir-if-larger-than 100
- name: Determine version and release channel
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
run: |
set -eu
version=$(script/get-crate-version zed)
channel=$(cat crates/zed/RELEASE_CHANNEL)
echo "Publishing version: ${version} on release channel ${channel}"
echo "RELEASE_CHANNEL=${channel}" >> $GITHUB_ENV
expected_tag_name=""
case ${channel} in
stable)
expected_tag_name="v${version}";;
preview)
expected_tag_name="v${version}-pre";;
nightly)
expected_tag_name="v${version}-nightly";;
*)
echo "can't publish a release on channel ${channel}"
exit 1;;
esac
if [[ $GITHUB_REF_NAME != $expected_tag_name ]]; then
echo "invalid release tag ${GITHUB_REF_NAME}. expected ${expected_tag_name}"
exit 1
fi
- name: Generate license file
run: script/generate-licenses
- name: Create app bundle
run: script/bundle
- name: Upload app bundle to workflow run if main branch or specific label
uses: actions/upload-artifact@v3
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-build-dmg') }}
with:
name: Zed_${{ github.event.pull_request.head.sha || github.sha }}.dmg
path: target/release/Zed.dmg
- uses: softprops/action-gh-release@v1
name: Upload app bundle to release
if: ${{ env.RELEASE_CHANNEL == 'preview' || env.RELEASE_CHANNEL == 'stable' }}
with:
draft: true
prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}
files: target/release/Zed.dmg
body: ""
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

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

View File

@@ -45,8 +45,18 @@ jobs:
submodules: "recursive"
fetch-depth: 0
- name: Install cargo nextest
shell: bash -euxo pipefail {0}
run: |
cargo install cargo-nextest
- name: Limit target directory size
shell: bash -euxo pipefail {0}
run: script/clear-target-dir-if-larger-than 100
- name: Run tests
uses: ./.github/actions/run_tests
shell: bash -euxo pipefail {0}
run: cargo nextest run --package collab --no-fail-fast
publish:
name: Publish collab server image
@@ -63,9 +73,6 @@ jobs:
- name: Sign into DigitalOcean docker registry
run: doctl registry login
- name: Prune Docker system
run: docker system prune --filter 'until=720h' -f
- name: Checkout repo
uses: actions/checkout@v4
with:
@@ -78,6 +85,9 @@ jobs:
- name: Publish docker image
run: docker push registry.digitalocean.com/zed/collab:${GITHUB_SHA}
- name: Prune Docker system
run: docker system prune --filter 'until=72h' -f
deploy:
name: Deploy new server image
needs:
@@ -90,22 +100,26 @@ jobs:
- name: Sign into Kubernetes
run: doctl kubernetes cluster kubeconfig save --expiry-seconds 600 ${{ secrets.CLUSTER_NAME }}
- name: Determine namespace
- name: Start rollout
run: |
set -eu
if [[ $GITHUB_REF_NAME = "collab-production" ]]; then
echo "Deploying collab:$GITHUB_SHA to production"
echo "KUBE_NAMESPACE=production" >> $GITHUB_ENV
export ZED_KUBE_NAMESPACE=production
elif [[ $GITHUB_REF_NAME = "collab-staging" ]]; then
echo "Deploying collab:$GITHUB_SHA to staging"
echo "KUBE_NAMESPACE=staging" >> $GITHUB_ENV
export ZED_KUBE_NAMESPACE=staging
else
echo "cowardly refusing to deploy from an unknown branch"
exit 1
fi
- name: Start rollout
run: kubectl -n "$KUBE_NAMESPACE" set image deployment/collab collab=registry.digitalocean.com/zed/collab:${GITHUB_SHA}
echo "Deploying collab:$GITHUB_SHA to $ZED_KUBE_NAMESPACE"
- name: Wait for rollout to finish
run: kubectl -n "$KUBE_NAMESPACE" rollout status deployment/collab
source script/lib/deploy-helpers.sh
export_vars_for_environment $ZED_KUBE_NAMESPACE
export ZED_DO_CERTIFICATE_ID=$(doctl compute certificate list --format ID --no-header)
export ZED_IMAGE_ID="registry.digitalocean.com/zed/collab:${GITHUB_SHA}"
envsubst < crates/collab/k8s/collab.template.yml | kubectl apply -f -
kubectl -n "$ZED_KUBE_NAMESPACE" rollout status deployment/collab --watch
echo "deployed collab.template.yml to ${ZED_KUBE_NAMESPACE}"

View File

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

View File

@@ -1,98 +1,98 @@
name: Release Nightly
on:
schedule:
# Fire every day at 7:00am UTC (Roughly before EU workday and after US workday)
- cron: "0 7 * * *"
push:
tags:
- "nightly"
schedule:
# Fire every day at 7:00am UTC (Roughly before EU workday and after US workday)
- cron: "0 7 * * *"
push:
tags:
- "nightly"
env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: 1
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: 1
jobs:
style:
name: Check formatting and Clippy lints
if: github.repository_owner == 'zed-industries'
runs-on:
- self-hosted
- test
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
fetch-depth: 0
style:
name: Check formatting and Clippy lints
if: github.repository_owner == 'zed-industries'
runs-on:
- self-hosted
- test
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
fetch-depth: 0
- name: Run style checks
uses: ./.github/actions/check_style
- name: Run style checks
uses: ./.github/actions/check_style
- name: Run clippy
shell: bash -euxo pipefail {0}
run: script/clippy
tests:
name: Run tests
if: github.repository_owner == 'zed-industries'
runs-on:
- self-hosted
- test
needs: style
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
- name: Run clippy
shell: bash -euxo pipefail {0}
run: script/clippy
tests:
name: Run tests
if: github.repository_owner == 'zed-industries'
runs-on:
- self-hosted
- test
needs: style
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
- name: Run tests
uses: ./.github/actions/run_tests
- name: Run tests
uses: ./.github/actions/run_tests
bundle:
name: Bundle app
if: github.repository_owner == 'zed-industries'
runs-on:
- self-hosted
- bundle
needs: tests
env:
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
APPLE_NOTARIZATION_USERNAME: ${{ secrets.APPLE_NOTARIZATION_USERNAME }}
APPLE_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_NOTARIZATION_PASSWORD }}
DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}
DIGITALOCEAN_SPACES_SECRET_KEY: ${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }}
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
steps:
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "18"
bundle:
name: Bundle app
if: github.repository_owner == 'zed-industries'
runs-on:
- self-hosted
- bundle
needs: tests
env:
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
APPLE_NOTARIZATION_USERNAME: ${{ secrets.APPLE_NOTARIZATION_USERNAME }}
APPLE_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_NOTARIZATION_PASSWORD }}
DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}
DIGITALOCEAN_SPACES_SECRET_KEY: ${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }}
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
steps:
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "18"
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
submodules: "recursive"
- name: Limit target directory size
run: script/clear-target-dir-if-larger-than 100
- name: Limit target directory size
run: script/clear-target-dir-if-larger-than 100
- name: Set release channel to nightly
run: |
set -eu
version=$(git rev-parse --short HEAD)
echo "Publishing version: ${version} on release channel nightly"
echo "nightly" > crates/zed/RELEASE_CHANNEL
- name: Set release channel to nightly
run: |
set -eu
version=$(git rev-parse --short HEAD)
echo "Publishing version: ${version} on release channel nightly"
echo "nightly" > crates/zed/RELEASE_CHANNEL
- name: Generate license file
run: script/generate-licenses
- name: Generate license file
run: script/generate-licenses
- name: Create app bundle
run: script/bundle
- name: Create app bundle
run: script/bundle
- name: Upload Zed Nightly
run: script/upload-nightly
- name: Upload Zed Nightly
run: script/upload-nightly

View File

@@ -1,18 +1,18 @@
on:
schedule:
- cron: "0 */12 * * *"
workflow_dispatch:
schedule:
- cron: "0 */12 * * *"
workflow_dispatch:
jobs:
update_top_ranking_issues:
runs-on: ubuntu-latest
if: github.repository_owner == 'zed-industries'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
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 5393 --github-token ${{ secrets.GITHUB_TOKEN }} --prod
update_top_ranking_issues:
runs-on: ubuntu-latest
if: github.repository_owner == 'zed-industries'
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 5393 --github-token ${{ secrets.GITHUB_TOKEN }} --prod

View File

@@ -1,18 +1,18 @@
on:
schedule:
- cron: "0 15 * * *"
workflow_dispatch:
schedule:
- cron: "0 15 * * *"
workflow_dispatch:
jobs:
update_top_ranking_issues:
runs-on: ubuntu-latest
if: github.repository_owner == 'zed-industries'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
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
update_top_ranking_issues:
runs-on: ubuntu-latest
if: github.repository_owner == 'zed-industries'
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

5
.gitignore vendored
View File

@@ -5,12 +5,8 @@
.DS_Store
/plugins/bin
/script/node_modules
/styles/node_modules
/styles/src/types/zed.ts
/crates/theme/schemas/theme.json
/crates/collab/static/styles.css
/crates/collab/.admins.json
/vendor/bin
/assets/*licenses.md
**/venv
.build
@@ -25,3 +21,4 @@ DerivedData/
**/*.db
.pytest_cache
.venv
.blob_store

View File

@@ -11,6 +11,8 @@
Antonio Scandurra <me@as-cii.com>
Antonio Scandurra <me@as-cii.com> <antonio@zed.dev>
Christian Bergschneider <christian.bergschneider@gmx.de>
Christian Bergschneider <christian.bergschneider@gmx.de> <magiclake@gmx.de>
Conrad Irwin <conrad@zed.dev>
Conrad Irwin <conrad@zed.dev> <conrad.irwin@gmail.com>
Greg Morenz <greg-morenz@droid.cafe>

View File

@@ -1,6 +1,16 @@
{
"JSON": {
"tab_size": 4
"languages": {
"Markdown": {
"tab_size": 2,
"formatter": "prettier"
},
"TOML": {
"formatter": "prettier",
"format_on_save": "off"
},
"YAML": {
"formatter": "prettier"
}
},
"formatter": "auto"
}

1516
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -63,6 +63,8 @@ members = [
"crates/rich_text",
"crates/rope",
"crates/rpc",
"crates/task",
"crates/tasks_ui",
"crates/search",
"crates/semantic_index",
"crates/settings",
@@ -153,6 +155,8 @@ release_channel = { path = "crates/release_channel" }
rich_text = { path = "crates/rich_text" }
rope = { path = "crates/rope" }
rpc = { path = "crates/rpc" }
task = { path = "crates/task" }
tasks_ui = { path = "crates/tasks_ui" }
search = { path = "crates/search" }
semantic_index = { path = "crates/semantic_index" }
settings = { path = "crates/settings" }
@@ -181,6 +185,9 @@ anyhow = "1.0.57"
async-compression = { version = "0.4", features = ["gzip", "futures-io"] }
async-tar = "0.4.2"
async-trait = "0.1"
blade-graphics = { git = "https://github.com/kvark/blade", rev = "e9d93a4d41f3946a03ffb76136290d6ccf7f2b80" }
blade-macros = { git = "https://github.com/kvark/blade", rev = "e9d93a4d41f3946a03ffb76136290d6ccf7f2b80" }
blade-rwh = { package = "raw-window-handle", version = "0.5" }
chrono = { version = "0.4", features = ["serde"] }
ctor = "0.2.6"
derive_more = "0.99.17"
@@ -190,11 +197,9 @@ git2 = { version = "0.15", default-features = false }
globset = "0.4"
indoc = "1"
# We explicitly disable a http2 support in isahc.
isahc = { version = "1.7.2", default-features = false, features = [
"static-curl",
"text-decoding",
] }
isahc = { version = "1.7.2", default-features = false, features = ["static-curl", "text-decoding"] }
lazy_static = "1.4.0"
linkify = "0.10.0"
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
ordered-float = "2.1.1"
parking_lot = "0.11.1"
@@ -208,13 +213,11 @@ regex = "1.5"
rusqlite = { version = "0.29.0", features = ["blob", "array", "modern_sqlite"] }
rust-embed = { version = "8.0", features = ["include-exclude"] }
schemars = "0.8"
semver = "1.0"
serde = { version = "1.0", features = ["derive", "rc"] }
serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
serde_json = { version = "1.0", features = ["preserve_order", "raw_value"] }
serde_json_lenient = { version = "0.1", features = [
"preserve_order",
"raw_value",
] }
serde_json_lenient = { version = "0.1", features = ["preserve_order", "raw_value"] }
serde_repr = "0.1"
smallvec = { version = "1.6", features = ["union"] }
smol = "1.2"
@@ -223,17 +226,18 @@ sysinfo = "0.29.10"
tempfile = "3.9.0"
thiserror = "1.0.29"
tiktoken-rs = "0.5.7"
time = { version = "0.3", features = ["serde", "serde-well-known"] }
toml = "0.5"
time = { version = "0.3", features = ["serde", "serde-well-known", "formatting"] }
toml = "0.8"
tree-sitter = { version = "0.20", features = ["wasm"] }
tree-sitter-astro = { git = "https://github.com/virchau13/tree-sitter-astro.git", rev = "e924787e12e8a03194f36a113290ac11d6dc10f3" }
tree-sitter-bash = { git = "https://github.com/tree-sitter/tree-sitter-bash", rev = "7331995b19b8f8aba2d5e26deb51d2195c18bc94" }
tree-sitter-beancount = { git = "https://github.com/polarmutex/tree-sitter-beancount", rev = "da1bf8c6eb0ae7a97588affde7227630bcd678b6" }
tree-sitter-c = "0.20.1"
tree-sitter-clojure = { git = "https://github.com/prcastro/tree-sitter-clojure", branch = "update-ts" }
tree-sitter-c-sharp = { git = "https://github.com/tree-sitter/tree-sitter-c-sharp", rev = "dd5e59721a5f8dae34604060833902b882023aaf" }
tree-sitter-cpp = { git = "https://github.com/tree-sitter/tree-sitter-cpp", rev = "f44509141e7e483323d2ec178f2d2e6c0fc041c1" }
tree-sitter-css = { git = "https://github.com/tree-sitter/tree-sitter-css", rev = "769203d0f9abe1a9a691ac2b9fe4bb4397a73c51" }
tree-sitter-dockerfile = { git = "https://github.com/camdencheek/tree-sitter-dockerfile", rev = "33e22c33bcdbfc33d42806ee84cfd0b1248cc392" }
tree-sitter-dart = { git = "https://github.com/agent3bood/tree-sitter-dart", rev = "48934e3bf757a9b78f17bdfaa3e2b4284656fdc7" }
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-embedded-template = "0.20.0"
@@ -244,7 +248,7 @@ tree-sitter-glsl = { git = "https://github.com/theHamsta/tree-sitter-glsl", rev
tree-sitter-go = { git = "https://github.com/tree-sitter/tree-sitter-go", rev = "aeb2f33b366fd78d5789ff104956ce23508b85db" }
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-haskell = { git = "https://github.com/tree-sitter/tree-sitter-haskell", rev = "8a99848fc734f9c4ea523b3f2a07df133cbbcec2" }
tree-sitter-hcl = { git = "https://github.com/MichaHoffmann/tree-sitter-hcl", rev = "v1.1.0" }
tree-sitter-heex = { git = "https://github.com/phoenixframework/tree-sitter-heex", rev = "2e1348c3cf2c9323e87c2744796cf3f3868aa82a" }
tree-sitter-html = "0.19.0"
@@ -252,7 +256,7 @@ tree-sitter-json = { git = "https://github.com/tree-sitter/tree-sitter-json", re
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-nu = { git = "https://github.com/nushell/tree-sitter-nu", rev = "7dd29f9616822e5fc259f5b4ae6c4ded9a71a132" }
tree-sitter-ocaml = { git = "https://github.com/tree-sitter/tree-sitter-ocaml", rev = "4abfdc1c7af2c6c77a370aee974627be1c285b3b" }
tree-sitter-php = "0.21.1"
tree-sitter-prisma-io = { git = "https://github.com/victorhqc/tree-sitter-prisma" }
@@ -263,10 +267,10 @@ tree-sitter-racket = { git = "https://github.com/zed-industries/tree-sitter-rack
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-svelte = { git = "https://github.com/Himujjal/tree-sitter-svelte", rev = "bd60db7d3d06f89b6ec3b287c9a6e9190b5564bd" }
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-uiua = { git = "https://github.com/shnarazk/tree-sitter-uiua", rev = "21dc2db39494585bf29a3f86d5add6e9d11a22ba" }
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" }
@@ -274,6 +278,8 @@ unindent = "0.1.7"
url = "2.2"
uuid = { version = "1.1.2", features = ["v4"] }
wasmtime = "16"
which = "6.0.0"
sys-locale = "0.3.1"
[patch.crates-io]
tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "1d8975319c2d5de1bf710e7e21db25b0eee4bc66" }

View File

@@ -11,6 +11,7 @@ ARG GITHUB_SHA
ENV GITHUB_SHA=$GITHUB_SHA
RUN --mount=type=cache,target=./script/node_modules \
--mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/usr/local/cargo/git \
--mount=type=cache,target=./target \
cargo build --release --package collab --bin collab

View File

@@ -1,2 +1,3 @@
collab: cd crates/collab && RUST_LOG=${RUST_LOG:-warn,collab=info} cargo run serve
collab: RUST_LOG=${RUST_LOG:-warn,collab=info} cargo run --package=collab serve
livekit: livekit-server --dev
blob_store: MINIO_ROOT_USER=the-blob-store-access-key MINIO_ROOT_PASSWORD=the-blob-store-secret-key minio server .blob_store

View File

@@ -21,7 +21,8 @@ brew install zed
## Developing Zed
- [Building Zed](./docs/src/developing_zed__building_zed.md)
- [Building Zed for macOS](./docs/src/developing_zed__building_zed_macos.md)
- [Building Zed for Linux](./docs/src/developing_zed__building_zed_linux.md)
- [Running Collaboration Locally](./docs/src/developing_zed__local_collaboration.md)
## Contributing

View File

@@ -16,10 +16,12 @@
"bmp": "image",
"c": "code",
"cc": "code",
"cjs": "code",
"conf": "settings",
"cpp": "code",
"css": "css",
"csv": "storage",
"cts": "typescript",
"dat": "storage",
"db": "storage",
"dbf": "storage",
@@ -73,6 +75,7 @@
"ldf": "storage",
"lock": "lock",
"log": "log",
"lua": "lua",
"m4a": "audio",
"m4v": "video",
"md": "document",
@@ -80,12 +83,14 @@
"mdf": "storage",
"mdx": "document",
"mkv": "video",
"mjs": "code",
"mka": "audio",
"ml": "ocaml",
"mli": "ocaml",
"mov": "video",
"mp3": "audio",
"mp4": "video",
"mts": "typescript",
"myd": "storage",
"myi": "storage",
"odp": "document",
@@ -206,8 +211,11 @@
"log": {
"icon": "icons/file_icons/info.svg"
},
"lua": {
"icon": "icons/file_icons/lua.svg"
},
"ocaml": {
"icon": "icons/file_icons/ocaml.svg"
"icon": "icons/file_icons/ocaml.svg"
},
"phoenix": {
"icon": "icons/file_icons/phoenix.svg"

View File

@@ -0,0 +1,10 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_34_7)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.2625 7.00157C12.2625 4.09565 9.90677 1.73994 7.00085 1.73994C4.09494 1.73994 1.73923 4.09565 1.73923 7.00157C1.73923 9.90749 4.09494 12.2632 7.00085 12.2632C9.90677 12.2632 12.2625 9.90749 12.2625 7.00157ZM10.7213 4.82205C10.7213 3.97095 10.0313 3.281 9.18023 3.281C8.32913 3.281 7.63917 3.97095 7.63917 4.82205C7.63917 5.67315 8.32913 6.3631 9.18023 6.3631C10.0313 6.3631 10.7213 5.67315 10.7213 4.82205ZM11.9704 11.3383L12.2849 11.6123C12.4856 11.3819 12.6732 11.1354 12.8424 10.8799L12.4947 10.6496C12.3355 10.8899 12.1591 11.1216 11.9704 11.3383ZM3.09019 12.8218C2.83557 12.6512 2.59021 12.4623 2.36091 12.2602L2.63665 11.9473C2.85224 12.1372 3.08295 12.3149 3.3224 12.4754L3.09019 12.8218ZM13.2533 9.09685L13.6488 9.22944C13.5516 9.51944 13.4341 9.806 13.2995 10.0812L12.9248 9.89792C13.0514 9.63912 13.1619 9.3696 13.2533 9.09685ZM11.3601 11.9516L11.6357 12.2647C11.4062 12.4667 11.1607 12.6555 10.9061 12.8259L10.6741 12.4793C10.9135 12.3191 11.1443 12.1415 11.3601 11.9516ZM9.92464 12.9125L10.1093 13.2865C9.83502 13.4219 9.54901 13.5406 9.25919 13.6391L9.12488 13.2442C9.39754 13.1515 9.66662 13.0399 9.92464 12.9125ZM0.698663 10.0754C0.564769 9.80041 0.447705 9.51376 0.350717 9.22346L0.746321 9.09128C0.837577 9.36442 0.947715 9.63411 1.0737 9.89286L0.698663 10.0754ZM4.7358 13.6379C4.44725 13.539 4.16148 13.4197 3.88645 13.2833L4.07178 12.9096C4.33065 13.038 4.59955 13.1503 4.871 13.2433L4.7358 13.6379ZM7.50907 0.425028L7.54103 0.00914471C7.84635 0.0326099 8.15299 0.0767322 8.45243 0.140276L8.36583 0.548303C8.08435 0.488557 7.79609 0.447081 7.50907 0.425028ZM8.28868 13.469L8.37042 13.878C8.07006 13.9381 7.76308 13.9787 7.45798 13.999L7.4304 13.5828C7.71737 13.5638 8.00614 13.5255 8.28868 13.469ZM6.56475 13.5831L6.5374 13.9993C6.23179 13.9792 5.92469 13.9385 5.62462 13.8783L5.70667 13.4693C5.98876 13.5259 6.27745 13.5642 6.56475 13.5831ZM1.71207 11.6074C1.51131 11.3768 1.32385 11.1303 1.15489 10.8748L1.5028 10.6447C1.66168 10.885 1.83795 11.1167 2.0267 11.3336L1.71207 11.6074ZM1.54706 3.2931L1.20185 3.05901C1.3739 2.80528 1.56421 2.56096 1.76748 2.33281L2.07891 2.61029C1.8878 2.82479 1.70885 3.05452 1.54706 3.2931ZM13.5829 7.4008L13.9993 7.42603C13.9807 7.73163 13.9418 8.03889 13.8834 8.33925L13.474 8.25969C13.5289 7.97721 13.5655 7.68824 13.5829 7.4008ZM2.69596 2.00373L2.42386 1.68762C2.65562 1.48813 2.90314 1.30202 3.15957 1.13448L3.38773 1.48365C3.14661 1.64119 2.91386 1.81619 2.69596 2.00373ZM4.14194 1.05867L3.96141 0.682649C4.23716 0.550267 4.52444 0.434775 4.8153 0.339358L4.9453 0.735689C4.67165 0.825447 4.40135 0.934116 4.14194 1.05867ZM6.64336 0.415732C6.35624 0.431589 6.06708 0.466692 5.78392 0.520066L5.70665 0.110177C6.00769 0.0534275 6.3151 0.0161137 6.62038 -0.00073251L6.64336 0.415732ZM9.33849 0.390492C9.62584 0.492485 9.91028 0.614901 10.1839 0.754324L9.99453 1.12595C9.73698 0.994735 9.46932 0.879534 9.19896 0.783565L9.33849 0.390492ZM0.116692 8.3334C0.0582983 8.03277 0.0192821 7.72556 0.000734408 7.42027L0.417082 7.39499C0.434525 7.68212 0.471213 7.97108 0.526144 8.25385L0.116692 8.3334ZM13.8694 5.59362C13.9308 5.89342 13.9729 6.20022 13.9945 6.50553L13.5785 6.53503C13.5581 6.24787 13.5185 5.95929 13.4607 5.67733L13.8694 5.59362ZM0.421578 6.52929L0.00551996 6.49964C0.0272978 6.19421 0.0697176 5.88734 0.131602 5.58753L0.540095 5.67184C0.48192 5.95369 0.442046 6.24218 0.421578 6.52929ZM13.268 3.85795C13.4053 4.1315 13.5257 4.41681 13.6258 4.70597L13.2317 4.84251C13.1375 4.57059 13.0243 4.30226 12.8952 4.045L13.268 3.85795ZM0.770731 4.83746L0.376902 4.70007C0.477381 4.41205 0.598242 4.12695 0.736138 3.85268L1.10878 4.04006C0.979007 4.29819 0.86526 4.56647 0.770731 4.83746ZM10.7213 1.73979C10.7213 0.888612 11.4113 0.198592 12.2625 0.198592C13.1137 0.198592 13.8037 0.888612 13.8037 1.73979C13.8037 2.59098 13.1137 3.281 12.2625 3.281C11.4113 3.281 10.7213 2.59098 10.7213 1.73979Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_34_7">
<rect width="14" height="14" fill="white" transform="matrix(-1 0 0 1 14 0)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

1
assets/icons/play.svg Normal file
View File

@@ -0,0 +1 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3.24182 2.32181C3.3919 2.23132 3.5784 2.22601 3.73338 2.30781L12.7334 7.05781C12.8974 7.14436 13 7.31457 13 7.5C13 7.68543 12.8974 7.85564 12.7334 7.94219L3.73338 12.6922C3.5784 12.774 3.3919 12.7687 3.24182 12.6782C3.09175 12.5877 3 12.4252 3 12.25V2.75C3 2.57476 3.09175 2.4123 3.24182 2.32181ZM4 3.57925V11.4207L11.4288 7.5L4 3.57925Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>

After

Width:  |  Height:  |  Size: 518 B

View File

@@ -0,0 +1,561 @@
[
{
"bindings": {
"up": "menu::SelectPrev",
"pageup": "menu::SelectFirst",
"shift-pageup": "menu::SelectFirst",
"ctrl-p": "menu::SelectPrev",
"down": "menu::SelectNext",
"pagedown": "menu::SelectLast",
"shift-pagedown": "menu::SelectFirst",
"ctrl-n": "menu::SelectNext",
"ctrl-up": "menu::SelectFirst",
"ctrl-down": "menu::SelectLast",
"enter": "menu::Confirm",
"shift-f10": "menu::ShowContextMenu",
"ctrl-enter": "menu::SecondaryConfirm",
"escape": "menu::Cancel",
"ctrl-c": "menu::Cancel",
"ctrl-shift-w": "workspace::CloseWindow",
"shift-escape": "workspace::ToggleZoom",
"ctrl-o": "workspace::Open",
"ctrl-=": "zed::IncreaseBufferFontSize",
"ctrl-+": "zed::IncreaseBufferFontSize",
"ctrl--": "zed::DecreaseBufferFontSize",
"ctrl-0": "zed::ResetBufferFontSize",
"ctrl-,": "zed::OpenSettings",
"ctrl-q": "zed::Quit",
"ctrl-h": "zed::Hide",
"alt-ctrl-h": "zed::HideOthers",
"ctrl-m": "zed::Minimize",
"f11": "zed::ToggleFullScreen"
}
},
{
"context": "Editor",
"bindings": {
"escape": "editor::Cancel",
"backspace": "editor::Backspace",
"shift-backspace": "editor::Backspace",
"ctrl-h": "editor::Backspace",
"delete": "editor::Delete",
"ctrl-d": "editor::Delete",
"tab": "editor::Tab",
"shift-tab": "editor::TabPrev",
"ctrl-k": "editor::CutToEndOfLine",
"ctrl-t": "editor::Transpose",
"ctrl-backspace": "editor::DeleteToBeginningOfLine",
"ctrl-delete": "editor::DeleteToEndOfLine",
"alt-backspace": "editor::DeleteToPreviousWordStart",
"alt-delete": "editor::DeleteToNextWordEnd",
"alt-h": "editor::DeleteToPreviousWordStart",
"alt-d": "editor::DeleteToNextWordEnd",
"ctrl-x": "editor::Cut",
"ctrl-c": "editor::Copy",
"ctrl-v": "editor::Paste",
"ctrl-z": "editor::Undo",
"ctrl-shift-z": "editor::Redo",
"ctrl-y": "editor::Redo",
"up": "editor::MoveUp",
"ctrl-up": "editor::MoveToStartOfParagraph",
"pageup": "editor::PageUp",
"shift-pageup": "editor::MovePageUp",
"home": "editor::MoveToBeginningOfLine",
"down": "editor::MoveDown",
"ctrl-down": "editor::MoveToEndOfParagraph",
"pagedown": "editor::PageDown",
"shift-pagedown": "editor::MovePageDown",
"end": "editor::MoveToEndOfLine",
"left": "editor::MoveLeft",
"right": "editor::MoveRight",
"ctrl-p": "editor::MoveUp",
"ctrl-n": "editor::MoveDown",
"ctrl-b": "editor::MoveLeft",
"ctrl-f": "editor::MoveRight",
"ctrl-shift-l": "editor::NextScreen", // todo!(linux): What is this
"alt-left": "editor::MoveToPreviousWordStart",
"alt-b": "editor::MoveToPreviousWordStart",
"alt-right": "editor::MoveToNextWordEnd",
"alt-f": "editor::MoveToNextWordEnd",
"ctrl-e": "editor::MoveToEndOfLine",
"ctrl-home": "editor::MoveToBeginning",
"ctrl-=end": "editor::MoveToEnd",
"shift-up": "editor::SelectUp",
"ctrl-shift-p": "editor::SelectUp",
"shift-down": "editor::SelectDown",
"ctrl-shift-n": "editor::SelectDown",
"shift-left": "editor::SelectLeft",
"ctrl-shift-b": "editor::SelectLeft",
"shift-right": "editor::SelectRight",
"ctrl-shift-f": "editor::SelectRight",
"alt-shift-left": "editor::SelectToPreviousWordStart",
"alt-shift-b": "editor::SelectToPreviousWordStart",
"alt-shift-right": "editor::SelectToNextWordEnd",
"alt-shift-f": "editor::SelectToNextWordEnd",
"ctrl-shift-up": "editor::SelectToStartOfParagraph",
"ctrl-shift-down": "editor::SelectToEndOfParagraph",
"ctrl-shift-home": "editor::SelectToBeginning",
"ctrl-shift-end": "editor::SelectToEnd",
"ctrl-a": "editor::SelectAll",
"ctrl-l": "editor::SelectLine",
"ctrl-shift-i": "editor::Format",
"shift-home": [
"editor::SelectToBeginningOfLine",
{
"stop_at_soft_wraps": true
}
],
"shift-end": [
"editor::SelectToEndOfLine",
{
"stop_at_soft_wraps": true
}
],
"ctrl-shift-e": [
"editor::SelectToEndOfLine",
{
"stop_at_soft_wraps": true
}
]
}
},
{
"context": "Editor && mode == full",
"bindings": {
"enter": "editor::Newline",
"shift-enter": "editor::Newline",
"ctrl-shift-enter": "editor::NewlineAbove",
"ctrl-enter": "editor::NewlineBelow",
"alt-z": "editor::ToggleSoftWrap",
"ctrl-f": [
"buffer_search::Deploy",
{
"focus": true
}
],
"alt-\\": "copilot::Suggest",
"alt-]": "copilot::NextSuggestion",
"alt-[": "copilot::PreviousSuggestion",
"ctrl->": "assistant::QuoteSelection"
}
},
{
"context": "Editor && mode == auto_height",
"bindings": {
"ctrl-enter": "editor::Newline",
"shift-enter": "editor::Newline",
"ctrl-shift-enter": "editor::NewlineBelow"
}
},
{
"context": "AssistantPanel",
"bindings": {
"f3": "search::SelectNextMatch",
"shift-f3": "search::SelectPrevMatch"
}
},
{
"context": "ConversationEditor > Editor",
"bindings": {
"ctrl-enter": "assistant::Assist",
"ctrl-s": "workspace::Save",
"ctrl->": "assistant::QuoteSelection",
"shift-enter": "assistant::Split",
"ctrl-r": "assistant::CycleMessageRole"
}
},
{
"context": "BufferSearchBar",
"bindings": {
"escape": "buffer_search::Dismiss",
"tab": "buffer_search::FocusEditor",
"enter": "search::SelectNextMatch",
"shift-enter": "search::SelectPrevMatch",
"alt-enter": "search::SelectAllMatches",
"alt-tab": "search::CycleMode"
}
},
{
"context": "BufferSearchBar && in_replace",
"bindings": {
"enter": "search::ReplaceNext",
"ctrl-enter": "search::ReplaceAll"
}
},
{
"context": "BufferSearchBar && !in_replace > Editor",
"bindings": {
"up": "search::PreviousHistoryQuery",
"down": "search::NextHistoryQuery"
}
},
{
"context": "ProjectSearchBar",
"bindings": {
"escape": "project_search::ToggleFocus",
"alt-tab": "search::CycleMode",
"ctrl-shift-h": "search::ToggleReplace",
"ctrl-alt-g": "search::ActivateRegexMode",
"ctrl-alt-s": "search::ActivateSemanticMode",
"ctrl-alt-x": "search::ActivateTextMode"
}
},
{
"context": "ProjectSearchBar > Editor",
"bindings": {
"up": "search::PreviousHistoryQuery",
"down": "search::NextHistoryQuery"
}
},
{
"context": "ProjectSearchBar && in_replace",
"bindings": {
"enter": "search::ReplaceNext",
"ctrl-enter": "search::ReplaceAll"
}
},
{
"context": "ProjectSearchView",
"bindings": {
"escape": "project_search::ToggleFocus",
"alt-tab": "search::CycleMode",
"ctrl-shift-h": "search::ToggleReplace",
"ctrl-alt-g": "search::ActivateRegexMode",
"ctrl-alt-s": "search::ActivateSemanticMode",
"ctrl-alt-x": "search::ActivateTextMode"
}
},
{
"context": "Pane",
"bindings": {
"ctrl-{": "pane::ActivatePrevItem",
"ctrl-}": "pane::ActivateNextItem",
"ctrl-alt-left": "pane::ActivatePrevItem",
"ctrl-alt-right": "pane::ActivateNextItem",
"ctrl-w": "pane::CloseActiveItem",
"ctrl-alt-t": "pane::CloseInactiveItems",
"ctrl-alt-shift-w": "workspace::CloseInactiveTabsAndPanes",
"ctrl-k u": "pane::CloseCleanItems",
"ctrl-k ctrl-w": "pane::CloseAllItems",
"ctrl-f": "project_search::ToggleFocus",
"f3": "search::SelectNextMatch",
"shift-f3": "search::SelectPrevMatch",
"ctrl-shift-h": "search::ToggleReplace",
"alt-enter": "search::SelectAllMatches",
"ctrl-alt-c": "search::ToggleCaseSensitive",
"ctrl-alt-w": "search::ToggleWholeWord",
"alt-tab": "search::CycleMode",
"ctrl-alt-f": "project_search::ToggleFilters",
"ctrl-alt-g": "search::ActivateRegexMode",
"ctrl-alt-s": "search::ActivateSemanticMode",
"ctrl-alt-x": "search::ActivateTextMode"
}
},
// Bindings from VS Code
{
"context": "Editor",
"bindings": {
"ctrl-[": "editor::Outdent",
"ctrl-]": "editor::Indent",
"ctrl-alt-up": "editor::AddSelectionAbove",
"ctrl-alt-down": "editor::AddSelectionBelow",
"ctrl-d": [
"editor::SelectNext",
{
"replace_newest": false
}
],
"ctrl-shift-l": "editor::SelectAllMatches",
"ctrl-shift-d": [
"editor::SelectPrevious",
{
"replace_newest": false
}
],
"ctrl-k ctrl-d": [
"editor::SelectNext",
{
"replace_newest": true
}
],
"ctrl-k ctrl-shift-d": [
"editor::SelectPrevious",
{
"replace_newest": true
}
],
"ctrl-k ctrl-i": "editor::Hover",
"ctrl-/": [
"editor::ToggleComments",
{
"advance_downwards": false
}
],
"alt-up": "editor::SelectLargerSyntaxNode",
"alt-down": "editor::SelectSmallerSyntaxNode",
"ctrl-u": "editor::UndoSelection",
"ctrl-shift-u": "editor::RedoSelection",
"f8": "editor::GoToDiagnostic",
"shift-f8": "editor::GoToPrevDiagnostic",
"f2": "editor::Rename",
"f12": "editor::GoToDefinition",
"alt-f12": "editor::GoToDefinitionSplit",
"ctrl-f12": "editor::GoToTypeDefinition",
"ctrl-alt-f12": "editor::GoToTypeDefinitionSplit",
"alt-shift-f12": "editor::FindAllReferences",
"ctrl-m": "editor::MoveToEnclosingBracket",
"ctrl-alt-[": "editor::Fold",
"ctrl-alt-]": "editor::UnfoldLines",
"ctrl-space": "editor::ShowCompletions",
"ctrl-.": "editor::ToggleCodeActions",
"ctrl-alt-r": "editor::RevealInFinder",
"ctrl-alt-c": "editor::DisplayCursorNames"
}
},
{
"context": "Editor && mode == full",
"bindings": {
"ctrl-shift-o": "outline::Toggle",
"ctrl-g": "go_to_line::Toggle"
}
},
{
"context": "Pane",
"bindings": {
"ctrl-1": ["pane::ActivateItem", 0],
"ctrl-2": ["pane::ActivateItem", 1],
"ctrl-3": ["pane::ActivateItem", 2],
"ctrl-4": ["pane::ActivateItem", 3],
"ctrl-5": ["pane::ActivateItem", 4],
"ctrl-6": ["pane::ActivateItem", 5],
"ctrl-7": ["pane::ActivateItem", 6],
"ctrl-8": ["pane::ActivateItem", 7],
"ctrl-9": ["pane::ActivateItem", 8],
"ctrl-0": "pane::ActivateLastItem",
"ctrl--": "pane::GoBack",
"ctrl-_": "pane::GoForward",
"ctrl-shift-t": "pane::ReopenClosedItem",
"ctrl-shift-f": "project_search::ToggleFocus"
}
},
{
"context": "Workspace",
"bindings": {
"ctrl-alt-o": "projects::OpenRecent",
"ctrl-alt-b": "branches::OpenRecent",
"ctrl-~": "workspace::NewTerminal",
"ctrl-s": "workspace::Save",
"ctrl-shift-s": "workspace::SaveAs",
"ctrl-n": "workspace::NewFile",
"ctrl-shift-n": "workspace::NewWindow",
"ctrl-`": "terminal_panel::ToggleFocus",
"ctrl-1": ["workspace::ActivatePane", 0],
"ctrl-2": ["workspace::ActivatePane", 1],
"ctrl-3": ["workspace::ActivatePane", 2],
"ctrl-4": ["workspace::ActivatePane", 3],
"ctrl-5": ["workspace::ActivatePane", 4],
"ctrl-6": ["workspace::ActivatePane", 5],
"ctrl-7": ["workspace::ActivatePane", 6],
"ctrl-8": ["workspace::ActivatePane", 7],
"ctrl-9": ["workspace::ActivatePane", 8],
"ctrl-b": "workspace::ToggleLeftDock",
"ctrl-r": "workspace::ToggleRightDock",
"ctrl-j": "workspace::ToggleBottomDock",
"ctrl-alt-y": "workspace::CloseAllDocks",
"ctrl-shift-f": "pane::DeploySearch",
"ctrl-k ctrl-t": "theme_selector::Toggle",
"ctrl-k ctrl-s": "zed::OpenKeymap",
"ctrl-t": "project_symbols::Toggle",
"ctrl-p": "file_finder::Toggle",
"ctrl-shift-p": "command_palette::Toggle",
"ctrl-shift-m": "diagnostics::Deploy",
"ctrl-shift-e": "project_panel::ToggleFocus",
"ctrl-?": "assistant::ToggleFocus",
"ctrl-alt-s": "workspace::SaveAll",
"ctrl-k m": "language_selector::Toggle",
"escape": "workspace::Unfollow",
"ctrl-k ctrl-left": ["workspace::ActivatePaneInDirection", "Left"],
"ctrl-k ctrl-right": ["workspace::ActivatePaneInDirection", "Right"],
"ctrl-k ctrl-up": ["workspace::ActivatePaneInDirection", "Up"],
"ctrl-k ctrl-down": ["workspace::ActivatePaneInDirection", "Down"],
"ctrl-k shift-left": ["workspace::SwapPaneInDirection", "Left"],
"ctrl-k shift-right": ["workspace::SwapPaneInDirection", "Right"],
"ctrl-k shift-up": ["workspace::SwapPaneInDirection", "Up"],
"ctrl-k shift-down": ["workspace::SwapPaneInDirection", "Down"],
"alt-t": "task::Rerun",
"alt-shift-t": "task::Spawn"
}
},
// Bindings from Sublime Text
// todo!(linux) make sure these match linux bindings or remove above comment?
{
"context": "Editor",
"bindings": {
"ctrl-shift-k": "editor::DeleteLine",
"ctrl-shift-d": "editor::DuplicateLine",
"ctrl-j": "editor::JoinLines",
"ctrl-alt-up": "editor::MoveLineUp",
"ctrl-alt-down": "editor::MoveLineDown",
"ctrl-alt-backspace": "editor::DeleteToPreviousSubwordStart",
"ctrl-alt-h": "editor::DeleteToPreviousSubwordStart",
"ctrl-alt-delete": "editor::DeleteToNextSubwordEnd",
"ctrl-alt-d": "editor::DeleteToNextSubwordEnd",
"ctrl-alt-left": "editor::MoveToPreviousSubwordStart",
"ctrl-alt-b": "editor::MoveToPreviousSubwordStart",
"ctrl-alt-right": "editor::MoveToNextSubwordEnd",
"ctrl-alt-f": "editor::MoveToNextSubwordEnd",
"ctrl-alt-shift-left": "editor::SelectToPreviousSubwordStart",
"ctrl-alt-shift-b": "editor::SelectToPreviousSubwordStart",
"ctrl-alt-shift-right": "editor::SelectToNextSubwordEnd",
"ctrl-alt-shift-f": "editor::SelectToNextSubwordEnd"
}
},
// Bindings from Atom
// todo!(linux) make sure these match linux bindings or remove above comment?
{
"context": "Pane",
"bindings": {
"ctrl-k up": "pane::SplitUp",
"ctrl-k down": "pane::SplitDown",
"ctrl-k left": "pane::SplitLeft",
"ctrl-k right": "pane::SplitRight"
}
},
// Bindings that should be unified with bindings for more general actions
{
"context": "Editor && renaming",
"bindings": {
"enter": "editor::ConfirmRename"
}
},
{
"context": "Editor && showing_completions",
"bindings": {
"enter": "editor::ConfirmCompletion",
"tab": "editor::ConfirmCompletion"
}
},
{
"context": "Editor && showing_code_actions",
"bindings": {
"enter": "editor::ConfirmCodeAction"
}
},
{
"context": "Editor && (showing_code_actions || showing_completions)",
"bindings": {
"up": "editor::ContextMenuPrev",
"ctrl-p": "editor::ContextMenuPrev",
"down": "editor::ContextMenuNext",
"ctrl-n": "editor::ContextMenuNext",
"pageup": "editor::ContextMenuFirst",
"pagedown": "editor::ContextMenuLast"
}
},
// Custom bindings
{
"bindings": {
"ctrl-alt-shift-f": "workspace::FollowNextCollaborator",
// TODO: Move this to a dock open action
"ctrl-alt-c": "collab_panel::ToggleFocus",
"ctrl-alt-i": "zed::DebugElements",
"ctrl-:": "editor::ToggleInlayHints"
}
},
{
"context": "Editor && mode == full",
"bindings": {
"alt-enter": "editor::OpenExcerpts",
"ctrl-f8": "editor::GoToHunk",
"ctrl-shift-f8": "editor::GoToPrevHunk",
"ctrl-enter": "assistant::InlineAssist"
}
},
{
"context": "ProjectSearchBar && !in_replace",
"bindings": {
"ctrl-enter": "project_search::SearchInNew"
}
},
{
"context": "ProjectPanel",
"bindings": {
"left": "project_panel::CollapseSelectedEntry",
"right": "project_panel::ExpandSelectedEntry",
"ctrl-n": "project_panel::NewFile",
"ctrl-alt-n": "project_panel::NewDirectory",
"ctrl-x": "project_panel::Cut",
"ctrl-c": "project_panel::Copy",
"ctrl-v": "project_panel::Paste",
"ctrl-alt-c": "project_panel::CopyPath",
"ctrl-alt-shift-c": "project_panel::CopyRelativePath",
"f2": "project_panel::Rename",
"enter": "project_panel::Rename",
"backspace": "project_panel::Delete",
"ctrl-alt-r": "project_panel::RevealInFinder",
"alt-shift-f": "project_panel::NewSearchInDirectory"
}
},
{
"context": "ProjectPanel && not_editing",
"bindings": {
"space": "project_panel::Open"
}
},
{
"context": "CollabPanel && not_editing",
"bindings": {
"ctrl-backspace": "collab_panel::Remove",
"space": "menu::Confirm"
}
},
{
"context": "(CollabPanel && editing) > Editor",
"bindings": {
"space": "collab_panel::InsertSpace"
}
},
{
"context": "ChannelModal",
"bindings": {
"tab": "channel_modal::ToggleMode"
}
},
{
"context": "ChannelModal > Picker > Editor",
"bindings": {
"tab": "channel_modal::ToggleMode"
}
},
{
"context": "ChatPanel > MessageEditor",
"bindings": {
"escape": "chat_panel::CloseReplyPreview"
}
},
{
"context": "Terminal",
"bindings": {
"ctrl-alt-space": "terminal::ShowCharacterPalette",
"ctrl-shift-c": "terminal::Copy",
"ctrl-shift-v": "terminal::Paste",
"ctrl-k": "terminal::Clear",
// Some nice conveniences
"ctrl-backspace": ["terminal::SendText", "\u0015"],
"ctrl-right": ["terminal::SendText", "\u0005"],
"ctrl-left": ["terminal::SendText", "\u0001"],
// Terminal.app compatibility
"alt-left": ["terminal::SendText", "\u001bb"],
"alt-right": ["terminal::SendText", "\u001bf"],
// There are conflicting bindings for these keys in the global context.
// these bindings override them, remove at your own risk:
"up": ["terminal::SendKeystroke", "up"],
"pageup": ["terminal::SendKeystroke", "pageup"],
"down": ["terminal::SendKeystroke", "down"],
"pagedown": ["terminal::SendKeystroke", "pagedown"],
"escape": ["terminal::SendKeystroke", "escape"],
"enter": ["terminal::SendKeystroke", "enter"],
"ctrl-c": ["terminal::SendKeystroke", "ctrl-c"]
}
}
]

View File

@@ -423,7 +423,9 @@
"cmd-k shift-left": ["workspace::SwapPaneInDirection", "Left"],
"cmd-k shift-right": ["workspace::SwapPaneInDirection", "Right"],
"cmd-k shift-up": ["workspace::SwapPaneInDirection", "Up"],
"cmd-k shift-down": ["workspace::SwapPaneInDirection", "Down"]
"cmd-k shift-down": ["workspace::SwapPaneInDirection", "Down"],
"alt-t": "task::Rerun",
"alt-shift-t": "task::Spawn"
}
},
// Bindings from Sublime Text

View File

@@ -117,12 +117,15 @@
"ctrl-e": "vim::LineDown",
"ctrl-y": "vim::LineUp",
// "g" commands
"g e": "vim::PreviousWordEnd",
"g shift-e": ["vim::PreviousWordEnd", { "ignorePunctuation": true }],
"g g": "vim::StartOfDocument",
"g h": "editor::Hover",
"g t": "pane::ActivateNextItem",
"g shift-t": "pane::ActivatePrevItem",
"g d": "editor::GoToDefinition",
"g shift-d": "editor::GoToTypeDefinition",
"g x": "editor::OpenUrl",
"g n": "vim::SelectNext",
"g shift-n": "vim::SelectPrevious",
"g >": [
@@ -505,5 +508,27 @@
"enter": "vim::SearchSubmit",
"escape": "buffer_search::Dismiss"
}
},
{
// netrw compatibility
"context": "ProjectPanel && not_editing",
"bindings": {
":": "command_palette::Toggle",
"%": "project_panel::NewFile",
"/": "project_panel::NewSearchInDirectory",
"d": "project_panel::NewDirectory",
"enter": "project_panel::Open",
"escape": "project_panel::ToggleFocus",
"h": "project_panel::CollapseSelectedEntry",
"j": "menu::SelectNext",
"k": "menu::SelectPrev",
"l": "project_panel::ExpandSelectedEntry",
"o": "project_panel::Open",
"shift-d": "project_panel::Delete",
"shift-r": "project_panel::Rename",
"t": "project_panel::Open",
"v": "project_panel::Open",
"x": "project_panel::RevealInFinder"
}
}
]

View File

@@ -451,7 +451,10 @@
// Set the terminal's font family. If this option is not included,
// the terminal will default to matching the buffer's font family.
// "font_family": "Zed Mono",
// ---
// Sets the maximum number of lines in the terminal's scrollback buffer.
// Default: 10_000, maximum: 100_000 (all bigger values set will be treated as 100_000), 0 disables the scrolling.
// Existing terminals will not pick up this change until they are recreated.
// "max_scroll_history_lines": 10000,
},
// Difference settings for semantic_index
"semantic_index": {
@@ -482,6 +485,7 @@
"deno": {
"enable": false
},
"code_actions_on_format": {},
// Different settings for specific languages.
"languages": {
"Plain Text": {
@@ -490,11 +494,18 @@
"Elixir": {
"tab_size": 2
},
"Gleam": {
"tab_size": 2
},
"Go": {
"tab_size": 4,
"hard_tabs": true
"hard_tabs": true,
"code_actions_on_format": {
"source.organizeImports": true
}
},
"Markdown": {
"tab_size": 2,
"soft_wrap": "preferred_line_length"
},
"JavaScript": {
@@ -536,10 +547,10 @@
"lsp": {
// Specify the LSP name as a key here.
// "rust-analyzer": {
// //These initialization options are merged into Zed's defaults
// // These initialization options are merged into Zed's defaults
// "initialization_options": {
// "checkOnSave": {
// "command": "clippy"
// "check": {
// "command": "clippy" // rust-analyzer.check.command (default: "check")
// }
// }
// }

View File

@@ -0,0 +1,19 @@
// Static tasks configuration.
//
// Example:
[
{
"label": "Example task",
"command": "bash",
// rest of the parameters are optional
"args": ["-c", "for i in {1..5}; do echo \"Hello $i/5\"; sleep 1; done"],
// Env overrides for the command, will be appended to the terminal's environment from the settings.
"env": { "foo": "bar" },
// Current working directory to spawn the command into, defaults to current project root.
//"cwd": "/path/to/working/directory",
// Whether to use a new terminal tab or reuse the existing one to spawn the process, defaults to `false`.
"use_new_terminal": false,
// Whether to allow multiple instances of the same task to be run, or rather wait for the existing ones to finish, defaults to `false`.
"allow_concurrent_runs": false
}
]

View File

@@ -46,7 +46,7 @@
"panel.background": "#21242bff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#f7f7f84c",
"scrollbar.thumb.background": "#f7f7f84c",
"scrollbar.thumb.hover_background": "#252931ff",
"scrollbar.thumb.border": "#252931ff",
"scrollbar.track.background": "#00000000",

View File

@@ -46,7 +46,7 @@
"panel.background": "#221f26ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#efecf44c",
"scrollbar.thumb.background": "#efecf44c",
"scrollbar.thumb.hover_background": "#332f38ff",
"scrollbar.thumb.border": "#332f38ff",
"scrollbar.track.background": "#00000000",
@@ -430,7 +430,7 @@
"panel.background": "#e6e3ebff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#19171c4c",
"scrollbar.thumb.background": "#19171c4c",
"scrollbar.thumb.hover_background": "#cbc8d1ff",
"scrollbar.thumb.border": "#cbc8d1ff",
"scrollbar.track.background": "#00000000",
@@ -814,7 +814,7 @@
"panel.background": "#262622ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#fefbec4c",
"scrollbar.thumb.background": "#fefbec4c",
"scrollbar.thumb.hover_background": "#3b3933ff",
"scrollbar.thumb.border": "#3b3933ff",
"scrollbar.track.background": "#00000000",
@@ -1198,7 +1198,7 @@
"panel.background": "#eeebd7ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#20201d4c",
"scrollbar.thumb.background": "#20201d4c",
"scrollbar.thumb.hover_background": "#d7d3beff",
"scrollbar.thumb.border": "#d7d3beff",
"scrollbar.track.background": "#00000000",
@@ -1582,7 +1582,7 @@
"panel.background": "#2c2b23ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#f4f3ec4c",
"scrollbar.thumb.background": "#f4f3ec4c",
"scrollbar.thumb.hover_background": "#3c3b31ff",
"scrollbar.thumb.border": "#3c3b31ff",
"scrollbar.track.background": "#00000000",
@@ -1966,7 +1966,7 @@
"panel.background": "#ebeae3ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#22221b4c",
"scrollbar.thumb.background": "#22221b4c",
"scrollbar.thumb.hover_background": "#d0cfc5ff",
"scrollbar.thumb.border": "#d0cfc5ff",
"scrollbar.track.background": "#00000000",
@@ -2350,7 +2350,7 @@
"panel.background": "#27211eff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#f0eeed4c",
"scrollbar.thumb.background": "#f0eeed4c",
"scrollbar.thumb.hover_background": "#3b3431ff",
"scrollbar.thumb.border": "#3b3431ff",
"scrollbar.track.background": "#00000000",
@@ -2734,7 +2734,7 @@
"panel.background": "#e9e6e4ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#1b19184c",
"scrollbar.thumb.background": "#1b19184c",
"scrollbar.thumb.hover_background": "#d6d1cfff",
"scrollbar.thumb.border": "#d6d1cfff",
"scrollbar.track.background": "#00000000",
@@ -3118,7 +3118,7 @@
"panel.background": "#252025ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#f7f3f74c",
"scrollbar.thumb.background": "#f7f3f74c",
"scrollbar.thumb.hover_background": "#393239ff",
"scrollbar.thumb.border": "#393239ff",
"scrollbar.track.background": "#00000000",
@@ -3502,7 +3502,7 @@
"panel.background": "#e0d5e0ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#1b181b4c",
"scrollbar.thumb.background": "#1b181b4c",
"scrollbar.thumb.hover_background": "#ccbdccff",
"scrollbar.thumb.border": "#ccbdccff",
"scrollbar.track.background": "#00000000",
@@ -3886,7 +3886,7 @@
"panel.background": "#1c2529ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#ebf8ff4c",
"scrollbar.thumb.background": "#ebf8ff4c",
"scrollbar.thumb.hover_background": "#2c3b42ff",
"scrollbar.thumb.border": "#2c3b42ff",
"scrollbar.track.background": "#00000000",
@@ -4270,7 +4270,7 @@
"panel.background": "#cdeaf9ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#161b1d4c",
"scrollbar.thumb.background": "#161b1d4c",
"scrollbar.thumb.hover_background": "#b0d3e5ff",
"scrollbar.thumb.border": "#b0d3e5ff",
"scrollbar.track.background": "#00000000",
@@ -4654,7 +4654,7 @@
"panel.background": "#252020ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#f4ecec4c",
"scrollbar.thumb.background": "#f4ecec4c",
"scrollbar.thumb.hover_background": "#352f2fff",
"scrollbar.thumb.border": "#352f2fff",
"scrollbar.track.background": "#00000000",
@@ -5038,7 +5038,7 @@
"panel.background": "#ebe3e3ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#1b18184c",
"scrollbar.thumb.background": "#1b18184c",
"scrollbar.thumb.hover_background": "#cfc7c7ff",
"scrollbar.thumb.border": "#cfc7c7ff",
"scrollbar.track.background": "#00000000",
@@ -5422,7 +5422,7 @@
"panel.background": "#1f2621ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#ecf4ee4c",
"scrollbar.thumb.background": "#ecf4ee4c",
"scrollbar.thumb.hover_background": "#2f3832ff",
"scrollbar.thumb.border": "#2f3832ff",
"scrollbar.track.background": "#00000000",
@@ -5806,7 +5806,7 @@
"panel.background": "#e3ebe6ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#171c194c",
"scrollbar.thumb.background": "#171c194c",
"scrollbar.thumb.hover_background": "#c8d1cbff",
"scrollbar.thumb.border": "#c8d1cbff",
"scrollbar.track.background": "#00000000",
@@ -6190,7 +6190,7 @@
"panel.background": "#1f231fff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#f3faf34c",
"scrollbar.thumb.background": "#f3faf34c",
"scrollbar.thumb.hover_background": "#333b33ff",
"scrollbar.thumb.border": "#333b33ff",
"scrollbar.track.background": "#00000000",
@@ -6574,7 +6574,7 @@
"panel.background": "#daeedaff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#1315134c",
"scrollbar.thumb.background": "#1315134c",
"scrollbar.thumb.hover_background": "#bed7beff",
"scrollbar.thumb.border": "#bed7beff",
"scrollbar.track.background": "#00000000",
@@ -6958,7 +6958,7 @@
"panel.background": "#262f51ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#f5f7ff4c",
"scrollbar.thumb.background": "#f5f7ff4c",
"scrollbar.thumb.hover_background": "#363f62ff",
"scrollbar.thumb.border": "#363f62ff",
"scrollbar.track.background": "#00000000",
@@ -7342,7 +7342,7 @@
"panel.background": "#e5e8f5ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#2026464c",
"scrollbar.thumb.background": "#2026464c",
"scrollbar.thumb.hover_background": "#ccd0e1ff",
"scrollbar.thumb.border": "#ccd0e1ff",
"scrollbar.track.background": "#00000000",

View File

@@ -46,7 +46,7 @@
"panel.background": "#1f2127ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#bfbdb64c",
"scrollbar.thumb.background": "#bfbdb64c",
"scrollbar.thumb.hover_background": "#2d2f34ff",
"scrollbar.thumb.border": "#2d2f34ff",
"scrollbar.track.background": "#00000000",
@@ -415,7 +415,7 @@
"panel.background": "#ececedff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#5c61664c",
"scrollbar.thumb.background": "#5c61664c",
"scrollbar.thumb.hover_background": "#dfe0e1ff",
"scrollbar.thumb.border": "#dfe0e1ff",
"scrollbar.track.background": "#00000000",
@@ -784,7 +784,7 @@
"panel.background": "#353944ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#cccac24c",
"scrollbar.thumb.background": "#cccac24c",
"scrollbar.thumb.hover_background": "#43464fff",
"scrollbar.thumb.border": "#43464fff",
"scrollbar.track.background": "#00000000",

View File

@@ -46,7 +46,7 @@
"panel.background": "#3a3735ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#fbf1c74c",
"scrollbar.thumb.background": "#fbf1c74c",
"scrollbar.thumb.hover_background": "#494340ff",
"scrollbar.thumb.border": "#494340ff",
"scrollbar.track.background": "#00000000",
@@ -420,7 +420,7 @@
"panel.background": "#393634ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#fbf1c74c",
"scrollbar.thumb.background": "#fbf1c74c",
"scrollbar.thumb.hover_background": "#494340ff",
"scrollbar.thumb.border": "#494340ff",
"scrollbar.track.background": "#00000000",
@@ -794,7 +794,7 @@
"panel.background": "#3b3735ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#fbf1c74c",
"scrollbar.thumb.background": "#fbf1c74c",
"scrollbar.thumb.hover_background": "#494340ff",
"scrollbar.thumb.border": "#494340ff",
"scrollbar.track.background": "#00000000",
@@ -1168,7 +1168,7 @@
"panel.background": "#ecddb4ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#2828284c",
"scrollbar.thumb.background": "#2828284c",
"scrollbar.thumb.hover_background": "#ddcca7ff",
"scrollbar.thumb.border": "#ddcca7ff",
"scrollbar.track.background": "#00000000",
@@ -1542,7 +1542,7 @@
"panel.background": "#ecddb5ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#2828284c",
"scrollbar.thumb.background": "#2828284c",
"scrollbar.thumb.hover_background": "#ddcca7ff",
"scrollbar.thumb.border": "#ddcca7ff",
"scrollbar.track.background": "#00000000",
@@ -1916,7 +1916,7 @@
"panel.background": "#ecdcb3ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#2828284c",
"scrollbar.thumb.background": "#2828284c",
"scrollbar.thumb.hover_background": "#ddcca7ff",
"scrollbar.thumb.border": "#ddcca7ff",
"scrollbar.track.background": "#00000000",

View File

@@ -46,7 +46,7 @@
"panel.background": "#2f343eff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#c8ccd44c",
"scrollbar.thumb.background": "#c8ccd44c",
"scrollbar.thumb.hover_background": "#363c46ff",
"scrollbar.thumb.border": "#363c46ff",
"scrollbar.track.background": "#00000000",
@@ -420,7 +420,7 @@
"panel.background": "#ebebecff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#383a414c",
"scrollbar.thumb.background": "#383a414c",
"scrollbar.thumb.hover_background": "#dfdfe0ff",
"scrollbar.thumb.border": "#dfdfe0ff",
"scrollbar.track.background": "#00000000",

View File

@@ -46,7 +46,7 @@
"panel.background": "#1c1b2aff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#e0def44c",
"scrollbar.thumb.background": "#e0def44c",
"scrollbar.thumb.hover_background": "#232132ff",
"scrollbar.thumb.border": "#232132ff",
"scrollbar.track.background": "#00000000",
@@ -425,7 +425,7 @@
"panel.background": "#fef9f2ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#5752794c",
"scrollbar.thumb.background": "#5752794c",
"scrollbar.thumb.hover_background": "#e5e0dfff",
"scrollbar.thumb.border": "#e5e0dfff",
"scrollbar.track.background": "#00000000",
@@ -804,7 +804,7 @@
"panel.background": "#28253cff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#e0def44c",
"scrollbar.thumb.background": "#e0def44c",
"scrollbar.thumb.hover_background": "#322f48ff",
"scrollbar.thumb.border": "#322f48ff",
"scrollbar.track.background": "#00000000",

View File

@@ -46,7 +46,7 @@
"panel.background": "#2b3038ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#fdf4c14c",
"scrollbar.thumb.background": "#fdf4c14c",
"scrollbar.thumb.hover_background": "#313741ff",
"scrollbar.thumb.border": "#313741ff",
"scrollbar.track.background": "#00000000",

View File

@@ -46,7 +46,7 @@
"panel.background": "#04313bff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#fdf6e34c",
"scrollbar.thumb.background": "#fdf6e34c",
"scrollbar.thumb.hover_background": "#053541ff",
"scrollbar.thumb.border": "#053541ff",
"scrollbar.track.background": "#00000000",
@@ -415,7 +415,7 @@
"panel.background": "#f3eddaff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#002a354c",
"scrollbar.thumb.background": "#002a354c",
"scrollbar.thumb.hover_background": "#dcdacbff",
"scrollbar.thumb.border": "#dcdacbff",
"scrollbar.track.background": "#00000000",

View File

@@ -46,7 +46,7 @@
"panel.background": "#231f16ff",
"panel.focused_border": null,
"pane.focused_border": null,
"scrollbar_thumb.background": "#f8f5de4c",
"scrollbar.thumb.background": "#f8f5de4c",
"scrollbar.thumb.hover_background": "#29251bff",
"scrollbar.thumb.border": "#29251bff",
"scrollbar.track.background": "#00000000",

View File

@@ -1,5 +1,5 @@
pub mod assistant_panel;
mod assistant_settings;
pub mod assistant_settings;
mod codegen;
mod prompts;
mod streaming_diff;

View File

@@ -122,16 +122,13 @@ impl AssistantPanel {
.await
.log_err()
.unwrap_or_default();
let (api_url, model_name) = cx
.update(|cx| {
let settings = AssistantSettings::get_global(cx);
(
settings.openai_api_url.clone(),
settings.default_open_ai_model.full_name().to_string(),
)
})
.log_err()
.unwrap();
let (api_url, model_name) = cx.update(|cx| {
let settings = AssistantSettings::get_global(cx);
(
settings.openai_api_url.clone(),
settings.default_open_ai_model.full_name().to_string(),
)
})?;
let completion_provider = OpenAiCompletionProvider::new(
api_url,
model_name,

View File

@@ -348,6 +348,21 @@ impl ChannelStore {
.is_some_and(|state| state.has_new_messages())
}
pub fn last_acknowledge_message_id(&self, channel_id: ChannelId) -> Option<u64> {
self.channel_states.get(&channel_id).and_then(|state| {
if let Some(last_message_id) = state.latest_chat_message {
if state
.last_acknowledged_message_id()
.is_some_and(|id| id < last_message_id)
{
return state.last_acknowledged_message_id();
}
}
None
})
}
pub fn acknowledge_message_id(
&mut self,
channel_id: ChannelId,
@@ -1152,6 +1167,10 @@ impl ChannelState {
})
}
fn last_acknowledged_message_id(&self) -> Option<u64> {
self.observed_chat_message
}
fn acknowledge_message_id(&mut self, message_id: u64) {
let observed = self.observed_chat_message.get_or_insert(message_id);
*observed = (*observed).max(message_id);

View File

@@ -4,16 +4,21 @@ use std::{
fmt, iter,
};
/// A unique identifier for each distributed node
pub type ReplicaId = u16;
/// A [Lamport sequence number](https://en.wikipedia.org/wiki/Lamport_timestamp),
pub type Seq = u32;
/// A [Lamport timestamp](https://en.wikipedia.org/wiki/Lamport_timestamp),
/// used to determine the ordering of events in the editor.
#[derive(Clone, Copy, Default, Eq, Hash, PartialEq)]
pub struct Lamport {
pub replica_id: ReplicaId,
pub value: Seq,
}
/// A vector clock
/// A [vector clock](https://en.wikipedia.org/wiki/Vector_clock)
#[derive(Clone, Default, Hash, Eq, PartialEq)]
pub struct Global(SmallVec<[u32; 8]>);

View File

@@ -7,6 +7,11 @@ ZED_ENVIRONMENT = "development"
LIVE_KIT_SERVER = "http://localhost:7880"
LIVE_KIT_KEY = "devkey"
LIVE_KIT_SECRET = "secret"
BLOB_STORE_ACCESS_KEY = "the-blob-store-access-key"
BLOB_STORE_SECRET_KEY = "the-blob-store-secret-key"
BLOB_STORE_BUCKET = "the-extensions-bucket"
BLOB_STORE_URL = "http://127.0.0.1:9000"
BLOB_STORE_REGION = "the-region"
# RUST_LOG=info
# LOG_JSON=true

View File

@@ -17,6 +17,8 @@ required-features = ["seed-support"]
[dependencies]
anyhow.workspace = true
async-tungstenite = "0.16"
aws-config = { version = "1.1.5" }
aws-sdk-s3 = { version = "1.15.0" }
axum = { version = "0.5", features = ["json", "headers", "ws"] }
axum-extra = { version = "0.3", features = ["erased-json"] }
base64 = "0.13"
@@ -41,6 +43,7 @@ reqwest = { version = "0.11", features = ["json"], optional = true }
rpc.workspace = true
scrypt = "0.7"
sea-orm = { version = "0.12.x", features = ["sqlx-postgres", "postgres-array", "runtime-tokio-rustls", "with-uuid"] }
semver.workspace = true
serde.workspace = true
serde_derive.workspace = true
serde_json.workspace = true
@@ -61,7 +64,6 @@ util.workspace = true
uuid.workspace = true
[dev-dependencies]
release_channel.workspace = true
async-trait.workspace = true
audio.workspace = true
call = { workspace = true, features = ["test-support"] }
@@ -86,6 +88,7 @@ node_runtime.workspace = true
notifications = { workspace = true, features = ["test-support"] }
pretty_assertions.workspace = true
project = { workspace = true, features = ["test-support"] }
release_channel.workspace = true
rpc = { workspace = true, features = ["test-support"] }
sea-orm = { version = "0.12.x", features = ["sqlx-sqlite"] }
serde_json.workspace = true

View File

@@ -105,6 +105,31 @@ spec:
secretKeyRef:
name: livekit
key: secret
- name: BLOB_STORE_ACCESS_KEY
valueFrom:
secretKeyRef:
name: blob-store
key: access_key
- name: BLOB_STORE_SECRET_KEY
valueFrom:
secretKeyRef:
name: blob-store
key: secret_key
- name: BLOB_STORE_URL
valueFrom:
secretKeyRef:
name: blob-store
key: url
- name: BLOB_STORE_REGION
valueFrom:
secretKeyRef:
name: blob-store
key: region
- name: BLOB_STORE_BUCKET
valueFrom:
secretKeyRef:
name: blob-store
key: bucket
- name: INVITE_LINK_PREFIX
value: ${INVITE_LINK_PREFIX}
- name: RUST_BACKTRACE

View File

@@ -353,3 +353,25 @@ CREATE TABLE contributors (
signed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (user_id)
);
CREATE TABLE extensions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
external_id TEXT NOT NULL,
name TEXT NOT NULL,
latest_version TEXT NOT NULL,
total_download_count INTEGER NOT NULL DEFAULT 0
);
CREATE TABLE extension_versions (
extension_id INTEGER REFERENCES extensions(id),
version TEXT NOT NULL,
published_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
authors TEXT NOT NULL,
repository TEXT NOT NULL,
description TEXT NOT NULL,
download_count INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY (extension_id, version)
);
CREATE UNIQUE INDEX "index_extensions_external_id" ON "extensions" ("external_id");
CREATE INDEX "index_extensions_total_download_count" ON "extensions" ("total_download_count");

View File

@@ -0,0 +1,4 @@
-- Add migration script here
ALTER TABLE rooms DROP COLUMN enviroment;
ALTER TABLE rooms DROP COLUMN environment;
ALTER TABLE room_participants DROP COLUMN in_call;

View File

@@ -0,0 +1,22 @@
CREATE TABLE IF NOT EXISTS extensions (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
external_id TEXT NOT NULL,
latest_version TEXT NOT NULL,
total_download_count BIGINT NOT NULL DEFAULT 0
);
CREATE TABLE IF NOT EXISTS extension_versions (
extension_id INTEGER REFERENCES extensions(id),
version TEXT NOT NULL,
published_at TIMESTAMP NOT NULL DEFAULT now(),
authors TEXT NOT NULL,
repository TEXT NOT NULL,
description TEXT NOT NULL,
download_count BIGINT NOT NULL DEFAULT 0,
PRIMARY KEY(extension_id, version)
);
CREATE UNIQUE INDEX "index_extensions_external_id" ON "extensions" ("external_id");
CREATE INDEX "trigram_index_extensions_name" ON "extensions" USING GIN(name gin_trgm_ops);
CREATE INDEX "index_extensions_total_download_count" ON "extensions" ("total_download_count");

View File

@@ -1,3 +1,5 @@
mod extensions;
use crate::{
auth,
db::{ContributorSelector, User, UserId},
@@ -20,6 +22,8 @@ use std::sync::Arc;
use tower::ServiceBuilder;
use tracing::instrument;
pub use extensions::fetch_extensions_from_blob_store_periodically;
pub fn routes(rpc_server: Arc<rpc::Server>, state: Arc<AppState>) -> Router<Body> {
Router::new()
.route("/user", get(get_authenticated_user))
@@ -28,6 +32,7 @@ pub fn routes(rpc_server: Arc<rpc::Server>, state: Arc<AppState>) -> Router<Body
.route("/rpc_server_snapshot", get(get_rpc_server_snapshot))
.route("/contributors", get(get_contributors).post(add_contributor))
.route("/contributor", get(check_is_contributor))
.merge(extensions::router())
.layer(
ServiceBuilder::new()
.layer(Extension(state))

View File

@@ -0,0 +1,237 @@
use crate::{
db::{ExtensionMetadata, NewExtensionVersion},
executor::Executor,
AppState, Error, Result,
};
use anyhow::{anyhow, Context as _};
use aws_sdk_s3::presigning::PresigningConfig;
use axum::{
extract::{Path, Query},
response::Redirect,
routing::get,
Extension, Json, Router,
};
use collections::HashMap;
use hyper::StatusCode;
use serde::{Deserialize, Serialize};
use std::{sync::Arc, time::Duration};
use time::PrimitiveDateTime;
use util::ResultExt;
pub fn router() -> Router {
Router::new()
.route("/extensions", get(get_extensions))
.route(
"/extensions/:extension_id/:version/download",
get(download_extension),
)
}
#[derive(Debug, Deserialize)]
struct GetExtensionsParams {
filter: Option<String>,
}
#[derive(Debug, Deserialize)]
struct DownloadExtensionParams {
extension_id: String,
version: String,
}
#[derive(Debug, Serialize)]
struct GetExtensionsResponse {
pub data: Vec<ExtensionMetadata>,
}
#[derive(Deserialize)]
struct ExtensionManifest {
name: String,
version: String,
description: Option<String>,
authors: Vec<String>,
repository: String,
}
async fn get_extensions(
Extension(app): Extension<Arc<AppState>>,
Query(params): Query<GetExtensionsParams>,
) -> Result<Json<GetExtensionsResponse>> {
let extensions = app.db.get_extensions(params.filter.as_deref(), 30).await?;
Ok(Json(GetExtensionsResponse { data: extensions }))
}
async fn download_extension(
Extension(app): Extension<Arc<AppState>>,
Path(params): Path<DownloadExtensionParams>,
) -> Result<Redirect> {
let Some((blob_store_client, bucket)) = app
.blob_store_client
.clone()
.zip(app.config.blob_store_bucket.clone())
else {
Err(Error::Http(
StatusCode::NOT_IMPLEMENTED,
"not supported".into(),
))?
};
let DownloadExtensionParams {
extension_id,
version,
} = params;
let version_exists = app
.db
.record_extension_download(&extension_id, &version)
.await?;
if !version_exists {
Err(Error::Http(
StatusCode::NOT_FOUND,
"unknown extension version".into(),
))?;
}
let url = blob_store_client
.get_object()
.bucket(bucket)
.key(format!(
"extensions/{extension_id}/{version}/archive.tar.gz"
))
.presigned(PresigningConfig::expires_in(EXTENSION_DOWNLOAD_URL_LIFETIME).unwrap())
.await
.map_err(|e| anyhow!("failed to create presigned extension download url {e}"))?;
Ok(Redirect::temporary(url.uri()))
}
const EXTENSION_FETCH_INTERVAL: Duration = Duration::from_secs(5 * 60);
const EXTENSION_DOWNLOAD_URL_LIFETIME: Duration = Duration::from_secs(3 * 60);
pub fn fetch_extensions_from_blob_store_periodically(app_state: Arc<AppState>, executor: Executor) {
let Some(blob_store_client) = app_state.blob_store_client.clone() else {
log::info!("no blob store client");
return;
};
let Some(blob_store_bucket) = app_state.config.blob_store_bucket.clone() else {
log::info!("no blob store bucket");
return;
};
executor.spawn_detached({
let executor = executor.clone();
async move {
loop {
fetch_extensions_from_blob_store(
&blob_store_client,
&blob_store_bucket,
&app_state,
)
.await
.log_err();
executor.sleep(EXTENSION_FETCH_INTERVAL).await;
}
}
});
}
async fn fetch_extensions_from_blob_store(
blob_store_client: &aws_sdk_s3::Client,
blob_store_bucket: &String,
app_state: &Arc<AppState>,
) -> anyhow::Result<()> {
let list = blob_store_client
.list_objects()
.bucket(blob_store_bucket)
.prefix("extensions/")
.send()
.await?;
let objects = list
.contents
.ok_or_else(|| anyhow!("missing bucket contents"))?;
let mut published_versions = HashMap::<&str, Vec<&str>>::default();
for object in &objects {
let Some(key) = object.key.as_ref() else {
continue;
};
let mut parts = key.split('/');
let Some(_) = parts.next().filter(|part| *part == "extensions") else {
continue;
};
let Some(extension_id) = parts.next() else {
continue;
};
let Some(version) = parts.next() else {
continue;
};
published_versions
.entry(extension_id)
.or_default()
.push(version);
}
let known_versions = app_state.db.get_known_extension_versions().await?;
let mut new_versions = HashMap::<&str, Vec<NewExtensionVersion>>::default();
let empty = Vec::new();
for (extension_id, published_versions) in published_versions {
let known_versions = known_versions.get(extension_id).unwrap_or(&empty);
for published_version in published_versions {
if known_versions
.binary_search_by_key(&published_version, String::as_str)
.is_err()
{
let object = blob_store_client
.get_object()
.bucket(blob_store_bucket)
.key(format!(
"extensions/{extension_id}/{published_version}/manifest.json"
))
.send()
.await?;
let manifest_bytes = object
.body
.collect()
.await
.map(|data| data.into_bytes())
.with_context(|| format!("failed to download manifest for extension {extension_id} version {published_version}"))?
.to_vec();
let manifest = serde_json::from_slice::<ExtensionManifest>(&manifest_bytes)
.with_context(|| format!("invalid manifest for extension {extension_id} version {published_version}: {}", String::from_utf8_lossy(&manifest_bytes)))?;
let published_at = object.last_modified.ok_or_else(|| anyhow!("missing last modified timestamp for extension {extension_id} version {published_version}"))?;
let published_at =
time::OffsetDateTime::from_unix_timestamp_nanos(published_at.as_nanos())?;
let published_at = PrimitiveDateTime::new(published_at.date(), published_at.time());
let version = semver::Version::parse(&manifest.version).with_context(|| {
format!(
"invalid version for extension {extension_id} version {published_version}"
)
})?;
new_versions
.entry(extension_id)
.or_default()
.push(NewExtensionVersion {
name: manifest.name,
version,
description: manifest.description.unwrap_or_default(),
authors: manifest.authors,
repository: manifest.repository,
published_at,
});
}
}
}
app_state
.db
.insert_extension_versions(&new_versions)
.await?;
Ok(())
}

View File

@@ -18,8 +18,8 @@ struct GitHubUser {
async fn main() {
load_dotenv().expect("failed to load .env.toml file");
let mut admin_logins =
load_admins("./.admins.default.json").expect("failed to load default admins file");
let mut admin_logins = load_admins("crates/collab/.admins.default.json")
.expect("failed to load default admins file");
if let Ok(other_admins) = load_admins("./.admins.json") {
admin_logins.extend(other_admins);
}

View File

@@ -1,12 +1,8 @@
#[cfg(test)]
pub mod tests;
#[cfg(test)]
pub use tests::TestDb;
mod ids;
mod queries;
mod tables;
#[cfg(test)]
pub mod tests;
use crate::{executor::Executor, Error, Result};
use anyhow::anyhow;
@@ -25,7 +21,7 @@ use sea_orm::{
FromQueryResult, IntoActiveModel, IsolationLevel, JoinType, QueryOrder, QuerySelect, Statement,
TransactionTrait,
};
use serde::{Deserialize, Serialize};
use serde::{ser::Error as _, Deserialize, Serialize, Serializer};
use sqlx::{
migrate::{Migrate, Migration, MigrationSource},
Connection,
@@ -40,13 +36,17 @@ use std::{
sync::Arc,
time::Duration,
};
pub use tables::*;
use time::{format_description::well_known::iso8601, PrimitiveDateTime};
use tokio::sync::{Mutex, OwnedMutexGuard};
#[cfg(test)]
pub use tests::TestDb;
pub use ids::*;
pub use queries::contributors::ContributorSelector;
pub use sea_orm::ConnectOptions;
pub use tables::user::Model as User;
pub use tables::*;
/// Database gives you a handle that lets you access the database.
/// It handles pooling internally.
@@ -717,3 +717,43 @@ pub struct WorktreeSettingsFile {
pub path: String,
pub content: String,
}
pub struct NewExtensionVersion {
pub name: String,
pub version: semver::Version,
pub description: String,
pub authors: Vec<String>,
pub repository: String,
pub published_at: PrimitiveDateTime,
}
#[derive(Debug, Serialize, PartialEq)]
pub struct ExtensionMetadata {
pub id: String,
pub name: String,
pub version: String,
pub authors: Vec<String>,
pub description: String,
pub repository: String,
#[serde(serialize_with = "serialize_iso8601")]
pub published_at: PrimitiveDateTime,
pub download_count: u64,
}
pub fn serialize_iso8601<S: Serializer>(
datetime: &PrimitiveDateTime,
serializer: S,
) -> Result<S::Ok, S::Error> {
const SERDE_CONFIG: iso8601::EncodedConfig = iso8601::Config::DEFAULT
.set_year_is_six_digits(false)
.set_time_precision(iso8601::TimePrecision::Second {
decimal_digits: None,
})
.encode();
datetime
.assume_utc()
.format(&time::format_description::well_known::Iso8601::<SERDE_CONFIG>)
.map_err(S::Error::custom)?
.serialize(serializer)
}

View File

@@ -85,6 +85,7 @@ id_type!(SignupId);
id_type!(UserId);
id_type!(ChannelBufferCollaboratorId);
id_type!(FlagId);
id_type!(ExtensionId);
id_type!(NotificationId);
id_type!(NotificationKindId);

View File

@@ -5,6 +5,7 @@ pub mod buffers;
pub mod channels;
pub mod contacts;
pub mod contributors;
pub mod extensions;
pub mod messages;
pub mod notifications;
pub mod projects;

View File

@@ -0,0 +1,206 @@
use super::*;
impl Database {
pub async fn get_extensions(
&self,
filter: Option<&str>,
limit: usize,
) -> Result<Vec<ExtensionMetadata>> {
self.transaction(|tx| async move {
let mut condition = Condition::all();
if let Some(filter) = filter {
let fuzzy_name_filter = Self::fuzzy_like_string(filter);
condition = condition.add(Expr::cust_with_expr("name ILIKE $1", fuzzy_name_filter));
}
let extensions = extension::Entity::find()
.filter(condition)
.order_by_desc(extension::Column::TotalDownloadCount)
.order_by_asc(extension::Column::Name)
.limit(Some(limit as u64))
.filter(
extension::Column::LatestVersion
.into_expr()
.eq(extension_version::Column::Version.into_expr()),
)
.inner_join(extension_version::Entity)
.select_also(extension_version::Entity)
.all(&*tx)
.await?;
Ok(extensions
.into_iter()
.filter_map(|(extension, latest_version)| {
let version = latest_version?;
Some(ExtensionMetadata {
id: extension.external_id,
name: extension.name,
version: version.version,
authors: version
.authors
.split(',')
.map(|author| author.trim().to_string())
.collect::<Vec<_>>(),
description: version.description,
repository: version.repository,
published_at: version.published_at,
download_count: extension.total_download_count as u64,
})
})
.collect())
})
.await
}
pub async fn get_known_extension_versions<'a>(&self) -> Result<HashMap<String, Vec<String>>> {
self.transaction(|tx| async move {
let mut extension_external_ids_by_id = HashMap::default();
let mut rows = extension::Entity::find().stream(&*tx).await?;
while let Some(row) = rows.next().await {
let row = row?;
extension_external_ids_by_id.insert(row.id, row.external_id);
}
drop(rows);
let mut known_versions_by_extension_id: HashMap<String, Vec<String>> =
HashMap::default();
let mut rows = extension_version::Entity::find().stream(&*tx).await?;
while let Some(row) = rows.next().await {
let row = row?;
let Some(extension_id) = extension_external_ids_by_id.get(&row.extension_id) else {
continue;
};
let versions = known_versions_by_extension_id
.entry(extension_id.clone())
.or_default();
if let Err(ix) = versions.binary_search(&row.version) {
versions.insert(ix, row.version);
}
}
drop(rows);
Ok(known_versions_by_extension_id)
})
.await
}
pub async fn insert_extension_versions(
&self,
versions_by_extension_id: &HashMap<&str, Vec<NewExtensionVersion>>,
) -> Result<()> {
self.transaction(|tx| async move {
for (external_id, versions) in versions_by_extension_id {
if versions.is_empty() {
continue;
}
let latest_version = versions
.iter()
.max_by_key(|version| &version.version)
.unwrap();
let insert = extension::Entity::insert(extension::ActiveModel {
name: ActiveValue::Set(latest_version.name.clone()),
external_id: ActiveValue::Set(external_id.to_string()),
id: ActiveValue::NotSet,
latest_version: ActiveValue::Set(latest_version.version.to_string()),
total_download_count: ActiveValue::NotSet,
})
.on_conflict(
OnConflict::columns([extension::Column::ExternalId])
.update_column(extension::Column::ExternalId)
.to_owned(),
);
let extension = if tx.support_returning() {
insert.exec_with_returning(&*tx).await?
} else {
// Sqlite
insert.exec_without_returning(&*tx).await?;
extension::Entity::find()
.filter(extension::Column::ExternalId.eq(*external_id))
.one(&*tx)
.await?
.ok_or_else(|| anyhow!("failed to insert extension"))?
};
extension_version::Entity::insert_many(versions.iter().map(|version| {
extension_version::ActiveModel {
extension_id: ActiveValue::Set(extension.id),
published_at: ActiveValue::Set(version.published_at),
version: ActiveValue::Set(version.version.to_string()),
authors: ActiveValue::Set(version.authors.join(", ")),
repository: ActiveValue::Set(version.repository.clone()),
description: ActiveValue::Set(version.description.clone()),
download_count: ActiveValue::NotSet,
}
}))
.on_conflict(OnConflict::new().do_nothing().to_owned())
.exec_without_returning(&*tx)
.await?;
if let Ok(db_version) = semver::Version::parse(&extension.latest_version) {
if db_version >= latest_version.version {
continue;
}
}
let mut extension = extension.into_active_model();
extension.latest_version = ActiveValue::Set(latest_version.version.to_string());
extension.name = ActiveValue::set(latest_version.name.clone());
extension::Entity::update(extension).exec(&*tx).await?;
}
Ok(())
})
.await
}
pub async fn record_extension_download(&self, extension: &str, version: &str) -> Result<bool> {
self.transaction(|tx| async move {
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
enum QueryId {
Id,
}
let extension_id: Option<ExtensionId> = extension::Entity::find()
.filter(extension::Column::ExternalId.eq(extension))
.select_only()
.column(extension::Column::Id)
.into_values::<_, QueryId>()
.one(&*tx)
.await?;
let Some(extension_id) = extension_id else {
return Ok(false);
};
extension_version::Entity::update_many()
.col_expr(
extension_version::Column::DownloadCount,
extension_version::Column::DownloadCount.into_expr().add(1),
)
.filter(
extension_version::Column::ExtensionId
.eq(extension_id)
.and(extension_version::Column::Version.eq(version)),
)
.exec(&*tx)
.await?;
extension::Entity::update_many()
.col_expr(
extension::Column::TotalDownloadCount,
extension::Column::TotalDownloadCount.into_expr().add(1),
)
.filter(extension::Column::Id.eq(extension_id))
.exec(&*tx)
.await?;
Ok(true)
})
.await
}
}

View File

@@ -10,6 +10,8 @@ pub mod channel_message;
pub mod channel_message_mention;
pub mod contact;
pub mod contributor;
pub mod extension;
pub mod extension_version;
pub mod feature_flag;
pub mod follower;
pub mod language_server;

View File

@@ -0,0 +1,27 @@
use crate::db::ExtensionId;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "extensions")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: ExtensionId,
pub external_id: String,
pub name: String,
pub latest_version: String,
pub total_download_count: i64,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(has_one = "super::extension_version::Entity")]
LatestVersion,
}
impl Related<super::extension_version::Entity> for Entity {
fn to() -> RelationDef {
Relation::LatestVersion.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,36 @@
use crate::db::ExtensionId;
use sea_orm::entity::prelude::*;
use time::PrimitiveDateTime;
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "extension_versions")]
pub struct Model {
#[sea_orm(primary_key)]
pub extension_id: ExtensionId,
#[sea_orm(primary_key)]
pub version: String,
pub published_at: PrimitiveDateTime,
pub authors: String,
pub repository: String,
pub description: String,
pub download_count: i64,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::extension::Entity",
from = "Column::ExtensionId",
to = "super::extension::Column::Id"
on_condition = r#"super::extension::Column::LatestVersion.into_expr().eq(Column::Version.into_expr())"#
)]
Extension,
}
impl Related<super::extension::Entity> for Entity {
fn to() -> RelationDef {
Relation::Extension.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -2,6 +2,7 @@ mod buffer_tests;
mod channel_tests;
mod contributor_tests;
mod db_tests;
mod extension_tests;
mod feature_flag_tests;
mod message_tests;

View File

@@ -0,0 +1,225 @@
use super::Database;
use crate::{
db::{ExtensionMetadata, NewExtensionVersion},
test_both_dbs,
};
use std::sync::Arc;
use time::{OffsetDateTime, PrimitiveDateTime};
test_both_dbs!(
test_extensions,
test_extensions_postgres,
test_extensions_sqlite
);
async fn test_extensions(db: &Arc<Database>) {
let versions = db.get_known_extension_versions().await.unwrap();
assert!(versions.is_empty());
let extensions = db.get_extensions(None, 5).await.unwrap();
assert!(extensions.is_empty());
let t0 = OffsetDateTime::from_unix_timestamp_nanos(0).unwrap();
let t0 = PrimitiveDateTime::new(t0.date(), t0.time());
db.insert_extension_versions(
&[
(
"ext1",
vec![
NewExtensionVersion {
name: "Extension 1".into(),
version: semver::Version::parse("0.0.1").unwrap(),
description: "an extension".into(),
authors: vec!["max".into()],
repository: "ext1/repo".into(),
published_at: t0,
},
NewExtensionVersion {
name: "Extension One".into(),
version: semver::Version::parse("0.0.2").unwrap(),
description: "a good extension".into(),
authors: vec!["max".into(), "marshall".into()],
repository: "ext1/repo".into(),
published_at: t0,
},
],
),
(
"ext2",
vec![NewExtensionVersion {
name: "Extension Two".into(),
version: semver::Version::parse("0.2.0").unwrap(),
description: "a great extension".into(),
authors: vec!["marshall".into()],
repository: "ext2/repo".into(),
published_at: t0,
}],
),
]
.into_iter()
.collect(),
)
.await
.unwrap();
let versions = db.get_known_extension_versions().await.unwrap();
assert_eq!(
versions,
[
("ext1".into(), vec!["0.0.1".into(), "0.0.2".into()]),
("ext2".into(), vec!["0.2.0".into()])
]
.into_iter()
.collect()
);
// The latest version of each extension is returned.
let extensions = db.get_extensions(None, 5).await.unwrap();
assert_eq!(
extensions,
&[
ExtensionMetadata {
id: "ext1".into(),
name: "Extension One".into(),
version: "0.0.2".into(),
authors: vec!["max".into(), "marshall".into()],
description: "a good extension".into(),
repository: "ext1/repo".into(),
published_at: t0,
download_count: 0,
},
ExtensionMetadata {
id: "ext2".into(),
name: "Extension Two".into(),
version: "0.2.0".into(),
authors: vec!["marshall".into()],
description: "a great extension".into(),
repository: "ext2/repo".into(),
published_at: t0,
download_count: 0
},
]
);
// Record extensions being downloaded.
for _ in 0..7 {
assert!(db.record_extension_download("ext2", "0.0.2").await.unwrap());
}
for _ in 0..3 {
assert!(db.record_extension_download("ext1", "0.0.1").await.unwrap());
}
for _ in 0..2 {
assert!(db.record_extension_download("ext1", "0.0.2").await.unwrap());
}
// Record download returns false if the extension does not exist.
assert!(!db
.record_extension_download("no-such-extension", "0.0.2")
.await
.unwrap());
// Extensions are returned in descending order of total downloads.
let extensions = db.get_extensions(None, 5).await.unwrap();
assert_eq!(
extensions,
&[
ExtensionMetadata {
id: "ext2".into(),
name: "Extension Two".into(),
version: "0.2.0".into(),
authors: vec!["marshall".into()],
description: "a great extension".into(),
repository: "ext2/repo".into(),
published_at: t0,
download_count: 7
},
ExtensionMetadata {
id: "ext1".into(),
name: "Extension One".into(),
version: "0.0.2".into(),
authors: vec!["max".into(), "marshall".into()],
description: "a good extension".into(),
repository: "ext1/repo".into(),
published_at: t0,
download_count: 5,
},
]
);
// Add more extensions, including a new version of `ext1`, and backfilling
// an older version of `ext2`.
db.insert_extension_versions(
&[
(
"ext1",
vec![NewExtensionVersion {
name: "Extension One".into(),
version: semver::Version::parse("0.0.3").unwrap(),
description: "a real good extension".into(),
authors: vec!["max".into(), "marshall".into()],
repository: "ext1/repo".into(),
published_at: t0,
}],
),
(
"ext2",
vec![NewExtensionVersion {
name: "Extension Two".into(),
version: semver::Version::parse("0.1.0").unwrap(),
description: "an old extension".into(),
authors: vec!["marshall".into()],
repository: "ext2/repo".into(),
published_at: t0,
}],
),
]
.into_iter()
.collect(),
)
.await
.unwrap();
let versions = db.get_known_extension_versions().await.unwrap();
assert_eq!(
versions,
[
(
"ext1".into(),
vec!["0.0.1".into(), "0.0.2".into(), "0.0.3".into()]
),
("ext2".into(), vec!["0.1.0".into(), "0.2.0".into()])
]
.into_iter()
.collect()
);
let extensions = db.get_extensions(None, 5).await.unwrap();
assert_eq!(
extensions,
&[
ExtensionMetadata {
id: "ext2".into(),
name: "Extension Two".into(),
version: "0.2.0".into(),
authors: vec!["marshall".into()],
description: "a great extension".into(),
repository: "ext2/repo".into(),
published_at: t0,
download_count: 7
},
ExtensionMetadata {
id: "ext1".into(),
name: "Extension One".into(),
version: "0.0.3".into(),
authors: vec!["max".into(), "marshall".into()],
description: "a real good extension".into(),
repository: "ext1/repo".into(),
published_at: t0,
download_count: 5,
},
]
);
}

View File

@@ -3,7 +3,8 @@ use std::fs;
pub fn load_dotenv() -> anyhow::Result<()> {
let env: toml::map::Map<String, toml::Value> = toml::de::from_str(
&fs::read_to_string("./.env.toml").map_err(|_| anyhow!("no .env.toml file found"))?,
&fs::read_to_string("./crates/collab/.env.toml")
.map_err(|_| anyhow!("no .env.toml file found"))?,
)?;
for (key, value) in env {

View File

@@ -8,11 +8,14 @@ pub mod rpc;
#[cfg(test)]
mod tests;
use anyhow::anyhow;
use aws_config::{BehaviorVersion, Region};
use axum::{http::StatusCode, response::IntoResponse};
use db::Database;
use executor::Executor;
use serde::Deserialize;
use std::{path::PathBuf, sync::Arc};
use util::ResultExt;
pub type Result<T, E = Error> = std::result::Result<T, E>;
@@ -100,6 +103,11 @@ pub struct Config {
pub live_kit_secret: Option<String>,
pub rust_log: Option<String>,
pub log_json: Option<bool>,
pub blob_store_url: Option<String>,
pub blob_store_region: Option<String>,
pub blob_store_access_key: Option<String>,
pub blob_store_secret_key: Option<String>,
pub blob_store_bucket: Option<String>,
pub zed_environment: Arc<str>,
}
@@ -118,6 +126,7 @@ pub struct MigrateConfig {
pub struct AppState {
pub db: Arc<Database>,
pub live_kit_client: Option<Arc<dyn live_kit_server::api::Client>>,
pub blob_store_client: Option<aws_sdk_s3::Client>,
pub config: Config,
}
@@ -146,8 +155,44 @@ impl AppState {
let this = Self {
db: Arc::new(db),
live_kit_client,
blob_store_client: build_blob_store_client(&config).await.log_err(),
config,
};
Ok(Arc::new(this))
}
}
async fn build_blob_store_client(config: &Config) -> anyhow::Result<aws_sdk_s3::Client> {
let keys = aws_sdk_s3::config::Credentials::new(
config
.blob_store_access_key
.clone()
.ok_or_else(|| anyhow!("missing blob_store_access_key"))?,
config
.blob_store_secret_key
.clone()
.ok_or_else(|| anyhow!("missing blob_store_secret_key"))?,
None,
None,
"env",
);
let s3_config = aws_config::defaults(BehaviorVersion::latest())
.endpoint_url(
config
.blob_store_url
.as_ref()
.ok_or_else(|| anyhow!("missing blob_store_url"))?,
)
.region(Region::new(
config
.blob_store_region
.clone()
.ok_or_else(|| anyhow!("missing blob_store_region"))?,
))
.credentials_provider(keys)
.load()
.await;
Ok(aws_sdk_s3::Client::new(&s3_config))
}

View File

@@ -1,6 +1,9 @@
use anyhow::anyhow;
use axum::{routing::get, Extension, Router};
use collab::{db, env, executor::Executor, AppState, Config, MigrateConfig, Result};
use collab::{
api::fetch_extensions_from_blob_store_periodically, db, env, executor::Executor, AppState,
Config, MigrateConfig, Result,
};
use db::Database;
use std::{
env::args,
@@ -50,6 +53,8 @@ async fn main() -> Result<()> {
let rpc_server = collab::rpc::Server::new(epoch, state.clone(), Executor::Production);
rpc_server.start().await?;
fetch_extensions_from_blob_store_periodically(state.clone(), Executor::Production);
let app = collab::api::routes(rpc_server.clone(), state.clone())
.merge(collab::rpc::routes(rpc_server.clone()))
.merge(

View File

@@ -2088,21 +2088,16 @@ async fn update_followers(request: proto::UpdateFollowers, session: Session) ->
};
// For now, don't send view update messages back to that view's current leader.
let connection_id_to_omit = request.variant.as_ref().and_then(|variant| match variant {
let peer_id_to_omit = request.variant.as_ref().and_then(|variant| match variant {
proto::update_followers::Variant::UpdateView(payload) => payload.leader_id,
_ => None,
});
for follower_peer_id in request.follower_ids.iter().copied() {
let follower_connection_id = follower_peer_id.into();
if Some(follower_peer_id) != connection_id_to_omit
&& connection_ids.contains(&follower_connection_id)
{
session.peer.forward_send(
session.connection_id,
follower_connection_id,
request.clone(),
)?;
for connection_id in connection_ids.iter().cloned() {
if Some(connection_id.into()) != peer_id_to_omit && connection_id != session.connection_id {
session
.peer
.forward_send(session.connection_id, connection_id, request.clone())?;
}
}
Ok(())

View File

@@ -2077,3 +2077,66 @@ async fn test_following_to_channel_notes_other_workspace(
assert_eq!(editor.tab_description(0, cx).unwrap(), "1.txt");
});
}
#[gpui::test]
async fn test_following_while_deactivated(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
let (_, client_a, client_b, channel) = TestServer::start2(cx_a, cx_b).await;
let mut cx_a2 = cx_a.clone();
let (workspace_a, cx_a) = client_a.build_test_workspace(cx_a).await;
join_channel(channel, &client_a, cx_a).await.unwrap();
share_workspace(&workspace_a, cx_a).await.unwrap();
// a opens 1.txt
cx_a.simulate_keystrokes("cmd-p 1 enter");
cx_a.run_until_parked();
workspace_a.update(cx_a, |workspace, cx| {
let editor = workspace.active_item(cx).unwrap();
assert_eq!(editor.tab_description(0, cx).unwrap(), "1.txt");
});
// b joins channel and is following a
join_channel(channel, &client_b, cx_b).await.unwrap();
cx_b.run_until_parked();
let (workspace_b, cx_b) = client_b.active_workspace(cx_b);
workspace_b.update(cx_b, |workspace, cx| {
let editor = workspace.active_item(cx).unwrap();
assert_eq!(editor.tab_description(0, cx).unwrap(), "1.txt");
});
// stop following
cx_b.simulate_keystrokes("down");
// a opens a different file while not followed
cx_a.simulate_keystrokes("cmd-p 2 enter");
workspace_b.update(cx_b, |workspace, cx| {
let editor = workspace.active_item_as::<Editor>(cx).unwrap();
assert_eq!(editor.tab_description(0, cx).unwrap(), "1.txt");
});
// a opens a file in a new window
let (_, cx_a2) = client_a.build_test_workspace(&mut cx_a2).await;
cx_a2.update(|cx| cx.activate_window());
cx_a2.simulate_keystrokes("cmd-p 3 enter");
cx_a2.run_until_parked();
// b starts following a again
cx_b.simulate_keystrokes("cmd-ctrl-alt-f");
cx_a.run_until_parked();
// a returns to the shared project
cx_a.update(|cx| cx.activate_window());
cx_a.run_until_parked();
workspace_a.update(cx_a, |workspace, cx| {
let editor = workspace.active_item(cx).unwrap();
assert_eq!(editor.tab_description(0, cx).unwrap(), "2.js");
});
// b should follow a back
workspace_b.update(cx_b, |workspace, cx| {
let editor = workspace.active_item_as::<Editor>(cx).unwrap();
assert_eq!(editor.tab_description(0, cx).unwrap(), "2.js");
});
}

View File

@@ -273,7 +273,7 @@ impl TestServer {
collab_ui::init(&app_state, cx);
file_finder::init(cx);
menu::init();
settings::KeymapFile::load_asset("keymaps/default.json", cx).unwrap();
settings::KeymapFile::load_asset("keymaps/default-macos.json", cx).unwrap();
});
client
@@ -479,6 +479,7 @@ impl TestServer {
Arc::new(AppState {
db: test_db.db().clone(),
live_kit_client: Some(Arc::new(fake_server.create_api_client())),
blob_store_client: None,
config: Config {
http_port: 0,
database_url: "".into(),
@@ -491,6 +492,11 @@ impl TestServer {
rust_log: None,
log_json: None,
zed_environment: "test".into(),
blob_store_url: None,
blob_store_region: None,
blob_store_access_key: None,
blob_store_secret_key: None,
blob_store_bucket: None,
},
})
}

View File

@@ -66,6 +66,7 @@ util.workspace = true
vcs_menu.workspace = true
workspace.workspace = true
zed_actions.workspace = true
sys-locale.workspace = true
[dev-dependencies]
call = { workspace = true, features = ["test-support"] }

View File

@@ -63,6 +63,7 @@ pub struct ChatPanel {
focus_handle: FocusHandle,
open_context_menu: Option<(u64, Subscription)>,
highlighted_message: Option<(u64, Task<()>)>,
last_acknowledged_message_id: Option<u64>,
}
#[derive(Serialize, Deserialize)]
@@ -126,6 +127,7 @@ impl ChatPanel {
focus_handle: cx.focus_handle(),
open_context_menu: None,
highlighted_message: None,
last_acknowledged_message_id: None,
};
if let Some(channel_id) = ActiveCall::global(cx)
@@ -281,6 +283,13 @@ impl ChatPanel {
fn acknowledge_last_message(&mut self, cx: &mut ViewContext<Self>) {
if self.active && self.is_scrolled_to_bottom {
if let Some((chat, _)) = &self.active_chat {
if let Some(channel_id) = self.channel_id(cx) {
self.last_acknowledged_message_id = self
.channel_store
.read(cx)
.last_acknowledge_message_id(channel_id);
}
chat.update(cx, |chat, cx| {
chat.acknowledge_last_message(cx);
});
@@ -362,9 +371,9 @@ impl ChatPanel {
.px_1()
.py_0p5()
.mb_1()
.overflow_hidden()
.child(
div()
.overflow_hidden()
.max_h_12()
.child(reply_to_message_body.element(body_element_id, cx)),
),
@@ -454,119 +463,145 @@ impl ChatPanel {
cx.theme().colors().panel_background
};
v_flex().w_full().relative().child(
div()
.bg(background)
.rounded_md()
.overflow_hidden()
.px_1()
.py_0p5()
.when(!is_continuation_from_previous, |this| {
this.mt_2().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,
v_flex()
.w_full()
.relative()
.child(
div()
.bg(background)
.rounded_md()
.overflow_hidden()
.px_1()
.py_0p5()
.when(!is_continuation_from_previous, |this| {
this.mt_2().child(
h_flex()
.text_ui_sm()
.child(div().absolute().child(
Avatar::new(message.sender.avatar_uri.clone()).size(rems(1.)),
))
.size(LabelSize::Small)
.color(Color::Muted),
),
.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,
None,
))
.size(LabelSize::Small)
.color(Color::Muted),
),
)
})
.when(
message.reply_to_message_id.is_some() && reply_to_message.is_none(),
|this| {
const MESSAGE_DELETED: &str = "Message has been deleted";
let body_text = StyledText::new(MESSAGE_DELETED).with_highlights(
&cx.text_style(),
vec![(
0..MESSAGE_DELETED.len(),
HighlightStyle {
font_style: Some(FontStyle::Italic),
..Default::default()
},
)],
);
this.child(
div()
.border_l_2()
.text_ui_xs()
.border_color(cx.theme().colors().border)
.px_1()
.py_0p5()
.child(body_text),
)
},
)
})
.when(
message.reply_to_message_id.is_some() && reply_to_message.is_none(),
|this| {
const MESSAGE_DELETED: &str = "Message has been deleted";
let body_text = StyledText::new(MESSAGE_DELETED).with_highlights(
&cx.text_style(),
vec![(
0..MESSAGE_DELETED.len(),
HighlightStyle {
font_style: Some(FontStyle::Italic),
..Default::default()
},
)],
);
this.child(
div()
.border_l_2()
.text_ui_xs()
.border_color(cx.theme().colors().border)
.px_1()
.py_0p5()
.child(body_text),
.when_some(reply_to_message, |el, reply_to_message| {
el.child(self.render_replied_to_message(
Some(message.id),
&reply_to_message,
cx,
))
})
.when(mentioning_you || replied_to_you, |this| this.my_0p5())
.map(|el| {
let text = self.markdown_data.entry(message.id).or_insert_with(|| {
Self::render_markdown_with_mentions(
&self.languages,
self.client.id(),
&message,
)
});
el.child(
v_flex()
.w_full()
.text_ui_sm()
.id(element_id)
.group("")
.child(text.element("body".into(), cx))
.child(
div()
.absolute()
.z_index(1)
.right_0()
.w_6()
.bg(background)
.when(!self.has_open_menu(message_id), |el| {
el.visible_on_hover("")
})
.when_some(message_id, |el, message_id| {
el.child(
popover_menu(("menu", message_id))
.trigger(IconButton::new(
("trigger", message_id),
IconName::Ellipsis,
))
.menu(move |cx| {
Some(Self::render_message_menu(
&this,
message_id,
can_delete_message,
cx,
))
}),
)
}),
),
)
},
)
.when_some(reply_to_message, |el, reply_to_message| {
el.child(self.render_replied_to_message(
Some(message.id),
&reply_to_message,
cx,
))
})
.when(mentioning_you || replied_to_you, |this| this.my_0p5())
.map(|el| {
let text = self.markdown_data.entry(message.id).or_insert_with(|| {
Self::render_markdown_with_mentions(
&self.languages,
self.client.id(),
&message,
)
});
el.child(
v_flex()
.w_full()
.text_ui_sm()
.id(element_id)
.group("")
.child(text.element("body".into(), cx))
}),
)
.when(
self.last_acknowledged_message_id
.is_some_and(|l| Some(l) == message_id),
|this| {
this.child(
h_flex()
.py_2()
.gap_1()
.items_center()
.child(div().w_full().h_0p5().bg(cx.theme().colors().border))
.child(
div()
.absolute()
.z_index(1)
.right_0()
.w_6()
.bg(background)
.when(!self.has_open_menu(message_id), |el| {
el.visible_on_hover("")
})
.when_some(message_id, |el, message_id| {
el.child(
popover_menu(("menu", message_id))
.trigger(IconButton::new(
("trigger", message_id),
IconName::Ellipsis,
))
.menu(move |cx| {
Some(Self::render_message_menu(
&this,
message_id,
can_delete_message,
cx,
))
}),
)
}),
),
.px_1()
.rounded_md()
.text_ui_xs()
.bg(cx.theme().colors().background)
.child("New messages"),
)
.child(div().w_full().h_0p5().bg(cx.theme().colors().border)),
)
}),
)
},
)
}
fn has_open_menu(&self, message_id: Option<u64>) -> bool {
@@ -681,8 +716,11 @@ impl ChatPanel {
cx.spawn(|this, mut cx| async move {
let chat = open_chat.await?;
this.update(&mut cx, |this, cx| {
let highlight_message_id = scroll_to_message_id;
let scroll_to_message_id = this.update(&mut cx, |this, cx| {
this.set_active_chat(chat.clone(), cx);
scroll_to_message_id.or_else(|| this.last_acknowledged_message_id)
})?;
if let Some(message_id) = scroll_to_message_id {
@@ -690,21 +728,22 @@ impl ChatPanel {
ChannelChat::load_history_since_message(chat.clone(), message_id, (*cx).clone())
.await
{
let task = cx.spawn({
let this = this.clone();
|mut cx| async move {
cx.background_executor().timer(Duration::from_secs(2)).await;
this.update(&mut cx, |this, cx| {
this.highlighted_message.take();
cx.notify();
})
.ok();
}
});
this.update(&mut cx, |this, cx| {
this.highlighted_message = Some((message_id, task));
if let Some(highlight_message_id) = highlight_message_id {
let task = cx.spawn({
|this, mut cx| async move {
cx.background_executor().timer(Duration::from_secs(2)).await;
this.update(&mut cx, |this, cx| {
this.highlighted_message.take();
cx.notify();
})
.ok();
}
});
this.highlighted_message = Some((highlight_message_id, task));
}
if this.active_chat.as_ref().map_or(false, |(c, _)| *c == chat) {
this.message_list.scroll_to(ListOffset {
item_ix,
@@ -733,7 +772,7 @@ impl Render for ChatPanel {
v_flex()
.key_context("ChatPanel")
.track_focus(&self.focus_handle)
.full()
.size_full()
.on_action(cx.listener(Self::send))
.child(
h_flex().z_index(1).child(
@@ -755,11 +794,11 @@ impl Render for ChatPanel {
)
.child(div().flex_grow().px_2().map(|this| {
if self.active_chat.is_some() {
this.child(list(self.message_list.clone()).full())
this.child(list(self.message_list.clone()).size_full())
} else {
this.child(
div()
.full()
.size_full()
.p_4()
.child(
Label::new("Select a channel to chat in.")
@@ -801,18 +840,21 @@ impl Render for ChatPanel {
el.when_some(reply_message, |el, reply_message| {
el.child(
div()
h_flex()
.when(!self.is_scrolled_to_bottom, |el| {
el.border_t_1().border_color(cx.theme().colors().border)
})
.flex()
.w_full()
.items_start()
.justify_between()
.overflow_hidden()
.items_start()
.py_1()
.px_2()
.bg(cx.theme().colors().background)
.child(self.render_replied_to_message(None, &reply_message, cx))
.child(
div().flex_shrink().overflow_hidden().child(
self.render_replied_to_message(None, &reply_message, cx),
),
)
.child(
IconButton::new("close-reply-preview", IconName::Close)
.shape(ui::IconButtonShape::Square)
@@ -917,26 +959,58 @@ impl Panel for ChatPanel {
impl EventEmitter<PanelEvent> for ChatPanel {}
fn is_12_hour_clock(locale: String) -> bool {
[
"es-MX", "es-CO", "es-SV", "es-NI",
"es-HN", // Mexico, Colombia, El Salvador, Nicaragua, Honduras
"en-US", "en-CA", "en-AU", "en-NZ", // U.S, Canada, Australia, New Zealand
"ar-SA", "ar-EG", "ar-JO", // Saudi Arabia, Egypt, Jordan
"en-IN", "hi-IN", // India, Hindu
"en-PK", "ur-PK", // Pakistan, Urdu
"en-PH", "fil-PH", // Philippines, Filipino
"bn-BD", "ccp-BD", // Bangladesh, Chakma
"en-IE", "ga-IE", // Ireland, Irish
"en-MY", "ms-MY", // Malaysia, Malay
]
.contains(&locale.as_str())
}
fn format_timestamp(
reference: OffsetDateTime,
timestamp: OffsetDateTime,
timezone: UtcOffset,
locale: Option<String>,
) -> String {
let locale = match locale {
Some(locale) => locale,
None => sys_locale::get_locale().unwrap_or_else(|| String::from("en-US")),
};
let timestamp_local = timestamp.to_offset(timezone);
let timestamp_local_hour = timestamp_local.hour();
let hour_12 = match timestamp_local_hour {
0 => 12, // Midnight
13..=23 => timestamp_local_hour - 12, // PM hours
_ => timestamp_local_hour, // AM hours
};
let meridiem = if timestamp_local_hour >= 12 {
"pm"
} else {
"am"
};
let timestamp_local_minute = timestamp_local.minute();
let formatted_time = format!("{:02}:{:02} {}", hour_12, timestamp_local_minute, meridiem);
let (hour, meridiem) = if is_12_hour_clock(locale) {
let meridiem = if timestamp_local_hour >= 12 {
"pm"
} else {
"am"
};
let hour_12 = match timestamp_local_hour {
0 => 12, // Midnight
13..=23 => timestamp_local_hour - 12, // PM hours
_ => timestamp_local_hour, // AM hours
};
(hour_12, Some(meridiem))
} else {
(timestamp_local_hour, None)
};
let formatted_time = match meridiem {
Some(meridiem) => format!("{:02}:{:02} {}", hour, timestamp_local_minute, meridiem),
None => format!("{:02}:{:02}", hour, timestamp_local_minute),
};
let reference_local = reference.to_offset(timezone);
let reference_local_date = reference_local.date();
@@ -950,12 +1024,20 @@ fn format_timestamp(
return format!("yesterday at {}", formatted_time);
}
format!(
"{:02}/{:02}/{}",
timestamp_local_date.month() as u32,
timestamp_local_date.day(),
timestamp_local_date.year()
)
match meridiem {
Some(_) => format!(
"{:02}/{:02}/{}",
timestamp_local_date.month() as u32,
timestamp_local_date.day(),
timestamp_local_date.year()
),
None => format!(
"{:02}/{:02}/{}",
timestamp_local_date.day(),
timestamp_local_date.month() as u32,
timestamp_local_date.year()
),
}
}
#[cfg(test)]
@@ -1015,13 +1097,135 @@ mod tests {
);
}
#[gpui::test]
fn test_render_markdown_with_auto_detect_links() {
let language_registry = Arc::new(LanguageRegistry::test());
let message = channel::ChannelMessage {
id: ChannelMessageId::Saved(0),
body: "Here is a link https://zed.dev to zeds website".to_string(),
timestamp: OffsetDateTime::now_utc(),
sender: Arc::new(client::User {
github_login: "fgh".into(),
avatar_uri: "avatar_fgh".into(),
id: 103,
}),
nonce: 5,
mentions: Vec::new(),
reply_to_message_id: None,
};
let message = ChatPanel::render_markdown_with_mentions(&language_registry, 102, &message);
// Note that the "'" was replaced with due to smart punctuation.
let (body, ranges) =
marked_text_ranges("Here is a link «https://zed.dev» to zeds website", false);
assert_eq!(message.text, body);
assert_eq!(1, ranges.len());
assert_eq!(
message.highlights,
vec![(
ranges[0].clone(),
HighlightStyle {
underline: Some(gpui::UnderlineStyle {
thickness: 1.0.into(),
..Default::default()
}),
..Default::default()
}
.into()
),]
);
}
#[gpui::test]
fn test_render_markdown_with_auto_detect_links_and_additional_formatting() {
let language_registry = Arc::new(LanguageRegistry::test());
let message = channel::ChannelMessage {
id: ChannelMessageId::Saved(0),
body: "**Here is a link https://zed.dev to zeds website**".to_string(),
timestamp: OffsetDateTime::now_utc(),
sender: Arc::new(client::User {
github_login: "fgh".into(),
avatar_uri: "avatar_fgh".into(),
id: 103,
}),
nonce: 5,
mentions: Vec::new(),
reply_to_message_id: None,
};
let message = ChatPanel::render_markdown_with_mentions(&language_registry, 102, &message);
// Note that the "'" was replaced with due to smart punctuation.
let (body, ranges) = marked_text_ranges(
"«Here is a link »«https://zed.dev»« to zeds website»",
false,
);
assert_eq!(message.text, body);
assert_eq!(3, ranges.len());
assert_eq!(
message.highlights,
vec![
(
ranges[0].clone(),
HighlightStyle {
font_weight: Some(gpui::FontWeight::BOLD),
..Default::default()
}
.into()
),
(
ranges[1].clone(),
HighlightStyle {
font_weight: Some(gpui::FontWeight::BOLD),
underline: Some(gpui::UnderlineStyle {
thickness: 1.0.into(),
..Default::default()
}),
..Default::default()
}
.into()
),
(
ranges[2].clone(),
HighlightStyle {
font_weight: Some(gpui::FontWeight::BOLD),
..Default::default()
}
.into()
),
]
);
}
#[test]
fn test_format_locale() {
let reference = create_offset_datetime(1990, 4, 12, 16, 45, 0);
let timestamp = create_offset_datetime(1990, 4, 12, 15, 30, 0);
assert_eq!(
format_timestamp(
reference,
timestamp,
test_timezone(),
Some(String::from("en-GB"))
),
"15:30"
);
}
#[test]
fn test_format_today() {
let reference = create_offset_datetime(1990, 4, 12, 16, 45, 0);
let timestamp = create_offset_datetime(1990, 4, 12, 15, 30, 0);
assert_eq!(
format_timestamp(reference, timestamp, test_timezone()),
format_timestamp(
reference,
timestamp,
test_timezone(),
Some(String::from("en-US"))
),
"03:30 pm"
);
}
@@ -1032,7 +1236,12 @@ mod tests {
let timestamp = create_offset_datetime(1990, 4, 11, 9, 0, 0);
assert_eq!(
format_timestamp(reference, timestamp, test_timezone()),
format_timestamp(
reference,
timestamp,
test_timezone(),
Some(String::from("en-US"))
),
"yesterday at 09:00 am"
);
}
@@ -1043,7 +1252,12 @@ mod tests {
let timestamp = create_offset_datetime(1990, 4, 11, 20, 0, 0);
assert_eq!(
format_timestamp(reference, timestamp, test_timezone()),
format_timestamp(
reference,
timestamp,
test_timezone(),
Some(String::from("en-US"))
),
"yesterday at 08:00 pm"
);
}
@@ -1054,7 +1268,12 @@ mod tests {
let timestamp = create_offset_datetime(1990, 4, 11, 18, 0, 0);
assert_eq!(
format_timestamp(reference, timestamp, test_timezone()),
format_timestamp(
reference,
timestamp,
test_timezone(),
Some(String::from("en-US"))
),
"yesterday at 06:00 pm"
);
}
@@ -1065,7 +1284,12 @@ mod tests {
let timestamp = create_offset_datetime(1990, 4, 11, 23, 55, 0);
assert_eq!(
format_timestamp(reference, timestamp, test_timezone()),
format_timestamp(
reference,
timestamp,
test_timezone(),
Some(String::from("en-US"))
),
"yesterday at 11:55 pm"
);
}
@@ -1076,7 +1300,12 @@ mod tests {
let timestamp = create_offset_datetime(1990, 4, 1, 20, 0, 0);
assert_eq!(
format_timestamp(reference, timestamp, test_timezone()),
format_timestamp(
reference,
timestamp,
test_timezone(),
Some(String::from("en-US"))
),
"yesterday at 08:00 pm"
);
}
@@ -1087,7 +1316,12 @@ mod tests {
let timestamp = create_offset_datetime(1990, 4, 10, 20, 20, 0);
assert_eq!(
format_timestamp(reference, timestamp, test_timezone()),
format_timestamp(
reference,
timestamp,
test_timezone(),
Some(String::from("en-US"))
),
"04/10/1990"
);
}

View File

@@ -2036,7 +2036,7 @@ impl CollabPanel {
fn render_signed_in(&mut self, cx: &mut ViewContext<Self>) -> Div {
v_flex()
.size_full()
.child(list(self.list_state.clone()).full())
.child(list(self.list_state.clone()).size_full())
.child(
v_flex()
.child(div().mx_2().border_primary(cx).border_t())

View File

@@ -11,7 +11,7 @@ use gpui::{
};
use picker::{Picker, PickerDelegate};
use std::sync::Arc;
use ui::{prelude::*, Avatar, Checkbox, ContextMenu, ListItem, ListItemSpacing};
use ui::{prelude::*, Avatar, CheckboxWithLabel, ContextMenu, ListItem, ListItemSpacing};
use util::TryFutureExt;
use workspace::{notifications::DetachAndPromptErr, ModalView};
@@ -43,7 +43,7 @@ impl ChannelModal {
cx.observe(&channel_store, |_, _, cx| cx.notify()).detach();
let channel_modal = cx.view().downgrade();
let picker = cx.new_view(|cx| {
Picker::new(
Picker::uniform_list(
ChannelModalDelegate {
channel_modal,
matching_users: Vec::new(),
@@ -177,22 +177,16 @@ impl Render for ChannelModal {
.h(rems(22. / 16.))
.justify_between()
.line_height(rems(1.25))
.child(
h_flex()
.gap_2()
.child(
Checkbox::new(
"is-public",
if visibility == ChannelVisibility::Public {
ui::Selection::Selected
} else {
ui::Selection::Unselected
},
)
.on_click(cx.listener(Self::set_channel_visibility)),
)
.child(Label::new("Public").size(LabelSize::Small)),
)
.child(CheckboxWithLabel::new(
"is-public",
Label::new("Public").size(LabelSize::Small),
if visibility == ChannelVisibility::Public {
ui::Selection::Selected
} else {
ui::Selection::Unselected
},
cx.listener(Self::set_channel_visibility),
))
.children(
Some(
Button::new("copy-link", "Copy Link")

View File

@@ -22,7 +22,7 @@ impl ContactFinder {
potential_contacts: Arc::from([]),
selected_index: 0,
};
let picker = cx.new_view(|cx| Picker::new(delegate, cx).modal(false));
let picker = cx.new_view(|cx| Picker::uniform_list(delegate, cx).modal(false));
Self { picker }
}

View File

@@ -566,10 +566,7 @@ impl CollabTitlebarItem {
ActiveCall::global(cx)
.update(cx, |call, cx| call.set_location(Some(&self.project), cx))
.detach_and_log_err(cx);
return;
}
if cx.active_window().is_none() {
} else if cx.active_window().is_none() {
ActiveCall::global(cx)
.update(cx, |call, cx| call.set_location(None, cx))
.detach_and_log_err(cx);

View File

@@ -28,6 +28,7 @@ ui.workspace = true
util.workspace = true
workspace.workspace = true
zed_actions.workspace = true
postage.workspace = true
[dev-dependencies]
ctor.workspace = true

View File

@@ -1,6 +1,7 @@
use std::{
cmp::{self, Reverse},
sync::Arc,
time::Duration,
};
use client::telemetry::Telemetry;
@@ -9,11 +10,12 @@ use copilot::CommandPaletteFilter;
use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{
actions, Action, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Global,
ParentElement, Render, Styled, View, ViewContext, VisualContext, WeakView,
ParentElement, Render, Styled, Task, View, ViewContext, VisualContext, WeakView,
};
use picker::{Picker, PickerDelegate};
use release_channel::{parse_zed_link, ReleaseChannel};
use postage::{sink::Sink, stream::Stream};
use release_channel::parse_zed_link;
use ui::{h_flex, prelude::*, v_flex, HighlightedLabel, KeyBinding, ListItem, ListItemSpacing};
use util::ResultExt;
use workspace::{ModalView, Workspace};
@@ -80,7 +82,7 @@ impl CommandPalette {
previous_focus_handle,
);
let picker = cx.new_view(|cx| Picker::new(delegate, cx));
let picker = cx.new_view(|cx| Picker::uniform_list(delegate, cx));
Self { picker }
}
}
@@ -119,6 +121,10 @@ pub struct CommandPaletteDelegate {
selected_ix: usize,
telemetry: Arc<Telemetry>,
previous_focus_handle: FocusHandle,
updating_matches: Option<(
Task<()>,
postage::dispatch::Receiver<(Vec<Command>, Vec<StringMatch>)>,
)>,
}
struct Command {
@@ -138,7 +144,7 @@ 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 a user already knows a keystroke for a command, they are unlikely to use a command palette to look for it.
#[derive(Default)]
#[derive(Default, Clone)]
struct HitCounts(HashMap<String, usize>);
impl Global for HitCounts {}
@@ -158,6 +164,66 @@ impl CommandPaletteDelegate {
selected_ix: 0,
telemetry,
previous_focus_handle,
updating_matches: None,
}
}
fn matches_updated(
&mut self,
query: String,
mut commands: Vec<Command>,
mut matches: Vec<StringMatch>,
cx: &mut ViewContext<Picker<Self>>,
) {
self.updating_matches.take();
let mut intercept_result =
if let Some(interceptor) = cx.try_global::<CommandPaletteInterceptor>() {
(interceptor.0)(&query, cx)
} else {
None
};
if parse_zed_link(&query).is_some() {
intercept_result = Some(CommandInterceptResult {
action: OpenZedUrl { url: query.clone() }.boxed_clone(),
string: query.clone(),
positions: vec![],
})
}
if let Some(CommandInterceptResult {
action,
string,
positions,
}) = intercept_result
{
if let Some(idx) = matches
.iter()
.position(|m| commands[m.candidate_id].action.type_id() == action.type_id())
{
matches.remove(idx);
}
commands.push(Command {
name: string.clone(),
action,
});
matches.insert(
0,
StringMatch {
candidate_id: commands.len() - 1,
string,
positions,
score: 0.0,
},
)
}
self.commands = commands;
self.matches = matches;
if self.matches.is_empty() {
self.selected_ix = 0;
} else {
self.selected_ix = cmp::min(self.selected_ix, self.matches.len() - 1);
}
}
}
@@ -186,113 +252,99 @@ impl PickerDelegate for CommandPaletteDelegate {
query: String,
cx: &mut ViewContext<Picker<Self>>,
) -> gpui::Task<()> {
let mut commands = self.all_commands.clone();
cx.spawn(move |picker, mut cx| async move {
cx.read_global::<HitCounts, _>(|hit_counts, _| {
let (mut tx, mut rx) = postage::dispatch::channel(1);
let task = cx.background_executor().spawn({
let mut commands = self.all_commands.clone();
let hit_counts = cx.global::<HitCounts>().clone();
let executor = cx.background_executor().clone();
let query = query.clone();
async move {
commands.sort_by_key(|action| {
(
Reverse(hit_counts.0.get(&action.name).cloned()),
action.name.clone(),
)
});
})
.ok();
let candidates = commands
.iter()
.enumerate()
.map(|(ix, command)| StringMatchCandidate {
id: ix,
string: command.name.to_string(),
char_bag: command.name.chars().collect(),
})
.collect::<Vec<_>>();
let mut matches = if query.is_empty() {
candidates
.into_iter()
let candidates = commands
.iter()
.enumerate()
.map(|(index, candidate)| StringMatch {
candidate_id: index,
string: candidate.string,
positions: Vec::new(),
score: 0.0,
.map(|(ix, command)| StringMatchCandidate {
id: ix,
string: command.name.to_string(),
char_bag: command.name.chars().collect(),
})
.collect()
} else {
fuzzy::match_strings(
&candidates,
&query,
true,
10000,
&Default::default(),
cx.background_executor().clone(),
)
.await
.collect::<Vec<_>>();
let matches = if query.is_empty() {
candidates
.into_iter()
.enumerate()
.map(|(index, candidate)| StringMatch {
candidate_id: index,
string: candidate.string,
positions: Vec::new(),
score: 0.0,
})
.collect()
} else {
let ret = fuzzy::match_strings(
&candidates,
&query,
true,
10000,
&Default::default(),
executor,
)
.await;
ret
};
tx.send((commands, matches)).await.log_err();
}
});
self.updating_matches = Some((task, rx.clone()));
cx.spawn(move |picker, mut cx| async move {
let Some((commands, matches)) = rx.recv().await else {
return;
};
let mut intercept_result = cx
.try_read_global(|interceptor: &CommandPaletteInterceptor, cx| {
(interceptor.0)(&query, cx)
})
.flatten();
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(),
string: query.clone(),
positions: vec![],
})
}
}
if let Some(CommandInterceptResult {
action,
string,
positions,
}) = intercept_result
{
if let Some(idx) = matches
.iter()
.position(|m| commands[m.candidate_id].action.type_id() == action.type_id())
{
matches.remove(idx);
}
commands.push(Command {
name: string.clone(),
action,
});
matches.insert(
0,
StringMatch {
candidate_id: commands.len() - 1,
string,
positions,
score: 0.0,
},
)
}
picker
.update(&mut cx, |picker, _| {
let delegate = &mut picker.delegate;
delegate.commands = commands;
delegate.matches = matches;
if delegate.matches.is_empty() {
delegate.selected_ix = 0;
} else {
delegate.selected_ix =
cmp::min(delegate.selected_ix, delegate.matches.len() - 1);
}
.update(&mut cx, |picker, cx| {
picker
.delegate
.matches_updated(query, commands, matches, cx)
})
.log_err();
})
}
fn finalize_update_matches(
&mut self,
query: String,
duration: Duration,
cx: &mut ViewContext<Picker<Self>>,
) -> bool {
let Some((task, rx)) = self.updating_matches.take() else {
return true;
};
match cx
.background_executor()
.block_with_timeout(duration, rx.clone().recv())
{
Ok(Some((commands, matches))) => {
self.matches_updated(query, commands, matches, cx);
true
}
_ => {
self.updating_matches = Some((task, rx));
false
}
}
}
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
self.command_palette
.update(cx, |_, cx| cx.emit(DismissEvent))
@@ -317,8 +369,8 @@ impl PickerDelegate for CommandPaletteDelegate {
});
let action = command.action;
cx.focus(&self.previous_focus_handle);
cx.dispatch_action(action);
self.dismissed(cx);
cx.dispatch_action(action);
}
fn render_match(

View File

@@ -38,6 +38,9 @@ smol.workspace = true
theme.workspace = true
util.workspace = true
[target.'cfg(windows)'.dependencies]
async-std = { version = "1.12.0", features = ["unstable"] }
[dev-dependencies]
clock.workspace = true
collections = { workspace = true, features = ["test-support"] }

View File

@@ -428,6 +428,8 @@ impl Copilot {
let binary = LanguageServerBinary {
path: node_path,
arguments,
// TODO: We could set HTTP_PROXY etc here and fix the copilot issue.
env: None,
};
let server = LanguageServer::new(
@@ -512,7 +514,7 @@ impl Copilot {
.await?;
match sign_in {
request::SignInInitiateResult::AlreadySignedIn { user } => {
Ok(request::SignInStatus::Ok { user })
Ok(request::SignInStatus::Ok { user: Some(user) })
}
request::SignInInitiateResult::PromptUserDeviceFlow(flow) => {
this.update(&mut cx, |this, cx| {
@@ -920,7 +922,7 @@ impl Copilot {
if let Ok(server) = self.server.as_running() {
match lsp_status {
request::SignInStatus::Ok { .. }
request::SignInStatus::Ok { user: Some(_) }
| request::SignInStatus::MaybeOk { .. }
| request::SignInStatus::AlreadySignedIn { .. } => {
server.sign_in_status = SignInStatus::Authorized;
@@ -936,7 +938,7 @@ impl Copilot {
self.unregister_buffer(&buffer);
}
}
request::SignInStatus::NotSignedIn => {
request::SignInStatus::Ok { user: None } | request::SignInStatus::NotSignedIn => {
server.sign_in_status = SignInStatus::SignedOut;
for buffer in self.buffers.iter().cloned().collect::<Vec<_>>() {
self.unregister_buffer(&buffer);

View File

@@ -52,7 +52,7 @@ pub struct SignInConfirmParams {
pub enum SignInStatus {
#[serde(rename = "OK")]
Ok {
user: String,
user: Option<String>,
},
MaybeOk {
user: String,

View File

@@ -116,6 +116,11 @@ impl CopilotCodeVerification {
.full_width()
.style(ButtonStyle::Filled),
)
.child(
Button::new("copilot-enable-cancel-button", "Cancel")
.full_width()
.on_click(cx.listener(|_, _, cx| cx.emit(DismissEvent))),
)
}
fn render_enabled_modal(cx: &mut ViewContext<Self>) -> impl Element {
v_flex()
@@ -131,7 +136,7 @@ impl CopilotCodeVerification {
)
}
fn render_unauthorized_modal() -> impl Element {
fn render_unauthorized_modal(cx: &mut ViewContext<Self>) -> impl Element {
v_flex()
.child(Headline::new("You must have an active GitHub Copilot subscription.").size(HeadlineSize::Large))
@@ -143,6 +148,11 @@ impl CopilotCodeVerification {
.full_width()
.on_click(|_, cx| cx.open_url(COPILOT_SIGN_UP_URL)),
)
.child(
Button::new("copilot-subscribe-cancel-button", "Cancel")
.full_width()
.on_click(cx.listener(|_, _, cx| cx.emit(DismissEvent))),
)
}
fn render_disabled_modal() -> impl Element {
@@ -160,7 +170,7 @@ impl Render for CopilotCodeVerification {
} => Self::render_prompting_modal(self.connect_clicked, &prompt, cx).into_any_element(),
Status::Unauthorized => {
self.connect_clicked = false;
Self::render_unauthorized_modal().into_any_element()
Self::render_unauthorized_modal(cx).into_any_element()
}
Status::Authorized => {
self.connect_clicked = false;

View File

@@ -40,7 +40,7 @@ indoc = "1.0.4"
itertools = "0.10"
language.workspace = true
lazy_static.workspace = true
linkify = "0.10.0"
linkify.workspace = true
log.workspace = true
lsp.workspace = true
multi_buffer.workspace = true

View File

@@ -165,6 +165,7 @@ gpui::actions!(
GoToPrevHunk,
GoToTypeDefinition,
GoToTypeDefinitionSplit,
OpenUrl,
HalfPageDown,
HalfPageUp,
Hover,

View File

@@ -67,15 +67,27 @@ pub trait ToDisplayPoint {
type TextHighlights = TreeMap<Option<TypeId>, Arc<(HighlightStyle, Vec<Range<Anchor>>)>>;
type InlayHighlights = BTreeMap<TypeId, HashMap<InlayId, (HighlightStyle, InlayHighlight)>>;
/// Decides how text in a [`MultiBuffer`] should be displayed in a buffer, handling inlay hints,
/// folding, hard tabs, soft wrapping, custom blocks (like diagnostics), and highlighting.
///
/// See the [module level documentation](self) for more information.
pub struct DisplayMap {
/// The buffer that we are displaying.
buffer: Model<MultiBuffer>,
buffer_subscription: BufferSubscription,
fold_map: FoldMap,
/// Decides where the [`Inlay`]s should be displayed.
inlay_map: InlayMap,
/// Decides where the fold indicators should be and tracks parts of a source file that are currently folded.
fold_map: FoldMap,
/// Keeps track of hard tabs in a buffer.
tab_map: TabMap,
/// Handles soft wrapping.
wrap_map: Model<WrapMap>,
/// Tracks custom blocks such as diagnostics that should be displayed within buffer.
block_map: BlockMap,
/// Regions of text that should be highlighted.
text_highlights: TextHighlights,
/// Regions of inlays that should be highlighted.
inlay_highlights: InlayHighlights,
pub clip_at_line_ends: bool,
}

View File

@@ -22,6 +22,9 @@ use text::Edit;
const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize];
/// Tracks custom blocks such as diagnostics that should be displayed within buffer.
///
/// See the [`display_map` module documentation](crate::display_map) for more information.
pub struct BlockMap {
next_block_id: AtomicUsize,
wrap_snapshot: RefCell<WrapSnapshot>,

View File

@@ -178,6 +178,9 @@ impl<'a> FoldMapWriter<'a> {
}
}
/// Decides where the fold indicators should be; also tracks parts of a source file that are currently folded.
///
/// See the [`display_map` module documentation](crate::display_map) for more information.
pub(crate) struct FoldMap {
snapshot: FoldSnapshot,
ellipses_color: Option<Hsla>,

View File

@@ -16,6 +16,9 @@ use text::{Patch, Rope};
use super::Highlights;
/// Decides where the [`Inlay`]s should be displayed.
///
/// See the [`display_map` module documentation](crate::display_map) for more information.
pub struct InlayMap {
snapshot: InlaySnapshot,
inlays: Vec<Inlay>,

View File

@@ -9,6 +9,9 @@ use sum_tree::Bias;
const MAX_EXPANSION_COLUMN: u32 = 256;
/// Keeps track of hard tabs in a text buffer.
///
/// See the [`display_map` module documentation](crate::display_map) for more information.
pub struct TabMap(TabSnapshot);
impl TabMap {

View File

@@ -15,6 +15,9 @@ use text::Patch;
pub use super::tab_map::TextSummary;
pub type WrapEdit = text::Edit<u32>;
/// Handles soft wrapping of text.
///
/// See the [`display_map` module documentation](crate::display_map) for more information.
pub struct WrapMap {
snapshot: WrapSnapshot,
pending_edits: VecDeque<(TabSnapshot, Vec<TabEdit>)>,

View File

@@ -123,6 +123,8 @@ use util::{maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
use workspace::Toast;
use workspace::{searchable::SearchEvent, ItemNavHistory, Pane, SplitDirection, ViewId, Workspace};
use crate::hover_links::find_url;
const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
const MAX_LINE_LEN: usize = 1024;
const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
@@ -290,7 +292,7 @@ pub enum SelectPhase {
}
#[derive(Clone, Debug)]
pub(crate) enum SelectMode {
pub enum SelectMode {
Character,
Word(Range<Anchor>),
Line(Range<Anchor>),
@@ -349,10 +351,16 @@ type CompletionId = usize;
type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Vec<Range<Anchor>>);
type InlayBackgroundHighlight = (fn(&ThemeColors) -> Hsla, Vec<InlayHighlight>);
/// Zed's primary text input `View`, allowing users to edit a [`MultiBuffer`]
///
/// See the [module level documentation](self) for more information.
pub struct Editor {
handle: WeakView<Self>,
focus_handle: FocusHandle,
/// The text buffer being edited
buffer: Model<MultiBuffer>,
/// Map of how text in the buffer should be displayed.
/// Handles soft wraps, folds, fake inlay text insertions, etc.
display_map: Model<DisplayMap>,
pub selections: SelectionsCollection,
pub scroll_manager: ScrollManager,
@@ -7362,6 +7370,28 @@ impl Editor {
.detach_and_log_err(cx);
}
pub fn open_url(&mut self, _: &OpenUrl, cx: &mut ViewContext<Self>) {
let position = self.selections.newest_anchor().head();
let Some((buffer, buffer_position)) = self
.buffer
.read(cx)
.text_anchor_for_position(position.clone(), cx)
else {
return;
};
cx.spawn(|editor, mut cx| async move {
if let Some((_, url)) = find_url(&buffer, buffer_position, cx.clone()) {
editor.update(&mut cx, |_, cx| {
cx.open_url(&url);
})
} else {
Ok(())
}
})
.detach();
}
pub fn navigate_to_hover_links(
&mut self,
mut definitions: Vec<HoverLink>,
@@ -7660,8 +7690,9 @@ impl Editor {
let snapshot = cursor_buffer.read(cx).snapshot();
let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
let prepare_rename = project.update(cx, |project, cx| {
project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
project.prepare_rename(cursor_buffer.clone(), cursor_buffer_offset, cx)
});
drop(snapshot);
Some(cx.spawn(|this, mut cx| async move {
let rename_range = if let Some(range) = prepare_rename.await? {
@@ -7681,11 +7712,12 @@ impl Editor {
})?
};
if let Some(rename_range) = rename_range {
let rename_buffer_range = rename_range.to_offset(&snapshot);
let cursor_offset_in_rename_range =
cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
this.update(&mut cx, |this, cx| {
let snapshot = cursor_buffer.read(cx).snapshot();
let rename_buffer_range = rename_range.to_offset(&snapshot);
let cursor_offset_in_rename_range =
cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
this.take_rename(false, cx);
let buffer = this.buffer.read(cx).read(cx);
let cursor_offset = selection.head().to_offset(&buffer);

View File

@@ -262,6 +262,7 @@ impl EditorElement {
register_action(view, cx, Editor::go_to_definition_split);
register_action(view, cx, Editor::go_to_type_definition);
register_action(view, cx, Editor::go_to_type_definition_split);
register_action(view, cx, Editor::open_url);
register_action(view, cx, Editor::fold);
register_action(view, cx, Editor::fold_at);
register_action(view, cx, Editor::unfold_lines);

View File

@@ -569,7 +569,7 @@ pub fn show_link_definition(
editor.hovered_link_state = Some(hovered_link_state);
}
fn find_url(
pub(crate) fn find_url(
buffer: &Model<language::Buffer>,
position: text::Anchor,
mut cx: AsyncWindowContext,

View File

@@ -289,6 +289,7 @@ fn show_hover(
})?;
let hover_result = hover_request.await.ok().flatten();
let snapshot = this.update(&mut cx, |this, cx| this.snapshot(cx))?;
let hover_popover = match hover_result {
Some(hover_result) if !hover_result.is_empty() => {
// Create symbol range of anchors for highlighting and filtering of future requests.

View File

@@ -704,10 +704,12 @@ impl Item for Editor {
fn save(&mut self, project: Model<Project>, cx: &mut ViewContext<Self>) -> Task<Result<()>> {
self.report_editor_event("save", None, cx);
let format = self.perform_format(project.clone(), FormatTrigger::Save, cx);
let buffers = self.buffer().clone().read(cx).all_buffers();
cx.spawn(|_, mut cx| async move {
format.await?;
cx.spawn(|this, mut cx| async move {
this.update(&mut cx, |this, cx| {
this.perform_format(project.clone(), FormatTrigger::Save, cx)
})?
.await?;
if buffers.len() == 1 {
project

View File

@@ -5,6 +5,7 @@ use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint};
use crate::{char_kind, scroll::ScrollAnchor, CharKind, EditorStyle, ToOffset, ToPoint};
use gpui::{px, Pixels, WindowTextSystem};
use language::Point;
use multi_buffer::MultiBufferSnapshot;
use std::{ops::Range, sync::Arc};
@@ -254,7 +255,7 @@ pub fn previous_word_start(map: &DisplaySnapshot, point: DisplayPoint) -> Displa
let raw_point = point.to_point(map);
let scope = map.buffer_snapshot.language_scope_at(raw_point);
find_preceding_boundary(map, point, FindRange::MultiLine, |left, right| {
find_preceding_boundary_display_point(map, point, FindRange::MultiLine, |left, right| {
(char_kind(&scope, left) != char_kind(&scope, right) && !right.is_whitespace())
|| left == '\n'
})
@@ -267,7 +268,7 @@ pub fn previous_subword_start(map: &DisplaySnapshot, point: DisplayPoint) -> Dis
let raw_point = point.to_point(map);
let scope = map.buffer_snapshot.language_scope_at(raw_point);
find_preceding_boundary(map, point, FindRange::MultiLine, |left, right| {
find_preceding_boundary_display_point(map, point, FindRange::MultiLine, |left, right| {
let is_word_start =
char_kind(&scope, left) != char_kind(&scope, right) && !right.is_whitespace();
let is_subword_start =
@@ -366,16 +367,16 @@ pub fn end_of_paragraph(
/// indicated by the given predicate returning true.
/// The predicate is called with the character to the left and right of the candidate boundary location.
/// If FindRange::SingleLine is specified and no boundary is found before the start of the current line, the start of the current line will be returned.
pub fn find_preceding_boundary(
map: &DisplaySnapshot,
from: DisplayPoint,
pub fn find_preceding_boundary_point(
buffer_snapshot: &MultiBufferSnapshot,
from: Point,
find_range: FindRange,
mut is_boundary: impl FnMut(char, char) -> bool,
) -> DisplayPoint {
) -> Point {
let mut prev_ch = None;
let mut offset = from.to_point(map).to_offset(&map.buffer_snapshot);
let mut offset = from.to_offset(&buffer_snapshot);
for ch in map.buffer_snapshot.reversed_chars_at(offset) {
for ch in buffer_snapshot.reversed_chars_at(offset) {
if find_range == FindRange::SingleLine && ch == '\n' {
break;
}
@@ -389,7 +390,26 @@ pub fn find_preceding_boundary(
prev_ch = Some(ch);
}
map.clip_point(offset.to_display_point(map), Bias::Left)
offset.to_point(&buffer_snapshot)
}
/// Scans for a boundary preceding the given start point `from` until a boundary is found,
/// indicated by the given predicate returning true.
/// The predicate is called with the character to the left and right of the candidate boundary location.
/// If FindRange::SingleLine is specified and no boundary is found before the start of the current line, the start of the current line will be returned.
pub fn find_preceding_boundary_display_point(
map: &DisplaySnapshot,
from: DisplayPoint,
find_range: FindRange,
is_boundary: impl FnMut(char, char) -> bool,
) -> DisplayPoint {
let result = find_preceding_boundary_point(
&map.buffer_snapshot,
from.to_point(map),
find_range,
is_boundary,
);
map.clip_point(result.to_display_point(map), Bias::Left)
}
/// Scans for a boundary following the given start point until a boundary is found, indicated by the
@@ -626,7 +646,7 @@ mod tests {
) {
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
assert_eq!(
find_preceding_boundary(
find_preceding_boundary_display_point(
&snapshot,
display_points[1],
FindRange::MultiLine,
@@ -700,7 +720,7 @@ mod tests {
});
assert_eq!(
find_preceding_boundary(
find_preceding_boundary_display_point(
&snapshot,
buffer_snapshot.len().to_display_point(&snapshot),
FindRange::MultiLine,

View File

@@ -29,8 +29,11 @@ pub struct SelectionsCollection {
buffer: Model<MultiBuffer>,
pub next_selection_id: usize,
pub line_mode: bool,
disjoint: Arc<[Selection<Anchor>]>,
pending: Option<PendingSelection>,
/// The non-pending, non-overlapping selections.
/// The [SelectionsCollection::pending] selection could possibly overlap these
pub disjoint: Arc<[Selection<Anchor>]>,
/// A pending selection, such as when the mouse is being dragged
pub pending: Option<PendingSelection>,
}
impl SelectionsCollection {

View File

@@ -147,7 +147,7 @@ impl EditorTestContext {
self.add_assertion_context(format!("Simulated Keystroke: {:?}", keystroke_text));
let keystroke = Keystroke::parse(keystroke_text).unwrap();
self.cx.dispatch_keystroke(self.window, keystroke, false);
self.cx.dispatch_keystroke(self.window, keystroke);
keystroke_under_test_handle
}

View File

@@ -42,6 +42,7 @@ pub struct Extension {
pub description: Option<String>,
pub authors: Vec<String>,
pub repository: String,
pub download_count: usize,
}
#[derive(Clone)]
@@ -414,8 +415,8 @@ impl ExtensionStore {
language.matcher.clone(),
vec![],
move || {
let config = std::fs::read(language_path.join("config.toml"))?;
let config: LanguageConfig = ::toml::from_slice(&config)?;
let config = std::fs::read_to_string(language_path.join("config.toml"))?;
let config: LanguageConfig = ::toml::from_str(&config)?;
let queries = load_plugin_queries(&language_path);
Ok((config, queries))
},
@@ -650,6 +651,12 @@ impl ExtensionStore {
let Ok(relative_path) = language_path.strip_prefix(&extension_dir) else {
continue;
};
let Ok(Some(fs_metadata)) = fs.metadata(&language_path).await else {
continue;
};
if !fs_metadata.is_dir {
continue;
}
let config = fs.load(&language_path.join("config.toml")).await?;
let config = ::toml::from_str::<LanguageConfig>(&config)?;

View File

@@ -1,12 +1,11 @@
use client::telemetry::Telemetry;
use editor::{Editor, EditorElement, EditorStyle};
use extension::{Extension, ExtensionStatus, ExtensionStore};
use fs::Fs;
use gpui::{
actions, canvas, uniform_list, AnyElement, AppContext, AvailableSpace, EventEmitter,
FocusableView, FontStyle, FontWeight, InteractiveElement, KeyContext, ParentElement, Render,
Styled, Task, TextStyle, UniformListScrollHandle, View, ViewContext, VisualContext, WeakView,
WhiteSpace, WindowContext,
Styled, Task, TextStyle, UniformListScrollHandle, View, ViewContext, VisualContext, WhiteSpace,
WindowContext,
};
use settings::Settings;
use std::time::Duration;
@@ -32,8 +31,6 @@ pub fn init(cx: &mut AppContext) {
}
pub struct ExtensionsPage {
workspace: WeakView<Workspace>,
fs: Arc<dyn Fs>,
list: UniformListScrollHandle,
telemetry: Arc<Telemetry>,
is_fetching_extensions: bool,
@@ -46,7 +43,7 @@ pub struct ExtensionsPage {
impl ExtensionsPage {
pub fn new(workspace: &Workspace, cx: &mut ViewContext<Workspace>) -> View<Self> {
let extensions_panel = cx.new_view(|cx: &mut ViewContext<Self>| {
cx.new_view(|cx: &mut ViewContext<Self>| {
let store = ExtensionStore::global(cx);
let subscription = cx.observe(&store, |_, _, cx| cx.notify());
@@ -54,8 +51,6 @@ impl ExtensionsPage {
cx.subscribe(&query_editor, Self::on_query_change).detach();
let mut this = Self {
fs: workspace.project().read(cx).fs().clone(),
workspace: workspace.weak_handle(),
list: UniformListScrollHandle::new(),
telemetry: workspace.client().telemetry().clone(),
is_fetching_extensions: false,
@@ -67,8 +62,7 @@ impl ExtensionsPage {
};
this.fetch_extensions(None, cx);
this
});
extensions_panel
})
}
fn install_extension(
@@ -200,6 +194,8 @@ impl ExtensionsPage {
}
.color(Color::Accent);
let repository_url = extension.repository.clone();
div().w_full().child(
v_flex()
.w_full()
@@ -236,18 +232,24 @@ impl ExtensionsPage {
),
)
.child(
h_flex().justify_between().child(
Label::new(format!(
"{}: {}",
if extension.authors.len() > 1 {
"Authors"
} else {
"Author"
},
extension.authors.join(", ")
))
.size(LabelSize::Small),
),
h_flex()
.justify_between()
.child(
Label::new(format!(
"{}: {}",
if extension.authors.len() > 1 {
"Authors"
} else {
"Author"
},
extension.authors.join(", ")
))
.size(LabelSize::Small),
)
.child(
Label::new(format!("Downloads: {}", extension.download_count))
.size(LabelSize::Small),
),
)
.child(
h_flex()
@@ -256,7 +258,19 @@ impl ExtensionsPage {
Label::new(description.clone())
.size(LabelSize::Small)
.color(Color::Default)
})),
}))
.child(
IconButton::new(
SharedString::from(format!("repository-{}", extension.id)),
IconName::Github,
)
.icon_color(Color::Accent)
.icon_size(IconSize::Small)
.style(ButtonStyle::Filled)
.on_click(cx.listener(move |_, _, cx| {
cx.open_url(&repository_url);
})),
),
),
)
}
@@ -453,25 +467,9 @@ impl Item for ExtensionsPage {
fn clone_on_split(
&self,
_workspace_id: WorkspaceId,
cx: &mut ViewContext<Self>,
_: &mut ViewContext<Self>,
) -> Option<View<Self>> {
Some(cx.new_view(|cx| {
let store = ExtensionStore::global(cx);
let subscription = cx.observe(&store, |_, _, cx| cx.notify());
ExtensionsPage {
fs: self.fs.clone(),
workspace: self.workspace.clone(),
list: UniformListScrollHandle::new(),
telemetry: self.telemetry.clone(),
is_fetching_extensions: false,
extensions_entries: Default::default(),
query_editor: self.query_editor.clone(),
_subscription: subscription,
query_contains_error: false,
extension_fetch_task: None,
}
}))
None
}
fn to_item_events(event: &Self::Event, mut f: impl FnMut(workspace::item::ItemEvent)) {

View File

@@ -93,7 +93,7 @@ impl FileFinder {
fn new(delegate: FileFinderDelegate, cx: &mut ViewContext<Self>) -> Self {
Self {
picker: cx.new_view(|cx| Picker::new(delegate, cx)),
picker: cx.new_view(|cx| Picker::uniform_list(delegate, cx)),
}
}
}

View File

@@ -11,6 +11,9 @@ use fsevent::StreamFlags;
#[cfg(not(target_os = "macos"))]
use notify::{Config, EventKind, Watcher};
#[cfg(unix)]
use std::os::unix::fs::MetadataExt;
use futures::{future::BoxFuture, Stream, StreamExt};
use git2::Repository as LibGitRepository;
use parking_lot::Mutex;
@@ -21,7 +24,6 @@ use std::io::Write;
use std::sync::Arc;
use std::{
io,
os::unix::fs::MetadataExt,
path::{Component, Path, PathBuf},
pin::Pin,
time::{Duration, SystemTime},
@@ -239,8 +241,16 @@ impl Fs for RealFs {
} else {
symlink_metadata
};
#[cfg(unix)]
let inode = metadata.ino();
// todo!("windows")
#[cfg(windows)]
let inode = 0;
Ok(Some(Metadata {
inode: metadata.ino(),
inode,
mtime: metadata.modified().unwrap(),
is_symlink,
is_dir: metadata.file_type().is_dir(),

View File

@@ -16,6 +16,7 @@ test-support = [
"util/test-support",
]
runtime_shaders = []
macos-blade = ["blade-graphics", "blade-macros", "blade-rwh", "bytemuck"]
[lib]
path = "src/gpui.rs"
@@ -26,6 +27,10 @@ anyhow.workspace = true
async-task = "4.7"
backtrace = { version = "0.3", optional = true }
bitflags = "2.4.0"
blade-graphics = { workspace = true, optional = true }
blade-macros = { workspace = true, optional = true }
blade-rwh = { workspace = true, optional = true }
bytemuck = { version = "1", optional = true }
collections.workspace = true
ctor.workspace = true
derive_more.workspace = true
@@ -33,7 +38,7 @@ dhat = { version = "0.3", optional = true }
env_logger = { version = "0.9", optional = true }
etagere = "0.2"
futures.workspace = true
font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "d97147f" }
font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "5a5c4d4" }
gpui_macros.workspace = true
image = "0.23"
itertools = "0.10"
@@ -48,7 +53,6 @@ pathfinder_geometry = "0.5"
postage.workspace = true
rand.workspace = true
raw-window-handle = "0.6"
blade-rwh = { package = "raw-window-handle", version = "0.5" }
refineable.workspace = true
resvg = "0.14"
schemars.workspace = true
@@ -86,20 +90,28 @@ cbindgen = "0.26.0"
block = "0.1"
cocoa = "0.25"
core-foundation = { version = "0.9.3", features = ["with-uuid"] }
core-graphics = "0.22.3"
core-text = "19.2"
foreign-types = "0.3"
core-graphics = "0.23"
core-text = "20.1"
foreign-types = "0.5"
log.workspace = true
media.workspace = true
metal = "0.21.0"
metal = "0.25"
objc = "0.2"
[target.'cfg(target_os = "linux")'.dependencies]
flume = "0.11"
xcb = { version = "1.3", features = ["as-raw-xcb-connection"] }
open = "5.0.1"
ashpd = "0.7.0"
# todo!(linux) - Technically do not use `randr`, but it doesn't compile otherwise
xcb = { version = "1.3", features = ["as-raw-xcb-connection", "present", "randr", "xkb"] }
wayland-client= { version = "0.31.2" }
wayland-protocols = { version = "0.31.2", features = ["client", "staging", "unstable"] }
wayland-backend = { version = "0.3.3", features = ["client_system"] }
xkbcommon = { version = "0.7", features = ["wayland", "x11"] }
as-raw-xcb-connection = "1"
#TODO: use these on all platforms
blade-graphics = { git = "https://github.com/kvark/blade", rev = "c4f951a88b345724cb952e920ad30e39851f7760" }
blade-macros = { git = "https://github.com/kvark/blade", rev = "c4f951a88b345724cb952e920ad30e39851f7760" }
blade-graphics.workspace = true
blade-macros.workspace = true
blade-rwh.workspace = true
bytemuck = "1"
cosmic-text = "0.10.0"

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