Compare commits

...

1290 Commits

Author SHA1 Message Date
Kirill Bulatov
eb12b0b9ed Merge pull request #2463 from zed-industries/kb/reapply-modal-accessibility
Reintroduce more accesible modal keybindings
2023-05-17 13:46:25 -04:00
Kirill Bulatov
5c3690c3f1 Merge pull request #2470 from zed-industries/kb/fix-project-search-esc
In project search on ESC, reduce multiple carets to one first
2023-05-17 13:45:18 -04:00
Joseph Lyons
baedb7d0fc v0.86.x stable 2023-05-17 12:38:43 -04:00
Joseph Lyons
fd72d24060 zed 0.86.1 2023-05-11 11:42:14 -04:00
Joseph Lyons
a17b5e8a80 Revert "More keybindings in macOs modals with buttons"
This reverts commit 1398a12062.
2023-05-11 11:38:30 -04:00
Joseph Lyons
0092df7c51 v0.86.x preview 2023-05-10 16:46:52 -04:00
Mikayla Maki
c7fcc031eb Merge pull request #2460 from zed-industries/show-dock-on-activate
Fix bug with terminal button
2023-05-10 09:08:45 -07:00
Mikayla Maki
0dce5ba7ae Fix bug with terminal button 2023-05-10 08:15:20 -07:00
Kirill Bulatov
eec60556ab Highlight include/exclude inputs when errors happen there 2023-05-10 11:11:31 +03:00
Kirill Bulatov
dfdf7e4866 Test the search inclusions/exclusions 2023-05-10 11:11:31 +03:00
Kirill Bulatov
80fc1bc276 Use placeholder in include/exclude editors 2023-05-10 11:11:31 +03:00
Kirill Bulatov
0e31d13a1e Rework tab and escape key handling in search panel
Fixes
https://linear.app/zed-industries/issue/Z-1238/focus-the-results-multibuffer-in-project-search-on-esc

and adds a tab shortcut for project search inputs

co-authored-by: Julia <julia@zed.dev>
2023-05-10 11:11:31 +03:00
Kirill Bulatov
3da55c14a6 Fix arrow layout 2023-05-10 11:11:31 +03:00
Kirill Bulatov
6fb8679184 Trim glob input 2023-05-10 11:11:31 +03:00
Kirill Bulatov
13296d502c Extra rows approach draft
co-authored-by: Max <max@zed.dev>
2023-05-10 11:11:31 +03:00
Kirill Bulatov
b5abac6af6 Draft search include/exclude logic 2023-05-10 11:11:31 +03:00
Kirill Bulatov
915154b047 Add initial include/exclude project search UI 2023-05-10 11:11:31 +03:00
Antonio Scandurra
3115c8381d Merge pull request #2458 from zed-industries/fix-context-menu-click
Always dismiss context menu on click
2023-05-10 09:24:23 +02:00
Antonio Scandurra
1b5e79251c Always dismiss context menu on click 2023-05-10 09:18:13 +02:00
Nathan Sobo
5e8b7bd06d Replace todo with unimplemented to reduce distractions 2023-05-09 16:31:53 -06:00
Mikayla Maki
26d80eef0a Merge pull request #2456 from zed-industries/fix-dock-pane-focus
Make dock not eagerly steal focus from sub items
2023-05-09 15:59:06 -04:00
Mikayla Maki
0214228689 Fix format 2023-05-09 12:54:53 -07:00
Mikayla Maki
6dfb48dbd5 Fix center items not being activated when deserialized 2023-05-09 12:27:07 -07:00
Mikayla Maki
8d561d6408 Make dock not eagerly steal focus from sub items 2023-05-09 12:00:09 -07:00
Mikayla Maki
2d7cfb8c7c Merge pull request #2454 from zed-industries/fix-mismatching-panes-when-deserializing-empty-panes
Fully remove panes and update internal data structures
2023-05-08 20:37:06 -04:00
Mikayla Maki
fa049bea6e Refactor and fix format 2023-05-08 17:32:40 -07:00
Mikayla Maki
49335d017a Add manual removal code to remove_panes 2023-05-08 17:25:28 -07:00
Mikayla Maki
9b2d3fcd48 Fully remove panes and update internal data structures 2023-05-08 17:09:29 -07:00
Max Brunsfeld
8fd0c9fb0e collab 0.12.0 2023-05-08 15:54:11 -07:00
Mikayla Maki
1d66f24f23 Merge pull request #2428 from zed-industries/add-branch-name
Add branch name and synchronize repository representations on the worktree.
2023-05-08 17:40:47 -04:00
Mikayla Maki
9366a0dbee Bump protocol version number 2023-05-08 14:34:14 -07:00
Mikayla Maki
f28419cfd1 Fix styling of titlebar highlights 2023-05-08 14:33:59 -07:00
Mikayla Maki
712fb5ad7f Add postgres migration 2023-05-08 14:33:59 -07:00
Mikayla Maki
1a9afd186b Restore randomized integration tests 2023-05-08 14:33:59 -07:00
Mikayla Maki
15d2f19b4a fix format 2023-05-08 14:33:59 -07:00
Mikayla Maki
d2279674a7 Fix panic in tests 2023-05-08 14:33:59 -07:00
Mikayla Maki
62e763d0d3 Removed test modifications, added special case to git initialization for when the repository is inside a .git folder 2023-05-08 14:33:59 -07:00
Mikayla Maki
f9e4464658 Refresh titlebar on project notifications 2023-05-08 14:33:59 -07:00
Mikayla Maki
2c2076bd77 Adjust tests to not create repositories inside repositories 2023-05-08 14:33:59 -07:00
Mikayla Maki
ab952f1b31 Fixed randomized test failures
co-authored-by: Max <max@zed.dev>
2023-05-08 14:33:59 -07:00
Mikayla Maki
d8dac07408 Removed scan ID from repository interfaces
co-authored-by: Max <max@zed.dev>
2023-05-08 14:33:59 -07:00
Mikayla Maki
270147d20c Finished RepositoryEntry refactoring, smoke tests passing
co-authored-by: Max <max@zed.dev>
2023-05-08 14:33:59 -07:00
Mikayla Maki
53569ece03 WIP: Change RepositoryEntry representation to be keyed off of the work directory
Removes branches button scaffolding
2023-05-08 14:33:59 -07:00
Mikayla Maki
b6d6f5c650 WIP: re-arranging the RepositoryEntry representation
Added branches to the randomized test to check the git branch
Added the remaining database integrations in collab

Co-authored-by: Max <max@zed.dev>
Co-authored-by: Petros <petros@zed.dev>
2023-05-08 14:33:59 -07:00
Mikayla Maki
8bde496e74 Add branch name in title UI
co-authored-by: Petros <petros@zed.dev>
2023-05-08 14:33:59 -07:00
Petros Amoiridis
5302c256a4 Rebase main and fix error 2023-05-08 14:33:59 -07:00
Mikayla Maki
8301ee43d6 WIP: Add repository entries to collab and synchronize when rejoining the room
co-authored-by: Max <max@zed.dev>
2023-05-08 14:33:59 -07:00
Mikayla Maki
2fe5bf419b Add proto fields for repository entry maintenance 2023-05-08 14:33:59 -07:00
Mikayla Maki
c6d7ed33c2 Add smoke test for collaboration 2023-05-08 14:33:59 -07:00
Petros Amoiridis
ca4da52e39 Remove unused functions 2023-05-08 14:33:59 -07:00
Petros Amoiridis
e057b0193f Introduce BrancesButton in title bar
Co-Authored-By: Mikayla Maki <mikayla.c.maki@gmail.com>
2023-05-08 14:33:58 -07:00
Petros Amoiridis
797d47a08c Render title root names without branches 2023-05-08 14:33:58 -07:00
Petros Amoiridis
92a222aba8 Introduce a version control branch icon 2023-05-08 14:33:58 -07:00
Mikayla Maki
8f0aa3c6d9 Add branch name into title 2023-05-08 14:33:58 -07:00
Petros Amoiridis
d34ec462f8 Display branch information per worktree root
Co-Authored-By: Mikayla Maki <mikayla.c.maki@gmail.com>
2023-05-08 14:33:58 -07:00
Mikayla Maki
ffd9d4eb59 Fix bug in repo detection 2023-05-08 14:33:58 -07:00
Mikayla Maki
3570810516 Add API for accessing git branch 2023-05-08 14:33:58 -07:00
Mikayla Maki
26afd592c5 Wire in the branch name 2023-05-08 14:33:58 -07:00
Mikayla Maki
5b4e58d1de Fix repo_for and clean up repository_entries 2023-05-08 14:33:58 -07:00
Mikayla Maki
023d665fb3 Fix TreeMap retain 2023-05-08 14:33:58 -07:00
Mikayla Maki
ae890212e3 Restored a lost API and got everything compiling 2023-05-08 14:33:58 -07:00
Mikayla Maki
bcf608e9e9 WIP: Refactor existing git code to use new representation.
co-authored-by: petros <petros@zed.dev>
2023-05-08 14:33:58 -07:00
Mikayla Maki
563f13925f WIP: Convert old git repository vec to new treemap based approach.
co-authored-by: Nathan <nathan@zed.dev>
2023-05-08 14:33:58 -07:00
Mikayla Maki
a58d3d8128 Add a data driven representation of the current git repository state to the worktree snapshots
WIP: Switch git repositories to use SumTrees

Co-authored-by: Nathan <nathan@zed.dev>
2023-05-08 14:33:58 -07:00
Mikayla Maki
bb93447a0d Merge pull request #2453 from zed-industries/fix-click-fallthrough
Fixed clicks falling through the modal terminal
2023-05-08 13:59:57 -04:00
Mikayla Maki
2cf928c85a Fixed clicks falling through the modal terminal
co-authored-by: Antonio <antonio@zed.dev>
2023-05-08 10:54:12 -07:00
Kirill Bulatov
39bddfc7b7 Only allow invisbles in local and leader selections 2023-05-08 19:36:51 +03:00
Kirill Bulatov
98ff18c430 Code review fixes 2023-05-08 19:36:51 +03:00
Kirill Bulatov
e6489e999d Add invisibles wrapping test 2023-05-08 19:36:51 +03:00
Kirill Bulatov
d2b2dc39d9 Do not print invisibles in non-full mode editors 2023-05-08 19:36:51 +03:00
Nate Butler
ab6b3adb2b Add a theme entry for whitespace, use it to style whitespaces
Co-Authored-By: Antonio Scandurra <me@as-cii.com>
Co-Authored-By: Kirill Bulatov <mail4score@gmail.com>
2023-05-08 19:36:51 +03:00
Kirill Bulatov
fb3ef4bcf6 Fix wrapped line detection 2023-05-08 19:36:51 +03:00
Kirill Bulatov
075bab2ea9 Use more convntional name for the settings 2023-05-08 19:36:51 +03:00
Kirill Bulatov
706f6f495a Add a test 2023-05-08 19:36:51 +03:00
Kirill Bulatov
ec725fe399 Do not print extra invisibles on line wraps 2023-05-08 19:36:51 +03:00
Kirill Bulatov
95bcd19020 Refactor line glyphs drawing methods 2023-05-08 19:36:51 +03:00
Kirill Bulatov
4aaf44df94 Support invisibles in the selection 2023-05-08 19:36:51 +03:00
Kirill Bulatov
1eeeec157e Use cached standard glyphs for invisible symbols
Co-Authored-By: Max Brunsfeld <maxbrunsfeld@gmail.com>
2023-05-08 19:36:51 +03:00
Kirill Bulatov
714734d279 Add whitespaces rendering 2023-05-08 19:36:51 +03:00
Kirill Bulatov
2d8c88ad73 Draw tabs with svg icons in editor code only 2023-05-08 19:36:51 +03:00
Kirill Bulatov
f0a88b3337 Make invisibles display configurable 2023-05-08 19:36:51 +03:00
Kirill Bulatov
ad731ea6d2 Draft invisibles' tabs display 2023-05-08 19:36:51 +03:00
Max Brunsfeld
4f8607039c Add is_tab field to chunks
Co-authored-by: Antonio Scandurra <antonio@zed.dev>
2023-05-08 19:36:51 +03:00
Julia
cf304a0edc Merge pull request #2452 from zed-industries/prevent-some-rounding-clipping
Prevent some cases of clipping icons due to pixel coord rounding
2023-05-08 11:48:33 -04:00
Julia
332b364a30 Prevent some cases of clipping icons due to pixel coord rounding 2023-05-08 11:42:00 -04:00
Antonio Scandurra
235470bbfd Merge pull request #2451 from zed-industries/kb/extra-deps
Remove unused dependencies
2023-05-08 14:46:27 +02:00
Kirill Bulatov
6cb0bc89d2 Remove unused dependencies 2023-05-07 21:07:55 +03:00
Antonio Scandurra
0296974ab1 Merge pull request #2441 from zed-industries/implicit-ancestry
Determine view ancestry during layout
2023-05-05 10:58:00 +02:00
Antonio Scandurra
5e16f70067 💄 2023-05-05 10:53:15 +02:00
Antonio Scandurra
080a1f00a3 Delay focus_in event for window activation till after layout 2023-05-05 10:47:42 +02:00
Antonio Scandurra
b9ed327b94 Replace usages of is_parent_view_focused with is_self_focused
Previously, this was used because we didn't have access to the current
view and `EventContext` was an element-only abstraction. Now that the
`EventContext` wraps the current view's `ViewContext` we can simply check
for the view's focus and avoid querying ancestors.
2023-05-05 10:08:22 +02:00
Antonio Scandurra
80ad59a620 Make focusing the parent an effect to avoid querying ancestors 2023-05-05 10:04:54 +02:00
Joseph T. Lyons
c55a4c0feb Merge pull request #2447 from zed-industries/fix-auto-update
Do not use post_json() to auto update
2023-05-04 17:15:09 -04:00
Mikayla Maki
3631b3a86c Merge pull request #2446 from zed-industries/fix-copilot-logged-out
Fix copilot stuck in logged out state
2023-05-04 16:52:04 -04:00
Mikayla Maki
89af803565 Rearrange the state machine 2023-05-04 13:45:31 -07:00
Julia
137cbaba34 Merge pull request #2445 from zed-industries/construct-text-buffer-in-background
Construct text buffer in background
2023-05-04 16:01:39 -04:00
Julia
eacea55aaf Fixup cases using buffer model handle id as buffer id 2023-05-04 12:32:31 -04:00
Julia
1883e260ce Offload text::Buffer construction to background worker
Co-Authored-By: Antonio Scandurra <me@as-cii.com>
2023-05-04 12:32:31 -04:00
Julia
7e06062bdb Store history base text as rope
Co-Authored-By: Antonio Scandurra <me@as-cii.com>
2023-05-04 12:32:31 -04:00
Joseph Lyons
8313414e1e Do not use post_json() to auto update 2023-05-04 12:12:36 -04:00
Antonio Scandurra
d6b0569bed Merge branch 'main' into implicit-ancestry 2023-05-04 17:19:06 +02:00
Antonio Scandurra
f51425d390 Merge pull request #2444 from zed-industries/fix-clicking-sidebar-buttons
Use `Workspace::toggle_sidebar_item` when clicking on sidebar button
2023-05-04 17:14:40 +02:00
Antonio Scandurra
64e0c16baa Use Workspace::toggle_sidebar_item when clicking on sidebar button
Previously, we were mistakenly using `Sidebar::toggle_item`, which only
performs part of the toggle operation.
2023-05-04 17:06:37 +02:00
Antonio Scandurra
cbae4e751b Merge pull request #2443 from zed-industries/fix-vim-mode-rename
Avoid calling `update_window` twice in `blurred` event handler
2023-05-04 16:24:51 +02:00
Antonio Scandurra
912a4cf549 Avoid calling update_window twice in blurred event handler
This was preventing us from unhooking vim when performing a rename,
which prevented typing in the rename editor.
2023-05-04 16:18:01 +02:00
Antonio Scandurra
0f93714d4f Merge pull request #2442 from zed-industries/filter-vim-commands
Filter out vim commands when vim mode is disabled
2023-05-04 15:00:19 +02:00
Antonio Scandurra
b1f5cfaa79 Merge pull request #2440 from zed-industries/fix-navigate-to-definitions-panic
Fix panic when opening multiple definitions in a multibuffer
2023-05-04 14:56:43 +02:00
Antonio Scandurra
b3baebde22 Filter out vim commands when vim mode is disabled 2023-05-04 14:52:34 +02:00
Antonio Scandurra
da19edc3e3 Merge branch 'main' into implicit-ancestry 2023-05-04 14:39:58 +02:00
Antonio Scandurra
121264d35a Fix panic when opening multiple definitions in a multibuffer
The editor is on the stack, so adding an item to the `Pane` containing
the editor will cause a double borrow and a consequent panic. This
commit fixes the issue by deferring the opening of the definitions.
2023-05-04 14:34:42 +02:00
Antonio Scandurra
7e2a461486 Merge pull request #2439 from zed-industries/fix-keystrokes-for-action
Cache view's type id and keymap context into a separate map
2023-05-04 14:22:42 +02:00
Antonio Scandurra
5cc6304fa6 Verify keystrokes can be queried while views are on the stack 2023-05-04 12:09:32 +02:00
Antonio Scandurra
3d679ddb26 Avoid re-allocating KeymapContext after every view notification 2023-05-04 12:04:30 +02:00
Antonio Scandurra
18e39ef2fa Cache view's type id and keymap context into a separate map
During `layout`, we now pass a mutable reference to the element's
parent view. This is a recursive process that causes the view to
be removed from `AppContext` and then re-inserted back into it once
the layout is complete.

As such, querying parent views during `layout` does not work as such
views will have been removed from `AppContext` and not yet re-inserted
into it. This caused a bug in `KeystrokeLabel`, which used the `keystrokes_for_action`
method to query its ancestors to determine their type id and keymap context.

Now, any time a view notifies, we will cache its keymap context so that
we don't need to query the parent view during `layout`.
2023-05-04 10:47:56 +02:00
Antonio Scandurra
7b7a495be3 Remove stray dbg! statement 2023-05-04 09:56:49 +02:00
Antonio Scandurra
f6f18be9c3 Remove WindowContext::is_child_focused 2023-05-04 09:53:35 +02:00
Antonio Scandurra
67a3891f15 Make dispatch_event related methods public to the crate only 2023-05-04 09:53:35 +02:00
Antonio Scandurra
92183e0d72 Ensure querying keystrokes or actions is safe
This is achieved by moving `available_actions` into `AsyncAppContext` (where
we know no view/window is on the stack) and `keystrokes_for_action` into `LayoutContext`
where we'll fetch the previous frame's ancestors and notify the current view if those
change after we perform a layout.
2023-05-04 09:53:31 +02:00
Joseph Lyons
053b34875b collab 0.11.0 2023-05-03 14:59:04 -04:00
Joseph Lyons
653ea3a85d v0.86.x dev 2023-05-03 14:38:41 -04:00
Max Brunsfeld
c2c29d3fb6 Merge pull request #2427 from zed-industries/copilot-disabled-globs
Add copilot.disabled_globs setting
2023-05-03 11:00:08 -07:00
Max Brunsfeld
9d41f83b1b Merge branch 'main' into copilot-disabled-globs 2023-05-03 10:53:28 -07:00
Antonio Scandurra
040cc4d4c3 Allow notifying views when the ancestry of another view is outdated 2023-05-03 19:25:00 +02:00
Max Brunsfeld
8eb1312deb Add copilot menu item for enabling paths by glob 2023-05-03 10:14:01 -07:00
Antonio Scandurra
7250754f8e Make dispatch_keystroke public to the crate only 2023-05-03 19:13:17 +02:00
Antonio Scandurra
9e8f852afb Remove ViewContext::is_child 2023-05-03 19:09:07 +02:00
Antonio Scandurra
5157442703 Fix integration test relying on deferred happening after focus
Focus is now one of the last things that happens during `flush_effects`,
and we shouldn't have relied on `defer` in the first place to verify
focus changes.
2023-05-03 19:00:32 +02:00
Antonio Scandurra
c65465b0b5 Ensure workspace gets rendered in collab integration tests 2023-05-03 18:31:07 +02:00
Antonio Scandurra
e9ed40da37 Remove the ability to retrieve the view's parent 2023-05-03 16:52:55 +02:00
Antonio Scandurra
7f137ed3dd Compute view ancestry at layout time 2023-05-03 16:36:14 +02:00
Antonio Scandurra
7f345f8bf5 Separate Window::build_scene into layout and paint 2023-05-03 12:18:16 +02:00
Antonio Scandurra
3f037e5128 Merge pull request #2437 from zed-industries/fix-joining-call
Fix "IncomingCallNotification was dropped" error when accepting a call
2023-05-03 10:49:04 +02:00
Antonio Scandurra
376aa1235f Fix "IncomingCallNotification was dropped" error when accepting a call
This was caused by accepting the call, which caused the notification to
be removed. When `active_call.accept_incoming()` finally completed, we
would try to get the app state from it in order to join the project, but
couldn't becuase the view would have already been dropped.

This commit fixes the bug by capturing a weak handle to the app state
when accepting the call as opposed to trying to read it from the view
when the accept completes.
2023-05-03 10:37:57 +02:00
Antonio Scandurra
c3cf9e3185 Merge pull request #2436 from zed-industries/close-window-end-call
Move methods querying window state into `AsyncAppContext`
2023-05-03 10:27:05 +02:00
Joseph T. Lyons
f576586cd7 Merge pull request #2435 from zed-industries/use-json-post-request-for-update-requests
Use json_post() request for update requests
2023-05-02 23:42:25 -04:00
Joseph Lyons
41d4454f45 Use post_json so that the Content-Type is set to application/json 2023-05-02 23:23:43 -04:00
Joseph Lyons
69a4fffae2 Update post_json to take in a bool for allowing for redirects 2023-05-02 23:22:55 -04:00
Joseph T. Lyons
7f5afeb9fa Merge pull request #2434 from zed-industries/add-download-and-uplodate-metadata-to-update-request
Add download and upload metadata to update request
2023-05-02 22:27:58 -04:00
Joseph Lyons
2b95aba99c Add download and upload metadata to update request 2023-05-02 17:16:42 -04:00
Kirill Bulatov
1398a12062 More keybindings in macOs modals with buttons
Closes https://github.com/zed-industries/community/issues/1095
by forcing the non-Cancel button to get a focus.
Due to the way macOs handles buttons on modals, the focus gain had to be
achieved via certain button addition order, rather than conventional
"setFocus"-ish API, see the related comment for details.

Co-authored-by: Antonio Scandurra <antonio@zed.dev>
2023-05-02 21:10:20 +03:00
Antonio Scandurra
70f8cf4cf6 Move methods querying window state into AsyncAppContext 2023-05-02 19:38:48 +02:00
Julia
4f6939732e Merge pull request #2432 from zed-industries/more-diagnostic-source
Show diagnostic source in more places
2023-05-02 09:16:11 -04:00
Julia
185c1650df Show diagnostic source in inline diagnostic 2023-05-02 09:08:07 -04:00
Antonio Scandurra
9108e4151e Merge pull request #2431 from zed-industries/fix-broken-contact-finder
Fix broken styling in contact finder
2023-05-02 13:55:14 +02:00
Antonio Scandurra
94f1775533 Fix broken styling in contact finder
This regressed as part of #2372, where we forgot to theme the contact
finder picker differently from the rest of the app.
2023-05-02 13:46:49 +02:00
Antonio Scandurra
e8f2d985ff Merge pull request #2430 from zed-industries/fix-toggle-contacts-panic
Fix panic when showing contacts popover via keybinding
2023-05-02 11:53:24 +02:00
Antonio Scandurra
f985fac6f9 Fix panic when showing contacts popover via keybinding 2023-05-02 11:47:05 +02:00
Antonio Scandurra
484cda51cf Merge pull request #2429 from zed-industries/fix-debug-elements-panic
Move `debug_elements` to `AsyncAppContext`
2023-05-02 11:15:57 +02:00
Kirill Bulatov
f5278c49b0 Clarify GH Token scope requirements 2023-05-02 12:12:57 +03:00
Antonio Scandurra
794446bf8b Move debug_elements to AsyncAppContext
Previously, `debug_elements` was available on `WindowContext`. If that
method was called while having a borrow out to a view, it would panic because
the view would already have been borrowed.

By moving it to an `AsyncAppContext` we ensure the method can't be called while
a view is being used.
2023-05-02 11:09:40 +02:00
Antonio Scandurra
4c1cba6def Remove unnecessary Element impl for RootElement 2023-05-02 10:09:57 +02:00
Julia
f7de0ad8ae Show diagnostic source in diagnostic multibuffer headers 2023-05-01 16:48:27 -04:00
Max Brunsfeld
c485fc86a2 Add copilot.disabled_globs setting 2023-05-01 13:45:47 -07:00
Joseph T. Lyons
f62ba2eec7 use installation_id over device_id 2023-05-01 16:29:51 -04:00
Julia
5fb9d53dd0 Merge pull request #2425 from zed-industries/trim-eslint-workspace-configuration
Trim eslint workspace configuration & remove diagnostic source underline
2023-05-01 13:24:14 -04:00
Julia
40ab5c1fb9 Remove underline from diagnostic source 2023-05-01 13:15:41 -04:00
Julia
4966a4a681 Reduce hardcoded ESLint workspace configuration 2023-05-01 13:14:35 -04:00
Antonio Scandurra
a8084ad3f4 Merge pull request #2424 from zed-industries/constrain-programmatic-dispatch
Allow programmatic action dispatch only via `AsyncAppContext`
2023-05-01 17:10:57 +02:00
Antonio Scandurra
780ece551e Defer hiding the dock and going back/forward when Pane is on the stack 2023-05-01 17:06:05 +02:00
Antonio Scandurra
e3b2407ebf Run until parked now that the command palette spawns to dispatch action 2023-05-01 16:58:07 +02:00
Antonio Scandurra
6c931ab9da Inline test-only AppContext methods 2023-05-01 16:49:17 +02:00
Antonio Scandurra
eb2cce98a7 Move dispatch_action_any_action_at to AsyncAppContext 2023-05-01 16:40:57 +02:00
Antonio Scandurra
c4472b0786 Remove ViewContext::dispatch_action 2023-05-01 16:27:36 +02:00
Antonio Scandurra
d815fc88ae Remove ViewContext::dispatch_any_action 2023-05-01 14:24:00 +02:00
Antonio Scandurra
029538fe21 Make dispatch_global_action private 2023-05-01 11:45:35 +02:00
Antonio Scandurra
0f44648b38 Merge pull request #2423 from zed-industries/fix-panic-in-go-to-definition
Fix panic when clicking on a definition
2023-04-29 15:02:18 +02:00
Antonio Scandurra
e566929d9e Fix panic when clicking on a definition
This was introduced with #2420 and was caused by re-entrantly updating
the workspace. Instead of passing the workspace reference from the outside,
we now define the definition navigation as a method on the editor which solves
the issue.

Note that we also needed to introduce a `defer` call when navigating to a definition
to prevent the workspace from reading the editor during `open_project_item`.
2023-04-29 14:53:17 +02:00
Joseph T. Lyons
ae5794d911 Merge pull request #2421 from zed-industries/metrics-2
Rework telemetry code to support sending events to Clickhouse
2023-04-28 17:16:33 -04:00
Julia
32f26d1e9a Merge pull request #2422 from zed-industries/source-eslint-server-from-github
Source ESLint server from Github rather than 3rd party NPM package
2023-04-28 16:53:07 -04:00
Julia
1bf85214a4 Source ESLint server from Github rather than 3rd party NPM package 2023-04-28 16:42:36 -04:00
Joseph Lyons
6b0faa2d9c Rework telemetry code to support sending events to Clickhouse
Co-Authored-By: Max Brunsfeld <maxbrunsfeld@gmail.com>
2023-04-28 16:06:55 -04:00
Antonio Scandurra
dc999f719b Merge pull request #2420 from zed-industries/simplify-action-dispatch
Remove `impl_internal_actions!` macro
2023-04-28 17:39:56 +02:00
Antonio Scandurra
106ebeb386 Remove obsolete presenter modules 2023-04-28 17:37:25 +02:00
Antonio Scandurra
489b1f6a63 Merge remote-tracking branch 'origin/main' into simplify-action-dispatch 2023-04-28 17:31:12 +02:00
Antonio Scandurra
1c5376a560 Remove impl_internal_actions macro 2023-04-28 17:29:57 +02:00
Antonio Scandurra
1d41a703ad Remove internal actions from terminal_button 2023-04-28 17:29:17 +02:00
Antonio Scandurra
33da9e5690 Remove internal actions from project_panel 2023-04-28 17:21:10 +02:00
Antonio Scandurra
e1535735b8 Remove DeployContextMenu internal action 2023-04-28 16:51:01 +02:00
Antonio Scandurra
f65e64829e Remove Scroll internal action 2023-04-28 16:43:31 +02:00
Antonio Scandurra
3409ee1785 Remove DeployMouseContextMenu internal action
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-04-28 16:41:46 +02:00
Antonio Scandurra
c22342e271 💄
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-04-28 16:38:00 +02:00
Antonio Scandurra
1f35e1dbf9 Remove internal actions from copilot_button
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-04-28 16:36:57 +02:00
Antonio Scandurra
a91903c2ab Remove internal actions from link_go_to_definition 2023-04-28 16:24:46 +02:00
Antonio Scandurra
264a2c1835 Remove HideHover and HoverAt internal actions 2023-04-28 16:12:24 +02:00
Antonio Scandurra
e6f561ce46 Remove unused Jump internal action 2023-04-28 15:57:36 +02:00
Antonio Scandurra
c04cb0286a Remove Dismiss and RespondToContactRequest internal actions 2023-04-28 15:56:41 +02:00
Antonio Scandurra
0469e25de6 Remove LeaveCall internal action 2023-04-28 14:14:25 +02:00
Antonio Scandurra
83436213ad Remove Jump internal action 2023-04-28 14:13:07 +02:00
Antonio Scandurra
3763b985e3 Fix panic due to re-entrantly reading Workspace 2023-04-28 12:36:50 +02:00
Antonio Scandurra
a978f3fe4f Remove Select internal action 2023-04-28 12:33:22 +02:00
Antonio Scandurra
5215adbd3f Remove ToggleExpanded and Call internal actions 2023-04-28 12:26:47 +02:00
Antonio Scandurra
5d8fcceee3 Remove RespondToCall internal action 2023-04-28 12:24:07 +02:00
Antonio Scandurra
4bcba487c5 Remove SplitWithProjectEntry internal action 2023-04-28 12:19:38 +02:00
Antonio Scandurra
272039a858 Remove SplitWithItem internal action 2023-04-28 12:17:31 +02:00
Antonio Scandurra
6857426b78 Remove RemoveWorktreeFromProject internal action 2023-04-28 12:11:59 +02:00
Antonio Scandurra
71a4bc7905 Remove OpenSharedScreen internal action 2023-04-28 12:09:34 +02:00
Antonio Scandurra
d953729233 Remove JoinProject internal action 2023-04-28 11:22:04 +02:00
Antonio Scandurra
f881f9e3d8 Remove ToggleFollow internal action 2023-04-28 10:07:44 +02:00
Antonio Scandurra
06c01a5937 Eliminate OpenPaths global action for workspace and replace with methods
We no longer want to invoke this with dispatch_action.
2023-04-27 18:36:28 -06:00
Max Brunsfeld
db73ba5a1a Merge pull request #2419 from zed-industries/zombie-copilot-process
Shutdown copilot server when quitting zed
2023-04-27 14:47:30 -07:00
Max Brunsfeld
1533c17cd7 Shutdown copilot server when quitting zed 2023-04-27 14:39:00 -07:00
Max Brunsfeld
7258db7a4e Merge pull request #2417 from zed-industries/hover-markdown
Render markdown more correctly in the editor hover popover
2023-04-27 14:15:04 -07:00
Max Brunsfeld
6042df393b Give code spans in markdown a background highlight 2023-04-27 13:58:30 -07:00
Max Brunsfeld
8eb9c6563a Generalize Text element to let you add arbitrary scene primitives for runs of text 2023-04-27 13:58:06 -07:00
Julia
92c9de1f50 Merge pull request #2415 from zed-industries/diagnostic-hovers-source
Show source of diagnostic on hover
2023-04-27 15:12:22 -04:00
Julia
87539e7b82 Update test to not fail due to absence of diagnostic source 2023-04-27 15:04:48 -04:00
Max Brunsfeld
66d4cb8c14 Tweak rendering of multi-paragraph list items in markdown 2023-04-27 11:39:34 -07:00
Julia
a284fae515 Don't hardcode workspaceFolder for ESLint adapter 2023-04-27 14:35:34 -04:00
Julia
678c188de0 Re-allow diagnostics hovers to soft wrap
Co-Authored-By: Max Brunsfeld <max@zed.dev>
2023-04-27 14:35:34 -04:00
Julia
3f7533a0b4 Show source of diagnostic hovers 2023-04-27 14:35:34 -04:00
Max Brunsfeld
45c7073934 Merge pull request #2418 from zed-industries/vim-inactive-window-crash
Fix vim mode crash when active editor changes in inactive window
2023-04-27 10:48:49 -07:00
Max Brunsfeld
30f20024c0 Fix vim mode crash when active editor changes in inactive window
Co-authored-by: Antonio Scandurra <antonio@zed.dev>
2023-04-27 10:43:35 -07:00
Antonio Scandurra
6cbc1dcd87 💄 2023-04-27 14:56:16 +02:00
Antonio Scandurra
20e38d2def Remove OpenProjectEntryInPane internal action 2023-04-27 14:51:02 +02:00
Antonio Scandurra
b6437d6d9e Remove Toast and DismissToast internal actions 2023-04-27 14:43:10 +02:00
Antonio Scandurra
2950344c25 Remove internal actions from Dock 2023-04-27 14:29:15 +02:00
Antonio Scandurra
15d83d40b0 Remove internal actions from menu crate 2023-04-27 14:29:11 +02:00
Antonio Scandurra
816eb06a7b Remove internal actions from CopilotCodeVerification 2023-04-27 14:25:34 +02:00
Antonio Scandurra
32f21771a6 Remove internal actions from ContextMenu 2023-04-27 14:25:14 +02:00
Antonio Scandurra
022368225e Remove internal actions from Pane 2023-04-27 14:23:26 +02:00
Antonio Scandurra
5521ff1b22 Allow passing a handler function to context menu items 2023-04-27 11:32:12 +02:00
Antonio Scandurra
d3b976d044 Remove ContextMenu::element_item 2023-04-27 11:11:21 +02:00
Antonio Scandurra
df2f471ddf Remove unused ContextMenu::ViewAction 2023-04-27 10:55:37 +02:00
Antonio Scandurra
06b12bbb68 Remove unnecessarily general keystroke_label_for function 2023-04-27 10:55:14 +02:00
Max Brunsfeld
c6abb0db3a Improve rendering of multi-paragraph list items in hover markdown 2023-04-26 17:09:20 -07:00
Max Brunsfeld
c75207c4e5 Merge pull request #2416 from zed-industries/outline-view-leaving-lines-highlighted
Remove highlighted rows when confirming outline view
2023-04-26 16:11:22 -07:00
Max Brunsfeld
c15dadbb8c Remove highlighted rows when confirming outline view 2023-04-26 15:46:29 -07:00
Max Brunsfeld
d298ce3fd3 Render more markdown features in hover popover 2023-04-26 15:33:10 -07:00
Max Brunsfeld
7960067cf9 Fix bug where Text element would wrap at the right glyph in the wrong run 2023-04-26 15:33:10 -07:00
Max Brunsfeld
54e7464163 collab 0.10.0 2023-04-26 13:24:08 -07:00
Max Brunsfeld
1bbcff543b Add API for adding mouse regions within Text 2023-04-26 12:00:26 -07:00
Joseph Lyons
99e82d829f v0.85.x dev 2023-04-26 14:19:52 -04:00
Nathan Sobo
a45282eb63 Merge pull request #2414 from zed-industries/spawn-weak-for-views
Pass a `WeakViewHandle` to `ViewContext::spawn`
2023-04-26 06:10:44 -06:00
Antonio Scandurra
6317e885c7 Don't allow strong view handles to be read/updated with an AsyncAppContext
This avoids an invitation to hold strong view handles across async await
points, which is a common source of leaks.

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-04-26 13:36:13 +02:00
Antonio Scandurra
689e878bd8 Use a WeakViewHandle in Client for view message handlers 2023-04-26 12:21:02 +02:00
Antonio Scandurra
57beec6071 Allow direct read/update of WeakViewHandle only in AsyncAppContext 2023-04-26 12:20:57 +02:00
Antonio Scandurra
2b6830c798 Remove unnecessary calls to WeakViewHandle::upgrade 2023-04-26 11:13:46 +02:00
Antonio Scandurra
94c2eaad23 Replace ViewContext::spawn with ViewContext::spawn_weak 2023-04-26 10:23:27 +02:00
Antonio Scandurra
09f7e41907 Merge pull request #2405 from zed-industries/fewer-context-traits
Simplify traits for accessing app state uniformly across different kinds of contexts
2023-04-26 10:02:56 +02:00
Antonio Scandurra
7ca412ade3 Merge remote-tracking branch 'origin/main' into fewer-context-traits 2023-04-26 09:54:58 +02:00
Max Brunsfeld
a7145021b6 Extract a named struct from text_layout::Line's style runs 2023-04-25 14:05:23 -07:00
Max Brunsfeld
3cb50ed6b7 Merge pull request #2413 from zed-industries/project-search-focus
Preserve previous focus when re-focusing project search
2023-04-25 09:33:09 -07:00
Max Brunsfeld
3db67a48b5 Preserve previous focus when re-focusing project search 2023-04-25 09:28:50 -07:00
Max Brunsfeld
c31a5063d0 Merge pull request #2411 from zed-industries/rust-snippet-variable-completions
Avoid treating snippet completions' details as their variable types
2023-04-25 08:59:35 -07:00
Max Brunsfeld
db276a422f Merge pull request #2412 from zed-industries/more-workspace-deps
Specify more dependencies at the cargo workspace level, to avoid repeating versions
2023-04-24 18:09:35 -07:00
Max Brunsfeld
ebbe52e6b0 🎨 Specify more dependencies at the workspace level 2023-04-24 17:41:55 -07:00
Max Brunsfeld
dd3f6ff4ca Avoid treating snippet completions' details as their variable types 2023-04-24 16:20:14 -07:00
Max Brunsfeld
b76194db97 Merge pull request #2409 from zed-industries/stale-excerpts
Fix stale project diagnostic excerpts for guests
2023-04-24 14:22:04 -07:00
Max Brunsfeld
7bd51851c2 🎨
Co-authored-by: Julia Risley <julia@zed.dev>
2023-04-24 13:54:47 -07:00
Max Brunsfeld
a8ddba55d8 Send language server updates via the same task that sends buffer operations
Co-authored-by: Julia Risley <julia@zed.dev>
2023-04-24 13:52:03 -07:00
Max Brunsfeld
ce34bf62fe Add failing test for diagnostic message ordering
Co-authored-by: Julia Risley <julia@zed.dev>
2023-04-24 13:18:37 -07:00
Max Brunsfeld
d2ba1ec275 Merge pull request #2407 from zed-industries/lsp-log-view
Add a simple language server log view
2023-04-24 10:15:29 -07:00
Max Brunsfeld
f3ada72785 Avoid accidentally dropping subscription when detaching it 2023-04-24 10:04:45 -07:00
Max Brunsfeld
1793c5ff6c Upgrade tree-sitter-json 2023-04-24 09:43:31 -07:00
Max Brunsfeld
e7cb996044 Improve the LSP log UI 2023-04-24 09:43:31 -07:00
Max Brunsfeld
6ed7f1281f Persist LSP logs after log view is closed 2023-04-24 09:43:31 -07:00
Max Brunsfeld
6ef6f03322 Update LspLog view to use new gpui APIs 2023-04-24 09:43:31 -07:00
Max Brunsfeld
a280a93cd8 Start work on a language server log view 2023-04-24 09:43:31 -07:00
Max Brunsfeld
2dd4920625 Add LanguageServer::on_io method, for observing JSON sent back and forth 2023-04-24 09:43:31 -07:00
Max Brunsfeld
abdccf7393 Use a workspace dependency for the futures crate 2023-04-24 09:43:31 -07:00
Mikayla Maki
d82cc49f79 Merge pull request #2408 from zed-industries/change-copilot-wording
Change copilot wording to be more explicit
2023-04-25 04:28:23 +12:00
Mikayla Maki
c12e2ac3fb fix formatting 2023-04-24 09:25:04 -07:00
Mikayla Maki
c7874cf169 Update copilot_button.rs 2023-04-24 09:03:54 -07:00
Antonio Scandurra
c165fb9be5 Remove ReadView and UpdateView traits
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-04-24 17:36:14 +02:00
Antonio Scandurra
a6115d9330 Remove UpdateModel trait
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-04-24 16:52:09 +02:00
Antonio Scandurra
a9417f3d2e Remove ReadModelWith trait
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-04-24 16:44:54 +02:00
Antonio Scandurra
5f500d34b2 Remove UpgradeModelHandle trait
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-04-24 16:40:30 +02:00
Antonio Scandurra
b8fab6fde9 Remove UpgradeViewHandle trait
Co-authored-by: Nathan Sobo <nathan@zed.dev>
2023-04-24 16:33:27 +02:00
Mikayla Maki
455d383d08 Change copilot wording to be more explicit 2023-04-21 21:42:23 -07:00
Mikayla Maki
f10de10915 Merge pull request #2406 from zed-industries/fix-nondeterministic-terminal-test
Fix minor terminal bugs
2023-04-22 12:42:20 +12:00
Mikayla Maki
fa7f4974a0 Remove unused import 2023-04-21 17:26:45 -07:00
Mikayla Maki
733abc9ed2 Revert previous change 2023-04-21 17:24:20 -07:00
Mikayla Maki
616188c541 Fix a bug where the character under a cursor could not reliably be selected 2023-04-21 17:15:29 -07:00
Mikayla Maki
8e0d359c63 Merge branch 'main' into fix-nondeterministic-terminal-test 2023-04-22 11:17:06 +12:00
Mikayla Maki
d841c3729b Wire through the gutter, rather than implicitly adding it 2023-04-21 16:12:33 -07:00
Mikayla Maki
23932b7e6c Fixed non-deterministic test failure and made mouse to cell conversion work correctly 2023-04-21 16:06:07 -07:00
Max Brunsfeld
06cb388beb Merge pull request #2404 from zed-industries/fix-command-palette
Stopgap: Fix missing actions in the command palette
2023-04-21 15:16:45 -07:00
Nathan Sobo
e6604d1641 Fix missing actions in the command palette
Previously, the workspace view was on the stack when we were computing the
available actions, which excluded it. This is a stopgap. We should find a
better solution ASAP.
2023-04-21 15:59:29 -06:00
Nathan Sobo
83bf3d071d WIP 2023-04-21 15:36:52 -06:00
Nathan Sobo
55db28e074 Eliminate ReadModel trait 2023-04-21 15:05:19 -06:00
Nathan Sobo
5dac95c47c Eliminate ReadView trait 2023-04-21 15:03:57 -06:00
Nathan Sobo
bce51c521a Merge pull request #2403 from zed-industries/into-element
Rename Element to AnyElement, Drawable to Element, and make containers generic over any Element type
2023-04-21 14:44:22 -06:00
Nathan Sobo
993dbf86cb Merge remote-tracking branch 'origin/main' into into-element 2023-04-21 14:27:25 -06:00
Nathan Sobo
09111b65d8 Merge pull request #2401 from zed-industries/simplify-context-lifetimes
Remove unnecessary lifetime parameter from WindowContext
2023-04-21 14:26:26 -06:00
Nathan Sobo
caf3d5c163 Fix more formatting 2023-04-21 14:25:49 -06:00
Nathan Sobo
c1810e8ec9 Fix formatting 2023-04-21 13:09:36 -06:00
Nathan Sobo
fe492eacbf Refine naming of element-related types and traits
Co-Authored-By: Max Brunsfeld <max@zed.dev>
2023-04-21 13:06:37 -06:00
Antonio Scandurra
03619dfa55 Rename Drawable::boxed to into_element and make containers generic
Multi-element are now generic over any drawable child, which can be converted
into an element.

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
Co-Authored-By: Max Brunsfeld <max@zed.dev>
2023-04-21 12:43:19 -06:00
Mikayla Maki
69273648b3 Merge pull request #2402 from zed-industries/fix-panics
Fix panic in pane.rs
2023-04-22 05:53:48 +12:00
Mikayla Maki
b8fd6435d7 Fix test covering this case 2023-04-21 10:48:34 -07:00
Mikayla Maki
aa2af53f56 Align return of close_active_item to match original format 2023-04-21 10:44:27 -07:00
Mikayla Maki
39512655aa Remove unused cyclic dependency from copilot crate
Move notification handling earlier so that there are less copilot messages in the log
2023-04-21 10:38:36 -07:00
Mikayla Maki
6ee0d104d6 Fix panic in remove active item 2023-04-21 10:08:52 -07:00
Petros Amoiridis
c9048b54c1 Merge pull request #2399 from zed-industries/petros/z-827-copy-the-newline-shortcuts-to-zed
Add newline shortcuts for popular editors
2023-04-21 19:09:10 +03:00
Antonio Scandurra
4ac894ffbe Remove unnecessary lifetime parameter from WindowContext
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-04-21 18:06:53 +02:00
Antonio Scandurra
a4fbcbf160 Merge pull request #2400 from zed-industries/fix-breadcrumbs-signature
Fix `ProjectDiagnosticsEditor::breadcrumbs` signature
2023-04-21 18:06:22 +02:00
Antonio Scandurra
4d433663bd Fix ProjectDiagnosticsEditor::breadcrumbs signature
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-04-21 17:54:26 +02:00
Antonio Scandurra
238ebafa48 Merge pull request #2372 from zed-industries/window_context_2
Give elements access to their parent views and simplify contexts
2023-04-21 17:32:14 +02:00
Julia
88406045f5 Merge pull request #2398 from zed-industries/fix-diagnostics-breadcrumbs
Render breadcrumbs for diagnostics multi-buffer
2023-04-21 11:31:35 -04:00
Petros Amoiridis
3992e95109 Add newline shortcuts for popular editors 2023-04-21 18:29:35 +03:00
Antonio Scandurra
f54a289b6f Remove handle to view from TerminalElement
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-04-21 16:56:06 +02:00
Antonio Scandurra
a860a6cd62 Make App::notify_view private
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-04-21 16:50:57 +02:00
Antonio Scandurra
8c7f821d14 Introduce a new WindowContext::remove_window API
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-04-21 16:39:53 +02:00
Petros Amoiridis
c3231047ad Merge pull request #2396 from zed-industries/petros/z-77-the-start-local-collaboration-script-no
Fix script/start-local-collaboration
2023-04-21 17:36:22 +03:00
Antonio Scandurra
f12746c4b7 Avoid double borrow of view in ProjectPanel drag and drop
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-04-21 16:23:47 +02:00
Antonio Scandurra
31e906d068 Avoid double borrow of views on up and up_out in DragAndDrop
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-04-21 16:19:51 +02:00
Petros Amoiridis
65c5605e68 Look for the main display always 2023-04-21 17:18:01 +03:00
Petros Amoiridis
3c54b14c5b Remove debugging information 2023-04-21 17:17:04 +03:00
Antonio Scandurra
5b40641fde Take a target view when marking an element as draggable 2023-04-21 15:52:05 +02:00
Antonio Scandurra
b0cbd13e7a Remove stray push_layer from EditorElement::paint 2023-04-21 11:46:13 +02:00
Antonio Scandurra
e5192a4853 Use update_default_global to filter command palette items for copilot 2023-04-21 11:07:36 +02:00
Antonio Scandurra
c76b9794e4 Merge branch 'main' into window_context_2 2023-04-21 10:58:08 +02:00
Antonio Scandurra
d32a7218cd Remove stray dbg! statements 2023-04-21 10:49:47 +02:00
Julia
c7cc5bca02 Render breadcrumbs for diagnostics multi-buffer 2023-04-20 23:39:45 -04:00
Mikayla Maki
8e4cc549dc Merge pull request #2397 from zed-industries/finally-fix-terminal-line-height
Create a new setting to adjust the line height in the terminal
2023-04-21 12:39:49 +12:00
Mikayla Maki
1fa52adabd Fix warning 2023-04-20 17:34:47 -07:00
Mikayla Maki
c72b70d4ae Fixed silly custom settings parsing 2023-04-20 17:33:06 -07:00
Mikayla Maki
f54ab73b47 Merge branch 'main' into finally-fix-terminal-line-height 2023-04-21 12:26:32 +12:00
Mikayla Maki
dfdc826015 Create a new setting to adjust the line height in the terminal 2023-04-20 17:24:27 -07:00
Nathan Sobo
1b2e480e1e Remove unnecessary replace 2023-04-20 15:26:48 -06:00
Nathan Sobo
0bce80b6f8 Fix remaining vim failures 2023-04-20 15:25:11 -06:00
Antonio Scandurra
137d9384b5 Initialize the active editor when vim mode is enabled
Instead of waiting for a focus event. This makes more tests pass.
2023-04-20 14:02:40 -06:00
Petros Amoiridis
7b4b1d6312 Use regex to make extraction bulletproof 2023-04-20 20:06:33 +03:00
Max Brunsfeld
abdfb5a451 collab 0.9.0 2023-04-20 09:52:32 -07:00
Max Brunsfeld
3a855184bc v0.84.x dev 2023-04-20 09:52:07 -07:00
Joseph T. Lyons
65f7228fed Merge pull request #2389 from zed-industries/save-panics-as-structured-data
Save panics as structured data
2023-04-20 12:51:00 -04:00
Max Brunsfeld
b414d43ee3 Merge pull request #2392 from zed-industries/eslint-multiple-language-server-adapters-rebased
Allow buffers to use multiple language servers
2023-04-20 09:40:23 -07:00
Petros Amoiridis
dcc804783c Install jq if it's missing 2023-04-20 19:06:23 +03:00
Petros Amoiridis
460ea8e16c Increase reliability and support multiple monitors 2023-04-20 19:06:23 +03:00
Petros Amoiridis
b11e239779 Make the script more reliable 2023-04-20 19:06:22 +03:00
Mikayla Maki
ad71020990 Merge pull request #2395 from zed-industries/remove-stable-hiding-copilot
Remove stable guard for copilot
2023-04-21 04:04:04 +12:00
Max Brunsfeld
21bb13d309 Fix comment formatting errors for rust 1.69 2023-04-20 09:03:42 -07:00
Max Brunsfeld
32c57bcd22 Store buffer's diagnostic sets in a smallvec 2023-04-20 08:58:41 -07:00
Max Brunsfeld
960a2bc589 Don't use ESLint for now 2023-04-20 08:58:41 -07:00
Max Brunsfeld
0ebe44bfd5 Handle multiple language servers for a given path in project diagnostics view 2023-04-20 08:58:41 -07:00
Max Brunsfeld
4dd917c123 Introduce a LanguageServerId wrapper type
Clarify the meaning of all the usizes in use in all of these
struct fields an method signatures.
2023-04-20 08:58:41 -07:00
Julia
c5f86bc6af Avoid language servers fighting over diagnostics summaries
Previously each server would stomp all over the existing results

Co-Authored-By: Max Brunsfeld <max@zed.dev>
2023-04-20 08:58:41 -07:00
Julia
9e2949e7ba Refactor language server startup
Avoid parallel vecs

Co-Authored-By: Max Brunsfeld <max@zed.dev>
2023-04-20 08:58:41 -07:00
Julia
c59204c5e6 Cleanup
Co-Authored-By: Max Brunsfeld <max@zed.dev>
2023-04-20 08:58:41 -07:00
Julia
26abc824a9 Bump protocol version 2023-04-20 08:58:41 -07:00
Julia
df94aee758 Fix failing tests
Co-Authored-By: Max Brunsfeld <max@zed.dev>
2023-04-20 08:58:41 -07:00
Julia
6156dbced0 Finish getting multiple diagnostics sources building and running 2023-04-20 08:58:41 -07:00
Julia
bb4de47b15 Start getting diagnostics sets to work with multiple servers
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-04-20 08:58:41 -07:00
Julia
2a5c0fa5f8 Get ESLint to launch and provide diagnostics
Co-Authored-By: Antonio Scandurra <me@as-cii.com>
2023-04-20 08:58:41 -07:00
Julia
6e68ff5a50 Get it to build with multiple adapters per language! 🎉
Co-Authored-By: Max Brunsfeld <max@zed.dev>
2023-04-20 08:58:41 -07:00
Julia
ba7233f265 Incomplete refactor to allow for multiple adapters per language 2023-04-20 08:58:39 -07:00
Mikayla Maki
c1daf0fc36 Fix format 2023-04-20 08:54:44 -07:00
Mikayla Maki
ad8162fc9c Make sign_in init conditional 2023-04-20 08:36:29 -07:00
Mikayla Maki
f5bbb41cc2 Remove import 2023-04-20 08:34:50 -07:00
Mikayla Maki
5c8b41dd54 Remove stable guard for copilot 2023-04-20 08:33:45 -07:00
Antonio Scandurra
0d5eea8169 Track active window id in test platform
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-04-20 17:22:11 +02:00
Antonio Scandurra
d9bb37c649 Add WindowContext::update_default_global
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-04-20 17:13:13 +02:00
Antonio Scandurra
1d487e19f9 Fix editor tests
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-04-20 16:15:14 +02:00
Antonio Scandurra
c52b6328b7 Merge branch 'main' into window_context_2 2023-04-20 16:01:47 +02:00
Antonio Scandurra
e282c7ad45 Finish converting all the pickers to the new API 2023-04-20 15:26:05 +02:00
Petros Amoiridis
21e39e7523 Merge pull request #2394 from zed-industries/petros/z-804-change-default-settings-for-json-to-2
Set default tab_size for JSON to 2 and apply new formatting
2023-04-20 14:45:52 +03:00
Antonio Scandurra
370875b1d4 Merge pull request #2393 from zed-industries/relay-change-events-to-copilot
Relay buffer change events to Copilot language server
2023-04-20 13:34:42 +02:00
Petros Amoiridis
eca93c124a Apply the tab_size change to keymaps and settings 2023-04-20 14:08:30 +03:00
Petros Amoiridis
bed76462e2 Define tab_size equal to 2 in default settings 2023-04-20 14:06:43 +03:00
Antonio Scandurra
df71a9cfae Move buffer change reporting to a background task 2023-04-20 11:59:05 +02:00
Antonio Scandurra
4151bd39da Add buffer management test to Copilot 2023-04-20 10:51:50 +02:00
Antonio Scandurra
4d207981ae Notify LSP when Copilot suggestions are accepted/rejected 2023-04-20 10:15:31 +02:00
Antonio Scandurra
5d57167302 Make it easier to access a running/authenticated copilot server 2023-04-20 10:12:10 +02:00
Antonio Scandurra
4c3d6c854a Send editor information to copilot 2023-04-20 09:34:20 +02:00
Antonio Scandurra
b9a7b70e52 Register unknown buffer on the fly if completions are requested for it 2023-04-20 09:34:20 +02:00
Antonio Scandurra
34bcf6f072 Reopen file in Copilot language server when language or URI changes 2023-04-20 09:34:20 +02:00
Antonio Scandurra
672cf6b8c7 Relay buffer change events to Copilot 2023-04-20 09:34:20 +02:00
Nathan Sobo
d70644618a WIP: Make PickerDelegate a fully owned object instead of a view
This avoids issues with the parent view being on the stack when we want to
interact with the delegate from the picker. Still have several picker usages
to convert.
2023-04-19 22:05:29 -06:00
Mikayla Maki
ce8442a3d8 Fix underflow potential 2023-04-19 17:42:19 -07:00
Mikayla Maki
dd73233973 Merge pull request #2386 from zed-industries/copilot-shipping
Get copilot ready to ship
2023-04-20 12:38:27 +12:00
Mikayla Maki
26ab774b7f Removed debounce on suggestion cycling code 2023-04-19 17:34:09 -07:00
Mikayla Maki
f16b96cafc add copilot menu 2023-04-19 17:27:44 -07:00
Mikayla Maki
9b8a3e4de5 Fixed panic in new cycling code
Made cycling fire without debounce
2023-04-19 16:50:31 -07:00
Mikayla Maki
2882e0fa5b Remove new CTA in copilot sign in UI
Add a trim_end to copilot suggestions
2023-04-19 16:39:55 -07:00
Mikayla Maki
745e5e3a09 Add italic styles to copilot suggestions 2023-04-19 15:23:19 -07:00
Mikayla Maki
70ff4ca48f WIP: lower our usage of the copilot API by seperating out the cycling completion
Restore copilot setting visibility

co-authored-by: antonio <antonio@zed.dev>
2023-04-19 15:23:17 -07:00
Mikayla Maki
ea1c3fa7a0 Only fire completion cycling requests if specifically asked for by the user 2023-04-19 15:21:30 -07:00
Antonio Scandurra
8610f3acf3 Introduce a button to disable copilot integration
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-04-19 20:25:59 +02:00
Joseph Lyons
0326a45a91 Give closure parameter a name 2023-04-19 14:21:53 -04:00
Antonio Scandurra
54a78d7024 Clarify Copilot context menu
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-04-19 20:18:06 +02:00
Antonio Scandurra
4a9989fe38 Clear all suggestions from Editor when disabling Copilot
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-04-19 20:10:57 +02:00
Antonio Scandurra
1fd07b6fcf Clarify copilot settings
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-04-19 20:07:05 +02:00
Petros Amoiridis
699b2060b3 Merge pull request #2390 from zed-industries/petros/z-66-command-to-add-a-new-line-on-the
Add newline above and improve newline below
2023-04-19 21:02:55 +03:00
Petros Amoiridis
b3b8f8532d Assert the editor and unmarked texts are the same
Co-Authored-By: Max Brunsfeld <maxbrunsfeld@gmail.com>
2023-04-19 20:52:01 +03:00
Petros Amoiridis
f9c60b98c0 Add newline above and improve newline below
Add a new action for inserting a new line above the current line. @ForLoveOfCats  also helped fix a bug among other things. When two collaborators had their cursors at the end of a line, and one collaborator performed a newline below action, the second collaborator's cursor would be dragged to the new line. This is also fixing that.

Co-Authored-By: Julia <30666851+ForLoveOfCats@users.noreply.github.com>
2023-04-19 19:57:23 +03:00
Joseph Lyons
27a6bacab8 Save panics as structured data 2023-04-19 08:31:47 -04:00
Nathan Sobo
5514349b6b Add Component elements 2023-04-18 22:49:06 -06:00
Max Brunsfeld
c5e56a5e45 Fail the randomized test build after reporting the error to zed.dev 2023-04-18 18:41:33 -07:00
Max Brunsfeld
5934e882b8 Merge pull request #2379 from zed-industries/shebang
Select language based on a file's first content line in addition to its path
2023-04-18 17:31:19 -07:00
Max Brunsfeld
ad9fe79cf2 Merge pull request #2388 from zed-industries/cmd-o-new-window
When opening projects, only reuse the current window if it is empty
2023-04-18 17:31:08 -07:00
Max Brunsfeld
7cc868bc8c When opening projects, only reuse the current window if it is empty 2023-04-18 17:05:26 -07:00
Max Brunsfeld
44d26b69ae Merge pull request #2387 from zed-industries/panic-in-set-selections-from-remote
Fix 'invalid insertion' panic when following
2023-04-18 16:43:47 -07:00
Max Brunsfeld
bd7d50f339 Fix 'invalid insertion' panic when following
Wait for the necessary buffer operations to arrive before attempting to
set selections and scroll top.
2023-04-18 16:13:18 -07:00
Nate Butler
a8b3826955 Merge pull request #2384 from zed-industries/update-copilot-styles
Update copilot styles
2023-04-18 15:17:10 -04:00
Julia
4c086a4836 Merge pull request #2385 from zed-industries/rerender-breadcrumbs-on-focus-change
Re-render toolbar items when updating their knowledge of pane focus
2023-04-18 15:13:53 -04:00
Julia
721baf5746 Re-render toolbar items when updating their knowledge of pane focus 2023-04-18 14:56:39 -04:00
Nate Butler
957ab65422 Mix neutral and blue to make a predictive color that is unique 2023-04-18 14:47:52 -04:00
Joseph T. Lyons
614a9c8977 Merge pull request #2377 from zed-industries/add-tab-tooltips
Add tab tooltips
2023-04-18 14:37:38 -04:00
Nate Butler
ae0647c3a9 Update predictive color
Co-Authored-By: Mikayla Maki <mikayla.c.maki@gmail.com>
2023-04-18 14:29:08 -04:00
Joseph Lyons
304eddbbe4 Remove unnecessary lifetimes from tab_description 2023-04-18 14:15:56 -04:00
Joseph Lyons
9afd804062 Remove unnecessary lifetimes from tab_tooltip_text 2023-04-18 14:03:02 -04:00
Antonio Scandurra
eee39b4c5c Don't capture Workspace handle in defer when rendering Dock
GPUI already provides a mutable reference to the `Workspace` in the
`defer` callback.
2023-04-18 17:41:40 +02:00
Antonio Scandurra
136e599051 Merge branch 'main' into window_context_2 2023-04-18 17:29:37 +02:00
Antonio Scandurra
bcba11ba82 Reflect new behavior in test now that we don't replace windows anymore 2023-04-18 17:27:05 +02:00
Antonio Scandurra
d03c431f9a Fix warnings/errors now that AsyncAppContext::update returns Result 2023-04-18 14:58:57 +02:00
Antonio Scandurra
31e6bb4fc1 Return Result from AsyncAppContext::update_view 2023-04-18 12:03:53 +02:00
Antonio Scandurra
1b477c9e38 Merge pull request #2383 from zed-industries/show-copilot-more-often
Clean up completion tasks, even if they fail or return no results
2023-04-18 11:16:47 +02:00
Antonio Scandurra
d26d0ac56f Clean up completion tasks, even if they fail or return no results
This fixes a bug where leaving the completion task in `completion_tasks`
could cause the Copilot suggestion to not be shown due to the LSP not
successfully return a completion.
2023-04-18 11:03:17 +02:00
Antonio Scandurra
75d6b6360f Add failing test to demonstrate Copilot not showing enough suggestions 2023-04-18 10:24:20 +02:00
Max Brunsfeld
8f25b98e6f Print the final minimized test plan before sending it to zed.dev 2023-04-17 16:35:54 -07:00
Max Brunsfeld
695973d117 Use Node 18 on CI, for fetch API 2023-04-17 16:22:30 -07:00
Max Brunsfeld
516964280b Merge pull request #2376 from zed-industries/randomized-tests-runner
Add an Actions workflow that repeatedly runs the randomized integration tests
2023-04-17 15:45:45 -07:00
Max Brunsfeld
485c56e3bd Don't run randomized tests on pushes to main 2023-04-17 15:43:12 -07:00
Max Brunsfeld
837866f962 Consolidate logic for running randomized tests in scripts 2023-04-17 15:37:11 -07:00
Julia
4adc92b8e5 Merge pull request #2382 from zed-industries/dont-have-contacts-popover-affect-share-unshare-button
Don't have contacts popover affect appearance of Share/Unshare button
2023-04-17 14:18:39 -04:00
Julia
14ef0edd7f Don't have contacts popover affect appearance of Share/Unshare button 2023-04-17 14:13:28 -04:00
Max Brunsfeld
233cd80f63 Merge pull request #2381 from zed-industries/fix-buffer-latency
Send buffer operations in batches to reduce latency
2023-04-17 10:59:51 -07:00
Max Brunsfeld
5d73e646d8 Delete pull_request_template.md 2023-04-17 10:23:57 -07:00
Antonio Scandurra
1f284408a9 Send buffer operations in batches to reduce latency
Co-Authored-By: Max Brunsfeld <max@zed.dev>
2023-04-17 10:22:13 -07:00
Julia
f5a2534c1b Merge pull request #2380 from zed-industries/show-followers-full-color
Unconditionally display followers in full color
2023-04-17 13:16:19 -04:00
Julia
61f4f8aaeb Unconditionally display followers in full color 2023-04-17 13:10:40 -04:00
Antonio Scandurra
493a418c91 WIP 2023-04-17 18:52:48 +02:00
Antonio Scandurra
38ab6b123f Make production code compile again 2023-04-17 12:33:33 +02:00
Antonio Scandurra
bed94455b9 Avoid calling focus_in when creating window
This is redundant because when creating a window we will receive
an activation event if the window was effectively focused. And if
it wasn't, we shouldn't call `focus_in`.
2023-04-17 11:57:51 +02:00
Max Brunsfeld
1dcd4717b1 Select language based on a file's first content line in addition to its path 2023-04-16 12:28:27 -07:00
Joseph Lyons
ebe57254e0 Add tab tooltips 2023-04-15 06:46:14 -04:00
Max Brunsfeld
3569c61784 Minimize randomized test failures before reporting issues 2023-04-14 17:51:24 -07:00
Max Brunsfeld
5c3da91e15 Report randomized test failures to zed.dev, to create issues in linear 2023-04-14 15:45:01 -07:00
Max Brunsfeld
c329546570 Extract randomized test CI process into a script 2023-04-14 14:25:55 -07:00
Max Brunsfeld
253411bfd0 Start work randomized test runner GH action workflow 2023-04-14 13:54:57 -07:00
Max Brunsfeld
e655a6c767 Merge pull request #2375 from zed-industries/worktree-scan-id-fix
Always bump worktree's scan_id when refreshing an entry
2023-04-14 09:53:40 -07:00
Antonio Scandurra
f09e21aa93 WIP: force using (at least) a window context to update views 2023-04-14 17:08:13 +02:00
Antonio Scandurra
a820862165 Take WindowContext in workspace::ItemHandle methods 2023-04-14 16:36:23 +02:00
Antonio Scandurra
a8e75a9b55 Remove weak handle from EditorElement to Editor 2023-04-14 14:29:03 +02:00
Antonio Scandurra
060242a28a Take a WindowContext in StatusItemViewHandle::set_active_pane_item 2023-04-14 14:26:20 +02:00
Antonio Scandurra
2652f65bee Fix handle_window_activation_effect updating the wrong view 2023-04-14 12:33:13 +02:00
Antonio Scandurra
98dce89379 Pass WindowContext to ViewHandle::is_focused 2023-04-14 12:12:08 +02:00
Antonio Scandurra
74ca223114 Fix formatting for the entire workspace 2023-04-14 12:08:33 +02:00
Antonio Scandurra
33bc47dbe2 Merge branch 'main' into window_context_2 2023-04-14 12:07:09 +02:00
Antonio Scandurra
183b9ef809 Make full-screen and window bounds callbacks take a WindowContext 2023-04-14 11:56:31 +02:00
Antonio Scandurra
7394bf1cdc Fix most of the warnings 2023-04-14 10:51:53 +02:00
Antonio Scandurra
5666e8301e Log an error when a scene fails to build 2023-04-14 10:32:56 +02:00
Antonio Scandurra
9ef79735dc Move more window-specific methods from AppContext to WindowContext 2023-04-14 10:29:35 +02:00
Antonio Scandurra
c62357db02 Fix window activation 2023-04-14 08:24:01 +02:00
Max Brunsfeld
5ea49b3ae3 Fix inconsistent worktree state when renaming entries while scanning 2023-04-13 22:34:34 -07:00
Max Brunsfeld
bb1cfd51b8 Add randomized test for mutating worktree during initial scan 2023-04-13 22:34:03 -07:00
Max Brunsfeld
debb694d97 Always bump scan_id when refreshing an entry
The scan_id needs to be bumped even if a scan is already in progress,
so that worktree updates can detect that entries have changed. This
means that the worktree's completed_scan_id may increase by more than
one at the end of a scan.
2023-04-13 16:51:11 -07:00
Max Brunsfeld
c13914bda1 Merge pull request #2371 from zed-industries/refresh-entry-delay
Restructure background scanner to handle refresh requests even while scanning directories
2023-04-13 12:44:04 -07:00
Joseph T. Lyons
6a75e884c0 Merge pull request #2374 from zed-industries/add-vim-mode-metric
Add vim mode metric
2023-04-13 13:52:31 -04:00
Joseph Lyons
5f0bf5929f Add vim mode metric 2023-04-13 13:46:22 -04:00
Antonio Scandurra
84d2605ccf Avoid passing cx when emitting from CallbackCollection
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-04-13 16:53:44 +02:00
Antonio Scandurra
ff774786bf Reimplement AppContext::handle_focus_effect 2023-04-13 16:21:39 +02:00
Antonio Scandurra
4900e04ff3 Fix window refreshing 2023-04-13 12:19:11 +02:00
Antonio Scandurra
0269a8699b Start removing some warnings 2023-04-13 12:04:37 +02:00
Antonio Scandurra
702c4ce403 Uncomment List tests and fix related problems in WindowContext 2023-04-13 12:01:18 +02:00
Antonio Scandurra
f4daeb4778 Merge pull request #2373 from zed-industries/fix-copilot-panic
Avoid interpolating Copilot suggestion if cursor excerpt differs
2023-04-13 10:48:57 +02:00
Antonio Scandurra
495c7acadf Avoid interpolating Copilot suggestion if cursor excerpt differs 2023-04-13 10:44:08 +02:00
Max Brunsfeld
5ca603dbeb Don't process gitignore updates after the initial scan 2023-04-12 18:17:29 -07:00
Max Brunsfeld
3d14bfd90c Prioritize path refresh requests over gitignore status updates 2023-04-12 18:08:00 -07:00
Max Brunsfeld
2d97387f49 Restructure background scanner to handle refresh requests even while scanning 2023-04-12 16:29:51 -07:00
Nathan Sobo
b89c4e06be WIP: Compiling, many warnings, haven't tested 2023-04-12 12:54:24 -06:00
Nathan Sobo
25ad635577 WIP 2023-04-12 12:38:26 -06:00
Nathan Sobo
4cb13fb39c WIP 2023-04-12 12:22:26 -06:00
Nathan Sobo
a25f962185 WIP 2023-04-12 12:13:35 -06:00
Max Brunsfeld
a85c2d71ad collab 0.8.3 2023-04-12 11:11:32 -07:00
Joseph Lyons
afbd275f4f v0.83.x dev 2023-04-12 13:31:39 -04:00
Joseph T. Lyons
ee238a300e Merge pull request #2368 from zed-industries/Improve-tab-closing-experience
Add tab context menu
2023-04-12 13:08:46 -04:00
Joseph T. Lyons
b39b3541e9 Merge pull request #2370 from zed-industries/flip-screen-sharing-icon-states
Flip screen sharing icon states
2023-04-12 12:50:00 -04:00
Nathan Sobo
40896352ff wip 2023-04-12 10:28:53 -06:00
Nathan Sobo
868301bedb WIP 2023-04-12 10:07:17 -06:00
Antonio Scandurra
83070a19c4 WIP 2023-04-12 15:55:43 +02:00
Nathan Sobo
b54f08db77 WIP 2023-04-12 06:51:03 -06:00
Antonio Scandurra
6d8635fa29 Merge pull request #2349 from zed-industries/randomized-tests-operation-script
Restructure randomized integration test to allow editing and replaying a plan, fix discovered bugs
2023-04-12 11:45:47 +02:00
Antonio Scandurra
12a286ac50 Forget buffered operations when resyncing with the host
Previously, we could end up with a situation where the host did not
see an operation but a guest that didn't have that buffer open would. When
such guest would finally open the buffer, they would apply the operation
without however sending it to the host. The guest wouldn't bother resyncing
it because it wasn't part of its open buffers.
2023-04-12 10:39:01 +02:00
Nathan Sobo
d9e4136b02 WIP 2023-04-11 21:56:37 -06:00
Nathan Sobo
e6cc132b19 WIP 2023-04-11 18:48:00 -06:00
Nathan Sobo
e115baa60c WIP 2023-04-11 18:21:56 -06:00
Joseph Lyons
0b52308c99 Represent dirty state in item-testing code 2023-04-11 16:27:12 -04:00
Joseph Lyons
c39764487c Construct context menu in a more clear way 2023-04-11 15:46:52 -04:00
Max Brunsfeld
61d048cb25 Don't wait for host's reply before broadcasting buffer updates to guests 2023-04-11 12:37:08 -07:00
Joseph Lyons
ae930bde87 Flip screen sharing icon states 2023-04-11 15:30:44 -04:00
Max Brunsfeld
727afae4ff Fix unit tests after fixing gpui model drop semantics
co-authored-by: Antonio Scandurra <antonio@zed.dev>
2023-04-11 10:58:01 -07:00
Nate Butler
209e9a59c5 Merge pull request #2369 from zed-industries/nate/update-ayu-theme
Update `Ayu` Theme Family
2023-04-11 13:05:25 -04:00
Nate Butler
7ba094e10e add license_checksum 2023-04-11 12:56:18 -04:00
Nate Butler
20ec9f6daf Add meta fields to ayu 2023-04-11 12:37:20 -04:00
Nate Butler
65c2fb1cc6 Fix paths after publishing 2023-04-11 12:25:02 -04:00
Nate Butler
de60657d53 Ignore the target folder 2023-04-11 12:23:54 -04:00
Nate Butler
975f5d5fa8 Format 2023-04-11 12:23:21 -04:00
Nate Butler
527f0310e2 Update + publish the Ayu theme 2023-04-11 12:22:49 -04:00
Antonio Scandurra
42b10044fc Fix running client crate tests 2023-04-11 17:43:05 +02:00
Antonio Scandurra
5eb1719ab8 Re-send worktree if reconnecting while initial scan isn't finished yet
Previously, if the client was disconnected while the initial worktree
state was being sent, it would not see the remaining state after reconnecting.
This was due to `scan_id` and `completed_scan_id` both being initialized to
`0`, so the client would ask for updates since `0` and get nothing.

This commit changes the worktree to initialize `scan_id` to `1` and
`completed_scan_id` to `0`, so that we get the full worktree again on reconnect.
2023-04-11 17:15:43 +02:00
Antonio Scandurra
172441ab72 Cancel pending calls when participant fails to reconnect
Previously, we would only cancel pending calls when the room became
empty.
2023-04-11 16:33:08 +02:00
Antonio Scandurra
5e37c893c2 Ensure project is still alive by the time remote LSP request starts 2023-04-11 15:14:32 +02:00
Antonio Scandurra
5898600239 Use LspCommand to handle code actions 2023-04-11 15:12:58 +02:00
Antonio Scandurra
651a83977e 🔥 2023-04-11 14:53:08 +02:00
Antonio Scandurra
ac532cb6fa Use LspCommand to handle completions 2023-04-11 14:52:07 +02:00
Antonio Scandurra
9e6d865882 Prevent already dropped model from being upgraded during release 2023-04-11 10:43:05 +02:00
Antonio Scandurra
6ba5e06247 Stop waiting for buffers when releasing a remote project 2023-04-11 10:42:43 +02:00
Antonio Scandurra
643381ce0c Make UpdateDiffBase a Foreground message to prevent reordering 2023-04-11 08:50:26 +02:00
Joseph Lyons
67cb046298 Add tab context menu 2023-04-11 00:03:47 -04:00
Max Brunsfeld
abfbba68f0 Improve randomized test assertion message when diff base is wrong 2023-04-10 18:28:34 -07:00
Max Brunsfeld
25e3c4e586 Fix leak when project is unshared while LSP handler waits for edits 2023-04-10 17:03:58 -07:00
Nathan Sobo
3de8fe0f87 WIP 2023-04-10 17:27:47 -06:00
Max Brunsfeld
e853e77d59 Upgrade postage for oneshot channel drop fix
Previously, dropping a oneshot sender didn't wake the receiver.
2023-04-10 16:03:49 -07:00
Nathan Sobo
6638407ff9 WIP: Everything shredded 2023-04-10 16:10:32 -06:00
Julia
0e4de87e0e Merge pull request #2367 from zed-industries/log-github-api-response-for-deserialize-error
In the case of Github release deserialize error, log response text
2023-04-10 17:04:59 -04:00
Julia
189784f5fd In the case of Github release deserialize error, log response text
Co-Authored-By: Joseph Lyons <joseph@zed.dev>
2023-04-10 16:45:15 -04:00
Max Brunsfeld
e79815622c Preserve ordering between UpdateProject and CreateBufferForPeer messages
Previously, because UpdateProject messages were sent in a separately-
spawned task, they could be sent after CreateBufferForPeer messages that
were intended to be sent after them.

Co-authored-by: Antonio Scandurra <antonio@zed.dev>
2023-04-10 12:54:25 -07:00
Antonio Scandurra
9761febf82 Avoid broadcasting SaveBuffer in response to a client's save request
The host will send a `SaveBuffer` message anyway and this prevents re-querying
the database, which could cause two `BufferSaved` messages to race and, as a
result, cause guest to apply them in the wrong order.
2023-04-10 10:02:13 +02:00
Antonio Scandurra
3a82c04248 Improve assertion message when buffer state diverges 2023-04-10 10:01:44 +02:00
Antonio Scandurra
016343e65d Merge branch 'main' into randomized-tests-operation-script 2023-04-10 08:45:22 +02:00
Antonio Scandurra
7f73ebdab5 Apply BufferReloaded message to incomplete remote buffers 2023-04-10 08:41:31 +02:00
Antonio Scandurra
116d4f20fa Merge pull request #2366 from zed-industries/avoid-unwrapping-on-try-send
Don't panic if worktree was dropped before sending path changes
2023-04-10 08:36:53 +02:00
Antonio Scandurra
372e31d54f Don't panic if worktree was dropped before sending path changes
In `refresh_entry`, we send a message to the `self.path_changes_tx` channel
to notify the background thread that a path has changed. However, given that
`refresh_entry` uses `spawn_weak`, the worktree could get dropped before sending
the message, which could cause a panic.

This commit changes the code to return an error instead of panicking.
2023-04-10 08:16:08 +02:00
Nathan Sobo
7536645eea WIP 2023-04-08 08:01:05 -06:00
Nathan Sobo
9d23a98157 WIP 2023-04-08 06:43:39 -06:00
Max Brunsfeld
acbf9b55d7 Halt UpdateBuffer messages until sync if one errors
Co-authored-by: Antonio Scandurra <antonio@zed.dev>
2023-04-07 17:31:52 -07:00
Max Brunsfeld
e50c48852a Wait for host to acknowledge buffer updates before sending them to other guests 2023-04-07 16:27:48 -07:00
Max Brunsfeld
f519f32ec2 Fixed removal of closed projects in randomized test
Co-authored-by: Antonio Scandurra <antonio@zed.dev>
2023-04-07 12:24:59 -07:00
Joseph Lyons
035189a2a1 Put file location details of panic on separate line 2023-04-07 14:32:14 -04:00
Nathan Sobo
676dbdc57b Merge pull request #2365 from zed-industries/dont_export_gpui_platform_module
Don't export platform::* from gpui
2023-04-07 12:12:05 -06:00
Nathan Sobo
2186de38ab Merge AppContext impl blocks 2023-04-07 11:54:08 -06:00
Nathan Sobo
2615a11f7c Don't export platform::* from gpui
I'd like to avoid cluttering the top-level namespace with all the platform-
specific types.
2023-04-07 11:45:53 -06:00
Joseph T. Lyons
f450692e77 Merge pull request #2364 from zed-industries/put-backtrace-on-a-newline-when-reporting-panics
Put backtrace on a new line when reporting panics
2023-04-07 12:46:53 -04:00
Joseph Lyons
9cf3481fc7 Put backtrace on a new line when reporting panics 2023-04-07 12:12:18 -04:00
Joseph T. Lyons
c58601ab8d Merge pull request #2363 from zed-industries/add-copy-path-commands
Update copy path commands
2023-04-07 12:09:39 -04:00
Joseph Lyons
5e55eb279c Update copy path commands 2023-04-07 02:36:56 -04:00
Nathan Sobo
dad403f483 Merge pull request #2362 from zed-industries/eliminate-mutable-app-context
Merge MutableAppContext into AppContext
2023-04-06 16:19:47 -06:00
Nathan Sobo
95eb918b4c Fix broken test due to trimmed trailing whitespace during formatting 2023-04-06 16:10:39 -06:00
Nathan Sobo
8136d8a8a6 Fix formatting 2023-04-06 15:54:44 -06:00
Nathan Sobo
de9bf6dfbd Merge MutableAppContext into AppContext
There may have been a good reason for the difference at some point, or I was
still learning Rust. But now it's just &mut AppContext vs &AppContext.
2023-04-06 15:49:03 -06:00
Max Brunsfeld
aa7918c4b5 Fix handling of redundant buffer creation messages on guests
Check if the buffer already exists *before* overwriting it.
Ignore redundant registrations on remote projects.

Co-authored-by: Antonio Scandurra <antonio@zed.dev>
2023-04-06 12:17:25 -07:00
Max Brunsfeld
22a6a243bc Move project assertions into main assertion function
Co-authored-by: Antonio Scandurra <antonio@zed.dev>
2023-04-06 11:38:42 -07:00
Joseph T. Lyons
dd00966cc6 Merge pull request #2361 from zed-industries/add-close-items-to-left-and-right
Add close items to left and right
2023-04-06 13:06:25 -04:00
Antonio Scandurra
ef04dc14cc Update file on incomplete buffer instead of waiting for it to be opened
This ensures that two successive file updates coming from the host are
not applied in reverse order.
2023-04-06 17:48:44 +02:00
Julia
4b39df4a6b Merge pull request #2304 from zed-industries/bump-diff-width
Increase git diff gutter width slightly
2023-04-06 10:58:55 -04:00
Antonio Scandurra
8020ea783f Wait to see guest's buffer version before converting completion anchor 2023-04-06 16:23:10 +02:00
Antonio Scandurra
4a61e2dfa4 Save server operations that were in the middle of being applied
Previously, if the test panicked before it had a chance to fully
apply an operation, it would end up not being saved in the plan.
With this commit we will mark the operation as applied before we
start processing it, and mark it as not applied if, once we're done,
we've found out that it couldn't be applied. This is consistent with
what we do for client operations.
2023-04-06 16:02:16 +02:00
Antonio Scandurra
f995d07542 Return error if subscribing to an entity that was already subscribed to 2023-04-06 14:42:19 +02:00
Antonio Scandurra
d7f56d6126 Forget which buffers were shared when host reconnects
This fixes a bug where the host would momentarily disconnect and the guest
would close and reopen the project. This would cause the host to not observe
the guest closing the project. When the guest tried to open one of the
buffers opened prior to closing the project, the host would not send them the
buffer state because it would still remember that the buffer was shared.

The `shared_buffers` map is now cleared when the host reconnects and will slowly
get re-filled as guests issue `SynchronizeBuffers` requests.
2023-04-06 13:49:09 +02:00
Joseph Lyons
4883ec2201 Add close items to left and right 2023-04-06 03:33:38 -04:00
Max Brunsfeld
bf3b8adf35 Avoid async fs call before checking if operation is applicable
This way, the executor isn't influenced by operations that aren't applicable.
2023-04-05 18:11:33 -07:00
Max Brunsfeld
1064b14779 Don't use TestPlan's rng in fake LSP handlers
These should use the test context's rng, so that they behave the same whether
a pre-recorded plan was used, or the plan is being generated.
2023-04-05 17:50:04 -07:00
Max Brunsfeld
2d63ed3ca4 Merge branch 'main' into randomized-tests-operation-script 2023-04-05 17:10:20 -07:00
Nathan Sobo
e5861d6c35 Fix typos in EULA 2023-04-05 18:02:35 -06:00
Max Brunsfeld
bda7086220 Clear guest's shared buffers if they rejoin project after leaving while host was disconnected 2023-04-05 16:53:36 -07:00
Max Brunsfeld
8e68c7f808 Do include operations in serialized test plan if they cause a client to hang 2023-04-05 16:52:39 -07:00
Max Brunsfeld
43a94cda5f Don't skip worktree updates if unknown entries are removed
When rejoining a project, if entries were both created and deleted since
joining the project, the guest will receive those entries ids in as
removed.
2023-04-05 15:36:01 -07:00
Max Brunsfeld
661fba8640 Run executor until parked at end of each iteration of random collaboration test
Without this, the server doesn't get dropped at the end of the test, and we eventually run
out of file handles due to sqlite connections being retained.
2023-04-05 15:05:32 -07:00
Max Brunsfeld
781d66f628 Omit operations for non-existent users from serialized test plan 2023-04-05 15:04:27 -07:00
Julia
43b6e7a944 Merge pull request #2360 from zed-industries/maybe-fix-lock-screen-crash
Hesitant possible fix for lock screen crash
2023-04-05 14:10:40 -04:00
Julia
6e3bf7fed4 Hesitant possible fix for lock screen crash
This may cause issues in this situations but as I cannot repro this I'm
making the call to try something and see how it plays out
2023-04-05 14:01:54 -04:00
Joseph Lyons
e4bb5c296c v0.82.x dev 2023-04-05 13:00:52 -04:00
Joseph T. Lyons
3b6c975658 Merge pull request #2357 from zed-industries/update-eula
Update EULA
2023-04-05 12:57:34 -04:00
Joseph Lyons
5becdbdea8 Fix spacing in EULA 2023-04-05 12:45:48 -04:00
Julia
af826f5efd Merge pull request #2359 from zed-industries/prefer-right-statusbar-items
Prioritize displaying right statusbar items overtop left items if needed
2023-04-05 12:16:30 -04:00
Julia
77bb52f72c Prioritize displaying right statusbar items overtop left items if needed 2023-04-05 12:08:52 -04:00
Mikayla Maki
64428bac15 Merge pull request #2358 from zed-industries/more-copilot-fixes
Invalidate copilot suggestion on backspaces
2023-04-05 08:59:30 -07:00
Mikayla Maki
78742d5615 Add additional case to range invalidation test
co-authored-by: antonio <antonio@zed.dev>
2023-04-05 08:52:56 -07:00
Mikayla Maki
b585470518 Invalidate copilot suggestion on backspaces
Restore an observation on initialization

co-authored-by: antonio <antonio@zed.dev>
2023-04-05 08:48:39 -07:00
Antonio Scandurra
e2c690cece Merge pull request #2356 from zed-industries/copilot-on-preview
Disable Copilot entirely for stable releases
2023-04-05 16:20:53 +02:00
Nathan Sobo
0d0f6b5a05 Update EULA 2023-04-05 08:18:51 -06:00
Antonio Scandurra
7ba46a0630 Honor ZED_RELEASE_CHANNEL environment variable only in development
We don't want people to be able to override the release channel in
production.
2023-04-05 15:55:44 +02:00
Antonio Scandurra
15b26e10d4 Disable Copilot entirely for stable releases
This lets us remove the logic for detecting whether a user is a staff
member.
2023-04-05 15:49:59 +02:00
Antonio Scandurra
781d065d0b Merge pull request #2355 from zed-industries/refine-copilot
Iterate some more on Copilot
2023-04-05 14:21:18 +02:00
Antonio Scandurra
03a4c9d6d5 Use the cursor variable instead of selection.start 2023-04-05 14:15:58 +02:00
Antonio Scandurra
dd416cdfd2 📝 2023-04-05 14:14:43 +02:00
Antonio Scandurra
f920e02d96 Indent instead of accepting suggestion if cursor is in leading whitespace 2023-04-05 14:09:09 +02:00
Antonio Scandurra
908a7cf47e 💄 2023-04-05 13:59:19 +02:00
Antonio Scandurra
dcd8bdfc88 Update visible suggestion if edit occurs outside the current editor 2023-04-05 11:06:14 +02:00
Antonio Scandurra
6e821eea4b Prevent tab from accepting a copilot suggestion when it isn't visible 2023-04-05 10:31:11 +02:00
Antonio Scandurra
661be7ba51 Refresh copilot suggestions when accepting a completion 2023-04-05 10:28:00 +02:00
Antonio Scandurra
7a7dc95611 Refresh copilot suggestions when undoing/redoing 2023-04-05 10:27:19 +02:00
Antonio Scandurra
399451b676 Capture copilot behavior in a editor unit test 2023-04-05 10:26:43 +02:00
Max Brunsfeld
1159f5517b Avoid applying outdated UpdateProject methods after rejoining a room 2023-04-04 21:49:37 -07:00
Max Brunsfeld
1ccf174388 Avoid applying outdated UpdateProject messages
Co-authored-by: Nathan Sobo <nathan@zed.dev>
2023-04-04 18:34:39 -07:00
Antonio Scandurra
808ddc26e7 Merge pull request #2353 from zed-industries/more-intuitive-autocomplete-with-copilot
Match VS Code's behavior when deciding whether to show Copilot suggestion
2023-04-04 20:03:07 +02:00
Antonio Scandurra
65fd605b82 Revert "Return the previous suggestion when replacing it"
This reverts commit 971c88db80.
2023-04-04 18:59:28 +02:00
Nate Butler
e7150d0b66 Merge pull request #2354 from zed-industries/nate/fix-embedded-highlight-in-ruby
Fix embedded highlight in ruby
2023-04-04 12:46:32 -04:00
Nate Butler
7ff97e50a7 Fix embedded highlight in ruby 2023-04-04 12:41:38 -04:00
Antonio Scandurra
b58ac815a8 Trigger copilot only on insertion and deletion
Also, avoid showing the suggestion if a completion is in progress to
avoid flickering.
2023-04-04 17:51:37 +02:00
Antonio Scandurra
971c88db80 Return the previous suggestion when replacing it 2023-04-04 17:14:52 +02:00
Antonio Scandurra
a2aad31cf1 Merge pull request #2352 from zed-industries/debounce-copilot
Debounce copilot completions
2023-04-04 16:52:59 +02:00
Antonio Scandurra
e970d84184 Debounce copilot completions
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-04-04 16:02:25 +02:00
Antonio Scandurra
5df5973262 Merge pull request #2351 from zed-industries/more-copilot-enhancements
Fix additional Copilot issues
2023-04-04 14:22:12 +02:00
Antonio Scandurra
60955fb8ee Refresh active copilot suggestion when accepting completion 2023-04-04 11:15:14 +02:00
Antonio Scandurra
79d5bb45dc Clear Copilot suggestions when it gets disabled from the settings 2023-04-04 10:45:56 +02:00
Antonio Scandurra
abf158a9e4 Merge pull request #2347 from zed-industries/disable-yaml-key-ordering
Disable key ordering diagnostic for YAML
2023-04-04 10:00:48 +02:00
Max Brunsfeld
bcf9b2f10d Add missing random delays in FakeFs 2023-04-03 22:42:34 -07:00
Mikayla Maki
91d85987b9 Merge pull request #2350 from zed-industries/copilot-disable
Disable copilot unless the staff mode flag is flipped
2023-04-03 22:00:47 -07:00
Mikayla Maki
fa32a20c42 fixup! fixup! fixup! removed copilot from generated schema and command palette 2023-04-03 21:46:59 -07:00
Mikayla Maki
a1abe8d33c fixup! fixup! removed copilot from generated schema and command palette 2023-04-03 21:46:30 -07:00
Mikayla Maki
ae6a671fea fixup! removed copilot from generated schema and command palette 2023-04-03 21:45:18 -07:00
Mikayla Maki
47de4dcd32 removed copilot from generated schema and command palette 2023-04-03 21:38:26 -07:00
Mikayla Maki
6bfecd7f66 Refactor staff mode into a seperate crate and make copilot initialization wait for the staff mode flag to be flipped 2023-04-03 20:16:45 -07:00
Max Brunsfeld
b251e249a7 Check for consistency between clients every time the system quiesces 2023-04-03 19:11:37 -07:00
Max Brunsfeld
7b0a6c0dfa Add an 'on_failure' attribute to gpui tests
This lets us perform a finalization step when a randomized test fails.
2023-04-03 18:16:08 -07:00
Max Brunsfeld
5ecc9606af Use synchronous locks in FakeFs
This way, the state can be accessed without running the deterministic
executor.
2023-04-03 18:15:07 -07:00
Mikayla Maki
55297c1a26 Merge pull request #2348 from zed-industries/copilot-feedback
Fix issues from copilot feedback
2023-04-03 16:29:41 -07:00
Max Brunsfeld
f95732e981 Fix bug where guest would drop BufferSaved messages while opening the buffer 2023-04-03 16:23:44 -07:00
Mikayla Maki
1627cf7eae Add standard copilot keybinding 2023-04-03 16:14:07 -07:00
Mikayla Maki
bab0e88b6f Fix bug where copilot auth window will only be shown once 2023-04-03 16:01:04 -07:00
Max Brunsfeld
543301f949 Avoid repeatedly loading/saving the test plan for each iteration 2023-04-03 15:58:11 -07:00
Mikayla Maki
2e33f8b228 fixup! Remove per-file copilot enable/disable 2023-04-03 15:21:43 -07:00
Mikayla Maki
48d9c30b0e Remove per-file copilot enable/disable 2023-04-03 15:15:42 -07:00
Mikayla Maki
0250898a2b Enable copilot in all file types by default 2023-04-03 15:06:49 -07:00
Max Brunsfeld
c960277349 Merge branch 'main' into randomized-tests-operation-script 2023-04-03 13:09:25 -07:00
Antonio Scandurra
2c0a645f19 Provide workspace_folders capability when initializing LSP
This fixes the YAML language server which was erroring on startup
after adding the `did_change_watched_files` capability in #2258.
2023-04-03 20:12:26 +02:00
Antonio Scandurra
588b2da40a Disable key ordering diagnostic for YAML language server 2023-04-03 20:02:29 +02:00
Petros Amoiridis
69557655ee Merge pull request #2346 from zed-industries/petros/z-360-dragging-file-from-tree-to-split-results
Fix dragging file from tree to split results in empty pane
2023-04-03 16:43:12 +03:00
Petros Amoiridis
5a397726d2 Do not drop the task
Co-Authored-By: Antonio Scandurra <me@as-cii.com>
2023-04-03 16:36:08 +03:00
Nathan Sobo
cdfe873802 Merge pull request #2343 from zed-industries/handles
Make typed handles wrappers around their untyped equivalents
2023-04-03 09:20:37 -04:00
Antonio Scandurra
0cb73b6579 Merge pull request #2345 from zed-industries/autocomplete-vs-copilot
Make autocompletion and copilot mutually exclusive
2023-04-03 14:37:02 +02:00
Antonio Scandurra
b88aa9af1d Clear active copilot suggestion only if there is one 2023-04-03 14:32:41 +02:00
Antonio Scandurra
ec5309b543 Make autocompletion and copilot mutually exclusive 2023-04-03 14:15:21 +02:00
Antonio Scandurra
b6a9d90609 Move creation of GetCompletionsParams entirely to the background 2023-04-03 13:30:57 +02:00
Antonio Scandurra
0b9e609e2a Merge pull request #2344 from zed-industries/copilot-collaboration
Fix Copilot errors when opening buffers that don't exist locally
2023-04-03 13:26:21 +02:00
Antonio Scandurra
b26d4f4f62 🔥 2023-04-03 13:18:16 +02:00
Antonio Scandurra
75ecf92ae4 Fix Copilot errors when opening buffers that don't exist locally 2023-04-03 12:28:32 +02:00
Nathan Sobo
138aa0492a Add downcast_ref to AnyViewHandle
I use unsafe code to transmute an AnyViewHandle to a ViewHandle<T> when
the type matches. Because ViewHandle<T> is repr(transparent) to the
wrapped AnyViewHandle, this is safe.
2023-04-02 16:10:16 -06:00
Nathan Sobo
71cf6ec5ae Use into_any instead of weird 'as' hack 2023-04-02 15:55:30 -06:00
Nathan Sobo
27258a0889 Remove From trait for AnyViewHandle in favor of into_any method
I think it's more explicit.
2023-04-02 15:50:42 -06:00
Nathan Sobo
aed8df96ff Wrap AnyWeak handles with their typed counterparts 2023-04-02 15:42:03 -06:00
Nathan Sobo
82a713fd1d Store AnyViewHandle inside ViewHandle and Deref to it 2023-04-02 14:59:55 -06:00
Julia
59fb4b3d29 Merge pull request #2342 from zed-industries/trust-npm-version-management
Trust NPM instead of managing dirs for different Node server versions
2023-03-31 17:21:16 -04:00
Julia
8c0db17634 Trust NPM instead of managing dirs for different Node server versions 2023-03-31 17:15:44 -04:00
Mikayla Maki
c19e6f8f0e Merge pull request #2338 from zed-industries/more-copilot-bugs
More copilot bugs
2023-03-31 12:45:19 -07:00
Mikayla Maki
297fd5f985 Merge pull request #2340 from zed-industries/fix-json-lsp-remove
Fix bug where JSON LSP would be deleted on installation.
2023-03-31 12:41:28 -07:00
Mikayla Maki
dcaa355ad2 Switch from removing all directories in the container dir to removing the non-version dir 2023-03-31 12:36:23 -07:00
Julia
0ae298a86f Merge pull request #2339 from zed-industries/revert
Revert "Merge pull request #2335 from zed-industries/dont-delete-json…
2023-03-31 15:33:12 -04:00
Julia
0f4f3bfbde Revert "Merge pull request #2335 from zed-industries/dont-delete-json-server-after-downloading-it"
This reverts commit 516e77906e, reversing
changes made to 54582fd77f.
2023-03-31 15:31:50 -04:00
Mikayla Maki
57b5c1ee20 Fix crash on initiating copilot during collaboration. Copilot is now disabled when collaborating. 2023-03-31 12:28:58 -07:00
Mikayla Maki
f616e8bac9 Merge pull request #2336 from zed-industries/respect-languages-alias-in-settings-file
Add support for writing to the langauge_overrides alias in settings
2023-03-31 11:51:25 -07:00
Mikayla Maki
b6882f0cbf Fix typo in copilot button 2023-03-31 11:50:31 -07:00
Mikayla Maki
e5a6b11d8f Merge pull request #2334 from zed-industries/quiet-copilot-loggin
Add handlers for LogMessage and statusNotification to copilot LSP
2023-03-31 11:22:23 -07:00
Mikayla Maki
c1445fe015 Add support for the langauge_overrides alias in the settings file updating code 2023-03-31 11:20:46 -07:00
Mikayla Maki
bdce96b19a Update copilot.rs 2023-03-31 11:16:52 -07:00
Julia
516e77906e Merge pull request #2335 from zed-industries/dont-delete-json-server-after-downloading-it
Don't delete JSON language server directly after downloading it
2023-03-31 14:08:09 -04:00
Julia
0199ffed5f Don't delete JSON language server directly after downloading it
This was accidentally added during the messy NodeRuntime/Copilot rebase

Co-Authored-By: Joseph Lyons <joseph@zed.dev>
Co-Authored-By: Mikayla Maki <mikayla@zed.dev>
2023-03-31 14:01:04 -04:00
Mikayla Maki
f561e5ef2c remove spare debug 2023-03-31 11:00:06 -07:00
Mikayla Maki
4865ea2efc Add handlers for LogMessage and statusNotification to copilot LSP 2023-03-31 10:58:18 -07:00
Antonio Scandurra
54582fd77f Merge pull request #2333 from zed-industries/copilot-improvements
Fix several Copilot bugs
2023-03-31 18:14:39 +02:00
Antonio Scandurra
b208d1a489 🎨 2023-03-31 18:10:10 +02:00
Antonio Scandurra
5f579a4287 Fix prefix/suffix calculation when determining copilot suggestion
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
Co-Authored-By: Mikayla Maki <mikayla@zed.dev>
2023-03-31 18:05:07 +02:00
Antonio Scandurra
b588ba1435 Avoid auto-indenting when accepting copilot suggestion
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-03-31 17:17:35 +02:00
Antonio Scandurra
6e43e77c3f Use copilot's Completion::{range,text} to determine suggestion
Previously, we were using display text, but this isn't always correct. Now,
we just attempt to determine what text Copilot wants to insert by finding
a prefix and suffix in the existing text with the suggested text.

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-03-31 17:08:41 +02:00
Mikayla Maki
9b16277cf8 Merge pull request #2316 from zed-industries/copilot
🚨 WIP 🚨 Copilot
2023-03-30 18:27:14 -07:00
Mikayla Maki
713f5f604f Fix leaked handle and failure to update language in context menu 2023-03-30 17:40:11 -07:00
Mikayla Maki
e38f52d595 Fix unrelated panics in tests 2023-03-30 17:19:29 -07:00
Mikayla Maki
c3188be4c1 cargofmt 2023-03-30 16:58:11 -07:00
Mikayla Maki
81411b9114 Merge branch 'main' into copilot 2023-03-30 16:57:18 -07:00
Mikayla Maki
c28d2c490b Remove test modals 2023-03-30 16:52:55 -07:00
Mikayla Maki
afc9b832c8 Finish device code flow for copilot 2023-03-30 16:50:33 -07:00
Mikayla Maki
f5d4bcd934 Added erorr states and first-pass error handling to the copilot status bar item.
Added correct icons
Added a new 'Toast' action which allows other crates to easily pop toasts with an optional click action
2023-03-30 14:10:57 -07:00
Julia
3cfe61a4f4 Merge pull request #2332 from zed-industries/per-server-code-action-kinds
Allow each language adapter to provide their own code action kinds array
2023-03-30 15:54:55 -04:00
Julia
cdde523ea4 Allow each language adapter to provide their own code action kinds array 2023-03-30 15:41:54 -04:00
Nate Butler
f235d9f411 Add zed plus copilot icon 2023-03-30 14:34:33 -04:00
Nate Butler
655897b182 Update icons 2023-03-30 14:32:51 -04:00
Nate Butler
58b453ad6e Add new copilot state icons 2023-03-30 14:18:03 -04:00
Mikayla Maki
b7461c32dd Improve settings writing for more cases 2023-03-30 10:40:53 -07:00
Mikayla Maki
e46cd2def3 Switch to using zed hosted copilot LSP (again)
co-authored-by: antonio <antionio@zed.dev>
2023-03-30 09:29:49 -07:00
Petros Amoiridis
007aa92581 Merge pull request #2323 from zed-industries/petros/z-402-recent-projects-replace-expanded-home
Replace home directory with the tilde substitution
2023-03-30 18:12:39 +03:00
Petros Amoiridis
dc51735112 Fix doctests 2023-03-30 17:57:14 +03:00
Petros Amoiridis
adc5ef911f Remove the Errors section from rust docs
The section does not add anything that we don't already know.

Co-Authored-By: Antonio Scandurra <me@as-cii.com>
2023-03-30 14:04:37 +03:00
Petros Amoiridis
18c6c7ebb7 Fix error 2023-03-30 14:03:59 +03:00
Petros Amoiridis
9ef3e45bcd Update crates/recent_projects/src/highlighted_workspace_location.rs
Co-authored-by: Antonio Scandurra <me@as-cii.com>
2023-03-30 11:35:17 +03:00
Antonio Scandurra
e3a311fda9 Merge pull request #2331 from zed-industries/tab-map-sync
Speed up tab searching in `TabMap::sync`
2023-03-30 10:20:57 +02:00
Antonio Scandurra
af4c4c7cf0 Speed up tab searching in TabMap::sync by looking for \t only
Instead of looking for `\n` as a stopping condition, we cap the range
we pass to `SuggestionSnapshot::chunks` to stop on the next line. This
makes character searching faster, because looking for a single character
uses `memchr`. Also, this avoids an extra conditional in a tight loop such
as the chunk scanning one contained in `TabMap::sync`.
2023-03-30 10:03:13 +02:00
Antonio Scandurra
79346b0706 Use + instead of | to break out of tab expansion in TabMap::sync 2023-03-30 09:49:07 +02:00
Mikayla Maki
5b3b74345d Remove some strays 2023-03-29 22:22:02 -07:00
Mikayla Maki
a8033b266d Fix bug with enable setting, clean up sign in UIs 2023-03-29 21:51:07 -07:00
Mikayla Maki
cc7c5b416c Add status bar icon reflecting copilot state to Zed status bar 2023-03-29 21:31:33 -07:00
Mikayla Maki
8fac32e1eb WIP, not compiling 2023-03-29 17:59:21 -07:00
Max Brunsfeld
ec5ffe9922 Merge pull request #2329 from zed-industries/new-window-on-click-doc
Open a new window when activating Zed from the dock w/ no windows open
2023-03-29 17:33:32 -07:00
Mikayla Maki
76efab005f WIP 2023-03-29 17:25:41 -07:00
Max Brunsfeld
b5f762ab25 Open a new window when activating Zed from the dock w/ no windows open 2023-03-29 17:24:44 -07:00
Nate Butler
e3822a5b5c Add copilot icons 2023-03-29 20:23:32 -04:00
Mikayla Maki
ae3b3ea458 Merge branch 'main' into copilot 2023-03-29 16:57:38 -07:00
Mikayla Maki
ebd06b43f5 Add copilot settings
Add copilot pause / resume
Add copilot statusbar item stub
2023-03-29 16:40:52 -07:00
Max Brunsfeld
7601e7902a Merge pull request #2325 from zed-industries/tab-map-long-lines
Avoid slowdowns with long lines by skipping tab expansion beyond a certain column
2023-03-29 14:48:23 -07:00
Max Brunsfeld
0c07a373a8 🎨 2023-03-29 14:43:01 -07:00
Julia
e558d3f903 Merge pull request #2328 from zed-industries/breadcrumbs-outline-toggle
Open symbol outline when clicking on editor breadcrumbs
2023-03-29 15:51:30 -04:00
Julia
737e2e1b3c Open symbol outline when clicking on editor breadcrumbs 2023-03-29 15:46:43 -04:00
Mikayla Maki
f812796283 Make modal behavior stateless
Co-authored-by: antonio <antonio@zed.dev>
2023-03-29 12:45:53 -07:00
Antonio Scandurra
49447128a9 Make edits smaller when tab expansion changes on a line 2023-03-29 21:40:37 +02:00
Joseph Lyons
6e2a9297ff v0.81.x dev 2023-03-29 14:13:01 -04:00
Antonio Scandurra
b86f8188d1 Expand edit to end of the line when old/new row exceeds max expansion 2023-03-29 16:49:21 +02:00
Petros Amoiridis
b15632bd45 Remove convert_to_shortened_path from gpui platform 2023-03-29 17:41:01 +03:00
Petros Amoiridis
b4593cd90b Use util::paths::compact 2023-03-29 17:40:39 +03:00
Petros Amoiridis
69989d0463 Introduce compact function in util create 2023-03-29 17:32:27 +03:00
Petros Amoiridis
d5f53111e8 Enable doctests in util crate 2023-03-29 17:31:55 +03:00
Petros Amoiridis
a128439699 Move code into the platform
Co-Authored-By: Joseph T. Lyons <19867440+JosephTLyons@users.noreply.github.com>
2023-03-29 16:25:13 +03:00
Petros Amoiridis
87c1b190a8 Replace home directory with the tilde substitution 2023-03-29 16:25:10 +03:00
Antonio Scandurra
719d0f0abf Rename skip_leading_tab to inside_leading_tab 2023-03-29 14:53:28 +02:00
Antonio Scandurra
813f722925 Increment input_column correctly when inside the leading tab 2023-03-29 14:52:50 +02:00
Antonio Scandurra
056f4e914f Merge pull request #2327 from zed-industries/remove-unused-code
Delete unused code
2023-03-29 09:09:47 +02:00
Antonio Scandurra
a64296938d Delete unused code 2023-03-29 09:04:13 +02:00
Mikayla Maki
d60ef03d66 WIP Add copilot disabled setting 2023-03-28 20:52:50 -07:00
Mikayla Maki
0b0c7e4ce9 Move command palette filter into collections crate
Filter out copilot commands from command palette when not active
2023-03-28 20:13:17 -07:00
Mikayla Maki
aea8475d30 Apply cargo fmt 2023-03-28 18:45:37 -07:00
Antonio Scandurra
a5cec18775 Remove copilot smoke test 2023-03-28 18:41:35 -07:00
Antonio Scandurra
12370f120e 🎨 2023-03-28 18:40:51 -07:00
Antonio Scandurra
ce9774be53 Improve detection of common prefix in text_for_active_completion 2023-03-28 18:40:42 -07:00
Mikayla Maki
9d8d2bb8f4 Add rough versions of all 3 modals 2023-03-28 18:00:09 -07:00
Max Brunsfeld
5c1f82ae3d Don't use TabMap::expand_tabs in line_indent_for_buffer_row 2023-03-28 17:37:57 -07:00
Max Brunsfeld
08e93e9321 Only expand tabs up until a limited column 2023-03-28 16:40:04 -07:00
Max Brunsfeld
c23feeab3a 🎨 Make expand_tabs and collapse_tabs instance methods on TabSnapshot 2023-03-28 14:10:43 -07:00
Mikayla Maki
941da24f73 Refactor out the node runtime crate and hook up all related imports 2023-03-28 10:27:31 -07:00
Mikayla Maki
0ef9cefe0f Finish shape of copilot auth UI 2023-03-28 09:42:01 -07:00
Mikayla Maki
6ff09865eb Create copilot auth popup UI 2023-03-28 09:42:01 -07:00
Antonio Scandurra
da81ff3295 Optimize CopilotState::text_for_active_completion 2023-03-28 09:42:01 -07:00
Antonio Scandurra
034bc75467 Refresh copilot suggestions when hitting alt-] if none are showing
Co-Authored-By: Mikayla Maki <mikayla@zed.dev>
2023-03-28 09:42:01 -07:00
Antonio Scandurra
d236d9e8c9 Clear copilot suggestions when hitting escape
Co-Authored-By: Mikayla Maki <mikayla@zed.dev>
2023-03-28 09:42:01 -07:00
Nate Butler
ef6c28326d Update editor.ts 2023-03-28 09:42:01 -07:00
Nate Butler
2fede1c01f Use syntax.predictive.color to style suggestions 2023-03-28 09:42:01 -07:00
Antonio Scandurra
1162615043 Reuse existing suggestion when inserting to avoid flickering 2023-03-28 09:42:01 -07:00
Antonio Scandurra
6715e5247c Rework SuggestionMap to take highlight style when retrieving chunks 2023-03-28 09:42:01 -07:00
Antonio Scandurra
093e0a30e9 Replace editor::CycleCopilotSuggestions with copilot::NextSuggestion 2023-03-28 09:42:01 -07:00
Antonio Scandurra
2f95510a2e Start integrating Copilot with editor
There's still a bit to do in terms of reusing the previous suggestion
when the prefix matches, but we're getting there.
2023-03-28 09:42:01 -07:00
Antonio Scandurra
b16e2169ce WIP: Start on showing window for authenticating with copilot
Co-Authored-By: Mikayla Maki <mikayla@zed.dev>
2023-03-28 09:42:01 -07:00
Antonio Scandurra
9713d1bb31 Fix invalid translation between bottom/top left coordinate spaces
Co-Authored-By: Mikayla Maki <mikayla@zed.dev>
2023-03-28 09:42:01 -07:00
Antonio Scandurra
99cca59c84 Restructure verification code prompting to open a window instead
Also, prevent multiple calls to `sign_in` from racing with each other.
2023-03-28 09:42:01 -07:00
Mikayla Maki
19cc86a2d4 Wait to show the auth modal until the sign request has returned 2023-03-28 09:42:00 -07:00
Mikayla Maki
15e29d44b9 Add basic copilot modal 2023-03-28 09:42:00 -07:00
Mikayla Maki
b57d5174aa Add copilot theme, start sketching out the auth modal 2023-03-28 09:42:00 -07:00
Antonio Scandurra
591e246450 Implement Copilot::completions_cycling
Co-Authored-By: Mikayla Maki <mikayla@zed.dev>
2023-03-28 09:42:00 -07:00
Antonio Scandurra
155594c8b8 Successfully fetch completions from Copilot
We still need to process them and return them into a more Zed-friendly
structure, but we're getting there.
2023-03-28 09:42:00 -07:00
Antonio Scandurra
180371929b Start on copilot completions 2023-03-28 09:42:00 -07:00
Antonio Scandurra
8ba9e63ab8 🎨 2023-03-28 09:42:00 -07:00
Antonio Scandurra
59d9277a74 Implement Copilot sign in and sign out 2023-03-28 09:42:00 -07:00
Antonio Scandurra
797bb7d780 Start copilot and check sign in status 2023-03-28 09:42:00 -07:00
Antonio Scandurra
5471217089 Use the same serde version across the entire workspace 2023-03-28 09:42:00 -07:00
Mikayla Maki
9a99eaee96 Fix bad global 2023-03-28 09:42:00 -07:00
Mikayla Maki
455cdc8b37 Add copilot crate
Refactor HTTP and github release downloading into util
Lazily download / upgrade the copilot LSP from Zed

Co-authored-by: Max <max@zed.dev>
Co-Authored-By: Antonio <antonio@zed.dev>
2023-03-28 09:41:59 -07:00
Julia
35b2aceffb Merge pull request #2324 from zed-industries/download-node
Automatically download Node for Node based language servers
2023-03-28 12:10:24 -04:00
Julia
ee3ac9c344 Rename installation.rs -> github.rs now that is all it concerns 2023-03-28 11:51:09 -04:00
Julia
350f8ed304 Download the JSON LSP package instead of our own bundled binary 2023-03-28 11:48:00 -04:00
Julia
d4560fe321 Prevent deadlock when multiple languages attempt to install Node at once 2023-03-28 10:18:22 -04:00
Max Brunsfeld
c68c8462bb Merge pull request #2322 from zed-industries/project-panel-paste-infinite-loop
Fix infinite loop in ProjectPanel::paste when filename has multiple dots
2023-03-27 17:09:10 -07:00
Max Brunsfeld
17bc83d699 Fix infinite loop in ProjectPanel::paste when filename has multiple dots 2023-03-27 16:45:11 -07:00
Max Brunsfeld
e5d552ef97 Merge pull request #2321 from zed-industries/new-file-with-no-window
Make 'new file' action open a window when there are no windows open
2023-03-27 16:10:30 -07:00
Max Brunsfeld
4a2132bc91 Make 'new file' action open a window when there are no windows open 2023-03-27 16:05:00 -07:00
Joseph T. Lyons
e10338ed17 Merge pull request #2313 from zed-industries/unify-spelling-of-key-binding-to-be-two-words
Unify spelling of `key binding` to be two words
2023-03-27 15:58:12 -04:00
Petros Amoiridis
eb7c6028f4 Merge pull request #2320 from zed-industries/petros/z-349-make-restart-to-update-zed-look
Make "Restart to update Zed" look clickable
2023-03-27 20:19:06 +03:00
Julia
df4380b066 Download aarch64 or x64 Node binary according to system architecture 2023-03-27 11:05:17 -04:00
Petros Amoiridis
b153bf7118 Add a hovered style to lspStatus background 2023-03-27 14:26:56 +03:00
Petros Amoiridis
374b284a3d Run prettier on TS files 2023-03-27 14:26:05 +03:00
Julia
c72d33e029 Initial impl of NodeRuntime w/JSON borked and a deadlock :)
Co-Authored-By: Antonio Scandurra <me@as-cii.com>
2023-03-27 00:00:16 -04:00
Julia
1a2e509e35 Remove server_args from LspAdapter
Prepare to remove concept of a runtime from greater server startup code,
which is important for future language server extensibility

Co-Authored-By: Antonio Scandurra <me@as-cii.com>
2023-03-27 00:00:16 -04:00
Julia
ed442cfc8c Invoke npm from downloaded Node 2023-03-27 00:00:16 -04:00
Julia
edd6c85af7 Initial running of servers on downloaded Node 2023-03-27 00:00:16 -04:00
Julia
b579211861 Report if language server requires Node or not 2023-03-27 00:00:16 -04:00
Nathan Sobo
d89936e4a9 Merge pull request #2319 from zed-industries/copy-highlight-json
Add "editor: copy highlight json" command
2023-03-24 17:17:45 -06:00
Nathan Sobo
f0992e7d67 Trim empty tokens; copy selected range if non-empty 2023-03-24 17:10:50 -06:00
Nathan Sobo
3dfedd1b21 Merge adjacent chunks with the same highlight name in copied JSON 2023-03-24 16:52:00 -06:00
Nathan Sobo
195215f1e0 Add "editor: copy highlight json" command
Nate needs this to feed to Figma for highlighted code in designs.
2023-03-24 16:37:57 -06:00
Max Brunsfeld
c74f8eb9e3 Merge pull request #2258 from zed-industries/lsp-file-change-notifications
Implement `DidChangedWatchedFiles` LSP feature
2023-03-24 14:42:23 -07:00
Max Brunsfeld
455ffb17f1 Handle path changes and progress updates from all worker threads during initial scan 2023-03-24 14:35:18 -07:00
Max Brunsfeld
027def6800 Merge branch 'main' into lsp-file-change-notifications 2023-03-24 08:52:43 -07:00
Max Brunsfeld
a0e98ccc35 🎨 BackgroundScanner::run 2023-03-23 18:05:12 -07:00
Max Brunsfeld
89e99d2902 🎨 Don't store path changes statefully on the background scanner 2023-03-23 16:04:47 -07:00
Max Brunsfeld
3ff5aee4a1 Respect LSP servers watch glob patterns 2023-03-23 16:03:07 -07:00
Mikayla Maki
76b75b4b43 Merge pull request #2318 from zed-industries/fix-unknown-in-non-editor-buffers
changed language status bar item to only show on editors
2023-03-23 13:33:42 -07:00
Mikayla Maki
5db11c628b changed language status bar item to only show on editors 2023-03-23 13:29:23 -07:00
Mikayla Maki
5cad3d3a67 Merge pull request #2317 from zed-industries/fix-titlebar-right-spacing
Fix misaligned UI in the right titlebar
2023-03-23 13:16:40 -07:00
Mikayla Maki
bb5c2833a3 Aligned title bar items to the center and fixed left spacing on the sign in button
co-authored-by: max <max@zed.dev>
2023-03-23 13:08:31 -07:00
Petros Amoiridis
566a04ebca Merge pull request #2311 from zed-industries/petros/z-279-add-terminals-count
Add terminals count
2023-03-23 10:52:01 +02:00
Mikayla Maki
f9d3963dbb Merge pull request #2315 from zed-industries/fix-fold-indicator-offsets
Fix fold indicator offsets
2023-03-22 17:13:13 -07:00
Mikayla Maki
e87c3b6dd7 Update element.rs
remove spare parens
2023-03-22 17:05:54 -07:00
Mikayla Maki
e729c4ad4f Fix fold indicator offsets 2023-03-22 17:04:52 -07:00
Max Brunsfeld
361b7c3a0c Clear auto-indent requests when replacing a buffer's entire text 2023-03-22 15:10:16 -07:00
Max Brunsfeld
eaee5571a0 Use a more stable, readable serialization format for neovim-backed vim tests 2023-03-22 14:31:11 -07:00
Joseph Lyons
6de38f7410 v0.80.x dev 2023-03-22 16:33:06 -04:00
Julia
df553de363 Merge pull request #2314 from zed-industries/another-one
Remove another spot with a flag old npm does not like
2023-03-22 15:56:23 -04:00
Julia
4fc37cf982 Remove another spot with a flag old npm does not like 2023-03-22 15:40:51 -04:00
Joseph Lyons
9d88cd8842 Unify spelling of key binding to be two words 2023-03-22 13:34:12 -04:00
Petros Amoiridis
fd9eff3a78 Remove struct 2023-03-22 19:28:06 +02:00
Petros Amoiridis
bd1515cdd2 Only show count when we have terminals
Co-Authored-By: Mikayla Maki <mikayla.c.maki@gmail.com>
2023-03-22 18:23:41 +02:00
Joseph T. Lyons
bd85ef363f Merge pull request #2312 from zed-industries/unkown
Fix typo in "Unknown" language
2023-03-22 11:20:18 -04:00
Nathan Sobo
e017b99384 Fix typo 2023-03-22 09:13:10 -06:00
Petros Amoiridis
15406ff2d9 Remove comment 2023-03-22 16:31:42 +02:00
Petros Amoiridis
d5bb2d13b8 Introduce terminal button count
Co-Authored-By: Antonio Scandurra <me@as-cii.com>
2023-03-22 16:13:58 +02:00
Petros Amoiridis
aa7254167a Fix typo 2023-03-22 15:31:21 +02:00
Antonio Scandurra
005eb559ee Merge pull request #2310 from zed-industries/fix-sharing-status-indicator
Remove screen sharing indicator when call ends
2023-03-22 09:40:23 +01:00
Antonio Scandurra
7df798ded5 Remove screen sharing indicator when call ends
Previously, we would only remove it when the screen sharing stopped.
2023-03-22 09:32:27 +01:00
Max Brunsfeld
c1f53358ba Remove unnecessary Arc around background scanner's snapshot 2023-03-21 15:47:02 -07:00
Max Brunsfeld
f7b2713b77 Fix error in joining empty paths 2023-03-21 15:41:24 -07:00
Max Brunsfeld
5da2b123b5 Allow refreshing worktree entries while the initial scan is in-progress 2023-03-21 15:15:12 -07:00
Max Brunsfeld
b10b0dbd75 Only mutate background snapshot in the background scanner 2023-03-21 11:26:33 -07:00
Max Brunsfeld
d742c758bc Restructure communication from BackgroundScanner to LocalWorktree
The worktree no longer pulls the background snapshot from the background scanner.
Instead, the background scanner sends both snapshots to the worktree. Along with
these, it sends the path change sets.

Also, add randomized test coverage for the worktree UpdatedEntries events.
2023-03-21 11:26:13 -07:00
Max Brunsfeld
cbeb6e692d Move postage crate version specification to workspace Cargo.toml 2023-03-21 11:26:13 -07:00
Max Brunsfeld
d36b2a3129 🎨 Simplify some worktree methods
* Consolidate local worktree construction into one method
* Simplify remote worktree construction
* Reduce indirection around pulling worktree snapshots from the background
2023-03-21 11:26:13 -07:00
Max Brunsfeld
399f082415 Update wrong assertions after fixing missing event in FakeFs 2023-03-21 11:26:13 -07:00
Max Brunsfeld
51b093197d Add missing import in project tests 2023-03-21 11:26:13 -07:00
Max Brunsfeld
27ad6a57ce Tweak logging in worktree randomized test 2023-03-21 11:26:13 -07:00
Max Brunsfeld
c730dca3c5 Update worktree randomized test to use worktree's public interface and the fake fs 2023-03-21 11:26:13 -07:00
Max Brunsfeld
be5868e1c0 Conservatively report fs events that occurred during initial worktree scan
Co-authored-by: Nathan Sobo <nathan@zed.dev>
2023-03-21 11:26:13 -07:00
Max Brunsfeld
61172c8478 Notify language servers of FS changes 2023-03-21 11:26:13 -07:00
Max Brunsfeld
9837a6e288 Add failing test for reporting FS change events to language servers 2023-03-21 11:26:13 -07:00
Antonio Scandurra
194c7a3af0 Merge pull request #2309 from zed-industries/suggestion-map
Introduce `DisplayMap::replace_suggestion`
2023-03-21 18:18:50 +01:00
Antonio Scandurra
2893c9bdb7 Don't move up/down by more rows than the requested ones
Co-Authored-By: Max Brunsfeld <max@zed.dev>
2023-03-21 17:52:53 +01:00
Antonio Scandurra
f7cba4cec4 Make Suggestion fields public 2023-03-21 16:51:33 +01:00
Antonio Scandurra
ba3913df8c Expose a DisplayMap::replace_suggestion method 2023-03-21 16:41:54 +01:00
Antonio Scandurra
9c8732a355 Integrate SuggestionMap into the rest of DisplayMap 2023-03-21 16:39:02 +01:00
Antonio Scandurra
d1978a719b Add a version field to SuggestionSnapshot 2023-03-21 12:47:04 +01:00
Antonio Scandurra
3d165f705f Extract a SuggestionMap::randomly_mutate method 2023-03-21 11:51:06 +01:00
Antonio Scandurra
35830a0271 Implement SuggestionSnapshot::to_{fold,suggestion}_point 2023-03-21 11:39:29 +01:00
Antonio Scandurra
d448a5cb5c Implement SuggestionSnapshot::to_point 2023-03-21 11:28:36 +01:00
Antonio Scandurra
f829ce5641 Implement SuggestionSnapshot::to_offset 2023-03-21 11:28:33 +01:00
Antonio Scandurra
c0e124a55a Implement SuggestionSnapshot::text_summary_for_range 2023-03-21 11:28:30 +01:00
Antonio Scandurra
52a156aebe Implement SuggestionSnapshot::clip_point 2023-03-21 11:28:27 +01:00
Antonio Scandurra
ccb6196224 Implement SuggestionSnapshot::buffer_rows 2023-03-21 11:28:23 +01:00
Antonio Scandurra
1a9dbfa86a Add unit test to verify basic properties of the SuggestionMap 2023-03-21 08:29:33 +01:00
Joseph T. Lyons
8c0dd887ff Merge pull request #2299 from zed-industries/correct-verb-tense-in-default-settings
Correct verb tense in default settings
2023-03-20 14:50:46 -04:00
Antonio Scandurra
3edf83cb99 Implement SuggestionSnapshot::line_len
Co-Authored-By: Max Brunsfeld <max@zed.dev>
2023-03-20 19:45:39 +01:00
Antonio Scandurra
f44549eb29 Enhance randomized test to verify SuggestionMap::{chunks,sync}
Co-Authored-By: Max Brunsfeld <max@zed.dev>
2023-03-20 19:29:22 +01:00
Antonio Scandurra
4d6726ef39 WIP: Flesh out more of the suggestions randomized test 2023-03-20 18:06:24 +01:00
Petros Amoiridis
98ae69a61f Merge pull request #2282 from zed-industries/petros/z-283-make-pop-up-positioning-consistent
Consistent pop-up menu positions
2023-03-20 17:55:40 +02:00
Petros Amoiridis
24bbca7326 Position pane new, split, and dock context menus
Co-Authored-By: Julia <30666851+ForLoveOfCats@users.noreply.github.com>
2023-03-20 17:49:33 +02:00
Nathan Sobo
d429ce0f62 Merge pull request #2308 from zed-industries/feedback-icon
Use an envelope as the feedback icon so we can use the speech bubble for discussions
2023-03-20 09:14:34 -06:00
Nathan Sobo
10e6c5b651 Use an envelope as the feedback icon
This makes room to use the speech bubble for discussion threads.
2023-03-20 09:04:30 -06:00
Antonio Scandurra
9970e5f60c Start on randomized test and add SuggestionMapSnapshot::chunks 2023-03-20 15:56:15 +01:00
Antonio Scandurra
fb48854e5a Simplify signature of SuggestionMap::replace 2023-03-20 14:00:14 +01:00
Antonio Scandurra
83051f1e86 Add SuggestionMap::replace 2023-03-20 13:50:14 +01:00
Antonio Scandurra
94a9e28e35 Start on SuggestionMap 2023-03-20 13:22:14 +01:00
Mikayla Maki
2a024a255f Merge pull request #2307 from zed-industries/fix-panic-in-editor-tab-content
Do UTF8-aware truncation on long item names in editor item
2023-03-18 15:49:06 -07:00
Mikayla Maki
436c59d8ef Do UTF8-aware truncation on long item names in editor item 2023-03-18 15:44:23 -07:00
Mikayla Maki
5356ec4730 Merge pull request #2287 from zed-industries/fix-fold-range-finding
Fix code folds with wraps
2023-03-17 17:18:56 -07:00
Mikayla Maki
5a3d5dff42 Make folds tab aware 2023-03-17 17:14:40 -07:00
Mikayla Maki
c39b4ac229 Fix boundary condition in buffer_line_len when at the end of a file
co-authored-by: max <max@zed.dev>
2023-03-17 16:56:44 -07:00
Mikayla Maki
5a1bbb96ba Merge pull request #2302 from zed-industries/fix-dispatch-path-panic
Align dispatch_keystroke with other uses of ancestors iterator
2023-03-17 16:34:00 -07:00
Mikayla Maki
b16e53a577 Merge pull request #2306 from zed-industries/fix-panic-with-multi-line-env
Parse user enviroment using null terminators instead of newlines
2023-03-17 16:18:05 -07:00
Mikayla Maki
109e17b4b2 Parse user enviroment using null terminators instead of newlines 2023-03-17 16:14:07 -07:00
Mikayla Maki
eba119b914 Fix fold tests with new representation
Switch UI code from using display rows to using buffer rows
Make folds only show up on lines with line layouts

co-authored-by: Max <max@zed.dev>
2023-03-17 16:00:22 -07:00
Max Brunsfeld
fc828971f1 collab 0.8.2 2023-03-17 15:00:31 -07:00
Max Brunsfeld
691383ca68 Merge pull request #2305 from zed-industries/faster-access-token-validation
Faster access token validation
2023-03-17 14:56:30 -07:00
Max Brunsfeld
b8e8363a72 Add logging and metric for time spent hashing auth tokens
Co-authored-by: Mikayla Maki <mikayla@zed.dev>
2023-03-17 14:32:13 -07:00
Max Brunsfeld
623133ffa0 Reduce scrypt work factor to speed up websocket authentication
Co-authored-by: Mikayla Maki <mikayla@zed.dev>
2023-03-17 14:31:39 -07:00
Max Brunsfeld
9633a4b527 Return a 400, not a 500 when token validation fails
Co-authored-by: Antonio Scandurra <antonio@zed.dev>
2023-03-17 13:56:12 -07:00
Julia
368a6405a1 Increase git diff gutter width slightly 2023-03-17 15:28:38 -04:00
Mikayla Maki
459e320d79 Merge pull request #2303 from zed-industries/add-unknown-language
Add an 'Unknown' state for a mouse-driven way to select a file language
2023-03-17 11:41:40 -07:00
Mikayla Maki
04f52c3d50 Show active buffer language in all cases 2023-03-17 11:34:17 -07:00
Max Brunsfeld
26dae3c04e Lookup access tokens by id when authenticating a connection
This avoids the cost of hashing an access token multiple times,
to compare it to all known access tokens for a given user.

Co-authored-by: Antonio Scandurra <antonio@zed.dev>
2023-03-17 11:13:50 -07:00
Mikayla Maki
578c69476d Align dispatch_keystroke with other uses of ancestors iterator and filter out non-existant views 2023-03-17 11:07:10 -07:00
Mikayla Maki
1125a168f4 Merge pull request #2301 from zed-industries/fix-file-finder-panic
Never call set_selected_index with an invalid index
2023-03-17 10:18:27 -07:00
Antonio Scandurra
d8758658e3 Merge pull request #2300 from zed-industries/ligatures
Allow customization of OpenType features
2023-03-17 17:34:17 +01:00
Mikayla Maki
f7f9b8cffe Never call set_selected_index with an invalid index 2023-03-17 09:32:01 -07:00
Antonio Scandurra
1af8f4be19 Deserialize Theme directly into the heap to avoid stack overflow
Co-Authored-By: Julia Risley <julia@zed.dev>
2023-03-17 15:58:52 +01:00
Antonio Scandurra
786d95b8c8 Avoid storing fonts::Features in TextStyle
We were only using it for debugging purposes and that was causing
the `Theme` struct to become too big to hold on the stack.

Co-Authored-By: Julia Risley <julia@zed.dev>
2023-03-17 15:12:02 +01:00
Antonio Scandurra
4d915f4530 Don't make fonts::Features Copy 2023-03-17 13:54:56 +01:00
Antonio Scandurra
989c9f0196 Mention calt: false in the default settings to disable ligatures 2023-03-17 13:48:34 +01:00
Antonio Scandurra
f9d793cb4a Honor more OpenType features 2023-03-17 13:40:00 +01:00
Petros Amoiridis
3bddf01962 Run prettier to format things 2023-03-17 14:05:49 +02:00
Petros Amoiridis
86ed5b8b83 Position contacts and user menus
Using the new approach for consistency
2023-03-17 14:05:46 +02:00
Antonio Scandurra
9181ac9872 Honor the calt font feature 2023-03-17 12:01:27 +01:00
Antonio Scandurra
76167ca65c Allow setting font features on TextStyle 2023-03-17 11:49:22 +01:00
Antonio Scandurra
7d13b00914 Allow setting font features on the terminal as well 2023-03-17 11:42:24 +01:00
Antonio Scandurra
b2c733baab WIP: Allow specifying font features in the editor
This just lays the foundation for threading through a `fonts::Features`
struct, but it's not used yet.
2023-03-17 09:51:36 +01:00
Joseph Lyons
6eb65eb989 Correct verb tense in default settings 2023-03-17 00:51:03 -04:00
Julia
3464961aa4 Merge pull request #2298 from zed-industries/fix-deadlock
Fix deadlock while initializing JSON language server
2023-03-16 17:41:54 -04:00
Julia
757f05042d Fix deadlock while initializing JSON language server
As it turns out both parking-lot and std's `RwLock` disallows taking
multiple read locks on the same thread

Co-Authored-By: Max Brunsfeld <max@zed.dev>
2023-03-16 17:32:51 -04:00
Max Brunsfeld
9633732db7 collab 0.8.1 2023-03-16 14:21:35 -07:00
Max Brunsfeld
e34d80cff4 Merge pull request #2296 from zed-industries/tx-serialization-retry-delay
Introduce a delay before retrying a transaction after a serialization failure
2023-03-16 14:16:20 -07:00
Mikayla Maki
f2492666d4 Merge pull request #2297 from zed-industries/fix-random-panics
WIP: Fix random panics
2023-03-16 13:20:54 -07:00
Joseph T. Lyons
b3b20e4c46 Merge pull request #2295 from zed-industries/swap-atom-keybinding-for-CollapseSelectedEntry
Swap atom keybinding for CollapseSelectedEntry
2023-03-16 16:12:28 -04:00
Max Brunsfeld
b9bc66aa9b Log the delay when retrying a transaction 2023-03-16 13:07:38 -07:00
Max Brunsfeld
35280f7d80 Introduce a delay before retrying a transaction after a serialization failure
Co-authored-by: Antonio Scandurra <antonio@zed.dev>
2023-03-16 13:07:38 -07:00
Mikayla Maki
6571555c4d Fix unwrap in git2 library causing panics in Zed 2023-03-16 13:06:23 -07:00
Joseph Lyons
a252c2a15b Swap atom keybinding for CollapseSelectedEntry
This is a temporary solution to this bug:

https://linear.app/zed-industries/issue/Z-340/the-project-panel-shouldnt-be-listening-to-key-commands-when-editing-a
2023-03-16 15:59:14 -04:00
Mikayla Maki
c3325430ca Fix divide by 0 in terminal
Fix fail to remove contact in contact list
2023-03-16 12:31:50 -07:00
Julia
1fbdea6a03 Merge pull request #2294 from zed-industries/remove-flag-old-npm-does-not-like
Remove CLI flag which old versions of NPM do not like
2023-03-16 13:11:45 -04:00
Julia
24dba2157f Remove CLI flag which old versions of NPM do not like
TODO: Bundle or version restrict Node
2023-03-16 13:07:09 -04:00
Mikayla Maki
c427a8c584 WIP - DEBUGGING 2023-03-16 08:41:19 -07:00
Antonio Scandurra
356b8c6980 Merge pull request #2293 from zed-industries/yaml-hover-bug
Fix hover popover rendering lots of `&emsp` for YAML
2023-03-16 16:15:14 +01:00
Antonio Scandurra
9498f02f2c Retrieve workspace configuration before initializing language server 2023-03-16 15:01:31 +01:00
Antonio Scandurra
f5a4c6a7c1 Provide editor.tabSize in workspace configuration for YAML
This fixes a bug that caused the hover popover to display lots of
`&emsp;` occurrences.
2023-03-16 10:46:55 +01:00
Antonio Scandurra
88e664bfd9 Add test for language registration and loading 2023-03-16 10:46:55 +01:00
Antonio Scandurra
8a685fa52a Use LanguageRegistry::workspace_configuration everywhere 2023-03-16 10:46:55 +01:00
Antonio Scandurra
4d52fc0d12 Remove available language only when it has loaded
This also ensures that, if you load the same language more than once,
a future that resolves to the language (or an error) is returned at
all times. Previously, we would only return it the first time the language
was loaded.
2023-03-16 10:46:55 +01:00
Antonio Scandurra
a8ac08f5bd Coalesce multiple RwLocks into one LanguageRegistryState struct 2023-03-16 10:46:55 +01:00
Antonio Scandurra
e30ea43a14 Include loaded languages when computing lsp workspace configuration 2023-03-16 10:46:55 +01:00
Antonio Scandurra
60d3fb48e2 Start computing workspace configuration more dynamically 2023-03-16 10:46:55 +01:00
Max Brunsfeld
ed9927b495 Merge pull request #2292 from zed-industries/restart-app
Make 'restart' action more reliable
2023-03-15 18:01:22 -07:00
Max Brunsfeld
d69868fa44 Make restart action more reliable 2023-03-15 17:44:50 -07:00
Julia
1ed3aedb16 Merge pull request #2291 from zed-industries/change-LSHandlerRank
Change `LSHandlerRank` to `Alternate`
2023-03-15 19:33:33 -04:00
Julia
905e2586e9 Change LSHandlerRank to Alternate 2023-03-15 19:18:39 -04:00
Max Brunsfeld
51eb53be0d Merge pull request #2290 from zed-industries/close-remote-projects-when-leaving-call
Close remote project windows when leaving a call
2023-03-15 15:34:51 -07:00
Max Brunsfeld
b34477458e Close remote project windows when leaving a call 2023-03-15 15:24:58 -07:00
Nathan Sobo
385dfe1661 Merge pull request #2289 from zed-industries/sort-language-names-case-agnostically
Sort language names case agnostically
2023-03-14 20:00:52 -06:00
Joseph Lyons
3c7237e600 Sort language names case agnostically 2023-03-14 21:45:17 -04:00
Nathan Sobo
44a2506c40 Merge pull request #2288 from zed-industries/cut-off-collaborator-avatars
Fix collaborator avatars being clipped and not centered
2023-03-14 19:39:01 -06:00
Max Brunsfeld
c4e7611d04 Fix collaborator avatars being clipped and not centered
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Julia Risley <julia@zed.dev>
2023-03-14 17:59:33 -07:00
Mikayla Maki
75bea91245 Convert code folding to be in terms of buffer points instead of display points
Co-authored-by: max <max@zed.dev>
2023-03-14 16:48:03 -07:00
Max Brunsfeld
828e9c1bb8 v0.79.x dev 2023-03-14 12:49:33 -07:00
Max Brunsfeld
2042188f5a Merge pull request #2286 from zed-industries/discoverable-sign-in
Make sign-in more discoverable
2023-03-14 11:08:18 -07:00
Max Brunsfeld
0bbb4b22c6 Rename 'Authenticate' action to 'Sign In'
Co-authored-by: Antonio Scandurra <antonio@zed.dev>
2023-03-14 11:01:23 -07:00
Max Brunsfeld
75901f1c33 Show sign in button directly in titlebar when not signed in
Co-authored-by: Antonio Scandurra <antonio@zed.dev>
2023-03-14 10:59:39 -07:00
Antonio Scandurra
a6ebc9bd26 collab 0.8.0 2023-03-14 18:21:16 +01:00
Antonio Scandurra
9e3085b0c4 Merge pull request #2284 from zed-industries/automatic-user-creation
Create user record automatically when someone logs in on the website
2023-03-14 18:20:13 +01:00
Mikayla Maki
7af9dda869 Merge pull request #2285 from zed-industries/fix-failed-theme-setting
Fix failed theme setting
2023-03-14 10:10:39 -07:00
Mikayla Maki
2a5ac4f203 Merge pull request #2283 from zed-industries/fix-code-fold-indicator-scaling
Change fold icon width to scale with font size
2023-03-14 10:07:25 -07:00
Mikayla Maki
d8a3f16891 Refactor load into a seperate function 2023-03-14 09:56:27 -07:00
Mikayla Maki
99257a8213 Fix failed initialization of setting file in welcome experience 2023-03-14 09:46:28 -07:00
Antonio Scandurra
0f429243d7 Fix seed binary 2023-03-14 12:37:56 +01:00
Antonio Scandurra
cba41ef7c5 Create user record automatically when someone logs in on the website
Now that we are moving out of the private alpha, we should let everyone
in when they try to log into zed.dev.
2023-03-14 12:25:04 +01:00
Mikayla Maki
2ba38b2fca Change icon width to scale with font size 2023-03-14 00:42:15 -07:00
Petros Amoiridis
e7f78c4f74 Merge pull request #2281 from zed-industries/petros/z-298-the-terminal-button-is-not-shown-for
Remove check for staffmode
2023-03-14 00:12:13 -07:00
Petros Amoiridis
8980df1f5d Remove feature flag
It wasn't working in production anyway
2023-03-14 09:05:04 +02:00
Nathan Sobo
2db8ac4a6f Merge pull request #2280 from zed-industries/terms
Add terms of use to DMG in the bundle script
2023-03-13 16:08:07 -06:00
Nathan Sobo
818a514110 Add terms of use to DMG in the bundle script
Co-Authored-By: Max Brunsfeld <max@zed.dev>
2023-03-13 15:47:59 -06:00
Mikayla Maki
1b4f783b97 Merge pull request #2279 from zed-industries/remove-invite-link
Remove UI for invite link in prep for beta launch
2023-03-13 14:35:52 -07:00
Mikayla Maki
88599add56 Remove UI for invite link in prep for beta launch 2023-03-13 12:54:20 -07:00
Max Brunsfeld
05f6747132 v0.78.x dev 2023-03-13 12:00:56 -07:00
Max Brunsfeld
1096720b41 Merge pull request #2244 from zed-industries/less-surprising-defaults
Clean up some default settings
2023-03-13 11:54:46 -07:00
Julia
5c7c4dd4dd Clean up some default settings 2023-03-13 14:54:28 -04:00
Max Brunsfeld
da35202bbf Merge pull request #2278 from zed-industries/screenshares-from-before-joining
Fix failure to see screenshare tracks that were started prior to join…
2023-03-13 11:22:40 -07:00
Max Brunsfeld
f5c4a2a0dd Fix failure to see screenshare tracks that were started prior to joining a call
Co-authored-by: Antonio Scandurra <antonio@zed.dev>
2023-03-13 11:15:22 -07:00
Mikayla Maki
77a63c6598 Merge pull request #2277 from zed-industries/switch-invite-person-icon
Update Collab + icon
2023-03-13 10:54:33 -07:00
Nate Butler
edd925f77b Format 2023-03-13 13:49:00 -04:00
Nate Butler
6d0f8290a4 Merge pull request #2276 from zed-industries/improve-picker-state-contrast
Improve picker state contrast
2023-03-13 13:48:04 -04:00
Nate Butler
6497ca8ccb Update icon 2023-03-13 13:47:58 -04:00
Max Brunsfeld
e60dea7049 collab 0.7.2 2023-03-13 10:11:44 -07:00
Nate Butler
e64fe6d660 Improve contrast of project panel active state in light themes 2023-03-13 13:10:46 -04:00
Antonio Scandurra
f6f09e8661 Merge pull request #2274 from zed-industries/leave-on-quit
Leave room on quit
2023-03-13 18:08:01 +01:00
Max Brunsfeld
ef7d8f46df Merge pull request #2273 from zed-industries/fix-dock-opening-in-collaboration
Fix dock opening on collaboration
2023-03-13 10:04:43 -07:00
Nate Butler
7df2440757 Format 2023-03-13 12:59:23 -04:00
Nate Butler
6fd4e28813 Update picker active and hover styles
Thanks for pointing this out @ForLoveOfCats

Co-Authored-By: Julia <30666851+ForLoveOfCats@users.noreply.github.com>
2023-03-13 12:59:17 -04:00
Antonio Scandurra
bca1acf6d3 Leave room on quit
Co-Authored-By: Max Brunsfeld <max@zed.dev>
2023-03-13 17:52:10 +01:00
Mikayla Maki
097a768725 Remove panic in recent projects picker 2023-03-13 09:44:42 -07:00
Mikayla Maki
404dd43c30 Fix dock opening on collaboration 2023-03-13 09:36:45 -07:00
Petros Amoiridis
c6f27903cc Merge pull request #2271 from zed-industries/petros/z-278-lock-pop-up-to-the-terminal-button
A new approach for positioning pop-up menus tied to an icon/button
2023-03-13 08:46:44 -07:00
Antonio Scandurra
f6b0c56a47 Merge pull request #2268 from zed-industries/mention-username-of-user-in-prompt-when-removing-a-contact
Mention username of user in prompt when removing a contact
2023-03-13 14:28:11 +01:00
Antonio Scandurra
e45d680126 Merge pull request #2272 from zed-industries/add-textmate-keymap
Add TextMate keymap
2023-03-13 14:26:32 +01:00
Antonio Scandurra
e993d32900 Merge pull request #2270 from zed-industries/add-default-keybindings-for-reveal-in-finder
Add default keybindings for reveal in finder
2023-03-13 14:25:26 +01:00
Antonio Scandurra
09911d43bc Merge pull request #2269 from zed-industries/unify-action-names-with-keybinding-names
Unify action names with keybinding name
2023-03-13 14:24:42 +01:00
Joseph Lyons
c8696149b8 Add TextMate keymap 2023-03-13 08:27:04 -04:00
Petros Amoiridis
db73d831c7 Use local overlay position mode and alignment
When we show the context menu, we don't specify an explicit position other than the default one which is equal to the origin in the context of a local overlay position mode. We then rely on `AnchorCorner` and aligning the context menu child view in the stack with `top().right()` for pop-ups that need to appear at the top of the icon/button.

Co-Authored-By: Antonio Scandurra <me@as-cii.com>
2023-03-13 12:50:12 +02:00
Petros Amoiridis
d2411a6c86 Merge pull request #2253 from zed-industries/joseph/z-226-add-terminal-popup-menu
Add terminal pop up menu
2023-03-13 02:47:44 -07:00
Petros Amoiridis
726c8eb43f Use type_id to determine what has the focus
Co-Authored-By: Antonio Scandurra <me@as-cii.com>
2023-03-13 11:42:40 +02:00
Joseph Lyons
c59dafab7e Update method names to match action names 2023-03-12 06:08:12 -04:00
Joseph Lyons
e272a1a18f Switch github_login to String
One of our macros, `impl_actions!`, didn't like that `RemoveContact` had a lifetime.
2023-03-12 05:03:31 -04:00
Joseph Lyons
90bca1b94a Add default keybindings for reveal in finder 2023-03-12 04:58:17 -04:00
Joseph Lyons
06ad3a7f7b Unify action names with keybinding name 2023-03-12 04:37:39 -04:00
Joseph Lyons
c18f1b6246 Mention username of user in prompt when removing a contact 2023-03-11 19:28:35 -05:00
Joseph Lyons
46efb844af Remove hardcoding of "Terminal" string 2023-03-11 16:27:00 -05:00
Petros Amoiridis
29f0078084 Show tooltip for Give Feedback icon 2023-03-11 17:40:47 +02:00
Petros Amoiridis
432aeeac56 Remove comment
I know @JosephTLyons you had added that, but I am not entirely sure this is how the view should be called. Let's discuss this further if you fill strong about it.
2023-03-11 17:22:21 +02:00
Petros Amoiridis
8440a98850 Activate terminal button when a terminal is focus
This is code I brought over from https://github.com/zed-industries/zed/pull/2267 by @mikayla-maki after fixing the conflicts.

Co-Authored-By: Mikayla Maki <mikayla.c.maki@gmail.com>
2023-03-11 16:45:28 +02:00
Petros Amoiridis
bccc34c61a Merge branch 'main' into joseph/z-226-add-terminal-popup-menu 2023-03-11 16:42:42 +02:00
Max Brunsfeld
e8b3d4e0fa Encode db-max-connections env var as a string in k8s manifest 2023-03-10 17:19:16 -08:00
Max Brunsfeld
ff1c7db38f collab 0.7.1 2023-03-10 16:36:03 -08:00
Mikayla Maki
30a08467b0 Merge pull request #2263 from zed-industries/language-status-bar
Language status bar
2023-03-10 16:26:42 -08:00
Mikayla Maki
c8de738972 Align feedback button styles with other sidebar buttons
Make feedback button reflect whether you're in a feedback buffer
2023-03-10 16:19:33 -08:00
Mikayla Maki
87ac409e51 Merge pull request #2265 from zed-industries/fix-menu-bar-greying-out
Fix too-agressive menu bar greying
2023-03-10 16:05:07 -08:00
Mikayla Maki
badfe70a93 Merge pull request #2267 from zed-industries/fix-terminal-icon-active-state
Activate and deactivate the terminal icon based on focused view
2023-03-10 16:02:34 -08:00
Mikayla Maki
11d8394af2 Fix the terminal icon button to only be active when a terminal is focused 2023-03-10 15:54:42 -08:00
Mikayla Maki
c24194156e Merge pull request #2257 from zed-industries/check-all-contexts
Fix keyboard shortcuts not showing
2023-03-10 15:52:56 -08:00
Mikayla Maki
ece2af1e22 Fix a corner case in available action resolution
Add tests for new keybinding resolution behavior

co-authored-by: max <max@zed.dev>
2023-03-10 15:36:20 -08:00
Mikayla Maki
adf94a1681 Switch from using the key window to the main window mac platform API
When the help menu is open, the help menu's search field is the key window, and this was causing menu item action resolution to fail

co-authored-by: Max <max@zed.dev>
2023-03-10 14:43:28 -08:00
Julia
09d306df85 Merge pull request #2266 from zed-industries/avoid-panic
Avoid panic scanning recursive symlink before gitignore is encountered
2023-03-10 17:19:55 -05:00
Julia
0a5cf4b831 Avoid panic scanning recursive symlink before gitignore is encountered 2023-03-10 16:54:57 -05:00
Mikayla Maki
9398de6a57 Add feedback for telemetry item when there's no data 2023-03-10 13:15:36 -08:00
Mikayla Maki
e45104a1c0 Move feedback to overflow menu and help menu 2023-03-10 12:48:11 -08:00
Mikayla Maki
74b10e4ba5 Merge pull request #2264 from zed-industries/make-terminal-default-better
Make terminal fallback correctly when unable to deserialize a cwd
2023-03-10 12:27:54 -08:00
Mikayla Maki
ddbffd2c41 Make terminal fallback correctly when unable to deserialize your cwd 2023-03-10 12:23:29 -08:00
Mikayla Maki
00a38e4c3b Bound the search range for the keybindings by the highest handler path 2023-03-10 12:12:32 -08:00
Mikayla Maki
37d01c7fb3 Merge pull request #2199 from zed-industries/welcome-experience
Welcome experience
2023-03-10 10:48:30 -08:00
Mikayla Maki
281ff92236 Stub out blank pane experience 2023-03-10 09:58:11 -08:00
Antonio Scandurra
bb721a08f5 💄 2023-03-10 17:43:48 +01:00
Antonio Scandurra
f50b51bdad Adjust styling 2023-03-10 17:37:45 +01:00
Antonio Scandurra
693172854c Show active buffer's language on the right in the status bar 2023-03-10 17:37:34 +01:00
Antonio Scandurra
b3c7526fb5 Return last excerpt in MultiBuffer::excerpt_containing if overshooting 2023-03-10 17:28:08 +01:00
Antonio Scandurra
6e37ff880f Replace "Give Feedback" with an icon and move it to the left
This is so we can show the current language in the status bar on
the right, and having two pieces of text sitting next to each other
felt too busy.

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-03-10 17:02:52 +01:00
Antonio Scandurra
f08685f65f Merge pull request #2262 from zed-industries/manual-language-selection
Introduce language selector
2023-03-10 16:38:17 +01:00
Antonio Scandurra
ce828d55d5 Bind language_selector::Toggle to cmd-k m
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-03-10 16:32:18 +01:00
Antonio Scandurra
f28806d09b Emphasize currently-selected language 2023-03-10 15:48:39 +01:00
Antonio Scandurra
686f5439ad Set buffer language when confirming selection in language selector
Co-Authored-By: Julia Risley <julia@zed.dev>
2023-03-10 15:34:26 +01:00
Antonio Scandurra
b402f27d50 Introduce a new language picker that displays available languages
Right now this panics when trying to select a language, so that's
what we're going to implement next.

Co-Authored-By: Julia Risley <julia@zed.dev>
2023-03-10 15:28:10 +01:00
Antonio Scandurra
d39c761de5 Merge pull request #2261 from zed-industries/async-language-loading
Allow waiting for language to be loaded in LanguageRegistry APIs
2023-03-10 15:27:08 +01:00
Antonio Scandurra
7a600e7a65 Allow waiting for language to be loaded in LanguageRegistry APIs 2023-03-10 12:17:47 +01:00
Antonio Scandurra
221bb54e48 Introduce a new TryFutureExt::unwrap method 2023-03-10 11:41:13 +01:00
Antonio Scandurra
431e11a033 Merge pull request #2260 from zed-industries/increase-max-connections
Increase the amount of max connections to the database
2023-03-10 10:27:26 +01:00
Antonio Scandurra
8b7273e46e Increase the amount of max connections to the database 2023-03-10 10:10:59 +01:00
Mikayla Maki
9dc608dc4b Switch from changing the meaning of the predicate to adding an additional match_dispatch_path_context API for UI elements 2023-03-09 19:32:09 -08:00
Mikayla Maki
648f0e5b7b Remove new logo from style tree 2023-03-09 19:18:17 -08:00
Julia
b40ea4df14 Merge pull request #2259 from zed-industries/preserve-contacts-editor-contents
Preserve contacts popover editor contents when switching to search mode
2023-03-09 22:10:46 -05:00
Julia
01e3173ed0 Preserve contacts popover editor contents when switching to search mode 2023-03-09 22:05:32 -05:00
Nate Butler
8ee25be7b9 Update empty pane styling
Co-Authored-By: Mikayla Maki <mikayla.c.maki@gmail.com>
2023-03-09 20:18:29 -05:00
Mikayla Maki
0384456e7d Change context matcher to search the entire stack 2023-03-09 16:18:37 -08:00
Mikayla Maki
20064b5629 Add welcome to menu
remove debug
2023-03-09 15:38:28 -08:00
Mikayla Maki
daed75096e Fix editor test to clearly show trailing whitespace
Adjsut default dock size to be a multiple of 16
2023-03-09 13:18:03 -08:00
Mikayla Maki
718052bb72 Undo accidental indent change of hoverpopover.ts 2023-03-09 13:02:25 -08:00
Mikayla Maki
4eb75f058f Fix bug with wrong view ids being passed 2023-03-09 12:00:58 -08:00
Petros Amoiridis
6c68a3e709 Remove unneeded code 2023-03-09 20:02:40 +02:00
Nate Butler
e7af3f223a Merge pull request #2254 from zed-industries/nate/update-themes
WIP: More theme updates
2023-03-09 11:00:20 -05:00
Nate Butler
baff428de5 Re-add Ayu 2023-03-09 10:51:28 -05:00
Nate Butler
3952e98320 Remove Zed Pro theme 2023-03-09 10:50:11 -05:00
Nate Butler
51be0efa1f Format 2023-03-09 10:47:58 -05:00
Nate Butler
5bfd5e35b3 Add light themes 2023-03-09 10:47:01 -05:00
Petros Amoiridis
d53c18cc57 Open menu relative to the mouse cursor 2023-03-09 16:57:38 +02:00
Petros Amoiridis
5b7d0ee6fe Show button in a normal state 2023-03-09 16:54:47 +02:00
Petros Amoiridis
e2bdd261a1 Remove debugging statement 2023-03-09 16:53:34 +02:00
Mikayla Maki
9187863d0e re-add spaces removed by new setting 2023-03-09 00:45:05 -08:00
Antonio Scandurra
3daeabc1d6 collab 0.7.0 2023-03-09 09:30:04 +01:00
Antonio Scandurra
bebfe53e89 Merge pull request #2256 from zed-industries/liveness-probe
Introduce Kubernetes liveness probe to ensure database works
2023-03-09 09:26:21 +01:00
Antonio Scandurra
9328bb0153 Introduce Kubernetes liveness probe to ensure database works 2023-03-09 09:17:55 +01:00
Antonio Scandurra
89c283ecf0 Merge pull request #2252 from zed-industries/limit-messages-processed-in-parallel
Prevent collab server from being overwhelmed with messages
2023-03-09 08:51:48 +01:00
Antonio Scandurra
a00ce3f286 Add randomized test to remove active selections from buffer 2023-03-09 07:47:05 +01:00
Antonio Scandurra
4ce51c8138 Limit dirty buffer save optimization to multi-buffers 2023-03-09 07:26:22 +01:00
Mikayla Maki
f626920af1 Remove permanent Zed stateless 2023-03-08 19:03:50 -08:00
Mikayla Maki
325827699e Adjust styling for blank page experience 2023-03-08 19:02:13 -08:00
Julia
a3b1980a5e Merge pull request #2255 from zed-industries/sort-collaborators-alphabetical
Sort collaborators in titlebar alphabetically instead of by replica id
2023-03-08 21:54:29 -05:00
Mikayla Maki
709c101834 Adjust styles on usage note 2023-03-08 18:50:17 -08:00
Mikayla Maki
943ea61452 Add a note on how to check the telemetry 2023-03-08 18:47:52 -08:00
Julia
981b3a459f Sort collaborators in titlebar alphabetically instead of by replica id 2023-03-08 21:38:30 -05:00
Mikayla Maki
a65dd0fd98 Restore correct checkbox text 2023-03-08 18:15:29 -08:00
Mikayla Maki
cf6ea6d698 Fix bug with action keybindings not being resolved 2023-03-08 18:11:11 -08:00
Mikayla Maki
152755b043 Add blank pane experience 2023-03-08 17:56:39 -08:00
Mikayla Maki
dad66eb3fb Make the workspace always open the dock 2023-03-08 14:49:52 -08:00
Mikayla Maki
f62e0b502a Remove welcome experience action
Make logo switch between light and dark

co-authored-by: Nathan <nathan@zed.dev>
2023-03-08 14:49:48 -08:00
Nate Butler
344f59adf7 Tweak welcome design
Co-Authored-By: Mikayla Maki <mikayla.c.maki@gmail.com>
2023-03-08 17:14:15 -05:00
Nate Butler
cc33f83e4e Add Zed logo icon
Co-Authored-By: Mikayla Maki <mikayla.c.maki@gmail.com>
2023-03-08 16:45:35 -05:00
Nate Butler
9842b7ad1a WIP
Co-Authored-By: Mikayla Maki <mikayla.c.maki@gmail.com>
2023-03-08 16:34:27 -05:00
Max Brunsfeld
14497027d4 collab 0.6.2 2023-03-08 12:22:16 -08:00
Joseph Lyons
ae510c80db v0.77.x dev 2023-03-08 13:25:32 -05:00
Petros Amoiridis
ad7e49ed06 Give focus to the selected terminal
Co-Authored-By: Max Brunsfeld <maxbrunsfeld@gmail.com>
2023-03-08 19:47:57 +02:00
Antonio Scandurra
b687aec9d9 Avoid saving buffer if it's neither dirty nor in conflict
However, keep emitting `Saved` events so that the language server
is notified and can perform tasks related to saving (e.g., running
`cargo check` in the case of rust-analyzer).
2023-03-08 17:04:01 +01:00
Antonio Scandurra
a435dc1339 Clear selections on buffer only if they hadn't been cleared already 2023-03-08 17:04:01 +01:00
Antonio Scandurra
b4561b848d Limit the number of parallel messages handled for any given connection 2023-03-08 17:04:01 +01:00
Petros Amoiridis
baa9e271d5 Make pop up open on the side of the button 2023-03-08 11:00:30 +02:00
Mikayla Maki
350ddf2025 Add keymap picker UI
Co-authored-by: Max <max@zed.dev>
2023-03-07 17:29:08 -08:00
Mikayla Maki
3594243644 Fix bug where open would offer to hang up a remote call
Co-authored-by: max <max@zed.dev>
2023-03-07 17:29:08 -08:00
Mikayla Maki
904993dfc9 Change open paths to replace the existing window if dispatched from a window
co-authored-by: Max <max@zed.dev>
2023-03-07 17:28:52 -08:00
Julia
4179ed66a6 Merge pull request #2251 from zed-industries/clear-follow-state-on-project-close
Update db followers table when user leaves a project
2023-03-07 19:09:09 -05:00
Julia
d173b1d412 Update db followers table when user leaves a project
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2023-03-07 18:56:03 -05:00
Mikayla Maki
ab4b3293d1 Fix project panel button and style it
Co-authored-by: max <max@zed.dev>
2023-03-07 14:49:13 -08:00
Mikayla Maki
5892f16602 Add test for base keymap setting 2023-03-07 14:02:42 -08:00
Nate Butler
84aefb9dcb Add the rest of the Atelier dark themes 2023-03-07 13:47:18 -08:00
Nate Butler
4e81513af1 Add more Atelier themes 2023-03-07 13:35:32 -08:00
Nate Butler
90296667b0 Remove current staff themes 2023-03-07 13:35:23 -08:00
Nate Butler
e0f9b2b40f Update Atelier Dune 2023-03-07 13:27:01 -08:00
Nate Butler
477453c396 Update existing Atelier themes 2023-03-07 13:22:07 -08:00
Mikayla Maki
19fc143209 Add base keymap setting
Format all files

Co-Authored-by: Nathan <nathan@zed.dev>
2023-03-07 12:19:51 -08:00
Joseph Lyons
ca03d871a6 Add a separator between the New Terminal button and existing terminal buttons 2023-03-07 15:09:06 -05:00
Joseph Lyons
0a3f0c5252 Use terminal titles for buttons 2023-03-07 15:04:12 -05:00
Joseph Lyons
c80942ea00 Begin work to dynamically add terminal names to menu 2023-03-07 14:41:18 -05:00
Joseph Lyons
caa6a75238 Show a pop up menu for terminals
Co-Authored-By: Joseph T. Lyons <19867440+JosephTLyons@users.noreply.github.com>
Co-Authored-By: Antonio Scandurra <me@as-cii.com>
2023-03-07 20:16:08 +02:00
Julia
4f4af55329 Merge pull request #2248 from zed-industries/increase-reconnect-timeout
Increase room reconnect timeout
2023-03-07 12:30:20 -05:00
Julia
1e5aff9e51 Update collab integration test to new reconnect timeout 2023-03-07 12:23:18 -05:00
Nate Butler
ee154feda4 Merge pull request #2231 from zed-industries/nate/add-gruvbox
Add Gruvbox Theme
2023-03-06 18:49:26 -08:00
Mikayla Maki
3b31f10c6f Made the theme picker sort from dark to light
Added a layer into 'ConstrainedBox' to clip it
's children

Made the welcome experience responsive to small and large sizes
2023-03-06 18:36:18 -08:00
Mikayla Maki
8db7e17ac5 Move install_cli function to a seperate crate
Add install cli button to welcome experience
Add toast pop ups for CLI installation status
2023-03-06 17:55:58 -08:00
Mikayla Maki
1f6bd0ea77 Fix edge case where the welcome page might open in the dock if the user's actions race the welcome experience action 2023-03-06 16:35:15 -08:00
Mikayla Maki
ba652fc033 Finish basic welcome experience 2023-03-06 16:28:23 -08:00
Max Brunsfeld
7163ba429b Merge pull request #2250 from zed-industries/syntax-highlighting-tweaks
Syntax highlighting tweaks
2023-03-06 16:05:10 -08:00
Max Brunsfeld
c832e4406e Remove more colors from default syntax theme
These default colors weren't consistent with the rest of the
syntax theme.
2023-03-06 15:57:04 -08:00
Max Brunsfeld
515724821e Make racket highlight query more consistent with other langs 2023-03-06 15:56:20 -08:00
Max Brunsfeld
0867162c87 Fix lua highlight query
* Fix regex predicate on constants.
* Remove 'function.call' highlight name. In other languages, we
differentiate calls from definitions with the 'function.definition'
highlight name. We actually don't use this in any themes though.
2023-03-06 15:54:17 -08:00
Max Brunsfeld
aba2914a31 Fix constant highlighting in JS, TypeScript
Previously, SCREAMING_SNAKE_CASE identifiers were highlighted as
types due to a mistake in the order of patterns in the highlight
queries.
2023-03-06 15:38:12 -08:00
Max Brunsfeld
246a6adab7 Merge pull request #2239 from zed-industries/add-constructor-to-syntax-overrides
Add constructor to syntax overrides
2023-03-06 15:27:09 -08:00
Mikayla Maki
020a0965b0 WIP 2023-03-06 14:26:32 -08:00
Mikayla Maki
b74553455f Add an element to pane to take care of wiring initial mouse handlers 2023-03-06 14:26:32 -08:00
Mikayla Maki
4a8527478d Add child item alignment to flex implementation
Fix checkbox styling

co-authored-by: Nathan <nathan@zed.dev>
2023-03-06 14:26:32 -08:00
Mikayla Maki
4c179875ab Add png image loading to gpui
add zed logo into welcome experience

Co-authored-by: Nathan <nathan@zed.dev>
2023-03-06 14:26:32 -08:00
Mikayla Maki
f89f33347d Added CTA buttons to welcome experience
Co-authored-by: Nathan <nathan@zed.dev>
2023-03-06 14:26:32 -08:00
Mikayla Maki
9dee2ca2be WIP 2023-03-06 14:26:32 -08:00
Mikayla Maki
62aeb6b8b3 Added background to welcome page 2023-03-06 14:26:32 -08:00
Mikayla Maki
5210be95fe Added welcome experience sketch
Made toolbar hideable
2023-03-06 14:26:32 -08:00
Mikayla Maki
7d7053b990 Move to using stateless 2023-03-06 14:26:31 -08:00
Mikayla Maki
118435a348 Added basic styling for checkboxes, yay
Co-authored-by: Max <max@zed.dev>
2023-03-06 14:25:10 -08:00
Mikayla Maki
86e2101592 Added the ability to nested values to the settings file, while preserving user formatting
co-authored-by: max <max@zed.dev>
2023-03-06 14:25:10 -08:00
Mikayla Maki
50586812ec Make generate licenses quieter 2023-03-06 14:25:10 -08:00
Mikayla Maki
416c793076 Start on welcome experience settings 2023-03-06 14:25:10 -08:00
Mikayla Maki
a0637a769c WIP 2023-03-06 14:25:10 -08:00
Mikayla Maki
9401ef223d Add welcome crate and associated types 2023-03-06 14:25:10 -08:00
Julia
620890c411 Merge pull request #2247 from zed-industries/tidy-up-sprite-cache
Tidy up `SpriteCache::render_glyph`
2023-03-06 11:29:51 -05:00
Antonio Scandurra
0ec984f924 Tidy up SpriteCache::render_glyph 2023-03-06 17:08:35 +01:00
Max Brunsfeld
01bbf20962 Merge pull request #2235 from zed-industries/no-panic-uploads-in-debug
Don't upload panic files when running in a PTY
2023-03-04 09:42:46 -08:00
Mikayla Maki
996294ba67 Merge pull request #2246 from zed-industries/fix-lsp-derive-error
Make diagnostic processing order independent
2023-03-04 02:25:47 -08:00
Mikayla Maki
ddf2f2cb0a Update test to use new group ids and new ordering of diagnostics. 2023-03-04 02:21:55 -08:00
Mikayla Maki
bd4d7551a5 Make diagnostic processing order independent
Co-authored-by: max <max@zed.dev>
2023-03-03 16:35:12 -08:00
Julia
5097cf5cb7 Merge pull request #2245 from zed-industries/confirm-restart-unsaved
Confirm restart if prompt-quit is enabled or there are unsaved changes
2023-03-03 16:10:40 -08:00
Julia
13212d274e Confirm restart if prompt-quit is enabled or there are unsaved changes 2023-03-03 16:06:03 -08:00
Julia
b9110c9268 Increase reconnect timeout
Co-Authored-By: Antonio Scandurra <me@as-cii.com>
2023-03-03 13:10:08 -08:00
Joseph T. Lyons
b9573872e1 Merge pull request #2243 from zed-industries/joseph/z-225-track-open-terminals
Keep track of open terminals
2023-03-03 12:55:28 -08:00
Joseph Lyons
3ec71a742d Keep track of open terminals
Co-Authored-By: Petros Amoiridis <petros@hey.com>
Co-Authored-By: Mikayla Maki <mikayla.c.maki@gmail.com>
2023-03-03 12:50:08 -08:00
Mikayla Maki
50682dc685 Merge pull request #2233 from zed-industries/fix-code-folding-initialization
Initialize code fold indicators on buffer startup
2023-03-03 12:30:53 -08:00
Julia
2bca64f13b Merge pull request #2242 from zed-industries/mouse-event-handlers-yes
Fix surprising mouse propagation & avoid focusing tab while closing
2023-03-03 12:29:50 -08:00
Mikayla Maki
606d683f29 Add interactable fold markers
Change fold handlers to be driven by the fold map
Switch to a mouse region based implementation for click regions

Co-authored-by: Max <max@zed.dev>
2023-03-03 12:26:36 -08:00
Julia
ff2e6bc3bd Fix surprising mouse propagation & avoid focusing tab while closing
Co-Authored-By: Antonio Scandurra <me@as-cii.com>
2023-03-03 12:22:58 -08:00
Joseph T. Lyons
218f2fd0fe Merge pull request #2241 from zed-industries/joseph/z-223-add-terminal-button-to-status-bar
Add a terminal button to status bar
2023-03-03 12:15:59 -08:00
Max Brunsfeld
bb0257bbac Merge branch 'main' into no-panic-uploads-in-debug 2023-03-03 12:02:44 -08:00
Joseph Lyons
929ebd7175 Add a terminal button to status bar
Co-Authored-By: Petros Amoiridis <petros@hey.com>
2023-03-03 12:00:41 -08:00
Max Brunsfeld
124aa74b03 Merge pull request #2234 from zed-industries/error-on-combined-injections-in-injections
Fix range relativization when combined injections occur inside of other injections
2023-03-03 11:58:13 -08:00
Petros Amoiridis
a2f75eb031 Merge pull request #2240 from zed-industries/petros/update-setup-instructions
Add missing steps to the setup instructions
2023-03-03 11:47:38 -08:00
Petros Amoiridis
6194c5df16 Add missing steps to the setup instructions 2023-03-03 11:36:26 -08:00
Max Brunsfeld
d14b684237 Merge pull request #2236 from zed-industries/avoid-clobbering-panic-file
Avoid clobbering panic files when they happen at the same time
2023-03-03 11:15:48 -08:00
Max Brunsfeld
7a8cba0544 Merge pull request #2227 from zed-industries/strip-trailing-whitespace
Add settings to normalize whitespace on save
2023-03-03 11:15:32 -08:00
Nate Butler
f1b5bf051a Fornat 2023-03-03 10:58:43 -08:00
Nate Butler
ad4201f768 Hack to fix syntax.constructor causing TS error 2023-03-03 10:58:25 -08:00
Nate Butler
75a9cfdabe Remove leftover log 2023-03-03 10:22:50 -08:00
Julia
3b6f66791f Merge pull request #2238 from zed-industries/subpixel-variant-incorrectly-wrapping
Avoid wrapping to the 0th glyph variant when the 4th should be used
2023-03-03 13:13:34 -05:00
Julia
9311e01271 Avoid wrapping to the 0th glyph variant when the 4th should be used
Co-Authored-By: Antonio Scandurra <me@as-cii.com>
2023-03-03 10:04:44 -08:00
Nate Butler
6d068e926b Merge pull request #2237 from zed-industries/revert-2232-tab-bar-background-focus-drag
Revert "Avoid tab bar background activating an item at the end of a tab drag"
2023-03-03 09:49:55 -08:00
Nate Butler
6854063d0b Revert "Avoid tab bar background activating an item at the end of a tab drag" 2023-03-03 09:47:58 -08:00
Mikayla Maki
7ca0b38048 Made fold inline styles be driven by the fold map
co-authored-by: Max <max@zed.dev>
2023-03-03 09:32:58 -08:00
Max Brunsfeld
a598f0b13c Avoid clobbering panic files when they happen at the same time
Co-authored-by: Antonio Scandurra <antonio@zed.dev>
2023-03-02 20:51:29 -08:00
Max Brunsfeld
eb6088701e Don't upload panic files when running in a PTY 2023-03-02 20:32:49 -08:00
Mikayla Maki
24ba47e75d Merge pull request #2230 from zed-industries/fix-tab-x
Fix tab bar x
2023-03-02 19:48:08 -08:00
Mikayla Maki
3dd5b3f426 Attempted to initialize code-fold indicators, does not work 2023-03-02 19:42:59 -08:00
Mikayla Maki
9f86ca8574 Update tabBar.ts 2023-03-02 15:48:31 -08:00
Julia
bc2ea58c6a Merge pull request #2232 from zed-industries/tab-bar-background-focus-drag
Avoid tab bar background activating an item at the end of a tab drag
2023-03-02 16:18:04 -05:00
Julia
b343e8056a Avoid tab bar background activating an item at the end of a tab drag 2023-03-02 13:06:58 -08:00
Mikayla Maki
6a2a1303c4 Fix failing license
co-authored-by: nate <nate@zed.dev>
2023-03-02 12:32:47 -08:00
Max Brunsfeld
a366ba19af Fix range relativization when combined injections occur inside of other injections
For example, ERB template inside of a markdown code block

Co-authored-by: Antonio Scandurra <antonio@zed.dev>
2023-03-01 14:38:35 -08:00
Max Brunsfeld
70cb2fa8d7 Apply external command formatting if buffer has changed while computing it 2023-03-01 10:17:04 -08:00
Nate Butler
f67c3f1f1d Add Gruvbox syntax overrides 2023-03-01 11:49:13 -05:00
Nate Butler
bde0456111 Init gruvbox theme 2023-03-01 10:16:45 -05:00
Mikayla Maki
8734bd8435 Seperate out x-mark width 2023-03-01 00:18:45 -08:00
Mikayla Maki
34fbffb4cc Fix tab bar x 2023-02-28 22:48:31 -08:00
Max Brunsfeld
368d2a73ea Perform whitespace formatting regardless of whether buffer has a language server or path 2023-02-28 21:52:00 -08:00
Max Brunsfeld
e7b56f6342 adjust buffer-formatting assertion to reflect final newline addition 2023-02-28 21:52:00 -08:00
Max Brunsfeld
1deff43639 Avoid calling edits_since in apply_diff 2023-02-28 21:52:00 -08:00
Max Brunsfeld
a890b8f3b7 Add a setting for ensuring a single final newline on save 2023-02-28 21:52:00 -08:00
Max Brunsfeld
7faa0da5c7 Avoid finalizing transactions inside Buffer::apply_diff 2023-02-28 21:52:00 -08:00
Max Brunsfeld
ff85bc6d42 Add setting for removing trailing whitespace on save 2023-02-28 21:52:00 -08:00
Max Brunsfeld
b00e467ede Add APIs for stripping trailing whitespace from a buffer 2023-02-28 21:52:00 -08:00
Mikayla Maki
2e1adb0724 Merge pull request #2229 from zed-industries/fix-click-range-bug
Fix off by one error in click ranges
2023-02-28 20:33:00 -08:00
Mikayla Maki
269df10a16 Fix off by one error in click ranges 2023-02-28 20:27:34 -08:00
Mikayla Maki
8358efbd6c Merge pull request #2228 from zed-industries/make-folds-show-on-active-lines
Made code fold indicators show up on active line indicators
2023-02-28 20:01:09 -08:00
Mikayla Maki
dc11d2726e Made code fold indicators show up on active line indicators 2023-02-28 19:53:53 -08:00
Mikayla Maki
41d3c5287b Merge pull request #2220 from zed-industries/add-fold-indicators
Add code folding indicators into the gutter.
2023-02-28 17:30:17 -08:00
Mikayla Maki
2198c295b3 Fix comments 2023-02-28 17:25:35 -08:00
Mikayla Maki
6cf62a5b02 Update tests to use new fold indicator 2023-02-28 17:17:29 -08:00
Mikayla Maki
f8401394f5 Removed DisplayRow abstraction 2023-02-28 17:12:48 -08:00
Mikayla Maki
b53d1eef71 Added background styling of the ... 2023-02-28 16:35:44 -08:00
Mikayla Maki
c397fd9a71 Added click regions and cursor styles 2023-02-28 16:34:28 -08:00
Mikayla Maki
9b8adecf05 Adjusted code-folding behavior 2023-02-28 16:34:28 -08:00
Mikayla Maki
e0f553c0f5 WIp 2023-02-28 16:34:28 -08:00
Mikayla Maki
37a2ef9d41 Make chevrons and lightning bolt interactive 2023-02-28 16:34:28 -08:00
Mikayla Maki
89b93d4f6f Added fold changes on gutter hover 2023-02-28 16:34:28 -08:00
Mikayla Maki
2036fc48b5 moved code action indicator to the left 2023-02-28 16:34:28 -08:00
Mikayla Maki
cb3e873a67 Fixed autoscroll timing 2023-02-28 16:34:28 -08:00
Mikayla Maki
da78abd99f Added DisplayRow abstraction to make folding code more readable 2023-02-28 16:34:28 -08:00
Mikayla Maki
637e8ada42 Fix bugs in code folding 2023-02-28 16:34:28 -08:00
Mikayla Maki
e3061066c9 Add code folding indicators into the gutter. 2023-02-28 16:34:28 -08:00
Nate Butler
514da604d7 Merge pull request #2223 from zed-industries/nate/extend-syntax
Extend syntax styles available in the theme
2023-02-28 11:26:47 -05:00
Nate Butler
b9811e48e4 One family tune up + tidy 2023-02-28 11:20:16 -05:00
Nate Butler
fb69611568 Fix order of template_substitution in highlights.scm 2023-02-28 11:19:55 -05:00
Nate Butler
a8a045e8bf Merge branch 'main' into nate/extend-syntax 2023-02-28 11:07:35 -05:00
Nate Butler
59bd503696 Tidy theme names 2023-02-28 10:55:08 -05:00
Nate Butler
fb7818f93c Revert changes to Andromeda 2023-02-28 10:50:03 -05:00
Joseph T. Lyons
3fb426e8b2 Merge pull request #2209 from zed-industries/add-link-to-community-repo-in-feedback-editor
Add link to community repo in feedback editor text
2023-02-28 09:03:21 -05:00
Kay Simmons
f0a31f86c7 remove commented line 2023-02-27 12:06:10 -08:00
Kay Simmons
dc7fe72f18 Merge pull request #2226 from zed-industries/fix-infinite-loop-in-path-distance
fix infinite loop in path distance for fuzzy finder
2023-02-27 12:03:40 -08:00
Kay Simmons
b3dffeaf2a fix infinite loop in path distance for fuzzy finder 2023-02-27 11:57:21 -08:00
Kay Simmons
81cbefec22 Merge pull request #2225 from zed-industries/vim-go-to-definition
add go to definition binding to vim normal mode
2023-02-27 11:10:23 -08:00
Joseph Lyons
4f9a07cffc Merge branch 'main' into add-link-to-community-repo-in-feedback-editor 2023-02-27 13:56:01 -05:00
Kay Simmons
184f37015a add go to definition binding to vim normal mode 2023-02-27 10:23:30 -08:00
Nate Butler
c9997a81a3 Move syntax types to syntax.ts 2023-02-27 11:01:32 -05:00
Nate Butler
df798c1a7f Remove punctuation.special until bug is fixed 2023-02-27 10:52:53 -05:00
Nate Butler
465fcec36d Format 2023-02-27 10:48:55 -05:00
Nate Butler
40c2409b80 Add missing variable property to Syntax 2023-02-27 10:47:52 -05:00
Nate Butler
46dc347a1a Bring andromeda in line with it's correct colors 2023-02-27 10:47:30 -05:00
Nate Butler
f84046b74f use @boolean in all flavors of js 2023-02-27 10:40:01 -05:00
Nate Butler
8c51a62a8d Unify regex highlight style under @string.regex 2023-02-27 10:08:24 -05:00
Nate Butler
794e6e22a6 Format 2023-02-26 17:12:48 -05:00
Nate Butler
504d88d56c Remove unused code 2023-02-26 17:12:11 -05:00
Nate Butler
94c76c45e6 Style One Dark with new properties 2023-02-26 17:11:02 -05:00
Nate Butler
f2d6a03dff Finish adding default properties 2023-02-26 17:10:52 -05:00
Nate Butler
3b19a409f8 Add some comments 2023-02-26 15:01:18 -05:00
Nate Butler
7854f4a1ef WIP need to finish default styles 2023-02-26 14:57:43 -05:00
Nate Butler
6cb35536b3 Start on syntax organization 2023-02-26 14:15:06 -05:00
Nate Butler
161373710c WIP 2023-02-26 13:24:41 -05:00
Joseph T. Lyons
11e2caff15 Merge pull request #2222 from zed-industries/update-app-icons
Update app icons
2023-02-26 11:07:07 -05:00
Joseph Lyons
36ada13966 Update app-icon@2x.png 2023-02-26 11:03:43 -05:00
Joseph Lyons
2c61eeb56d Update app-icon.png 2023-02-26 11:03:31 -05:00
Joseph Lyons
ccae9448d4 Update app-icon-preview@2x.png 2023-02-26 11:03:22 -05:00
Joseph Lyons
bb46b26494 Update app-icon-preview.png 2023-02-26 11:03:03 -05:00
Nate Butler
098e6969f7 Merge pull request #2221 from zed-industries/theme-syntax-overrides
Theme syntax overrides 🎉
2023-02-26 10:56:45 -05:00
Nate Butler
d910eed1f1 Format 2023-02-26 10:31:19 -05:00
Nate Butler
64b07dbfeb Add syntax overrides for One Light 2023-02-26 10:09:16 -05:00
Nate Butler
4f307c7601 Use syntax as merged name instead of mergedSyntax
This lets us retain the existing uses of syntax, and fixes colors that were being assigned incorrectly.
2023-02-26 01:20:44 -05:00
Nate Butler
23c967418a Add a syntax override style for One Dark 2023-02-26 01:07:45 -05:00
Nate Butler
77ed437cda Add the ability to override the system syntax config 2023-02-26 01:07:21 -05:00
Kay Simmons
0b1334b8c5 Merge pull request #2218 from zed-industries/file-finder-distance-sorting
Sort matches in file finder by distance to active item after score
2023-02-25 14:26:05 -08:00
Kay Simmons
cdc6566d87 fixup poor utility naming 2023-02-25 14:12:25 -08:00
Kay Simmons
36f3d3d738 Add test for new sorting behavior 2023-02-25 14:06:54 -08:00
Nate Butler
27712c25ef Merge pull request #2219 from zed-industries/nate/theme-tidying
Tidy `styles` app
2023-02-25 12:07:02 -05:00
Nate Butler
68af726ee4 Update packages
Tested post update.
2023-02-25 11:53:01 -05:00
Nate Butler
0ea7959ba4 Remove unused/old base16 theme code
Neither of these files have anything in them that is used anywhere else in the styles app.

Tested both `build` and `build-licenses` after removing these.
2023-02-25 11:50:22 -05:00
Nate Butler
bcb7b80517 Don't format package or package-lock 2023-02-25 11:47:27 -05:00
Nate Butler
10a30cf330 Format styles with updated prettier config
In the system color PR I updated the prettier config to match what we use on zed.dev. I didn't want to format all of styles as it would add a lot of unrelated line changes to that PR.

Doing that format now.
2023-02-25 11:46:33 -05:00
Nate Butler
06a86162bb Merge pull request #2150 from zed-industries/nate/system-colors
Add system color palette
2023-02-25 11:43:02 -05:00
Nate Butler
b986c38a31 Format using new prettier config 2023-02-25 11:33:57 -05:00
Nate Butler
69fd273367 Add the same prettier config as zed.dev 2023-02-25 11:33:16 -05:00
Nate Butler
8e828947fb Add missing dep 2023-02-25 11:32:04 -05:00
Nate Butler
2d8adf4c56 Remove theme tool for now
This is likely the biggest thing I'm unsure about for this PR, so I'm going to pull it into a seperate branch so I can merge the system color library in.

I think a better approach for this will be one of two things:
- Have a single next app that covers everything to do with themes (previewing ramps, generated themes, components etc.)
- Create a mono or turborepo that splits things up into packages (system, theme, themes, theme-tool, etc)
2023-02-25 11:27:18 -05:00
Kay Simmons
0b48e238f2 Sort file finder matches by distance to the active item after match score 2023-02-24 18:13:26 -08:00
Max Brunsfeld
04495aa8cd Merge pull request #2217 from zed-industries/format-on-save-trigger
Pass the 'Save' format trigger when formatting on save
2023-02-24 17:29:52 -08:00
Max Brunsfeld
5fea49e639 Pass the 'Save' format trigger when formatting on save
In an earlier refactor, I accidentally caused the 'Manual'
trigger to *always* be passed.

Co-authored-by: Mikayla Maki <mikayla@zed.dev>
2023-02-24 17:11:35 -08:00
Mikayla Maki
0704d9dcdb Merge pull request #2216 from zed-industries/update-bromberg
Update bromberg dependency to have alloc
2023-02-24 14:41:32 -08:00
Mikayla Maki
a57fcf5afc Update bromberg dependency to have alloc 2023-02-24 14:34:24 -08:00
Max Brunsfeld
e910fd8493 collab 0.6.1 2023-02-24 09:44:23 -08:00
Mikayla Maki
d5123bc832 Merge pull request #2215 from zed-industries/revert-2214-fix-reveal-path-panic
Revert "Remove borrow from reveal_path()"
2023-02-24 09:40:03 -08:00
Mikayla Maki
8656708de4 Revert "Remove borrow from reveal_path()" 2023-02-24 09:39:52 -08:00
Joseph Lyons
72197802a2 Tweak code to remove duplication 2023-02-24 08:53:58 -05:00
Joseph Lyons
f8f1a3f86e Unify text style names 2023-02-24 08:46:28 -05:00
Mikayla Maki
2ec25bef84 Merge pull request #2214 from zed-industries/fix-reveal-path-panic
Remove borrow from reveal_path()
2023-02-23 21:48:09 -08:00
Mikayla Maki
89ddf14b0e Remove borrow from reveal_path() 2023-02-23 21:36:17 -08:00
Julia
be86cb35ba Merge pull request #2213 from zed-industries/per-project-follow-status
Differentiate between follow state on a per-project basis
2023-02-24 00:26:41 -05:00
Julia
465d8cc2ff Differentiate between follow state on a per-project basis 2023-02-24 00:07:17 -05:00
Max Brunsfeld
93b9e762ec Merge pull request #2212 from zed-industries/initial-traffic-light-position
Adjust window's traffic light position when setting its title
2023-02-23 15:02:05 -08:00
Max Brunsfeld
fbc934b884 Adjust window's traffic light position when setting its title 2023-02-23 14:44:45 -08:00
Mikayla Maki
350b7b82f7 Merge pull request #2207 from zed-industries/project-panel-actions
Fix project panel actions
2023-02-23 14:31:29 -08:00
Mikayla Maki
b179fc2b99 Remove comment 2023-02-23 14:24:53 -08:00
Mikayla Maki
8860346324 Fix broken dock command 2023-02-23 14:15:29 -08:00
Mikayla Maki
9004640586 Convert keymap context to use generics and Cow<'static> so we don't have to add .to_string() and .into() for each usage 2023-02-23 14:10:55 -08:00
Kay Simmons
03498314fa Merge pull request #2211 from zed-industries/hover-binding
add hover binding from vscode
2023-02-23 14:05:28 -08:00
Kay Simmons
ce4b672a14 add hover binding from vscode 2023-02-23 13:50:43 -08:00
Kay Simmons
3f9405f8f1 Merge pull request #2210 from zed-industries/fix-enter-vim-normal-mode
Fix enter in normal mode acting incorrectly
2023-02-23 13:46:15 -08:00
Kay Simmons
2276d25bdf Fix enter in normal mode acting incorrectly 2023-02-23 13:40:31 -08:00
Mikayla Maki
ffe53bed87 Reverts keymap precedence order change 2023-02-23 13:32:45 -08:00
Joseph Lyons
37f910949d Add link to community repo in feedback editor 2023-02-23 16:30:00 -05:00
Julia
1e3b4f0387 Merge pull request #2208 from zed-industries/new-collab-ui-3
Add same grayscale logic to followers which leaders have; new call UI [3/N]
2023-02-23 16:05:42 -05:00
Julia
e1df85e86d Add same grayscale logic to followers which leaders have 2023-02-23 15:59:37 -05:00
Mikayla Maki
f6601f64e5 Added editor-in-project-panel overrides to the default keymap 2023-02-23 09:36:30 -08:00
Antonio Scandurra
6ccc90327c Merge pull request #2206 from zed-industries/fix-refresh-room
Fix error when deleting rooms containing projects on refresh
2023-02-23 16:03:14 +01:00
Antonio Scandurra
bbeb33bc7e Fix error when deleting rooms containing projects on refresh
A foreign key violation was causing the server to never delete stale
rooms during `Database::refresh_room` due to having one or more project
records referencing the room.
2023-02-23 15:54:35 +01:00
Antonio Scandurra
e74db2d180 Merge pull request #2205 from zed-industries/call-ui-follow-up
Refine new call UI
2023-02-23 15:39:59 +01:00
Antonio Scandurra
74e0bed38f Fix compilation errors after restructuring room_transaction 2023-02-23 15:17:22 +01:00
Antonio Scandurra
832549f1a3 Merge branch 'main' into call-ui-follow-up 2023-02-23 15:15:46 +01:00
Antonio Scandurra
b965333325 Show avatar in user menu 2023-02-23 15:09:32 +01:00
Joseph T. Lyons
2be0283bf2 Merge pull request #2204 from zed-industries/fix-description-of-telemetry-setting
Fix description of telemetry setting
2023-02-23 08:59:30 -05:00
Antonio Scandurra
59a66190e5 Avoid trying to reconnect to a room if client is signed out 2023-02-23 14:53:10 +01:00
Antonio Scandurra
9334267bd0 Tear down peer when signing out 2023-02-23 14:47:02 +01:00
Antonio Scandurra
a0daf47134 Don't panic when rendering collab titlebar item while signed out 2023-02-23 14:46:02 +01:00
Petros Amoiridis
9a729a2e64 Merge pull request #2202 from zed-industries/petros/z-86-replace-terminal-tab-title-with-an-icon
Replace terminal tab title with an icon
2023-02-23 11:04:43 +02:00
Antonio Scandurra
1c636500de Merge pull request #2200 from zed-industries/fix-slow-project-join
Hold room lock through the entirety of a `room_transaction`
2023-02-23 09:11:58 +01:00
Max Brunsfeld
65a9ac449f Remove leave button from the title bar 2023-02-22 23:30:32 -08:00
Max Brunsfeld
bf5c3d963a Rearrange collab titlebar items to avoid movement of the toggle contacts button
* Replace username in titelbar with a `...` user menu that shows
  the current user name and contains a sign-in/sign-out button.
* Move the '+' (toggle contacts) button back to the right side.
* Move the collaborators back to the right side.
* Move the share/unshare button to the left side, beside the project title
* Only show the share/unshare button when in a call.
2023-02-22 22:56:11 -08:00
Joseph Lyons
c33d0f940a Fix description of telemetry setting 2023-02-22 23:50:20 -05:00
Max Brunsfeld
24e0a027ee Run check-formatting CI job on a mac mini 2023-02-22 15:35:05 -08:00
Max Brunsfeld
d49e35f947 Merge pull request #2203 from zed-industries/collab-ui-fixes
Fix minor issues with new collab UI
2023-02-22 14:22:05 -08:00
Max Brunsfeld
40aee8d7bc Add missing tooltip for contacts menu button
Co-authored-by: Joseph Lyons <joseph@zed.dev>
2023-02-22 14:18:17 -08:00
Max Brunsfeld
d33d27faa4 Fix ToggleContactsMenu action name in keymap
Co-authored-by: Joseph Lyons <joseph@zed.dev>
2023-02-22 14:17:59 -08:00
Max Brunsfeld
46ead28971 Bump RPC protocol version number 2023-02-22 13:40:14 -08:00
Max Brunsfeld
111aff29cc collab 0.6.0 2023-02-22 12:35:15 -08:00
Max Brunsfeld
e2a2e40599 v0.76.x dev 2023-02-22 12:34:29 -08:00
Petros Amoiridis
3d6c81584f Add an icon to the terminal view tab
The terminal icon already existed in `assets/icons`

Co-Authored-By: Julia <30666851+ForLoveOfCats@users.noreply.github.com>
2023-02-22 20:30:44 +02:00
Petros Amoiridis
81ece4fd44 Deduplicate tab theme related code
We've noticed that the search theme struct had two fields for a tab icon width and spacing. But we already have those in the tab theme struct. We decided to remove the duplicate and reuse the tab fields.

We also wanted to move where the spacing is being used. Instead of doing it at the left of the label, we do it at the right of the icon to match how it is done in other areas of the UI.

Co-Authored-By: Julia <30666851+ForLoveOfCats@users.noreply.github.com>
2023-02-22 20:28:57 +02:00
Petros Amoiridis
2ec5c88f98 Make icon width match other areas
This was 8 but we've seen areas where this was 14, like the project search tab icon. We want to match this.

Co-Authored-By: Julia <30666851+ForLoveOfCats@users.noreply.github.com>
2023-02-22 20:25:39 +02:00
Antonio Scandurra
8dd249a7cd Hold room lock through the entirety of a room_transaction
Previously, when the host repeatedly sent `UpdateWorktree` messages,
new guests attempting to join a project would observe a severe slowdown
caused by a database serialization error (e.g., the coherence of the data
would get violated midway through `Database::join_project` due to worktree
entries being mutated as the user joined). Writing entries is pretty fast,
whereas reading all of them for a project can take more than 100ms.
Transactions that failed due to a serialization error are retried, but the guest
would keep retrying until the host finished writing because the guest's read
was slow.

This commit changes the semantics of `room_transaction` to acquire a room
lock before even starting the transaction and holding it all the way after
commit (storing it, as before, in the `RoomGuard`). This ensures that a fast
writer (the host) can't starve a slow reader (the guest), allowing the latter
to make progress by temporarily pausing writes by the former.
2023-02-22 16:04:29 +01:00
Max Brunsfeld
51cea1b1fb Merge branch 'main' into randomized-tests-operation-script 2023-02-20 10:39:00 -08:00
Nate Butler
5ce147a2ad Remove unneeded API from theme-tool 2023-02-12 21:04:54 -05:00
Nate Butler
a32c0d1c9b Update colors. 2023-02-12 21:04:31 -05:00
Nate Butler
e65c0810ba Add gray color families 2023-02-12 20:02:51 -05:00
Nate Butler
1fcfa5d272 Generate color meta, document functions and tidy. 2023-02-12 19:45:16 -05:00
Nate Butler
addfcdc1f4 Remove bezier-easing from styles
This was installed in the wrong app
2023-02-12 11:59:36 -05:00
Nate Butler
4501a5a7ee Add initial system palette reference colors 2023-02-12 00:15:41 -05:00
Nate Butler
a120996f0d Test using HSL + curves to build a scale 2023-02-11 23:34:46 -05:00
Nate Butler
187fac1579 Allow passing a chroma color as a start/mid/end color 2023-02-11 21:10:47 -05:00
Nate Butler
0acb820f04 Document ref/color further 2023-02-11 08:14:13 -05:00
Nate Butler
dda0febf39 Organize 2023-02-11 07:56:41 -05:00
Nate Butler
0e238210bb Update chip style 2023-02-10 22:06:03 -05:00
Nate Butler
76685406ed Add remaining colors 2023-02-09 23:26:44 -05:00
Nate Butler
70eedbb48e Group light and dark scales 2023-02-09 13:41:37 -05:00
Nate Butler
42b5fa1fa3 WIP: Use algorithm to generate reference color palette
Adapted from @k-vyn/coloralgorithm
Generate colors for our reference palette.
2023-02-09 12:37:05 -05:00
Nate Butler
f787f6054b List colors for reference palette in theme tool 2023-02-09 00:39:57 -05:00
Nate Butler
6f342bb2c6 Remove leftovers from create-next-app 2023-02-09 00:10:32 -05:00
Nate Butler
0ba44c6dc4 Start on system colors 2023-02-09 00:09:52 -05:00
Nate Butler
2ff82732b9 Init theme tool 2023-02-09 00:09:44 -05:00
Nate Butler
cbfdfa8124 Remove comment 2023-02-08 22:52:27 -05:00
Max Brunsfeld
9c25d37dfc Merge branch 'main' into randomized-tests-operation-script
In randomized integration test, incorporate random updates of existing
files into the test's new structure.
2023-01-24 12:39:24 -08:00
Max Brunsfeld
2c84b74126 Avoid retaining project in randomized test while LSP request is outstanding 2023-01-16 09:48:54 -08:00
Max Brunsfeld
1a9ff2420e Clean up how applications are marked as inapplicable 2023-01-12 22:09:36 -08:00
Max Brunsfeld
e04d0be853 Remove unneeded log messages in randomized test 2023-01-12 14:30:53 -08:00
Max Brunsfeld
00e8625037 Simplify management of entity ids for different app contexts in randomized test 2023-01-12 14:30:10 -08:00
Max Brunsfeld
a3c7416218 Don't include user ids with MutateClients ops in serialized test plans 2023-01-12 14:28:31 -08:00
Max Brunsfeld
576a9bb92c Drop project's buffers when closing a remote project 2023-01-09 14:49:36 -08:00
Max Brunsfeld
5a4fa4b11e Merge branch 'main' into randomized-tests-operation-script 2023-01-09 12:26:11 -08:00
Max Brunsfeld
3e3a703b60 Skip inapplicable operations when running an edited test plan 2023-01-09 11:36:53 -08:00
Max Brunsfeld
c503ba00b6 Add env vars to store and load test plan from JSON files 2023-01-06 17:51:25 -08:00
Max Brunsfeld
2351f2bd0c Tolerate failure to join remote projects in randomized test 2023-01-06 15:40:42 -08:00
Max Brunsfeld
99390a7237 Represent all randomized test actions as operations 2023-01-06 15:32:11 -08:00
Max Brunsfeld
210286da48 Make operations for all buffer manipulations 2023-01-06 15:32:11 -08:00
Max Brunsfeld
f1b3692a35 Tweak operation rates 2023-01-06 15:32:11 -08:00
Max Brunsfeld
f243633f3e Set up flow for mutating clients via explicit operation values 2023-01-06 15:32:11 -08:00
Max Brunsfeld
ce8dd5a286 wip 2023-01-06 15:32:11 -08:00
Max Brunsfeld
a74c5073a4 Start work on allowing random collab test to be minimized
Represent operations as an explicit enum.
2023-01-06 15:32:11 -08:00
564 changed files with 54374 additions and 26484 deletions

View File

@@ -1,9 +0,0 @@
## Description of feature or change
## Link to related issues from zed or community
## Before Merging
- [ ] Does this have tests or have existing tests been updated to cover this change?
- [ ] Have you added the necessary settings to configure this feature?
- [ ] Has documentation been created or updated (including above changes to settings)?

View File

@@ -19,7 +19,9 @@ env:
jobs:
rustfmt:
name: Check formatting
runs-on: self-hosted
runs-on:
- self-hosted
- test
steps:
- name: Install Rust
run: |
@@ -52,7 +54,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v2
with:
node-version: '16'
node-version: '18'
- name: Checkout repo
uses: actions/checkout@v2
@@ -100,7 +102,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v2
with:
node-version: '16'
node-version: '18'
- name: Checkout repo
uses: actions/checkout@v2

43
.github/workflows/randomized_tests.yml vendored Normal file
View File

@@ -0,0 +1,43 @@
name: Randomized Tests
concurrency: randomized-tests
on:
push:
branches:
- randomized-tests-runner
schedule:
- cron: '0 * * * *'
env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: 1
ZED_SERVER_URL: https://zed.dev
ZED_CLIENT_SECRET_TOKEN: ${{ secrets.ZED_CLIENT_SECRET_TOKEN }}
jobs:
tests:
name: Run randomized tests
runs-on:
- self-hosted
- randomized-tests
steps:
- name: Install Rust
run: |
rustup set profile minimal
rustup update stable
- name: Install Node
uses: actions/setup-node@v2
with:
node-version: '18'
- name: Checkout repo
uses: actions/checkout@v2
with:
clean: false
submodules: 'recursive'
- name: Run randomized tests
run: script/randomized-test-ci

247
Cargo.lock generated
View File

@@ -148,9 +148,6 @@ name = "anyhow"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
dependencies = [
"backtrace",
]
[[package]]
name = "arrayref"
@@ -518,6 +515,7 @@ dependencies = [
"menu",
"project",
"serde",
"serde_derive",
"serde_json",
"settings",
"smol",
@@ -784,6 +782,7 @@ dependencies = [
"gpui",
"itertools",
"language",
"outline",
"project",
"search",
"settings",
@@ -794,7 +793,7 @@ dependencies = [
[[package]]
name = "bromberg_sl2"
version = "0.6.0"
source = "git+https://github.com/zed-industries/bromberg_sl2?rev=dac565a90e8f9245f48ff46225c915dc50f76920#dac565a90e8f9245f48ff46225c915dc50f76920"
source = "git+https://github.com/zed-industries/bromberg_sl2?rev=950bc5482c216c395049ae33ae4501e08975f17f#950bc5482c216c395049ae33ae4501e08975f17f"
dependencies = [
"digest 0.9.0",
"lazy_static",
@@ -1097,6 +1096,7 @@ dependencies = [
"ipc-channel",
"plist",
"serde",
"serde_derive",
]
[[package]]
@@ -1111,7 +1111,6 @@ dependencies = [
"futures 0.3.25",
"gpui",
"image",
"isahc",
"lazy_static",
"log",
"parking_lot 0.11.2",
@@ -1119,8 +1118,10 @@ dependencies = [
"rand 0.8.5",
"rpc",
"serde",
"serde_derive",
"settings",
"smol",
"staff_mode",
"sum_tree",
"tempfile",
"thiserror",
@@ -1188,7 +1189,7 @@ dependencies = [
[[package]]
name = "collab"
version = "0.5.4"
version = "0.12.0"
dependencies = [
"anyhow",
"async-tungstenite",
@@ -1209,6 +1210,7 @@ dependencies = [
"git",
"gpui",
"hyper",
"indoc",
"language",
"lazy_static",
"lipsum",
@@ -1228,6 +1230,7 @@ dependencies = [
"sea-orm",
"sea-query",
"serde",
"serde_derive",
"serde_json",
"settings",
"sha-1 0.9.8",
@@ -1257,7 +1260,9 @@ dependencies = [
"client",
"clock",
"collections",
"context_menu",
"editor",
"feedback",
"futures 0.3.25",
"fuzzy",
"gpui",
@@ -1267,6 +1272,7 @@ dependencies = [
"postage",
"project",
"serde",
"serde_derive",
"settings",
"theme",
"util",
@@ -1325,6 +1331,50 @@ dependencies = [
"theme",
]
[[package]]
name = "copilot"
version = "0.1.0"
dependencies = [
"anyhow",
"async-compression",
"async-tar",
"clock",
"collections",
"context_menu",
"fs",
"futures 0.3.25",
"gpui",
"language",
"log",
"lsp",
"node_runtime",
"rpc",
"serde",
"serde_derive",
"settings",
"smol",
"theme",
"util",
]
[[package]]
name = "copilot_button"
version = "0.1.0"
dependencies = [
"anyhow",
"assets",
"context_menu",
"copilot",
"editor",
"futures 0.3.25",
"gpui",
"settings",
"smol",
"theme",
"util",
"workspace",
]
[[package]]
name = "core-foundation"
version = "0.9.3"
@@ -1737,6 +1787,7 @@ dependencies = [
"log",
"parking_lot 0.11.2",
"serde",
"serde_derive",
"smol",
"sqlez",
"sqlez_macros",
@@ -1780,6 +1831,7 @@ dependencies = [
"editor",
"gpui",
"language",
"lsp",
"postage",
"project",
"serde_json",
@@ -1909,21 +1961,17 @@ version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2"
[[package]]
name = "easy-parallel"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6907e25393cdcc1f4f3f513d9aac1e840eb1cc341a0fccb01171f7d14d10b946"
[[package]]
name = "editor"
version = "0.1.0"
dependencies = [
"aho-corasick",
"anyhow",
"client",
"clock",
"collections",
"context_menu",
"copilot",
"ctor",
"db",
"drag_and_drop",
@@ -1931,6 +1979,7 @@ dependencies = [
"futures 0.3.25",
"fuzzy",
"git",
"glob",
"gpui",
"indoc",
"itertools",
@@ -1942,9 +1991,11 @@ dependencies = [
"parking_lot 0.11.2",
"postage",
"project",
"pulldown-cmark",
"rand 0.8.5",
"rpc",
"serde",
"serde_derive",
"settings",
"smallvec",
"smol",
@@ -2098,7 +2149,9 @@ dependencies = [
"project",
"search",
"serde",
"serde_derive",
"settings",
"smallvec",
"sysinfo",
"theme",
"tree-sitter-markdown",
@@ -2294,6 +2347,7 @@ dependencies = [
"regex",
"rope",
"serde",
"serde_derive",
"serde_json",
"smol",
"tempfile",
@@ -2580,9 +2634,9 @@ dependencies = [
[[package]]
name = "glob"
version = "0.3.0"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "globset"
@@ -2662,8 +2716,10 @@ dependencies = [
"postage",
"rand 0.8.5",
"resvg",
"schemars",
"seahash",
"serde",
"serde_derive",
"serde_json",
"simplelog",
"smallvec",
@@ -3017,6 +3073,17 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adab1eaa3408fb7f0c777a73e7465fd5656136fc93b670eb6df3c88c2c1344e3"
[[package]]
name = "install_cli"
version = "0.1.0"
dependencies = [
"anyhow",
"gpui",
"log",
"smol",
"util",
]
[[package]]
name = "instant"
version = "0.1.12"
@@ -3156,6 +3223,7 @@ dependencies = [
name = "journal"
version = "0.1.0"
dependencies = [
"anyhow",
"chrono",
"dirs 4.0.0",
"editor",
@@ -3261,6 +3329,7 @@ dependencies = [
"regex",
"rpc",
"serde",
"serde_derive",
"serde_json",
"settings",
"similar",
@@ -3284,6 +3353,23 @@ dependencies = [
"util",
]
[[package]]
name = "language_selector"
version = "0.1.0"
dependencies = [
"anyhow",
"editor",
"fuzzy",
"gpui",
"language",
"picker",
"project",
"settings",
"theme",
"util",
"workspace",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@@ -3440,6 +3526,7 @@ dependencies = [
"parking_lot 0.11.2",
"postage",
"serde",
"serde_derive",
"serde_json",
"sha2 0.10.6",
"simplelog",
@@ -3460,6 +3547,7 @@ dependencies = [
"prost-types 0.8.0",
"reqwest",
"serde",
"serde_derive",
"sha2 0.10.6",
]
@@ -3500,6 +3588,7 @@ dependencies = [
"parking_lot 0.11.2",
"postage",
"serde",
"serde_derive",
"serde_json",
"smol",
"unindent",
@@ -3519,6 +3608,26 @@ dependencies = [
"url",
]
[[package]]
name = "lsp_log"
version = "0.1.0"
dependencies = [
"anyhow",
"collections",
"editor",
"futures 0.3.25",
"gpui",
"language",
"lsp",
"project",
"serde",
"settings",
"theme",
"unindent",
"util",
"workspace",
]
[[package]]
name = "mach"
version = "0.3.2"
@@ -3865,6 +3974,23 @@ dependencies = [
"memoffset 0.6.5",
]
[[package]]
name = "node_runtime"
version = "0.1.0"
dependencies = [
"anyhow",
"async-compression",
"async-tar",
"futures 0.3.25",
"gpui",
"parking_lot 0.11.2",
"serde",
"serde_derive",
"serde_json",
"smol",
"util",
]
[[package]]
name = "nom"
version = "7.1.1"
@@ -4419,6 +4545,7 @@ dependencies = [
"bincode",
"plugin_macros",
"serde",
"serde_derive",
]
[[package]]
@@ -4429,6 +4556,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde",
"serde_derive",
"syn",
]
@@ -4440,6 +4568,7 @@ dependencies = [
"bincode",
"pollster",
"serde",
"serde_derive",
"serde_json",
"smol",
"wasi-common",
@@ -4481,14 +4610,15 @@ checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7"
[[package]]
name = "postage"
version = "0.4.1"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a63d25391d04a097954b76aba742b6b5b74f213dfe3dbaeeb36e8ddc1c657f0b"
checksum = "af3fb618632874fb76937c2361a7f22afd393c982a2165595407edc75b06d3c1"
dependencies = [
"atomic",
"crossbeam-queue",
"futures 0.3.25",
"log",
"parking_lot 0.12.1",
"pin-project",
"pollster",
"static_assertions",
@@ -4577,25 +4707,31 @@ dependencies = [
"client",
"clock",
"collections",
"copilot",
"ctor",
"db",
"env_logger",
"fs",
"fsevent",
"futures 0.3.25",
"fuzzy",
"git",
"glob",
"gpui",
"ignore",
"itertools",
"language",
"lazy_static",
"log",
"lsp",
"parking_lot 0.11.2",
"postage",
"pulldown-cmark",
"pretty_assertions",
"rand 0.8.5",
"regex",
"rpc",
"serde",
"serde_derive",
"serde_json",
"settings",
"sha2 0.10.6",
@@ -4961,6 +5097,7 @@ dependencies = [
"settings",
"smol",
"text",
"util",
"workspace",
]
@@ -5218,6 +5355,7 @@ dependencies = [
"rand 0.8.5",
"rsa",
"serde",
"serde_derive",
"smol",
"smol-timeout",
"tempdir",
@@ -5634,6 +5772,7 @@ dependencies = [
"collections",
"editor",
"futures 0.3.25",
"glob",
"gpui",
"language",
"log",
@@ -5641,6 +5780,7 @@ dependencies = [
"postage",
"project",
"serde",
"serde_derive",
"serde_json",
"settings",
"smallvec",
@@ -5750,15 +5890,6 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_path_to_error"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "184c643044780f7ceb59104cef98a5a6f12cb2288a7bc701ab93a362b49fd47d"
dependencies = [
"serde",
]
[[package]]
name = "serde_repr"
version = "0.1.9"
@@ -5824,14 +5955,18 @@ dependencies = [
"collections",
"fs",
"futures 0.3.25",
"glob",
"gpui",
"json_comments",
"lazy_static",
"postage",
"pretty_assertions",
"schemars",
"serde",
"serde_derive",
"serde_json",
"serde_path_to_error",
"sqlez",
"staff_mode",
"theme",
"toml",
"tree-sitter",
@@ -6242,6 +6377,14 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "staff_mode"
version = "0.1.0"
dependencies = [
"anyhow",
"gpui",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
@@ -6449,6 +6592,7 @@ dependencies = [
"procinfo",
"rand 0.8.5",
"serde",
"serde_derive",
"settings",
"shellexpand",
"smallvec",
@@ -6480,6 +6624,7 @@ dependencies = [
"project",
"rand 0.8.5",
"serde",
"serde_derive",
"settings",
"shellexpand",
"smallvec",
@@ -6539,8 +6684,8 @@ dependencies = [
"indexmap",
"parking_lot 0.11.2",
"serde",
"serde_derive",
"serde_json",
"serde_path_to_error",
"toml",
]
@@ -6557,6 +6702,7 @@ dependencies = [
"postage",
"settings",
"smol",
"staff_mode",
"theme",
"util",
"workspace",
@@ -7101,7 +7247,7 @@ dependencies = [
[[package]]
name = "tree-sitter-json"
version = "0.20.0"
source = "git+https://github.com/tree-sitter/tree-sitter-json?rev=137e1ce6a02698fc246cdb9c6b886ed1de9a1ed8#137e1ce6a02698fc246cdb9c6b886ed1de9a1ed8"
source = "git+https://github.com/tree-sitter/tree-sitter-json?rev=40a81c01a40ac48744e0c8ccabbaba1920441199#40a81c01a40ac48744e0c8ccabbaba1920441199"
dependencies = [
"cc",
"tree-sitter",
@@ -7441,11 +7587,15 @@ dependencies = [
"dirs 3.0.2",
"futures 0.3.25",
"git2",
"isahc",
"lazy_static",
"log",
"rand 0.8.5",
"serde",
"serde_json",
"smol",
"tempdir",
"url",
]
[[package]]
@@ -7532,6 +7682,7 @@ dependencies = [
"project",
"search",
"serde",
"serde_derive",
"serde_json",
"settings",
"tokio",
@@ -8011,6 +8162,26 @@ version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
[[package]]
name = "welcome"
version = "0.1.0"
dependencies = [
"anyhow",
"db",
"editor",
"fuzzy",
"gpui",
"install_cli",
"log",
"picker",
"project",
"settings",
"theme",
"theme_selector",
"util",
"workspace",
]
[[package]]
name = "wepoll-ffi"
version = "0.1.2"
@@ -8273,6 +8444,7 @@ name = "workspace"
version = "0.1.0"
dependencies = [
"anyhow",
"assets",
"async-recursion 1.0.0",
"bincode",
"call",
@@ -8286,6 +8458,7 @@ dependencies = [
"futures 0.3.25",
"gpui",
"indoc",
"install_cli",
"language",
"lazy_static",
"log",
@@ -8294,9 +8467,11 @@ dependencies = [
"postage",
"project",
"serde",
"serde_derive",
"serde_json",
"settings",
"smallvec",
"terminal",
"theme",
"util",
"uuid 1.2.2",
@@ -8356,7 +8531,7 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
[[package]]
name = "zed"
version = "0.75.0"
version = "0.86.1"
dependencies = [
"activity_indicator",
"anyhow",
@@ -8377,9 +8552,11 @@ dependencies = [
"collections",
"command_palette",
"context_menu",
"copilot",
"copilot_button",
"ctor",
"db",
"diagnostics",
"easy-parallel",
"editor",
"env_logger",
"feedback",
@@ -8393,13 +8570,17 @@ dependencies = [
"ignore",
"image",
"indexmap",
"install_cli",
"isahc",
"journal",
"language",
"language_selector",
"lazy_static",
"libc",
"log",
"lsp",
"lsp_log",
"node_runtime",
"num_cpus",
"outline",
"parking_lot 0.11.2",
@@ -8416,12 +8597,13 @@ dependencies = [
"rust-embed",
"search",
"serde",
"serde_derive",
"serde_json",
"serde_path_to_error",
"settings",
"simplelog",
"smallvec",
"smol",
"staff_mode",
"sum_tree",
"tempdir",
"terminal_view",
@@ -8457,6 +8639,7 @@ dependencies = [
"util",
"uuid 1.2.2",
"vim",
"welcome",
"workspace",
]

View File

@@ -13,6 +13,8 @@ members = [
"crates/collections",
"crates/command_palette",
"crates/context_menu",
"crates/copilot",
"crates/copilot_button",
"crates/db",
"crates/diagnostics",
"crates/drag_and_drop",
@@ -26,13 +28,17 @@ members = [
"crates/go_to_line",
"crates/gpui",
"crates/gpui_macros",
"crates/install_cli",
"crates/journal",
"crates/language",
"crates/language_selector",
"crates/live_kit_client",
"crates/live_kit_server",
"crates/lsp",
"crates/lsp_log",
"crates/media",
"crates/menu",
"crates/node_runtime",
"crates/outline",
"crates/picker",
"crates/plugin",
@@ -49,6 +55,7 @@ members = [
"crates/snippet",
"crates/sqlez",
"crates/sqlez_macros",
"crates/staff_mode",
"crates/sum_tree",
"crates/terminal",
"crates/text",
@@ -58,15 +65,35 @@ members = [
"crates/util",
"crates/vim",
"crates/workspace",
"crates/welcome",
"crates/zed",
]
default-members = ["crates/zed"]
resolver = "2"
[workspace.dependencies]
anyhow = { version = "1.0.57" }
async-trait = { version = "0.1" }
ctor = { version = "0.1" }
env_logger = { version = "0.9" }
futures = { version = "0.3" }
glob = { version = "0.3.1" }
lazy_static = { version = "1.4.0" }
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
ordered-float = { version = "2.1.1" }
parking_lot = { version = "0.11.1" }
postage = { version = "0.5", features = ["futures-traits"] }
rand = { version = "0.8.5" }
regex = { version = "1.5" }
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"] }
rand = { version = "0.8" }
smallvec = { version = "1.6", features = ["union"] }
smol = { version = "1.2" }
tempdir = { version = "0.3.7" }
thiserror = { version = "1.0.29" }
time = { version = "0.3", features = ["serde", "serde-well-known"] }
unindent = { version = "0.1.7" }
[patch.crates-io]
tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "c51896d32dcc11a38e41f36e3deb1a6a9c4f4b14" }

View File

@@ -2,7 +2,7 @@
[![CI](https://github.com/zed-industries/zed/actions/workflows/ci.yml/badge.svg)](https://github.com/zed-industries/zed/actions/workflows/ci.yml)
Welcome to Zed, a lightning-fast, collaborative code editor that makes your dreams come true.
Welcome to Zed, a lightning-fast, collaborative code editor that makes your dreams come true.
## Development tips
@@ -23,10 +23,19 @@ Welcome to Zed, a lightning-fast, collaborative code editor that makes your drea
git clone https://github.com/zed-industries/zed.dev
```
* Set up a local `zed` database and seed it with some initial users:
* Initialize submodules
```
script/bootstrap
git submodule update --init --recursive
```
* Set up a local `zed` database and seed it with some initial users:
Create a personal GitHub token to run `script/bootstrap` once successfully: the token needs to have an access to private repositories for the script to work (`repo` OAuth scope).
Then delete that token.
```
GITHUB_TOKEN=<$token> script/bootstrap
```
### Testing against locally-running servers

View File

@@ -0,0 +1,12 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.2926 3.48996C3.79162 3.79616 3.44871 4.26316 3.44871 4.93872C3.44871 5.75753 3.65302 6.19648 3.88658 6.43349C4.11948 6.66983 4.47018 6.79529 4.95638 6.79529C5.64158 6.79529 6.23176 6.65786 6.64548 6.37099C7.03216 6.10286 7.32149 5.66636 7.35698 4.91278C7.38386 4.34213 7.36863 3.96084 7.21748 3.68905C7.09721 3.47279 6.81682 3.2089 5.96976 3.11109C5.4731 3.05374 4.81346 3.17162 4.2926 3.48996ZM3.72539 2.5525C4.46348 2.10138 5.36842 1.93724 6.09436 2.02107C7.1336 2.14107 7.8142 2.51324 8.17039 3.15373C8.49569 3.73867 8.47238 4.43479 8.44743 4.96466C8.39736 6.02772 7.95809 6.7938 7.26541 7.27411C6.59976 7.73566 5.75982 7.89249 4.95638 7.89249C4.2936 7.89249 3.61755 7.71967 3.11095 7.20558C2.605 6.69216 2.35705 5.92853 2.35705 4.93872C2.35705 3.80566 2.96744 3.01576 3.72539 2.5525Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.69546 8.97734C7.02432 8.97734 7.29091 9.24528 7.29091 9.57581V10.8725C7.29091 11.203 7.02432 11.471 6.69546 11.471C6.3666 11.471 6.1 11.203 6.1 10.8725V9.57581C6.1 9.24528 6.3666 8.97734 6.69546 8.97734Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.45301 7.32072C2.56382 6.90477 2.81104 6.35118 3.40175 6.17048L3.74851 7.31556C3.74509 7.31822 3.73425 7.32798 3.71842 7.35038C3.68409 7.39897 3.64151 7.48723 3.6034 7.6303C3.52629 7.91973 3.49839 8.31081 3.4984 8.73318V10.8761C3.5122 10.9688 3.52011 11.0083 3.53501 11.0478C3.5474 11.0807 3.57295 11.1339 3.6523 11.2153C3.83266 11.4004 4.24428 11.6866 5.21016 12.1174C5.99398 12.467 6.35125 12.6243 6.68361 12.7078C6.99799 12.7869 7.30564 12.8031 7.99999 12.8031V14C7.31311 14 6.86876 13.9882 6.3946 13.869C5.95125 13.7575 5.49691 13.5549 4.78914 13.2391C4.76868 13.23 4.74801 13.2208 4.72712 13.2115C3.73729 12.77 3.14865 12.4092 2.80139 12.0527C2.61692 11.8634 2.49682 11.6721 2.42136 11.4719C2.35507 11.2961 2.33141 11.1302 2.31663 11.0266C2.31561 11.0194 2.31463 11.0126 2.31369 11.0061L2.30749 10.9632V8.73321C2.30748 8.28334 2.33457 7.76532 2.45301 7.32072Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.83439 7.54965C2.14812 7.21281 2.52306 6.88008 2.81315 6.70729L3.42031 7.737C3.27036 7.82631 2.98468 8.06607 2.7038 8.36764C2.43565 8.65553 2.2592 8.90729 2.19783 9.04784C2.18425 9.16608 2.18871 9.38528 2.22654 9.6452C2.26959 9.94104 2.33715 10.1608 2.37974 10.2387L2.42237 10.3167L2.44057 10.4038C2.46806 10.5353 2.60072 10.7284 2.96139 10.9852C3.24332 11.1859 3.57562 11.3661 3.93098 11.5588C4.00968 11.6015 4.0895 11.6448 4.17017 11.689C4.56251 11.8768 5.17152 12.1512 5.7408 12.3785C6.02948 12.4938 6.30016 12.5938 6.5233 12.664C6.63493 12.6991 6.72826 12.7247 6.802 12.7411C6.87402 12.7571 6.90715 12.7597 6.91166 12.76L6.91213 13.957C6.68654 13.957 6.40667 13.8815 6.16744 13.8062C5.90465 13.7235 5.60351 13.6116 5.30115 13.4909C4.69547 13.2491 4.05361 12.9594 3.64268 12.7623L3.62786 12.7552L3.61345 12.7473C3.54294 12.7085 3.46868 12.6683 3.39187 12.6268C3.03384 12.433 2.62042 12.2092 2.27302 11.9619C1.88328 11.6844 1.44476 11.2894 1.29544 10.735C1.17095 10.4701 1.09192 10.1191 1.04817 9.81844C0.999401 9.48332 0.975841 9.08189 1.03513 8.7778L1.04265 8.73927L1.05511 8.70206C1.18745 8.30673 1.53258 7.87368 1.83439 7.54965Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.59243 10.4347V8.41995H2.78334V10.4347H1.59243Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.7074 3.48996C12.2084 3.79616 12.5513 4.26316 12.5513 4.93872C12.5513 5.75753 12.347 6.19648 12.1134 6.43349C11.8805 6.66983 11.5298 6.79529 11.0436 6.79529C10.3584 6.79529 9.76824 6.65786 9.35452 6.37099C8.96784 6.10286 8.67851 5.66636 8.64302 4.91278C8.61614 4.34213 8.63137 3.96084 8.78252 3.68905C8.90279 3.47279 9.18318 3.2089 10.0302 3.11109C10.5269 3.05374 11.1865 3.17162 11.7074 3.48996ZM12.2746 2.5525C11.5365 2.10138 10.6316 1.93724 9.90564 2.02107C8.8664 2.14107 8.1858 2.51324 7.82961 3.15373C7.50431 3.73867 7.52762 4.43479 7.55258 4.96466C7.60264 6.02772 8.04191 6.7938 8.73459 7.27411C9.40024 7.73566 10.2402 7.89249 11.0436 7.89249C11.7064 7.89249 12.3824 7.71967 12.889 7.20558C13.395 6.69216 13.643 5.92853 13.643 4.93872C13.643 3.80566 13.0326 3.01576 12.2746 2.5525Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.30454 8.97734C8.97568 8.97734 8.70909 9.24528 8.70909 9.57581V10.8725C8.70909 11.203 8.97568 11.471 9.30454 11.471C9.6334 11.471 9.9 11.203 9.9 10.8725V9.57581C9.9 9.24528 9.6334 8.97734 9.30454 8.97734Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.547 7.32072C13.4362 6.90477 13.189 6.35118 12.5982 6.17048L12.2515 7.31556C12.2549 7.31822 12.2658 7.32798 12.2816 7.35038C12.3159 7.39897 12.3585 7.48723 12.3966 7.6303C12.4737 7.91973 12.5016 8.31081 12.5016 8.73318V10.8761C12.4878 10.9688 12.4799 11.0083 12.465 11.0478C12.4526 11.0807 12.427 11.1339 12.3477 11.2153C12.1673 11.4004 11.7557 11.6866 10.7898 12.1174C10.006 12.467 9.64875 12.6243 9.31639 12.7078C9.00201 12.7869 8.69433 12.8031 7.99999 12.8031V14C8.68686 14 9.13124 13.9882 9.6054 13.869C10.0488 13.7575 10.5031 13.5549 11.2109 13.2391C11.2313 13.23 11.252 13.2208 11.2729 13.2115C12.2627 12.77 12.8513 12.4092 13.1986 12.0527C13.3831 11.8634 13.5032 11.6721 13.5786 11.4719C13.6449 11.2961 13.6686 11.1302 13.6834 11.0266C13.6844 11.0194 13.6854 11.0126 13.6863 11.0061L13.6925 10.9632V8.73321C13.6925 8.28334 13.6654 7.76532 13.547 7.32072Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.1656 7.54965C13.8519 7.21281 13.4769 6.88008 13.1868 6.70729L12.5797 7.737C12.7296 7.82631 13.0153 8.06607 13.2962 8.36764C13.5643 8.65553 13.7408 8.90729 13.8022 9.04784C13.8158 9.16608 13.8113 9.38528 13.7735 9.6452C13.7304 9.94104 13.6628 10.1608 13.6203 10.2387L13.5776 10.3167L13.5594 10.4038C13.5319 10.5353 13.3993 10.7284 13.0386 10.9852C12.7567 11.1859 12.4244 11.3661 12.069 11.5588C11.9903 11.6015 11.9105 11.6448 11.8298 11.689C11.4375 11.8768 10.8285 12.1512 10.2592 12.3785C9.97052 12.4938 9.69984 12.5938 9.4767 12.664C9.36507 12.6991 9.27174 12.7247 9.198 12.7411C9.12598 12.7571 9.09285 12.7597 9.08834 12.76L9.08787 13.957C9.31345 13.957 9.59333 13.8815 9.83256 13.8062C10.0953 13.7235 10.3965 13.6116 10.6989 13.4909C11.3045 13.2491 11.9464 12.9594 12.3573 12.7623L12.3721 12.7552L12.3865 12.7473C12.4571 12.7085 12.5313 12.6683 12.6081 12.6268C12.9662 12.433 13.3796 12.2092 13.727 11.9619C14.1167 11.6844 14.5552 11.2894 14.7046 10.735C14.829 10.4701 14.9081 10.1191 14.9518 9.81844C15.0006 9.48332 15.0242 9.08189 14.9649 8.7778L14.9574 8.73927L14.9449 8.70206C14.8126 8.30673 14.4674 7.87368 14.1656 7.54965Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.4076 10.4347V8.41995H13.2167V10.4347H14.4076Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -0,0 +1,9 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g opacity="0.5">
<path d="M2.38084 5.44737C2.42754 5.92437 2.54311 6.33779 2.72903 6.68417C2.70043 6.72678 2.67414 6.77043 2.64995 6.81463C2.39071 6.9994 2.09192 7.27314 1.83439 7.54965C1.53258 7.87368 1.18745 8.30673 1.05511 8.70206L1.04265 8.73927L1.03513 8.7778C0.975841 9.08189 0.999401 9.48332 1.04817 9.81844C1.09192 10.1191 1.17095 10.4701 1.29544 10.735C1.44476 11.2894 1.88328 11.6844 2.27302 11.9619C2.6204 12.2092 3.03378 12.4329 3.39179 12.6267C3.4686 12.6683 3.54294 12.7085 3.61345 12.7473L3.62786 12.7552L3.64268 12.7623C4.05361 12.9594 4.69547 13.2491 5.30115 13.4909C5.60351 13.6116 5.90465 13.7235 6.16744 13.8062C6.39236 13.877 6.6532 13.948 6.87108 13.9562C7.19351 13.9948 7.54309 14 7.99999 14C8.45688 14 8.80648 13.9948 9.12892 13.9562C9.34679 13.948 9.60764 13.877 9.83256 13.8062C10.0953 13.7235 10.3965 13.6116 10.6989 13.4909C11.0041 13.369 11.3186 13.235 11.6081 13.1067L10.5467 12.2257C9.92791 12.5006 9.61228 12.6334 9.31639 12.7078C9.00201 12.7869 8.69433 12.8031 7.99999 12.8031C7.30564 12.8031 6.99799 12.7869 6.68361 12.7078C6.35125 12.6243 5.99398 12.467 5.21016 12.1174C4.24428 11.6866 3.83266 11.4004 3.6523 11.2153C3.57295 11.1339 3.5474 11.0807 3.53501 11.0478C3.52011 11.0083 3.5122 10.9688 3.4984 10.8761V8.73318C3.49839 8.31081 3.52629 7.91973 3.6034 7.6303C3.60757 7.61463 3.6118 7.59961 3.61607 7.58523C4.02831 7.80894 4.49555 7.89249 4.95638 7.89249C5.07488 7.89249 5.19417 7.88908 5.31358 7.88178L2.38084 5.44737Z" fill="white"/>
<path d="M6.63684 8.9802C6.3355 9.00979 6.1 9.26516 6.1 9.57581V10.8725C6.1 11.203 6.3666 11.471 6.69546 11.471C7.02432 11.471 7.29091 11.203 7.29091 10.8725V9.57581C7.29091 9.55736 7.29008 9.53911 7.28846 9.52109L6.63684 8.9802Z" fill="white"/>
<path d="M8.70909 10.7003V10.8725C8.70909 11.203 8.97568 11.471 9.30454 11.471C9.39795 11.471 9.48633 11.4493 9.56501 11.4108L8.70909 10.7003Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.09436 2.02107C5.38898 1.93962 4.51459 2.09228 3.78849 2.51486L4.71538 3.28426C5.1409 3.1227 5.6004 3.06844 5.96976 3.11109C6.81682 3.2089 7.09721 3.4728 7.21748 3.68905C7.36863 3.96084 7.38386 4.34213 7.35698 4.91278C7.34816 5.1 7.32368 5.26765 7.28592 5.41801L10.1885 7.82737C10.4719 7.87295 10.76 7.89249 11.0436 7.89249C11.5044 7.89249 11.9717 7.80894 12.3839 7.58523C12.3882 7.59961 12.3924 7.61463 12.3966 7.6303C12.4737 7.91973 12.5016 8.31081 12.5016 8.73318V9.74745L14.4065 11.3287C14.5379 11.1547 14.6446 10.9577 14.7046 10.735C14.829 10.4701 14.9081 10.1191 14.9518 9.81844C15.0006 9.48332 15.0242 9.08189 14.9649 8.7778L14.9574 8.73927L14.9449 8.70206C14.8126 8.30673 14.4674 7.87368 14.1656 7.54965C13.9081 7.27314 13.6093 6.9994 13.35 6.81463C13.3259 6.77043 13.2996 6.72678 13.271 6.68417C13.5201 6.21998 13.643 5.63537 13.643 4.93872C13.643 3.80567 13.0326 3.01576 12.2746 2.5525C11.5365 2.10138 10.6316 1.93724 9.90564 2.02107C9.01315 2.12413 8.38516 2.41316 8 2.89841C7.61484 2.41316 6.98685 2.12413 6.09436 2.02107ZM11.7074 3.48996C12.2084 3.79616 12.5513 4.26316 12.5513 4.93872C12.5513 5.75753 12.347 6.19648 12.1134 6.43349C11.8805 6.66983 11.5298 6.79529 11.0436 6.79529C10.3584 6.79529 9.76824 6.65786 9.35452 6.37099C8.96784 6.10286 8.67851 5.66636 8.64302 4.91278C8.61614 4.34213 8.63137 3.96084 8.78252 3.68905C8.90279 3.4728 9.18318 3.2089 10.0302 3.11109C10.5269 3.05374 11.1865 3.17162 11.7074 3.48996Z" fill="white"/>
</g>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.1225 13.809C14.0341 13.9146 13.877 13.9289 13.7711 13.8409L1.1931 3.40021C1.08658 3.31178 1.0722 3.15362 1.16103 3.04743L1.87751 2.19101C1.96587 2.0854 2.12299 2.07112 2.22894 2.15906L14.8069 12.5998C14.9134 12.6882 14.9278 12.8464 14.839 12.9526L14.1225 13.809Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -0,0 +1,7 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g opacity="0.5">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.09436 2.02107C5.36842 1.93724 4.46348 2.10138 3.72539 2.5525C2.96744 3.01576 2.35705 3.80567 2.35705 4.93872C2.35705 5.63537 2.47988 6.21998 2.72903 6.68417C2.70043 6.72678 2.67414 6.77043 2.64995 6.81463C2.39071 6.9994 2.09192 7.27314 1.83439 7.54965C1.53258 7.87368 1.18745 8.30673 1.05511 8.70206L1.04265 8.73927L1.03513 8.7778C0.975841 9.08189 0.999401 9.48332 1.04817 9.81844C1.09192 10.1191 1.17095 10.4701 1.29544 10.735C1.44476 11.2894 1.88328 11.6844 2.27302 11.9619C2.6204 12.2092 3.03378 12.4329 3.39179 12.6267C3.4686 12.6683 3.54294 12.7085 3.61345 12.7473L3.62786 12.7552L3.64268 12.7623C4.05361 12.9594 4.69547 13.2491 5.30115 13.4909C5.60351 13.6116 5.90465 13.7235 6.16744 13.8062C6.39236 13.877 6.6532 13.948 6.87108 13.9562C7.19351 13.9948 7.54309 14 7.99999 14C8.01293 14 8.02579 14 8.03857 14C7.97904 13.903 7.99191 13.7743 8.07655 13.6911L9.0197 12.7639C8.77857 12.7952 8.48273 12.8031 7.99999 12.8031C7.30564 12.8031 6.99799 12.7869 6.68361 12.7078C6.35125 12.6243 5.99398 12.467 5.21016 12.1174C4.24428 11.6866 3.83266 11.4004 3.6523 11.2153C3.57295 11.1339 3.5474 11.0807 3.53501 11.0478C3.52011 11.0083 3.5122 10.9688 3.4984 10.8761V8.73318C3.49839 8.31081 3.52629 7.91973 3.6034 7.6303C3.60757 7.61463 3.6118 7.59961 3.61607 7.58523C4.02831 7.80894 4.49555 7.89249 4.95638 7.89249C5.75982 7.89249 6.59976 7.73566 7.26541 7.27411C7.55937 7.07027 7.8077 6.81497 8 6.50734C8.1923 6.81497 8.44063 7.07027 8.73459 7.27411C9.40024 7.73566 10.2402 7.89249 11.0436 7.89249C11.5044 7.89249 11.9717 7.80894 12.3839 7.58523C12.3882 7.59961 12.3924 7.61463 12.3966 7.6303C12.4737 7.91973 12.5016 8.31081 12.5016 8.73318V9.34082L14.1266 7.7433C14.169 7.70159 14.2225 7.67811 14.2775 7.67276C14.2398 7.63028 14.2024 7.58915 14.1656 7.54965C13.9081 7.27314 13.6093 6.9994 13.35 6.81463C13.3259 6.77043 13.2996 6.72678 13.271 6.68417C13.5201 6.21998 13.643 5.63537 13.643 4.93872C13.643 3.80567 13.0326 3.01576 12.2746 2.5525C11.5365 2.10138 10.6316 1.93724 9.90564 2.02107C9.01315 2.12413 8.38516 2.41316 8 2.89841C7.61484 2.41316 6.98685 2.12413 6.09436 2.02107ZM3.44871 4.93872C3.44871 4.26316 3.79162 3.79616 4.2926 3.48996C4.81346 3.17162 5.4731 3.05374 5.96976 3.11109C6.81682 3.2089 7.09721 3.4728 7.21748 3.68905C7.36863 3.96084 7.38386 4.34213 7.35698 4.91278C7.32149 5.66636 7.03216 6.10286 6.64548 6.37099C6.23176 6.65786 5.64158 6.79529 4.95638 6.79529C4.47018 6.79529 4.11948 6.66983 3.88658 6.43349C3.65302 6.19648 3.44871 5.75753 3.44871 4.93872ZM12.5513 4.93872C12.5513 4.26316 12.2084 3.79616 11.7074 3.48996C11.1865 3.17162 10.5269 3.05374 10.0302 3.11109C9.18318 3.2089 8.90279 3.4728 8.78252 3.68905C8.63137 3.96084 8.61614 4.34213 8.64302 4.91278C8.67851 5.66636 8.96784 6.10286 9.35452 6.37099C9.76824 6.65786 10.3584 6.79529 11.0436 6.79529C11.5298 6.79529 11.8805 6.66983 12.1134 6.43349C12.347 6.19648 12.5513 5.75753 12.5513 4.93872Z" fill="white"/>
<path d="M7.29091 9.57581C7.29091 9.24528 7.02432 8.97734 6.69546 8.97734C6.3666 8.97734 6.1 9.24528 6.1 9.57581V10.8725C6.1 11.203 6.3666 11.471 6.69546 11.471C7.02432 11.471 7.29091 11.203 7.29091 10.8725V9.57581Z" fill="white"/>
</g>
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.6668 14.9102C13.7644 15.0078 13.9227 15.0078 14.0203 14.9102L14.908 14.0224C15.0056 13.9248 15.0056 13.7665 14.908 13.6688L13.2229 11.9836L14.908 10.2983C15.0057 10.2007 15.0057 10.0424 14.908 9.94474L14.0203 9.05695C13.9227 8.95931 13.7644 8.95931 13.6668 9.05695L11.9817 10.7422L10.2966 9.05693C10.199 8.95929 10.0407 8.95929 9.94306 9.05693L9.05535 9.94473C8.95773 10.0424 8.95773 10.2007 9.05535 10.2983L10.7405 11.9836L9.05537 13.6688C8.95775 13.7665 8.95775 13.9248 9.05537 14.0224L9.94308 14.9102C10.0407 15.0079 10.199 15.0079 10.2966 14.9102L11.9817 13.225L13.6668 14.9102Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M3.44872 4.93872C3.44872 4.26316 3.79163 3.79616 4.29261 3.48996C4.81346 3.17162 5.47311 3.05374 5.96976 3.11109C6.81683 3.2089 7.09722 3.4728 7.21749 3.68905C7.36864 3.96084 7.38387 4.34213 7.35699 4.91278C7.3215 5.66636 7.03217 6.10286 6.64549 6.37099C6.23177 6.65786 5.64159 6.79529 4.95639 6.79529C4.47019 6.79529 4.11949 6.66983 3.88658 6.43349C3.65303 6.19648 3.44872 5.75753 3.44872 4.93872ZM6.09437 2.02107C5.36843 1.93724 4.46349 2.10138 3.7254 2.5525C2.96745 3.01576 2.35706 3.80567 2.35706 4.93872C2.35706 5.63537 2.47988 6.21998 2.72904 6.68417C2.70044 6.72678 2.67415 6.77043 2.64996 6.81463C2.39072 6.9994 2.09193 7.27314 1.83439 7.54965C1.53259 7.87368 1.18745 8.30673 1.05511 8.70206L1.04266 8.73927L1.03514 8.7778C0.975849 9.08189 0.999409 9.48332 1.04818 9.81844C1.09193 10.1191 1.17096 10.4701 1.29545 10.735C1.44476 11.2894 1.88329 11.6844 2.27303 11.9619C2.6204 12.2092 3.03379 12.4329 3.3918 12.6267L3.39183 12.6267L3.39185 12.6267L3.39188 12.6268C3.46869 12.6683 3.54295 12.7085 3.61346 12.7473L3.62787 12.7552L3.64269 12.7623C4.05362 12.9594 4.69548 13.2491 5.30115 13.4909C5.60352 13.6116 5.90466 13.7235 6.16745 13.8062C6.39237 13.877 6.65321 13.948 6.87108 13.9562C7.19351 13.9948 7.5431 14 8 14C8.45052 14 8.79672 13.9949 9.11543 13.9578C9.04001 13.6509 9.00001 13.3301 9.00001 13C9.00001 12.9213 9.00229 12.8431 9.00677 12.7656C8.76798 12.7955 8.47414 12.8031 8 12.8031C7.30565 12.8031 6.998 12.7869 6.68362 12.7078C6.35125 12.6243 5.99399 12.467 5.21017 12.1174C4.24429 11.6866 3.83267 11.4004 3.6523 11.2153C3.57296 11.1339 3.54741 11.0807 3.53502 11.0478C3.52011 11.0083 3.51221 10.9688 3.4984 10.8761V8.73318C3.49839 8.31081 3.5263 7.91973 3.6034 7.6303C3.60758 7.61463 3.61181 7.59961 3.61608 7.58523C4.02832 7.80894 4.49556 7.89249 4.95639 7.89249C5.75982 7.89249 6.59977 7.73566 7.26541 7.27411C7.55938 7.07027 7.8077 6.81497 8.00001 6.50734C8.19231 6.81497 8.44064 7.07027 8.7346 7.27411C9.40025 7.73566 10.2402 7.89249 11.0436 7.89249C11.5045 7.89249 11.9717 7.80894 12.3839 7.58523C12.3882 7.59961 12.3924 7.61463 12.3966 7.6303C12.4737 7.91973 12.5016 8.31081 12.5016 8.73318V9.03072C12.6649 9.01043 12.8312 8.99997 13 8.99997C13.7226 8.99997 14.4004 9.19157 14.9855 9.52673C15.0073 9.26739 15.0077 8.99725 14.9649 8.7778L14.9574 8.73927L14.9449 8.70206C14.8126 8.30673 14.4674 7.87368 14.1656 7.54965C13.9081 7.27314 13.6093 6.9994 13.3501 6.81463C13.3259 6.77043 13.2996 6.72678 13.271 6.68417C13.5201 6.21998 13.643 5.63537 13.643 4.93872C13.643 3.80567 13.0326 3.01576 12.2746 2.5525C11.5365 2.10138 10.6316 1.93724 9.90565 2.02107C9.01315 2.12413 8.38517 2.41316 8.00001 2.89841C7.61485 2.41316 6.98686 2.12413 6.09437 2.02107ZM9.9 10.4719V9.57581C9.9 9.24528 9.63341 8.97734 9.30455 8.97734C8.97569 8.97734 8.7091 9.24528 8.7091 9.57581V10.8725C8.7091 11.2024 8.97466 11.4699 9.30265 11.471C9.45294 11.1079 9.65515 10.7718 9.9 10.4719ZM7.29092 9.57581C7.29092 9.24528 7.02433 8.97734 6.69547 8.97734C6.36661 8.97734 6.10001 9.24528 6.10001 9.57581V10.8725C6.10001 11.203 6.36661 11.471 6.69547 11.471C7.02433 11.471 7.29092 11.203 7.29092 10.8725V9.57581ZM12.5513 4.93872C12.5513 4.26316 12.2084 3.79616 11.7074 3.48996C11.1866 3.17162 10.5269 3.05374 10.0303 3.11109C9.18318 3.2089 8.90279 3.4728 8.78253 3.68905C8.63138 3.96084 8.61615 4.34213 8.64303 4.91278C8.67852 5.66636 8.96785 6.10286 9.35453 6.37099C9.76825 6.65786 10.3584 6.79529 11.0436 6.79529C11.5298 6.79529 11.8805 6.66983 12.1134 6.43349C12.347 6.19648 12.5513 5.75753 12.5513 4.93872Z" fill="white"/>
<circle cx="13" cy="13" r="3" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -0,0 +1,3 @@
<svg width="14" height="4" viewBox="0 0 14 4" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.125 2C3.125 2.62132 2.62132 3.125 2 3.125C1.37868 3.125 0.875 2.62132 0.875 2C0.875 1.37868 1.37868 0.875 2 0.875C2.62132 0.875 3.125 1.37868 3.125 2ZM8.125 2C8.125 2.62132 7.62132 3.125 7 3.125C6.37868 3.125 5.875 2.62132 5.875 2C5.875 1.37868 6.37868 0.875 7 0.875C7.62132 0.875 8.125 1.37868 8.125 2ZM12 3.125C12.6213 3.125 13.125 2.62132 13.125 2C13.125 1.37868 12.6213 0.875 12 0.875C11.3787 0.875 10.875 1.37868 10.875 2C10.875 2.62132 11.3787 3.125 12 3.125Z" fill="#ABB2BF"/>
</svg>

After

Width:  |  Height:  |  Size: 637 B

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.9083 3.19699L7.99999 10.3949L0.0916311 3.1969C0.346537 2.49164 1.10447 1.98018 2 1.98018H14C14.8943 1.98018 15.653 2.49168 15.9083 3.19699ZM16 4.7153L12.1526 8.21715L16 11.688V4.7153ZM8.52024 11.5232L11.4199 8.88404L15.9081 12.933C15.6528 13.6378 14.8941 14.1501 14 14.1501H2C1.10461 14.1501 0.346779 13.6378 0.0917535 12.9331L4.58012 8.88404L7.47975 11.5232L7.99999 11.9967L8.52024 11.5232ZM3.84742 8.21715L0 4.71532V11.688L3.84742 8.21715Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 614 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1.24em" height="1em" viewBox="0 0 256 208"><path d="M205.28 31.36c14.096 14.88 20.016 35.2 22.512 63.68c6.626 0 12.805 1.47 16.976 7.152l7.792 10.56A17.548 17.548 0 0 1 256 123.2v28.688c-.008 3.704-1.843 7.315-4.832 9.504C215.885 187.222 172.35 208 128 208c-49.066 0-98.19-28.273-123.168-46.608c-2.989-2.189-4.825-5.8-4.832-9.504V123.2c0-3.776 1.2-7.424 3.424-10.464l7.792-10.544c4.173-5.657 10.38-7.152 16.992-7.152c2.496-28.48 8.4-48.8 22.512-63.68C77.331 3.165 112.567.06 127.552 0H128c14.72 0 50.4 2.88 77.28 31.36Zm-77.264 47.376c-3.04 0-6.544.176-10.272.544c-1.312 4.896-3.248 9.312-6.08 12.128c-11.2 11.2-24.704 12.928-31.936 12.928c-6.802 0-13.927-1.42-19.744-5.088c-5.502 1.808-10.786 4.415-11.136 10.912c-.586 12.28-.637 24.55-.688 36.824c-.026 6.16-.05 12.322-.144 18.488c.024 3.579 2.182 6.903 5.44 8.384C79.936 185.92 104.976 192 128.016 192c23.008 0 48.048-6.08 74.512-18.144c3.258-1.48 5.415-4.805 5.44-8.384c.317-18.418.062-36.912-.816-55.312h.016c-.342-6.534-5.648-9.098-11.168-10.912c-5.82 3.652-12.927 5.088-19.728 5.088c-7.232 0-20.72-1.728-31.936-12.928c-2.832-2.816-4.768-7.232-6.08-12.128a106.26 106.26 0 0 0-10.24-.544Zm-26.941 43.93c5.748 0 10.408 4.66 10.408 10.409v19.183c0 5.749-4.66 10.409-10.408 10.409c-5.748 0-10.408-4.66-10.408-10.409v-19.183c0-5.748 4.66-10.408 10.408-10.408Zm53.333 0c5.749 0 10.409 4.66 10.409 10.409v19.183c0 5.749-4.66 10.409-10.409 10.409c-5.748 0-10.408-4.66-10.408-10.409v-19.183c0-5.748 4.66-10.408 10.408-10.408ZM81.44 28.32c-11.2 1.12-20.64 4.8-25.44 9.92c-10.4 11.36-8.16 40.16-2.24 46.24c4.32 4.32 12.48 7.2 21.28 7.2c6.72 0 19.52-1.44 30.08-12.16c4.64-4.48 7.52-15.68 7.2-27.04c-.32-9.12-2.88-16.64-6.72-19.84c-4.16-3.68-13.6-5.28-24.16-4.32Zm68.96 4.32c-3.84 3.2-6.4 10.72-6.72 19.84c-.32 11.36 2.56 22.56 7.2 27.04c10.56 10.72 23.36 12.16 30.08 12.16c8.8 0 16.96-2.88 21.28-7.2c5.92-6.08 8.16-34.88-2.24-46.24c-4.8-5.12-14.24-8.8-25.44-9.92c-10.56-.96-20 .64-24.16 4.32ZM128 56c-2.56 0-5.6.16-8.96.48c.32 1.76.48 3.68.64 5.76c0 1.44 0 2.88-.16 4.48c3.2-.32 5.92-.32 8.48-.32c2.56 0 5.28 0 8.48.32c-.16-1.6-.16-3.04-.16-4.48c.16-2.08.32-4 .64-5.76c-3.36-.32-6.4-.48-8.96-.48Z"/></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1,5 @@
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.5 1H7.5H8.75C8.88807 1 9 1.11193 9 1.25V4.5" stroke="#838994" stroke-linecap="round"/>
<path d="M3.64645 5.64645C3.45118 5.84171 3.45118 6.15829 3.64645 6.35355C3.84171 6.54882 4.15829 6.54882 4.35355 6.35355L3.64645 5.64645ZM8.64645 0.646447L3.64645 5.64645L4.35355 6.35355L9.35355 1.35355L8.64645 0.646447Z" fill="#838994"/>
<path d="M7.5 6.5V9C7.5 9.27614 7.27614 9.5 7 9.5H1C0.723858 9.5 0.5 9.27614 0.5 9V3C0.5 2.72386 0.723858 2.5 1 2.5H3.5" stroke="#838994" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 605 B

3
assets/icons/logo_96.svg Normal file
View File

@@ -0,0 +1,3 @@
<svg width="96" height="96" viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 6C7.34315 6 6 7.34315 6 9V75H0V9C0 4.02944 4.02944 0 9 0H89.3787C93.3878 0 95.3955 4.84715 92.5607 7.68198L43.0551 57.1875H57V51H63V58.6875C63 61.1728 60.9853 63.1875 58.5 63.1875H37.0551L26.7426 73.5H73.5V36H79.5V73.5C79.5 76.8137 76.8137 79.5 73.5 79.5H20.7426L10.2426 90H87C88.6569 90 90 88.6569 90 87V21H96V87C96 91.9706 91.9706 96 87 96H6.62132C2.61224 96 0.604504 91.1529 3.43934 88.318L52.7574 39H39V45H33V37.5C33 35.0147 35.0147 33 37.5 33H58.7574L69.2574 22.5H22.5V60H16.5V22.5C16.5 19.1863 19.1863 16.5 22.5 16.5H75.2574L85.7574 6H9Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 715 B

View File

@@ -0,0 +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>

After

Width:  |  Height:  |  Size: 636 B

View File

@@ -1,3 +1,5 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.2 6.00001C5.52563 6.00001 6.6 4.92545 6.6 3.60001C6.6 2.27457 5.52563 1.20001 4.2 1.20001C2.87438 1.20001 1.8 2.27457 1.8 3.60001C1.8 4.92545 2.87438 6.00001 4.2 6.00001ZM5.15063 6.90001H3.24938C1.45519 6.90001 0 8.35501 0 10.1494C0 10.5094 0.291 10.8 0.649875 10.8H7.7505C8.10938 10.8 8.4 10.5094 8.4 10.1494C8.4 8.35501 6.945 6.90001 5.15063 6.90001ZM11.55 4.95001H10.65V4.05001C10.65 3.80251 10.4494 3.60001 10.2 3.60001C9.95063 3.60001 9.75 3.80157 9.75 4.05001V4.95001H8.85C8.6025 4.95001 8.4 5.15251 8.4 5.40001C8.4 5.64751 8.60156 5.85001 8.85 5.85001H9.75V6.75001C9.75 6.99939 9.9525 7.20001 10.2 7.20001C10.4475 7.20001 10.65 6.99845 10.65 6.75001V5.85001H11.55C11.7994 5.85001 12 5.64939 12 5.40001C12 5.15064 11.7994 4.95001 11.55 4.95001Z" fill="white"/>
<path d="M5.75062 7.09998H3.24938C1.45519 7.09998 0 8.55498 0 10.3493C0 10.7093 0.291 11 0.649875 11H8.3505C8.70938 11 9 10.7093 9 10.3493C9 8.55498 7.545 7.09998 5.75062 7.09998Z" fill="white"/>
<path d="M7 3.5C7 4.82544 5.82562 6 4.5 6C3.17438 6 2 4.82544 2 3.5C2 2.17456 3.17438 1 4.5 1C5.82562 1 7 2.17456 7 3.5Z" fill="white"/>
<path d="M9.5 3.75V5.5M9.5 7.25V5.5M9.5 5.5H11.25M9.5 5.5H7.75" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 882 B

After

Width:  |  Height:  |  Size: 564 B

View File

@@ -1,3 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.9 8.00002C7.44656 8.00002 8.7 6.74637 8.7 5.20002C8.7 3.65368 7.44656 2.40002 5.9 2.40002C4.35344 2.40002 3.1 3.65368 3.1 5.20002C3.1 6.74637 4.35344 8.00002 5.9 8.00002ZM7.00906 9.05002H4.79094C2.69772 9.05002 1 10.7475 1 12.841C1 13.261 1.3395 13.6 1.75819 13.6H10.0422C10.4609 13.6 10.8 13.261 10.8 12.841C10.8 10.7475 9.1025 9.05002 7.00906 9.05002ZM14.475 6.77502H13.425V5.72502C13.425 5.43627 13.1909 5.20002 12.9 5.20002C12.6091 5.20002 12.375 5.43518 12.375 5.72502V6.77502H11.325C11.0363 6.77502 10.8 7.01127 10.8 7.30002C10.8 7.58877 11.0352 7.82502 11.325 7.82502H12.375V8.87502C12.375 9.16596 12.6112 9.40002 12.9 9.40002C13.1887 9.40002 13.425 9.16487 13.425 8.87502V7.82502H14.475C14.7659 7.82502 15 7.59096 15 7.30002C15 7.00909 14.7659 6.77502 14.475 6.77502Z" fill="white"/>
<path d="M7.00906 8.99999H4.79094C2.69772 8.99999 1 11.1475 1 13.2409C1 13.6609 1.3395 14 1.75819 14H10.0422C10.4609 14 10.8 13.6609 10.8 13.2409C10.8 11.1475 9.1025 8.99999 7.00906 8.99999Z" fill="white"/>
<path d="M9 5C9 6.54634 7.44657 7.99998 5.90001 7.99998C4.35344 7.99998 3 6.54634 3 5C3 3.45366 4.45344 2 6 2C7.54656 2 9 3.45366 9 5Z" fill="white"/>
<path d="M13.025 6H14.475C14.7659 6 15 6.20906 15 6.5C15 6.79094 14.7659 7 14.475 7H13V8.49995C13 8.7898 12.7638 9.02495 12.475 9.02495C12.1863 9.02495 11.95 8.79089 11.95 8.49995V7H10.525C10.2352 7 10 6.78875 10 6.5C10 6.21125 10.2362 6 10.525 6H11.975V4.525C11.975 4.23516 12.2091 4 12.5 4C12.7909 4 13.025 4.23625 13.025 4.525V6Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 907 B

After

Width:  |  Height:  |  Size: 810 B

View File

@@ -0,0 +1,3 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.75 1.875C4.75 2.71406 4.19922 3.42422 3.4375 3.66328V5.97891C3.9086 5.64609 4.4711 5.4375 5.125 5.4375H7.375C8.30782 5.4375 9.0625 4.68281 9.0625 3.75V3.66328C8.30078 3.42422 7.75 2.71406 7.75 1.875C7.75 0.839531 8.58907 0 9.625 0C10.6609 0 11.5 0.839531 11.5 1.875C11.5 2.71406 10.9492 3.42422 10.1875 3.66328V3.75C10.1875 5.30391 8.92891 6.5625 7.375 6.5625H5.125C4.19219 6.5625 3.4375 7.31719 3.4375 8.25V8.33672C4.19922 8.57578 4.75 9.28594 4.75 10.125C4.75 11.1609 3.91094 12 2.875 12C1.83953 12 1 11.1609 1 10.125C1 9.28594 1.55172 8.57578 2.3125 8.33672V3.66328C1.55172 3.42422 1 2.71406 1 1.875C1 0.839531 1.83953 0 2.875 0C3.91094 0 4.75 0.839531 4.75 1.875ZM2.875 2.625C3.28914 2.625 3.625 2.28914 3.625 1.875C3.625 1.46086 3.28914 1.125 2.875 1.125C2.46086 1.125 2.125 1.46086 2.125 1.875C2.125 2.28914 2.46086 2.625 2.875 2.625ZM9.625 1.125C9.21016 1.125 8.875 1.46086 8.875 1.875C8.875 2.28914 9.21016 2.625 9.625 2.625C10.0398 2.625 10.375 2.28914 10.375 1.875C10.375 1.46086 10.0398 1.125 9.625 1.125ZM2.875 10.875C3.28914 10.875 3.625 10.5398 3.625 10.125C3.625 9.71016 3.28914 9.375 2.875 9.375C2.46086 9.375 2.125 9.71016 2.125 10.125C2.125 10.5398 2.46086 10.875 2.875 10.875Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,14 @@
<svg width="93" height="32" viewBox="0 0 93 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.03996 7.04962C8.00936 7.67635 7.30396 8.63219 7.30396 10.0149C7.30396 11.6908 7.72425 12.5893 8.2047 13.0744C8.68381 13.5581 9.40526 13.8149 10.4054 13.8149C11.815 13.8149 13.0291 13.5336 13.8802 12.9464C14.6756 12.3977 15.2708 11.5042 15.3438 9.96182C15.3991 8.79382 15.3678 8.01341 15.0568 7.45711C14.8094 7.01449 14.2326 6.47436 12.4901 6.27416C11.4684 6.15678 10.1114 6.39804 9.03996 7.04962ZM7.87312 5.13084C9.39147 4.2075 11.2531 3.87155 12.7464 4.04312C14.8843 4.28874 16.2844 5.05049 17.0171 6.36142C17.6863 7.55867 17.6384 8.98348 17.587 10.068C17.484 12.2439 16.5804 13.8118 15.1554 14.7949C13.7861 15.7396 12.0582 16.0606 10.4054 16.0606C9.04201 16.0606 7.65128 15.7069 6.60913 14.6547C5.56832 13.6038 5.05825 12.0408 5.05825 10.0149C5.05825 7.6958 6.3139 6.07903 7.87312 5.13084Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.983 18.2811C14.6595 18.2811 15.2079 18.8295 15.2079 19.506V22.16C15.2079 22.8365 14.6595 23.385 13.983 23.385C13.3065 23.385 12.758 22.8365 12.758 22.16V19.506C12.758 18.8295 13.3065 18.2811 13.983 18.2811Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.25566 14.8903C5.48361 14.039 5.99218 12.9059 7.20734 12.5361L7.92068 14.8798C7.92068 14.8798 7.91996 14.8801 7.92068 14.8798L7.92375 14.8785C7.92411 14.8783 7.92375 14.8785 7.92375 14.8785C7.92374 14.8785 7.92322 14.8778 7.92068 14.8798C7.91364 14.8852 7.89133 14.9052 7.85878 14.951C7.78816 15.0505 7.70057 15.2311 7.62216 15.524C7.46354 16.1164 7.40614 16.9168 7.40616 17.7813V22.1675C7.43456 22.3571 7.45082 22.438 7.48148 22.5189C7.50697 22.5862 7.55954 22.695 7.72276 22.8617C8.09379 23.2406 8.94055 23.8264 10.9275 24.7081C12.5399 25.4236 13.2749 25.7456 13.9586 25.9166C14.6053 26.0784 15.2382 26.1115 16.6666 26.1115V28.5613C15.2536 28.5613 14.3395 28.5372 13.3641 28.2932C12.452 28.0651 11.5174 27.6502 10.0614 27.004C10.0193 26.9853 9.97679 26.9664 9.93382 26.9474C7.8976 26.0438 6.68669 25.3053 5.97232 24.5757C5.59284 24.1882 5.34578 23.7967 5.19055 23.387C5.05418 23.0271 5.0055 22.6875 4.97509 22.4754C4.973 22.4608 4.97099 22.4468 4.96905 22.4335L4.95629 22.3458V17.7814C4.95629 17.7814 4.95629 17.7814 4.95629 17.7814C4.95627 16.8606 5.012 15.8003 5.25566 14.8903Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.98306 15.3589C4.62844 14.6695 5.39976 13.9884 5.99652 13.6348L7.24552 15.7424C6.93706 15.9252 6.34938 16.4159 5.77155 17.0332C5.21994 17.6224 4.85695 18.1377 4.73071 18.4254C4.70277 18.6674 4.71195 19.116 4.78977 19.648C4.87834 20.2536 5.01731 20.7033 5.10492 20.8628L5.19261 21.0224L5.23006 21.2007C5.28661 21.4698 5.55952 21.8651 6.30146 22.3907C6.88143 22.8015 7.56502 23.1703 8.29605 23.5648C8.45794 23.6521 8.62215 23.7407 8.78809 23.8313C9.5952 24.2156 10.848 24.7773 12.0191 25.2425C12.613 25.4784 13.1698 25.6831 13.6288 25.8268C13.8584 25.8987 14.0505 25.9512 14.2021 25.9847C14.3503 26.0175 14.4185 26.0227 14.4277 26.0234C14.4288 26.0234 14.4281 26.0234 14.4277 26.0234L14.4287 28.4733C13.9646 28.4733 13.3889 28.3188 12.8968 28.1648C12.3562 27.9955 11.7367 27.7664 11.1147 27.5193C9.86871 27.0244 8.54832 26.4314 7.70298 26.028L7.67249 26.0135L7.64284 25.9973C7.49779 25.918 7.34502 25.8357 7.18702 25.7506C6.4505 25.354 5.60004 24.896 4.88539 24.3898C4.08363 23.8219 3.18153 23.0135 2.87437 21.8785C2.61828 21.3365 2.4557 20.618 2.3657 20.0026C2.26537 19.3167 2.2169 18.4951 2.33888 17.8727L2.35434 17.7938L2.37996 17.7176C2.6522 16.9085 3.36219 16.0221 3.98306 15.3589Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.48531 21.264V17.1402H5.93518V21.264H3.48531Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.2932 7.04962C25.3238 7.67635 26.0292 8.63219 26.0292 10.0149C26.0292 11.6908 25.609 12.5893 25.1285 13.0744C24.6494 13.5581 23.9279 13.8149 22.9278 13.8149C21.5182 13.8149 20.3041 13.5336 19.453 12.9464C18.6576 12.3977 18.0624 11.5042 17.9894 9.96182C17.9341 8.79382 17.9654 8.01341 18.2764 7.45711C18.5238 7.01449 19.1006 6.47436 20.8431 6.27416C21.8648 6.15678 23.2218 6.39804 24.2932 7.04962ZM25.4601 5.13084C23.9417 4.2075 22.0801 3.87155 20.5868 4.04312C18.4489 4.28874 17.0488 5.05049 16.3161 6.36142C15.6469 7.55867 15.6948 8.98348 15.7462 10.068C15.8492 12.2439 16.7528 13.8118 18.1778 14.7949C19.5471 15.7396 21.275 16.0606 22.9278 16.0606C24.2912 16.0606 25.6819 15.7069 26.7241 14.6547C27.7649 13.6038 28.275 12.0408 28.275 10.0149C28.275 7.6958 27.0193 6.07903 25.4601 5.13084Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.3502 18.2811C18.6737 18.2811 18.1253 18.8295 18.1253 19.506V22.16C18.1253 22.8365 18.6737 23.385 19.3502 23.385C20.0267 23.385 20.5752 22.8365 20.5752 22.16V19.506C20.5752 18.8295 20.0267 18.2811 19.3502 18.2811Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M28.0775 14.8903C27.8496 14.039 27.341 12.9059 26.1259 12.5361L25.4125 14.8798C25.4125 14.8798 25.4132 14.8801 25.4125 14.8798L25.4095 14.8785C25.4091 14.8783 25.4095 14.8785 25.4095 14.8785C25.4095 14.8785 25.41 14.8778 25.4125 14.8798C25.4196 14.8852 25.4419 14.9052 25.4744 14.951C25.545 15.0505 25.6326 15.2311 25.711 15.524C25.8697 16.1164 25.9271 16.9168 25.927 17.7813V22.1675C25.8986 22.3571 25.8824 22.438 25.8517 22.5189C25.8262 22.5862 25.7737 22.695 25.6104 22.8617C25.2394 23.2406 24.3927 23.8264 22.4057 24.7081C20.7933 25.4236 20.0583 25.7456 19.3746 25.9166C18.7279 26.0784 18.0949 26.1115 16.6666 26.1115V28.5613C18.0796 28.5613 18.9937 28.5372 19.9691 28.2932C20.8812 28.0651 21.8158 27.6502 23.2718 27.004C23.3139 26.9853 23.3564 26.9664 23.3994 26.9474C25.4356 26.0438 26.6465 25.3053 27.3609 24.5757C27.7404 24.1882 27.9874 23.7967 28.1427 23.387C28.279 23.0271 28.3277 22.6875 28.3581 22.4754C28.3602 22.4608 28.3622 22.4468 28.3642 22.4335L28.3769 22.3458V17.7814C28.3769 17.7814 28.3769 17.7814 28.3769 17.7814C28.3769 16.8606 28.3212 15.8003 28.0775 14.8903Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M29.3501 15.3589C28.7048 14.6695 27.9334 13.9884 27.3367 13.6348L26.0877 15.7424C26.3961 15.9252 26.9838 16.4159 27.5616 17.0332C28.1133 17.6224 28.4763 18.1377 28.6025 18.4254C28.6304 18.6674 28.6213 19.116 28.5434 19.648C28.4549 20.2536 28.3159 20.7033 28.2283 20.8628L28.1406 21.0224L28.1031 21.2007C28.0466 21.4698 27.7737 21.8651 27.0317 22.3907C26.4518 22.8015 25.7682 23.1703 25.0372 23.5648C24.8753 23.6521 24.711 23.7407 24.5451 23.8313C23.738 24.2156 22.4852 24.7773 21.3141 25.2425C20.7202 25.4784 20.1634 25.6831 19.7044 25.8268C19.4748 25.8987 19.2827 25.9512 19.1311 25.9847C18.9829 26.0175 18.9147 26.0227 18.9055 26.0234C18.9044 26.0234 18.9051 26.0234 18.9055 26.0234L18.9045 28.4733C19.3686 28.4733 19.9443 28.3188 20.4364 28.1648C20.977 27.9955 21.5965 27.7664 22.2185 27.5193C23.4645 27.0244 24.7849 26.4314 25.6302 26.028L25.6607 26.0135L25.6904 25.9973C25.8354 25.918 25.9882 25.8357 26.1462 25.7506C26.8827 25.354 27.7332 24.896 28.4478 24.3898C29.2496 23.8219 30.1517 23.0135 30.4588 21.8785C30.7149 21.3365 30.8775 20.618 30.9675 20.0026C31.0678 19.3167 31.1163 18.4951 30.9943 17.8727L30.9789 17.7938L30.9532 17.7176C30.681 16.9085 29.971 16.0221 29.3501 15.3589Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M29.8479 21.264V17.1402H27.398V21.264H29.8479Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M48.6666 11C49.2189 11 49.6666 11.4477 49.6666 12V15H52.6666C53.2189 15 53.6666 15.4477 53.6666 16C53.6666 16.5523 53.2189 17 52.6666 17H49.6666V20C49.6666 20.5523 49.2189 21 48.6666 21C48.1143 21 47.6666 20.5523 47.6666 20V17H44.6666C44.1143 17 43.6666 16.5523 43.6666 16C43.6666 15.4477 44.1143 15 44.6666 15H47.6666V12C47.6666 11.4477 48.1143 11 48.6666 11Z" fill="white" fill-opacity="0.5"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M67.1666 4.33329C66.7064 4.33329 66.3333 4.70639 66.3333 5.16663V23.5H64.6666V5.16663C64.6666 3.78591 65.7859 2.66663 67.1666 2.66663H89.494C90.6077 2.66663 91.1654 4.01306 90.3779 4.80051L76.6264 18.552H80.5V16.8333H82.1666V18.9687C82.1666 19.6591 81.607 20.2187 80.9166 20.2187H74.9597L72.0951 23.0833H85.0833V12.6666H86.75V23.0833C86.75 24.0038 86.0038 24.75 85.0833 24.75H70.4285L67.5118 27.6666H88.8333C89.2935 27.6666 89.6666 27.2935 89.6666 26.8333V8.49996H91.3333V26.8333C91.3333 28.214 90.214 29.3333 88.8333 29.3333H66.5059C65.3922 29.3333 64.8345 27.9869 65.622 27.1994L79.3214 13.5H75.5V15.1666H73.8333V13.0833C73.8333 12.3929 74.3929 11.8333 75.0833 11.8333H80.9881L83.9048 8.91663H70.9166V19.3333H69.25V8.91663C69.25 7.99615 69.9962 7.24996 70.9166 7.24996H85.5714L88.4881 4.33329H67.1666Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 8.5 KiB

70
assets/keymaps/atom.json Normal file
View File

@@ -0,0 +1,70 @@
[
{
"bindings": {
"cmd-k cmd-p": "workspace::ActivatePreviousPane",
"cmd-k cmd-n": "workspace::ActivateNextPane"
}
},
{
"context": "Editor",
"bindings": {
"cmd-b": "editor::GoToDefinition",
"cmd-<": "editor::ScrollCursorCenter",
"cmd-g": [
"editor::SelectNext",
{
"replace_newest": true
}
],
"ctrl-shift-down": "editor::AddSelectionBelow",
"ctrl-shift-up": "editor::AddSelectionAbove",
"cmd-shift-backspace": "editor::DeleteToBeginningOfLine",
"cmd-shift-enter": "editor::NewlineAbove",
"cmd-enter": "editor::NewlineBelow"
}
},
{
"context": "Editor && mode == full",
"bindings": {
"cmd-r": "outline::Toggle"
}
},
{
"context": "BufferSearchBar",
"bindings": {
"cmd-f3": "search::SelectNextMatch",
"cmd-shift-f3": "search::SelectPrevMatch"
}
},
{
"context": "Workspace",
"bindings": {
"cmd-\\": "workspace::ToggleLeftSidebar",
"cmd-k cmd-b": "workspace::ToggleLeftSidebar",
"cmd-t": "file_finder::Toggle",
"cmd-shift-r": "project_symbols::Toggle"
}
},
{
"context": "Pane",
"bindings": {
"alt-cmd-/": "search::ToggleRegex",
"ctrl-0": "project_panel::ToggleFocus"
}
},
{
"context": "ProjectPanel",
"bindings": {
"ctrl-[": "project_panel::CollapseSelectedEntry",
"ctrl-b": "project_panel::CollapseSelectedEntry",
"alt-b": "project_panel::CollapseSelectedEntry",
"ctrl-]": "project_panel::ExpandSelectedEntry",
"ctrl-f": "project_panel::ExpandSelectedEntry",
"ctrl-shift-c": "project_panel::CopyPath"
}
},
{
"context": "Dock",
"bindings": {}
}
]

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,79 @@
[
{
"bindings": {
"cmd-shift-[": "pane::ActivatePrevItem",
"cmd-shift-]": "pane::ActivateNextItem"
}
},
{
"context": "Editor",
"bindings": {
"ctrl->": "zed::IncreaseBufferFontSize",
"ctrl-<": "zed::DecreaseBufferFontSize",
"cmd-d": "editor::DuplicateLine",
"cmd-pagedown": "editor::MovePageDown",
"cmd-pageup": "editor::MovePageUp",
"ctrl-alt-shift-b": "editor::SelectToPreviousWordStart",
"cmd-alt-enter": "editor::NewlineAbove",
"shift-enter": "editor::NewlineBelow",
"cmd--": "editor::Fold",
"cmd-=": "editor::UnfoldLines",
"alt-shift-g": "editor::SplitSelectionIntoLines",
"ctrl-g": [
"editor::SelectNext",
{
"replace_newest": false
}
],
"cmd-/": [
"editor::ToggleComments",
{
"advance_downwards": true
}
],
"shift-alt-up": "editor::MoveLineUp",
"shift-alt-down": "editor::MoveLineDown",
"cmd-[": "pane::GoBack",
"cmd-]": "pane::GoForward",
"alt-f7": "editor::FindAllReferences",
"cmd-alt-f7": "editor::FindAllReferences",
"cmd-b": "editor::GoToDefinition",
"cmd-alt-b": "editor::GoToDefinition",
"cmd-shift-b": "editor::GoToTypeDefinition",
"alt-enter": "editor::ToggleCodeActions",
"f2": "editor::GoToDiagnostic",
"cmd-f2": "editor::GoToPrevDiagnostic",
"ctrl-alt-shift-down": "editor::GoToHunk",
"ctrl-alt-shift-up": "editor::GoToPrevHunk",
"cmd-home": "editor::MoveToBeginning",
"cmd-end": "editor::MoveToEnd",
"cmd-shift-home": "editor::SelectToBeginning",
"cmd-shift-end": "editor::SelectToEnd"
}
},
{
"context": "Editor && mode == full",
"bindings": {
"cmd-f12": "outline::Toggle",
"cmd-7": "outline::Toggle",
"cmd-shift-o": "file_finder::Toggle",
"cmd-l": "go_to_line::Toggle"
}
},
{
"context": "Workspace",
"bindings": {
"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"
}
}
]

View File

@@ -0,0 +1,62 @@
[
{
"bindings": {
"cmd-shift-[": "pane::ActivatePrevItem",
"cmd-shift-]": "pane::ActivateNextItem",
"ctrl-pagedown": "pane::ActivatePrevItem",
"ctrl-pageup": "pane::ActivateNextItem",
"ctrl-shift-tab": "pane::ActivateNextItem",
"ctrl-tab": "pane::ActivatePrevItem",
"cmd-+": "zed::IncreaseBufferFontSize"
}
},
{
"context": "Editor",
"bindings": {
"ctrl-shift-up": "editor::AddSelectionAbove",
"ctrl-shift-down": "editor::AddSelectionBelow",
"cmd-shift-space": "editor::SelectAll",
"ctrl-shift-m": "editor::SelectLargerSyntaxNode",
"cmd-shift-a": "editor::SelectLargerSyntaxNode",
"shift-f12": "editor::FindAllReferences",
"alt-cmd-down": "editor::GoToDefinition",
"alt-shift-cmd-down": "editor::FindAllReferences",
"ctrl-.": "editor::GoToHunk",
"ctrl-,": "editor::GoToPrevHunk",
"ctrl-backspace": "editor::DeleteToPreviousWordStart",
"ctrl-delete": "editor::DeleteToNextWordEnd",
"cmd-shift-enter": "editor::NewlineAbove",
"cmd-enter": "editor::NewlineBelow"
}
},
{
"context": "Editor && mode == full",
"bindings": {
"cmd-r": "outline::Toggle"
}
},
{
"context": "Pane",
"bindings": {
"f4": "search::SelectNextMatch",
"shift-f4": "search::SelectPrevMatch"
}
},
{
"context": "Workspace",
"bindings": {
"ctrl-`": "dock::FocusDock",
"cmd-k cmd-b": "workspace::ToggleLeftSidebar",
"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

@@ -0,0 +1,91 @@
[
{
"bindings": {
"cmd-shift-o": "projects::OpenRecent",
"cmd-alt-tab": "project_panel::ToggleFocus"
}
},
{
"context": "Editor",
"bindings": {
"cmd-l": "go_to_line::Toggle",
"ctrl-shift-d": "editor::DuplicateLine",
"cmd-b": "editor::GoToDefinition",
"cmd-j": "editor::ScrollCursorCenter",
"cmd-alt-enter": "editor::NewlineAbove",
"cmd-enter": "editor::NewlineBelow",
"cmd-shift-l": "editor::SelectLine",
"cmd-shift-t": "outline::Toggle",
"alt-backspace": "editor::DeleteToPreviousWordStart",
"alt-shift-backspace": "editor::DeleteToNextWordEnd",
"alt-delete": "editor::DeleteToNextWordEnd",
"alt-shift-delete": "editor::DeleteToNextWordEnd",
"ctrl-backspace": "editor::DeleteToPreviousSubwordStart",
"ctrl-delete": "editor::DeleteToNextSubwordEnd",
"alt-left": [
"editor::MoveToPreviousWordStart",
{
"stop_at_soft_wraps": true
}
],
"alt-right": [
"editor::MoveToNextWordEnd",
{
"stop_at_soft_wraps": true
}
],
"ctrl-left": "editor::MoveToPreviousSubwordStart",
"ctrl-right": "editor::MoveToNextSubwordEnd",
"cmd-shift-left": "editor::SelectToBeginningOfLine",
"cmd-shift-right": "editor::SelectToEndOfLine",
"alt-shift-left": [
"editor::SelectToBeginningOfLine",
{
"stop_at_soft_wraps": true
}
],
"alt-shift-right": [
"editor::SelectToEndOfLine",
{
"stop_at_soft_wraps": true
}
],
"ctrl-shift-left": "editor::SelectToPreviousSubwordStart",
"ctrl-shift-right": "editor::SelectToNextSubwordEnd"
}
},
{
"context": "Editor && mode == full",
"bindings": {}
},
{
"context": "BufferSearchBar",
"bindings": {
"ctrl-s": "search::SelectNextMatch",
"ctrl-shift-s": "search::SelectPrevMatch"
}
},
{
"context": "Workspace",
"bindings": {
"cmd-alt-ctrl-d": "workspace::ToggleLeftSidebar",
"cmd-t": "file_finder::Toggle",
"cmd-shift-t": "project_symbols::Toggle"
}
},
{
"context": "Pane",
"bindings": {
"alt-cmd-r": "search::ToggleRegex",
"ctrl-tab": "project_panel::ToggleFocus"
}
},
{
"context": "ProjectPanel",
"bindings": {}
},
{
"context": "Dock",
"bindings": {}
}
]

View File

@@ -1,323 +1,325 @@
[
{
"context": "Editor && VimControl && !VimWaiting",
"bindings": {
"g": [
"vim::PushOperator",
{
"Namespace": "G"
}
],
"i": [
"vim::PushOperator",
{
"Object": {
"around": false
}
}
],
"a": [
"vim::PushOperator",
{
"Object": {
"around": true
}
}
],
"h": "vim::Left",
"backspace": "vim::Backspace",
"j": "vim::Down",
"k": "vim::Up",
"l": "vim::Right",
"$": "vim::EndOfLine",
"shift-g": "vim::EndOfDocument",
"w": "vim::NextWordStart",
"shift-w": [
"vim::NextWordStart",
{
"ignorePunctuation": true
}
],
"e": "vim::NextWordEnd",
"shift-e": [
"vim::NextWordEnd",
{
"ignorePunctuation": true
}
],
"b": "vim::PreviousWordStart",
"shift-b": [
"vim::PreviousWordStart",
{
"ignorePunctuation": true
}
],
"%": "vim::Matching",
"ctrl-y": [
"vim::Scroll",
"LineUp"
],
"f": [
"vim::PushOperator",
{
"FindForward": {
"before": false
}
}
],
"t": [
"vim::PushOperator",
{
"FindForward": {
"before": true
}
}
],
"shift-f": [
"vim::PushOperator",
{
"FindBackward": {
"after": false
}
}
],
"shift-t": [
"vim::PushOperator",
{
"FindBackward": {
"after": true
}
}
],
"escape": "editor::Cancel",
"0": "vim::StartOfLine", // When no number operator present, use start of line motion
"1": [
"vim::Number",
1
],
"2": [
"vim::Number",
2
],
"3": [
"vim::Number",
3
],
"4": [
"vim::Number",
4
],
"5": [
"vim::Number",
5
],
"6": [
"vim::Number",
6
],
"7": [
"vim::Number",
7
],
"8": [
"vim::Number",
8
],
"9": [
"vim::Number",
9
]
{
"context": "Editor && VimControl && !VimWaiting",
"bindings": {
"g": [
"vim::PushOperator",
{
"Namespace": "G"
}
},
{
"context": "Editor && vim_mode == normal && vim_operator == none && !VimWaiting",
"bindings": {
"c": [
"vim::PushOperator",
"Change"
],
"shift-c": "vim::ChangeToEndOfLine",
"d": [
"vim::PushOperator",
"Delete"
],
"shift-d": "vim::DeleteToEndOfLine",
"y": [
"vim::PushOperator",
"Yank"
],
"z": [
"vim::PushOperator",
{
"Namespace": "Z"
}
],
"i": [
"vim::SwitchMode",
"Insert"
],
"shift-i": "vim::InsertFirstNonWhitespace",
"a": "vim::InsertAfter",
"shift-a": "vim::InsertEndOfLine",
"x": "vim::DeleteRight",
"shift-x": "vim::DeleteLeft",
"^": "vim::FirstNonWhitespace",
"o": "vim::InsertLineBelow",
"shift-o": "vim::InsertLineAbove",
"v": [
"vim::SwitchMode",
{
"Visual": {
"line": false
}
}
],
"shift-v": [
"vim::SwitchMode",
{
"Visual": {
"line": true
}
}
],
"p": "vim::Paste",
"u": "editor::Undo",
"ctrl-r": "editor::Redo",
"ctrl-o": "pane::GoBack",
"/": [
"buffer_search::Deploy",
{
"focus": true
}
],
"ctrl-f": [
"vim::Scroll",
"PageDown"
],
"ctrl-b": [
"vim::Scroll",
"PageUp"
],
"ctrl-d": [
"vim::Scroll",
"HalfPageDown"
],
"ctrl-u": [
"vim::Scroll",
"HalfPageUp"
],
"ctrl-e": [
"vim::Scroll",
"LineDown"
],
"r": [
"vim::PushOperator",
"Replace"
]
],
"i": [
"vim::PushOperator",
{
"Object": {
"around": false
}
}
},
{
"context": "Editor && vim_operator == n",
"bindings": {
"0": [
"vim::Number",
0
]
],
"a": [
"vim::PushOperator",
{
"Object": {
"around": true
}
}
},
{
"context": "Editor && vim_operator == g",
"bindings": {
"g": "vim::StartOfDocument",
"h": "editor::Hover",
"escape": [
"vim::SwitchMode",
"Normal"
]
],
"h": "vim::Left",
"backspace": "vim::Backspace",
"j": "vim::Down",
"enter": "vim::NextLineStart",
"k": "vim::Up",
"l": "vim::Right",
"$": "vim::EndOfLine",
"shift-g": "vim::EndOfDocument",
"w": "vim::NextWordStart",
"shift-w": [
"vim::NextWordStart",
{
"ignorePunctuation": true
}
},
{
"context": "Editor && vim_operator == c",
"bindings": {
"c": "vim::CurrentLine"
],
"e": "vim::NextWordEnd",
"shift-e": [
"vim::NextWordEnd",
{
"ignorePunctuation": true
}
},
{
"context": "Editor && vim_operator == d",
"bindings": {
"d": "vim::CurrentLine"
],
"b": "vim::PreviousWordStart",
"shift-b": [
"vim::PreviousWordStart",
{
"ignorePunctuation": true
}
},
{
"context": "Editor && vim_operator == y",
"bindings": {
"y": "vim::CurrentLine"
],
"%": "vim::Matching",
"ctrl-y": [
"vim::Scroll",
"LineUp"
],
"f": [
"vim::PushOperator",
{
"FindForward": {
"before": false
}
}
},
{
"context": "Editor && vim_operator == z",
"bindings": {
"t": "editor::ScrollCursorTop",
"z": "editor::ScrollCursorCenter",
"b": "editor::ScrollCursorBottom",
"escape": [
"vim::SwitchMode",
"Normal"
]
],
"t": [
"vim::PushOperator",
{
"FindForward": {
"before": true
}
}
},
{
"context": "Editor && VimObject",
"bindings": {
"w": "vim::Word",
"shift-w": [
"vim::Word",
{
"ignorePunctuation": true
}
],
"s": "vim::Sentence",
"'": "vim::Quotes",
"`": "vim::BackQuotes",
"\"": "vim::DoubleQuotes",
"(": "vim::Parentheses",
")": "vim::Parentheses",
"[": "vim::SquareBrackets",
"]": "vim::SquareBrackets",
"{": "vim::CurlyBrackets",
"}": "vim::CurlyBrackets",
"<": "vim::AngleBrackets",
">": "vim::AngleBrackets"
],
"shift-f": [
"vim::PushOperator",
{
"FindBackward": {
"after": false
}
}
},
{
"context": "Editor && vim_mode == visual && !VimWaiting",
"bindings": {
"u": "editor::Undo",
"c": "vim::VisualChange",
"d": "vim::VisualDelete",
"x": "vim::VisualDelete",
"y": "vim::VisualYank",
"p": "vim::VisualPaste",
"r": [
"vim::PushOperator",
"Replace"
]
}
},
{
"context": "Editor && vim_mode == insert",
"bindings": {
"escape": "vim::NormalBefore",
"ctrl-c": "vim::NormalBefore"
}
},
{
"context": "Editor && VimWaiting",
"bindings": {
"tab": "vim::Tab",
"enter": "vim::Enter",
"escape": "editor::Cancel"
],
"shift-t": [
"vim::PushOperator",
{
"FindBackward": {
"after": true
}
}
],
"escape": "editor::Cancel",
"0": "vim::StartOfLine", // When no number operator present, use start of line motion
"1": [
"vim::Number",
1
],
"2": [
"vim::Number",
2
],
"3": [
"vim::Number",
3
],
"4": [
"vim::Number",
4
],
"5": [
"vim::Number",
5
],
"6": [
"vim::Number",
6
],
"7": [
"vim::Number",
7
],
"8": [
"vim::Number",
8
],
"9": [
"vim::Number",
9
]
}
]
},
{
"context": "Editor && vim_mode == normal && vim_operator == none && !VimWaiting",
"bindings": {
"c": [
"vim::PushOperator",
"Change"
],
"shift-c": "vim::ChangeToEndOfLine",
"d": [
"vim::PushOperator",
"Delete"
],
"shift-d": "vim::DeleteToEndOfLine",
"y": [
"vim::PushOperator",
"Yank"
],
"z": [
"vim::PushOperator",
{
"Namespace": "Z"
}
],
"i": [
"vim::SwitchMode",
"Insert"
],
"shift-i": "vim::InsertFirstNonWhitespace",
"a": "vim::InsertAfter",
"shift-a": "vim::InsertEndOfLine",
"x": "vim::DeleteRight",
"shift-x": "vim::DeleteLeft",
"^": "vim::FirstNonWhitespace",
"o": "vim::InsertLineBelow",
"shift-o": "vim::InsertLineAbove",
"v": [
"vim::SwitchMode",
{
"Visual": {
"line": false
}
}
],
"shift-v": [
"vim::SwitchMode",
{
"Visual": {
"line": true
}
}
],
"p": "vim::Paste",
"u": "editor::Undo",
"ctrl-r": "editor::Redo",
"ctrl-o": "pane::GoBack",
"/": [
"buffer_search::Deploy",
{
"focus": true
}
],
"ctrl-f": [
"vim::Scroll",
"PageDown"
],
"ctrl-b": [
"vim::Scroll",
"PageUp"
],
"ctrl-d": [
"vim::Scroll",
"HalfPageDown"
],
"ctrl-u": [
"vim::Scroll",
"HalfPageUp"
],
"ctrl-e": [
"vim::Scroll",
"LineDown"
],
"r": [
"vim::PushOperator",
"Replace"
]
}
},
{
"context": "Editor && vim_operator == n",
"bindings": {
"0": [
"vim::Number",
0
]
}
},
{
"context": "Editor && vim_operator == g",
"bindings": {
"g": "vim::StartOfDocument",
"h": "editor::Hover",
"escape": [
"vim::SwitchMode",
"Normal"
],
"d": "editor::GoToDefinition"
}
},
{
"context": "Editor && vim_operator == c",
"bindings": {
"c": "vim::CurrentLine"
}
},
{
"context": "Editor && vim_operator == d",
"bindings": {
"d": "vim::CurrentLine"
}
},
{
"context": "Editor && vim_operator == y",
"bindings": {
"y": "vim::CurrentLine"
}
},
{
"context": "Editor && vim_operator == z",
"bindings": {
"t": "editor::ScrollCursorTop",
"z": "editor::ScrollCursorCenter",
"b": "editor::ScrollCursorBottom",
"escape": [
"vim::SwitchMode",
"Normal"
]
}
},
{
"context": "Editor && VimObject",
"bindings": {
"w": "vim::Word",
"shift-w": [
"vim::Word",
{
"ignorePunctuation": true
}
],
"s": "vim::Sentence",
"'": "vim::Quotes",
"`": "vim::BackQuotes",
"\"": "vim::DoubleQuotes",
"(": "vim::Parentheses",
")": "vim::Parentheses",
"[": "vim::SquareBrackets",
"]": "vim::SquareBrackets",
"{": "vim::CurlyBrackets",
"}": "vim::CurlyBrackets",
"<": "vim::AngleBrackets",
">": "vim::AngleBrackets"
}
},
{
"context": "Editor && vim_mode == visual && !VimWaiting",
"bindings": {
"u": "editor::Undo",
"c": "vim::VisualChange",
"d": "vim::VisualDelete",
"x": "vim::VisualDelete",
"y": "vim::VisualYank",
"p": "vim::VisualPaste",
"r": [
"vim::PushOperator",
"Replace"
]
}
},
{
"context": "Editor && vim_mode == insert",
"bindings": {
"escape": "vim::NormalBefore",
"ctrl-c": "vim::NormalBefore"
}
},
{
"context": "Editor && VimWaiting",
"bindings": {
"tab": "vim::Tab",
"enter": "vim::Enter",
"escape": "editor::Cancel"
}
}
]

View File

@@ -1,249 +1,287 @@
{
// The name of the Zed theme to use for the UI
"theme": "One Dark",
// The name of a font to use for rendering text in the editor
"buffer_font_family": "Zed Mono",
// The default font size for text in the editor
"buffer_font_size": 15,
// The factor to grow the active pane by. Defaults to 1.0
// which gives the same size as all other panes.
"active_pane_magnification": 1.0,
// Whether to enable vim modes and key bindings
"vim_mode": false,
// Whether to show the informational hover box when moving the mouse
// over symbols in the editor.
"hover_popover_enabled": true,
// Whether to confirm before quitting Zed.
"confirm_quit": false,
// Whether the cursor blinks in the editor.
"cursor_blink": true,
// Whether to pop the completions menu while typing in an editor without
// explicitly requesting it.
"show_completions_on_input": true,
// Whether the screen sharing icon is showed in the os status bar.
"show_call_status_icon": true,
// Whether new projects should start out 'online'. Online projects
// appear in the contacts panel under your name, so that your contacts
// can see which projects you are working on. Regardless of this
// setting, projects keep their last online status when you reopen them.
"projects_online_by_default": true,
// Whether to use language servers to provide code intelligence.
"enable_language_server": true,
// When to automatically save edited buffers. This setting can
// take four values.
//
// 1. Never automatically save:
// "autosave": "off",
// 2. Save when changing focus away from the Zed window:
// "autosave": "on_window_change",
// 3. Save when changing focus away from a specific buffer:
// "autosave": "on_focus_change",
// 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": "right",
// Whether or not to perform a buffer format before saving
"format_on_save": "on",
// 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"
// 2. Format code using an external command:
// "format_on_save": {
// "external": {
// "command": "prettier",
// "arguments": ["--stdin-filepath", "{buffer_path}"]
// }
// The name of the Zed theme to use for the UI
"theme": "One Dark",
// Features that can be globally enabled or disabled
"features": {
// Show Copilot icon in status bar
"copilot": true
},
// The name of a font to use for rendering text in the editor
"buffer_font_family": "Zed Mono",
// The OpenType features to enable for text in the editor.
"buffer_font_features": {
// Disable ligatures:
// "calt": false
},
// The default font size for text in the editor
"buffer_font_size": 15,
// The factor to grow the active pane by. Defaults to 1.0
// which gives the same size as all other panes.
"active_pane_magnification": 1.0,
// Whether to enable vim modes and key bindings
"vim_mode": false,
// Whether to show the informational hover box when moving the mouse
// over symbols in the editor.
"hover_popover_enabled": true,
// Whether to confirm before quitting Zed.
"confirm_quit": false,
// Whether the cursor blinks in the editor.
"cursor_blink": true,
// Whether to pop the completions menu while typing in an editor without
// explicitly requesting it.
"show_completions_on_input": true,
// Controls whether copilot provides suggestion immediately
// or waits for a `copilot::Toggle`
"show_copilot_suggestions": true,
// Whether to show tabs and spaces in the editor.
// This setting can take two values:
//
// 1. Draw tabs and spaces only for the selected text (default):
// "selection"
// 2. Do not draw any tabs or spaces:
// "none"
// 3. Draw all invisible symbols:
// "all"
"show_whitespaces": "selection",
// 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.
"enable_language_server": true,
// When to automatically save edited buffers. This setting can
// take four values.
//
// 1. Never automatically save:
// "autosave": "off",
// 2. Save when changing focus away from the Zed window:
// "autosave": "on_window_change",
// 3. Save when changing focus away from a specific buffer:
// "autosave": "on_focus_change",
// 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 or not to ensure there's a single newline at the end of a buffer
// when saving it.
"ensure_final_newline_on_save": true,
// Whether or not to perform a buffer format before saving
"format_on_save": "on",
// 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"
// 2. Format code using an external command:
// "format_on_save": {
// "external": {
// "command": "prettier",
// "arguments": ["--stdin-filepath", "{buffer_path}"]
// }
// }
"formatter": "language_server",
// How to soft-wrap long lines of text. This setting can take
// three values:
//
// 1. Do not soft wrap.
// "soft_wrap": "none",
// 2. Soft wrap lines that overflow the editor:
// "soft_wrap": "editor_width",
// 3. Soft wrap lines at the preferred line length
// "soft_wrap": "preferred_line_length",
"soft_wrap": "none",
// The column at which to soft-wrap lines, for buffers where soft-wrap
// is enabled.
"preferred_line_length": 80,
// Whether to indent lines using tab characters, as opposed to multiple
// spaces.
"hard_tabs": false,
// How many columns a tab should occupy.
"tab_size": 4,
// Control what info is collected by Zed.
"telemetry": {
// Send debug info like crash reports.
"diagnostics": true,
// Send anonymized usage data like what languages you're using Zed with.
"metrics": true
},
// Automatically update Zed
"auto_update": true,
// Git gutter behavior configuration.
"git": {
// Control whether the git gutter is shown. May take 2 values:
// 1. Show the gutter
// "git_gutter": "tracked_files"
// 2. Hide the gutter
// "git_gutter": "hide"
"git_gutter": "tracked_files"
},
"copilot": {
// The set of glob patterns for which copilot should be disabled
// in any matching file.
"disabled_globs": [
".env"
]
},
// Settings specific to journaling
"journal": {
// The path of the directory where journal entries are stored
"path": "~",
// What format to display the hours in
// May take 2 values:
// 1. hour12
// 2. hour24
"hour_format": "hour12"
},
// Settings specific to the terminal
"terminal": {
// What shell to use when opening a terminal. May take 3 values:
// 1. Use the system's default terminal configuration in /etc/passwd
// "shell": "system"
// 2. A program:
// "shell": {
// "program": "sh"
// }
// 3. A program with arguments:
// "shell": {
// "with_arguments": {
// "program": "/bin/bash",
// "arguments": ["--login"]
// }
// }
"formatter": "language_server",
// How to soft-wrap long lines of text. This setting can take
// three values:
"shell": "system",
// 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
// first project directory strategy if unsuccessful
// "working_directory": "current_project_directory"
// 2. Use the first project in this workspace's directory
// "working_directory": "first_project_directory"
// 3. Always use this platform's home directory (if we can find it)
// "working_directory": "always_home"
// 4. Always use a specific directory. This value will be shell expanded.
// If this path is not a valid directory the terminal will default to
// this platform's home directory (if we can find it)
// "working_directory": {
// "always": {
// "directory": "~/zed/projects/"
// }
// }
//
// 1. Do not soft wrap.
// "soft_wrap": "none",
// 2. Soft wrap lines that overflow the editor:
// "soft_wrap": "editor_width",
// 3. Soft wrap lines at the preferred line length
// "soft_wrap": "preferred_line_length",
"soft_wrap": "none",
// The column at which to soft-wrap lines, for buffers where soft-wrap
// is enabled.
"preferred_line_length": 80,
// Whether to indent lines using tab characters, as opposed to multiple
// spaces.
"hard_tabs": false,
// How many columns a tab should occupy.
"tab_size": 4,
// Control what info Zed sends to our servers
"telemetry": {
// Send debug info like crash reports.
"diagnostics": true,
// Send anonymized usage data like what languages you're using Zed with.
"metrics": true
//
"working_directory": "current_project_directory",
// Set the cursor blinking behavior in the terminal.
// May take 4 values:
// 1. Never blink the cursor, ignoring the terminal mode
// "blinking": "off",
// 2. Default the cursor blink to off, but allow the terminal to
// set blinking
// "blinking": "terminal_controlled",
// 3. Always blink the cursor, ignoring the terminal mode
// "blinking": "on",
"blinking": "terminal_controlled",
// Set whether Alternate Scroll mode (code: ?1007) is active by default.
// Alternate Scroll mode converts mouse scroll events into up / down key
// presses when in the alternate screen (e.g. when running applications
// like vim or less). The terminal can still set and unset this mode.
// May take 2 values:
// 1. Default alternate scroll mode to on
// "alternate_scroll": "on",
// 2. Default alternate scroll mode to off
// "alternate_scroll": "off",
"alternate_scroll": "off",
// Set whether the option key behaves as the meta key.
// May take 2 values:
// 1. Rely on default platform handling of option key, on macOS
// this means generating certain unicode characters
// "option_to_meta": false,
// 2. Make the option keys behave as a 'meta' key, e.g. for emacs
// "option_to_meta": true,
"option_as_meta": false,
// Whether or not selecting text in the terminal will automatically
// 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.
"env": {
// "KEY": "value1:value2"
},
// Automatically update Zed
"auto_update": true,
// Git gutter behavior configuration.
"git": {
// Control whether the git gutter is shown. May take 2 values:
// 1. Show the gutter
// "git_gutter": "tracked_files"
// 2. Hide the gutter
// "git_gutter": "hide"
"git_gutter": "tracked_files"
// Set the terminal's line height.
// May take 3 values:
// 1. Use a line height that's comfortable for reading, 1.618
// "line_height": "comfortable"
// 2. Use a standard line height, 1.3. This option is useful for TUIs,
// particularly if they use box characters
// "line_height": "standard",
// 3. Use a custom line height.
// "line_height": {
// "custom": 2
// },
//
"line_height": "comfortable"
// Set the terminal's font size. If this option is not included,
// the terminal will default to matching the buffer's font size.
// "font_size": "15"
// Set the terminal's font family. If this option is not included,
// the terminal will default to matching the buffer's font family.
// "font_family": "Zed Mono"
},
// Different settings for specific languages.
"languages": {
"Plain Text": {
"soft_wrap": "preferred_line_length"
},
// Settings specific to journaling
"journal": {
// The path of the directory where journal entries are stored
"path": "~",
// What format to display the hours in
// May take 2 values:
// 1. hour12
// 2. hour24
"hour_format": "hour12"
"Elixir": {
"tab_size": 2
},
// Settings specific to the terminal
"terminal": {
// What shell to use when opening a terminal. May take 3 values:
// 1. Use the system's default terminal configuration (e.g. $TERM).
// "shell": "system"
// 2. A program:
// "shell": {
// "program": "sh"
// }
// 3. A program with arguments:
// "shell": {
// "with_arguments": {
// "program": "/bin/bash",
// "arguments": ["--login"]
// }
// }
"shell": "system",
// 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
// first project directory strategy if unsuccessful
// "working_directory": "current_project_directory"
// 2. Use the first project in this workspace's directory
// "working_directory": "first_project_directory"
// 3. Always use this platform's home directory (if we can find it)
// "working_directory": "always_home"
// 4. Always use a specific directory. This value will be shell expanded.
// If this path is not a valid directory the terminal will default to
// this platform's home directory (if we can find it)
// "working_directory": {
// "always": {
// "directory": "~/zed/projects/"
// }
// }
//
//
"working_directory": "current_project_directory",
// Set the cursor blinking behavior in the terminal.
// May take 4 values:
// 1. Never blink the cursor, ignoring the terminal mode
// "blinking": "off",
// 2. Default the cursor blink to off, but allow the terminal to
// set blinking
// "blinking": "terminal_controlled",
// 3. Always blink the cursor, ignoring the terminal mode
// "blinking": "on",
"blinking": "terminal_controlled",
// Set whether Alternate Scroll mode (code: ?1007) is active by default.
// Alternate Scroll mode converts mouse scroll events into up / down key
// presses when in the alternate screen (e.g. when running applications
// like vim or less). The terminal can still set and unset this mode.
// May take 2 values:
// 1. Default alternate scroll mode to on
// "alternate_scroll": "on",
// 2. Default alternate scroll mode to off
// "alternate_scroll": "off",
"alternate_scroll": "off",
// Set whether the option key behaves as the meta key.
// May take 2 values:
// 1. Rely on default platform handling of option key, on macOS
// this means generating certain unicode characters
// "option_to_meta": false,
// 2. Make the option keys behave as a 'meta' key, e.g. for emacs
// "option_to_meta": true,
"option_as_meta": false,
// Whether or not selecting text in the terminal will automatically
// 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.
"env": {
// "KEY": "value1:value2"
}
// Set the terminal's font size. If this option is not included,
// the terminal will default to matching the buffer's font size.
// "font_size": "15"
// Set the terminal's font family. If this option is not included,
// the terminal will default to matching the buffer's font family.
// "font_family": "Zed Mono"
"Go": {
"tab_size": 4,
"hard_tabs": true
},
// Different settings for specific languages.
"languages": {
"Plain Text": {
"soft_wrap": "preferred_line_length"
},
"C": {
"tab_size": 2
},
"C++": {
"tab_size": 2
},
"Elixir": {
"tab_size": 2
},
"Go": {
"tab_size": 4,
"hard_tabs": true
},
"Markdown": {
"soft_wrap": "preferred_line_length"
},
"Rust": {
"tab_size": 4
},
"JavaScript": {
"tab_size": 2
},
"TypeScript": {
"tab_size": 2
},
"TSX": {
"tab_size": 2
},
"YAML": {
"tab_size": 2
}
"Markdown": {
"soft_wrap": "preferred_line_length"
},
// LSP Specific settings.
"lsp": {
// Specify the LSP name as a key here.
// As of 8/10/22, supported LSPs are:
// pyright
// gopls
// rust-analyzer
// typescript-language-server
// vscode-json-languageserver
// "rust-analyzer": {
// //These initialization options are merged into Zed's defaults
// "initialization_options": {
// "checkOnSave": {
// "command": "clippy"
// }
// }
// }
"JavaScript": {
"tab_size": 2
},
"TypeScript": {
"tab_size": 2
},
"TSX": {
"tab_size": 2
},
"YAML": {
"tab_size": 2
},
"JSON": {
"tab_size": 2
}
},
// LSP Specific settings.
"lsp": {
// Specify the LSP name as a key here.
// As of 8/10/22, supported LSPs are:
// pyright
// gopls
// rust-analyzer
// typescript-language-server
// vscode-json-languageserver
// "rust-analyzer": {
// //These initialization options are merged into Zed's defaults
// "initialization_options": {
// "checkOnSave": {
// "command": "clippy"
// }
// }
// }
}
}

View File

@@ -7,5 +7,5 @@
// custom settings, run the `open default settings` command
// from the command palette or from `Zed` application menu.
{
"buffer_font_size": 15
"buffer_font_size": 15
}

View File

@@ -17,5 +17,5 @@ project = { path = "../project" }
settings = { path = "../settings" }
util = { path = "../util" }
workspace = { path = "../workspace" }
futures = "0.3"
smallvec = { version = "1.6", features = ["union"] }
futures.workspace = true
smallvec.workspace = true

View File

@@ -2,8 +2,10 @@ use auto_update::{AutoUpdateStatus, AutoUpdater, DismissErrorMessage};
use editor::Editor;
use futures::StreamExt;
use gpui::{
actions, elements::*, platform::CursorStyle, Action, AppContext, Entity, ModelHandle,
MouseButton, MutableAppContext, RenderContext, View, ViewContext, ViewHandle,
actions, anyhow,
elements::*,
platform::{CursorStyle, MouseButton},
AppContext, Entity, ModelHandle, View, ViewContext, ViewHandle,
};
use language::{LanguageRegistry, LanguageServerBinaryStatus};
use project::{LanguageServerProgress, Project};
@@ -43,10 +45,10 @@ struct PendingWork<'a> {
struct Content {
icon: Option<&'static str>,
message: String,
action: Option<Box<dyn Action>>,
on_click: Option<Arc<dyn Fn(&mut ActivityIndicator, &mut ViewContext<ActivityIndicator>)>>,
}
pub fn init(cx: &mut MutableAppContext) {
pub fn init(cx: &mut AppContext) {
cx.add_action(ActivityIndicator::show_error_message);
cx.add_action(ActivityIndicator::dismiss_error_message);
}
@@ -61,21 +63,18 @@ impl ActivityIndicator {
let auto_updater = AutoUpdater::get(cx);
let this = cx.add_view(|cx: &mut ViewContext<Self>| {
let mut status_events = languages.language_server_binary_statuses();
cx.spawn_weak(|this, mut cx| async move {
cx.spawn(|this, mut cx| async move {
while let Some((language, event)) = status_events.next().await {
if let Some(this) = this.upgrade(&cx) {
this.update(&mut cx, |this, cx| {
this.statuses.retain(|s| s.name != language.name());
this.statuses.push(LspStatus {
name: language.name(),
status: event,
});
cx.notify();
this.update(&mut cx, |this, cx| {
this.statuses.retain(|s| s.name != language.name());
this.statuses.push(LspStatus {
name: language.name(),
status: event,
});
} else {
break;
}
cx.notify();
})?;
}
anyhow::Ok(())
})
.detach();
cx.observe(&project, |_, _, cx| cx.notify()).detach();
@@ -170,7 +169,7 @@ impl ActivityIndicator {
.flatten()
}
fn content_to_render(&mut self, cx: &mut RenderContext<Self>) -> Content {
fn content_to_render(&mut self, cx: &mut ViewContext<Self>) -> Content {
// Show any language server has pending activity.
let mut pending_work = self.pending_language_server_work(cx);
if let Some(PendingWork {
@@ -200,7 +199,7 @@ impl ActivityIndicator {
return Content {
icon: None,
message,
action: None,
on_click: None,
};
}
@@ -231,7 +230,7 @@ impl ActivityIndicator {
downloading.join(", "),
if downloading.len() > 1 { "s" } else { "" }
),
action: None,
on_click: None,
};
} else if !checking_for_update.is_empty() {
return Content {
@@ -245,7 +244,7 @@ impl ActivityIndicator {
""
}
),
action: None,
on_click: None,
};
} else if !failed.is_empty() {
return Content {
@@ -255,7 +254,9 @@ impl ActivityIndicator {
failed.join(", "),
if failed.len() > 1 { "s" } else { "" }
),
action: Some(Box::new(ShowErrorMessage)),
on_click: Some(Arc::new(|this, cx| {
this.show_error_message(&Default::default(), cx)
})),
};
}
@@ -265,27 +266,31 @@ impl ActivityIndicator {
AutoUpdateStatus::Checking => Content {
icon: Some(DOWNLOAD_ICON),
message: "Checking for Zed updates…".to_string(),
action: None,
on_click: None,
},
AutoUpdateStatus::Downloading => Content {
icon: Some(DOWNLOAD_ICON),
message: "Downloading Zed update…".to_string(),
action: None,
on_click: None,
},
AutoUpdateStatus::Installing => Content {
icon: Some(DOWNLOAD_ICON),
message: "Installing Zed update…".to_string(),
action: None,
on_click: None,
},
AutoUpdateStatus::Updated => Content {
icon: None,
message: "Click to restart and update Zed".to_string(),
action: Some(Box::new(workspace::Restart)),
on_click: Some(Arc::new(|_, cx| {
workspace::restart(&Default::default(), cx)
})),
},
AutoUpdateStatus::Errored => Content {
icon: Some(WARNING_ICON),
message: "Auto update failed".to_string(),
action: Some(Box::new(DismissErrorMessage)),
on_click: Some(Arc::new(|this, cx| {
this.dismiss_error_message(&Default::default(), cx)
})),
},
AutoUpdateStatus::Idle => Default::default(),
};
@@ -295,7 +300,7 @@ impl ActivityIndicator {
return Content {
icon: None,
message: most_recent_active_task.to_string(),
action: None,
on_click: None,
};
}
@@ -312,21 +317,21 @@ impl View for ActivityIndicator {
"ActivityIndicator"
}
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
let Content {
icon,
message,
action,
on_click,
} = self.content_to_render(cx);
let mut element = MouseEventHandler::<Self>::new(0, cx, |state, cx| {
let mut element = MouseEventHandler::<Self, _>::new(0, cx, |state, cx| {
let theme = &cx
.global::<Settings>()
.theme
.workspace
.status_bar
.lsp_status;
let style = if state.hovered() && action.is_some() {
let style = if state.hovered() && on_click.is_some() {
theme.hover.as_ref().unwrap_or(&theme.default)
} else {
&theme.default
@@ -340,31 +345,27 @@ impl View for ActivityIndicator {
.contained()
.with_margin_right(style.icon_spacing)
.aligned()
.named("activity-icon")
.into_any_named("activity-icon")
}))
.with_child(
Text::new(message, style.message.clone())
.with_soft_wrap(false)
.aligned()
.boxed(),
.aligned(),
)
.constrained()
.with_height(style.height)
.contained()
.with_style(style.container)
.aligned()
.boxed()
});
if let Some(action) = action {
if let Some(on_click) = on_click.clone() {
element = element
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, move |_, cx| {
cx.dispatch_any_action(action.boxed_clone())
});
.on_click(MouseButton::Left, move |_, this, cx| on_click(this, cx));
}
element.boxed()
element.into_any()
}
}

View File

@@ -10,6 +10,5 @@ doctest = false
[dependencies]
gpui = { path = "../gpui" }
anyhow = "1.0.38"
anyhow.workspace = true
rust-embed = { version = "6.3", features = ["include-exclude"] }

View File

@@ -18,11 +18,12 @@ settings = { path = "../settings" }
theme = { path = "../theme" }
workspace = { path = "../workspace" }
util = { path = "../util" }
anyhow = "1.0.38"
anyhow.workspace = true
isahc = "1.7"
lazy_static = "1.4"
log = "0.4"
serde = { version = "1.0", features = ["derive", "rc"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
smol = "1.2.5"
tempdir = "0.3.7"
lazy_static.workspace = true
log.workspace = true
serde.workspace = true
serde_derive.workspace = true
serde_json.workspace = true
smol.workspace = true
tempdir.workspace = true

View File

@@ -1,19 +1,21 @@
mod update_notification;
use anyhow::{anyhow, Context, Result};
use client::{http::HttpClient, ZED_SECRET_CLIENT_TOKEN};
use client::{ZED_APP_PATH, ZED_APP_VERSION};
use client::{Client, 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,
MutableAppContext, Task, WeakViewHandle,
Task, WeakViewHandle,
};
use isahc::AsyncBody;
use serde::Deserialize;
use serde_derive::Serialize;
use settings::Settings;
use smol::{fs::File, io::AsyncReadExt, process::Command};
use std::{ffi::OsString, sync::Arc, time::Duration};
use update_notification::UpdateNotification;
use util::channel::ReleaseChannel;
use util::http::HttpClient;
use workspace::Workspace;
const SHOULD_SHOW_UPDATE_NOTIFICATION_KEY: &str = "auto-updater-should-show-updated-notification";
@@ -21,6 +23,13 @@ const POLL_INTERVAL: Duration = Duration::from_secs(60 * 60);
actions!(auto_update, [Check, DismissErrorMessage, ViewReleaseNotes]);
#[derive(Serialize)]
struct UpdateRequestBody {
installation_id: Option<Arc<str>>,
release_channel: Option<&'static str>,
telemetry: bool,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum AutoUpdateStatus {
Idle,
@@ -49,11 +58,10 @@ impl Entity for AutoUpdater {
type Event = ();
}
pub fn init(http_client: Arc<dyn HttpClient>, server_url: String, cx: &mut MutableAppContext) {
pub fn init(http_client: Arc<dyn HttpClient>, server_url: String, cx: &mut AppContext) {
if let Some(version) = (*ZED_APP_VERSION).or_else(|| cx.platform().app_version().ok()) {
let server_url = server_url;
let auto_updater = cx.add_model(|cx| {
let updater = AutoUpdater::new(version, http_client, server_url.clone());
let updater = AutoUpdater::new(version, http_client, server_url);
let mut update_subscription = cx
.global::<Settings>()
@@ -63,10 +71,10 @@ pub fn init(http_client: Arc<dyn HttpClient>, server_url: String, cx: &mut Mutab
cx.observe_global::<Settings, _>(move |updater, cx| {
if cx.global::<Settings>().auto_update {
if update_subscription.is_none() {
*(&mut update_subscription) = Some(updater.start_polling(cx))
update_subscription = Some(updater.start_polling(cx))
}
} else {
(&mut update_subscription).take();
update_subscription.take();
}
})
.detach();
@@ -74,28 +82,35 @@ pub fn init(http_client: Arc<dyn HttpClient>, server_url: String, cx: &mut Mutab
updater
});
cx.set_global(Some(auto_updater));
cx.add_global_action(|_: &Check, cx| {
if let Some(updater) = AutoUpdater::get(cx) {
updater.update(cx, |updater, cx| updater.poll(cx));
}
});
cx.add_global_action(move |_: &ViewReleaseNotes, cx| {
let latest_release_url = if cx.has_global::<ReleaseChannel>()
&& *cx.global::<ReleaseChannel>() == ReleaseChannel::Preview
{
format!("{server_url}/releases/preview/latest")
} else {
format!("{server_url}/releases/latest")
};
cx.platform().open_url(&latest_release_url);
});
cx.add_global_action(check);
cx.add_global_action(view_release_notes);
cx.add_action(UpdateNotification::dismiss);
}
}
pub fn check(_: &Check, cx: &mut AppContext) {
if let Some(updater) = AutoUpdater::get(cx) {
updater.update(cx, |updater, cx| updater.poll(cx));
}
}
fn view_release_notes(_: &ViewReleaseNotes, cx: &mut AppContext) {
if let Some(auto_updater) = AutoUpdater::get(cx) {
let server_url = &auto_updater.read(cx).server_url;
let latest_release_url = if cx.has_global::<ReleaseChannel>()
&& *cx.global::<ReleaseChannel>() == ReleaseChannel::Preview
{
format!("{server_url}/releases/preview/latest")
} else {
format!("{server_url}/releases/latest")
};
cx.platform().open_url(&latest_release_url);
}
}
pub fn notify_of_any_new_update(
workspace: WeakViewHandle<Workspace>,
cx: &mut MutableAppContext,
cx: &mut AppContext,
) -> Option<()> {
let updater = AutoUpdater::get(cx)?;
let version = updater.read(cx).current_version;
@@ -104,17 +119,15 @@ pub fn notify_of_any_new_update(
cx.spawn(|mut cx| async move {
let should_show_notification = should_show_notification.await?;
if should_show_notification {
if let Some(workspace) = workspace.upgrade(&cx) {
workspace.update(&mut cx, |workspace, cx| {
workspace.show_notification(0, cx, |cx| {
cx.add_view(|_| UpdateNotification::new(version))
});
updater
.read(cx)
.set_should_show_update_notification(false, cx)
.detach_and_log_err(cx);
workspace.update(&mut cx, |workspace, cx| {
workspace.show_notification(0, cx, |cx| {
cx.add_view(|_| UpdateNotification::new(version))
});
}
updater
.read(cx)
.set_should_show_update_notification(false, cx)
.detach_and_log_err(cx);
})?;
}
anyhow::Ok(())
})
@@ -124,7 +137,7 @@ pub fn notify_of_any_new_update(
}
impl AutoUpdater {
pub fn get(cx: &mut MutableAppContext) -> Option<ModelHandle<Self>> {
pub fn get(cx: &mut AppContext) -> Option<ModelHandle<Self>> {
cx.default_global::<Option<ModelHandle<Self>>>().clone()
}
@@ -243,7 +256,24 @@ impl AutoUpdater {
mounted_app_path.push("/");
let mut dmg_file = File::create(&dmg_path).await?;
let mut response = client.get(&release.url, Default::default(), true).await?;
let (installation_id, release_channel, telemetry) = cx.read(|cx| {
let installation_id = cx.global::<Arc<Client>>().telemetry().installation_id();
let release_channel = cx
.has_global::<ReleaseChannel>()
.then(|| cx.global::<ReleaseChannel>().display_name());
let telemetry = cx.global::<Settings>().telemetry().metrics();
(installation_id, release_channel, telemetry)
});
let request_body = AsyncBody::from(serde_json::to_string(&UpdateRequestBody {
installation_id,
release_channel,
telemetry,
})?);
let mut response = client.get(&release.url, request_body, true).await?;
smol::io::copy(response.body_mut(), &mut dmg_file).await?;
log::info!("downloaded update. path:{:?}", dmg_path);

View File

@@ -1,8 +1,8 @@
use crate::ViewReleaseNotes;
use gpui::{
elements::{Flex, MouseEventHandler, Padding, ParentElement, Svg, Text},
platform::{AppVersion, CursorStyle},
Element, Entity, MouseButton, View, ViewContext,
platform::{AppVersion, CursorStyle, MouseButton},
Element, Entity, View, ViewContext,
};
use menu::Cancel;
use settings::Settings;
@@ -26,13 +26,13 @@ impl View for UpdateNotification {
"UpdateNotification"
}
fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> gpui::ElementBox {
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> gpui::AnyElement<Self> {
let theme = cx.global::<Settings>().theme.clone();
let theme = &theme.update_notification;
let app_name = cx.global::<ReleaseChannel>().display_name();
MouseEventHandler::<ViewReleaseNotes>::new(0, cx, |state, cx| {
MouseEventHandler::<ViewReleaseNotes, _>::new(0, cx, |state, cx| {
Flex::column()
.with_child(
Flex::row()
@@ -46,11 +46,10 @@ impl View for UpdateNotification {
.aligned()
.top()
.left()
.flex(1., true)
.boxed(),
.flex(1., true),
)
.with_child(
MouseEventHandler::<Cancel>::new(0, cx, |state, _| {
MouseEventHandler::<Cancel, _>::new(0, cx, |state, _| {
let style = theme.dismiss_button.style_for(state, false);
Svg::new("icons/x_mark_8.svg")
.with_color(style.color)
@@ -62,35 +61,32 @@ impl View for UpdateNotification {
.constrained()
.with_width(style.button_width)
.with_height(style.button_width)
.boxed()
})
.with_padding(Padding::uniform(5.))
.on_click(MouseButton::Left, move |_, cx| cx.dispatch_action(Cancel))
.on_click(MouseButton::Left, move |_, this, cx| {
this.dismiss(&Default::default(), cx)
})
.aligned()
.constrained()
.with_height(cx.font_cache().line_height(theme.message.text.font_size))
.aligned()
.top()
.flex_float()
.boxed(),
)
.boxed(),
.flex_float(),
),
)
.with_child({
let style = theme.action_message.style_for(state, false);
Text::new("View the release notes", style.text.clone())
.contained()
.with_style(style.container)
.boxed()
})
.contained()
.boxed()
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, |_, cx| {
cx.dispatch_action(ViewReleaseNotes)
.on_click(MouseButton::Left, |_, _, cx| {
crate::view_release_notes(&Default::default(), cx)
})
.boxed()
.into_any_named("update notification")
}
}

View File

@@ -18,6 +18,7 @@ search = { path = "../search" }
settings = { path = "../settings" }
theme = { path = "../theme" }
workspace = { path = "../workspace" }
outline = { path = "../outline" }
itertools = "0.10"
[dev-dependencies]

View File

@@ -1,12 +1,13 @@
use gpui::{
elements::*, AppContext, Entity, RenderContext, Subscription, View, ViewContext, ViewHandle,
elements::*, platform::MouseButton, AppContext, Entity, Subscription, View, ViewContext,
ViewHandle, WeakViewHandle,
};
use itertools::Itertools;
use search::ProjectSearchView;
use settings::Settings;
use workspace::{
item::{ItemEvent, ItemHandle},
ToolbarItemLocation, ToolbarItemView,
ToolbarItemLocation, ToolbarItemView, Workspace,
};
pub enum Event {
@@ -14,17 +15,21 @@ pub enum Event {
}
pub struct Breadcrumbs {
pane_focused: bool,
active_item: Option<Box<dyn ItemHandle>>,
project_search: Option<ViewHandle<ProjectSearchView>>,
subscription: Option<Subscription>,
workspace: WeakViewHandle<Workspace>,
}
impl Breadcrumbs {
pub fn new() -> Self {
pub fn new(workspace: &Workspace) -> Self {
Self {
pane_focused: false,
active_item: Default::default(),
subscription: Default::default(),
project_search: Default::default(),
workspace: workspace.weak_handle(),
}
}
}
@@ -38,25 +43,67 @@ impl View for Breadcrumbs {
"Breadcrumbs"
}
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
let active_item = match &self.active_item {
Some(active_item) => active_item,
None => return Empty::new().into_any(),
};
let not_editor = active_item.downcast::<editor::Editor>().is_none();
let theme = cx.global::<Settings>().theme.clone();
if let Some(breadcrumbs) = self
.active_item
.as_ref()
.and_then(|item| item.breadcrumbs(&theme, cx))
{
Flex::row()
.with_children(Itertools::intersperse_with(breadcrumbs.into_iter(), || {
Label::new("", theme.breadcrumbs.text.clone()).boxed()
}))
.contained()
.with_style(theme.breadcrumbs.container)
let style = &theme.workspace.breadcrumbs;
let breadcrumbs = match active_item.breadcrumbs(&theme, cx) {
Some(breadcrumbs) => breadcrumbs,
None => return Empty::new().into_any(),
}
.into_iter()
.map(|breadcrumb| {
Text::new(
breadcrumb.text,
theme.workspace.breadcrumbs.default.text.clone(),
)
.with_highlights(breadcrumb.highlights.unwrap_or_default())
.into_any()
});
let crumbs = Flex::row()
.with_children(Itertools::intersperse_with(breadcrumbs, || {
Label::new("", style.default.text.clone()).into_any()
}))
.constrained()
.with_height(theme.workspace.breadcrumb_height)
.contained();
if not_editor || !self.pane_focused {
return crumbs
.with_style(style.default.container)
.aligned()
.left()
.boxed()
} else {
Empty::new().boxed()
.into_any();
}
MouseEventHandler::<Breadcrumbs, Breadcrumbs>::new(0, cx, |state, _| {
let style = style.style_for(state, false);
crumbs.with_style(style.container)
})
.on_click(MouseButton::Left, |_, this, cx| {
if let Some(workspace) = this.workspace.upgrade(cx) {
workspace.update(cx, |workspace, cx| {
outline::toggle(workspace, &Default::default(), cx)
})
}
})
.with_tooltip::<Breadcrumbs>(
0,
"Show symbol outline".to_owned(),
Some(Box::new(outline::Toggle)),
theme.tooltip.clone(),
cx,
)
.aligned()
.left()
.into_any()
}
}
@@ -103,4 +150,8 @@ impl ToolbarItemView for Breadcrumbs {
current_location
}
}
fn pane_focus_update(&mut self, pane_focused: bool, _: &mut ViewContext<Self>) {
self.pane_focused = pane_focused;
}
}

View File

@@ -22,7 +22,7 @@ test-support = [
client = { path = "../client" }
collections = { path = "../collections" }
gpui = { path = "../gpui" }
log = "0.4"
log.workspace = true
live_kit_client = { path = "../live_kit_client" }
fs = { path = "../fs" }
language = { path = "../language" }
@@ -31,10 +31,10 @@ project = { path = "../project" }
settings = { path = "../settings" }
util = { path = "../util" }
anyhow = "1.0.38"
anyhow.workspace = true
async-broadcast = "0.4"
futures = "0.3"
postage = { version = "0.4.1", features = ["futures-traits"] }
futures.workspace = true
postage.workspace = true
[dev-dependencies]
client = { path = "../client", features = ["test-support"] }

View File

@@ -10,15 +10,15 @@ use futures::{future::Shared, FutureExt};
use postage::watch;
use gpui::{
AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext,
Subscription, Task, WeakModelHandle,
AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Subscription, Task,
WeakModelHandle,
};
use project::Project;
pub use participant::ParticipantLocation;
pub use room::Room;
pub fn init(client: Arc<Client>, user_store: ModelHandle<UserStore>, cx: &mut MutableAppContext) {
pub fn init(client: Arc<Client>, user_store: ModelHandle<UserStore>, cx: &mut AppContext) {
let active_call = cx.add_model(|cx| ActiveCall::new(client, user_store, cx));
cx.set_global(active_call);
}
@@ -264,12 +264,13 @@ impl ActiveCall {
Ok(())
}
pub fn hang_up(&mut self, cx: &mut ModelContext<Self>) -> Result<()> {
pub fn hang_up(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
cx.notify();
if let Some((room, _)) = self.room.take() {
room.update(cx, |room, cx| room.leave(cx))?;
cx.notify();
room.update(cx, |room, cx| room.leave(cx))
} else {
Task::ready(Ok(()))
}
Ok(())
}
pub fn share_project(

View File

@@ -10,17 +10,15 @@ use client::{
use collections::{BTreeMap, HashMap, HashSet};
use fs::Fs;
use futures::{FutureExt, StreamExt};
use gpui::{
AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task, WeakModelHandle,
};
use gpui::{AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Task, WeakModelHandle};
use language::LanguageRegistry;
use live_kit_client::{LocalTrackPublication, LocalVideoTrack, RemoteVideoTrackUpdate};
use postage::stream::Stream;
use project::Project;
use std::{mem, sync::Arc, time::Duration};
use std::{future::Future, mem, pin::Pin, sync::Arc, time::Duration};
use util::{post_inc, ResultExt, TryFutureExt};
pub const RECONNECT_TIMEOUT: Duration = client::RECEIVE_TIMEOUT;
pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30);
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Event {
@@ -55,7 +53,7 @@ pub struct Room {
leave_when_empty: bool,
client: Arc<Client>,
user_store: ModelHandle<UserStore>,
follows_by_leader_id: HashMap<PeerId, Vec<PeerId>>,
follows_by_leader_id_project_id: HashMap<(PeerId, u64), Vec<PeerId>>,
subscriptions: Vec<client::Subscription>,
pending_room_update: Option<Task<()>>,
maintain_connection: Option<Task<Option<()>>>,
@@ -64,10 +62,24 @@ pub struct Room {
impl Entity for Room {
type Event = Event;
fn release(&mut self, _: &mut MutableAppContext) {
fn release(&mut self, cx: &mut AppContext) {
if self.status.is_online() {
log::info!("room was released, sending leave message");
let _ = self.client.send(proto::LeaveRoom {});
self.leave_internal(cx).detach_and_log_err(cx);
}
}
fn app_will_quit(&mut self, cx: &mut AppContext) -> Option<Pin<Box<dyn Future<Output = ()>>>> {
if self.status.is_online() {
let leave = self.leave_internal(cx);
Some(
cx.background()
.spawn(async move {
leave.await.log_err();
})
.boxed(),
)
} else {
None
}
}
}
@@ -149,7 +161,7 @@ impl Room {
pending_room_update: None,
client,
user_store,
follows_by_leader_id: Default::default(),
follows_by_leader_id_project_id: Default::default(),
maintain_connection: Some(maintain_connection),
}
}
@@ -159,7 +171,7 @@ impl Room {
initial_project: Option<ModelHandle<Project>>,
client: Arc<Client>,
user_store: ModelHandle<UserStore>,
cx: &mut MutableAppContext,
cx: &mut AppContext,
) -> Task<Result<ModelHandle<Self>>> {
cx.spawn(|mut cx| async move {
let response = client.request(proto::CreateRoom {}).await?;
@@ -202,7 +214,7 @@ impl Room {
call: &IncomingCall,
client: Arc<Client>,
user_store: ModelHandle<UserStore>,
cx: &mut MutableAppContext,
cx: &mut AppContext,
) -> Task<Result<ModelHandle<Self>>> {
let room_id = call.room_id;
cx.spawn(|mut cx| async move {
@@ -234,13 +246,17 @@ impl Room {
&& self.pending_call_count == 0
}
pub(crate) fn leave(&mut self, cx: &mut ModelContext<Self>) -> Result<()> {
if self.status.is_offline() {
return Err(anyhow!("room is offline"));
}
pub(crate) fn leave(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
cx.notify();
cx.emit(Event::Left);
self.leave_internal(cx)
}
fn leave_internal(&mut self, cx: &mut AppContext) -> Task<Result<()>> {
if self.status.is_offline() {
return Task::ready(Err(anyhow!("room is offline")));
}
log::info!("leaving room");
for project in self.shared_projects.drain() {
@@ -254,6 +270,7 @@ impl Room {
if let Some(project) = project.upgrade(cx) {
project.update(cx, |project, cx| {
project.disconnected_from_host(cx);
project.close(cx);
});
}
}
@@ -266,8 +283,12 @@ impl Room {
self.live_kit.take();
self.pending_room_update.take();
self.maintain_connection.take();
self.client.send(proto::LeaveRoom {})?;
Ok(())
let leave_room = self.client.request(proto::LeaveRoom {});
cx.background().spawn(async move {
leave_room.await?;
anyhow::Ok(())
})
}
async fn maintain_connection(
@@ -277,14 +298,12 @@ impl Room {
) -> Result<()> {
let mut client_status = client.status();
loop {
let is_connected = client_status
.next()
.await
.map_or(false, |s| s.is_connected());
let _ = client_status.try_recv();
let is_connected = client_status.borrow().is_connected();
// Even if we're initially connected, any future change of the status means we momentarily disconnected.
if !is_connected || client_status.next().await.is_some() {
log::info!("detected client disconnection");
this.upgrade(&cx)
.ok_or_else(|| anyhow!("room was dropped"))?
.update(&mut cx, |this, cx| {
@@ -298,12 +317,7 @@ impl Room {
let client_reconnection = async {
let mut remaining_attempts = 3;
while remaining_attempts > 0 {
log::info!(
"waiting for client status change, remaining attempts {}",
remaining_attempts
);
let Some(status) = client_status.next().await else { break };
if status.is_connected() {
if client_status.borrow().is_connected() {
log::info!("client reconnected, attempting to rejoin room");
let Some(this) = this.upgrade(&cx) else { break };
@@ -317,7 +331,15 @@ impl Room {
} else {
remaining_attempts -= 1;
}
} else if client_status.borrow().is_signed_out() {
return false;
}
log::info!(
"waiting for client status change, remaining attempts {}",
remaining_attempts
);
client_status.next().await;
}
false
}
@@ -339,18 +361,20 @@ impl Room {
}
}
// The client failed to re-establish a connection to the server
// or an error occurred while trying to re-join the room. Either way
// we leave the room and return an error.
if let Some(this) = this.upgrade(&cx) {
log::info!("reconnection failed, leaving room");
let _ = this.update(&mut cx, |this, cx| this.leave(cx));
}
return Err(anyhow!(
"can't reconnect to room: client failed to re-establish connection"
));
break;
}
}
// The client failed to re-establish a connection to the server
// or an error occurred while trying to re-join the room. Either way
// we leave the room and return an error.
if let Some(this) = this.upgrade(&cx) {
log::info!("reconnection failed, leaving room");
let _ = this.update(&mut cx, |this, cx| this.leave(cx));
}
Err(anyhow!(
"can't reconnect to room: client failed to re-establish connection"
))
}
fn rejoin(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
@@ -395,7 +419,7 @@ impl Room {
false
});
let response = self.client.request(proto::RejoinRoom {
let response = self.client.request_envelope(proto::RejoinRoom {
id: self.id,
reshared_projects,
rejoined_projects,
@@ -403,6 +427,8 @@ impl Room {
cx.spawn(|this, mut cx| async move {
let response = response.await?;
let message_id = response.message_id;
let response = response.payload;
let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?;
this.update(&mut cx, |this, cx| {
this.status = RoomStatus::Online;
@@ -419,7 +445,7 @@ impl Room {
for rejoined_project in response.rejoined_projects {
if let Some(project) = projects.get(&rejoined_project.id) {
project.update(cx, |project, cx| {
project.rejoined(rejoined_project, cx).log_err();
project.rejoined(rejoined_project, message_id, cx).log_err();
});
}
}
@@ -459,9 +485,9 @@ impl Room {
self.participant_user_ids.contains(&user_id)
}
pub fn followers_for(&self, leader_id: PeerId) -> &[PeerId] {
self.follows_by_leader_id
.get(&leader_id)
pub fn followers_for(&self, leader_id: PeerId, project_id: u64) -> &[PeerId] {
self.follows_by_leader_id_project_id
.get(&(leader_id, project_id))
.map_or(&[], |v| v.as_slice())
}
@@ -598,7 +624,7 @@ impl Room {
if let Some(live_kit) = this.live_kit.as_ref() {
let tracks =
live_kit.room.remote_video_tracks(&peer_id.to_string());
live_kit.room.remote_video_tracks(&user.id.to_string());
for track in tracks {
this.remote_video_track_updated(
RemoteVideoTrackUpdate::Subscribed(track),
@@ -631,8 +657,9 @@ impl Room {
}
}
this.follows_by_leader_id.clear();
this.follows_by_leader_id_project_id.clear();
for follower in room.followers {
let project_id = follower.project_id;
let (leader, follower) = match (follower.leader_id, follower.follower_id) {
(Some(leader), Some(follower)) => (leader, follower),
@@ -643,8 +670,8 @@ impl Room {
};
let list = this
.follows_by_leader_id
.entry(leader)
.follows_by_leader_id_project_id
.entry((leader, project_id))
.or_insert(Vec::new());
if !list.contains(&follower) {
list.push(follower);
@@ -754,10 +781,10 @@ impl Room {
this.update(&mut cx, |this, cx| {
this.pending_call_count -= 1;
if this.should_leave() {
this.leave(cx)?;
this.leave(cx).detach_and_log_err(cx);
}
result
})?;
});
result?;
Ok(())
})
}

View File

@@ -13,11 +13,12 @@ name = "cli"
path = "src/main.rs"
[dependencies]
anyhow = "1.0"
anyhow.workspace = true
clap = { version = "3.1", features = ["derive"] }
dirs = "3.0"
ipc-channel = "0.16"
serde = { version = "1.0", features = ["derive", "rc"] }
serde.workspace = true
serde_derive.workspace = true
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.9"

View File

@@ -17,26 +17,28 @@ db = { path = "../db" }
gpui = { path = "../gpui" }
util = { path = "../util" }
rpc = { path = "../rpc" }
settings = { path = "../settings" }
staff_mode = { path = "../staff_mode" }
sum_tree = { path = "../sum_tree" }
anyhow = "1.0.38"
anyhow.workspace = true
async-recursion = "0.3"
async-tungstenite = { version = "0.16", features = ["async-tls"] }
futures = "0.3"
futures.workspace = true
image = "0.23"
isahc = "1.7"
lazy_static = "1.4.0"
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
parking_lot = "0.11.1"
postage = { version = "0.4.1", features = ["futures-traits"] }
rand = "0.8.3"
smol = "1.2.5"
thiserror = "1.0.29"
time = { version = "0.3", features = ["serde", "serde-well-known"] }
lazy_static.workspace = true
log.workspace = true
parking_lot.workspace = true
postage.workspace = true
rand.workspace = true
smol.workspace = true
thiserror.workspace = true
time.workspace = true
tiny_http = "0.8"
uuid = { version = "1.1.2", features = ["v4"] }
url = "2.2"
serde = { version = "*", features = ["derive"] }
settings = { path = "../settings" }
serde.workspace = true
serde_derive.workspace = true
tempfile = "3"
[dev-dependencies]
@@ -44,3 +46,4 @@ collections = { path = "../collections", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
rpc = { path = "../rpc", features = ["test-support"] }
settings = { path = "../settings", features = ["test-support"] }
util = { path = "../util", features = ["test-support"] }

View File

@@ -1,7 +1,6 @@
#[cfg(any(test, feature = "test-support"))]
pub mod test;
pub mod http;
pub mod telemetry;
pub mod user;
@@ -11,21 +10,24 @@ use async_tungstenite::tungstenite::{
error::Error as WebsocketError,
http::{Request, StatusCode},
};
use futures::{future::LocalBoxFuture, AsyncReadExt, FutureExt, SinkExt, StreamExt, TryStreamExt};
use futures::{
future::LocalBoxFuture, AsyncReadExt, FutureExt, SinkExt, StreamExt, TryFutureExt as _,
TryStreamExt,
};
use gpui::{
actions,
serde_json::{self, Value},
AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AnyWeakViewHandle, AppContext, AppVersion,
AsyncAppContext, Entity, ModelHandle, MutableAppContext, Task, View, ViewContext, ViewHandle,
platform::AppVersion,
serde_json::{self},
AnyModelHandle, AnyWeakModelHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity,
ModelHandle, Task, View, ViewContext, WeakViewHandle,
};
use http::HttpClient;
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, TelemetrySettings};
use settings::Settings;
use std::{
any::TypeId,
collections::HashMap,
@@ -41,9 +43,11 @@ use telemetry::Telemetry;
use thiserror::Error;
use url::Url;
use util::channel::ReleaseChannel;
use util::http::HttpClient;
use util::{ResultExt, TryFutureExt};
pub use rpc::*;
pub use telemetry::ClickhouseEvent;
pub use user::*;
lazy_static! {
@@ -66,12 +70,12 @@ pub const ZED_SECRET_CLIENT_TOKEN: &str = "618033988749894";
pub const INITIAL_RECONNECTION_DELAY: Duration = Duration::from_millis(100);
pub const CONNECTION_TIMEOUT: Duration = Duration::from_secs(5);
actions!(client, [Authenticate]);
actions!(client, [SignIn, SignOut]);
pub fn init(client: Arc<Client>, cx: &mut MutableAppContext) {
pub fn init(client: Arc<Client>, cx: &mut AppContext) {
cx.add_global_action({
let client = client.clone();
move |_: &Authenticate, cx| {
move |_: &SignIn, cx| {
let client = client.clone();
cx.spawn(
|cx| async move { client.authenticate_and_connect(true, &cx).log_err().await },
@@ -79,6 +83,16 @@ pub fn init(client: Arc<Client>, cx: &mut MutableAppContext) {
.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();
}
});
}
pub struct Client {
@@ -120,7 +134,7 @@ pub enum EstablishConnectionError {
#[error("{0}")]
Other(#[from] anyhow::Error),
#[error("{0}")]
Http(#[from] http::Error),
Http(#[from] util::http::Error),
#[error("{0}")]
Io(#[from] std::io::Error),
#[error("{0}")]
@@ -169,6 +183,10 @@ impl Status {
pub fn is_connected(&self) -> bool {
matches!(self, Self::Connected { .. })
}
pub fn is_signed_out(&self) -> bool {
matches!(self, Self::SignedOut | Self::UpgradeRequired)
}
}
struct ClientState {
@@ -204,7 +222,7 @@ enum WeakSubscriber {
enum Subscriber {
Model(AnyModelHandle),
View(AnyViewHandle),
View(AnyWeakViewHandle),
}
#[derive(Clone, Debug)]
@@ -280,7 +298,7 @@ impl<T: Entity> PendingEntitySubscription<T> {
state
.entities_by_type_and_remote_id
.insert(id, WeakSubscriber::Model(model.downgrade().into()));
.insert(id, WeakSubscriber::Model(model.downgrade().into_any()));
drop(state);
for message in messages {
self.client.handle_message(message, cx);
@@ -447,7 +465,7 @@ impl Client {
self.state
.write()
.entities_by_type_and_remote_id
.insert(id, WeakSubscriber::View(cx.weak_handle().into()));
.insert(id, WeakSubscriber::View(cx.weak_handle().into_any()));
Subscription::Entity {
client: Arc::downgrade(self),
id,
@@ -457,18 +475,22 @@ impl Client {
pub fn subscribe_to_entity<T: Entity>(
self: &Arc<Self>,
remote_id: u64,
) -> PendingEntitySubscription<T> {
) -> Result<PendingEntitySubscription<T>> {
let id = (TypeId::of::<T>(), remote_id);
self.state
.write()
.entities_by_type_and_remote_id
.insert(id, WeakSubscriber::Pending(Default::default()));
PendingEntitySubscription {
client: self.clone(),
remote_id,
consumed: false,
_entity_type: PhantomData,
let mut state = self.state.write();
if state.entities_by_type_and_remote_id.contains_key(&id) {
return Err(anyhow!("already subscribed to entity"));
} else {
state
.entities_by_type_and_remote_id
.insert(id, WeakSubscriber::Pending(Default::default()));
Ok(PendingEntitySubscription {
client: self.clone(),
remote_id,
consumed: false,
_entity_type: PhantomData,
})
}
}
@@ -491,7 +513,7 @@ impl Client {
let mut state = self.state.write();
state
.models_by_message_type
.insert(message_type_id, model.downgrade().into());
.insert(message_type_id, model.downgrade().into_any());
let prev_handler = state.message_handlers.insert(
message_type_id,
@@ -546,7 +568,7 @@ impl Client {
H: 'static
+ Send
+ Sync
+ Fn(ViewHandle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
+ Fn(WeakViewHandle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
F: 'static + Future<Output = Result<()>>,
{
self.add_entity_message_handler::<M, E, _, _>(move |handle, message, client, cx| {
@@ -645,7 +667,7 @@ impl Client {
H: 'static
+ Send
+ Sync
+ Fn(ViewHandle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
+ Fn(WeakViewHandle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
F: 'static + Future<Output = Result<M::Response>>,
{
self.add_view_message_handler(move |entity, envelope, client, cx| {
@@ -715,7 +737,7 @@ impl Client {
read_from_keychain = credentials.is_some();
if read_from_keychain {
cx.read(|cx| {
self.report_event(
self.telemetry().report_mixpanel_event(
"read credentials from keychain",
Default::default(),
cx.global::<Settings>().telemetry(),
@@ -1095,7 +1117,7 @@ impl Client {
.context("failed to decrypt access token")?;
platform.activate(true);
telemetry.report_event(
telemetry.report_mixpanel_event(
"authenticate with browser",
Default::default(),
metrics_enabled,
@@ -1152,11 +1174,9 @@ impl Client {
})
}
pub fn disconnect(self: &Arc<Self>, cx: &AsyncAppContext) -> Result<()> {
let conn_id = self.connection_id()?;
self.peer.disconnect(conn_id);
pub fn disconnect(self: &Arc<Self>, cx: &AsyncAppContext) {
self.peer.teardown();
self.set_status(Status::SignedOut, cx);
Ok(())
}
fn connection_id(&self) -> Result<ConnectionId> {
@@ -1176,6 +1196,14 @@ impl Client {
&self,
request: T,
) -> impl Future<Output = Result<T::Response>> {
self.request_envelope(request)
.map_ok(|envelope| envelope.payload)
}
pub fn request_envelope<T: RequestMessage>(
&self,
request: T,
) -> impl Future<Output = Result<TypedEnvelope<T::Response>>> {
let client_id = self.id;
log::debug!(
"rpc request start. client_id:{}. name:{}",
@@ -1184,7 +1212,7 @@ impl Client {
);
let response = self
.connection_id()
.map(|conn_id| self.peer.request(conn_id, request));
.map(|conn_id| self.peer.request_envelope(conn_id, request));
async move {
let response = response?.await;
log::debug!(
@@ -1246,7 +1274,15 @@ impl Client {
pending.push(message);
return;
}
Some(weak_subscriber @ _) => subscriber = weak_subscriber.upgrade(cx),
Some(weak_subscriber @ _) => match weak_subscriber {
WeakSubscriber::Model(handle) => {
subscriber = handle.upgrade(cx).map(Subscriber::Model);
}
WeakSubscriber::View(handle) => {
subscriber = Some(Subscriber::View(handle.clone()));
}
WeakSubscriber::Pending(_) => {}
},
_ => {}
}
}
@@ -1303,40 +1339,8 @@ impl Client {
}
}
pub fn start_telemetry(&self) {
self.telemetry.start();
}
pub fn report_event(
&self,
kind: &str,
properties: Value,
telemetry_settings: TelemetrySettings,
) {
self.telemetry
.report_event(kind, properties.clone(), telemetry_settings);
}
pub fn telemetry_log_file_path(&self) -> Option<PathBuf> {
self.telemetry.log_file_path()
}
pub fn metrics_id(&self) -> Option<Arc<str>> {
self.telemetry.metrics_id()
}
pub fn is_staff(&self) -> Option<bool> {
self.telemetry.is_staff()
}
}
impl WeakSubscriber {
fn upgrade(&self, cx: &AsyncAppContext) -> Option<Subscriber> {
match self {
WeakSubscriber::Model(handle) => handle.upgrade(cx).map(Subscriber::Model),
WeakSubscriber::View(handle) => handle.upgrade(cx).map(Subscriber::View),
WeakSubscriber::Pending(_) => None,
}
pub fn telemetry(&self) -> &Arc<Telemetry> {
&self.telemetry
}
}
@@ -1384,10 +1388,11 @@ pub fn decode_worktree_url(url: &str) -> Option<(u64, String)> {
#[cfg(test)]
mod tests {
use super::*;
use crate::test::{FakeHttpClient, FakeServer};
use crate::test::FakeServer;
use gpui::{executor::Deterministic, TestAppContext};
use parking_lot::Mutex;
use std::future;
use util::http::FakeHttpClient;
#[gpui::test(iterations = 10)]
async fn test_reconnection(cx: &mut TestAppContext) {
@@ -1582,14 +1587,17 @@ mod tests {
let _subscription1 = client
.subscribe_to_entity(1)
.unwrap()
.set_model(&model1, &mut cx.to_async());
let _subscription2 = client
.subscribe_to_entity(2)
.unwrap()
.set_model(&model2, &mut cx.to_async());
// Ensure dropping a subscription for the same entity type still allows receiving of
// messages for other entity IDs of the same type.
let subscription3 = client
.subscribe_to_entity(3)
.unwrap()
.set_model(&model3, &mut cx.to_async());
drop(subscription3);
@@ -1618,11 +1626,13 @@ mod tests {
},
);
drop(subscription1);
let _subscription2 =
client.add_message_handler(model, move |_, _: TypedEnvelope<proto::Ping>, _, _| {
let _subscription2 = client.add_message_handler(
model.clone(),
move |_, _: TypedEnvelope<proto::Ping>, _, _| {
done_tx2.try_send(()).unwrap();
async { Ok(()) }
});
},
);
server.send(proto::Ping {});
done_rx2.next().await.unwrap();
}

View File

@@ -1,57 +0,0 @@
pub use anyhow::{anyhow, Result};
use futures::future::BoxFuture;
use isahc::{
config::{Configurable, RedirectPolicy},
AsyncBody,
};
pub use isahc::{
http::{Method, Uri},
Error,
};
use smol::future::FutureExt;
use std::{sync::Arc, time::Duration};
pub use url::Url;
pub type Request = isahc::Request<AsyncBody>;
pub type Response = isahc::Response<AsyncBody>;
pub trait HttpClient: Send + Sync {
fn send(&self, req: Request) -> BoxFuture<Result<Response, Error>>;
fn get<'a>(
&'a self,
uri: &str,
body: AsyncBody,
follow_redirects: bool,
) -> BoxFuture<'a, Result<Response, Error>> {
let request = isahc::Request::builder()
.redirect_policy(if follow_redirects {
RedirectPolicy::Follow
} else {
RedirectPolicy::None
})
.method(Method::GET)
.uri(uri)
.body(body);
match request {
Ok(request) => self.send(request),
Err(error) => async move { Err(error.into()) }.boxed(),
}
}
}
pub fn client() -> Arc<dyn HttpClient> {
Arc::new(
isahc::HttpClient::builder()
.connect_timeout(Duration::from_secs(5))
.low_speed_timeout(100, Duration::from_secs(5))
.build()
.unwrap(),
)
}
impl HttpClient for isahc::HttpClient {
fn send(&self, req: Request) -> BoxFuture<Result<Response, Error>> {
Box::pin(async move { self.send_async(req).await })
}
}

View File

@@ -1,11 +1,10 @@
use crate::http::HttpClient;
use crate::{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 isahc::Request;
use lazy_static::lazy_static;
use parking_lot::Mutex;
use serde::Serialize;
@@ -19,6 +18,7 @@ use std::{
time::{Duration, SystemTime, UNIX_EPOCH},
};
use tempfile::NamedTempFile;
use util::http::HttpClient;
use util::{channel::ReleaseChannel, post_inc, ResultExt, TryFutureExt};
use uuid::Uuid;
@@ -30,26 +30,62 @@ pub struct Telemetry {
#[derive(Default)]
struct TelemetryState {
metrics_id: Option<Arc<str>>,
device_id: Option<Arc<str>>,
metrics_id: Option<Arc<str>>, // Per logged-in user
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,
queue: Vec<MixpanelEvent>,
next_event_id: usize,
flush_task: Option<Task<()>>,
mixpanel_events_queue: Vec<MixpanelEvent>,
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);
}
#[derive(Serialize, Debug)]
struct ClickhouseEventRequestBody {
token: &'static str,
installation_id: Option<Arc<str>>,
app_version: Option<Arc<str>>,
os_name: &'static str,
os_version: Option<Arc<str>>,
release_channel: Option<&'static str>,
events: Vec<ClickhouseEventWrapper>,
}
#[derive(Serialize, Debug)]
struct ClickhouseEventWrapper {
time: u128,
signed_in: bool,
#[serde(flatten)]
event: ClickhouseEvent,
}
#[derive(Serialize, Debug)]
#[serde(tag = "type")]
pub enum ClickhouseEvent {
Editor {
operation: &'static str,
file_extension: Option<String>,
vim_mode: bool,
copilot_enabled: bool,
copilot_enabled_for_language: bool,
},
}
#[derive(Serialize, Debug)]
@@ -64,7 +100,8 @@ struct MixpanelEventProperties {
#[serde(skip_serializing_if = "str::is_empty")]
token: &'static str,
time: u128,
distinct_id: Option<Arc<str>>,
#[serde(rename = "distinct_id")]
installation_id: Option<Arc<str>>,
#[serde(rename = "$insert_id")]
insert_id: usize,
// Custom fields
@@ -87,7 +124,7 @@ struct MixpanelEngageRequest {
#[serde(rename = "$token")]
token: &'static str,
#[serde(rename = "$distinct_id")]
distinct_id: Arc<str>,
installation_id: Arc<str>,
#[serde(rename = "$set")]
set: Value,
}
@@ -120,11 +157,13 @@ impl Telemetry {
os_name: platform.os_name().into(),
app_version: platform.app_version().ok().map(|v| v.to_string().into()),
release_channel,
device_id: None,
installation_id: None,
metrics_id: None,
queue: Default::default(),
flush_task: Default::default(),
next_event_id: 0,
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,
}),
@@ -155,29 +194,38 @@ impl Telemetry {
self.executor
.spawn(
async move {
let device_id =
if let Ok(Some(device_id)) = KEY_VALUE_STORE.read_kvp("device_id") {
device_id
let installation_id =
if let Ok(Some(installation_id)) = KEY_VALUE_STORE.read_kvp("device_id") {
installation_id
} else {
let device_id = Uuid::new_v4().to_string();
let installation_id = Uuid::new_v4().to_string();
KEY_VALUE_STORE
.write_kvp("device_id".to_string(), device_id.clone())
.write_kvp("device_id".to_string(), installation_id.clone())
.await?;
device_id
installation_id
};
let device_id: Arc<str> = device_id.into();
let installation_id: Arc<str> = installation_id.into();
let mut state = this.state.lock();
state.device_id = Some(device_id.clone());
for event in &mut state.queue {
state.installation_id = Some(installation_id.clone());
for event in &mut state.mixpanel_events_queue {
event
.properties
.distinct_id
.get_or_insert_with(|| device_id.clone());
.installation_id
.get_or_insert_with(|| installation_id.clone());
}
if !state.queue.is_empty() {
drop(state);
this.flush();
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();
}
if has_clickhouse_events {
this.flush_clickhouse_events();
}
anyhow::Ok(())
@@ -201,30 +249,30 @@ impl Telemetry {
let this = self.clone();
let mut state = self.state.lock();
let device_id = state.device_id.clone();
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, device_id)) = MIXPANEL_TOKEN.as_ref().zip(device_id) {
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,
distinct_id: device_id,
installation_id,
set: json!({
"Staff": is_staff,
"ID": metrics_id,
"App": true
}),
}])?;
let request = Request::post(MIXPANEL_ENGAGE_URL)
.header("Content-Type", "application/json")
.body(json_bytes.into())?;
this.http_client.send(request).await?;
Ok(())
this.http_client
.post_json(MIXPANEL_ENGAGE_URL, json_bytes.into())
.await?;
anyhow::Ok(())
}
.log_err(),
)
@@ -232,7 +280,42 @@ impl Telemetry {
}
}
pub fn report_event(
pub fn report_clickhouse_event(
self: &Arc<Self>,
event: ClickhouseEvent,
telemetry_settings: TelemetrySettings,
) {
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,
});
if state.installation_id.is_some() {
if state.mixpanel_events_queue.len() >= MAX_QUEUE_LEN {
drop(state);
self.flush_clickhouse_events();
} else {
let this = self.clone();
let executor = self.executor.clone();
state.flush_clickhouse_events_task = Some(self.executor.spawn(async move {
executor.timer(DEBOUNCE_INTERVAL).await;
this.flush_clickhouse_events();
}));
}
}
}
pub fn report_mixpanel_event(
self: &Arc<Self>,
kind: &str,
properties: Value,
@@ -244,15 +327,15 @@ impl Telemetry {
let mut state = self.state.lock();
let event = MixpanelEvent {
event: kind.to_string(),
event: kind.into(),
properties: MixpanelEventProperties {
token: "",
time: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis(),
distinct_id: state.device_id.clone(),
insert_id: post_inc(&mut state.next_event_id),
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 {
@@ -265,17 +348,17 @@ impl Telemetry {
signed_in: state.metrics_id.is_some(),
},
};
state.queue.push(event);
if state.device_id.is_some() {
if state.queue.len() >= MAX_QUEUE_LEN {
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();
self.flush_mixpanel_events();
} else {
let this = self.clone();
let executor = self.executor.clone();
state.flush_task = Some(self.executor.spawn(async move {
state.flush_mixpanel_events_task = Some(self.executor.spawn(async move {
executor.timer(DEBOUNCE_INTERVAL).await;
this.flush();
this.flush_mixpanel_events();
}));
}
}
@@ -285,14 +368,18 @@ impl Telemetry {
self.state.lock().metrics_id.clone()
}
pub fn installation_id(self: &Arc<Self>) -> Option<Arc<str>> {
self.state.lock().installation_id.clone()
}
pub fn is_staff(self: &Arc<Self>) -> Option<bool> {
self.state.lock().is_staff
}
fn flush(self: &Arc<Self>) {
fn flush_mixpanel_events(self: &Arc<Self>) {
let mut state = self.state.lock();
let mut events = mem::take(&mut state.queue);
state.flush_task.take();
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() {
@@ -316,15 +403,63 @@ impl Telemetry {
json_bytes.clear();
serde_json::to_writer(&mut json_bytes, &events)?;
let request = Request::post(MIXPANEL_EVENTS_URL)
.header("Content-Type", "application/json")
.body(json_bytes.into())?;
this.http_client.send(request).await?;
Ok(())
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);
state.flush_clickhouse_events_task.take();
drop(state);
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")?;
}
}
{
let state = this.state.lock();
json_bytes.clear();
serde_json::to_writer(
&mut json_bytes,
&ClickhouseEventRequestBody {
token: ZED_SECRET_CLIENT_TOKEN,
installation_id: state.installation_id.clone(),
app_version: state.app_version.clone(),
os_name: state.os_name,
os_version: state.os_version.clone(),
release_channel: state.release_channel,
events,
},
)?;
}
this.http_client
.post_json(CLICKHOUSE_EVENTS_URL.as_str(), json_bytes.into())
.await?;
anyhow::Ok(())
}
.log_err(),
)
.detach();
}
}

View File

@@ -1,16 +1,14 @@
use crate::{
http::{self, HttpClient, Request, Response},
Client, Connection, Credentials, EstablishConnectionError, UserStore,
};
use crate::{Client, Connection, Credentials, EstablishConnectionError, UserStore};
use anyhow::{anyhow, Result};
use futures::{future::BoxFuture, stream::BoxStream, Future, StreamExt};
use futures::{stream::BoxStream, StreamExt};
use gpui::{executor, ModelHandle, TestAppContext};
use parking_lot::Mutex;
use rpc::{
proto::{self, GetPrivateUserInfo, GetPrivateUserInfoResponse},
ConnectionId, Peer, Receipt, TypedEnvelope,
};
use std::{fmt, rc::Rc, sync::Arc};
use std::{rc::Rc, sync::Arc};
use util::http::FakeHttpClient;
pub struct FakeServer {
peer: Arc<Peer>,
@@ -219,46 +217,3 @@ impl Drop for FakeServer {
self.disconnect();
}
}
pub struct FakeHttpClient {
handler: Box<
dyn 'static
+ Send
+ Sync
+ Fn(Request) -> BoxFuture<'static, Result<Response, http::Error>>,
>,
}
impl FakeHttpClient {
pub fn create<Fut, F>(handler: F) -> Arc<dyn HttpClient>
where
Fut: 'static + Send + Future<Output = Result<Response, http::Error>>,
F: 'static + Send + Sync + Fn(Request) -> Fut,
{
Arc::new(Self {
handler: Box::new(move |req| Box::pin(handler(req))),
})
}
pub fn with_404_response() -> Arc<dyn HttpClient> {
Self::create(|_| async move {
Ok(isahc::Response::builder()
.status(404)
.body(Default::default())
.unwrap())
})
}
}
impl fmt::Debug for FakeHttpClient {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FakeHttpClient").finish()
}
}
impl HttpClient for FakeHttpClient {
fn send(&self, req: Request) -> BoxFuture<Result<Response, crate::http::Error>> {
let future = (self.handler)(req);
Box::pin(async move { future.await.map(Into::into) })
}
}

View File

@@ -1,4 +1,4 @@
use super::{http::HttpClient, proto, Client, Status, TypedEnvelope};
use super::{proto, Client, Status, TypedEnvelope};
use anyhow::{anyhow, Context, Result};
use collections::{hash_map::Entry, HashMap, HashSet};
use futures::{channel::mpsc, future, AsyncReadExt, Future, StreamExt};
@@ -6,8 +6,10 @@ 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::{StaffMode, TryFutureExt as _};
use util::http::HttpClient;
use util::TryFutureExt as _;
#[derive(Default, Debug)]
pub struct User {
@@ -183,6 +185,11 @@ impl UserStore {
}
}
#[cfg(feature = "test-support")]
pub fn clear_cache(&mut self) {
self.users.clear();
}
async fn handle_update_invite_info(
this: ModelHandle<Self>,
message: TypedEnvelope<proto::UpdateInviteInfo>,

View File

@@ -9,4 +9,4 @@ path = "src/clock.rs"
doctest = false
[dependencies]
smallvec = { version = "1.6", features = ["union"] }
smallvec.workspace = true

View File

@@ -1,4 +1,5 @@
DATABASE_URL = "postgres://postgres@localhost/zed"
DATABASE_MAX_CONNECTIONS = 5
HTTP_PORT = 8080
API_TOKEN = "secret"
INVITE_LINK_PREFIX = "http://localhost:3000/invites/"

View File

@@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathan@zed.dev>"]
default-run = "collab"
edition = "2021"
name = "collab"
version = "0.5.4"
version = "0.12.0"
publish = false
[[bin]]
@@ -19,7 +19,7 @@ live_kit_server = { path = "../live_kit_server" }
rpc = { path = "../rpc" }
util = { path = "../util" }
anyhow = "1.0.40"
anyhow.workspace = true
async-tungstenite = "0.16"
axum = { version = "0.5", features = ["json", "headers", "ws"] }
axum-extra = { version = "0.3", features = ["erased-json"] }
@@ -27,24 +27,26 @@ base64 = "0.13"
clap = { version = "3.1", features = ["derive"], optional = true }
dashmap = "5.4"
envy = "0.4.2"
futures = "0.3"
futures.workspace = true
hyper = "0.14"
lazy_static = "1.4"
lazy_static.workspace = true
lipsum = { version = "0.8", optional = true }
log.workspace = true
nanoid = "0.4"
parking_lot = "0.11.1"
parking_lot.workspace = true
prometheus = "0.13"
rand = "0.8"
rand.workspace = true
reqwest = { version = "0.11", features = ["json"], optional = true }
scrypt = "0.7"
# Remove fork dependency when a version with https://github.com/SeaQL/sea-orm/pull/1283 is released.
sea-orm = { git = "https://github.com/zed-industries/sea-orm", rev = "18f4c691085712ad014a51792af75a9044bacee6", features = ["sqlx-postgres", "postgres-array", "runtime-tokio-rustls"] }
sea-query = "0.27"
serde = { version = "1.0", features = ["derive", "rc"] }
serde_json = "1.0"
serde.workspace = true
serde_derive.workspace = true
serde_json.workspace = true
sha-1 = "0.9"
sqlx = { version = "0.6", features = ["runtime-tokio-rustls", "postgres", "json", "time", "uuid", "any"] }
time = { version = "0.3", features = ["serde", "serde-well-known"] }
time.workspace = true
tokio = { version = "1", features = ["full"] }
tokio-tungstenite = "0.17"
tonic = "0.6"
@@ -72,15 +74,15 @@ settings = { path = "../settings", features = ["test-support"] }
theme = { path = "../theme" }
workspace = { path = "../workspace", features = ["test-support"] }
ctor = "0.1"
env_logger = "0.9"
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
ctor.workspace = true
env_logger.workspace = true
indoc = "1.0.4"
util = { path = "../util" }
lazy_static = "1.4"
lazy_static.workspace = true
sea-orm = { git = "https://github.com/zed-industries/sea-orm", rev = "18f4c691085712ad014a51792af75a9044bacee6", features = ["sqlx-sqlite"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
serde_json.workspace = true
sqlx = { version = "0.6", features = ["sqlite"] }
unindent = "0.1"
unindent.workspace = true
[features]
seed-support = ["clap", "lipsum", "reqwest"]

View File

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

View File

@@ -1,3 +1,4 @@
ZED_ENVIRONMENT=production
RUST_LOG=info
INVITE_LINK_PREFIX=https://zed.dev/invites/
DATABASE_MAX_CONNECTIONS=85

View File

@@ -1,3 +1,4 @@
ZED_ENVIRONMENT=staging
RUST_LOG=info
INVITE_LINK_PREFIX=https://staging.zed.dev/invites/
DATABASE_MAX_CONNECTIONS=5

View File

@@ -59,6 +59,13 @@ spec:
ports:
- containerPort: 8080
protocol: TCP
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /
@@ -73,6 +80,8 @@ spec:
secretKeyRef:
name: database
key: url
- name: DATABASE_MAX_CONNECTIONS
value: "${DATABASE_MAX_CONNECTIONS}"
- name: API_TOKEN
valueFrom:
secretKeyRef:

View File

@@ -82,6 +82,20 @@ CREATE TABLE "worktree_entries" (
CREATE INDEX "index_worktree_entries_on_project_id" ON "worktree_entries" ("project_id");
CREATE INDEX "index_worktree_entries_on_project_id_and_worktree_id" ON "worktree_entries" ("project_id", "worktree_id");
CREATE TABLE "worktree_repositories" (
"project_id" INTEGER NOT NULL,
"worktree_id" INTEGER NOT NULL,
"work_directory_id" INTEGER NOT NULL,
"scan_id" INTEGER NOT NULL,
"branch" VARCHAR,
"is_deleted" BOOL NOT NULL,
PRIMARY KEY(project_id, worktree_id, work_directory_id),
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
);
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_diagnostic_summaries" (
"project_id" INTEGER NOT NULL,
"worktree_id" INTEGER NOT NULL,
@@ -153,7 +167,7 @@ CREATE TABLE "followers" (
"follower_connection_server_id" INTEGER NOT NULL REFERENCES servers (id) ON DELETE CASCADE,
"follower_connection_id" INTEGER NOT NULL
);
CREATE UNIQUE INDEX
CREATE UNIQUE INDEX
"index_followers_on_project_id_and_leader_connection_server_id_and_leader_connection_id_and_follower_connection_server_id_and_follower_connection_id"
ON "followers" ("project_id", "leader_connection_server_id", "leader_connection_id", "follower_connection_server_id", "follower_connection_id");
CREATE INDEX "index_followers_on_room_id" ON "followers" ("room_id");

View File

@@ -0,0 +1,13 @@
CREATE TABLE "worktree_repositories" (
"project_id" INTEGER NOT NULL,
"worktree_id" INT8 NOT NULL,
"work_directory_id" INT8 NOT NULL,
"scan_id" INT8 NOT NULL,
"branch" VARCHAR,
"is_deleted" BOOL NOT NULL,
PRIMARY KEY(project_id, worktree_id, work_directory_id),
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
);
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");

View File

@@ -78,6 +78,7 @@ pub async fn validate_api_token<B>(req: Request<B>, next: Next<B>) -> impl IntoR
struct AuthenticatedUserParams {
github_user_id: Option<i32>,
github_login: String,
github_email: Option<String>,
}
#[derive(Debug, Serialize)]
@@ -92,7 +93,11 @@ async fn get_authenticated_user(
) -> Result<Json<AuthenticatedUserResponse>> {
let user = app
.db
.get_user_by_github_account(&params.github_login, params.github_user_id)
.get_or_create_user_by_github_account(
&params.github_login,
params.github_user_id,
params.github_email.as_deref(),
)
.await?
.ok_or_else(|| Error::Http(StatusCode::NOT_FOUND, "user not found".into()))?;
let metrics_id = app.db.get_user_metrics_id(user.id).await?;
@@ -297,11 +302,7 @@ async fn create_access_token(
let mut user_id = user.id;
if let Some(impersonate) = params.impersonate {
if user.admin {
if let Some(impersonated_user) = app
.db
.get_user_by_github_account(&impersonate, None)
.await?
{
if let Some(impersonated_user) = app.db.get_user_by_github_login(&impersonate).await? {
user_id = impersonated_user.id;
} else {
return Err(Error::Http(

View File

@@ -1,5 +1,5 @@
use crate::{
db::{self, UserId},
db::{self, AccessTokenId, Database, UserId},
AppState, Error, Result,
};
use anyhow::{anyhow, Context};
@@ -8,12 +8,24 @@ use axum::{
middleware::Next,
response::IntoResponse,
};
use lazy_static::lazy_static;
use prometheus::{exponential_buckets, register_histogram, Histogram};
use rand::thread_rng;
use scrypt::{
password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
Scrypt,
};
use std::sync::Arc;
use serde::{Deserialize, Serialize};
use std::{sync::Arc, time::Instant};
lazy_static! {
static ref METRIC_ACCESS_TOKEN_HASHING_TIME: Histogram = register_histogram!(
"access_token_hashing_time",
"time spent hashing access tokens",
exponential_buckets(10.0, 2.0, 10).unwrap(),
)
.unwrap();
}
pub async fn validate_header<B>(mut req: Request<B>, next: Next<B>) -> impl IntoResponse {
let mut auth_header = req
@@ -42,20 +54,14 @@ pub async fn validate_header<B>(mut req: Request<B>, next: Next<B>) -> impl Into
)
})?;
let mut credentials_valid = false;
let state = req.extensions().get::<Arc<AppState>>().unwrap();
if let Some(admin_token) = access_token.strip_prefix("ADMIN_TOKEN:") {
if state.config.api_token == admin_token {
credentials_valid = true;
}
let credentials_valid = if let Some(admin_token) = access_token.strip_prefix("ADMIN_TOKEN:") {
state.config.api_token == admin_token
} else {
for password_hash in state.db.get_access_token_hashes(user_id).await? {
if verify_access_token(access_token, &password_hash)? {
credentials_valid = true;
break;
}
}
}
verify_access_token(&access_token, user_id, &state.db)
.await
.unwrap_or(false)
};
if credentials_valid {
let user = state
@@ -75,13 +81,26 @@ pub async fn validate_header<B>(mut req: Request<B>, next: Next<B>) -> impl Into
const MAX_ACCESS_TOKENS_TO_STORE: usize = 8;
#[derive(Serialize, Deserialize)]
struct AccessTokenJson {
version: usize,
id: AccessTokenId,
token: String,
}
pub async fn create_access_token(db: &db::Database, user_id: UserId) -> Result<String> {
const VERSION: usize = 1;
let access_token = rpc::auth::random_token();
let access_token_hash =
hash_access_token(&access_token).context("failed to hash access token")?;
db.create_access_token_hash(user_id, &access_token_hash, MAX_ACCESS_TOKENS_TO_STORE)
let id = db
.create_access_token(user_id, &access_token_hash, MAX_ACCESS_TOKENS_TO_STORE)
.await?;
Ok(access_token)
Ok(serde_json::to_string(&AccessTokenJson {
version: VERSION,
id,
token: access_token,
})?)
}
fn hash_access_token(token: &str) -> Result<String> {
@@ -89,7 +108,7 @@ fn hash_access_token(token: &str) -> Result<String> {
let params = if cfg!(debug_assertions) {
scrypt::Params::new(1, 1, 1).unwrap()
} else {
scrypt::Params::recommended()
scrypt::Params::new(14, 8, 1).unwrap()
};
Ok(Scrypt
@@ -112,7 +131,21 @@ pub fn encrypt_access_token(access_token: &str, public_key: String) -> Result<St
Ok(encrypted_access_token)
}
pub fn verify_access_token(token: &str, hash: &str) -> Result<bool> {
let hash = PasswordHash::new(hash).map_err(anyhow::Error::new)?;
Ok(Scrypt.verify_password(token.as_bytes(), &hash).is_ok())
pub async fn verify_access_token(token: &str, user_id: UserId, db: &Arc<Database>) -> Result<bool> {
let token: AccessTokenJson = serde_json::from_str(&token)?;
let db_token = db.get_access_token(token.id).await?;
if db_token.user_id != user_id {
return Err(anyhow!("no such access token"))?;
}
let db_hash = PasswordHash::new(&db_token.hash).map_err(anyhow::Error::new)?;
let t0 = Instant::now();
let is_valid = Scrypt
.verify_password(token.token.as_bytes(), &db_hash)
.is_ok();
let duration = t0.elapsed();
log::info!("hashed access token in {:?}", duration);
METRIC_ACCESS_TOKEN_HASHING_TIME.observe(duration.as_millis() as f64);
Ok(is_valid)
}

View File

@@ -1,4 +1,4 @@
use collab::db;
use collab::{db, executor::Executor};
use db::{ConnectOptions, Database};
use serde::{de::DeserializeOwned, Deserialize};
use std::fmt::Write;
@@ -13,7 +13,7 @@ struct GitHubUser {
#[tokio::main]
async fn main() {
let database_url = std::env::var("DATABASE_URL").expect("missing DATABASE_URL env var");
let db = Database::new(ConnectOptions::new(database_url))
let db = Database::new(ConnectOptions::new(database_url), Executor::Production)
.await
.expect("failed to connect to postgres database");
let github_token = std::env::var("GITHUB_TOKEN").expect("missing GITHUB_TOKEN env var");
@@ -59,7 +59,7 @@ async fn main() {
for (github_user, admin) in zed_users {
if db
.get_user_by_github_account(&github_user.login, Some(github_user.id))
.get_user_by_github_login(&github_user.login)
.await
.expect("failed to fetch user")
.is_none()

File diff suppressed because it is too large Load Diff

View File

@@ -92,8 +92,8 @@ test_both_dbs!(
);
test_both_dbs!(
test_get_user_by_github_account_postgres,
test_get_user_by_github_account_sqlite,
test_get_or_create_user_by_github_account_postgres,
test_get_or_create_user_by_github_account_sqlite,
db,
{
let user_id1 = db
@@ -124,7 +124,7 @@ test_both_dbs!(
.user_id;
let user = db
.get_user_by_github_account("login1", None)
.get_or_create_user_by_github_account("login1", None, None)
.await
.unwrap()
.unwrap();
@@ -133,19 +133,28 @@ test_both_dbs!(
assert_eq!(user.github_user_id, Some(101));
assert!(db
.get_user_by_github_account("non-existent-login", None)
.get_or_create_user_by_github_account("non-existent-login", None, None)
.await
.unwrap()
.is_none());
let user = db
.get_user_by_github_account("the-new-login2", Some(102))
.get_or_create_user_by_github_account("the-new-login2", Some(102), None)
.await
.unwrap()
.unwrap();
assert_eq!(user.id, user_id2);
assert_eq!(&user.github_login, "the-new-login2");
assert_eq!(user.github_user_id, Some(102));
let user = db
.get_or_create_user_by_github_account("login3", Some(103), Some("user3@example.com"))
.await
.unwrap()
.unwrap();
assert_eq!(&user.github_login, "login3");
assert_eq!(user.github_user_id, Some(103));
assert_eq!(user.email_address, Some("user3@example.com".into()));
}
);
@@ -168,30 +177,63 @@ test_both_dbs!(
.unwrap()
.user_id;
db.create_access_token_hash(user, "h1", 3).await.unwrap();
db.create_access_token_hash(user, "h2", 3).await.unwrap();
let token_1 = db.create_access_token(user, "h1", 2).await.unwrap();
let token_2 = db.create_access_token(user, "h2", 2).await.unwrap();
assert_eq!(
db.get_access_token_hashes(user).await.unwrap(),
&["h2".to_string(), "h1".to_string()]
db.get_access_token(token_1).await.unwrap(),
access_token::Model {
id: token_1,
user_id: user,
hash: "h1".into(),
}
);
assert_eq!(
db.get_access_token(token_2).await.unwrap(),
access_token::Model {
id: token_2,
user_id: user,
hash: "h2".into()
}
);
db.create_access_token_hash(user, "h3", 3).await.unwrap();
let token_3 = db.create_access_token(user, "h3", 2).await.unwrap();
assert_eq!(
db.get_access_token_hashes(user).await.unwrap(),
&["h3".to_string(), "h2".to_string(), "h1".to_string(),]
db.get_access_token(token_3).await.unwrap(),
access_token::Model {
id: token_3,
user_id: user,
hash: "h3".into()
}
);
assert_eq!(
db.get_access_token(token_2).await.unwrap(),
access_token::Model {
id: token_2,
user_id: user,
hash: "h2".into()
}
);
assert!(db.get_access_token(token_1).await.is_err());
db.create_access_token_hash(user, "h4", 3).await.unwrap();
let token_4 = db.create_access_token(user, "h4", 2).await.unwrap();
assert_eq!(
db.get_access_token_hashes(user).await.unwrap(),
&["h4".to_string(), "h3".to_string(), "h2".to_string(),]
db.get_access_token(token_4).await.unwrap(),
access_token::Model {
id: token_4,
user_id: user,
hash: "h4".into()
}
);
db.create_access_token_hash(user, "h5", 3).await.unwrap();
assert_eq!(
db.get_access_token_hashes(user).await.unwrap(),
&["h5".to_string(), "h4".to_string(), "h3".to_string()]
db.get_access_token(token_3).await.unwrap(),
access_token::Model {
id: token_3,
user_id: user,
hash: "h3".into()
}
);
assert!(db.get_access_token(token_2).await.is_err());
assert!(db.get_access_token(token_1).await.is_err());
}
);

View File

@@ -0,0 +1,21 @@
use super::ProjectId;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "worktree_repositories")]
pub struct Model {
#[sea_orm(primary_key)]
pub project_id: ProjectId,
#[sea_orm(primary_key)]
pub worktree_id: i64,
#[sea_orm(primary_key)]
pub work_directory_id: i64,
pub scan_id: i64,
pub branch: Option<String>,
pub is_deleted: bool,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -10,6 +10,7 @@ mod tests;
use axum::{http::StatusCode, response::IntoResponse};
use db::Database;
use executor::Executor;
use serde::Deserialize;
use std::{path::PathBuf, sync::Arc};
@@ -91,6 +92,7 @@ impl std::error::Error for Error {}
pub struct Config {
pub http_port: u16,
pub database_url: String,
pub database_max_connections: u32,
pub api_token: String,
pub invite_link_prefix: String,
pub live_kit_server: Option<String>,
@@ -116,8 +118,8 @@ pub struct AppState {
impl AppState {
pub async fn new(config: Config) -> Result<Arc<Self>> {
let mut db_options = db::ConnectOptions::new(config.database_url.clone());
db_options.max_connections(5);
let db = Database::new(db_options).await?;
db_options.max_connections(config.database_max_connections);
let db = Database::new(db_options, Executor::Production).await?;
let live_kit_client = if let Some(((server, key), secret)) = config
.live_kit_server
.as_ref()

View File

@@ -1,11 +1,12 @@
use anyhow::anyhow;
use axum::{routing::get, Router};
use axum::{routing::get, Extension, Router};
use collab::{db, env, executor::Executor, AppState, Config, MigrateConfig, Result};
use db::Database;
use std::{
env::args,
net::{SocketAddr, TcpListener},
path::Path,
sync::Arc,
};
use tokio::signal::unix::SignalKind;
use tracing_log::LogTracer;
@@ -31,7 +32,7 @@ async fn main() -> Result<()> {
let config = envy::from_env::<MigrateConfig>().expect("error loading config");
let mut db_options = db::ConnectOptions::new(config.database_url.clone());
db_options.max_connections(5);
let db = Database::new(db_options).await?;
let db = Database::new(db_options, Executor::Production).await?;
let migrations_path = config
.migrations_path
@@ -66,7 +67,12 @@ async fn main() -> Result<()> {
let app = collab::api::routes(rpc_server.clone(), state.clone())
.merge(collab::rpc::routes(rpc_server.clone()))
.merge(Router::new().route("/", get(handle_root)));
.merge(
Router::new()
.route("/", get(handle_root))
.route("/healthz", get(handle_liveness_probe))
.layer(Extension(state.clone())),
);
axum::Server::from_tcp(listener)?
.serve(app.into_make_service_with_connect_info::<SocketAddr>())
@@ -95,6 +101,11 @@ async fn handle_root() -> String {
format!("collab v{VERSION}")
}
async fn handle_liveness_probe(Extension(state): Extension<Arc<AppState>>) -> Result<String> {
state.db.get_all_users(0, 1).await?;
Ok("ok".to_string())
}
pub fn init_tracing(config: &Config) -> Option<()> {
use std::str::FromStr;
use tracing_subscriber::layer::SubscriberExt;

View File

@@ -53,11 +53,11 @@ use std::{
},
time::Duration,
};
use tokio::sync::watch;
use tokio::sync::{watch, Semaphore};
use tower::ServiceBuilder;
use tracing::{info_span, instrument, Instrument};
pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(5);
pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30);
pub const CLEANUP_TIMEOUT: Duration = Duration::from_secs(10);
lazy_static! {
@@ -186,7 +186,7 @@ impl Server {
.add_request_handler(create_room)
.add_request_handler(join_room)
.add_request_handler(rejoin_room)
.add_message_handler(leave_room)
.add_request_handler(leave_room)
.add_request_handler(call)
.add_request_handler(cancel_call)
.add_message_handler(decline_call)
@@ -228,7 +228,7 @@ impl Server {
.add_message_handler(update_buffer_file)
.add_message_handler(buffer_reloaded)
.add_message_handler(buffer_saved)
.add_request_handler(save_buffer)
.add_request_handler(forward_project_request::<proto::SaveBuffer>)
.add_request_handler(get_users)
.add_request_handler(fuzzy_search_users)
.add_request_handler(request_contact)
@@ -270,8 +270,11 @@ impl Server {
let mut live_kit_room = String::new();
let mut delete_live_kit_room = false;
if let Ok(mut refreshed_room) =
app_state.db.refresh_room(room_id, server_id).await
if let Some(mut refreshed_room) = app_state
.db
.refresh_room(room_id, server_id)
.await
.trace_err()
{
tracing::info!(
room_id = room_id.0,
@@ -539,8 +542,13 @@ impl Server {
// This arrangement ensures we will attempt to process earlier messages first, but fall
// back to processing messages arrived later in the spirit of making progress.
let mut foreground_message_handlers = FuturesUnordered::new();
let concurrent_handlers = Arc::new(Semaphore::new(256));
loop {
let next_message = incoming_rx.next().fuse();
let next_message = async {
let permit = concurrent_handlers.clone().acquire_owned().await.unwrap();
let message = incoming_rx.next().await;
(permit, message)
}.fuse();
futures::pin_mut!(next_message);
futures::select_biased! {
_ = teardown.changed().fuse() => return Ok(()),
@@ -551,7 +559,8 @@ impl Server {
break;
}
_ = foreground_message_handlers.next() => {}
message = next_message => {
next_message = next_message => {
let (permit, message) = next_message;
if let Some(message) = message {
let type_name = message.payload_type_name();
let span = tracing::info_span!("receive message", %user_id, %login, %connection_id, %address, type_name);
@@ -561,7 +570,10 @@ impl Server {
let handle_message = (handler)(message, session.clone());
drop(span_enter);
let handle_message = handle_message.instrument(span);
let handle_message = async move {
handle_message.await;
drop(permit);
}.instrument(span);
if is_background {
executor.spawn_detached(handle_message);
} else {
@@ -1051,6 +1063,8 @@ async fn rejoin_room(
removed_entries: worktree.removed_entries,
scan_id: worktree.scan_id,
is_last_update: worktree.completed_scan_id == worktree.scan_id,
updated_repositories: worktree.updated_repositories,
removed_repositories: worktree.removed_repositories,
};
for update in proto::split_worktree_update(message, MAX_CHUNK_SIZE) {
session.peer.send(session.connection_id, update.clone())?;
@@ -1090,8 +1104,14 @@ async fn rejoin_room(
Ok(())
}
async fn leave_room(_message: proto::LeaveRoom, session: Session) -> Result<()> {
leave_room_for_session(&session).await
async fn leave_room(
_: proto::LeaveRoom,
response: Response<proto::LeaveRoom>,
session: Session,
) -> Result<()> {
leave_room_for_session(&session).await?;
response.send(proto::Ack {})?;
Ok(())
}
async fn call(
@@ -1365,6 +1385,8 @@ async fn join_project(
removed_entries: Default::default(),
scan_id: worktree.scan_id,
is_last_update: worktree.scan_id == worktree.completed_scan_id,
updated_repositories: worktree.repository_entries,
removed_repositories: Default::default(),
};
for update in proto::split_worktree_update(message, MAX_CHUNK_SIZE) {
session.peer.send(session.connection_id, update.clone())?;
@@ -1405,7 +1427,7 @@ async fn leave_project(request: proto::LeaveProject, session: Session) -> Result
let sender_id = session.connection_id;
let project_id = ProjectId::from_proto(request.project_id);
let project = session
let (room, project) = &*session
.db()
.await
.leave_project(project_id, sender_id)
@@ -1416,7 +1438,9 @@ async fn leave_project(request: proto::LeaveProject, session: Session) -> Result
host_connection_id = %project.host_connection_id,
"leave project"
);
project_left(&project, &session);
room_updated(&room, &session.peer);
Ok(())
}
@@ -1571,51 +1595,6 @@ where
Ok(())
}
async fn save_buffer(
request: proto::SaveBuffer,
response: Response<proto::SaveBuffer>,
session: Session,
) -> Result<()> {
let project_id = ProjectId::from_proto(request.project_id);
let host_connection_id = {
let collaborators = session
.db()
.await
.project_collaborators(project_id, session.connection_id)
.await?;
collaborators
.iter()
.find(|collaborator| collaborator.is_host)
.ok_or_else(|| anyhow!("host not found"))?
.connection_id
};
let response_payload = session
.peer
.forward_request(session.connection_id, host_connection_id, request.clone())
.await?;
let mut collaborators = session
.db()
.await
.project_collaborators(project_id, session.connection_id)
.await?;
collaborators.retain(|collaborator| collaborator.connection_id != session.connection_id);
let project_connection_ids = collaborators
.iter()
.map(|collaborator| collaborator.connection_id);
broadcast(
Some(host_connection_id),
project_connection_ids,
|conn_id| {
session
.peer
.forward_send(host_connection_id, conn_id, response_payload.clone())
},
);
response.send(response_payload)?;
Ok(())
}
async fn create_buffer_for_peer(
request: proto::CreateBufferForPeer,
session: Session,
@@ -1635,23 +1614,42 @@ async fn update_buffer(
) -> Result<()> {
session.executor.record_backtrace();
let project_id = ProjectId::from_proto(request.project_id);
let project_connection_ids = session
.db()
.await
.project_connection_ids(project_id, session.connection_id)
.await?;
let mut guest_connection_ids;
let mut host_connection_id = None;
{
let collaborators = session
.db()
.await
.project_collaborators(project_id, session.connection_id)
.await?;
guest_connection_ids = Vec::with_capacity(collaborators.len() - 1);
for collaborator in collaborators.iter() {
if collaborator.is_host {
host_connection_id = Some(collaborator.connection_id);
} else {
guest_connection_ids.push(collaborator.connection_id);
}
}
}
let host_connection_id = host_connection_id.ok_or_else(|| anyhow!("host not found"))?;
session.executor.record_backtrace();
broadcast(
Some(session.connection_id),
project_connection_ids.iter().copied(),
guest_connection_ids,
|connection_id| {
session
.peer
.forward_send(session.connection_id, connection_id, request.clone())
},
);
if host_connection_id != session.connection_id {
session
.peer
.forward_request(session.connection_id, host_connection_id, request.clone())
.await?;
}
response.send(proto::Ack {})?;
Ok(())
}
@@ -1855,7 +1853,7 @@ async fn fuzzy_search_users(
1 | 2 => session
.db()
.await
.get_user_by_github_account(&query, None)
.get_user_by_github_login(&query)
.await?
.into_iter()
.collect(),

View File

@@ -7,29 +7,31 @@ use crate::{
use anyhow::anyhow;
use call::ActiveCall;
use client::{
self, proto::PeerId, test::FakeHttpClient, Client, Connection, Credentials,
EstablishConnectionError, UserStore,
self, proto::PeerId, Client, Connection, Credentials, EstablishConnectionError, UserStore,
};
use collections::{HashMap, HashSet};
use fs::FakeFs;
use futures::{channel::oneshot, StreamExt as _};
use gpui::{
executor::Deterministic, test::EmptyView, ModelHandle, Task, TestAppContext, ViewHandle,
elements::*, executor::Deterministic, AnyElement, Entity, ModelHandle, TestAppContext, View,
ViewContext, ViewHandle, WeakViewHandle,
};
use language::LanguageRegistry;
use parking_lot::Mutex;
use project::{Project, WorktreeId};
use settings::Settings;
use std::{
cell::{Ref, RefCell, RefMut},
env,
ops::Deref,
path::{Path, PathBuf},
ops::{Deref, DerefMut},
path::Path,
sync::{
atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst},
Arc,
},
};
use theme::ThemeRegistry;
use util::http::FakeHttpClient;
use workspace::Workspace;
mod integration_tests;
@@ -104,11 +106,7 @@ impl TestServer {
});
let http = FakeHttpClient::with_404_response();
let user_id = if let Ok(Some(user)) = self
.app_state
.db
.get_user_by_github_account(name, None)
.await
let user_id = if let Ok(Some(user)) = self.app_state.db.get_user_by_github_login(name).await
{
user.id
} else {
@@ -192,12 +190,13 @@ impl TestServer {
let app_state = Arc::new(workspace::AppState {
client: client.clone(),
user_store: user_store.clone(),
languages: Arc::new(LanguageRegistry::new(Task::ready(()))),
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: |_, _| unimplemented!(),
dock_default_item_factory: |_, _| None,
background_actions: || &[],
});
Project::init(&client);
@@ -214,13 +213,10 @@ impl TestServer {
let client = TestClient {
client,
username: name.to_string(),
local_projects: Default::default(),
remote_projects: Default::default(),
next_root_dir_id: 0,
state: Default::default(),
user_store,
fs,
language_registry: Arc::new(LanguageRegistry::test()),
buffers: Default::default(),
};
client.wait_for_current_user(cx).await;
client
@@ -319,12 +315,16 @@ impl Drop for TestServer {
struct TestClient {
client: Arc<Client>,
username: String,
local_projects: Vec<ModelHandle<Project>>,
remote_projects: Vec<ModelHandle<Project>>,
next_root_dir_id: usize,
state: RefCell<TestClientState>,
pub user_store: ModelHandle<UserStore>,
language_registry: Arc<LanguageRegistry>,
fs: Arc<FakeFs>,
}
#[derive(Default)]
struct TestClientState {
local_projects: Vec<ModelHandle<Project>>,
remote_projects: Vec<ModelHandle<Project>>,
buffers: HashMap<ModelHandle<Project>, HashSet<ModelHandle<language::Buffer>>>,
}
@@ -363,6 +363,38 @@ impl TestClient {
.await;
}
fn local_projects<'a>(&'a self) -> impl Deref<Target = Vec<ModelHandle<Project>>> + 'a {
Ref::map(self.state.borrow(), |state| &state.local_projects)
}
fn remote_projects<'a>(&'a self) -> impl Deref<Target = Vec<ModelHandle<Project>>> + 'a {
Ref::map(self.state.borrow(), |state| &state.remote_projects)
}
fn local_projects_mut<'a>(&'a self) -> impl DerefMut<Target = Vec<ModelHandle<Project>>> + 'a {
RefMut::map(self.state.borrow_mut(), |state| &mut state.local_projects)
}
fn remote_projects_mut<'a>(&'a self) -> impl DerefMut<Target = Vec<ModelHandle<Project>>> + 'a {
RefMut::map(self.state.borrow_mut(), |state| &mut state.remote_projects)
}
fn buffers_for_project<'a>(
&'a self,
project: &ModelHandle<Project>,
) -> impl DerefMut<Target = HashSet<ModelHandle<language::Buffer>>> + 'a {
RefMut::map(self.state.borrow_mut(), |state| {
state.buffers.entry(project.clone()).or_default()
})
}
fn buffers<'a>(
&'a self,
) -> impl DerefMut<Target = HashMap<ModelHandle<Project>, HashSet<ModelHandle<language::Buffer>>>> + 'a
{
RefMut::map(self.state.borrow_mut(), |state| &mut state.buffers)
}
fn summarize_contacts(&self, cx: &TestAppContext) -> ContactsSummary {
self.user_store.read_with(cx, |store, _| ContactsSummary {
current: store
@@ -433,25 +465,41 @@ impl TestClient {
project: &ModelHandle<Project>,
cx: &mut TestAppContext,
) -> ViewHandle<Workspace> {
let (_, root_view) = cx.add_window(|_| EmptyView);
cx.add_view(&root_view, |cx| {
Workspace::new(
Default::default(),
0,
project.clone(),
|_, _| unimplemented!(),
cx,
)
})
}
struct WorkspaceContainer {
workspace: Option<WeakViewHandle<Workspace>>,
}
fn create_new_root_dir(&mut self) -> PathBuf {
format!(
"/{}-root-{}",
self.username,
util::post_inc(&mut self.next_root_dir_id)
)
.into()
impl Entity for WorkspaceContainer {
type Event = ();
}
impl View for WorkspaceContainer {
fn ui_name() -> &'static str {
"WorkspaceContainer"
}
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
if let Some(workspace) = self
.workspace
.as_ref()
.and_then(|workspace| workspace.upgrade(cx))
{
ChildView::new(&workspace, cx).into_any()
} else {
Empty::new().into_any()
}
}
}
// We use a workspace container so that we don't need to remove the window in order to
// drop the workspace and we can use a ViewHandle instead.
let (window_id, container) = cx.add_window(|_| WorkspaceContainer { workspace: None });
let workspace = cx.add_view(window_id, |cx| Workspace::test_new(project.clone(), cx));
container.update(cx, |container, cx| {
container.workspace = Some(workspace.downgrade());
cx.notify();
});
workspace
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -27,7 +27,9 @@ call = { path = "../call" }
client = { path = "../client" }
clock = { path = "../clock" }
collections = { path = "../collections" }
context_menu = { path = "../context_menu" }
editor = { path = "../editor" }
feedback = { path = "../feedback" }
fuzzy = { path = "../fuzzy" }
gpui = { path = "../gpui" }
menu = { path = "../menu" }
@@ -37,11 +39,13 @@ settings = { path = "../settings" }
theme = { path = "../theme" }
util = { path = "../util" }
workspace = { path = "../workspace" }
anyhow = "1.0"
futures = "0.3"
log = "0.4"
postage = { version = "0.4.1", features = ["futures-traits"] }
serde = { version = "1.0", features = ["derive", "rc"] }
anyhow.workspace = true
futures.workspace = true
log.workspace = true
postage.workspace = true
serde.workspace = true
serde_derive.workspace = true
[dev-dependencies]
call = { path = "../call", features = ["test-support"] }

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,4 @@
mod collab_titlebar_item;
mod collaborator_list_popover;
mod contact_finder;
mod contact_list;
mod contact_notification;
@@ -10,32 +9,27 @@ mod notifications;
mod project_shared_notification;
mod sharing_status_indicator;
use anyhow::anyhow;
use call::ActiveCall;
pub use collab_titlebar_item::{CollabTitlebarItem, ToggleContactsMenu};
use gpui::{actions, MutableAppContext, Task};
use gpui::{actions, AppContext, Task};
use std::sync::Arc;
use workspace::{AppState, JoinProject, ToggleFollow, Workspace};
use workspace::AppState;
actions!(collab, [ToggleScreenSharing]);
pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
collab_titlebar_item::init(cx);
contact_notification::init(cx);
contact_list::init(cx);
contact_finder::init(cx);
contacts_popover::init(cx);
incoming_call_notification::init(cx);
project_shared_notification::init(cx);
incoming_call_notification::init(&app_state, cx);
project_shared_notification::init(&app_state, cx);
sharing_status_indicator::init(cx);
cx.add_global_action(toggle_screen_sharing);
cx.add_global_action(move |action: &JoinProject, cx| {
join_project(action, app_state.clone(), cx);
});
}
pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut MutableAppContext) {
pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut AppContext) {
if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
let toggle_screen_sharing = room.update(cx, |room, cx| {
if room.is_screen_sharing() {
@@ -47,87 +41,3 @@ pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut MutableAppContext
toggle_screen_sharing.detach_and_log_err(cx);
}
}
fn join_project(action: &JoinProject, app_state: Arc<AppState>, cx: &mut MutableAppContext) {
let project_id = action.project_id;
let follow_user_id = action.follow_user_id;
cx.spawn(|mut cx| async move {
let existing_workspace = cx.update(|cx| {
cx.window_ids()
.filter_map(|window_id| cx.root_view::<Workspace>(window_id))
.find(|workspace| {
workspace.read(cx).project().read(cx).remote_id() == Some(project_id)
})
});
let workspace = if let Some(existing_workspace) = existing_workspace {
existing_workspace
} else {
let active_call = cx.read(ActiveCall::global);
let room = active_call
.read_with(&cx, |call, _| call.room().cloned())
.ok_or_else(|| anyhow!("not in a call"))?;
let project = room
.update(&mut cx, |room, cx| {
room.join_project(
project_id,
app_state.languages.clone(),
app_state.fs.clone(),
cx,
)
})
.await?;
let (_, workspace) = cx.add_window(
(app_state.build_window_options)(None, None, cx.platform().as_ref()),
|cx| {
let mut workspace = Workspace::new(
Default::default(),
0,
project,
app_state.dock_default_item_factory,
cx,
);
(app_state.initialize_workspace)(&mut workspace, &app_state, cx);
workspace
},
);
workspace
};
cx.activate_window(workspace.window_id());
cx.platform().activate(true);
workspace.update(&mut cx, |workspace, cx| {
if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
let follow_peer_id = room
.read(cx)
.remote_participants()
.iter()
.find(|(_, participant)| participant.user.id == follow_user_id)
.map(|(_, p)| p.peer_id)
.or_else(|| {
// If we couldn't follow the given user, follow the host instead.
let collaborator = workspace
.project()
.read(cx)
.collaborators()
.values()
.find(|collaborator| collaborator.replica_id == 0)?;
Some(collaborator.peer_id)
});
if let Some(follow_peer_id) = follow_peer_id {
if !workspace.is_being_followed(follow_peer_id) {
workspace
.toggle_follow(&ToggleFollow(follow_peer_id), cx)
.map(|follow| follow.detach_and_log_err(cx));
}
}
}
});
anyhow::Ok(())
})
.detach_and_log_err(cx);
}

View File

@@ -1,165 +0,0 @@
use call::ActiveCall;
use client::UserStore;
use gpui::Action;
use gpui::{
actions, elements::*, Entity, ModelHandle, MouseButton, RenderContext, View, ViewContext,
};
use settings::Settings;
use crate::collab_titlebar_item::ToggleCollaboratorList;
pub(crate) enum Event {
Dismissed,
}
enum Collaborator {
SelfUser { username: String },
RemoteUser { username: String },
}
actions!(collaborator_list_popover, [NoOp]);
pub(crate) struct CollaboratorListPopover {
list_state: ListState,
}
impl Entity for CollaboratorListPopover {
type Event = Event;
}
impl View for CollaboratorListPopover {
fn ui_name() -> &'static str {
"CollaboratorListPopover"
}
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
let theme = cx.global::<Settings>().theme.clone();
MouseEventHandler::<Self>::new(0, cx, |_, _| {
List::new(self.list_state.clone())
.contained()
.with_style(theme.contacts_popover.container) //TODO: Change the name of this theme key
.constrained()
.with_width(theme.contacts_popover.width)
.with_height(theme.contacts_popover.height)
.boxed()
})
.on_down_out(MouseButton::Left, move |_, cx| {
cx.dispatch_action(ToggleCollaboratorList);
})
.boxed()
}
fn focus_out(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext<Self>) {
cx.emit(Event::Dismissed);
}
}
impl CollaboratorListPopover {
pub fn new(user_store: ModelHandle<UserStore>, cx: &mut ViewContext<Self>) -> Self {
let active_call = ActiveCall::global(cx);
let mut collaborators = user_store
.read(cx)
.current_user()
.map(|u| Collaborator::SelfUser {
username: u.github_login.clone(),
})
.into_iter()
.collect::<Vec<_>>();
//TODO: What should the canonical sort here look like, consult contacts list implementation
if let Some(room) = active_call.read(cx).room() {
for participant in room.read(cx).remote_participants() {
collaborators.push(Collaborator::RemoteUser {
username: participant.1.user.github_login.clone(),
});
}
}
Self {
list_state: ListState::new(
collaborators.len(),
Orientation::Top,
0.,
cx,
move |_, index, cx| match &collaborators[index] {
Collaborator::SelfUser { username } => render_collaborator_list_entry(
index,
username,
None::<NoOp>,
None,
Svg::new("icons/chevron_right_12.svg"),
NoOp,
"Leave call".to_owned(),
cx,
),
Collaborator::RemoteUser { username } => render_collaborator_list_entry(
index,
username,
Some(NoOp),
Some(format!("Follow {username}")),
Svg::new("icons/x_mark_12.svg"),
NoOp,
format!("Remove {username} from call"),
cx,
),
},
),
}
}
}
fn render_collaborator_list_entry<UA: Action + Clone, IA: Action + Clone>(
index: usize,
username: &str,
username_action: Option<UA>,
username_tooltip: Option<String>,
icon: Svg,
icon_action: IA,
icon_tooltip: String,
cx: &mut RenderContext<CollaboratorListPopover>,
) -> ElementBox {
enum Username {}
enum UsernameTooltip {}
enum Icon {}
enum IconTooltip {}
let theme = &cx.global::<Settings>().theme;
let username_theme = theme.contact_list.contact_username.text.clone();
let tooltip_theme = theme.tooltip.clone();
let username = MouseEventHandler::<Username>::new(index, cx, |_, _| {
Label::new(username.to_owned(), username_theme.clone()).boxed()
})
.on_click(MouseButton::Left, move |_, cx| {
if let Some(username_action) = username_action.clone() {
cx.dispatch_action(username_action);
}
});
Flex::row()
.with_child(if let Some(username_tooltip) = username_tooltip {
username
.with_tooltip::<UsernameTooltip, _>(
index,
username_tooltip,
None,
tooltip_theme.clone(),
cx,
)
.boxed()
} else {
username.boxed()
})
.with_child(
MouseEventHandler::<Icon>::new(index, cx, |_, _| icon.boxed())
.on_click(MouseButton::Left, move |_, cx| {
cx.dispatch_action(icon_action.clone())
})
.with_tooltip::<IconTooltip, _>(index, icon_tooltip, None, tooltip_theme, cx)
.boxed(),
)
.boxed()
}

View File

@@ -1,49 +1,42 @@
use client::{ContactRequestStatus, User, UserStore};
use gpui::{
elements::*, AnyViewHandle, Entity, ModelHandle, MouseState, MutableAppContext, RenderContext,
Task, View, ViewContext, ViewHandle,
};
use picker::{Picker, PickerDelegate};
use gpui::{elements::*, AppContext, ModelHandle, MouseState, Task, ViewContext};
use picker::{Picker, PickerDelegate, PickerEvent};
use settings::Settings;
use std::sync::Arc;
use util::TryFutureExt;
pub fn init(cx: &mut MutableAppContext) {
Picker::<ContactFinder>::init(cx);
pub fn init(cx: &mut AppContext) {
Picker::<ContactFinderDelegate>::init(cx);
}
pub struct ContactFinder {
picker: ViewHandle<Picker<Self>>,
pub type ContactFinder = Picker<ContactFinderDelegate>;
pub fn build_contact_finder(
user_store: ModelHandle<UserStore>,
cx: &mut ViewContext<ContactFinder>,
) -> ContactFinder {
Picker::new(
ContactFinderDelegate {
user_store,
potential_contacts: Arc::from([]),
selected_index: 0,
},
cx,
)
.with_theme(|theme| theme.contact_finder.picker.clone())
}
pub struct ContactFinderDelegate {
potential_contacts: Arc<[Arc<User>]>,
user_store: ModelHandle<UserStore>,
selected_index: usize,
}
pub enum Event {
Dismissed,
}
impl Entity for ContactFinder {
type Event = Event;
}
impl View for ContactFinder {
fn ui_name() -> &'static str {
"ContactFinder"
impl PickerDelegate for ContactFinderDelegate {
fn placeholder_text(&self) -> Arc<str> {
"Search collaborator by username...".into()
}
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
ChildView::new(self.picker.clone(), cx).boxed()
}
fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
if cx.is_self_focused() {
cx.focus(&self.picker);
}
}
}
impl PickerDelegate for ContactFinder {
fn match_count(&self) -> usize {
self.potential_contacts.len()
}
@@ -52,30 +45,30 @@ impl PickerDelegate for ContactFinder {
self.selected_index
}
fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext<Self>) {
fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext<Picker<Self>>) {
self.selected_index = ix;
}
fn update_matches(&mut self, query: String, cx: &mut ViewContext<Self>) -> Task<()> {
fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
let search_users = self
.user_store
.update(cx, |store, cx| store.fuzzy_search_users(query, cx));
cx.spawn(|this, mut cx| async move {
cx.spawn(|picker, mut cx| async move {
async {
let potential_contacts = search_users.await?;
this.update(&mut cx, |this, cx| {
this.potential_contacts = potential_contacts.into();
picker.update(&mut cx, |picker, cx| {
picker.delegate_mut().potential_contacts = potential_contacts.into();
cx.notify();
});
Ok(())
})?;
anyhow::Ok(())
}
.log_err()
.await;
})
}
fn confirm(&mut self, cx: &mut ViewContext<Self>) {
fn confirm(&mut self, cx: &mut ViewContext<Picker<Self>>) {
if let Some(user) = self.potential_contacts.get(self.selected_index) {
let user_store = self.user_store.read(cx);
match user_store.contact_request_status(user) {
@@ -94,8 +87,8 @@ impl PickerDelegate for ContactFinder {
}
}
fn dismiss(&mut self, cx: &mut ViewContext<Self>) {
cx.emit(Event::Dismissed);
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
cx.emit(PickerEvent::Dismiss);
}
fn render_match(
@@ -104,7 +97,7 @@ impl PickerDelegate for ContactFinder {
mouse_state: &mut MouseState,
selected: bool,
cx: &gpui::AppContext,
) -> ElementBox {
) -> AnyElement<Picker<Self>> {
let theme = &cx.global::<Settings>().theme;
let user = &self.potential_contacts[ix];
let request_status = self.user_store.read(cx).contact_request_status(user);
@@ -128,19 +121,17 @@ impl PickerDelegate for ContactFinder {
.style_for(mouse_state, selected);
Flex::row()
.with_children(user.avatar.clone().map(|avatar| {
Image::new(avatar)
Image::from_data(avatar)
.with_style(theme.contact_finder.contact_avatar)
.aligned()
.left()
.boxed()
}))
.with_child(
Label::new(user.github_login.clone(), style.label.clone())
.contained()
.with_style(theme.contact_finder.contact_username)
.aligned()
.left()
.boxed(),
.left(),
)
.with_children(icon_path.map(|icon_path| {
Svg::new(icon_path)
@@ -155,27 +146,11 @@ impl PickerDelegate for ContactFinder {
.with_height(button_style.button_width)
.aligned()
.flex_float()
.boxed()
}))
.contained()
.with_style(style.container)
.constrained()
.with_height(theme.contact_finder.row_height)
.boxed()
}
}
impl ContactFinder {
pub fn new(user_store: ModelHandle<UserStore>, cx: &mut ViewContext<Self>) -> Self {
let this = cx.weak_handle();
Self {
picker: cx.add_view(|cx| {
Picker::new("Search collaborator by username...", this, cx)
.with_theme(|theme| theme.contact_finder.picker.clone())
}),
potential_contacts: Arc::from([]),
user_store,
selected_index: 0,
}
.into_any()
}
}

View File

@@ -1,5 +1,3 @@
use super::collab_titlebar_item::LeaveCall;
use crate::contacts_popover;
use call::ActiveCall;
use client::{proto::PeerId, Contact, User, UserStore};
use editor::{Cancel, Editor};
@@ -8,10 +6,10 @@ use fuzzy::{match_strings, StringMatchCandidate};
use gpui::{
elements::*,
geometry::{rect::RectF, vector::vec2f},
impl_actions, impl_internal_actions,
impl_actions,
keymap_matcher::KeymapContext,
AppContext, CursorStyle, Entity, ModelHandle, MouseButton, MutableAppContext, PromptLevel,
RenderContext, Subscription, View, ViewContext, ViewHandle,
platform::{CursorStyle, MouseButton, PromptLevel},
AppContext, Entity, ModelHandle, Subscription, View, ViewContext, ViewHandle, WeakViewHandle,
};
use menu::{Confirm, SelectNext, SelectPrev};
use project::Project;
@@ -19,29 +17,17 @@ use serde::Deserialize;
use settings::Settings;
use std::{mem, sync::Arc};
use theme::IconButton;
use workspace::{JoinProject, OpenSharedScreen};
use workspace::Workspace;
impl_actions!(contact_list, [RemoveContact, RespondToContactRequest]);
impl_internal_actions!(contact_list, [ToggleExpanded, Call]);
pub fn init(cx: &mut MutableAppContext) {
pub fn init(cx: &mut AppContext) {
cx.add_action(ContactList::remove_contact);
cx.add_action(ContactList::respond_to_contact_request);
cx.add_action(ContactList::cancel);
cx.add_action(ContactList::select_next);
cx.add_action(ContactList::select_prev);
cx.add_action(ContactList::confirm);
cx.add_action(ContactList::toggle_expanded);
cx.add_action(ContactList::call);
}
#[derive(Clone, PartialEq)]
struct ToggleExpanded(Section);
#[derive(Clone, PartialEq)]
struct Call {
recipient_user_id: u64,
initial_project: Option<ModelHandle<Project>>,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)]
@@ -141,7 +127,10 @@ impl PartialEq for ContactEntry {
pub struct RequestContact(pub u64);
#[derive(Clone, Deserialize, PartialEq)]
pub struct RemoveContact(pub u64);
pub struct RemoveContact {
user_id: u64,
github_login: String,
}
#[derive(Clone, Deserialize, PartialEq)]
pub struct RespondToContactRequest {
@@ -150,14 +139,16 @@ pub struct RespondToContactRequest {
}
pub enum Event {
ToggleContactFinder,
Dismissed,
}
pub struct ContactList {
entries: Vec<ContactEntry>,
match_candidates: Vec<StringMatchCandidate>,
list_state: ListState,
list_state: ListState<Self>,
project: ModelHandle<Project>,
workspace: WeakViewHandle<Workspace>,
user_store: ModelHandle<UserStore>,
filter_editor: ViewHandle<Editor>,
collapsed_sections: Vec<Section>,
@@ -169,6 +160,7 @@ impl ContactList {
pub fn new(
project: ModelHandle<Project>,
user_store: ModelHandle<UserStore>,
workspace: WeakViewHandle<Workspace>,
cx: &mut ViewContext<Self>,
) -> Self {
let filter_editor = cx.add_view(|cx| {
@@ -199,7 +191,7 @@ impl ContactList {
})
.detach();
let list_state = ListState::new(0, Orientation::Top, 1000., cx, move |this, ix, cx| {
let list_state = ListState::<Self>::new(0, Orientation::Top, 1000., move |this, ix, cx| {
let theme = cx.global::<Settings>().theme.clone();
let is_selected = this.selection == Some(ix);
let current_project_id = this.project.read(cx).remote_id();
@@ -288,23 +280,46 @@ impl ContactList {
filter_editor,
_subscriptions: subscriptions,
project,
workspace,
user_store,
};
this.update_entries(cx);
this
}
pub fn editor_text(&self, cx: &AppContext) -> String {
self.filter_editor.read(cx).text(cx)
}
pub fn with_editor_text(self, editor_text: String, cx: &mut ViewContext<Self>) -> Self {
self.filter_editor
.update(cx, |picker, cx| picker.set_text(editor_text, cx));
self
}
fn remove_contact(&mut self, request: &RemoveContact, cx: &mut ViewContext<Self>) {
let user_id = request.0;
let user_id = request.user_id;
let github_login = &request.github_login;
let user_store = self.user_store.clone();
let prompt_message = "Are you sure you want to remove this contact?";
let mut answer = cx.prompt(PromptLevel::Warning, prompt_message, &["Remove", "Cancel"]);
let prompt_message = format!(
"Are you sure you want to remove \"{}\" from your contacts?",
github_login
);
let mut answer = cx.prompt(PromptLevel::Warning, &prompt_message, &["Remove", "Cancel"]);
let window_id = cx.window_id();
cx.spawn(|_, mut cx| async move {
if answer.next().await == Some(0) {
user_store
if let Err(e) = user_store
.update(&mut cx, |store, cx| store.remove_contact(user_id, cx))
.await
.unwrap();
{
cx.prompt(
window_id,
PromptLevel::Info,
&format!("Failed to remove contact: {}", e),
&["Ok"],
);
}
}
})
.detach();
@@ -378,18 +393,11 @@ impl ContactList {
if let Some(entry) = self.entries.get(selection) {
match entry {
ContactEntry::Header(section) => {
let section = *section;
self.toggle_expanded(&ToggleExpanded(section), cx);
self.toggle_expanded(*section, cx);
}
ContactEntry::Contact { contact, calling } => {
if contact.online && !contact.busy && !calling {
self.call(
&Call {
recipient_user_id: contact.user.id,
initial_project: Some(self.project.clone()),
},
cx,
);
self.call(contact.user.id, Some(self.project.clone()), cx);
}
}
ContactEntry::ParticipantProject {
@@ -397,13 +405,23 @@ impl ContactList {
host_user_id,
..
} => {
cx.dispatch_global_action(JoinProject {
project_id: *project_id,
follow_user_id: *host_user_id,
});
if let Some(workspace) = self.workspace.upgrade(cx) {
let app_state = workspace.read(cx).app_state().clone();
workspace::join_remote_project(
*project_id,
*host_user_id,
app_state,
cx,
)
.detach_and_log_err(cx);
}
}
ContactEntry::ParticipantScreen { peer_id, .. } => {
cx.dispatch_action(OpenSharedScreen { peer_id: *peer_id });
if let Some(workspace) = self.workspace.upgrade(cx) {
workspace.update(cx, |workspace, cx| {
workspace.open_shared_screen(*peer_id, cx)
});
}
}
_ => {}
}
@@ -411,8 +429,7 @@ impl ContactList {
}
}
fn toggle_expanded(&mut self, action: &ToggleExpanded, cx: &mut ViewContext<Self>) {
let section = action.0;
fn toggle_expanded(&mut self, section: Section, cx: &mut ViewContext<Self>) {
if let Some(ix) = self.collapsed_sections.iter().position(|s| *s == section) {
self.collapsed_sections.remove(ix);
} else {
@@ -723,14 +740,13 @@ impl ContactList {
is_pending: bool,
is_selected: bool,
theme: &theme::ContactList,
) -> ElementBox {
) -> AnyElement<Self> {
Flex::row()
.with_children(user.avatar.clone().map(|avatar| {
Image::new(avatar)
Image::from_data(avatar)
.with_style(theme.contact_avatar)
.aligned()
.left()
.boxed()
}))
.with_child(
Label::new(
@@ -741,16 +757,14 @@ impl ContactList {
.with_style(theme.contact_username.container)
.aligned()
.left()
.flex(1., true)
.boxed(),
.flex(1., true),
)
.with_children(if is_pending {
Some(
Label::new("Calling", theme.calling_indicator.text.clone())
.contained()
.with_style(theme.calling_indicator.container)
.aligned()
.boxed(),
.aligned(),
)
} else {
None
@@ -763,7 +777,7 @@ impl ContactList {
.contact_row
.style_for(&mut Default::default(), is_selected),
)
.boxed()
.into_any()
}
fn render_participant_project(
@@ -774,8 +788,10 @@ impl ContactList {
is_last: bool,
is_selected: bool,
theme: &theme::ContactList,
cx: &mut RenderContext<Self>,
) -> ElementBox {
cx: &mut ViewContext<Self>,
) -> AnyElement<Self> {
enum JoinProject {}
let font_cache = cx.font_cache();
let host_avatar_height = theme
.contact_avatar
@@ -794,48 +810,44 @@ impl ContactList {
worktree_root_names.join(", ")
};
MouseEventHandler::<JoinProject>::new(project_id as usize, cx, |mouse_state, _| {
MouseEventHandler::<JoinProject, Self>::new(project_id as usize, cx, |mouse_state, _| {
let tree_branch = *tree_branch.style_for(mouse_state, is_selected);
let row = theme.project_row.style_for(mouse_state, is_selected);
Flex::row()
.with_child(
Stack::new()
.with_child(
Canvas::new(move |bounds, _, cx| {
let start_x = bounds.min_x() + (bounds.width() / 2.)
- (tree_branch.width / 2.);
let end_x = bounds.max_x();
let start_y = bounds.min_y();
let end_y = bounds.min_y() + baseline_offset - (cap_height / 2.);
.with_child(Canvas::new(move |scene, bounds, _, _, _| {
let start_x =
bounds.min_x() + (bounds.width() / 2.) - (tree_branch.width / 2.);
let end_x = bounds.max_x();
let start_y = bounds.min_y();
let end_y = bounds.min_y() + baseline_offset - (cap_height / 2.);
cx.scene.push_quad(gpui::Quad {
bounds: RectF::from_points(
vec2f(start_x, start_y),
vec2f(
start_x + tree_branch.width,
if is_last { end_y } else { bounds.max_y() },
),
scene.push_quad(gpui::Quad {
bounds: RectF::from_points(
vec2f(start_x, start_y),
vec2f(
start_x + tree_branch.width,
if is_last { end_y } else { bounds.max_y() },
),
background: Some(tree_branch.color),
border: gpui::Border::default(),
corner_radius: 0.,
});
cx.scene.push_quad(gpui::Quad {
bounds: RectF::from_points(
vec2f(start_x, end_y),
vec2f(end_x, end_y + tree_branch.width),
),
background: Some(tree_branch.color),
border: gpui::Border::default(),
corner_radius: 0.,
});
})
.boxed(),
)
),
background: Some(tree_branch.color),
border: gpui::Border::default(),
corner_radius: 0.,
});
scene.push_quad(gpui::Quad {
bounds: RectF::from_points(
vec2f(start_x, end_y),
vec2f(end_x, end_y + tree_branch.width),
),
background: Some(tree_branch.color),
border: gpui::Border::default(),
corner_radius: 0.,
});
}))
.constrained()
.with_width(host_avatar_height)
.boxed(),
.with_width(host_avatar_height),
)
.with_child(
Label::new(project_name, row.name.text.clone())
@@ -843,29 +855,28 @@ impl ContactList {
.left()
.contained()
.with_style(row.name.container)
.flex(1., false)
.boxed(),
.flex(1., false),
)
.constrained()
.with_height(theme.row_height)
.contained()
.with_style(row.container)
.boxed()
})
.with_cursor_style(if !is_current {
CursorStyle::PointingHand
} else {
CursorStyle::Arrow
})
.on_click(MouseButton::Left, move |_, cx| {
.on_click(MouseButton::Left, move |_, this, cx| {
if !is_current {
cx.dispatch_global_action(JoinProject {
project_id,
follow_user_id: host_user_id,
});
if let Some(workspace) = this.workspace.upgrade(cx) {
let app_state = workspace.read(cx).app_state().clone();
workspace::join_remote_project(project_id, host_user_id, app_state, cx)
.detach_and_log_err(cx);
}
}
})
.boxed()
.into_any()
}
fn render_participant_screen(
@@ -873,8 +884,10 @@ impl ContactList {
is_last: bool,
is_selected: bool,
theme: &theme::ContactList,
cx: &mut RenderContext<Self>,
) -> ElementBox {
cx: &mut ViewContext<Self>,
) -> AnyElement<Self> {
enum OpenSharedScreen {}
let font_cache = cx.font_cache();
let host_avatar_height = theme
.contact_avatar
@@ -888,7 +901,7 @@ impl ContactList {
let baseline_offset =
row.name.text.baseline_offset(font_cache) + (theme.row_height - line_height) / 2.;
MouseEventHandler::<OpenSharedScreen>::new(
MouseEventHandler::<OpenSharedScreen, Self>::new(
peer_id.as_u64() as usize,
cx,
|mouse_state, _| {
@@ -898,42 +911,37 @@ impl ContactList {
Flex::row()
.with_child(
Stack::new()
.with_child(
Canvas::new(move |bounds, _, cx| {
let start_x = bounds.min_x() + (bounds.width() / 2.)
- (tree_branch.width / 2.);
let end_x = bounds.max_x();
let start_y = bounds.min_y();
let end_y =
bounds.min_y() + baseline_offset - (cap_height / 2.);
.with_child(Canvas::new(move |scene, bounds, _, _, _| {
let start_x = bounds.min_x() + (bounds.width() / 2.)
- (tree_branch.width / 2.);
let end_x = bounds.max_x();
let start_y = bounds.min_y();
let end_y = bounds.min_y() + baseline_offset - (cap_height / 2.);
cx.scene.push_quad(gpui::Quad {
bounds: RectF::from_points(
vec2f(start_x, start_y),
vec2f(
start_x + tree_branch.width,
if is_last { end_y } else { bounds.max_y() },
),
scene.push_quad(gpui::Quad {
bounds: RectF::from_points(
vec2f(start_x, start_y),
vec2f(
start_x + tree_branch.width,
if is_last { end_y } else { bounds.max_y() },
),
background: Some(tree_branch.color),
border: gpui::Border::default(),
corner_radius: 0.,
});
cx.scene.push_quad(gpui::Quad {
bounds: RectF::from_points(
vec2f(start_x, end_y),
vec2f(end_x, end_y + tree_branch.width),
),
background: Some(tree_branch.color),
border: gpui::Border::default(),
corner_radius: 0.,
});
})
.boxed(),
)
),
background: Some(tree_branch.color),
border: gpui::Border::default(),
corner_radius: 0.,
});
scene.push_quad(gpui::Quad {
bounds: RectF::from_points(
vec2f(start_x, end_y),
vec2f(end_x, end_y + tree_branch.width),
),
background: Some(tree_branch.color),
border: gpui::Border::default(),
corner_radius: 0.,
});
}))
.constrained()
.with_width(host_avatar_height)
.boxed(),
.with_width(host_avatar_height),
)
.with_child(
Svg::new("icons/disable_screen_sharing_12.svg")
@@ -943,8 +951,7 @@ impl ContactList {
.aligned()
.left()
.contained()
.with_style(row.icon.container)
.boxed(),
.with_style(row.icon.container),
)
.with_child(
Label::new("Screen", row.name.text.clone())
@@ -952,21 +959,23 @@ impl ContactList {
.left()
.contained()
.with_style(row.name.container)
.flex(1., false)
.boxed(),
.flex(1., false),
)
.constrained()
.with_height(theme.row_height)
.contained()
.with_style(row.container)
.boxed()
},
)
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, move |_, cx| {
cx.dispatch_action(OpenSharedScreen { peer_id });
.on_click(MouseButton::Left, move |_, this, cx| {
if let Some(workspace) = this.workspace.upgrade(cx) {
workspace.update(cx, |workspace, cx| {
workspace.open_shared_screen(peer_id, cx)
});
}
})
.boxed()
.into_any()
}
fn render_header(
@@ -974,8 +983,8 @@ impl ContactList {
theme: &theme::ContactList,
is_selected: bool,
is_collapsed: bool,
cx: &mut RenderContext<Self>,
) -> ElementBox {
cx: &mut ViewContext<Self>,
) -> AnyElement<Self> {
enum Header {}
enum LeaveCallContactList {}
@@ -990,23 +999,25 @@ impl ContactList {
};
let leave_call = if section == Section::ActiveCall {
Some(
MouseEventHandler::<LeaveCallContactList>::new(0, cx, |state, _| {
MouseEventHandler::<LeaveCallContactList, Self>::new(0, cx, |state, _| {
let style = theme.leave_call.style_for(state, false);
Label::new("Leave Call", style.text.clone())
.contained()
.with_style(style.container)
.boxed()
})
.on_click(MouseButton::Left, |_, cx| cx.dispatch_action(LeaveCall))
.aligned()
.boxed(),
.on_click(MouseButton::Left, |_, _, cx| {
ActiveCall::global(cx)
.update(cx, |call, cx| call.hang_up(cx))
.detach_and_log_err(cx);
})
.aligned(),
)
} else {
None
};
let icon_size = theme.section_icon_size;
MouseEventHandler::<Header>::new(section as usize, cx, |_, _| {
MouseEventHandler::<Header, Self>::new(section as usize, cx, |_, _| {
Flex::row()
.with_child(
Svg::new(if is_collapsed {
@@ -1020,8 +1031,7 @@ impl ContactList {
.with_max_height(icon_size)
.aligned()
.constrained()
.with_width(icon_size)
.boxed(),
.with_width(icon_size),
)
.with_child(
Label::new(text, header_style.text.clone())
@@ -1029,21 +1039,19 @@ impl ContactList {
.left()
.contained()
.with_margin_left(theme.contact_username.container.margin.left)
.flex(1., true)
.boxed(),
.flex(1., true),
)
.with_children(leave_call)
.constrained()
.with_height(theme.row_height)
.contained()
.with_style(header_style.container)
.boxed()
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, move |_, cx| {
cx.dispatch_action(ToggleExpanded(section))
.on_click(MouseButton::Left, move |_, this, cx| {
this.toggle_expanded(section, cx);
})
.boxed()
.into_any()
}
fn render_contact(
@@ -1052,14 +1060,15 @@ impl ContactList {
project: &ModelHandle<Project>,
theme: &theme::ContactList,
is_selected: bool,
cx: &mut RenderContext<Self>,
) -> ElementBox {
cx: &mut ViewContext<Self>,
) -> AnyElement<Self> {
let online = contact.online;
let busy = contact.busy || calling;
let user_id = contact.user.id;
let github_login = contact.user.github_login.clone();
let initial_project = project.clone();
let mut element =
MouseEventHandler::<Contact>::new(contact.user.id as usize, cx, |_, cx| {
let mut event_handler =
MouseEventHandler::<Contact, Self>::new(contact.user.id as usize, cx, |_, cx| {
Flex::row()
.with_children(contact.user.avatar.clone().map(|avatar| {
let status_badge = if contact.online {
@@ -1072,22 +1081,19 @@ impl ContactList {
} else {
theme.contact_status_free
})
.aligned()
.boxed(),
.aligned(),
)
} else {
None
};
Stack::new()
.with_child(
Image::new(avatar)
Image::from_data(avatar)
.with_style(theme.contact_avatar)
.aligned()
.left()
.boxed(),
.left(),
)
.with_children(status_badge)
.boxed()
}))
.with_child(
Label::new(
@@ -1098,11 +1104,10 @@ impl ContactList {
.with_style(theme.contact_username.container)
.aligned()
.left()
.flex(1., true)
.boxed(),
.flex(1., true),
)
.with_child(
MouseEventHandler::<Cancel>::new(
MouseEventHandler::<Cancel, Self>::new(
contact.user.id as usize,
cx,
|mouse_state, _| {
@@ -1111,24 +1116,27 @@ impl ContactList {
render_icon_button(button_style, "icons/x_mark_8.svg")
.aligned()
.flex_float()
.boxed()
},
)
.with_padding(Padding::uniform(2.))
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, move |_, cx| {
cx.dispatch_action(RemoveContact(user_id))
.on_click(MouseButton::Left, move |_, this, cx| {
this.remove_contact(
&RemoveContact {
user_id,
github_login: github_login.clone(),
},
cx,
);
})
.flex_float()
.boxed(),
.flex_float(),
)
.with_children(if calling {
Some(
Label::new("Calling", theme.calling_indicator.text.clone())
.contained()
.with_style(theme.calling_indicator.container)
.aligned()
.boxed(),
.aligned(),
)
} else {
None
@@ -1141,22 +1149,18 @@ impl ContactList {
.contact_row
.style_for(&mut Default::default(), is_selected),
)
.boxed()
})
.on_click(MouseButton::Left, move |_, cx| {
.on_click(MouseButton::Left, move |_, this, cx| {
if online && !busy {
cx.dispatch_action(Call {
recipient_user_id: user_id,
initial_project: Some(initial_project.clone()),
});
this.call(user_id, Some(initial_project.clone()), cx);
}
});
if online {
element = element.with_cursor_style(CursorStyle::PointingHand);
event_handler = event_handler.with_cursor_style(CursorStyle::PointingHand);
}
element.boxed()
event_handler.into_any()
}
fn render_contact_request(
@@ -1165,19 +1169,18 @@ impl ContactList {
theme: &theme::ContactList,
is_incoming: bool,
is_selected: bool,
cx: &mut RenderContext<Self>,
) -> ElementBox {
cx: &mut ViewContext<Self>,
) -> AnyElement<Self> {
enum Decline {}
enum Accept {}
enum Cancel {}
let mut row = Flex::row()
.with_children(user.avatar.clone().map(|avatar| {
Image::new(avatar)
Image::from_data(avatar)
.with_style(theme.contact_avatar)
.aligned()
.left()
.boxed()
}))
.with_child(
Label::new(
@@ -1188,37 +1191,40 @@ impl ContactList {
.with_style(theme.contact_username.container)
.aligned()
.left()
.flex(1., true)
.boxed(),
.flex(1., true),
);
let user_id = user.id;
let github_login = user.github_login.clone();
let is_contact_request_pending = user_store.read(cx).is_contact_request_pending(&user);
let button_spacing = theme.contact_button_spacing;
if is_incoming {
row.add_children([
MouseEventHandler::<Decline>::new(user.id as usize, cx, |mouse_state, _| {
row.add_child(
MouseEventHandler::<Decline, Self>::new(user.id as usize, cx, |mouse_state, _| {
let button_style = if is_contact_request_pending {
&theme.disabled_button
} else {
theme.contact_button.style_for(mouse_state, false)
};
render_icon_button(button_style, "icons/x_mark_8.svg")
.aligned()
.boxed()
render_icon_button(button_style, "icons/x_mark_8.svg").aligned()
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, move |_, cx| {
cx.dispatch_action(RespondToContactRequest {
user_id,
accept: false,
})
.on_click(MouseButton::Left, move |_, this, cx| {
this.respond_to_contact_request(
&RespondToContactRequest {
user_id,
accept: false,
},
cx,
);
})
.contained()
.with_margin_right(button_spacing)
.boxed(),
MouseEventHandler::<Accept>::new(user.id as usize, cx, |mouse_state, _| {
.with_margin_right(button_spacing),
);
row.add_child(
MouseEventHandler::<Accept, Self>::new(user.id as usize, cx, |mouse_state, _| {
let button_style = if is_contact_request_pending {
&theme.disabled_button
} else {
@@ -1227,20 +1233,21 @@ impl ContactList {
render_icon_button(button_style, "icons/check_8.svg")
.aligned()
.flex_float()
.boxed()
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, move |_, cx| {
cx.dispatch_action(RespondToContactRequest {
user_id,
accept: true,
})
})
.boxed(),
]);
.on_click(MouseButton::Left, move |_, this, cx| {
this.respond_to_contact_request(
&RespondToContactRequest {
user_id,
accept: true,
},
cx,
);
}),
);
} else {
row.add_child(
MouseEventHandler::<Cancel>::new(user.id as usize, cx, |mouse_state, _| {
MouseEventHandler::<Cancel, Self>::new(user.id as usize, cx, |mouse_state, _| {
let button_style = if is_contact_request_pending {
&theme.disabled_button
} else {
@@ -1249,15 +1256,19 @@ impl ContactList {
render_icon_button(button_style, "icons/x_mark_8.svg")
.aligned()
.flex_float()
.boxed()
})
.with_padding(Padding::uniform(2.))
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, move |_, cx| {
cx.dispatch_action(RemoveContact(user_id))
.on_click(MouseButton::Left, move |_, this, cx| {
this.remove_contact(
&RemoveContact {
user_id,
github_login: github_login.clone(),
},
cx,
);
})
.flex_float()
.boxed(),
.flex_float(),
);
}
@@ -1269,12 +1280,15 @@ impl ContactList {
.contact_row
.style_for(&mut Default::default(), is_selected),
)
.boxed()
.into_any()
}
fn call(&mut self, action: &Call, cx: &mut ViewContext<Self>) {
let recipient_user_id = action.recipient_user_id;
let initial_project = action.initial_project.clone();
fn call(
&mut self,
recipient_user_id: u64,
initial_project: Option<ModelHandle<Project>>,
cx: &mut ViewContext<Self>,
) {
ActiveCall::global(cx)
.update(cx, |call, cx| {
call.invite(recipient_user_id, initial_project, cx)
@@ -1292,13 +1306,12 @@ impl View for ContactList {
"ContactList"
}
fn keymap_context(&self, _: &AppContext) -> KeymapContext {
let mut cx = Self::default_keymap_context();
cx.set.insert("menu".into());
cx
fn update_keymap_context(&self, keymap: &mut KeymapContext, _: &AppContext) {
Self::reset_to_default_keymap_context(keymap);
keymap.add_identifier("menu");
}
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
enum AddContact {}
let theme = cx.global::<Settings>().theme.clone();
@@ -1306,39 +1319,35 @@ impl View for ContactList {
.with_child(
Flex::row()
.with_child(
ChildView::new(self.filter_editor.clone(), cx)
ChildView::new(&self.filter_editor, cx)
.contained()
.with_style(theme.contact_list.user_query_editor.container)
.flex(1., true)
.boxed(),
.flex(1., true),
)
.with_child(
MouseEventHandler::<AddContact>::new(0, cx, |_, _| {
MouseEventHandler::<AddContact, Self>::new(0, cx, |_, _| {
render_icon_button(
&theme.contact_list.add_contact_button,
"icons/user_plus_16.svg",
)
.boxed()
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, |_, cx| {
cx.dispatch_action(contacts_popover::ToggleContactFinder)
.on_click(MouseButton::Left, |_, _, cx| {
cx.emit(Event::ToggleContactFinder)
})
.with_tooltip::<AddContact, _>(
.with_tooltip::<AddContact>(
0,
"Search for new contact".into(),
None,
theme.tooltip.clone(),
cx,
)
.boxed(),
),
)
.constrained()
.with_height(theme.contact_list.user_query_editor_height)
.boxed(),
.with_height(theme.contact_list.user_query_editor_height),
)
.with_child(List::new(self.list_state.clone()).flex(1., false).boxed())
.boxed()
.with_child(List::new(self.list_state.clone()).flex(1., false))
.into_any()
}
fn focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext<Self>) {
@@ -1354,7 +1363,7 @@ impl View for ContactList {
}
}
fn render_icon_button(style: &IconButton, svg_path: &'static str) -> impl Element {
fn render_icon_button(style: &IconButton, svg_path: &'static str) -> impl Element<ContactList> {
Svg::new(svg_path)
.with_color(style.color)
.constrained()

View File

@@ -2,19 +2,9 @@ use std::sync::Arc;
use crate::notifications::render_user_notification;
use client::{ContactEventKind, User, UserStore};
use gpui::{
elements::*, impl_internal_actions, Entity, ModelHandle, MutableAppContext, RenderContext,
View, ViewContext,
};
use gpui::{elements::*, Entity, ModelHandle, View, ViewContext};
use workspace::notifications::Notification;
impl_internal_actions!(contact_notifications, [Dismiss, RespondToContactRequest]);
pub fn init(cx: &mut MutableAppContext) {
cx.add_action(ContactNotification::dismiss);
cx.add_action(ContactNotification::respond_to_contact_request);
}
pub struct ContactNotification {
user_store: ModelHandle<UserStore>,
user: Arc<User>,
@@ -43,26 +33,24 @@ impl View for ContactNotification {
"ContactNotification"
}
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
match self.kind {
ContactEventKind::Requested => render_user_notification(
self.user.clone(),
"wants to add you as a contact",
Some("They won't be alerted if you decline."),
Dismiss(self.user.id),
|notification, cx| notification.dismiss(cx),
vec![
(
"Decline",
Box::new(RespondToContactRequest {
user_id: self.user.id,
accept: false,
Box::new(|notification, cx| {
notification.respond_to_contact_request(false, cx)
}),
),
(
"Accept",
Box::new(RespondToContactRequest {
user_id: self.user.id,
accept: true,
Box::new(|notification, cx| {
notification.respond_to_contact_request(true, cx)
}),
),
],
@@ -72,7 +60,7 @@ impl View for ContactNotification {
self.user.clone(),
"accepted your contact request",
None,
Dismiss(self.user.id),
|notification, cx| notification.dismiss(cx),
vec![],
cx,
),
@@ -114,7 +102,7 @@ impl ContactNotification {
}
}
fn dismiss(&mut self, _: &Dismiss, cx: &mut ViewContext<Self>) {
fn dismiss(&mut self, cx: &mut ViewContext<Self>) {
self.user_store.update(cx, |store, cx| {
store
.dismiss_contact_request(self.user.id, cx)
@@ -123,14 +111,10 @@ impl ContactNotification {
cx.emit(Event::Dismiss);
}
fn respond_to_contact_request(
&mut self,
action: &RespondToContactRequest,
cx: &mut ViewContext<Self>,
) {
fn respond_to_contact_request(&mut self, accept: bool, cx: &mut ViewContext<Self>) {
self.user_store
.update(cx, |store, cx| {
store.respond_to_contact_request(action.user_id, action.accept, cx)
store.respond_to_contact_request(self.user.id, accept, cx)
})
.detach();
}

View File

@@ -1,15 +1,20 @@
use crate::{contact_finder::ContactFinder, contact_list::ContactList, ToggleContactsMenu};
use crate::{
contact_finder::{build_contact_finder, ContactFinder},
contact_list::ContactList,
};
use client::UserStore;
use gpui::{
actions, elements::*, ClipboardItem, CursorStyle, Entity, ModelHandle, MouseButton,
MutableAppContext, RenderContext, View, ViewContext, ViewHandle,
actions, elements::*, platform::MouseButton, AppContext, Entity, ModelHandle, View,
ViewContext, ViewHandle, WeakViewHandle,
};
use picker::PickerEvent;
use project::Project;
use settings::Settings;
use workspace::Workspace;
actions!(contacts_popover, [ToggleContactFinder]);
pub fn init(cx: &mut MutableAppContext) {
pub fn init(cx: &mut AppContext) {
cx.add_action(ContactsPopover::toggle_contact_finder);
}
@@ -26,6 +31,7 @@ pub struct ContactsPopover {
child: Child,
project: ModelHandle<Project>,
user_store: ModelHandle<UserStore>,
workspace: WeakViewHandle<Workspace>,
_subscription: Option<gpui::Subscription>,
}
@@ -33,43 +39,59 @@ impl ContactsPopover {
pub fn new(
project: ModelHandle<Project>,
user_store: ModelHandle<UserStore>,
workspace: WeakViewHandle<Workspace>,
cx: &mut ViewContext<Self>,
) -> Self {
let mut this = Self {
child: Child::ContactList(
cx.add_view(|cx| ContactList::new(project.clone(), user_store.clone(), cx)),
),
child: Child::ContactList(cx.add_view(|cx| {
ContactList::new(project.clone(), user_store.clone(), workspace.clone(), cx)
})),
project,
user_store,
workspace,
_subscription: None,
};
this.show_contact_list(cx);
this.show_contact_list(String::new(), cx);
this
}
fn toggle_contact_finder(&mut self, _: &ToggleContactFinder, cx: &mut ViewContext<Self>) {
match &self.child {
Child::ContactList(_) => self.show_contact_finder(cx),
Child::ContactFinder(_) => self.show_contact_list(cx),
Child::ContactList(list) => self.show_contact_finder(list.read(cx).editor_text(cx), cx),
Child::ContactFinder(finder) => self.show_contact_list(finder.read(cx).query(cx), cx),
}
}
fn show_contact_finder(&mut self, cx: &mut ViewContext<ContactsPopover>) {
let child = cx.add_view(|cx| ContactFinder::new(self.user_store.clone(), cx));
fn show_contact_finder(&mut self, editor_text: String, cx: &mut ViewContext<ContactsPopover>) {
let child = cx.add_view(|cx| {
let finder = build_contact_finder(self.user_store.clone(), cx);
finder.set_query(editor_text, cx);
finder
});
cx.focus(&child);
self._subscription = Some(cx.subscribe(&child, |_, _, event, cx| match event {
crate::contact_finder::Event::Dismissed => cx.emit(Event::Dismissed),
PickerEvent::Dismiss => cx.emit(Event::Dismissed),
}));
self.child = Child::ContactFinder(child);
cx.notify();
}
fn show_contact_list(&mut self, cx: &mut ViewContext<ContactsPopover>) {
let child =
cx.add_view(|cx| ContactList::new(self.project.clone(), self.user_store.clone(), cx));
fn show_contact_list(&mut self, editor_text: String, cx: &mut ViewContext<ContactsPopover>) {
let child = cx.add_view(|cx| {
ContactList::new(
self.project.clone(),
self.user_store.clone(),
self.workspace.clone(),
cx,
)
.with_editor_text(editor_text, cx)
});
cx.focus(&child);
self._subscription = Some(cx.subscribe(&child, |_, _, event, cx| match event {
self._subscription = Some(cx.subscribe(&child, |this, _, event, cx| match event {
crate::contact_list::Event::Dismissed => cx.emit(Event::Dismissed),
crate::contact_list::Event::ToggleContactFinder => {
this.toggle_contact_finder(&Default::default(), cx)
}
}));
self.child = Child::ContactList(child);
cx.notify();
@@ -85,79 +107,24 @@ impl View for ContactsPopover {
"ContactsPopover"
}
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
let theme = cx.global::<Settings>().theme.clone();
let child = match &self.child {
Child::ContactList(child) => ChildView::new(child, cx),
Child::ContactFinder(child) => ChildView::new(child, cx),
};
MouseEventHandler::<ContactsPopover>::new(0, cx, |_, cx| {
MouseEventHandler::<ContactsPopover, Self>::new(0, cx, |_, _| {
Flex::column()
.with_child(child.flex(1., true).boxed())
.with_children(
self.user_store
.read(cx)
.invite_info()
.cloned()
.and_then(|info| {
enum InviteLink {}
if info.count > 0 {
Some(
MouseEventHandler::<InviteLink>::new(0, cx, |state, cx| {
let style = theme
.contacts_popover
.invite_row
.style_for(state, false)
.clone();
let copied =
cx.read_from_clipboard().map_or(false, |item| {
item.text().as_str() == info.url.as_ref()
});
Label::new(
format!(
"{} invite link ({} left)",
if copied { "Copied" } else { "Copy" },
info.count
),
style.label.clone(),
)
.aligned()
.left()
.constrained()
.with_height(theme.contacts_popover.invite_row_height)
.contained()
.with_style(style.container)
.boxed()
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, move |_, cx| {
cx.write_to_clipboard(ClipboardItem::new(
info.url.to_string(),
));
cx.notify();
})
.boxed(),
)
} else {
None
}
}),
)
.with_child(child.flex(1., true))
.contained()
.with_style(theme.contacts_popover.container)
.constrained()
.with_width(theme.contacts_popover.width)
.with_height(theme.contacts_popover.height)
.boxed()
})
.on_down_out(MouseButton::Left, move |_, cx| {
cx.dispatch_action(ToggleContactsMenu);
})
.boxed()
.on_down_out(MouseButton::Left, move |_, _, cx| cx.emit(Event::Dismissed))
.into_any()
}
fn focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext<Self>) {

View File

@@ -7,12 +7,14 @@ use gpui::{
},
json::ToJson,
serde_json::{self, json},
Axis, DebugContext, Element, ElementBox, MeasurementContext, PaintContext,
AnyElement, Axis, Element, LayoutContext, SceneBuilder, ViewContext,
};
use crate::CollabTitlebarItem;
pub(crate) struct FacePile {
overlap: f32,
faces: Vec<ElementBox>,
faces: Vec<AnyElement<CollabTitlebarItem>>,
}
impl FacePile {
@@ -24,20 +26,21 @@ impl FacePile {
}
}
impl Element for FacePile {
impl Element<CollabTitlebarItem> for FacePile {
type LayoutState = ();
type PaintState = ();
fn layout(
&mut self,
constraint: gpui::SizeConstraint,
cx: &mut gpui::LayoutContext,
view: &mut CollabTitlebarItem,
cx: &mut LayoutContext<CollabTitlebarItem>,
) -> (Vector2F, Self::LayoutState) {
debug_assert!(constraint.max_along(Axis::Horizontal) == f32::INFINITY);
let mut width = 0.;
for face in &mut self.faces {
width += face.layout(constraint, cx).x();
width += face.layout(constraint, view, cx).x();
}
width -= self.overlap * self.faces.len().saturating_sub(1) as f32;
@@ -46,10 +49,12 @@ impl Element for FacePile {
fn paint(
&mut self,
scene: &mut SceneBuilder,
bounds: RectF,
visible_bounds: RectF,
_layout: &mut Self::LayoutState,
cx: &mut PaintContext,
view: &mut CollabTitlebarItem,
cx: &mut ViewContext<CollabTitlebarItem>,
) -> Self::PaintState {
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
@@ -59,8 +64,8 @@ impl Element for FacePile {
for face in self.faces.iter_mut().rev() {
let size = face.size();
origin_x -= size.x();
cx.paint_layer(None, |cx| {
face.paint(vec2f(origin_x, origin_y), visible_bounds, cx);
scene.paint_layer(None, |scene| {
face.paint(scene, vec2f(origin_x, origin_y), visible_bounds, view, cx);
});
origin_x += self.overlap;
}
@@ -75,7 +80,8 @@ impl Element for FacePile {
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
_: &MeasurementContext,
_: &CollabTitlebarItem,
_: &ViewContext<CollabTitlebarItem>,
) -> Option<RectF> {
None
}
@@ -85,7 +91,8 @@ impl Element for FacePile {
bounds: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
_: &DebugContext,
_: &CollabTitlebarItem,
_: &ViewContext<CollabTitlebarItem>,
) -> serde_json::Value {
json!({
"type": "FacePile",
@@ -94,8 +101,8 @@ impl Element for FacePile {
}
}
impl Extend<ElementBox> for FacePile {
fn extend<T: IntoIterator<Item = ElementBox>>(&mut self, children: T) {
impl Extend<AnyElement<CollabTitlebarItem>> for FacePile {
fn extend<T: IntoIterator<Item = AnyElement<CollabTitlebarItem>>>(&mut self, children: T) {
self.faces.extend(children);
}
}

View File

@@ -1,21 +1,20 @@
use std::sync::{Arc, Weak};
use call::{ActiveCall, IncomingCall};
use client::proto;
use futures::StreamExt;
use gpui::{
elements::*,
geometry::{rect::RectF, vector::vec2f},
impl_internal_actions, CursorStyle, Entity, MouseButton, MutableAppContext, RenderContext,
View, ViewContext, WindowBounds, WindowKind, WindowOptions,
platform::{CursorStyle, MouseButton, WindowBounds, WindowKind, WindowOptions},
AnyElement, AppContext, Entity, View, ViewContext,
};
use settings::Settings;
use util::ResultExt;
use workspace::JoinProject;
impl_internal_actions!(incoming_call_notification, [RespondToCall]);
pub fn init(cx: &mut MutableAppContext) {
cx.add_action(IncomingCallNotification::respond_to_call);
use workspace::AppState;
pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
let app_state = Arc::downgrade(app_state);
let mut incoming_call = ActiveCall::global(cx).read(cx).incoming();
cx.spawn(|mut cx| async move {
let mut notification_windows = Vec::new();
@@ -47,7 +46,7 @@ pub fn init(cx: &mut MutableAppContext) {
is_movable: false,
screen: Some(screen),
},
|_| IncomingCallNotification::new(incoming_call.clone()),
|_| IncomingCallNotification::new(incoming_call.clone(), app_state.clone()),
);
notification_windows.push(window_id);
@@ -65,32 +64,40 @@ struct RespondToCall {
pub struct IncomingCallNotification {
call: IncomingCall,
app_state: Weak<AppState>,
}
impl IncomingCallNotification {
pub fn new(call: IncomingCall) -> Self {
Self { call }
pub fn new(call: IncomingCall, app_state: Weak<AppState>) -> Self {
Self { call, app_state }
}
fn respond_to_call(&mut self, action: &RespondToCall, cx: &mut ViewContext<Self>) {
fn respond(&mut self, accept: bool, cx: &mut ViewContext<Self>) {
let active_call = ActiveCall::global(cx);
if action.accept {
if accept {
let join = active_call.update(cx, |active_call, cx| active_call.accept_incoming(cx));
let caller_user_id = self.call.calling_user.id;
let initial_project_id = self.call.initial_project.as_ref().map(|project| project.id);
cx.spawn_weak(|_, mut cx| async move {
join.await?;
if let Some(project_id) = initial_project_id {
cx.update(|cx| {
cx.dispatch_global_action(JoinProject {
project_id,
follow_user_id: caller_user_id,
})
});
}
anyhow::Ok(())
})
.detach_and_log_err(cx);
let app_state = self.app_state.clone();
cx.app_context()
.spawn(|mut cx| async move {
join.await?;
if let Some(project_id) = initial_project_id {
cx.update(|cx| {
if let Some(app_state) = app_state.upgrade() {
workspace::join_remote_project(
project_id,
caller_user_id,
app_state,
cx,
)
.detach_and_log_err(cx);
}
});
}
anyhow::Ok(())
})
.detach_and_log_err(cx);
} else {
active_call.update(cx, |active_call, _| {
active_call.decline_incoming().log_err();
@@ -98,7 +105,7 @@ impl IncomingCallNotification {
}
}
fn render_caller(&self, cx: &mut RenderContext<Self>) -> ElementBox {
fn render_caller(&self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
let theme = &cx.global::<Settings>().theme.incoming_call_notification;
let default_project = proto::ParticipantProject::default();
let initial_project = self
@@ -108,10 +115,9 @@ impl IncomingCallNotification {
.unwrap_or(&default_project);
Flex::row()
.with_children(self.call.calling_user.avatar.clone().map(|avatar| {
Image::new(avatar)
Image::from_data(avatar)
.with_style(theme.caller_avatar)
.aligned()
.boxed()
}))
.with_child(
Flex::column()
@@ -121,8 +127,7 @@ impl IncomingCallNotification {
theme.caller_username.text.clone(),
)
.contained()
.with_style(theme.caller_username.container)
.boxed(),
.with_style(theme.caller_username.container),
)
.with_child(
Label::new(
@@ -137,8 +142,7 @@ impl IncomingCallNotification {
theme.caller_message.text.clone(),
)
.contained()
.with_style(theme.caller_message.container)
.boxed(),
.with_style(theme.caller_message.container),
)
.with_children(if initial_project.worktree_root_names.is_empty() {
None
@@ -149,57 +153,51 @@ impl IncomingCallNotification {
theme.worktree_roots.text.clone(),
)
.contained()
.with_style(theme.worktree_roots.container)
.boxed(),
.with_style(theme.worktree_roots.container),
)
})
.contained()
.with_style(theme.caller_metadata)
.aligned()
.boxed(),
.aligned(),
)
.contained()
.with_style(theme.caller_container)
.flex(1., true)
.boxed()
.into_any()
}
fn render_buttons(&self, cx: &mut RenderContext<Self>) -> ElementBox {
fn render_buttons(&self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
enum Accept {}
enum Decline {}
Flex::column()
.with_child(
MouseEventHandler::<Accept>::new(0, cx, |_, cx| {
MouseEventHandler::<Accept, Self>::new(0, cx, |_, cx| {
let theme = &cx.global::<Settings>().theme.incoming_call_notification;
Label::new("Accept", theme.accept_button.text.clone())
.aligned()
.contained()
.with_style(theme.accept_button.container)
.boxed()
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, |_, cx| {
cx.dispatch_action(RespondToCall { accept: true });
.on_click(MouseButton::Left, |_, this, cx| {
this.respond(true, cx);
})
.flex(1., true)
.boxed(),
.flex(1., true),
)
.with_child(
MouseEventHandler::<Decline>::new(0, cx, |_, cx| {
MouseEventHandler::<Decline, Self>::new(0, cx, |_, cx| {
let theme = &cx.global::<Settings>().theme.incoming_call_notification;
Label::new("Decline", theme.decline_button.text.clone())
.aligned()
.contained()
.with_style(theme.decline_button.container)
.boxed()
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, |_, cx| {
cx.dispatch_action(RespondToCall { accept: false });
.on_click(MouseButton::Left, |_, this, cx| {
this.respond(false, cx);
})
.flex(1., true)
.boxed(),
.flex(1., true),
)
.constrained()
.with_width(
@@ -208,7 +206,7 @@ impl IncomingCallNotification {
.incoming_call_notification
.button_width,
)
.boxed()
.into_any()
}
}
@@ -221,7 +219,7 @@ impl View for IncomingCallNotification {
"IncomingCallNotification"
}
fn render(&mut self, cx: &mut RenderContext<Self>) -> gpui::ElementBox {
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
let background = cx
.global::<Settings>()
.theme
@@ -234,6 +232,6 @@ impl View for IncomingCallNotification {
.contained()
.with_background_color(background)
.expanded()
.boxed()
.into_any()
}
}

View File

@@ -1,7 +1,8 @@
use client::User;
use gpui::{
elements::*, platform::CursorStyle, Action, Element, ElementBox, MouseButton, RenderContext,
View,
elements::*,
platform::{CursorStyle, MouseButton},
AnyElement, Element, View, ViewContext,
};
use settings::Settings;
use std::sync::Arc;
@@ -9,14 +10,18 @@ use std::sync::Arc;
enum Dismiss {}
enum Button {}
pub fn render_user_notification<V: View, A: Action + Clone>(
pub fn render_user_notification<F, V>(
user: Arc<User>,
title: &'static str,
body: Option<&'static str>,
dismiss_action: A,
buttons: Vec<(&'static str, Box<dyn Action>)>,
cx: &mut RenderContext<V>,
) -> ElementBox {
on_dismiss: F,
buttons: Vec<(&'static str, Box<dyn Fn(&mut V, &mut ViewContext<V>)>)>,
cx: &mut ViewContext<V>,
) -> AnyElement<V>
where
F: 'static + Fn(&mut V, &mut ViewContext<V>),
V: View,
{
let theme = cx.global::<Settings>().theme.clone();
let theme = &theme.contact_notification;
@@ -24,7 +29,7 @@ pub fn render_user_notification<V: View, A: Action + Clone>(
.with_child(
Flex::row()
.with_children(user.avatar.clone().map(|avatar| {
Image::new(avatar)
Image::from_data(avatar)
.with_style(theme.header_avatar)
.aligned()
.constrained()
@@ -34,7 +39,6 @@ pub fn render_user_notification<V: View, A: Action + Clone>(
)
.aligned()
.top()
.boxed()
}))
.with_child(
Text::new(
@@ -46,11 +50,10 @@ pub fn render_user_notification<V: View, A: Action + Clone>(
.aligned()
.top()
.left()
.flex(1., true)
.boxed(),
.flex(1., true),
)
.with_child(
MouseEventHandler::<Dismiss>::new(user.id as usize, cx, |state, _| {
MouseEventHandler::<Dismiss, V>::new(user.id as usize, cx, |state, _| {
let style = theme.dismiss_button.style_for(state, false);
Svg::new("icons/x_mark_8.svg")
.with_color(style.color)
@@ -62,13 +65,10 @@ pub fn render_user_notification<V: View, A: Action + Clone>(
.constrained()
.with_width(style.button_width)
.with_height(style.button_width)
.boxed()
})
.with_cursor_style(CursorStyle::PointingHand)
.with_padding(Padding::uniform(5.))
.on_click(MouseButton::Left, move |_, cx| {
cx.dispatch_any_action(dismiss_action.boxed_clone())
})
.on_click(MouseButton::Left, move |_, view, cx| on_dismiss(view, cx))
.aligned()
.constrained()
.with_height(
@@ -77,16 +77,14 @@ pub fn render_user_notification<V: View, A: Action + Clone>(
)
.aligned()
.top()
.flex_float()
.boxed(),
.flex_float(),
)
.named("contact notification header"),
.into_any_named("contact notification header"),
)
.with_children(body.map(|body| {
Label::new(body, theme.body_message.text.clone())
.contained()
.with_style(theme.body_message.container)
.boxed()
}))
.with_children(if buttons.is_empty() {
None
@@ -94,26 +92,21 @@ pub fn render_user_notification<V: View, A: Action + Clone>(
Some(
Flex::row()
.with_children(buttons.into_iter().enumerate().map(
|(ix, (message, action))| {
MouseEventHandler::<Button>::new(ix, cx, |state, _| {
|(ix, (message, handler))| {
MouseEventHandler::<Button, V>::new(ix, cx, |state, _| {
let button = theme.button.style_for(state, false);
Label::new(message, button.text.clone())
.contained()
.with_style(button.container)
.boxed()
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, move |_, cx| {
cx.dispatch_any_action(action.boxed_clone())
})
.boxed()
.on_click(MouseButton::Left, move |_, view, cx| handler(view, cx))
},
))
.aligned()
.right()
.boxed(),
.right(),
)
})
.contained()
.boxed()
.into_any()
}

View File

@@ -2,22 +2,17 @@ use call::{room, ActiveCall};
use client::User;
use collections::HashMap;
use gpui::{
actions,
elements::*,
geometry::{rect::RectF, vector::vec2f},
CursorStyle, Entity, MouseButton, MutableAppContext, RenderContext, View, ViewContext,
WindowBounds, WindowKind, WindowOptions,
platform::{CursorStyle, MouseButton, WindowBounds, WindowKind, WindowOptions},
AppContext, Entity, View, ViewContext,
};
use settings::Settings;
use std::sync::Arc;
use workspace::JoinProject;
actions!(project_shared_notification, [DismissProject]);
pub fn init(cx: &mut MutableAppContext) {
cx.add_action(ProjectSharedNotification::join);
cx.add_action(ProjectSharedNotification::dismiss);
use std::sync::{Arc, Weak};
use workspace::AppState;
pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
let app_state = Arc::downgrade(app_state);
let active_call = ActiveCall::global(cx);
let mut notification_windows = HashMap::default();
cx.subscribe(&active_call, move |_, event, cx| match event {
@@ -50,6 +45,7 @@ pub fn init(cx: &mut MutableAppContext) {
owner.clone(),
*project_id,
worktree_root_names.clone(),
app_state.clone(),
)
},
);
@@ -62,14 +58,14 @@ pub fn init(cx: &mut MutableAppContext) {
room::Event::RemoteProjectUnshared { project_id } => {
if let Some(window_ids) = notification_windows.remove(&project_id) {
for window_id in window_ids {
cx.remove_window(window_id);
cx.update_window(window_id, |cx| cx.remove_window());
}
}
}
room::Event::Left => {
for (_, window_ids) in notification_windows.drain() {
for window_id in window_ids {
cx.remove_window(window_id);
cx.update_window(window_id, |cx| cx.remove_window());
}
}
}
@@ -82,36 +78,43 @@ pub struct ProjectSharedNotification {
project_id: u64,
worktree_root_names: Vec<String>,
owner: Arc<User>,
app_state: Weak<AppState>,
}
impl ProjectSharedNotification {
fn new(owner: Arc<User>, project_id: u64, worktree_root_names: Vec<String>) -> Self {
fn new(
owner: Arc<User>,
project_id: u64,
worktree_root_names: Vec<String>,
app_state: Weak<AppState>,
) -> Self {
Self {
project_id,
worktree_root_names,
owner,
app_state,
}
}
fn join(&mut self, _: &JoinProject, cx: &mut ViewContext<Self>) {
let window_id = cx.window_id();
cx.remove_window(window_id);
cx.propagate_action();
fn join(&mut self, cx: &mut ViewContext<Self>) {
cx.remove_window();
if let Some(app_state) = self.app_state.upgrade() {
workspace::join_remote_project(self.project_id, self.owner.id, app_state, cx)
.detach_and_log_err(cx);
}
}
fn dismiss(&mut self, _: &DismissProject, cx: &mut ViewContext<Self>) {
let window_id = cx.window_id();
cx.remove_window(window_id);
fn dismiss(&mut self, cx: &mut ViewContext<Self>) {
cx.remove_window();
}
fn render_owner(&self, cx: &mut RenderContext<Self>) -> ElementBox {
fn render_owner(&self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
let theme = &cx.global::<Settings>().theme.project_shared_notification;
Flex::row()
.with_children(self.owner.avatar.clone().map(|avatar| {
Image::new(avatar)
Image::from_data(avatar)
.with_style(theme.owner_avatar)
.aligned()
.boxed()
}))
.with_child(
Flex::column()
@@ -121,8 +124,7 @@ impl ProjectSharedNotification {
theme.owner_username.text.clone(),
)
.contained()
.with_style(theme.owner_username.container)
.boxed(),
.with_style(theme.owner_username.container),
)
.with_child(
Label::new(
@@ -137,8 +139,7 @@ impl ProjectSharedNotification {
theme.message.text.clone(),
)
.contained()
.with_style(theme.message.container)
.boxed(),
.with_style(theme.message.container),
)
.with_children(if self.worktree_root_names.is_empty() {
None
@@ -149,63 +150,49 @@ impl ProjectSharedNotification {
theme.worktree_roots.text.clone(),
)
.contained()
.with_style(theme.worktree_roots.container)
.boxed(),
.with_style(theme.worktree_roots.container),
)
})
.contained()
.with_style(theme.owner_metadata)
.aligned()
.boxed(),
.aligned(),
)
.contained()
.with_style(theme.owner_container)
.flex(1., true)
.boxed()
.into_any()
}
fn render_buttons(&self, cx: &mut RenderContext<Self>) -> ElementBox {
fn render_buttons(&self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
enum Open {}
enum Dismiss {}
let project_id = self.project_id;
let owner_user_id = self.owner.id;
Flex::column()
.with_child(
MouseEventHandler::<Open>::new(0, cx, |_, cx| {
MouseEventHandler::<Open, Self>::new(0, cx, |_, cx| {
let theme = &cx.global::<Settings>().theme.project_shared_notification;
Label::new("Open", theme.open_button.text.clone())
.aligned()
.contained()
.with_style(theme.open_button.container)
.boxed()
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, move |_, cx| {
cx.dispatch_action(JoinProject {
project_id,
follow_user_id: owner_user_id,
});
})
.flex(1., true)
.boxed(),
.on_click(MouseButton::Left, move |_, this, cx| this.join(cx))
.flex(1., true),
)
.with_child(
MouseEventHandler::<Dismiss>::new(0, cx, |_, cx| {
MouseEventHandler::<Dismiss, Self>::new(0, cx, |_, cx| {
let theme = &cx.global::<Settings>().theme.project_shared_notification;
Label::new("Dismiss", theme.dismiss_button.text.clone())
.aligned()
.contained()
.with_style(theme.dismiss_button.container)
.boxed()
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, |_, cx| {
cx.dispatch_action(DismissProject);
.on_click(MouseButton::Left, |_, this, cx| {
this.dismiss(cx);
})
.flex(1., true)
.boxed(),
.flex(1., true),
)
.constrained()
.with_width(
@@ -214,7 +201,7 @@ impl ProjectSharedNotification {
.project_shared_notification
.button_width,
)
.boxed()
.into_any()
}
}
@@ -227,7 +214,7 @@ impl View for ProjectSharedNotification {
"ProjectSharedNotification"
}
fn render(&mut self, cx: &mut RenderContext<Self>) -> gpui::ElementBox {
fn render(&mut self, cx: &mut ViewContext<Self>) -> gpui::AnyElement<Self> {
let background = cx
.global::<Settings>()
.theme
@@ -239,6 +226,6 @@ impl View for ProjectSharedNotification {
.contained()
.with_background_color(background)
.expanded()
.boxed()
.into_any()
}
}

View File

@@ -1,14 +1,14 @@
use crate::toggle_screen_sharing;
use call::ActiveCall;
use gpui::{
color::Color,
elements::{MouseEventHandler, Svg},
Appearance, Element, ElementBox, Entity, MouseButton, MutableAppContext, RenderContext, View,
platform::{Appearance, MouseButton},
AnyElement, AppContext, Element, Entity, View, ViewContext,
};
use settings::Settings;
use crate::ToggleScreenSharing;
pub fn init(cx: &mut MutableAppContext) {
pub fn init(cx: &mut AppContext) {
let active_call = ActiveCall::global(cx);
let mut status_indicator = None;
@@ -19,8 +19,10 @@ pub fn init(cx: &mut MutableAppContext) {
status_indicator = Some(cx.add_status_bar_item(|_| SharingStatusIndicator));
}
} else if let Some((window_id, _)) = status_indicator.take() {
cx.remove_status_bar_item(window_id);
cx.update_window(window_id, |cx| cx.remove_window());
}
} else if let Some((window_id, _)) = status_indicator.take() {
cx.update_window(window_id, |cx| cx.remove_window());
}
})
.detach();
@@ -37,23 +39,22 @@ impl View for SharingStatusIndicator {
"SharingStatusIndicator"
}
fn render(&mut self, cx: &mut RenderContext<'_, Self>) -> ElementBox {
let color = match cx.appearance {
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
let color = match cx.window_appearance() {
Appearance::Light | Appearance::VibrantLight => Color::black(),
Appearance::Dark | Appearance::VibrantDark => Color::white(),
};
MouseEventHandler::<Self>::new(0, cx, |_, _| {
MouseEventHandler::<Self, Self>::new(0, cx, |_, _| {
Svg::new("icons/disable_screen_sharing_12.svg")
.with_color(color)
.constrained()
.with_width(18.)
.aligned()
.boxed()
})
.on_click(MouseButton::Left, |_, cx| {
cx.dispatch_action(ToggleScreenSharing);
.on_click(MouseButton::Left, |_, _, cx| {
toggle_screen_sharing(&Default::default(), cx)
})
.boxed()
.into_any()
}
}

View File

@@ -24,3 +24,10 @@ pub type HashMap<K, V> = std::collections::HashMap<K, V>;
pub type HashSet<T> = std::collections::HashSet<T>;
pub use std::collections::*;
// NEW TYPES
#[derive(Default)]
pub struct CommandPaletteFilter {
pub filtered_namespaces: HashSet<&'static str>,
}

View File

@@ -24,7 +24,7 @@ workspace = { path = "../workspace" }
gpui = { path = "../gpui", features = ["test-support"] }
editor = { path = "../editor", features = ["test-support"] }
project = { path = "../project", features = ["test-support"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
serde_json.workspace = true
workspace = { path = "../workspace", features = ["test-support"] }
ctor = "0.1"
env_logger = "0.9"
ctor.workspace = true
env_logger.workspace = true

View File

@@ -1,31 +1,25 @@
use collections::HashSet;
use collections::CommandPaletteFilter;
use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{
actions,
elements::{ChildView, Flex, Label, ParentElement},
keymap_matcher::Keystroke,
Action, AnyViewHandle, Element, Entity, MouseState, MutableAppContext, RenderContext, View,
ViewContext, ViewHandle,
actions, elements::*, keymap_matcher::Keystroke, Action, AppContext, Element, MouseState,
ViewContext,
};
use picker::{Picker, PickerDelegate};
use picker::{Picker, PickerDelegate, PickerEvent};
use settings::Settings;
use std::cmp;
use util::ResultExt;
use workspace::Workspace;
#[derive(Default)]
pub struct CommandPaletteFilter {
pub filtered_namespaces: HashSet<&'static str>,
}
pub fn init(cx: &mut MutableAppContext) {
cx.add_action(CommandPalette::toggle);
Picker::<CommandPalette>::init(cx);
pub fn init(cx: &mut AppContext) {
cx.add_action(toggle_command_palette);
CommandPalette::init(cx);
}
actions!(command_palette, [Toggle]);
pub struct CommandPalette {
picker: ViewHandle<Picker<Self>>,
pub type CommandPalette = Picker<CommandPaletteDelegate>;
pub struct CommandPaletteDelegate {
actions: Vec<Command>,
matches: Vec<StringMatch>,
selected_ix: usize,
@@ -47,104 +41,29 @@ struct Command {
keystrokes: Vec<Keystroke>,
}
impl CommandPalette {
pub fn new(focused_view_id: usize, cx: &mut ViewContext<Self>) -> Self {
let this = cx.weak_handle();
let actions = cx
.available_actions(cx.window_id(), focused_view_id)
.filter_map(|(name, action, bindings)| {
if cx.has_global::<CommandPaletteFilter>() {
let filter = cx.global::<CommandPaletteFilter>();
if filter.filtered_namespaces.contains(action.namespace()) {
return None;
}
}
fn toggle_command_palette(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>) {
let focused_view_id = cx.focused_view_id().unwrap_or_else(|| cx.view_id());
workspace.toggle_modal(cx, |_, cx| {
cx.add_view(|cx| Picker::new(CommandPaletteDelegate::new(focused_view_id), cx))
});
}
Some(Command {
name: humanize_action_name(name),
action,
keystrokes: bindings
.iter()
.map(|binding| binding.keystrokes())
.last()
.map_or(Vec::new(), |keystrokes| keystrokes.to_vec()),
})
})
.collect();
let picker = cx.add_view(|cx| Picker::new("Execute a command...", this, cx));
impl CommandPaletteDelegate {
pub fn new(focused_view_id: usize) -> Self {
Self {
picker,
actions,
actions: Default::default(),
matches: vec![],
selected_ix: 0,
focused_view_id,
}
}
fn toggle(_: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>) {
let workspace = cx.handle();
let window_id = cx.window_id();
let focused_view_id = cx
.focused_view_id(window_id)
.unwrap_or_else(|| workspace.id());
cx.as_mut().defer(move |cx| {
let this = cx.add_view(workspace.clone(), |cx| Self::new(focused_view_id, cx));
workspace.update(cx, |workspace, cx| {
workspace.toggle_modal(cx, |_, cx| {
cx.subscribe(&this, Self::on_event).detach();
this
});
});
});
}
fn on_event(
workspace: &mut Workspace,
_: ViewHandle<Self>,
event: &Event,
cx: &mut ViewContext<Workspace>,
) {
match event {
Event::Dismissed => workspace.dismiss_modal(cx),
Event::Confirmed {
window_id,
focused_view_id,
action,
} => {
let window_id = *window_id;
let focused_view_id = *focused_view_id;
let action = action.boxed_clone();
workspace.dismiss_modal(cx);
cx.as_mut()
.defer(move |cx| cx.dispatch_any_action_at(window_id, focused_view_id, action))
}
}
}
}
impl Entity for CommandPalette {
type Event = Event;
}
impl View for CommandPalette {
fn ui_name() -> &'static str {
"CommandPalette"
impl PickerDelegate for CommandPaletteDelegate {
fn placeholder_text(&self) -> std::sync::Arc<str> {
"Execute a command...".into()
}
fn render(&mut self, cx: &mut RenderContext<Self>) -> gpui::ElementBox {
ChildView::new(self.picker.clone(), cx).boxed()
}
fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
if cx.is_self_focused() {
cx.focus(&self.picker);
}
}
}
impl PickerDelegate for CommandPalette {
fn match_count(&self) -> usize {
self.matches.len()
}
@@ -153,26 +72,55 @@ impl PickerDelegate for CommandPalette {
self.selected_ix
}
fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext<Self>) {
fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext<Picker<Self>>) {
self.selected_ix = ix;
}
fn update_matches(
&mut self,
query: String,
cx: &mut gpui::ViewContext<Self>,
cx: &mut ViewContext<Picker<Self>>,
) -> gpui::Task<()> {
let candidates = self
.actions
.iter()
.enumerate()
.map(|(ix, command)| StringMatchCandidate {
id: ix,
string: command.name.to_string(),
char_bag: command.name.chars().collect(),
})
.collect::<Vec<_>>();
cx.spawn(move |this, mut cx| async move {
let window_id = cx.window_id();
let view_id = self.focused_view_id;
cx.spawn(move |picker, mut cx| async move {
let actions = cx
.available_actions(window_id, view_id)
.into_iter()
.filter_map(|(name, action, bindings)| {
let filtered = cx.read(|cx| {
if cx.has_global::<CommandPaletteFilter>() {
let filter = cx.global::<CommandPaletteFilter>();
filter.filtered_namespaces.contains(action.namespace())
} else {
false
}
});
if filtered {
None
} else {
Some(Command {
name: humanize_action_name(name),
action,
keystrokes: bindings
.iter()
.map(|binding| binding.keystrokes())
.last()
.map_or(Vec::new(), |keystrokes| keystrokes.to_vec()),
})
}
})
.collect::<Vec<_>>();
let candidates = actions
.iter()
.enumerate()
.map(|(ix, command)| StringMatchCandidate {
id: ix,
string: command.name.to_string(),
char_bag: command.name.chars().collect(),
})
.collect::<Vec<_>>();
let matches = if query.is_empty() {
candidates
.into_iter()
@@ -195,32 +143,37 @@ impl PickerDelegate for CommandPalette {
)
.await
};
this.update(&mut cx, |this, _| {
this.matches = matches;
if this.matches.is_empty() {
this.selected_ix = 0;
} else {
this.selected_ix = cmp::min(this.selected_ix, this.matches.len() - 1);
}
});
picker
.update(&mut cx, |picker, _| {
let delegate = picker.delegate_mut();
delegate.actions = actions;
delegate.matches = matches;
if delegate.matches.is_empty() {
delegate.selected_ix = 0;
} else {
delegate.selected_ix =
cmp::min(delegate.selected_ix, delegate.matches.len() - 1);
}
})
.log_err();
})
}
fn dismiss(&mut self, cx: &mut ViewContext<Self>) {
cx.emit(Event::Dismissed);
}
fn dismissed(&mut self, _cx: &mut ViewContext<Picker<Self>>) {}
fn confirm(&mut self, cx: &mut ViewContext<Self>) {
fn confirm(&mut self, cx: &mut ViewContext<Picker<Self>>) {
if !self.matches.is_empty() {
let window_id = cx.window_id();
let focused_view_id = self.focused_view_id;
let action_ix = self.matches[self.selected_ix].candidate_id;
cx.emit(Event::Confirmed {
window_id: cx.window_id(),
focused_view_id: self.focused_view_id,
action: self.actions.remove(action_ix).action,
});
} else {
cx.emit(Event::Dismissed);
let action = self.actions.remove(action_ix).action;
cx.app_context()
.spawn(move |mut cx| async move {
cx.dispatch_action(window_id, focused_view_id, action.as_ref())
})
.detach_and_log_err(cx);
}
cx.emit(PickerEvent::Dismiss);
}
fn render_match(
@@ -229,7 +182,7 @@ impl PickerDelegate for CommandPalette {
mouse_state: &mut MouseState,
selected: bool,
cx: &gpui::AppContext,
) -> gpui::ElementBox {
) -> AnyElement<Picker<Self>> {
let mat = &self.matches[ix];
let command = &self.actions[mat.candidate_id];
let settings = cx.global::<Settings>();
@@ -241,8 +194,7 @@ impl PickerDelegate for CommandPalette {
Flex::row()
.with_child(
Label::new(mat.string.clone(), style.label.clone())
.with_highlights(mat.positions.clone())
.boxed(),
.with_highlights(mat.positions.clone()),
)
.with_children(command.keystrokes.iter().map(|keystroke| {
Flex::row()
@@ -259,8 +211,7 @@ impl PickerDelegate for CommandPalette {
Some(
Label::new(label, key_style.label.clone())
.contained()
.with_style(key_style.container)
.boxed(),
.with_style(key_style.container),
)
} else {
None
@@ -270,17 +221,15 @@ impl PickerDelegate for CommandPalette {
.with_child(
Label::new(keystroke.key.clone(), key_style.label.clone())
.contained()
.with_style(key_style.container)
.boxed(),
.with_style(key_style.container),
)
.contained()
.with_margin_left(keystroke_spacing)
.flex_float()
.boxed()
}))
.contained()
.with_style(style.container)
.boxed()
.into_any()
}
}
@@ -319,9 +268,11 @@ impl std::fmt::Debug for Command {
#[cfg(test)]
mod tests {
use std::sync::Arc;
use super::*;
use editor::Editor;
use gpui::TestAppContext;
use gpui::{executor::Deterministic, TestAppContext};
use project::Project;
use workspace::{AppState, Workspace};
@@ -342,7 +293,8 @@ mod tests {
}
#[gpui::test]
async fn test_command_palette(cx: &mut TestAppContext) {
async fn test_command_palette(deterministic: Arc<Deterministic>, cx: &mut TestAppContext) {
deterministic.forbid_parking();
let app_state = cx.update(AppState::test);
cx.update(|cx| {
@@ -352,22 +304,20 @@ mod tests {
});
let project = Project::test(app_state.fs.clone(), [], cx).await;
let (_, workspace) = cx.add_window(|cx| {
Workspace::new(Default::default(), 0, project, |_, _| unimplemented!(), cx)
});
let editor = cx.add_view(&workspace, |cx| {
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let editor = cx.add_view(window_id, |cx| {
let mut editor = Editor::single_line(None, cx);
editor.set_text("abc", cx);
editor
});
workspace.update(cx, |workspace, cx| {
cx.focus(editor.clone());
cx.focus(&editor);
workspace.add_item(Box::new(editor.clone()), cx)
});
workspace.update(cx, |workspace, cx| {
CommandPalette::toggle(workspace, &Toggle, cx)
toggle_command_palette(workspace, &Toggle, cx);
});
let palette = workspace.read_with(cx, |workspace, _| {
@@ -376,15 +326,17 @@ mod tests {
palette
.update(cx, |palette, cx| {
palette.update_matches("bcksp".to_string(), cx)
palette
.delegate_mut()
.update_matches("bcksp".to_string(), cx)
})
.await;
palette.update(cx, |palette, cx| {
assert_eq!(palette.matches[0].string, "editor: backspace");
palette.confirm(cx);
assert_eq!(palette.delegate().matches[0].string, "editor: backspace");
palette.confirm(&Default::default(), cx);
});
deterministic.run_until_parked();
editor.read_with(cx, |editor, cx| {
assert_eq!(editor.text(cx), "ab");
});
@@ -397,7 +349,7 @@ mod tests {
});
workspace.update(cx, |workspace, cx| {
CommandPalette::toggle(workspace, &Toggle, cx);
toggle_command_palette(workspace, &Toggle, cx);
});
// Assert editor command not present
@@ -407,10 +359,14 @@ mod tests {
palette
.update(cx, |palette, cx| {
palette.update_matches("bcksp".to_string(), cx)
palette
.delegate_mut()
.update_matches("bcksp".to_string(), cx)
})
.await;
palette.update(cx, |palette, _| assert!(palette.matches.is_empty()));
palette.update(cx, |palette, _| {
assert!(palette.delegate().matches.is_empty())
});
}
}

View File

@@ -13,4 +13,4 @@ gpui = { path = "../gpui" }
menu = { path = "../menu" }
settings = { path = "../settings" }
theme = { path = "../theme" }
smallvec = "1.6"
smallvec.workspace = true

View File

@@ -1,40 +1,100 @@
use gpui::{
elements::*, geometry::vector::Vector2F, impl_internal_actions, keymap_matcher::KeymapContext,
platform::CursorStyle, Action, AnyViewHandle, AppContext, Axis, Entity, MouseButton,
MutableAppContext, RenderContext, SizeConstraint, Subscription, View, ViewContext,
anyhow,
elements::*,
geometry::vector::Vector2F,
keymap_matcher::KeymapContext,
platform::{CursorStyle, MouseButton},
Action, AnyViewHandle, AppContext, Axis, Entity, MouseState, SizeConstraint, Subscription,
View, ViewContext,
};
use menu::*;
use settings::Settings;
use std::{any::TypeId, time::Duration};
use std::{any::TypeId, borrow::Cow, sync::Arc, time::Duration};
#[derive(Copy, Clone, PartialEq)]
struct Clicked;
impl_internal_actions!(context_menu, [Clicked]);
pub fn init(cx: &mut MutableAppContext) {
pub fn init(cx: &mut AppContext) {
cx.add_action(ContextMenu::select_first);
cx.add_action(ContextMenu::select_last);
cx.add_action(ContextMenu::select_next);
cx.add_action(ContextMenu::select_prev);
cx.add_action(ContextMenu::clicked);
cx.add_action(ContextMenu::confirm);
cx.add_action(ContextMenu::cancel);
}
pub type StaticItem = Box<dyn Fn(&mut AppContext) -> AnyElement<ContextMenu>>;
type ContextMenuItemBuilder =
Box<dyn Fn(&mut MouseState, &theme::ContextMenuItem) -> AnyElement<ContextMenu>>;
pub enum ContextMenuItemLabel {
String(Cow<'static, str>),
Element(ContextMenuItemBuilder),
}
impl From<Cow<'static, str>> for ContextMenuItemLabel {
fn from(s: Cow<'static, str>) -> Self {
Self::String(s)
}
}
impl From<&'static str> for ContextMenuItemLabel {
fn from(s: &'static str) -> Self {
Self::String(s.into())
}
}
impl From<String> for ContextMenuItemLabel {
fn from(s: String) -> Self {
Self::String(s.into())
}
}
impl<T> From<T> for ContextMenuItemLabel
where
T: 'static + Fn(&mut MouseState, &theme::ContextMenuItem) -> AnyElement<ContextMenu>,
{
fn from(f: T) -> Self {
Self::Element(Box::new(f))
}
}
pub enum ContextMenuItemAction {
Action(Box<dyn Action>),
Handler(Arc<dyn Fn(&mut ViewContext<ContextMenu>)>),
}
impl Clone for ContextMenuItemAction {
fn clone(&self) -> Self {
match self {
Self::Action(action) => Self::Action(action.boxed_clone()),
Self::Handler(handler) => Self::Handler(handler.clone()),
}
}
}
pub enum ContextMenuItem {
Item {
label: String,
action: Box<dyn Action>,
label: ContextMenuItemLabel,
action: ContextMenuItemAction,
},
Static(StaticItem),
Separator,
}
impl ContextMenuItem {
pub fn item(label: impl ToString, action: impl 'static + Action) -> Self {
pub fn action(label: impl Into<ContextMenuItemLabel>, action: impl 'static + Action) -> Self {
Self::Item {
label: label.to_string(),
action: Box::new(action),
label: label.into(),
action: ContextMenuItemAction::Action(Box::new(action)),
}
}
pub fn handler(
label: impl Into<ContextMenuItemLabel>,
handler: impl 'static + Fn(&mut ViewContext<ContextMenu>),
) -> Self {
Self::Item {
label: label.into(),
action: ContextMenuItemAction::Handler(Arc::new(handler)),
}
}
@@ -42,14 +102,17 @@ impl ContextMenuItem {
Self::Separator
}
fn is_separator(&self) -> bool {
matches!(self, Self::Separator)
fn is_action(&self) -> bool {
matches!(self, Self::Item { .. })
}
fn action_id(&self) -> Option<TypeId> {
match self {
ContextMenuItem::Item { action, .. } => Some(action.id()),
ContextMenuItem::Separator => None,
ContextMenuItem::Item { action, .. } => match action {
ContextMenuItemAction::Action(action) => Some(action.id()),
ContextMenuItemAction::Handler(_) => None,
},
ContextMenuItem::Static(..) | ContextMenuItem::Separator => None,
}
}
}
@@ -58,11 +121,11 @@ pub struct ContextMenu {
show_count: usize,
anchor_position: Vector2F,
anchor_corner: AnchorCorner,
position_mode: OverlayPositionMode,
items: Vec<ContextMenuItem>,
selected_index: Option<usize>,
visible: bool,
previously_focused_view_id: Option<usize>,
clicked: bool,
parent_view_id: usize,
_actions_observation: Subscription,
}
@@ -76,36 +139,35 @@ impl View for ContextMenu {
"ContextMenu"
}
fn keymap_context(&self, _: &AppContext) -> KeymapContext {
let mut cx = Self::default_keymap_context();
cx.set.insert("menu".into());
cx
fn update_keymap_context(&self, keymap: &mut KeymapContext, _: &AppContext) {
Self::reset_to_default_keymap_context(keymap);
keymap.add_identifier("menu");
}
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
if !self.visible {
return Empty::new().boxed();
return Empty::new().into_any();
}
// Render the menu once at minimum width.
let mut collapsed_menu = self.render_menu_for_measurement(cx).boxed();
let expanded_menu = self
.render_menu(cx)
.constrained()
.dynamically(move |constraint, cx| {
SizeConstraint::strict_along(
Axis::Horizontal,
collapsed_menu.layout(constraint, cx).x(),
)
})
.boxed();
let mut collapsed_menu = self.render_menu_for_measurement(cx);
let expanded_menu =
self.render_menu(cx)
.constrained()
.dynamically(move |constraint, view, cx| {
SizeConstraint::strict_along(
Axis::Horizontal,
collapsed_menu.layout(constraint, view, cx).0.x(),
)
});
Overlay::new(expanded_menu)
.with_hoverable(true)
.with_fit_mode(OverlayFitMode::SnapToWindow)
.with_anchor_position(self.anchor_position)
.with_anchor_corner(self.anchor_corner)
.boxed()
.with_position_mode(self.position_mode)
.into_any()
}
fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
@@ -114,18 +176,16 @@ impl View for ContextMenu {
}
impl ContextMenu {
pub fn new(cx: &mut ViewContext<Self>) -> Self {
let parent_view_id = cx.parent().unwrap();
pub fn new(parent_view_id: usize, cx: &mut ViewContext<Self>) -> Self {
Self {
show_count: 0,
anchor_position: Default::default(),
anchor_corner: AnchorCorner::TopLeft,
position_mode: OverlayPositionMode::Window,
items: Default::default(),
selected_index: Default::default(),
visible: Default::default(),
previously_focused_view_id: Default::default(),
clicked: false,
parent_view_id,
_actions_observation: cx.observe_actions(Self::action_dispatched),
}
@@ -141,28 +201,33 @@ impl ContextMenu {
.iter()
.position(|item| item.action_id() == Some(action_id))
{
if self.clicked {
self.cancel(&Default::default(), cx);
} else {
self.selected_index = Some(ix);
cx.notify();
cx.spawn(|this, mut cx| async move {
cx.background().timer(Duration::from_millis(50)).await;
this.update(&mut cx, |this, cx| this.cancel(&Default::default(), cx));
})
.detach();
}
self.selected_index = Some(ix);
cx.notify();
cx.spawn(|this, mut cx| async move {
cx.background().timer(Duration::from_millis(50)).await;
this.update(&mut cx, |this, cx| this.cancel(&Default::default(), cx))?;
anyhow::Ok(())
})
.detach_and_log_err(cx);
}
}
fn clicked(&mut self, _: &Clicked, _: &mut ViewContext<Self>) {
self.clicked = true;
}
fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
if let Some(ix) = self.selected_index {
if let Some(ContextMenuItem::Item { action, .. }) = self.items.get(ix) {
cx.dispatch_any_action(action.boxed_clone());
match action {
ContextMenuItemAction::Action(action) => {
let window_id = cx.window_id();
let view_id = self.parent_view_id;
let action = action.boxed_clone();
cx.app_context()
.spawn(|mut cx| async move {
cx.dispatch_action(window_id, view_id, action.as_ref())
})
.detach_and_log_err(cx);
}
ContextMenuItemAction::Handler(handler) => handler(cx),
}
self.reset(cx);
}
}
@@ -183,18 +248,17 @@ impl ContextMenu {
self.items.clear();
self.visible = false;
self.selected_index.take();
self.clicked = false;
cx.notify();
}
fn select_first(&mut self, _: &SelectFirst, cx: &mut ViewContext<Self>) {
self.selected_index = self.items.iter().position(|item| !item.is_separator());
self.selected_index = self.items.iter().position(|item| item.is_action());
cx.notify();
}
fn select_last(&mut self, _: &SelectLast, cx: &mut ViewContext<Self>) {
for (ix, item) in self.items.iter().enumerate().rev() {
if !item.is_separator() {
if item.is_action() {
self.selected_index = Some(ix);
cx.notify();
break;
@@ -205,7 +269,7 @@ impl ContextMenu {
fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext<Self>) {
if let Some(ix) = self.selected_index {
for (ix, item) in self.items.iter().enumerate().skip(ix + 1) {
if !item.is_separator() {
if item.is_action() {
self.selected_index = Some(ix);
cx.notify();
break;
@@ -219,7 +283,7 @@ impl ContextMenu {
fn select_prev(&mut self, _: &SelectPrev, cx: &mut ViewContext<Self>) {
if let Some(ix) = self.selected_index {
for (ix, item) in self.items.iter().enumerate().take(ix).rev() {
if !item.is_separator() {
if item.is_action() {
self.selected_index = Some(ix);
cx.notify();
break;
@@ -234,7 +298,7 @@ impl ContextMenu {
&mut self,
anchor_position: Vector2F,
anchor_corner: AnchorCorner,
items: impl IntoIterator<Item = ContextMenuItem>,
items: Vec<ContextMenuItem>,
cx: &mut ViewContext<Self>,
) {
let mut items = items.into_iter().peekable();
@@ -245,7 +309,7 @@ impl ContextMenu {
self.visible = true;
self.show_count += 1;
if !cx.is_self_focused() {
self.previously_focused_view_id = cx.focused_view_id(cx.window_id());
self.previously_focused_view_id = cx.focused_view_id();
}
cx.focus_self();
} else {
@@ -254,35 +318,46 @@ impl ContextMenu {
cx.notify();
}
fn render_menu_for_measurement(&self, cx: &mut RenderContext<Self>) -> impl Element {
let window_id = cx.window_id();
pub fn set_position_mode(&mut self, mode: OverlayPositionMode) {
self.position_mode = mode;
}
fn render_menu_for_measurement(&self, cx: &mut ViewContext<Self>) -> impl Element<ContextMenu> {
let style = cx.global::<Settings>().theme.context_menu.clone();
Flex::row()
.with_child(
Flex::column()
.with_children(self.items.iter().enumerate().map(|(ix, item)| {
match item {
ContextMenuItem::Item { label, .. } => {
let style = style.item.style_for(
&mut Default::default(),
Some(ix) == self.selected_index,
);
Flex::column().with_children(self.items.iter().enumerate().map(|(ix, item)| {
match item {
ContextMenuItem::Item { label, .. } => {
let style = style.item.style_for(
&mut Default::default(),
Some(ix) == self.selected_index,
);
Label::new(label.to_string(), style.label.clone())
.contained()
.with_style(style.container)
.boxed()
match label {
ContextMenuItemLabel::String(label) => {
Label::new(label.to_string(), style.label.clone())
.contained()
.with_style(style.container)
.into_any()
}
ContextMenuItemLabel::Element(element) => {
element(&mut Default::default(), style)
}
}
ContextMenuItem::Separator => Empty::new()
.collapsed()
.contained()
.with_style(style.separator)
.constrained()
.with_height(1.)
.boxed(),
}
}))
.boxed(),
ContextMenuItem::Static(f) => f(cx),
ContextMenuItem::Separator => Empty::new()
.collapsed()
.contained()
.with_style(style.separator)
.constrained()
.with_height(1.)
.into_any(),
}
})),
)
.with_child(
Flex::column()
@@ -293,92 +368,125 @@ impl ContextMenu {
&mut Default::default(),
Some(ix) == self.selected_index,
);
KeystrokeLabel::new(
window_id,
self.parent_view_id,
action.boxed_clone(),
style.keystroke.container,
style.keystroke.text.clone(),
)
.boxed()
match action {
ContextMenuItemAction::Action(action) => KeystrokeLabel::new(
self.parent_view_id,
action.boxed_clone(),
style.keystroke.container,
style.keystroke.text.clone(),
)
.into_any(),
ContextMenuItemAction::Handler(_) => Empty::new().into_any(),
}
}
ContextMenuItem::Static(_) => Empty::new().into_any(),
ContextMenuItem::Separator => Empty::new()
.collapsed()
.constrained()
.with_height(1.)
.contained()
.with_style(style.separator)
.boxed(),
.into_any(),
}
}))
.contained()
.with_margin_left(style.keystroke_margin)
.boxed(),
.with_margin_left(style.keystroke_margin),
)
.contained()
.with_style(style.container)
}
fn render_menu(&self, cx: &mut RenderContext<Self>) -> impl Element {
fn render_menu(&self, cx: &mut ViewContext<Self>) -> impl Element<ContextMenu> {
enum Menu {}
enum MenuItem {}
let style = cx.global::<Settings>().theme.context_menu.clone();
let window_id = cx.window_id();
MouseEventHandler::<Menu>::new(0, cx, |_, cx| {
MouseEventHandler::<Menu, ContextMenu>::new(0, cx, |_, cx| {
Flex::column()
.with_children(self.items.iter().enumerate().map(|(ix, item)| {
match item {
ContextMenuItem::Item { label, action } => {
let action = action.boxed_clone();
MouseEventHandler::<MenuItem>::new(ix, cx, |state, _| {
let action = action.clone();
let view_id = self.parent_view_id;
MouseEventHandler::<MenuItem, ContextMenu>::new(ix, cx, |state, _| {
let style =
style.item.style_for(state, Some(ix) == self.selected_index);
Flex::row()
.with_child(
Label::new(label.to_string(), style.label.clone())
.contained()
.boxed(),
)
.with_child({
let keystroke = match &action {
ContextMenuItemAction::Action(action) => Some(
KeystrokeLabel::new(
window_id,
self.parent_view_id,
view_id,
action.boxed_clone(),
style.keystroke.container,
style.keystroke.text.clone(),
)
.flex_float()
.boxed()
.flex_float(),
),
ContextMenuItemAction::Handler(_) => None,
};
Flex::row()
.with_child(match label {
ContextMenuItemLabel::String(label) => {
Label::new(label.clone(), style.label.clone())
.contained()
.into_any()
}
ContextMenuItemLabel::Element(element) => {
element(state, style)
}
})
.with_children(keystroke)
.contained()
.with_style(style.container)
.boxed()
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, move |_, cx| {
cx.dispatch_action(Clicked);
cx.dispatch_any_action(action.boxed_clone());
.on_up(MouseButton::Left, |_, _, _| {}) // Capture these events
.on_down(MouseButton::Left, |_, _, _| {}) // Capture these events
.on_click(MouseButton::Left, move |_, menu, cx| {
menu.cancel(&Default::default(), cx);
let window_id = cx.window_id();
match &action {
ContextMenuItemAction::Action(action) => {
let action = action.boxed_clone();
cx.app_context()
.spawn(|mut cx| async move {
cx.dispatch_action(
window_id,
view_id,
action.as_ref(),
)
})
.detach_and_log_err(cx);
}
ContextMenuItemAction::Handler(handler) => handler(cx),
}
})
.on_drag(MouseButton::Left, |_, _| {})
.boxed()
.on_drag(MouseButton::Left, |_, _, _| {})
.into_any()
}
ContextMenuItem::Static(f) => f(cx),
ContextMenuItem::Separator => Empty::new()
.constrained()
.with_height(1.)
.contained()
.with_style(style.separator)
.boxed(),
.into_any(),
}
}))
.contained()
.with_style(style.container)
.boxed()
})
.on_down_out(MouseButton::Left, |_, cx| cx.dispatch_action(Cancel))
.on_down_out(MouseButton::Right, |_, cx| cx.dispatch_action(Cancel))
.on_down_out(MouseButton::Left, |_, this, cx| {
this.cancel(&Default::default(), cx);
})
.on_down_out(MouseButton::Right, |_, this, cx| {
this.cancel(&Default::default(), cx);
})
}
}

49
crates/copilot/Cargo.toml Normal file
View File

@@ -0,0 +1,49 @@
[package]
name = "copilot"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
path = "src/copilot.rs"
doctest = false
[features]
test-support = [
"collections/test-support",
"gpui/test-support",
"language/test-support",
"lsp/test-support",
"settings/test-support",
"util/test-support",
]
[dependencies]
collections = { path = "../collections" }
context_menu = { path = "../context_menu" }
gpui = { path = "../gpui" }
language = { path = "../language" }
settings = { path = "../settings" }
theme = { path = "../theme" }
lsp = { path = "../lsp" }
node_runtime = { path = "../node_runtime"}
util = { path = "../util" }
async-compression = { version = "0.3", features = ["gzip", "futures-bufread"] }
async-tar = "0.4.2"
anyhow.workspace = true
log.workspace = true
serde.workspace = true
serde_derive.workspace = true
smol.workspace = true
futures.workspace = true
[dev-dependencies]
clock = { path = "../clock" }
collections = { path = "../collections", features = ["test-support"] }
fs = { path = "../fs", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
language = { path = "../language", features = ["test-support"] }
lsp = { path = "../lsp", features = ["test-support"] }
rpc = { path = "../rpc", features = ["test-support"] }
settings = { path = "../settings", features = ["test-support"] }
util = { path = "../util", features = ["test-support"] }

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,225 @@
use serde::{Deserialize, Serialize};
pub enum CheckStatus {}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CheckStatusParams {
pub local_checks_only: bool,
}
impl lsp::request::Request for CheckStatus {
type Params = CheckStatusParams;
type Result = SignInStatus;
const METHOD: &'static str = "checkStatus";
}
pub enum SignInInitiate {}
#[derive(Debug, Serialize, Deserialize)]
pub struct SignInInitiateParams {}
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "status")]
pub enum SignInInitiateResult {
AlreadySignedIn { user: String },
PromptUserDeviceFlow(PromptUserDeviceFlow),
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PromptUserDeviceFlow {
pub user_code: String,
pub verification_uri: String,
}
impl lsp::request::Request for SignInInitiate {
type Params = SignInInitiateParams;
type Result = SignInInitiateResult;
const METHOD: &'static str = "signInInitiate";
}
pub enum SignInConfirm {}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SignInConfirmParams {
pub user_code: String,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "status")]
pub enum SignInStatus {
#[serde(rename = "OK")]
Ok {
user: String,
},
MaybeOk {
user: String,
},
AlreadySignedIn {
user: String,
},
NotAuthorized {
user: String,
},
NotSignedIn,
}
impl lsp::request::Request for SignInConfirm {
type Params = SignInConfirmParams;
type Result = SignInStatus;
const METHOD: &'static str = "signInConfirm";
}
pub enum SignOut {}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SignOutParams {}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SignOutResult {}
impl lsp::request::Request for SignOut {
type Params = SignOutParams;
type Result = SignOutResult;
const METHOD: &'static str = "signOut";
}
pub enum GetCompletions {}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetCompletionsParams {
pub doc: GetCompletionsDocument,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetCompletionsDocument {
pub tab_size: u32,
pub indent_size: u32,
pub insert_spaces: bool,
pub uri: lsp::Url,
pub relative_path: String,
pub position: lsp::Position,
pub version: usize,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetCompletionsResult {
pub completions: Vec<Completion>,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Completion {
pub text: String,
pub position: lsp::Position,
pub uuid: String,
pub range: lsp::Range,
pub display_text: String,
}
impl lsp::request::Request for GetCompletions {
type Params = GetCompletionsParams;
type Result = GetCompletionsResult;
const METHOD: &'static str = "getCompletions";
}
pub enum GetCompletionsCycling {}
impl lsp::request::Request for GetCompletionsCycling {
type Params = GetCompletionsParams;
type Result = GetCompletionsResult;
const METHOD: &'static str = "getCompletionsCycling";
}
pub enum LogMessage {}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LogMessageParams {
pub level: u8,
pub message: String,
pub metadata_str: String,
pub extra: Vec<String>,
}
impl lsp::notification::Notification for LogMessage {
type Params = LogMessageParams;
const METHOD: &'static str = "LogMessage";
}
pub enum StatusNotification {}
#[derive(Debug, Serialize, Deserialize)]
pub struct StatusNotificationParams {
pub message: String,
pub status: String, // One of Normal/InProgress
}
impl lsp::notification::Notification for StatusNotification {
type Params = StatusNotificationParams;
const METHOD: &'static str = "statusNotification";
}
pub enum SetEditorInfo {}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SetEditorInfoParams {
pub editor_info: EditorInfo,
pub editor_plugin_info: EditorPluginInfo,
}
impl lsp::request::Request for SetEditorInfo {
type Params = SetEditorInfoParams;
type Result = String;
const METHOD: &'static str = "setEditorInfo";
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EditorInfo {
pub name: String,
pub version: String,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EditorPluginInfo {
pub name: String,
pub version: String,
}
pub enum NotifyAccepted {}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NotifyAcceptedParams {
pub uuid: String,
}
impl lsp::request::Request for NotifyAccepted {
type Params = NotifyAcceptedParams;
type Result = String;
const METHOD: &'static str = "notifyAccepted";
}
pub enum NotifyRejected {}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NotifyRejectedParams {
pub uuids: Vec<String>,
}
impl lsp::request::Request for NotifyRejected {
type Params = NotifyRejectedParams;
type Result = String;
const METHOD: &'static str = "notifyRejected";
}

View File

@@ -0,0 +1,376 @@
use crate::{request::PromptUserDeviceFlow, Copilot, Status};
use gpui::{
elements::*,
geometry::rect::RectF,
platform::{WindowBounds, WindowKind, WindowOptions},
AnyElement, AnyViewHandle, AppContext, ClipboardItem, Element, Entity, View, ViewContext,
ViewHandle,
};
use settings::Settings;
use theme::ui::modal;
#[derive(PartialEq, Eq, Debug, Clone)]
struct CopyUserCode;
#[derive(PartialEq, Eq, Debug, Clone)]
struct OpenGithub;
const COPILOT_SIGN_UP_URL: &'static str = "https://github.com/features/copilot";
pub fn init(cx: &mut AppContext) {
if let Some(copilot) = Copilot::global(cx) {
let mut code_verification: Option<ViewHandle<CopilotCodeVerification>> = None;
cx.observe(&copilot, move |copilot, cx| {
let status = copilot.read(cx).status();
match &status {
crate::Status::SigningIn { prompt } => {
if let Some(code_verification_handle) = code_verification.as_mut() {
let window_id = code_verification_handle.window_id();
let updated = cx.update_window(window_id, |cx| {
code_verification_handle.update(cx, |code_verification, cx| {
code_verification.set_status(status.clone(), cx)
});
cx.activate_window();
});
if updated.is_none() {
code_verification = Some(create_copilot_auth_window(cx, &status));
}
} else if let Some(_prompt) = prompt {
code_verification = Some(create_copilot_auth_window(cx, &status));
}
}
Status::Authorized | Status::Unauthorized => {
if let Some(code_verification) = code_verification.as_ref() {
let window_id = code_verification.window_id();
cx.update_window(window_id, |cx| {
code_verification.update(cx, |code_verification, cx| {
code_verification.set_status(status, cx)
});
cx.platform().activate(true);
cx.activate_window();
});
}
}
_ => {
if let Some(code_verification) = code_verification.take() {
cx.update_window(code_verification.window_id(), |cx| cx.remove_window());
}
}
}
})
.detach();
}
}
fn create_copilot_auth_window(
cx: &mut AppContext,
status: &Status,
) -> ViewHandle<CopilotCodeVerification> {
let window_size = cx.global::<Settings>().theme.copilot.modal.dimensions();
let window_options = WindowOptions {
bounds: WindowBounds::Fixed(RectF::new(Default::default(), window_size)),
titlebar: None,
center: true,
focus: true,
kind: WindowKind::Normal,
is_movable: true,
screen: None,
};
let (_, view) = cx.add_window(window_options, |_cx| {
CopilotCodeVerification::new(status.clone())
});
view
}
pub struct CopilotCodeVerification {
status: Status,
connect_clicked: bool,
}
impl CopilotCodeVerification {
pub fn new(status: Status) -> Self {
Self {
status,
connect_clicked: false,
}
}
pub fn set_status(&mut self, status: Status, cx: &mut ViewContext<Self>) {
self.status = status;
cx.notify();
}
fn render_device_code(
data: &PromptUserDeviceFlow,
style: &theme::Copilot,
cx: &mut ViewContext<Self>,
) -> impl Element<Self> {
let copied = cx
.read_from_clipboard()
.map(|item| item.text() == &data.user_code)
.unwrap_or(false);
let device_code_style = &style.auth.prompting.device_code;
MouseEventHandler::<Self, _>::new(0, cx, |state, _cx| {
Flex::row()
.with_child(
Label::new(data.user_code.clone(), device_code_style.text.clone())
.aligned()
.contained()
.with_style(device_code_style.left_container)
.constrained()
.with_width(device_code_style.left),
)
.with_child(
Label::new(
if copied { "Copied!" } else { "Copy" },
device_code_style.cta.style_for(state, false).text.clone(),
)
.aligned()
.contained()
.with_style(*device_code_style.right_container.style_for(state, false))
.constrained()
.with_width(device_code_style.right),
)
.contained()
.with_style(device_code_style.cta.style_for(state, false).container)
})
.on_click(gpui::platform::MouseButton::Left, {
let user_code = data.user_code.clone();
move |_, _, cx| {
cx.platform()
.write_to_clipboard(ClipboardItem::new(user_code.clone()));
cx.notify();
}
})
.with_cursor_style(gpui::platform::CursorStyle::PointingHand)
}
fn render_prompting_modal(
connect_clicked: bool,
data: &PromptUserDeviceFlow,
style: &theme::Copilot,
cx: &mut ViewContext<Self>,
) -> AnyElement<Self> {
enum ConnectButton {}
Flex::column()
.with_child(
Flex::column()
.with_children([
Label::new(
"Enable Copilot by connecting",
style.auth.prompting.subheading.text.clone(),
)
.aligned(),
Label::new(
"your existing license.",
style.auth.prompting.subheading.text.clone(),
)
.aligned(),
])
.align_children_center()
.contained()
.with_style(style.auth.prompting.subheading.container),
)
.with_child(Self::render_device_code(data, &style, cx))
.with_child(
Flex::column()
.with_children([
Label::new(
"Paste this code into GitHub after",
style.auth.prompting.hint.text.clone(),
)
.aligned(),
Label::new(
"clicking the button below.",
style.auth.prompting.hint.text.clone(),
)
.aligned(),
])
.align_children_center()
.contained()
.with_style(style.auth.prompting.hint.container.clone()),
)
.with_child(theme::ui::cta_button::<ConnectButton, _, _, _>(
if connect_clicked {
"Waiting for connection..."
} else {
"Connect to GitHub"
},
style.auth.content_width,
&style.auth.cta_button,
cx,
{
let verification_uri = data.verification_uri.clone();
move |_, verification, cx| {
cx.platform().open_url(&verification_uri);
verification.connect_clicked = true;
}
},
))
.align_children_center()
.into_any()
}
fn render_enabled_modal(
style: &theme::Copilot,
cx: &mut ViewContext<Self>,
) -> AnyElement<Self> {
enum DoneButton {}
let enabled_style = &style.auth.authorized;
Flex::column()
.with_child(
Label::new("Copilot Enabled!", enabled_style.subheading.text.clone())
.contained()
.with_style(enabled_style.subheading.container)
.aligned(),
)
.with_child(
Flex::column()
.with_children([
Label::new(
"You can update your settings or",
enabled_style.hint.text.clone(),
)
.aligned(),
Label::new(
"sign out from the Copilot menu in",
enabled_style.hint.text.clone(),
)
.aligned(),
Label::new("the status bar.", enabled_style.hint.text.clone()).aligned(),
])
.align_children_center()
.contained()
.with_style(enabled_style.hint.container),
)
.with_child(theme::ui::cta_button::<DoneButton, _, _, _>(
"Done",
style.auth.content_width,
&style.auth.cta_button,
cx,
|_, _, cx| cx.remove_window(),
))
.align_children_center()
.into_any()
}
fn render_unauthorized_modal(
style: &theme::Copilot,
cx: &mut ViewContext<Self>,
) -> AnyElement<Self> {
let unauthorized_style = &style.auth.not_authorized;
Flex::column()
.with_child(
Flex::column()
.with_children([
Label::new(
"Enable Copilot by connecting",
unauthorized_style.subheading.text.clone(),
)
.aligned(),
Label::new(
"your existing license.",
unauthorized_style.subheading.text.clone(),
)
.aligned(),
])
.align_children_center()
.contained()
.with_style(unauthorized_style.subheading.container),
)
.with_child(
Flex::column()
.with_children([
Label::new(
"You must have an active copilot",
unauthorized_style.warning.text.clone(),
)
.aligned(),
Label::new(
"license to use it in Zed.",
unauthorized_style.warning.text.clone(),
)
.aligned(),
])
.align_children_center()
.contained()
.with_style(unauthorized_style.warning.container),
)
.with_child(theme::ui::cta_button::<Self, _, _, _>(
"Subscribe on GitHub",
style.auth.content_width,
&style.auth.cta_button,
cx,
|_, _, cx| {
cx.remove_window();
cx.platform().open_url(COPILOT_SIGN_UP_URL)
},
))
.align_children_center()
.into_any()
}
}
impl Entity for CopilotCodeVerification {
type Event = ();
}
impl View for CopilotCodeVerification {
fn ui_name() -> &'static str {
"CopilotCodeVerification"
}
fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
cx.notify()
}
fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
cx.notify()
}
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
enum ConnectModal {}
let style = cx.global::<Settings>().theme.clone();
modal::<ConnectModal, _, _, _, _>(
"Connect Copilot to Zed",
&style.copilot.modal,
cx,
|cx| {
Flex::column()
.with_children([
theme::ui::icon(&style.copilot.auth.header).into_any(),
match &self.status {
Status::SigningIn {
prompt: Some(prompt),
} => Self::render_prompting_modal(
self.connect_clicked,
&prompt,
&style.copilot,
cx,
),
Status::Unauthorized => {
self.connect_clicked = false;
Self::render_unauthorized_modal(&style.copilot, cx)
}
Status::Authorized => {
self.connect_clicked = false;
Self::render_enabled_modal(&style.copilot, cx)
}
_ => Empty::new().into_any(),
},
])
.align_children_center()
},
)
.into_any()
}
}

View File

@@ -0,0 +1,23 @@
[package]
name = "copilot_button"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
path = "src/copilot_button.rs"
doctest = false
[dependencies]
assets = { path = "../assets" }
copilot = { path = "../copilot" }
editor = { path = "../editor" }
context_menu = { path = "../context_menu" }
gpui = { path = "../gpui" }
settings = { path = "../settings" }
theme = { path = "../theme" }
util = { path = "../util" }
workspace = { path = "../workspace" }
anyhow.workspace = true
smol.workspace = true
futures.workspace = true

View File

@@ -0,0 +1,434 @@
use anyhow::Result;
use context_menu::{ContextMenu, ContextMenuItem};
use copilot::{Copilot, SignOut, Status};
use editor::{scroll::autoscroll::Autoscroll, Editor};
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 std::{path::Path, sync::Arc};
use util::{paths, ResultExt};
use workspace::{
create_and_open_local_file, item::ItemHandle,
notifications::simple_message_notification::OsOpen, StatusItemView, Toast, Workspace,
};
const COPILOT_SETTINGS_URL: &str = "https://github.com/settings/copilot";
const COPILOT_STARTING_TOAST_ID: usize = 1337;
const COPILOT_ERROR_TOAST_ID: usize = 1338;
pub struct CopilotButton {
popup_menu: ViewHandle<ContextMenu>,
editor_subscription: Option<(Subscription, usize)>,
editor_enabled: Option<bool>,
language: Option<Arc<str>>,
path: Option<Arc<Path>>,
}
impl Entity for CopilotButton {
type Event = ();
}
impl View for CopilotButton {
fn ui_name() -> &'static str {
"CopilotButton"
}
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
let settings = cx.global::<Settings>();
if !settings.features.copilot {
return Empty::new().into_any();
}
let theme = settings.theme.clone();
let active = self.popup_menu.read(cx).visible();
let Some(copilot) = Copilot::global(cx) else {
return Empty::new().into_any();
};
let status = copilot.read(cx).status();
let enabled = self
.editor_enabled
.unwrap_or(settings.show_copilot_suggestions(None, None));
Stack::new()
.with_child(
MouseEventHandler::<Self, _>::new(0, cx, {
let theme = theme.clone();
let status = status.clone();
move |state, _cx| {
let style = theme
.workspace
.status_bar
.sidebar_buttons
.item
.style_for(state, active);
Flex::row()
.with_child(
Svg::new({
match status {
Status::Error(_) => "icons/copilot_error_16.svg",
Status::Authorized => {
if enabled {
"icons/copilot_16.svg"
} else {
"icons/copilot_disabled_16.svg"
}
}
_ => "icons/copilot_init_16.svg",
}
})
.with_color(style.icon_color)
.constrained()
.with_width(style.icon_size)
.aligned()
.into_any_named("copilot-icon"),
)
.constrained()
.with_height(style.icon_size)
.contained()
.with_style(style.container)
}
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, {
let status = status.clone();
move |_, this, cx| match status {
Status::Authorized => this.deploy_copilot_menu(cx),
Status::Error(ref e) => {
if let Some(workspace) = cx.root_view().clone().downcast::<Workspace>()
{
workspace.update(cx, |workspace, cx| {
workspace.show_toast(
Toast::new(
COPILOT_ERROR_TOAST_ID,
format!("Copilot can't be started: {}", e),
)
.on_click(
"Reinstall Copilot",
|cx| {
if let Some(copilot) = Copilot::global(cx) {
copilot
.update(cx, |copilot, cx| {
copilot.reinstall(cx)
})
.detach();
}
},
),
cx,
);
});
}
}
_ => this.deploy_copilot_start_menu(cx),
}
})
.with_tooltip::<Self>(
0,
"GitHub Copilot".into(),
None,
theme.tooltip.clone(),
cx,
),
)
.with_child(ChildView::new(&self.popup_menu, cx).aligned().top().right())
.into_any()
}
}
impl CopilotButton {
pub fn new(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);
menu.set_position_mode(OverlayPositionMode::Local);
menu
});
cx.observe(&menu, |_, _, cx| cx.notify()).detach();
Copilot::global(cx).map(|copilot| cx.observe(&copilot, |_, _, cx| cx.notify()).detach());
cx.observe_global::<Settings, _>(move |_, cx| cx.notify())
.detach();
Self {
popup_menu: menu,
editor_subscription: None,
editor_enabled: None,
language: None,
path: None,
}
}
pub fn deploy_copilot_start_menu(&mut self, cx: &mut ViewContext<Self>) {
let mut menu_options = Vec::with_capacity(2);
menu_options.push(ContextMenuItem::handler("Sign In", |cx| {
initiate_sign_in(cx)
}));
menu_options.push(ContextMenuItem::handler("Disable Copilot", |cx| {
hide_copilot(cx)
}));
self.popup_menu.update(cx, |menu, cx| {
menu.show(
Default::default(),
AnchorCorner::BottomRight,
menu_options,
cx,
);
});
}
pub fn deploy_copilot_menu(&mut self, cx: &mut ViewContext<Self>) {
let settings = cx.global::<Settings>();
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()));
menu_options.push(ContextMenuItem::handler(
format!(
"{} Suggestions for {}",
if language_enabled { "Hide" } else { "Show" },
language
),
move |cx| toggle_copilot_for_language(language.clone(), cx),
));
}
if let Some(path) = self.path.as_ref() {
let path_enabled = settings.copilot_enabled_for_path(path);
let path = path.clone();
menu_options.push(ContextMenuItem::handler(
format!(
"{} Suggestions for This Path",
if path_enabled { "Hide" } else { "Show" }
),
move |cx| {
if let Some(workspace) = cx.root_view().clone().downcast::<Workspace>() {
let workspace = workspace.downgrade();
cx.spawn(|_, cx| {
configure_disabled_globs(
workspace,
path_enabled.then_some(path.clone()),
cx,
)
})
.detach_and_log_err(cx);
}
},
));
}
let globally_enabled = cx.global::<Settings>().features.copilot;
menu_options.push(ContextMenuItem::handler(
if globally_enabled {
"Hide Suggestions for All Files"
} else {
"Show Suggestions for All Files"
},
|cx| toggle_copilot_globally(cx),
));
menu_options.push(ContextMenuItem::Separator);
let icon_style = settings.theme.copilot.out_link_icon.clone();
menu_options.push(ContextMenuItem::action(
move |state: &mut MouseState, style: &theme::ContextMenuItem| {
Flex::row()
.with_child(Label::new("Copilot Settings", style.label.clone()))
.with_child(theme::ui::icon(icon_style.style_for(state, false)))
.align_children_center()
.into_any()
},
OsOpen::new(COPILOT_SETTINGS_URL),
));
menu_options.push(ContextMenuItem::action("Sign Out", SignOut));
self.popup_menu.update(cx, |menu, cx| {
menu.show(
Default::default(),
AnchorCorner::BottomRight,
menu_options,
cx,
);
});
}
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_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;
cx.notify()
}
}
impl StatusItemView for CopilotButton {
fn set_active_pane_item(&mut self, item: Option<&dyn ItemHandle>, cx: &mut ViewContext<Self>) {
if let Some(editor) = item.map(|item| item.act_as::<Editor>(cx)).flatten() {
self.editor_subscription =
Some((cx.observe(&editor, Self::update_enabled), editor.id()));
self.update_enabled(editor, cx);
} else {
self.language = None;
self.editor_subscription = None;
self.editor_enabled = None;
}
cx.notify();
}
}
async fn configure_disabled_globs(
workspace: WeakViewHandle<Workspace>,
path_to_disable: Option<Arc<Path>>,
mut cx: AsyncAppContext,
) -> Result<()> {
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()
})
})?
.await?
.downcast::<Editor>()
.unwrap();
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 copilot = file.copilot.get_or_insert_with(Default::default);
let globs = copilot.disabled_globs.get_or_insert_with(|| {
cx.global::<Settings>()
.copilot
.disabled_globs
.clone()
.iter()
.map(|glob| glob.as_str().to_string())
.collect::<Vec<_>>()
});
if let Some(path_to_disable) = &path_to_disable {
globs.push(path_to_disable.to_string_lossy().into_owned());
} else {
globs.clear();
}
});
if !edits.is_empty() {
item.change_selections(Some(Autoscroll::newest()), cx, |selections| {
selections.select_ranges(edits.iter().map(|e| e.0.clone()));
});
// When *enabling* a path, don't actually perform an edit, just select the range.
if path_to_disable.is_some() {
item.edit(edits.iter().cloned(), cx);
}
}
})?;
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_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 hide_copilot(cx: &mut AppContext) {
SettingsFile::update(cx, move |file_contents| {
file_contents.features.copilot = Some(false)
});
}
fn initiate_sign_in(cx: &mut WindowContext) {
let Some(copilot) = Copilot::global(cx) else {
return;
};
let status = copilot.read(cx).status();
match status {
Status::Starting { task } => {
let Some(workspace) = cx.root_view().clone().downcast::<Workspace>() else {
return;
};
workspace.update(cx, |workspace, cx| {
workspace.show_toast(
Toast::new(COPILOT_STARTING_TOAST_ID, "Copilot is starting..."),
cx,
)
});
let workspace = workspace.downgrade();
cx.spawn(|mut cx| async move {
task.await;
if let Some(copilot) = cx.read(Copilot::global) {
workspace
.update(&mut cx, |workspace, cx| match copilot.read(cx).status() {
Status::Authorized => workspace.show_toast(
Toast::new(COPILOT_STARTING_TOAST_ID, "Copilot has started!"),
cx,
),
_ => {
workspace.dismiss_toast(COPILOT_STARTING_TOAST_ID, cx);
copilot
.update(cx, |copilot, cx| copilot.sign_in(cx))
.detach_and_log_err(cx);
}
})
.log_err();
}
})
.detach();
}
_ => {
copilot
.update(cx, |copilot, cx| copilot.sign_in(cx))
.detach_and_log_err(cx);
}
}
}

View File

@@ -17,16 +17,17 @@ gpui = { path = "../gpui" }
sqlez = { path = "../sqlez" }
sqlez_macros = { path = "../sqlez_macros" }
util = { path = "../util" }
anyhow = "1.0.57"
anyhow.workspace = true
indoc = "1.0.4"
async-trait = "0.1"
lazy_static = "1.4.0"
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
parking_lot = "0.11.1"
serde = { version = "1.0", features = ["derive"] }
smol = "1.2"
async-trait.workspace = true
lazy_static.workspace = true
log.workspace = true
parking_lot.workspace = true
serde.workspace = true
serde_derive.workspace = true
smol.workspace = true
[dev-dependencies]
gpui = { path = "../gpui", features = ["test-support"] }
env_logger = "0.9.1"
tempdir = { version = "0.3.7" }
env_logger.workspace = true
tempdir.workspace = true

View File

@@ -4,6 +4,7 @@ pub mod query;
// Re-export
pub use anyhow;
use anyhow::Context;
use gpui::AppContext;
pub use indoc::indoc;
pub use lazy_static;
use parking_lot::{Mutex, RwLock};
@@ -17,6 +18,7 @@ use sqlez::domain::Migrator;
use sqlez::thread_safe_connection::ThreadSafeConnection;
use sqlez_macros::sql;
use std::fs::create_dir_all;
use std::future::Future;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
@@ -39,6 +41,7 @@ const FALLBACK_DB_NAME: &'static str = "FALLBACK_MEMORY_DB";
const DB_FILE_NAME: &'static str = "db.sqlite";
lazy_static::lazy_static! {
// !!!!!!! CHANGE BACK TO DEFAULT FALSE BEFORE SHIPPING
static ref ZED_STATELESS: bool = std::env::var("ZED_STATELESS").map_or(false, |v| !v.is_empty());
static ref DB_FILE_OPERATIONS: Mutex<()> = Mutex::new(());
pub static ref BACKUP_DB_PATH: RwLock<Option<PathBuf>> = RwLock::new(None);
@@ -63,11 +66,11 @@ pub async fn open_db<M: Migrator + 'static>(
let connection = async_iife!({
// Note: This still has a race condition where 1 set of migrations succeeds
// (e.g. (Workspace, Editor)) and another fails (e.g. (Workspace, Terminal))
// This will cause the first connection to have the database taken out
// This will cause the first connection to have the database taken out
// from under it. This *should* be fine though. The second dabatase failure will
// cause errors in the log and so should be observed by developers while writing
// soon-to-be good migrations. If user databases are corrupted, we toss them out
// and try again from a blank. As long as running all migrations from start to end
// and try again from a blank. As long as running all migrations from start to end
// on a blank database is ok, this race condition will never be triggered.
//
// Basically: Don't ever push invalid migrations to stable or everyone will have
@@ -85,7 +88,7 @@ pub async fn open_db<M: Migrator + 'static>(
};
}
// Take a lock in the failure case so that we move the db once per process instead
// Take a lock in the failure case so that we move the db once per process instead
// of potentially multiple times from different threads. This shouldn't happen in the
// normal path
let _lock = DB_FILE_OPERATIONS.lock();
@@ -236,6 +239,15 @@ macro_rules! define_connection {
};
}
pub fn write_and_log<F>(cx: &mut AppContext, db_write: impl FnOnce() -> F + Send + 'static)
where
F: Future<Output = anyhow::Result<()>> + Send,
{
cx.background()
.spawn(async move { db_write().await.log_err() })
.detach()
}
#[cfg(test)]
mod tests {
use std::{fs, thread};

View File

@@ -9,24 +9,28 @@ path = "src/diagnostics.rs"
doctest = false
[dependencies]
anyhow = "1.0"
smallvec = { version = "1.6", features = ["union"] }
collections = { path = "../collections" }
editor = { path = "../editor" }
language = { path = "../language" }
gpui = { path = "../gpui" }
language = { path = "../language" }
lsp = { path = "../lsp" }
project = { path = "../project" }
settings = { path = "../settings" }
theme = { path = "../theme" }
util = { path = "../util" }
workspace = { path = "../workspace" }
postage = { version = "0.4", features = ["futures-traits"] }
anyhow.workspace = true
smallvec.workspace = true
postage.workspace = true
[dev-dependencies]
unindent = "0.1"
client = { path = "../client", features = ["test-support"] }
editor = { path = "../editor", features = ["test-support"] }
language = { path = "../language", features = ["test-support"] }
lsp = { path = "../lsp", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
workspace = { path = "../workspace", features = ["test-support"] }
serde_json = { version = "1", features = ["preserve_order"] }
serde_json.workspace = true
unindent.workspace = true

View File

@@ -1,7 +1,7 @@
pub mod items;
use anyhow::Result;
use collections::{BTreeMap, HashSet};
use collections::{BTreeSet, HashSet};
use editor::{
diagnostic_block_renderer,
display_map::{BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock},
@@ -10,20 +10,21 @@ use editor::{
Editor, ExcerptId, ExcerptRange, MultiBuffer, ToOffset,
};
use gpui::{
actions, elements::*, fonts::TextStyle, impl_internal_actions, serde_json, AnyViewHandle,
AppContext, Entity, ModelHandle, MutableAppContext, RenderContext, Task, View, ViewContext,
ViewHandle, WeakViewHandle,
actions, elements::*, fonts::TextStyle, serde_json, AnyViewHandle, AppContext, Entity,
ModelHandle, Task, View, ViewContext, ViewHandle, WeakViewHandle,
};
use language::{
Anchor, Bias, Buffer, Diagnostic, DiagnosticEntry, DiagnosticSeverity, Point, Selection,
SelectionGoal,
};
use lsp::LanguageServerId;
use project::{DiagnosticSummary, Project, ProjectPath};
use serde_json::json;
use settings::Settings;
use smallvec::SmallVec;
use std::{
any::{Any, TypeId},
borrow::Cow,
cmp::Ordering,
ops::Range,
path::PathBuf,
@@ -31,17 +32,15 @@ use std::{
};
use util::TryFutureExt;
use workspace::{
item::{Item, ItemEvent, ItemHandle},
ItemNavHistory, Pane, Workspace,
item::{BreadcrumbText, Item, ItemEvent, ItemHandle},
ItemNavHistory, Pane, ToolbarItemLocation, Workspace,
};
actions!(diagnostics, [Deploy]);
impl_internal_actions!(diagnostics, [Jump]);
const CONTEXT_LINE_COUNT: u32 = 1;
pub fn init(cx: &mut MutableAppContext) {
pub fn init(cx: &mut AppContext) {
cx.add_action(ProjectDiagnosticsEditor::deploy);
items::init(cx);
}
@@ -55,7 +54,7 @@ struct ProjectDiagnosticsEditor {
summary: DiagnosticSummary,
excerpts: ModelHandle<MultiBuffer>,
path_states: Vec<PathState>,
paths_to_update: BTreeMap<ProjectPath, usize>,
paths_to_update: BTreeSet<(ProjectPath, LanguageServerId)>,
}
struct PathState {
@@ -71,6 +70,7 @@ struct Jump {
}
struct DiagnosticGroupState {
language_server_id: LanguageServerId,
primary_diagnostic: DiagnosticEntry<language::Anchor>,
primary_excerpt_ix: usize,
excerpts: Vec<ExcerptId>,
@@ -87,16 +87,16 @@ impl View for ProjectDiagnosticsEditor {
"ProjectDiagnosticsEditor"
}
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
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)
.boxed()
.into_any()
} else {
ChildView::new(&self.editor, cx).boxed()
ChildView::new(&self.editor, cx).into_any()
}
}
@@ -115,7 +115,7 @@ impl View for ProjectDiagnosticsEditor {
}),
"summary": self.summary,
"paths_to_update": self.paths_to_update.iter().map(|(path, server_id)|
(path.path.to_string_lossy(), server_id)
(path.path.to_string_lossy(), server_id.0)
).collect::<Vec<_>>(),
"paths_states": self.path_states.iter().map(|state|
json!({
@@ -148,7 +148,7 @@ impl ProjectDiagnosticsEditor {
path,
} => {
this.paths_to_update
.insert(path.clone(), *language_server_id);
.insert((path.clone(), *language_server_id));
}
_ => {}
})
@@ -167,7 +167,7 @@ impl ProjectDiagnosticsEditor {
let project = project_handle.read(cx);
let paths_to_update = project
.diagnostic_summaries(cx)
.map(|e| (e.0, e.1.language_server_id))
.map(|(path, server_id, _)| (path, server_id))
.collect();
let summary = project.diagnostic_summary(cx);
let mut this = Self {
@@ -195,9 +195,13 @@ impl ProjectDiagnosticsEditor {
}
}
fn update_excerpts(&mut self, language_server_id: Option<usize>, cx: &mut ViewContext<Self>) {
fn update_excerpts(
&mut self,
language_server_id: Option<LanguageServerId>,
cx: &mut ViewContext<Self>,
) {
let mut paths = Vec::new();
self.paths_to_update.retain(|path, server_id| {
self.paths_to_update.retain(|(path, server_id)| {
if language_server_id
.map_or(true, |language_server_id| language_server_id == *server_id)
{
@@ -214,7 +218,9 @@ impl ProjectDiagnosticsEditor {
let buffer = project
.update(&mut cx, |project, cx| project.open_buffer(path.clone(), cx))
.await?;
this.update(&mut cx, |this, cx| this.populate_excerpts(path, buffer, cx))
this.update(&mut cx, |this, cx| {
this.populate_excerpts(path, language_server_id, buffer, cx)
})?;
}
Result::<_, anyhow::Error>::Ok(())
}
@@ -226,6 +232,7 @@ impl ProjectDiagnosticsEditor {
fn populate_excerpts(
&mut self,
path: ProjectPath,
language_server_id: Option<LanguageServerId>,
buffer: ModelHandle<Buffer>,
cx: &mut ViewContext<Self>,
) {
@@ -264,9 +271,9 @@ impl ProjectDiagnosticsEditor {
let excerpts_snapshot = self.excerpts.update(cx, |excerpts, excerpts_cx| {
let mut old_groups = path_state.diagnostic_groups.iter().enumerate().peekable();
let mut new_groups = snapshot
.diagnostic_groups()
.diagnostic_groups(language_server_id)
.into_iter()
.filter(|group| {
.filter(|(_, group)| {
group.entries[group.primary_ix].diagnostic.severity
<= DiagnosticSeverity::WARNING
})
@@ -278,12 +285,27 @@ impl ProjectDiagnosticsEditor {
match (old_groups.peek(), new_groups.peek()) {
(None, None) => break,
(None, Some(_)) => to_insert = new_groups.next(),
(Some(_), None) => to_remove = old_groups.next(),
(Some((_, old_group)), Some(new_group)) => {
(Some((_, old_group)), None) => {
if language_server_id.map_or(true, |id| id == old_group.language_server_id)
{
to_remove = old_groups.next();
} else {
to_keep = old_groups.next();
}
}
(Some((_, old_group)), Some((_, new_group))) => {
let old_primary = &old_group.primary_diagnostic;
let new_primary = &new_group.entries[new_group.primary_ix];
match compare_diagnostics(old_primary, new_primary, &snapshot) {
Ordering::Less => to_remove = old_groups.next(),
Ordering::Less => {
if language_server_id
.map_or(true, |id| id == old_group.language_server_id)
{
to_remove = old_groups.next();
} else {
to_keep = old_groups.next();
}
}
Ordering::Equal => {
to_keep = old_groups.next();
new_groups.next();
@@ -293,8 +315,9 @@ impl ProjectDiagnosticsEditor {
}
}
if let Some(group) = to_insert {
if let Some((language_server_id, group)) = to_insert {
let mut group_state = DiagnosticGroupState {
language_server_id,
primary_diagnostic: group.entries[group.primary_ix].clone(),
primary_excerpt_ix: 0,
excerpts: Default::default(),
@@ -505,12 +528,12 @@ impl ProjectDiagnosticsEditor {
}
impl Item for ProjectDiagnosticsEditor {
fn tab_content(
fn tab_content<T: View>(
&self,
_detail: Option<usize>,
style: &theme::Tab,
cx: &AppContext,
) -> ElementBox {
) -> AnyElement<T> {
render_summary(
&self.summary,
&style.label.text,
@@ -526,11 +549,20 @@ impl Item for ProjectDiagnosticsEditor {
false
}
fn added_to_workspace(&mut self, workspace: &mut Workspace, cx: &mut ViewContext<Self>) {
self.editor
.update(cx, |editor, cx| editor.added_to_workspace(workspace, cx));
}
fn navigate(&mut self, data: Box<dyn Any>, cx: &mut ViewContext<Self>) -> bool {
self.editor
.update(cx, |editor, cx| editor.navigate(data, cx))
}
fn tab_tooltip_text(&self, _: &AppContext) -> Option<Cow<str>> {
Some("Project Diagnostics".into())
}
fn is_dirty(&self, cx: &AppContext) -> bool {
self.excerpts.read(cx).is_dirty(cx)
}
@@ -602,16 +634,16 @@ impl Item for ProjectDiagnosticsEditor {
))
}
fn act_as_type(
&self,
fn act_as_type<'a>(
&'a self,
type_id: TypeId,
self_handle: &ViewHandle<Self>,
_: &AppContext,
) -> Option<AnyViewHandle> {
self_handle: &'a ViewHandle<Self>,
_: &'a AppContext,
) -> Option<&AnyViewHandle> {
if type_id == TypeId::of::<Self>() {
Some(self_handle.into())
Some(self_handle)
} else if type_id == TypeId::of::<Editor>() {
Some((&self.editor).into())
Some(&self.editor)
} else {
None
}
@@ -625,6 +657,14 @@ impl Item for ProjectDiagnosticsEditor {
Some("diagnostics")
}
fn breadcrumbs(&self, theme: &theme::Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
self.editor.breadcrumbs(theme, cx)
}
fn breadcrumb_location(&self) -> ToolbarItemLocation {
ToolbarItemLocation::PrimaryLeft { flex: None }
}
fn deserialize(
project: ModelHandle<Project>,
workspace: WeakViewHandle<Workspace>,
@@ -637,7 +677,7 @@ impl Item for ProjectDiagnosticsEditor {
}
fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
let (message, highlights) = highlight_diagnostic_message(&diagnostic.message);
let (message, highlights) = highlight_diagnostic_message(Vec::new(), &diagnostic.message);
Arc::new(move |cx| {
let settings = cx.global::<Settings>();
let theme = &settings.theme.editor;
@@ -658,8 +698,17 @@ fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
.with_width(icon_width)
.aligned()
.contained()
.boxed(),
.with_margin_right(cx.gutter_padding),
)
.with_children(diagnostic.source.as_ref().map(|source| {
Label::new(
format!("{source}: "),
style.source.label.clone().with_font_size(font_size),
)
.contained()
.with_style(style.message.container)
.aligned()
}))
.with_child(
Label::new(
message.clone(),
@@ -668,47 +717,45 @@ fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
.with_highlights(highlights.clone())
.contained()
.with_style(style.message.container)
.with_margin_left(cx.gutter_padding)
.aligned()
.boxed(),
.aligned(),
)
.with_children(diagnostic.code.clone().map(|code| {
Label::new(code, style.code.text.clone().with_font_size(font_size))
.contained()
.with_style(style.code.container)
.aligned()
.boxed()
}))
.contained()
.with_style(style.container)
.with_padding_left(cx.gutter_padding)
.with_padding_right(cx.gutter_padding)
.expanded()
.named("diagnostic header")
.into_any_named("diagnostic header")
})
}
pub(crate) fn render_summary(
pub(crate) fn render_summary<T: View>(
summary: &DiagnosticSummary,
text_style: &TextStyle,
theme: &theme::ProjectDiagnostics,
) -> ElementBox {
) -> AnyElement<T> {
if summary.error_count == 0 && summary.warning_count == 0 {
Label::new("No problems", text_style.clone()).boxed()
Label::new("No problems", text_style.clone()).into_any()
} else {
let icon_width = theme.tab_icon_width;
let icon_spacing = theme.tab_icon_spacing;
let summary_spacing = theme.tab_summary_spacing;
Flex::row()
.with_children([
.with_child(
Svg::new("icons/circle_x_mark_12.svg")
.with_color(text_style.color)
.constrained()
.with_width(icon_width)
.aligned()
.contained()
.with_margin_right(icon_spacing)
.named("no-icon"),
.with_margin_right(icon_spacing),
)
.with_child(
Label::new(
summary.error_count.to_string(),
LabelStyle {
@@ -716,8 +763,9 @@ pub(crate) fn render_summary(
highlight_text: None,
},
)
.aligned()
.boxed(),
.aligned(),
)
.with_child(
Svg::new("icons/triangle_exclamation_12.svg")
.with_color(text_style.color)
.constrained()
@@ -725,8 +773,9 @@ pub(crate) fn render_summary(
.aligned()
.contained()
.with_margin_left(summary_spacing)
.with_margin_right(icon_spacing)
.named("warn-icon"),
.with_margin_right(icon_spacing),
)
.with_child(
Label::new(
summary.warning_count.to_string(),
LabelStyle {
@@ -734,10 +783,9 @@ pub(crate) fn render_summary(
highlight_text: None,
},
)
.aligned()
.boxed(),
])
.boxed()
.aligned(),
)
.into_any()
}
}
@@ -766,28 +814,26 @@ mod tests {
display_map::{BlockContext, TransformBlock},
DisplayPoint,
};
use gpui::TestAppContext;
use gpui::{TestAppContext, WindowContext};
use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, PointUtf16, Unclipped};
use project::FakeFs;
use serde_json::json;
use unindent::Unindent as _;
use workspace::AppState;
#[gpui::test]
async fn test_diagnostics(cx: &mut TestAppContext) {
let app_state = cx.update(AppState::test);
app_state
.fs
.as_fake()
.insert_tree(
"/test",
json!({
"consts.rs": "
Settings::test_async(cx);
let fs = FakeFs::new(cx.background());
fs.insert_tree(
"/test",
json!({
"consts.rs": "
const a: i32 = 'a';
const b: i32 = c;
"
.unindent(),
.unindent(),
"main.rs": "
"main.rs": "
fn main() {
let x = vec![];
let y = vec![];
@@ -799,27 +845,20 @@ mod tests {
d(x);
}
"
.unindent(),
}),
)
.await;
.unindent(),
}),
)
.await;
let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await;
let (_, workspace) = cx.add_window(|cx| {
Workspace::new(
Default::default(),
0,
project.clone(),
|_, _| unimplemented!(),
cx,
)
});
let language_server_id = LanguageServerId(0);
let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
// Create some diagnostics
project.update(cx, |project, cx| {
project
.update_diagnostic_entries(
0,
language_server_id,
PathBuf::from("/test/main.rs"),
None,
vec![
@@ -900,7 +939,7 @@ mod tests {
});
// Open the project diagnostics view while there are already diagnostics.
let view = cx.add_view(&workspace, |cx| {
let view = cx.add_view(window_id, |cx| {
ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx)
});
@@ -968,10 +1007,10 @@ mod tests {
// Diagnostics are added for another earlier path.
project.update(cx, |project, cx| {
project.disk_based_diagnostics_started(0, cx);
project.disk_based_diagnostics_started(language_server_id, cx);
project
.update_diagnostic_entries(
0,
language_server_id,
PathBuf::from("/test/consts.rs"),
None,
vec![DiagnosticEntry {
@@ -988,7 +1027,7 @@ mod tests {
cx,
)
.unwrap();
project.disk_based_diagnostics_finished(0, cx);
project.disk_based_diagnostics_finished(language_server_id, cx);
});
view.next_notification(cx).await;
@@ -1068,10 +1107,10 @@ mod tests {
// Diagnostics are added to the first path
project.update(cx, |project, cx| {
project.disk_based_diagnostics_started(0, cx);
project.disk_based_diagnostics_started(language_server_id, cx);
project
.update_diagnostic_entries(
0,
language_server_id,
PathBuf::from("/test/consts.rs"),
None,
vec![
@@ -1104,7 +1143,7 @@ mod tests {
cx,
)
.unwrap();
project.disk_based_diagnostics_finished(0, cx);
project.disk_based_diagnostics_finished(language_server_id, cx);
});
view.next_notification(cx).await;
@@ -1184,13 +1223,274 @@ mod tests {
});
}
fn editor_blocks(
editor: &ViewHandle<Editor>,
cx: &mut MutableAppContext,
) -> Vec<(u32, String)> {
let mut presenter = cx.build_presenter(editor.id(), 0., Default::default());
let mut cx = presenter.build_layout_context(Default::default(), false, cx);
cx.render(editor, |editor, cx| {
#[gpui::test]
async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
Settings::test_async(cx);
let fs = FakeFs::new(cx.background());
fs.insert_tree(
"/test",
json!({
"main.js": "
a();
b();
c();
d();
e();
".unindent()
}),
)
.await;
let server_id_1 = LanguageServerId(100);
let server_id_2 = LanguageServerId(101);
let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let view = cx.add_view(window_id, |cx| {
ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx)
});
// Two language servers start updating diagnostics
project.update(cx, |project, cx| {
project.disk_based_diagnostics_started(server_id_1, cx);
project.disk_based_diagnostics_started(server_id_2, cx);
project
.update_diagnostic_entries(
server_id_1,
PathBuf::from("/test/main.js"),
None,
vec![DiagnosticEntry {
range: Unclipped(PointUtf16::new(0, 0))..Unclipped(PointUtf16::new(0, 1)),
diagnostic: Diagnostic {
message: "error 1".to_string(),
severity: DiagnosticSeverity::WARNING,
is_primary: true,
is_disk_based: true,
group_id: 1,
..Default::default()
},
}],
cx,
)
.unwrap();
project
.update_diagnostic_entries(
server_id_2,
PathBuf::from("/test/main.js"),
None,
vec![DiagnosticEntry {
range: Unclipped(PointUtf16::new(1, 0))..Unclipped(PointUtf16::new(1, 1)),
diagnostic: Diagnostic {
message: "warning 1".to_string(),
severity: DiagnosticSeverity::ERROR,
is_primary: true,
is_disk_based: true,
group_id: 2,
..Default::default()
},
}],
cx,
)
.unwrap();
});
// The first language server finishes
project.update(cx, |project, cx| {
project.disk_based_diagnostics_finished(server_id_1, cx);
});
// Only the first language server's diagnostics are shown.
cx.foreground().run_until_parked();
view.update(cx, |view, cx| {
assert_eq!(
editor_blocks(&view.editor, cx),
[
(0, "path header block".into()),
(2, "diagnostic header".into()),
]
);
assert_eq!(
view.editor.update(cx, |editor, cx| editor.display_text(cx)),
concat!(
"\n", // filename
"\n", // padding
// diagnostic group 1
"\n", // primary message
"\n", // padding
"a();\n", //
"b();",
)
);
});
// The second language server finishes
project.update(cx, |project, cx| {
project.disk_based_diagnostics_finished(server_id_2, cx);
});
// Both language server's diagnostics are shown.
cx.foreground().run_until_parked();
view.update(cx, |view, cx| {
assert_eq!(
editor_blocks(&view.editor, cx),
[
(0, "path header block".into()),
(2, "diagnostic header".into()),
(6, "collapsed context".into()),
(7, "diagnostic header".into()),
]
);
assert_eq!(
view.editor.update(cx, |editor, cx| editor.display_text(cx)),
concat!(
"\n", // filename
"\n", // padding
// diagnostic group 1
"\n", // primary message
"\n", // padding
"a();\n", // location
"b();\n", //
"\n", // collapsed context
// diagnostic group 2
"\n", // primary message
"\n", // padding
"a();\n", // context
"b();\n", //
"c();", // context
)
);
});
// Both language servers start updating diagnostics, and the first server finishes.
project.update(cx, |project, cx| {
project.disk_based_diagnostics_started(server_id_1, cx);
project.disk_based_diagnostics_started(server_id_2, cx);
project
.update_diagnostic_entries(
server_id_1,
PathBuf::from("/test/main.js"),
None,
vec![DiagnosticEntry {
range: Unclipped(PointUtf16::new(2, 0))..Unclipped(PointUtf16::new(2, 1)),
diagnostic: Diagnostic {
message: "warning 2".to_string(),
severity: DiagnosticSeverity::WARNING,
is_primary: true,
is_disk_based: true,
group_id: 1,
..Default::default()
},
}],
cx,
)
.unwrap();
project
.update_diagnostic_entries(
server_id_2,
PathBuf::from("/test/main.rs"),
None,
vec![],
cx,
)
.unwrap();
project.disk_based_diagnostics_finished(server_id_1, cx);
});
// Only the first language server's diagnostics are updated.
cx.foreground().run_until_parked();
view.update(cx, |view, cx| {
assert_eq!(
editor_blocks(&view.editor, cx),
[
(0, "path header block".into()),
(2, "diagnostic header".into()),
(7, "collapsed context".into()),
(8, "diagnostic header".into()),
]
);
assert_eq!(
view.editor.update(cx, |editor, cx| editor.display_text(cx)),
concat!(
"\n", // filename
"\n", // padding
// diagnostic group 1
"\n", // primary message
"\n", // padding
"a();\n", // location
"b();\n", //
"c();\n", // context
"\n", // collapsed context
// diagnostic group 2
"\n", // primary message
"\n", // padding
"b();\n", // context
"c();\n", //
"d();", // context
)
);
});
// The second language server finishes.
project.update(cx, |project, cx| {
project
.update_diagnostic_entries(
server_id_2,
PathBuf::from("/test/main.js"),
None,
vec![DiagnosticEntry {
range: Unclipped(PointUtf16::new(3, 0))..Unclipped(PointUtf16::new(3, 1)),
diagnostic: Diagnostic {
message: "warning 2".to_string(),
severity: DiagnosticSeverity::WARNING,
is_primary: true,
is_disk_based: true,
group_id: 1,
..Default::default()
},
}],
cx,
)
.unwrap();
project.disk_based_diagnostics_finished(server_id_2, cx);
});
// Both language servers' diagnostics are updated.
cx.foreground().run_until_parked();
view.update(cx, |view, cx| {
assert_eq!(
editor_blocks(&view.editor, cx),
[
(0, "path header block".into()),
(2, "diagnostic header".into()),
(7, "collapsed context".into()),
(8, "diagnostic header".into()),
]
);
assert_eq!(
view.editor.update(cx, |editor, cx| editor.display_text(cx)),
concat!(
"\n", // filename
"\n", // padding
// diagnostic group 1
"\n", // primary message
"\n", // padding
"b();\n", // location
"c();\n", //
"d();\n", // context
"\n", // collapsed context
// diagnostic group 2
"\n", // primary message
"\n", // padding
"c();\n", // context
"d();\n", //
"e();", // context
)
);
});
}
fn editor_blocks(editor: &ViewHandle<Editor>, cx: &mut WindowContext) -> Vec<(u32, String)> {
editor.update(cx, |editor, cx| {
let snapshot = editor.snapshot(cx);
snapshot
.blocks_in_range(0..snapshot.max_point().row())
@@ -1198,7 +1498,7 @@ mod tests {
let name = match block {
TransformBlock::Custom(block) => block
.render(&mut BlockContext {
cx,
view_context: cx,
anchor_x: 0.,
scroll_x: 0.,
gutter_padding: 0.,

View File

@@ -1,28 +1,33 @@
use collections::HashSet;
use editor::{Editor, GoToDiagnostic};
use gpui::{
elements::*, platform::CursorStyle, serde_json, Entity, ModelHandle, MouseButton,
MutableAppContext, RenderContext, Subscription, View, ViewContext, ViewHandle, WeakViewHandle,
elements::*,
platform::{CursorStyle, MouseButton},
serde_json, AppContext, Entity, Subscription, View, ViewContext, ViewHandle, WeakViewHandle,
};
use language::Diagnostic;
use project::Project;
use lsp::LanguageServerId;
use settings::Settings;
use workspace::{item::ItemHandle, StatusItemView};
use workspace::{item::ItemHandle, StatusItemView, Workspace};
use crate::ProjectDiagnosticsEditor;
pub struct DiagnosticIndicator {
summary: project::DiagnosticSummary,
active_editor: Option<WeakViewHandle<Editor>>,
workspace: WeakViewHandle<Workspace>,
current_diagnostic: Option<Diagnostic>,
in_progress_checks: HashSet<usize>,
in_progress_checks: HashSet<LanguageServerId>,
_observe_active_editor: Option<Subscription>,
}
pub fn init(cx: &mut MutableAppContext) {
pub fn init(cx: &mut AppContext) {
cx.add_action(DiagnosticIndicator::go_to_next_diagnostic);
}
impl DiagnosticIndicator {
pub fn new(project: &ModelHandle<Project>, cx: &mut ViewContext<Self>) -> Self {
pub fn new(workspace: &Workspace, cx: &mut ViewContext<Self>) -> Self {
let project = workspace.project();
cx.subscribe(project, |this, project, event, cx| match event {
project::Event::DiskBasedDiagnosticsStarted { language_server_id } => {
this.in_progress_checks.insert(*language_server_id);
@@ -43,6 +48,7 @@ impl DiagnosticIndicator {
.language_servers_running_disk_based_diagnostics()
.collect(),
active_editor: None,
workspace: workspace.weak_handle(),
current_diagnostic: None,
_observe_active_editor: None,
}
@@ -82,14 +88,14 @@ impl View for DiagnosticIndicator {
"DiagnosticIndicator"
}
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
enum Summary {}
enum Message {}
let tooltip_style = cx.global::<Settings>().theme.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| {
MouseEventHandler::<Summary, _>::new(0, cx, |state, cx| {
let style = cx
.global::<Settings>()
.theme
@@ -100,23 +106,23 @@ impl View for DiagnosticIndicator {
let mut summary_row = Flex::row();
if self.summary.error_count > 0 {
summary_row.add_children([
summary_row.add_child(
Svg::new("icons/circle_x_mark_16.svg")
.with_color(style.icon_color_error)
.constrained()
.with_width(style.icon_width)
.aligned()
.contained()
.with_margin_right(style.icon_spacing)
.named("error-icon"),
.with_margin_right(style.icon_spacing),
);
summary_row.add_child(
Label::new(self.summary.error_count.to_string(), style.text.clone())
.aligned()
.boxed(),
]);
.aligned(),
);
}
if self.summary.warning_count > 0 {
summary_row.add_children([
summary_row.add_child(
Svg::new("icons/triangle_exclamation_16.svg")
.with_color(style.icon_color_warning)
.constrained()
@@ -128,12 +134,12 @@ impl View for DiagnosticIndicator {
style.summary_spacing
} else {
0.
})
.named("warning-icon"),
}),
);
summary_row.add_child(
Label::new(self.summary.warning_count.to_string(), style.text.clone())
.aligned()
.boxed(),
]);
.aligned(),
);
}
if self.summary.error_count == 0 && self.summary.warning_count == 0 {
@@ -143,7 +149,7 @@ impl View for DiagnosticIndicator {
.constrained()
.with_width(style.icon_width)
.aligned()
.named("ok-icon"),
.into_any_named("ok-icon"),
);
}
@@ -158,11 +164,16 @@ impl View for DiagnosticIndicator {
} else {
style.container_ok
})
.boxed()
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, |_, cx| cx.dispatch_action(crate::Deploy))
.with_tooltip::<Summary, _>(
.on_click(MouseButton::Left, |_, this, cx| {
if let Some(workspace) = this.workspace.upgrade(cx) {
workspace.update(cx, |workspace, cx| {
ProjectDiagnosticsEditor::deploy(workspace, &Default::default(), cx)
})
}
})
.with_tooltip::<Summary>(
0,
"Project Diagnostics".to_string(),
Some(Box::new(crate::Deploy)),
@@ -170,7 +181,7 @@ impl View for DiagnosticIndicator {
cx,
)
.aligned()
.boxed(),
.into_any(),
);
let style = &cx.global::<Settings>().theme.workspace.status_bar;
@@ -181,13 +192,12 @@ impl View for DiagnosticIndicator {
Label::new("Checking…", style.diagnostic_message.default.text.clone())
.aligned()
.contained()
.with_margin_left(item_spacing)
.boxed(),
.with_margin_left(item_spacing),
);
} else if let Some(diagnostic) = &self.current_diagnostic {
let message_style = style.diagnostic_message.clone();
element.add_child(
MouseEventHandler::<Message>::new(1, cx, |state, _| {
MouseEventHandler::<Message, _>::new(1, cx, |state, _| {
Label::new(
diagnostic.message.split('\n').next().unwrap().to_string(),
message_style.style_for(state, false).text.clone(),
@@ -195,17 +205,15 @@ impl View for DiagnosticIndicator {
.aligned()
.contained()
.with_margin_left(item_spacing)
.boxed()
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, |_, cx| {
cx.dispatch_action(GoToDiagnostic)
})
.boxed(),
.on_click(MouseButton::Left, |_, this, cx| {
this.go_to_next_diagnostic(&Default::default(), cx)
}),
);
}
element.named("diagnostic indicator")
element.into_any_named("diagnostic indicator")
}
fn debug_json(&self, _: &gpui::AppContext) -> serde_json::Value {

View File

@@ -4,9 +4,9 @@ use collections::HashSet;
use gpui::{
elements::{Empty, MouseEventHandler, Overlay},
geometry::{rect::RectF, vector::Vector2F},
platform::{CursorStyle, MouseButton},
scene::{MouseDown, MouseDrag},
CursorStyle, Element, ElementBox, EventContext, MouseButton, MutableAppContext, RenderContext,
View, WeakViewHandle,
AnyElement, Element, View, ViewContext, WeakViewHandle, WindowContext,
};
const DEAD_ZONE: f32 = 4.;
@@ -26,7 +26,7 @@ enum State<V: View> {
region_offset: Vector2F,
region: RectF,
payload: Rc<dyn Any + 'static>,
render: Rc<dyn Fn(Rc<dyn Any>, &mut RenderContext<V>) -> ElementBox>,
render: Rc<dyn Fn(Rc<dyn Any>, &mut ViewContext<V>) -> AnyElement<V>>,
},
Canceled,
}
@@ -111,7 +111,7 @@ impl<V: View> DragAndDrop<V> {
})
}
pub fn drag_started(event: MouseDown, cx: &mut EventContext) {
pub fn drag_started(event: MouseDown, cx: &mut WindowContext) {
cx.update_global(|this: &mut Self, _| {
this.currently_dragged = Some(State::Down {
region_offset: event.position - event.region.origin(),
@@ -123,8 +123,8 @@ impl<V: View> DragAndDrop<V> {
pub fn dragging<T: Any>(
event: MouseDrag,
payload: Rc<T>,
cx: &mut EventContext,
render: Rc<impl 'static + Fn(&T, &mut RenderContext<V>) -> ElementBox>,
cx: &mut WindowContext,
render: Rc<impl 'static + Fn(&T, &mut ViewContext<V>) -> AnyElement<V>>,
) {
let window_id = cx.window_id();
cx.update_global(|this: &mut Self, cx| {
@@ -178,7 +178,7 @@ impl<V: View> DragAndDrop<V> {
});
}
pub fn render(cx: &mut RenderContext<V>) -> Option<ElementBox> {
pub fn render(cx: &mut ViewContext<V>) -> Option<AnyElement<V>> {
enum DraggedElementHandler {}
cx.global::<Self>()
.currently_dragged
@@ -199,23 +199,25 @@ impl<V: View> DragAndDrop<V> {
return None;
}
let position = position - region_offset;
let position = (position - region_offset).round();
Some(
Overlay::new(
MouseEventHandler::<DraggedElementHandler>::new(0, cx, |_, cx| {
render(payload, cx)
})
MouseEventHandler::<DraggedElementHandler, V>::new(
0,
cx,
|_, cx| render(payload, cx),
)
.with_cursor_style(CursorStyle::Arrow)
.on_up(MouseButton::Left, |_, cx| {
cx.defer(|cx| {
.on_up(MouseButton::Left, |_, _, cx| {
cx.window_context().defer(|cx| {
cx.update_global::<Self, _, _>(|this, cx| {
this.finish_dragging(cx)
});
});
cx.propagate_event();
})
.on_up_out(MouseButton::Left, |_, cx| {
cx.defer(|cx| {
.on_up_out(MouseButton::Left, |_, _, cx| {
cx.window_context().defer(|cx| {
cx.update_global::<Self, _, _>(|this, cx| {
this.finish_dragging(cx)
});
@@ -225,43 +227,38 @@ impl<V: View> DragAndDrop<V> {
.with_hoverable(false)
.constrained()
.with_width(region.width())
.with_height(region.height())
.boxed(),
.with_height(region.height()),
)
.with_anchor_position(position)
.boxed(),
.into_any(),
)
}
State::Canceled => Some(
MouseEventHandler::<DraggedElementHandler>::new(0, cx, |_, _| {
Empty::new()
.constrained()
.with_width(0.)
.with_height(0.)
.boxed()
MouseEventHandler::<DraggedElementHandler, V>::new(0, cx, |_, _| {
Empty::new().constrained().with_width(0.).with_height(0.)
})
.on_up(MouseButton::Left, |_, cx| {
cx.defer(|cx| {
.on_up(MouseButton::Left, |_, _, cx| {
cx.window_context().defer(|cx| {
cx.update_global::<Self, _, _>(|this, _| {
this.currently_dragged = None;
});
});
})
.on_up_out(MouseButton::Left, |_, cx| {
cx.defer(|cx| {
.on_up_out(MouseButton::Left, |_, _, cx| {
cx.window_context().defer(|cx| {
cx.update_global::<Self, _, _>(|this, _| {
this.currently_dragged = None;
});
});
})
.boxed(),
.into_any(),
),
}
})
}
pub fn cancel_dragging<P: Any>(&mut self, cx: &mut MutableAppContext) {
pub fn cancel_dragging<P: Any>(&mut self, cx: &mut WindowContext) {
if let Some(State::Dragging {
payload, window_id, ..
}) = &self.currently_dragged
@@ -274,13 +271,13 @@ impl<V: View> DragAndDrop<V> {
}
}
fn finish_dragging(&mut self, cx: &mut MutableAppContext) {
fn finish_dragging(&mut self, cx: &mut WindowContext) {
if let Some(State::Dragging { window_id, .. }) = self.currently_dragged.take() {
self.notify_containers_for_window(window_id, cx);
}
}
fn notify_containers_for_window(&mut self, window_id: usize, cx: &mut MutableAppContext) {
fn notify_containers_for_window(&mut self, window_id: usize, cx: &mut WindowContext) {
self.containers.retain(|container| {
if let Some(container) = container.upgrade(cx) {
if container.window_id() == window_id {
@@ -294,35 +291,35 @@ impl<V: View> DragAndDrop<V> {
}
}
pub trait Draggable {
fn as_draggable<V: View, P: Any>(
pub trait Draggable<V: View> {
fn as_draggable<D: View, P: Any>(
self,
payload: P,
render: impl 'static + Fn(&P, &mut RenderContext<V>) -> ElementBox,
render: impl 'static + Fn(&P, &mut ViewContext<D>) -> AnyElement<D>,
) -> Self
where
Self: Sized;
}
impl<Tag> Draggable for MouseEventHandler<Tag> {
fn as_draggable<V: View, P: Any>(
impl<Tag, V: View> Draggable<V> for MouseEventHandler<Tag, V> {
fn as_draggable<D: View, P: Any>(
self,
payload: P,
render: impl 'static + Fn(&P, &mut RenderContext<V>) -> ElementBox,
render: impl 'static + Fn(&P, &mut ViewContext<D>) -> AnyElement<D>,
) -> Self
where
Self: Sized,
{
let payload = Rc::new(payload);
let render = Rc::new(render);
self.on_down(MouseButton::Left, move |e, cx| {
self.on_down(MouseButton::Left, move |e, _, cx| {
cx.propagate_event();
DragAndDrop::<V>::drag_started(e, cx);
DragAndDrop::<D>::drag_started(e, cx);
})
.on_drag(MouseButton::Left, move |e, cx| {
.on_drag(MouseButton::Left, move |e, _, cx| {
let payload = payload.clone();
let render = render.clone();
DragAndDrop::<V>::dragging(e, payload, cx, render)
DragAndDrop::<D>::dragging(e, payload, cx, render)
})
}
}

View File

@@ -11,6 +11,7 @@ doctest = false
[features]
test-support = [
"rand",
"copilot/test-support",
"text/test-support",
"language/test-support",
"gpui/test-support",
@@ -22,10 +23,11 @@ test-support = [
]
[dependencies]
drag_and_drop = { path = "../drag_and_drop" }
text = { path = "../text" }
client = { path = "../client" }
clock = { path = "../clock" }
copilot = { path = "../copilot" }
db = { path = "../db" }
drag_and_drop = { path = "../drag_and_drop" }
collections = { path = "../collections" }
context_menu = { path = "../context_menu" }
fuzzy = { path = "../fuzzy" }
@@ -38,30 +40,35 @@ rpc = { path = "../rpc" }
settings = { path = "../settings" }
snippet = { path = "../snippet" }
sum_tree = { path = "../sum_tree" }
text = { path = "../text" }
theme = { path = "../theme" }
util = { path = "../util" }
sqlez = { path = "../sqlez" }
workspace = { path = "../workspace" }
aho-corasick = "0.7"
anyhow = "1.0"
futures = "0.3"
anyhow.workspace = true
futures.workspace = true
indoc = "1.0.4"
itertools = "0.10"
lazy_static = "1.4"
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
ordered-float = "2.1.1"
parking_lot = "0.11"
postage = { version = "0.4", features = ["futures-traits"] }
rand = { version = "0.8.3", optional = true }
serde = { workspace = true }
smallvec = { version = "1.6", features = ["union"] }
smol = "1.2"
lazy_static.workspace = true
log.workspace = true
ordered-float.workspace = true
parking_lot.workspace = true
postage.workspace = true
pulldown-cmark = { version = "0.9.2", default-features = false }
rand = { workspace = true, optional = true }
serde.workspace = true
serde_derive.workspace = true
smallvec.workspace = true
smol.workspace = true
tree-sitter-rust = { version = "*", optional = true }
tree-sitter-html = { version = "*", optional = true }
tree-sitter-javascript = { version = "*", optional = true }
tree-sitter-typescript = { git = "https://github.com/tree-sitter/tree-sitter-typescript", rev = "5d20856f34315b068c41edaee2ac8a100081d259", optional = true }
[dev-dependencies]
copilot = { path = "../copilot", features = ["test-support"] }
text = { path = "../text", features = ["test-support"] }
language = { path = "../language", features = ["test-support"] }
lsp = { path = "../lsp", features = ["test-support"] }
@@ -70,10 +77,12 @@ util = { path = "../util", features = ["test-support"] }
project = { path = "../project", features = ["test-support"] }
settings = { path = "../settings", features = ["test-support"] }
workspace = { path = "../workspace", features = ["test-support"] }
ctor = "0.1"
env_logger = "0.9"
rand = "0.8"
unindent = "0.1.7"
ctor.workspace = true
env_logger.workspace = true
glob.workspace = true
rand.workspace = true
unindent.workspace = true
tree-sitter = "0.20"
tree-sitter-rust = "0.20"
tree-sitter-html = "0.19"

View File

@@ -15,12 +15,9 @@ pub struct BlinkManager {
impl BlinkManager {
pub fn new(blink_interval: Duration, cx: &mut ModelContext<Self>) -> Self {
let weak_handle = cx.weak_handle();
cx.observe_global::<Settings, _>(move |_, cx| {
if let Some(this) = weak_handle.upgrade(cx) {
// Make sure we blink the cursors if the setting is re-enabled
this.update(cx, |this, cx| this.blink_cursors(this.blink_epoch, cx));
}
cx.observe_global::<Settings, _>(move |this, cx| {
// Make sure we blink the cursors if the setting is re-enabled
this.blink_cursors(this.blink_epoch, cx)
})
.detach();

View File

@@ -1,19 +1,23 @@
mod block_map;
mod fold_map;
mod suggestion_map;
mod tab_map;
mod wrap_map;
use crate::{Anchor, AnchorRangeExt, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint};
use block_map::{BlockMap, BlockPoint};
pub use block_map::{BlockMap, BlockPoint};
use collections::{HashMap, HashSet};
use fold_map::FoldMap;
use fold_map::{FoldMap, FoldOffset};
use gpui::{
color::Color,
fonts::{FontId, HighlightStyle},
Entity, ModelContext, ModelHandle,
};
use language::{OffsetUtf16, Point, Subscription as BufferSubscription};
use settings::Settings;
use std::{any::TypeId, fmt::Debug, num::NonZeroU32, ops::Range, sync::Arc};
pub use suggestion_map::Suggestion;
use suggestion_map::SuggestionMap;
use sum_tree::{Bias, TreeMap};
use tab_map::TabMap;
use wrap_map::WrapMap;
@@ -23,6 +27,12 @@ pub use block_map::{
BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock, TransformBlock,
};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum FoldStatus {
Folded,
Foldable,
}
pub trait ToDisplayPoint {
fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint;
}
@@ -33,6 +43,7 @@ pub struct DisplayMap {
buffer: ModelHandle<MultiBuffer>,
buffer_subscription: BufferSubscription,
fold_map: FoldMap,
suggestion_map: SuggestionMap,
tab_map: TabMap,
wrap_map: ModelHandle<WrapMap>,
block_map: BlockMap,
@@ -58,6 +69,7 @@ impl DisplayMap {
let tab_size = Self::tab_size(&buffer, cx);
let (fold_map, snapshot) = FoldMap::new(buffer.read(cx).snapshot(cx));
let (suggestion_map, snapshot) = SuggestionMap::new(snapshot);
let (tab_map, snapshot) = TabMap::new(snapshot, tab_size);
let (wrap_map, snapshot) = WrapMap::new(snapshot, font_id, font_size, wrap_width, cx);
let block_map = BlockMap::new(snapshot, buffer_header_height, excerpt_header_height);
@@ -66,6 +78,7 @@ impl DisplayMap {
buffer,
buffer_subscription,
fold_map,
suggestion_map,
tab_map,
wrap_map,
block_map,
@@ -77,21 +90,25 @@ impl DisplayMap {
pub fn snapshot(&self, cx: &mut ModelContext<Self>) -> DisplaySnapshot {
let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
let edits = self.buffer_subscription.consume().into_inner();
let (folds_snapshot, edits) = self.fold_map.read(buffer_snapshot, edits);
let (fold_snapshot, edits) = self.fold_map.read(buffer_snapshot, edits);
let (suggestion_snapshot, edits) = self.suggestion_map.sync(fold_snapshot.clone(), edits);
let tab_size = Self::tab_size(&self.buffer, cx);
let (tabs_snapshot, edits) = self.tab_map.sync(folds_snapshot.clone(), edits, tab_size);
let (wraps_snapshot, edits) = self
let (tab_snapshot, edits) = self
.tab_map
.sync(suggestion_snapshot.clone(), edits, tab_size);
let (wrap_snapshot, edits) = self
.wrap_map
.update(cx, |map, cx| map.sync(tabs_snapshot.clone(), edits, cx));
let blocks_snapshot = self.block_map.read(wraps_snapshot.clone(), edits);
.update(cx, |map, cx| map.sync(tab_snapshot.clone(), edits, cx));
let block_snapshot = self.block_map.read(wrap_snapshot.clone(), edits);
DisplaySnapshot {
buffer_snapshot: self.buffer.read(cx).snapshot(cx),
folds_snapshot,
tabs_snapshot,
wraps_snapshot,
blocks_snapshot,
fold_snapshot,
suggestion_snapshot,
tab_snapshot,
wrap_snapshot,
block_snapshot,
text_highlights: self.text_highlights.clone(),
clip_at_line_ends: self.clip_at_line_ends,
}
@@ -115,12 +132,14 @@ impl DisplayMap {
let edits = self.buffer_subscription.consume().into_inner();
let tab_size = Self::tab_size(&self.buffer, cx);
let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
let (snapshot, edits) = self.suggestion_map.sync(snapshot, edits);
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
let (snapshot, edits) = self
.wrap_map
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
self.block_map.read(snapshot, edits);
let (snapshot, edits) = fold_map.fold(ranges);
let (snapshot, edits) = self.suggestion_map.sync(snapshot, edits);
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
let (snapshot, edits) = self
.wrap_map
@@ -138,12 +157,14 @@ impl DisplayMap {
let edits = self.buffer_subscription.consume().into_inner();
let tab_size = Self::tab_size(&self.buffer, cx);
let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
let (snapshot, edits) = self.suggestion_map.sync(snapshot, edits);
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
let (snapshot, edits) = self
.wrap_map
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
self.block_map.read(snapshot, edits);
let (snapshot, edits) = fold_map.unfold(ranges, inclusive);
let (snapshot, edits) = self.suggestion_map.sync(snapshot, edits);
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
let (snapshot, edits) = self
.wrap_map
@@ -160,6 +181,7 @@ impl DisplayMap {
let edits = self.buffer_subscription.consume().into_inner();
let tab_size = Self::tab_size(&self.buffer, cx);
let (snapshot, edits) = self.fold_map.read(snapshot, edits);
let (snapshot, edits) = self.suggestion_map.sync(snapshot, edits);
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
let (snapshot, edits) = self
.wrap_map
@@ -177,6 +199,7 @@ impl DisplayMap {
let edits = self.buffer_subscription.consume().into_inner();
let tab_size = Self::tab_size(&self.buffer, cx);
let (snapshot, edits) = self.fold_map.read(snapshot, edits);
let (snapshot, edits) = self.suggestion_map.sync(snapshot, edits);
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
let (snapshot, edits) = self
.wrap_map
@@ -207,11 +230,41 @@ impl DisplayMap {
self.text_highlights.remove(&Some(type_id))
}
pub fn has_suggestion(&self) -> bool {
self.suggestion_map.has_suggestion()
}
pub fn replace_suggestion<T>(
&self,
new_suggestion: Option<Suggestion<T>>,
cx: &mut ModelContext<Self>,
) -> Option<Suggestion<FoldOffset>>
where
T: ToPoint,
{
let snapshot = self.buffer.read(cx).snapshot(cx);
let edits = self.buffer_subscription.consume().into_inner();
let tab_size = Self::tab_size(&self.buffer, cx);
let (snapshot, edits) = self.fold_map.read(snapshot, edits);
let (snapshot, edits, old_suggestion) =
self.suggestion_map.replace(new_suggestion, snapshot, edits);
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
let (snapshot, edits) = self
.wrap_map
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
self.block_map.read(snapshot, edits);
old_suggestion
}
pub fn set_font(&self, font_id: FontId, font_size: f32, cx: &mut ModelContext<Self>) -> bool {
self.wrap_map
.update(cx, |map, cx| map.set_font(font_id, font_size, cx))
}
pub fn set_fold_ellipses_color(&mut self, color: Color) -> bool {
self.fold_map.set_ellipses_color(color)
}
pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut ModelContext<Self>) -> bool {
self.wrap_map
.update(cx, |map, cx| map.set_wrap_width(width, cx))
@@ -235,10 +288,11 @@ impl DisplayMap {
pub struct DisplaySnapshot {
pub buffer_snapshot: MultiBufferSnapshot,
folds_snapshot: fold_map::FoldSnapshot,
tabs_snapshot: tab_map::TabSnapshot,
wraps_snapshot: wrap_map::WrapSnapshot,
blocks_snapshot: block_map::BlockSnapshot,
fold_snapshot: fold_map::FoldSnapshot,
suggestion_snapshot: suggestion_map::SuggestionSnapshot,
tab_snapshot: tab_map::TabSnapshot,
wrap_snapshot: wrap_map::WrapSnapshot,
block_snapshot: block_map::BlockSnapshot,
text_highlights: TextHighlights,
clip_at_line_ends: bool,
}
@@ -246,7 +300,7 @@ pub struct DisplaySnapshot {
impl DisplaySnapshot {
#[cfg(test)]
pub fn fold_count(&self) -> usize {
self.folds_snapshot.fold_count()
self.fold_snapshot.fold_count()
}
pub fn is_empty(&self) -> bool {
@@ -254,7 +308,7 @@ impl DisplaySnapshot {
}
pub fn buffer_rows(&self, start_row: u32) -> DisplayBufferRows {
self.blocks_snapshot.buffer_rows(start_row)
self.block_snapshot.buffer_rows(start_row)
}
pub fn max_buffer_row(&self) -> u32 {
@@ -263,9 +317,9 @@ impl DisplaySnapshot {
pub fn prev_line_boundary(&self, mut point: Point) -> (Point, DisplayPoint) {
loop {
let mut fold_point = self.folds_snapshot.to_fold_point(point, Bias::Left);
let mut fold_point = self.fold_snapshot.to_fold_point(point, Bias::Left);
*fold_point.column_mut() = 0;
point = fold_point.to_buffer_point(&self.folds_snapshot);
point = fold_point.to_buffer_point(&self.fold_snapshot);
let mut display_point = self.point_to_display_point(point, Bias::Left);
*display_point.column_mut() = 0;
@@ -279,9 +333,9 @@ impl DisplaySnapshot {
pub fn next_line_boundary(&self, mut point: Point) -> (Point, DisplayPoint) {
loop {
let mut fold_point = self.folds_snapshot.to_fold_point(point, Bias::Right);
*fold_point.column_mut() = self.folds_snapshot.line_len(fold_point.row());
point = fold_point.to_buffer_point(&self.folds_snapshot);
let mut fold_point = self.fold_snapshot.to_fold_point(point, Bias::Right);
*fold_point.column_mut() = self.fold_snapshot.line_len(fold_point.row());
point = fold_point.to_buffer_point(&self.fold_snapshot);
let mut display_point = self.point_to_display_point(point, Bias::Right);
*display_point.column_mut() = self.line_len(display_point.row());
@@ -311,37 +365,39 @@ impl DisplaySnapshot {
}
fn point_to_display_point(&self, point: Point, bias: Bias) -> DisplayPoint {
let fold_point = self.folds_snapshot.to_fold_point(point, bias);
let tab_point = self.tabs_snapshot.to_tab_point(fold_point);
let wrap_point = self.wraps_snapshot.tab_point_to_wrap_point(tab_point);
let block_point = self.blocks_snapshot.to_block_point(wrap_point);
let fold_point = self.fold_snapshot.to_fold_point(point, bias);
let suggestion_point = self.suggestion_snapshot.to_suggestion_point(fold_point);
let tab_point = self.tab_snapshot.to_tab_point(suggestion_point);
let wrap_point = self.wrap_snapshot.tab_point_to_wrap_point(tab_point);
let block_point = self.block_snapshot.to_block_point(wrap_point);
DisplayPoint(block_point)
}
fn display_point_to_point(&self, point: DisplayPoint, bias: Bias) -> Point {
let block_point = point.0;
let wrap_point = self.blocks_snapshot.to_wrap_point(block_point);
let tab_point = self.wraps_snapshot.to_tab_point(wrap_point);
let fold_point = self.tabs_snapshot.to_fold_point(tab_point, bias).0;
fold_point.to_buffer_point(&self.folds_snapshot)
let wrap_point = self.block_snapshot.to_wrap_point(block_point);
let tab_point = self.wrap_snapshot.to_tab_point(wrap_point);
let suggestion_point = self.tab_snapshot.to_suggestion_point(tab_point, bias).0;
let fold_point = self.suggestion_snapshot.to_fold_point(suggestion_point);
fold_point.to_buffer_point(&self.fold_snapshot)
}
pub fn max_point(&self) -> DisplayPoint {
DisplayPoint(self.blocks_snapshot.max_point())
DisplayPoint(self.block_snapshot.max_point())
}
/// Returns text chunks starting at the given display row until the end of the file
pub fn text_chunks(&self, display_row: u32) -> impl Iterator<Item = &str> {
self.blocks_snapshot
.chunks(display_row..self.max_point().row() + 1, false, None)
self.block_snapshot
.chunks(display_row..self.max_point().row() + 1, false, None, None)
.map(|h| h.text)
}
/// Returns text chunks starting at the end of the given display row in reverse until the start of the file
pub fn reverse_text_chunks(&self, display_row: u32) -> impl Iterator<Item = &str> {
(0..=display_row).into_iter().rev().flat_map(|row| {
self.blocks_snapshot
.chunks(row..row + 1, false, None)
self.block_snapshot
.chunks(row..row + 1, false, None, None)
.map(|h| h.text)
.collect::<Vec<_>>()
.into_iter()
@@ -349,16 +405,25 @@ impl DisplaySnapshot {
})
}
pub fn chunks(&self, display_rows: Range<u32>, language_aware: bool) -> DisplayChunks<'_> {
self.blocks_snapshot
.chunks(display_rows, language_aware, Some(&self.text_highlights))
pub fn chunks(
&self,
display_rows: Range<u32>,
language_aware: bool,
suggestion_highlight: Option<HighlightStyle>,
) -> DisplayChunks<'_> {
self.block_snapshot.chunks(
display_rows,
language_aware,
Some(&self.text_highlights),
suggestion_highlight,
)
}
pub fn chars_at(
&self,
mut point: DisplayPoint,
) -> impl Iterator<Item = (char, DisplayPoint)> + '_ {
point = DisplayPoint(self.blocks_snapshot.clip_point(point.0, Bias::Left));
point = DisplayPoint(self.block_snapshot.clip_point(point.0, Bias::Left));
self.text_chunks(point.row())
.flat_map(str::chars)
.skip_while({
@@ -385,7 +450,7 @@ impl DisplaySnapshot {
&self,
mut point: DisplayPoint,
) -> impl Iterator<Item = (char, DisplayPoint)> + '_ {
point = DisplayPoint(self.blocks_snapshot.clip_point(point.0, Bias::Left));
point = DisplayPoint(self.block_snapshot.clip_point(point.0, Bias::Left));
self.reverse_text_chunks(point.row())
.flat_map(|chunk| chunk.chars().rev())
.skip_while({
@@ -499,7 +564,7 @@ impl DisplaySnapshot {
}
pub fn clip_point(&self, point: DisplayPoint, bias: Bias) -> DisplayPoint {
let mut clipped = self.blocks_snapshot.clip_point(point.0, bias);
let mut clipped = self.block_snapshot.clip_point(point.0, bias);
if self.clip_at_line_ends {
clipped = self.clip_at_line_end(DisplayPoint(clipped)).0
}
@@ -510,7 +575,7 @@ impl DisplaySnapshot {
let mut point = point.0;
if point.column == self.line_len(point.row) {
point.column = point.column.saturating_sub(1);
point = self.blocks_snapshot.clip_point(point, Bias::Left);
point = self.block_snapshot.clip_point(point, Bias::Left);
}
DisplayPoint(point)
}
@@ -519,37 +584,34 @@ impl DisplaySnapshot {
where
T: ToOffset,
{
self.folds_snapshot.folds_in_range(range)
self.fold_snapshot.folds_in_range(range)
}
pub fn blocks_in_range(
&self,
rows: Range<u32>,
) -> impl Iterator<Item = (u32, &TransformBlock)> {
self.blocks_snapshot.blocks_in_range(rows)
self.block_snapshot.blocks_in_range(rows)
}
pub fn intersects_fold<T: ToOffset>(&self, offset: T) -> bool {
self.folds_snapshot.intersects_fold(offset)
self.fold_snapshot.intersects_fold(offset)
}
pub fn is_line_folded(&self, display_row: u32) -> bool {
let block_point = BlockPoint(Point::new(display_row, 0));
let wrap_point = self.blocks_snapshot.to_wrap_point(block_point);
let tab_point = self.wraps_snapshot.to_tab_point(wrap_point);
self.folds_snapshot.is_line_folded(tab_point.row())
pub fn is_line_folded(&self, buffer_row: u32) -> bool {
self.fold_snapshot.is_line_folded(buffer_row)
}
pub fn is_block_line(&self, display_row: u32) -> bool {
self.blocks_snapshot.is_block_line(display_row)
self.block_snapshot.is_block_line(display_row)
}
pub fn soft_wrap_indent(&self, display_row: u32) -> Option<u32> {
let wrap_row = self
.blocks_snapshot
.block_snapshot
.to_wrap_point(BlockPoint::new(display_row, 0))
.row();
self.wraps_snapshot.soft_wrap_indent(wrap_row)
self.wrap_snapshot.soft_wrap_indent(wrap_row)
}
pub fn text(&self) -> String {
@@ -583,12 +645,92 @@ impl DisplaySnapshot {
(indent, is_blank)
}
pub fn line_indent_for_buffer_row(&self, buffer_row: u32) -> (u32, bool) {
let (buffer, range) = self
.buffer_snapshot
.buffer_line_for_row(buffer_row)
.unwrap();
let mut indent_size = 0;
let mut is_blank = false;
for c in buffer.chars_at(Point::new(range.start.row, 0)) {
if c == ' ' || c == '\t' {
indent_size += 1;
} else {
if c == '\n' {
is_blank = true;
}
break;
}
}
(indent_size, is_blank)
}
pub fn line_len(&self, row: u32) -> u32 {
self.blocks_snapshot.line_len(row)
self.block_snapshot.line_len(row)
}
pub fn longest_row(&self) -> u32 {
self.blocks_snapshot.longest_row()
self.block_snapshot.longest_row()
}
pub fn fold_for_line(self: &Self, buffer_row: u32) -> Option<FoldStatus> {
if self.is_line_folded(buffer_row) {
Some(FoldStatus::Folded)
} else if self.is_foldable(buffer_row) {
Some(FoldStatus::Foldable)
} else {
None
}
}
pub fn is_foldable(self: &Self, buffer_row: u32) -> bool {
let max_row = self.buffer_snapshot.max_buffer_row();
if buffer_row >= max_row {
return false;
}
let (indent_size, is_blank) = self.line_indent_for_buffer_row(buffer_row);
if is_blank {
return false;
}
for next_row in (buffer_row + 1)..=max_row {
let (next_indent_size, next_line_is_blank) = self.line_indent_for_buffer_row(next_row);
if next_indent_size > indent_size {
return true;
} else if !next_line_is_blank {
break;
}
}
false
}
pub fn foldable_range(self: &Self, buffer_row: u32) -> Option<Range<Point>> {
let start = Point::new(buffer_row, self.buffer_snapshot.line_len(buffer_row));
if self.is_foldable(start.row) && !self.is_line_folded(start.row) {
let (start_indent, _) = self.line_indent_for_buffer_row(buffer_row);
let max_point = self.buffer_snapshot.max_point();
let mut end = None;
for row in (buffer_row + 1)..=max_point.row {
let (indent, is_blank) = self.line_indent_for_buffer_row(row);
if !is_blank && indent <= start_indent {
let prev_row = row - 1;
end = Some(Point::new(
prev_row,
self.buffer_snapshot.line_len(prev_row),
));
break;
}
}
let end = end.unwrap_or(max_point);
Some(start..end)
} else {
None
}
}
#[cfg(any(test, feature = "test-support"))]
@@ -647,10 +789,11 @@ impl DisplayPoint {
}
pub fn to_offset(self, map: &DisplaySnapshot, bias: Bias) -> usize {
let unblocked_point = map.blocks_snapshot.to_wrap_point(self.0);
let unwrapped_point = map.wraps_snapshot.to_tab_point(unblocked_point);
let unexpanded_point = map.tabs_snapshot.to_fold_point(unwrapped_point, bias).0;
unexpanded_point.to_buffer_offset(&map.folds_snapshot)
let wrap_point = map.block_snapshot.to_wrap_point(self.0);
let tab_point = map.wrap_snapshot.to_tab_point(wrap_point);
let suggestion_point = map.tab_snapshot.to_suggestion_point(tab_point, bias).0;
let fold_point = map.suggestion_snapshot.to_fold_point(suggestion_point);
fold_point.to_buffer_offset(&map.fold_snapshot)
}
}
@@ -678,11 +821,29 @@ impl ToDisplayPoint for Anchor {
}
}
pub fn next_rows(display_row: u32, display_map: &DisplaySnapshot) -> impl Iterator<Item = u32> {
let max_row = display_map.max_point().row();
let start_row = display_row + 1;
let mut current = None;
std::iter::from_fn(move || {
if current == None {
current = Some(start_row);
} else {
current = Some(current.unwrap() + 1)
}
if current.unwrap() > max_row {
None
} else {
current
}
})
}
#[cfg(test)]
pub mod tests {
use super::*;
use crate::{movement, test::marked_display_snapshot};
use gpui::{color::Color, elements::*, test::observe, MutableAppContext};
use gpui::{color::Color, elements::*, test::observe, AppContext};
use language::{Buffer, Language, LanguageConfig, SelectionGoal};
use rand::{prelude::*, Rng};
use smol::stream::StreamExt;
@@ -703,7 +864,9 @@ pub mod tests {
let mut tab_size = rng.gen_range(1..=4);
let buffer_start_excerpt_header_height = rng.gen_range(1..=5);
let excerpt_header_height = rng.gen_range(1..=5);
let family_id = font_cache.load_family(&["Helvetica"]).unwrap();
let family_id = font_cache
.load_family(&["Helvetica"], &Default::default())
.unwrap();
let font_id = font_cache
.select_font(family_id, &Default::default())
.unwrap();
@@ -753,10 +916,10 @@ pub mod tests {
let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
log::info!("buffer text: {:?}", snapshot.buffer_snapshot.text());
log::info!("fold text: {:?}", snapshot.folds_snapshot.text());
log::info!("tab text: {:?}", snapshot.tabs_snapshot.text());
log::info!("wrap text: {:?}", snapshot.wraps_snapshot.text());
log::info!("block text: {:?}", snapshot.blocks_snapshot.text());
log::info!("fold text: {:?}", snapshot.fold_snapshot.text());
log::info!("tab text: {:?}", snapshot.tab_snapshot.text());
log::info!("wrap text: {:?}", snapshot.wrap_snapshot.text());
log::info!("block text: {:?}", snapshot.block_snapshot.text());
log::info!("display text: {:?}", snapshot.text());
for _i in 0..operations {
@@ -810,7 +973,7 @@ pub mod tests {
position,
height,
disposition,
render: Arc::new(|_| Empty::new().boxed()),
render: Arc::new(|_| Empty::new().into_any()),
}
})
.collect::<Vec<_>>();
@@ -861,10 +1024,10 @@ pub mod tests {
let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
fold_count = snapshot.fold_count();
log::info!("buffer text: {:?}", snapshot.buffer_snapshot.text());
log::info!("fold text: {:?}", snapshot.folds_snapshot.text());
log::info!("tab text: {:?}", snapshot.tabs_snapshot.text());
log::info!("wrap text: {:?}", snapshot.wraps_snapshot.text());
log::info!("block text: {:?}", snapshot.blocks_snapshot.text());
log::info!("fold text: {:?}", snapshot.fold_snapshot.text());
log::info!("tab text: {:?}", snapshot.tab_snapshot.text());
log::info!("wrap text: {:?}", snapshot.wrap_snapshot.text());
log::info!("block text: {:?}", snapshot.block_snapshot.text());
log::info!("display text: {:?}", snapshot.text());
// Line boundaries
@@ -954,13 +1117,15 @@ pub mod tests {
}
#[gpui::test(retries = 5)]
fn test_soft_wraps(cx: &mut MutableAppContext) {
fn test_soft_wraps(cx: &mut AppContext) {
cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX);
cx.foreground().forbid_parking();
let font_cache = cx.font_cache();
let family_id = font_cache.load_family(&["Helvetica"]).unwrap();
let family_id = font_cache
.load_family(&["Helvetica"], &Default::default())
.unwrap();
let font_id = font_cache
.select_font(family_id, &Default::default())
.unwrap();
@@ -1045,11 +1210,14 @@ pub mod tests {
}
#[gpui::test]
fn test_text_chunks(cx: &mut gpui::MutableAppContext) {
fn test_text_chunks(cx: &mut gpui::AppContext) {
cx.set_global(Settings::test(cx));
let text = sample_text(6, 6, 'a');
let buffer = MultiBuffer::build_simple(&text, cx);
let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
let family_id = cx
.font_cache()
.load_family(&["Helvetica"], &Default::default())
.unwrap();
let font_id = cx
.font_cache()
.select_font(family_id, &Default::default())
@@ -1132,7 +1300,9 @@ pub mod tests {
let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
let font_cache = cx.font_cache();
let family_id = font_cache.load_family(&["Helvetica"]).unwrap();
let family_id = font_cache
.load_family(&["Helvetica"], &Default::default())
.unwrap();
let font_id = font_cache
.select_font(family_id, &Default::default())
.unwrap();
@@ -1167,7 +1337,7 @@ pub mod tests {
vec![
("fn ".to_string(), None),
("out".to_string(), Some(Color::blue())),
("".to_string(), None),
("".to_string(), None),
(" fn ".to_string(), Some(Color::red())),
("inner".to_string(), Some(Color::blue())),
("() {}\n}".to_string(), Some(Color::red())),
@@ -1220,7 +1390,9 @@ pub mod tests {
let font_cache = cx.font_cache();
let family_id = font_cache.load_family(&["Courier"]).unwrap();
let family_id = font_cache
.load_family(&["Courier"], &Default::default())
.unwrap();
let font_id = font_cache
.select_font(family_id, &Default::default())
.unwrap();
@@ -1248,7 +1420,7 @@ pub mod tests {
cx.update(|cx| syntax_chunks(1..4, &map, &theme, cx)),
[
("out".to_string(), Some(Color::blue())),
("\n".to_string(), None),
("\n".to_string(), None),
(" \nfn ".to_string(), Some(Color::red())),
("i\n".to_string(), Some(Color::blue()))
]
@@ -1292,7 +1464,9 @@ pub mod tests {
let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
let font_cache = cx.font_cache();
let family_id = font_cache.load_family(&["Courier"]).unwrap();
let family_id = font_cache
.load_family(&["Courier"], &Default::default())
.unwrap();
let font_id = font_cache
.select_font(family_id, &Default::default())
.unwrap();
@@ -1335,9 +1509,9 @@ pub mod tests {
}
#[gpui::test]
fn test_clip_point(cx: &mut gpui::MutableAppContext) {
fn test_clip_point(cx: &mut gpui::AppContext) {
cx.set_global(Settings::test(cx));
fn assert(text: &str, shift_right: bool, bias: Bias, cx: &mut gpui::MutableAppContext) {
fn assert(text: &str, shift_right: bool, bias: Bias, cx: &mut gpui::AppContext) {
let (unmarked_snapshot, mut markers) = marked_display_snapshot(text, cx);
match bias {
@@ -1384,10 +1558,10 @@ pub mod tests {
}
#[gpui::test]
fn test_clip_at_line_ends(cx: &mut gpui::MutableAppContext) {
fn test_clip_at_line_ends(cx: &mut gpui::AppContext) {
cx.set_global(Settings::test(cx));
fn assert(text: &str, cx: &mut gpui::MutableAppContext) {
fn assert(text: &str, cx: &mut gpui::AppContext) {
let (mut unmarked_snapshot, markers) = marked_display_snapshot(text, cx);
unmarked_snapshot.clip_at_line_ends = true;
assert_eq!(
@@ -1403,12 +1577,14 @@ pub mod tests {
}
#[gpui::test]
fn test_tabs_with_multibyte_chars(cx: &mut gpui::MutableAppContext) {
fn test_tabs_with_multibyte_chars(cx: &mut gpui::AppContext) {
cx.set_global(Settings::test(cx));
let text = "\t\tα\nβ\t\n🏀β\t\tγ";
let buffer = MultiBuffer::build_simple(text, cx);
let font_cache = cx.font_cache();
let family_id = font_cache.load_family(&["Helvetica"]).unwrap();
let family_id = font_cache
.load_family(&["Helvetica"], &Default::default())
.unwrap();
let font_id = font_cache
.select_font(family_id, &Default::default())
.unwrap();
@@ -1462,11 +1638,13 @@ pub mod tests {
}
#[gpui::test]
fn test_max_point(cx: &mut gpui::MutableAppContext) {
fn test_max_point(cx: &mut gpui::AppContext) {
cx.set_global(Settings::test(cx));
let buffer = MultiBuffer::build_simple("aaa\n\t\tbbb", cx);
let font_cache = cx.font_cache();
let family_id = font_cache.load_family(&["Helvetica"]).unwrap();
let family_id = font_cache
.load_family(&["Helvetica"], &Default::default())
.unwrap();
let font_id = font_cache
.select_font(family_id, &Default::default())
.unwrap();
@@ -1509,7 +1687,7 @@ pub mod tests {
rows: Range<u32>,
map: &ModelHandle<DisplayMap>,
theme: &'a SyntaxTheme,
cx: &mut MutableAppContext,
cx: &mut AppContext,
) -> Vec<(String, Option<Color>)> {
chunks(rows, map, theme, cx)
.into_iter()
@@ -1521,11 +1699,11 @@ pub mod tests {
rows: Range<u32>,
map: &ModelHandle<DisplayMap>,
theme: &'a SyntaxTheme,
cx: &mut MutableAppContext,
cx: &mut AppContext,
) -> Vec<(String, Option<Color>, Option<Color>)> {
let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
let mut chunks: Vec<(String, Option<Color>, Option<Color>)> = Vec::new();
for chunk in snapshot.chunks(rows, true) {
for chunk in snapshot.chunks(rows, true, None) {
let syntax_color = chunk
.syntax_highlight_id
.and_then(|id| id.style(theme)?.color);

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