Compare commits

..

478 Commits

Author SHA1 Message Date
Max Brunsfeld
c1c67a5025 zed 0.97.6 2023-08-08 12:35:38 -07:00
Piotr Osiewicz
cbaf7cc972 Piotr/optimize search selections with a limit (#2831)
/cc @nathansobo @maxbrunsfeld 

Release Notes:
- Fixed scrollbar selections causing noticeable slowdowns with large
quantities of selections.
2023-08-08 12:34:37 -07:00
Max Brunsfeld
c24c4063e3 Make LspAdapter::process_diagnostics synchronous (#2829)
When editing rust code, the project diagnostics view sometimes fails to
update, so that you have to close the view and re-open it to see the
correct state.

This PR fixes one possible cause of that problem. There was an async
step in between *receiving* diagnostics from the language server and
updating the diagnostics, due to an async call to
`LspAdapter::process_diagnostics`. This could cause the following
sequence of events to happen:

1. Rust-analyzer sends us new diagnostics for a file `a.rs`
2. We call `process_diagnostics` with those diagnostics
3. Rust-analyzer sends us a `WorkDoneProgress` message, indicating that
the "flycheck" (aka `cargo check`) process has completed
4. We update the project diagnostics view due to this message.
5. The `process_diagnostics` call for `a.rs` completes
6. 💥 We have the new diagnostics for `a.rs`, but do not update the
project diagnostics view again.

This PR fixes this bug by simply making `process_diagnostics`
synchronous. There is no I/O or expensive computation happening in that
method. If we need to make it asynchronous in the future, we need to
introduce a queue that ensures that `publishDiagnostics` and
`workDoneProgress` messages are processed serially.

Release Notes:

- Fixed a bug where the project diagnostics view would sometimes fail to
update properly when using Rust-analyzer.
2023-08-08 12:34:28 -07:00
Joseph T. Lyons
707da9bbbf v0.97.x stable 2023-08-02 13:50:08 -04:00
Mikayla Maki
c826010008 Halve opacity on wrap guides (#2815)
Wrap guides are a little too bright as is
2023-08-02 12:52:33 -04:00
Mikayla Maki
e928e1db4e disable wrap guides in the assitant panel (#2814)
Wrap guides do not look correct in the assistant due to it's current
header styling. Disable them in that context now.

Release Notes:

- Fix a visual bug displaying when enabling wrap guides in the
assistant.
2023-08-02 12:51:34 -04:00
Joseph T. Lyons
f5b1962b0e zed 0.97.5 2023-07-28 18:13:24 -04:00
Mikayla Maki
0b13c6bc84 Make wrap guides respect scroll position (#2810)
Release Notes:

- Fixed a visual bug when scrolling with wrap guides active
2023-07-28 18:11:23 -04:00
Joseph T. Lyons
75365249cc Update release action to choose between preview and stable URL in Discord announcements
This is what ChatGPT told me, so we'll see.
2023-07-28 15:12:46 -04:00
Conrad Irwin
1c2f5162bc Don't highlight project search matches either (#2807)
@JosephTLyons this is probably worth merging alongside #2803

- vim: Fix a bug where focusing project search results unexpectedly
entered visual mode
2023-07-28 14:31:22 -04:00
Conrad Irwin
209c68c85e Fix jumping to definition in a new file (#2803)
This is broken because vim currently sets settings only on the active
editor. Fix this by correcting the range on the currently active editor.

It would be nice (at some point) to refactor how vim sets settings, but
that's for another day.

Release Notes:

- vim: Fix bug when jumping to definition in new file accidentally
entered visual mode.
2023-07-28 14:30:58 -04:00
Mikayla Maki
023a617b30 zed 0.97.4 2023-07-27 16:36:49 -07:00
Mikayla Maki
286d302273 Make mode indicator follow vim enabled state (#2802)
There was a minor visual bug introduced in
https://github.com/zed-industries/zed/pull/2801, this PR corrects it.

Release Notes:

- N/A
2023-07-27 16:32:28 -07:00
Julia
4091a2004e Avoid panic by accessing view handle by global in wrong window
View handles are window specific but this global will be doing things
in all windows, that would cause a panic when it attempted to update
a status bar mode indicator in a background window

Co-Authored-By: Mikayla Maki <mikayla@zed.dev>
2023-07-27 16:32:20 -07:00
Joseph T. Lyons
9d4a2bfb58 Publish preview releases to discord (#2800)
Release Notes:

- N/A
2023-07-27 15:26:36 -04:00
Joseph T. Lyons
33e58d47ac zed 0.97.3 2023-07-27 14:30:24 -04:00
Mikayla Maki
056282f59b Downgrade our dependency on treesitter-cpp (#2799)
Our dependency on `tree-sitter-cpp` got upgraded to an incompatible
version despite semver 'guarantees'. This pins the dependency onto the
commit of version 0.20.0

Release Notes:

- Restored language detection for C++ (preview-only)
2023-07-27 14:21:16 -04:00
Antonio Scandurra
553b9601d1 zed 0.97.2 2023-07-27 14:47:09 +02:00
Antonio Scandurra
a166f0b56a Maintain cursor stack's position correctly when ascending the tree (#2795)
This fixes a bug that could cause the cursor to incorrectly report its
start when using `slice` or `seek_forward`, and then calling `prev`. We
didn't notice this because we were not testing those three methods
together.

I suppose this could explain some of the panics we've observed because
we do use `slice`/`seek_forward` followed by `prev` calls in production.
2023-07-27 14:45:38 +02:00
Joseph T. Lyons
badc2ec0e9 zed 0.97.1 2023-07-26 18:57:44 -04:00
Mikayla Maki
9c3c719ce0 Downgrade tree sitter elm to 5.6.4 (#2794)
The tree sitter elm parser contains a c symbol which collides with other
linked symbols. This PR downgrades the tree sitter elm parser to a
version which doesn't have this problem.

Release Notes:
- Fixed crash when parsing elm files
2023-07-26 18:55:24 -04:00
Mikayla Maki
3594b5e2a8 Block extra drag events in original drag handlers (#2793)
In https://github.com/zed-industries/zed/pull/2790 I added an extra drag
event on mouse_up which signaled the end of a drag event, as mouse_up
event themselves wouldn't reliably fire if users moved their mouse too
quickly. This broke the assumptions of the terminal element. This PR
adds filters to all current on_drag handlers which removes this new
event.

Release Notes:

- Fixed a bug causing terminal links to never open (preview only)
- Fixed a bug in terminal link detection causing it to miss files with a
`-` in it
2023-07-26 18:20:50 -04:00
Joseph T. Lyons
99d0ed4a76 v0.97.x preview 2023-07-26 13:27:34 -04:00
Derek Briggs
603387ace5 icon updates (#2791)
Updated app icon and icon system updates
2023-07-26 11:23:23 -06:00
Derek Briggs
9fc1ebcb5b icon updates 2023-07-26 11:19:34 -06:00
Mikayla Maki
711073cf3c Simple cascading split (#2790)
This PR cascades the split resizing to adjacent splits, if the current
split has already hit the minimum size. This PR also adds support for
detecting the end of a drag event to GPUI, via a bool on the dispatched
drag.

Release Notes:

- Made split resizing more flexible
2023-07-26 09:49:27 -07:00
Mikayla Maki
a58c9ed7d3 fmt 2023-07-26 09:39:35 -07:00
Mikayla Maki
56704c7c5f Remove placeholders 2023-07-26 09:37:52 -07:00
Kyle Caverly
fc1844d684 Semantic search v2 (#2789)
Move semantic search from navigation modal, to project search option.
This PR is intended to be released in Preview only, and requires an
opt-in semantic_index option to enable. Without this opt-in setting
enable, the user should perceive no differences between previous project
search.

Release Notes: (Preview-only)

- Added Semantic Search as a opt-in feature within Project Search
- Show indexing feedback on indexing process within project search view
2023-07-26 10:34:58 -04:00
KCaverly
0b61c93a25 ensure semantic search is not enabled on stable 2023-07-26 10:22:33 -04:00
KCaverly
394a105639 fix warnings 2023-07-26 10:03:30 -04:00
KCaverly
0ac919f6e0 catchup with main 2023-07-26 09:50:38 -04:00
KCaverly
ca6f7d8a80 add worktree previously indexed functionality to vector db 2023-07-26 09:17:04 -04:00
Conrad Irwin
39f02c2b72 Add a mode indicator for vim (#2763)
Release Notes:

- vim: add a mode indicator
([#409](https://github.com/zed-industries/community/issues/409))

Now updated screenshots with @iamnbutler 
<img width="1043" alt="Screenshot 2023-07-25 at 11 11 57"
src="https://github.com/zed-industries/zed/assets/94272/8301479a-8b58-42d8-81a1-bc40e1e0a4df">
<img width="1043" alt="Screenshot 2023-07-25 at 11 12 00"
src="https://github.com/zed-industries/zed/assets/94272/89c3b8bd-9cbc-4fd7-ad10-dac5538ed3a3">
<img width="1043" alt="Screenshot 2023-07-25 at 11 12 12"
src="https://github.com/zed-industries/zed/assets/94272/adc87fe3-a720-4779-853b-df9443407046">
2023-07-25 20:18:23 -06:00
KCaverly
75999204ad update project search to only show semantic button visible with semantic_index enabled 2023-07-25 16:26:37 -04:00
Joseph T. Lyons
cc23360bab Add sort lines command (#2786)
This PR adds command palette actions for:

- `sort lines case sensitive`
- `sort lines case insensitive`
- `reverse lines`
- `shuffle lines`

Closes out:

- https://github.com/zed-industries/community/issues/658

and partially closing out:

- https://github.com/zed-industries/community/issues/57 (which is
currently a top-ranked issue)

There are issues with dedupe lines and I didn't try to tackle converting
variable names between different conventions. I'll likely close out 57
with a note to just upvote the remaining individual issues

Release Notes:

- added command palette actions for `sort lines case sensitive`, `sort
lines case insensitive`, `reverse lines`, and`shuffle lines`
(([#57](https://github.com/zed-industries/community/issues/57)),
([#658](https://github.com/zed-industries/community/issues/658)))
2023-07-25 15:39:45 -04:00
KCaverly
e8210b827d move visible text to just start anchor with context lines for semantic search 2023-07-25 15:24:27 -04:00
KCaverly
cdceddd2cc update semantic index tests for elixir 2023-07-25 15:20:35 -04:00
Joseph T. Lyons
4085df5146 Add tests for manipulate_lines() 2023-07-25 15:17:16 -04:00
Conrad Irwin
64b252e81a A little refactor
Co-Authored-By: Mikayla Maki <mikayla.c.maki@gmail.com>
2023-07-25 12:55:01 -06:00
Joseph T. Lyons
bf2ca57f55 Remove { and } from single-line closure 2023-07-25 14:48:11 -04:00
Mikayla Maki
c32fd57643 Add support for Elm and GLSL (#2782)
This adds tree-sitter grammars for both Elm and GLSL, with injections
for GLSL embedded within Elm. It also adds an `outline.scm` for Elm,
though limitations in the tree-sitter grammar meant that I wasn't able
to get it looking exactly how I'd have liked.

In particular, it wasn't clear how to nicely annotate functions in the
outline as being functions, or how to prevent the fields of a record
declaration from being increasingly indented.


![image](https://github.com/zed-industries/zed/assets/285821/544bc00b-3bfc-4ec7-be9d-764b9f0292ab)

![image](https://github.com/zed-industries/zed/assets/285821/74e57e95-bf87-4989-ae29-a2f625141bcf)

![image](https://github.com/zed-industries/zed/assets/285821/9e283c78-66d5-4c15-9827-1b5b446cdc37)

fixes https://github.com/zed-industries/community/issues/598

Release Notes:
- Added syntax highlighting for the Elm and GLSL languages
2023-07-25 11:34:22 -07:00
Joseph T. Lyons
93ec73da29 Fix code computing new selections
Co-Authored-By: Mikayla Maki <mikayla.c.maki@gmail.com>
2023-07-25 14:04:25 -04:00
KCaverly
97c3d97792 update semantic index tests for cpp 2023-07-25 13:30:38 -04:00
Conrad Irwin
1f65effe57 Update status bar theming
Co-Authored-By: Nate Butler <iamnbutler@gmail.com>
2023-07-25 11:06:41 -06:00
Joseph T. Lyons
299818cde0 Fix rand import and tweak callbacks
Co-Authored-By: Julia <30666851+ForLoveOfCats@users.noreply.github.com>
2023-07-25 11:44:13 -04:00
KCaverly
c86096a886 update semantic index tests for javascript 2023-07-25 10:38:37 -04:00
Joseph T. Lyons
1a84382881 WIP 2023-07-25 10:33:20 -04:00
Joseph T. Lyons
bdd0b9f387 Add open file in project finder via space (#2785)
@mikayla-maki for 👀 

[This PR added in the ability to rename a file via
`enter`](https://github.com/zed-industries/zed/pull/2784). Previously,
`enter` was used to both open a file and confirm a rename, so this PR
changes the opening of a file to use `space`, which is what VS Code
uses. It also makes a bit more sense because now `enter` is just used to
start a rename and confirm the rename, vs being used for 2 different
actions.

N/A on the release notes, as I adjusted the release note in the
previously-tagged PR.

Release Notes:

- N/A
2023-07-24 16:35:01 -04:00
Max Brunsfeld
a01d973477 More git status optimizations (#2779)
Follow-up to https://github.com/zed-industries/zed/pull/2777
Refs https://github.com/zed-industries/community/issues/1770

In this PR, I reworked the way that git statuses are retrieved. In a
huge repository like `WebKit`, the really slow part of computing a list
of git statuses is the *unstaged* portion of the diff. For the *staged*
diff, `git` can avoid comparing the contents of unchanged directories,
because the index contains hashes of every tree. But for the *unstaged*
portion, Git needs to compare every file in the worktree against the
index. In the common case, when there are no changes, it's enough to
check the `mtime` of every file (because the index stores the mtimes of
files when they are added). But this still requires an `lstat` call to
retrieve each file's metadata.

I realized that this is redundant work, because the worktree is
*already* calling `lstat` on every file, and caching their metadata. So
in this PR, I've changed the `Repository` API so that there are separate
methods for retrieving a file's *staged* and *unstaged* statuses. The
*staged* statuses are retrieved in one giant batch, like before, to
reduce our git calls (which also have an inherent cost). But the
`unstaged` statuses are retrieved one-by-one, after we load files'
mtimes. Often, all that's required is an index lookup, and an mtime
comparison.

With this optimization, it once again becomes pretty responsive to open
`WebKit` or `chromium` in Zed.

Release Notes:

- Optimized the loading of project file when working in very large git
repositories
2023-07-24 11:23:32 -07:00
Quinn Wilton
e199a6a3a1 Highlight all Elm function calls 2023-07-24 10:55:15 -07:00
Mikayla Maki
7603659479 Add MacOS standard key binding for file renames (#2784)
Release Notes:

- Added a default keybinding for using enter to rename files in the
project panel
2023-07-24 10:46:43 -07:00
Quinn Wilton
3cc88904bf Add all Elm functions to the outline, including locals 2023-07-24 10:45:46 -07:00
Quinn Wilton
62ee52a5fc Highlight qualified Elm function calls 2023-07-24 10:45:15 -07:00
Quinn Wilton
fbe0108959 Highlight Elm arrows as keywords 2023-07-24 10:44:56 -07:00
Mikayla Maki
41105136a4 Add MacOS standard key binding for file renames 2023-07-24 10:20:10 -07:00
Mikayla Maki
d95c4fdb2b Remove unbound highlight queries 2023-07-24 09:01:15 -07:00
Conrad Irwin
baa16a2fc6 Better method ordering 2023-07-24 09:57:51 -06:00
Mikayla Maki
ea74734b0a Touch up elm tree sitter integration 2023-07-24 08:52:51 -07:00
Conrad Irwin
43d94e37ec Refactor mode indicator to remove itself
One of the problems we had is that the status_bar shows a gap between
items, and we want to not add an additional gap for an invisible status
indicator.
2023-07-24 09:51:54 -06:00
Mikayla Maki
25e4bcea7f Implement cascading resize algorithm 2023-07-24 08:04:46 -07:00
Kirill Bulatov
52154f76ac Fixes a crash when SelectAllMatches action was called on no matches (#2783)
Release Notes:

- Fixes a crash when SelectAllMatches action was called on no matches
2023-07-24 15:48:45 +03:00
Kirill Bulatov
7dccb487de Fixes a crash when SelectAllMatches action was called on no matches 2023-07-24 15:42:10 +03:00
Quinn Wilton
6ad0852a70 Add outline.scm for Elm 2023-07-24 00:58:59 -07:00
Quinn Wilton
dd504f5965 Add tree-sitter-glsl 2023-07-23 22:22:11 -07:00
Quinn Wilton
a4914fcf3b Add tree-sitter-elm 2023-07-23 22:22:11 -07:00
Max Brunsfeld
8fff0b0ff8 Fix pathspec in staged_statuses
Enable non-literal matching so that directory paths match
all files contained within them.
2023-07-23 21:36:29 -07:00
Mikayla Maki
429a2fc623 Add drag end events
Fix left dragging cascade
WIP: Implement right dragging, WIP: use drag end events to set and reset state around initial flex orientation
2023-07-23 13:28:30 -07:00
Mikayla Maki
28ee05b324 WIP: cascade split resizes 2023-07-23 01:20:25 -07:00
Mikayla Maki
b4b53eb5f1 Refactor resize handle code to be amenable to cascading resizes 2023-07-22 21:48:45 -07:00
Max Brunsfeld
a3a9d024ba Fix filtering of staged statuses 2023-07-22 17:53:58 -07:00
Max Brunsfeld
b338ffe8d8 Rely on git status for any paths not matching the git index 2023-07-22 17:47:36 -07:00
Kirill Bulatov
e0915190d4 In terminal, open paths starting with ~ and focus on project panel when opening directories (#2780)
Further improves terminal navigation with cmd+click, now allowing to
open paths starting with `~` (if they are present otherwise) and
focusing project panel with highlighted entry for the directories
opened.

Release Notes:

- Further improves terminal navigation with cmd+click, now allowing to
open paths starting with `~` (if they are present otherwise) and
focusing project panel with highlighted entry for the directories
opened.
2023-07-23 00:23:19 +03:00
Kirill Bulatov
f05095a6dd Focus project panel on directory select 2023-07-23 00:12:25 +03:00
Max Brunsfeld
6c09782aa2 Optimize full file status via passing in known file mtime 2023-07-22 11:53:26 -07:00
Max Brunsfeld
51d311affd Compute unstaged git status separately, to take advantage of our cached file mtimes 2023-07-21 17:58:43 -07:00
Max Brunsfeld
ff0864026e Only fetch statuses for changed paths 2023-07-21 17:08:31 -07:00
Max Brunsfeld
05b161118c Don't call git status when ignored files change 2023-07-21 17:05:42 -07:00
Kirill Bulatov
dcaf8a9af8 Open paths starting with ~ from terminal click 2023-07-22 01:34:25 +03:00
Conrad Irwin
d14a484a20 Add support for adding/removing status items 2023-07-21 16:06:14 -06:00
Conrad Irwin
458916409c Add a mode indicator for vim
This is the second most common remaining complaint (after :w not
working).

Fixes: zed-industries/community#409
2023-07-21 16:06:14 -06:00
Max Brunsfeld
7788eabec0 Avoid performance bottlenecks from git status calls during worktree scanning (#2777)
Closes
https://linear.app/zed-industries/issue/Z-2689/huge-slowdown-when-working-in-large-git-repositories-like-webkit
Closes https://github.com/zed-industries/community/issues/1770

In large git repositories (like Webkit), `git status` can be very slow.
And our current approach of retrieving git statuses (one by one as we
load paths), causes catastrophic slowdowns in these repos. This PR
further optimizes our retrieval of git statuses (started in
https://github.com/zed-industries/zed/pull/2728), so that when scanning
a directory, we only load git statuses once, in a single batch, at the
beginning of the scan.

There is still an initial lag when opening `WebKit` in Zed, while the
initial git status runs. But once this call completes, everything is
fast. Let's come back to this problem later.

For now, this makes Zed's directory scanning massively more efficient,
even in the case of normal-sized repos like `zed`. The git status code
was a huge percentage of zed's CPU usage when launching. Here is that
code, highlighted in a flamegraph before and after this change:

Before:

![before](https://github.com/zed-industries/zed/assets/326587/627012f2-6131-44ac-95c2-ea4a4531cb24)

After:

![after](https://github.com/zed-industries/zed/assets/326587/a11a3e1b-e925-4bff-a421-ea71cb4de85d)


Release Notes:

- Fixed a bug where project paths took a very long time to load when
working in large git repositories
([#1770](https://github.com/zed-industries/community/issues/1770))
2023-07-21 14:46:53 -07:00
Kirill Bulatov
bd9118f673 Do not scroll when selecting all (#2778)
In big buffers, when I press `cmd-a`, the view gets scrolled to the very
bottom.
Usually it's now that I want, I can scroll to bottom with `cmd-down`
separately, and selecting all text is used for copy-pasting it
somewhere, no need to scroll anywhere for that — I can get back to the
same place later.

Release Notes:

- Removed the scroll to the end of the editor after `editor::SelectAll`
action
2023-07-22 00:25:37 +03:00
Kirill Bulatov
c538504b9c Do not scroll when selecting all 2023-07-22 00:17:02 +03:00
Max Brunsfeld
4bd415f2b6 Retrieve git statuses in one batch when scanning dirs 2023-07-21 13:50:54 -07:00
Kirill Bulatov
25ea07cd41 When renaming in project panel, select file names without extensions (#2776)
Closes
https://github.com/zed-industries/community/issues/1789#issuecomment-1646061712

<img width="196" alt="Screenshot 2023-07-21 at 23 23 47"
src="https://github.com/zed-industries/zed/assets/2690773/f5c7025b-6dc8-4f0c-81e5-3cc98a3a9c8b">
<img width="197" alt="Screenshot 2023-07-21 at 23 23 52"
src="https://github.com/zed-industries/zed/assets/2690773/596f8ab0-15e0-4285-be34-ce4c276b686f">

When renaming in project panel, select file names without extensions.

Release Notes:

- Improved project panel rename by selecting file names without
extensions
2023-07-21 23:30:55 +03:00
Kirill Bulatov
33b215a288 Add search in directory action in the project panel (#2774)
Part of https://github.com/zed-industries/zed/issues/1153
Closes https://github.com/zed-industries/community/issues/1326

<img width="432" alt="image"
src="https://github.com/zed-industries/zed/assets/2690773/a50ee073-9d2e-4e5c-ae5e-23312693c540">

Adds an `project_panel::NewSearchInDirectory` action ("alt-shift-f"
default) in the project editor context to open a new project search in
the selected directory.

Release Notes:

- Adds an action to open project search in the project panel's directory
2023-07-21 23:25:45 +03:00
Julia
f2c9738a69 Put our downloaded copy of Node in the env for every NPM action (#2775)
Intelephense (PHP language server) has a dependency on `protobufjs`
which invokes `node` in the `postinstall` script and if the user did not
have a system Node runtime installed that would fail. Have this use our
downloaded installation too

Fixes
https://linear.app/zed-industries/issue/Z-2687/php-language-server-failed

Release Notes:
- Fixed PHP language server installation on systems without a system
Node installation.
2023-07-21 16:24:05 -04:00
Kirill Bulatov
804da68af7 When renaming in project panel, select file names without extensions 2023-07-21 23:22:22 +03:00
Julia
2d8159998d Put our downloaded copy of Node in the env for every NPM action
Intelephense (PHP language server) has a dependency on `protobufjs`
which invokes `node` in the `postinstall` script and if the user did
not have a system Node runtime installed that would fail. Have this
use our downloaded installation too
2023-07-21 16:13:00 -04:00
Kirill Bulatov
595bc16749 Add search in directory action in the project panel 2023-07-21 22:47:57 +03:00
Julia
e002d9efb0 Avoid panic from assuming a vim operator exists on the operator stack (#2773)
Fixes
https://linear.app/zed-industries/issue/Z-338/operator-popped-when-no-operator-was-on-the-stack-this-likely-means

Release Notes:
- Fixed a panic that could occur when invoking a Vim object without an
operator.
2023-07-21 14:47:38 -04:00
Julia
243a1a854e Avoid panic from assuming a vim operator exists on the operator stack 2023-07-21 14:25:30 -04:00
Conrad Irwin
56c657fe79 Vim shortcuts (#2760)
Refactors some of the vim bindings to make the vim.json file less
obtuse.

Release Notes:

- vim: add `;` and `,` to repeat last `{f,F,t,T}`
- vim: add zed-specific shortcuts for common IDE actions:
- - `g A` to find all references
- - `g .` to open the code actions menu.
- - `c d` for rename
2023-07-21 10:31:18 -06:00
Conrad Irwin
4772e4ccee vim: add , and ; 2023-07-21 09:50:22 -06:00
Conrad Irwin
a50d30bf8e Quality of life shortcuts for code actions 2023-07-21 09:47:15 -06:00
Conrad Irwin
8ba69c15d1 refactor: Remove G/Z Namespace support
This previously enabled things like `d g g` to work, but we can
fix that instead by not clearing out pending vim state on change.

Either way, it is unnecessary and causes some user-confusion
(zed-industries/community#176), so remove this code for now; and use
comments to organize the file a bit instead.
2023-07-21 09:47:14 -06:00
Conrad Irwin
bf2583414b Fix shift-enter in search (#2772)
Fixes shift-enter to go to previous result.

Release Notes:

- To type a newline in search use `ctrl-enter` (or `ctrl-shift-enter`
for a newline below).
2023-07-21 09:23:04 -06:00
Conrad Irwin
807279208d Fix shift-enter in search
If you want to type a newline in an auto_height editor, ctrl and
ctrl-shift are your friends.
2023-07-21 09:10:12 -06:00
Joseph T. Lyons
5f89de0b80 Add key binding to close all docks (#2769)
Fixes:
https://linear.app/zed-industries/issue/Z-2680/add-a-close-all-docks-action

I frequently get stuck in this state:

<img width="1608" alt="SCR-20230721-dgvs"
src="https://github.com/zed-industries/zed/assets/19867440/13257e6d-f75a-4d1c-9718-153499e90c60">

I could zoom, but I dont want to in this case, I just want to close
everything, to get back to a truly decluttered state. Running 3 toggle
commands is cumbersome. I'd like to be able to close all docks with one
action.

I added an action with the key binding `alt-cmd-y` (similar
to`alt-cmd-t`, which is used to close all tabs). My original choice was
`alt-cmd-d` (`d` for dock), but that is the default macOS key binding to
hide the system dock.


Release Notes:

- Added a `workspace: close all docks` action (deployed via
`alt-cmd-y`).
2023-07-21 11:08:43 -04:00
Kirill Bulatov
35400d5797 Do not highlight fake URLs in terminal (#2770)
Closes https://github.com/zed-industries/community/issues/1794
See also https://github.com/alacritty/alacritty/pull/7101

Release Notes:

- Fixed terminal incorrectly highlighting certain strings as URLs
2023-07-21 11:57:29 +03:00
Kirill Bulatov
cd3620692b Do not highlight fake URLs in terminal 2023-07-21 11:28:56 +03:00
Joseph T. Lyons
d98fcc4402 Add key binding to close all docks 2023-07-21 02:44:44 -04:00
Conrad Irwin
57b6e25278 Fix enter in search (#2768)
Fixes a regression in non-vim search caused by my changes to vim search.

Release Notes:

- N/A
2023-07-20 20:53:31 -06:00
Conrad Irwin
7337910034 Fix enter in search 2023-07-20 20:48:36 -06:00
Mikayla Maki
76188c9508 Add wrap guides (#2767)
fixes https://github.com/zed-industries/community/issues/48

Release notes
- Added wrap guides and two associated language settings:
`"show_wrap_guides": bool` and `"wrap_guides": [..]`. The first controls
whether wrap guides are shown when `"soft_wrap":
"preferred_line_length"` is enabled and the second allows Zed to show
additional wrap guides at whichever column index you prefer.

Here's a screenshot of Zed with wrap guides at 60 and 90, and soft wrap
active with a preferred_line_length of 80:

<img width="956" alt="Screenshot 2023-07-20 at 4 42 11 PM"
src="https://github.com/zed-industries/zed/assets/2280405/48f36be1-3bdc-48eb-bfca-e61fcfd6dbc2">
2023-07-20 17:15:06 -07:00
Mikayla Maki
05a8409363 bump the brightness of the active wrap guide 2023-07-20 16:45:41 -07:00
Mikayla Maki
a9bfe97361 Add wrap guides and associated settings 2023-07-20 16:39:13 -07:00
Max Brunsfeld
81b05f2a08 Optimize glob filtering of semantic search
Co-authored-by: Kyle <kyle@zed.dev>
2023-07-20 14:23:11 -07:00
Derek Briggs
4557adf693 Icon adjustments (#2766)
Icon tweaks
2023-07-20 15:06:50 -06:00
Derek Briggs
1d1da74d72 Adjustment 2023-07-20 15:05:26 -06:00
Derek Briggs
0769458ae4 Detail adjustments 2023-07-20 15:04:23 -06:00
Mikayla Maki
a85af79892 Folder icons (#2764)
- Updates icons and adds more
- Adds ability to choose folders or chevrons in user settings
- Adds ability to set indent size in user settings
2023-07-20 13:59:21 -07:00
Mikayla Maki
6b95ac9b26 fmt 2023-07-20 13:45:19 -07:00
Joseph T. Lyons
719c56734a Reuse previously-obtained call object 2023-07-20 16:21:21 -04:00
Joseph T. Lyons
6095525b56 Add microphone toggle events (#2765)
Release Notes:

- N/A
2023-07-20 16:10:20 -04:00
Joseph T. Lyons
429daf5f8c Add microphone events to calls 2023-07-20 16:00:11 -04:00
Joseph T. Lyons
7d3d54652b Remove unused method 2023-07-20 15:54:26 -04:00
Derek Briggs
1242b5b4a2 Solid tab on folder icon 2023-07-20 13:13:44 -06:00
Julia
0b6155609d In macOS platform layer map a ctrl-click to a right click (#2755)
Maps a ctrl left down event into a ctrl-less right down and then up pair
and filters out ctrl left up. Hopefully this ensures that mouse down/up
events remain balanced and somewhat matching.

Release Notes:
- Added the ability to ctrl-click in place of right click to summon
context menus
([#1150](https://github.com/zed-industries/community/issues/1150)).
2023-07-20 14:54:55 -04:00
Derek Briggs
abb145da70 add indent size to project panel settings 2023-07-20 12:30:35 -06:00
Derek Briggs
95947f6d3a icon adjustment 2023-07-20 12:29:57 -06:00
Derek Briggs
c56d62fd84 gitmodules to git icon 2023-07-20 12:29:50 -06:00
Derek Briggs
0e068a644f organize settings 2023-07-20 12:23:47 -06:00
Derek Briggs
13ae1249f5 Allow for folders or chevrons 2023-07-20 12:23:28 -06:00
Nate Butler
2f4e5b7e0e Add the local and declare keywords to bash syntax highlighting (#2761)
Release Notes:

- Improved Bash / Shell Script syntax highlighting
2023-07-20 14:17:29 -04:00
Derek Briggs
a7695c47bf Update default settings 2023-07-20 12:03:07 -06:00
Derek Briggs
8f0b24b264 Add moar icons 2023-07-20 12:01:41 -06:00
Mikayla Maki
0e9cad4935 Add a double click to reset resized splits (#2762)
fixes https://github.com/zed-industries/community/issues/1791

Release Notes:

- Double clicking on split resize handles now resets the split's
dimensions
2023-07-20 10:55:19 -07:00
KCaverly
e02d6bc0d4 add glob filtering functionality to semantic search 2023-07-20 13:46:27 -04:00
Mikayla Maki
d84d663ac3 fmt 2023-07-20 10:36:23 -07:00
Mikayla Maki
4d1dbb8aa3 Add a double click to reset resized splits 2023-07-20 10:33:28 -07:00
Nate Butler
5d22a300c3 Add the local and declare keywords to bash syntax highlighting 2023-07-20 13:18:15 -04:00
Conrad Irwin
372f66c88a Add workspace::ActivatePaneInDirection (#2757)
This change adds support for choosing a pane based on direction; and
adds default keybindings (`cmd+k cmd+{left,right,up,down}`) and vim
keybindings.

Release Notes:

- Add support for navigating to the next pane in a given direction using
`cmd+k cmd-{up,down,left,right}`
([#476](https://github.com/zed-industries/community/issues/476),
[#478](https://github.com/zed-industries/community/issues/478))
- Vim: adds support for many window related shortcuts: `ctrl-w
{h,j,k,l,up,down,left,right,w,W,p}` for navigating around panes, `ctrl-w
{q,c}` for closing panes and `ctrl-w {v,s}` for splitting panes.
2023-07-20 11:17:13 -06:00
Conrad Irwin
0e984e1e69 Ignore off-screen cursors 2023-07-20 11:11:47 -06:00
Conrad Irwin
464cc2e71a Assertions for assumptions 2023-07-20 11:11:37 -06:00
Conrad Irwin
d6a463afb8 Better calculation of pane distance 2023-07-20 11:06:16 -06:00
Derek Briggs
f051e66231 code icon adjustment 2023-07-20 10:15:20 -06:00
Derek Briggs
a90b151d52 Updated icons with additions 2023-07-20 10:07:32 -06:00
Kirill Bulatov
54378a5f57 Keep basic line height for single line editors (#2759) 2023-07-20 17:37:41 +03:00
Kirill Bulatov
0237276557 Fully revert the line height change 2023-07-20 15:39:22 +03:00
Kirill Bulatov
0e6048a85d Keep basic line height for single line editors 2023-07-20 13:42:11 +03:00
Kirill Bulatov
257dd57fe4 Properly display keybindings in context menus (#2758)
Fixes https://github.com/zed-industries/community/issues/1751


![image](https://github.com/zed-industries/zed/assets/2690773/cbf29eca-0fca-4aff-be50-810eb80fdcb5)

![image](https://github.com/zed-industries/zed/assets/2690773/330054eb-ba92-4c8f-862f-06f276cc262b)


Release Notes:

- Fixed context menu keybindings not updating with custom keybinding
values
2023-07-20 12:25:41 +03:00
Kirill Bulatov
a5e63fbf77 Properly display keybindings in context menus 2023-07-20 12:18:04 +03:00
Conrad Irwin
15dc8b43c4 Default keybindings for activating pane by direction
Breaking change: previously cmd-k cmd-{left,right} moved to the
{previous,next} pane; now they will move in the specified direction.
2023-07-19 18:33:08 -06:00
Conrad Irwin
2762f9b1c6 vim: Add support for ctrl-w commands
Primarily {h,j,k,l,left,right,up,down} for moving to a pane by
direction; but also {w,W,p} for going forward/back, and {v,s} for
splitting a pane vertically/horizontally, and {c,q} to close a pane.

There are a large number of ctrl-w commands that are not supported, and
which fall into three buckets:

* switch this pane with that one (VScode also has this, and it's a
  requested feature)
* move to top/bottom/leftmost/rightmost
* counts on any of these
* jump to "definition/file-under-cursor/etc.etc." in a new pane.
2023-07-19 18:29:13 -06:00
Conrad Irwin
e1379f0ef0 Add support for activating a pane by direction
Contributes: zed-industries/community#476
Contributes: zed-industries/community#478
2023-07-19 18:29:11 -06:00
Mikayla Maki
6f1dcb4e94 Fix buffer_line_height bugs (#2756)
Release Notes:

- Bug fix: Raise minimum line height to 1.1
- Bug fix: Disable buffer_line_height setting in non-buffer UI
2023-07-19 17:11:45 -07:00
Mikayla Maki
cb97f5a69c fmt 2023-07-19 16:56:49 -07:00
Mikayla Maki
aa67413abc Raise minimum line height to 1.1
Disable buffer_line_height setting in non-buffer editors
2023-07-19 16:55:28 -07:00
Conrad Irwin
b13e86aba6 Make tab non-functional in vim mode (#2753)
Make tab do nothing (a surprisingly common vim request).

- Fixes
([#988](https://github.com/zed-industries/community/issues/988)).
- Fixes
([#897](https://github.com/zed-industries/community/issues/897)).
2023-07-19 16:26:32 -06:00
KCaverly
efe973ebe2 add embedding query for json with nested arrays and strings
Co-authored-by: maxbrunsfeld <max@zed.dev>
2023-07-19 16:52:44 -04:00
Joseph T. Lyons
7e904183bf Fix return type in watch_file_types() 2023-07-19 16:24:29 -04:00
KCaverly
9809ec3d70 update treesitter parsing to accomodate for collapsed nested functions
Co-authored-by: maxbrunsfeld <max@zed.dev>
2023-07-19 15:47:05 -04:00
Julia
2e3aa703d9 In macOS platform layer map a ctrl-click to a right click 2023-07-19 15:43:45 -04:00
Mikayla Maki
491b3d5515 Mute mics by default (#2754)
This adds a setting to mute mics by default.

fixes https://github.com/zed-industries/community/issues/1769

Release notes:

- Fixed a bug with gutter spacing on files that end on a new significant
digit
- Added a setting for muting on join, and set it to true by default.
2023-07-19 12:42:30 -07:00
Mikayla Maki
1e4bddd276 fmt 2023-07-19 12:34:56 -07:00
Mikayla Maki
5ceb258b3e Mute mics by default
Fix bug when file ends in line with 1 more digit displayed than previous lines
Remove stale UI elements from voice call development
2023-07-19 12:34:24 -07:00
Joseph T. Lyons
34488ca863 v0.97.x dev 2023-07-19 15:33:58 -04:00
Joseph T. Lyons
64d134a0dc Update Cargo.lock 2023-07-19 15:32:41 -04:00
Mikayla Maki
07dc82409b File icons (#2719)
This PR adds the next most requested editor feature.

TODO:
- [x] Figure out styles and icons for supported file types with

fixes https://github.com/zed-industries/community/issues/206

Release Notes:

- Added file icons
2023-07-19 11:17:01 -07:00
Mikayla Maki
9c9ce15afc Add a few more spare associations 2023-07-19 11:14:31 -07:00
Mikayla Maki
e3f9a01f6b fmt 2023-07-19 11:10:31 -07:00
Derek Briggs
f4413b0969 Fix files that don’t have a prefix 2023-07-19 11:10:30 -07:00
Derek Briggs
c754c1e9e2 Update icons to new zed file icon set 2023-07-19 11:10:30 -07:00
Mikayla Maki
aacc4bb8b0 fmt 2023-07-19 11:10:30 -07:00
Mikayla Maki
8c855680e7 Make file types live reload 2023-07-19 11:10:30 -07:00
Mikayla Maki
96ef6ab326 Add willow license 2023-07-19 11:10:30 -07:00
Mikayla Maki
929a9f97b2 Fix tests 2023-07-19 11:10:30 -07:00
Mikayla Maki
fd72f4526d Added file suffix and icon associations data 2023-07-19 11:10:30 -07:00
Mikayla Maki
d023189bda Add settings 2023-07-19 11:10:30 -07:00
Mikayla Maki
d26f76ba90 Add suffix based file icons 2023-07-19 11:10:30 -07:00
Conrad Irwin
dd8863d0de Make tab non-functional in vim mode
Fixes: zed-industries/community#988
Fixes: zed-industries/community#897
2023-07-19 10:25:17 -06:00
Conrad Irwin
843e74689d Vim search (#2657)
This PR makes searching in vim mode significantly more like vim.

I re-used search to implement "go to next instance of word under cursor"
as this is how it works in vim (for integration with other
search-related keyboard shortcuts) and to avoid having to rewrite all
the logic to be vim-specific; but that did mean I had to make some
changes to the way search works (in particular to allow different
searches to run with specific options).

Release Notes:
- vim: `<enter>` in search now puts you back in normal mode
([#1583](https://github.com/zed-industries/community/issues/1583))
- vim: `?` now works to search backwards.
- vim: jumping to definitions or search results keeps you in normal mode
([#1284](https://github.com/zed-industries/community/issues/1284))
([#1514](https://github.com/zed-industries/community/issues/1514))
- vim: `n`/`N` are now supported to jump to next/previous match after a
search
([#1583](https://github.com/zed-industries/community/issues/1583))
- vim: `*`/`#`/`g*`/`g#` are now supported to jump to the next/previous
occurrence of the word under the cursor.
- vim: `gD` now jumps to type definition
2023-07-19 10:15:23 -06:00
Conrad Irwin
98b8008bcc Merge branch 'main' into vim-search 2023-07-19 09:48:25 -06:00
Kirill Bulatov
c528880155 Clean up stale conflicting hints (#2751)
Closes
https://linear.app/zed-industries/issue/Z-2618/thread-main-panicked-at-excerpt-not-found-crateseditorsrcmulti
Closes
https://linear.app/zed-industries/issue/Z-2616/thread-main-panicked-at-excerpt-not-found-crateseditorsrcmulti

Fixes inlay hints cache not removing stale hints on invalidating
refreshes.

Release Notes:

- Fixes inlay hint panics after visible kinds settings update
2023-07-19 15:38:27 +03:00
Kirill Bulatov
3058a96dee Clean up stale conflicting hints 2023-07-19 15:29:00 +03:00
Kirill Bulatov
c5e47f27f5 Rework terminal highlight mechanism (#2743)
<img width="807" alt="image"
src="https://github.com/zed-industries/zed/assets/2690773/ef3bfeef-28f5-458f-abe6-7c19bf820106">

Closes https://github.com/zed-industries/community/issues/10
Closes https://github.com/zed-industries/community/issues/560

Initial version of improved terminal highlights and "open link"
functionality: drops old behavior where URLs were highlighted on hover.
Now, Cmd + hover is needed to highlight the links and click opens both
URLs and files that exist (either abs paths, or anything relative to the
project workspace worktree roots).
Only paths eligible for opening are highlighted.

Release Notes:

- Improved terminal highlights and selections: Cmd+Click opens local
files and links
2023-07-19 09:05:48 +03:00
KCaverly
0e071919a0 parellelize embedding api calls 2023-07-18 16:09:44 -04:00
Kirill Bulatov
33921183dc Avoid extra blinking on mouse moves 2023-07-18 22:59:41 +03:00
Kirill Bulatov
6ed7820f7c Consider all terminal when searching for words 2023-07-18 22:59:41 +03:00
Kirill Bulatov
10db05f87f Rework terminal highlight event flow 2023-07-18 22:59:41 +03:00
Kirill Bulatov
6f7a6e57fc Avoid excessive blinking on cmd-hover 2023-07-18 22:59:41 +03:00
Kirill Bulatov
94358ffb16 Use lines and columns from the file url strings 2023-07-18 22:59:41 +03:00
Kirill Bulatov
82a9d53c8a Only highlight the openable things 2023-07-18 22:59:41 +03:00
Kirill Bulatov
6349d90cac Properly open project directories 2023-07-18 22:59:41 +03:00
Kirill Bulatov
6123c67de9 Detect and open URLs properly 2023-07-18 22:59:41 +03:00
Kirill Bulatov
23f25562b5 Map initial approach to string opening 2023-07-18 22:59:41 +03:00
Kirill Bulatov
f52722b6a4 Properly handle Cmd press for terminal highlights 2023-07-18 22:59:41 +03:00
Kirill Bulatov
75d900704e Refactor terminal highlights and open mechanisms
Co-authored-by: Mikayla <mikayla@zed.dev>
2023-07-18 22:59:41 +03:00
Kirill Bulatov
91ba80ae98 Ignore empty hover contents, trim final hover label text (#2747)
Removes empty hovers from appearing:

![image](https://github.com/zed-industries/zed/assets/2690773/b8a8af17-e20f-4d87-8782-465dfbf9b561)

And trims final hover label to ensure no trailing whitespaces are
present:

![image](https://github.com/zed-industries/zed/assets/2690773/24aeb0f0-d4f0-4e2b-9265-53694bfec437)


Release Notes:

- Tidies up hover elements by trimming them and removing the empty ones
2023-07-18 22:59:24 +03:00
Kirill Bulatov
9aeb970f09 Ignore empty hover contents, trim final hover label text 2023-07-18 22:53:44 +03:00
Max Brunsfeld
342dbc6945 Fix rendering of project search while semantic index is indexing or running
Co-authored-by: Kyle <kyle@zed.dev>
2023-07-18 12:01:42 -07:00
Max Brunsfeld
8d0614ce74 Populate project search results multi-buffer from semantic search
Co-authored-by: Kyle <kyle@zed.dev>
2023-07-18 11:44:58 -07:00
Joseph T. Lyons
7cb5326ba0 Fix ZED_SERVER_URL port number
This change accidentally slipped into https://github.com/zed-industries/zed/pull/2746
2023-07-18 12:43:27 -04:00
Joseph T. Lyons
e73f394604 Add is_staff to events (#2746)
Release Notes:

- N/A
2023-07-18 12:41:24 -04:00
Joseph T. Lyons
018eb06091 Add is_staff to events 2023-07-18 12:32:53 -04:00
Nate Butler
b00703a149 Add syntax highlighting for Bash, Shell Scripts (#2722)
Release Notes:

- Added syntax highlighting for Bash, Shell Scripts
2023-07-18 12:21:52 -04:00
Nate Butler
bf2dcd4582 Update cargo.toml 2023-07-18 12:15:03 -04:00
Nate Butler
fab26267db Merge branch 'main' into nate/add-bash-highlighting 2023-07-18 12:08:53 -04:00
KCaverly
80ef92a3e1 fix db schema update process to ensure all tables are dropped 2023-07-18 11:14:13 -04:00
KCaverly
ed1b1a5ccd update logging for open ai embedding and remove redundant truncation 2023-07-18 11:00:21 -04:00
KCaverly
b9fdfd60f0 catch up with main 2023-07-18 10:26:28 -04:00
Julia
192f747bd1 Detect Node broken-ness initially (#2745)
This will help cases where Node is broken causing Copilot to fail to
start but because it doesn't install via NPM we would not have caught it
prior.

Release Notes:
- Improved detection of broken Node installation impacting Copilot
([#1551](https://github.com/zed-industries/community/issues/1551)).
2023-07-18 10:20:47 -04:00
Julia
aee008440b Detect Node broken-ness initially
This will help cases where Node is broken causing Copilot to fail to
start but because it doesn't install via NPM we would not have caught
it prior.

Co-Authored-By: Antonio Scandurra <me@as-cii.com>
2023-07-18 10:02:14 -04:00
Piotr Osiewicz
137734cfcf Piotr/z 2588 php (#2721)
Release Notes:

- Added syntax highlighting & Intelephense LSP support for PHP language.
([#46](https://github.com/zed-industries/community/issues/406)).
2023-07-18 14:57:40 +02:00
Kirill Bulatov
009cf48b26 Slightly tidy up vector_db code (#2744)
Code snippet
```rust
fn main() {
    //√√√√√√√√√√√√√√√√√√√√√√√√√√√√√√√√√√√√√√√√√√√√√√√√√√√√
}
```

has length of 191, but consists of 87 chars, and the debug code with
`.truncate(100)` panicked.
Fixed that issue, cc @KCaverly 


Release Notes:

- N/A
2023-07-18 14:22:34 +03:00
Kirill Bulatov
a884bd77e1 Slightly tidy up vector_db code
Avoid panicking when truncating code with special chars
2023-07-18 14:06:57 +03:00
Piotr Osiewicz
fa529d9590 Remove redundant debug impl 2023-07-18 12:21:00 +02:00
Piotr Osiewicz
7fde3614fe Remove leftover comment 2023-07-18 12:19:35 +02:00
Max Brunsfeld
afc4c10ec1 Start work on exposing semantic search via project search view
Co-authored-by: Kyle <kyle@zed.dev>
2023-07-17 18:10:51 -07:00
KCaverly
d83c4ffb07 remove debug logging for enabled settings 2023-07-17 17:09:51 -04:00
Conrad Irwin
96abba2b7d vim: Allow ctrl+[ as an alias for escape (#2741)
Also remove unneeded mappings in `g` and `z` modes

Release Notes:

- Adds `ctrl+[` as an alias for escape
([#538](https://github.com/zed-industries/community/issues/538)).
2023-07-17 15:07:53 -06:00
KCaverly
8b42f5b1b3 rename vector_store crate to semantic_index 2023-07-17 17:06:10 -04:00
Conrad Irwin
9e44de90af Allow ctrl+[ as an alias for escape
Also remove unneeded mappings in `g` and `z` modes

Fixes: zed-industries/community#358
2023-07-17 14:59:08 -06:00
KCaverly
e630ff38c4 add embedding treesitter queries for elixir 2023-07-17 16:29:25 -04:00
Joseph T. Lyons
9f650dfa52 Prevent multiple submissions of the same feedback text (#2740)
Fixes:
https://linear.app/zed-industries/issue/Z-2416/improvements-to-feedback-submission

We get a lot of duplicate messages through our in-app feedback. My best
guess is that because we do not tell the user we are doing anything, and
because submission takes awhile, users are hitting the submission button
mutliple times. This PR blocks the submission code, once an initial
submission is sent. If the original submission fails, we unblock the
submission code. The submit button is disabled and enabled accordingly
as well.

Release Notes:

- N/A
2023-07-17 16:15:49 -04:00
Mikayla Maki
1a8bfdfa21 feat(workspace): add option for moving the tab close button to the left (#2739)
Fixes https://github.com/zed-industries/community/issues/1760

Release Notes:

- Add option for chosing where the close button should be displayed on
editor tabs
2023-07-17 13:10:42 -07:00
Joseph T. Lyons
ede86a686c Prevent multiple submissions of the same feedback text
Co-Authored-By: Julia <30666851+ForLoveOfCats@users.noreply.github.com>
2023-07-17 16:10:34 -04:00
Alex Viscreanu
4efcf492ee feat(workspace): add option for moving the tab close button to the left 2023-07-17 21:17:28 +02:00
Mikayla Maki
04625fe376 feat(workspace): show git status on editor tabs (#2736)
Fixes https://github.com/zed-industries/community/issues/1674

Release Notes:

- Added option for showing git status on editor tabs
2023-07-17 12:14:39 -07:00
KCaverly
f0bf60fded add css as a embeddable file type in which the entire file is embedded individually 2023-07-17 14:53:57 -04:00
Alex Viscreanu
6793d4b6b8 feat(workspace): show git status on editor tabs 2023-07-17 20:53:42 +02:00
Conrad Irwin
c9bf407431 Avoid optional on select_match 2023-07-17 12:49:59 -06:00
KCaverly
1362c5a3d9 add embedding treesitter query for cpp 2023-07-17 14:43:29 -04:00
Max Brunsfeld
fef73ae921 Make macOS application menu aware of which key bindings are disabled (#2735)
Follow-up of https://github.com/zed-industries/zed/pull/2678
Deals with https://github.com/zed-industries/community/issues/772

Refreshes macOs menu panel on keymap file change and properly ignore
disabled actions.

Release Notes:

- Fixes a bug when disabled actions from macOs menu were still working
2023-07-17 11:20:41 -07:00
Julia
3e136943c0 After first panic, ignore others and tear down process even if in thread (#2725)
Spent a bit in a deep dive into how to handle this and honestly the
situation is rather unfortunate. The core problem is that when we have a
panic anywhere we need to tear down the app, and we'd like to do that as
cleanly as possible, avoiding throwing any other panics along the way if
possible.

We've been seeing a number of panics being reported which are
nonsensical, seemingly pointing to being a fallout panic from a worker
thread panic-ing, at which point we would write multiple panics to the
panic file, and we could possibly upload either both or the wrong panic
causing a wild goose chase. Unfortunately I've been entirely unable to
reproduce the specific panic we've been seeing but I was able to read
through the code responsible and confirm that under specific situations
a panic on one worker can cause another worker or the main thread to
also panic.

An easy solution to this is just to ignore any panics after the first
one. I'm thinking that *hopefully* we can trust the first panic to reach
the panic hook first so that the flag doesn't accidentally filter out
the panic we actually care about.

That being said we were expecting that to have already been the case
about which panic gets written to the panic file first, the first one in
the file being the one we upload, which doesn't seem to have been the
case. I'm hoping it was IO silliness causing that and that the flag
shouldn't be race-y, however this is still a shot in the dark. 🤞

As for cleanly shutting down, there's not really much we can do. One
thread physically cannot cause another to unwind without somehow sending
a message which isn't super useful. The only way for a thread to shut
down all threads and the process is to go nuclear and abort/exit the
process. This will never unwind other threads, effectively having the
same effect on those threads as compiling with `panic = "abort"` would.

With some (mis)use of `std::panic::resume_unwind` we can at least say
that for whatever thread actually panic-ed we will unwind, and any other
threads that panic as a result will probably get at least partway
through unwinding. This is weird, almost a combination of panic
rewinding and aborting, and may actually be worse than just biting the
bullet and aborting immediately.

I'm really not a fan of where I've ended up but it does seem to at the
very least an improvement. The main question in my mind at this point is
whether it would be better to attempt to unwind what we can or go all in
on abort. I'd love some input on that.

Release Notes:
- Improved panic reporting when a background thread panics.
2023-07-17 13:52:33 -04:00
Julia
6770aeeb3c After first panic, ignore others and tear down process even if in thread 2023-07-17 13:43:43 -04:00
Kirill Bulatov
a4bf19c5bd Simplify NoAction filtering logic
co-authored-by: Max Brunsfeld <max@zed.dev>
2023-07-17 20:42:35 +03:00
Kirill Bulatov
4cc06748c9 Ignore keybindings with NoAction in config overrides 2023-07-17 18:34:39 +03:00
Conrad Irwin
f887a17ffe Merge branch 'main' into vim-search 2023-07-17 09:27:02 -06:00
KCaverly
cf0dd09b5c update vector_store to accomodate for full file parsing for JSON, TOML and YAML files 2023-07-17 10:04:32 -04:00
KCaverly
4bece54655 update jsx family of languages for preceeding comments and nested exports 2023-07-17 09:22:37 -04:00
Piotr Osiewicz
dd6b674e7e Remove dbg calls 2023-07-17 13:08:41 +02:00
Piotr Osiewicz
8642a1d074 Remove dbg! calls 2023-07-17 13:03:57 +02:00
Piotr Osiewicz
ee9123a7da Remove test 2023-07-17 12:56:25 +02:00
Piotr Osiewicz
5b6582a7c2 rustfmt 2023-07-17 12:51:00 +02:00
Piotr Osiewicz
6c7a6d43fc Cargo fix 2023-07-17 12:38:35 +02:00
Piotr Osiewicz
94796e943b Set language id for PHP. LSP works! 2023-07-17 12:36:08 +02:00
Piotr Osiewicz
965cc2efbc Fix a crash in tree-sitter-php 2023-07-17 12:07:25 +02:00
Piotr Osiewicz
11173b2199 Merge branch 'main' into piotr/z-2588-php 2023-07-17 11:47:08 +02:00
Piotr Osiewicz
dc557e1647 Add scaffolding of php language server 2023-07-17 11:43:32 +02:00
Kirill Bulatov
f5eac82e81 Reload menu after keybindings change 2023-07-17 12:30:42 +03:00
Kirill Bulatov
eaa8224076 Use id instead of type_id for actions
Currently, both are the same thing, so the logic is not changed.
2023-07-17 12:24:56 +03:00
Mikayla Maki
10a1df3faa Fix fold indicator active hover style (#2731)
Release Notes:

- Fix: adjusted fold indicator styles
2023-07-15 23:06:01 -07:00
Mikayla Maki
419cbcbaf8 Fix fold indicator active hover style 2023-07-15 22:51:04 -07:00
Mikayla Maki
f24001c130 Simplify db tests (#2730)
The open_db function I wrote was doing far more than it needed to to
preserve the database and it was doing it badly. It no longer does all
of that.
2023-07-14 16:16:33 -07:00
Mikayla Maki
322ebc33d1 Simplify db tests 2023-07-14 16:09:02 -07:00
Joseph T. Lyons
4d91409bbc Require only a single click to split pane when using cmd in project panel (#2729)
Release Notes:

- N/A
2023-07-14 18:31:38 -04:00
Joseph T. Lyons
c3e8ea304a Require only a single click to split pane when using cmd in project panel 2023-07-14 18:27:40 -04:00
Max Brunsfeld
dcc2cd8dff Optimize two slow code paths (#2728)
Linear:
https://linear.app/zed-industries/issue/Z-2578/zed-launches-very-slow-for-user

I was searching for the cause of a slow startup time reported in the
above issue, and I don't think I found it, but I did find two very
noticeable slow code paths while profiling, and fixed them.

###  Notes

1. When starting the JSON language server, we provide it with a JSON
schema for our settings. For the `theme` setting, the JSON schema needs
to read all of the themes in the registry, to generate a list of valid
theme names. Previously, as part of this, we were deserializing each
theme from JSON, which took a lot of CPU. Now, we don't do that.
2. When an FS event occurs within a git repository, we reload the git
status for all entries in that git repository. Previously, we did that
via a separate `libgit2` call per FS entry (including ignored entries,
so many thousands in the case of the `zed` repo). Now we do one
`libgit2` call, asking for all of the statuses. Git carries an index of
all of the files with statuses, so this is fast.

Release Notes:

- Improved the the performance of starting up a  JSON language server.
- Improved the performance of handling changes to git repositories, such
as changing branches or committing.
2023-07-14 14:38:55 -07:00
Max Brunsfeld
b9e0074793 Perform only one git statuses call when reloading a git repo after it changes 2023-07-14 14:29:22 -07:00
Max Brunsfeld
c69d0d50cd Avoid deserializing all themes to compute settings JSON schema 2023-07-14 14:29:22 -07:00
Alex Viscreanu
031172d3f2 file/symbol navigation modifiers (#2727)
Fixes https://github.com/zed-industries/community/issues/54

Release Notes:

- Added modifiers for opening files and symbols on a split
- Added modifiers for navigating to definition and type definitions on a
split
2023-07-14 22:03:48 +02:00
Alex Viscreanu
c0b2326053 fix(flexes): reset flexes when collapsing axis
Co-authored-by: Mikayla Maki <mikayla@zed.dev>
2023-07-14 21:49:33 +02:00
Alex Viscreanu
c7669317ec feat(workspace): allow alternative actions to open files and symbols in split
Co-authored-by: Mikayla Maki <mikayla@zed.dev>
2023-07-14 21:49:15 +02:00
Piotr Osiewicz
369ccc725c branch_list: Bail in case of missing worktrees. (#2726)
Z-2632

Release Notes:
- Fixed a crash that occurred when opening a modal branch picker without
a corktree.
2023-07-14 21:41:11 +02:00
Kirill Bulatov
cde5b3952d Select all matches (#2717)
Closes https://github.com/zed-industries/community/issues/75
Closes https://github.com/zed-industries/community/issues/1749

The PR 

* changes keybindings for `Editor && mode == auto_height` context:
before, `alt-enter` and `alt-shift-enter` added new lines in such
editors, including the one from buffer search.

New bindings are the same as in `Editor && mode == full` context.

* adds `search::SelectAllMatches` action and binds it to `Alt + Enter`
by default, to select all matches of a buffer search

The behavior mimics VSCode: we do not move the screen even if all
selections are out of the visible range (Cmd+G will navigate there) and
allow reselecting the results from both pane and search field, as long
as the search is not dismissed.

Release Notes:

- Added `search::SelectAllMatches` (`Alt + Enter` default) action to
place carets and select all buffer search results
([#75](https://github.com/zed-industries/community/issues/75),
[#1749](https://github.com/zed-industries/community/issues/1749)).
2023-07-14 21:37:04 +03:00
KCaverly
2dae42b1ba update embedding query for tsx to accomodate for leading comments 2023-07-14 14:25:08 -04:00
KCaverly
d4971e9ead update typescript parsing to manage for leading tsdoc comments 2023-07-14 13:47:10 -04:00
Piotr Osiewicz
c6195e6176 branch_list: Bail in case of missing worktrees.
Z-2632
2023-07-14 19:33:27 +02:00
Max Brunsfeld
0f5489397f Fix syntax map issues that caused bugs in editing HEEx (#2723)
Fixes [Z-2575 : HEEX files are using deprecated commenting
sytle](https://linear.app/zed-industries/issue/Z-2575/heex-files-are-using-deprecated-commenting-sytle)

- Fixed a bug where comment toggling and bracket matching used the wrong
characters in templating languages like ERB and HEEx
([#1724](https://github.com/zed-industries/community/issues/1724)).
- Fixed a bug where interpolated code was sometimes not parsed correctly
within templating languages like ERB and HEEx.
2023-07-14 10:23:25 -07:00
Piotr Osiewicz
c466711cd1 branch_list: Ensure index is within list bounds. (#2724)
Z-2630


Release Notes:
- Fixed a crash in branch list that occurred when confirming a match in
empty list.
2023-07-14 19:11:24 +02:00
Piotr Osiewicz
9c150252aa branch_list: Ensure index is within list bounds.
Z-2630
2023-07-14 19:00:14 +02:00
Max Brunsfeld
31720d8825 Add randomized syntax map test with elixir within heex within elixir 2023-07-14 09:58:34 -07:00
Max Brunsfeld
21e7e35e73 Include newlines in between combined injection ranges on different lines 2023-07-14 09:25:56 -07:00
Max Brunsfeld
2f2ef7c165 Use workspace dependencies for tree-sitter grammars 2023-07-14 09:25:51 -07:00
Max Brunsfeld
2e2333107a Find the layer with the smallest enclosing node in language_scope_at 2023-07-14 09:11:56 -07:00
KCaverly
3a625d15d3 update c embedding query for preceding comments 2023-07-14 11:33:49 -04:00
Kirill Bulatov
b14cd5f56d Add a new button for the action 2023-07-14 17:32:10 +03:00
Kirill Bulatov
ccc78000bd Preserve serach index for multicaret selection editor events 2023-07-14 14:47:12 +03:00
Nate Butler
c130dd6b47 Add styles for an action_button ahead of the "Select all matches" UI button 2023-07-14 14:47:12 +03:00
Kirill Bulatov
f710efca3b Use a better name 2023-07-14 14:47:12 +03:00
Kirill Bulatov
2053418f21 Use VSCode-like shortcuts by default 2023-07-14 14:47:12 +03:00
Kirill Bulatov
29cbeb39bd Allow selecting all search matches in buffer 2023-07-14 14:47:12 +03:00
Mikayla Maki
bf9dfa3b51 Add Svelte support (#2720)
fixes https://github.com/zed-industries/community/issues/432

Release Notes:

- Added support for the svelte language
2023-07-13 22:43:46 -07:00
Mikayla Maki
f1b034d4f8 fmt 2023-07-13 22:32:29 -07:00
Mikayla Maki
ff8a89a075 Refine svelte queries to work with zed-style highlights
Bump scheme dependency:
2023-07-13 22:30:58 -07:00
Mikayla Maki
1424a7a56a Add svelte language server
Add svelte tree sitter
Add svelte config file
Add svelte highlighting
2023-07-13 21:43:53 -07:00
Mikayla Maki
415b8f0147 Add line height settings for the editor (#2718)
I'm a bit tired of the complaining for this feature. But also, we should
have it. Hence, this PR.

fixes:
https://github.com/zed-industries/community/issues/304#issue-1305112032

Release Notes:
- Added a `buffer_line_height` setting
2023-07-13 16:21:47 -07:00
Mikayla Maki
77c4fc98bd Add line height settings for the editor 2023-07-13 16:14:33 -07:00
Mikayla Maki
b7ed467690 WIP: Resizing splits (#2715)
We're finally doing the thing. 


TODO:
- [x] Choose an approach 
- Decided to add a new element just for the pane axis, containing a
slimmed down copy of the flex code.
- [x] Wire through callbacks and pointers so that data goes where it
needs to
- [x] Do the flex juggling math on resize
- [x] Update the flexes when updating the split tree
- [x] Restore the active_pane_magnification setting
- [x] Serialize an axis' flexes

Release Notes:
- Made the center pane group splits resizable. Note that resizing is
disabled if the `active_pane_magnification` setting is changed from
default.
2023-07-13 15:29:34 -07:00
KCaverly
b38e3b804c remove reindexing subscription, and add status methods for vector store
Co-authored-by: maxbrunsfeld <max@zed.dev>
2023-07-13 18:14:44 -04:00
Mikayla Maki
50623c018c Fix serialization error 2023-07-13 14:48:56 -07:00
Mikayla Maki
9da8f609cf tidy up names 2023-07-13 14:34:32 -07:00
Mikayla Maki
331fd896b5 fmt 2023-07-13 14:21:30 -07:00
Mikayla Maki
5797282b98 Add resising serialization 2023-07-13 14:21:14 -07:00
KCaverly
d8fd0be598 update vector store to remove dummy embeddings 2023-07-13 17:01:56 -04:00
KCaverly
623cb9833c add tests for rust context parsing, and update rust embedding query
Co-authored-by: maxbrunsfeld <max@zed.dev>
2023-07-13 16:58:42 -04:00
KCaverly
0a0e40fb24 refactored code context retrieval and standardized database migration
Co-authored-by: maxbrunsfeld <max@zed.dev>
2023-07-13 16:34:32 -04:00
Mikayla Maki
00b04f1c85 Restore active pane magnification 2023-07-13 13:10:36 -07:00
KCaverly
5eab628580 Added go parsing for semantic search, and added preceeding comments on go and rust.
Co-authored-by: Alex <alexviscreanu@gmail.com>
Co-authored-by: maxbrunsfeld <max@zed.dev>
2023-07-13 14:33:31 -04:00
Mikayla Maki
d5f7ad08fa Styled and refined behavior for split resizing 2023-07-13 11:28:21 -07:00
Nate Butler
ef7aa66959 Add first line pattern 2023-07-13 12:09:43 -04:00
Nate Butler
9a1a9813cb WIP 2023-07-13 11:56:53 -04:00
KCaverly
a56d454a07 added semantic search support for c 2023-07-13 10:10:24 -04:00
Piotr Osiewicz
608c16342c Update outline queries; add enum as a highlighted keyword 2023-07-13 12:23:49 +02:00
Kirill Bulatov
c2ffd8975b Update another deprecated plugin (#2716)
Follow-up of https://github.com/zed-industries/zed/pull/2713, fixing the
same for the bundling part of the pipeline.

Release Notes:

- N/A
2023-07-13 12:12:42 +03:00
Kirill Bulatov
8cce403c11 Update another deprecated plugin 2023-07-13 11:52:35 +03:00
Mikayla Maki
26b9be628e Add the math for pane resizing 2023-07-12 22:35:51 -07:00
Mikayla Maki
5385ca411b Added the new elements and wired through the pointers to update the pane axis ratios 2023-07-12 17:53:01 -07:00
Joseph T. Lyons
c9ba4c764a Fix screen sharing panic introduced by call events (#2714)
Release Notes:

- Fixed a bug where Zed would crash when enabling screen share.
2023-07-12 16:22:42 -04:00
Joseph T. Lyons
6da5008f32 Fix screen sharing panic introduced by call events
Co-Authored-By: Max Brunsfeld <maxbrunsfeld@gmail.com>
2023-07-12 16:12:07 -04:00
Mikayla Maki
488b41826b WIP 2023-07-12 12:46:56 -07:00
Kirill Bulatov
1e8ee5361d Update GH Actions (#2713)
Fixes deprecation warnings the CI jobs started to have:

https://github.com/zed-industries/zed/actions/runs/5535503789

<img width="1383" alt="image"
src="https://github.com/zed-industries/zed/assets/2690773/a33ecc2a-d6d3-451d-8033-da5754df4731">

Release Notes:

- N/A
2023-07-12 22:25:59 +03:00
Kirill Bulatov
7cbcc28b1b Update checkout actions 2023-07-12 22:18:37 +03:00
Kirill Bulatov
d164034198 Allow all completions with preresolved additional text edits (#2711)
Deals with https://github.com/zed-industries/community/issues/752
Deals with https://github.com/zed-industries/community/issues/566

Currently, when converting from LSP to Zed objects, completions with
non-empty `additional_text_edits` are filtered out.
Later, all other completions form a list and the selected one gets the
`Editor::confirm_completion` call, which always queries an LSP
completion resolve request to get the `additional_text_edits` field.

Otherwise, `additional_text_edits` field is ignored entirely for the
rest of the completion lifetime — and we always pass the selected
completion through the resolve request.

The PR changes the logic, removing the `additional_text_edits` filtering
and instead of resolving every completion, now we check for
`additional_text_edits` in the completion before resolving: resolve
happens only if the data is absent.

Generally, feels like resolve has to happen before the completion
selection: LSP servers may send us markdown for completion documentation
preview pop ups and similar extra info.
Also, the server may lack resolve capabilities entirely, always sending
the request seems dangerous.
For now, the PR does not attempt to change either.

Release Notes:

- Brings rust-analyzer's postfix completions and others completions with
preresolved additional text edits
2023-07-12 22:10:18 +03:00
Nate Butler
ad4f5e55cb Update docs (#2712)
Update docs

Release Notes:

- N/A
2023-07-12 15:00:11 -04:00
Kirill Bulatov
0c7949bdee Force resolve all completions, to ensure their edits are up-to-date
co-authored-by: Max Brunsfeld <max@zed.dev>
2023-07-12 21:10:01 +03:00
Nate Butler
6297675055 Update building-zed.md
Co-Authored-By: Derek Briggs <1648941+PixelJanitor@users.noreply.github.com>
2023-07-12 14:09:21 -04:00
Derek Briggs
0e600ad2a4 Update README.md 2023-07-12 11:35:38 -06:00
Piotr Osiewicz
1cc8ecad12 Fix HTML injections (Thanks Max!)
Co-authored-by: Max <max@zed.dev>
2023-07-12 19:33:09 +02:00
Joseph T. Lyons
af9506b21d v0.96.x dev 2023-07-12 13:30:28 -04:00
Kirill Bulatov
c732aa1617 Do not resolve completions if extra edits are available 2023-07-12 20:28:16 +03:00
Kyle Caverly
37568ccbf0 Vector store (#2658)
This PR includes a new crate, aimed at maintaining a consistent semantic
embedding database, for any project opened with Zed. At a high level,
for each file in a project, we parse the file with treesitter, embed the
symbol "document" objects with OpenAI, and maintain a consistent
database of these embeddings and offset locations in a sqlite database.
Once stored, we have built a simple modal interface for querying on
these symbols embeddings using natural language, offering the
opportunity to navigate to the selected symbol.

This initial PR is intended to provide this functionality only in preview,
as we explore, evaluate and iterate on the vector store.

- Full task details are provided in the [Semantic Search Linear
Project](https://linear.app/zed-industries/project/semantic-search-7c787d198ebe/Z)
2023-07-12 13:26:17 -04:00
KCaverly
c141519dba merged with main 2023-07-12 13:15:23 -04:00
Derek Briggs
dc09a11090 Update README.md 2023-07-12 10:58:39 -06:00
Derek Briggs
2cb7d8aa96 Update README.md 2023-07-12 10:51:09 -06:00
Piotr Osiewicz
e69240cf13 Piotr/z 2556 add create branch button (#2696)
Release Notes:

- N/A
2023-07-12 18:46:33 +02:00
Nate Butler
001e848393 Update picker footer button style
Co-Authored-By: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
2023-07-12 12:40:37 -04:00
Piotr Osiewicz
2ac485a6ec Merge branch 'main' into piotr/z-2556-add-create-branch-button 2023-07-12 18:11:52 +02:00
Piotr Osiewicz
c12821f6c5 Increase trailoff limit for modal branch picker. (#2710)
Z-2601

Follow-up to modal branch picker, this is the updated version:

![image](https://github.com/zed-industries/zed/assets/24362066/1017e2d9-7107-4e4c-805e-bae46412079a)
Previously a trail off limit was much smaller:

![image](https://github.com/zed-industries/zed/assets/24362066/efb6c7cf-d90d-4fbc-8c28-84872f215ac5)

Release notes:
- N/A
2023-07-12 18:07:54 +02:00
Piotr Osiewicz
6260d977fb Increase trailoff limit for modal branch picker.
Z-2601
2023-07-12 17:58:00 +02:00
Kirill Bulatov
6d96c6ef51 Draft the postfix completions support 2023-07-12 18:32:03 +03:00
Julia
3db1aac119 Avoid user menu toggle button overlapping with tab bar top border (#2707)
Something little I noticed today

| | Normal  | Hovered |
| ------------- | ------------- | - |
| Before | ![CleanShot 2023-07-11 at 16 57
37](https://github.com/zed-industries/zed/assets/30666851/30769d09-678e-4d66-96de-df51c6d030cc)
![CleanShot 2023-07-12 at 10 17
20](https://github.com/zed-industries/zed/assets/30666851/801e1f26-1cea-45a7-8a50-b620095e2131)
| ![CleanShot 2023-07-11 at 16 59
46](https://github.com/zed-industries/zed/assets/30666851/fd1324c2-669f-42f8-96b3-4d65b555fb6e)
![CleanShot 2023-07-12 at 10 17
39](https://github.com/zed-industries/zed/assets/30666851/b286488d-b81e-44d5-a67c-dd816c072f86)
|
| After | ![CleanShot 2023-07-11 at 16 59
25](https://github.com/zed-industries/zed/assets/30666851/9942733f-8129-4854-bbfe-9a292b0e2c0e)
![CleanShot 2023-07-12 at 10 18
52](https://github.com/zed-industries/zed/assets/30666851/0b0f5fec-4c44-4c4f-8921-3b8a2cfff38c)
| ![CleanShot 2023-07-11 at 17 02
19](https://github.com/zed-industries/zed/assets/30666851/6ab82b26-0548-4ce7-8fdc-38ae561d26aa)
![CleanShot 2023-07-12 at 10 19
28](https://github.com/zed-industries/zed/assets/30666851/a024f6e8-f0f4-4d81-9f90-38a655a09031)
|

Also makes it match the contacts button and seems to more closely
resemble the mockups as far as I can tell

![CleanShot 2023-07-11 at 17 02
55](https://github.com/zed-industries/zed/assets/30666851/07fb1dea-5922-4bdc-9a3b-f7c1b105d017)

Release Notes:

- Fixed the titlebar user menu button obscuring part of the border below
it.
2023-07-12 11:31:42 -04:00
Piotr Osiewicz
99c2395a86 chore: Disable http2 feature in isahc. (#2709)
This removes transitive dependency on libnghttp2, which is pretty heavy.

Release Notes:

- N/A
2023-07-12 17:24:01 +02:00
Piotr Osiewicz
78c8324698 chore: Disable http2 feature in isahc.
This removes transitive dependency on libnghttp2, which is pretty heavy.
2023-07-12 16:53:01 +02:00
Piotr Osiewicz
10c62779d9 chore: Bump ipc-channel to 0.16.1. (#2708)
Kevin Hovsäter reported a crash in cli when running 'cargo run -p cli --
--bundle-path target/debug/Zed'. It was caused by unaligned pointer
access in ipc-channel library; rustc started generating debug_asserts
for pointer alignment starting with 1.70, which we have
oh-so-conveniently upgraded to shortly before Kevin noticed a crash.
Rust 1.70 did not introduce this panic, it merely started triggering on
UB that was previously ignored.

/cc @hovsater @SomeoneToIgnore 
Release Notes:

- N/A
2023-07-12 13:49:55 +02:00
Piotr Osiewicz
5086e37e73 chore: Bump ipc-channel to 0.16.1.
Kevin Hovsäter reported a crash in cli when running 'cargo run -po cli -- --bundle-path target/debug/Zed'. It was caused by unaligned pointer access in ipc-channel library; rustc started generating debug_asserts for pointer alignment starting with 1.70, which we have oh-so-conveniently upgraded to shortly before Kevin noticed a fix.
Rust 1.70 did not introduce this panic, it merely started triggering on UB that was previously ignored.
2023-07-12 13:27:14 +02:00
Piotr Osiewicz
b9f5cb0301 recent_projects: Perform fuzzy search on compacted paths. (#2703)
Match highlighting for recent projects picker was off, because the path
representation was compacted - for a path '/Users/hiro/Projects/zed' we
compact it to use a tilde instead of home directory. However, the
highlight positions were always calculated for a full path, leading to a
mismatch in highlights. This commit addresses this by running fuzzy
search on compacted paths instead of using long paths. This might lead
to a slight performance hit, but given that recent projects modal
shouldn't have that many items in the first place, it should be okay.

Z-2546

Release Notes:

- Fixed result highlighting in "Recent projects" modal.
2023-07-12 11:47:28 +02:00
KCaverly
33e2b52a01 added test registration for project settings 2023-07-11 20:12:43 -04:00
KCaverly
297fa029e3 Merge branch 'main' of github.com:zed-industries/zed into vector_store 2023-07-11 20:05:29 -04:00
KCaverly
b68cd58a3b updated vector store settings to remove batch embeddings size 2023-07-11 19:54:03 -04:00
Max Brunsfeld
4b3bb2c661 Define semantic search action regardless of whether the feature is enabled 2023-07-11 15:02:43 -07:00
Max Brunsfeld
4a4dd39875 Fix TSX embedding query 2023-07-11 15:02:19 -07:00
Max Brunsfeld
d244c0fcea Get vector store test passing - wait for indexing
Co-authored-by: Kyle <kyle@zed.dev>
2023-07-11 14:30:11 -07:00
Max Brunsfeld
badf94b097 Update dot product test to use larger vectors
Co-authored-by: Kyle <kyle@zed.dev>
2023-07-11 14:29:48 -07:00
Max Brunsfeld
08e24bbbae Use cmd-ctrl-t for semantic search key binding
Co-authored-by: Kyle <kyle@zed.dev>
2023-07-11 14:29:06 -07:00
KCaverly
af7b2f17ae added initial keymap for toggle semantic search
Co-authored-by: maxbrunsfeld <max@zed.dev>
2023-07-11 17:13:58 -04:00
Julia
ef296e46cb Avoid user menu toggle button overlapping with tab bar top border 2023-07-11 16:49:53 -04:00
KCaverly
2ca4b3f4cc cleaned up warnings and added javascript 2023-07-11 16:41:08 -04:00
KCaverly
debe6f107e updated embedding queries for tsx and typescript 2023-07-11 16:22:40 -04:00
KCaverly
02f523094b expanded embeddable context to accomodate for struct context and file paths 2023-07-11 15:58:33 -04:00
Mikayla Maki
9165320390 Fix a bug where the terminal would not be closed by the terminal exiting (#2706)
Release Notes:

- Fixed a bug where terminal tabs in the panel would not close on tty
process exit.
2023-07-11 12:23:26 -07:00
Mikayla Maki
550aa2d6bd fmt 2023-07-11 12:17:50 -07:00
Mikayla Maki
be881369fa Fix a bug where the terminal panel's items wouldn't be hooked up properly to workspace actions 2023-07-11 12:12:37 -07:00
Kirill Bulatov
5483bd1404 Refactor LSP restart logic (#2705)
Instead of storing `initialization_options` in every LSP adapter as
before, store previous LSP settings in `Project` entirely.

This way, we can later have use multiple different project
configurations per single LSP with its associated adapter.

Release Notes:

- N/A
2023-07-11 22:09:40 +03:00
Kirill Bulatov
4b4d049b0a Refactor LSP restart logic
Instead of storing `initialization_options` in every LSP adapter as
before, store previous LSP settings in `Project` entirely.

This way, we can later have use multiple different project
configurations per single LSP with its associated adapter.

co-authored-by: Max Brunsfeld <max@zed.dev>
2023-07-11 21:56:55 +03:00
KCaverly
dd0dbdc5bd brought up to speed with main 2023-07-11 14:50:48 -04:00
KCaverly
1649cf81de added versioning to files table 2023-07-11 14:42:03 -04:00
Joseph T. Lyons
5012d618e6 Add call events (#2704)
Release Notes:

- N/A
2023-07-11 14:40:07 -04:00
Joseph T. Lyons
98a0113ac3 Add call events
Co-Authored-By: Max Brunsfeld <maxbrunsfeld@gmail.com>
2023-07-11 13:58:55 -04:00
Kirill Bulatov
efe8b8b6d0 Revert "Fix language servers improper restarts"
This reverts commit 91832c8cd8.
2023-07-11 20:46:45 +03:00
KCaverly
298c2213a0 added opt-in default settings for vector store 2023-07-11 12:03:56 -04:00
Kirill Bulatov
8161438a85 Fix language servers improper restarts (#2702)
Fixes
https://linear.app/zed-industries/issue/Z-2595/language-servers-are-unnecessarily-restarted-when-unrelated-settings

Language servers mixed `initialization_options` from hardcodes and user
settings, fix that to ensure we restart servers on their settings
changes only.

Release Notes:

- N/A
2023-07-11 17:15:19 +03:00
Kirill Bulatov
748e7af5a2 Add a test 2023-07-11 17:10:34 +03:00
KCaverly
f5fec55930 updated vector_store to handle for removed files 2023-07-11 10:03:53 -04:00
Kirill Bulatov
91832c8cd8 Fix language servers improper restarts
Language servers mixed `initialization_options` from hardcodes and user
settings, fix that to ensure we restart servers on their settings
changes only.
2023-07-11 16:36:20 +03:00
Piotr Osiewicz
15010e94fd fixup! recent_projects: Perform fuzzy search on compacted paths. 2023-07-11 15:29:15 +02:00
Piotr Osiewicz
f164eb5289 recent_projects: Perform fuzzy search on compacted paths.
Match highlighting for recent projects picker was off, because the path representation was compacted - for a path '/Users/hiro/Projects/zed' we compact it to use a tilde instead of home directory. However, the highlight positions were always calculated for a full path, leading to a mismatch in highlights.
This commit addresses this by running fuzzy search on compacted paths instead of using long paths. This might lead to a slight performance hit, but given that recent projects modal shouldn't have that many items in the first place, it should be okay.

Z-2546
2023-07-11 15:23:17 +02:00
Piotr Osiewicz
1fbf09fe4c branches: Add a modal branch list. (#2697)
Extract branch list into a separate vcs_menu crate akin to
recent_projects. Add current bind for a modal branch to branch popover's
tooltip.

Z-2555

Release Notes:
- N/A
2023-07-11 14:40:00 +02:00
Piotr Osiewicz
a1fe5abeaf Add rudimentary PHP syntax highlighting 2023-07-11 12:31:20 +02:00
Piotr Osiewicz
3c1ab3d0b8 Piotr/z 2590 search result marks jump around in scrollbar as cursor (#2700)
This closes ticket Z-2590 reported by @JosephTLyons . Thanks Joseph =)
Release Notes:

- N/A
2023-07-11 09:40:00 +02:00
Piotr Osiewicz
4125e7eccc editor: Keep scrollbar up if there are selections (#2698)
Z-2556

/cc @JosephTLyons 

Release Notes:

- N/A
2023-07-11 09:32:34 +02:00
Piotr Osiewicz
e83afdc5ab Rename background_highlights_in_range_for_key to background_highlights_in_range_for 2023-07-11 09:31:08 +02:00
Piotr Osiewicz
4f60679861 Highlight only search results 2023-07-11 09:28:34 +02:00
KCaverly
dce72a1ce7 updated tests to accomodate for new dot location 2023-07-10 18:19:29 -04:00
KCaverly
307d8d9c8d Reduced redundant database connections on each worktree change.
Co-authored-by: maxbrunsfeld <max@zed.dev>
2023-07-10 17:50:19 -04:00
KCaverly
82079dd422 Updated batching to accomodate for full flushes, and cleaned up reindexing.
Co-authored-by: maxbrunsfeld <max@zed.dev>
2023-07-10 16:33:14 -04:00
Piotr Osiewicz
a6d713eb3d editor: Keep scrollbar up if there are selections
Z-2556
2023-07-10 17:44:27 +02:00
Piotr Osiewicz
e00e73f608 branches: Add a modal branch list.
Extract branch list into a separate vcs_menu crate akin to recent_projects.
Add current bind for a modal branch to branch popover's tooltip.

Z-2555
2023-07-10 17:18:12 +02:00
Nate Butler
6739c31594 Update assistant styles (#2665)
Updates the assistant with some style quality of life changes.

## Changes

Restyled the conversation list

<img width="646" alt="CleanShot 2023-07-10 at 10 25 23@2x"
src="https://github.com/zed-industries/zed/assets/1714999/5c9a4f94-11c1-4d28-8aac-4d38141829a9">

Updated the assistant header to be a bit more compact, and use a new tab
bar icon style. The existing tab bar icons will be updated in a later
PR.

<img width="646" alt="CleanShot 2023-07-10 at 10 26 30@2x"
src="https://github.com/zed-industries/zed/assets/1714999/3ef9a053-59fa-4d34-9b76-3bb2701acb33">

Updated the remaining token indicator to have 3 steps:
<img width="662" alt="CleanShot 2023-07-10 at 10 29 51@2x"
src="https://github.com/zed-industries/zed/assets/1714999/13d31545-5b00-427c-b7da-b4dfeac037d6">

Updated role labels, added a hover state to make it more clear these are
interactive
<img width="984" alt="CleanShot 2023-07-10 at 10 32 28@2x"
src="https://github.com/zed-industries/zed/assets/1714999/24748495-dde4-4ee9-98f1-6a082f0c1d4d">


Release Notes:

- Improved the UI of some elements in the Assistant panel.
2023-07-10 10:54:20 -04:00
Piotr Osiewicz
a75a7e2b1d Add tooltip to recent projects button (#2694)
Z-2545

Release Notes:

- N/A
2023-07-10 16:53:50 +02:00
Piotr Osiewicz
92a0a4e367 Add styles for branch create button 2023-07-10 16:51:18 +02:00
Nate Butler
273b9e1636 Avoid overlapping the scrollbar 2023-07-10 10:44:39 -04:00
Nate Butler
9ffe220def Update tab_bar_button.ts 2023-07-10 10:24:24 -04:00
Nate Butler
4029481fd0 Merge branch 'main' into update-assistant-styles 2023-07-10 10:22:18 -04:00
Nate Butler
f0cddeb478 Update zoom icons 2023-07-10 10:09:59 -04:00
KCaverly
0189742497 pulled treesitter parsing to own file for ease of testing and management 2023-07-10 10:06:07 -04:00
Piotr Osiewicz
3318896ad9 Display key bind of a modal project picker 2023-07-10 14:29:30 +02:00
Piotr Osiewicz
6c8cb6b2a9 project_search: display result count on cmd-enter
It also focuses the first result (just like a normal enter).
2023-07-10 14:21:55 +02:00
Piotr Osiewicz
6e24ded2bc collab_ui: Add tooltip to branches popover (#2695)
Z-2554

Release Notes:

- N/A
2023-07-10 14:20:12 +02:00
Joseph T. Lyons
52a497be21 Remove code block for GitHub release notes
Discord can directly render the Markdown now.
2023-07-08 18:03:18 -04:00
Conrad Irwin
b4b0f622de Rebuild vim search experience on refactored code 2023-07-07 15:57:54 -06:00
Conrad Irwin
232d14a3ae Make search less magic
Co-Authored-By: Antonio <antonio@zed.dev>
2023-07-07 15:57:54 -06:00
Conrad Irwin
dea728a7e5 Better waiting in tests 2023-07-07 15:57:54 -06:00
Conrad Irwin
6cf13c62d1 vim: ? to search backwards, and /<enter> to repeat search 2023-07-07 15:57:52 -06:00
Conrad Irwin
d70f415e8e vim: add gD to go to type definition 2023-07-07 15:57:37 -06:00
Conrad Irwin
dbec2ed1f1 vim: add */#/g*/g# for jumping to next word
As in vim, this toggles the normal search experience.
2023-07-07 15:57:35 -06:00
Conrad Irwin
96ce0bb783 vim: Enter/n/N to navigate search results 2023-07-07 15:57:14 -06:00
Conrad Irwin
2ffce24ef0 vim: Don't enter visual mode in search/go to definition
Fixes: zed-industries/community#1514
Contributes: zed-industries/community#1284
2023-07-07 15:56:37 -06:00
Conrad Irwin
75fe77c11d search: Allow running a search with different options
Refactor search options to use bitflags so that we can represent
the entire set of settings in one place.
2023-07-07 15:56:37 -06:00
Conrad Irwin
20d8a2a1ec vim: indent in visual mode uses only one <
Fixes: zed-industries/community#1562
2023-07-07 15:56:35 -06:00
Conrad Irwin
460bf93866 vim: { } to navigate by paragraph (#2668)
As part of this I added `assert_shared_state()` to the
NeovimBackedTestContext so that it is more like a drop-in replacement
for the VimTestContext.

The remaining part of zed-industries/community#682 is adding bracket
matching to plain text. It looks like the current logic requires there
to be a tree sitter language for the language in order to support
bracket matching. I didn't fix this in this PR because I was unsure
whether to try and work around that, or to try and add a plain text tree
sitter language.

Release Notes:

- vim: support `{` and `}` for paragraph motion
([#470](https://github.com/zed-industries/community/issues/470)).
- vim: fix `%` at the end of the line
([#682](https://github.com/zed-industries/community/issues/682)).
2023-07-07 14:59:06 -06:00
Conrad Irwin
362023ccf2 vim: keymap tweaks (#2674)
A few small tweaks to fix some of the community issues

Release Notes:

- vim: Fix `escape` in command palette
([#1347](https://github.com/zed-industries/community/issues/1347)).
- vim: Allow `^` as a motion in actions
([#856](https://github.com/zed-industries/community/issues/856)).
- vim: Allow `ctrl-c` to exit visual mode
([#1447](https://github.com/zed-industries/community/issues/1447)).
2023-07-07 14:58:01 -06:00
Julia
da7dce79f6 Prevent duplicate instances by coordinating via a socket (#2691)
We've been getting a bunch of panics from duplicate app instances
competing over the local sqlite DB. After chatting with @mikayla-maki we
determined it was probably best to add our own mechanism to prevent
duplicates rather than just relying on the OS. My logic is that we'd
need to build a system like this eventually for Windows/Linux anyway so
it's more appealing than reworking our local DB access to be able to
cooperate with another process while likely isn't something we want to
support anyway.

I attempted to keep this mechanism conservative so in the case of
another program interfering with it we should fail somewhat gracefully
and still continue to launch, albeit without the ability to prevent
another instance from launching.

Fixes
https://linear.app/zed-industries/issue/Z-2435/thread-background-executor-1-panicked-at-could-not-send-write-action

Release Notes:
- Added a mechanism to prevent duplicate Zed instances from launching to
avoid a crash.
2023-07-07 14:38:55 -04:00
KCaverly
3f5667b101 merged main 2023-07-07 14:24:29 -04:00
Julia
caa29d57c2 Avoid checking for duplicate instance when local DB is disabled 2023-07-07 14:20:39 -04:00
Julia
b70b76029e Use different port and handshake for different release channels 2023-07-07 14:20:39 -04:00
Julia
66bf56fc4f Prevent duplicate instances by coordinating via a socket 2023-07-07 14:19:44 -04:00
Piotr Osiewicz
4a69c71167 fixup! vcs: Add 'create branch' button 2023-07-07 18:37:53 +02:00
Piotr Osiewicz
cb24cb1ea5 vcs: Add 'create branch' button 2023-07-07 18:36:55 +02:00
Piotr Osiewicz
d69b07bafd Add tooltip to recent projects button
Z-2545
2023-07-07 16:30:19 +02:00
Piotr Osiewicz
abf3b4a54e chore: Replace lazy_static Mutex with const. (#2693)
Mutex::new() is const-stable as of Rust 1.63.

Release Notes:
- N/A
2023-07-07 15:07:12 +02:00
Antonio Scandurra
79ece8a86e Skip key down event if preceded by its key equivalent version (#2692)
Fixes
https://linear.app/zed-industries/issue/Z-2552/pressing-two-keystrokes-in-rapid-succession-ignores-the-latter

Previously, we would only track whether the previous key down event was
a key equivalent. However, this could cause issues when pressing certain
keystrokes in rapid succession, e.g.:

- Pressing `shift-right` (to select a character, dispatched as a key
equivalent)
- Pressing a character (with or without `shift` held down, dispatched as
a key down)

This would cause GPUI to ignore the second event because it was preceded
by a key equivalent event. With this commit, we track the last key
equivalent event, and skip the key down event only if it matches the
last key equivalent event.

Release Notes:

- Fixed a bug that could cause certain keystrokes performed in rapid
succession to incorrectly get ignored.
2023-07-07 12:13:32 +02:00
Antonio Scandurra
318deed25b Skip key down event if preceded by its key equivalent version
Previously, we would only track whether the previous key down event
was a key equivalent. However, this could cause issues when pressing
certain keystrokes in rapid succession, e.g.:

- Pressing `shift-right` (to select a character)
- Pressing a character (with or without `shift` held down)

This would cause GPUI to ignore the second event because it was
preceded by a key equivalent event. With this commit, we track the
last key equivalent event, and skip the key down event only if it
matches the last key equivalent event.
2023-07-07 12:02:08 +02:00
KCaverly
c03dda1a0c fixed bug on absolute vs relative path 2023-07-06 17:15:41 -04:00
KCaverly
6f1e988cb9 updated embedding treesitter query for python 2023-07-06 16:36:28 -04:00
KCaverly
7d634f66e2 updated vector_store to include extra context for semantic search modal 2023-07-06 16:33:54 -04:00
Kirill Bulatov
4ab2b8b24b Restart LSP server on corresponding initialization_options change (#2690)
Inlay hints depend on LSP server settings, but servers do not update the
initialization options and query hints with old settings.

Generally, we cannot know whether a certain option can be changed
without server restart, which the name of the options implies too, so be
on the safe side and restart the server.
Hints will update automatically after the server either sends a /refresh
request or reports its work progress end after startup.

Release Notes:

- Fixed LSP server not restarting after `initialization_options`
settings changes
2023-07-06 23:32:34 +03:00
Kirill Bulatov
e6ec0af743 Remove redundant hint kind checks in tests 2023-07-06 23:27:25 +03:00
Kirill Bulatov
fff65968bf Restart LSP server on initialization options change 2023-07-06 23:27:22 +03:00
KCaverly
e57f6f21fe reindexing update to appropriately accomodate for buffer delay and persistent pending files list 2023-07-06 15:26:43 -04:00
Piotr Osiewicz
3ca0170264 Z 1332/show search results in scrollbar (#2687)
This PR adds highlighting of search results to the scrollbar.

Release Notes:

- Added highlighting of search results to the scrollbar.
2023-07-06 19:28:21 +02:00
KCaverly
a86b6c42c7 corrected batching order and managed for open ai embedding errors 2023-07-06 11:11:39 -04:00
Nate Butler
793eff1695 Update scrollbar selection style 2023-07-06 10:54:47 -04:00
Antonio Scandurra
b4ed0347b4 Filter out non-json files when loading conversations (#2688)
Fixes
https://linear.app/zed-industries/issue/Z-2540/filter-out-non-conversation-files-from-the-assistant-history
2023-07-06 16:51:52 +02:00
Kirill Bulatov
2c7e5e0671 Clip find preceding boundary (#2689)
Fixes inability to do `alt + left arrow` when an inlay with `Bias::Left`
is right to the left of the caret.

Release Notes:

- N/A
2023-07-06 17:24:03 +03:00
Kirill Bulatov
11ae99fbd6 Add a test 2023-07-06 17:16:34 +03:00
Antonio Scandurra
708852aa00 Clip left when finding preceding (line) boundary
This fixes an issue that was causing `alt-left` to not move the cursor
when it was located right after an inlay hint with a `Left` bias.
2023-07-06 16:25:27 +03:00
Kirill Bulatov
348c93e8bb Show inlay hints on startup for every language server with work events (#2686)
Closes https://linear.app/zed-industries/issue/Z-2537/inlay-hint-issues

Language servers such as typescript-language-servers report a single
work event, ending right after server's startup.

Other servers might send more similar event, also during startup. The
rest of the events are diagnostic-related and we filter them out.

React on such events with /refresh-like hint update, that will check
only the visible part of the editor for hints and might be replaced by
other /refresh requests, if needed.

Release Notes:

- N/A
2023-07-06 16:18:22 +03:00
Antonio Scandurra
5408275c7a Filter out non-json files when loading conversations 2023-07-06 14:38:05 +02:00
Piotr Osiewicz
3e245fec90 Save a flushing line instead of discarding it 2023-07-06 13:52:03 +02:00
Piotr Osiewicz
5e7d9dc718 Add hunk merging 2023-07-06 13:31:45 +02:00
Piotr Osiewicz
b66453e771 fixup! Do not render multiple hunks for the same line 2023-07-06 12:11:08 +02:00
Kirill Bulatov
0b0a161626 Show inlay hints on startup for every language server with work events
Language servers such as typescript-language-servers report a single
work event, ending right after server's startup.

Other servers might send more similar event, also during startup.
The rest of the events are diagnostic-related and we filter them out.

React on such events with /refresh-like hint update, that will check
only the visible part of the editor for hints and might be replaced by
other /refresh requests, if needed.
2023-07-06 13:10:59 +03:00
Piotr Osiewicz
492b849ea1 Do not render multiple hunks for the same line 2023-07-06 12:09:33 +02:00
Piotr Osiewicz
8ced7ab00a Merge branch 'main' into Z-1292/show_search_results_in_scrollbar 2023-07-06 11:43:44 +02:00
Kirill Bulatov
c298cf7527 Use less padding for typescript parameter hints (#2684)
Part of https://linear.app/zed-industries/issue/Z-2537/inlay-hint-issues

Release Notes:

- N/A
2023-07-06 11:23:20 +03:00
Kirill Bulatov
1936bdebb3 Use less padding for typescript parameter hints 2023-07-06 11:16:39 +03:00
Antonio Scandurra
dd6629416c Fix panic when saved conversations directory changes (#2685)
Fixes
https://linear.app/zed-industries/issue/Z-2542/deleting-assistant-conversations-with-zed-open-can-cause-a-crash

We were updating the view's state but missed a `notify`, which caused
the `UniformList` responsible for rendering the saved conversations to
panic when some files were deleted.

Release Notes:

- Fixed a crash that could happen when deleting a saved assistant
conversation from the filesystem.
2023-07-06 10:06:43 +02:00
Antonio Scandurra
f6c96ec892 Fix panic when saved conversations directory changes
We were updating the view's state but missed a `notify`, which caused
the `UniformList` responsible for rendering the saved conversations
to panic when some files were deleted.
2023-07-06 09:53:34 +02:00
Mikayla Maki
801f41e68e Move audio dependency to dev 2023-07-05 12:15:56 -07:00
Mikayla Maki
8b8bafef22 Remove spurious audio depedency 2023-07-05 12:05:16 -07:00
Mikayla Maki
594b6e8d64 collab 0.16.0 2023-07-05 11:47:17 -07:00
Joseph T. Lyons
6a15ae9c01 v0.95.x dev 2023-07-05 14:17:37 -04:00
KCaverly
afccf608f4 updated both embed and parsing tasks to be multi-threaded. 2023-07-05 12:39:08 -04:00
KCaverly
eff0ee3b60 enabled batching for embedding calls 2023-07-05 10:02:42 -04:00
KCaverly
b6520a8f1d updated vector_store to reindex on save after timed delay 2023-07-04 14:42:12 -04:00
KCaverly
e45d3a0a63 WIP: initial reindexing logic worked out 2023-07-04 11:46:09 -04:00
Conrad Irwin
0d18b72cf8 vim: Further improve ~ handling
Now works with Visual{line} mode, collapses selections like nvim,
and doesn't fall off the end of the line.
2023-07-03 23:58:09 -06:00
Conrad Irwin
0733e8d50f Remove editor::Cancel binding from vim
When you hit <escape> in the command palette, it first editor::Cancel
because the command palette is also a focused editor; this binding was
catching before the `menu::Cancel` that you probably want.

From looking at the uses of editor::Cancel it seems like the only way to
trigger this is with <escape> in an editor. Rather than trying to hook
into the existing editor cancel and add vim-specific behaviour, we'll
instead take responsibility for binding directly to <escape> when
necessary.

Fixes: zed-industries/community#1347
2023-07-03 15:26:39 -06:00
Conrad Irwin
fe57e04016 vim: Allow ^ as a motion
Fixes: zed-industries/community#856
2023-07-03 12:55:41 -06:00
Conrad Irwin
b055f594b0 vim: ctrl-c to exit visual mode
Fixes: zed-industries/community#1447
Contributes: zed-industries/community#1089
2023-07-03 12:52:33 -06:00
Conrad Irwin
e36d5f41c8 Fix % when on the last character of the line
Contributes: zed-industries/community#682
2023-07-01 13:51:11 -06:00
KCaverly
18a5a47f8a moved semantic search model to dev and preview only.
moved db update tasks to long lived persistent task.

Co-authored-by: maxbrunsfeld <max@zed.dev>
2023-06-30 18:41:19 -04:00
KCaverly
3408b98167 updated file compare in the semantic indexing engine, to work off of modified system times as opposed to file hashes
Co-authored-by: maxbrunsfeld <max@zed.dev>
2023-06-30 16:53:23 -04:00
KCaverly
36907bb4dc updated vector store indexing to only use languages with an embedding.scm treesitter query
Co-authored-by: maxbrunsfeld <max@zed.dev>
2023-06-30 16:14:11 -04:00
KCaverly
0db0876289 implemented file deletes on project indexing 2023-06-30 11:01:35 -04:00
KCaverly
e3ab54942e removed sleep from directory scanning as fixes upstream appear to be scanning correctly 2023-06-30 10:17:31 -04:00
KCaverly
1d737e490b Merge branch 'main' of github.com:zed-industries/zed into vector_store 2023-06-30 09:58:13 -04:00
Conrad Irwin
abb58c41db vim: Fix edge-case in } when trailing newline is absent
Added .assert_shared_state() to NeovimBackedTestContext – although it's
not strictly necessary to show the expected behaviour in the test file
(as we can just compare to neovim's JSON recording), it makes it much
easier to understand what we're testing.
2023-06-29 23:31:22 -06:00
Conrad Irwin
9ee2707d43 vim: Add }/{ for start/end of paragraph
Fixes: zed-industries/community#470
2023-06-29 23:31:22 -06:00
Nate Butler
530561e4eb Extract assistant tool buttons into tab_bar_button 2023-06-29 18:13:31 -04:00
Nate Butler
77b120323b Add low_tokens_remaining case to the assistant 2023-06-29 17:44:47 -04:00
Nate Butler
d6112e4a59 Add doc comments for ColorScheme layer properties 2023-06-29 17:32:19 -04:00
Nate Butler
2678dfdc57 Update assistant styles 2023-06-29 17:32:04 -04:00
KCaverly
39137fc19f updated vector_store db to leverage EMBEDDINGS_DIR path 2023-06-29 15:18:32 -04:00
KCaverly
0a7245a583 updated semantic search modal to manage for duplicate queries 2023-06-29 13:50:49 -04:00
KCaverly
a08d60fc61 added navigation on confirm to semantic search modal 2023-06-29 11:58:47 -04:00
Max Brunsfeld
fd68a2afae Debounce searches in semantic search modal 2023-06-28 15:02:20 -07:00
KCaverly
85e71415fe updated embedding database calls to maintain project consistency
Co-authored-by: maxbrunsfeld <max@zed.dev>
2023-06-28 16:25:05 -04:00
KCaverly
400d39740c updated both indexing and search method for vector store, to maintain both zed worktree ids and db worktree ids
Co-authored-by: maxbrunsfeld <max@zed.dev>
2023-06-28 16:21:03 -04:00
KCaverly
3ca3de807c Merge branch 'main' of github.com:zed-industries/zed into vector_store 2023-06-28 14:42:24 -04:00
KCaverly
40ff7779bb WIP: Working modal, without navigation and search on every keystroke 2023-06-28 13:27:26 -04:00
KCaverly
9d19dea7dd updated vector_store to remove parsing module 2023-06-28 08:58:50 -04:00
KCaverly
d1bdfa0be6 Added a dummy action for testing the semantic search functionality in the command palette.
Co-authored-by: maxbrunsfeld <max@zed.dev>
2023-06-27 15:53:07 -04:00
KCaverly
4bfe3de1f2 Working incremental index engine, with streaming similarity search!
Co-authored-by: maxbrunsfeld <max@zed.dev>
2023-06-27 15:31:21 -04:00
KCaverly
953e928bdb WIP: Got the streaming matrix multiplication working, and started work on file hashing.
Co-authored-by: maxbrunsfeld <max@zed.dev>
2023-06-26 19:01:19 -04:00
KCaverly
74b693d6b9 Updated database calls to share single connection, and simplified top_k_search sorting.
Co-authored-by: maxbrunsfeld <max@zed.dev>
2023-06-26 14:57:57 -04:00
KCaverly
0f232e0ce2 added file metadata retrieval from db 2023-06-26 10:35:56 -04:00
KCaverly
7937a16002 added brute force search and VectorSearch trait 2023-06-26 10:34:12 -04:00
KCaverly
65bbb7c57b added proper blob serialization for embeddings and vector search trait 2023-06-25 20:02:56 -04:00
KCaverly
c071b271be removed tokio and sqlx dependency, added dummy embeddings provider to save on open ai costs when testing 2023-06-23 10:25:12 -04:00
KCaverly
dd309070eb open ai indexing on open for rust files 2023-06-22 16:50:07 -04:00
KCaverly
d4a4db42aa WIP: started DB creating and naive inserts 2023-06-22 13:25:33 -04:00
KCaverly
80a894b829 WIP: started work on vector store db, by walking project worktrees.\n\nCo-Authored-By: Max <max@zed.dev> 2023-06-21 14:53:08 -04:00
Piotr Osiewicz
86247bf657 editor: Highlight search results
Z-1292
2023-06-15 09:44:44 +02:00
240 changed files with 13775 additions and 2824 deletions

View File

@@ -29,7 +29,7 @@ jobs:
rustup update stable
- name: Checkout repo
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
clean: false
submodules: 'recursive'
@@ -54,12 +54,12 @@ jobs:
cargo install cargo-nextest
- name: Install Node
uses: actions/setup-node@v2
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Checkout repo
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
clean: false
submodules: 'recursive'
@@ -104,12 +104,12 @@ jobs:
rustup target add wasm32-wasi
- name: Install Node
uses: actions/setup-node@v2
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Checkout repo
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
clean: false
submodules: 'recursive'
@@ -148,8 +148,8 @@ jobs:
- name: Create app bundle
run: script/bundle
- name: Upload app bundle to workflow run if main branch or specifi label
uses: actions/upload-artifact@v2
- name: Upload app bundle to workflow run if main branch or specific label
uses: actions/upload-artifact@v3
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-build-dmg') }}
with:
name: Zed_${{ github.event.pull_request.head.sha || github.sha }}.dmg

View File

@@ -29,12 +29,12 @@ jobs:
rustup update stable
- name: Install Node
uses: actions/setup-node@v2
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Checkout repo
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
clean: false
submodules: 'recursive'

View File

@@ -6,18 +6,23 @@ jobs:
discord_release:
runs-on: ubuntu-latest
steps:
- name: Get appropriate URL
id: get-appropriate-url
run: |
if [ "${{ github.event.release.prerelease }}" == "true" ]; then
URL="https://zed.dev/releases/preview/latest"
else
URL="https://zed.dev/releases/stable/latest"
fi
echo "::set-output name=URL::$URL"
- name: Discord Webhook Action
uses: tsickert/discord-webhook@v5.3.0
if: ${{ ! github.event.release.prerelease }}
with:
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
content: |
📣 Zed ${{ github.event.release.tag_name }} was just released!
Restart your Zed or head to https://zed.dev/releases/stable/latest to grab it.
```md
# Changelog
Restart your Zed or head to ${{ steps.get-appropriate-url.outputs.URL }} to grab it.
${{ github.event.release.body }}
```

1617
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -63,7 +63,9 @@ members = [
"crates/theme",
"crates/theme_selector",
"crates/util",
"crates/semantic_index",
"crates/vim",
"crates/vcs_menu",
"crates/workspace",
"crates/welcome",
"crates/xtask",
@@ -81,7 +83,8 @@ env_logger = { version = "0.9" }
futures = { version = "0.3" }
globset = { version = "0.4" }
indoc = "1"
isahc = "1.7.2"
# We explicitly disable a http2 support in isahc.
isahc = { version = "1.7.2", default-features = false, features = ["static-curl", "text-decoding"] }
lazy_static = { version = "1.4.0" }
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
ordered-float = { version = "2.1.1" }
@@ -104,8 +107,33 @@ tree-sitter = "0.20"
unindent = { version = "0.1.7" }
pretty_assertions = "1.3.0"
tree-sitter-bash = { git = "https://github.com/tree-sitter/tree-sitter-bash", rev = "1b0321ee85701d5036c334a6f04761cdc672e64c" }
tree-sitter-c = "0.20.1"
tree-sitter-cpp = { git = "https://github.com/tree-sitter/tree-sitter-cpp", rev="f44509141e7e483323d2ec178f2d2e6c0fc041c1" }
tree-sitter-css = { git = "https://github.com/tree-sitter/tree-sitter-css", rev = "769203d0f9abe1a9a691ac2b9fe4bb4397a73c51" }
tree-sitter-elixir = { git = "https://github.com/elixir-lang/tree-sitter-elixir", rev = "4ba9dab6e2602960d95b2b625f3386c27e08084e" }
tree-sitter-elm = { git = "https://github.com/elm-tooling/tree-sitter-elm", rev = "692c50c0b961364c40299e73c1306aecb5d20f40"}
tree-sitter-embedded-template = "0.20.0"
tree-sitter-glsl = { git = "https://github.com/theHamsta/tree-sitter-glsl", rev = "2a56fb7bc8bb03a1892b4741279dd0a8758b7fb3" }
tree-sitter-go = { git = "https://github.com/tree-sitter/tree-sitter-go", rev = "aeb2f33b366fd78d5789ff104956ce23508b85db" }
tree-sitter-heex = { git = "https://github.com/phoenixframework/tree-sitter-heex", rev = "2e1348c3cf2c9323e87c2744796cf3f3868aa82a" }
tree-sitter-json = { git = "https://github.com/tree-sitter/tree-sitter-json", rev = "40a81c01a40ac48744e0c8ccabbaba1920441199" }
tree-sitter-rust = "0.20.3"
tree-sitter-markdown = { git = "https://github.com/MDeiml/tree-sitter-markdown", rev = "330ecab87a3e3a7211ac69bbadc19eabecdb1cca" }
tree-sitter-php = { git = "https://github.com/tree-sitter/tree-sitter-php", rev = "d43130fd1525301e9826f420c5393a4d169819fc" }
tree-sitter-python = "0.20.2"
tree-sitter-toml = { git = "https://github.com/tree-sitter/tree-sitter-toml", rev = "342d9be207c2dba869b9967124c679b5e6fd0ebe" }
tree-sitter-typescript = { git = "https://github.com/tree-sitter/tree-sitter-typescript", rev = "5d20856f34315b068c41edaee2ac8a100081d259" }
tree-sitter-ruby = "0.20.0"
tree-sitter-html = "0.19.0"
tree-sitter-scheme = { git = "https://github.com/6cdh/tree-sitter-scheme", rev = "af0fd1fa452cb2562dc7b5c8a8c55551c39273b9"}
tree-sitter-svelte = { git = "https://github.com/Himujjal/tree-sitter-svelte", rev = "697bb515471871e85ff799ea57a76298a71a9cca"}
tree-sitter-racket = { git = "https://github.com/zed-industries/tree-sitter-racket", rev = "eb010cf2c674c6fd9a6316a84e28ef90190fe51a"}
tree-sitter-yaml = { git = "https://github.com/zed-industries/tree-sitter-yaml", rev = "f545a41f57502e1b5ddf2a6668896c1b0620f930"}
tree-sitter-lua = "0.0.14"
[patch.crates-io]
tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "49226023693107fba9a1191136a4f47f38cdca73" }
tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "1c65ca24bc9a734ab70115188f465e12eecf224e" }
async-task = { git = "https://github.com/zed-industries/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e" }
# TODO - Remove when a version is released with this PR: https://github.com/servo/core-foundation-rs/pull/457

View File

@@ -16,22 +16,25 @@ Welcome to Zed, a lightning-fast, collaborative code editor that makes your drea
brew install foreman
```
* Ensure the Zed.dev website is checked out in a sibling directory:
* Ensure the Zed.dev website is checked out in a sibling directory and install it's dependencies:
```
cd ..
git clone https://github.com/zed-industries/zed.dev
cd zed.dev && npm install
npm install -g vercel
```
* Initialize submodules
* Return to Zed project directory and Initialize submodules
```
cd zed
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).
[Create a personal GitHub token](https://github.com/settings/tokens/new) 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.
```

View File

@@ -0,0 +1,27 @@
<svg width="14" height="16" viewBox="0 0 14 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.375 8.74577V10.375C7.30597 10.375 6.69403 10.375 4.625 10.375V10.1226L9.375 5.87742V5.625H4.625V7.27717" stroke="black" stroke-width="1.25"/>
<circle cx="0.5" cy="8" r="0.5" fill="black" fill-opacity="0.3"/>
<circle cx="1.49976" cy="5.82825" r="0.5" fill="black" fill-opacity="0.6"/>
<circle cx="1.49976" cy="10.1719" r="0.5" fill="black" fill-opacity="0.6"/>
<circle cx="13.5" cy="8.01581" r="0.5" fill="black" fill-opacity="0.3"/>
<circle cx="12.5" cy="5.84387" r="0.5" fill="black" fill-opacity="0.6"/>
<circle cx="12.5" cy="10.1877" r="0.5" fill="black" fill-opacity="0.6"/>
<circle cx="6.99213" cy="1.48438" r="0.5" fill="black" fill-opacity="0.3"/>
<circle cx="4.50391" cy="2.48438" r="0.5" fill="black" fill-opacity="0.6"/>
<circle cx="2.49976" cy="3.48438" r="0.5" fill="black" fill-opacity="0.3"/>
<circle cx="2.49976" cy="12.5" r="0.5" fill="black" fill-opacity="0.3"/>
<circle cx="0.5" cy="12.016" r="0.5" fill="black" fill-opacity="0.3"/>
<circle cx="0.5" cy="3.98438" r="0.5" fill="black" fill-opacity="0.3"/>
<circle cx="13.5" cy="12.016" r="0.5" fill="black" fill-opacity="0.3"/>
<circle cx="13.5" cy="3.98438" r="0.5" fill="black" fill-opacity="0.3"/>
<circle cx="2.49976" cy="14.516" r="0.5" fill="black" fill-opacity="0.3"/>
<circle cx="2.48413" cy="1.48438" r="0.5" fill="black" fill-opacity="0.3"/>
<circle cx="11.5" cy="14.516" r="0.5" fill="black" fill-opacity="0.3"/>
<circle cx="11.5" cy="1.48438" r="0.5" fill="black" fill-opacity="0.3"/>
<circle cx="11.5" cy="3.48438" r="0.5" fill="black" fill-opacity="0.3"/>
<circle cx="11.5" cy="12.516" r="0.5" fill="black" fill-opacity="0.3"/>
<circle cx="9.49609" cy="2.48438" r="0.5" fill="black" fill-opacity="0.6"/>
<circle cx="6.99213" cy="14.5" r="0.5" fill="black" fill-opacity="0.3"/>
<circle cx="4.50391" cy="13.516" r="0.5" fill="black" fill-opacity="0.6"/>
<circle cx="9.49609" cy="13.5" r="0.5" fill="black" fill-opacity="0.6"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,5 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 7.63H8" stroke="black" stroke-opacity="0.6" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<rect x="2" y="2" width="10" height="3" rx="0.5" fill="black" fill-opacity="0.3" stroke="black" stroke-width="1.25"/>
<path d="M2.59375 5H11.4375L10.5581 11.5664C10.5248 11.8146 10.313 12 10.0625 12H3.93944C3.68812 12 3.47585 11.8134 3.44358 11.5642L2.59375 5Z" stroke="black" stroke-width="1.25"/>
</svg>

After

Width:  |  Height:  |  Size: 527 B

View File

@@ -0,0 +1,6 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 11C5.46973 11 4.1268 11.1873 3.31522 11.3327C2.94367 11.3992 2.60079 11.0563 2.66733 10.6848C2.81266 9.8732 3 8.53027 3 7C3 5.8387 2.89211 4.78529 2.77656 3.99011C2.73589 3.71017 3.19546 3.51715 3.36119 3.7464C4.09612 4.76304 5.23301 6.23301 6.5 7.5C7.76699 8.76699 9.23696 9.90388 10.2536 10.6388C10.4828 10.8045 10.2898 11.2641 10.0099 11.2234C9.21472 11.1079 8.1613 11 7 11Z" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.365 3.8478L10.3381 1.82088" stroke="black" stroke-opacity="0.3" stroke-width="1.25" stroke-linecap="round"/>
<path d="M11.3516 7.36803L6.64062 2.64155" stroke="black" stroke-opacity="0.6" stroke-width="1.25" stroke-linecap="round"/>
<rect x="2.72266" y="8.73828" width="3.58525" height="2.72899" rx="0.5" transform="rotate(45 2.72266 8.73828)" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 950 B

View File

@@ -0,0 +1,6 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 10L12 10.8374C12 10.9431 11.9665 11.046 11.9044 11.1315L11.1498 12.1691C11.0557 12.2985 10.9054 12.375 10.7454 12.375L3.25461 12.375C3.09464 12.375 2.94433 12.2985 2.85024 12.1691L2.09563 11.1315C2.03348 11.046 2 10.9431 2 10.8374L2 2" stroke="black" stroke-width="1.25" stroke-linecap="round"/>
<path d="M2 12V10L7 11H12V12H2Z" fill="black"/>
<path d="M5.63246 2.04415C6.44914 2.31638 7 3.08066 7 3.94152V10.7306C7 11.0924 6.62757 11.3345 6.29693 11.1875L2.79693 9.63197C2.61637 9.55172 2.5 9.37266 2.5 9.17506V1.69371C2.5 1.35243 2.83435 1.11145 3.15811 1.21937L5.63246 2.04415Z" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8.5 2C7.67157 2 7 2.67157 7 3.5V12C7 11.1954 10.2366 11.0382 11.5017 11.0075C11.7778 11.0008 12 10.7761 12 10.5V2.5C12 2.22386 11.7761 2 11.5 2H8.5Z" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,4 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 10.5C12 10.7761 11.7761 11 11.5 11H2.5C2.22386 11 2 10.7761 2 10.5V4.88C2 4.60386 2.22386 4.38 2.5 4.38H4.4342C4.61518 4.38 4.78204 4.2822 4.87046 4.12428L5.35681 3.25572C5.44524 3.0978 5.61209 3 5.79308 3H8.20692C8.38791 3 8.55476 3.0978 8.64319 3.25572L9.12954 4.12428C9.21796 4.2822 9.38482 4.38 9.5658 4.38H11.5C11.7761 4.38 12 4.60386 12 4.88V10.5Z" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.005 9C7.90246 9 8.63 8.27246 8.63 7.375C8.63 6.47754 7.90246 5.75 7.005 5.75C6.10754 5.75 5.38 6.47754 5.38 7.375C5.38 8.27246 6.10754 9 7.005 9Z" fill="black" fill-opacity="0.3" stroke="black" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 850 B

View File

@@ -0,0 +1,3 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.63281 5.66406L6.99344 8.89844L10.3672 5.66406" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 246 B

View File

@@ -0,0 +1,3 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.35938 3.63281L5.125 6.99344L8.35938 10.3672" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 244 B

View File

@@ -0,0 +1,3 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.64062 3.64062L8.89062 7.00125L5.64062 10.375" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 245 B

View File

@@ -0,0 +1,3 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.63281 8.36719L6.99344 5.13281L10.3672 8.36719" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 246 B

View File

@@ -0,0 +1,4 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.375 2C2.5 2 2.5 3.5 2.5 4.5C2.5 5.5 2 6.50106 1 7C2 7.50106 2.5 8.5 2.5 9.5C2.5 10.5 2.5 12 4.375 12" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.63281 2C11.5078 2 11.5078 3.5 11.5078 4.5C11.5078 5.5 12.0078 6.50106 13.0078 7C12.0078 7.50106 11.5078 8.5 11.5078 9.5C11.5078 10.5 11.5078 12 9.63281 12" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 553 B

View File

@@ -0,0 +1,4 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.46115 8.43419C7.30678 8.43419 8.92229 7.43411 8.92229 5.21171C8.92229 2.98933 7.30678 1.98926 5.46115 1.98926C3.61553 1.98926 2 2.98933 2 5.21171C2 6.028 2.21794 6.67935 2.58519 7.17685C2.7184 7.35732 2.69033 7.77795 2.58387 7.97539C2.32908 8.44793 2.81048 8.9657 3.33372 8.84571C3.72539 8.75597 4.13621 8.63447 4.49574 8.4715C4.62736 8.41181 4.7727 8.38777 4.91631 8.40402C5.09471 8.42416 5.27678 8.43419 5.46115 8.43419Z" fill="black" fill-opacity="0.33" stroke="black" stroke-width="0.990499" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.6055 5.87971C11.4329 5.66762 11.1208 5.6357 10.9088 5.80842C10.6967 5.98114 10.6648 6.29308 10.8375 6.50518L11.6055 5.87971ZM6.4361 10.4149C6.21522 10.2536 5.90539 10.3018 5.74404 10.5226C5.58268 10.7435 5.6309 11.0533 5.85177 11.2147L6.4361 10.4149ZM12.3808 8.25929C12.3808 7.28754 12.1013 6.48847 11.6055 5.87971L10.8375 6.50518C11.1712 6.91492 11.3903 7.48485 11.3903 8.25929H12.3808ZM11.6988 10.5186C12.137 9.92499 12.3808 9.16705 12.3808 8.25929H11.3903C11.3903 8.98414 11.1982 9.52892 10.9019 9.93034L11.6988 10.5186ZM9.1854 11.9702C9.58603 12.1518 10.0316 12.2822 10.4412 12.3761L10.6625 11.4106C10.2888 11.3249 9.91276 11.2124 9.59435 11.068L9.1854 11.9702ZM8.42443 11.977C8.62663 11.977 8.8273 11.9661 9.02494 11.9437L8.91361 10.9595C8.75447 10.9775 8.59097 10.9865 8.42443 10.9865V11.977ZM5.85177 11.2147C6.5749 11.743 7.49105 11.977 8.42443 11.977V10.9865C7.64656 10.9865 6.9503 10.7906 6.4361 10.4149L5.85177 11.2147ZM9.59435 11.068C9.38377 10.9726 9.14869 10.9329 8.91361 10.9595L9.02494 11.9437C9.07704 11.9378 9.13271 11.9463 9.1854 11.9702L9.59435 11.068ZM10.8658 11.2581C10.8784 11.2813 10.8772 11.2932 10.8762 11.2995C10.8746 11.3097 10.8681 11.3291 10.8481 11.3517C10.8049 11.4004 10.7343 11.4271 10.6625 11.4106L10.4412 12.3761C10.8927 12.4796 11.3244 12.3073 11.5891 12.0089C11.8602 11.7033 11.9778 11.2332 11.7377 10.7879L10.8658 11.2581ZM10.9019 9.93034C10.7358 10.1554 10.7116 10.4435 10.7161 10.6293C10.7209 10.8293 10.7634 11.0682 10.8658 11.2581L11.7377 10.7879C11.739 10.7905 11.7304 10.7736 11.7214 10.7331C11.713 10.6954 11.7074 10.6506 11.7063 10.6054C11.7052 10.5594 11.709 10.5234 11.7139 10.5006C11.7196 10.4738 11.7217 10.4876 11.6988 10.5186L10.9019 9.93034Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,5 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<ellipse cx="7" cy="4" rx="5" ry="2" fill="black" fill-opacity="0.3" stroke="black" stroke-width="1.25"/>
<path d="M12 4V10C12 11.1046 9.76142 12 7 12C4.23858 12 2 11.1046 2 10V4" stroke="black" stroke-width="1.25"/>
<path d="M12 7C12 8.10457 9.76142 9 7 9C4.23858 9 2 8.10457 2 7" stroke="black" stroke-width="1.25"/>
</svg>

After

Width:  |  Height:  |  Size: 422 B

View File

@@ -0,0 +1,4 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.5413 7.3125C12.6529 7.11913 12.6529 6.88088 12.5413 6.6875L10.0413 2.35738C9.92962 2.164 9.72329 2.04488 9.5 2.04488L4.5 2.04488C4.27671 2.04488 4.07038 2.164 3.95873 2.35738L1.45873 6.6875C1.34709 6.88088 1.34709 7.11913 1.45873 7.3125L3.95873 11.6426C4.07038 11.836 4.27671 11.9551 4.5 11.9551L9.5 11.9551C9.72329 11.9551 9.92962 11.836 10.0413 11.6426L12.5413 7.3125Z" stroke="black" stroke-width="1.25" stroke-linejoin="round"/>
<path d="M6.75 4.14434C6.9047 4.05502 7.0953 4.05502 7.25 4.14434L9.34808 5.35566C9.50278 5.44498 9.59808 5.61004 9.59808 5.78868V8.21132C9.59808 8.38996 9.50278 8.55502 9.34808 8.64434L7.25 9.85566C7.0953 9.94498 6.9047 9.94498 6.75 9.85566L4.65192 8.64434C4.49722 8.55502 4.40192 8.38996 4.40192 8.21132L4.40192 5.78868C4.40192 5.61004 4.49722 5.44498 4.65192 5.35566L6.75 4.14434Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 949 B

View File

@@ -0,0 +1,5 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 4H10" stroke="black" stroke-opacity="0.6" stroke-width="1.25" stroke-linecap="round"/>
<path d="M2 7H12" stroke="black" stroke-width="1.25" stroke-linecap="round"/>
<path d="M2 10H8" stroke="black" stroke-opacity="0.3" stroke-width="1.25" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 379 B

View File

@@ -0,0 +1,159 @@
{
"suffixes": {
"aac": "audio",
"bash": "terminal",
"bmp": "image",
"c": "code",
"conf": "settings",
"cpp": "code",
"cc": "code",
"css": "code",
"doc": "document",
"docx": "document",
"eslintrc": "eslint",
"eslintrc.js": "eslint",
"eslintrc.json": "eslint",
"flac": "audio",
"fish": "terminal",
"gitattributes": "vcs",
"gitignore": "vcs",
"gitmodules": "vcs",
"gif": "image",
"go": "code",
"h": "code",
"handlebars": "code",
"hbs": "template",
"htm": "template",
"html": "template",
"svelte": "template",
"hpp": "code",
"ico": "image",
"ini": "settings",
"java": "code",
"jpeg": "image",
"jpg": "image",
"js": "code",
"json": "storage",
"lock": "lock",
"log": "log",
"md": "document",
"mdx": "document",
"mp3": "audio",
"mp4": "video",
"ods": "document",
"odp": "document",
"odt": "document",
"ogg": "video",
"pdf": "document",
"php": "code",
"png": "image",
"ppt": "document",
"pptx": "document",
"prettierrc": "prettier",
"prettierignore": "prettier",
"ps1": "terminal",
"psd": "image",
"py": "code",
"rb": "code",
"rkt": "code",
"rs": "rust",
"rtf": "document",
"scm": "code",
"sh": "terminal",
"bashrc": "terminal",
"bash_profile": "terminal",
"bash_aliases": "terminal",
"bash_logout": "terminal",
"profile": "terminal",
"zshrc": "terminal",
"zshenv": "terminal",
"zsh_profile": "terminal",
"zsh_aliases": "terminal",
"zsh_histfile": "terminal",
"zlogin": "terminal",
"sql": "code",
"svg": "image",
"swift": "code",
"tiff": "image",
"toml": "toml",
"ts": "typescript",
"tsx": "code",
"txt": "document",
"wav": "audio",
"webm": "video",
"xls": "document",
"xlsx": "document",
"xml": "template",
"yaml": "settings",
"yml": "settings",
"zsh": "terminal"
},
"types": {
"audio": {
"icon": "icons/file_icons/audio.svg"
},
"code": {
"icon": "icons/file_icons/code.svg"
},
"collapsed_chevron": {
"icon": "icons/file_icons/chevron_right.svg"
},
"collapsed_folder": {
"icon": "icons/file_icons/folder.svg"
},
"default": {
"icon": "icons/file_icons/file.svg"
},
"document": {
"icon": "icons/file_icons/book.svg"
},
"eslint": {
"icon": "icons/file_icons/eslint.svg"
},
"expanded_chevron": {
"icon": "icons/file_icons/chevron_down.svg"
},
"expanded_folder": {
"icon": "icons/file_icons/folder_open.svg"
},
"image": {
"icon": "icons/file_icons/image.svg"
},
"lock": {
"icon": "icons/file_icons/lock.svg"
},
"log": {
"icon": "icons/file_icons/info.svg"
},
"prettier": {
"icon": "icons/file_icons/prettier.svg"
},
"rust": {
"icon": "icons/file_icons/rust.svg"
},
"settings": {
"icon": "icons/file_icons/settings.svg"
},
"storage": {
"icon": "icons/file_icons/database.svg"
},
"template": {
"icon": "icons/file_icons/html.svg"
},
"terminal": {
"icon": "icons/file_icons/terminal.svg"
},
"toml": {
"icon": "icons/file_icons/toml.svg"
},
"typescript": {
"icon": "icons/file_icons/typescript.svg"
},
"vcs": {
"icon": "icons/file_icons/git.svg"
},
"video": {
"icon": "icons/file_icons/video.svg"
}
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 43 KiB

View File

@@ -0,0 +1,4 @@
<svg width="15" height="14" viewBox="0 0 15 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.49165 6.13802C3.4991 5.86198 3.72386 5.64062 4 5.64062H13C13.2761 5.64062 13.4991 5.86198 13.4916 6.13802C13.4529 7.57407 13.2341 11.625 12 11.625H2C3.23412 11.625 3.45287 7.57407 3.49165 6.13802Z" fill="black" stroke="black" stroke-width="1.25" stroke-linejoin="round"/>
<path d="M4.00781 11.625H2.42841C2.18186 11.625 1.97212 11.4453 1.93432 11.2017L0.651964 2.93603C0.604944 2.63296 0.839355 2.35938 1.14605 2.35938H4.6164C4.95332 2.35938 5.26759 2.52904 5.45244 2.81072L5.8125 3.35938H8.89008C9.37767 3.35938 9.79418 3.71103 9.87593 4.19171L10.125 5.65625" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 760 B

View File

@@ -0,0 +1,6 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="4" cy="10" r="2" stroke="black" stroke-width="1.25"/>
<circle cx="10" cy="4" r="2" fill="black" fill-opacity="0.3" stroke="black" stroke-width="1.25"/>
<line x1="3.625" y1="2.625" x2="3.625" y2="7.375" stroke="black" stroke-width="1.25" stroke-linecap="round"/>
<path d="M10 6V6C10 8.20914 8.20914 10 6 10V10" stroke="black" stroke-width="1.25"/>
</svg>

After

Width:  |  Height:  |  Size: 462 B

View File

@@ -0,0 +1,6 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<line x1="10.2795" y1="2.63847" x2="7.74785" y2="11.0142" stroke="black" stroke-width="1.25" stroke-linecap="round"/>
<line x1="6.26624" y1="2.99597" x2="3.7346" y2="11.3717" stroke="black" stroke-width="1.25" stroke-linecap="round"/>
<line x1="3.15982" y1="5.3799" x2="11.9098" y2="5.3799" stroke="black" stroke-width="1.25" stroke-linecap="round"/>
<line x1="2.0983" y1="8.62407" x2="10.8483" y2="8.62407" stroke="black" stroke-width="1.25" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 571 B

View File

@@ -0,0 +1,5 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.15732 3.17108L5.84268 10.8289" stroke="black" stroke-width="1.25" stroke-linecap="round"/>
<path d="M4 5L2 7L4 9" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10 9L12 7L10 5" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 423 B

View File

@@ -0,0 +1,7 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.5 3C6.91421 3 7.25 2.66421 7.25 2.25C7.25 1.83579 6.91421 1.5 6.5 1.5C6.08579 1.5 5.75 1.83579 5.75 2.25C5.75 2.66421 6.08579 3 6.5 3Z" fill="black" stroke="black" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6 8L9 5L12 8H6Z" fill="black" fill-opacity="0.3"/>
<path d="M2 10L5 7L7.375 9.375" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6 8L7.5 6.5L9 5L10.5 6.5L12 8" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3.375 2H2.5C2.22386 2 2 2.22386 2 2.5V11.5C2 11.7761 2.22386 12 2.5 12H7.35938M9.64062 2H11.5C11.7761 2 12 2.22386 12 2.5V11.5C12 11.7761 11.7761 12 11.5 12H10.125" stroke="black" stroke-width="1.25" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 865 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 46 KiB

View File

@@ -0,0 +1,6 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="3" y="5" width="8" height="7" rx="0.5" stroke="black" stroke-width="1.25"/>
<path d="M4 4C4 2.89543 4.89543 2 6 2H8C9.10457 2 10 2.89543 10 4V5H4V4Z" stroke="black" stroke-opacity="0.6" stroke-width="1.25"/>
<circle cx="7" cy="8" r="1" fill="black"/>
<path d="M7 8V9.375" stroke="black" stroke-width="1.25" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 444 B

View File

@@ -0,0 +1,3 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 12L9.41379 9.41379M2 6.31034C2 3.92981 3.92981 2 6.31034 2C8.6909 2 10.6207 3.92981 10.6207 6.31034C10.6207 8.6909 8.6909 10.6207 6.31034 10.6207C3.92981 10.6207 2 8.6909 2 6.31034Z" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 383 B

View File

@@ -0,0 +1,8 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.03125 2.96875C2.03125 2.41647 2.47897 1.96875 3.03125 1.96875H5V12H3.03125C2.47897 12 2.03125 11.5523 2.03125 11V2.96875Z" fill="black" fill-opacity="0.3"/>
<rect x="2" y="2" width="10" height="10" rx="0.5" stroke="black" stroke-width="1.25"/>
<path d="M9.5 5L7.5 5" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.5 7H7.5" stroke="black" stroke-opacity="0.6" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.5 9H7.5" stroke="black" stroke-opacity="0.3" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5 2V13" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 820 B

View File

@@ -0,0 +1,4 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.62677 3.88472L6.99983 6.78517M1.62677 3.88472L1.63137 9.90006L7.00442 12.8005M1.62677 3.88472L4.31117 2.54211M6.99983 6.78517L7.00442 12.8005M6.99983 6.78517L9.68414 5.33084M7.00442 12.8005L12.373 9.89186L12.3684 3.87652M4.31117 2.54211L6.99556 1.1995L12.3684 3.87652M4.31117 2.54211L9.68414 5.33084M12.3684 3.87652L9.68414 5.33084" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.03125 12.5625V6.78125L1.5625 3.9375V9.75L7.03125 12.5625Z" fill="black" fill-opacity="0.3"/>
</svg>

After

Width:  |  Height:  |  Size: 637 B

View File

@@ -0,0 +1,3 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 3V11M11 7H3" stroke="black" stroke-width="1.25" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 188 B

View File

@@ -0,0 +1,12 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 2.86328H8.51563" stroke="black" stroke-width="1.25" stroke-linecap="round"/>
<path d="M11 2.86328L12 2.86328" stroke="black" stroke-opacity="0.3" stroke-width="1.25" stroke-linecap="round"/>
<path d="M9.64062 5.6263L12 5.6263" stroke="black" stroke-opacity="0.6" stroke-width="1.25" stroke-linecap="round"/>
<path d="M4.79688 5.6263L7.15625 5.6263" stroke="black" stroke-width="1.25" stroke-linecap="round"/>
<path d="M2 5.6263L2.35937 5.6263" stroke="black" stroke-opacity="0.3" stroke-width="1.25" stroke-linecap="round"/>
<path d="M7.15625 8.3737L12 8.3737" stroke="black" stroke-width="1.25" stroke-linecap="round"/>
<path d="M2 8.3737L4.64062 8.3737" stroke="black" stroke-opacity="0.6" stroke-width="1.25" stroke-linecap="round"/>
<path d="M2 11.1094H3.54687" stroke="black" stroke-width="1.25" stroke-linecap="round"/>
<path d="M5.97656 11.1094H8.35938" stroke="black" stroke-width="1.25" stroke-linecap="round"/>
<path d="M10.8203 11.1094L12 11.1094" stroke="black" stroke-opacity="0.3" stroke-width="1.25" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,5 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.03125 2V2.03125M2.03125 8C2.03125 10 5 10 5 10M2.03125 8V2.03125M2.03125 8L2.03125 11M2.03125 2.03125C2.03125 4 5 4 5 4" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<rect x="7.375" y="2.375" width="4.25" height="3.25" rx="1.125" fill="black" fill-opacity="0.33" stroke="black" stroke-width="1.25"/>
<rect x="7.375" y="8.375" width="4.25" height="3.25" rx="1.125" fill="black" fill-opacity="0.33" stroke="black" stroke-width="1.25"/>
</svg>

After

Width:  |  Height:  |  Size: 588 B

View File

@@ -0,0 +1,11 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 12C4.97279 12 3.22735 10.7936 2.4425 9.0595M7 2C9.11228 2 10.9186 3.30981 11.6512 5.16152" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="1.65625" cy="1.67188" r="0.625" fill="black" fill-opacity="0.5"/>
<circle cx="3.71094" cy="1.67188" r="0.625" fill="black" fill-opacity="0.5"/>
<circle cx="4.96094" cy="3.36719" r="0.625" fill="black" fill-opacity="0.5"/>
<circle cx="3.71094" cy="4.79688" r="0.625" fill="black" fill-opacity="0.5"/>
<circle cx="4.60156" cy="6.67188" r="0.625" fill="black" fill-opacity="0.5"/>
<circle cx="1.65625" cy="4.17188" r="0.625" fill="black" fill-opacity="0.5"/>
<circle cx="1.65625" cy="6.67188" r="0.625" fill="black" fill-opacity="0.5"/>
<path d="M10.7802 10.8195C10.838 10.8195 10.8906 10.8527 10.9155 10.9048L11.7174 12.5811C11.8088 12.7721 12.0017 12.8938 12.2135 12.8938H12.3394C12.7483 12.8938 13.0142 12.4635 12.8314 12.0978L12.1619 10.7589C12.1232 10.6816 12.1582 10.5823 12.241 10.5349C12.7565 10.2397 13.0695 9.66858 13.0695 9.00391C13.0695 8.43361 12.8777 7.97006 12.5248 7.64951C12.1725 7.3295 11.6652 7.15703 11.043 7.15703H9.49609C9.19234 7.15703 8.94609 7.40327 8.94609 7.70703V12.3438C8.94609 12.6475 9.19234 12.8938 9.49609 12.8938H9.60156C9.90532 12.8938 10.1516 12.6475 10.1516 12.3438V10.9695C10.1516 10.8867 10.2187 10.8195 10.3016 10.8195H10.7802ZM10.1516 8.31328C10.1516 8.23044 10.2187 8.16328 10.3016 8.16328H10.8984C11.2023 8.16328 11.4371 8.2449 11.5954 8.38814C11.7529 8.5308 11.8406 8.73993 11.8406 9.00781C11.8406 9.28155 11.751 9.49461 11.5909 9.63971C11.4302 9.7854 11.1925 9.86797 10.8867 9.86797H10.3016C10.2187 9.86797 10.1516 9.80081 10.1516 9.71797V8.31328Z" fill="black" stroke="black" stroke-width="0.1"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,5 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.10517 5.8012C4.07193 5.73172 4.00176 5.6875 3.92475 5.6875H3.44609C3.33564 5.6875 3.24609 5.77704 3.24609 5.8875V7.26172C3.24609 7.53786 3.02224 7.76172 2.74609 7.76172H2.64062C2.36448 7.76172 2.14062 7.53786 2.14062 7.26172V2.625C2.14062 2.34886 2.36448 2.125 2.64062 2.125H4.1875C5.41406 2.125 6.16406 2.80469 6.16406 3.92188C6.16406 4.57081 5.85885 5.12418 5.36073 5.40943C5.25888 5.46775 5.20921 5.59421 5.2617 5.69918L5.93117 7.03811C6.09739 7.37056 5.85564 7.76172 5.48395 7.76172H5.35806C5.16552 7.76172 4.99009 7.65117 4.907 7.47748L4.10517 5.8012ZM3.44609 3.03125C3.33564 3.03125 3.24609 3.12079 3.24609 3.23125V4.63594C3.24609 4.74639 3.33564 4.83594 3.44609 4.83594H4.03125C4.66016 4.83594 5.03516 4.49609 5.03516 3.92578C5.03516 3.36719 4.66797 3.03125 4.04297 3.03125H3.44609Z" fill="black" fill-opacity="0.5"/>
<path d="M3.92475 5.7375C3.98251 5.7375 4.03514 5.77067 4.06006 5.82277L4.8619 7.49905C4.95329 7.69011 5.14627 7.81172 5.35806 7.81172H5.48395C5.89281 7.81172 6.15873 7.38145 5.97589 7.01575L5.30642 5.67682C5.26778 5.59953 5.30269 5.50028 5.38557 5.45282C5.90107 5.15762 6.21406 4.58655 6.21406 3.92188C6.21406 3.35158 6.02226 2.88803 5.66936 2.56748C5.31705 2.24747 4.80973 2.075 4.1875 2.075H2.64062C2.33687 2.075 2.09062 2.32124 2.09062 2.625V7.26172C2.09062 7.56548 2.33687 7.81172 2.64062 7.81172H2.74609C3.04985 7.81172 3.29609 7.56548 3.29609 7.26172V5.8875C3.29609 5.80466 3.36325 5.7375 3.44609 5.7375H3.92475ZM3.29609 3.23125C3.29609 3.14841 3.36325 3.08125 3.44609 3.08125H4.04297C4.34688 3.08125 4.58164 3.16287 4.73988 3.30611C4.89748 3.44876 4.98516 3.6579 4.98516 3.92578C4.98516 4.19952 4.89553 4.41258 4.73546 4.55768C4.57475 4.70337 4.33706 4.78594 4.03125 4.78594H3.44609C3.36325 4.78594 3.29609 4.71878 3.29609 4.63594V3.23125Z" stroke="black" stroke-opacity="0.5" stroke-width="0.1"/>
<path d="M9.32812 6.65625V9.32812M9.32812 12V9.32812M12 9.32812H9.32812M6.65625 9.32812H9.32812M9.32812 9.32812L11.1094 7.54688M9.32812 9.32812L7.54688 11.1094M9.32812 9.32812L11.1094 11.1094M9.32812 9.32812L7.54688 7.54688" stroke="black" stroke-width="1.25" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,5 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.96454 5.6762C3.93131 5.60672 3.86114 5.5625 3.78412 5.5625H3.30547C3.19501 5.5625 3.10547 5.65204 3.10547 5.7625V7.13672C3.10547 7.41286 2.88161 7.63672 2.60547 7.63672H2.5C2.22386 7.63672 2 7.41286 2 7.13672V2.5C2 2.22386 2.22386 2 2.5 2H4.04688C5.27344 2 6.02344 2.67969 6.02344 3.79688C6.02344 4.44581 5.71823 4.99918 5.2201 5.28443C5.11826 5.34275 5.06859 5.46921 5.12107 5.57418L5.79054 6.91311C5.95677 7.24556 5.71502 7.63672 5.34333 7.63672H5.21743C5.02489 7.63672 4.84946 7.52617 4.76638 7.35248L3.96454 5.6762ZM3.30547 2.90625C3.19501 2.90625 3.10547 2.99579 3.10547 3.10625V4.51094C3.10547 4.62139 3.19501 4.71094 3.30547 4.71094H3.89062C4.51953 4.71094 4.89453 4.37109 4.89453 3.80078C4.89453 3.24219 4.52734 2.90625 3.90234 2.90625H3.30547Z" fill="black" fill-opacity="0.5"/>
<path d="M3.78412 5.6125C3.84188 5.6125 3.89451 5.64567 3.91944 5.69777L4.72127 7.37405C4.81266 7.56511 5.00564 7.68672 5.21743 7.68672H5.34333C5.75219 7.68672 6.01811 7.25645 5.83526 6.89075L5.1658 5.55182C5.12715 5.47453 5.16207 5.37528 5.24495 5.32782C5.76044 5.03262 6.07344 4.46155 6.07344 3.79688C6.07344 3.22658 5.88164 2.76303 5.52873 2.44248C5.17642 2.12247 4.6691 1.95 4.04688 1.95H2.5C2.19624 1.95 1.95 2.19624 1.95 2.5V7.13672C1.95 7.44048 2.19624 7.68672 2.5 7.68672H2.60547C2.90923 7.68672 3.15547 7.44048 3.15547 7.13672V5.7625C3.15547 5.67966 3.22263 5.6125 3.30547 5.6125H3.78412ZM3.15547 3.10625C3.15547 3.02341 3.22263 2.95625 3.30547 2.95625H3.90234C4.20626 2.95625 4.44101 3.03787 4.59926 3.18111C4.75686 3.32376 4.84453 3.5329 4.84453 3.80078C4.84453 4.07452 4.75491 4.28758 4.59484 4.43268C4.43413 4.57837 4.19643 4.66094 3.89062 4.66094H3.30547C3.22263 4.66094 3.15547 4.59378 3.15547 4.51094V3.10625Z" stroke="black" stroke-opacity="0.5" stroke-width="0.1"/>
<path d="M7.5 5.88672C9.433 5.88672 11 7.45372 11 9.38672V12M11 12L13 10M11 12L9 10" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,4 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.27935 9.98207C4.32063 9.4038 3.9204 8.89049 3.35998 8.80276L2.60081 8.68387C2.37979 8.64945 2.20167 8.48001 2.15225 8.25614L2.01378 7.63511C1.96382 7.41235 2.05233 7.1807 2.23696 7.05125L2.8631 6.61242C3.33337 6.28297 3.47456 5.6369 3.18621 5.13364L2.79467 4.45092C2.68118 4.25261 2.69801 4.00374 2.83757 3.82321L3.22314 3.32436C3.3627 3.14438 3.59621 3.06994 3.81071 3.13772L4.57531 3.37769C5.11944 3.54879 5.70048 3.26159 5.90683 2.71886L6.1811 1.99782C6.26255 1.78395 6.46345 1.64285 6.68772 1.6423L7.31007 1.64063C7.53434 1.64007 7.73579 1.78006 7.81834 1.99337L8.09965 2.72275C8.30821 3.26214 8.88655 3.54712 9.42903 3.37714L10.1632 3.14716C10.3772 3.07994 10.6096 3.15382 10.7492 3.3327L11.1374 3.83099" stroke="black" stroke-opacity="0.6" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.76988 10.5933C7.76988 10.6595 7.8236 10.7133 7.88988 10.7133H7.97588C8.32602 10.7133 8.60988 10.9971 8.60988 11.3472C8.60988 11.6974 8.32602 11.9812 7.97588 11.9812H6.05587C5.70573 11.9812 5.42188 11.6974 5.42188 11.3472C5.42188 10.9971 5.70573 10.7133 6.05587 10.7133H6.14188C6.20815 10.7133 6.26188 10.6595 6.26188 10.5933V6.66925C6.26188 6.60298 6.20815 6.54925 6.14188 6.54925H6.05588C5.70573 6.54925 5.42188 6.2654 5.42188 5.91525C5.42188 5.5651 5.70573 5.28125 6.05588 5.28125H8.89988C10.0518 5.28125 11.8619 5.71487 11.8619 7.15185C11.8619 7.67078 11.7284 8.10362 11.4642 8.45348C11.1981 8.79765 10.8458 9.05637 10.4056 9.22931C10.3782 9.24007 10.3673 9.27304 10.3829 9.29801L11.2163 10.6342C11.247 10.6834 11.3008 10.7133 11.3588 10.7133H11.7319C12.082 10.7133 12.3659 10.9971 12.3659 11.3472C12.3659 11.6974 12.082 11.9812 11.7319 11.9812H10.5637C10.4955 11.9812 10.432 11.9465 10.3952 11.889L8.96523 9.65406C8.92847 9.59661 8.86496 9.56185 8.79676 9.56185H7.96988C7.85942 9.56185 7.76988 9.65139 7.76988 9.76185V10.5933ZM8.61188 6.54925C9.02963 6.54925 10.125 6.54925 10.2339 7.18785C10.2975 7.56123 10.1181 7.86557 9.88118 8.07715C9.64227 8.29046 9.20527 8.38985 8.58788 8.38985H7.86988C7.81465 8.38985 7.76988 8.34508 7.76988 8.28985V6.64925C7.76988 6.59402 7.81465 6.54925 7.86988 6.54925H8.61188Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,4 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.60081 8.94324L3.35998 9.06214C3.9204 9.14986 4.32063 9.66317 4.27935 10.2414L4.22342 11.0252C4.20713 11.2536 4.32877 11.4686 4.53024 11.568L5.09174 11.8446C5.29321 11.9441 5.53379 11.9068 5.69834 11.7519L6.26255 11.2186C6.67855 10.8253 7.32041 10.8253 7.7369 11.2186L8.3011 11.7519C8.46565 11.9074 8.70572 11.9441 8.90772 11.8446L9.47027 11.5674C9.67124 11.4686 9.79234 11.2541 9.77607 11.0264L9.72007 10.2414C9.67883 9.66317 10.079 9.14986 10.6394 9.06214L11.3986 8.94324C11.6197 8.90883 11.7978 8.73938 11.8477 8.51607L11.9862 7.89504C12.0362 7.67172 11.9477 7.44007 11.763 7.31117L11.1293 6.86731C10.6617 6.53959 10.5189 5.89966 10.8013 5.3969L11.1841 4.71586C11.2954 4.51754 11.277 4.26923 11.1374 4.09036L10.7492 3.59207C10.6096 3.41319 10.3772 3.33932 10.1632 3.40653L9.42903 3.63651C8.88655 3.80649 8.30821 3.52152 8.09965 2.98213L7.81834 2.25275C7.73579 2.03944 7.53434 1.89945 7.31007 1.9L6.68772 1.90167C6.46345 1.90222 6.26255 2.04333 6.1811 2.25719L5.90683 2.97824C5.70048 3.52097 5.11944 3.80816 4.57531 3.63706L3.81071 3.39709C3.59621 3.32932 3.3627 3.40375 3.22314 3.58374L2.83757 4.08258C2.69801 4.26312 2.68118 4.51199 2.79467 4.7103L3.18621 5.39302C3.47456 5.89628 3.33337 6.54235 2.8631 6.87179L2.23696 7.31062C2.05233 7.44007 1.96382 7.67173 2.01378 7.89448L2.15225 8.51552C2.20167 8.73938 2.37979 8.90883 2.60081 8.94324Z" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8.14913 5.85093L8.14909 5.85089C7.51453 5.21637 6.48549 5.21637 5.85092 5.85089L5.85089 5.85092C5.21637 6.48549 5.21637 7.51453 5.85089 8.14909L5.85093 8.14913C6.48549 8.78362 7.51452 8.78362 8.14908 8.14913L8.14913 8.14908C8.78362 7.51452 8.78362 6.48549 8.14913 5.85093Z" fill="black" fill-opacity="0.3" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,5 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.65625 2.5C1.65625 2.22386 1.88011 2 2.15625 2H11.8437C12.1199 2 12.3438 2.22386 12.3438 2.5V11.5C12.3438 11.7761 12.1199 12 11.8437 12H2.15625C1.88011 12 1.65625 11.7761 1.65625 11.5V2.5Z" stroke="black" stroke-width="1.25"/>
<path d="M4.375 9L6.375 7L4.375 5" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.625 9L9.90625 9" stroke="black" stroke-width="1.25" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 549 B

View File

@@ -0,0 +1,5 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5 5H9" stroke="black" stroke-width="1.25" stroke-linecap="round"/>
<path d="M7 5L7 10" stroke="black" stroke-width="1.25" stroke-linecap="round"/>
<path d="M4 2H2.5C2.22386 2 2 2.22386 2 2.5V11.5C2 11.7761 2.22386 12 2.5 12H4M10 2H11.5C11.7761 2 12 2.22386 12 2.5V11.5C12 11.7761 11.7761 12 11.5 12H10" stroke="black" stroke-opacity="0.6" stroke-width="1.25" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 497 B

View File

@@ -0,0 +1,5 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 4.375V2.5C12 2.22386 11.7761 2 11.5 2H2.5C2.22386 2 2 2.22386 2 2.5V11.5C2 11.7761 2.22386 12 2.5 12H3.375" stroke="black" stroke-opacity="0.6" stroke-width="1.25" stroke-linecap="round"/>
<path d="M10.6836 7.82805C10.7933 7.65392 10.9823 7.57377 11.174 7.57377C11.2904 7.57377 11.4019 7.59384 11.5092 7.62792C11.8324 7.73069 12.2148 7.63925 12.3392 7.32368L12.3773 7.22707C12.4703 6.99131 12.3823 6.71761 12.1522 6.61154C11.8328 6.46436 11.4984 6.375 11.1262 6.375C9.87708 6.375 8.91935 7.60671 9.4239 8.84869C9.54205 9.13951 9.74219 9.36166 9.9515 9.54337C10.1061 9.6776 10.2858 9.80516 10.4475 9.92002C10.4972 9.95529 10.5452 9.98936 10.5903 10.0221C11.0283 10.34 11.2526 10.5876 11.2526 10.9466C11.2526 11.1518 11.1622 11.3133 11.016 11.4128C10.8777 11.5071 10.7055 11.5357 10.5454 11.5222C10.3931 11.5093 10.2529 11.4717 10.1214 11.4196C9.81633 11.2989 9.45533 11.4015 9.33641 11.7073L9.2814 11.8487C9.19162 12.0796 9.2749 12.3463 9.49799 12.4539C10.0894 12.7391 10.7377 12.8279 11.3915 12.5872C12.0569 12.3423 12.595 11.7708 12.595 10.9068C12.595 10.1301 12.1336 9.69583 11.6966 9.36109C11.606 9.29163 11.5259 9.23292 11.4493 9.17682C11.3259 9.08638 11.1964 8.99109 11.0734 8.88536C10.8937 8.73082 10.7518 8.57274 10.6595 8.38613C10.5746 8.21464 10.5815 7.99013 10.6836 7.82805Z" fill="black"/>
<path d="M6.98644 7.70936H7.69396C7.98162 7.70936 8.21481 7.47617 8.21481 7.18851V7.02346C8.21481 6.73581 7.98162 6.50261 7.69396 6.50261H4.96848C4.68082 6.50261 4.44763 6.73581 4.44763 7.02346V7.18851C4.44763 7.47617 4.68082 7.70936 4.96848 7.70936H5.676V12.102C5.676 12.3896 5.90919 12.6228 6.19685 12.6228H6.46559C6.75325 12.6228 6.98644 12.3896 6.98644 12.102V7.70936Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,4 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.65625 2H11.8437C12.1199 2 12.3438 2.22386 12.3438 2.5V9.34375M12.3438 12H2.15625C1.88011 12 1.65625 11.7761 1.65625 11.5V4.65625" stroke="black" stroke-width="1.25" stroke-linecap="round"/>
<path d="M9 7.01562L5.65624 9.3125L5.65624 4.6875L9 7.01562Z" fill="black" fill-opacity="0.3" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 483 B

View File

@@ -0,0 +1,4 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.5 1.5H13.5M13.5 1.5V5.5M13.5 1.5C12.1332 2.86683 10.3668 4.63317 9 6" stroke="white" stroke-linecap="round"/>
<path d="M1.5 9.5V13.5M1.5 13.5L6 9M1.5 13.5H5.5" stroke="white" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 315 B

View File

@@ -0,0 +1,4 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13 6L9 6M9 6L9 2M9 6C10.3668 4.63316 12.1332 2.86683 13.5 1.5" stroke="white" stroke-linecap="round"/>
<path d="M6 13L6 9M6 9L1.5 13.5M6 9L2 9" stroke="white" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 297 B

View File

@@ -9,6 +9,7 @@
"context": "Editor",
"bindings": {
"cmd-b": "editor::GoToDefinition",
"alt-cmd-b": "editor::GoToDefinitionSplit",
"cmd-<": "editor::ScrollCursorCenter",
"cmd-g": [
"editor::SelectNext",

View File

@@ -13,6 +13,7 @@
"cmd-up": "menu::SelectFirst",
"cmd-down": "menu::SelectLast",
"enter": "menu::Confirm",
"cmd-enter": "menu::SecondaryConfirm",
"escape": "menu::Cancel",
"ctrl-c": "menu::Cancel",
"cmd-{": "pane::ActivatePrevItem",
@@ -39,6 +40,7 @@
"cmd-shift-n": "workspace::NewWindow",
"cmd-o": "workspace::Open",
"alt-cmd-o": "projects::OpenRecent",
"alt-cmd-b": "branches::OpenRecent",
"ctrl-~": "workspace::NewTerminal",
"ctrl-`": "terminal_panel::ToggleFocus",
"shift-escape": "workspace::ToggleZoom"
@@ -193,8 +195,8 @@
{
"context": "Editor && mode == auto_height",
"bindings": {
"alt-enter": "editor::Newline",
"cmd-alt-enter": "editor::NewlineBelow"
"ctrl-enter": "editor::Newline",
"ctrl-shift-enter": "editor::NewlineBelow"
}
},
{
@@ -220,7 +222,8 @@
"escape": "buffer_search::Dismiss",
"tab": "buffer_search::FocusEditor",
"enter": "search::SelectNextMatch",
"shift-enter": "search::SelectPrevMatch"
"shift-enter": "search::SelectPrevMatch",
"alt-enter": "search::SelectAllMatches"
}
},
{
@@ -241,6 +244,7 @@
"cmd-f": "project_search::ToggleFocus",
"cmd-g": "search::SelectNextMatch",
"cmd-shift-g": "search::SelectPrevMatch",
"alt-enter": "search::SelectAllMatches",
"alt-cmd-c": "search::ToggleCaseSensitive",
"alt-cmd-w": "search::ToggleWholeWord",
"alt-cmd-r": "search::ToggleRegex"
@@ -295,7 +299,9 @@
"shift-f8": "editor::GoToPrevDiagnostic",
"f2": "editor::Rename",
"f12": "editor::GoToDefinition",
"alt-f12": "editor::GoToDefinitionSplit",
"cmd-f12": "editor::GoToTypeDefinition",
"alt-cmd-f12": "editor::GoToTypeDefinitionSplit",
"alt-shift-f12": "editor::FindAllReferences",
"ctrl-m": "editor::MoveToEnclosingBracket",
"alt-cmd-[": "editor::Fold",
@@ -400,10 +406,12 @@
"cmd-b": "workspace::ToggleLeftDock",
"cmd-r": "workspace::ToggleRightDock",
"cmd-j": "workspace::ToggleBottomDock",
"alt-cmd-y": "workspace::CloseAllDocks",
"cmd-shift-f": "workspace::NewSearch",
"cmd-k cmd-t": "theme_selector::Toggle",
"cmd-k cmd-s": "zed::OpenKeymap",
"cmd-t": "project_symbols::Toggle",
"cmd-ctrl-t": "semantic_search::Toggle",
"cmd-p": "file_finder::Toggle",
"cmd-shift-p": "command_palette::Toggle",
"cmd-shift-m": "diagnostics::Deploy",
@@ -439,8 +447,22 @@
},
{
"bindings": {
"cmd-k cmd-left": "workspace::ActivatePreviousPane",
"cmd-k cmd-right": "workspace::ActivateNextPane"
"cmd-k cmd-left": [
"workspace::ActivatePaneInDirection",
"Left"
],
"cmd-k cmd-right": [
"workspace::ActivatePaneInDirection",
"Right"
],
"cmd-k cmd-up": [
"workspace::ActivatePaneInDirection",
"Up"
],
"cmd-k cmd-down": [
"workspace::ActivatePaneInDirection",
"Down"
]
}
},
// Bindings from Atom
@@ -506,8 +528,11 @@
"cmd-alt-c": "project_panel::CopyPath",
"alt-cmd-shift-c": "project_panel::CopyRelativePath",
"f2": "project_panel::Rename",
"enter": "project_panel::Rename",
"space": "project_panel::Open",
"backspace": "project_panel::Delete",
"alt-cmd-r": "project_panel::RevealInFinder"
"alt-cmd-r": "project_panel::RevealInFinder",
"alt-shift-f": "project_panel::NewSearchInDirectory"
}
},
{

View File

@@ -46,8 +46,9 @@
"alt-f7": "editor::FindAllReferences",
"cmd-alt-f7": "editor::FindAllReferences",
"cmd-b": "editor::GoToDefinition",
"cmd-alt-b": "editor::GoToDefinition",
"cmd-alt-b": "editor::GoToDefinitionSplit",
"cmd-shift-b": "editor::GoToTypeDefinition",
"cmd-alt-shift-b": "editor::GoToTypeDefinitionSplit",
"alt-enter": "editor::ToggleCodeActions",
"f2": "editor::GoToDiagnostic",
"cmd-f2": "editor::GoToPrevDiagnostic",

View File

@@ -20,6 +20,7 @@
"cmd-shift-a": "editor::SelectLargerSyntaxNode",
"shift-f12": "editor::FindAllReferences",
"alt-cmd-down": "editor::GoToDefinition",
"ctrl-alt-cmd-down": "editor::GoToDefinitionSplit",
"alt-shift-cmd-down": "editor::FindAllReferences",
"ctrl-.": "editor::GoToHunk",
"ctrl-,": "editor::GoToPrevHunk",

View File

@@ -2,6 +2,7 @@
{
"bindings": {
"cmd-shift-o": "projects::OpenRecent",
"cmd-shift-b": "branches::OpenRecent",
"cmd-alt-tab": "project_panel::ToggleFocus"
}
},
@@ -11,6 +12,7 @@
"cmd-l": "go_to_line::Toggle",
"ctrl-shift-d": "editor::DuplicateLine",
"cmd-b": "editor::GoToDefinition",
"alt-cmd-b": "editor::GoToDefinition",
"cmd-j": "editor::ScrollCursorCenter",
"cmd-shift-l": "editor::SelectLine",
"cmd-shift-t": "outline::Toggle",

View File

@@ -2,12 +2,6 @@
{
"context": "Editor && VimControl && !VimWaiting && !menu",
"bindings": {
"g": [
"vim::PushOperator",
{
"Namespace": "G"
}
],
"i": [
"vim::PushOperator",
{
@@ -30,13 +24,18 @@
"j": "vim::Down",
"down": "vim::Down",
"enter": "vim::NextLineStart",
"tab": "vim::Tab",
"shift-tab": "vim::Tab",
"k": "vim::Up",
"up": "vim::Up",
"l": "vim::Right",
"right": "vim::Right",
"$": "vim::EndOfLine",
"^": "vim::FirstNonWhitespace",
"shift-g": "vim::EndOfDocument",
"w": "vim::NextWordStart",
"{": "vim::StartOfParagraph",
"}": "vim::EndOfParagraph",
"shift-w": [
"vim::NextWordStart",
{
@@ -57,6 +56,8 @@
"ignorePunctuation": true
}
],
"n": "search::SelectNextMatch",
"shift-n": "search::SelectPrevMatch",
"%": "vim::Matching",
"f": [
"vim::PushOperator",
@@ -92,8 +93,43 @@
],
"ctrl-o": "pane::GoBack",
"ctrl-]": "editor::GoToDefinition",
"escape": "editor::Cancel",
"escape": [
"vim::SwitchMode",
"Normal"
],
"ctrl+[": [
"vim::SwitchMode",
"Normal"
],
"*": "vim::MoveToNext",
"#": "vim::MoveToPrev",
"0": "vim::StartOfLine", // When no number operator present, use start of line motion
// "g" commands
"g g": "vim::StartOfDocument",
"g h": "editor::Hover",
"g t": "pane::ActivateNextItem",
"g shift-t": "pane::ActivatePrevItem",
"g d": "editor::GoToDefinition",
"g shift-d": "editor::GoToTypeDefinition",
"g .": "editor::ToggleCodeActions", // zed specific
"g shift-a": "editor::FindAllReferences", // zed specific
"g *": [
"vim::MoveToNext",
{
"partialWord": true
}
],
"g #": [
"vim::MoveToPrev",
{
"partialWord": true
}
],
// z commands
"z t": "editor::ScrollCursorTop",
"z z": "editor::ScrollCursorCenter",
"z b": "editor::ScrollCursorBottom",
// Count support
"1": [
"vim::Number",
1
@@ -129,7 +165,75 @@
"9": [
"vim::Number",
9
]
],
// window related commands (ctrl-w X)
"ctrl-w left": [
"workspace::ActivatePaneInDirection",
"Left"
],
"ctrl-w right": [
"workspace::ActivatePaneInDirection",
"Right"
],
"ctrl-w up": [
"workspace::ActivatePaneInDirection",
"Up"
],
"ctrl-w down": [
"workspace::ActivatePaneInDirection",
"Down"
],
"ctrl-w h": [
"workspace::ActivatePaneInDirection",
"Left"
],
"ctrl-w l": [
"workspace::ActivatePaneInDirection",
"Right"
],
"ctrl-w k": [
"workspace::ActivatePaneInDirection",
"Up"
],
"ctrl-w j": [
"workspace::ActivatePaneInDirection",
"Down"
],
"ctrl-w ctrl-h": [
"workspace::ActivatePaneInDirection",
"Left"
],
"ctrl-w ctrl-l": [
"workspace::ActivatePaneInDirection",
"Right"
],
"ctrl-w ctrl-k": [
"workspace::ActivatePaneInDirection",
"Up"
],
"ctrl-w ctrl-j": [
"workspace::ActivatePaneInDirection",
"Down"
],
"ctrl-w g t": "pane::ActivateNextItem",
"ctrl-w ctrl-g t": "pane::ActivateNextItem",
"ctrl-w g shift-t": "pane::ActivatePrevItem",
"ctrl-w ctrl-g shift-t": "pane::ActivatePrevItem",
"ctrl-w w": "workspace::ActivateNextPane",
"ctrl-w ctrl-w": "workspace::ActivateNextPane",
"ctrl-w p": "workspace::ActivatePreviousPane",
"ctrl-w ctrl-p": "workspace::ActivatePreviousPane",
"ctrl-w shift-w": "workspace::ActivatePreviousPane",
"ctrl-w ctrl-shift-w": "workspace::ActivatePreviousPane",
"ctrl-w v": "pane::SplitLeft",
"ctrl-w ctrl-v": "pane::SplitLeft",
"ctrl-w s": "pane::SplitUp",
"ctrl-w shift-s": "pane::SplitUp",
"ctrl-w ctrl-s": "pane::SplitUp",
"ctrl-w c": "pane::CloseAllItems",
"ctrl-w ctrl-c": "pane::CloseAllItems",
"ctrl-w q": "pane::CloseAllItems",
"ctrl-w ctrl-q": "pane::CloseAllItems"
}
},
{
@@ -150,12 +254,6 @@
"vim::PushOperator",
"Yank"
],
"z": [
"vim::PushOperator",
{
"Namespace": "Z"
}
],
"i": [
"vim::SwitchMode",
"Insert"
@@ -165,7 +263,6 @@
"shift-a": "vim::InsertEndOfLine",
"x": "vim::DeleteRight",
"shift-x": "vim::DeleteLeft",
"^": "vim::FirstNonWhitespace",
"o": "vim::InsertLineBelow",
"shift-o": "vim::InsertLineAbove",
"~": "vim::ChangeCase",
@@ -188,10 +285,18 @@
"p": "vim::Paste",
"u": "editor::Undo",
"ctrl-r": "editor::Redo",
"/": [
"buffer_search::Deploy",
"/": "vim::Search",
"?": [
"vim::Search",
{
"focus": true
"backwards": true
}
],
";": "vim::RepeatFind",
",": [
"vim::RepeatFind",
{
"backwards": true
}
],
"ctrl-f": "vim::PageDown",
@@ -222,24 +327,11 @@
]
}
},
{
"context": "Editor && vim_operator == g",
"bindings": {
"g": "vim::StartOfDocument",
"h": "editor::Hover",
"t": "pane::ActivateNextItem",
"shift-t": "pane::ActivatePrevItem",
"escape": [
"vim::SwitchMode",
"Normal"
],
"d": "editor::GoToDefinition"
}
},
{
"context": "Editor && vim_operator == c",
"bindings": {
"c": "vim::CurrentLine"
"c": "vim::CurrentLine",
"d": "editor::Rename" // zed specific
}
},
{
@@ -254,18 +346,6 @@
"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": {
@@ -305,15 +385,20 @@
"vim::PushOperator",
"Replace"
],
"> >": "editor::Indent",
"< <": "editor::Outdent"
"ctrl-c": [
"vim::SwitchMode",
"Normal"
],
">": "editor::Indent",
"<": "editor::Outdent"
}
},
{
"context": "Editor && vim_mode == insert",
"bindings": {
"escape": "vim::NormalBefore",
"ctrl-c": "vim::NormalBefore"
"ctrl-c": "vim::NormalBefore",
"ctrl-[": "vim::NormalBefore"
}
},
{
@@ -321,7 +406,21 @@
"bindings": {
"tab": "vim::Tab",
"enter": "vim::Enter",
"escape": "editor::Cancel"
"escape": [
"vim::SwitchMode",
"Normal"
],
"ctrl+[": [
"vim::SwitchMode",
"Normal"
]
}
},
{
"context": "BufferSearchBar > VimEnabled",
"bindings": {
"enter": "vim::SearchSubmit",
"escape": "buffer_search::Dismiss"
}
}
]

View File

@@ -24,6 +24,17 @@
},
// The default font size for text in the editor
"buffer_font_size": 15,
// Set the buffer'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)
// "line_height": "standard",
// 3. Use a custom line height
// "line_height": {
// "custom": 2
// },
"buffer_line_height": "comfortable",
// 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,
@@ -39,6 +50,13 @@
// Whether to pop the completions menu while typing in an editor without
// explicitly requesting it.
"show_completions_on_input": true,
// Whether to show wrap guides in the editor. Setting this to true will
// show a guide at the 'preferred_line_length' value if softwrap is set to
// 'preferred_line_length', and will show any additional guides as specified
// by the 'wrap_guides' setting.
"show_wrap_guides": true,
// Character counts at which to show wrap guides in the editor.
"wrap_guides": [],
// Whether to use additional LSP queries to format (and amend) the code after
// every "trigger" symbol input, defined by LSP server capabilities.
"use_on_type_format": true,
@@ -55,6 +73,11 @@
// 3. Draw all invisible symbols:
// "all"
"show_whitespaces": "selection",
// Settings related to calls in Zed
"calls": {
// Join calls with the microphone muted by default
"mute_on_join": true
},
// Scrollbar related settings
"scrollbar": {
// When to show the scrollbar in the editor.
@@ -71,25 +94,33 @@
// "never"
"show": "auto",
// Whether to show git diff indicators in the scrollbar.
"git_diff": true
"git_diff": true,
// Whether to show selections in the scrollbar.
"selections": true
},
// Inlay hint related settings
"inlay_hints": {
// Global switch to toggle hints on and off, switched off by default.
"enabled": false,
"enabled": false,
// Toggle certain types of hints on and off, all switched on by default.
"show_type_hints": true,
"show_parameter_hints": true,
"show_parameter_hints": true,
// Corresponds to null/None LSP hint type value.
"show_other_hints": true
},
"project_panel": {
// Whether to show the git status in the project panel.
"git_status": true,
// Default width of the project panel.
"default_width": 240,
// Where to dock project panel. Can be 'left' or 'right'.
"dock": "left",
// Default width of the project panel.
"default_width": 240
// Whether to show file icons in the project panel.
"file_icons": true,
// Whether to show folder icons or chevrons for directories in the project panel.
"folder_icons": true,
// Whether to show the git status in the project panel.
"git_status": true,
// Amount of indentation for nested items.
"indent_size": 20
},
"assistant": {
// Where to dock the assistant. Can be 'left', 'right' or 'bottom'.
@@ -115,6 +146,13 @@
// 4. Save when idle for a certain amount of time:
// "autosave": { "after_delay": {"milliseconds": 500} },
"autosave": "off",
// Settings related to the editor's tabs
"tabs": {
// Show git status colors in the editor tabs.
"git_status": false,
// Position of the close button on the editor tabs.
"close_position": "right"
},
// Whether or not to remove any trailing whitespace from lines of a buffer
// before saving it.
"remove_trailing_whitespace_on_save": true,
@@ -176,9 +214,7 @@
"copilot": {
// The set of glob patterns for which copilot should be disabled
// in any matching file.
"disabled_globs": [
".env"
]
"disabled_globs": [".env"]
},
// Settings specific to journaling
"journal": {
@@ -280,7 +316,6 @@
// "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.
@@ -289,6 +324,11 @@
// the terminal will default to matching the buffer's font family.
// "font_family": "Zed Mono"
},
// Difference settings for semantic_index
"semantic_index": {
"enabled": false,
"reindexing_delay_seconds": 600
},
// Different settings for specific languages.
"languages": {
"Plain Text": {
@@ -323,12 +363,6 @@
// 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": {

View File

@@ -12,6 +12,7 @@ use regex::Regex;
use serde::{Deserialize, Serialize};
use std::{
cmp::Reverse,
ffi::OsStr,
fmt::{self, Display},
path::PathBuf,
sync::Arc,
@@ -80,6 +81,9 @@ impl SavedConversationMetadata {
let mut conversations = Vec::<SavedConversationMetadata>::new();
while let Some(path) = paths.next().await {
let path = path?;
if path.extension() != Some(OsStr::new("json")) {
continue;
}
let pattern = r" - \d+.zed.json$";
let re = Regex::new(pattern).unwrap();

View File

@@ -298,12 +298,22 @@ impl AssistantPanel {
}
fn deploy(&mut self, action: &search::buffer_search::Deploy, cx: &mut ViewContext<Self>) {
let mut propagate_action = true;
if let Some(search_bar) = self.toolbar.read(cx).item_of_type::<BufferSearchBar>() {
if search_bar.update(cx, |search_bar, cx| search_bar.show(action.focus, true, cx)) {
return;
}
search_bar.update(cx, |search_bar, cx| {
if search_bar.show(cx) {
search_bar.search_suggested(cx);
if action.focus {
search_bar.select_query(cx);
cx.focus_self();
}
propagate_action = false
}
});
}
if propagate_action {
cx.propagate_action();
}
cx.propagate_action();
}
fn handle_editor_cancel(&mut self, _: &editor::Cancel, cx: &mut ViewContext<Self>) {
@@ -320,13 +330,13 @@ impl AssistantPanel {
fn select_next_match(&mut self, _: &search::SelectNextMatch, cx: &mut ViewContext<Self>) {
if let Some(search_bar) = self.toolbar.read(cx).item_of_type::<BufferSearchBar>() {
search_bar.update(cx, |bar, cx| bar.select_match(Direction::Next, cx));
search_bar.update(cx, |bar, cx| bar.select_match(Direction::Next, 1, cx));
}
}
fn select_prev_match(&mut self, _: &search::SelectPrevMatch, cx: &mut ViewContext<Self>) {
if let Some(search_bar) = self.toolbar.read(cx).item_of_type::<BufferSearchBar>() {
search_bar.update(cx, |bar, cx| bar.select_match(Direction::Prev, cx));
search_bar.update(cx, |bar, cx| bar.select_match(Direction::Prev, 1, cx));
}
}
@@ -1627,6 +1637,7 @@ impl ConversationEditor {
let mut editor = Editor::for_buffer(conversation.read(cx).buffer.clone(), None, cx);
editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx);
editor.set_show_gutter(false, cx);
editor.set_show_wrap_guides(false, cx);
editor
});
@@ -2061,6 +2072,8 @@ impl ConversationEditor {
let remaining_tokens = self.conversation.read(cx).remaining_tokens()?;
let remaining_tokens_style = if remaining_tokens <= 0 {
&style.no_remaining_tokens
} else if remaining_tokens <= 500 {
&style.low_remaining_tokens
} else {
&style.remaining_tokens
};

View File

@@ -36,6 +36,10 @@ anyhow.workspace = true
async-broadcast = "0.4"
futures.workspace = true
postage.workspace = true
schemars.workspace = true
serde.workspace = true
serde_json.workspace = true
serde_derive.workspace = true
[dev-dependencies]
client = { path = "../client", features = ["test-support"] }

View File

@@ -1,10 +1,12 @@
pub mod call_settings;
pub mod participant;
pub mod room;
use std::sync::Arc;
use anyhow::{anyhow, Result};
use client::{proto, Client, TypedEnvelope, User, UserStore};
use call_settings::CallSettings;
use client::{proto, ClickhouseEvent, Client, TelemetrySettings, TypedEnvelope, User, UserStore};
use collections::HashSet;
use futures::{future::Shared, FutureExt};
use postage::watch;
@@ -19,6 +21,8 @@ pub use participant::ParticipantLocation;
pub use room::Room;
pub fn init(client: Arc<Client>, user_store: ModelHandle<UserStore>, cx: &mut AppContext) {
settings::register::<CallSettings>(cx);
let active_call = cx.add_model(|cx| ActiveCall::new(client, user_store, cx));
cx.set_global(active_call);
}
@@ -198,6 +202,7 @@ impl ActiveCall {
let result = invite.await;
this.update(&mut cx, |this, cx| {
this.pending_invites.remove(&called_user_id);
this.report_call_event("invite", cx);
cx.notify();
});
result
@@ -243,21 +248,26 @@ impl ActiveCall {
};
let join = Room::join(&call, self.client.clone(), self.user_store.clone(), cx);
cx.spawn(|this, mut cx| async move {
let room = join.await?;
this.update(&mut cx, |this, cx| this.set_room(Some(room.clone()), cx))
.await?;
this.update(&mut cx, |this, cx| {
this.report_call_event("accept incoming", cx)
});
Ok(())
})
}
pub fn decline_incoming(&mut self) -> Result<()> {
pub fn decline_incoming(&mut self, cx: &mut ModelContext<Self>) -> Result<()> {
let call = self
.incoming_call
.0
.borrow_mut()
.take()
.ok_or_else(|| anyhow!("no incoming call"))?;
Self::report_call_event_for_room("decline incoming", call.room_id, &self.client, cx);
self.client.send(proto::DeclineCall {
room_id: call.room_id,
})?;
@@ -266,6 +276,7 @@ impl ActiveCall {
pub fn hang_up(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
cx.notify();
self.report_call_event("hang up", cx);
if let Some((room, _)) = self.room.take() {
room.update(cx, |room, cx| room.leave(cx))
} else {
@@ -279,6 +290,7 @@ impl ActiveCall {
cx: &mut ModelContext<Self>,
) -> Task<Result<u64>> {
if let Some((room, _)) = self.room.as_ref() {
self.report_call_event("share project", cx);
room.update(cx, |room, cx| room.share_project(project, cx))
} else {
Task::ready(Err(anyhow!("no active call")))
@@ -291,6 +303,7 @@ impl ActiveCall {
cx: &mut ModelContext<Self>,
) -> Result<()> {
if let Some((room, _)) = self.room.as_ref() {
self.report_call_event("unshare project", cx);
room.update(cx, |room, cx| room.unshare_project(project, cx))
} else {
Err(anyhow!("no active call"))
@@ -349,7 +362,29 @@ impl ActiveCall {
self.room.as_ref().map(|(room, _)| room)
}
pub fn client(&self) -> Arc<Client> {
self.client.clone()
}
pub fn pending_invites(&self) -> &HashSet<u64> {
&self.pending_invites
}
fn report_call_event(&self, operation: &'static str, cx: &AppContext) {
if let Some(room) = self.room() {
Self::report_call_event_for_room(operation, room.read(cx).id(), &self.client, cx)
}
}
pub fn report_call_event_for_room(
operation: &'static str,
room_id: u64,
client: &Arc<Client>,
cx: &AppContext,
) {
let telemetry = client.telemetry();
let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
let event = ClickhouseEvent::Call { operation, room_id };
telemetry.report_clickhouse_event(event, telemetry_settings);
}
}

View File

@@ -0,0 +1,27 @@
use schemars::JsonSchema;
use serde_derive::{Deserialize, Serialize};
use settings::Setting;
#[derive(Deserialize, Debug)]
pub struct CallSettings {
pub mute_on_join: bool,
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
pub struct CallSettingsContent {
pub mute_on_join: Option<bool>,
}
impl Setting for CallSettings {
const KEY: Option<&'static str> = Some("calls");
type FileContent = CallSettingsContent;
fn load(
default_value: &Self::FileContent,
user_values: &[&Self::FileContent],
_: &gpui::AppContext,
) -> anyhow::Result<Self> {
Self::load_via_json_merge(default_value, user_values)
}
}

View File

@@ -1,4 +1,5 @@
use crate::{
call_settings::CallSettings,
participant::{LocalParticipant, ParticipantLocation, RemoteParticipant, RemoteVideoTrack},
IncomingCall,
};
@@ -153,8 +154,10 @@ impl Room {
cx.spawn(|this, mut cx| async move {
connect.await?;
this.update(&mut cx, |this, cx| this.share_microphone(cx))
.await?;
if !cx.read(|cx| settings::get::<CallSettings>(cx).mute_on_join) {
this.update(&mut cx, |this, cx| this.share_microphone(cx))
.await?;
}
anyhow::Ok(())
})
@@ -656,7 +659,7 @@ impl Room {
peer_id,
projects: participant.projects,
location,
muted: false,
muted: true,
speaking: false,
video_tracks: Default::default(),
audio_tracks: Default::default(),
@@ -670,6 +673,10 @@ impl Room {
live_kit.room.remote_video_tracks(&user.id.to_string());
let audio_tracks =
live_kit.room.remote_audio_tracks(&user.id.to_string());
let publications = live_kit
.room
.remote_audio_track_publications(&user.id.to_string());
for track in video_tracks {
this.remote_video_track_updated(
RemoteVideoTrackUpdate::Subscribed(track),
@@ -677,9 +684,15 @@ impl Room {
)
.log_err();
}
for track in audio_tracks {
for (track, publication) in
audio_tracks.iter().zip(publications.iter())
{
this.remote_audio_track_updated(
RemoteAudioTrackUpdate::Subscribed(track),
RemoteAudioTrackUpdate::Subscribed(
track.clone(),
publication.clone(),
),
cx,
)
.log_err();
@@ -819,8 +832,8 @@ impl Room {
cx.notify();
}
RemoteAudioTrackUpdate::MuteChanged { track_id, muted } => {
let mut found = false;
for participant in &mut self.remote_participants.values_mut() {
let mut found = false;
for track in participant.audio_tracks.values() {
if track.sid() == track_id {
found = true;
@@ -832,16 +845,20 @@ impl Room {
break;
}
}
cx.notify();
}
RemoteAudioTrackUpdate::Subscribed(track) => {
RemoteAudioTrackUpdate::Subscribed(track, publication) => {
let user_id = track.publisher_id().parse()?;
let track_id = track.sid().to_string();
let participant = self
.remote_participants
.get_mut(&user_id)
.ok_or_else(|| anyhow!("subscribed to track by unknown participant"))?;
participant.audio_tracks.insert(track_id.clone(), track);
participant.muted = publication.is_muted();
cx.emit(Event::RemoteAudioTracksChanged {
participant_id: participant.peer_id,
});
@@ -1053,7 +1070,7 @@ impl Room {
self.live_kit
.as_ref()
.and_then(|live_kit| match &live_kit.microphone_track {
LocalTrack::None => None,
LocalTrack::None => Some(true),
LocalTrack::Pending { muted, .. } => Some(*muted),
LocalTrack::Published { muted, .. } => Some(*muted),
})
@@ -1070,6 +1087,7 @@ impl Room {
self.live_kit.as_ref().map(|live_kit| live_kit.deafened)
}
#[track_caller]
pub fn share_microphone(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
if self.status.is_offline() {
return Task::ready(Err(anyhow!("room is offline")));
@@ -1244,6 +1262,10 @@ impl Room {
pub fn toggle_mute(&mut self, cx: &mut ModelContext<Self>) -> Result<Task<Result<()>>> {
let should_mute = !self.is_muted();
if let Some(live_kit) = self.live_kit.as_mut() {
if matches!(live_kit.microphone_track, LocalTrack::None) {
return Ok(self.share_microphone(cx));
}
let (ret_task, old_muted) = live_kit.set_mute(should_mute, cx)?;
live_kit.muted_by_user = should_mute;

View File

@@ -201,6 +201,7 @@ impl Bundle {
self.zed_version_string()
);
}
Self::LocalPath { executable, .. } => {
let executable_parent = executable
.parent()

View File

@@ -40,6 +40,7 @@ lazy_static! {
struct ClickhouseEventRequestBody {
token: &'static str,
installation_id: Option<Arc<str>>,
is_staff: Option<bool>,
app_version: Option<Arc<str>>,
os_name: &'static str,
os_version: Option<Arc<str>>,
@@ -70,6 +71,10 @@ pub enum ClickhouseEvent {
suggestion_accepted: bool,
file_extension: Option<String>,
},
Call {
operation: &'static str,
room_id: u64,
},
}
#[cfg(debug_assertions)]
@@ -220,6 +225,7 @@ impl Telemetry {
&ClickhouseEventRequestBody {
token: ZED_SECRET_CLIENT_TOKEN,
installation_id: state.installation_id.clone(),
is_staff: state.is_staff.clone(),
app_version: state.app_version.clone(),
os_name: state.os_name,
os_version: state.os_version.clone(),

View File

@@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathan@zed.dev>"]
default-run = "collab"
edition = "2021"
name = "collab"
version = "0.15.0"
version = "0.16.0"
publish = false
[[bin]]
@@ -14,7 +14,6 @@ name = "seed"
required-features = ["seed-support"]
[dependencies]
audio = { path = "../audio" }
collections = { path = "../collections" }
live_kit_server = { path = "../live_kit_server" }
rpc = { path = "../rpc" }
@@ -58,6 +57,7 @@ tracing-log = "0.1.3"
tracing-subscriber = { version = "0.3.11", features = ["env-filter", "json"] }
[dev-dependencies]
audio = { path = "../audio" }
collections = { path = "../collections", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
call = { path = "../call", features = ["test-support"] }

View File

@@ -3517,7 +3517,6 @@ pub use test::*;
mod test {
use super::*;
use gpui::executor::Background;
use lazy_static::lazy_static;
use parking_lot::Mutex;
use sea_orm::ConnectionTrait;
use sqlx::migrate::MigrateDatabase;
@@ -3566,9 +3565,7 @@ mod test {
}
pub fn postgres(background: Arc<Background>) -> Self {
lazy_static! {
static ref LOCK: Mutex<()> = Mutex::new(());
}
static LOCK: Mutex<()> = Mutex::new(());
let _guard = LOCK.lock();
let mut rng = StdRng::from_entropy();

View File

@@ -157,7 +157,7 @@ async fn test_basic_calls(
// User C receives the call, but declines it.
let call_c = incoming_call_c.next().await.unwrap().unwrap();
assert_eq!(call_c.calling_user.github_login, "user_b");
active_call_c.update(cx_c, |call, _| call.decline_incoming().unwrap());
active_call_c.update(cx_c, |call, cx| call.decline_incoming(cx).unwrap());
assert!(incoming_call_c.next().await.unwrap().is_none());
deterministic.run_until_parked();
@@ -1080,7 +1080,7 @@ async fn test_calls_on_multiple_connections(
// User B declines the call on one of the two connections, causing both connections
// to stop ringing.
active_call_b2.update(cx_b2, |call, _| call.decline_incoming().unwrap());
active_call_b2.update(cx_b2, |call, cx| call.decline_incoming(cx).unwrap());
deterministic.run_until_parked();
assert!(incoming_call_b1.next().await.unwrap().is_none());
assert!(incoming_call_b2.next().await.unwrap().is_none());
@@ -5945,7 +5945,7 @@ async fn test_contacts(
[("user_b".to_string(), "online", "busy")]
);
active_call_b.update(cx_b, |call, _| call.decline_incoming().unwrap());
active_call_b.update(cx_b, |call, cx| call.decline_incoming(cx).unwrap());
deterministic.run_until_parked();
assert_eq!(
contacts(&client_a, cx_a),
@@ -7217,7 +7217,7 @@ async fn test_peers_following_each_other(
// Clients A and B follow each other in split panes
workspace_a.update(cx_a, |workspace, cx| {
workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
workspace.split_and_clone(workspace.active_pane().clone(), SplitDirection::Right, cx);
});
workspace_a
.update(cx_a, |workspace, cx| {
@@ -7228,7 +7228,7 @@ async fn test_peers_following_each_other(
.await
.unwrap();
workspace_b.update(cx_b, |workspace, cx| {
workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
workspace.split_and_clone(workspace.active_pane().clone(), SplitDirection::Right, cx);
});
workspace_b
.update(cx_b, |workspace, cx| {
@@ -7455,7 +7455,7 @@ async fn test_auto_unfollowing(
// When client B activates a different pane, it continues following client A in the original pane.
workspace_b.update(cx_b, |workspace, cx| {
workspace.split_pane(pane_b.clone(), SplitDirection::Right, cx)
workspace.split_and_clone(pane_b.clone(), SplitDirection::Right, cx)
});
assert_eq!(
workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),

View File

@@ -37,9 +37,9 @@ use util::ResultExt;
lazy_static::lazy_static! {
static ref PLAN_LOAD_PATH: Option<PathBuf> = path_env_var("LOAD_PLAN");
static ref PLAN_SAVE_PATH: Option<PathBuf> = path_env_var("SAVE_PLAN");
static ref LOADED_PLAN_JSON: Mutex<Option<Vec<u8>>> = Default::default();
static ref PLAN: Mutex<Option<Arc<Mutex<TestPlan>>>> = Default::default();
}
static LOADED_PLAN_JSON: Mutex<Option<Vec<u8>>> = Mutex::new(None);
static PLAN: Mutex<Option<Arc<Mutex<TestPlan>>>> = Mutex::new(None);
#[gpui::test(iterations = 100, on_failure = "on_failure")]
async fn test_random_collaboration(
@@ -365,7 +365,7 @@ async fn apply_client_operation(
}
log::info!("{}: declining incoming call", client.username);
active_call.update(cx, |call, _| call.decline_incoming())?;
active_call.update(cx, |call, cx| call.decline_incoming(cx))?;
}
ClientOperation::LeaveCall => {

View File

@@ -39,6 +39,7 @@ recent_projects = {path = "../recent_projects"}
settings = { path = "../settings" }
theme = { path = "../theme" }
theme_selector = { path = "../theme_selector" }
vcs_menu = { path = "../vcs_menu" }
util = { path = "../util" }
workspace = { path = "../workspace" }
zed-actions = {path = "../zed-actions"}

View File

@@ -1,8 +1,5 @@
use crate::{
branch_list::{build_branch_list, BranchList},
contact_notification::ContactNotification,
contacts_popover,
face_pile::FacePile,
contact_notification::ContactNotification, contacts_popover, face_pile::FacePile,
toggle_deafen, toggle_mute, toggle_screen_sharing, LeaveCall, ToggleDeafen, ToggleMute,
ToggleScreenSharing,
};
@@ -27,6 +24,7 @@ use recent_projects::{build_recent_projects, RecentProjects};
use std::{ops::Range, sync::Arc};
use theme::{AvatarStyle, Theme};
use util::ResultExt;
use vcs_menu::{build_branch_list, BranchList, OpenRecent as ToggleVcsMenu};
use workspace::{FollowNextCollaborator, Workspace, WORKSPACE_DB};
const MAX_PROJECT_NAME_LENGTH: usize = 40;
@@ -37,7 +35,6 @@ actions!(
[
ToggleContactsMenu,
ToggleUserMenu,
ToggleVcsMenu,
ToggleProjectMenu,
SwitchBranch,
ShareProject,
@@ -229,15 +226,23 @@ impl CollabTitlebarItem {
let mut ret = Flex::row().with_child(
Stack::new()
.with_child(
MouseEventHandler::<ToggleProjectMenu, Self>::new(0, cx, |mouse_state, _| {
MouseEventHandler::<ToggleProjectMenu, Self>::new(0, cx, |mouse_state, cx| {
let style = project_style
.in_state(self.project_popover.is_some())
.style_for(mouse_state);
enum RecentProjectsTooltip {}
Label::new(name, style.text.clone())
.contained()
.with_style(style.container)
.aligned()
.left()
.with_tooltip::<RecentProjectsTooltip>(
0,
"Recent projects".into(),
Some(Box::new(recent_projects::OpenRecent)),
theme.tooltip.clone(),
cx,
)
.into_any_named("title-project-name")
})
.with_cursor_style(CursorStyle::PointingHand)
@@ -264,7 +269,8 @@ impl CollabTitlebarItem {
MouseEventHandler::<ToggleVcsMenu, Self>::new(
0,
cx,
|mouse_state, _| {
|mouse_state, cx| {
enum BranchPopoverTooltip {}
let style = git_style
.in_state(self.branch_popover.is_some())
.style_for(mouse_state);
@@ -274,6 +280,13 @@ impl CollabTitlebarItem {
.with_margin_right(item_spacing)
.aligned()
.left()
.with_tooltip::<BranchPopoverTooltip>(
0,
"Recent branches".into(),
Some(Box::new(ToggleVcsMenu)),
theme.tooltip.clone(),
cx,
)
.into_any_named("title-project-branch")
},
)
@@ -639,10 +652,10 @@ impl CollabTitlebarItem {
let is_muted = room.read(cx).is_muted();
if is_muted {
icon = "icons/radix/mic-mute.svg";
tooltip = "Unmute microphone\nRight click for options";
tooltip = "Unmute microphone";
} else {
icon = "icons/radix/mic.svg";
tooltip = "Mute microphone\nRight click for options";
tooltip = "Mute microphone";
}
let titlebar = &theme.titlebar;
@@ -692,10 +705,10 @@ impl CollabTitlebarItem {
let is_deafened = room.read(cx).is_deafened().unwrap_or(false);
if is_deafened {
icon = "icons/radix/speaker-off.svg";
tooltip = "Unmute speakers\nRight click for options";
tooltip = "Unmute speakers";
} else {
icon = "icons/radix/speaker-loud.svg";
tooltip = "Mute speakers\nRight click for options";
tooltip = "Mute speakers";
}
let titlebar = &theme.titlebar;

View File

@@ -1,4 +1,3 @@
mod branch_list;
mod collab_titlebar_item;
mod contact_finder;
mod contact_list;
@@ -19,17 +18,11 @@ use workspace::AppState;
actions!(
collab,
[
ToggleScreenSharing,
ToggleMute,
ToggleDeafen,
LeaveCall,
ShareMicrophone
]
[ToggleScreenSharing, ToggleMute, ToggleDeafen, LeaveCall]
);
pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
branch_list::init(cx);
vcs_menu::init(cx);
collab_titlebar_item::init(cx);
contact_list::init(cx);
contact_finder::init(cx);
@@ -41,15 +34,28 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
cx.add_global_action(toggle_screen_sharing);
cx.add_global_action(toggle_mute);
cx.add_global_action(toggle_deafen);
cx.add_global_action(share_microphone);
}
pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut AppContext) {
if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
let call = ActiveCall::global(cx).read(cx);
if let Some(room) = call.room().cloned() {
let client = call.client();
let toggle_screen_sharing = room.update(cx, |room, cx| {
if room.is_screen_sharing() {
ActiveCall::report_call_event_for_room(
"disable screen share",
room.id(),
&client,
cx,
);
Task::ready(room.unshare_screen(cx))
} else {
ActiveCall::report_call_event_for_room(
"enable screen share",
room.id(),
&client,
cx,
);
room.share_screen(cx)
}
});
@@ -58,10 +64,24 @@ pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut AppContext) {
}
pub fn toggle_mute(_: &ToggleMute, cx: &mut AppContext) {
if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
room.update(cx, Room::toggle_mute)
.map(|task| task.detach_and_log_err(cx))
.log_err();
let call = ActiveCall::global(cx).read(cx);
if let Some(room) = call.room().cloned() {
let client = call.client();
room.update(cx, |room, cx| {
if room.is_muted() {
ActiveCall::report_call_event_for_room("enable microphone", room.id(), &client, cx);
} else {
ActiveCall::report_call_event_for_room(
"disable microphone",
room.id(),
&client,
cx,
);
}
room.toggle_mute(cx)
})
.map(|task| task.detach_and_log_err(cx))
.log_err();
}
}
@@ -72,10 +92,3 @@ pub fn toggle_deafen(_: &ToggleDeafen, cx: &mut AppContext) {
.log_err();
}
}
pub fn share_microphone(_: &ShareMicrophone, cx: &mut AppContext) {
if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
room.update(cx, Room::share_microphone)
.detach_and_log_err(cx)
}
}

View File

@@ -67,7 +67,7 @@ impl PickerDelegate for ContactFinderDelegate {
})
}
fn confirm(&mut self, cx: &mut ViewContext<Picker<Self>>) {
fn confirm(&mut self, _: bool, 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) {

View File

@@ -99,8 +99,8 @@ impl IncomingCallNotification {
})
.detach_and_log_err(cx);
} else {
active_call.update(cx, |active_call, _| {
active_call.decline_incoming().log_err();
active_call.update(cx, |active_call, cx| {
active_call.decline_incoming(cx).log_err();
});
}
}

View File

@@ -160,7 +160,7 @@ impl PickerDelegate for CommandPaletteDelegate {
fn dismissed(&mut self, _cx: &mut ViewContext<Picker<Self>>) {}
fn confirm(&mut self, cx: &mut ViewContext<Picker<Self>>) {
fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<Self>>) {
if !self.matches.is_empty() {
let window_id = cx.window_id();
let focused_view_id = self.focused_view_id;
@@ -369,6 +369,7 @@ mod tests {
editor::init(cx);
workspace::init(app_state.clone(), cx);
init(cx);
Project::init_settings(cx);
app_state
})
}

View File

@@ -7,7 +7,6 @@ use anyhow::Context;
use gpui::AppContext;
pub use indoc::indoc;
pub use lazy_static;
use parking_lot::{Mutex, RwLock};
pub use smol;
pub use sqlez;
pub use sqlez_macros;
@@ -17,11 +16,9 @@ pub use util::paths::DB_DIR;
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};
use util::channel::ReleaseChannel;
use util::{async_iife, ResultExt};
@@ -41,10 +38,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);
pub static ref ZED_STATELESS: bool = std::env::var("ZED_STATELESS").map_or(false, |v| !v.is_empty());
pub static ref ALL_FILE_DB_FAILED: AtomicBool = AtomicBool::new(false);
}
@@ -64,66 +58,14 @@ pub async fn open_db<M: Migrator + 'static>(
let main_db_dir = db_dir.join(Path::new(&format!("0-{}", release_channel_name)));
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
// 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
// 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
// a bad time.
// If no db folder, create one at 0-{channel}
create_dir_all(&main_db_dir).context("Could not create db directory")?;
smol::fs::create_dir_all(&main_db_dir)
.await
.context("Could not create db directory")
.log_err()?;
let db_path = main_db_dir.join(Path::new(DB_FILE_NAME));
// Optimistically open databases in parallel
if !DB_FILE_OPERATIONS.is_locked() {
// Try building a connection
if let Some(connection) = open_main_db(&db_path).await {
return Ok(connection)
};
}
// 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();
if let Some(connection) = open_main_db(&db_path).await {
return Ok(connection)
};
let backup_timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("System clock is set before the unix timestamp, Zed does not support this region of spacetime")
.as_millis();
// If failed, move 0-{channel} to {current unix timestamp}-{channel}
let backup_db_dir = db_dir.join(Path::new(&format!(
"{}-{}",
backup_timestamp,
release_channel_name,
)));
std::fs::rename(&main_db_dir, &backup_db_dir)
.context("Failed clean up corrupted database, panicking.")?;
// Set a static ref with the failed timestamp and error so we can notify the user
{
let mut guard = BACKUP_DB_PATH.write();
*guard = Some(backup_db_dir);
}
// Create a new 0-{channel}
create_dir_all(&main_db_dir).context("Should be able to create the database directory")?;
let db_path = main_db_dir.join(Path::new(DB_FILE_NAME));
// Try again
open_main_db(&db_path).await.context("Could not newly created db")
}).await.log_err();
open_main_db(&db_path).await
})
.await;
if let Some(connection) = connection {
return connection;
@@ -250,13 +192,13 @@ where
#[cfg(test)]
mod tests {
use std::{fs, thread};
use std::thread;
use sqlez::{connection::Connection, domain::Domain};
use sqlez::domain::Domain;
use sqlez_macros::sql;
use tempdir::TempDir;
use crate::{open_db, DB_FILE_NAME};
use crate::open_db;
// Test bad migration panics
#[gpui::test]
@@ -322,31 +264,10 @@ mod tests {
.unwrap()
.is_none()
);
let mut corrupted_backup_dir = fs::read_dir(tempdir.path())
.unwrap()
.find(|entry| {
!entry
.as_ref()
.unwrap()
.file_name()
.to_str()
.unwrap()
.starts_with("0")
})
.unwrap()
.unwrap()
.path();
corrupted_backup_dir.push(DB_FILE_NAME);
let backup = Connection::open_file(&corrupted_backup_dir.to_string_lossy());
assert!(backup.select_row::<usize>("SELECT * FROM test").unwrap()()
.unwrap()
.is_none());
}
/// Test that DB exists but corrupted (causing recreate)
#[gpui::test]
#[gpui::test(iterations = 30)]
async fn test_simultaneous_db_corruption() {
enum CorruptedDB {}

View File

@@ -10,7 +10,6 @@ doctest = false
[features]
test-support = [
"rand",
"copilot/test-support",
"text/test-support",
"language/test-support",
@@ -57,16 +56,16 @@ 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 }
schemars.workspace = 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 }
rand.workspace = true
tree-sitter-rust = { workspace = true, optional = true }
tree-sitter-html = { workspace = true, optional = true }
tree-sitter-typescript = { workspace = true, optional = true }
[dev-dependencies]
copilot = { path = "../copilot", features = ["test-support"] }
@@ -84,7 +83,6 @@ env_logger.workspace = true
rand.workspace = true
unindent.workspace = true
tree-sitter.workspace = true
tree-sitter-rust = "0.20"
tree-sitter-html = "0.19"
tree-sitter-typescript = { git = "https://github.com/tree-sitter/tree-sitter-typescript", rev = "5d20856f34315b068c41edaee2ac8a100081d259" }
tree-sitter-javascript = "0.20"
tree-sitter-rust.workspace = true
tree-sitter-html.workspace = true
tree-sitter-typescript.workspace = true

View File

@@ -74,6 +74,7 @@ pub use multi_buffer::{
};
use ordered_float::OrderedFloat;
use project::{FormatTrigger, Location, LocationLink, Project, ProjectPath, ProjectTransaction};
use rand::{seq::SliceRandom, thread_rng};
use scroll::{
autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide,
};
@@ -88,7 +89,7 @@ use std::{
cmp::{self, Ordering, Reverse},
mem,
num::NonZeroU32,
ops::{ControlFlow, Deref, DerefMut, Range},
ops::{ControlFlow, Deref, DerefMut, Range, RangeInclusive},
path::Path,
sync::Arc,
time::{Duration, Instant},
@@ -226,6 +227,10 @@ actions!(
MoveLineUp,
MoveLineDown,
JoinLines,
SortLinesCaseSensitive,
SortLinesCaseInsensitive,
ReverseLines,
ShuffleLines,
Transpose,
Cut,
Copy,
@@ -271,7 +276,9 @@ actions!(
SelectLargerSyntaxNode,
SelectSmallerSyntaxNode,
GoToDefinition,
GoToDefinitionSplit,
GoToTypeDefinition,
GoToTypeDefinitionSplit,
MoveToEnclosingBracket,
UndoSelection,
RedoSelection,
@@ -342,6 +349,10 @@ pub fn init(cx: &mut AppContext) {
cx.add_action(Editor::outdent);
cx.add_action(Editor::delete_line);
cx.add_action(Editor::join_lines);
cx.add_action(Editor::sort_lines_case_sensitive);
cx.add_action(Editor::sort_lines_case_insensitive);
cx.add_action(Editor::reverse_lines);
cx.add_action(Editor::shuffle_lines);
cx.add_action(Editor::delete_to_previous_word_start);
cx.add_action(Editor::delete_to_previous_subword_start);
cx.add_action(Editor::delete_to_next_word_end);
@@ -407,7 +418,9 @@ pub fn init(cx: &mut AppContext) {
cx.add_action(Editor::go_to_hunk);
cx.add_action(Editor::go_to_prev_hunk);
cx.add_action(Editor::go_to_definition);
cx.add_action(Editor::go_to_definition_split);
cx.add_action(Editor::go_to_type_definition);
cx.add_action(Editor::go_to_type_definition_split);
cx.add_action(Editor::fold);
cx.add_action(Editor::fold_at);
cx.add_action(Editor::unfold_lines);
@@ -494,6 +507,7 @@ pub enum SoftWrap {
#[derive(Clone)]
pub struct EditorStyle {
pub text: TextStyle,
pub line_height_scalar: f32,
pub placeholder_text: Option<TextStyle>,
pub theme: theme::Editor,
pub theme_id: usize,
@@ -529,6 +543,7 @@ pub struct Editor {
show_local_selections: bool,
mode: EditorMode,
show_gutter: bool,
show_wrap_guides: Option<bool>,
placeholder_text: Option<Arc<str>>,
highlighted_rows: Option<Range<u32>>,
#[allow(clippy::type_complexity)]
@@ -544,6 +559,7 @@ pub struct Editor {
pending_rename: Option<RenameState>,
searchable: bool,
cursor_shape: CursorShape,
collapse_matches: bool,
workspace: Option<(WeakViewHandle<Workspace>, i64)>,
keymap_context_layers: BTreeMap<TypeId, KeymapContext>,
input_enabled: bool,
@@ -557,6 +573,7 @@ pub struct Editor {
inlay_hint_cache: InlayHintCache,
next_inlay_id: usize,
_subscriptions: Vec<Subscription>,
pixel_position_of_newest_cursor: Option<Vector2F>,
}
pub struct EditorSnapshot {
@@ -1359,6 +1376,7 @@ impl Editor {
show_local_selections: true,
mode,
show_gutter: mode == EditorMode::Full,
show_wrap_guides: None,
placeholder_text: None,
highlighted_rows: None,
background_highlights: Default::default(),
@@ -1376,6 +1394,7 @@ impl Editor {
searchable: true,
override_text_style: None,
cursor_shape: Default::default(),
collapse_matches: false,
workspace: None,
keymap_context_layers: Default::default(),
input_enabled: true,
@@ -1387,6 +1406,7 @@ impl Editor {
copilot_state: Default::default(),
inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
gutter_hovered: false,
pixel_position_of_newest_cursor: None,
_subscriptions: vec![
cx.observe(&buffer, Self::on_buffer_changed),
cx.subscribe(&buffer, Self::on_buffer_event),
@@ -1515,6 +1535,17 @@ impl Editor {
cx.notify();
}
pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
self.collapse_matches = collapse_matches;
}
pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
if self.collapse_matches {
return range.start..range.start;
}
range.clone()
}
pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
if self.display_map.read(cx).clip_at_line_ends != clip {
self.display_map
@@ -2654,11 +2685,16 @@ impl Editor {
InlayRefreshReason::RefreshRequested => (InvalidationStrategy::RefreshRequested, None),
};
self.inlay_hint_cache.refresh_inlay_hints(
if let Some(InlaySplice {
to_remove,
to_insert,
}) = self.inlay_hint_cache.spawn_hint_refresh(
self.excerpt_visible_offsets(required_languages.as_ref(), cx),
invalidate_cache,
cx,
)
) {
self.splice_inlay_hints(to_remove, to_insert, cx);
}
}
fn visible_inlay_hints(&self, cx: &ViewContext<'_, '_, Editor>) -> Vec<Inlay> {
@@ -4180,6 +4216,96 @@ impl Editor {
});
}
pub fn sort_lines_case_sensitive(
&mut self,
_: &SortLinesCaseSensitive,
cx: &mut ViewContext<Self>,
) {
self.manipulate_lines(cx, |text| text.sort())
}
pub fn sort_lines_case_insensitive(
&mut self,
_: &SortLinesCaseInsensitive,
cx: &mut ViewContext<Self>,
) {
self.manipulate_lines(cx, |text| text.sort_by_key(|line| line.to_lowercase()))
}
pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
self.manipulate_lines(cx, |lines| lines.reverse())
}
pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
}
fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
where
Fn: FnMut(&mut [&str]),
{
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let buffer = self.buffer.read(cx).snapshot(cx);
let mut edits = Vec::new();
let selections = self.selections.all::<Point>(cx);
let mut selections = selections.iter().peekable();
let mut contiguous_row_selections = Vec::new();
let mut new_selections = Vec::new();
while let Some(selection) = selections.next() {
let (start_row, end_row) = consume_contiguous_rows(
&mut contiguous_row_selections,
selection,
&display_map,
&mut selections,
);
let start_point = Point::new(start_row, 0);
let end_point = Point::new(end_row - 1, buffer.line_len(end_row - 1));
let text = buffer
.text_for_range(start_point..end_point)
.collect::<String>();
let mut text = text.split("\n").collect_vec();
let text_len = text.len();
callback(&mut text);
// This is a current limitation with selections.
// If we wanted to support removing or adding lines, we'd need to fix the logic associated with selections.
debug_assert!(
text.len() == text_len,
"callback should not change the number of lines"
);
edits.push((start_point..end_point, text.join("\n")));
let start_anchor = buffer.anchor_after(start_point);
let end_anchor = buffer.anchor_before(end_point);
// Make selection and push
new_selections.push(Selection {
id: selection.id,
start: start_anchor.to_offset(&buffer),
end: end_anchor.to_offset(&buffer),
goal: SelectionGoal::None,
reversed: selection.reversed,
});
}
self.transact(cx, |this, cx| {
this.buffer.update(cx, |buffer, cx| {
buffer.edit(edits, None, cx);
});
this.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.select(new_selections);
});
this.request_autoscroll(Autoscroll::fit(), cx);
});
}
pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let buffer = &display_map.buffer_snapshot;
@@ -5123,7 +5249,7 @@ impl Editor {
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.move_with(|map, selection| {
selection.collapse_to(
movement::start_of_paragraph(map, selection.head()),
movement::start_of_paragraph(map, selection.head(), 1),
SelectionGoal::None,
)
});
@@ -5143,7 +5269,7 @@ impl Editor {
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.move_with(|map, selection| {
selection.collapse_to(
movement::end_of_paragraph(map, selection.head()),
movement::end_of_paragraph(map, selection.head(), 1),
SelectionGoal::None,
)
});
@@ -5162,7 +5288,10 @@ impl Editor {
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.move_heads_with(|map, head, _| {
(movement::start_of_paragraph(map, head), SelectionGoal::None)
(
movement::start_of_paragraph(map, head, 1),
SelectionGoal::None,
)
});
})
}
@@ -5179,7 +5308,10 @@ impl Editor {
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.move_heads_with(|map, head, _| {
(movement::end_of_paragraph(map, head), SelectionGoal::None)
(
movement::end_of_paragraph(map, head, 1),
SelectionGoal::None,
)
});
})
}
@@ -5267,7 +5399,7 @@ impl Editor {
pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
let end = self.buffer.read(cx).read(cx).len();
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
self.change_selections(None, cx, |s| {
s.select_ranges(vec![0..end]);
});
}
@@ -6178,14 +6310,31 @@ impl Editor {
}
pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, cx);
self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
}
pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
self.go_to_definition_of_kind(GotoDefinitionKind::Type, cx);
self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx);
}
fn go_to_definition_of_kind(&mut self, kind: GotoDefinitionKind, cx: &mut ViewContext<Self>) {
pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext<Self>) {
self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx);
}
pub fn go_to_type_definition_split(
&mut self,
_: &GoToTypeDefinitionSplit,
cx: &mut ViewContext<Self>,
) {
self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx);
}
fn go_to_definition_of_kind(
&mut self,
kind: GotoDefinitionKind,
split: bool,
cx: &mut ViewContext<Self>,
) {
let Some(workspace) = self.workspace(cx) else { return };
let buffer = self.buffer.read(cx);
let head = self.selections.newest::<usize>(cx).head();
@@ -6204,7 +6353,7 @@ impl Editor {
cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
let definitions = definitions.await?;
editor.update(&mut cx, |editor, cx| {
editor.navigate_to_definitions(definitions, cx);
editor.navigate_to_definitions(definitions, split, cx);
})?;
Ok::<(), anyhow::Error>(())
})
@@ -6214,6 +6363,7 @@ impl Editor {
pub fn navigate_to_definitions(
&mut self,
mut definitions: Vec<LocationLink>,
split: bool,
cx: &mut ViewContext<Editor>,
) {
let Some(workspace) = self.workspace(cx) else { return };
@@ -6226,6 +6376,7 @@ impl Editor {
.range
.to_offset(definition.target.buffer.read(cx));
let range = self.range_for_match(&range);
if Some(&definition.target.buffer) == self.buffer.read(cx).as_singleton().as_ref() {
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.select_ranges([range]);
@@ -6233,7 +6384,11 @@ impl Editor {
} else {
cx.window_context().defer(move |cx| {
let target_editor: ViewHandle<Self> = workspace.update(cx, |workspace, cx| {
workspace.open_project_item(definition.target.buffer.clone(), cx)
if split {
workspace.split_project_item(definition.target.buffer.clone(), cx)
} else {
workspace.open_project_item(definition.target.buffer.clone(), cx)
}
});
target_editor.update(cx, |target_editor, cx| {
// When selecting a definition in a different buffer, disable the nav history
@@ -6269,7 +6424,9 @@ impl Editor {
.map(|definition| definition.target)
.collect();
workspace.update(cx, |workspace, cx| {
Self::open_locations_in_multibuffer(workspace, locations, replica_id, title, cx)
Self::open_locations_in_multibuffer(
workspace, locations, replica_id, title, split, cx,
)
});
});
}
@@ -6314,7 +6471,7 @@ impl Editor {
})
.unwrap();
Self::open_locations_in_multibuffer(
workspace, locations, replica_id, title, cx,
workspace, locations, replica_id, title, false, cx,
);
})?;
@@ -6329,6 +6486,7 @@ impl Editor {
mut locations: Vec<Location>,
replica_id: ReplicaId,
title: String,
split: bool,
cx: &mut ViewContext<Workspace>,
) {
// If there are multiple definitions, open them in a multibuffer
@@ -6375,7 +6533,11 @@ impl Editor {
cx,
);
});
workspace.add_item(Box::new(editor), cx);
if split {
workspace.split_item(Box::new(editor), cx);
} else {
workspace.add_item(Box::new(editor), cx);
}
}
pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
@@ -7024,6 +7186,24 @@ impl Editor {
.text()
}
pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
let mut wrap_guides = smallvec::smallvec![];
if self.show_wrap_guides == Some(false) {
return wrap_guides;
}
let settings = self.buffer.read(cx).settings_at(0, cx);
if settings.show_wrap_guides {
if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
wrap_guides.push((soft_wrap as usize, true));
}
wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
}
wrap_guides
}
pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
let settings = self.buffer.read(cx).settings_at(0, cx);
let mode = self
@@ -7070,6 +7250,11 @@ impl Editor {
cx.notify();
}
pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
self.show_wrap_guides = Some(show_gutter);
cx.notify();
}
pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
if let Some(buffer) = self.buffer().read(cx).as_singleton() {
if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
@@ -7216,6 +7401,119 @@ impl Editor {
}
results
}
pub fn background_highlights_in_range_for<T: 'static>(
&self,
search_range: Range<Anchor>,
display_snapshot: &DisplaySnapshot,
theme: &Theme,
) -> Vec<(Range<DisplayPoint>, Color)> {
let mut results = Vec::new();
let buffer = &display_snapshot.buffer_snapshot;
let Some((color_fetcher, ranges)) = self.background_highlights
.get(&TypeId::of::<T>()) else {
return vec![];
};
let color = color_fetcher(theme);
let start_ix = match ranges.binary_search_by(|probe| {
let cmp = probe.end.cmp(&search_range.start, buffer);
if cmp.is_gt() {
Ordering::Greater
} else {
Ordering::Less
}
}) {
Ok(i) | Err(i) => i,
};
for range in &ranges[start_ix..] {
if range.start.cmp(&search_range.end, buffer).is_ge() {
break;
}
let start = range
.start
.to_point(buffer)
.to_display_point(display_snapshot);
let end = range
.end
.to_point(buffer)
.to_display_point(display_snapshot);
results.push((start..end, color))
}
results
}
pub fn background_highlight_row_ranges<T: 'static>(
&self,
search_range: Range<Anchor>,
display_snapshot: &DisplaySnapshot,
count: usize,
) -> Vec<RangeInclusive<DisplayPoint>> {
let mut results = Vec::new();
let buffer = &display_snapshot.buffer_snapshot;
let Some((_, ranges)) = self.background_highlights
.get(&TypeId::of::<T>()) else {
return vec![];
};
let start_ix = match ranges.binary_search_by(|probe| {
let cmp = probe.end.cmp(&search_range.start, buffer);
if cmp.is_gt() {
Ordering::Greater
} else {
Ordering::Less
}
}) {
Ok(i) | Err(i) => i,
};
let mut push_region = |start: Option<Point>, end: Option<Point>| {
if let (Some(start_display), Some(end_display)) = (start, end) {
results.push(
start_display.to_display_point(display_snapshot)
..=end_display.to_display_point(display_snapshot),
);
}
};
let mut start_row: Option<Point> = None;
let mut end_row: Option<Point> = None;
if ranges.len() > count {
return vec![];
}
for range in &ranges[start_ix..] {
if range.start.cmp(&search_range.end, buffer).is_ge() {
break;
}
let end = range.end.to_point(buffer);
if let Some(current_row) = &end_row {
if end.row == current_row.row {
continue;
}
}
let start = range.start.to_point(buffer);
if start_row.is_none() {
assert_eq!(end_row, None);
start_row = Some(start);
end_row = Some(end);
continue;
}
if let Some(current_end) = end_row.as_mut() {
if start.row > current_end.row + 1 {
push_region(start_row, end_row);
start_row = Some(start);
end_row = Some(end);
} else {
// Merge two hunks.
*current_end = end;
}
} else {
unreachable!();
}
}
// We might still have a hunk that was not rendered (if there was a search hit on the last line)
push_region(start_row, end_row);
results
}
pub fn highlight_text<T: 'static>(
&mut self,
@@ -7518,7 +7816,7 @@ impl Editor {
fn report_editor_event(
&self,
name: &'static str,
operation: &'static str,
file_extension: Option<String>,
cx: &AppContext,
) {
@@ -7555,7 +7853,7 @@ impl Editor {
let event = ClickhouseEvent::Editor {
file_extension,
vim_mode,
operation: name,
operation,
copilot_enabled,
copilot_enabled_for_language,
};
@@ -8054,7 +8352,7 @@ fn build_style(
cx: &AppContext,
) -> EditorStyle {
let font_cache = cx.font_cache();
let line_height_scalar = settings.line_height();
let theme_id = settings.theme.meta.id;
let mut theme = settings.theme.editor.clone();
let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
@@ -8068,6 +8366,7 @@ fn build_style(
EditorStyle {
text: field_editor_theme.text,
placeholder_text: field_editor_theme.placeholder_text,
line_height_scalar,
theme,
theme_id,
}
@@ -8090,6 +8389,7 @@ fn build_style(
underline: Default::default(),
},
placeholder_text: None,
line_height_scalar,
theme,
theme_id,
}

View File

@@ -15,6 +15,7 @@ pub struct EditorSettings {
pub struct Scrollbar {
pub show: ShowScrollbar,
pub git_diff: bool,
pub selections: bool,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
@@ -39,6 +40,7 @@ pub struct EditorSettingsContent {
pub struct ScrollbarContent {
pub show: Option<ShowScrollbar>,
pub git_diff: Option<bool>,
pub selections: Option<bool>,
}
impl Setting for EditorSettings {

View File

@@ -22,7 +22,10 @@ use language::{
BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageRegistry, Point,
};
use parking_lot::Mutex;
use project::project_settings::{LspSettings, ProjectSettings};
use project::FakeFs;
use std::sync::atomic;
use std::sync::atomic::AtomicUsize;
use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
use unindent::Unindent;
use util::{
@@ -1796,7 +1799,7 @@ async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
"});
}
// Ensure that comment continuations can be disabled.
update_test_settings(cx, |settings| {
update_test_language_settings(cx, |settings| {
settings.defaults.extend_comment_on_newline = Some(false);
});
let mut cx = EditorTestContext::new(cx).await;
@@ -2497,6 +2500,156 @@ fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
});
}
#[gpui::test]
async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let mut cx = EditorTestContext::new(cx).await;
// Test sort_lines_case_insensitive()
cx.set_state(indoc! {"
«z
y
x
Z
Y
Xˇ»
"});
cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
cx.assert_editor_state(indoc! {"
«x
X
y
Y
z
Zˇ»
"});
// Test reverse_lines()
cx.set_state(indoc! {"
«5
4
3
2
1ˇ»
"});
cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
cx.assert_editor_state(indoc! {"
«1
2
3
4
5ˇ»
"});
// Skip testing shuffle_line()
// From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
// Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
// Don't manipulate when cursor is on single line, but expand the selection
cx.set_state(indoc! {"
ddˇdd
ccc
bb
a
"});
cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
cx.assert_editor_state(indoc! {"
«ddddˇ»
ccc
bb
a
"});
// Basic manipulate case
// Start selection moves to column 0
// End of selection shrinks to fit shorter line
cx.set_state(indoc! {"
dd«d
ccc
bb
aaaaaˇ»
"});
cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
cx.assert_editor_state(indoc! {"
«aaaaa
bb
ccc
dddˇ»
"});
// Manipulate case with newlines
cx.set_state(indoc! {"
dd«d
ccc
bb
aaaaa
ˇ»
"});
cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
cx.assert_editor_state(indoc! {"
«
aaaaa
bb
ccc
dddˇ»
"});
}
#[gpui::test]
async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let mut cx = EditorTestContext::new(cx).await;
// Manipulate with multiple selections on a single line
cx.set_state(indoc! {"
dd«dd
cˇ»c«c
bb
aaaˇ»aa
"});
cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
cx.assert_editor_state(indoc! {"
«aaaaa
bb
ccc
ddddˇ»
"});
// Manipulate with multiple disjoin selections
cx.set_state(indoc! {"
4
3
2
1ˇ»
dd«dd
ccc
bb
aaaˇ»aa
"});
cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
cx.assert_editor_state(indoc! {"
«1
2
3
4
5ˇ»
«aaaaa
bb
ccc
ddddˇ»
"});
}
#[gpui::test]
fn test_duplicate_line(cx: &mut TestAppContext) {
init_test(cx, |_| {});
@@ -3833,7 +3986,7 @@ async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
autoclose_before: "})]>".into(),
..Default::default()
},
Some(tree_sitter_javascript::language()),
Some(tree_sitter_typescript::language_tsx()),
));
let registry = Arc::new(LanguageRegistry::test());
@@ -4546,7 +4699,7 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
assert!(!cx.read(|cx| editor.is_dirty(cx)));
// Set rust language override and assert overridden tabsize is sent to language server
update_test_settings(cx, |settings| {
update_test_language_settings(cx, |settings| {
settings.languages.insert(
"Rust".into(),
LanguageSettingsContent {
@@ -4660,7 +4813,7 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
assert!(!cx.read(|cx| editor.is_dirty(cx)));
// Set rust language override and assert overridden tabsize is sent to language server
update_test_settings(cx, |settings| {
update_test_language_settings(cx, |settings| {
settings.languages.insert(
"Rust".into(),
LanguageSettingsContent {
@@ -5380,7 +5533,7 @@ async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
line_comment: Some("// ".into()),
..Default::default()
},
Some(tree_sitter_javascript::language()),
Some(tree_sitter_typescript::language_tsx()),
));
let registry = Arc::new(LanguageRegistry::test());
@@ -7084,6 +7237,233 @@ async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
});
}
#[gpui::test]
async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
let language_name: Arc<str> = "Rust".into();
let mut language = Language::new(
LanguageConfig {
name: Arc::clone(&language_name),
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
);
let server_restarts = Arc::new(AtomicUsize::new(0));
let closure_restarts = Arc::clone(&server_restarts);
let language_server_name = "test language server";
let mut fake_servers = language
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
name: language_server_name,
initialization_options: Some(json!({
"testOptionValue": true
})),
initializer: Some(Box::new(move |fake_server| {
let task_restarts = Arc::clone(&closure_restarts);
fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
task_restarts.fetch_add(1, atomic::Ordering::Release);
futures::future::ready(Ok(()))
});
})),
..Default::default()
}))
.await;
let fs = FakeFs::new(cx.background());
fs.insert_tree(
"/a",
json!({
"main.rs": "fn main() { let a = 5; }",
"other.rs": "// Test file",
}),
)
.await;
let project = Project::test(fs, ["/a".as_ref()], cx).await;
project.update(cx, |project, _| project.languages().add(Arc::new(language)));
let (_, _workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let _buffer = project
.update(cx, |project, cx| {
project.open_local_buffer("/a/main.rs", cx)
})
.await
.unwrap();
let _fake_server = fake_servers.next().await.unwrap();
update_test_language_settings(cx, |language_settings| {
language_settings.languages.insert(
Arc::clone(&language_name),
LanguageSettingsContent {
tab_size: NonZeroU32::new(8),
..Default::default()
},
);
});
cx.foreground().run_until_parked();
assert_eq!(
server_restarts.load(atomic::Ordering::Acquire),
0,
"Should not restart LSP server on an unrelated change"
);
update_test_project_settings(cx, |project_settings| {
project_settings.lsp.insert(
"Some other server name".into(),
LspSettings {
initialization_options: Some(json!({
"some other init value": false
})),
},
);
});
cx.foreground().run_until_parked();
assert_eq!(
server_restarts.load(atomic::Ordering::Acquire),
0,
"Should not restart LSP server on an unrelated LSP settings change"
);
update_test_project_settings(cx, |project_settings| {
project_settings.lsp.insert(
language_server_name.into(),
LspSettings {
initialization_options: Some(json!({
"anotherInitValue": false
})),
},
);
});
cx.foreground().run_until_parked();
assert_eq!(
server_restarts.load(atomic::Ordering::Acquire),
1,
"Should restart LSP server on a related LSP settings change"
);
update_test_project_settings(cx, |project_settings| {
project_settings.lsp.insert(
language_server_name.into(),
LspSettings {
initialization_options: Some(json!({
"anotherInitValue": false
})),
},
);
});
cx.foreground().run_until_parked();
assert_eq!(
server_restarts.load(atomic::Ordering::Acquire),
1,
"Should not restart LSP server on a related LSP settings change that is the same"
);
update_test_project_settings(cx, |project_settings| {
project_settings.lsp.insert(
language_server_name.into(),
LspSettings {
initialization_options: None,
},
);
});
cx.foreground().run_until_parked();
assert_eq!(
server_restarts.load(atomic::Ordering::Acquire),
2,
"Should restart LSP server on another related LSP settings change"
);
}
#[gpui::test]
async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
let mut cx = EditorLspTestContext::new_rust(
lsp::ServerCapabilities {
completion_provider: Some(lsp::CompletionOptions {
trigger_characters: Some(vec![".".to_string()]),
..Default::default()
}),
..Default::default()
},
cx,
)
.await;
cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
cx.simulate_keystroke(".");
let completion_item = lsp::CompletionItem {
label: "some".into(),
kind: Some(lsp::CompletionItemKind::SNIPPET),
detail: Some("Wrap the expression in an `Option::Some`".to_string()),
documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
kind: lsp::MarkupKind::Markdown,
value: "```rust\nSome(2)\n```".to_string(),
})),
deprecated: Some(false),
sort_text: Some("fffffff2".to_string()),
filter_text: Some("some".to_string()),
insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
range: lsp::Range {
start: lsp::Position {
line: 0,
character: 22,
},
end: lsp::Position {
line: 0,
character: 22,
},
},
new_text: "Some(2)".to_string(),
})),
additional_text_edits: Some(vec![lsp::TextEdit {
range: lsp::Range {
start: lsp::Position {
line: 0,
character: 20,
},
end: lsp::Position {
line: 0,
character: 22,
},
},
new_text: "".to_string(),
}]),
..Default::default()
};
let closure_completion_item = completion_item.clone();
let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
let task_completion_item = closure_completion_item.clone();
async move {
Ok(Some(lsp::CompletionResponse::Array(vec![
task_completion_item,
])))
}
});
request.next().await;
cx.condition(|editor, _| editor.context_menu_visible())
.await;
let apply_additional_edits = cx.update_editor(|editor, cx| {
editor
.confirm_completion(&ConfirmCompletion::default(), cx)
.unwrap()
});
cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
let task_completion_item = completion_item.clone();
async move { Ok(task_completion_item) }
})
.next()
.await
.unwrap();
apply_additional_edits.await.unwrap();
cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
}
fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
let point = DisplayPoint::new(row as u32, column as u32);
point..point
@@ -7203,7 +7583,7 @@ fn handle_copilot_completion_request(
});
}
pub(crate) fn update_test_settings(
pub(crate) fn update_test_language_settings(
cx: &mut TestAppContext,
f: impl Fn(&mut AllLanguageSettingsContent),
) {
@@ -7214,6 +7594,17 @@ pub(crate) fn update_test_settings(
});
}
pub(crate) fn update_test_project_settings(
cx: &mut TestAppContext,
f: impl Fn(&mut ProjectSettings),
) {
cx.update(|cx| {
cx.update_global::<SettingsStore, _, _>(|store, cx| {
store.update_user_settings::<ProjectSettings>(cx, f);
});
});
}
pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
cx.foreground().forbid_parking();
@@ -7227,5 +7618,5 @@ pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsC
crate::init(cx);
});
update_test_settings(cx, f);
update_test_language_settings(cx, f);
}

View File

@@ -61,6 +61,7 @@ enum FoldMarkers {}
struct SelectionLayout {
head: DisplayPoint,
cursor_shape: CursorShape,
is_newest: bool,
range: Range<DisplayPoint>,
}
@@ -70,6 +71,7 @@ impl SelectionLayout {
line_mode: bool,
cursor_shape: CursorShape,
map: &DisplaySnapshot,
is_newest: bool,
) -> Self {
if line_mode {
let selection = selection.map(|p| p.to_point(&map.buffer_snapshot));
@@ -77,6 +79,7 @@ impl SelectionLayout {
Self {
head: selection.head().to_display_point(map),
cursor_shape,
is_newest,
range: point_range.start.to_display_point(map)
..point_range.end.to_display_point(map),
}
@@ -85,6 +88,7 @@ impl SelectionLayout {
Self {
head: selection.head(),
cursor_shape,
is_newest,
range: selection.range(),
}
}
@@ -156,6 +160,7 @@ impl EditorElement {
event.position,
event.cmd,
event.shift,
event.alt,
position_map.as_ref(),
text_bounds,
cx,
@@ -167,6 +172,10 @@ impl EditorElement {
.on_drag(MouseButton::Left, {
let position_map = position_map.clone();
move |event, editor, cx| {
if event.end {
return;
}
if !Self::mouse_dragged(
editor,
event.platform_event,
@@ -308,6 +317,7 @@ impl EditorElement {
position: Vector2F,
cmd: bool,
shift: bool,
alt: bool,
position_map: &PositionMap,
text_bounds: RectF,
cx: &mut EventContext<Editor>,
@@ -324,9 +334,9 @@ impl EditorElement {
if point == target_point {
if shift {
go_to_fetched_type_definition(editor, point, cx);
go_to_fetched_type_definition(editor, point, alt, cx);
} else {
go_to_fetched_definition(editor, point, cx);
go_to_fetched_definition(editor, point, alt, cx);
}
return true;
@@ -535,6 +545,36 @@ impl EditorElement {
corner_radius: 0.,
});
}
let scroll_left =
layout.position_map.snapshot.scroll_position().x() * layout.position_map.em_width;
for (wrap_position, active) in layout.wrap_guides.iter() {
let x =
(text_bounds.origin_x() + wrap_position + layout.position_map.em_width / 2.)
- scroll_left;
if x < text_bounds.origin_x()
|| (layout.show_scrollbars && x > self.scrollbar_left(&bounds))
{
continue;
}
let color = if *active {
self.style.active_wrap_guide
} else {
self.style.wrap_guide
};
scene.push_quad(Quad {
bounds: RectF::new(
vec2f(x, text_bounds.origin_y()),
vec2f(1., text_bounds.height()),
),
background: Some(color),
border: Border::new(0., Color::transparent_black()),
corner_radius: 0.,
});
}
}
}
@@ -862,6 +902,12 @@ impl EditorElement {
let x = cursor_character_x - scroll_left;
let y = cursor_position.row() as f32 * layout.position_map.line_height
- scroll_top;
if selection.is_newest {
editor.pixel_position_of_newest_cursor = Some(vec2f(
bounds.origin_x() + x + block_width / 2.,
bounds.origin_y() + y + layout.position_map.line_height / 2.,
));
}
cursors.push(Cursor {
color: selection_style.cursor,
block_width,
@@ -1002,12 +1048,17 @@ impl EditorElement {
scene.pop_layer();
}
fn scrollbar_left(&self, bounds: &RectF) -> f32 {
bounds.max_x() - self.style.theme.scrollbar.width
}
fn paint_scrollbar(
&mut self,
scene: &mut SceneBuilder,
bounds: RectF,
layout: &mut LayoutState,
cx: &mut ViewContext<Editor>,
editor: &Editor,
) {
enum ScrollbarMouseHandlers {}
if layout.mode != EditorMode::Full {
@@ -1019,7 +1070,7 @@ impl EditorElement {
let top = bounds.min_y();
let bottom = bounds.max_y();
let right = bounds.max_x();
let left = right - style.width;
let left = self.scrollbar_left(&bounds);
let row_range = &layout.scrollbar_row_range;
let max_row = layout.max_row as f32 + (row_range.end - row_range.start);
@@ -1050,9 +1101,52 @@ impl EditorElement {
background: style.track.background_color,
..Default::default()
});
let scrollbar_settings = settings::get::<EditorSettings>(cx).scrollbar;
let theme = theme::current(cx);
let scrollbar_theme = &theme.editor.scrollbar;
if layout.is_singleton && scrollbar_settings.selections {
let start_anchor = Anchor::min();
let end_anchor = Anchor::max();
let color = scrollbar_theme.selections;
let border = Border {
width: 1.,
color: style.thumb.border.color,
overlay: false,
top: false,
right: true,
bottom: false,
left: true,
};
let mut push_region = |start: DisplayPoint, end: DisplayPoint| {
let start_y = y_for_row(start.row() as f32);
let mut end_y = y_for_row(end.row() as f32);
if end_y - start_y < 1. {
end_y = start_y + 1.;
}
let bounds = RectF::from_points(vec2f(left, start_y), vec2f(right, end_y));
if layout.is_singleton && settings::get::<EditorSettings>(cx).scrollbar.git_diff {
let diff_style = theme::current(cx).editor.scrollbar.git.clone();
scene.push_quad(Quad {
bounds,
background: Some(color),
border,
corner_radius: style.thumb.corner_radius,
})
};
let background_ranges = editor
.background_highlight_row_ranges::<crate::items::BufferSearchHighlights>(
start_anchor..end_anchor,
&layout.position_map.snapshot,
50000,
);
for row in background_ranges {
let start = row.start();
let end = row.end();
push_region(*start, *end);
}
}
if layout.is_singleton && scrollbar_settings.git_diff {
let diff_style = scrollbar_theme.git.clone();
for hunk in layout
.position_map
.snapshot
@@ -1114,8 +1208,10 @@ impl EditorElement {
});
scene.push_mouse_region(
MouseRegion::new::<ScrollbarMouseHandlers>(cx.view_id(), cx.view_id(), track_bounds)
.on_move(move |_, editor: &mut Editor, cx| {
editor.scroll_manager.show_scrollbar(cx);
.on_move(move |event, editor: &mut Editor, cx| {
if event.pressed_button.is_none() {
editor.scroll_manager.show_scrollbar(cx);
}
})
.on_down(MouseButton::Left, {
let row_range = row_range.clone();
@@ -1135,6 +1231,10 @@ impl EditorElement {
})
.on_drag(MouseButton::Left, {
move |event, editor: &mut Editor, cx| {
if event.end {
return;
}
let y = event.prev_mouse_position.y();
let new_y = event.position.y();
if thumb_top < y && y < thumb_bottom {
@@ -1238,16 +1338,15 @@ impl EditorElement {
}
}
fn max_line_number_width(&self, snapshot: &EditorSnapshot, cx: &ViewContext<Editor>) -> f32 {
let digit_count = (snapshot.max_buffer_row() as f32).log10().floor() as usize + 1;
fn column_pixels(&self, column: usize, cx: &ViewContext<Editor>) -> f32 {
let style = &self.style;
cx.text_layout_cache()
.layout_str(
"1".repeat(digit_count).as_str(),
" ".repeat(column).as_str(),
style.text.font_size,
&[(
digit_count,
column,
RunStyle {
font_id: style.text.font_id,
color: Color::black(),
@@ -1258,6 +1357,11 @@ impl EditorElement {
.width()
}
fn max_line_number_width(&self, snapshot: &EditorSnapshot, cx: &ViewContext<Editor>) -> f32 {
let digit_count = (snapshot.max_buffer_row() as f32 + 1.).log10().floor() as usize + 1;
self.column_pixels(digit_count, cx)
}
//Folds contained in a hunk are ignored apart from shrinking visual size
//If a fold contains any hunks then that fold line is marked as modified
fn layout_git_gutters(
@@ -1905,7 +2009,8 @@ impl Element<Editor> for EditorElement {
let snapshot = editor.snapshot(cx);
let style = self.style.clone();
let line_height = style.text.line_height(cx.font_cache());
let line_height = (style.text.font_size * style.line_height_scalar).round();
let gutter_padding;
let gutter_width;
@@ -1942,6 +2047,12 @@ impl Element<Editor> for EditorElement {
}
};
let wrap_guides = editor
.wrap_guides(cx)
.iter()
.map(|(guide, active)| (self.column_pixels(*guide, cx), *active))
.collect();
let scroll_height = (snapshot.max_point().row() + 1) as f32 * line_height;
if let EditorMode::AutoHeight { max_lines } = snapshot.mode {
size.set_y(
@@ -2036,6 +2147,7 @@ impl Element<Editor> for EditorElement {
line_mode,
cursor_shape,
&snapshot.display_snapshot,
false,
));
}
selections.extend(remote_selections);
@@ -2045,6 +2157,7 @@ impl Element<Editor> for EditorElement {
.selections
.disjoint_in_range(start_anchor..end_anchor, cx);
local_selections.extend(editor.selections.pending(cx));
let newest = editor.selections.newest(cx);
for selection in &local_selections {
let is_empty = selection.start == selection.end;
let selection_start = snapshot.prev_line_boundary(selection.start).1;
@@ -2067,11 +2180,13 @@ impl Element<Editor> for EditorElement {
local_selections
.into_iter()
.map(|selection| {
let is_newest = selection == newest;
SelectionLayout::new(
selection,
editor.selections.line_mode,
editor.cursor_shape,
&snapshot.display_snapshot,
is_newest,
)
})
.collect(),
@@ -2083,6 +2198,9 @@ impl Element<Editor> for EditorElement {
ShowScrollbar::Auto => {
// Git
(is_singleton && scrollbar_settings.git_diff && snapshot.buffer_snapshot.has_git_diffs())
||
// Selections
(is_singleton && scrollbar_settings.selections && !highlighted_ranges.is_empty())
// Scrollmanager
|| editor.scroll_manager.scrollbars_visible()
}
@@ -2295,6 +2413,7 @@ impl Element<Editor> for EditorElement {
snapshot,
}),
visible_display_row_range: start_row..end_row,
wrap_guides,
gutter_size,
gutter_padding,
text_size,
@@ -2368,7 +2487,7 @@ impl Element<Editor> for EditorElement {
if !layout.blocks.is_empty() {
self.paint_blocks(scene, bounds, visible_bounds, layout, editor, cx);
}
self.paint_scrollbar(scene, bounds, layout, cx);
self.paint_scrollbar(scene, bounds, layout, cx, &editor);
scene.pop_layer();
scene.pop_layer();
@@ -2445,6 +2564,7 @@ pub struct LayoutState {
gutter_margin: f32,
text_size: Vector2F,
mode: EditorMode,
wrap_guides: SmallVec<[(f32, bool); 2]>,
visible_display_row_range: Range<u32>,
active_rows: BTreeMap<u32, bool>,
highlighted_rows: Option<Range<u32>>,
@@ -2845,7 +2965,7 @@ mod tests {
use super::*;
use crate::{
display_map::{BlockDisposition, BlockProperties},
editor_tests::{init_test, update_test_settings},
editor_tests::{init_test, update_test_language_settings},
Editor, MultiBuffer,
};
use gpui::TestAppContext;
@@ -3042,7 +3162,7 @@ mod tests {
let resize_step = 10.0;
let mut editor_width = 200.0;
while editor_width <= 1000.0 {
update_test_settings(cx, |s| {
update_test_language_settings(cx, |s| {
s.defaults.tab_size = NonZeroU32::new(tab_size);
s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
s.defaults.preferred_line_length = Some(editor_width as u32);

View File

@@ -198,7 +198,7 @@ fn show_hover(
// Construct new hover popover from hover request
let hover_popover = hover_request.await.ok().flatten().and_then(|hover_result| {
if hover_result.contents.is_empty() {
if hover_result.is_empty() {
return None;
}
@@ -420,7 +420,7 @@ fn render_blocks(
RenderedInfo {
theme_id,
text,
text: text.trim().to_string(),
highlights,
region_ranges,
regions,
@@ -816,6 +816,118 @@ mod tests {
});
}
#[gpui::test]
async fn test_empty_hovers_filtered(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
let mut cx = EditorLspTestContext::new_rust(
lsp::ServerCapabilities {
hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
..Default::default()
},
cx,
)
.await;
// Hover with keyboard has no delay
cx.set_state(indoc! {"
fˇn test() { println!(); }
"});
cx.update_editor(|editor, cx| hover(editor, &Hover, cx));
let symbol_range = cx.lsp_range(indoc! {"
«fn» test() { println!(); }
"});
cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
Ok(Some(lsp::Hover {
contents: lsp::HoverContents::Array(vec![
lsp::MarkedString::String("regular text for hover to show".to_string()),
lsp::MarkedString::String("".to_string()),
lsp::MarkedString::LanguageString(lsp::LanguageString {
language: "Rust".to_string(),
value: "".to_string(),
}),
]),
range: Some(symbol_range),
}))
})
.next()
.await;
cx.condition(|editor, _| editor.hover_state.visible()).await;
cx.editor(|editor, _| {
assert_eq!(
editor.hover_state.info_popover.clone().unwrap().blocks,
vec![HoverBlock {
text: "regular text for hover to show".to_string(),
kind: HoverBlockKind::Markdown,
}],
"No empty string hovers should be shown"
);
});
}
#[gpui::test]
async fn test_line_ends_trimmed(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
let mut cx = EditorLspTestContext::new_rust(
lsp::ServerCapabilities {
hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
..Default::default()
},
cx,
)
.await;
// Hover with keyboard has no delay
cx.set_state(indoc! {"
fˇn test() { println!(); }
"});
cx.update_editor(|editor, cx| hover(editor, &Hover, cx));
let symbol_range = cx.lsp_range(indoc! {"
«fn» test() { println!(); }
"});
let code_str = "\nlet hovered_point: Vector2F // size = 8, align = 0x4\n";
let markdown_string = format!("\n```rust\n{code_str}```");
let closure_markdown_string = markdown_string.clone();
cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| {
let future_markdown_string = closure_markdown_string.clone();
async move {
Ok(Some(lsp::Hover {
contents: lsp::HoverContents::Markup(lsp::MarkupContent {
kind: lsp::MarkupKind::Markdown,
value: future_markdown_string,
}),
range: Some(symbol_range),
}))
}
})
.next()
.await;
cx.condition(|editor, _| editor.hover_state.visible()).await;
cx.editor(|editor, cx| {
let blocks = editor.hover_state.info_popover.clone().unwrap().blocks;
assert_eq!(
blocks,
vec![HoverBlock {
text: markdown_string,
kind: HoverBlockKind::Markdown,
}],
);
let style = editor.style(cx);
let rendered = render_blocks(0, &blocks, &Default::default(), None, &style);
assert_eq!(
rendered.text,
code_str.trim(),
"Should not have extra line breaks at end of rendered hover"
);
});
}
#[gpui::test]
async fn test_hover_diagnostic_and_info_popovers(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});

View File

@@ -195,20 +195,41 @@ impl InlayHintCache {
}
}
pub fn refresh_inlay_hints(
pub fn spawn_hint_refresh(
&mut self,
mut excerpts_to_query: HashMap<ExcerptId, (ModelHandle<Buffer>, Global, Range<usize>)>,
invalidate: InvalidationStrategy,
cx: &mut ViewContext<Editor>,
) {
if !self.enabled || excerpts_to_query.is_empty() {
return;
) -> Option<InlaySplice> {
if !self.enabled {
return None;
}
let update_tasks = &mut self.update_tasks;
let mut invalidated_hints = Vec::new();
if invalidate.should_invalidate() {
update_tasks
.retain(|task_excerpt_id, _| excerpts_to_query.contains_key(task_excerpt_id));
let mut changed = false;
update_tasks.retain(|task_excerpt_id, _| {
let retain = excerpts_to_query.contains_key(task_excerpt_id);
changed |= !retain;
retain
});
self.hints.retain(|cached_excerpt, cached_hints| {
let retain = excerpts_to_query.contains_key(cached_excerpt);
changed |= !retain;
if !retain {
invalidated_hints.extend(cached_hints.read().hints.iter().map(|&(id, _)| id));
}
retain
});
if changed {
self.version += 1;
}
}
if excerpts_to_query.is_empty() && invalidated_hints.is_empty() {
return None;
}
let cache_version = self.version;
excerpts_to_query.retain(|visible_excerpt_id, _| {
match update_tasks.entry(*visible_excerpt_id) {
@@ -229,6 +250,15 @@ impl InlayHintCache {
.ok();
})
.detach();
if invalidated_hints.is_empty() {
None
} else {
Some(InlaySplice {
to_remove: invalidated_hints,
to_insert: Vec::new(),
})
}
}
fn new_allowed_hint_kinds_splice(
@@ -684,7 +714,7 @@ async fn fetch_and_update_hints(
if query.invalidate.should_invalidate() {
let mut outdated_excerpt_caches = HashSet::default();
for (excerpt_id, excerpt_hints) in editor.inlay_hint_cache().hints.iter() {
for (excerpt_id, excerpt_hints) in &editor.inlay_hint_cache().hints {
let excerpt_hints = excerpt_hints.read();
if excerpt_hints.buffer_id == query.buffer_id
&& excerpt_id != &query.excerpt_id
@@ -847,7 +877,7 @@ mod tests {
use text::Point;
use workspace::Workspace;
use crate::editor_tests::update_test_settings;
use crate::editor_tests::update_test_language_settings;
use super::*;
@@ -1022,9 +1052,9 @@ mod tests {
"Should get its first hints when opening the editor"
);
assert_eq!(expected_layers, visible_hint_labels(editor, cx));
let inlay_cache = editor.inlay_hint_cache();
assert_eq!(
inlay_cache.version, edits_made,
editor.inlay_hint_cache().version,
edits_made,
"The editor update the cache version after every cache/view change"
);
});
@@ -1053,9 +1083,9 @@ mod tests {
"Should not update hints while the work task is running"
);
assert_eq!(expected_layers, visible_hint_labels(editor, cx));
let inlay_cache = editor.inlay_hint_cache();
assert_eq!(
inlay_cache.version, edits_made,
editor.inlay_hint_cache().version,
edits_made,
"Should not update the cache while the work task is running"
);
});
@@ -1077,9 +1107,9 @@ mod tests {
"New hints should be queried after the work task is done"
);
assert_eq!(expected_layers, visible_hint_labels(editor, cx));
let inlay_cache = editor.inlay_hint_cache();
assert_eq!(
inlay_cache.version, edits_made,
editor.inlay_hint_cache().version,
edits_made,
"Cache version should udpate once after the work task is done"
);
});
@@ -1194,9 +1224,9 @@ mod tests {
"Should get its first hints when opening the editor"
);
assert_eq!(expected_layers, visible_hint_labels(editor, cx));
let inlay_cache = editor.inlay_hint_cache();
assert_eq!(
inlay_cache.version, 1,
editor.inlay_hint_cache().version,
1,
"Rust editor update the cache version after every cache/view change"
);
});
@@ -1252,8 +1282,7 @@ mod tests {
"Markdown editor should have a separate verison, repeating Rust editor rules"
);
assert_eq!(expected_layers, visible_hint_labels(editor, cx));
let inlay_cache = editor.inlay_hint_cache();
assert_eq!(inlay_cache.version, 1);
assert_eq!(editor.inlay_hint_cache().version, 1);
});
rs_editor.update(cx, |editor, cx| {
@@ -1269,9 +1298,9 @@ mod tests {
"Rust inlay cache should change after the edit"
);
assert_eq!(expected_layers, visible_hint_labels(editor, cx));
let inlay_cache = editor.inlay_hint_cache();
assert_eq!(
inlay_cache.version, 2,
editor.inlay_hint_cache().version,
2,
"Every time hint cache changes, cache version should be incremented"
);
});
@@ -1283,8 +1312,7 @@ mod tests {
"Markdown editor should not be affected by Rust editor changes"
);
assert_eq!(expected_layers, visible_hint_labels(editor, cx));
let inlay_cache = editor.inlay_hint_cache();
assert_eq!(inlay_cache.version, 1);
assert_eq!(editor.inlay_hint_cache().version, 1);
});
md_editor.update(cx, |editor, cx| {
@@ -1300,8 +1328,7 @@ mod tests {
"Rust editor should not be affected by Markdown editor changes"
);
assert_eq!(expected_layers, visible_hint_labels(editor, cx));
let inlay_cache = editor.inlay_hint_cache();
assert_eq!(inlay_cache.version, 2);
assert_eq!(editor.inlay_hint_cache().version, 2);
});
rs_editor.update(cx, |editor, cx| {
let expected_layers = vec!["1".to_string()];
@@ -1311,8 +1338,7 @@ mod tests {
"Markdown editor should also change independently"
);
assert_eq!(expected_layers, visible_hint_labels(editor, cx));
let inlay_cache = editor.inlay_hint_cache();
assert_eq!(inlay_cache.version, 2);
assert_eq!(editor.inlay_hint_cache().version, 2);
});
}
@@ -1433,9 +1459,9 @@ mod tests {
vec!["other hint".to_string(), "type hint".to_string()],
visible_hint_labels(editor, cx)
);
let inlay_cache = editor.inlay_hint_cache();
assert_eq!(
inlay_cache.version, edits_made,
editor.inlay_hint_cache().version,
edits_made,
"Should not update cache version due to new loaded hints being the same"
);
});
@@ -1476,7 +1502,7 @@ mod tests {
),
] {
edits_made += 1;
update_test_settings(cx, |settings| {
update_test_language_settings(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
enabled: true,
show_type_hints: new_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
@@ -1520,7 +1546,7 @@ mod tests {
edits_made += 1;
let another_allowed_hint_kinds = HashSet::from_iter([Some(InlayHintKind::Type)]);
update_test_settings(cx, |settings| {
update_test_language_settings(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
enabled: false,
show_type_hints: another_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
@@ -1568,16 +1594,15 @@ mod tests {
);
assert!(cached_hint_labels(editor).is_empty());
assert!(visible_hint_labels(editor, cx).is_empty());
let inlay_cache = editor.inlay_hint_cache();
assert_eq!(
inlay_cache.version, edits_made,
editor.inlay_hint_cache().version, edits_made,
"The editor should not update the cache version after /refresh query without updates"
);
});
let final_allowed_hint_kinds = HashSet::from_iter([Some(InlayHintKind::Parameter)]);
edits_made += 1;
update_test_settings(cx, |settings| {
update_test_language_settings(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
enabled: true,
show_type_hints: final_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
@@ -1641,8 +1666,7 @@ mod tests {
vec!["parameter hint".to_string()],
visible_hint_labels(editor, cx),
);
let inlay_cache = editor.inlay_hint_cache();
assert_eq!(inlay_cache.version, edits_made);
assert_eq!(editor.inlay_hint_cache().version, edits_made);
});
}
@@ -1720,9 +1744,8 @@ mod tests {
"Should get hints from the last edit landed only"
);
assert_eq!(expected_hints, visible_hint_labels(editor, cx));
let inlay_cache = editor.inlay_hint_cache();
assert_eq!(
inlay_cache.version, 1,
editor.inlay_hint_cache().version, 1,
"Only one update should be registered in the cache after all cancellations"
);
});
@@ -1766,9 +1789,9 @@ mod tests {
"Should get hints from the last edit landed only"
);
assert_eq!(expected_hints, visible_hint_labels(editor, cx));
let inlay_cache = editor.inlay_hint_cache();
assert_eq!(
inlay_cache.version, 2,
editor.inlay_hint_cache().version,
2,
"Should update the cache version once more, for the new change"
);
});
@@ -1886,9 +1909,8 @@ mod tests {
"Should have hints from both LSP requests made for a big file"
);
assert_eq!(expected_layers, visible_hint_labels(editor, cx));
let inlay_cache = editor.inlay_hint_cache();
assert_eq!(
inlay_cache.version, 2,
editor.inlay_hint_cache().version, 2,
"Both LSP queries should've bumped the cache version"
);
});
@@ -1918,8 +1940,7 @@ mod tests {
assert_eq!(expected_layers, cached_hint_labels(editor),
"Should have hints from the new LSP response after edit");
assert_eq!(expected_layers, visible_hint_labels(editor, cx));
let inlay_cache = editor.inlay_hint_cache();
assert_eq!(inlay_cache.version, 5, "Should update the cache for every LSP response with hints added");
assert_eq!(editor.inlay_hint_cache().version, 5, "Should update the cache for every LSP response with hints added");
});
}
@@ -2075,6 +2096,7 @@ mod tests {
panic!("unexpected uri: {:?}", params.text_document.uri);
};
// one hint per excerpt
let positions = [
lsp::Position::new(0, 2),
lsp::Position::new(4, 2),
@@ -2138,8 +2160,7 @@ mod tests {
"When scroll is at the edge of a multibuffer, its visible excerpts only should be queried for inlay hints"
);
assert_eq!(expected_layers, visible_hint_labels(editor, cx));
let inlay_cache = editor.inlay_hint_cache();
assert_eq!(inlay_cache.version, 4, "Every visible excerpt hints should bump the verison");
assert_eq!(editor.inlay_hint_cache().version, expected_layers.len(), "Every visible excerpt hints should bump the verison");
});
editor.update(cx, |editor, cx| {
@@ -2169,8 +2190,8 @@ mod tests {
assert_eq!(expected_layers, cached_hint_labels(editor),
"With more scrolls of the multibuffer, more hints should be added into the cache and nothing invalidated without edits");
assert_eq!(expected_layers, visible_hint_labels(editor, cx));
let inlay_cache = editor.inlay_hint_cache();
assert_eq!(inlay_cache.version, 9);
assert_eq!(editor.inlay_hint_cache().version, expected_layers.len(),
"Due to every excerpt having one hint, we update cache per new excerpt scrolled");
});
editor.update(cx, |editor, cx| {
@@ -2179,7 +2200,7 @@ mod tests {
});
});
cx.foreground().run_until_parked();
editor.update(cx, |editor, cx| {
let last_scroll_update_version = editor.update(cx, |editor, cx| {
let expected_layers = vec![
"main hint #0".to_string(),
"main hint #1".to_string(),
@@ -2197,8 +2218,8 @@ mod tests {
assert_eq!(expected_layers, cached_hint_labels(editor),
"After multibuffer was scrolled to the end, all hints for all excerpts should be fetched");
assert_eq!(expected_layers, visible_hint_labels(editor, cx));
let inlay_cache = editor.inlay_hint_cache();
assert_eq!(inlay_cache.version, 12);
assert_eq!(editor.inlay_hint_cache().version, expected_layers.len());
expected_layers.len()
});
editor.update(cx, |editor, cx| {
@@ -2225,12 +2246,14 @@ mod tests {
assert_eq!(expected_layers, cached_hint_labels(editor),
"After multibuffer was scrolled to the end, further scrolls up should not bring more hints");
assert_eq!(expected_layers, visible_hint_labels(editor, cx));
let inlay_cache = editor.inlay_hint_cache();
assert_eq!(inlay_cache.version, 12, "No updates should happen during scrolling already scolled buffer");
assert_eq!(editor.inlay_hint_cache().version, last_scroll_update_version, "No updates should happen during scrolling already scolled buffer");
});
editor_edited.store(true, Ordering::Release);
editor.update(cx, |editor, cx| {
editor.change_selections(None, cx, |s| {
s.select_ranges([Point::new(56, 0)..Point::new(56, 0)])
});
editor.handle_input("++++more text++++", cx);
});
cx.foreground().run_until_parked();
@@ -2240,19 +2263,253 @@ mod tests {
"main hint(edited) #1".to_string(),
"main hint(edited) #2".to_string(),
"main hint(edited) #3".to_string(),
"other hint #0".to_string(),
"other hint #1".to_string(),
"other hint #2".to_string(),
"other hint #3".to_string(),
"other hint #4".to_string(),
"other hint #5".to_string(),
"main hint(edited) #4".to_string(),
"main hint(edited) #5".to_string(),
"other hint(edited) #0".to_string(),
"other hint(edited) #1".to_string(),
];
assert_eq!(expected_layers, cached_hint_labels(editor),
"After multibuffer was edited, hints for the edited buffer (1st) should be invalidated and requeried for all of its visible excerpts, \
unedited (2nd) buffer should have the same hint");
assert_eq!(
expected_layers,
cached_hint_labels(editor),
"After multibuffer edit, editor gets scolled back to the last selection; \
all hints should be invalidated and requeried for all of its visible excerpts"
);
assert_eq!(expected_layers, visible_hint_labels(editor, cx));
let inlay_cache = editor.inlay_hint_cache();
assert_eq!(inlay_cache.version, 16);
assert_eq!(
editor.inlay_hint_cache().version,
last_scroll_update_version + expected_layers.len() + 1,
"Due to every excerpt having one hint, cache should update per new excerpt received + 1 for outdated hints removal"
);
});
}
#[gpui::test]
async fn test_excerpts_removed(
deterministic: Arc<Deterministic>,
cx: &mut gpui::TestAppContext,
) {
init_test(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
enabled: true,
show_type_hints: false,
show_parameter_hints: false,
show_other_hints: false,
})
});
let mut language = Language::new(
LanguageConfig {
name: "Rust".into(),
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
);
let mut fake_servers = language
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
capabilities: lsp::ServerCapabilities {
inlay_hint_provider: Some(lsp::OneOf::Left(true)),
..Default::default()
},
..Default::default()
}))
.await;
let language = Arc::new(language);
let fs = FakeFs::new(cx.background());
fs.insert_tree(
"/a",
json!({
"main.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|i| format!("let i = {i};\n")).collect::<Vec<_>>().join("")),
"other.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|j| format!("let j = {j};\n")).collect::<Vec<_>>().join("")),
}),
)
.await;
let project = Project::test(fs, ["/a".as_ref()], cx).await;
project.update(cx, |project, _| {
project.languages().add(Arc::clone(&language))
});
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let worktree_id = workspace.update(cx, |workspace, cx| {
workspace.project().read_with(cx, |project, cx| {
project.worktrees(cx).next().unwrap().read(cx).id()
})
});
let buffer_1 = project
.update(cx, |project, cx| {
project.open_buffer((worktree_id, "main.rs"), cx)
})
.await
.unwrap();
let buffer_2 = project
.update(cx, |project, cx| {
project.open_buffer((worktree_id, "other.rs"), cx)
})
.await
.unwrap();
let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
let (buffer_1_excerpts, buffer_2_excerpts) = multibuffer.update(cx, |multibuffer, cx| {
let buffer_1_excerpts = multibuffer.push_excerpts(
buffer_1.clone(),
[ExcerptRange {
context: Point::new(0, 0)..Point::new(2, 0),
primary: None,
}],
cx,
);
let buffer_2_excerpts = multibuffer.push_excerpts(
buffer_2.clone(),
[ExcerptRange {
context: Point::new(0, 1)..Point::new(2, 1),
primary: None,
}],
cx,
);
(buffer_1_excerpts, buffer_2_excerpts)
});
assert!(!buffer_1_excerpts.is_empty());
assert!(!buffer_2_excerpts.is_empty());
deterministic.run_until_parked();
cx.foreground().run_until_parked();
let (_, editor) =
cx.add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx));
let editor_edited = Arc::new(AtomicBool::new(false));
let fake_server = fake_servers.next().await.unwrap();
let closure_editor_edited = Arc::clone(&editor_edited);
fake_server
.handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
let task_editor_edited = Arc::clone(&closure_editor_edited);
async move {
let hint_text = if params.text_document.uri
== lsp::Url::from_file_path("/a/main.rs").unwrap()
{
"main hint"
} else if params.text_document.uri
== lsp::Url::from_file_path("/a/other.rs").unwrap()
{
"other hint"
} else {
panic!("unexpected uri: {:?}", params.text_document.uri);
};
let positions = [
lsp::Position::new(0, 2),
lsp::Position::new(4, 2),
lsp::Position::new(22, 2),
lsp::Position::new(44, 2),
lsp::Position::new(56, 2),
lsp::Position::new(67, 2),
];
let out_of_range_hint = lsp::InlayHint {
position: lsp::Position::new(
params.range.start.line + 99,
params.range.start.character + 99,
),
label: lsp::InlayHintLabel::String(
"out of excerpt range, should be ignored".to_string(),
),
kind: None,
text_edits: None,
tooltip: None,
padding_left: None,
padding_right: None,
data: None,
};
let edited = task_editor_edited.load(Ordering::Acquire);
Ok(Some(
std::iter::once(out_of_range_hint)
.chain(positions.into_iter().enumerate().map(|(i, position)| {
lsp::InlayHint {
position,
label: lsp::InlayHintLabel::String(format!(
"{hint_text}{} #{i}",
if edited { "(edited)" } else { "" },
)),
kind: None,
text_edits: None,
tooltip: None,
padding_left: None,
padding_right: None,
data: None,
}
}))
.collect(),
))
}
})
.next()
.await;
cx.foreground().run_until_parked();
editor.update(cx, |editor, cx| {
assert_eq!(
vec!["main hint #0".to_string(), "other hint #0".to_string()],
cached_hint_labels(editor),
"Cache should update for both excerpts despite hints display was disabled"
);
assert!(
visible_hint_labels(editor, cx).is_empty(),
"All hints are disabled and should not be shown despite being present in the cache"
);
assert_eq!(
editor.inlay_hint_cache().version,
2,
"Cache should update once per excerpt query"
);
});
editor.update(cx, |editor, cx| {
editor.buffer().update(cx, |multibuffer, cx| {
multibuffer.remove_excerpts(buffer_2_excerpts, cx)
})
});
cx.foreground().run_until_parked();
editor.update(cx, |editor, cx| {
assert_eq!(
vec!["main hint #0".to_string()],
cached_hint_labels(editor),
"For the removed excerpt, should clean corresponding cached hints"
);
assert!(
visible_hint_labels(editor, cx).is_empty(),
"All hints are disabled and should not be shown despite being present in the cache"
);
assert_eq!(
editor.inlay_hint_cache().version,
3,
"Excerpt removal should trigger cache update"
);
});
update_test_language_settings(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
enabled: true,
show_type_hints: true,
show_parameter_hints: true,
show_other_hints: true,
})
});
cx.foreground().run_until_parked();
editor.update(cx, |editor, cx| {
let expected_hints = vec!["main hint #0".to_string()];
assert_eq!(
expected_hints,
cached_hint_labels(editor),
"Hint display settings change should not change the cache"
);
assert_eq!(
expected_hints,
visible_hint_labels(editor, cx),
"Settings change should make cached hints visible"
);
assert_eq!(
editor.inlay_hint_cache().version,
4,
"Settings change should trigger cache update"
);
});
}
@@ -2269,7 +2526,7 @@ unedited (2nd) buffer should have the same hint");
crate::init(cx);
});
update_test_settings(cx, f);
update_test_language_settings(cx, f);
}
async fn prepare_test_objects(

View File

@@ -7,8 +7,10 @@ use anyhow::{Context, Result};
use collections::HashSet;
use futures::future::try_join_all;
use gpui::{
elements::*, geometry::vector::vec2f, AppContext, AsyncAppContext, Entity, ModelHandle,
Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle,
elements::*,
geometry::vector::{vec2f, Vector2F},
AppContext, AsyncAppContext, Entity, ModelHandle, Subscription, Task, View, ViewContext,
ViewHandle, WeakViewHandle,
};
use language::{
proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, OffsetRangeExt, Point,
@@ -750,6 +752,10 @@ impl Item for Editor {
Some(Box::new(handle.clone()))
}
fn pixel_position_of_cursor(&self) -> Option<Vector2F> {
self.pixel_position_of_newest_cursor
}
fn breadcrumb_location(&self) -> ToolbarItemLocation {
ToolbarItemLocation::PrimaryLeft { flex: None }
}
@@ -883,14 +889,24 @@ impl ProjectItem for Editor {
}
}
enum BufferSearchHighlights {}
pub(crate) enum BufferSearchHighlights {}
impl SearchableItem for Editor {
type Match = Range<Anchor>;
fn to_search_event(event: &Self::Event) -> Option<SearchEvent> {
fn to_search_event(
&mut self,
event: &Self::Event,
_: &mut ViewContext<Self>,
) -> Option<SearchEvent> {
match event {
Event::BufferEdited => Some(SearchEvent::MatchesInvalidated),
Event::SelectionsChanged { .. } => Some(SearchEvent::ActiveMatchChanged),
Event::SelectionsChanged { .. } => {
if self.selections.disjoint_anchors().len() == 1 {
Some(SearchEvent::ActiveMatchChanged)
} else {
None
}
}
_ => None,
}
}
@@ -936,46 +952,68 @@ impl SearchableItem for Editor {
cx: &mut ViewContext<Self>,
) {
self.unfold_ranges([matches[index].clone()], false, true, cx);
let range = self.range_for_match(&matches[index]);
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.select_ranges([matches[index].clone()])
});
s.select_ranges([range]);
})
}
fn select_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>) {
self.unfold_ranges(matches.clone(), false, false, cx);
let mut ranges = Vec::new();
for m in &matches {
ranges.push(self.range_for_match(&m))
}
self.change_selections(None, cx, |s| s.select_ranges(ranges));
}
fn match_index_for_direction(
&mut self,
matches: &Vec<Range<Anchor>>,
mut current_index: usize,
current_index: usize,
direction: Direction,
count: usize,
cx: &mut ViewContext<Self>,
) -> usize {
let buffer = self.buffer().read(cx).snapshot(cx);
let cursor = self.selections.newest_anchor().head();
if matches[current_index].start.cmp(&cursor, &buffer).is_gt() {
if direction == Direction::Prev {
if current_index == 0 {
current_index = matches.len() - 1;
let current_index_position = if self.selections.disjoint_anchors().len() == 1 {
self.selections.newest_anchor().head()
} else {
matches[current_index].start
};
let mut count = count % matches.len();
if count == 0 {
return current_index;
}
match direction {
Direction::Next => {
if matches[current_index]
.start
.cmp(&current_index_position, &buffer)
.is_gt()
{
count = count - 1
}
(current_index + count) % matches.len()
}
Direction::Prev => {
if matches[current_index]
.end
.cmp(&current_index_position, &buffer)
.is_lt()
{
count = count - 1;
}
if current_index >= count {
current_index - count
} else {
current_index -= 1;
matches.len() - (count - current_index)
}
}
} else if matches[current_index].end.cmp(&cursor, &buffer).is_lt() {
if direction == Direction::Next {
current_index = 0;
}
} else if direction == Direction::Prev {
if current_index == 0 {
current_index = matches.len() - 1;
} else {
current_index -= 1;
}
} else if direction == Direction::Next {
if current_index == matches.len() - 1 {
current_index = 0
} else {
current_index += 1;
}
};
current_index
}
}
fn find_matches(

View File

@@ -246,23 +246,26 @@ pub fn hide_link_definition(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
pub fn go_to_fetched_definition(
editor: &mut Editor,
point: DisplayPoint,
split: bool,
cx: &mut ViewContext<Editor>,
) {
go_to_fetched_definition_of_kind(LinkDefinitionKind::Symbol, editor, point, cx);
go_to_fetched_definition_of_kind(LinkDefinitionKind::Symbol, editor, point, split, cx);
}
pub fn go_to_fetched_type_definition(
editor: &mut Editor,
point: DisplayPoint,
split: bool,
cx: &mut ViewContext<Editor>,
) {
go_to_fetched_definition_of_kind(LinkDefinitionKind::Type, editor, point, cx);
go_to_fetched_definition_of_kind(LinkDefinitionKind::Type, editor, point, split, cx);
}
fn go_to_fetched_definition_of_kind(
kind: LinkDefinitionKind,
editor: &mut Editor,
point: DisplayPoint,
split: bool,
cx: &mut ViewContext<Editor>,
) {
let cached_definitions = editor.link_go_to_definition_state.definitions.clone();
@@ -275,7 +278,7 @@ fn go_to_fetched_definition_of_kind(
cx.focus_self();
}
editor.navigate_to_definitions(cached_definitions, cx);
editor.navigate_to_definitions(cached_definitions, split, cx);
} else {
editor.select(
SelectPhase::Begin {
@@ -403,7 +406,7 @@ mod tests {
});
cx.update_editor(|editor, cx| {
go_to_fetched_type_definition(editor, hover_point, cx);
go_to_fetched_type_definition(editor, hover_point, false, cx);
});
requests.next().await;
cx.foreground().run_until_parked();
@@ -614,7 +617,7 @@ mod tests {
// Cmd click with existing definition doesn't re-request and dismisses highlight
cx.update_editor(|editor, cx| {
go_to_fetched_definition(editor, hover_point, cx);
go_to_fetched_definition(editor, hover_point, false, cx);
});
// Assert selection moved to to definition
cx.lsp
@@ -655,7 +658,7 @@ mod tests {
])))
});
cx.update_editor(|editor, cx| {
go_to_fetched_definition(editor, hover_point, cx);
go_to_fetched_definition(editor, hover_point, false, cx);
});
requests.next().await;
cx.foreground().run_until_parked();

View File

@@ -193,7 +193,11 @@ pub fn next_subword_end(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPo
})
}
pub fn start_of_paragraph(map: &DisplaySnapshot, display_point: DisplayPoint) -> DisplayPoint {
pub fn start_of_paragraph(
map: &DisplaySnapshot,
display_point: DisplayPoint,
mut count: usize,
) -> DisplayPoint {
let point = display_point.to_point(map);
if point.row == 0 {
return map.max_point();
@@ -203,7 +207,11 @@ pub fn start_of_paragraph(map: &DisplaySnapshot, display_point: DisplayPoint) ->
for row in (0..point.row + 1).rev() {
let blank = map.buffer_snapshot.is_line_blank(row);
if found_non_blank_line && blank {
return Point::new(row, 0).to_display_point(map);
if count <= 1 {
return Point::new(row, 0).to_display_point(map);
}
count -= 1;
found_non_blank_line = false;
}
found_non_blank_line |= !blank;
@@ -212,7 +220,11 @@ pub fn start_of_paragraph(map: &DisplaySnapshot, display_point: DisplayPoint) ->
DisplayPoint::zero()
}
pub fn end_of_paragraph(map: &DisplaySnapshot, display_point: DisplayPoint) -> DisplayPoint {
pub fn end_of_paragraph(
map: &DisplaySnapshot,
display_point: DisplayPoint,
mut count: usize,
) -> DisplayPoint {
let point = display_point.to_point(map);
if point.row == map.max_buffer_row() {
return DisplayPoint::zero();
@@ -222,7 +234,11 @@ pub fn end_of_paragraph(map: &DisplaySnapshot, display_point: DisplayPoint) -> D
for row in point.row..map.max_buffer_row() + 1 {
let blank = map.buffer_snapshot.is_line_blank(row);
if found_non_blank_line && blank {
return Point::new(row, 0).to_display_point(map);
if count <= 1 {
return Point::new(row, 0).to_display_point(map);
}
count -= 1;
found_non_blank_line = false;
}
found_non_blank_line |= !blank;

View File

@@ -16,13 +16,13 @@ use crate::{
Anchor, DisplayPoint, ExcerptId, MultiBuffer, MultiBufferSnapshot, SelectMode, ToOffset,
};
#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct PendingSelection {
pub selection: Selection<Anchor>,
pub mode: SelectMode,
}
#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct SelectionsCollection {
display_map: ModelHandle<DisplayMap>,
buffer: ModelHandle<MultiBuffer>,
@@ -138,7 +138,7 @@ impl SelectionsCollection {
.collect()
}
// Returns all of the selections, adjusted to take into account the selection line_mode
/// Returns all of the selections, adjusted to take into account the selection line_mode
pub fn all_adjusted(&self, cx: &mut AppContext) -> Vec<Selection<Point>> {
let mut selections = self.all::<Point>(cx);
if self.line_mode {

View File

@@ -210,6 +210,10 @@ impl<'a> EditorTestContext<'a> {
self.assert_selections(expected_selections, marked_text.to_string())
}
pub fn editor_state(&mut self) -> String {
generate_marked_text(self.buffer_text().as_str(), &self.editor_selections(), true)
}
#[track_caller]
pub fn assert_editor_background_highlights<Tag: 'static>(&mut self, marked_text: &str) {
let expected_ranges = self.ranges(marked_text);
@@ -248,14 +252,8 @@ impl<'a> EditorTestContext<'a> {
self.assert_selections(expected_selections, expected_marked_text)
}
#[track_caller]
fn assert_selections(
&mut self,
expected_selections: Vec<Range<usize>>,
expected_marked_text: String,
) {
let actual_selections = self
.editor
fn editor_selections(&self) -> Vec<Range<usize>> {
self.editor
.read_with(self.cx, |editor, cx| editor.selections.all::<usize>(cx))
.into_iter()
.map(|s| {
@@ -265,12 +263,22 @@ impl<'a> EditorTestContext<'a> {
s.start..s.end
}
})
.collect::<Vec<_>>();
.collect::<Vec<_>>()
}
#[track_caller]
fn assert_selections(
&mut self,
expected_selections: Vec<Range<usize>>,
expected_marked_text: String,
) {
let actual_selections = self.editor_selections();
let actual_marked_text =
generate_marked_text(&self.buffer_text(), &actual_selections, true);
if expected_selections != actual_selections {
panic!(
indoc! {"
{}Editor has unexpected selections.
Expected selections:

View File

@@ -60,6 +60,7 @@ pub(crate) struct FeedbackEditor {
system_specs: SystemSpecs,
editor: ViewHandle<Editor>,
project: ModelHandle<Project>,
pub allow_submission: bool,
}
impl FeedbackEditor {
@@ -82,10 +83,15 @@ impl FeedbackEditor {
system_specs: system_specs.clone(),
editor,
project,
allow_submission: true,
}
}
pub fn submit(&mut self, cx: &mut ViewContext<Self>) -> Task<anyhow::Result<()>> {
if !self.allow_submission {
return Task::ready(Ok(()));
}
let feedback_text = self.editor.read(cx).text(cx);
let feedback_char_count = feedback_text.chars().count();
let feedback_text = feedback_text.trim().to_string();
@@ -122,19 +128,26 @@ impl FeedbackEditor {
let answer = answer.recv().await;
if answer == Some(0) {
this.update(&mut cx, |feedback_editor, cx| {
feedback_editor.set_allow_submission(false, cx);
})
.log_err();
match FeedbackEditor::submit_feedback(&feedback_text, client, specs).await {
Ok(_) => {
this.update(&mut cx, |_, cx| cx.emit(editor::Event::Closed))
.log_err();
}
Err(error) => {
log::error!("{}", error);
this.update(&mut cx, |_, cx| {
this.update(&mut cx, |feedback_editor, cx| {
cx.prompt(
PromptLevel::Critical,
FEEDBACK_SUBMISSION_ERROR_TEXT,
&["OK"],
);
feedback_editor.set_allow_submission(true, cx);
})
.log_err();
}
@@ -146,6 +159,11 @@ impl FeedbackEditor {
Task::ready(Ok(()))
}
fn set_allow_submission(&mut self, allow_submission: bool, cx: &mut ViewContext<Self>) {
self.allow_submission = allow_submission;
cx.notify();
}
async fn submit_feedback(
feedback_text: &str,
zed_client: Arc<Client>,
@@ -362,8 +380,13 @@ impl Item for FeedbackEditor {
impl SearchableItem for FeedbackEditor {
type Match = Range<Anchor>;
fn to_search_event(event: &Self::Event) -> Option<workspace::searchable::SearchEvent> {
Editor::to_search_event(event)
fn to_search_event(
&mut self,
event: &Self::Event,
cx: &mut ViewContext<Self>,
) -> Option<workspace::searchable::SearchEvent> {
self.editor
.update(cx, |editor, cx| editor.to_search_event(event, cx))
}
fn clear_matches(&mut self, cx: &mut ViewContext<Self>) {
@@ -391,6 +414,11 @@ impl SearchableItem for FeedbackEditor {
.update(cx, |editor, cx| editor.activate_match(index, matches, cx))
}
fn select_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>) {
self.editor
.update(cx, |e, cx| e.select_matches(matches, cx))
}
fn find_matches(
&mut self,
query: project::search::SearchQuery,

View File

@@ -46,10 +46,28 @@ impl View for SubmitFeedbackButton {
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
let theme = theme::current(cx).clone();
let allow_submission = self
.active_item
.as_ref()
.map_or(true, |i| i.read(cx).allow_submission);
enum SubmitFeedbackButton {}
MouseEventHandler::<SubmitFeedbackButton, Self>::new(0, cx, |state, _| {
let style = theme.feedback.submit_button.style_for(state);
Label::new("Submit as Markdown", style.text.clone())
let text;
let style = if allow_submission {
text = "Submit as Markdown";
theme.feedback.submit_button.style_for(state)
} else {
text = "Submitting...";
theme
.feedback
.submit_button
.disabled
.as_ref()
.unwrap_or(&theme.feedback.submit_button.default)
};
Label::new(text, style.text.clone())
.contained()
.with_style(style.container)
})

View File

@@ -442,53 +442,71 @@ impl PickerDelegate for FileFinderDelegate {
}
}
fn confirm(&mut self, cx: &mut ViewContext<FileFinder>) {
fn confirm(&mut self, secondary: bool, cx: &mut ViewContext<FileFinder>) {
if let Some(m) = self.matches.get(self.selected_index()) {
if let Some(workspace) = self.workspace.upgrade(cx) {
let open_task = workspace.update(cx, |workspace, cx| match m {
Match::History(history_match) => {
let worktree_id = history_match.project.worktree_id;
if workspace
.project()
.read(cx)
.worktree_for_id(worktree_id, cx)
.is_some()
{
workspace.open_path(
ProjectPath {
worktree_id,
path: Arc::clone(&history_match.project.path),
},
None,
true,
cx,
)
let open_task = workspace.update(cx, move |workspace, cx| {
let split_or_open = |workspace: &mut Workspace, project_path, cx| {
if secondary {
workspace.split_path(project_path, cx)
} else {
match history_match.absolute.as_ref() {
Some(abs_path) => {
workspace.open_abs_path(abs_path.to_path_buf(), false, cx)
}
None => workspace.open_path(
workspace.open_path(project_path, None, true, cx)
}
};
match m {
Match::History(history_match) => {
let worktree_id = history_match.project.worktree_id;
if workspace
.project()
.read(cx)
.worktree_for_id(worktree_id, cx)
.is_some()
{
split_or_open(
workspace,
ProjectPath {
worktree_id,
path: Arc::clone(&history_match.project.path),
},
None,
true,
cx,
),
)
} else {
match history_match.absolute.as_ref() {
Some(abs_path) => {
if secondary {
workspace.split_abs_path(
abs_path.to_path_buf(),
false,
cx,
)
} else {
workspace.open_abs_path(
abs_path.to_path_buf(),
false,
cx,
)
}
}
None => split_or_open(
workspace,
ProjectPath {
worktree_id,
path: Arc::clone(&history_match.project.path),
},
cx,
),
}
}
}
Match::Search(m) => split_or_open(
workspace,
ProjectPath {
worktree_id: WorktreeId::from_usize(m.worktree_id),
path: m.path.clone(),
},
cx,
),
}
Match::Search(m) => workspace.open_path(
ProjectPath {
worktree_id: WorktreeId::from_usize(m.worktree_id),
path: m.path.clone(),
},
None,
true,
cx,
),
});
let row = self

View File

@@ -1,6 +1,6 @@
use anyhow::Result;
use collections::HashMap;
use git2::{BranchType, ErrorCode};
use git2::{BranchType, StatusShow};
use parking_lot::Mutex;
use rpc::proto;
use serde_derive::{Deserialize, Serialize};
@@ -10,6 +10,7 @@ use std::{
os::unix::prelude::OsStrExt,
path::{Component, Path, PathBuf},
sync::Arc,
time::SystemTime,
};
use sum_tree::{MapSeekTarget, TreeMap};
use util::ResultExt;
@@ -25,20 +26,30 @@ pub struct Branch {
#[async_trait::async_trait]
pub trait GitRepository: Send {
fn reload_index(&self);
fn load_index_text(&self, relative_file_path: &Path) -> Option<String>;
fn branch_name(&self) -> Option<String>;
fn statuses(&self) -> Option<TreeMap<RepoPath, GitFileStatus>>;
/// Get the statuses of all of the files in the index that start with the given
/// path and have changes with resepect to the HEAD commit. This is fast because
/// the index stores hashes of trees, so that unchanged directories can be skipped.
fn staged_statuses(&self, path_prefix: &Path) -> TreeMap<RepoPath, GitFileStatus>;
fn status(&self, path: &RepoPath) -> Result<Option<GitFileStatus>>;
fn branches(&self) -> Result<Vec<Branch>> {
Ok(vec![])
}
fn change_branch(&self, _: &str) -> Result<()> {
Ok(())
}
/// Get the status of a given file in the working directory with respect to
/// the index. In the common case, when there are no changes, this only requires
/// an index lookup. The index stores the mtime of each file when it was added,
/// so there's no work to do if the mtime matches.
fn unstaged_status(&self, path: &RepoPath, mtime: SystemTime) -> Option<GitFileStatus>;
/// Get the status of a given file in the working directory with respect to
/// the HEAD commit. In the common case, when there are no changes, this only
/// requires an index lookup and blob comparison between the index and the HEAD
/// commit. The index stores the mtime of each file when it was added, so there's
/// no need to consider the working directory file if the mtime matches.
fn status(&self, path: &RepoPath, mtime: SystemTime) -> Option<GitFileStatus>;
fn branches(&self) -> Result<Vec<Branch>>;
fn change_branch(&self, _: &str) -> Result<()>;
fn create_branch(&self, _: &str) -> Result<()>;
}
impl std::fmt::Debug for dyn GitRepository {
@@ -47,7 +58,6 @@ impl std::fmt::Debug for dyn GitRepository {
}
}
#[async_trait::async_trait]
impl GitRepository for LibGitRepository {
fn reload_index(&self) {
if let Ok(mut index) = self.index() {
@@ -85,39 +95,67 @@ impl GitRepository for LibGitRepository {
Some(branch.to_string())
}
fn statuses(&self) -> Option<TreeMap<RepoPath, GitFileStatus>> {
let statuses = self.statuses(None).log_err()?;
fn staged_statuses(&self, path_prefix: &Path) -> TreeMap<RepoPath, GitFileStatus> {
let mut map = TreeMap::default();
for status in statuses
.iter()
.filter(|status| !status.status().contains(git2::Status::IGNORED))
{
let path = RepoPath(PathBuf::from(OsStr::from_bytes(status.path_bytes())));
let Some(status) = read_status(status.status()) else {
continue
};
let mut options = git2::StatusOptions::new();
options.pathspec(path_prefix);
options.show(StatusShow::Index);
map.insert(path, status)
}
Some(map)
}
fn status(&self, path: &RepoPath) -> Result<Option<GitFileStatus>> {
let status = self.status_file(path);
match status {
Ok(status) => Ok(read_status(status)),
Err(e) => {
if e.code() == ErrorCode::NotFound {
Ok(None)
} else {
Err(e.into())
if let Some(statuses) = self.statuses(Some(&mut options)).log_err() {
for status in statuses.iter() {
let path = RepoPath(PathBuf::from(OsStr::from_bytes(status.path_bytes())));
let status = status.status();
if !status.contains(git2::Status::IGNORED) {
if let Some(status) = read_status(status) {
map.insert(path, status)
}
}
}
}
map
}
fn unstaged_status(&self, path: &RepoPath, mtime: SystemTime) -> Option<GitFileStatus> {
// If the file has not changed since it was added to the index, then
// there can't be any changes.
if matches_index(self, path, mtime) {
return None;
}
let mut options = git2::StatusOptions::new();
options.pathspec(&path.0);
options.disable_pathspec_match(true);
options.include_untracked(true);
options.recurse_untracked_dirs(true);
options.include_unmodified(true);
options.show(StatusShow::Workdir);
let statuses = self.statuses(Some(&mut options)).log_err()?;
let status = statuses.get(0).and_then(|s| read_status(s.status()));
status
}
fn status(&self, path: &RepoPath, mtime: SystemTime) -> Option<GitFileStatus> {
let mut options = git2::StatusOptions::new();
options.pathspec(&path.0);
options.disable_pathspec_match(true);
options.include_untracked(true);
options.recurse_untracked_dirs(true);
options.include_unmodified(true);
// If the file has not changed since it was added to the index, then
// there's no need to examine the working directory file: just compare
// the blob in the index to the one in the HEAD commit.
if matches_index(self, path, mtime) {
options.show(StatusShow::Index);
}
let statuses = self.statuses(Some(&mut options)).log_err()?;
let status = statuses.get(0).and_then(|s| read_status(s.status()));
status
}
fn branches(&self) -> Result<Vec<Branch>> {
let local_branches = self.branches(Some(BranchType::Local))?;
let valid_branches = local_branches
@@ -152,6 +190,27 @@ impl GitRepository for LibGitRepository {
)?;
Ok(())
}
fn create_branch(&self, name: &str) -> Result<()> {
let current_commit = self.head()?.peel_to_commit()?;
self.branch(name, &current_commit, false)?;
Ok(())
}
}
fn matches_index(repo: &LibGitRepository, path: &RepoPath, mtime: SystemTime) -> bool {
if let Some(index) = repo.index().log_err() {
if let Some(entry) = index.get_path(&path, 0) {
if let Some(mtime) = mtime.duration_since(SystemTime::UNIX_EPOCH).log_err() {
if entry.mtime.seconds() == mtime.as_secs() as i32
&& entry.mtime.nanoseconds() == mtime.subsec_nanos()
{
return true;
}
}
}
}
false
}
fn read_status(status: git2::Status) -> Option<GitFileStatus> {
@@ -203,18 +262,40 @@ impl GitRepository for FakeGitRepository {
state.branch_name.clone()
}
fn statuses(&self) -> Option<TreeMap<RepoPath, GitFileStatus>> {
let state = self.state.lock();
fn staged_statuses(&self, path_prefix: &Path) -> TreeMap<RepoPath, GitFileStatus> {
let mut map = TreeMap::default();
let state = self.state.lock();
for (repo_path, status) in state.worktree_statuses.iter() {
map.insert(repo_path.to_owned(), status.to_owned());
if repo_path.0.starts_with(path_prefix) {
map.insert(repo_path.to_owned(), status.to_owned());
}
}
Some(map)
map
}
fn status(&self, path: &RepoPath) -> Result<Option<GitFileStatus>> {
fn unstaged_status(&self, _path: &RepoPath, _mtime: SystemTime) -> Option<GitFileStatus> {
None
}
fn status(&self, path: &RepoPath, _mtime: SystemTime) -> Option<GitFileStatus> {
let state = self.state.lock();
Ok(state.worktree_statuses.get(path).cloned())
state.worktree_statuses.get(path).cloned()
}
fn branches(&self) -> Result<Vec<Branch>> {
Ok(vec![])
}
fn change_branch(&self, name: &str) -> Result<()> {
let mut state = self.state.lock();
state.branch_name = Some(name.to_owned());
Ok(())
}
fn create_branch(&self, name: &str) -> Result<()> {
let mut state = self.state.lock();
state.branch_name = Some(name.to_owned());
Ok(())
}
}

View File

@@ -1073,7 +1073,7 @@ impl AppContext {
pub fn is_action_available(&self, action: &dyn Action) -> bool {
let mut available_in_window = false;
let action_type = action.as_any().type_id();
let action_id = action.id();
if let Some(window_id) = self.platform.main_window_id() {
available_in_window = self
.read_window(window_id, |cx| {
@@ -1083,7 +1083,7 @@ impl AppContext {
cx.views_metadata.get(&(window_id, view_id))
{
if let Some(actions) = cx.actions.get(&view_metadata.type_id) {
if actions.contains_key(&action_type) {
if actions.contains_key(&action_id) {
return true;
}
}
@@ -1094,7 +1094,7 @@ impl AppContext {
})
.unwrap_or(false);
}
available_in_window || self.global_actions.contains_key(&action_type)
available_in_window || self.global_actions.contains_key(&action_id)
}
fn actions_mut(
@@ -3399,7 +3399,7 @@ impl<'a, 'b, 'c, V: View> LayoutContext<'a, 'b, 'c, V> {
for (i, view_id) in self.ancestors(view_id).enumerate() {
if let Some(view_metadata) = self.views_metadata.get(&(window_id, view_id)) {
if let Some(actions) = self.actions.get(&view_metadata.type_id) {
if actions.contains_key(&action.as_any().type_id()) {
if actions.contains_key(&action.id()) {
handler_depth = Some(i);
}
}
@@ -3407,22 +3407,18 @@ impl<'a, 'b, 'c, V: View> LayoutContext<'a, 'b, 'c, V> {
}
}
if self.global_actions.contains_key(&action.as_any().type_id()) {
if self.global_actions.contains_key(&action.id()) {
handler_depth = Some(contexts.len())
}
let action_contexts = if let Some(depth) = handler_depth {
&contexts[depth..]
} else {
&contexts
};
self.keystroke_matcher
.bindings_for_action_type(action.as_any().type_id())
.find_map(|b| {
let highest_handler = handler_depth?;
if action.eq(b.action())
&& (0..=highest_handler).any(|depth| b.match_context(&contexts[depth..]))
{
Some(b.keystrokes().into())
} else {
None
}
})
.keystrokes_for_action(action, action_contexts)
}
fn notify_if_view_ancestors_change(&mut self, view_id: usize) {

View File

@@ -14,8 +14,8 @@ use crate::{
text_layout::TextLayoutCache,
util::post_inc,
Action, AnyView, AnyViewHandle, AppContext, BorrowAppContext, BorrowWindowContext, Effect,
Element, Entity, Handle, LayoutContext, MouseRegion, MouseRegionId, NoAction, SceneBuilder,
Subscription, View, ViewContext, ViewHandle, WindowInvalidation,
Element, Entity, Handle, LayoutContext, MouseRegion, MouseRegionId, SceneBuilder, Subscription,
View, ViewContext, ViewHandle, WindowInvalidation,
};
use anyhow::{anyhow, bail, Result};
use collections::{HashMap, HashSet};
@@ -363,17 +363,13 @@ impl<'a> WindowContext<'a> {
) -> Vec<(&'static str, Box<dyn Action>, SmallVec<[Binding; 1]>)> {
let window_id = self.window_id;
let mut contexts = Vec::new();
let mut handler_depths_by_action_type = HashMap::<TypeId, usize>::default();
let mut handler_depths_by_action_id = HashMap::<TypeId, usize>::default();
for (depth, view_id) in self.ancestors(view_id).enumerate() {
if let Some(view_metadata) = self.views_metadata.get(&(window_id, view_id)) {
contexts.push(view_metadata.keymap_context.clone());
if let Some(actions) = self.actions.get(&view_metadata.type_id) {
handler_depths_by_action_type.extend(
actions
.keys()
.copied()
.map(|action_type| (action_type, depth)),
);
handler_depths_by_action_id
.extend(actions.keys().copied().map(|action_id| (action_id, depth)));
}
} else {
log::error!(
@@ -383,21 +379,21 @@ impl<'a> WindowContext<'a> {
}
}
handler_depths_by_action_type.extend(
handler_depths_by_action_id.extend(
self.global_actions
.keys()
.copied()
.map(|action_type| (action_type, contexts.len())),
.map(|action_id| (action_id, contexts.len())),
);
self.action_deserializers
.iter()
.filter_map(move |(name, (type_id, deserialize))| {
if let Some(action_depth) = handler_depths_by_action_type.get(type_id).copied() {
.filter_map(move |(name, (action_id, deserialize))| {
if let Some(action_depth) = handler_depths_by_action_id.get(action_id).copied() {
let action = deserialize(serde_json::Value::Object(Default::default())).ok()?;
let bindings = self
.keystroke_matcher
.bindings_for_action_type(*type_id)
.bindings_for_action(*action_id)
.filter(|b| {
action.eq(b.action())
&& (0..=action_depth)
@@ -434,11 +430,7 @@ impl<'a> WindowContext<'a> {
MatchResult::None => false,
MatchResult::Pending => true,
MatchResult::Matches(matches) => {
let no_action_id = (NoAction {}).id();
for (view_id, action) in matches {
if action.id() == no_action_id {
return false;
}
if self.dispatch_action(Some(*view_id), action.as_ref()) {
self.keystroke_matcher.clear_pending();
handled_by = Some(action.boxed_clone());
@@ -526,6 +518,18 @@ impl<'a> WindowContext<'a> {
// NOTE: The order of event pushes is important! MouseUp events MUST be fired
// before click events, and so the MouseUp events need to be pushed before
// MouseClick events.
// Synthesize one last drag event to end the drag
mouse_events.push(MouseEvent::Drag(MouseDrag {
region: Default::default(),
prev_mouse_position: self.window.mouse_position,
platform_event: MouseMovedEvent {
position: e.position,
pressed_button: Some(e.button),
modifiers: e.modifiers,
},
end: true,
}));
mouse_events.push(MouseEvent::Up(MouseUp {
region: Default::default(),
platform_event: e.clone(),
@@ -573,8 +577,16 @@ impl<'a> WindowContext<'a> {
region: Default::default(),
prev_mouse_position: self.window.mouse_position,
platform_event: e.clone(),
end: false,
}));
} else if let Some((_, clicked_button)) = self.window.clicked_region {
mouse_events.push(MouseEvent::Drag(MouseDrag {
region: Default::default(),
prev_mouse_position: self.window.mouse_position,
platform_event: e.clone(),
end: true,
}));
// Mouse up event happened outside the current window. Simulate mouse up button event
let button_event = e.to_button_event(clicked_button);
mouse_events.push(MouseEvent::Up(MouseUp {
@@ -1268,6 +1280,19 @@ impl Vector2FExt for Vector2F {
}
}
pub trait RectFExt {
fn length_along(self, axis: Axis) -> f32;
}
impl RectFExt for RectF {
fn length_along(self, axis: Axis) -> f32 {
match axis {
Axis::Horizontal => self.width(),
Axis::Vertical => self.height(),
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct SizeConstraint {
pub min: Vector2F,

View File

@@ -147,6 +147,9 @@ impl<V: View> Element<V> for Resizable<V> {
let max_size = side.relevant_component(constraint.max);
let on_resize = self.on_resize.clone();
move |event, view: &mut V, cx| {
if event.end {
return;
}
let new_size = min_size
.max(prev_size + side.compute_delta(event))
.min(max_size)

View File

@@ -27,7 +27,7 @@ pub mod json;
pub mod keymap_matcher;
pub mod platform;
pub use gpui_macros::{test, Element};
pub use window::{Axis, SizeConstraint, Vector2FExt, WindowContext};
pub use window::{Axis, RectFExt, SizeConstraint, Vector2FExt, WindowContext};
pub use anyhow;
pub use serde_json;

View File

@@ -8,7 +8,7 @@ use std::{any::TypeId, fmt::Debug};
use collections::HashMap;
use smallvec::SmallVec;
use crate::Action;
use crate::{Action, NoAction};
pub use binding::{Binding, BindingMatchResult};
pub use keymap::Keymap;
@@ -47,8 +47,8 @@ impl KeymapMatcher {
self.keymap.clear();
}
pub fn bindings_for_action_type(&self, action_type: TypeId) -> impl Iterator<Item = &Binding> {
self.keymap.bindings_for_action_type(action_type)
pub fn bindings_for_action(&self, action_id: TypeId) -> impl Iterator<Item = &Binding> {
self.keymap.bindings_for_action(action_id)
}
pub fn clear_pending(&mut self) {
@@ -81,6 +81,7 @@ impl KeymapMatcher {
// The key is the reverse position of the binding in the bindings list so that later bindings
// match before earlier ones in the user's config
let mut matched_bindings: Vec<(usize, Box<dyn Action>)> = Default::default();
let no_action_id = (NoAction {}).id();
let first_keystroke = self.pending_keystrokes.is_empty();
self.pending_keystrokes.push(keystroke.clone());
@@ -108,7 +109,9 @@ impl KeymapMatcher {
match binding.match_keys_and_context(&self.pending_keystrokes, &self.contexts[i..])
{
BindingMatchResult::Complete(action) => {
matched_bindings.push((*view_id, action));
if action.id() != no_action_id {
matched_bindings.push((*view_id, action));
}
}
BindingMatchResult::Partial => {
self.pending_views

View File

@@ -7,8 +7,8 @@ use super::{KeymapContext, KeymapContextPredicate, Keystroke};
pub struct Binding {
action: Box<dyn Action>,
keystrokes: SmallVec<[Keystroke; 2]>,
context_predicate: Option<KeymapContextPredicate>,
pub(super) keystrokes: SmallVec<[Keystroke; 2]>,
pub(super) context_predicate: Option<KeymapContextPredicate>,
}
impl std::fmt::Debug for Binding {

View File

@@ -1,61 +1,388 @@
use collections::HashSet;
use smallvec::SmallVec;
use std::{
any::{Any, TypeId},
collections::HashMap,
};
use std::{any::TypeId, collections::HashMap};
use super::Binding;
use crate::{Action, NoAction};
use super::{Binding, KeymapContextPredicate, Keystroke};
#[derive(Default)]
pub struct Keymap {
bindings: Vec<Binding>,
binding_indices_by_action_type: HashMap<TypeId, SmallVec<[usize; 3]>>,
binding_indices_by_action_id: HashMap<TypeId, SmallVec<[usize; 3]>>,
disabled_keystrokes: HashMap<SmallVec<[Keystroke; 2]>, HashSet<Option<KeymapContextPredicate>>>,
}
impl Keymap {
pub fn new(bindings: Vec<Binding>) -> Self {
let mut binding_indices_by_action_type = HashMap::new();
for (ix, binding) in bindings.iter().enumerate() {
binding_indices_by_action_type
.entry(binding.action().type_id())
.or_insert_with(SmallVec::new)
.push(ix);
}
Self {
binding_indices_by_action_type,
bindings,
}
#[cfg(test)]
pub(super) fn new(bindings: Vec<Binding>) -> Self {
let mut this = Self::default();
this.add_bindings(bindings);
this
}
pub(crate) fn bindings_for_action_type(
pub(crate) fn bindings_for_action(
&self,
action_type: TypeId,
action_id: TypeId,
) -> impl Iterator<Item = &'_ Binding> {
self.binding_indices_by_action_type
.get(&action_type)
self.binding_indices_by_action_id
.get(&action_id)
.map(SmallVec::as_slice)
.unwrap_or(&[])
.iter()
.map(|ix| &self.bindings[*ix])
.filter(|binding| !self.binding_disabled(binding))
}
pub(crate) fn add_bindings<T: IntoIterator<Item = Binding>>(&mut self, bindings: T) {
let no_action_id = (NoAction {}).id();
let mut new_bindings = Vec::new();
let mut has_new_disabled_keystrokes = false;
for binding in bindings {
self.binding_indices_by_action_type
.entry(binding.action().as_any().type_id())
.or_default()
.push(self.bindings.len());
self.bindings.push(binding);
if binding.action().id() == no_action_id {
has_new_disabled_keystrokes |= self
.disabled_keystrokes
.entry(binding.keystrokes)
.or_default()
.insert(binding.context_predicate);
} else {
new_bindings.push(binding);
}
}
if has_new_disabled_keystrokes {
self.binding_indices_by_action_id.retain(|_, indices| {
indices.retain(|ix| {
let binding = &self.bindings[*ix];
match self.disabled_keystrokes.get(&binding.keystrokes) {
Some(disabled_predicates) => {
!disabled_predicates.contains(&binding.context_predicate)
}
None => true,
}
});
!indices.is_empty()
});
}
for new_binding in new_bindings {
if !self.binding_disabled(&new_binding) {
self.binding_indices_by_action_id
.entry(new_binding.action().id())
.or_default()
.push(self.bindings.len());
self.bindings.push(new_binding);
}
}
}
pub(crate) fn clear(&mut self) {
self.bindings.clear();
self.binding_indices_by_action_type.clear();
self.binding_indices_by_action_id.clear();
self.disabled_keystrokes.clear();
}
pub fn bindings(&self) -> &Vec<Binding> {
&self.bindings
pub fn bindings(&self) -> Vec<&Binding> {
self.bindings
.iter()
.filter(|binding| !self.binding_disabled(binding))
.collect()
}
fn binding_disabled(&self, binding: &Binding) -> bool {
match self.disabled_keystrokes.get(&binding.keystrokes) {
Some(disabled_predicates) => disabled_predicates.contains(&binding.context_predicate),
None => false,
}
}
}
#[cfg(test)]
mod tests {
use crate::actions;
use super::*;
actions!(
keymap_test,
[Present1, Present2, Present3, Duplicate, Missing]
);
#[test]
fn regular_keymap() {
let present_1 = Binding::new("ctrl-q", Present1 {}, None);
let present_2 = Binding::new("ctrl-w", Present2 {}, Some("pane"));
let present_3 = Binding::new("ctrl-e", Present3 {}, Some("editor"));
let keystroke_duplicate_to_1 = Binding::new("ctrl-q", Duplicate {}, None);
let full_duplicate_to_2 = Binding::new("ctrl-w", Present2 {}, Some("pane"));
let missing = Binding::new("ctrl-r", Missing {}, None);
let all_bindings = [
&present_1,
&present_2,
&present_3,
&keystroke_duplicate_to_1,
&full_duplicate_to_2,
&missing,
];
let mut keymap = Keymap::default();
assert_absent(&keymap, &all_bindings);
assert!(keymap.bindings().is_empty());
keymap.add_bindings([present_1.clone(), present_2.clone(), present_3.clone()]);
assert_absent(&keymap, &[&keystroke_duplicate_to_1, &missing]);
assert_present(
&keymap,
&[(&present_1, "q"), (&present_2, "w"), (&present_3, "e")],
);
keymap.add_bindings([
keystroke_duplicate_to_1.clone(),
full_duplicate_to_2.clone(),
]);
assert_absent(&keymap, &[&missing]);
assert!(
!keymap.binding_disabled(&keystroke_duplicate_to_1),
"Duplicate binding 1 was added and should not be disabled"
);
assert!(
!keymap.binding_disabled(&full_duplicate_to_2),
"Duplicate binding 2 was added and should not be disabled"
);
assert_eq!(
keymap
.bindings_for_action(keystroke_duplicate_to_1.action().id())
.map(|binding| &binding.keystrokes)
.flatten()
.collect::<Vec<_>>(),
vec![&Keystroke {
ctrl: true,
alt: false,
shift: false,
cmd: false,
function: false,
key: "q".to_string()
}],
"{keystroke_duplicate_to_1:?} should have the expected keystroke in the keymap"
);
assert_eq!(
keymap
.bindings_for_action(full_duplicate_to_2.action().id())
.map(|binding| &binding.keystrokes)
.flatten()
.collect::<Vec<_>>(),
vec![
&Keystroke {
ctrl: true,
alt: false,
shift: false,
cmd: false,
function: false,
key: "w".to_string()
},
&Keystroke {
ctrl: true,
alt: false,
shift: false,
cmd: false,
function: false,
key: "w".to_string()
}
],
"{full_duplicate_to_2:?} should have a duplicated keystroke in the keymap"
);
let updated_bindings = keymap.bindings();
let expected_updated_bindings = vec![
&present_1,
&present_2,
&present_3,
&keystroke_duplicate_to_1,
&full_duplicate_to_2,
];
assert_eq!(
updated_bindings.len(),
expected_updated_bindings.len(),
"Unexpected updated keymap bindings {updated_bindings:?}"
);
for (i, expected) in expected_updated_bindings.iter().enumerate() {
let keymap_binding = &updated_bindings[i];
assert_eq!(
keymap_binding.context_predicate, expected.context_predicate,
"Unexpected context predicate for keymap {i} element: {keymap_binding:?}"
);
assert_eq!(
keymap_binding.keystrokes, expected.keystrokes,
"Unexpected keystrokes for keymap {i} element: {keymap_binding:?}"
);
}
keymap.clear();
assert_absent(&keymap, &all_bindings);
assert!(keymap.bindings().is_empty());
}
#[test]
fn keymap_with_ignored() {
let present_1 = Binding::new("ctrl-q", Present1 {}, None);
let present_2 = Binding::new("ctrl-w", Present2 {}, Some("pane"));
let present_3 = Binding::new("ctrl-e", Present3 {}, Some("editor"));
let keystroke_duplicate_to_1 = Binding::new("ctrl-q", Duplicate {}, None);
let full_duplicate_to_2 = Binding::new("ctrl-w", Present2 {}, Some("pane"));
let ignored_1 = Binding::new("ctrl-q", NoAction {}, None);
let ignored_2 = Binding::new("ctrl-w", NoAction {}, Some("pane"));
let ignored_3_with_other_context =
Binding::new("ctrl-e", NoAction {}, Some("other_context"));
let mut keymap = Keymap::default();
keymap.add_bindings([
ignored_1.clone(),
ignored_2.clone(),
ignored_3_with_other_context.clone(),
]);
assert_absent(&keymap, &[&present_3]);
assert_disabled(
&keymap,
&[
&present_1,
&present_2,
&ignored_1,
&ignored_2,
&ignored_3_with_other_context,
],
);
assert!(keymap.bindings().is_empty());
keymap.clear();
keymap.add_bindings([
present_1.clone(),
present_2.clone(),
present_3.clone(),
ignored_1.clone(),
ignored_2.clone(),
ignored_3_with_other_context.clone(),
]);
assert_present(&keymap, &[(&present_3, "e")]);
assert_disabled(
&keymap,
&[
&present_1,
&present_2,
&ignored_1,
&ignored_2,
&ignored_3_with_other_context,
],
);
keymap.clear();
keymap.add_bindings([
present_1.clone(),
present_2.clone(),
present_3.clone(),
ignored_1.clone(),
]);
assert_present(&keymap, &[(&present_2, "w"), (&present_3, "e")]);
assert_disabled(&keymap, &[&present_1, &ignored_1]);
assert_absent(&keymap, &[&ignored_2, &ignored_3_with_other_context]);
keymap.clear();
keymap.add_bindings([
present_1.clone(),
present_2.clone(),
present_3.clone(),
keystroke_duplicate_to_1.clone(),
full_duplicate_to_2.clone(),
ignored_1.clone(),
ignored_2.clone(),
ignored_3_with_other_context.clone(),
]);
assert_present(&keymap, &[(&present_3, "e")]);
assert_disabled(
&keymap,
&[
&present_1,
&present_2,
&keystroke_duplicate_to_1,
&full_duplicate_to_2,
&ignored_1,
&ignored_2,
&ignored_3_with_other_context,
],
);
keymap.clear();
}
#[track_caller]
fn assert_present(keymap: &Keymap, expected_bindings: &[(&Binding, &str)]) {
let keymap_bindings = keymap.bindings();
assert_eq!(
expected_bindings.len(),
keymap_bindings.len(),
"Unexpected keymap bindings {keymap_bindings:?}"
);
for (i, (expected, expected_key)) in expected_bindings.iter().enumerate() {
assert!(
!keymap.binding_disabled(expected),
"{expected:?} should not be disabled as it was added into keymap for element {i}"
);
assert_eq!(
keymap
.bindings_for_action(expected.action().id())
.map(|binding| &binding.keystrokes)
.flatten()
.collect::<Vec<_>>(),
vec![&Keystroke {
ctrl: true,
alt: false,
shift: false,
cmd: false,
function: false,
key: expected_key.to_string()
}],
"{expected:?} should have the expected keystroke with key '{expected_key}' in the keymap for element {i}"
);
let keymap_binding = &keymap_bindings[i];
assert_eq!(
keymap_binding.context_predicate, expected.context_predicate,
"Unexpected context predicate for keymap {i} element: {keymap_binding:?}"
);
assert_eq!(
keymap_binding.keystrokes, expected.keystrokes,
"Unexpected keystrokes for keymap {i} element: {keymap_binding:?}"
);
}
}
#[track_caller]
fn assert_absent(keymap: &Keymap, bindings: &[&Binding]) {
for binding in bindings.iter() {
assert!(
!keymap.binding_disabled(binding),
"{binding:?} should not be disabled in the keymap where was not added"
);
assert_eq!(
keymap.bindings_for_action(binding.action().id()).count(),
0,
"{binding:?} should have no actions in the keymap where was not added"
);
}
}
#[track_caller]
fn assert_disabled(keymap: &Keymap, bindings: &[&Binding]) {
for binding in bindings.iter() {
assert!(
keymap.binding_disabled(binding),
"{binding:?} should be disabled in the keymap"
);
assert_eq!(
keymap.bindings_for_action(binding.action().id()).count(),
0,
"{binding:?} should have no actions in the keymap where it was disabled"
);
}
}
}

View File

@@ -44,7 +44,7 @@ impl KeymapContext {
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum KeymapContextPredicate {
Identifier(String),
Equal(String, String),

View File

@@ -3,7 +3,7 @@ use std::fmt::Write;
use anyhow::anyhow;
use serde::Deserialize;
#[derive(Clone, Debug, Eq, PartialEq, Default, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq, Default, Deserialize, Hash)]
pub struct Keystroke {
pub ctrl: bool,
pub alt: bool,

View File

@@ -4,7 +4,7 @@ use pathfinder_geometry::vector::vec2f;
use crate::{geometry::vector::Vector2F, keymap_matcher::Keystroke};
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct KeyDownEvent {
pub keystroke: Keystroke,
pub is_held: bool,

View File

@@ -231,7 +231,7 @@ impl MacForegroundPlatform {
} => {
// TODO
let keystrokes = keystroke_matcher
.bindings_for_action_type(action.as_any().type_id())
.bindings_for_action(action.id())
.find(|binding| binding.action().eq(action.as_ref()))
.map(|binding| binding.keystrokes());
let selector = match os_action {

View File

@@ -10,8 +10,8 @@ use crate::{
mac::{
platform::NSViewLayerContentsRedrawDuringViewResize, renderer::Renderer, screen::Screen,
},
Event, InputHandler, KeyDownEvent, ModifiersChangedEvent, MouseButton, MouseButtonEvent,
MouseMovedEvent, Scene, WindowBounds, WindowKind,
Event, InputHandler, KeyDownEvent, Modifiers, ModifiersChangedEvent, MouseButton,
MouseButtonEvent, MouseMovedEvent, Scene, WindowBounds, WindowKind,
},
};
use block::ConcreteBlock;
@@ -232,10 +232,6 @@ unsafe fn build_window_class(name: &'static str, superclass: &Class) -> *const C
sel!(canBecomeKeyWindow),
yes as extern "C" fn(&Object, Sel) -> BOOL,
);
decl.add_method(
sel!(sendEvent:),
send_event as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(windowDidResize:),
window_did_resize as extern "C" fn(&Object, Sel, id),
@@ -299,7 +295,7 @@ struct WindowState {
appearance_changed_callback: Option<Box<dyn FnMut()>>,
input_handler: Option<Box<dyn InputHandler>>,
pending_key_down: Option<(KeyDownEvent, Option<InsertText>)>,
performed_key_equivalent: bool,
last_key_equivalent: Option<KeyDownEvent>,
synthetic_drag_counter: usize,
executor: Rc<executor::Foreground>,
scene_to_render: Option<Scene>,
@@ -521,7 +517,7 @@ impl Window {
appearance_changed_callback: None,
input_handler: None,
pending_key_down: None,
performed_key_equivalent: false,
last_key_equivalent: None,
synthetic_drag_counter: 0,
executor,
scene_to_render: Default::default(),
@@ -965,36 +961,34 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
let window_height = window_state_borrow.content_size().y();
let event = unsafe { Event::from_native(native_event, Some(window_height)) };
if let Some(event) = event {
if let Some(Event::KeyDown(event)) = event {
// For certain keystrokes, macOS will first dispatch a "key equivalent" event.
// If that event isn't handled, it will then dispatch a "key down" event. GPUI
// makes no distinction between these two types of events, so we need to ignore
// the "key down" event if we've already just processed its "key equivalent" version.
if key_equivalent {
window_state_borrow.performed_key_equivalent = true;
} else if window_state_borrow.performed_key_equivalent {
window_state_borrow.last_key_equivalent = Some(event.clone());
} else if window_state_borrow.last_key_equivalent.take().as_ref() == Some(&event) {
return NO;
}
let function_is_held;
window_state_borrow.pending_key_down = match event {
Event::KeyDown(event) => {
let keydown = event.keystroke.clone();
// Ignore events from held-down keys after some of the initially-pressed keys
// were released.
if event.is_held {
if window_state_borrow.last_fresh_keydown.as_ref() != Some(&keydown) {
return YES;
}
} else {
window_state_borrow.last_fresh_keydown = Some(keydown);
}
function_is_held = event.keystroke.function;
Some((event, None))
let keydown = event.keystroke.clone();
let fn_modifier = keydown.function;
// Ignore events from held-down keys after some of the initially-pressed keys
// were released.
if event.is_held {
if window_state_borrow.last_fresh_keydown.as_ref() != Some(&keydown) {
return YES;
}
_ => return NO,
};
} else {
window_state_borrow.last_fresh_keydown = Some(keydown);
}
window_state_borrow.pending_key_down = Some((event, None));
drop(window_state_borrow);
if !function_is_held {
// Send the event to the input context for IME handling, unless the `fn` modifier is
// being pressed.
if !fn_modifier {
unsafe {
let input_context: id = msg_send![this, inputContext];
let _: BOOL = msg_send![input_context, handleEvent: native_event];
@@ -1059,7 +1053,44 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
let window_height = window_state_borrow.content_size().y();
let event = unsafe { Event::from_native(native_event, Some(window_height)) };
if let Some(event) = event {
if let Some(mut event) = event {
let synthesized_second_event = match &mut event {
Event::MouseDown(
event @ MouseButtonEvent {
button: MouseButton::Left,
modifiers: Modifiers { ctrl: true, .. },
..
},
) => {
*event = MouseButtonEvent {
button: MouseButton::Right,
modifiers: Modifiers {
ctrl: false,
..event.modifiers
},
click_count: 1,
..*event
};
Some(Event::MouseUp(MouseButtonEvent {
button: MouseButton::Right,
..*event
}))
}
// Because we map a ctrl-left_down to a right_down -> right_up let's ignore
// the ctrl-left_up to avoid having a mismatch in button down/up events if the
// user is still holding ctrl when releasing the left mouse button
Event::MouseUp(MouseButtonEvent {
button: MouseButton::Left,
modifiers: Modifiers { ctrl: true, .. },
..
}) => return,
_ => None,
};
match &event {
Event::MouseMoved(
event @ MouseMovedEvent {
@@ -1111,6 +1142,9 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
if let Some(mut callback) = window_state_borrow.event_callback.take() {
drop(window_state_borrow);
callback(event);
if let Some(event) = synthesized_second_event {
callback(event);
}
window_state.borrow_mut().event_callback = Some(callback);
}
}
@@ -1143,13 +1177,6 @@ extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
}
}
extern "C" fn send_event(this: &Object, _: Sel, native_event: id) {
unsafe {
let _: () = msg_send![super(this, class!(NSWindow)), sendEvent: native_event];
get_window_state(this).borrow_mut().performed_key_equivalent = false;
}
}
extern "C" fn window_did_resize(this: &Object, _: Sel, _: id) {
let window_state = unsafe { get_window_state(this) };
window_state.as_ref().borrow().move_traffic_light();

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