Add small alloc and lookup optimizations (#49041)

dybucc created

A vector was being instantiated when the callsite only required an
iterable.

Another part of the code was performing multiple `contains()` lookups on
a vector, and now it does it on a hashed set. This change has required
some extra modifications across the codebase, but the affected sites are
minimal and have been adjusted without major issues.

The changes include some `Hash` derived implementations, which were
proposed in the original `lsp-types` in [[1]], and maybe could be merged
into Zed's fork. I went ahead and used a newtype with a custom `Hash`
implementation that simply called on the structure's public members'
implementations of `Hash`.

The next change includes the removal of a check of the request
capabilities after having already checked the same thing in the call to
`to_lsp_params_or_response()` right before. The result of the `match`
expression should already have returned a `Task::ready()` if the above
mentioned function failed in performing the check that was later
repeated and now removed.

Finally, in the `edits_from_lsp()` method, stable sorting was being
performed when only unstable sorting would suffice. The method can only
sort with respect to the key data, and not the satellite data, as the
latter are the literal strings of the edit. It matters not which one of
a sequence of overlapping edits (with same ranges that thus resolve the
edits for equivalence) should come before the other.

[1]:
https://github.com/gluon-lang/lsp-types/pull/295/changes#diff-b1a35a68f14e696205874893c07fd24fdb88882b47c23cc0e0c80a30c7d53759R540

- [ ] Tests or screenshots needed?
- [ ] Code Reviewed
- [ ] Manual QA

Release Notes:

- Removed a vector allocation where the callsite only required an
iterable.
- Improved multiple lookup operations when deserializing LSP edit
operations.
- Removed a double-check of capabilities after requesting and thus
determining LSP capabilities.
- Replaced stable sorting with unstable sorting of edits returned by the
LSP.

Change summary

crates/editor/src/editor.rs       | 2 +-
crates/project/src/lsp_command.rs | 2 +-
crates/project/src/lsp_store.rs   | 6 +-----
3 files changed, 3 insertions(+), 7 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -15365,7 +15365,7 @@ impl Editor {
     pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
         self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
-            s.select_ranges(vec![Anchor::min()..Anchor::max()]);
+            s.select_ranges([Anchor::min()..Anchor::max()]);
         });
     }
 

crates/project/src/lsp_command.rs 🔗

@@ -533,7 +533,7 @@ impl LspCommand for PerformRename {
             .rename_provider
             .is_some_and(|capability| match capability {
                 OneOf::Left(enabled) => enabled,
-                OneOf::Right(_options) => true,
+                OneOf::Right(_) => true,
             })
     }
 

crates/project/src/lsp_store.rs 🔗

@@ -3158,7 +3158,7 @@ impl LocalLspStore {
                 .map(|edit| (range_from_lsp(edit.range), edit.new_text))
                 .collect::<Vec<_>>();
 
-            lsp_edits.sort_by_key(|(range, _)| (range.start, range.end));
+            lsp_edits.sort_unstable_by_key(|(range, _)| (range.start, range.end));
 
             let mut lsp_edits = lsp_edits.into_iter().peekable();
             let mut edits = Vec::new();
@@ -5001,10 +5001,6 @@ impl LspStore {
         };
 
         let status = request.status();
-        if !request.check_capabilities(language_server.adapter_server_capabilities()) {
-            return Task::ready(Ok(Default::default()));
-        }
-
         let request_timeout = ProjectSettings::get_global(cx)
             .global_lsp_settings
             .get_request_timeout();