Fix some issues with branch buffers (#18945)

* `Open Excerpts` command always opens the locations in the base buffer
* LSP features like document-highlights, go-to-def, and inlay hints work
correctly in branch buffers
* Other LSP features like completions, code actions, and rename are
disabled in branch buffers

Release Notes:

- N/A
This commit is contained in:
Max Brunsfeld 2024-10-09 16:55:25 -07:00 committed by GitHub
parent cae548a50d
commit 53cc82b132
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 554 additions and 267 deletions

View file

@ -1,4 +1,4 @@
use crate::{Editor, EditorEvent};
use crate::{Editor, EditorEvent, SemanticsProvider};
use collections::HashSet;
use futures::{channel::mpsc, future::join_all};
use gpui::{AppContext, EventEmitter, FocusableView, Model, Render, Subscription, Task, View};
@ -6,7 +6,7 @@ use language::{Buffer, BufferEvent, Capability};
use multi_buffer::{ExcerptRange, MultiBuffer};
use project::Project;
use smol::stream::StreamExt;
use std::{any::TypeId, ops::Range, time::Duration};
use std::{any::TypeId, ops::Range, rc::Rc, time::Duration};
use text::ToOffset;
use ui::prelude::*;
use workspace::{
@ -35,6 +35,12 @@ struct RecalculateDiff {
debounce: bool,
}
/// A provider of code semantics for branch buffers.
///
/// Requests in edited regions will return nothing, but requests in unchanged
/// regions will be translated into the base buffer's coordinates.
struct BranchBufferSemanticsProvider(Rc<dyn SemanticsProvider>);
impl ProposedChangesEditor {
pub fn new<T: ToOffset>(
buffers: Vec<ProposedChangesBuffer<T>>,
@ -66,6 +72,13 @@ impl ProposedChangesEditor {
editor: cx.new_view(|cx| {
let mut editor = Editor::for_multibuffer(multibuffer.clone(), project, true, cx);
editor.set_expand_all_diff_hunks();
editor.set_completion_provider(None);
editor.clear_code_action_providers();
editor.set_semantics_provider(
editor
.semantics_provider()
.map(|provider| Rc::new(BranchBufferSemanticsProvider(provider)) as _),
);
editor
}),
recalculate_diffs_tx,
@ -76,7 +89,7 @@ impl ProposedChangesEditor {
while recalculate_diff.debounce {
cx.background_executor()
.timer(Duration::from_millis(250))
.timer(Duration::from_millis(50))
.await;
let mut had_further_changes = false;
while let Ok(next_recalculate_diff) = recalculate_diffs_rx.try_next() {
@ -245,3 +258,103 @@ impl ToolbarItemView for ProposedChangesEditorToolbar {
self.get_toolbar_item_location()
}
}
impl BranchBufferSemanticsProvider {
fn to_base(
&self,
buffer: &Model<Buffer>,
positions: &[text::Anchor],
cx: &AppContext,
) -> Option<Model<Buffer>> {
let base_buffer = buffer.read(cx).diff_base_buffer()?;
let version = base_buffer.read(cx).version();
if positions
.iter()
.any(|position| !version.observed(position.timestamp))
{
return None;
}
Some(base_buffer)
}
}
impl SemanticsProvider for BranchBufferSemanticsProvider {
fn hover(
&self,
buffer: &Model<Buffer>,
position: text::Anchor,
cx: &mut AppContext,
) -> Option<Task<Vec<project::Hover>>> {
let buffer = self.to_base(buffer, &[position], cx)?;
self.0.hover(&buffer, position, cx)
}
fn inlay_hints(
&self,
buffer: Model<Buffer>,
range: Range<text::Anchor>,
cx: &mut AppContext,
) -> Option<Task<anyhow::Result<Vec<project::InlayHint>>>> {
let buffer = self.to_base(&buffer, &[range.start, range.end], cx)?;
self.0.inlay_hints(buffer, range, cx)
}
fn resolve_inlay_hint(
&self,
hint: project::InlayHint,
buffer: Model<Buffer>,
server_id: lsp::LanguageServerId,
cx: &mut AppContext,
) -> Option<Task<anyhow::Result<project::InlayHint>>> {
let buffer = self.to_base(&buffer, &[], cx)?;
self.0.resolve_inlay_hint(hint, buffer, server_id, cx)
}
fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &AppContext) -> bool {
if let Some(buffer) = self.to_base(&buffer, &[], cx) {
self.0.supports_inlay_hints(&buffer, cx)
} else {
false
}
}
fn document_highlights(
&self,
buffer: &Model<Buffer>,
position: text::Anchor,
cx: &mut AppContext,
) -> Option<Task<gpui::Result<Vec<project::DocumentHighlight>>>> {
let buffer = self.to_base(&buffer, &[position], cx)?;
self.0.document_highlights(&buffer, position, cx)
}
fn definitions(
&self,
buffer: &Model<Buffer>,
position: text::Anchor,
kind: crate::GotoDefinitionKind,
cx: &mut AppContext,
) -> Option<Task<gpui::Result<Vec<project::LocationLink>>>> {
let buffer = self.to_base(&buffer, &[position], cx)?;
self.0.definitions(&buffer, position, kind, cx)
}
fn range_for_rename(
&self,
_: &Model<Buffer>,
_: text::Anchor,
_: &mut AppContext,
) -> Option<Task<gpui::Result<Option<Range<text::Anchor>>>>> {
None
}
fn perform_rename(
&self,
_: &Model<Buffer>,
_: text::Anchor,
_: String,
_: &mut AppContext,
) -> Option<Task<gpui::Result<project::ProjectTransaction>>> {
None
}
}