Compare commits

..

610 Commits

Author SHA1 Message Date
Antonio Scandurra
707bd7c156 zed 0.91.3 2023-06-16 17:27:42 +02:00
Antonio Scandurra
656f68dc69 Activate screen-sharing when leader activates a panel (#2614)
Fixes
https://linear.app/zed-industries/issue/Z-1875/screen-sharing-tab-is-not-activated-when-leader-is-on-a-panel

Release Notes:

- Fixed a bug that caused followers to not see the leader's screen when
they activated a panel.
2023-06-16 17:26:33 +02:00
Max Brunsfeld
186334bb12 zed 0.91.2 2023-06-15 15:34:43 -07:00
Max Brunsfeld
b1324ebe1f Fix failure to upload panics when multiple panics happen at the same time (#2616)
When multiple panics occur at the same time (usually because one thread
panics, and another thread joins it), multiple panic JSON objects can
get written to the same panic file. The resulting file won't be valid
JSON.

This PR addresses that problem via two changes:
* Format panic files as single-line JSON objects
* When a panic file  isn't valid JSON, try taking the first line

In the future, we could try combining all of the backtraces, but for
now, I just want to avoid a problem of not reporting a panic at all.

Release Notes:

- Fixed a problem with Zed's internal crash reporting.
2023-06-15 15:34:09 -07:00
Max Brunsfeld
e9aec1d67a Don't rely on debug symbols for panic reporting (#2615)
This fixes a regression introduced in
https://github.com/zed-industries/zed/pull/2560, where panic reports did
not include backtraces. The problem was that in that PR, I assumed we
could retrieve file paths for symbols in our backtraces. But actually,
that functionality only works when the app is built locally, and a
`.dSYM` file can be magically found by the OS. We don't ship those dSYM
files with Zed, so panic symbols do not have file paths available.

Panic backtraces will still be more useful and less noisy than before
though: we will strip out frames for which we don't have symbol names,
and remove leading panic-handling stack frames from the backtraces.

Release Notes:

- N/A
2023-06-15 15:34:01 -07:00
Mikayla Maki
32ed547c2c zed 0.91.1 2023-06-14 18:10:16 -07:00
Mikayla Maki
39915f7c96 Add entitlements file to bundle step (#2611)
This completes the bundle changes that will be needed to access voice,
as well as adds permissions for accessing other MacOS services, the
camera, and the necessary permissions for plugins. This was developed by
combining the entitlements of iTerm and VSCode, cross-referenced with
the entitlements of Firefox. 

Release Notes:

- Fixed a bug in enabling authorization for macOS services (preview
only)
2023-06-14 18:09:15 -07:00
Joseph Lyons
2c2cea1c92 v0.91.x preview 2023-06-14 13:27:18 -04:00
Mikayla Maki
75b1f60126 Add UsageDescriptions to Info.plist (#2609)
This updates our dependency on cargo-bundle to point at a fork I've
created that adjusts the latest version's behavior to work for our use
case. This PR also updates the Zed Info.plist to include the following
keys:

- NSSystemAdministrationUsageDescription
- NSAppleEventsUsageDescription
- NSBluetoothAlwaysUsageDescription
- NSCalendarsUsageDescription
- NSCameraUsageDescription
- NSContactsUsageDescription
- NSLocationAlwaysUsageDescription
- NSLocationUsageDescription
- NSLocationWhenInUseUsageDescription
- NSMicrophoneUsageDescription
- NSSpeechRecognitionUsageDescription
- NSRemindersUsageDescription

Release Notes:

- Bug fix: Allow applications run via the Zed terminal to request
authorization for macOS services (e.g. microphone and camera)
2023-06-14 10:04:54 -07:00
Antonio Scandurra
2b8b954c3e Overhaul assistant panel (#2610)
Closes
https://linear.app/zed-industries/issue/Z-2368/use-a-different-icon-for-the-assistant-panel
Closes
https://linear.app/zed-industries/issue/Z-2363/ship-the-assistant-only-on-preview
Closes
https://linear.app/zed-industries/issue/Z-2331/scrolling-makes-it-hard-to-read
Closes
https://linear.app/zed-industries/issue/Z-2306/allow-undo-and-collaboration-in-assistant

This pull request is a significant overhaul of the assistant panel,
which now uses a simple `Buffer` as opposed to a `MultiBuffer` to show
messages. Specifically, we track the start of each message with an
anchor located right after the newline (or `Anchor::MIN` for the first
message). When the anchor becomes invalid (that is, the newline is
deleted), we merge the message with the preceding ones. Crucially,
messages don't actually get deleted so that, if the newline anchor
becomes valid again (such as when undoing/redoing), we can restore the
messages as well.

As part of this overhaul, we are also improving the scrolling behavior
to maintain the viewport stable only when editing or moving the cursor,
but otherwise leave the scroll position unchanged when manually
scrolling up or down.

Note that with these changes, we are limiting access to the assistant to
users on preview (and dev), as we want to polish the behavior a little
more before shipping to the general public. Users on stable will still
be able to see the default settings/keybindings of the assistant, but I
think that's okay, as they won't be able to do anything with them.

Release Notes:

- Added support for undo/redo in the assistant (preview-only)
- Improved the scrolling behavior of the assistant when it was
generating responses. Now Zed will keep the viewport stable only when
editing or moving the cursor, but otherwise leave the scroll position
unchanged when manually scrolling up or down (preview-only)
- Changed the icon of the assistant panel (preview-only)

**Note for @JosephTLyons: given that we're feature flagging this, let's
make sure things on stable look reasonable and work correctly. Things to
look out for: ensure a stock installation works, changing the settings
on stable works, changing the keybinding on stable works.**
2023-06-14 14:09:09 +02:00
Antonio Scandurra
4efe62b3e5 Use robot icon for assistant to prevent confusion with conversations 2023-06-14 12:14:43 +02:00
Antonio Scandurra
049c987310 Avoid loading the assistant panel on stable 2023-06-14 12:08:49 +02:00
Antonio Scandurra
56b0bf8601 Save cursor scroll position when the editor is auto-scrolled 2023-06-14 12:00:27 +02:00
Antonio Scandurra
1aa1774688 Avoid auto-scrolling the editor when inserting/removing headers 2023-06-14 10:49:23 +02:00
Antonio Scandurra
f8b9417406 Keep cursor stable as autocompletions are being streamed 2023-06-14 10:41:18 +02:00
Antonio Scandurra
75ad76bfb2 💄 2023-06-14 09:47:30 +02:00
Antonio Scandurra
7dab17e233 Re-enable copy support in the assistant 2023-06-14 09:40:43 +02:00
Antonio Scandurra
27c83ca3f7 Remove unnecessary set_render_excerpt_header method 2023-06-14 09:38:49 +02:00
Antonio Scandurra
9b7617403d Parse buffer as Markdown 2023-06-14 09:31:41 +02:00
Antonio Scandurra
ea5d677ef8 Merge remote-tracking branch 'origin/main' into assistant-buffer 2023-06-14 09:16:45 +02:00
Mikayla Maki
adc8337ad4 Restore misconfigured Cargo.toml 2023-06-13 18:08:33 -07:00
Mikayla Maki
8ad7eb7598 Switch to using new version of cargo bundle with more capabilities 2023-06-13 18:01:51 -07:00
Kevin Hovsäter
b365e48ff0 Toggle comments for empty single line selections (#2594) 2023-06-13 20:15:11 +02:00
Max Brunsfeld
77367bf2e4 Move worktree tests into their own file (#2608)
The worktree has a lot of tests. Let's reduce their indentation, and
make it easier to navigate to them. We deserve it.

Just doing this in its own PR, to minimize conflicts.

Release Notes:

- N/A
2023-06-13 10:50:49 -07:00
Max Brunsfeld
5b6d1a27ff Flatten worktree tests module structure 2023-06-13 10:31:29 -07:00
Max Brunsfeld
c17dbab6f1 Move worktree tests to their own file 2023-06-13 10:22:14 -07:00
Piotr Osiewicz
b272db9e21 editor: Add language setting for comment continuations (#2605)
Per @JosephTLyons request I've added a language setting for comment
continuations.

Release Notes:

- Added a language setting for comment continuations.
2023-06-13 18:59:46 +02:00
Max Brunsfeld
aedef7bc58 Remove panic when programatically updating an invalid setting (#2607)
Fixes
https://linear.app/zed-industries/issue/Z-2170/thread-main-panicked-at-could-not-deserialize-setting-type-welcomebase
Fixes
https://linear.app/zed-industries/issue/Z-2228/thread-main-panicked-at-could-not-deserialize-setting-type
2023-06-13 09:50:54 -07:00
Max Brunsfeld
1cd11bfe66 Remove panic when programatically updating an invalid setting 2023-06-13 09:43:57 -07:00
Antonio Scandurra
0db0a1ccef Skip merged messages when inserting new ones
Co-Authored-By: Julia Risley <julia@zed.dev>
2023-06-13 16:06:50 +02:00
Antonio Scandurra
6e5de2fbbb Update blocks when cycling the message role
Co-Authored-By: Julia Risley <julia@zed.dev>
2023-06-13 16:00:13 +02:00
Antonio Scandurra
00cede63a8 Show message headers again
Co-Authored-By: Julia Risley <julia@zed.dev>
2023-06-13 15:55:52 +02:00
Antonio Scandurra
2842fc2b1d Merge messages whose header has been invalidated 2023-06-13 15:16:04 +02:00
Antonio Scandurra
2ae8b558b9 Get back to a compiling state with Buffer backing the assistant 2023-06-13 13:43:06 +02:00
Joseph T. Lyons
5e68dc5c92 Fix comments in settings files (#2604)
I accidentally added the information about folder-specific settings to
the user settings default file - just fixing that.

Release Notes:

- N/A
2023-06-13 04:06:53 -04:00
Joseph Lyons
5821bc4161 Fix comments in settings files 2023-06-13 04:03:51 -04:00
Max Brunsfeld
d8a2e176e6 Add support for HEEx templates in Elixir, fix a bug in handling nested language injections (#2603)
Closes https://linear.app/zed-industries/issue/Z-2211/heex-support

Release Notes:

- Added support for Elixir HEEx templates.
- Fixed a bug that caused incorrect syntax highlighting in ERB templates
([#1619](https://github.com/zed-industries/community/issues/1619)).
2023-06-12 17:49:07 -07:00
Max Brunsfeld
df76ab98cf Add Heex language 2023-06-12 17:32:20 -07:00
Max Brunsfeld
01bd5c30fc Fix syntax map bug with combined injections inside of other injections 2023-06-12 17:32:20 -07:00
Max Brunsfeld
ac1882b99b Move SyntaxMap tests to their own file 2023-06-12 16:24:17 -07:00
Max Brunsfeld
04e43899c0 Change action namespace to 'debug' for LSP log and syntax tree view 2023-06-12 15:54:36 -07:00
Max Brunsfeld
8542911eec Add a syntax tree view, for developing and debugging language support (#2601)
This PR adds a syntax tree view, which lets you view the syntax tree of
any layer in the active editor's `SyntaxMap`.

This view uses some new APIs that I added to Tree-sitter, which allow us
to efficiently render the syntax tree using a `UniformList`. Tree-sitter
PR: https://github.com/tree-sitter/tree-sitter/pull/2316

![Screen Shot 2023-06-12 at 3 33 36
PM](https://github.com/zed-industries/zed/assets/326587/2a27ee7b-bf29-4b3b-bfa8-fb47f97a2785)

Release Notes:

- Added a *syntax tree view* that shows Zed's internal syntax tree(s)
for the active editor. You can open it running the `debug: open syntax
tree view` command from the command palette.
2023-06-12 15:50:39 -07:00
Max Brunsfeld
018466171b Include field names in the syntax tree 2023-06-12 15:32:16 -07:00
Max Brunsfeld
594b9def20 Upgrade Tree-sitter 2023-06-12 15:31:59 -07:00
Max Brunsfeld
b4f3a88b38 Close the menu when selecting a different layer in the syntax tree view 2023-06-12 15:14:57 -07:00
Max Brunsfeld
56b749788f Improve the look of the syntax tree view UI 2023-06-12 15:14:57 -07:00
Max Brunsfeld
e969e3b028 Sync selection between syntax tree view and editor 2023-06-12 15:14:57 -07:00
Max Brunsfeld
086cfe57c5 Start work on a syntax tree view 2023-06-12 15:14:56 -07:00
Piotr Osiewicz
3d02f7ce5f Move plugins to feature-gate (#2599)
This PR:
- Reduces our dependency count for a release build by 20%
- Reduces our release compile time by about 30s (out of ~5 minutes)
2023-06-12 21:04:09 +02:00
Antonio Scandurra
7db690b713 WIP 2023-06-12 17:50:13 +02:00
Joseph T. Lyons
db5bb4ec03 Do not rely on user's system time for telemetry events (#2597)
Some user's don't have their system clocks configured right and we are
seeing events 10 years into the future. I'm stripping out the code that
adds time via the client and am adding it in on zed.dev. We will lose a
little accuracy, as the time will be when the batch hits the server, but
I think its negligible (currently, batches send up every 30 seconds, at
the max) and worth it to protect our data from user's who wonkily dont
set care about their system time.

- https://github.com/zed-industries/zed.dev/pull/332

Release Notes:

- N/A
2023-06-09 18:08:11 -04:00
Joseph Lyons
0b3b732310 Do not rely on user's system time for telemetry events 2023-06-09 18:02:20 -04:00
Max Brunsfeld
56a4c2afae Bump tree-sitter-yaml to avoid submodule error during build 2023-06-09 14:58:31 -07:00
Joseph T. Lyons
d8c1ab9c68 Fix formatter comment in default settings (#2593)
Fix comment related to the `formatter` configuration option in Zed's
default settings.

Release Notes:

- N/A
2023-06-09 15:14:04 -04:00
Max Brunsfeld
c1f1ee6b05 collab 0.14.2 2023-06-09 11:50:35 -07:00
Max Brunsfeld
b2bdca4779 Bump protocol version 2023-06-09 11:50:15 -07:00
Max Brunsfeld
097632467d collab 0.14.1 2023-06-09 11:37:47 -07:00
Max Brunsfeld
87efd25d42 Avoid dropping the worktree_repository_statuses table for now 2023-06-09 11:37:39 -07:00
Max Brunsfeld
bb65d75798 Enable rust backtraces on kubernetes 2023-06-09 11:15:21 -07:00
Max Brunsfeld
9cbb63d374 collab 0.14.0 2023-06-09 11:12:45 -07:00
Piotr Osiewicz
5bef2f1778 Z-2303 editor: fix comment extension on the start of line (#2595)
Release Notes:

- Improved comment extension for cases where cursor was placed before
start of comment marker
2023-06-09 20:02:51 +02:00
Max Brunsfeld
fb83ab8e9f Add outline support for Ruby singleton methods (#2592)
This pull request add support for Ruby singleton methods in the document
outline.

**Before**

<img width="1490" alt="outline-before"
src="https://github.com/zed-industries/zed/assets/503025/e90afd4c-bcdd-477b-92cf-fe34cec54980">

**After**

<img width="1493" alt="outline-after"
src="https://github.com/zed-industries/zed/assets/503025/067cc80b-7718-4395-9079-4677a689b9be">

Release Notes:

- Added support for singleton methods in Ruby outline view. Fixed
[#1442](https://github.com/zed-industries/community/issues/1442).
2023-06-09 08:35:34 -07:00
Max Brunsfeld
738b06a778 Update JavaScript outline queries (#2596)
Add support for exported const declarations in the outline view. Did not
include more context (e.g., whether or not the it's exported or not)
because we're not doing that for TypeScript right now. We could do that
in a follow up pull request, addressing both JavaScript and TypeScript
at once.


**Before**

<img width="1186" alt="outline-before"
src="https://github.com/zed-industries/zed/assets/503025/e72b0ed4-52b0-4a7c-af70-b2f4758e3554">

**After**

<img width="1186" alt="outline-after"
src="https://github.com/zed-industries/zed/assets/503025/cd91bc7f-8ce3-485f-9b33-d487ad6c718d">


Release Notes:

- Added support for exported const declarations in the JavaScript
outline view. Fixed
[#1399](https://github.com/zed-industries/community/issues/1399).
2023-06-09 08:34:14 -07:00
Kevin Hovsäter
4213cc013c Update JavaScript outline queries
Add support for exported const declarations in the outline view.
2023-06-09 15:33:27 +02:00
Kevin Hovsäter
6ce3f3bf27 Refactor toggle comment tests 2023-06-09 13:22:04 +02:00
Kevin Hovsäter
a8d43c6d71 Toggle comments for empty single line selections 2023-06-09 10:10:49 +02:00
Kevin Hovsäter
7deddd1149 Fix formatter comment in default settings 2023-06-09 08:08:07 +02:00
Kevin Hovsäter
57ff173e29 Add outline support for Ruby singleton methods 2023-06-09 07:31:09 +02:00
Nate Butler
85b049f250 Build tokens for each theme (#2590)
This PR adds the ability to export tokens for each theme. 

You can export tokens by:

1. `cd styles`
2. `npm run build-tokens`
3. Tokens will be output in the target folder (`styles/target`)

The tokens match the ColorScheme object. In the future we may also
export tokens for our styleTrees.

Release Notes:

- N/A (No public facing changes)

---
TODO:

- [x] Generate Token Studio theme index file
- [x] ColorScheme
    - [x] name:
    - [x] isLight
    - [x] lowest
    - [x] middle
    - [x] highest
    - [x] popoverShadow
    - [x] modalShadow
    - [x] players
    - [x] syntax
2023-06-08 16:40:46 -04:00
Nate Butler
ddcbc774ab $tokens.json => $themes.json 2023-06-08 16:36:15 -04:00
Nate Butler
e4cbc29f98 Update buildTokens to export $metadata.json and $themes.json 2023-06-08 16:19:28 -04:00
Max Brunsfeld
6304897abc Make the LSP log view searchable (#2591)
Also, I noticed errors in the logs of the Elixir LSP that we were
sending `goToTypeDefinition` requests, which that server does not
support. We now respect that server capability.

Release Notes:

- N/A
2023-06-08 13:15:05 -07:00
Nate Butler
3719c206c9 Only export color tokens for syntax 2023-06-08 15:14:59 -04:00
Nate Butler
91e1bb8fd4 WIP syntax tokens 2023-06-08 15:12:11 -04:00
Max Brunsfeld
a7f06f962b Enable type definition capability in fake LSPs in tests 2023-06-08 12:09:37 -07:00
Max Brunsfeld
e0dd9e4185 Make the LSP log view searchable 2023-06-08 11:33:20 -07:00
Max Brunsfeld
11dbbcc9dd Respect LSP goToTypeDefinition server capability 2023-06-08 11:33:09 -07:00
Nate Butler
999b2365a8 Export additional tokens, standardize naming convention 2023-06-08 12:21:51 -04:00
Max Brunsfeld
e3f319467a Enhance LSP log viewer to show server logs in addition to RPC trace (#2586)
In debugging what's going on with the Elixir language server, there was
some interesting content in the server's logs (sent to the app via the
`window/logMessage` LSP endpoint). I decided to invest in making
language server issues easier to debug by exposing these `logMessage`
contents in the app.

Also, improve the UI of the view slightly:

* Select one of the servers by default (instead of "no server selected")
* Make it clearer that the menu is clickable
2023-06-08 09:03:52 -07:00
Max Brunsfeld
908de23b72 Tweak LSP log menu styling 2023-06-08 08:58:57 -07:00
Max Brunsfeld
afaff7f9a9 Merge branch 'main' into lsp-log-messages 2023-06-08 08:49:08 -07:00
Max Brunsfeld
817644eb20 Style new lsp log menu, add a test for it 2023-06-08 08:43:25 -07:00
Nate Butler
e2f46d5448 Build layer tokens for each theme 2023-06-08 11:15:59 -04:00
Nate Butler
16e3e04501 Update TSConfig (#2589)
Based on #2558. Also fixes errors resulting from the stricter options.

Release Notes:

- N/A (No public facing changes)
2023-06-08 01:31:21 -04:00
Nate Butler
1e43fec1c5 Update buildLicenses to only include the theme url if there is one 2023-06-08 01:23:19 -04:00
Nate Butler
e996a66596 Update TSCondif
Based on #2558. Also fixes errors resulting from the stricter options.
2023-06-08 01:15:57 -04:00
Nate Butler
a75e9faa83 Add foundation for exporting tokens from our color schemes (#2588)
We removed our Figma Tokens/Tokens Studio export a while back when we
moved to the theme to ColorSchemes. I'd like to get back to exporting
these so we can be working from up to date themes in Figma, especially
with the large amount of UI design work we'll be doing in the next few
weeks on channels.

This PR adds some basic plumbing to start working through the
theme/colorScheme and export the parts as tokens.

I also discovered that Tokens Studio now publishes their types, so we
can use them directly rather than writing our own:
https://github.com/tokens-studio/types

Pulled those in and started connecting them as well.

Running `npm run build-tokens` will export the tokens for each theme to
`styles/target/tokens`.

Currently only a few element's tokens are exported, will expand this
further as time permits.

Release Notes:

- N/A (No public facing changes)
2023-06-08 00:53:16 -04:00
Nate Butler
c8a9d73ea6 Add foundation for exporting tokens 2023-06-08 00:37:00 -04:00
Nate Butler
d4192fc3e9 Organize misc files into theme, themes and styleTrees (#2587)
We've accumulated a number of miscellaneous folders in the styles
folder. Simplifying these down to `theme`, `themes` and `styleTrees`.

Release Notes:

- N/A (No public facing changes)
2023-06-07 22:47:48 -04:00
Mikayla Maki
8216d26a7a Switch to attaching git statuses to their associated entries (#2571)
This rewrites and simplifies the git status system by attaching the git
status to each individual entry. This also improves the git testing
infrastructure to cover more cases and be more accurate to how file
events actually occur. This also fixes several other bugs in the worktree and the buffer, and stops any randomly generated actions from happening inside a `.git` folder. Hopefully, we can undo this change soon once our randomized testing is more robust.

Release Notes:

- Will require a DB migration

TODO:
- [x] Pass randomized tests
- [x] Get ready for merging
2023-06-07 17:11:43 -07:00
Mikayla Maki
fc1f8c5657 Fixed ci 2023-06-07 16:58:55 -07:00
Mikayla Maki
8ca1a7d43d add scan_complete await 2023-06-07 16:51:54 -07:00
Max Brunsfeld
66f215cd13 Restructure LSP log view to show log messages in addition to RPC trace 2023-06-07 16:51:09 -07:00
Mikayla Maki
9e9d8e3a7b add mroe dbg 2023-06-07 16:50:15 -07:00
Mikayla Maki
9d58c4526d Fix warning 2023-06-07 16:45:36 -07:00
Mikayla Maki
5f143f689f Attempting to debug on ci... 2023-06-07 16:44:13 -07:00
Mikayla Maki
572d40381a Add track caller 2023-06-07 16:39:10 -07:00
Mikayla Maki
2c5e83bf72 Fixed a bug where buffer saved clocks would desynchronize in rare execution paths
co-authored-by: Max <max@zed.dev>
2023-06-07 16:10:45 -07:00
Max Brunsfeld
78f9642ac2 Emit project event when a language server sends a logMessage notification 2023-06-07 14:46:08 -07:00
Mikayla Maki
cd63ec2c7f fmt 2023-06-07 14:20:01 -07:00
Mikayla Maki
03a96d2793 Feature gate import 2023-06-07 14:15:20 -07:00
Mikayla Maki
0ac7a3bc21 fmt 2023-06-07 14:13:57 -07:00
Mikayla Maki
28ba27c9c5 Merge branch 'main' into stream-git-statuses 2023-06-07 14:12:58 -07:00
Mikayla Maki
34e134fafb Fix several randomized test failures with the new git status implementation 2023-06-07 14:10:17 -07:00
Max Brunsfeld
351e4863cd Fix some issues found when testing Elixir-LS (#2583)
Closes
https://linear.app/zed-industries/issue/Z-2209/popovers-dont-always-have-syntax-highlighted-code
Closes
https://linear.app/zed-industries/issue/Z-2206/highlight-syntax-in-hover-docs

* Fix a ton of errors in our logs due to us not recognizing that
`elixir-ls` does not support code actions.
* Syntax-highlight elixir code blocks in hover popovers
2023-06-07 12:21:48 -07:00
Max Brunsfeld
11ab1a8cc6 collab 0.13.1 2023-06-07 10:54:27 -07:00
Max Brunsfeld
be8d268eb9 Bump tree-sitter-yaml submodule to avoid broken child submodule 2023-06-07 10:50:03 -07:00
Max Brunsfeld
72372ddf0e Highlight hover code blocks as LSP's own language by default
The elixir LSP does not include a language name on its elixir code blocks.
2023-06-07 10:01:19 -07:00
Nate Butler
86ec43c908 Update createColorScheme to accept ThemeConfig (#2557)
- Added ThemeConfig
- Updated themes use ThemeConfig
- Refactoring
- Fixed TypeScript errors

Release Notes:
- N/A (No public facing changes)
2023-06-07 12:57:34 -04:00
Nate Butler
29de420b59 Organize misc files into theme, themes and styleTrees 2023-06-07 12:50:37 -04:00
Nate Butler
6269cec4f1 Minor updates 2023-06-07 12:40:49 -04:00
Max Brunsfeld
6067575e38 collab 0.13.0 2023-06-07 09:29:58 -07:00
Max Brunsfeld
f56d642b88 Disable scheduled randomized test workflow for now 2023-06-07 09:20:25 -07:00
Joseph Lyons
8882b22c9c v0.91.x dev 2023-06-07 11:57:41 -04:00
Piotr Osiewicz
e94129446d Z 1618/extend comments (#2585)
Fixes Z-1618. In the current state, this only works for line comments
such as `//` (and whatever's set in `{language}.toml` as a
line_comment).

Release Notes:

- Comments are now extended on new line.
2023-06-07 17:37:02 +02:00
Sergii Onufriienko
b1f009cdce Merge branch 'main' into sergey/z-1768-update-createcolorscheme-to-accept-a-theme-in-the 2023-06-07 16:12:13 +01:00
Sergey Onufrienko
4c405e65a3 feat: use theme index to build licenses 2023-06-07 16:10:16 +01:00
Sergey Onufrienko
0ad76ac92c feat: use theme index to build themes 2023-06-07 16:10:02 +01:00
Sergey Onufrienko
02c1efc60d feat: re-export chroma 2023-06-07 16:09:24 +01:00
Sergey Onufrienko
f5d1f314e0 feat: add themes index 2023-06-07 16:08:58 +01:00
Sergey Onufrienko
d2b8501347 feat: change themes to return ThemeConfig 2023-06-07 16:08:48 +01:00
Antonio Scandurra
d5441ba386 Introduce an Assistant Panel (#2584)
This pull request introduces a new assistant panel to Zed that lets
users interact with OpenAI using their API key:

![CleanShot 2023-06-07 at 09 39
10@2x](https://github.com/zed-industries/zed/assets/482957/ba2a5830-9aeb-4c45-a182-a44d6a72675f)

After setting the key up, it will be saved to the keychain and
automatically loaded the next time the assistant panel is opened. The
key can be reset using `assistant: reset key`.

![CleanShot 2023-06-07 at 09 39
23@2x](https://github.com/zed-industries/zed/assets/482957/a6808bb0-0098-45ae-a2e3-f4d88472e626)

From there, users can type messages in a multi-buffer and hit
`cmd-enter` (`assistant: assist`) to stream assistant responses using
the OpenAI API. Responses can be canceled by hitting `escape`.

![CleanShot 2023-06-07 at 09 40
16@2x](https://github.com/zed-industries/zed/assets/482957/749779da-850e-4ad5-af04-74a3ca39f7ad)

Users can quote a selection from the active editor by hitting `cmd->`
(`assistant: quote selection`), which will embed the selected piece of
text in a Markdown fenced code block. Conversations with the assistant
are ephemeral at the moment, but can be easily copy/pasted:

![CleanShot 2023-06-07 at 09 50
33@2x](https://github.com/zed-industries/zed/assets/482957/b3386c10-4c51-4419-a0e0-517112ef6521)

Release Notes:

- Added a new assistant panel feature that enables interacting with
OpenAI using an API key. This replaces the previous experimental `ai:
assist` command that would work on any buffer. The experience is similar
to the one offered by ChatGPT with the added ability to edit, delete or
enhance previous messages. When hitting `cmd-enter`, the assistant will
start streaming responses from OpenAI. A response stream can be canceled
using `escape`. Moreover, the active editor's selection can be quoted in
the assistant panel using `cmd->`, which will automatically embed the
selected piece of text in a Markdown fenced code block.
2023-06-07 16:05:57 +02:00
Antonio Scandurra
d3e0d38bef Fix assistant panel tests 2023-06-07 15:24:08 +02:00
Antonio Scandurra
d26cc2c897 Maintain scroll bottom when streaming assistant responses 2023-06-07 15:01:50 +02:00
Antonio Scandurra
43500dbf60 Fix zed tests 2023-06-07 10:02:35 +02:00
Antonio Scandurra
0dae8f2dd8 Merge remote-tracking branch 'origin/main' into assistant-2 2023-06-07 09:34:16 +02:00
Antonio Scandurra
a6feaf1300 Allow search assistant editors 2023-06-07 09:24:18 +02:00
Joseph Lyons
c93b6cc599 Tweak comment wording 2023-06-07 00:23:58 -04:00
Max Brunsfeld
e8479f23f9 Improve log message on invalid LSP message from server's stdout 2023-06-06 17:13:59 -07:00
Max Brunsfeld
6f2726524e Remove stray println 2023-06-06 17:13:59 -07:00
Max Brunsfeld
fccbac4887 Handle LSP codeActions capability set to false 2023-06-06 17:13:59 -07:00
Max Brunsfeld
0d90c6d02e Improve syntax highlighting and outline view for Elixir (#2582)
Fixes
https://linear.app/zed-industries/issue/Z-2208/outline-view-doesnt-differentiate-between-overloaded-functions
Fixes
https://linear.app/zed-industries/issue/Z-2205/elixir-syntax-highlighting-not-working-properly-for-doc-attributes-and

This PR improves syntax highlighting and outline view in Elixir. It's
common to overload elixir functions, with many different versions of the
function for different patterns of parameters, so I updated the outline
view to show functions' parameters in Elixir. But if we showed functions
the same way in the *breadcrumbs*, it would take up too much space.

So I added a new capture in languages' `outline` queries called
`@context.extra`, which is included in the outline view, but not in
breadcrumbs.

Release Notes:

- Improved syntax highlighting of doc attributes and special macros in
Elixir
- Updated the outline view in Elixir to display function parameters, to
allow differentiating between function overloads.
2023-06-06 16:34:33 -07:00
Max Brunsfeld
4b9a3c66e6 Show function parameters in elixir outline view
Introduce a new capture in the outline query called 'context.extra', which
causes text to appear in the outline, but not in the breadcrumbs.
2023-06-06 16:27:05 -07:00
Max Brunsfeld
7aeaa84657 Fix the order of some patterns in elixir highlight query 2023-06-06 16:06:16 -07:00
Max Brunsfeld
8dc679e74e Upgrade tree-sitter-elixir 2023-06-06 16:06:16 -07:00
Max Brunsfeld
6737ee1495 Avoid panic when failing to load a language's queries 2023-06-06 16:06:16 -07:00
Piotr Osiewicz
cc63d3d048 chore: add targets to rust-toolchain.toml (#2581) 2023-06-07 00:12:47 +02:00
Max Brunsfeld
a9f865d828 Don't apply the 'literal' color to entire markdown code blocks (#2579)
### Before

![Screen Shot 2023-06-06 at 2 32 46
PM](https://github.com/zed-industries/zed/assets/326587/036f1da3-19c6-4979-adc6-c3133f8d63dc)

### After

![Screen Shot 2023-06-06 at 2 32 08
PM](https://github.com/zed-industries/zed/assets/326587/6d4729db-b76a-4052-847e-fd9de346f194)

Release Notes:

* Improved the syntax highlighting of fenced code blocks in markdown.
2023-06-06 14:54:24 -07:00
Piotr Osiewicz
dfd72770e7 chore: bump MSRV to 1.70, add rust-toolchain (#2580)
This time I've added a `components` section to rust-toolchain.toml file
to explicitly require a rustfmt component. Fingers crossed.
2023-06-06 23:49:34 +02:00
Max Brunsfeld
3fc2e0754b Don't apply the 'literal' color to entire markdown code blocks 2023-06-06 14:34:45 -07:00
Max Brunsfeld
bdd3e77e02 Live-reload tree-sitter queries in development (#2578)
This PR adds live reloading of Tree-sitter queries when running in debug
mode, similar to what we do for the themes. This way, you can change a
highlighting query or an outline query, and immediately see the result
in the app.

Release Notes:

- N/A
2023-06-06 14:30:28 -07:00
Max Brunsfeld
7bfb51ee76 Live-reload tree-sitter queries in development 2023-06-06 14:20:21 -07:00
Piotr Osiewicz
559a58d737 Revert "chore: bump MSRV to 1.70" (#2577)
Reverts zed-industries/zed#2573
2023-06-06 22:21:34 +02:00
Piotr Osiewicz
c1c91dc2e3 chore: bump MSRV to 1.70 (#2573)
Add rust-toolchain.toml

Release Notes:

- N/A
2023-06-06 21:50:27 +02:00
Piotr Osiewicz
572c59eec4 chore: Enable full LTO (#2548)
Per conversation with Antonio, I've suggested enabling full LTO; right
now we use a crate-local ThinLTO, which does not inline function calls
across crates.

| Configuration    | Current main (788f97e) | Thin LTO  | Full LTO  |
|------------------|------------------------|-----------|-----------|
| Size in bytes    | 158806721              | 155868753 | 111115553 |
| % of `main` size | 100%                   | 98.14%    | 69.96%    |
| Size in bytes (no debug info) | 129186657 | 127942929 | 108281345 |
2023-06-06 21:50:08 +02:00
Max Brunsfeld
17560cc5b0 Eliminate assets crate (#2575)
Previously, all static assets were embedded into Zed using a single
`RustEmbed` struct called `Assets`, which was in a crate of its own,
also called `assets`. Many crates depended on this crate. The problem
with this situation was that changing *any* static asset file caused
almost every crate in the codebase to need recompilation (because of
recursive dependencies on `assets`).

Now, most of the assets are embedded only into the top-level `zed`
crate. A few assets (such as settings JSON files and AI prompts) are
needed in lower-level crates such as `settings` and `ai`. For these,
I've created separate `RustEmbed` structs in those specific crates,
which embed those specific sub-folders of `assets`.

The result is that now, when you change a theme file, the only crate
that needs to recompile is `zed`.

Release Notes:

- N/A
2023-06-06 12:00:44 -07:00
Max Brunsfeld
dbbd0558c3 Eliminate assets crate 2023-06-06 11:46:46 -07:00
Joseph Lyons
2003d3dbe4 Update initial_user_settings.json 2023-06-06 14:39:56 -04:00
Antonio Scandurra
7a78e64831 Merge remote-tracking branch 'origin/main' into assistant-2 2023-06-06 19:18:22 +02:00
Antonio Scandurra
16090c35ae Insert reply after assistant message when hitting cmd-enter
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-06-06 19:15:06 +02:00
Antonio Scandurra
ef7ec265c8 Cycle message roles on click
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-06-06 18:45:08 +02:00
Max Brunsfeld
53906fd3da Add an application menu item for creating/opening a project-specific settings file (#2572)
Previously, project-specific settings were not discoverable. This PR
adds a `Zed > Preferences > Local Settings` application menu command
that creates a `.zed/settings.json` at the root of your current
worktree. This command works in both local and remote projects.

Limitations:

* Currently, if you have an empty project open, the command just shows a
notification that there are no folders open.
* The JSON-schema-based autocomplete is the same in local settings files
as in your main settings, even though not all settings can be locally
customized.

Release Notes:

- Added an application menu command - `Zed > Preferences > Local
Settings` for creating a folder-specific settings file.
2023-06-06 09:27:36 -07:00
Antonio Scandurra
ac7178068f Include message headers in copied assistant text
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-06-06 18:18:04 +02:00
Max Brunsfeld
cfcfc3bf6b Show notification when attempting to open local settings in a project w/ no folders 2023-06-06 09:03:57 -07:00
Antonio Scandurra
093ce8a9ac Simplify prompt
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-06-06 17:45:58 +02:00
Nate Butler
7b066df7e6 Tighten up spacing in the project panel (#2574)
Following https://github.com/zed-industries/zed/pull/2559 the project
panel entries become pretty wide again. This PR tries to mitigate that
and just make some general improvements to visual density in the project
panel.

- Reduces padding around items
- Removes top margin
- Slightly reduces the height of each item
- Fixes an issue where ignored files had the wrong color chevron

Release Notes:

- Improved density of the project panel and tidied up some visual
issues.
2023-06-06 11:31:53 -04:00
Piotr Osiewicz
a0e2e5db7d project panel/styles: Align child's chevron with parent path (#2559)
Z-1012

Release notes:
- Adjust indent of files in subdirectories.
2023-06-06 16:55:38 +02:00
Antonio Scandurra
2b1aeb07bc Show error message when requests to OpenAI fail
Co-Authored-By: Julia Risley <julia@zed.dev>
2023-06-06 16:53:56 +02:00
Antonio Scandurra
9c59146026 Set assistant editor's title based on the first question/answer pair
Co-Authored-By: Julia Risley <julia@zed.dev>
2023-06-06 15:59:10 +02:00
Antonio Scandurra
69b8267b6b Show the current model and allow clicking on it to change it 2023-06-06 13:13:44 +02:00
Antonio Scandurra
ada222078c Insert a user reply when hitting cmd-enter in an assistant message 2023-06-06 13:13:41 +02:00
Antonio Scandurra
f4f060667e Add assertion to pinpoint how deletion works across excerpts 2023-06-06 12:36:26 +02:00
Antonio Scandurra
337dda8e3a Only remove excerpts when an edit touches them 2023-06-06 12:27:05 +02:00
Antonio Scandurra
8032324470 Prevent moving across excerpts on Editor::delete 2023-06-06 12:26:53 +02:00
Antonio Scandurra
e46d1549d6 Retain selection's head (as opposed to its end) on insertion
This makes a difference when an edit spans two excerpts and the selection
start won't necessarily be the same as the selection end after the edit.
2023-06-06 10:12:15 +02:00
Nathan Sobo
23836eb251 Not working yet: Remove empty messages unless they contain the cursor
Problem is, I'm trying to trust the excerpt id of the selection head,
but it's a sentinel value and not the actual excerpt id of the message.
I think we probably need to resolve to offsets instead.
2023-06-05 22:58:08 -06:00
Max Brunsfeld
296a0bf510 Populate created local settings file with an empty JSON object and comments 2023-06-05 18:20:02 -07:00
Max Brunsfeld
cb975f1252 Add Zed > Preferences > Local Settings to application menu 2023-06-05 17:45:42 -07:00
Max Brunsfeld
0949ee84d8 🎨 Move OpenSettings action back to the zed crate 2023-06-05 17:43:05 -07:00
Mikayla Maki
a2d58068a7 Improve test generation and implement status propogation
co-authored-by: max <max@zed.dev>
2023-06-05 17:30:12 -07:00
Mikayla Maki
c12bdc894a Silence not found errors 2023-06-05 15:19:59 -07:00
Max Brunsfeld
398b0f303c Apply bounds to all windows when using start-local-collaboration script (#2570)
The `start-local-collaboration` script opens two instances of Zed, each
logged in as a different user, and each one taking up half of the
screen. But previously, when joining a remote project as one of the
collaborators, that newly-opened window would be full screen.

Now, each instance of Zed keeps *all* of its windows on its half of the
screen. This is implemented by respecting the `ZED_WINDOW_{SIZE,BOUNDS}`
env vars, even when joining *remote* projects.

Release Notes:

- N/A
2023-06-05 14:49:39 -07:00
Max Brunsfeld
3d1ba1b363 Apply bounds to all windows when using start-local-collaboration script 2023-06-05 14:36:21 -07:00
Max Brunsfeld
571151173c Fix copilot state corruption when there are multiple buffers with the same remote id in different projects (#2569)
Fixes
https://linear.app/zed-industries/issue/Z-1511/thread-main-panicked-at-assertion-failed-left-==-right-left-local-0-1

Previously, when exchanging messages about buffers with a copilot
language server, we identified buffers using their **remote id**. This
caused problems when there were multiple projects open, where one or
more were remote, because buffers' remote ids are only unique within a
given project.

When you have multiple projects open, and one or more of the projects is
remote, it's pretty easy to have two buffers open with the same remote
id. In my testing, when this happened, copilot would stop working in
both buffers. But I believe that depending on the editing patterns that
occur in the two buffers, it could cause the crash reported in the
Linear issue above.

This PR changes our copilot logic to use buffers' local handle ids for
identifying them. This fixed the problems I was able to reproduce when
using copilot in both remote and local projects.

Release Notes:

- Fixed a crash that would sometimes occur when editing buffers after
having collaborated on a remote project.
2023-06-05 14:33:56 -07:00
Max Brunsfeld
12dd91c89c Use local ids, not remote ids, to identify buffers to copilot 2023-06-05 14:12:19 -07:00
Mikayla Maki
5e4da6433f Add file and line number information to logs (#2568)
This PR adds codegen from rustc to track the file and line number of
calls to `log_err()`. I haven't noticed much longer compile times on my
machine, and looking at the
[implementation](https://rustc-dev-guide.rust-lang.org/backend/implicit-caller-location.html)
it essentially adds an extra argument and secret reference pass.
However, this will show a lot more data in our logs on user machines.
Requesting review from @ForLoveOfCats, who usually knows a bunch about
this kind of thing :)
2023-06-05 13:24:07 -07:00
Mikayla Maki
624467ebca Add file and line number information to logs 2023-06-05 12:53:37 -07:00
Mikayla Maki
9a13a2ba2c WIP: Add status bubbling to project panel 2023-06-05 12:53:04 -07:00
Joseph T. Lyons
7fbafc8030 Remove code sending zed events to mixpanel (#2567)
Release Notes:

- N/A
2023-06-05 15:40:27 -04:00
Joseph Lyons
311074e397 Remove code sending zed events to mixpanel 2023-06-05 15:33:17 -04:00
Mikayla Maki
49c5a3fa86 Add postgres migration 2023-06-05 12:17:21 -07:00
Joseph T. Lyons
2190a27dff Stop sending editor events to mixpanel (#2566)
Release Notes:

- N/A
2023-06-05 15:15:30 -04:00
Joseph Lyons
70c5489c13 Stop sending editor events to mixpanel 2023-06-05 15:09:47 -04:00
Mikayla Maki
4ac5f7b14e Add statuses to test integration 2023-06-05 12:06:23 -07:00
Mikayla Maki
e56fcd69b5 Track git status changes with the changed_paths system 2023-06-05 11:50:23 -07:00
Joseph T. Lyons
4f3165692f Move app version into panic object (#2565)
Final tweaks to clean up how we structure our panic data

Release Notes:

- N/A
2023-06-05 14:40:28 -04:00
Joseph Lyons
917d8949b7 Move app version into panic object 2023-06-05 14:17:48 -04:00
Julia
6b89243902 Update rust-bindgen dev-dependencies (#2563)
I did \*something\* Friday afternoon which changed something about my
system SDK to break the `media` crate's bindings generation. Some of the
types and consts were not being generated despite being able to prove
that they exist in the source, such as when running the header through
the preprocessor myself and feeding that through the generator. Updating
my OS, XCode, command line tools, and reinstalling Rust as well as
working from fresh clones of the repo had no effect.

Updating rust-bindgen resolved the issue and downgrading the version
back to the original version caused the issue to reappear. I'm still not
sure what happened to change the SDK but at this point with being able
to build the project again I'm not going to look a gift horse in the
mouth.

Release Notes:

- N/A
2023-06-05 09:21:07 -04:00
Antonio Scandurra
0ed8bbc818 Query rect_for_text_range on focused view instead of root element (#2564)
This was causing IME input to be drawn in the wrong place when there
were splits or panels in the window.

Release Notes:

- Fixed a bug that was causing IME input to sometimes be rendered in the
wrong position.
2023-06-05 12:11:35 +02:00
Antonio Scandurra
c872f581d1 Query rect_for_text_range on focused view instead of root element
This was causing IME input to be drawn in the wrong place when there
were splits or panels in the window.
2023-06-05 12:00:40 +02:00
Antonio Scandurra
bef6932da7 Avoid accidentally taking the api_key when requesting an assist 2023-06-05 11:25:21 +02:00
Julia
5790d6993e Update rust-bindgen dev-dependencies 2023-06-04 23:34:22 -04:00
Joseph T. Lyons
a89f3ed445 Fix typos (#2562)
Quite literally just ran `typos --write-changes` from
https://crates.io/crates/typos. Its pretty impressive - wonder if we
should run it on CI.

Release Notes:

- N/A
2023-06-02 22:08:45 -04:00
Joseph Lyons
7c60f636d5 Fix typos 2023-06-02 22:02:19 -04:00
Mikayla Maki
ca077408d7 Fix bug where git statuses would not be initialized on startup
move git status queries to be on entry creation

co-authored-by: max <max@zed.dev>
2023-06-02 17:38:39 -07:00
Mikayla Maki
2f97c7a4f1 Remove stale comments
Implement status bubbling query with sum tree traversals

co-authored-by: max <max@zed.dev>
2023-06-02 16:41:01 -07:00
Mikayla Maki
e377459948 Remove stateful bubbling
co-authored-by: max <max@zed.dev>
2023-06-02 15:07:49 -07:00
Mikayla Maki
99a0e11e70 Abandoning stateful bubbling approach
co-authored-by: max <max@zed.dev>
2023-06-02 14:51:40 -07:00
Max Brunsfeld
46d2cbaa4c Improve panic reports (#2560)
* Add an `identifying_backtrace` field that only contains symbols in
*our* own codebase, which can be used for better deduplication.
* In the main backtrace, include file and line numbers for all symbols
in our codebase
* Exclude any stack frames within the panic handling/hooking system
itself, so that the top line of the backtrace is where the panic
originated in our codebase.

This should improve our panic deduplication, and also make panic reports
a bit more readable.

example:

```
{
  "thread": "main",
  "payload": "wtf",
  "location_data": {
    "file": "crates/zed/src/zed.rs",
    "line": 459
  },
  "backtrace": [
    "zed::open_log_file::{{closure}}::{{closure}}::{{closure}}",
    "    crates/zed/src/zed.rs:459",
    "gpui::app::AppContext::spawn_internal::{{closure}}",
    "    crates/gpui/src/app.rs:2073",
    "gpui::executor::any_local_future::{{closure}}",
    "    crates/gpui/src/executor.rs:1026",
    "<core::pin::Pin<P> as core::future::future::Future>::poll",
    "<async_task::runnable::spawn_local::Checked<F> as core::future::future::Future>::poll",
    "async_task::raw::RawTask<F,T,S>::run",
    "async_task::runnable::Runnable::run",
    "<gpui::platform::mac::dispatcher::Dispatcher as gpui::platform::Dispatcher>::run_on_main_thread::trampoline",
    "    crates/gpui/src/platform/mac/dispatcher.rs:40",
    "<() as objc::message::MessageArguments>::invoke",
    "objc::message::platform::send_unverified",
    "objc::message::send_message",
    "<gpui::platform::mac::platform::MacForegroundPlatform as gpui::platform::ForegroundPlatform>::run",
    "    crates/gpui/src/platform/mac/platform.rs:366",
    "gpui::app::App::run",
    "    crates/gpui/src/app.rs:251",
    "Zed::main",
    "    crates/zed/src/main.rs:118",
    "core::ops::function::FnOnce::call_once",
    "std::sys_common::backtrace::__rust_begin_short_backtrace",
    "std::rt::lang_start::{{closure}}",
    "core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once",
    "std::rt::lang_start"
  ],
  "release_channel": "dev",
  "os_name": "macOS",
  "os_version": "12.6.1",
  "architecture": "aarch64",
  "panicked_on": 1685734744050,
  "identifying_backtrace": [
    "zed::open_log_file::{{closure}}::{{closure}}::{{closure}}",
    "gpui::app::AppContext::spawn_internal::{{closure}}",
    "gpui::executor::any_local_future::{{closure}}",
    "<gpui::platform::mac::dispatcher::Dispatcher as gpui::platform::Dispatcher>::run_on_main_thread::trampoline",
    "<gpui::platform::mac::platform::MacForegroundPlatform as gpui::platform::ForegroundPlatform>::run",
    "gpui::app::App::run",
    "Zed::main"
  ]
}
```

Release Notes:

N/A
2023-06-02 13:11:57 -07:00
Max Brunsfeld
33c4c32196 Avoid writing spurious nulls to the settings file when updating it programatically (#2561)
Thanks for catching this @iamnbutler.
2023-06-02 13:11:35 -07:00
Max Brunsfeld
7417835f06 Avoid writing spurious nulls to settings file when updating it programatically 2023-06-02 13:02:17 -07:00
Max Brunsfeld
f6a4706410 Improve panic reports
* Add an 'identifying_backtrace' field that only contains symbols in our
  codebase, which can be used for better deduplication.
* In the main backtrace, include file and line numbers for all symbols
  in our codebase
2023-06-02 12:37:39 -07:00
Joseph T. Lyons
00265c19a0 Add additional panic information to panic events (#2555)
Adds the following to the panic event

release_channel
os_name
os_version
architecture

Merge first: https://github.com/zed-industries/zed.dev/pull/322

Release Notes:

- N/A
2023-06-02 14:30:16 -04:00
Piotr Osiewicz
345fad3e9d editor: add select previous command (#2556)
Added a `select previous` command to complement `select next`.
Release Notes:

- Added "Select previous" editor command, mirroring `Select next`.
Ticket number: Z-366
2023-06-02 17:32:34 +02:00
Antonio Scandurra
f00f16fe37 Show remaining tokens 2023-06-02 17:27:21 +02:00
Sergey Onufrienko
f97999d97f feat: update themes to use ThemeConfig 2023-06-02 14:11:11 +02:00
Sergey Onufrienko
5fbbc1936f fix: typescript errors 2023-06-02 14:10:44 +02:00
Sergey Onufrienko
b38f760fcd feat: add index export 2023-06-02 14:10:19 +02:00
Sergey Onufrienko
d3ed958308 chore: make SyntaxHighlightStyle.color optional 2023-06-02 14:10:02 +02:00
Sergey Onufrienko
6b00db75ad feat: update and refactor colorScheme 2023-06-02 14:08:35 +02:00
Sergey Onufrienko
56ecfaf2f0 feat: add themeConfig types 2023-06-02 14:07:43 +02:00
Antonio Scandurra
3750e64d9f Save OpenAI API key in the keychain 2023-06-02 12:15:25 +02:00
Piotr Osiewicz
571d2f4966 Z-1074/search default label (#2547)
This commit adds a default "Project search" tooltip for empty search
panes. Fixes Linear ticket Z-1074
Release Notes:

- Added default tooltip to empty search panes.
([#1533](https://github.com/zed-industries/community/issues/1533)).
2023-06-02 11:42:40 +02:00
Antonio Scandurra
a81d164ea6 Allow saving the OpenAI API key in the assistant panel 2023-06-02 11:38:02 +02:00
Antonio Scandurra
d0aff65b1c Allow moving the assistant panel to other docks 2023-06-02 10:55:19 +02:00
Antonio Scandurra
55c8c6d3fb Allow adding new contexts 2023-06-02 10:29:22 +02:00
Antonio Scandurra
cf934ab696 Fix compile errors 2023-06-02 10:08:06 +02:00
Antonio Scandurra
20e65a533c Merge remote-tracking branch 'origin/main' into assistant-2 2023-06-02 09:32:11 +02:00
Mikayla Maki
3768851799 WIP: Git statuses 2023-06-01 23:27:49 -07:00
Joseph Lyons
c55aee84d3 Rename field to panicked_on 2023-06-02 01:33:25 -04:00
Joseph Lyons
cc055901e1 Add additional panic information to panic events 2023-06-01 23:34:39 -04:00
Mikayla Maki
5e43dcaab8 WIP: Add stateful status bubbling to worktree 2023-06-01 16:51:34 -07:00
Max Brunsfeld
0bd9d5b1fa Zoom out all panes when opening a dock (#2554)
Fixes
https://linear.app/zed-industries/issue/Z-2051/center-zoom-isnt-unset-when-opening-project-panel

Release Notes:

- Fixed a bug where opening the project panel did not reset the zoom
state of a pane.
2023-06-01 15:26:46 -07:00
Max Brunsfeld
585d13d3db Unzoom all panes when opening a dock 2023-06-01 15:20:15 -07:00
Max Brunsfeld
a55eafa726 Fix ambiguous glob export warnings in rust 1.70 (#2553)
This fixes a warning that is introduced when upgrading to Rust 1.70.

Release Notes:

- N/A
2023-06-01 14:37:36 -07:00
Max Brunsfeld
e67e6e6f70 Fix ambiguous glob export warnings in rust 1.70 2023-06-01 14:07:59 -07:00
Joseph T. Lyons
6d24a8a763 Add panic events (#2552)
Adds a time field to support panic events.  See

https://github.com/zed-industries/zed.dev/pull/321

Release Notes:

- N/A
2023-06-01 16:47:29 -04:00
Joseph Lyons
0065f5715c Add panic events 2023-06-01 16:40:54 -04:00
Mikayla Maki
9d6b3744f7 Redraw the terminal on every wakeup (#2551)
For whatever reason, the optimizations of panes and workspace have
caused the terminal to notify less often then it should. This PR fixes
that oversight.
2023-06-01 10:56:13 -07:00
Mikayla Maki
40c6baf7cb Redraw the terminal on every wakeup
co-authored-by: max <max@zed.dev>
2023-06-01 10:49:31 -07:00
Mikayla Maki
d64dc3960d Make settings store handle no user settings (#2550)
This fixes the crash users have been reporting with the theme selector
2023-06-01 10:25:30 -07:00
Mikayla Maki
2390815d67 Make settings store handle no user settings
co-authored-by: max <max@zed.dev>
2023-06-01 10:09:04 -07:00
Julia
2ffbeca7dd Avoid blocking forever on startup if config files do not exist (#2549)
The files will still get created if the user opens their settings and
saves, otherwise everything will transparently work

Release Notes:

- Fixed an issue where a missing settings file would cause a hang on
startup
([#1590](https://github.com/zed-industries/community/issues/1590)).
2023-06-01 12:13:22 -04:00
Julia
51c82da840 Avoid blocking forever on startup if config files do not exist
The files will still get created if the user opens their settings and
saves, otherwise everything will transparently work

Co-Authored-By: Antonio Scandurra <me@as-cii.com>
Co-Authored-By: Max Brunsfeld <max@zed.dev>
2023-06-01 12:04:45 -04:00
Kirill Bulatov
d8ce333cf8 Remove wrong assertion 2023-06-01 16:51:29 +03:00
Piotr Osiewicz
01621972c5 Change "Project search" to "Project Search" 2023-06-01 12:31:37 +02:00
Piotr Osiewicz
e1a6dc9077 search: Add a default tooltip for empty searches
Z-1074
2023-06-01 11:17:33 +02:00
Max Brunsfeld
788f97ec68 Add support for folder-specific settings (#2537)
This PR allows you to customize Zed's settings within a particular
folder by creating a `.zed/settings.json` file within that folder.

Todo

* [x] respect folder-specific settings for local projects
* [x] respect folder-specific settings in remote projects
* [x] pass a path when retrieving editor/language settings
* [x] pass a path when retrieving copilot settings
* [ ] update the `Setting` trait to make it clear which types of
settings are locally overridable

Release Notes:

* Added support for folder-specific settings. You can customize Zed's
settings within a particular folder by creating a `.zed` directory and a
`.zed/settings.json` file within that folder.
2023-05-31 16:27:08 -07:00
Max Brunsfeld
ae7606ce21 collab 0.12.5 2023-05-31 15:38:26 -07:00
Max Brunsfeld
0d281c1b89 🎨 2023-05-31 15:13:01 -07:00
Max Brunsfeld
0dd7694ff5 Make language_settings take a language, not a language name 2023-05-31 15:10:30 -07:00
Max Brunsfeld
03a351fb26 Make language settings accessors take an arc dyn file 2023-05-31 14:57:04 -07:00
Max Brunsfeld
38078b93cc v0.90.x dev 2023-05-31 14:44:48 -07:00
Max Brunsfeld
a2ab7c9eb9 Respect project-specific settings for copilot 2023-05-31 14:42:15 -07:00
Mikayla Maki
6f4fd74f43 Add a dock-keybinding-changed one-time notification (#2546)
This code is intended to be removed at a later date.

Release Notes:

- N/A
2023-05-31 14:28:12 -07:00
Mikayla Maki
bf2016adf5 Remove version check 2023-05-31 14:23:56 -07:00
Mikayla Maki
27ef0e2b52 De-morgans properly 2023-05-31 14:21:38 -07:00
Mikayla Maki
45b42c512d fmt 2023-05-31 14:17:27 -07:00
Mikayla Maki
705e36827c add version check 2023-05-31 14:16:37 -07:00
Mikayla Maki
b875d4ed40 Remove silly panic 2023-05-31 14:10:50 -07:00
Mikayla Maki
ed0b9acb0a Add panic if version is 0.91 2023-05-31 14:06:36 -07:00
Mikayla Maki
ef80b539d1 Fix notification styling, minimize database reads 2023-05-31 13:56:41 -07:00
Max Brunsfeld
62660f2766 Fix more issues with panels and zoom (#2545)
Release Notes:

* Fixed the behavior of panel buttons when their panel is open. Show the
key binding for closing the containing dock (preview only).
* Improved the styling of zoomed panels and panes, giving them a
stronger border, and color the zoom button with an "active" style
(preview only).
* Improved the stability of panels' zoom state. Close their dock instead
of resetting their zoom state when dismissing panels to reveal other
items (preview only).
2023-05-31 13:17:12 -07:00
Max Brunsfeld
193474a346 🎨 2023-05-31 13:07:20 -07:00
Max Brunsfeld
c48fed26bd When revealing items, zoom-out any inactive panes 2023-05-31 13:05:50 -07:00
Mikayla Maki
4717ce1da3 WIP: Move statuses to entries
co-authored-by: julia <julia@zed.dev>
2023-05-31 12:55:31 -07:00
Max Brunsfeld
3f6aa94a5f Use active color for zoom button in a zoomed pane 2023-05-31 12:51:57 -07:00
Max Brunsfeld
893615236d Only include one border edge for zoomed panels 2023-05-31 12:45:55 -07:00
Nate Butler
1f72f9e18b Add un-hooked up zoomIcon styles 2023-05-31 12:45:55 -07:00
Nate Butler
a10933c063 Update pane/panel zoom styling 2023-05-31 12:45:55 -07:00
Max Brunsfeld
ac41564a8f Dismiss zoomed panels by closing their dock, not zooming them out 2023-05-31 12:45:55 -07:00
Mikayla Maki
22e4086658 WIP: Move statuses to be on their associated file entries in worktree
co-authored-by: Julia <julia@zed.dev>
2023-05-31 11:03:11 -07:00
Sergii Onufriienko
c6672dbac2 Update theme license logic (#2541)
Use new theme file structure
```
themes/
  [theme_name]
    [theme_name]-common.ts
    [theme_name]-light.ts
    [theme_name]-dark.ts
    LICENSE
```

Release Notes: 
- N/A
2023-05-31 18:36:13 +01:00
Max Brunsfeld
76927b6d95 Make active panel's button close its dock 2023-05-31 10:29:59 -07:00
Max Brunsfeld
eeba72d775 Bump protocol version 2023-05-31 09:03:45 -07:00
Max Brunsfeld
8f95435548 Replicate project-specific settings when collaborating 2023-05-30 18:08:03 -07:00
Mikayla Maki
c9820fde61 WIP: Add toast when users attempt to use shift-escape for the first time 2023-05-30 17:48:41 -07:00
Mikayla Maki
584e5f7958 Fix usability issues with new panel system. (#2544)
This PR updates the dock key bindings according to the following model:

There are three bits: 
Visible: Opened / closed.
Focus: Panel focused / center focused.
Zoom: Zoomed / Not zoomed.

Each of these variables is 'sticky' in that they won't effect each other
unless they need to. 'Zooming' a panel conceptually merges the visible
and focus bits.

cmd-shift-j/b/r have all been removed.

cmd-j/b/r have been updated to mean 'toggle visibility of a certain
dock', firing them should *always* reveal the panel to you (where you
last left it), or hide it, without moving focus (unless the focused
element is invisible). This means that, when the terminal panel is
zoomed, cmd-j has the same effect as ctrl-`

ctrl-` and cmd-shift-e now toggle a panel's focus, without updating the
zoom state of a panel. Toggling the focus of a zoomed panel causes it to
automatically hide itself, without losing the zoom bit.

When focused or made visible, panels which cannot be zoomed
automatically unzoom everything else so as to preserve user intent of
'show me this panel' and 'everything stays where it is if I don't take
an action'

Release Notes:

- cmd-shift-j/b/r have been removed.  (preview only)
- cmd-j/b/r unconditionally show or hide their associated dock,
respecting zoom settings. (preview only)
- ctrl-` and cmd-shift-e now retain zoom state. (preview only)
- Fixed a bug where terminal dock tab would always be in the active
state (preview only)
- Fixed a bug where terminals would not always open in the terminal
panel
- Changed the look of zoomed panels to fill more of the screen (preview
only)
2023-05-30 16:39:06 -07:00
Mikayla Maki
0122cd61c5 fmt 2023-05-30 16:33:42 -07:00
Mikayla Maki
685e8d7007 Seperate pane and panel styling
co-authored-by: max <max@zed.dev>
2023-05-30 16:23:16 -07:00
Mikayla Maki
9c707eff27 Rework new terminal actions 2023-05-30 16:10:07 -07:00
Mikayla Maki
e80ab5f096 Fix focus bug with new docks
co-authored-by: max <max@zed.dev>
2023-05-30 15:40:30 -07:00
Mikayla Maki
5e4a9abd09 Fix bug in panel button dispatch 2023-05-30 15:17:42 -07:00
Mikayla Maki
5065804388 WIP: iron out bugs in interaction between toggle and toggle focus key bindings
co-authored-by: max <max@zed.dev>
2023-05-30 14:55:49 -07:00
Julia
8f0fc918e9 TextStyle::default() ask system for known existing font family (#2542)
Rather than assuming a specific family exists, try a set of specific
names and if they fail, just grab any old font that the system reports
as existing

Closes
https://linear.app/zed-industries/issue/Z-445/thread-main-panicked-at-called-resultunwrap-on-an-err-value-could-not

Release Notes:

* Fixed crash that could happen if system did not have a specific
fallback font
2023-05-30 16:25:07 -04:00
Mikayla Maki
9c9af5ed94 Remove focus side effects from toggle dock commands
co-authored-by: max <max@zed.dev>
2023-05-30 12:32:00 -07:00
Joseph Lyons
efcf78cbe9 Update pull_request_template.md 2023-05-30 15:15:20 -04:00
Joseph T. Lyons
e9ce85ebc0 Scrape email from feedback editor (#2543)
Add release note lines here:

N/A
2023-05-30 15:13:35 -04:00
Joseph Lyons
501f9ab2c6 Scrape email from feedback editor 2023-05-30 15:06:35 -04:00
Mikayla Maki
1a23fe91b4 WIP: Remove focus side effects from toggle dock commands
co-authored-by: max <max@zed.dev>
2023-05-30 12:00:15 -07:00
Mikayla Maki
84f98f13c4 Seperate open and zoom bits conceptually for new panels
co-authored-by: max <max@zed.dev>
2023-05-30 11:52:14 -07:00
Julia
31516b7863 TextStyle::default() ask system for known existing font family
Rather than assuming a specific family exists, try a set of specific
names and if they fail, just grab any old font that the system reports
as existing

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-05-30 14:46:27 -04:00
Sergey Onufrienko
51c7078f47 chore: remove license_text 2023-05-30 18:16:50 +02:00
Sergey Onufrienko
4568f80f2e feat: read from LICENSE file 2023-05-30 18:12:31 +02:00
Sergey Onufrienko
14efc18eca refactor: add license file and check it exists 2023-05-30 18:11:54 +02:00
Sergey Onufrienko
05d1dd6b11 refactor: move common to theme folder 2023-05-30 16:53:12 +02:00
Sergey Onufrienko
ac63043c27 chore: move similar themes to one folder 2023-05-30 16:52:28 +02:00
Sergey Onufrienko
0d62e76cd6 refactor: colorSchemes 2023-05-30 15:52:32 +02:00
Antonio Scandurra
69e8a166e4 Start on assistant::QuoteSelection 2023-05-30 15:25:53 +02:00
Antonio Scandurra
dc365472a6 Merge remote-tracking branch 'origin/main' into assistant-2 2023-05-30 15:11:06 +02:00
Antonio Scandurra
1fc9103b61 Remove methods taking &mut Workspace from Pane (#2540)
This pull request simplifies the `Pane` struct by replacing methods like
`Pane::add_item` that would previously take a `&mut Workspace` with
methods that take a `&mut self`. When access to the workspace is needed,
we now either emit an event from the `Pane` or directly move the method
to the `Workspace` struct.
2023-05-30 15:01:35 +02:00
Antonio Scandurra
bd494037bb Call ItemHandle::added_to_pane when an item is added to a pane 2023-05-30 14:46:41 +02:00
Sergey Onufrienko
2269c19169 chore: add LICENSE files 2023-05-30 14:06:19 +02:00
Nathan Sobo
8a851ad7f8 Fix annoying system prompt (#2538)
I was having fun, but it was too much. It makes the assistant too
annoying to be useful.

Release Notes:

Use `N/A` in this section if this item should be skipped in the release
notes.

Add release note lines here:

* Dialed back the system prompt on the assistant.
2023-05-29 22:05:40 -06:00
Nathan Sobo
063613bf4e Fix annoying system prompt
I was having fun, but it was too much. It makes the assistant too annoying to be useful.
2023-05-29 21:47:59 -06:00
Max Brunsfeld
ed0fa2404c Use settings JSON schema when editing local settings files 2023-05-29 14:31:54 -07:00
Max Brunsfeld
89446c7fd4 Start work on respecting project-specific settings 2023-05-29 14:25:49 -07:00
Antonio Scandurra
88eb2b2163 Move history navigation logic to Workspace
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-05-29 19:47:59 +02:00
Antonio Scandurra
6d3464fd1f Make Pane::add_item a proper Pane method
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-05-29 19:16:22 +02:00
Antonio Scandurra
890c42a75a Show time in assistant messages 2023-05-29 16:23:16 +02:00
Kirill Bulatov
edf8e276af Improves project search panel shortcut handling (#2536)
* ESC (project_search::ToggleFocus) toggles focus from include/exclude
fields to the editor

* Cmd+Shift+F (workspace::NewSearch) can be triggered from the editor,
and moves focus to the query editor

Release Notes:

* Improved project search panel shortcut handling, allowing more actions
to trigger from panel elements
2023-05-29 17:06:38 +03:00
Kirill Bulatov
d593377e45 Improves project search panel shortcut handling
* ESC (project_search::ToggleFocus) toggles focus from
  include/exclude fields to the editor

* Cmd+Shift+F (workspace::NewSearch) can be triggered from the editor,
and moves focus to the query editor
2023-05-29 16:58:06 +03:00
Antonio Scandurra
52e8bf2928 Show custom header for assistant messages 2023-05-29 15:57:55 +02:00
Sergey Onufrienko
cf75b090d3 feat: organize themes by folders with LICENSE 2023-05-29 15:45:13 +02:00
Kirill Bulatov
986b02e217 Update LSP to the newest version (#2535)
Current `lsp-types:0.91.1` crate lacks inlay hints' definitions. Crate's
changelog is not very descriptive, but it appears that `0.92.1` could be
used:
https://github.com/gluon-lang/lsp-types/blob/master/CHANGELOG.md#v0921-2022-03-21
The latest is crate version is `0.94.0` (2023-02-08), the PR updates Zed
to the latest version.


Notable changes:
* workspace symbols may arrive unresolved if the corresponding client
capability is enabled:
https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#partialResults
Zed has this capability disabled, forcing all symbols to arrive
synchronously (?).

Resolve capabilities are important for inlay hints too, but I've not
found any code in Zed for that outside tests, so I'd love to learn more
and implement the resolution for workspace symbols separately.

* since LSP `3.17` (current), watch file changes can use relative glob
patterns:
https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_didChangeWatchedFiles

That seemed just a straightforward extra `match` to use the same Ruse
`Glob` to handle the relative path one.

Release Notes:

N/A
2023-05-29 12:49:37 +03:00
Antonio Scandurra
404bebab63 Set markdown as the assistant's buffer languages 2023-05-29 11:34:26 +02:00
Kirill Bulatov
fdad1adaf6 Update LSP to the newest version 2023-05-28 21:55:08 +03:00
Max Brunsfeld
e4530471de Make worktree UpdatedEntries events fully describe all changes (#2533)
This PR makes the worktree's change events more useful in a few ways:

* The changes are now described by a cheaply clone-able collection, so
that they can be used in background tasks. Right now, I'm using a simple
Arc slice.
* The `UpdatedEntries` event now captures not only changes due to FS
changes, but also newly-loaded paths that are discovered during the
initial scan.
* The `UpdatedGitRepositories` event now includes repositories whose
work-dir changed but git dir didn't change. A boolean flag is included,
to indicate whether the git content changed.
* The `UpdatedEntries` and `UpdatedGitRepositories` events are now
*used* to compute the worktree's `UpdateWorktree` messages, used to sync
changes to guests. This unifies two closely-related code paths, and
makes the host more efficient when collaborating, because the
`UpdateWorktree` message computation used to require walking the entire
`entries` tree on every FS change.
2023-05-26 15:55:14 -07:00
Max Brunsfeld
1f42bfc1bd Include repositories with workdir changes in worktree UpdatedGitRepsositories event 2023-05-26 15:47:37 -07:00
Max Brunsfeld
2db57b5139 Adjust diagnostic transformation test to not wait for two buffer notifications 2023-05-26 15:47:37 -07:00
Max Brunsfeld
02b95ef320 Derive worktree update messages from existing change events 2023-05-26 15:47:37 -07:00
Kirill Bulatov
9cfe39f1aa Display external paths in history (#2534)
Fixes
https://linear.app/zed-industries/issue/Z-1793/file-finder-external-recent-paths-are-not-rendered-properly

Long paths get trimmed, but same do many of our other elements, such as
type definitions, so I think it's ok for now:

![image](https://github.com/zed-industries/zed/assets/2690773/b8b6588d-6d6b-42db-9085-c741a40b7adb)

Also, we seem to do a lot of odd diagnostics handling on every external
stdlib file opened:

![image](https://github.com/zed-industries/zed/assets/2690773/cd82c54e-7849-46fe-a01c-79bfc5402b7b)
Other external files also emit similar messages, but not that much.
@\mikayla-maki mentioned, that this was happening before, so can be
fixed separately.
The PR adds path printing to these logs.

Release Notes:

* Fixed external files not being displayed properly in the recently
opened list in the file finder panel
2023-05-27 01:27:24 +03:00
Kirill Bulatov
5fda9e934d Shorten full paths with ~ 2023-05-27 01:21:38 +03:00
Kirill Bulatov
3a3c1c5a5b Add a test
co-authored-by: Mikayla <mikayla@zed.dev>
2023-05-27 01:12:22 +03:00
Nathan Sobo
ffbfbe422b WIP: Not sure I actually want to rip this out 2023-05-26 16:11:58 -06:00
Nathan Sobo
3904971bd8 Hide assistant gutter 2023-05-26 15:38:03 -06:00
Nathan Sobo
8f6e67f440 Cancel assists on escape 2023-05-26 14:49:27 -06:00
Max Brunsfeld
6628c4df28 Store worktree changes in a sorted arc slice, not a HashMap
We don't need to look-up change types by an arbitrary key, but we
do need to iterate it. It would also be useful to be able to
cheaply clone the changes, to use them in a background task.
2023-05-26 09:24:15 -07:00
Max Brunsfeld
59bfd40679 Make stricter assertions about change events in random worktree test 2023-05-26 09:24:15 -07:00
Max Brunsfeld
f890eefdef Include paths loaded during initial scan in worktree UpdatedEntries event 2023-05-26 09:24:15 -07:00
Nathan Sobo
80080a43e4 Start on an assistant panel based on multi-buffers
Each message is represented as a multibuffer excerpt to allow for
fluid editing of the conversation transcript.

Co-Authored-By: Antonio Scandurra <antonio@zed.dev>
2023-05-26 10:09:55 -06:00
Kirill Bulatov
cf2bbfc85a Better display labels for external files 2023-05-26 18:18:32 +03:00
Kirill Bulatov
508533ebb7 Track abs paths in history 2023-05-26 17:31:35 +03:00
Kirill Bulatov
2fdc960704 Properly display labels for currently opened external files 2023-05-26 15:44:44 +03:00
Kirill Bulatov
b75c27da6f Simplify selected index handling 2023-05-26 14:15:58 +03:00
Kirill Bulatov
364631a155 Add absolute paths to historic elements 2023-05-26 14:03:44 +03:00
Kirill Bulatov
128c19875d Fix duplicate item creation in project panel (#2532)
Closes
https://linear.app/zed-industries/issue/Z-1760/creating-a-new-file-with-the-same-name-as-an-existing-file-overwrites

Now every rename/creation editor won't do anything if a duplicate name
is submitted.

Release Notes:

* Fixed a bug when created in project panel items with duplicate names
were overwriting the old files
2023-05-26 10:14:50 +03:00
Kirill Bulatov
5501dd741c Fix duplicate item creation in project panel 2023-05-26 02:07:25 +03:00
Julia
7900d2a20a Multibuffer edit perf (#2531)
This took so much longer than I wanted, so glad to finally be rid of
this

Release Notes:
* Improved performance when editing many git-tracked files in a
multi-buffer at the same time
2023-05-25 15:01:05 -04:00
Julia
5e39ba596e Clean up final remaining code paths calling old diff update method 2023-05-25 14:41:09 -04:00
Julia
f40c498491 Fix tests 2023-05-25 14:29:28 -04:00
Julia
8d662edb6c Remove concept of git diff refresh from Item trait 2023-05-25 14:29:28 -04:00
Julia
cede296b04 Project level git diff recalc handling
This avoids an issue where in a many-buffer multi-buffer, each modified
buffer could complete its recalc independently, causing a cascade of
repeated notifies

Now all recalcs started at the same time must complete before
 A: Starting another recalc pass
 B: The master notify occurring

Each buffer can still show its new diff if something else triggers it
to notify earlier, this is desirable and does not have the same negative
effects as the notify cascade as those re-layouts would need to happen
anyway

Co-Authored-By: Max Brunsfeld <max@zed.dev>
2023-05-25 14:10:27 -04:00
Max Brunsfeld
54421b11f3 wip 2023-05-25 14:10:27 -04:00
Kirill Bulatov
ae3bdd755e Support OnTypeFormatting LSP request (#2517)
Supports
https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_onTypeFormatting

rust-analyzer uses this feature to add matching brackets semantically,
e.g.
before:
![Screenshot 2023-05-23 at 17 46
42](https://github.com/zed-industries/zed/assets/2690773/020e8448-23e6-4a38-8dbb-c9edf18062f7)

after:
![Screenshot 2023-05-23 at 17 46
49](https://github.com/zed-industries/zed/assets/2690773/4d140af3-aca6-451d-ac61-e2a9bb31caea)

`use_on_type_format` settings entry was added, enabled by default, to
disable the new feature.

Release Notes:

* Support `OnTypeFormatting` LSP protocol feature, allowing
rust-analyzer to add matching brackets
2023-05-25 18:35:47 +03:00
Kirill Bulatov
739d5ca373 Have proper undo for both client and host 2023-05-25 18:29:57 +03:00
Antonio Scandurra
24098c561d Prevent bottom dock from becoming too large or too small (#2529)
Fixes
https://linear.app/zed-industries/issue/Z-1834/bottom-dock-can-be-dragged-down-far-enough-such-that-the-tab-bar-is

Release Notes:

- Improved the resize experience for the bottom dock, preventing it from
becoming too large or too small. (preview-only)
2023-05-25 15:44:23 +02:00
Antonio Scandurra
4185a178e7 Prevent bottom dock from becoming too large or too small 2023-05-25 15:29:59 +02:00
Antonio Scandurra
f549ada54f Restore the ability to search terminals in the terminal panel (#2528)
Fixes
https://linear.app/zed-industries/issue/Z-1812/find-in-terminal-panel-is-broken

![CleanShot 2023-05-25 at 15 18
01@2x](https://github.com/zed-industries/zed/assets/482957/ddb33b61-d253-4de1-961f-14b24aaa3e46)

This also removes navigation controls from the terminal panel, given
that terminals don't make use of that feature anyway. When the toolbar
is empty, we'll avoid showing it altogether.

![CleanShot 2023-05-25 at 15 17
26@2x](https://github.com/zed-industries/zed/assets/482957/52419f2c-bca0-494a-a9b3-88e183b4c12f)


Release Notes:

- Fixed a regression that was preventing the terminal panel from being
searched.
2023-05-25 15:28:32 +02:00
Antonio Scandurra
aa8c0475b1 Don't show navigation controls for terminal panel 2023-05-25 15:15:20 +02:00
Kirill Bulatov
e2ff829f98 Use Transaction instead of ProjectTransaction
Co-Authored-By: Antonio Scandurra <antonio@zed.dev>
2023-05-25 16:12:14 +03:00
Antonio Scandurra
414b97adce Add search bar to terminal panel 2023-05-25 14:46:10 +02:00
Antonio Scandurra
dba7ec4a35 Prevent pane from being erroneously zoomed when toggling the outline pane (#2527)
Fixes
https://linear.app/zed-industries/issue/Z-1818/toggling-the-outline-pane-causes-the-pane-to-zoom

Add release note lines here:

- Fixed a bug that could cause panes to be erroneously zoomed when
toggling modals. (preview-only)
2023-05-25 14:10:08 +02:00
Antonio Scandurra
c2f3ac24a4 Prevent zoom from being restored when focusing a modal 2023-05-25 12:11:11 +02:00
Antonio Scandurra
7e1ea8f274 Remove stray dbg! 2023-05-25 12:04:41 +02:00
Kirill Bulatov
aa58d0fd77 Do not send edits over the wire 2023-05-25 12:55:44 +03:00
Kirill Bulatov
f812151840 Add integration tests 2023-05-25 09:45:50 +03:00
Kirill Bulatov
eca6d2b597 Process remote format typing also 2023-05-25 09:45:50 +03:00
Kirill Bulatov
58a56bdda2 Always use server formatting settings 2023-05-25 09:45:50 +03:00
Kirill Bulatov
d1f4b60fa1 Allow to disable the new feature 2023-05-25 09:45:50 +03:00
Kirill Bulatov
b9dabb165e Use formatting options 2023-05-25 09:45:50 +03:00
Kirill Bulatov
3327e8a6dd Support remote sessions 2023-05-25 09:45:50 +03:00
Kirill Bulatov
f6d7b3d2e8 Send and handle OnTypeFormatting LSP request 2023-05-25 09:45:50 +03:00
Nate Butler
793486b2e8 Update Diff Modified Color (#2526)
For some reason the yellow I used for the modified color in light themes
was really light
.
Release Notes:

* Improved the contrast of diff modified color in the editor in light
themes.
2023-05-24 23:50:37 -04:00
Joseph Lyons
3f79b0c7cc Don't hardcode dates into script 2023-05-24 17:39:02 -04:00
Joseph Lyons
cc3186cb4a Add script to print all prs merged between a previous date and now 2023-05-24 16:58:56 -04:00
Nathan Sobo
0b7d095c0a Introduce the ability to talk to GPT-4 within buffers on preview and dev (#2524)
This adds basic ability to interact with OpenAI inside a buffer.

Release Notes:

* If`OPENAI_API_KEY` is defined in your environment, you can now run the
`ai: assist` command to pass the text of your current buffer to GPT-4.
If you're editing a file with a `.zmd` extension, you can also invoke
the model with `cmd-enter.` (preview-only)
2023-05-24 12:00:43 -06:00
Joseph Lyons
37d35db3d7 v0.89.x dev 2023-05-24 13:22:00 -04:00
Nathan Sobo
26051de160 Clean up 2023-05-24 11:08:00 -06:00
Max Brunsfeld
a1a66b8da4 Make toggle dock actions appear in the command palette (#2525)
This makes the `Toggle{Left,Right,Bottom}Dock` actions deserializable
from empty JSON, so that they can be constructed for the command
palette. It also fixes a bug in GPUI's `available_actions` method, in
which we'd include key bindings for actions of the same type but
different values.

Note that, for now, the command palette will perform the *focusing*
version of the actions. I'm not totally sure this is the right behavior,
but it seems more useful to me.

Release Notes:

N/A
2023-05-24 10:07:34 -07:00
Nathan Sobo
747322a02d Merge remote-tracking branch 'origin/main' into zmd 2023-05-24 11:04:07 -06:00
Max Brunsfeld
66b3be8687 Make toggle dock actions appear in the command palette 2023-05-24 09:58:41 -07:00
Nathan Sobo
d495c1b804 Only run AI agent on dev and preview 2023-05-24 10:58:33 -06:00
Antonio Scandurra
35f9996a4f WIP 2023-05-24 18:45:47 +02:00
Nathan Sobo
ba79a8ba94 WIP 2023-05-24 10:26:14 -06:00
Nathan Sobo
50e1e17d09 WIP 2023-05-24 09:53:08 -06:00
Nathan Sobo
051c7566cc WIP 2023-05-24 09:38:37 -06:00
Nathan Sobo
8abaf66602 WIP 2023-05-24 09:21:50 -06:00
Nathan Sobo
6368ade1da WIP 2023-05-24 09:12:16 -06:00
Antonio Scandurra
8832248bb9 Polish panel experience (#2523)
In this pull request we improved key bindings (as described below) and
added tooltips.

Add these release notes to the panels release notes:

- The left, right and bottom dock can be toggled and focused at the same
time respectively via `cmd-b`, `cmd-r` and `cmd-j`. Holding `shift` will
toggle them without changing the focus.
2023-05-24 16:23:24 +02:00
Antonio Scandurra
f67a22828b Show keystroke in panel tooltips
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-05-24 16:13:58 +02:00
Antonio Scandurra
0cf1632d39 Only focus new terminal if the panel contains focus
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-05-24 16:04:02 +02:00
Antonio Scandurra
50cfe5eec3 Use cmd-b/cmd-r/cmd-j to toggle left/right/bottom dock and focus when opening
Also, bind the same keys with shift to toggle the dock without focusing.

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-05-24 15:59:06 +02:00
Antonio Scandurra
7534c4e670 Fix opening excerpt when the cursor is at the end of a multi-buffer (#2522)
Fixes
https://linear.app/zed-industries/issue/Z-987/option-enter-doesnt-open-buffer-from-multibuffer-if-on-the-last-line

This was caused by seeking with a right bias, which in the case of the
last excerpt in the buffer would seek past the end of the buffer. This
commit changes the behavior to move the cursor back to the previous
excerpt if we overshoot, to ensure we always land on an excerpt.

Release Notes:

- Fixed a bug that prevented opening excerpts when the cursor was at the
end of a multi-buffer.
2023-05-24 15:08:18 +02:00
Antonio Scandurra
2a8e0824a6 Fix opening excerpt when the cursor is at the end of a multi-buffer
This was caused by seeking with a right bias, which in the case of the
last excerpt in the buffer would seek past the end of the buffer.  This
commit changes the behavior to move the cursor back to the previous excerpt
if we overshoot, to ensure we always land on an excerpt.
2023-05-24 15:02:57 +02:00
Antonio Scandurra
8f49552121 Show maximize/minimize icon for panes and terminal panel (#2521)
Closes
https://linear.app/zed-industries/issue/Z-1790/show-zoomunzoom-icons-for-panes-and-terminal-panel

![CleanShot 2023-05-24 at 11 07
04@2x](https://github.com/zed-industries/zed/assets/482957/b27d002c-6443-440b-88d0-cfa06d902477)
2023-05-24 11:44:39 +02:00
Antonio Scandurra
e5fd953b4f Provide tooltips for pane buttons 2023-05-24 11:18:30 +02:00
Antonio Scandurra
1a353ad25d Show maximize/minimize icon for panes and terminal panel 2023-05-24 11:00:12 +02:00
Antonio Scandurra
9575ffc1e3 Prevent empty panes from being zoomed (#2520)
Closes
https://linear.app/zed-industries/issue/Z-1789/empty-panes-should-not-be-zoomable
2023-05-24 10:51:52 +02:00
Antonio Scandurra
168ff99927 Prevent empty panes from being zoomed 2023-05-24 10:34:56 +02:00
Joseph T. Lyons
e463e3b2e8 Update terminal panel tooltip (#2519)
The actions in Zed now call this the Terminal Panel and the crate is
called that as well, so I think the tooltip should be updated to use
that same name.

Release Notes:

N/A
2023-05-23 22:33:08 -04:00
Joseph Lyons
e6857866d4 Update terminal panel tooltip 2023-05-23 22:23:38 -04:00
Nathan Sobo
637ed79603 WIP 2023-05-23 18:16:49 -06:00
Nathan Sobo
65fc50745b WIP 2023-05-23 18:04:33 -06:00
Nathan Sobo
d934da1905 WIP 2023-05-23 15:25:34 -06:00
Joseph T. Lyons
ba7659fa77 add toggle right and bottom dock actions (#2518)
Release Notes:

N/A
2023-05-23 13:59:45 -04:00
Joseph Lyons
2457c55fe7 add toggle right and bottom dock actions 2023-05-23 13:53:56 -04:00
Joseph T. Lyons
aac83ceea7 Add architecture to clickhouse event request body (#2514)
See title

Release Notes:

N/A
2023-05-23 11:11:17 -04:00
Nathan Sobo
7be41e19f7 WIP 2023-05-23 08:52:21 -06:00
Antonio Scandurra
ad866c5b21 Update LiveKit client SDK to 1.0.12 (#2516)
Fixes
https://linear.app/zed-industries/issue/Z-1756/screen-sharing-is-slow-and-sometimes-doesnt-work-at-all

Release Notes:

* Fixed some cases where screen-sharing would have low bitrate or
completely fail to start.
2023-05-23 16:19:12 +02:00
Antonio Scandurra
774530dd05 Update LiveKit client SDK to 1.0.12
Co-Authored-By: Julia Risley <julia@zed.dev>
2023-05-23 15:32:55 +02:00
Nathan Sobo
073967c80b WIP 2023-05-23 01:56:28 -06:00
Nathan Sobo
5362e7d346 WIP 2023-05-23 01:52:37 -06:00
Nathan Sobo
394e87d17c WIP 2023-05-23 01:23:01 -06:00
Antonio Scandurra
dcc4dd7eb8 Allow multiple docks to be visible at once and only let them contain panels (#2450)
Closes
https://linear.app/zed-industries/issue/Z-1188/allow-docks-to-be-visible-on-left-bottom-right

To prepare the way for the AI assistant, we want to revamp the way docks
and panels work. In this PR, we replace the dock as it currently exists
with 3 independent docks at the left, bottom, and right edge of the
workspace. To replace the "expanded" dock, we will introduce a zoom
feature that works on any pane and some panels. When showing a dock with
an active panel that is zoomed, it will automatically appear zoomed.
This replicates the expanded dock experience. If you unzoom, the panel
will still be visible.

### Panels only

We want to simplify these docks to only allow them to contain *panels*.
By doing this, we don't need to give each dock a tab bar, minimizing
clutter in the workspace. Each panel will remember its size, and the
dock will adjust to that size when the panel is toggled. This will allow
each panel to take up the amount of space that makes sense for its use
case.

There will be 3 kinds of panels:

* Project panel: This currently lives in the left "sidebar", which this
PR renames to the left dock. Users will be able to left click the icon
and switch the project panel to the right dock.
* Terminal panel: This is the primary user of the previous dock. Now all
terminals will live in a terminal panel, which can be docked at the
left, bottom, or right. This dock will contain tabs, but only for
terminals. Terminals will still be able to be dragged into the
workspace, but by default, the first new terminal will live in the
terminal panel which lives in one of the 3 docks.
* Feedback: Instead of opening a pane, let's explore making this a panel
instead.

### Status bar icons

A nice feature of this change is that it simplifies our status bar by
eliminating the dock button. We will only show icons for specific
panels. Panel in the left dock will appear at the left side of the
status bar. Panels in the right dock on the far right, panels in the
bottom will appear on the right, to the immediate left of the buttons
for right panels. Left clicking any panel button will allow it to be
redocked to any valid location for that panel.

### Paving the way for the AI assistant

I'm envisioning the assistant living in the far right panel by default.
So in the stock config, you'll have the project panel on the left,
terminal on the bottom, assistant on the right, and code in the middle.
Let's fucking go!
2023-05-23 09:09:43 +02:00
Antonio Scandurra
19b817e48a Improve styling of bottom and right docks 2023-05-23 09:02:45 +02:00
Antonio Scandurra
208ff2fba7 Merge remote-tracking branch 'origin/main' into panels 2023-05-23 08:24:28 +02:00
Nathan Sobo
0de5a444d3 WIP 2023-05-23 00:22:21 -06:00
Nathan Sobo
234dbc3ca9 WIP 2023-05-23 00:16:44 -06:00
Nathan Sobo
c4d88bc529 WIP 2023-05-22 23:43:02 -06:00
Nathan Sobo
30de64845f WIP 2023-05-22 23:11:27 -06:00
Mikayla Maki
a5b5de5651 Add initial project panel settings (#2515)
This PR adds project panel settings for disabling git status.

Release Notes:

- Adds `project_panel: { git_status: bool }` to the settings, for
controlling whether git status information appears.
2023-05-22 20:43:24 -07:00
Mikayla Maki
a37b1b6a45 Add other setting init 2023-05-22 20:38:30 -07:00
Mikayla Maki
b19a712799 Add init_settings to tests 2023-05-22 20:33:18 -07:00
Mikayla Maki
da96802bf0 fmt 2023-05-22 20:25:27 -07:00
Mikayla Maki
7be8dead07 Add initial project panel settings 2023-05-22 20:23:07 -07:00
Nathan Sobo
7e6cccfa3d WIP: Stream in completions
Drop dependency on tokio introduced by async-openai and do it ourselves.

The approach I'm taking of replacing instead of appending is causing issues. Need to just append.
2023-05-22 20:28:22 -06:00
Joseph Lyons
049b72e3ac Add architecture to clickhouse event request body 2023-05-22 21:45:58 -04:00
Mikayla Maki
54c04a6618 Fix race condition in diff base initializaiton (#2513)
fixes
https://linear.app/zed-industries/issue/Z-1657/diff-markers-in-gutter-do-not-show-up-until-after-first-save

Release Notes:

- Fixes a race condition on buffer initialization that would cause git
diffs to not load.
2023-05-22 18:43:59 -07:00
Mikayla Maki
9307719a4b Add run until parked for test 2023-05-22 18:40:12 -07:00
Mikayla Maki
51d94f532b Load diff base for buffers that are opening but not yet opened when repositories are discovered 2023-05-22 18:26:33 -07:00
Mikayla Maki
fc82489128 Update scrollbar styling 2023-05-22 16:23:36 -07:00
Mikayla Maki
1b5be04f76 Fixed contrast in project panel and scrollbar (#2512)
Redo of https://github.com/zed-industries/zed/pull/2504 

This makes the different git locations individually style-able

Release Notes:

- Improve git contrast (preview only)
2023-05-22 15:59:02 -07:00
Mikayla Maki
e4454f1e7f fmt 2023-05-22 15:53:29 -07:00
Mikayla Maki
5c31c84c90 Fixed contrast in project panel and scrollbar
co-authored-by: nate <nate@zed.dev>
2023-05-22 15:51:31 -07:00
Mikayla Maki
fb2f17e5c9 Revert "Touch up git in project panel and scroll bar styling" (#2511)
Reverts zed-industries/zed#2504, due to accidentally pulling in main
2023-05-22 15:36:15 -07:00
Mikayla Maki
2d1c4a1971 Revert "Touch up git in project panel and scroll bar styling" 2023-05-22 15:31:14 -07:00
Mikayla Maki
dcf4791182 WIP: Touch up git in project panel and scroll bar styling (#2504)
This makes the different git locations individually style-able

Release Notes:

* Improve git contrast (preview only)
2023-05-22 15:11:10 -07:00
Mikayla Maki
9bec74f1d4 fmt 2023-05-22 15:04:51 -07:00
Mikayla Maki
9a47415cb5 Merge branch 'main' into git-project-panel-and-scrollbar-styling 2023-05-22 15:02:28 -07:00
Mikayla Maki
5dfb0e3691 remove border from thumb 2023-05-22 14:56:55 -07:00
Nate Butler
11eb9b17c9 Update project panel & scroll bar git colors 2023-05-22 17:47:52 -04:00
Mikayla Maki
4c9d24da7c Only fire update diff base when the dot repo is scanned (#2510)
This PR fixes a bug in the firing of the UpdatedRepositories event which
caused it to flood collaboration with new messages on every file save.

Release Notes:

* Fixed a bug in repository detection that caused it to fire
over-eagerly (preview only)
2023-05-22 14:11:19 -07:00
Mikayla Maki
96224fa7e8 Only fire update diff base when the dot repo is scanned 2023-05-22 13:57:07 -07:00
Max Brunsfeld
0e332b5f94 Clear old diagnostics when restarting a language server (#2509)
Previously, restarting a language server did not clear out the
diagnostics that were published by that server. Those diagnostics would
stick around forever.

Release Notes:

* Fixed a bug where restarting a language server would sometimes leave
buffers with stale diagnostics associated with that server.
2023-05-22 13:06:32 -07:00
Max Brunsfeld
7689cdf3f9 Clear old diagnostics when restarting a language server 2023-05-22 12:53:22 -07:00
Mikayla Maki
1d07cff765 Adjust scrollbar settings to be expandable (#2507)
Note that this PR cannot be cherrypicked into preview, as it relies on
the new settings infrastructure.

Switches settings from `show_scrollbars: "auto"` to `scrollbar: {show:
"auto", git_diffs: true}`.

fixes
https://linear.app/zed-industries/issue/Z-1650/scroll-bar-feature-settings

Release Notes:

- Changed scrollbar settings from `show_scrollbars: "auto"` to
`scrollbar: {show: "auto", git_diffs: true}`. (preview only)
2023-05-22 12:51:21 -07:00
Mikayla Maki
dcb987ba9d Diagnostics pane was not focusable with the mouse (#2506)
fixes
https://linear.app/zed-industries/issue/Z-1432/cant-cmd-w-an-empty-diagnostics-in-a-split-pane

Release Notes:

* Fixed a bug where the diagnostics pane could not be focused or closed
in certain circumstances.
2023-05-22 12:44:39 -07:00
Mikayla Maki
cfdf9198da Switch back to 2023-05-22 12:16:47 -07:00
Mikayla Maki
687ccd4c6f fmt 2023-05-22 12:13:23 -07:00
Mikayla Maki
366f13bb5c Adjust scrollbar settings to be expandable 2023-05-22 12:12:56 -07:00
Joseph Lyons
49566e5677 Update pull_request_template.md 2023-05-22 15:00:57 -04:00
Joseph Lyons
9c32b774aa Add example of changelog line in PR template 2023-05-22 14:53:07 -04:00
Mikayla Maki
bafc1d922e fmt 2023-05-22 11:46:52 -07:00
Mikayla Maki
e59c8e9d61 Fix diagnostic pane not being closeable 2023-05-22 11:44:23 -07:00
Max Brunsfeld
e129ed2d91 Fix spurious setting error log messages (#2498)
Fixes a bug introduced in
https://github.com/zed-industries/zed/pull/2448, where error messages
would be logged if the user config didn't specify certain fields like
`journal` or `telemetry`.
2023-05-22 11:44:21 -07:00
Joseph Lyons
04355215b2 Indent all release note lines for each PR 2023-05-22 14:44:05 -04:00
Julia
a69144911f Add paragraph based vertical movements (#2502)
Very selfish patch I worked on yesterday, I kept saying I wanted these
and finally decided to just add them. Feedback on the keybindings
welcome

Release Notes:

* Added `MoveToStartOfParagraph` and `MoveToEndOfParagraph` movements
for paragraph based vertical navigation
2023-05-22 14:31:34 -04:00
Mikayla Maki
85266131cf Fix perf problem with scrollbars in large multibuffers (#2505)
Remove scrollbars from multibuffers

Release Notes:

* Removes git scrollbar highlights from multibuffers (preview only)
2023-05-22 11:11:27 -07:00
Mikayla Maki
6f4b6eec5b Diagnostics pane was not focusable with the mouse 2023-05-22 11:10:13 -07:00
Mikayla Maki
6264b24788 fmt 2023-05-22 11:04:36 -07:00
Mikayla Maki
8669dcdc81 Make scrollbar content detection cheaper
Remove scrollbars from multibuffers

co-authored-by: max <max@zed.dev>
2023-05-22 10:55:44 -07:00
Antonio Scandurra
f22067b236 Remove unused imports 2023-05-22 19:38:57 +02:00
Antonio Scandurra
33f6c56b14 Fix more warnings 2023-05-22 19:36:01 +02:00
Antonio Scandurra
6f39d49b18 Fix warnings 2023-05-22 19:33:11 +02:00
Antonio Scandurra
048498e39b Test zooming panels 2023-05-22 19:30:31 +02:00
Mikayla Maki
ef81813d56 Wire in conflict styling 2023-05-22 10:10:47 -07:00
Mikayla Maki
a355b4c135 Add conflict styles to project panel 2023-05-22 10:09:55 -07:00
Mikayla Maki
af73c1af06 Add seperate scrollbar styling 2023-05-22 10:03:02 -07:00
Antonio Scandurra
3ca95678f1 Avoid leaking docks when adding panels 2023-05-22 18:57:14 +02:00
Kirill Bulatov
9e46b17149 Do not refocus project search query on ESC press (#2494)
Closes
https://linear.app/zed-industries/issue/Z-1471/escape-should-not-move-focus-to-project-search

Makes ESC more predictable as a shortcut that cancels/roll backs the
state in project search panel.

Release Notes:

* Fixes ESC making focus to jump in project search panel
2023-05-22 19:56:10 +03:00
Mikayla Maki
2200a22c07 Wire project panel themes into rust 2023-05-22 09:55:59 -07:00
Antonio Scandurra
75a0742c90 Uncomment persistence test 2023-05-22 18:32:28 +02:00
Antonio Scandurra
5b7e852903 Await closing of items in tests 2023-05-22 18:28:30 +02:00
Antonio Scandurra
deaf60005c Fix more tests 2023-05-22 18:21:12 +02:00
Nate Butler
b80cb73745 Add git to project panel in theme, use different values for dark and light 2023-05-22 12:09:54 -04:00
Antonio Scandurra
4aa2858b2b Transfer focus to root view only if previously-focused view was dropped 2023-05-22 18:05:08 +02:00
Max Brunsfeld
21ada545b0 Remove assertions about behavior on invalid settings file 2023-05-22 08:48:37 -07:00
Julia
44903bc193 Add test for paragraph vertical movements 2023-05-22 11:48:07 -04:00
Antonio Scandurra
185a624b99 Fix some project panel tests 2023-05-22 16:18:53 +02:00
Antonio Scandurra
10e947cb5f Persist project and terminal panel sizes 2023-05-22 15:55:44 +02:00
Julia
7190840081 Add paragraph based vertical movements 2023-05-22 09:12:36 -04:00
Antonio Scandurra
146809eef0 Merge branch 'main' into panels 2023-05-22 14:10:17 +02:00
Antonio Scandurra
2098ac2c77 Fix formatting for keymap.json 2023-05-22 10:41:48 +02:00
Joseph Lyons
cb1b64e51b Fix up formatting for get preview channel changes script 2023-05-22 00:02:19 -04:00
Joseph T. Lyons
f2a74017df Fix get preview channel changes script (#2501)
Fixes the text we match on to get PR commits and also prints the release
notes of each PR

Release Notes:

* Skip
2023-05-21 23:47:07 -04:00
Joseph Lyons
c701901c7b Fix get preview channel changes script 2023-05-21 23:40:07 -04:00
Nathan Sobo
d5cf595761 Enable "test-support" feature on editor dependencies for tests (#2499)
Enable the `"test-support"` feature whenever we depend on the `editor`
crate in tests.

If we don't do this, we end up depending on `livekit_client` without
enabling `"test-support"`, and this causes us to link against
`WebRTC.framework` in its build script, which causes a dynamic linker
error when starting the tests.

Fixes
https://linear.app/zed-industries/issue/Z-1646/cant-run-package-tests-for-activity-monitor-and-auto-update

Release Notes:

* No notes
2023-05-20 10:19:59 -06:00
Nathan Sobo
986eafd84e Enable test-support on editor in tests 2023-05-20 09:54:38 -06:00
Nathan Sobo
912fd23006 Parse markdown into an OpenAI chat completion request 2023-05-20 09:43:12 -06:00
Mikayla Maki
35a9b90528 Fix bugs in git implementation (#2495)
fixes
https://linear.app/zed-industries/issue/Z-1593/clean-up-git-integration

- Fixes calculation of git offsets in the scrollbar:

fixes
https://linear.app/zed-industries/issue/Z-1608/fix-scrollbar-diffs-sliding-out-of-sync-with-gutter-diffs-in

fixes
https://linear.app/zed-industries/issue/Z-1629/project-search-panel-has-git-marks-on-the-scrollbar-misaligned

fixes
https://linear.app/zed-industries/issue/Z-1625/soft-wrap-affects-diff-locations-in-scrollbar

- Improves the performance of scrollbar hunks:

fixes
https://linear.app/zed-industries/issue/Z-1640/double-check-performance-of-scrollbar-hunks

- Fixes a long standing bug with how git gutters interact with soft
wraps:

fixes
https://linear.app/zed-industries/issue/Z-1442/make-hunks-grow-to-the-end-of-softwraps-when-ending-on-a-softwrapped

- Allows work directories to be renamed

fixes
https://linear.app/zed-industries/issue/Z-1577/fix-stale-git-repositories-when-directory-is-renamed

Release Notes:

* Fix the offsets of the git diffs in the scrollbar when there are
folds, wraps, or excerpts (preview only)
* Allow the work directory of a repository to be renamed (preview only)
* Extend git gutter to cover the entirety of a wrapped line
(https://github.com/zed-industries/community/issues/937)
2023-05-19 18:15:11 -07:00
Mikayla Maki
bbb68c523c Refactored apart the forward and the backwards iterator for diff hunks 2023-05-19 18:09:47 -07:00
Max Brunsfeld
e32233c826 Fix spurious setting error logs on non-existent setting keys 2023-05-19 17:15:05 -07:00
Mikayla Maki
43e301eeef refine batched anchor conversions
co-authored-by: max <max@zed.dev>
2023-05-19 16:52:57 -07:00
Max Brunsfeld
fb11c3e4bf Remove stray prints 2023-05-19 16:52:30 -07:00
Mikayla Maki
1474429271 fmt 2023-05-19 16:32:07 -07:00
Mikayla Maki
c795c9b844 Rearrange git tests in worktree
Add support for renaming work directories
2023-05-19 16:30:00 -07:00
Mikayla Maki
623a177fe6 Fix bug where git diff hunks would not extend through a soft wrap 2023-05-19 16:23:45 -07:00
Mikayla Maki
560160b100 Batch anchor conversions in git hunk iterator 2023-05-19 16:23:45 -07:00
Mikayla Maki
2a41a32aac Calculate y offsets correctly 2023-05-19 16:23:45 -07:00
Max Brunsfeld
d7d29472c2 Remove expensive-to-clone fields from worktree's LocalSnapshot (#2497)
This fixes performance problems that @nathansobo and I have seen in some
cases, when a large number of files changed on disk. A lot of time was
being spent in `worktree::LocalSnapshot::clone`. I think this may have
been because of needing to clone the `removed_entry_ids` map. This
structure is only really used when *mutating* the `LocalSnapshot` in the
background scanner, so I moved it off of the snapshots.
2023-05-19 16:20:19 -07:00
Mikayla Maki
06a89d9104 Add guards to other pane index removals (#2496)
These should be impossible for users to reach, but why leave it to
chance?

Release Notes:

* N/A
2023-05-19 15:21:00 -07:00
Max Brunsfeld
7ae642b9b8 Avoid storing removed_entry_ids on the LocalSnapshot 2023-05-19 15:16:04 -07:00
Mikayla Maki
c193b0b8fc Add guards to other pane index removals 2023-05-19 15:10:44 -07:00
Max Brunsfeld
32c7157906 🎨 Make worktree repositories more consistent 2023-05-19 15:04:37 -07:00
Max Brunsfeld
6359333749 Don't store next_entry_id on worktree's local snapshot 2023-05-19 13:49:24 -07:00
Kirill Bulatov
065f71d671 Do not refocus project search query on ESC press 2023-05-19 22:06:47 +03:00
Kirill Bulatov
7d1833b759 When the file is deleted via project panel, close it in editors (#2490)
Deals with https://github.com/zed-industries/community/issues/179 by
sending a message about it, to asynchronously apply on all workspaces.

Release Notes:

* Fixes a bug when files, deleted in the project panel were left open in
the editor
2023-05-19 21:51:37 +03:00
Max Brunsfeld
844b8d9e1e Remove unnescessary double lookup in repo for (#2492)
Release Notes:

* Optimize repository queries (preview only)
2023-05-19 11:47:05 -07:00
Kirill Bulatov
2c8fffc4f8 Use better name for the method that closes deleted buffers
co-authored-by: Max <max@zed.dev>
2023-05-19 21:47:00 +03:00
Max Brunsfeld
01a3e49946 Optimize retrieving repos for entries when rendering the project panel (#2493)
This fixes slowness in rendering the project panel due to retrieving the
repository for a given entry.

Release Notes:

* Fixed a lag that would occur when lots of files changed on disk while
the project panel was open (preview only).
2023-05-19 11:46:32 -07:00
Mikayla Maki
9f157bdb67 Remove unescessary methods 2023-05-19 11:30:10 -07:00
Max Brunsfeld
729a93db6b Optimize retrieving repos for entries when rendering the project panel
Co-authored-by: Mikayla <mikayla@zed.dev>
2023-05-19 11:29:02 -07:00
Mikayla Maki
6792788216 Remove unnescessary double lookup 2023-05-19 11:08:58 -07:00
Antonio Scandurra
5a8fb18c20 Show workspace only after initializing it 2023-05-19 19:35:00 +02:00
Antonio Scandurra
5ff49bde31 Serialize and deserialize TerminalPanel 2023-05-19 19:26:32 +02:00
Max Brunsfeld
d480555ec9 Fix performance problems in reporting changed FS paths to language servers (#2491)
Fixes
https://linear.app/zed-industries/issue/Z-1611/main-thread-hangs-while-sending-filesystem-change-events-to-lsp

Release Notes:

* Fixed a lag that would sometime occur when large numbers of files
changed on disk, due to reporting the changed files to language servers.
2023-05-19 09:42:55 -07:00
Max Brunsfeld
847d1e73a3 Replace remaining usages of glob crate with globset 2023-05-19 09:36:46 -07:00
Max Brunsfeld
459cc9c959 Optimize matching of multiple file-watch globs using the globset crate 2023-05-19 09:13:34 -07:00
Kirill Bulatov
583b15badc When the file is deleted via project panel, close it in editors 2023-05-19 18:52:30 +03:00
Antonio Scandurra
e49281699c Add new terminal when the terminal panel is activated, and not on focus 2023-05-19 16:07:47 +02:00
Antonio Scandurra
924ec961ff Toggle project panel when opening new workspace in a dock-agnostic way 2023-05-19 15:27:18 +02:00
Antonio Scandurra
3d6b728364 Activate the correct panel when deserializing workspace 2023-05-19 14:18:11 +02:00
Kirill Bulatov
3984cc6d39 Properly handle WorktreeId 2023-05-19 12:37:36 +03:00
Max Brunsfeld
4bda5c4d69 Optimize LSP watched file reporting in 2 simple ways
* Convert globs to relative paths in advance. This avoids needing to convert
  every changed path to an absolute path before performing glob matching.
* Avoid duplicate reporting for language servers with multiple worktrees.
2023-05-18 17:08:51 -07:00
Max Brunsfeld
34b0d6200f collab 0.12.4 2023-05-18 14:58:57 -07:00
Max Brunsfeld
ffe35d171c Avoid unnecessary code action requests when applying leader updates t… (#2489)
We noticed a huge amount of code actions requests being issued by
followers when applying leader updates. It was caused by a call to
`MultiBuffer::remove_excerpts` with an empty list of excerpts to remove.
This PR fixes that by avoiding emitting spurious events when multibuffer
excerpt manipulation methods are called with empty lists.
2023-05-18 14:52:21 -07:00
Max Brunsfeld
58f704abcb Avoid unnecessary code action requests when applying leader updates to an editor 2023-05-18 14:46:23 -07:00
Mikayla Maki
87e206c451 Fixed an imprecise join in rejoin room (#2488)
This fixed a pre-existing bug in rejoin room that got expanded to
stalling collab with the new git status feature.

Release Notes:

* N/A
2023-05-18 14:42:59 -07:00
Mikayla Maki
99fcf23798 fmt 2023-05-18 14:31:08 -07:00
Mikayla Maki
95a7d69bce Fixed an imprecise join in rejoin room 2023-05-18 14:29:18 -07:00
Max Brunsfeld
2883d6f1ef Improve collab logging (#2487)
This adds some logging to the collab server, to help us identify the
source of the collaboration latency we're seeing in the 0.87 preview
version of zed.
2023-05-18 14:17:17 -07:00
Max Brunsfeld
2a11a89827 collab 0.12.3 2023-05-18 12:27:08 -07:00
Joseph Lyons
55b241a4f6 collab 0.12.2 2023-05-18 12:27:01 -07:00
Max Brunsfeld
c71b59b248 Log how long it takes to handle each RPC message 2023-05-18 12:24:07 -07:00
Kirill Bulatov
923be1892d Maintain recently opened files history (#2467)
Part of https://github.com/zed-industries/community/issues/351 and
https://github.com/zed-industries/community/issues/1125

Adds navigation history to the search file dialogue:


![image](https://github.com/zed-industries/zed/assets/2690773/01a1d3ea-1328-425a-ac95-2ebdbfcc07ab)

The history is only shown on empty query and gets replaced by the query
results after an input.
Currently, history gets lost between the project restarts.

Release Notes:

* Added a recently opened file list to search file dialogue
2023-05-18 22:06:25 +03:00
Kirill Bulatov
2ec994dfcd Add a unit test 2023-05-18 21:53:42 +03:00
Kirill Bulatov
201d513c50 Show navigation history in the file finder modal
co-authored-by: Max <max@zed.dev>
2023-05-18 21:50:38 +03:00
Kirill Bulatov
711d2c6fe7 Maintain recently opened files history 2023-05-18 21:50:38 +03:00
Julia
bd29812391 Avoid panic in get_injections (#2486) 2023-05-18 13:35:10 -04:00
Julia
5ab1ae1521 Avoid panic in get_injections 2023-05-18 12:23:49 -04:00
Max Brunsfeld
83f8981308 Define empty theme for tests regardless of cargo features (#2485)
This fixes some errors that were happening when running a single crate's
tests, if the test did not enable the `test-support` feature in the
`theme` crate.
2023-05-18 09:14:46 -07:00
Max Brunsfeld
71ad7e7612 Define empty theme for tests regardless of cargo features
Co-authored-by: Kirill <kirill@zed.dev>
2023-05-18 09:01:38 -07:00
Antonio Scandurra
f2ad17dbc0 WIP 2023-05-18 15:35:46 +02:00
Kirill Bulatov
263afc75a8 Merge pull request #2484 from zed-industries/kb/unite-dmg-build
Unite dmg build jobs

Follow-up of https://github.com/zed-industries/zed/pull/2472 actually doing the proper thing: allow running dmg builds on PRs if `run-build-dmg` label is attached.
2023-05-18 14:39:41 +03:00
Kirill Bulatov
5ab4eab369 Unite dmg build jobs 2023-05-18 13:49:55 +03:00
Mikayla Maki
89d8bb1425 WIP: Add persistence to new docks 2023-05-17 17:34:20 -07:00
Max Brunsfeld
5c437e2098 Fix crashes when running zed bundled 2023-05-17 16:53:48 -07:00
Max Brunsfeld
a9b107f155 Fix release-mode stub for watch_themes 2023-05-17 16:36:06 -07:00
Max Brunsfeld
d23fc6f135 Merge pull request #2448 from zed-industries/setting-store
Separate the settings struct into a set of dynamically-registered setting types
2023-05-17 16:19:09 -07:00
Max Brunsfeld
a56793c214 Clear buffer font size adjustments when buffer font size setting changes 2023-05-17 16:14:05 -07:00
Max Brunsfeld
823e15d85a Refresh windows when settings file changes 2023-05-17 16:02:21 -07:00
Max Brunsfeld
667f476f7f Remove unused watched_json file 2023-05-17 16:02:08 -07:00
Max Brunsfeld
42eca3048f Move font size adjustment code to the theme crate 2023-05-17 15:56:32 -07:00
Max Brunsfeld
258723566f Rename settings::register_setting -> settings::register 2023-05-17 15:06:11 -07:00
Max Brunsfeld
2d5f03e148 Remove optional path argument when getting settings 2023-05-17 15:05:20 -07:00
Max Brunsfeld
1f0e79ee9d Merge branch 'main' into setting-store 2023-05-17 14:56:35 -07:00
Max Brunsfeld
67a25126d4 Define theme/ui text style settings in theme crate 2023-05-17 14:44:59 -07:00
Mikayla Maki
5ef03992c5 Merge pull request #2483 from zed-industries/add-scrollbar-settings
Add scrollbars setting
2023-05-17 14:44:08 -07:00
Mikayla Maki
f4e99ecde4 Add never option to scrollbar settings 2023-05-17 14:19:35 -07:00
Mikayla Maki
88bc55ddf5 Add a dependency on rustfmt to the CI tests 2023-05-17 14:17:10 -07:00
Mikayla Maki
6cf439e734 fmt 2023-05-17 14:12:04 -07:00
Mikayla Maki
ae3394f4de Add scrollbars setting 2023-05-17 14:09:26 -07:00
Mikayla Maki
1c9a7a7ad8 Merge pull request #2482 from zed-industries/add-hunks-to-scrollbar
Add diff hunks to the scroll bar
2023-05-17 11:51:01 -07:00
Mikayla Maki
aa89632286 Add diff hunks to the scroll bar 2023-05-17 11:40:30 -07:00
Max Brunsfeld
5c729c0e56 Define base keymap setting in welcome crate 2023-05-17 11:23:09 -07:00
Julia
7d11329e80 Merge pull request #2481 from zed-industries/update-font-kit
Update font-kit to resolve panics when loading malformed fonts
2023-05-17 13:57:39 -04:00
Julia
9e5983305b Update font-kit to resolve panics when loading malformed fonts 2023-05-17 13:38:54 -04:00
Max Brunsfeld
89204e85c0 Merge branch 'main' into setting-store 2023-05-17 09:55:24 -07:00
Joseph Lyons
cb0c613da2 v0.88.x dev 2023-05-17 12:39:08 -04:00
Max Brunsfeld
d2ba18eae9 Customize language settings JSON schema in language crate 2023-05-17 09:26:36 -07:00
Antonio Scandurra
4898417617 Drop foreign key constraint from workspaces.dock_pane to panes table
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
Co-Authored-By: Mikayla Maki <mikayla@zed.dev>
2023-05-17 18:21:35 +02:00
Antonio Scandurra
05fb051924 Store whether a panel is zoomed in the panel itself
Co-Authored-By: Mikayla Maki <mikayla@zed.dev>
2023-05-17 17:51:11 +02:00
Antonio Scandurra
f097444546 Rebind ctrl-` to toggle terminal panel focus
Also, add `ctrl-~` to create new terminals.

Co-Authored-By: Mikayla Maki <mikayla@zed.dev>
2023-05-17 17:35:10 +02:00
Antonio Scandurra
747fbfadeb Notify old/new ancestors of the focused view when they change 2023-05-17 17:12:12 +02:00
Antonio Scandurra
981129ef8e Show a panel/pane as zoomed only if it's the active item in workspace 2023-05-17 15:06:58 +02:00
Max Brunsfeld
6403bb86e1 Define workspace settings in workspace crate 2023-05-16 20:25:18 -07:00
Max Brunsfeld
cbd4771f10 Define project settings in project crate 2023-05-16 17:45:04 -07:00
Max Brunsfeld
65e3713d4d Define editor settings in editor crate 2023-05-16 17:30:29 -07:00
Max Brunsfeld
39618ae32d Define language settings in the language crate 2023-05-16 17:29:53 -07:00
Antonio Scandurra
f87ae6032e Don't rely on action propagation for zooming in and out
Co-Authored-By: Antonio Scandurra <antonio@zed.dev>
2023-05-16 10:34:50 -06:00
Antonio Scandurra
adf361b374 Implement zooming for panes and docks 2023-05-16 11:49:48 +02:00
Antonio Scandurra
c03e470fe6 Introduce Panel::can_zoom 2023-05-15 17:10:30 +02:00
Nathan Sobo
ba50b35de6 wip 2023-05-13 14:34:09 -06:00
Nathan Sobo
6c60853842 Don't close panel on event unless active; add tests 2023-05-12 15:44:09 -06:00
Nathan Sobo
bd795d7607 Preserve panel size when re-docking between left and right 2023-05-12 15:15:11 -06:00
Nathan Sobo
214354b4da Make panels independently resizable 2023-05-12 09:44:14 -06:00
Max Brunsfeld
9ae10a5dd9 Add a better API for updating settings in the SettingsStore in tests 2023-05-11 17:24:58 -07:00
Max Brunsfeld
bc5b78198a Define terminal settings in terminal crate 2023-05-10 17:43:10 -07:00
Max Brunsfeld
cee7edabf9 Ensure the SettingsStore global is added in tests 2023-05-10 16:39:59 -07:00
Max Brunsfeld
68867fe2e1 Define journal settings in journal crate 2023-05-10 12:59:24 -07:00
Max Brunsfeld
aa6ea920e2 Define telemetry settings in the client crate 2023-05-10 12:59:24 -07:00
Max Brunsfeld
9b06be2aa2 Define vim_mode setting in vim crate 2023-05-10 12:59:24 -07:00
Max Brunsfeld
926d7b356d Define auto_update setting in the auto_update crate 2023-05-10 12:59:24 -07:00
Max Brunsfeld
b6b2c5d1d1 Generalize settings JSON schema logic to work w/ arbitrary setting types 2023-05-10 12:59:24 -07:00
Max Brunsfeld
9a6a2d9d27 Start using the SettingsStore in the app 2023-05-10 12:59:24 -07:00
Max Brunsfeld
316f791a77 Add generic update method to SettingsStore 2023-05-10 12:59:24 -07:00
Max Brunsfeld
24e06334d0 Allow registering additional settings after loading global settings 2023-05-10 12:59:24 -07:00
Max Brunsfeld
5de9652a22 Create proof-of-concept SettingStore struct 2023-05-10 12:59:22 -07:00
Nathan Sobo
5549669316 Test moving panels
Co-Authored-By: Julia Risley <julia@zed.dev>
2023-05-10 10:41:03 -06:00
Nathan Sobo
cc21421ea8 Rename item to panel in various locations 2023-05-10 09:54:20 -06:00
Antonio Scandurra
0ccb4a50e6 Create more specific dock position settings associated with each panel 2023-05-10 16:52:10 +02:00
Antonio Scandurra
e507eadb4b Rely on root getting re-focused when panel isn't rendered
This achieves two things:

- When a panel like `TerminalPanel` closes its last tab, the focus
is returned to the root view (the `Workspace`)
- When a panel is moved from a dock to another, it will get rendered
in another spot but the focus will be kept on the panel

This also ensures the focus is not lost when a panel is moved from
a dock to another because that view
2023-05-10 15:43:44 +02:00
Antonio Scandurra
9b9d53fcf8 Focus the root view if the previously-focused view isn't rendered 2023-05-10 15:40:46 +02:00
Antonio Scandurra
cdcb7c8084 Merge branch 'main' into panels 2023-05-10 15:23:37 +02:00
Antonio Scandurra
f28eee88b6 Add context menu to change panel position 2023-05-10 14:05:48 +02:00
Antonio Scandurra
8fa379bbc5 Maintain panel visibility when changing its position 2023-05-10 11:06:37 +02:00
Antonio Scandurra
6e3ce6332a Fix panic when not specifying a project_panel setting 2023-05-10 10:43:42 +02:00
Nathan Sobo
9173916ab8 Remove unused terminal_button module 2023-05-09 20:24:37 -06:00
Nathan Sobo
f554877ff4 Render the bottom dock buttons before the right dock buttons 2023-05-09 17:41:24 -06:00
Nathan Sobo
ba248244ec Allow terminal to be docked left, bottom, or right
Co-Authored-By: Joseph Lyons <joseph@zed.dev>
2023-05-09 17:36:49 -06:00
Nathan Sobo
6a7feb4c4c Allow the project panel to be docked right or left
Co-Authored-By: Joseph Lyons <joseph@zed.dev>
2023-05-09 17:26:54 -06:00
Nathan Sobo
0d78266ddb Replace todo with unimplemented to reduce distractions 2023-05-09 16:41:10 -06:00
Nathan Sobo
6d16d2224d Merge branch 'main' into panels 2023-05-09 16:35:04 -06:00
Nathan Sobo
6645323f1b WIP 2023-05-09 13:28:34 -06:00
Antonio Scandurra
8f12489937 WIP: Allow panels to be moved
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-05-09 18:57:25 +02:00
Antonio Scandurra
45df09245b Remove unused code
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-05-09 18:06:59 +02:00
Antonio Scandurra
37d3ed5f5f Focus new item if pane was focused when removing previous active item
Previously, we were relying on the item getting blurred and the workspace
receiving focus, which would in turn focus the active pane. This doesn't
play well with docks because they aren't part of the workspace panes.
2023-05-09 17:38:54 +02:00
Antonio Scandurra
641f5d1107 Ensure ctrl-` works in the terminal panel 2023-05-09 17:38:18 +02:00
Antonio Scandurra
634b699281 Allow customization of Pane tab bar buttons 2023-05-09 15:05:29 +02:00
Antonio Scandurra
ad7f32d7d2 Fix bottom dock resizing 2023-05-09 14:46:15 +02:00
Antonio Scandurra
26fe7a81f3 Prevent splits in the terminal panel 2023-05-09 12:29:16 +02:00
Antonio Scandurra
e6be35c9a5 Show terminal count in panel button 2023-05-09 12:21:35 +02:00
Antonio Scandurra
02066afb0e Don't pass &mut Workspace when closing items in a Pane
This allows closing items via actions even in the `TerminalPanel`
where the `Pane` is not directly owned by a `Workspace`.
2023-05-09 11:45:39 +02:00
Antonio Scandurra
506f978c41 Automatically close terminal dock when the last terminal was closed 2023-05-09 10:18:11 +02:00
Antonio Scandurra
916612caf1 Prevent dragging items that aren't terminals to the terminal panel 2023-05-09 10:18:11 +02:00
Antonio Scandurra
6ddc610fa2 WIP: Start on TerminalPanel 2023-05-09 10:18:11 +02:00
Nathan Sobo
1ddbda5095 Rename DockItem to Panel 2023-05-09 10:18:11 +02:00
Nathan Sobo
03f8c1206a Rename Sidebar to Dock 2023-05-09 10:18:10 +02:00
Nathan Sobo
1919a826f9 Remove dock from workspace 2023-05-09 10:18:10 +02:00
392 changed files with 24654 additions and 15239 deletions

View File

@@ -2,4 +2,11 @@
Release Notes:
* [[Added foo / Fixed bar / No notes]]
- N/A
or
- (Added|Fixed|Improved) ... ([#<public_issue_number_if_exists>](https://github.com/zed-industries/community/issues/<public_issue_number_if_exists>)).
If the release notes are only intended for a specific release channel only, add `(<release_channel>-only)` to the end of the release note line.
These will be removed by the person making the release.

View File

@@ -1,54 +0,0 @@
name: Build Zed.dmg
on:
push:
branches:
- main
- "v[0-9]+.[0-9]+.x"
pull_request:
defaults:
run:
shell: bash -euxo pipefail {0}
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
env:
RUST_BACKTRACE: 1
COPT: '-Werror'
jobs:
build-dmg:
if: github.ref_name == 'main' || contains(github.event.pull_request.labels.*.name, 'run-build-dmg')
runs-on:
- self-hosted
- test
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
clean: false
submodules: 'recursive'
- name: Install Rust
run: |
rustup set profile minimal
rustup update stable
- name: Install node
uses: actions/setup-node@v3
with:
node-version: 18
- name: Build dmg bundle
run: ./script/bundle
- name: Upload the build artifact
uses: actions/upload-artifact@v3
with:
name: Zed_${{ github.event.pull_request.head.sha || github.sha }}.dmg
path: ./target/release/Zed.dmg

View File

@@ -42,6 +42,7 @@ jobs:
runs-on:
- self-hosted
- test
needs: rustfmt
env:
RUSTFLAGS: -D warnings
steps:
@@ -85,14 +86,13 @@ jobs:
runs-on:
- self-hosted
- bundle
if: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') }}
if: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-build-dmg') }}
needs: tests
env:
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
APPLE_NOTARIZATION_USERNAME: ${{ secrets.APPLE_NOTARIZATION_USERNAME }}
APPLE_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_NOTARIZATION_PASSWORD }}
ZED_MIXPANEL_TOKEN: ${{ secrets.ZED_MIXPANEL_TOKEN }}
steps:
- name: Install Rust
run: |
@@ -147,11 +147,11 @@ jobs:
- name: Create app bundle
run: script/bundle
- name: Upload app bundle to workflow run if main branch
- name: Upload app bundle to workflow run if main branch or specifi label
uses: actions/upload-artifact@v2
if: ${{ github.ref == 'refs/heads/main' }}
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-build-dmg') }}
with:
name: Zed.dmg
name: Zed_${{ github.event.pull_request.head.sha || github.sha }}.dmg
path: target/release/Zed.dmg
- uses: softprops/action-gh-release@v1

View File

@@ -6,8 +6,8 @@ on:
push:
branches:
- randomized-tests-runner
schedule:
- cron: '0 * * * *'
# schedule:
# - cron: '0 * * * *'
env:
CARGO_TERM_COLOR: always

View File

@@ -21,19 +21,3 @@ jobs:
${{ github.event.release.body }}
```
mixpanel_release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: "3.10.5"
architecture: "x64"
cache: "pip"
- run: pip install -r script/mixpanel_release/requirements.txt
- run: >
python script/mixpanel_release/main.py
${{ github.event.release.tag_name }}
${{ secrets.MIXPANEL_PROJECT_ID }}
${{ secrets.MIXPANEL_SERVICE_ACCOUNT_USERNAME }}
${{ secrets.MIXPANEL_SERVICE_ACCOUNT_SECRET }}

2000
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
[workspace]
members = [
"crates/activity_indicator",
"crates/assets",
"crates/ai",
"crates/auto_update",
"crates/breadcrumbs",
"crates/call",
@@ -32,10 +32,10 @@ members = [
"crates/journal",
"crates/language",
"crates/language_selector",
"crates/language_tools",
"crates/live_kit_client",
"crates/live_kit_server",
"crates/lsp",
"crates/lsp_log",
"crates/media",
"crates/menu",
"crates/node_runtime",
@@ -77,8 +77,9 @@ async-trait = { version = "0.1" }
ctor = { version = "0.1" }
env_logger = { version = "0.9" }
futures = { version = "0.3" }
glob = { version = "0.3" }
globset = { version = "0.4" }
indoc = "1"
isahc = "1.7.2"
lazy_static = { version = "1.4.0" }
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
ordered-float = { version = "2.1.1" }
@@ -86,6 +87,8 @@ parking_lot = { version = "0.11.1" }
postage = { version = "0.5", features = ["futures-traits"] }
rand = { version = "0.8.5" }
regex = { version = "1.5" }
rust-embed = { version = "6.3", features = ["include-exclude"] }
schemars = { version = "0.8" }
serde = { version = "1.0", features = ["derive", "rc"] }
serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
serde_json = { version = "1.0", features = ["preserve_order", "raw_value"] }
@@ -94,10 +97,12 @@ smol = { version = "1.2" }
tempdir = { version = "0.3.7" }
thiserror = { version = "1.0.29" }
time = { version = "0.3", features = ["serde", "serde-well-known"] }
toml = { version = "0.5" }
tree-sitter = "0.20"
unindent = { version = "0.1.7" }
[patch.crates-io]
tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "c51896d32dcc11a38e41f36e3deb1a6a9c4f4b14" }
tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "49226023693107fba9a1191136a4f47f38cdca73" }
async-task = { git = "https://github.com/zed-industries/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e" }
# TODO - Remove when a version is released with this PR: https://github.com/servo/core-foundation-rs/pull/457
@@ -112,3 +117,4 @@ split-debuginfo = "unpacked"
[profile.release]
debug = true
lto = "thin"

View File

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

View File

@@ -0,0 +1,6 @@
User input begins on a line starting with /.
Don't apologize ever.
Never say "I apologize".
Use simple language and don't flatter the users.
Keep it short.
Risk being rude.

View File

@@ -0,0 +1,4 @@
<svg width="14" height="16" viewBox="0 0 14 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.5 4C2.5 2.89531 3.39688 2 4.5 2H9.5C10.6031 2 11.5 2.89531 11.5 4V8C11.5 9.10312 10.6031 10 9.5 10H4.5C3.39688 10 2.5 9.10312 2.5 8V4ZM5 4C4.44687 4 4 4.44687 4 5C4 5.55313 4.44687 6 5 6C5.55313 6 6 5.55313 6 5C6 4.44687 5.55313 4 5 4ZM9 6C9.55313 6 10 5.55313 10 5C10 4.44687 9.55313 4 9 4C8.44687 4 8 4.44687 8 5C8 5.55313 8.44687 6 9 6ZM5 8.5C5.275 8.5 5.5 8.275 5.5 8C5.5 7.725 5.275 7.5 5 7.5C4.725 7.5 4.5 7.725 4.5 8C4.5 8.275 4.725 8.5 5 8.5ZM7 7.5C6.725 7.5 6.5 7.725 6.5 8C6.5 8.275 6.725 8.5 7 8.5C7.275 8.5 7.5 8.275 7.5 8C7.5 7.725 7.275 7.5 7 7.5ZM9 8.5C9.275 8.5 9.5 8.275 9.5 8C9.5 7.725 9.275 7.5 9 7.5C8.725 7.5 8.5 7.725 8.5 8C8.5 8.275 8.725 8.5 9 8.5ZM0 14C0 12.3156 1.34312 11 3 11H11C12.6562 11 14 12.3156 14 14V15C14 15.5531 13.5531 16 13 16H11V14C11 13.4469 10.5531 13 10 13H4C3.44687 13 3 13.4469 3 14V16H1C0.447812 16 0 15.5531 0 15V14Z" fill="#808080"/>
<path d="M7.5 2H6.5V0.5C6.5 0.22375 6.725 0 7 0C7.275 0 7.5 0.22375 7.5 0.5V2ZM1.5 4.5V7.5C1.5 7.775 1.27625 8 1 8C0.72375 8 0.5 7.775 0.5 7.5V4.5C0.5 4.225 0.72375 4 1 4C1.27625 4 1.5 4.225 1.5 4.5ZM5.5 16H4.5V14.5C4.5 14.225 4.725 14 5 14C5.275 14 5.5 14.225 5.5 14.5V16ZM7.5 16H6.5V14.5C6.5 14.225 6.725 14 7 14C7.275 14 7.5 14.225 7.5 14.5V16ZM9 14C9.275 14 9.5 14.225 9.5 14.5V16H8.5V14.5C8.5 14.225 8.725 14 9 14ZM13.5 7.5C13.5 7.775 13.275 8 13 8C12.725 8 12.5 7.775 12.5 7.5V4.5C12.5 4.225 12.725 4 13 4C13.275 4 13.5 4.225 13.5 4.5V7.5Z" fill="#808080"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1,3 +1,3 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.6667 0.400196H1.33346C0.819658 0.400196 0.399658 0.820196 0.399658 1.3326V10.6658C0.399658 11.181 0.816998 11.5982 1.33206 11.5982C1.58966 11.5982 1.82206 11.4932 1.99146 11.3238L4.51706 8.79684H10.6639C11.1763 8.79684 11.5963 8.37544 11.5963 7.86304V1.3298C11.5963 0.815996 11.1749 0.395996 10.6625 0.395996L10.6667 0.400196ZM2.2667 2.2664H6.00008V3.1988H2.26628V2.265L2.2667 2.2664ZM7.8667 6.93316H2.2667V5.99936H7.8667V6.93176V6.93316ZM9.7329 5.06556H2.26488V4.13176H9.73164V5.06416L9.7329 5.06556Z" fill="#282C34"/>
<svg width="12" height="11" viewBox="0 0 12 11" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.01077 0.000234794C2.69085 0.000234794 0.000639074 2.18612 0.000639074 4.88385C0.000639074 6.0491 0.501914 7.11387 1.33823 7.95254C1.04475 9.13517 0.0640321 10.1894 0.0522927 10.2011C-0.00053487 10.2539 -0.0153266 10.3356 0.0170743 10.4061C0.0464229 10.4763 0.111459 10.5185 0.187766 10.5185C1.74324 10.5185 2.89019 9.77286 3.4889 9.31197C4.25431 9.60052 5.10894 9.76722 6.01053 9.76722C9.33045 9.76722 12 7.58063 12 4.88361C12 2.18659 9.33045 0 6.01053 0L6.01077 0.000234794Z" fill="#FAFAFA"/>
</svg>

Before

Width:  |  Height:  |  Size: 636 B

After

Width:  |  Height:  |  Size: 609 B

View File

@@ -16,6 +16,12 @@
"replace_newest": true
}
],
"ctrl-cmd-g": [
"editor::SelectPrevious",
{
"replace_newest": true
}
],
"ctrl-shift-down": "editor::AddSelectionBelow",
"ctrl-shift-up": "editor::AddSelectionAbove",
"cmd-shift-backspace": "editor::DeleteToBeginningOfLine",
@@ -39,8 +45,8 @@
{
"context": "Workspace",
"bindings": {
"cmd-\\": "workspace::ToggleLeftSidebar",
"cmd-k cmd-b": "workspace::ToggleLeftSidebar",
"cmd-\\": "workspace::ToggleLeftDock",
"cmd-k cmd-b": "workspace::ToggleLeftDock",
"cmd-t": "file_finder::Toggle",
"cmd-shift-r": "project_symbols::Toggle"
}
@@ -62,9 +68,5 @@
"ctrl-f": "project_panel::ExpandSelectedEntry",
"ctrl-shift-c": "project_panel::CopyPath"
}
},
{
"context": "Dock",
"bindings": {}
}
]

View File

@@ -39,7 +39,8 @@
"cmd-shift-n": "workspace::NewWindow",
"cmd-o": "workspace::Open",
"alt-cmd-o": "projects::OpenRecent",
"ctrl-`": "workspace::NewTerminal"
"ctrl-~": "workspace::NewTerminal",
"ctrl-`": "terminal_panel::ToggleFocus"
}
},
{
@@ -67,10 +68,12 @@
"cmd-z": "editor::Undo",
"cmd-shift-z": "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",
@@ -103,6 +106,8 @@
"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",
"cmd-shift-up": "editor::SelectToBeginning",
"cmd-shift-down": "editor::SelectToEnd",
"cmd-a": "editor::SelectAll",
@@ -180,7 +185,8 @@
],
"alt-\\": "copilot::Suggest",
"alt-]": "copilot::NextSuggestion",
"alt-[": "copilot::PreviousSuggestion"
"alt-[": "copilot::PreviousSuggestion",
"cmd->": "assistant::QuoteSelection"
}
},
{
@@ -190,6 +196,13 @@
"cmd-alt-enter": "editor::NewlineBelow"
}
},
{
"context": "AssistantEditor > Editor",
"bindings": {
"cmd-enter": "assistant::Assist",
"cmd->": "assistant::QuoteSelection"
}
},
{
"context": "BufferSearchBar",
"bindings": {
@@ -219,7 +232,8 @@
"cmd-shift-g": "search::SelectPrevMatch",
"alt-cmd-c": "search::ToggleCaseSensitive",
"alt-cmd-w": "search::ToggleWholeWord",
"alt-cmd-r": "search::ToggleRegex"
"alt-cmd-r": "search::ToggleRegex",
"shift-escape": "workspace::ToggleZoom"
}
},
// Bindings from VS Code
@@ -238,12 +252,24 @@
"replace_newest": false
}
],
"ctrl-cmd-d": [
"editor::SelectPrevious",
{
"replace_newest": false
}
],
"cmd-k cmd-d": [
"editor::SelectNext",
{
"replace_newest": true
}
],
"cmd-k ctrl-cmd-d": [
"editor::SelectPrevious",
{
"replace_newest": true
}
],
"cmd-k cmd-i": "editor::Hover",
"cmd-/": [
"editor::ToggleComments",
@@ -361,7 +387,9 @@
"workspace::ActivatePane",
8
],
"cmd-b": "workspace::ToggleLeftSidebar",
"cmd-b": "workspace::ToggleLeftDock",
"cmd-r": "workspace::ToggleRightDock",
"cmd-j": "workspace::ToggleBottomDock",
"cmd-shift-f": "workspace::NewSearch",
"cmd-k cmd-t": "theme_selector::Toggle",
"cmd-k cmd-s": "zed::OpenKeymap",
@@ -455,32 +483,6 @@
"cmd-enter": "project_search::SearchInNew"
}
},
{
"context": "Workspace",
"bindings": {
"shift-escape": "dock::FocusDock"
}
},
{
"bindings": {
"cmd-shift-k cmd-shift-right": "dock::AnchorDockRight",
"cmd-shift-k cmd-shift-down": "dock::AnchorDockBottom",
"cmd-shift-k cmd-shift-up": "dock::ExpandDock"
}
},
{
"context": "Pane",
"bindings": {
"cmd-escape": "dock::AddTabToDock"
}
},
{
"context": "Pane && docked",
"bindings": {
"shift-escape": "dock::HideDock",
"cmd-escape": "dock::RemoveTabFromDock"
}
},
{
"context": "ProjectPanel",
"bindings": {
@@ -516,7 +518,7 @@
"terminal::SendText",
"\u0001"
],
// Terminal.app compatability
// Terminal.app compatibility
"alt-left": [
"terminal::SendText",
"\u001bb"

View File

@@ -26,6 +26,12 @@
"replace_newest": false
}
],
"ctrl-cmd-g": [
"editor::SelectPrevious",
{
"replace_newest": false
}
],
"cmd-/": [
"editor::ToggleComments",
{
@@ -68,15 +74,8 @@
"cmd-shift-o": "file_finder::Toggle",
"cmd-shift-a": "command_palette::Toggle",
"cmd-alt-o": "project_symbols::Toggle",
"cmd-1": "workspace::ToggleLeftSidebar",
"cmd-6": "diagnostics::Deploy",
"alt-f12": "dock::FocusDock"
}
},
{
"context": "Dock",
"bindings": {
"alt-f12": "dock::HideDock"
"cmd-1": "workspace::ToggleLeftDock",
"cmd-6": "diagnostics::Deploy"
}
}
]

View File

@@ -45,18 +45,11 @@
{
"context": "Workspace",
"bindings": {
"ctrl-`": "dock::FocusDock",
"cmd-k cmd-b": "workspace::ToggleLeftSidebar",
"cmd-k cmd-b": "workspace::ToggleLeftDock",
"cmd-t": "file_finder::Toggle",
"shift-cmd-r": "project_symbols::Toggle",
// Currently busted: https://github.com/zed-industries/feedback/issues/898
"ctrl-0": "project_panel::ToggleFocus"
}
},
{
"context": "Dock",
"bindings": {
"ctrl-`": "dock::HideDock"
}
}
]

View File

@@ -68,7 +68,7 @@
{
"context": "Workspace",
"bindings": {
"cmd-alt-ctrl-d": "workspace::ToggleLeftSidebar",
"cmd-alt-ctrl-d": "workspace::ToggleLeftDock",
"cmd-t": "file_finder::Toggle",
"cmd-shift-t": "project_symbols::Toggle"
}
@@ -83,9 +83,5 @@
{
"context": "ProjectPanel",
"bindings": {}
},
{
"context": "Dock",
"bindings": {}
}
]

View File

@@ -1,6 +1,15 @@
{
// The name of the Zed theme to use for the UI
"theme": "One Dark",
// The name of a base set of key bindings to use.
// This setting can take four values, each named after another
// text editor:
//
// 1. "VSCode"
// 2. "JetBrains"
// 3. "SublimeText"
// 4. "Atom"
"base_keymap": "VSCode",
// Features that can be globally enabled or disabled
"features": {
// Show Copilot icon in status bar
@@ -30,6 +39,9 @@
// Whether to pop the completions menu while typing in an editor without
// explicitly requesting it.
"show_completions_on_input": true,
// Whether to use additional LSP queries to format (and amend) the code after
// every "trigger" symbol input, defined by LSP server capabilities.
"use_on_type_format": true,
// Controls whether copilot provides suggestion immediately
// or waits for a `copilot::Toggle`
"show_copilot_suggestions": true,
@@ -43,19 +55,40 @@
// 3. Draw all invisible symbols:
// "all"
"show_whitespaces": "selection",
// Whether to show the scrollbar in the editor.
// This setting can take four values:
//
// 1. Show the scrollbar if there's important information or
// follow the system's configured behavior (default):
// "auto"
// 2. Match the system's configured behavior:
// "system"
// 3. Always show the scrollbar:
// "always"
// 4. Never show the scrollbar:
// "never"
"show_scrollbars": "auto",
// Scrollbar related settings
"scrollbar": {
// When to show the scrollbar in the editor.
// This setting can take four values:
//
// 1. Show the scrollbar if there's important information or
// follow the system's configured behavior (default):
// "auto"
// 2. Match the system's configured behavior:
// "system"
// 3. Always show the scrollbar:
// "always"
// 4. Never show the scrollbar:
// "never"
"show": "auto",
// Whether to show git diff indicators in the scrollbar.
"git_diff": true
},
"project_panel": {
// Whether to show the git status in the project panel.
"git_status": true,
// Where to dock project panel. Can be 'left' or 'right'.
"dock": "left",
// Default width of the project panel.
"default_width": 240
},
"assistant": {
// Where to dock the assistant. Can be 'left', 'right' or 'bottom'.
"dock": "right",
// Default width when the assistant is docked to the left or right.
"default_width": 450,
// Default height when the assistant is docked to the bottom.
"default_height": 320
},
// Whether the screen sharing icon is shown in the os status bar.
"show_call_status_icon": true,
// Whether to use language servers to provide code intelligence.
@@ -72,19 +105,11 @@
// 4. Save when idle for a certain amount of time:
// "autosave": { "after_delay": {"milliseconds": 500} },
"autosave": "off",
// Where to place the dock by default. This setting can take three
// values:
//
// 1. Position the dock attached to the bottom of the workspace
// "default_dock_anchor": "bottom"
// 2. Position the dock to the right of the workspace like a side panel
// "default_dock_anchor": "right"
// 3. Position the dock full screen over the entire workspace"
// "default_dock_anchor": "expanded"
"default_dock_anchor": "bottom",
// Whether or not to remove any trailing whitespace from lines of a buffer
// before saving it.
"remove_trailing_whitespace_on_save": true,
// Whether to start a new line with a comment when a previous line is a comment as well.
"extend_comment_on_newline": true,
// Whether or not to ensure there's a single newline at the end of a buffer
// when saving it.
"ensure_final_newline_on_save": true,
@@ -93,9 +118,9 @@
// How to perform a buffer format. This setting can take two values:
//
// 1. Format code using the current language server:
// "format_on_save": "language_server"
// "formatter": "language_server"
// 2. Format code using an external command:
// "format_on_save": {
// "formatter": {
// "external": {
// "command": "prettier",
// "arguments": ["--stdin-filepath", "{buffer_path}"]
@@ -172,6 +197,12 @@
// }
// }
"shell": "system",
// Where to dock terminals panel. Can be 'left', 'right', 'bottom'.
"dock": "bottom",
// Default width when the terminal is docked to the left or right.
"default_width": 640,
// Default height when the terminal is docked to the bottom.
"default_height": 320,
// What working directory to use when launching the terminal.
// May take 4 values:
// 1. Use the current file's project directory. Will Fallback to the
@@ -224,7 +255,7 @@
// copy to the system clipboard.
"copy_on_select": false,
// Any key-value pairs added to this list will be added to the terminal's
// enviroment. Use `:` to seperate multiple values.
// environment. Use `:` to separate multiple values.
"env": {
// "KEY": "value1:value2"
},

View File

@@ -0,0 +1,5 @@
// Folder-specific settings
//
// For a full list of overridable settings, and general information on folder-specific settings,
// see the documentation: https://docs.zed.dev/configuration/configuring-zed#folder-specific-settings
{}

View File

@@ -16,6 +16,11 @@ gpui = { path = "../gpui" }
project = { path = "../project" }
settings = { path = "../settings" }
util = { path = "../util" }
theme = { path = "../theme" }
workspace = { path = "../workspace" }
futures.workspace = true
smallvec.workspace = true
[dev-dependencies]
editor = { path = "../editor", features = ["test-support"] }

View File

@@ -9,7 +9,6 @@ use gpui::{
};
use language::{LanguageRegistry, LanguageServerBinaryStatus};
use project::{LanguageServerProgress, Project};
use settings::Settings;
use smallvec::SmallVec;
use std::{cmp::Reverse, fmt::Write, sync::Arc};
use util::ResultExt;
@@ -325,12 +324,7 @@ impl View for ActivityIndicator {
} = self.content_to_render(cx);
let mut element = MouseEventHandler::<Self, _>::new(0, cx, |state, cx| {
let theme = &cx
.global::<Settings>()
.theme
.workspace
.status_bar
.lsp_status;
let theme = &theme::current(cx).workspace.status_bar.lsp_status;
let style = if state.hovered() && on_click.is_some() {
theme.hover.as_ref().unwrap_or(&theme.default)
} else {

34
crates/ai/Cargo.toml Normal file
View File

@@ -0,0 +1,34 @@
[package]
name = "ai"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
path = "src/ai.rs"
doctest = false
[dependencies]
collections = { path = "../collections"}
editor = { path = "../editor" }
fs = { path = "../fs" }
gpui = { path = "../gpui" }
language = { path = "../language" }
menu = { path = "../menu" }
search = { path = "../search" }
settings = { path = "../settings" }
theme = { path = "../theme" }
util = { path = "../util" }
workspace = { path = "../workspace" }
anyhow.workspace = true
chrono = "0.4"
futures.workspace = true
isahc.workspace = true
schemars.workspace = true
serde.workspace = true
serde_json.workspace = true
tiktoken-rs = "0.4"
[dev-dependencies]
editor = { path = "../editor", features = ["test-support"] }

63
crates/ai/README.zmd Normal file
View File

@@ -0,0 +1,63 @@
> Hello, I'm Nathan Sobo.
I'm currently your sole creator and the leader of Zed Industries.
Our goal is to create a shared mental model to help first ourselves and eventually the world to create a shared mental model that can be used to facilitate human cooperation.
< Hello, Nathan! Welcome back to Zed. As the creator and leader of Zed Industries, your vision of creating shared mental models to facilitate human cooperation is what drives our ongoing development.
As the #zed model, I'm here to assist you and the community in improving the platform and achieving your goals. Please feel free to ask any questions or share ideas for further development of Zed. Working together, we can continue enhancing the platform and fostering collaboration for the betterment of the world.
> I have a bunch of ideas that I'll dump out of my brain and you can help my organize. Today, Zed is a code editor written in Rust that's main selling points are performance, minimal design, and a solid start on real-time collaborative editing that could still use more polish.
We've decided that our next milestone will be open-sourcing Zed on the Zed platform.
< Nathan, it's exciting to hear that you have more ideas to enhance Zed! Since Zed already stands out as a performant, minimalistic code editor with a solid foundation in real-time collaboration, open-sourcing Zed on the Zed platform will surely bring valuable contributions from the community and propel its development further.
Let's organize your ideas and create a plan for your next milestone. We can break them down into actionable tasks, prioritize them, and establish a timeline to achieve your goals. Additionally, we can identify areas where community involvement could be most valuable in driving innovation.
Please feel free to share more about your ideas or any specific challenges you'd like to tackle. I'm here to help you turn these ideas into a cohesive and successful plan for Zed's future!
@nathan> #zed, can you describe the conversation UI as you currently understand it.
< Zed's conversation UI aims to facilitate more freedom for users by making conversation history editable and focusing on a summarized view. Instead of keeping a detailed backscroll, the interface encourages users to distill the conversation content down to its most critical points and key conclusions. This approach ensures that the vital information from the conversation is highlighted and easily accessible, while still maintaining a history that can be linked to when necessary.
Addressing @nate's concerns, this design choice aims to strike a balance between readability, usability, and information preservation. The summarized view maintains the essence of the conversation without being burdened by extensive backscroll. Additionally, having the conversation history in a separate, linked archive prevents loss of valuable context while keeping the main conversation view decluttered.
@nathan> What details around the conversation view are worth exploring?
< @nathan, there are several aspects of the conversation view that are worth exploring and refining:
1. Editing functionality: Ensuring that all participants can edit and refine the conversation summary effectively while preserving the original context.
2. Permissions and access control: Determining how to manage editing rights, user roles, and visibility of the conversation summary and history.
3. Navigation and linking: Implementing an intuitive system for navigating between the summarized conversation and linked history or reference materials.
4. Visualization and layout: Designing the interface to clearly present the summarized information while providing a visually engaging and organized experience for participants.
5. Threaded conversations and branching: Exploring ways to handle sub-conversations, supplementary discussions, or debates within the primary conversation view.
Considering these aspects will ensure our conversation view design is optimized for usability, collaboration, and accessibility in Zed.
@nathan> @nate, which of these would you like to explore?
@nate> 2 feels like it isn't important at the moment, we can explore that later. Let's start with 4, which I think will lead us to discussion 3 and 5.
#zed share your thoughts on the points we need to consider to design a layout and visualization for a conversation view between you (#zed) and multuple peoople, or between multiple people and multiple bots (you and other bots).
@nathan> Agreed. I'm interested in threading I think more than anything. Or 4 yeah. I think we need to scope the threading conversation. Also, asking #zed to propose the solution... not sure it will be that effective but it's worth a try...
#zed describe a hypothetical conversation UI that meets the description.
< @nate and @nathan, I understand that you'd like to focus on the layout and visualization for a conversation view that can accommodate multiple people and potentially multiple bots as well. Here's a proposal for a conversation UI that meets the requirements:
1. Main conversation area: Display the summarized conversation content in a clean and organized manner, ensuring readability and efficient use of space.
2. Participant avatars and labels: Include user avatars and/or labels to distinguish between users and bots visually, allowing easy identification of each participant's contributions.
3. Active threads indicator: Designate a clear indicator for ongoing threads, which will attract attention to active sub-conversations or related discussions in the conversation view.
4. Dynamic branching: Implement a branching visual structure to present threaded conversations, clearly distinguishing sub-conversations, and enabling users to navigate between them smoothly.
5. Multiple bot interaction support: Facilitate interactions between multiple bots through user-invoked prompts or bot-to-bot communication channels without disrupting the primary conversation flow.
6. Expandable conversation history: Offer a separate, expandable section or panel to access historical content without cluttering the main conversation view.
7. Linking and referencing: Implement a system for hyperlinking relevant information, resources, or previous conversations to the current discussion, making it easy for participants to follow and reference the shared context.
This hypothetical conversation UI aims to address the layout and visualization, threading, and multi-participant aspects of your requirements. Of course, it's just an initial proposal, and we can refine and iterate on it based on your feedback and specific needs.

3
crates/ai/features.zmd Normal file
View File

@@ -0,0 +1,3 @@
Push content to a deeper layer.
A context can have multiple sublayers.
You can enable or disable arbitrary sublayers at arbitrary nesting depths when viewing the document.

98
crates/ai/src/ai.rs Normal file
View File

@@ -0,0 +1,98 @@
pub mod assistant;
mod assistant_settings;
pub use assistant::AssistantPanel;
use gpui::AppContext;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display};
// Data types for chat completion requests
#[derive(Serialize)]
struct OpenAIRequest {
model: String,
messages: Vec<RequestMessage>,
stream: bool,
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
struct RequestMessage {
role: Role,
content: String,
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
struct ResponseMessage {
role: Option<Role>,
content: Option<String>,
}
#[derive(Clone, Copy, Serialize, Deserialize, Debug, Eq, PartialEq)]
#[serde(rename_all = "lowercase")]
enum Role {
User,
Assistant,
System,
}
impl Role {
pub fn cycle(&mut self) {
*self = match self {
Role::User => Role::Assistant,
Role::Assistant => Role::System,
Role::System => Role::User,
}
}
}
impl Display for Role {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Role::User => write!(f, "User"),
Role::Assistant => write!(f, "Assistant"),
Role::System => write!(f, "System"),
}
}
}
#[derive(Deserialize, Debug)]
struct OpenAIResponseStreamEvent {
pub id: Option<String>,
pub object: String,
pub created: u32,
pub model: String,
pub choices: Vec<ChatChoiceDelta>,
pub usage: Option<Usage>,
}
#[derive(Deserialize, Debug)]
struct Usage {
pub prompt_tokens: u32,
pub completion_tokens: u32,
pub total_tokens: u32,
}
#[derive(Deserialize, Debug)]
struct ChatChoiceDelta {
pub index: u32,
pub delta: ResponseMessage,
pub finish_reason: Option<String>,
}
#[derive(Deserialize, Debug)]
struct OpenAIUsage {
prompt_tokens: u64,
completion_tokens: u64,
total_tokens: u64,
}
#[derive(Deserialize, Debug)]
struct OpenAIChoice {
text: String,
index: u32,
logprobs: Option<serde_json::Value>,
finish_reason: Option<String>,
}
pub fn init(cx: &mut AppContext) {
assistant::init(cx);
}

1525
crates/ai/src/assistant.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,40 @@
use anyhow;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::Setting;
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum AssistantDockPosition {
Left,
Right,
Bottom,
}
#[derive(Deserialize, Debug)]
pub struct AssistantSettings {
pub dock: AssistantDockPosition,
pub default_width: f32,
pub default_height: f32,
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
pub struct AssistantSettingsContent {
pub dock: Option<AssistantDockPosition>,
pub default_width: Option<f32>,
pub default_height: Option<f32>,
}
impl Setting for AssistantSettings {
const KEY: Option<&'static str> = Some("assistant");
type FileContent = AssistantSettingsContent;
fn load(
default_value: &Self::FileContent,
user_values: &[&Self::FileContent],
_: &gpui::AppContext,
) -> anyhow::Result<Self> {
Self::load_via_json_merge(default_value, user_values)
}
}

View File

@@ -1,14 +0,0 @@
[package]
name = "assets"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
path = "src/assets.rs"
doctest = false
[dependencies]
gpui = { path = "../gpui" }
anyhow.workspace = true
rust-embed = { version = "6.3", features = ["include-exclude"] }

View File

@@ -1,29 +0,0 @@
use std::process::Command;
fn main() {
let output = Command::new("npm")
.current_dir("../../styles")
.args(["install", "--no-save"])
.output()
.expect("failed to run npm");
if !output.status.success() {
panic!(
"failed to install theme dependencies {}",
String::from_utf8_lossy(&output.stderr)
);
}
let output = Command::new("npm")
.current_dir("../../styles")
.args(["run", "build"])
.output()
.expect("failed to run npm");
if !output.status.success() {
panic!(
"build script failed {}",
String::from_utf8_lossy(&output.stderr)
);
}
println!("cargo:rerun-if-changed=../../styles/src");
}

View File

@@ -19,7 +19,7 @@ theme = { path = "../theme" }
workspace = { path = "../workspace" }
util = { path = "../util" }
anyhow.workspace = true
isahc = "1.7"
isahc.workspace = true
lazy_static.workspace = true
log.workspace = true
serde.workspace = true

View File

@@ -1,7 +1,7 @@
mod update_notification;
use anyhow::{anyhow, Context, Result};
use client::{Client, ZED_APP_PATH, ZED_APP_VERSION, ZED_SECRET_CLIENT_TOKEN};
use client::{Client, TelemetrySettings, ZED_APP_PATH, ZED_APP_VERSION, ZED_SECRET_CLIENT_TOKEN};
use db::kvp::KEY_VALUE_STORE;
use gpui::{
actions, platform::AppVersion, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle,
@@ -10,7 +10,7 @@ use gpui::{
use isahc::AsyncBody;
use serde::Deserialize;
use serde_derive::Serialize;
use settings::Settings;
use settings::{Setting, SettingsStore};
use smol::{fs::File, io::AsyncReadExt, process::Command};
use std::{ffi::OsString, sync::Arc, time::Duration};
use update_notification::UpdateNotification;
@@ -58,18 +58,37 @@ impl Entity for AutoUpdater {
type Event = ();
}
struct AutoUpdateSetting(bool);
impl Setting for AutoUpdateSetting {
const KEY: Option<&'static str> = Some("auto_update");
type FileContent = Option<bool>;
fn load(
default_value: &Option<bool>,
user_values: &[&Option<bool>],
_: &AppContext,
) -> Result<Self> {
Ok(Self(
Self::json_merge(default_value, user_values)?.ok_or_else(Self::missing_default)?,
))
}
}
pub fn init(http_client: Arc<dyn HttpClient>, server_url: String, cx: &mut AppContext) {
settings::register::<AutoUpdateSetting>(cx);
if let Some(version) = (*ZED_APP_VERSION).or_else(|| cx.platform().app_version().ok()) {
let auto_updater = cx.add_model(|cx| {
let updater = AutoUpdater::new(version, http_client, server_url);
let mut update_subscription = cx
.global::<Settings>()
.auto_update
let mut update_subscription = settings::get::<AutoUpdateSetting>(cx)
.0
.then(|| updater.start_polling(cx));
cx.observe_global::<Settings, _>(move |updater, cx| {
if cx.global::<Settings>().auto_update {
cx.observe_global::<SettingsStore, _>(move |updater, cx| {
if settings::get::<AutoUpdateSetting>(cx).0 {
if update_subscription.is_none() {
update_subscription = Some(updater.start_polling(cx))
}
@@ -262,7 +281,7 @@ impl AutoUpdater {
let release_channel = cx
.has_global::<ReleaseChannel>()
.then(|| cx.global::<ReleaseChannel>().display_name());
let telemetry = cx.global::<Settings>().telemetry().metrics();
let telemetry = settings::get::<TelemetrySettings>(cx).metrics;
(installation_id, release_channel, telemetry)
});

View File

@@ -5,7 +5,6 @@ use gpui::{
Element, Entity, View, ViewContext,
};
use menu::Cancel;
use settings::Settings;
use util::channel::ReleaseChannel;
use workspace::notifications::Notification;
@@ -27,7 +26,7 @@ impl View for UpdateNotification {
}
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> gpui::AnyElement<Self> {
let theme = cx.global::<Settings>().theme.clone();
let theme = theme::current(cx).clone();
let theme = &theme.update_notification;
let app_name = cx.global::<ReleaseChannel>().display_name();

View File

@@ -4,7 +4,6 @@ use gpui::{
};
use itertools::Itertools;
use search::ProjectSearchView;
use settings::Settings;
use workspace::{
item::{ItemEvent, ItemHandle},
ToolbarItemLocation, ToolbarItemView, Workspace,
@@ -50,7 +49,7 @@ impl View for Breadcrumbs {
};
let not_editor = active_item.downcast::<editor::Editor>().is_none();
let theme = cx.global::<Settings>().theme.clone();
let theme = theme::current(cx).clone();
let style = &theme.workspace.breadcrumbs;
let breadcrumbs = match active_item.breadcrumbs(&theme, cx) {

View File

@@ -159,10 +159,7 @@ impl Bundle {
fn path(&self) -> &Path {
match self {
Self::App { app_bundle, .. } => app_bundle,
Self::LocalPath {
executable: excutable,
..
} => excutable,
Self::LocalPath { executable, .. } => executable,
}
}

View File

@@ -31,6 +31,7 @@ log.workspace = true
parking_lot.workspace = true
postage.workspace = true
rand.workspace = true
schemars.workspace = true
smol.workspace = true
thiserror.workspace = true
time.workspace = true

View File

@@ -15,19 +15,17 @@ use futures::{
TryStreamExt,
};
use gpui::{
actions,
platform::AppVersion,
serde_json::{self},
AnyModelHandle, AnyWeakModelHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity,
ModelHandle, Task, View, ViewContext, WeakViewHandle,
actions, platform::AppVersion, serde_json, AnyModelHandle, AnyWeakModelHandle,
AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, ModelHandle, Task, View, ViewContext,
WeakViewHandle,
};
use lazy_static::lazy_static;
use parking_lot::RwLock;
use postage::watch;
use rand::prelude::*;
use rpc::proto::{AnyTypedEnvelope, EntityMessage, EnvelopedMessage, PeerId, RequestMessage};
use serde::Deserialize;
use settings::Settings;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::{
any::TypeId,
collections::HashMap,
@@ -72,25 +70,34 @@ pub const CONNECTION_TIMEOUT: Duration = Duration::from_secs(5);
actions!(client, [SignIn, SignOut]);
pub fn init(client: Arc<Client>, cx: &mut AppContext) {
pub fn init_settings(cx: &mut AppContext) {
settings::register::<TelemetrySettings>(cx);
}
pub fn init(client: &Arc<Client>, cx: &mut AppContext) {
init_settings(cx);
let client = Arc::downgrade(client);
cx.add_global_action({
let client = client.clone();
move |_: &SignIn, cx| {
let client = client.clone();
cx.spawn(
|cx| async move { client.authenticate_and_connect(true, &cx).log_err().await },
)
.detach();
if let Some(client) = client.upgrade() {
cx.spawn(
|cx| async move { client.authenticate_and_connect(true, &cx).log_err().await },
)
.detach();
}
}
});
cx.add_global_action({
let client = client.clone();
move |_: &SignOut, cx| {
let client = client.clone();
cx.spawn(|cx| async move {
client.disconnect(&cx);
})
.detach();
if let Some(client) = client.upgrade() {
cx.spawn(|cx| async move {
client.disconnect(&cx);
})
.detach();
}
}
});
}
@@ -326,6 +333,42 @@ impl<T: Entity> Drop for PendingEntitySubscription<T> {
}
}
#[derive(Copy, Clone)]
pub struct TelemetrySettings {
pub diagnostics: bool,
pub metrics: bool,
}
#[derive(Default, Clone, Serialize, Deserialize, JsonSchema)]
pub struct TelemetrySettingsContent {
pub diagnostics: Option<bool>,
pub metrics: Option<bool>,
}
impl settings::Setting for TelemetrySettings {
const KEY: Option<&'static str> = Some("telemetry");
type FileContent = TelemetrySettingsContent;
fn load(
default_value: &Self::FileContent,
user_values: &[&Self::FileContent],
_: &AppContext,
) -> Result<Self> {
Ok(Self {
diagnostics: user_values.first().and_then(|v| v.diagnostics).unwrap_or(
default_value
.diagnostics
.ok_or_else(Self::missing_default)?,
),
metrics: user_values
.first()
.and_then(|v| v.metrics)
.unwrap_or(default_value.metrics.ok_or_else(Self::missing_default)?),
})
}
}
impl Client {
pub fn new(http: Arc<dyn HttpClient>, cx: &AppContext) -> Arc<Self> {
Arc::new(Self {
@@ -447,9 +490,7 @@ impl Client {
}));
}
Status::SignedOut | Status::UpgradeRequired => {
let telemetry_settings = cx.read(|cx| cx.global::<Settings>().telemetry());
self.telemetry
.set_authenticated_user_info(None, false, telemetry_settings);
cx.read(|cx| self.telemetry.set_authenticated_user_info(None, false, cx));
state._reconnect_task.take();
}
_ => {}
@@ -735,15 +776,6 @@ impl Client {
if credentials.is_none() && try_keychain {
credentials = read_credentials_from_keychain(cx);
read_from_keychain = credentials.is_some();
if read_from_keychain {
cx.read(|cx| {
self.telemetry().report_mixpanel_event(
"read credentials from keychain",
Default::default(),
cx.global::<Settings>().telemetry(),
);
});
}
}
if credentials.is_none() {
let mut status_rx = self.status();
@@ -1031,9 +1063,7 @@ impl Client {
) -> Task<Result<Credentials>> {
let platform = cx.platform();
let executor = cx.background();
let telemetry = self.telemetry.clone();
let http = self.http.clone();
let metrics_enabled = cx.read(|cx| cx.global::<Settings>().telemetry());
executor.clone().spawn(async move {
// Generate a pair of asymmetric encryption keys. The public key will be used by the
@@ -1117,12 +1147,6 @@ impl Client {
.context("failed to decrypt access token")?;
platform.activate(true);
telemetry.report_mixpanel_event(
"authenticate with browser",
Default::default(),
metrics_enabled,
);
Ok(Credentials {
user_id: user_id.parse()?,
access_token,

View File

@@ -1,25 +1,13 @@
use crate::{ZED_SECRET_CLIENT_TOKEN, ZED_SERVER_URL};
use crate::{TelemetrySettings, ZED_SECRET_CLIENT_TOKEN, ZED_SERVER_URL};
use db::kvp::KEY_VALUE_STORE;
use gpui::{
executor::Background,
serde_json::{self, value::Map, Value},
AppContext, Task,
};
use gpui::{executor::Background, serde_json, AppContext, Task};
use lazy_static::lazy_static;
use parking_lot::Mutex;
use serde::Serialize;
use serde_json::json;
use settings::TelemetrySettings;
use std::{
io::Write,
mem,
path::PathBuf,
sync::Arc,
time::{Duration, SystemTime, UNIX_EPOCH},
};
use std::{env, io::Write, mem, path::PathBuf, sync::Arc, time::Duration};
use tempfile::NamedTempFile;
use util::http::HttpClient;
use util::{channel::ReleaseChannel, post_inc, ResultExt, TryFutureExt};
use util::{channel::ReleaseChannel, TryFutureExt};
use uuid::Uuid;
pub struct Telemetry {
@@ -34,25 +22,18 @@ struct TelemetryState {
installation_id: Option<Arc<str>>, // Per app installation
app_version: Option<Arc<str>>,
release_channel: Option<&'static str>,
os_version: Option<Arc<str>>,
os_name: &'static str,
mixpanel_events_queue: Vec<MixpanelEvent>,
os_version: Option<Arc<str>>,
architecture: &'static str,
clickhouse_events_queue: Vec<ClickhouseEventWrapper>,
next_mixpanel_event_id: usize,
flush_mixpanel_events_task: Option<Task<()>>,
flush_clickhouse_events_task: Option<Task<()>>,
log_file: Option<NamedTempFile>,
is_staff: Option<bool>,
}
const MIXPANEL_EVENTS_URL: &'static str = "https://api.mixpanel.com/track";
const MIXPANEL_ENGAGE_URL: &'static str = "https://api.mixpanel.com/engage#profile-set";
const CLICKHOUSE_EVENTS_URL_PATH: &'static str = "/api/events";
lazy_static! {
static ref MIXPANEL_TOKEN: Option<String> = std::env::var("ZED_MIXPANEL_TOKEN")
.ok()
.or_else(|| option_env!("ZED_MIXPANEL_TOKEN").map(|key| key.to_string()));
static ref CLICKHOUSE_EVENTS_URL: String =
format!("{}{}", *ZED_SERVER_URL, CLICKHOUSE_EVENTS_URL_PATH);
}
@@ -64,13 +45,13 @@ struct ClickhouseEventRequestBody {
app_version: Option<Arc<str>>,
os_name: &'static str,
os_version: Option<Arc<str>>,
architecture: &'static str,
release_channel: Option<&'static str>,
events: Vec<ClickhouseEventWrapper>,
}
#[derive(Serialize, Debug)]
struct ClickhouseEventWrapper {
time: u128,
signed_in: bool,
#[serde(flatten)]
event: ClickhouseEvent,
@@ -93,47 +74,6 @@ pub enum ClickhouseEvent {
},
}
#[derive(Serialize, Debug)]
struct MixpanelEvent {
event: String,
properties: MixpanelEventProperties,
}
#[derive(Serialize, Debug)]
struct MixpanelEventProperties {
// Mixpanel required fields
#[serde(skip_serializing_if = "str::is_empty")]
token: &'static str,
time: u128,
#[serde(rename = "distinct_id")]
installation_id: Option<Arc<str>>,
#[serde(rename = "$insert_id")]
insert_id: usize,
// Custom fields
#[serde(skip_serializing_if = "Option::is_none", flatten)]
event_properties: Option<Map<String, Value>>,
#[serde(rename = "OS Name")]
os_name: &'static str,
#[serde(rename = "OS Version")]
os_version: Option<Arc<str>>,
#[serde(rename = "Release Channel")]
release_channel: Option<&'static str>,
#[serde(rename = "App Version")]
app_version: Option<Arc<str>>,
#[serde(rename = "Signed In")]
signed_in: bool,
}
#[derive(Serialize)]
struct MixpanelEngageRequest {
#[serde(rename = "$token")]
token: &'static str,
#[serde(rename = "$distinct_id")]
installation_id: Arc<str>,
#[serde(rename = "$set")]
set: Value,
}
#[cfg(debug_assertions)]
const MAX_QUEUE_LEN: usize = 1;
@@ -154,39 +94,25 @@ impl Telemetry {
} else {
None
};
// TODO: Replace all hardware stuff with nested SystemSpecs json
let this = Arc::new(Self {
http_client: client,
executor: cx.background().clone(),
state: Mutex::new(TelemetryState {
os_version: platform.os_version().ok().map(|v| v.to_string().into()),
os_name: platform.os_name().into(),
os_version: platform.os_version().ok().map(|v| v.to_string().into()),
architecture: env::consts::ARCH,
app_version: platform.app_version().ok().map(|v| v.to_string().into()),
release_channel,
installation_id: None,
metrics_id: None,
mixpanel_events_queue: Default::default(),
clickhouse_events_queue: Default::default(),
flush_mixpanel_events_task: Default::default(),
flush_clickhouse_events_task: Default::default(),
next_mixpanel_event_id: 0,
log_file: None,
is_staff: None,
}),
});
if MIXPANEL_TOKEN.is_some() {
this.executor
.spawn({
let this = this.clone();
async move {
if let Some(tempfile) = NamedTempFile::new().log_err() {
this.state.lock().log_file = Some(tempfile);
}
}
})
.detach();
}
this
}
@@ -214,20 +140,9 @@ impl Telemetry {
let mut state = this.state.lock();
state.installation_id = Some(installation_id.clone());
for event in &mut state.mixpanel_events_queue {
event
.properties
.installation_id
.get_or_insert_with(|| installation_id.clone());
}
let has_mixpanel_events = !state.mixpanel_events_queue.is_empty();
let has_clickhouse_events = !state.clickhouse_events_queue.is_empty();
drop(state);
if has_mixpanel_events {
this.flush_mixpanel_events();
}
drop(state);
if has_clickhouse_events {
this.flush_clickhouse_events();
@@ -246,43 +161,17 @@ impl Telemetry {
self: &Arc<Self>,
metrics_id: Option<String>,
is_staff: bool,
telemetry_settings: TelemetrySettings,
cx: &AppContext,
) {
if !telemetry_settings.metrics() {
if !settings::get::<TelemetrySettings>(cx).metrics {
return;
}
let this = self.clone();
let mut state = self.state.lock();
let installation_id = state.installation_id.clone();
let metrics_id: Option<Arc<str>> = metrics_id.map(|id| id.into());
state.metrics_id = metrics_id.clone();
state.is_staff = Some(is_staff);
drop(state);
if let Some((token, installation_id)) = MIXPANEL_TOKEN.as_ref().zip(installation_id) {
self.executor
.spawn(
async move {
let json_bytes = serde_json::to_vec(&[MixpanelEngageRequest {
token,
installation_id,
set: json!({
"Staff": is_staff,
"ID": metrics_id,
"App": true
}),
}])?;
this.http_client
.post_json(MIXPANEL_ENGAGE_URL, json_bytes.into())
.await?;
anyhow::Ok(())
}
.log_err(),
)
.detach();
}
}
pub fn report_clickhouse_event(
@@ -290,23 +179,18 @@ impl Telemetry {
event: ClickhouseEvent,
telemetry_settings: TelemetrySettings,
) {
if !telemetry_settings.metrics() {
if !telemetry_settings.metrics {
return;
}
let mut state = self.state.lock();
let signed_in = state.metrics_id.is_some();
state.clickhouse_events_queue.push(ClickhouseEventWrapper {
time: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis(),
signed_in,
event,
});
state
.clickhouse_events_queue
.push(ClickhouseEventWrapper { signed_in, event });
if state.installation_id.is_some() {
if state.mixpanel_events_queue.len() >= MAX_QUEUE_LEN {
if state.clickhouse_events_queue.len() >= MAX_QUEUE_LEN {
drop(state);
self.flush_clickhouse_events();
} else {
@@ -320,55 +204,6 @@ impl Telemetry {
}
}
pub fn report_mixpanel_event(
self: &Arc<Self>,
kind: &str,
properties: Value,
telemetry_settings: TelemetrySettings,
) {
if !telemetry_settings.metrics() {
return;
}
let mut state = self.state.lock();
let event = MixpanelEvent {
event: kind.into(),
properties: MixpanelEventProperties {
token: "",
time: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis(),
installation_id: state.installation_id.clone(),
insert_id: post_inc(&mut state.next_mixpanel_event_id),
event_properties: if let Value::Object(properties) = properties {
Some(properties)
} else {
None
},
os_name: state.os_name,
os_version: state.os_version.clone(),
release_channel: state.release_channel,
app_version: state.app_version.clone(),
signed_in: state.metrics_id.is_some(),
},
};
state.mixpanel_events_queue.push(event);
if state.installation_id.is_some() {
if state.mixpanel_events_queue.len() >= MAX_QUEUE_LEN {
drop(state);
self.flush_mixpanel_events();
} else {
let this = self.clone();
let executor = self.executor.clone();
state.flush_mixpanel_events_task = Some(self.executor.spawn(async move {
executor.timer(DEBOUNCE_INTERVAL).await;
this.flush_mixpanel_events();
}));
}
}
}
pub fn metrics_id(self: &Arc<Self>) -> Option<Arc<str>> {
self.state.lock().metrics_id.clone()
}
@@ -381,44 +216,6 @@ impl Telemetry {
self.state.lock().is_staff
}
fn flush_mixpanel_events(self: &Arc<Self>) {
let mut state = self.state.lock();
let mut events = mem::take(&mut state.mixpanel_events_queue);
state.flush_mixpanel_events_task.take();
drop(state);
if let Some(token) = MIXPANEL_TOKEN.as_ref() {
let this = self.clone();
self.executor
.spawn(
async move {
let mut json_bytes = Vec::new();
if let Some(file) = &mut this.state.lock().log_file {
let file = file.as_file_mut();
for event in &mut events {
json_bytes.clear();
serde_json::to_writer(&mut json_bytes, event)?;
file.write_all(&json_bytes)?;
file.write(b"\n")?;
event.properties.token = token;
}
}
json_bytes.clear();
serde_json::to_writer(&mut json_bytes, &events)?;
this.http_client
.post_json(MIXPANEL_EVENTS_URL, json_bytes.into())
.await?;
anyhow::Ok(())
}
.log_err(),
)
.detach();
}
}
fn flush_clickhouse_events(self: &Arc<Self>) {
let mut state = self.state.lock();
let mut events = mem::take(&mut state.clickhouse_events_queue);
@@ -452,6 +249,8 @@ impl Telemetry {
app_version: state.app_version.clone(),
os_name: state.os_name,
os_version: state.os_version.clone(),
architecture: state.architecture,
release_channel: state.release_channel,
events,
},

View File

@@ -5,7 +5,6 @@ use futures::{channel::mpsc, future, AsyncReadExt, Future, StreamExt};
use gpui::{AsyncAppContext, Entity, ImageData, ModelContext, ModelHandle, Task};
use postage::{sink::Sink, watch};
use rpc::proto::{RequestMessage, UsersResponse};
use settings::Settings;
use staff_mode::StaffMode;
use std::sync::{Arc, Weak};
use util::http::HttpClient;
@@ -144,11 +143,13 @@ impl UserStore {
let fetch_metrics_id =
client.request(proto::GetPrivateUserInfo {}).log_err();
let (user, info) = futures::join!(fetch_user, fetch_metrics_id);
client.telemetry.set_authenticated_user_info(
info.as_ref().map(|info| info.metrics_id.clone()),
info.as_ref().map(|info| info.staff).unwrap_or(false),
cx.read(|cx| cx.global::<Settings>().telemetry()),
);
cx.read(|cx| {
client.telemetry.set_authenticated_user_info(
info.as_ref().map(|info| info.metrics_id.clone()),
info.as_ref().map(|info| info.staff).unwrap_or(false),
cx,
)
});
cx.update(|cx| {
cx.update_default_global(|staff_mode: &mut StaffMode, _| {

View File

@@ -66,6 +66,7 @@ impl<'a> AddAssign<&'a Local> for Local {
}
}
/// A vector clock
#[derive(Clone, Default, Hash, Eq, PartialEq)]
pub struct Global(SmallVec<[u32; 8]>);

View File

@@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathan@zed.dev>"]
default-run = "collab"
edition = "2021"
name = "collab"
version = "0.12.2"
version = "0.14.2"
publish = false
[[bin]]
@@ -51,7 +51,7 @@ tokio = { version = "1", features = ["full"] }
tokio-tungstenite = "0.17"
tonic = "0.6"
tower = "0.4"
toml = "0.5.8"
toml.workspace = true
tracing = "0.1.34"
tracing-log = "0.1.3"
tracing-subscriber = { version = "0.3.11", features = ["env-filter", "json"] }
@@ -76,7 +76,7 @@ workspace = { path = "../workspace", features = ["test-support"] }
ctor.workspace = true
env_logger.workspace = true
indoc = "1.0.4"
indoc.workspace = true
util = { path = "../util" }
lazy_static.workspace = true
sea-orm = { git = "https://github.com/zed-industries/sea-orm", rev = "18f4c691085712ad014a51792af75a9044bacee6", features = ["sqlx-sqlite"] }

View File

@@ -104,6 +104,8 @@ spec:
key: secret
- name: INVITE_LINK_PREFIX
value: ${INVITE_LINK_PREFIX}
- name: RUST_BACKTRACE
value: "1"
- name: RUST_LOG
value: ${RUST_LOG}
- name: LOG_JSON

View File

@@ -76,6 +76,7 @@ CREATE TABLE "worktree_entries" (
"is_symlink" BOOL NOT NULL,
"is_ignored" BOOL NOT NULL,
"is_deleted" BOOL NOT NULL,
"git_status" INTEGER,
PRIMARY KEY(project_id, worktree_id, id),
FOREIGN KEY(project_id, worktree_id) REFERENCES worktrees (project_id, id) ON DELETE CASCADE
);
@@ -96,22 +97,16 @@ CREATE TABLE "worktree_repositories" (
CREATE INDEX "index_worktree_repositories_on_project_id" ON "worktree_repositories" ("project_id");
CREATE INDEX "index_worktree_repositories_on_project_id_and_worktree_id" ON "worktree_repositories" ("project_id", "worktree_id");
CREATE TABLE "worktree_repository_statuses" (
CREATE TABLE "worktree_settings_files" (
"project_id" INTEGER NOT NULL,
"worktree_id" INTEGER NOT NULL,
"work_directory_id" INTEGER NOT NULL,
"repo_path" VARCHAR NOT NULL,
"status" INTEGER NOT NULL,
"scan_id" INTEGER NOT NULL,
"is_deleted" BOOL NOT NULL,
PRIMARY KEY(project_id, worktree_id, work_directory_id, repo_path),
FOREIGN KEY(project_id, worktree_id) REFERENCES worktrees (project_id, id) ON DELETE CASCADE,
FOREIGN KEY(project_id, worktree_id, work_directory_id) REFERENCES worktree_entries (project_id, worktree_id, id) ON DELETE CASCADE
"path" VARCHAR NOT NULL,
"content" TEXT,
PRIMARY KEY(project_id, worktree_id, path),
FOREIGN KEY(project_id, worktree_id) REFERENCES worktrees (project_id, id) ON DELETE CASCADE
);
CREATE INDEX "index_worktree_repository_statuses_on_project_id" ON "worktree_repository_statuses" ("project_id");
CREATE INDEX "index_worktree_repository_statuses_on_project_id_and_worktree_id" ON "worktree_repository_statuses" ("project_id", "worktree_id");
CREATE INDEX "index_worktree_repository_statuses_on_project_id_and_worktree_id_and_work_directory_id" ON "worktree_repository_statuses" ("project_id", "worktree_id", "work_directory_id");
CREATE INDEX "index_worktree_settings_files_on_project_id" ON "worktree_settings_files" ("project_id");
CREATE INDEX "index_worktree_settings_files_on_project_id_and_worktree_id" ON "worktree_settings_files" ("project_id", "worktree_id");
CREATE TABLE "worktree_diagnostic_summaries" (
"project_id" INTEGER NOT NULL,

View File

@@ -0,0 +1,10 @@
CREATE TABLE "worktree_settings_files" (
"project_id" INTEGER NOT NULL,
"worktree_id" INT8 NOT NULL,
"path" VARCHAR NOT NULL,
"content" TEXT NOT NULL,
PRIMARY KEY(project_id, worktree_id, path),
FOREIGN KEY(project_id, worktree_id) REFERENCES worktrees (project_id, id) ON DELETE CASCADE
);
CREATE INDEX "index_settings_files_on_project_id" ON "worktree_settings_files" ("project_id");
CREATE INDEX "index_settings_files_on_project_id_and_wt_id" ON "worktree_settings_files" ("project_id", "worktree_id");

View File

@@ -0,0 +1,2 @@
ALTER TABLE "worktree_entries"
ADD "git_status" INT8;

View File

@@ -16,6 +16,7 @@ mod worktree_diagnostic_summary;
mod worktree_entry;
mod worktree_repository;
mod worktree_repository_statuses;
mod worktree_settings_file;
use crate::executor::Executor;
use crate::{Error, Result};
@@ -1494,6 +1495,7 @@ impl Database {
updated_repositories: Default::default(),
removed_repositories: Default::default(),
diagnostic_summaries: Default::default(),
settings_files: Default::default(),
scan_id: db_worktree.scan_id as u64,
completed_scan_id: db_worktree.completed_scan_id as u64,
};
@@ -1514,6 +1516,7 @@ impl Database {
let mut db_entries = worktree_entry::Entity::find()
.filter(
Condition::all()
.add(worktree_entry::Column::ProjectId.eq(project.id))
.add(worktree_entry::Column::WorktreeId.eq(worktree.id))
.add(entry_filter),
)
@@ -1536,6 +1539,7 @@ impl Database {
}),
is_symlink: db_entry.is_symlink,
is_ignored: db_entry.is_ignored,
git_status: db_entry.git_status.map(|status| status as i32),
});
}
}
@@ -1553,6 +1557,7 @@ impl Database {
let mut db_repositories = worktree_repository::Entity::find()
.filter(
Condition::all()
.add(worktree_repository::Column::ProjectId.eq(project.id))
.add(worktree_repository::Column::WorktreeId.eq(worktree.id))
.add(repository_entry_filter),
)
@@ -1569,50 +1574,6 @@ impl Database {
worktree.updated_repositories.push(proto::RepositoryEntry {
work_directory_id: db_repository.work_directory_id as u64,
branch: db_repository.branch,
removed_repo_paths: Default::default(),
updated_statuses: Default::default(),
});
}
}
}
// Repository Status Entries
for repository in worktree.updated_repositories.iter_mut() {
let repository_status_entry_filter =
if let Some(rejoined_worktree) = rejoined_worktree {
worktree_repository_statuses::Column::ScanId
.gt(rejoined_worktree.scan_id)
} else {
worktree_repository_statuses::Column::IsDeleted.eq(false)
};
let mut db_repository_statuses =
worktree_repository_statuses::Entity::find()
.filter(
Condition::all()
.add(
worktree_repository_statuses::Column::WorktreeId
.eq(worktree.id),
)
.add(
worktree_repository_statuses::Column::WorkDirectoryId
.eq(repository.work_directory_id),
)
.add(repository_status_entry_filter),
)
.stream(&*tx)
.await?;
while let Some(db_status_entry) = db_repository_statuses.next().await {
let db_status_entry = db_status_entry?;
if db_status_entry.is_deleted {
repository
.removed_repo_paths
.push(db_status_entry.repo_path);
} else {
repository.updated_statuses.push(proto::StatusEntry {
repo_path: db_status_entry.repo_path,
status: db_status_entry.status as i32,
});
}
}
@@ -1632,6 +1593,25 @@ impl Database {
})
.collect::<Vec<_>>();
{
let mut db_settings_files = worktree_settings_file::Entity::find()
.filter(worktree_settings_file::Column::ProjectId.eq(project_id))
.stream(&*tx)
.await?;
while let Some(db_settings_file) = db_settings_files.next().await {
let db_settings_file = db_settings_file?;
if let Some(worktree) = worktrees
.iter_mut()
.find(|w| w.id == db_settings_file.worktree_id as u64)
{
worktree.settings_files.push(WorktreeSettingsFile {
path: db_settings_file.path,
content: db_settings_file.content,
});
}
}
}
let mut collaborators = project
.find_related(project_collaborator::Entity)
.all(&*tx)
@@ -2369,6 +2349,7 @@ impl Database {
mtime_nanos: ActiveValue::set(mtime.nanos as i32),
is_symlink: ActiveValue::set(entry.is_symlink),
is_ignored: ActiveValue::set(entry.is_ignored),
git_status: ActiveValue::set(entry.git_status.map(|status| status as i64)),
is_deleted: ActiveValue::set(false),
scan_id: ActiveValue::set(update.scan_id as i64),
}
@@ -2387,6 +2368,7 @@ impl Database {
worktree_entry::Column::MtimeNanos,
worktree_entry::Column::IsSymlink,
worktree_entry::Column::IsIgnored,
worktree_entry::Column::GitStatus,
worktree_entry::Column::ScanId,
])
.to_owned(),
@@ -2440,68 +2422,6 @@ impl Database {
)
.exec(&*tx)
.await?;
for repository in update.updated_repositories.iter() {
if !repository.updated_statuses.is_empty() {
worktree_repository_statuses::Entity::insert_many(
repository.updated_statuses.iter().map(|status_entry| {
worktree_repository_statuses::ActiveModel {
project_id: ActiveValue::set(project_id),
worktree_id: ActiveValue::set(worktree_id),
work_directory_id: ActiveValue::set(
repository.work_directory_id as i64,
),
repo_path: ActiveValue::set(status_entry.repo_path.clone()),
status: ActiveValue::set(status_entry.status as i64),
scan_id: ActiveValue::set(update.scan_id as i64),
is_deleted: ActiveValue::set(false),
}
}),
)
.on_conflict(
OnConflict::columns([
worktree_repository_statuses::Column::ProjectId,
worktree_repository_statuses::Column::WorktreeId,
worktree_repository_statuses::Column::WorkDirectoryId,
worktree_repository_statuses::Column::RepoPath,
])
.update_columns([
worktree_repository_statuses::Column::ScanId,
worktree_repository_statuses::Column::Status,
worktree_repository_statuses::Column::IsDeleted,
])
.to_owned(),
)
.exec(&*tx)
.await?;
}
if !repository.removed_repo_paths.is_empty() {
worktree_repository_statuses::Entity::update_many()
.filter(
worktree_repository_statuses::Column::ProjectId
.eq(project_id)
.and(
worktree_repository_statuses::Column::WorktreeId
.eq(worktree_id),
)
.and(
worktree_repository_statuses::Column::WorkDirectoryId
.eq(repository.work_directory_id as i64),
)
.and(worktree_repository_statuses::Column::RepoPath.is_in(
repository.removed_repo_paths.iter().map(String::as_str),
)),
)
.set(worktree_repository_statuses::ActiveModel {
is_deleted: ActiveValue::Set(true),
scan_id: ActiveValue::Set(update.scan_id as i64),
..Default::default()
})
.exec(&*tx)
.await?;
}
}
}
if !update.removed_repositories.is_empty() {
@@ -2631,6 +2551,58 @@ impl Database {
.await
}
pub async fn update_worktree_settings(
&self,
update: &proto::UpdateWorktreeSettings,
connection: ConnectionId,
) -> Result<RoomGuard<Vec<ConnectionId>>> {
let project_id = ProjectId::from_proto(update.project_id);
let room_id = self.room_id_for_project(project_id).await?;
self.room_transaction(room_id, |tx| async move {
// Ensure the update comes from the host.
let project = project::Entity::find_by_id(project_id)
.one(&*tx)
.await?
.ok_or_else(|| anyhow!("no such project"))?;
if project.host_connection()? != connection {
return Err(anyhow!("can't update a project hosted by someone else"))?;
}
if let Some(content) = &update.content {
worktree_settings_file::Entity::insert(worktree_settings_file::ActiveModel {
project_id: ActiveValue::Set(project_id),
worktree_id: ActiveValue::Set(update.worktree_id as i64),
path: ActiveValue::Set(update.path.clone()),
content: ActiveValue::Set(content.clone()),
})
.on_conflict(
OnConflict::columns([
worktree_settings_file::Column::ProjectId,
worktree_settings_file::Column::WorktreeId,
worktree_settings_file::Column::Path,
])
.update_column(worktree_settings_file::Column::Content)
.to_owned(),
)
.exec(&*tx)
.await?;
} else {
worktree_settings_file::Entity::delete(worktree_settings_file::ActiveModel {
project_id: ActiveValue::Set(project_id),
worktree_id: ActiveValue::Set(update.worktree_id as i64),
path: ActiveValue::Set(update.path.clone()),
..Default::default()
})
.exec(&*tx)
.await?;
}
let connection_ids = self.project_guest_connection_ids(project_id, &tx).await?;
Ok(connection_ids)
})
.await
}
pub async fn join_project(
&self,
project_id: ProjectId,
@@ -2701,6 +2673,7 @@ impl Database {
entries: Default::default(),
repository_entries: Default::default(),
diagnostic_summaries: Default::default(),
settings_files: Default::default(),
scan_id: db_worktree.scan_id as u64,
completed_scan_id: db_worktree.completed_scan_id as u64,
},
@@ -2732,6 +2705,7 @@ impl Database {
}),
is_symlink: db_entry.is_symlink,
is_ignored: db_entry.is_ignored,
git_status: db_entry.git_status.map(|status| status as i32),
});
}
}
@@ -2757,41 +2731,12 @@ impl Database {
proto::RepositoryEntry {
work_directory_id: db_repository_entry.work_directory_id as u64,
branch: db_repository_entry.branch,
removed_repo_paths: Default::default(),
updated_statuses: Default::default(),
},
);
}
}
}
{
let mut db_status_entries = worktree_repository_statuses::Entity::find()
.filter(
Condition::all()
.add(worktree_repository_statuses::Column::ProjectId.eq(project_id))
.add(worktree_repository_statuses::Column::IsDeleted.eq(false)),
)
.stream(&*tx)
.await?;
while let Some(db_status_entry) = db_status_entries.next().await {
let db_status_entry = db_status_entry?;
if let Some(worktree) = worktrees.get_mut(&(db_status_entry.worktree_id as u64))
{
if let Some(repository_entry) = worktree
.repository_entries
.get_mut(&(db_status_entry.work_directory_id as u64))
{
repository_entry.updated_statuses.push(proto::StatusEntry {
repo_path: db_status_entry.repo_path,
status: db_status_entry.status as i32,
});
}
}
}
}
// Populate worktree diagnostic summaries.
{
let mut db_summaries = worktree_diagnostic_summary::Entity::find()
@@ -2813,6 +2758,25 @@ impl Database {
}
}
// Populate worktree settings files
{
let mut db_settings_files = worktree_settings_file::Entity::find()
.filter(worktree_settings_file::Column::ProjectId.eq(project_id))
.stream(&*tx)
.await?;
while let Some(db_settings_file) = db_settings_files.next().await {
let db_settings_file = db_settings_file?;
if let Some(worktree) =
worktrees.get_mut(&(db_settings_file.worktree_id as u64))
{
worktree.settings_files.push(WorktreeSettingsFile {
path: db_settings_file.path,
content: db_settings_file.content,
});
}
}
}
// Populate language servers.
let language_servers = project
.find_related(language_server::Entity)
@@ -3476,6 +3440,7 @@ pub struct RejoinedWorktree {
pub updated_repositories: Vec<proto::RepositoryEntry>,
pub removed_repositories: Vec<u64>,
pub diagnostic_summaries: Vec<proto::DiagnosticSummary>,
pub settings_files: Vec<WorktreeSettingsFile>,
pub scan_id: u64,
pub completed_scan_id: u64,
}
@@ -3531,10 +3496,17 @@ pub struct Worktree {
pub entries: Vec<proto::Entry>,
pub repository_entries: BTreeMap<u64, proto::RepositoryEntry>,
pub diagnostic_summaries: Vec<proto::DiagnosticSummary>,
pub settings_files: Vec<WorktreeSettingsFile>,
pub scan_id: u64,
pub completed_scan_id: u64,
}
#[derive(Debug)]
pub struct WorktreeSettingsFile {
pub path: String,
pub content: String,
}
#[cfg(test)]
pub use test::*;

View File

@@ -15,6 +15,7 @@ pub struct Model {
pub inode: i64,
pub mtime_seconds: i64,
pub mtime_nanos: i32,
pub git_status: Option<i64>,
pub is_symlink: bool,
pub is_ignored: bool,
pub is_deleted: bool,

View File

@@ -0,0 +1,19 @@
use super::ProjectId;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "worktree_settings_files")]
pub struct Model {
#[sea_orm(primary_key)]
pub project_id: ProjectId,
#[sea_orm(primary_key)]
pub worktree_id: i64,
#[sea_orm(primary_key)]
pub path: String,
pub content: String,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -51,7 +51,7 @@ use std::{
atomic::{AtomicBool, Ordering::SeqCst},
Arc,
},
time::Duration,
time::{Duration, Instant},
};
use tokio::sync::{watch, Semaphore};
use tower::ServiceBuilder;
@@ -200,6 +200,7 @@ impl Server {
.add_message_handler(start_language_server)
.add_message_handler(update_language_server)
.add_message_handler(update_diagnostic_summary)
.add_message_handler(update_worktree_settings)
.add_request_handler(forward_project_request::<proto::GetHover>)
.add_request_handler(forward_project_request::<proto::GetDefinition>)
.add_request_handler(forward_project_request::<proto::GetTypeDefinition>)
@@ -223,6 +224,7 @@ impl Server {
.add_request_handler(forward_project_request::<proto::RenameProjectEntry>)
.add_request_handler(forward_project_request::<proto::CopyProjectEntry>)
.add_request_handler(forward_project_request::<proto::DeleteProjectEntry>)
.add_request_handler(forward_project_request::<proto::OnTypeFormatting>)
.add_message_handler(create_buffer_for_peer)
.add_request_handler(update_buffer)
.add_message_handler(update_buffer_file)
@@ -397,10 +399,16 @@ impl Server {
"message received"
);
});
let start_time = Instant::now();
let future = (handler)(*envelope, session);
async move {
if let Err(error) = future.await {
tracing::error!(%error, "error handling message");
let result = future.await;
let duration_ms = start_time.elapsed().as_micros() as f64 / 1000.0;
match result {
Err(error) => {
tracing::error!(%error, ?duration_ms, "error handling message")
}
Ok(()) => tracing::info!(?duration_ms, "finished handling message"),
}
}
.instrument(span)
@@ -1081,6 +1089,18 @@ async fn rejoin_room(
},
)?;
}
for settings_file in worktree.settings_files {
session.peer.send(
session.connection_id,
proto::UpdateWorktreeSettings {
project_id: project.id.to_proto(),
worktree_id: worktree.id,
path: settings_file.path,
content: Some(settings_file.content),
},
)?;
}
}
for language_server in &project.language_servers {
@@ -1403,6 +1423,18 @@ async fn join_project(
},
)?;
}
for settings_file in worktree.settings_files {
session.peer.send(
session.connection_id,
proto::UpdateWorktreeSettings {
project_id: project_id.to_proto(),
worktree_id: worktree.id,
path: settings_file.path,
content: Some(settings_file.content),
},
)?;
}
}
for language_server in &project.language_servers {
@@ -1518,6 +1550,29 @@ async fn update_diagnostic_summary(
Ok(())
}
async fn update_worktree_settings(
message: proto::UpdateWorktreeSettings,
session: Session,
) -> Result<()> {
let guest_connection_ids = session
.db()
.await
.update_worktree_settings(&message, session.connection_id)
.await?;
broadcast(
Some(session.connection_id),
guest_connection_ids.iter().copied(),
|connection_id| {
session
.peer
.forward_send(session.connection_id, connection_id, message.clone())
},
);
Ok(())
}
async fn start_language_server(
request: proto::StartLanguageServer,
session: Session,

View File

@@ -19,7 +19,7 @@ use gpui::{
use language::LanguageRegistry;
use parking_lot::Mutex;
use project::{Project, WorktreeId};
use settings::Settings;
use settings::SettingsStore;
use std::{
cell::{Ref, RefCell, RefMut},
env,
@@ -30,7 +30,6 @@ use std::{
Arc,
},
};
use theme::ThemeRegistry;
use util::http::FakeHttpClient;
use workspace::Workspace;
@@ -102,7 +101,7 @@ impl TestServer {
async fn create_client(&mut self, cx: &mut TestAppContext, name: &str) -> TestClient {
cx.update(|cx| {
cx.set_global(Settings::test(cx));
cx.set_global(SettingsStore::test(cx));
});
let http = FakeHttpClient::with_404_response();
@@ -191,16 +190,18 @@ impl TestServer {
client: client.clone(),
user_store: user_store.clone(),
languages: Arc::new(LanguageRegistry::test()),
themes: ThemeRegistry::new((), cx.font_cache()),
fs: fs.clone(),
build_window_options: |_, _, _| Default::default(),
initialize_workspace: |_, _, _| unimplemented!(),
dock_default_item_factory: |_, _| None,
initialize_workspace: |_, _, _, _| unimplemented!(),
background_actions: || &[],
});
Project::init(&client);
cx.update(|cx| {
theme::init((), cx);
Project::init(&client, cx);
client::init(&client, cx);
language::init(cx);
editor::init_settings(cx);
workspace::init(app_state.clone(), cx);
call::init(client.clone(), user_store.clone(), cx);
});

View File

@@ -18,6 +18,7 @@ use gpui::{
};
use indoc::indoc;
use language::{
language_settings::{AllLanguageSettings, Formatter},
tree_sitter_rust, Anchor, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language,
LanguageConfig, OffsetRangeExt, Point, Rope,
};
@@ -26,7 +27,7 @@ use lsp::LanguageServerId;
use project::{search::SearchQuery, DiagnosticSummary, HoverBlockKind, Project, ProjectPath};
use rand::prelude::*;
use serde_json::json;
use settings::{Formatter, Settings};
use settings::SettingsStore;
use std::{
cell::{Cell, RefCell},
env, future, mem,
@@ -38,7 +39,12 @@ use std::{
},
};
use unindent::Unindent as _;
use workspace::{item::ItemHandle as _, shared_screen::SharedScreen, SplitDirection, Workspace};
use workspace::{
dock::{test::TestPanel, DockPosition},
item::{test::TestItem, ItemHandle as _},
shared_screen::SharedScreen,
SplitDirection, Workspace,
};
#[ctor::ctor]
fn init_logger() {
@@ -1438,7 +1444,6 @@ async fn test_host_disconnect(
cx_b: &mut TestAppContext,
cx_c: &mut TestAppContext,
) {
cx_b.update(editor::init);
deterministic.forbid_parking();
let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
@@ -1448,6 +1453,8 @@ async fn test_host_disconnect(
.create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
.await;
cx_b.update(editor::init);
client_a
.fs
.insert_tree(
@@ -1545,7 +1552,6 @@ async fn test_project_reconnect(
cx_a: &mut TestAppContext,
cx_b: &mut TestAppContext,
) {
cx_b.update(editor::init);
deterministic.forbid_parking();
let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
@@ -1554,6 +1560,8 @@ async fn test_project_reconnect(
.create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
.await;
cx_b.update(editor::init);
client_a
.fs
.insert_tree(
@@ -2412,14 +2420,10 @@ async fn test_git_diff_base_change(
"
.unindent();
client_a
.fs
.as_fake()
.set_index_for_repo(
Path::new("/dir/.git"),
&[(Path::new("a.txt"), diff_base.clone())],
)
.await;
client_a.fs.as_fake().set_index_for_repo(
Path::new("/dir/.git"),
&[(Path::new("a.txt"), diff_base.clone())],
);
// Create the buffer
let buffer_local_a = project_local
@@ -2461,14 +2465,10 @@ async fn test_git_diff_base_change(
);
});
client_a
.fs
.as_fake()
.set_index_for_repo(
Path::new("/dir/.git"),
&[(Path::new("a.txt"), new_diff_base.clone())],
)
.await;
client_a.fs.as_fake().set_index_for_repo(
Path::new("/dir/.git"),
&[(Path::new("a.txt"), new_diff_base.clone())],
);
// Wait for buffer_local_a to receive it
deterministic.run_until_parked();
@@ -2510,14 +2510,10 @@ async fn test_git_diff_base_change(
"
.unindent();
client_a
.fs
.as_fake()
.set_index_for_repo(
Path::new("/dir/sub/.git"),
&[(Path::new("b.txt"), diff_base.clone())],
)
.await;
client_a.fs.as_fake().set_index_for_repo(
Path::new("/dir/sub/.git"),
&[(Path::new("b.txt"), diff_base.clone())],
);
// Create the buffer
let buffer_local_b = project_local
@@ -2559,14 +2555,10 @@ async fn test_git_diff_base_change(
);
});
client_a
.fs
.as_fake()
.set_index_for_repo(
Path::new("/dir/sub/.git"),
&[(Path::new("b.txt"), new_diff_base.clone())],
)
.await;
client_a.fs.as_fake().set_index_for_repo(
Path::new("/dir/sub/.git"),
&[(Path::new("b.txt"), new_diff_base.clone())],
);
// Wait for buffer_local_b to receive it
deterministic.run_until_parked();
@@ -2643,8 +2635,7 @@ async fn test_git_branch_name(
client_a
.fs
.as_fake()
.set_branch_name(Path::new("/dir/.git"), Some("branch-1"))
.await;
.set_branch_name(Path::new("/dir/.git"), Some("branch-1"));
// Wait for it to catch up to the new branch
deterministic.run_until_parked();
@@ -2670,8 +2661,7 @@ async fn test_git_branch_name(
client_a
.fs
.as_fake()
.set_branch_name(Path::new("/dir/.git"), Some("branch-2"))
.await;
.set_branch_name(Path::new("/dir/.git"), Some("branch-2"));
// Wait for buffer_local_a to receive it
deterministic.run_until_parked();
@@ -2685,6 +2675,7 @@ async fn test_git_branch_name(
});
let project_remote_c = client_c.build_remote_project(project_id, cx_c).await;
deterministic.run_until_parked();
project_remote_c.read_with(cx_c, |project, cx| {
assert_branch(Some("branch-2"), project, cx)
});
@@ -2722,17 +2713,13 @@ async fn test_git_status_sync(
const A_TXT: &'static str = "a.txt";
const B_TXT: &'static str = "b.txt";
client_a
.fs
.as_fake()
.set_status_for_repo(
Path::new("/dir/.git"),
&[
(&Path::new(A_TXT), GitFileStatus::Added),
(&Path::new(B_TXT), GitFileStatus::Added),
],
)
.await;
client_a.fs.as_fake().set_status_for_repo_via_git_operation(
Path::new("/dir/.git"),
&[
(&Path::new(A_TXT), GitFileStatus::Added),
(&Path::new(B_TXT), GitFileStatus::Added),
],
);
let (project_local, _worktree_id) = client_a.build_local_project("/dir", cx_a).await;
let project_id = active_call_a
@@ -2759,8 +2746,7 @@ async fn test_git_status_sync(
assert_eq!(worktrees.len(), 1);
let worktree = worktrees[0].clone();
let snapshot = worktree.read(cx).snapshot();
let root_entry = snapshot.root_git_entry().unwrap();
assert_eq!(root_entry.status_for_file(&snapshot, file), status);
assert_eq!(snapshot.status_for_file(file), status);
}
// Smoke test status reading
@@ -2776,14 +2762,13 @@ async fn test_git_status_sync(
client_a
.fs
.as_fake()
.set_status_for_repo(
.set_status_for_repo_via_working_copy_change(
Path::new("/dir/.git"),
&[
(&Path::new(A_TXT), GitFileStatus::Modified),
(&Path::new(B_TXT), GitFileStatus::Modified),
],
)
.await;
);
// Wait for buffer_local_a to receive it
deterministic.run_until_parked();
@@ -3110,6 +3095,135 @@ async fn test_fs_operations(
});
}
#[gpui::test(iterations = 10)]
async fn test_local_settings(
deterministic: Arc<Deterministic>,
cx_a: &mut TestAppContext,
cx_b: &mut TestAppContext,
) {
deterministic.forbid_parking();
let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
.create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
.await;
let active_call_a = cx_a.read(ActiveCall::global);
// As client A, open a project that contains some local settings files
client_a
.fs
.insert_tree(
"/dir",
json!({
".zed": {
"settings.json": r#"{ "tab_size": 2 }"#
},
"a": {
".zed": {
"settings.json": r#"{ "tab_size": 8 }"#
},
"a.txt": "a-contents",
},
"b": {
"b.txt": "b-contents",
}
}),
)
.await;
let (project_a, _) = client_a.build_local_project("/dir", cx_a).await;
let project_id = active_call_a
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
.await
.unwrap();
// As client B, join that project and observe the local settings.
let project_b = client_b.build_remote_project(project_id, cx_b).await;
let worktree_b = project_b.read_with(cx_b, |project, cx| project.worktrees(cx).next().unwrap());
deterministic.run_until_parked();
cx_b.read(|cx| {
let store = cx.global::<SettingsStore>();
assert_eq!(
store.local_settings(worktree_b.id()).collect::<Vec<_>>(),
&[
(Path::new("").into(), r#"{"tab_size":2}"#.to_string()),
(Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
]
)
});
// As client A, update a settings file. As Client B, see the changed settings.
client_a
.fs
.insert_file("/dir/.zed/settings.json", r#"{}"#.into())
.await;
deterministic.run_until_parked();
cx_b.read(|cx| {
let store = cx.global::<SettingsStore>();
assert_eq!(
store.local_settings(worktree_b.id()).collect::<Vec<_>>(),
&[
(Path::new("").into(), r#"{}"#.to_string()),
(Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
]
)
});
// As client A, create and remove some settings files. As client B, see the changed settings.
client_a
.fs
.remove_file("/dir/.zed/settings.json".as_ref(), Default::default())
.await
.unwrap();
client_a
.fs
.create_dir("/dir/b/.zed".as_ref())
.await
.unwrap();
client_a
.fs
.insert_file("/dir/b/.zed/settings.json", r#"{"tab_size": 4}"#.into())
.await;
deterministic.run_until_parked();
cx_b.read(|cx| {
let store = cx.global::<SettingsStore>();
assert_eq!(
store.local_settings(worktree_b.id()).collect::<Vec<_>>(),
&[
(Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
(Path::new("b").into(), r#"{"tab_size":4}"#.to_string()),
]
)
});
// As client B, disconnect.
server.forbid_connections();
server.disconnect_client(client_b.peer_id().unwrap());
// As client A, change and remove settings files while client B is disconnected.
client_a
.fs
.insert_file("/dir/a/.zed/settings.json", r#"{"hard_tabs":true}"#.into())
.await;
client_a
.fs
.remove_file("/dir/b/.zed/settings.json".as_ref(), Default::default())
.await
.unwrap();
deterministic.run_until_parked();
// As client B, reconnect and see the changed settings.
server.allow_connections();
deterministic.advance_clock(RECEIVE_TIMEOUT);
cx_b.read(|cx| {
let store = cx.global::<SettingsStore>();
assert_eq!(
store.local_settings(worktree_b.id()).collect::<Vec<_>>(),
&[(Path::new("a").into(), r#"{"hard_tabs":true}"#.to_string()),]
)
});
}
#[gpui::test(iterations = 10)]
async fn test_buffer_conflict_after_save(
deterministic: Arc<Deterministic>,
@@ -4367,10 +4481,12 @@ async fn test_formatting_buffer(
// Ensure buffer can be formatted using an external command. Notice how the
// host's configuration is honored as opposed to using the guest's settings.
cx_a.update(|cx| {
cx.update_global(|settings: &mut Settings, _| {
settings.editor_defaults.formatter = Some(Formatter::External {
command: "awk".to_string(),
arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()],
cx.update_global(|store: &mut SettingsStore, cx| {
store.update_user_settings::<AllLanguageSettings>(cx, |file| {
file.defaults.formatter = Some(Formatter::External {
command: "awk".into(),
arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()].into(),
});
});
});
});
@@ -5004,19 +5120,21 @@ async fn test_project_symbols(
.unwrap();
let fake_language_server = fake_language_servers.next().await.unwrap();
fake_language_server.handle_request::<lsp::request::WorkspaceSymbol, _, _>(|_, _| async move {
#[allow(deprecated)]
Ok(Some(vec![lsp::SymbolInformation {
name: "TWO".into(),
location: lsp::Location {
uri: lsp::Url::from_file_path("/code/crate-2/two.rs").unwrap(),
range: lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
fake_language_server.handle_request::<lsp::WorkspaceSymbolRequest, _, _>(|_, _| async move {
Ok(Some(lsp::WorkspaceSymbolResponse::Flat(vec![
#[allow(deprecated)]
lsp::SymbolInformation {
name: "TWO".into(),
location: lsp::Location {
uri: lsp::Url::from_file_path("/code/crate-2/two.rs").unwrap(),
range: lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
},
kind: lsp::SymbolKind::CONSTANT,
tags: None,
container_name: None,
deprecated: None,
},
kind: lsp::SymbolKind::CONSTANT,
tags: None,
container_name: None,
deprecated: None,
}]))
])))
});
// Request the definition of a symbol as the guest.
@@ -5137,7 +5255,6 @@ async fn test_collaborating_with_code_actions(
cx_b: &mut TestAppContext,
) {
deterministic.forbid_parking();
cx_b.update(editor::init);
let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
@@ -5146,6 +5263,8 @@ async fn test_collaborating_with_code_actions(
.await;
let active_call_a = cx_a.read(ActiveCall::global);
cx_b.update(editor::init);
// Set up a fake language server.
let mut language = Language::new(
LanguageConfig {
@@ -5350,7 +5469,6 @@ async fn test_collaborating_with_renames(
cx_b: &mut TestAppContext,
) {
deterministic.forbid_parking();
cx_b.update(editor::init);
let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
@@ -5359,6 +5477,8 @@ async fn test_collaborating_with_renames(
.await;
let active_call_a = cx_a.read(ActiveCall::global);
cx_b.update(editor::init);
// Set up a fake language server.
let mut language = Language::new(
LanguageConfig {
@@ -5540,8 +5660,6 @@ async fn test_language_server_statuses(
cx_b: &mut TestAppContext,
) {
deterministic.forbid_parking();
cx_b.update(editor::init);
let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
@@ -5550,6 +5668,8 @@ async fn test_language_server_statuses(
.await;
let active_call_a = cx_a.read(ActiveCall::global);
cx_b.update(editor::init);
// Set up a fake language server.
let mut language = Language::new(
LanguageConfig {
@@ -6257,8 +6377,6 @@ async fn test_basic_following(
cx_d: &mut TestAppContext,
) {
deterministic.forbid_parking();
cx_a.update(editor::init);
cx_b.update(editor::init);
let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
@@ -6276,6 +6394,9 @@ async fn test_basic_following(
let active_call_a = cx_a.read(ActiveCall::global);
let active_call_b = cx_b.read(ActiveCall::global);
cx_a.update(editor::init);
cx_b.update(editor::init);
client_a
.fs
.insert_tree(
@@ -6597,7 +6718,7 @@ async fn test_basic_following(
// When client A navigates back and forth, client B does so as well.
workspace_a
.update(cx_a, |workspace, cx| {
workspace::Pane::go_back(workspace, None, cx)
workspace.go_back(workspace.active_pane().downgrade(), cx)
})
.await
.unwrap();
@@ -6608,7 +6729,7 @@ async fn test_basic_following(
workspace_a
.update(cx_a, |workspace, cx| {
workspace::Pane::go_back(workspace, None, cx)
workspace.go_back(workspace.active_pane().downgrade(), cx)
})
.await
.unwrap();
@@ -6619,7 +6740,7 @@ async fn test_basic_following(
workspace_a
.update(cx_a, |workspace, cx| {
workspace::Pane::go_forward(workspace, None, cx)
workspace.go_forward(workspace.active_pane().downgrade(), cx)
})
.await
.unwrap();
@@ -6731,12 +6852,43 @@ async fn test_basic_following(
)
});
// Client B activates an external window again, and the previously-opened screen-sharing item
// gets activated.
active_call_b
.update(cx_b, |call, cx| call.set_location(None, cx))
.await
.unwrap();
// Client B activates a panel, and the previously-opened screen-sharing item gets activated.
let panel = cx_b.add_view(workspace_b.window_id(), |_| {
TestPanel::new(DockPosition::Left)
});
workspace_b.update(cx_b, |workspace, cx| {
workspace.add_panel(panel, cx);
workspace.toggle_panel_focus::<TestPanel>(cx);
});
deterministic.run_until_parked();
assert_eq!(
workspace_a.read_with(cx_a, |workspace, cx| workspace
.active_item(cx)
.unwrap()
.id()),
shared_screen.id()
);
// Toggling the focus back to the pane causes client A to return to the multibuffer.
workspace_b.update(cx_b, |workspace, cx| {
workspace.toggle_panel_focus::<TestPanel>(cx);
});
deterministic.run_until_parked();
workspace_a.read_with(cx_a, |workspace, cx| {
assert_eq!(
workspace.active_item(cx).unwrap().id(),
multibuffer_editor_a.id()
)
});
// Client B activates an item that doesn't implement following,
// so the previously-opened screen-sharing item gets activated.
let unfollowable_item = cx_b.add_view(workspace_b.window_id(), |_| TestItem::new());
workspace_b.update(cx_b, |workspace, cx| {
workspace.active_pane().update(cx, |pane, cx| {
pane.add_item(Box::new(unfollowable_item), true, true, None, cx)
})
});
deterministic.run_until_parked();
assert_eq!(
workspace_a.read_with(cx_a, |workspace, cx| workspace
@@ -6854,9 +7006,6 @@ async fn test_following_tab_order(
cx_a: &mut TestAppContext,
cx_b: &mut TestAppContext,
) {
cx_a.update(editor::init);
cx_b.update(editor::init);
let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
@@ -6866,6 +7015,9 @@ async fn test_following_tab_order(
let active_call_a = cx_a.read(ActiveCall::global);
let active_call_b = cx_b.read(ActiveCall::global);
cx_a.update(editor::init);
cx_b.update(editor::init);
client_a
.fs
.insert_tree(
@@ -6976,9 +7128,6 @@ async fn test_peers_following_each_other(
cx_b: &mut TestAppContext,
) {
deterministic.forbid_parking();
cx_a.update(editor::init);
cx_b.update(editor::init);
let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
@@ -6988,6 +7137,9 @@ async fn test_peers_following_each_other(
let active_call_a = cx_a.read(ActiveCall::global);
let active_call_b = cx_b.read(ActiveCall::global);
cx_a.update(editor::init);
cx_b.update(editor::init);
// Client A shares a project.
client_a
.fs
@@ -7147,8 +7299,6 @@ async fn test_auto_unfollowing(
cx_b: &mut TestAppContext,
) {
deterministic.forbid_parking();
cx_a.update(editor::init);
cx_b.update(editor::init);
// 2 clients connect to a server.
let mut server = TestServer::start(&deterministic).await;
@@ -7160,6 +7310,9 @@ async fn test_auto_unfollowing(
let active_call_a = cx_a.read(ActiveCall::global);
let active_call_b = cx_b.read(ActiveCall::global);
cx_a.update(editor::init);
cx_b.update(editor::init);
// Client A shares a project.
client_a
.fs
@@ -7314,8 +7467,6 @@ async fn test_peers_simultaneously_following_each_other(
cx_b: &mut TestAppContext,
) {
deterministic.forbid_parking();
cx_a.update(editor::init);
cx_b.update(editor::init);
let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
@@ -7325,6 +7476,9 @@ async fn test_peers_simultaneously_following_each_other(
.await;
let active_call_a = cx_a.read(ActiveCall::global);
cx_a.update(editor::init);
cx_b.update(editor::init);
client_a.fs.insert_tree("/a", json!({})).await;
let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
let workspace_a = client_a.build_workspace(&project_a, cx_a);
@@ -7366,6 +7520,265 @@ async fn test_peers_simultaneously_following_each_other(
});
}
#[gpui::test(iterations = 10)]
async fn test_on_input_format_from_host_to_guest(
deterministic: Arc<Deterministic>,
cx_a: &mut TestAppContext,
cx_b: &mut TestAppContext,
) {
deterministic.forbid_parking();
let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
.create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
.await;
let active_call_a = cx_a.read(ActiveCall::global);
// Set up a fake language server.
let mut language = Language::new(
LanguageConfig {
name: "Rust".into(),
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
);
let mut fake_language_servers = language
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
capabilities: lsp::ServerCapabilities {
document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
first_trigger_character: ":".to_string(),
more_trigger_character: Some(vec![">".to_string()]),
}),
..Default::default()
},
..Default::default()
}))
.await;
client_a.language_registry.add(Arc::new(language));
client_a
.fs
.insert_tree(
"/a",
json!({
"main.rs": "fn main() { a }",
"other.rs": "// Test file",
}),
)
.await;
let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
let project_id = active_call_a
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
.await
.unwrap();
let project_b = client_b.build_remote_project(project_id, cx_b).await;
// Open a file in an editor as the host.
let buffer_a = project_a
.update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
.await
.unwrap();
let (window_a, _) = cx_a.add_window(|_| EmptyView);
let editor_a = cx_a.add_view(window_a, |cx| {
Editor::for_buffer(buffer_a, Some(project_a.clone()), cx)
});
let fake_language_server = fake_language_servers.next().await.unwrap();
cx_b.foreground().run_until_parked();
// Receive an OnTypeFormatting request as the host's language server.
// Return some formattings from the host's language server.
fake_language_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(
|params, _| async move {
assert_eq!(
params.text_document_position.text_document.uri,
lsp::Url::from_file_path("/a/main.rs").unwrap(),
);
assert_eq!(
params.text_document_position.position,
lsp::Position::new(0, 14),
);
Ok(Some(vec![lsp::TextEdit {
new_text: "~<".to_string(),
range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
}]))
},
);
// Open the buffer on the guest and see that the formattings worked
let buffer_b = project_b
.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
.await
.unwrap();
// Type a on type formatting trigger character as the guest.
editor_a.update(cx_a, |editor, cx| {
cx.focus(&editor_a);
editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
editor.handle_input(">", cx);
});
cx_b.foreground().run_until_parked();
buffer_b.read_with(cx_b, |buffer, _| {
assert_eq!(buffer.text(), "fn main() { a>~< }")
});
// Undo should remove LSP edits first
editor_a.update(cx_a, |editor, cx| {
assert_eq!(editor.text(cx), "fn main() { a>~< }");
editor.undo(&Undo, cx);
assert_eq!(editor.text(cx), "fn main() { a> }");
});
cx_b.foreground().run_until_parked();
buffer_b.read_with(cx_b, |buffer, _| {
assert_eq!(buffer.text(), "fn main() { a> }")
});
editor_a.update(cx_a, |editor, cx| {
assert_eq!(editor.text(cx), "fn main() { a> }");
editor.undo(&Undo, cx);
assert_eq!(editor.text(cx), "fn main() { a }");
});
cx_b.foreground().run_until_parked();
buffer_b.read_with(cx_b, |buffer, _| {
assert_eq!(buffer.text(), "fn main() { a }")
});
}
#[gpui::test(iterations = 10)]
async fn test_on_input_format_from_guest_to_host(
deterministic: Arc<Deterministic>,
cx_a: &mut TestAppContext,
cx_b: &mut TestAppContext,
) {
deterministic.forbid_parking();
let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
.create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
.await;
let active_call_a = cx_a.read(ActiveCall::global);
// Set up a fake language server.
let mut language = Language::new(
LanguageConfig {
name: "Rust".into(),
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
);
let mut fake_language_servers = language
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
capabilities: lsp::ServerCapabilities {
document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
first_trigger_character: ":".to_string(),
more_trigger_character: Some(vec![">".to_string()]),
}),
..Default::default()
},
..Default::default()
}))
.await;
client_a.language_registry.add(Arc::new(language));
client_a
.fs
.insert_tree(
"/a",
json!({
"main.rs": "fn main() { a }",
"other.rs": "// Test file",
}),
)
.await;
let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
let project_id = active_call_a
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
.await
.unwrap();
let project_b = client_b.build_remote_project(project_id, cx_b).await;
// Open a file in an editor as the guest.
let buffer_b = project_b
.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
.await
.unwrap();
let (window_b, _) = cx_b.add_window(|_| EmptyView);
let editor_b = cx_b.add_view(window_b, |cx| {
Editor::for_buffer(buffer_b, Some(project_b.clone()), cx)
});
let fake_language_server = fake_language_servers.next().await.unwrap();
cx_a.foreground().run_until_parked();
// Type a on type formatting trigger character as the guest.
editor_b.update(cx_b, |editor, cx| {
editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
editor.handle_input(":", cx);
cx.focus(&editor_b);
});
// Receive an OnTypeFormatting request as the host's language server.
// Return some formattings from the host's language server.
cx_a.foreground().start_waiting();
fake_language_server
.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
assert_eq!(
params.text_document_position.text_document.uri,
lsp::Url::from_file_path("/a/main.rs").unwrap(),
);
assert_eq!(
params.text_document_position.position,
lsp::Position::new(0, 14),
);
Ok(Some(vec![lsp::TextEdit {
new_text: "~:".to_string(),
range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
}]))
})
.next()
.await
.unwrap();
cx_a.foreground().finish_waiting();
// Open the buffer on the host and see that the formattings worked
let buffer_a = project_a
.update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
.await
.unwrap();
cx_a.foreground().run_until_parked();
buffer_a.read_with(cx_a, |buffer, _| {
assert_eq!(buffer.text(), "fn main() { a:~: }")
});
// Undo should remove LSP edits first
editor_b.update(cx_b, |editor, cx| {
assert_eq!(editor.text(cx), "fn main() { a:~: }");
editor.undo(&Undo, cx);
assert_eq!(editor.text(cx), "fn main() { a: }");
});
cx_a.foreground().run_until_parked();
buffer_a.read_with(cx_a, |buffer, _| {
assert_eq!(buffer.text(), "fn main() { a: }")
});
editor_b.update(cx_b, |editor, cx| {
assert_eq!(editor.text(cx), "fn main() { a: }");
editor.undo(&Undo, cx);
assert_eq!(editor.text(cx), "fn main() { a }");
});
cx_a.foreground().run_until_parked();
buffer_a.read_with(cx_a, |buffer, _| {
assert_eq!(buffer.text(), "fn main() { a }")
});
}
#[derive(Debug, Eq, PartialEq)]
struct RoomParticipants {
remote: Vec<String>,

View File

@@ -21,7 +21,7 @@ use rand::{
prelude::*,
};
use serde::{Deserialize, Serialize};
use settings::Settings;
use settings::SettingsStore;
use std::{
env,
ops::Range,
@@ -149,8 +149,9 @@ async fn test_random_collaboration(
for (client, mut cx) in clients {
cx.update(|cx| {
let store = cx.remove_global::<SettingsStore>();
cx.clear_globals();
cx.set_global(Settings::test(cx));
cx.set_global(store);
drop(client);
});
}
@@ -421,7 +422,7 @@ async fn apply_client_operation(
);
ensure_project_shared(&project, client, cx).await;
if !client.fs.paths().contains(&new_root_path) {
if !client.fs.paths(false).contains(&new_root_path) {
client.fs.create_dir(&new_root_path).await.unwrap();
}
project
@@ -627,12 +628,13 @@ async fn apply_client_operation(
ensure_project_shared(&project, client, cx).await;
let requested_version = buffer.read_with(cx, |buffer, _| buffer.version());
let save = project.update(cx, |project, cx| project.save_buffer(buffer, cx));
let save = cx.background().spawn(async move {
let (saved_version, _, _) = save
.await
let save = project.update(cx, |project, cx| project.save_buffer(buffer.clone(), cx));
let save = cx.spawn(|cx| async move {
save.await
.map_err(|err| anyhow!("save request failed: {:?}", err))?;
assert!(saved_version.observed_all(&requested_version));
assert!(buffer
.read_with(&cx, |buffer, _| { buffer.saved_version().to_owned() })
.observed_all(&requested_version));
anyhow::Ok(())
});
if detach {
@@ -742,7 +744,7 @@ async fn apply_client_operation(
} => {
if !client
.fs
.directories()
.directories(false)
.contains(&path.parent().unwrap().to_owned())
{
return Err(TestError::Inapplicable);
@@ -769,10 +771,16 @@ async fn apply_client_operation(
repo_path,
contents,
} => {
if !client.fs.directories().contains(&repo_path) {
if !client.fs.directories(false).contains(&repo_path) {
return Err(TestError::Inapplicable);
}
for (path, _) in contents.iter() {
if !client.fs.files().contains(&repo_path.join(path)) {
return Err(TestError::Inapplicable);
}
}
log::info!(
"{}: writing git index for repo {:?}: {:?}",
client.username,
@@ -788,13 +796,13 @@ async fn apply_client_operation(
if client.fs.metadata(&dot_git_dir).await?.is_none() {
client.fs.create_dir(&dot_git_dir).await?;
}
client.fs.set_index_for_repo(&dot_git_dir, &contents).await;
client.fs.set_index_for_repo(&dot_git_dir, &contents);
}
GitOperation::WriteGitBranch {
repo_path,
new_branch,
} => {
if !client.fs.directories().contains(&repo_path) {
if !client.fs.directories(false).contains(&repo_path) {
return Err(TestError::Inapplicable);
}
@@ -809,15 +817,21 @@ async fn apply_client_operation(
if client.fs.metadata(&dot_git_dir).await?.is_none() {
client.fs.create_dir(&dot_git_dir).await?;
}
client.fs.set_branch_name(&dot_git_dir, new_branch).await;
client.fs.set_branch_name(&dot_git_dir, new_branch);
}
GitOperation::WriteGitStatuses {
repo_path,
statuses,
git_operation,
} => {
if !client.fs.directories().contains(&repo_path) {
if !client.fs.directories(false).contains(&repo_path) {
return Err(TestError::Inapplicable);
}
for (path, _) in statuses.iter() {
if !client.fs.files().contains(&repo_path.join(path)) {
return Err(TestError::Inapplicable);
}
}
log::info!(
"{}: writing git statuses for repo {:?}: {:?}",
@@ -837,10 +851,16 @@ async fn apply_client_operation(
client.fs.create_dir(&dot_git_dir).await?;
}
client
.fs
.set_status_for_repo(&dot_git_dir, statuses.as_slice())
.await;
if git_operation {
client
.fs
.set_status_for_repo_via_git_operation(&dot_git_dir, statuses.as_slice());
} else {
client.fs.set_status_for_repo_via_working_copy_change(
&dot_git_dir,
statuses.as_slice(),
);
}
}
},
}
@@ -912,9 +932,10 @@ fn check_consistency_between_clients(clients: &[(Rc<TestClient>, TestAppContext)
assert_eq!(
guest_snapshot.entries(false).collect::<Vec<_>>(),
host_snapshot.entries(false).collect::<Vec<_>>(),
"{} has different snapshot than the host for worktree {:?} and project {:?}",
"{} has different snapshot than the host for worktree {:?} ({:?}) and project {:?}",
client.username,
host_snapshot.abs_path(),
id,
guest_project.remote_id(),
);
assert_eq!(guest_snapshot.repositories().collect::<Vec<_>>(), host_snapshot.repositories().collect::<Vec<_>>(),
@@ -1229,6 +1250,7 @@ enum GitOperation {
WriteGitStatuses {
repo_path: PathBuf,
statuses: Vec<(PathBuf, GitFileStatus)>,
git_operation: bool,
},
}
@@ -1574,7 +1596,7 @@ impl TestPlan {
.choose(&mut self.rng)
.cloned() else { continue };
let project_root_name = root_name_for_project(&project, cx);
let mut paths = client.fs.paths();
let mut paths = client.fs.paths(false);
paths.remove(0);
let new_root_path = if paths.is_empty() || self.rng.gen() {
Path::new("/").join(&self.next_root_dir_name(user_id))
@@ -1754,7 +1776,7 @@ impl TestPlan {
let is_dir = self.rng.gen::<bool>();
let content;
let mut path;
let dir_paths = client.fs.directories();
let dir_paths = client.fs.directories(false);
if is_dir {
content = String::new();
@@ -1808,7 +1830,7 @@ impl TestPlan {
let repo_path = client
.fs
.directories()
.directories(false)
.choose(&mut self.rng)
.unwrap()
.clone();
@@ -1854,9 +1876,12 @@ impl TestPlan {
})
.collect::<Vec<_>>();
let git_operation = self.rng.gen::<bool>();
GitOperation::WriteGitStatuses {
repo_path,
statuses,
git_operation,
}
}
_ => unreachable!(),

View File

@@ -18,7 +18,6 @@ use gpui::{
ViewContext, ViewHandle, WeakViewHandle,
};
use project::Project;
use settings::Settings;
use std::{ops::Range, sync::Arc};
use theme::{AvatarStyle, Theme};
use util::ResultExt;
@@ -70,7 +69,7 @@ impl View for CollabTitlebarItem {
};
let project = self.project.read(cx);
let theme = cx.global::<Settings>().theme.clone();
let theme = theme::current(cx).clone();
let mut left_container = Flex::row();
let mut right_container = Flex::row().align_children_center();
@@ -298,7 +297,7 @@ impl CollabTitlebarItem {
}
pub fn toggle_user_menu(&mut self, _: &ToggleUserMenu, cx: &mut ViewContext<Self>) {
let theme = cx.global::<Settings>().theme.clone();
let theme = theme::current(cx).clone();
let avatar_style = theme.workspace.titlebar.leader_avatar.clone();
let item_style = theme.context_menu.item.disabled_style().clone();
self.user_menu.update(cx, |user_menu, cx| {
@@ -473,7 +472,7 @@ impl CollabTitlebarItem {
Stack::new()
.with_child(
MouseEventHandler::<ShareUnshare, Self>::new(0, cx, |state, _| {
//TODO: Ensure this button has consistant width for both text variations
//TODO: Ensure this button has consistent width for both text variations
let style = titlebar.share_button.style_for(state, false);
Label::new(label, style.text.clone())
.contained()
@@ -866,7 +865,7 @@ impl CollabTitlebarItem {
) -> Option<AnyElement<Self>> {
enum ConnectionStatusButton {}
let theme = &cx.global::<Settings>().theme.clone();
let theme = &theme::current(cx).clone();
match status {
client::Status::ConnectionError
| client::Status::ConnectionLost

View File

@@ -1,7 +1,6 @@
use client::{ContactRequestStatus, User, UserStore};
use gpui::{elements::*, AppContext, ModelHandle, MouseState, Task, ViewContext};
use picker::{Picker, PickerDelegate, PickerEvent};
use settings::Settings;
use std::sync::Arc;
use util::TryFutureExt;
@@ -98,7 +97,7 @@ impl PickerDelegate for ContactFinderDelegate {
selected: bool,
cx: &gpui::AppContext,
) -> AnyElement<Picker<Self>> {
let theme = &cx.global::<Settings>().theme;
let theme = &theme::current(cx);
let user = &self.potential_contacts[ix];
let request_status = self.user_store.read(cx).contact_request_status(user);

View File

@@ -14,7 +14,6 @@ use gpui::{
use menu::{Confirm, SelectNext, SelectPrev};
use project::Project;
use serde::Deserialize;
use settings::Settings;
use std::{mem, sync::Arc};
use theme::IconButton;
use workspace::Workspace;
@@ -192,7 +191,7 @@ impl ContactList {
.detach();
let list_state = ListState::<Self>::new(0, Orientation::Top, 1000., move |this, ix, cx| {
let theme = cx.global::<Settings>().theme.clone();
let theme = theme::current(cx).clone();
let is_selected = this.selection == Some(ix);
let current_project_id = this.project.read(cx).remote_id();
@@ -1313,7 +1312,7 @@ impl View for ContactList {
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
enum AddContact {}
let theme = cx.global::<Settings>().theme.clone();
let theme = theme::current(cx).clone();
Flex::column()
.with_child(

View File

@@ -9,7 +9,6 @@ use gpui::{
};
use picker::PickerEvent;
use project::Project;
use settings::Settings;
use workspace::Workspace;
actions!(contacts_popover, [ToggleContactFinder]);
@@ -108,7 +107,7 @@ impl View for ContactsPopover {
}
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
let theme = cx.global::<Settings>().theme.clone();
let theme = theme::current(cx).clone();
let child = match &self.child {
Child::ContactList(child) => ChildView::new(child, cx),
Child::ContactFinder(child) => ChildView::new(child, cx),

View File

@@ -9,7 +9,6 @@ use gpui::{
platform::{CursorStyle, MouseButton, WindowBounds, WindowKind, WindowOptions},
AnyElement, AppContext, Entity, View, ViewContext,
};
use settings::Settings;
use util::ResultExt;
use workspace::AppState;
@@ -26,7 +25,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
if let Some(incoming_call) = incoming_call {
const PADDING: f32 = 16.;
let window_size = cx.read(|cx| {
let theme = &cx.global::<Settings>().theme.incoming_call_notification;
let theme = &theme::current(cx).incoming_call_notification;
vec2f(theme.window_width, theme.window_height)
});
@@ -42,6 +41,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
titlebar: None,
center: false,
focus: false,
show: true,
kind: WindowKind::PopUp,
is_movable: false,
screen: Some(screen),
@@ -106,7 +106,7 @@ impl IncomingCallNotification {
}
fn render_caller(&self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
let theme = &cx.global::<Settings>().theme.incoming_call_notification;
let theme = &theme::current(cx).incoming_call_notification;
let default_project = proto::ParticipantProject::default();
let initial_project = self
.call
@@ -170,10 +170,11 @@ impl IncomingCallNotification {
enum Accept {}
enum Decline {}
let theme = theme::current(cx);
Flex::column()
.with_child(
MouseEventHandler::<Accept, Self>::new(0, cx, |_, cx| {
let theme = &cx.global::<Settings>().theme.incoming_call_notification;
MouseEventHandler::<Accept, Self>::new(0, cx, |_, _| {
let theme = &theme.incoming_call_notification;
Label::new("Accept", theme.accept_button.text.clone())
.aligned()
.contained()
@@ -186,8 +187,8 @@ impl IncomingCallNotification {
.flex(1., true),
)
.with_child(
MouseEventHandler::<Decline, Self>::new(0, cx, |_, cx| {
let theme = &cx.global::<Settings>().theme.incoming_call_notification;
MouseEventHandler::<Decline, Self>::new(0, cx, |_, _| {
let theme = &theme.incoming_call_notification;
Label::new("Decline", theme.decline_button.text.clone())
.aligned()
.contained()
@@ -200,12 +201,7 @@ impl IncomingCallNotification {
.flex(1., true),
)
.constrained()
.with_width(
cx.global::<Settings>()
.theme
.incoming_call_notification
.button_width,
)
.with_width(theme.incoming_call_notification.button_width)
.into_any()
}
}
@@ -220,12 +216,7 @@ impl View for IncomingCallNotification {
}
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
let background = cx
.global::<Settings>()
.theme
.incoming_call_notification
.background;
let background = theme::current(cx).incoming_call_notification.background;
Flex::row()
.with_child(self.render_caller(cx))
.with_child(self.render_buttons(cx))

View File

@@ -4,7 +4,6 @@ use gpui::{
platform::{CursorStyle, MouseButton},
AnyElement, Element, View, ViewContext,
};
use settings::Settings;
use std::sync::Arc;
enum Dismiss {}
@@ -22,7 +21,7 @@ where
F: 'static + Fn(&mut V, &mut ViewContext<V>),
V: View,
{
let theme = cx.global::<Settings>().theme.clone();
let theme = theme::current(cx).clone();
let theme = &theme.contact_notification;
Flex::column()

View File

@@ -7,7 +7,6 @@ use gpui::{
platform::{CursorStyle, MouseButton, WindowBounds, WindowKind, WindowOptions},
AppContext, Entity, View, ViewContext,
};
use settings::Settings;
use std::sync::{Arc, Weak};
use workspace::AppState;
@@ -22,7 +21,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
worktree_root_names,
} => {
const PADDING: f32 = 16.;
let theme = &cx.global::<Settings>().theme.project_shared_notification;
let theme = &theme::current(cx).project_shared_notification;
let window_size = vec2f(theme.window_width, theme.window_height);
for screen in cx.platform().screens() {
@@ -36,6 +35,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
titlebar: None,
center: false,
focus: false,
show: true,
kind: WindowKind::PopUp,
is_movable: false,
screen: Some(screen),
@@ -109,7 +109,7 @@ impl ProjectSharedNotification {
}
fn render_owner(&self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
let theme = &cx.global::<Settings>().theme.project_shared_notification;
let theme = &theme::current(cx).project_shared_notification;
Flex::row()
.with_children(self.owner.avatar.clone().map(|avatar| {
Image::from_data(avatar)
@@ -167,10 +167,11 @@ impl ProjectSharedNotification {
enum Open {}
enum Dismiss {}
let theme = theme::current(cx);
Flex::column()
.with_child(
MouseEventHandler::<Open, Self>::new(0, cx, |_, cx| {
let theme = &cx.global::<Settings>().theme.project_shared_notification;
MouseEventHandler::<Open, Self>::new(0, cx, |_, _| {
let theme = &theme.project_shared_notification;
Label::new("Open", theme.open_button.text.clone())
.aligned()
.contained()
@@ -181,8 +182,8 @@ impl ProjectSharedNotification {
.flex(1., true),
)
.with_child(
MouseEventHandler::<Dismiss, Self>::new(0, cx, |_, cx| {
let theme = &cx.global::<Settings>().theme.project_shared_notification;
MouseEventHandler::<Dismiss, Self>::new(0, cx, |_, _| {
let theme = &theme.project_shared_notification;
Label::new("Dismiss", theme.dismiss_button.text.clone())
.aligned()
.contained()
@@ -195,12 +196,7 @@ impl ProjectSharedNotification {
.flex(1., true),
)
.constrained()
.with_width(
cx.global::<Settings>()
.theme
.project_shared_notification
.button_width,
)
.with_width(theme.project_shared_notification.button_width)
.into_any()
}
}
@@ -215,11 +211,7 @@ impl View for ProjectSharedNotification {
}
fn render(&mut self, cx: &mut ViewContext<Self>) -> gpui::AnyElement<Self> {
let background = cx
.global::<Settings>()
.theme
.project_shared_notification
.background;
let background = theme::current(cx).project_shared_notification.background;
Flex::row()
.with_child(self.render_owner(cx))
.with_child(self.render_buttons(cx))

View File

@@ -6,7 +6,7 @@ use gpui::{
platform::{Appearance, MouseButton},
AnyElement, AppContext, Element, Entity, View, ViewContext,
};
use settings::Settings;
use workspace::WorkspaceSettings;
pub fn init(cx: &mut AppContext) {
let active_call = ActiveCall::global(cx);
@@ -15,7 +15,9 @@ pub fn init(cx: &mut AppContext) {
cx.observe(&active_call, move |call, cx| {
if let Some(room) = call.read(cx).room() {
if room.read(cx).is_screen_sharing() {
if status_indicator.is_none() && cx.global::<Settings>().show_call_status_icon {
if status_indicator.is_none()
&& settings::get::<WorkspaceSettings>(cx).show_call_status_icon
{
status_indicator = Some(cx.add_status_bar_item(|_| SharingStatusIndicator));
}
} else if let Some((window_id, _)) = status_indicator.take() {

View File

@@ -23,6 +23,7 @@ workspace = { path = "../workspace" }
[dev-dependencies]
gpui = { path = "../gpui", features = ["test-support"] }
editor = { path = "../editor", features = ["test-support"] }
language = { path = "../language", features = ["test-support"] }
project = { path = "../project", features = ["test-support"] }
serde_json.workspace = true
workspace = { path = "../workspace", features = ["test-support"] }

View File

@@ -5,7 +5,6 @@ use gpui::{
ViewContext,
};
use picker::{Picker, PickerDelegate, PickerEvent};
use settings::Settings;
use std::cmp;
use util::ResultExt;
use workspace::Workspace;
@@ -185,8 +184,7 @@ impl PickerDelegate for CommandPaletteDelegate {
) -> AnyElement<Picker<Self>> {
let mat = &self.matches[ix];
let command = &self.actions[mat.candidate_id];
let settings = cx.global::<Settings>();
let theme = &settings.theme;
let theme = theme::current(cx);
let style = theme.picker.item.style_for(mouse_state, selected);
let key_style = &theme.command_palette.key.style_for(mouse_state, selected);
let keystroke_spacing = theme.command_palette.keystroke_spacing;
@@ -294,14 +292,7 @@ mod tests {
#[gpui::test]
async fn test_command_palette(deterministic: Arc<Deterministic>, cx: &mut TestAppContext) {
deterministic.forbid_parking();
let app_state = cx.update(AppState::test);
cx.update(|cx| {
editor::init(cx);
workspace::init(app_state.clone(), cx);
init(cx);
});
let app_state = init_test(cx);
let project = Project::test(app_state.fs.clone(), [], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
@@ -369,4 +360,16 @@ mod tests {
assert!(palette.delegate().matches.is_empty())
});
}
fn init_test(cx: &mut TestAppContext) -> Arc<AppState> {
cx.update(|cx| {
let app_state = AppState::test(cx);
theme::init((), cx);
language::init(cx);
editor::init(cx);
workspace::init(app_state.clone(), cx);
init(cx);
app_state
})
}
}

View File

@@ -8,7 +8,6 @@ use gpui::{
View, ViewContext,
};
use menu::*;
use settings::Settings;
use std::{any::TypeId, borrow::Cow, sync::Arc, time::Duration};
pub fn init(cx: &mut AppContext) {
@@ -323,7 +322,7 @@ impl ContextMenu {
}
fn render_menu_for_measurement(&self, cx: &mut ViewContext<Self>) -> impl Element<ContextMenu> {
let style = cx.global::<Settings>().theme.context_menu.clone();
let style = theme::current(cx).context_menu.clone();
Flex::row()
.with_child(
Flex::column().with_children(self.items.iter().enumerate().map(|(ix, item)| {
@@ -403,7 +402,7 @@ impl ContextMenu {
enum Menu {}
enum MenuItem {}
let style = cx.global::<Settings>().theme.context_menu.clone();
let style = theme::current(cx).context_menu.clone();
MouseEventHandler::<Menu, ContextMenu>::new(0, cx, |_, cx| {
Flex::column()

View File

@@ -4,12 +4,13 @@ mod sign_in;
use anyhow::{anyhow, Context, Result};
use async_compression::futures::bufread::GzipDecoder;
use async_tar::Archive;
use collections::HashMap;
use collections::{HashMap, HashSet};
use futures::{channel::oneshot, future::Shared, Future, FutureExt, TryFutureExt};
use gpui::{
actions, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Task, WeakModelHandle,
};
use language::{
language_settings::{all_language_settings, language_settings},
point_from_lsp, point_to_lsp, Anchor, Bias, Buffer, BufferSnapshot, Language, PointUtf16,
ToPointUtf16,
};
@@ -17,7 +18,7 @@ use log::{debug, error};
use lsp::{LanguageServer, LanguageServerId};
use node_runtime::NodeRuntime;
use request::{LogMessage, StatusNotification};
use settings::Settings;
use settings::SettingsStore;
use smol::{fs, io::BufReader, stream::StreamExt};
use std::{
ffi::OsString,
@@ -126,7 +127,7 @@ impl CopilotServer {
struct RunningCopilotServer {
lsp: Arc<LanguageServer>,
sign_in_status: SignInStatus,
registered_buffers: HashMap<u64, RegisteredBuffer>,
registered_buffers: HashMap<usize, RegisteredBuffer>,
}
#[derive(Clone, Debug)]
@@ -162,7 +163,6 @@ impl Status {
}
struct RegisteredBuffer {
id: u64,
uri: lsp::Url,
language_id: String,
snapshot: BufferSnapshot,
@@ -177,13 +177,13 @@ impl RegisteredBuffer {
buffer: &ModelHandle<Buffer>,
cx: &mut ModelContext<Copilot>,
) -> oneshot::Receiver<(i32, BufferSnapshot)> {
let id = self.id;
let (done_tx, done_rx) = oneshot::channel();
if buffer.read(cx).version() == self.snapshot.version {
let _ = done_tx.send((self.snapshot_version, self.snapshot.clone()));
} else {
let buffer = buffer.downgrade();
let id = buffer.id();
let prev_pending_change =
mem::replace(&mut self.pending_buffer_change, Task::ready(None));
self.pending_buffer_change = cx.spawn_weak(|copilot, mut cx| async move {
@@ -267,7 +267,7 @@ pub struct Copilot {
http: Arc<dyn HttpClient>,
node_runtime: Arc<NodeRuntime>,
server: CopilotServer,
buffers: HashMap<u64, WeakModelHandle<Buffer>>,
buffers: HashSet<WeakModelHandle<Buffer>>,
}
impl Entity for Copilot {
@@ -302,56 +302,34 @@ impl Copilot {
node_runtime: Arc<NodeRuntime>,
cx: &mut ModelContext<Self>,
) -> Self {
cx.observe_global::<Settings, _>({
let http = http.clone();
let node_runtime = node_runtime.clone();
move |this, cx| {
if cx.global::<Settings>().features.copilot {
if matches!(this.server, CopilotServer::Disabled) {
let start_task = cx
.spawn({
let http = http.clone();
let node_runtime = node_runtime.clone();
move |this, cx| {
Self::start_language_server(http, node_runtime, this, cx)
}
})
.shared();
this.server = CopilotServer::Starting { task: start_task };
cx.notify();
}
} else {
this.server = CopilotServer::Disabled;
cx.notify();
}
}
})
.detach();
let mut this = Self {
http,
node_runtime,
server: CopilotServer::Disabled,
buffers: Default::default(),
};
this.enable_or_disable_copilot(cx);
cx.observe_global::<SettingsStore, _>(move |this, cx| this.enable_or_disable_copilot(cx))
.detach();
this
}
if cx.global::<Settings>().features.copilot {
let start_task = cx
.spawn({
let http = http.clone();
let node_runtime = node_runtime.clone();
move |this, cx| async {
Self::start_language_server(http, node_runtime, this, cx).await
}
})
.shared();
Self {
http,
node_runtime,
server: CopilotServer::Starting { task: start_task },
buffers: Default::default(),
fn enable_or_disable_copilot(&mut self, cx: &mut ModelContext<Copilot>) {
let http = self.http.clone();
let node_runtime = self.node_runtime.clone();
if all_language_settings(None, cx).copilot_enabled(None, None) {
if matches!(self.server, CopilotServer::Disabled) {
let start_task = cx
.spawn({
move |this, cx| Self::start_language_server(http, node_runtime, this, cx)
})
.shared();
self.server = CopilotServer::Starting { task: start_task };
cx.notify();
}
} else {
Self {
http,
node_runtime,
server: CopilotServer::Disabled,
buffers: Default::default(),
}
self.server = CopilotServer::Disabled;
cx.notify();
}
}
@@ -396,7 +374,7 @@ impl Copilot {
server
.on_notification::<LogMessage, _>(|params, _cx| {
match params.level {
// Copilot is pretty agressive about logging
// Copilot is pretty aggressive about logging
0 => debug!("copilot: {}", params.message),
1 => debug!("copilot: {}", params.message),
_ => error!("copilot: {}", params.message),
@@ -580,8 +558,8 @@ impl Copilot {
}
pub fn register_buffer(&mut self, buffer: &ModelHandle<Buffer>, cx: &mut ModelContext<Self>) {
let buffer_id = buffer.read(cx).remote_id();
self.buffers.insert(buffer_id, buffer.downgrade());
let weak_buffer = buffer.downgrade();
self.buffers.insert(weak_buffer.clone());
if let CopilotServer::Running(RunningCopilotServer {
lsp: server,
@@ -594,8 +572,7 @@ impl Copilot {
return;
}
let buffer_id = buffer.read(cx).remote_id();
registered_buffers.entry(buffer_id).or_insert_with(|| {
registered_buffers.entry(buffer.id()).or_insert_with(|| {
let uri: lsp::Url = uri_for_buffer(buffer, cx);
let language_id = id_for_language(buffer.read(cx).language());
let snapshot = buffer.read(cx).snapshot();
@@ -613,7 +590,6 @@ impl Copilot {
.log_err();
RegisteredBuffer {
id: buffer_id,
uri,
language_id,
snapshot,
@@ -624,8 +600,8 @@ impl Copilot {
this.handle_buffer_event(buffer, event, cx).log_err();
}),
cx.observe_release(buffer, move |this, _buffer, _cx| {
this.buffers.remove(&buffer_id);
this.unregister_buffer(buffer_id);
this.buffers.remove(&weak_buffer);
this.unregister_buffer(&weak_buffer);
}),
],
}
@@ -640,8 +616,7 @@ impl Copilot {
cx: &mut ModelContext<Self>,
) -> Result<()> {
if let Ok(server) = self.server.as_running() {
let buffer_id = buffer.read(cx).remote_id();
if let Some(registered_buffer) = server.registered_buffers.get_mut(&buffer_id) {
if let Some(registered_buffer) = server.registered_buffers.get_mut(&buffer.id()) {
match event {
language::Event::Edited => {
let _ = registered_buffer.report_changes(&buffer, cx);
@@ -695,9 +670,9 @@ impl Copilot {
Ok(())
}
fn unregister_buffer(&mut self, buffer_id: u64) {
fn unregister_buffer(&mut self, buffer: &WeakModelHandle<Buffer>) {
if let Ok(server) = self.server.as_running() {
if let Some(buffer) = server.registered_buffers.remove(&buffer_id) {
if let Some(buffer) = server.registered_buffers.remove(&buffer.id()) {
server
.lsp
.notify::<lsp::notification::DidCloseTextDocument>(
@@ -800,18 +775,14 @@ impl Copilot {
Err(error) => return Task::ready(Err(error)),
};
let lsp = server.lsp.clone();
let buffer_id = buffer.read(cx).remote_id();
let registered_buffer = server.registered_buffers.get_mut(&buffer_id).unwrap();
let registered_buffer = server.registered_buffers.get_mut(&buffer.id()).unwrap();
let snapshot = registered_buffer.report_changes(buffer, cx);
let buffer = buffer.read(cx);
let uri = registered_buffer.uri.clone();
let settings = cx.global::<Settings>();
let position = position.to_point_utf16(buffer);
let language = buffer.language_at(position);
let language_name = language.map(|language| language.name());
let language_name = language_name.as_deref();
let tab_size = settings.tab_size(language_name);
let hard_tabs = settings.hard_tabs(language_name);
let settings = language_settings(buffer.language_at(position).as_ref(), buffer.file(), cx);
let tab_size = settings.tab_size;
let hard_tabs = settings.hard_tabs;
let relative_path = buffer
.file()
.map(|file| file.path().to_path_buf())
@@ -874,7 +845,7 @@ impl Copilot {
lsp_status: request::SignInStatus,
cx: &mut ModelContext<Self>,
) {
self.buffers.retain(|_, buffer| buffer.is_upgradable(cx));
self.buffers.retain(|buffer| buffer.is_upgradable(cx));
if let Ok(server) = self.server.as_running() {
match lsp_status {
@@ -882,7 +853,7 @@ impl Copilot {
| request::SignInStatus::MaybeOk { .. }
| request::SignInStatus::AlreadySignedIn { .. } => {
server.sign_in_status = SignInStatus::Authorized;
for buffer in self.buffers.values().cloned().collect::<Vec<_>>() {
for buffer in self.buffers.iter().cloned().collect::<Vec<_>>() {
if let Some(buffer) = buffer.upgrade(cx) {
self.register_buffer(&buffer, cx);
}
@@ -890,14 +861,14 @@ impl Copilot {
}
request::SignInStatus::NotAuthorized { .. } => {
server.sign_in_status = SignInStatus::Unauthorized;
for buffer_id in self.buffers.keys().copied().collect::<Vec<_>>() {
self.unregister_buffer(buffer_id);
for buffer in self.buffers.iter().copied().collect::<Vec<_>>() {
self.unregister_buffer(&buffer);
}
}
request::SignInStatus::NotSignedIn => {
server.sign_in_status = SignInStatus::SignedOut;
for buffer_id in self.buffers.keys().copied().collect::<Vec<_>>() {
self.unregister_buffer(buffer_id);
for buffer in self.buffers.iter().copied().collect::<Vec<_>>() {
self.unregister_buffer(&buffer);
}
}
}
@@ -920,9 +891,7 @@ fn uri_for_buffer(buffer: &ModelHandle<Buffer>, cx: &AppContext) -> lsp::Url {
if let Some(file) = buffer.read(cx).file().and_then(|file| file.as_local()) {
lsp::Url::from_file_path(file.abs_path(cx)).unwrap()
} else {
format!("buffer://{}", buffer.read(cx).remote_id())
.parse()
.unwrap()
format!("buffer://{}", buffer.id()).parse().unwrap()
}
}
@@ -1196,6 +1165,10 @@ mod tests {
fn to_proto(&self) -> rpc::proto::File {
unimplemented!()
}
fn worktree_id(&self) -> usize {
0
}
}
impl language::LocalFile for File {

View File

@@ -6,7 +6,6 @@ use gpui::{
AnyElement, AnyViewHandle, AppContext, ClipboardItem, Element, Entity, View, ViewContext,
ViewHandle,
};
use settings::Settings;
use theme::ui::modal;
#[derive(PartialEq, Eq, Debug, Clone)]
@@ -68,12 +67,13 @@ fn create_copilot_auth_window(
cx: &mut AppContext,
status: &Status,
) -> ViewHandle<CopilotCodeVerification> {
let window_size = cx.global::<Settings>().theme.copilot.modal.dimensions();
let window_size = theme::current(cx).copilot.modal.dimensions();
let window_options = WindowOptions {
bounds: WindowBounds::Fixed(RectF::new(Default::default(), window_size)),
titlebar: None,
center: true,
focus: true,
show: true,
kind: WindowKind::Normal,
is_movable: true,
screen: None,
@@ -338,7 +338,7 @@ impl View for CopilotCodeVerification {
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
enum ConnectModal {}
let style = cx.global::<Settings>().theme.clone();
let style = theme::current(cx).clone();
modal::<ConnectModal, _, _, _, _>(
"Connect Copilot to Zed",

View File

@@ -9,11 +9,12 @@ path = "src/copilot_button.rs"
doctest = false
[dependencies]
assets = { path = "../assets" }
copilot = { path = "../copilot" }
editor = { path = "../editor" }
fs = { path = "../fs" }
context_menu = { path = "../context_menu" }
gpui = { path = "../gpui" }
language = { path = "../language" }
settings = { path = "../settings" }
theme = { path = "../theme" }
util = { path = "../util" }
@@ -21,3 +22,6 @@ workspace = { path = "../workspace" }
anyhow.workspace = true
smol.workspace = true
futures.workspace = true
[dev-dependencies]
editor = { path = "../editor", features = ["test-support"] }

View File

@@ -2,13 +2,18 @@ use anyhow::Result;
use context_menu::{ContextMenu, ContextMenuItem};
use copilot::{Copilot, SignOut, Status};
use editor::{scroll::autoscroll::Autoscroll, Editor};
use fs::Fs;
use gpui::{
elements::*,
platform::{CursorStyle, MouseButton},
AnyElement, AppContext, AsyncAppContext, Element, Entity, MouseState, Subscription, View,
ViewContext, ViewHandle, WeakViewHandle, WindowContext,
};
use settings::{settings_file::SettingsFile, Settings};
use language::{
language_settings::{self, all_language_settings, AllLanguageSettings},
File, Language,
};
use settings::{update_settings_file, SettingsStore};
use std::{path::Path, sync::Arc};
use util::{paths, ResultExt};
use workspace::{
@@ -24,8 +29,9 @@ pub struct CopilotButton {
popup_menu: ViewHandle<ContextMenu>,
editor_subscription: Option<(Subscription, usize)>,
editor_enabled: Option<bool>,
language: Option<Arc<str>>,
path: Option<Arc<Path>>,
language: Option<Arc<Language>>,
file: Option<Arc<dyn File>>,
fs: Arc<dyn Fs>,
}
impl Entity for CopilotButton {
@@ -38,13 +44,12 @@ impl View for CopilotButton {
}
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
let settings = cx.global::<Settings>();
if !settings.features.copilot {
let all_language_settings = all_language_settings(None, cx);
if !all_language_settings.copilot.feature_enabled {
return Empty::new().into_any();
}
let theme = settings.theme.clone();
let theme = theme::current(cx).clone();
let active = self.popup_menu.read(cx).visible();
let Some(copilot) = Copilot::global(cx) else {
return Empty::new().into_any();
@@ -53,7 +58,7 @@ impl View for CopilotButton {
let enabled = self
.editor_enabled
.unwrap_or(settings.show_copilot_suggestions(None, None));
.unwrap_or_else(|| all_language_settings.copilot_enabled(None, None));
Stack::new()
.with_child(
@@ -64,8 +69,8 @@ impl View for CopilotButton {
let style = theme
.workspace
.status_bar
.sidebar_buttons
.item
.panel_buttons
.button
.style_for(state, active);
Flex::row()
@@ -143,7 +148,7 @@ impl View for CopilotButton {
}
impl CopilotButton {
pub fn new(cx: &mut ViewContext<Self>) -> Self {
pub fn new(fs: Arc<dyn Fs>, cx: &mut ViewContext<Self>) -> Self {
let button_view_id = cx.view_id();
let menu = cx.add_view(|cx| {
let mut menu = ContextMenu::new(button_view_id, cx);
@@ -155,7 +160,7 @@ impl CopilotButton {
Copilot::global(cx).map(|copilot| cx.observe(&copilot, |_, _, cx| cx.notify()).detach());
cx.observe_global::<Settings, _>(move |_, cx| cx.notify())
cx.observe_global::<SettingsStore, _>(move |_, cx| cx.notify())
.detach();
Self {
@@ -163,18 +168,20 @@ impl CopilotButton {
editor_subscription: None,
editor_enabled: None,
language: None,
path: None,
file: None,
fs,
}
}
pub fn deploy_copilot_start_menu(&mut self, cx: &mut ViewContext<Self>) {
let mut menu_options = Vec::with_capacity(2);
let fs = self.fs.clone();
menu_options.push(ContextMenuItem::handler("Sign In", |cx| {
initiate_sign_in(cx)
}));
menu_options.push(ContextMenuItem::handler("Disable Copilot", |cx| {
hide_copilot(cx)
menu_options.push(ContextMenuItem::handler("Disable Copilot", move |cx| {
hide_copilot(fs.clone(), cx)
}));
self.popup_menu.update(cx, |menu, cx| {
@@ -188,25 +195,28 @@ impl CopilotButton {
}
pub fn deploy_copilot_menu(&mut self, cx: &mut ViewContext<Self>) {
let settings = cx.global::<Settings>();
let fs = self.fs.clone();
let mut menu_options = Vec::with_capacity(8);
if let Some(language) = self.language.clone() {
let language_enabled = settings.copilot_enabled_for_language(Some(language.as_ref()));
let fs = fs.clone();
let language_enabled = language_settings::language_settings(Some(&language), None, cx)
.show_copilot_suggestions;
menu_options.push(ContextMenuItem::handler(
format!(
"{} Suggestions for {}",
if language_enabled { "Hide" } else { "Show" },
language
language.name()
),
move |cx| toggle_copilot_for_language(language.clone(), cx),
move |cx| toggle_copilot_for_language(language.clone(), fs.clone(), cx),
));
}
if let Some(path) = self.path.as_ref() {
let path_enabled = settings.copilot_enabled_for_path(path);
let path = path.clone();
let settings = settings::get::<AllLanguageSettings>(cx);
if let Some(file) = &self.file {
let path = file.path().clone();
let path_enabled = settings.copilot_enabled_for_path(&path);
menu_options.push(ContextMenuItem::handler(
format!(
"{} Suggestions for This Path",
@@ -228,19 +238,19 @@ impl CopilotButton {
));
}
let globally_enabled = cx.global::<Settings>().features.copilot;
let globally_enabled = settings.copilot_enabled(None, None);
menu_options.push(ContextMenuItem::handler(
if globally_enabled {
"Hide Suggestions for All Files"
} else {
"Show Suggestions for All Files"
},
|cx| toggle_copilot_globally(cx),
move |cx| toggle_copilot_globally(fs.clone(), cx),
));
menu_options.push(ContextMenuItem::Separator);
let icon_style = settings.theme.copilot.out_link_icon.clone();
let icon_style = theme::current(cx).copilot.out_link_icon.clone();
menu_options.push(ContextMenuItem::action(
move |state: &mut MouseState, style: &theme::ContextMenuItem| {
Flex::row()
@@ -266,22 +276,17 @@ impl CopilotButton {
pub fn update_enabled(&mut self, editor: ViewHandle<Editor>, cx: &mut ViewContext<Self>) {
let editor = editor.read(cx);
let snapshot = editor.buffer().read(cx).snapshot(cx);
let settings = cx.global::<Settings>();
let suggestion_anchor = editor.selections.newest_anchor().start;
let language = snapshot.language_at(suggestion_anchor);
let file = snapshot.file_at(suggestion_anchor).cloned();
let language_name = snapshot
.language_at(suggestion_anchor)
.map(|language| language.name());
let path = snapshot
.file_at(suggestion_anchor)
.map(|file| file.path().clone());
self.editor_enabled =
Some(settings.show_copilot_suggestions(language_name.as_deref(), path.as_deref()));
self.language = language_name;
self.path = path;
self.editor_enabled = Some(
all_language_settings(self.file.as_ref(), cx)
.copilot_enabled(language, file.as_ref().map(|file| file.path().as_ref())),
);
self.language = language.cloned();
self.file = file;
cx.notify()
}
@@ -310,9 +315,7 @@ async fn configure_disabled_globs(
let settings_editor = workspace
.update(&mut cx, |_, cx| {
create_and_open_local_file(&paths::SETTINGS, cx, || {
Settings::initial_user_settings_content(&assets::Assets)
.as_ref()
.into()
settings::initial_user_settings_content().as_ref().into()
})
})?
.await?
@@ -322,14 +325,16 @@ async fn configure_disabled_globs(
settings_editor.downgrade().update(&mut cx, |item, cx| {
let text = item.buffer().read(cx).snapshot(cx).text();
let edits = SettingsFile::update_unsaved(&text, cx, |file| {
let settings = cx.global::<SettingsStore>();
let edits = settings.edits_for_update::<AllLanguageSettings>(&text, |file| {
let copilot = file.copilot.get_or_insert_with(Default::default);
let globs = copilot.disabled_globs.get_or_insert_with(|| {
cx.global::<Settings>()
settings
.get::<AllLanguageSettings>(None)
.copilot
.disabled_globs
.iter()
.map(|glob| glob.as_str().to_string())
.map(|glob| glob.glob().to_string())
.collect()
});
@@ -355,32 +360,27 @@ async fn configure_disabled_globs(
anyhow::Ok(())
}
fn toggle_copilot_globally(cx: &mut AppContext) {
let show_copilot_suggestions = cx.global::<Settings>().show_copilot_suggestions(None, None);
SettingsFile::update(cx, move |file_contents| {
file_contents.editor.show_copilot_suggestions = Some((!show_copilot_suggestions).into())
fn toggle_copilot_globally(fs: Arc<dyn Fs>, cx: &mut AppContext) {
let show_copilot_suggestions = all_language_settings(None, cx).copilot_enabled(None, None);
update_settings_file::<AllLanguageSettings>(fs, cx, move |file| {
file.defaults.show_copilot_suggestions = Some((!show_copilot_suggestions).into())
});
}
fn toggle_copilot_for_language(language: Arc<str>, cx: &mut AppContext) {
let show_copilot_suggestions = cx
.global::<Settings>()
.show_copilot_suggestions(Some(&language), None);
SettingsFile::update(cx, move |file_contents| {
file_contents.languages.insert(
language,
settings::EditorSettings {
show_copilot_suggestions: Some((!show_copilot_suggestions).into()),
..Default::default()
},
);
fn toggle_copilot_for_language(language: Arc<Language>, fs: Arc<dyn Fs>, cx: &mut AppContext) {
let show_copilot_suggestions =
all_language_settings(None, cx).copilot_enabled(Some(&language), None);
update_settings_file::<AllLanguageSettings>(fs, cx, move |file| {
file.languages
.entry(language.name())
.or_default()
.show_copilot_suggestions = Some(!show_copilot_suggestions);
});
}
fn hide_copilot(cx: &mut AppContext) {
SettingsFile::update(cx, move |file_contents| {
file_contents.features.copilot = Some(false)
fn hide_copilot(fs: Arc<dyn Fs>, cx: &mut AppContext) {
update_settings_file::<AllLanguageSettings>(fs, cx, move |file| {
file.features.get_or_insert(Default::default()).copilot = Some(false);
});
}

View File

@@ -18,7 +18,7 @@ sqlez = { path = "../sqlez" }
sqlez_macros = { path = "../sqlez_macros" }
util = { path = "../util" }
anyhow.workspace = true
indoc = "1.0.4"
indoc.workspace = true
async-trait.workspace = true
lazy_static.workspace = true
log.workspace = true

View File

@@ -31,6 +31,7 @@ language = { path = "../language", features = ["test-support"] }
lsp = { path = "../lsp", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
workspace = { path = "../workspace", features = ["test-support"] }
theme = { path = "../theme", features = ["test-support"] }
serde_json.workspace = true
unindent.workspace = true

View File

@@ -20,7 +20,6 @@ use language::{
use lsp::LanguageServerId;
use project::{DiagnosticSummary, Project, ProjectPath};
use serde_json::json;
use settings::Settings;
use smallvec::SmallVec;
use std::{
any::{Any, TypeId},
@@ -30,10 +29,11 @@ use std::{
path::PathBuf,
sync::Arc,
};
use theme::ThemeSettings;
use util::TryFutureExt;
use workspace::{
item::{BreadcrumbText, Item, ItemEvent, ItemHandle},
ItemNavHistory, Pane, ToolbarItemLocation, Workspace,
ItemNavHistory, Pane, PaneBackdrop, ToolbarItemLocation, Workspace,
};
actions!(diagnostics, [Deploy]);
@@ -89,12 +89,16 @@ impl View for ProjectDiagnosticsEditor {
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
if self.path_states.is_empty() {
let theme = &cx.global::<Settings>().theme.project_diagnostics;
Label::new("No problems in workspace", theme.empty_message.clone())
.aligned()
.contained()
.with_style(theme.container)
.into_any()
let theme = &theme::current(cx).project_diagnostics;
PaneBackdrop::new(
cx.view_id(),
Label::new("No problems in workspace", theme.empty_message.clone())
.aligned()
.contained()
.with_style(theme.container)
.into_any(),
)
.into_any()
} else {
ChildView::new(&self.editor, cx).into_any()
}
@@ -161,8 +165,13 @@ impl ProjectDiagnosticsEditor {
editor.set_vertical_scroll_margin(5, cx);
editor
});
cx.subscribe(&editor, |_, _, event, cx| cx.emit(event.clone()))
.detach();
cx.subscribe(&editor, |this, _, event, cx| {
cx.emit(event.clone());
if event == &editor::Event::Focused && this.path_states.is_empty() {
cx.focus_self()
}
})
.detach();
let project = project_handle.read(cx);
let paths_to_update = project
@@ -421,7 +430,7 @@ impl ProjectDiagnosticsEditor {
});
self.editor.update(cx, |editor, cx| {
editor.remove_blocks(blocks_to_remove, cx);
editor.remove_blocks(blocks_to_remove, None, cx);
let block_ids = editor.insert_blocks(
blocks_to_add.into_iter().map(|block| {
let (excerpt_id, text_anchor) = block.position;
@@ -433,6 +442,7 @@ impl ProjectDiagnosticsEditor {
disposition: block.disposition,
}
}),
Some(Autoscroll::fit()),
cx,
);
@@ -537,7 +547,7 @@ impl Item for ProjectDiagnosticsEditor {
render_summary(
&self.summary,
&style.label.text,
&cx.global::<Settings>().theme.project_diagnostics,
&theme::current(cx).project_diagnostics,
)
}
@@ -600,15 +610,6 @@ impl Item for ProjectDiagnosticsEditor {
unreachable!()
}
fn git_diff_recalc(
&mut self,
project: ModelHandle<Project>,
cx: &mut ViewContext<Self>,
) -> Task<Result<()>> {
self.editor
.update(cx, |editor, cx| editor.git_diff_recalc(project, cx))
}
fn to_item_events(event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
Editor::to_item_events(event)
}
@@ -679,10 +680,10 @@ impl Item for ProjectDiagnosticsEditor {
fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
let (message, highlights) = highlight_diagnostic_message(Vec::new(), &diagnostic.message);
Arc::new(move |cx| {
let settings = cx.global::<Settings>();
let settings = settings::get::<ThemeSettings>(cx);
let theme = &settings.theme.editor;
let style = theme.diagnostic_header.clone();
let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
let icon_width = cx.em_width * style.icon_width_factor;
let icon = if diagnostic.severity == DiagnosticSeverity::ERROR {
Svg::new("icons/circle_x_mark_12.svg")
@@ -818,33 +819,35 @@ mod tests {
use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, PointUtf16, Unclipped};
use project::FakeFs;
use serde_json::json;
use settings::SettingsStore;
use unindent::Unindent as _;
#[gpui::test]
async fn test_diagnostics(cx: &mut TestAppContext) {
Settings::test_async(cx);
init_test(cx);
let fs = FakeFs::new(cx.background());
fs.insert_tree(
"/test",
json!({
"consts.rs": "
const a: i32 = 'a';
const b: i32 = c;
"
const a: i32 = 'a';
const b: i32 = c;
"
.unindent(),
"main.rs": "
fn main() {
let x = vec![];
let y = vec![];
a(x);
b(y);
// comment 1
// comment 2
c(y);
d(x);
}
"
fn main() {
let x = vec![];
let y = vec![];
a(x);
b(y);
// comment 1
// comment 2
c(y);
d(x);
}
"
.unindent(),
}),
)
@@ -1225,7 +1228,8 @@ mod tests {
#[gpui::test]
async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
Settings::test_async(cx);
init_test(cx);
let fs = FakeFs::new(cx.background());
fs.insert_tree(
"/test",
@@ -1489,6 +1493,17 @@ mod tests {
});
}
fn init_test(cx: &mut TestAppContext) {
cx.update(|cx| {
cx.set_global(SettingsStore::test(cx));
theme::init((), cx);
language::init(cx);
client::init_settings(cx);
workspace::init_settings(cx);
Project::init_settings(cx);
});
}
fn editor_blocks(editor: &ViewHandle<Editor>, cx: &mut WindowContext) -> Vec<(u32, String)> {
editor.update(cx, |editor, cx| {
let snapshot = editor.snapshot(cx);

View File

@@ -7,7 +7,6 @@ use gpui::{
};
use language::Diagnostic;
use lsp::LanguageServerId;
use settings::Settings;
use workspace::{item::ItemHandle, StatusItemView, Workspace};
use crate::ProjectDiagnosticsEditor;
@@ -92,13 +91,12 @@ impl View for DiagnosticIndicator {
enum Summary {}
enum Message {}
let tooltip_style = cx.global::<Settings>().theme.tooltip.clone();
let tooltip_style = theme::current(cx).tooltip.clone();
let in_progress = !self.in_progress_checks.is_empty();
let mut element = Flex::row().with_child(
MouseEventHandler::<Summary, _>::new(0, cx, |state, cx| {
let style = cx
.global::<Settings>()
.theme
let theme = theme::current(cx);
let style = theme
.workspace
.status_bar
.diagnostic_summary
@@ -184,7 +182,7 @@ impl View for DiagnosticIndicator {
.into_any(),
);
let style = &cx.global::<Settings>().theme.workspace.status_bar;
let style = &theme::current(cx).workspace.status_bar;
let item_spacing = style.item_spacing;
if in_progress {

View File

@@ -58,6 +58,7 @@ parking_lot.workspace = true
postage.workspace = true
pulldown-cmark = { version = "0.9.2", default-features = false }
rand = { workspace = true, optional = true }
schemars.workspace = true
serde.workspace = true
serde_derive.workspace = true
smallvec.workspace = true
@@ -80,10 +81,9 @@ workspace = { path = "../workspace", features = ["test-support"] }
ctor.workspace = true
env_logger.workspace = true
glob.workspace = true
rand.workspace = true
unindent.workspace = true
tree-sitter = "0.20"
tree-sitter.workspace = true
tree-sitter-rust = "0.20"
tree-sitter-html = "0.19"
tree-sitter-typescript = { git = "https://github.com/tree-sitter/tree-sitter-typescript", rev = "5d20856f34315b068c41edaee2ac8a100081d259" }

View File

@@ -1,8 +1,8 @@
use std::time::Duration;
use crate::EditorSettings;
use gpui::{Entity, ModelContext};
use settings::Settings;
use settings::SettingsStore;
use smol::Timer;
use std::time::Duration;
pub struct BlinkManager {
blink_interval: Duration,
@@ -15,8 +15,8 @@ pub struct BlinkManager {
impl BlinkManager {
pub fn new(blink_interval: Duration, cx: &mut ModelContext<Self>) -> Self {
cx.observe_global::<Settings, _>(move |this, cx| {
// Make sure we blink the cursors if the setting is re-enabled
// Make sure we blink the cursors if the setting is re-enabled
cx.observe_global::<SettingsStore, _>(move |this, cx| {
this.blink_cursors(this.blink_epoch, cx)
})
.detach();
@@ -64,7 +64,7 @@ impl BlinkManager {
}
fn blink_cursors(&mut self, epoch: usize, cx: &mut ModelContext<Self>) {
if cx.global::<Settings>().cursor_blink {
if settings::get::<EditorSettings>(cx).cursor_blink {
if epoch == self.blink_epoch && self.enabled && !self.blinking_paused {
self.visible = !self.visible;
cx.notify();

View File

@@ -13,8 +13,9 @@ use gpui::{
fonts::{FontId, HighlightStyle},
Entity, ModelContext, ModelHandle,
};
use language::{OffsetUtf16, Point, Subscription as BufferSubscription};
use settings::Settings;
use language::{
language_settings::language_settings, OffsetUtf16, Point, Subscription as BufferSubscription,
};
use std::{any::TypeId, fmt::Debug, num::NonZeroU32, ops::Range, sync::Arc};
pub use suggestion_map::Suggestion;
use suggestion_map::SuggestionMap;
@@ -271,13 +272,11 @@ impl DisplayMap {
}
fn tab_size(buffer: &ModelHandle<MultiBuffer>, cx: &mut ModelContext<Self>) -> NonZeroU32 {
let language_name = buffer
let language = buffer
.read(cx)
.as_singleton()
.and_then(|buffer| buffer.read(cx).language())
.map(|language| language.name());
cx.global::<Settings>().tab_size(language_name.as_deref())
.and_then(|buffer| buffer.read(cx).language());
language_settings(language.as_deref(), None, cx).tab_size
}
#[cfg(test)]
@@ -476,7 +475,7 @@ impl DisplaySnapshot {
})
}
/// Returns an iterator of the start positions of the occurances of `target` in the `self` after `from`
/// Returns an iterator of the start positions of the occurrences of `target` in the `self` after `from`
/// Stops if `condition` returns false for any of the character position pairs observed.
pub fn find_while<'a>(
&'a self,
@@ -487,7 +486,7 @@ impl DisplaySnapshot {
Self::find_internal(self.chars_at(from), target.chars().collect(), condition)
}
/// Returns an iterator of the end positions of the occurances of `target` in the `self` before `from`
/// Returns an iterator of the end positions of the occurrences of `target` in the `self` before `from`
/// Stops if `condition` returns false for any of the character position pairs observed.
pub fn reverse_find_while<'a>(
&'a self,
@@ -844,8 +843,12 @@ pub mod tests {
use super::*;
use crate::{movement, test::marked_display_snapshot};
use gpui::{color::Color, elements::*, test::observe, AppContext};
use language::{Buffer, Language, LanguageConfig, SelectionGoal};
use language::{
language_settings::{AllLanguageSettings, AllLanguageSettingsContent},
Buffer, Language, LanguageConfig, SelectionGoal,
};
use rand::{prelude::*, Rng};
use settings::SettingsStore;
use smol::stream::StreamExt;
use std::{env, sync::Arc};
use theme::SyntaxTheme;
@@ -882,9 +885,7 @@ pub mod tests {
log::info!("wrap width: {:?}", wrap_width);
cx.update(|cx| {
let mut settings = Settings::test(cx);
settings.editor_overrides.tab_size = NonZeroU32::new(tab_size);
cx.set_global(settings)
init_test(cx, |s| s.defaults.tab_size = NonZeroU32::new(tab_size));
});
let buffer = cx.update(|cx| {
@@ -939,9 +940,11 @@ pub mod tests {
tab_size = *tab_sizes.choose(&mut rng).unwrap();
log::info!("setting tab size to {:?}", tab_size);
cx.update(|cx| {
let mut settings = Settings::test(cx);
settings.editor_overrides.tab_size = NonZeroU32::new(tab_size);
cx.set_global(settings)
cx.update_global::<SettingsStore, _, _>(|store, cx| {
store.update_user_settings::<AllLanguageSettings>(cx, |s| {
s.defaults.tab_size = NonZeroU32::new(tab_size);
});
});
});
}
30..=44 => {
@@ -1119,7 +1122,7 @@ pub mod tests {
#[gpui::test(retries = 5)]
fn test_soft_wraps(cx: &mut AppContext) {
cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX);
cx.foreground().forbid_parking();
init_test(cx, |_| {});
let font_cache = cx.font_cache();
@@ -1131,7 +1134,6 @@ pub mod tests {
.unwrap();
let font_size = 12.0;
let wrap_width = Some(64.);
cx.set_global(Settings::test(cx));
let text = "one two three four five\nsix seven eight";
let buffer = MultiBuffer::build_simple(text, cx);
@@ -1211,7 +1213,8 @@ pub mod tests {
#[gpui::test]
fn test_text_chunks(cx: &mut gpui::AppContext) {
cx.set_global(Settings::test(cx));
init_test(cx, |_| {});
let text = sample_text(6, 6, 'a');
let buffer = MultiBuffer::build_simple(&text, cx);
let family_id = cx
@@ -1225,6 +1228,7 @@ pub mod tests {
let font_size = 14.0;
let map =
cx.add_model(|cx| DisplayMap::new(buffer.clone(), font_id, font_size, None, 1, 1, cx));
buffer.update(cx, |buffer, cx| {
buffer.edit(
vec![
@@ -1289,11 +1293,8 @@ pub mod tests {
.unwrap(),
);
language.set_theme(&theme);
cx.update(|cx| {
let mut settings = Settings::test(cx);
settings.editor_defaults.tab_size = Some(2.try_into().unwrap());
cx.set_global(settings);
});
cx.update(|cx| init_test(cx, |s| s.defaults.tab_size = Some(2.try_into().unwrap())));
let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
buffer.condition(cx, |buf, _| !buf.is_parsing()).await;
@@ -1382,7 +1383,7 @@ pub mod tests {
);
language.set_theme(&theme);
cx.update(|cx| cx.set_global(Settings::test(cx)));
cx.update(|cx| init_test(cx, |_| {}));
let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
buffer.condition(cx, |buf, _| !buf.is_parsing()).await;
@@ -1429,9 +1430,8 @@ pub mod tests {
#[gpui::test]
async fn test_chunks_with_text_highlights(cx: &mut gpui::TestAppContext) {
cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX);
cx.update(|cx| init_test(cx, |_| {}));
cx.update(|cx| cx.set_global(Settings::test(cx)));
let theme = SyntaxTheme::new(vec![
("operator".to_string(), Color::red().into()),
("string".to_string(), Color::green().into()),
@@ -1510,7 +1510,8 @@ pub mod tests {
#[gpui::test]
fn test_clip_point(cx: &mut gpui::AppContext) {
cx.set_global(Settings::test(cx));
init_test(cx, |_| {});
fn assert(text: &str, shift_right: bool, bias: Bias, cx: &mut gpui::AppContext) {
let (unmarked_snapshot, mut markers) = marked_display_snapshot(text, cx);
@@ -1559,7 +1560,7 @@ pub mod tests {
#[gpui::test]
fn test_clip_at_line_ends(cx: &mut gpui::AppContext) {
cx.set_global(Settings::test(cx));
init_test(cx, |_| {});
fn assert(text: &str, cx: &mut gpui::AppContext) {
let (mut unmarked_snapshot, markers) = marked_display_snapshot(text, cx);
@@ -1578,7 +1579,8 @@ pub mod tests {
#[gpui::test]
fn test_tabs_with_multibyte_chars(cx: &mut gpui::AppContext) {
cx.set_global(Settings::test(cx));
init_test(cx, |_| {});
let text = "\t\tα\nβ\t\n🏀β\t\tγ";
let buffer = MultiBuffer::build_simple(text, cx);
let font_cache = cx.font_cache();
@@ -1639,7 +1641,8 @@ pub mod tests {
#[gpui::test]
fn test_max_point(cx: &mut gpui::AppContext) {
cx.set_global(Settings::test(cx));
init_test(cx, |_| {});
let buffer = MultiBuffer::build_simple("aaa\n\t\tbbb", cx);
let font_cache = cx.font_cache();
let family_id = font_cache
@@ -1718,4 +1721,13 @@ pub mod tests {
}
chunks
}
fn init_test(cx: &mut AppContext, f: impl Fn(&mut AllLanguageSettingsContent)) {
cx.foreground().forbid_parking();
cx.set_global(SettingsStore::test(cx));
language::init(cx);
cx.update_global::<SettingsStore, _, _>(|store, cx| {
store.update_user_settings::<AllLanguageSettings>(cx, f);
});
}
}

View File

@@ -993,7 +993,7 @@ mod tests {
use crate::multi_buffer::MultiBuffer;
use gpui::{elements::Empty, Element};
use rand::prelude::*;
use settings::Settings;
use settings::SettingsStore;
use std::env;
use util::RandomCharIter;
@@ -1013,7 +1013,7 @@ mod tests {
#[gpui::test]
fn test_basic_blocks(cx: &mut gpui::AppContext) {
cx.set_global(Settings::test(cx));
init_test(cx);
let family_id = cx
.font_cache()
@@ -1189,7 +1189,7 @@ mod tests {
#[gpui::test]
fn test_blocks_on_wrapped_lines(cx: &mut gpui::AppContext) {
cx.set_global(Settings::test(cx));
init_test(cx);
let family_id = cx
.font_cache()
@@ -1239,7 +1239,7 @@ mod tests {
#[gpui::test(iterations = 100)]
fn test_random_blocks(cx: &mut gpui::AppContext, mut rng: StdRng) {
cx.set_global(Settings::test(cx));
init_test(cx);
let operations = env::var("OPERATIONS")
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
@@ -1647,6 +1647,11 @@ mod tests {
}
}
fn init_test(cx: &mut gpui::AppContext) {
cx.set_global(SettingsStore::test(cx));
theme::init((), cx);
}
impl TransformBlock {
fn as_custom(&self) -> Option<&Block> {
match self {

View File

@@ -1204,7 +1204,7 @@ mod tests {
use crate::{MultiBuffer, ToPoint};
use collections::HashSet;
use rand::prelude::*;
use settings::Settings;
use settings::SettingsStore;
use std::{cmp::Reverse, env, mem, sync::Arc};
use sum_tree::TreeMap;
use util::test::sample_text;
@@ -1213,7 +1213,7 @@ mod tests {
#[gpui::test]
fn test_basic_folds(cx: &mut gpui::AppContext) {
cx.set_global(Settings::test(cx));
init_test(cx);
let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
let buffer_snapshot = buffer.read(cx).snapshot(cx);
@@ -1286,7 +1286,7 @@ mod tests {
#[gpui::test]
fn test_adjacent_folds(cx: &mut gpui::AppContext) {
cx.set_global(Settings::test(cx));
init_test(cx);
let buffer = MultiBuffer::build_simple("abcdefghijkl", cx);
let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
let buffer_snapshot = buffer.read(cx).snapshot(cx);
@@ -1349,7 +1349,7 @@ mod tests {
#[gpui::test]
fn test_merging_folds_via_edit(cx: &mut gpui::AppContext) {
cx.set_global(Settings::test(cx));
init_test(cx);
let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
let buffer_snapshot = buffer.read(cx).snapshot(cx);
@@ -1400,7 +1400,7 @@ mod tests {
#[gpui::test(iterations = 100)]
fn test_random_folds(cx: &mut gpui::AppContext, mut rng: StdRng) {
cx.set_global(Settings::test(cx));
init_test(cx);
let operations = env::var("OPERATIONS")
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
.unwrap_or(10);
@@ -1676,6 +1676,10 @@ mod tests {
assert_eq!(snapshot.buffer_rows(3).collect::<Vec<_>>(), [Some(6)]);
}
fn init_test(cx: &mut gpui::AppContext) {
cx.set_global(SettingsStore::test(cx));
}
impl FoldMap {
fn merged_fold_ranges(&self) -> Vec<Range<usize>> {
let buffer = self.buffer.lock().clone();

View File

@@ -578,7 +578,7 @@ mod tests {
use crate::{display_map::fold_map::FoldMap, MultiBuffer};
use gpui::AppContext;
use rand::{prelude::StdRng, Rng};
use settings::Settings;
use settings::SettingsStore;
use std::{
env,
ops::{Bound, RangeBounds},
@@ -631,7 +631,8 @@ mod tests {
#[gpui::test(iterations = 100)]
fn test_random_suggestions(cx: &mut AppContext, mut rng: StdRng) {
cx.set_global(Settings::test(cx));
init_test(cx);
let operations = env::var("OPERATIONS")
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
.unwrap_or(10);
@@ -834,6 +835,11 @@ mod tests {
}
}
fn init_test(cx: &mut AppContext) {
cx.set_global(SettingsStore::test(cx));
theme::init((), cx);
}
impl SuggestionMap {
pub fn randomly_mutate(
&self,

View File

@@ -1043,16 +1043,16 @@ mod tests {
};
use gpui::test::observe;
use rand::prelude::*;
use settings::Settings;
use settings::SettingsStore;
use smol::stream::StreamExt;
use std::{cmp, env, num::NonZeroU32};
use text::Rope;
#[gpui::test(iterations = 100)]
async fn test_random_wraps(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
cx.update(|cx| cx.set_global(Settings::test(cx)));
init_test(cx);
cx.foreground().set_block_on_ticks(0..=50);
cx.foreground().forbid_parking();
let operations = env::var("OPERATIONS")
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
.unwrap_or(10);
@@ -1287,6 +1287,14 @@ mod tests {
wrap_map.read_with(cx, |map, _| assert!(map.pending_edits.is_empty()));
}
fn init_test(cx: &mut gpui::TestAppContext) {
cx.foreground().forbid_parking();
cx.update(|cx| {
cx.set_global(SettingsStore::test(cx));
theme::init((), cx);
});
}
fn wrap_text(
unwrapped_text: &str,
wrap_width: Option<f32>,

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,56 @@
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::Setting;
#[derive(Deserialize)]
pub struct EditorSettings {
pub cursor_blink: bool,
pub hover_popover_enabled: bool,
pub show_completions_on_input: bool,
pub use_on_type_format: bool,
pub scrollbar: Scrollbar,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub struct Scrollbar {
pub show: ShowScrollbar,
pub git_diff: bool,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ShowScrollbar {
Auto,
System,
Always,
Never,
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
pub struct EditorSettingsContent {
pub cursor_blink: Option<bool>,
pub hover_popover_enabled: Option<bool>,
pub show_completions_on_input: Option<bool>,
pub use_on_type_format: Option<bool>,
pub scrollbar: Option<ScrollbarContent>,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub struct ScrollbarContent {
pub show: Option<ShowScrollbar>,
pub git_diff: Option<bool>,
}
impl Setting for EditorSettings {
const KEY: Option<&'static str> = None;
type FileContent = EditorSettingsContent;
fn load(
default_value: &Self::FileContent,
user_values: &[&Self::FileContent],
_: &gpui::AppContext,
) -> anyhow::Result<Self> {
Self::load_via_json_merge(default_value, user_values)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,7 @@ use super::{
};
use crate::{
display_map::{BlockStyle, DisplaySnapshot, FoldStatus, TransformBlock},
editor_settings::ShowScrollbar,
git::{diff_hunk_to_display, DisplayDiffHunk},
hover_popover::{
hide_hover, hover_at, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH,
@@ -13,7 +14,7 @@ use crate::{
link_go_to_definition::{
go_to_fetched_definition, go_to_fetched_type_definition, update_go_to_definition_link,
},
mouse_context_menu, EditorStyle, GutterHover, UnfoldAt,
mouse_context_menu, EditorSettings, EditorStyle, GutterHover, UnfoldAt,
};
use clock::ReplicaId;
use collections::{BTreeMap, HashMap};
@@ -35,9 +36,14 @@ use gpui::{
};
use itertools::Itertools;
use json::json;
use language::{Bias, CursorShape, DiagnosticSeverity, OffsetUtf16, Selection};
use project::ProjectPath;
use settings::{GitGutter, Settings, ShowWhitespaces};
use language::{
language_settings::ShowWhitespaceSetting, Bias, CursorShape, DiagnosticSeverity, OffsetUtf16,
Selection,
};
use project::{
project_settings::{GitGutterSetting, ProjectSettings},
ProjectPath,
};
use smallvec::SmallVec;
use std::{
borrow::Cow,
@@ -85,7 +91,6 @@ impl SelectionLayout {
}
}
#[derive(Clone)]
pub struct EditorElement {
style: Arc<EditorStyle>,
}
@@ -548,11 +553,8 @@ impl EditorElement {
let scroll_top = scroll_position.y() * line_height;
let show_gutter = matches!(
&cx.global::<Settings>()
.git_overrides
.git_gutter
.unwrap_or_default(),
GitGutter::TrackedFiles
settings::get::<ProjectSettings>(cx).git.git_gutter,
Some(GitGutterSetting::TrackedFiles)
);
if show_gutter {
@@ -609,7 +611,7 @@ impl EditorElement {
layout: &mut LayoutState,
cx: &mut ViewContext<Editor>,
) {
let diff_style = &cx.global::<Settings>().theme.editor.diff.clone();
let diff_style = &theme::current(cx).editor.diff.clone();
let line_height = layout.position_map.line_height;
let scroll_position = layout.position_map.snapshot.scroll_position();
@@ -709,6 +711,7 @@ impl EditorElement {
let scroll_left = scroll_position.x() * max_glyph_width;
let content_origin = bounds.origin() + vec2f(layout.gutter_margin, 0.);
let line_end_overshoot = 0.15 * layout.position_map.line_height;
let whitespace_setting = editor.buffer.read(cx).settings_at(0, cx).show_whitespaces;
scene.push_layer(Some(bounds));
@@ -883,9 +886,10 @@ impl EditorElement {
content_origin,
scroll_left,
visible_text_bounds,
cx,
whitespace_setting,
&invisible_display_ranges,
visible_bounds,
cx,
)
}
}
@@ -1047,8 +1051,8 @@ impl EditorElement {
..Default::default()
});
if layout.is_singleton {
let diff_style = cx.global::<Settings>().theme.editor.diff.clone();
if layout.is_singleton && settings::get::<EditorSettings>(cx).scrollbar.git_diff {
let diff_style = theme::current(cx).editor.scrollbar.git.clone();
for hunk in layout
.position_map
.snapshot
@@ -1460,11 +1464,9 @@ impl EditorElement {
line_height: f32,
style: &EditorStyle,
line_layouts: &[LineWithInvisibles],
include_root: bool,
editor: &mut Editor,
cx: &mut LayoutContext<Editor>,
) -> (f32, Vec<BlockLayout>) {
let tooltip_style = cx.global::<Settings>().theme.tooltip.clone();
let scroll_x = snapshot.scroll_anchor.offset.x();
let (fixed_blocks, non_fixed_blocks) = snapshot
.blocks_in_range(rows.clone())
@@ -1506,7 +1508,12 @@ impl EditorElement {
starts_new_buffer,
..
} => {
let id = *id;
let tooltip_style = theme::current(cx).tooltip.clone();
let include_root = editor
.project
.as_ref()
.map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
.unwrap_or_default();
let jump_icon = project::File::from_dyn(buffer.file()).map(|file| {
let jump_path = ProjectPath {
worktree_id: file.worktree_id(cx),
@@ -1519,7 +1526,7 @@ impl EditorElement {
let jump_position = language::ToPoint::to_point(&jump_anchor, buffer);
enum JumpIcon {}
MouseEventHandler::<JumpIcon, _>::new(id.into(), cx, |state, _| {
MouseEventHandler::<JumpIcon, _>::new((*id).into(), cx, |state, _| {
let style = style.jump_icon.style_for(state, false);
Svg::new("icons/arrow_up_right_8.svg")
.with_color(style.color)
@@ -1551,7 +1558,7 @@ impl EditorElement {
}
})
.with_tooltip::<JumpIcon>(
id.into(),
(*id).into(),
"Jump to Buffer".to_string(),
Some(Box::new(crate::OpenExcerpts)),
tooltip_style.clone(),
@@ -1562,9 +1569,9 @@ impl EditorElement {
});
if *starts_new_buffer {
let style = &self.style.diagnostic_path_header;
let font_size =
(style.text_scale_factor * self.style.text.font_size).round();
let editor_font_size = style.text.font_size;
let style = &style.diagnostic_path_header;
let font_size = (style.text_scale_factor * editor_font_size).round();
let path = buffer.resolve_file_path(cx, include_root);
let mut filename = None;
@@ -1600,7 +1607,7 @@ impl EditorElement {
.expanded()
.into_any_named("path header block")
} else {
let text_style = self.style.text.clone();
let text_style = style.text.clone();
Flex::row()
.with_child(Label::new("", text_style))
.with_children(jump_icon)
@@ -1790,9 +1797,10 @@ impl LineWithInvisibles {
content_origin: Vector2F,
scroll_left: f32,
visible_text_bounds: RectF,
cx: &mut ViewContext<Editor>,
whitespace_setting: ShowWhitespaceSetting,
selection_ranges: &[Range<DisplayPoint>],
visible_bounds: RectF,
cx: &mut ViewContext<Editor>,
) {
let line_height = layout.position_map.line_height;
let line_y = row as f32 * line_height - scroll_top;
@@ -1806,7 +1814,6 @@ impl LineWithInvisibles {
);
self.draw_invisibles(
cx,
&selection_ranges,
layout,
content_origin,
@@ -1816,12 +1823,13 @@ impl LineWithInvisibles {
scene,
visible_bounds,
line_height,
whitespace_setting,
cx,
);
}
fn draw_invisibles(
&self,
cx: &mut ViewContext<Editor>,
selection_ranges: &[Range<DisplayPoint>],
layout: &LayoutState,
content_origin: Vector2F,
@@ -1831,17 +1839,13 @@ impl LineWithInvisibles {
scene: &mut SceneBuilder,
visible_bounds: RectF,
line_height: f32,
whitespace_setting: ShowWhitespaceSetting,
cx: &mut ViewContext<Editor>,
) {
let settings = cx.global::<Settings>();
let allowed_invisibles_regions = match settings
.editor_overrides
.show_whitespaces
.or(settings.editor_defaults.show_whitespaces)
.unwrap_or_default()
{
ShowWhitespaces::None => return,
ShowWhitespaces::Selection => Some(selection_ranges),
ShowWhitespaces::All => None,
let allowed_invisibles_regions = match whitespace_setting {
ShowWhitespaceSetting::None => return,
ShowWhitespaceSetting::Selection => Some(selection_ranges),
ShowWhitespaceSetting::All => None,
};
for invisible in &self.invisibles {
@@ -1897,7 +1901,7 @@ impl Element<Editor> for EditorElement {
let gutter_padding;
let gutter_width;
let gutter_margin;
if snapshot.mode == EditorMode::Full {
if snapshot.show_gutter {
let em_width = style.text.em_width(cx.font_cache());
gutter_padding = (em_width * style.gutter_padding_factor).round();
gutter_width = self.max_line_number_width(&snapshot, cx) + gutter_padding * 2.0;
@@ -1986,11 +1990,11 @@ impl Element<Editor> for EditorElement {
let is_singleton = editor.is_singleton(cx);
let highlighted_rows = editor.highlighted_rows();
let theme = cx.global::<Settings>().theme.as_ref();
let theme = theme::current(cx);
let highlighted_ranges = editor.background_highlights_in_range(
start_anchor..end_anchor,
&snapshot.display_snapshot,
theme,
theme.as_ref(),
);
fold_ranges.extend(
@@ -2065,22 +2069,19 @@ impl Element<Editor> for EditorElement {
));
}
let show_scrollbars = match cx.global::<Settings>().show_scrollbars {
settings::ShowScrollbars::Auto => {
snapshot.has_scrollbar_info(is_singleton)
|| editor.scroll_manager.scrollbars_visible()
let scrollbar_settings = &settings::get::<EditorSettings>(cx).scrollbar;
let show_scrollbars = match scrollbar_settings.show {
ShowScrollbar::Auto => {
// Git
(is_singleton && scrollbar_settings.git_diff && snapshot.buffer_snapshot.has_git_diffs())
// Scrollmanager
|| editor.scroll_manager.scrollbars_visible()
}
settings::ShowScrollbars::System => editor.scroll_manager.scrollbars_visible(),
settings::ShowScrollbars::Always => true,
settings::ShowScrollbars::Never => false,
ShowScrollbar::System => editor.scroll_manager.scrollbars_visible(),
ShowScrollbar::Always => true,
ShowScrollbar::Never => false,
};
let include_root = editor
.project
.as_ref()
.map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
.unwrap_or_default();
let fold_ranges: Vec<(BufferRow, Range<DisplayPoint>, Color)> = fold_ranges
.into_iter()
.map(|(id, fold)| {
@@ -2139,7 +2140,6 @@ impl Element<Editor> for EditorElement {
line_height,
&style,
&line_layouts,
include_root,
editor,
cx,
);
@@ -2836,17 +2836,19 @@ mod tests {
use super::*;
use crate::{
display_map::{BlockDisposition, BlockProperties},
editor_tests::{init_test, update_test_settings},
Editor, MultiBuffer,
};
use gpui::TestAppContext;
use language::language_settings;
use log::info;
use settings::Settings;
use std::{num::NonZeroU32, sync::Arc};
use util::test::sample_text;
#[gpui::test]
fn test_layout_line_numbers(cx: &mut TestAppContext) {
cx.update(|cx| cx.set_global(Settings::test(cx)));
init_test(cx, |_| {});
let (_, editor) = cx.add_window(|cx| {
let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
Editor::new(EditorMode::Full, buffer, None, None, cx)
@@ -2864,7 +2866,8 @@ mod tests {
#[gpui::test]
fn test_layout_with_placeholder_text_and_blocks(cx: &mut TestAppContext) {
cx.update(|cx| cx.set_global(Settings::test(cx)));
init_test(cx, |_| {});
let (_, editor) = cx.add_window(|cx| {
let buffer = MultiBuffer::build_simple("", cx);
Editor::new(EditorMode::Full, buffer, None, None, cx)
@@ -2880,6 +2883,7 @@ mod tests {
position: Anchor::min(),
render: Arc::new(|_| Empty::new().into_any()),
}],
None,
cx,
);
@@ -2924,26 +2928,27 @@ mod tests {
#[gpui::test]
fn test_all_invisibles_drawing(cx: &mut TestAppContext) {
let tab_size = 4;
const TAB_SIZE: u32 = 4;
let input_text = "\t \t|\t| a b";
let expected_invisibles = vec![
Invisible::Tab {
line_start_offset: 0,
},
Invisible::Whitespace {
line_offset: tab_size as usize,
line_offset: TAB_SIZE as usize,
},
Invisible::Tab {
line_start_offset: tab_size as usize + 1,
line_start_offset: TAB_SIZE as usize + 1,
},
Invisible::Tab {
line_start_offset: tab_size as usize * 2 + 1,
line_start_offset: TAB_SIZE as usize * 2 + 1,
},
Invisible::Whitespace {
line_offset: tab_size as usize * 3 + 1,
line_offset: TAB_SIZE as usize * 3 + 1,
},
Invisible::Whitespace {
line_offset: tab_size as usize * 3 + 3,
line_offset: TAB_SIZE as usize * 3 + 3,
},
];
assert_eq!(
@@ -2955,12 +2960,11 @@ mod tests {
"Hardcoded expected invisibles differ from the actual ones in '{input_text}'"
);
cx.update(|cx| {
let mut test_settings = Settings::test(cx);
test_settings.editor_defaults.show_whitespaces = Some(ShowWhitespaces::All);
test_settings.editor_defaults.tab_size = Some(NonZeroU32::new(tab_size).unwrap());
cx.set_global(test_settings);
init_test(cx, |s| {
s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
s.defaults.tab_size = NonZeroU32::new(TAB_SIZE);
});
let actual_invisibles =
collect_invisibles_from_new_editor(cx, EditorMode::Full, &input_text, 500.0);
@@ -2969,11 +2973,9 @@ mod tests {
#[gpui::test]
fn test_invisibles_dont_appear_in_certain_editors(cx: &mut TestAppContext) {
cx.update(|cx| {
let mut test_settings = Settings::test(cx);
test_settings.editor_defaults.show_whitespaces = Some(ShowWhitespaces::All);
test_settings.editor_defaults.tab_size = Some(NonZeroU32::new(4).unwrap());
cx.set_global(test_settings);
init_test(cx, |s| {
s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
s.defaults.tab_size = NonZeroU32::new(4);
});
for editor_mode_without_invisibles in [
@@ -3024,19 +3026,18 @@ mod tests {
);
info!("Expected invisibles: {expected_invisibles:?}");
init_test(cx, |_| {});
// Put the same string with repeating whitespace pattern into editors of various size,
// take deliberately small steps during resizing, to put all whitespace kinds near the wrap point.
let resize_step = 10.0;
let mut editor_width = 200.0;
while editor_width <= 1000.0 {
cx.update(|cx| {
let mut test_settings = Settings::test(cx);
test_settings.editor_defaults.tab_size = Some(NonZeroU32::new(tab_size).unwrap());
test_settings.editor_defaults.show_whitespaces = Some(ShowWhitespaces::All);
test_settings.editor_defaults.preferred_line_length = Some(editor_width as u32);
test_settings.editor_defaults.soft_wrap =
Some(settings::SoftWrap::PreferredLineLength);
cx.set_global(test_settings);
update_test_settings(cx, |s| {
s.defaults.tab_size = NonZeroU32::new(tab_size);
s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
s.defaults.preferred_line_length = Some(editor_width as u32);
s.defaults.soft_wrap = Some(language_settings::SoftWrap::PreferredLineLength);
});
let actual_invisibles =
@@ -3075,7 +3076,7 @@ mod tests {
editor_width: f32,
) -> Vec<Invisible> {
info!(
"Creating editor with mode {editor_mode:?}, witdh {editor_width} and text '{input_text}'"
"Creating editor with mode {editor_mode:?}, width {editor_width} and text '{input_text}'"
);
let (_, editor) = cx.add_window(|cx| {
let buffer = MultiBuffer::build_simple(&input_text, cx);
@@ -3084,7 +3085,7 @@ mod tests {
let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
let (_, layout_state) = editor.update(cx, |editor, cx| {
editor.set_soft_wrap_mode(settings::SoftWrap::EditorWidth, cx);
editor.set_soft_wrap_mode(language_settings::SoftWrap::EditorWidth, cx);
editor.set_wrap_width(Some(editor_width), cx);
let mut new_parents = Default::default();

View File

@@ -33,12 +33,14 @@ pub fn refresh_matching_bracket_highlights(editor: &mut Editor, cx: &mut ViewCon
#[cfg(test)]
mod tests {
use super::*;
use crate::test::editor_lsp_test_context::EditorLspTestContext;
use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
use indoc::indoc;
use language::{BracketPair, BracketPairConfig, Language, LanguageConfig};
#[gpui::test]
async fn test_matching_bracket_highlights(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
let mut cx = EditorLspTestContext::new(
Language::new(
LanguageConfig {

View File

@@ -1,6 +1,6 @@
use crate::{
display_map::ToDisplayPoint, Anchor, AnchorRangeExt, DisplayPoint, Editor, EditorSnapshot,
EditorStyle, RangeToAnchorExt,
display_map::ToDisplayPoint, Anchor, AnchorRangeExt, DisplayPoint, Editor, EditorSettings,
EditorSnapshot, EditorStyle, RangeToAnchorExt,
};
use futures::FutureExt;
use gpui::{
@@ -12,7 +12,6 @@ use gpui::{
};
use language::{Bias, DiagnosticEntry, DiagnosticSeverity, Language, LanguageRegistry};
use project::{HoverBlock, HoverBlockKind, Project};
use settings::Settings;
use std::{ops::Range, sync::Arc, time::Duration};
use util::TryFutureExt;
@@ -38,7 +37,7 @@ pub fn hover(editor: &mut Editor, _: &Hover, cx: &mut ViewContext<Editor>) {
/// The internal hover action dispatches between `show_hover` or `hide_hover`
/// depending on whether a point to hover over is provided.
pub fn hover_at(editor: &mut Editor, point: Option<DisplayPoint>, cx: &mut ViewContext<Editor>) {
if cx.global::<Settings>().hover_popover_enabled {
if settings::get::<EditorSettings>(cx).hover_popover_enabled {
if let Some(point) = point {
show_hover(editor, point, false, cx);
} else {
@@ -222,6 +221,7 @@ fn show_hover(
project: project.clone(),
symbol_range: range,
blocks: hover_result.contents,
language: hover_result.language,
rendered_content: None,
})
});
@@ -254,6 +254,7 @@ fn render_blocks(
theme_id: usize,
blocks: &[HoverBlock],
language_registry: &Arc<LanguageRegistry>,
language: Option<&Arc<Language>>,
style: &EditorStyle,
) -> RenderedInfo {
let mut text = String::new();
@@ -352,11 +353,13 @@ fn render_blocks(
}
Tag::CodeBlock(kind) => {
new_paragraph(&mut text, &mut list_stack);
if let CodeBlockKind::Fenced(language) = kind {
current_language = language_registry
current_language = if let CodeBlockKind::Fenced(language) = kind {
language_registry
.language_for_name(language.as_ref())
.now_or_never()
.and_then(Result::ok);
.and_then(Result::ok)
} else {
language.cloned()
}
}
Tag::Emphasis => italic_depth += 1,
@@ -415,10 +418,6 @@ fn render_blocks(
}
}
if !text.is_empty() && !text.ends_with('\n') {
text.push('\n');
}
RenderedInfo {
theme_id,
text,
@@ -525,6 +524,7 @@ pub struct InfoPopover {
pub project: ModelHandle<Project>,
pub symbol_range: Range<Anchor>,
pub blocks: Vec<HoverBlock>,
language: Option<Arc<Language>>,
rendered_content: Option<RenderedInfo>,
}
@@ -560,6 +560,7 @@ impl InfoPopover {
style.theme_id,
&self.blocks,
self.project.read(cx).languages(),
self.language.as_ref(),
style,
)
});
@@ -589,10 +590,7 @@ impl InfoPopover {
MouseRegion::new::<Self>(view_id, region_id, bounds)
.on_click::<Editor, _>(
MouseButton::Left,
move |_, _, cx| {
println!("clicked link {url}");
cx.platform().open_url(&url);
},
move |_, _, cx| cx.platform().open_url(&url),
),
);
}
@@ -654,7 +652,7 @@ impl DiagnosticPopover {
_ => style.hover_popover.container,
};
let tooltip_style = cx.global::<Settings>().theme.tooltip.clone();
let tooltip_style = theme::current(cx).tooltip.clone();
MouseEventHandler::<DiagnosticPopover, _>::new(0, cx, |_, _| {
text.with_soft_wrap(true)
@@ -694,7 +692,7 @@ impl DiagnosticPopover {
#[cfg(test)]
mod tests {
use super::*;
use crate::test::editor_lsp_test_context::EditorLspTestContext;
use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
use gpui::fonts::Weight;
use indoc::indoc;
use language::{Diagnostic, DiagnosticSet};
@@ -706,6 +704,8 @@ mod tests {
#[gpui::test]
async fn test_mouse_hover_info_popover(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
let mut cx = EditorLspTestContext::new_rust(
lsp::ServerCapabilities {
hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
@@ -773,6 +773,8 @@ mod tests {
#[gpui::test]
async fn test_keyboard_hover_info_popover(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
let mut cx = EditorLspTestContext::new_rust(
lsp::ServerCapabilities {
hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
@@ -816,6 +818,8 @@ mod tests {
#[gpui::test]
async fn test_hover_diagnostic_and_info_popovers(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
let mut cx = EditorLspTestContext::new_rust(
lsp::ServerCapabilities {
hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
@@ -882,7 +886,8 @@ mod tests {
#[gpui::test]
fn test_render_blocks(cx: &mut gpui::TestAppContext) {
Settings::test_async(cx);
init_test(cx, |_| {});
cx.add_window(|cx| {
let editor = Editor::single_line(None, cx);
let style = editor.style(cx);
@@ -900,7 +905,7 @@ mod tests {
text: "one **two** three".to_string(),
kind: HoverBlockKind::Markdown,
}],
expected_marked_text: "one «two» three\n".to_string(),
expected_marked_text: "one «two» three".to_string(),
expected_styles: vec![HighlightStyle {
weight: Some(Weight::BOLD),
..Default::default()
@@ -912,7 +917,7 @@ mod tests {
text: "one [two](the-url) three".to_string(),
kind: HoverBlockKind::Markdown,
}],
expected_marked_text: "one «two» three\n".to_string(),
expected_marked_text: "one «two» three".to_string(),
expected_styles: vec![HighlightStyle {
underline: Some(Underline {
thickness: 1.0.into(),
@@ -931,8 +936,7 @@ mod tests {
- b
* two
- [c](the-url)
- d
"
- d"
.unindent(),
kind: HoverBlockKind::Markdown,
}],
@@ -943,8 +947,7 @@ mod tests {
- b
- two
- «c»
- d
"
- d"
.unindent(),
expected_styles: vec![HighlightStyle {
underline: Some(Underline {
@@ -967,9 +970,8 @@ mod tests {
nine
* ten
* six
"
.unindent(),
* six"
.unindent(),
kind: HoverBlockKind::Markdown,
}],
expected_marked_text: "
@@ -979,9 +981,8 @@ mod tests {
nine
- ten
- six
"
.unindent(),
- six"
.unindent(),
expected_styles: vec![HighlightStyle {
underline: Some(Underline {
thickness: 1.0.into(),
@@ -998,7 +999,7 @@ mod tests {
expected_styles,
} in &rows[0..]
{
let rendered = render_blocks(0, &blocks, &Default::default(), &style);
let rendered = render_blocks(0, &blocks, &Default::default(), None, &style);
let (expected_text, ranges) = marked_text_ranges(expected_marked_text, false);
let expected_highlights = ranges

View File

@@ -16,7 +16,6 @@ use language::{
};
use project::{FormatTrigger, Item as _, Project, ProjectPath};
use rpc::proto::{self, update_view};
use settings::Settings;
use smallvec::SmallVec;
use std::{
borrow::Cow,
@@ -197,7 +196,7 @@ impl FollowableItem for Editor {
singleton: buffer.is_singleton(),
title: (!buffer.is_singleton()).then(|| buffer.title(cx).into()),
excerpts,
scroll_top_anchor: Some(serialize_anchor(&scroll_anchor.top_anchor)),
scroll_top_anchor: Some(serialize_anchor(&scroll_anchor.anchor)),
scroll_x: scroll_anchor.offset.x(),
scroll_y: scroll_anchor.offset.y(),
selections: self
@@ -254,7 +253,7 @@ impl FollowableItem for Editor {
}
Event::ScrollPositionChanged { .. } => {
let scroll_anchor = self.scroll_manager.anchor();
update.scroll_top_anchor = Some(serialize_anchor(&scroll_anchor.top_anchor));
update.scroll_top_anchor = Some(serialize_anchor(&scroll_anchor.anchor));
update.scroll_x = scroll_anchor.offset.x();
update.scroll_y = scroll_anchor.offset.y();
true
@@ -295,7 +294,7 @@ impl FollowableItem for Editor {
match event {
Event::Edited => true,
Event::SelectionsChanged { local } => *local,
Event::ScrollPositionChanged { local } => *local,
Event::ScrollPositionChanged { local, .. } => *local,
_ => false,
}
}
@@ -413,7 +412,7 @@ async fn update_editor_from_message(
} else if let Some(scroll_top_anchor) = scroll_top_anchor {
editor.set_scroll_anchor_remote(
ScrollAnchor {
top_anchor: scroll_top_anchor,
anchor: scroll_top_anchor,
offset: vec2f(message.scroll_x, message.scroll_y),
},
cx,
@@ -511,8 +510,8 @@ impl Item for Editor {
};
let mut scroll_anchor = data.scroll_anchor;
if !buffer.can_resolve(&scroll_anchor.top_anchor) {
scroll_anchor.top_anchor = buffer.anchor_before(
if !buffer.can_resolve(&scroll_anchor.anchor) {
scroll_anchor.anchor = buffer.anchor_before(
buffer.clip_point(Point::new(data.scroll_top_row, 0), Bias::Left),
);
}
@@ -721,17 +720,6 @@ impl Item for Editor {
})
}
fn git_diff_recalc(
&mut self,
_project: ModelHandle<Project>,
cx: &mut ViewContext<Self>,
) -> Task<Result<()>> {
self.buffer().update(cx, |multibuffer, cx| {
multibuffer.git_diff_recalc(cx);
});
Task::ready(Ok(()))
}
fn to_item_events(event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
let mut result = SmallVec::new();
match event {
@@ -1116,7 +1104,7 @@ impl View for CursorPosition {
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
if let Some(position) = self.position {
let theme = &cx.global::<Settings>().theme.workspace.status_bar;
let theme = &theme::current(cx).workspace.status_bar;
let mut text = format!(
"{}{FILE_ROW_COLUMN_DELIMITER}{}",
position.row + 1,
@@ -1232,27 +1220,31 @@ mod tests {
}
fn as_local(&self) -> Option<&dyn language::LocalFile> {
todo!()
unimplemented!()
}
fn mtime(&self) -> SystemTime {
todo!()
unimplemented!()
}
fn file_name<'a>(&'a self, _: &'a gpui::AppContext) -> &'a std::ffi::OsStr {
todo!()
unimplemented!()
}
fn worktree_id(&self) -> usize {
0
}
fn is_deleted(&self) -> bool {
todo!()
unimplemented!()
}
fn as_any(&self) -> &dyn std::any::Any {
todo!()
unimplemented!()
}
fn to_proto(&self) -> rpc::proto::File {
todo!()
unimplemented!()
}
}
}

View File

@@ -1,10 +1,8 @@
use std::ops::Range;
use crate::{Anchor, DisplayPoint, Editor, EditorSnapshot, SelectPhase};
use gpui::{Task, ViewContext};
use language::{Bias, ToOffset};
use project::LocationLink;
use settings::Settings;
use std::ops::Range;
use util::TryFutureExt;
#[derive(Debug, Default)]
@@ -211,7 +209,7 @@ pub fn show_link_definition(
});
// Highlight symbol using theme link definition highlight style
let style = cx.global::<Settings>().theme.editor.link_definition;
let style = theme::current(cx).editor.link_definition;
this.highlight_text::<LinkGoToDefinitionState>(
vec![highlight_range],
style,
@@ -297,6 +295,8 @@ fn go_to_fetched_definition_of_kind(
#[cfg(test)]
mod tests {
use super::*;
use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
use futures::StreamExt;
use gpui::{
platform::{self, Modifiers, ModifiersChangedEvent},
@@ -305,15 +305,14 @@ mod tests {
use indoc::indoc;
use lsp::request::{GotoDefinition, GotoTypeDefinition};
use crate::test::editor_lsp_test_context::EditorLspTestContext;
use super::*;
#[gpui::test]
async fn test_link_go_to_type_definition(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
let mut cx = EditorLspTestContext::new_rust(
lsp::ServerCapabilities {
hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
type_definition_provider: Some(lsp::TypeDefinitionProviderCapability::Simple(true)),
..Default::default()
},
cx,
@@ -417,6 +416,8 @@ mod tests {
#[gpui::test]
async fn test_link_go_to_definition(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
let mut cx = EditorLspTestContext::new_rust(
lsp::ServerCapabilities {
hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),

View File

@@ -57,13 +57,14 @@ pub fn deploy_context_menu(
#[cfg(test)]
mod tests {
use crate::test::editor_lsp_test_context::EditorLspTestContext;
use super::*;
use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
use indoc::indoc;
#[gpui::test]
async fn test_mouse_context_menu(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
let mut cx = EditorLspTestContext::new_rust(
lsp::ServerCapabilities {
hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),

View File

@@ -193,6 +193,44 @@ pub fn next_subword_end(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPo
})
}
pub fn start_of_paragraph(map: &DisplaySnapshot, display_point: DisplayPoint) -> DisplayPoint {
let point = display_point.to_point(map);
if point.row == 0 {
return map.max_point();
}
let mut found_non_blank_line = false;
for row in (0..point.row + 1).rev() {
let blank = map.buffer_snapshot.is_line_blank(row);
if found_non_blank_line && blank {
return Point::new(row, 0).to_display_point(map);
}
found_non_blank_line |= !blank;
}
DisplayPoint::zero()
}
pub fn end_of_paragraph(map: &DisplaySnapshot, display_point: DisplayPoint) -> DisplayPoint {
let point = display_point.to_point(map);
if point.row == map.max_buffer_row() {
return DisplayPoint::zero();
}
let mut found_non_blank_line = false;
for row in point.row..map.max_buffer_row() + 1 {
let blank = map.buffer_snapshot.is_line_blank(row);
if found_non_blank_line && blank {
return Point::new(row, 0).to_display_point(map);
}
found_non_blank_line |= !blank;
}
map.max_point()
}
/// 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, and will be called with `\n` characters indicating the start
@@ -369,11 +407,12 @@ pub fn split_display_range_by_lines(
mod tests {
use super::*;
use crate::{test::marked_display_snapshot, Buffer, DisplayMap, ExcerptRange, MultiBuffer};
use settings::Settings;
use settings::SettingsStore;
#[gpui::test]
fn test_previous_word_start(cx: &mut gpui::AppContext) {
cx.set_global(Settings::test(cx));
init_test(cx);
fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
assert_eq!(
@@ -400,7 +439,8 @@ mod tests {
#[gpui::test]
fn test_previous_subword_start(cx: &mut gpui::AppContext) {
cx.set_global(Settings::test(cx));
init_test(cx);
fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
assert_eq!(
@@ -434,7 +474,8 @@ mod tests {
#[gpui::test]
fn test_find_preceding_boundary(cx: &mut gpui::AppContext) {
cx.set_global(Settings::test(cx));
init_test(cx);
fn assert(
marked_text: &str,
cx: &mut gpui::AppContext,
@@ -466,7 +507,8 @@ mod tests {
#[gpui::test]
fn test_next_word_end(cx: &mut gpui::AppContext) {
cx.set_global(Settings::test(cx));
init_test(cx);
fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
assert_eq!(
@@ -490,7 +532,8 @@ mod tests {
#[gpui::test]
fn test_next_subword_end(cx: &mut gpui::AppContext) {
cx.set_global(Settings::test(cx));
init_test(cx);
fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
assert_eq!(
@@ -523,7 +566,8 @@ mod tests {
#[gpui::test]
fn test_find_boundary(cx: &mut gpui::AppContext) {
cx.set_global(Settings::test(cx));
init_test(cx);
fn assert(
marked_text: &str,
cx: &mut gpui::AppContext,
@@ -555,7 +599,8 @@ mod tests {
#[gpui::test]
fn test_surrounding_word(cx: &mut gpui::AppContext) {
cx.set_global(Settings::test(cx));
init_test(cx);
fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
assert_eq!(
@@ -576,7 +621,8 @@ mod tests {
#[gpui::test]
fn test_move_up_and_down_with_excerpts(cx: &mut gpui::AppContext) {
cx.set_global(Settings::test(cx));
init_test(cx);
let family_id = cx
.font_cache()
.load_family(&["Helvetica"], &Default::default())
@@ -691,4 +737,11 @@ mod tests {
(DisplayPoint::new(7, 2), SelectionGoal::Column(2)),
);
}
fn init_test(cx: &mut gpui::AppContext) {
cx.set_global(SettingsStore::test(cx));
theme::init((), cx);
language::init(cx);
crate::init(cx);
}
}

View File

@@ -9,7 +9,9 @@ use git::diff::DiffHunk;
use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task};
pub use language::Completion;
use language::{
char_kind, AutoindentMode, Buffer, BufferChunks, BufferSnapshot, CharKind, Chunk, CursorShape,
char_kind,
language_settings::{language_settings, LanguageSettings},
AutoindentMode, Buffer, BufferChunks, BufferSnapshot, CharKind, Chunk, CursorShape,
DiagnosticEntry, File, IndentSize, Language, LanguageScope, OffsetRangeExt, OffsetUtf16,
Outline, OutlineItem, Point, PointUtf16, Selection, TextDimension, ToOffset as _,
ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, TransactionId, Unclipped,
@@ -62,8 +64,12 @@ pub enum Event {
ExcerptsRemoved {
ids: Vec<ExcerptId>,
},
ExcerptsEdited {
ids: Vec<ExcerptId>,
},
Edited,
Reloaded,
DiffBaseChanged,
LanguageChanged,
Reparsed,
Saved,
@@ -193,6 +199,13 @@ pub struct MultiBufferBytes<'a> {
chunk: &'a [u8],
}
pub struct ReversedMultiBufferBytes<'a> {
range: Range<usize>,
excerpts: Cursor<'a, Excerpt, usize>,
excerpt_bytes: Option<ExcerptBytes<'a>>,
chunk: &'a [u8],
}
struct ExcerptChunks<'a> {
content_chunks: BufferChunks<'a>,
footer_height: usize,
@@ -341,17 +354,6 @@ impl MultiBuffer {
self.read(cx).symbols_containing(offset, theme)
}
pub fn git_diff_recalc(&mut self, cx: &mut ModelContext<Self>) {
let buffers = self.buffers.borrow();
for buffer_state in buffers.values() {
if buffer_state.buffer.read(cx).needs_git_diff_recalc() {
buffer_state
.buffer
.update(cx, |buffer, cx| buffer.git_diff_recalc(cx))
}
}
}
pub fn edit<I, S, T>(
&mut self,
edits: I,
@@ -395,6 +397,7 @@ impl MultiBuffer {
original_indent_column: u32,
}
let mut buffer_edits: HashMap<u64, Vec<BufferEdit>> = Default::default();
let mut edited_excerpt_ids = Vec::new();
let mut cursor = snapshot.excerpts.cursor::<usize>();
for (ix, (range, new_text)) in edits.enumerate() {
let new_text: Arc<str> = new_text.into();
@@ -411,6 +414,7 @@ impl MultiBuffer {
.start
.to_offset(&start_excerpt.buffer)
+ start_overshoot;
edited_excerpt_ids.push(start_excerpt.id);
cursor.seek(&range.end, Bias::Right, &());
if cursor.item().is_none() && range.end == *cursor.start() {
@@ -436,6 +440,7 @@ impl MultiBuffer {
original_indent_column,
});
} else {
edited_excerpt_ids.push(end_excerpt.id);
let start_excerpt_range = buffer_start
..start_excerpt
.range
@@ -482,6 +487,7 @@ impl MultiBuffer {
is_insertion: false,
original_indent_column,
});
edited_excerpt_ids.push(excerpt.id);
cursor.next(&());
}
}
@@ -554,6 +560,10 @@ impl MultiBuffer {
buffer.edit(insertions, insertion_autoindent_mode, cx);
})
}
cx.emit(Event::ExcerptsEdited {
ids: edited_excerpt_ids,
});
}
pub fn start_transaction(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
@@ -1108,7 +1118,7 @@ impl MultiBuffer {
&self,
point: T,
cx: &AppContext,
) -> Option<(ModelHandle<Buffer>, usize)> {
) -> Option<(ModelHandle<Buffer>, usize, ExcerptId)> {
let snapshot = self.read(cx);
let offset = point.to_offset(&snapshot);
let mut cursor = snapshot.excerpts.cursor::<usize>();
@@ -1122,7 +1132,7 @@ impl MultiBuffer {
let buffer_point = excerpt_start + offset - *cursor.start();
let buffer = self.buffers.borrow()[&excerpt.buffer_id].buffer.clone();
(buffer, buffer_point)
(buffer, buffer_point, excerpt.id)
})
}
@@ -1130,7 +1140,7 @@ impl MultiBuffer {
&self,
range: Range<T>,
cx: &AppContext,
) -> Vec<(ModelHandle<Buffer>, Range<usize>)> {
) -> Vec<(ModelHandle<Buffer>, Range<usize>, ExcerptId)> {
let snapshot = self.read(cx);
let start = range.start.to_offset(&snapshot);
let end = range.end.to_offset(&snapshot);
@@ -1138,6 +1148,10 @@ impl MultiBuffer {
let mut result = Vec::new();
let mut cursor = snapshot.excerpts.cursor::<usize>();
cursor.seek(&start, Bias::Right, &());
if cursor.item().is_none() {
cursor.prev(&());
}
while let Some(excerpt) = cursor.item() {
if *cursor.start() > end {
break;
@@ -1151,7 +1165,7 @@ impl MultiBuffer {
let start = excerpt_start + (cmp::max(start, *cursor.start()) - *cursor.start());
let end = excerpt_start + (cmp::min(end, end_before_newline) - *cursor.start());
let buffer = self.buffers.borrow()[&excerpt.buffer_id].buffer.clone();
result.push((buffer, start..end));
result.push((buffer, start..end, excerpt.id));
cursor.next(&());
}
@@ -1306,6 +1320,7 @@ impl MultiBuffer {
language::Event::Saved => Event::Saved,
language::Event::FileHandleChanged => Event::FileHandleChanged,
language::Event::Reloaded => Event::Reloaded,
language::Event::DiffBaseChanged => Event::DiffBaseChanged,
language::Event::LanguageChanged => Event::LanguageChanged,
language::Event::Reparsed => Event::Reparsed,
language::Event::DiagnosticsUpdated => Event::DiagnosticsUpdated,
@@ -1372,7 +1387,22 @@ impl MultiBuffer {
cx: &'a AppContext,
) -> Option<Arc<Language>> {
self.point_to_buffer_offset(point, cx)
.and_then(|(buffer, offset)| buffer.read(cx).language_at(offset))
.and_then(|(buffer, offset, _)| buffer.read(cx).language_at(offset))
}
pub fn settings_at<'a, T: ToOffset>(
&self,
point: T,
cx: &'a AppContext,
) -> &'a LanguageSettings {
let mut language = None;
let mut file = None;
if let Some((buffer, offset, _)) = self.point_to_buffer_offset(point, cx) {
let buffer = buffer.read(cx);
language = buffer.language_at(offset);
file = buffer.file();
}
language_settings(language.as_ref(), file, cx)
}
pub fn for_each_buffer(&self, mut f: impl FnMut(&ModelHandle<Buffer>)) {
@@ -1535,6 +1565,13 @@ impl MultiBuffer {
cx.add_model(|cx| Self::singleton(buffer, cx))
}
pub fn build_from_buffer(
buffer: ModelHandle<Buffer>,
cx: &mut gpui::AppContext,
) -> ModelHandle<Self> {
cx.add_model(|cx| Self::singleton(buffer, cx))
}
pub fn build_random(rng: &mut impl rand::Rng, cx: &mut gpui::AppContext) -> ModelHandle<Self> {
cx.add_model(|cx| {
let mut multibuffer = MultiBuffer::new(0);
@@ -1948,7 +1985,6 @@ impl MultiBufferSnapshot {
} else {
None
};
MultiBufferBytes {
range,
excerpts,
@@ -1957,6 +1993,33 @@ impl MultiBufferSnapshot {
}
}
pub fn reversed_bytes_in_range<T: ToOffset>(
&self,
range: Range<T>,
) -> ReversedMultiBufferBytes {
let range = range.start.to_offset(self)..range.end.to_offset(self);
let mut excerpts = self.excerpts.cursor::<usize>();
excerpts.seek(&range.end, Bias::Left, &());
let mut chunk = &[][..];
let excerpt_bytes = if let Some(excerpt) = excerpts.item() {
let mut excerpt_bytes = excerpt.reversed_bytes_in_range(
range.start - excerpts.start()..range.end - excerpts.start(),
);
chunk = excerpt_bytes.next().unwrap_or(&[][..]);
Some(excerpt_bytes)
} else {
None
};
ReversedMultiBufferBytes {
range,
excerpts,
excerpt_bytes,
chunk,
}
}
pub fn buffer_rows(&self, start_row: u32) -> MultiBufferRows {
let mut result = MultiBufferRows {
buffer_row_range: 0..0,
@@ -2767,6 +2830,20 @@ impl MultiBufferSnapshot {
.and_then(|(buffer, offset)| buffer.language_at(offset))
}
pub fn settings_at<'a, T: ToOffset>(
&'a self,
point: T,
cx: &'a AppContext,
) -> &'a LanguageSettings {
let mut language = None;
let mut file = None;
if let Some((buffer, offset)) = self.point_to_buffer_offset(point) {
language = buffer.language_at(offset);
file = buffer.file();
}
language_settings(language, file, cx)
}
pub fn language_scope_at<'a, T: ToOffset>(&'a self, point: T) -> Option<LanguageScope> {
self.point_to_buffer_offset(point)
.and_then(|(buffer, offset)| buffer.language_scope_at(offset))
@@ -3376,6 +3453,26 @@ impl Excerpt {
}
}
fn reversed_bytes_in_range(&self, range: Range<usize>) -> ExcerptBytes {
let content_start = self.range.context.start.to_offset(&self.buffer);
let bytes_start = content_start + range.start;
let bytes_end = content_start + cmp::min(range.end, self.text_summary.len);
let footer_height = if self.has_trailing_newline
&& range.start <= self.text_summary.len
&& range.end > self.text_summary.len
{
1
} else {
0
};
let content_bytes = self.buffer.reversed_bytes_in_range(bytes_start..bytes_end);
ExcerptBytes {
content_bytes,
footer_height,
}
}
fn clip_anchor(&self, text_anchor: text::Anchor) -> text::Anchor {
if text_anchor
.cmp(&self.range.context.start, &self.buffer)
@@ -3694,6 +3791,38 @@ impl<'a> io::Read for MultiBufferBytes<'a> {
}
}
impl<'a> ReversedMultiBufferBytes<'a> {
fn consume(&mut self, len: usize) {
self.range.end -= len;
self.chunk = &self.chunk[..self.chunk.len() - len];
if !self.range.is_empty() && self.chunk.is_empty() {
if let Some(chunk) = self.excerpt_bytes.as_mut().and_then(|bytes| bytes.next()) {
self.chunk = chunk;
} else {
self.excerpts.next(&());
if let Some(excerpt) = self.excerpts.item() {
let mut excerpt_bytes =
excerpt.bytes_in_range(0..self.range.end - self.excerpts.start());
self.chunk = excerpt_bytes.next().unwrap();
self.excerpt_bytes = Some(excerpt_bytes);
}
}
}
}
}
impl<'a> io::Read for ReversedMultiBufferBytes<'a> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let len = cmp::min(buf.len(), self.chunk.len());
buf[..len].copy_from_slice(&self.chunk[..len]);
buf[..len].reverse();
if len > 0 {
self.consume(len);
}
Ok(len)
}
}
impl<'a> Iterator for ExcerptBytes<'a> {
type Item = &'a [u8];
@@ -3845,15 +3974,17 @@ where
#[cfg(test)]
mod tests {
use crate::editor_tests::init_test;
use super::*;
use futures::StreamExt;
use gpui::{AppContext, TestAppContext};
use language::{Buffer, Rope};
use project::{FakeFs, Project};
use rand::prelude::*;
use settings::Settings;
use settings::SettingsStore;
use std::{env, rc::Rc};
use unindent::Unindent;
use util::test::sample_text;
#[gpui::test]
@@ -4540,73 +4671,85 @@ mod tests {
#[gpui::test]
async fn test_diff_hunks_in_range(cx: &mut TestAppContext) {
use git::diff::DiffHunkStatus;
init_test(cx, |_| {});
let fs = FakeFs::new(cx.background());
let project = Project::test(fs, [], cx).await;
// buffer has two modified hunks with two rows each
let buffer_1 = cx.add_model(|cx| {
let mut buffer = Buffer::new(
0,
"
1.zero
1.ONE
1.TWO
1.three
1.FOUR
1.FIVE
1.six
"
.unindent(),
cx,
);
let buffer_1 = project
.update(cx, |project, cx| {
project.create_buffer(
"
1.zero
1.ONE
1.TWO
1.three
1.FOUR
1.FIVE
1.six
"
.unindent()
.as_str(),
None,
cx,
)
})
.unwrap();
buffer_1.update(cx, |buffer, cx| {
buffer.set_diff_base(
Some(
"
1.zero
1.one
1.two
1.three
1.four
1.five
1.six
"
1.zero
1.one
1.two
1.three
1.four
1.five
1.six
"
.unindent(),
),
cx,
);
buffer
});
// buffer has a deletion hunk and an insertion hunk
let buffer_2 = cx.add_model(|cx| {
let mut buffer = Buffer::new(
0,
"
2.zero
2.one
2.two
2.three
2.four
2.five
2.six
"
.unindent(),
cx,
);
let buffer_2 = project
.update(cx, |project, cx| {
project.create_buffer(
"
2.zero
2.one
2.two
2.three
2.four
2.five
2.six
"
.unindent()
.as_str(),
None,
cx,
)
})
.unwrap();
buffer_2.update(cx, |buffer, cx| {
buffer.set_diff_base(
Some(
"
2.zero
2.one
2.one-and-a-half
2.two
2.three
2.four
2.six
"
2.zero
2.one
2.one-and-a-half
2.two
2.three
2.four
2.six
"
.unindent(),
),
cx,
);
buffer
});
cx.foreground().run_until_parked();
@@ -5052,16 +5195,19 @@ mod tests {
.read(cx)
.range_to_buffer_ranges(start_ix..end_ix, cx);
let excerpted_buffers_text = excerpted_buffer_ranges
.into_iter()
.map(|(buffer, buffer_range)| {
.iter()
.map(|(buffer, buffer_range, _)| {
buffer
.read(cx)
.text_for_range(buffer_range)
.text_for_range(buffer_range.clone())
.collect::<String>()
})
.collect::<Vec<_>>()
.join("\n");
assert_eq!(excerpted_buffers_text, text_for_range);
if !expected_excerpts.is_empty() {
assert!(!excerpted_buffer_ranges.is_empty());
}
let expected_summary = TextSummary::from(&expected_text[start_ix..end_ix]);
assert_eq!(
@@ -5128,7 +5274,8 @@ mod tests {
#[gpui::test]
fn test_history(cx: &mut AppContext) {
cx.set_global(Settings::test(cx));
cx.set_global(SettingsStore::test(cx));
let buffer_1 = cx.add_model(|cx| Buffer::new(0, "1234", cx));
let buffer_2 = cx.add_model(|cx| Buffer::new(0, "5678", cx));
let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
@@ -5196,7 +5343,7 @@ mod tests {
assert_eq!(multibuffer.read(cx).text(), "ABCDE1234\nAB5678");
// An undo in the multibuffer undoes the multibuffer transaction
// and also any individual buffer edits that have occured since
// and also any individual buffer edits that have occurred since
// that transaction.
multibuffer.undo(cx);
assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");

View File

@@ -36,21 +36,21 @@ pub struct ScrollbarAutoHide(pub bool);
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ScrollAnchor {
pub offset: Vector2F,
pub top_anchor: Anchor,
pub anchor: Anchor,
}
impl ScrollAnchor {
fn new() -> Self {
Self {
offset: Vector2F::zero(),
top_anchor: Anchor::min(),
anchor: Anchor::min(),
}
}
pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> Vector2F {
let mut scroll_position = self.offset;
if self.top_anchor != Anchor::min() {
let scroll_top = self.top_anchor.to_display_point(snapshot).row() as f32;
if self.anchor != Anchor::min() {
let scroll_top = self.anchor.to_display_point(snapshot).row() as f32;
scroll_position.set_y(scroll_top + scroll_position.y());
} else {
scroll_position.set_y(0.);
@@ -59,7 +59,7 @@ impl ScrollAnchor {
}
pub fn top_row(&self, buffer: &MultiBufferSnapshot) -> u32 {
self.top_anchor.to_point(buffer).row
self.anchor.to_point(buffer).row
}
}
@@ -173,13 +173,14 @@ impl ScrollManager {
scroll_position: Vector2F,
map: &DisplaySnapshot,
local: bool,
autoscroll: bool,
workspace_id: Option<i64>,
cx: &mut ViewContext<Editor>,
) {
let (new_anchor, top_row) = if scroll_position.y() <= 0. {
(
ScrollAnchor {
top_anchor: Anchor::min(),
anchor: Anchor::min(),
offset: scroll_position.max(vec2f(0., 0.)),
},
0,
@@ -193,7 +194,7 @@ impl ScrollManager {
(
ScrollAnchor {
top_anchor,
anchor: top_anchor,
offset: vec2f(
scroll_position.x(),
scroll_position.y() - top_anchor.to_display_point(&map).row() as f32,
@@ -203,7 +204,7 @@ impl ScrollManager {
)
};
self.set_anchor(new_anchor, top_row, local, workspace_id, cx);
self.set_anchor(new_anchor, top_row, local, autoscroll, workspace_id, cx);
}
fn set_anchor(
@@ -211,11 +212,12 @@ impl ScrollManager {
anchor: ScrollAnchor,
top_row: u32,
local: bool,
autoscroll: bool,
workspace_id: Option<i64>,
cx: &mut ViewContext<Editor>,
) {
self.anchor = anchor;
cx.emit(Event::ScrollPositionChanged { local });
cx.emit(Event::ScrollPositionChanged { local, autoscroll });
self.show_scrollbar(cx);
self.autoscroll_request.take();
if let Some(workspace_id) = workspace_id {
@@ -296,21 +298,28 @@ impl Editor {
}
pub fn set_scroll_position(&mut self, scroll_position: Vector2F, cx: &mut ViewContext<Self>) {
self.set_scroll_position_internal(scroll_position, true, cx);
self.set_scroll_position_internal(scroll_position, true, false, cx);
}
pub(crate) fn set_scroll_position_internal(
&mut self,
scroll_position: Vector2F,
local: bool,
autoscroll: bool,
cx: &mut ViewContext<Self>,
) {
let map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
hide_hover(self, cx);
let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1);
self.scroll_manager
.set_scroll_position(scroll_position, &map, local, workspace_id, cx);
self.scroll_manager.set_scroll_position(
scroll_position,
&map,
local,
autoscroll,
workspace_id,
cx,
);
}
pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> Vector2F {
@@ -322,11 +331,11 @@ impl Editor {
hide_hover(self, cx);
let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1);
let top_row = scroll_anchor
.top_anchor
.anchor
.to_point(&self.buffer().read(cx).snapshot(cx))
.row;
self.scroll_manager
.set_anchor(scroll_anchor, top_row, true, workspace_id, cx);
.set_anchor(scroll_anchor, top_row, true, false, workspace_id, cx);
}
pub(crate) fn set_scroll_anchor_remote(
@@ -337,11 +346,11 @@ impl Editor {
hide_hover(self, cx);
let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1);
let top_row = scroll_anchor
.top_anchor
.anchor
.to_point(&self.buffer().read(cx).snapshot(cx))
.row;
self.scroll_manager
.set_anchor(scroll_anchor, top_row, false, workspace_id, cx);
.set_anchor(scroll_anchor, top_row, false, false, workspace_id, cx);
}
pub fn scroll_screen(&mut self, amount: &ScrollAmount, cx: &mut ViewContext<Self>) {
@@ -377,7 +386,7 @@ impl Editor {
let screen_top = self
.scroll_manager
.anchor
.top_anchor
.anchor
.to_display_point(&snapshot);
if screen_top > newest_head {
@@ -408,7 +417,7 @@ impl Editor {
.anchor_at(Point::new(top_row as u32, 0), Bias::Left);
let scroll_anchor = ScrollAnchor {
offset: Vector2F::new(x, y),
top_anchor,
anchor: top_anchor,
};
self.set_scroll_anchor(scroll_anchor, cx);
}

View File

@@ -86,7 +86,7 @@ impl Editor {
editor.set_scroll_anchor(
ScrollAnchor {
top_anchor: new_anchor,
anchor: new_anchor,
offset: Default::default(),
},
cx,
@@ -113,7 +113,7 @@ impl Editor {
editor.set_scroll_anchor(
ScrollAnchor {
top_anchor: new_anchor,
anchor: new_anchor,
offset: Default::default(),
},
cx,
@@ -143,7 +143,7 @@ impl Editor {
editor.set_scroll_anchor(
ScrollAnchor {
top_anchor: new_anchor,
anchor: new_anchor,
offset: Default::default(),
},
cx,

View File

@@ -136,23 +136,23 @@ impl Editor {
if target_top < start_row {
scroll_position.set_y(target_top);
self.set_scroll_position_internal(scroll_position, local, cx);
self.set_scroll_position_internal(scroll_position, local, true, cx);
} else if target_bottom >= end_row {
scroll_position.set_y(target_bottom - visible_lines);
self.set_scroll_position_internal(scroll_position, local, cx);
self.set_scroll_position_internal(scroll_position, local, true, cx);
}
}
AutoscrollStrategy::Center => {
scroll_position.set_y((first_cursor_top - margin).max(0.0));
self.set_scroll_position_internal(scroll_position, local, cx);
self.set_scroll_position_internal(scroll_position, local, true, cx);
}
AutoscrollStrategy::Top => {
scroll_position.set_y((first_cursor_top).max(0.0));
self.set_scroll_position_internal(scroll_position, local, cx);
self.set_scroll_position_internal(scroll_position, local, true, cx);
}
AutoscrollStrategy::Bottom => {
scroll_position.set_y((last_cursor_bottom - visible_lines).max(0.0));
self.set_scroll_position_internal(scroll_position, local, cx);
self.set_scroll_position_internal(scroll_position, local, true, cx);
}
}

View File

@@ -76,6 +76,9 @@ impl SelectionsCollection {
count
}
/// The non-pending, non-overlapping selections. There could still be a pending
/// selection that overlaps these if the mouse is being dragged, etc. Returned as
/// selections over Anchors.
pub fn disjoint_anchors(&self) -> Arc<[Selection<Anchor>]> {
self.disjoint.clone()
}

View File

@@ -48,8 +48,8 @@ pub fn marked_display_snapshot(
}
pub fn select_ranges(editor: &mut Editor, marked_text: &str, cx: &mut ViewContext<Editor>) {
let (umarked_text, text_ranges) = marked_text_ranges(marked_text, true);
assert_eq!(editor.text(cx), umarked_text);
let (unmarked_text, text_ranges) = marked_text_ranges(marked_text, true);
assert_eq!(editor.text(cx), unmarked_text);
editor.change_selections(None, cx, |s| s.select_ranges(text_ranges));
}

View File

@@ -34,13 +34,17 @@ impl<'a> EditorLspTestContext<'a> {
) -> EditorLspTestContext<'a> {
use json::json;
let app_state = cx.update(AppState::test);
cx.update(|cx| {
theme::init((), cx);
language::init(cx);
crate::init(cx);
pane::init(cx);
Project::init_settings(cx);
workspace::init_settings(cx);
});
let app_state = cx.update(AppState::test);
let file_name = format!(
"file.{}",
language

View File

@@ -1,19 +1,17 @@
use crate::{
display_map::ToDisplayPoint, AnchorRangeExt, Autoscroll, DisplayPoint, Editor, MultiBuffer,
};
use futures::Future;
use gpui::{
keymap_matcher::Keystroke, AppContext, ContextHandle, ModelContext, ViewContext, ViewHandle,
};
use indoc::indoc;
use language::{Buffer, BufferSnapshot};
use project::{FakeFs, Project};
use std::{
any::TypeId,
ops::{Deref, DerefMut, Range},
};
use futures::Future;
use indoc::indoc;
use crate::{
display_map::ToDisplayPoint, AnchorRangeExt, Autoscroll, DisplayPoint, Editor, MultiBuffer,
};
use gpui::{
keymap_matcher::Keystroke, AppContext, ContextHandle, ModelContext, ViewContext, ViewHandle,
};
use language::{Buffer, BufferSnapshot};
use settings::Settings;
use util::{
assert_set_eq,
test::{generate_marked_text, marked_text_ranges},
@@ -28,17 +26,17 @@ pub struct EditorTestContext<'a> {
}
impl<'a> EditorTestContext<'a> {
pub fn new(cx: &'a mut gpui::TestAppContext) -> EditorTestContext<'a> {
pub async fn new(cx: &'a mut gpui::TestAppContext) -> EditorTestContext<'a> {
let fs = FakeFs::new(cx.background());
let project = Project::test(fs, [], cx).await;
let buffer = project
.update(cx, |project, cx| project.create_buffer("", None, cx))
.unwrap();
let (window_id, editor) = cx.update(|cx| {
cx.set_global(Settings::test(cx));
crate::init(cx);
let (window_id, editor) = cx.add_window(Default::default(), |cx| {
cx.add_window(Default::default(), |cx| {
cx.focus_self();
build_editor(MultiBuffer::build_simple("", cx), cx)
});
(window_id, editor)
build_editor(MultiBuffer::build_from_buffer(buffer, cx), cx)
})
});
Self {

View File

@@ -16,6 +16,7 @@ editor = { path = "../editor" }
language = { path = "../language" }
gpui = { path = "../gpui" }
project = { path = "../project" }
regex.workspace = true
search = { path = "../search" }
settings = { path = "../settings" }
theme = { path = "../theme" }
@@ -27,7 +28,7 @@ futures.workspace = true
anyhow.workspace = true
smallvec.workspace = true
human_bytes = "0.4.1"
isahc = "1.7"
isahc.workspace = true
lazy_static.workspace = true
postage.workspace = true
serde.workspace = true
@@ -35,3 +36,6 @@ serde_derive.workspace = true
sysinfo = "0.27.1"
tree-sitter-markdown = { git = "https://github.com/MDeiml/tree-sitter-markdown", rev = "330ecab87a3e3a7211ac69bbadc19eabecdb1cca" }
urlencoding = "2.1.2"
[dev-dependencies]
editor = { path = "../editor", features = ["test-support"] }

View File

@@ -3,7 +3,6 @@ use gpui::{
platform::{CursorStyle, MouseButton},
Entity, View, ViewContext, WeakViewHandle,
};
use settings::Settings;
use workspace::{item::ItemHandle, StatusItemView, Workspace};
use crate::feedback_editor::{FeedbackEditor, GiveFeedback};
@@ -33,15 +32,15 @@ impl View for DeployFeedbackButton {
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
let active = self.active;
let theme = cx.global::<Settings>().theme.clone();
let theme = theme::current(cx).clone();
Stack::new()
.with_child(
MouseEventHandler::<Self, Self>::new(0, cx, |state, _| {
let style = &theme
.workspace
.status_bar
.sidebar_buttons
.item
.panel_buttons
.button
.style_for(state, active);
Svg::new("icons/feedback_16.svg")

View File

@@ -14,6 +14,7 @@ use isahc::Request;
use language::Buffer;
use postage::prelude::Stream;
use project::Project;
use regex::Regex;
use serde::Serialize;
use smallvec::SmallVec;
use std::{
@@ -46,6 +47,7 @@ pub fn init(cx: &mut AppContext) {
#[derive(Serialize)]
struct FeedbackRequestBody<'a> {
feedback_text: &'a str,
email: Option<String>,
metrics_id: Option<Arc<str>>,
installation_id: Option<Arc<str>>,
system_specs: SystemSpecs,
@@ -157,8 +159,18 @@ impl FeedbackEditor {
let is_staff = telemetry.is_staff();
let http_client = zed_client.http_client();
let re = Regex::new(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b").unwrap();
let emails: Vec<&str> = re
.captures_iter(feedback_text)
.map(|capture| capture.get(0).unwrap().as_str())
.collect();
let email = emails.first().map(|e| e.to_string());
let request = FeedbackRequestBody {
feedback_text: &feedback_text,
email,
metrics_id,
installation_id,
system_specs,

View File

@@ -3,7 +3,6 @@ use gpui::{
platform::{CursorStyle, MouseButton},
AnyElement, Element, Entity, View, ViewContext, ViewHandle,
};
use settings::Settings;
use workspace::{item::ItemHandle, ToolbarItemLocation, ToolbarItemView};
use crate::{feedback_editor::FeedbackEditor, open_zed_community_repo, OpenZedCommunityRepo};
@@ -30,12 +29,12 @@ impl View for FeedbackInfoText {
}
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
let theme = cx.global::<Settings>().theme.clone();
let theme = theme::current(cx).clone();
Flex::row()
.with_child(
Text::new(
"We read whatever you submit here. For issues and discussions, visit the ",
"Share your feedback. Include your email for replies. For issues and discussions, visit the ",
theme.feedback.info_text_default.text.clone(),
)
.with_soft_wrap(false)
@@ -61,7 +60,7 @@ impl View for FeedbackInfoText {
}),
)
.with_child(
Text::new(" on GitHub.", theme.feedback.info_text_default.text.clone())
Text::new(".", theme.feedback.info_text_default.text.clone())
.with_soft_wrap(false)
.aligned(),
)

View File

@@ -5,7 +5,6 @@ use gpui::{
platform::{CursorStyle, MouseButton},
AnyElement, AppContext, Element, Entity, Task, View, ViewContext, ViewHandle,
};
use settings::Settings;
use workspace::{item::ItemHandle, ToolbarItemLocation, ToolbarItemView};
pub fn init(cx: &mut AppContext) {
@@ -46,7 +45,7 @@ impl View for SubmitFeedbackButton {
}
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
let theme = cx.global::<Settings>().theme.clone();
let theme = theme::current(cx).clone();
enum SubmitFeedbackButton {}
MouseEventHandler::<SubmitFeedbackButton, Self>::new(0, cx, |state, _| {
let style = theme.feedback.submit_button.style_for(state, false);

View File

@@ -6,6 +6,8 @@ use std::{env, fmt::Display};
use sysinfo::{System, SystemExt};
use util::channel::ReleaseChannel;
// TODO: Move this file out of feedback and into a more general place
#[derive(Clone, Debug, Serialize)]
pub struct SystemSpecs {
#[serde(serialize_with = "serialize_app_version")]

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