Support diagnostic navigation in multibuffers (#22620)
cc @nathansobo Release Notes: - Support diagnostic navigation in multibuffers
This commit is contained in:
parent
39af06085a
commit
11ec25aedb
13 changed files with 209 additions and 150 deletions
|
@ -797,10 +797,11 @@ impl InlineAssistant {
|
||||||
if let Some(model) = LanguageModelRegistry::read_global(cx).active_model() {
|
if let Some(model) = LanguageModelRegistry::read_global(cx).active_model() {
|
||||||
let language_name = assist.editor.upgrade().and_then(|editor| {
|
let language_name = assist.editor.upgrade().and_then(|editor| {
|
||||||
let multibuffer = editor.read(cx).buffer().read(cx);
|
let multibuffer = editor.read(cx).buffer().read(cx);
|
||||||
let ranges = multibuffer.range_to_buffer_ranges(assist.range.clone(), cx);
|
let multibuffer_snapshot = multibuffer.snapshot(cx);
|
||||||
|
let ranges = multibuffer_snapshot.range_to_buffer_ranges(assist.range.clone());
|
||||||
ranges
|
ranges
|
||||||
.first()
|
.first()
|
||||||
.and_then(|(buffer, _, _)| buffer.read(cx).language())
|
.and_then(|(excerpt, _)| excerpt.buffer().language())
|
||||||
.map(|language| language.name())
|
.map(|language| language.name())
|
||||||
});
|
});
|
||||||
report_assistant_event(
|
report_assistant_event(
|
||||||
|
@ -2615,26 +2616,29 @@ impl EventEmitter<CodegenEvent> for CodegenAlternative {}
|
||||||
|
|
||||||
impl CodegenAlternative {
|
impl CodegenAlternative {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
buffer: Model<MultiBuffer>,
|
multi_buffer: Model<MultiBuffer>,
|
||||||
range: Range<Anchor>,
|
range: Range<Anchor>,
|
||||||
active: bool,
|
active: bool,
|
||||||
telemetry: Option<Arc<Telemetry>>,
|
telemetry: Option<Arc<Telemetry>>,
|
||||||
builder: Arc<PromptBuilder>,
|
builder: Arc<PromptBuilder>,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let snapshot = buffer.read(cx).snapshot(cx);
|
let snapshot = multi_buffer.read(cx).snapshot(cx);
|
||||||
|
|
||||||
let (old_buffer, _, _) = buffer
|
let (old_excerpt, _) = snapshot
|
||||||
.read(cx)
|
.range_to_buffer_ranges(range.clone())
|
||||||
.range_to_buffer_ranges(range.clone(), cx)
|
|
||||||
.pop()
|
.pop()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let old_buffer = cx.new_model(|cx| {
|
let old_buffer = cx.new_model(|cx| {
|
||||||
let old_buffer = old_buffer.read(cx);
|
let text = old_excerpt.buffer().as_rope().clone();
|
||||||
let text = old_buffer.as_rope().clone();
|
let line_ending = old_excerpt.buffer().line_ending();
|
||||||
let line_ending = old_buffer.line_ending();
|
let language = old_excerpt.buffer().language().cloned();
|
||||||
let language = old_buffer.language().cloned();
|
let language_registry = multi_buffer
|
||||||
let language_registry = old_buffer.language_registry();
|
.read(cx)
|
||||||
|
.buffer(old_excerpt.buffer_id())
|
||||||
|
.unwrap()
|
||||||
|
.read(cx)
|
||||||
|
.language_registry();
|
||||||
|
|
||||||
let mut buffer = Buffer::local_normalized(text, line_ending, cx);
|
let mut buffer = Buffer::local_normalized(text, line_ending, cx);
|
||||||
buffer.set_language(language, cx);
|
buffer.set_language(language, cx);
|
||||||
|
@ -2645,7 +2649,7 @@ impl CodegenAlternative {
|
||||||
});
|
});
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
buffer: buffer.clone(),
|
buffer: multi_buffer.clone(),
|
||||||
old_buffer,
|
old_buffer,
|
||||||
edit_position: None,
|
edit_position: None,
|
||||||
message_id: None,
|
message_id: None,
|
||||||
|
@ -2656,7 +2660,7 @@ impl CodegenAlternative {
|
||||||
generation: Task::ready(()),
|
generation: Task::ready(()),
|
||||||
diff: Diff::default(),
|
diff: Diff::default(),
|
||||||
telemetry,
|
telemetry,
|
||||||
_subscription: cx.subscribe(&buffer, Self::handle_buffer_event),
|
_subscription: cx.subscribe(&multi_buffer, Self::handle_buffer_event),
|
||||||
builder,
|
builder,
|
||||||
active,
|
active,
|
||||||
edits: Vec::new(),
|
edits: Vec::new(),
|
||||||
|
@ -2867,10 +2871,11 @@ impl CodegenAlternative {
|
||||||
let telemetry = self.telemetry.clone();
|
let telemetry = self.telemetry.clone();
|
||||||
let language_name = {
|
let language_name = {
|
||||||
let multibuffer = self.buffer.read(cx);
|
let multibuffer = self.buffer.read(cx);
|
||||||
let ranges = multibuffer.range_to_buffer_ranges(self.range.clone(), cx);
|
let snapshot = multibuffer.snapshot(cx);
|
||||||
|
let ranges = snapshot.range_to_buffer_ranges(self.range.clone());
|
||||||
ranges
|
ranges
|
||||||
.first()
|
.first()
|
||||||
.and_then(|(buffer, _, _)| buffer.read(cx).language())
|
.and_then(|(excerpt, _)| excerpt.buffer().language())
|
||||||
.map(|language| language.name())
|
.map(|language| language.name())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -257,17 +257,20 @@ impl CodegenAlternative {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let snapshot = buffer.read(cx).snapshot(cx);
|
let snapshot = buffer.read(cx).snapshot(cx);
|
||||||
|
|
||||||
let (old_buffer, _, _) = buffer
|
let (old_excerpt, _) = snapshot
|
||||||
.read(cx)
|
.range_to_buffer_ranges(range.clone())
|
||||||
.range_to_buffer_ranges(range.clone(), cx)
|
|
||||||
.pop()
|
.pop()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let old_buffer = cx.new_model(|cx| {
|
let old_buffer = cx.new_model(|cx| {
|
||||||
let old_buffer = old_buffer.read(cx);
|
let text = old_excerpt.buffer().as_rope().clone();
|
||||||
let text = old_buffer.as_rope().clone();
|
let line_ending = old_excerpt.buffer().line_ending();
|
||||||
let line_ending = old_buffer.line_ending();
|
let language = old_excerpt.buffer().language().cloned();
|
||||||
let language = old_buffer.language().cloned();
|
let language_registry = buffer
|
||||||
let language_registry = old_buffer.language_registry();
|
.read(cx)
|
||||||
|
.buffer(old_excerpt.buffer_id())
|
||||||
|
.unwrap()
|
||||||
|
.read(cx)
|
||||||
|
.language_registry();
|
||||||
|
|
||||||
let mut buffer = Buffer::local_normalized(text, line_ending, cx);
|
let mut buffer = Buffer::local_normalized(text, line_ending, cx);
|
||||||
buffer.set_language(language, cx);
|
buffer.set_language(language, cx);
|
||||||
|
@ -471,10 +474,11 @@ impl CodegenAlternative {
|
||||||
let telemetry = self.telemetry.clone();
|
let telemetry = self.telemetry.clone();
|
||||||
let language_name = {
|
let language_name = {
|
||||||
let multibuffer = self.buffer.read(cx);
|
let multibuffer = self.buffer.read(cx);
|
||||||
let ranges = multibuffer.range_to_buffer_ranges(self.range.clone(), cx);
|
let snapshot = multibuffer.snapshot(cx);
|
||||||
|
let ranges = snapshot.range_to_buffer_ranges(self.range.clone());
|
||||||
ranges
|
ranges
|
||||||
.first()
|
.first()
|
||||||
.and_then(|(buffer, _, _)| buffer.read(cx).language())
|
.and_then(|(excerpt, _)| excerpt.buffer().language())
|
||||||
.map(|language| language.name())
|
.map(|language| language.name())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -871,10 +871,11 @@ impl InlineAssistant {
|
||||||
if let Some(model) = LanguageModelRegistry::read_global(cx).active_model() {
|
if let Some(model) = LanguageModelRegistry::read_global(cx).active_model() {
|
||||||
let language_name = assist.editor.upgrade().and_then(|editor| {
|
let language_name = assist.editor.upgrade().and_then(|editor| {
|
||||||
let multibuffer = editor.read(cx).buffer().read(cx);
|
let multibuffer = editor.read(cx).buffer().read(cx);
|
||||||
let ranges = multibuffer.range_to_buffer_ranges(assist.range.clone(), cx);
|
let snapshot = multibuffer.snapshot(cx);
|
||||||
|
let ranges = snapshot.range_to_buffer_ranges(assist.range.clone());
|
||||||
ranges
|
ranges
|
||||||
.first()
|
.first()
|
||||||
.and_then(|(buffer, _, _)| buffer.read(cx).language())
|
.and_then(|(excerpt, _)| excerpt.buffer().language())
|
||||||
.map(|language| language.name())
|
.map(|language| language.name())
|
||||||
});
|
});
|
||||||
report_assistant_event(
|
report_assistant_event(
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use editor::Editor;
|
use editor::{AnchorRangeExt, Editor};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
EventEmitter, IntoElement, ParentElement, Render, Styled, Subscription, Task, View,
|
EventEmitter, IntoElement, ParentElement, Render, Styled, Subscription, Task, View,
|
||||||
ViewContext, WeakView,
|
ViewContext, WeakView,
|
||||||
};
|
};
|
||||||
use language::Diagnostic;
|
use language::{Diagnostic, DiagnosticEntry};
|
||||||
use ui::{h_flex, prelude::*, Button, ButtonLike, Color, Icon, IconName, Label, Tooltip};
|
use ui::{h_flex, prelude::*, Button, ButtonLike, Color, Icon, IconName, Label, Tooltip};
|
||||||
use workspace::{item::ItemHandle, StatusItemView, ToolbarItemEvent, Workspace};
|
use workspace::{item::ItemHandle, StatusItemView, ToolbarItemEvent, Workspace};
|
||||||
|
|
||||||
|
@ -148,7 +148,11 @@ impl DiagnosticIndicator {
|
||||||
(buffer, cursor_position)
|
(buffer, cursor_position)
|
||||||
});
|
});
|
||||||
let new_diagnostic = buffer
|
let new_diagnostic = buffer
|
||||||
.diagnostics_in_range::<_, usize>(cursor_position..cursor_position, false)
|
.diagnostics_in_range(cursor_position..cursor_position, false)
|
||||||
|
.map(|DiagnosticEntry { diagnostic, range }| DiagnosticEntry {
|
||||||
|
diagnostic,
|
||||||
|
range: range.to_offset(&buffer),
|
||||||
|
})
|
||||||
.filter(|entry| !entry.range.is_empty())
|
.filter(|entry| !entry.range.is_empty())
|
||||||
.min_by_key(|entry| (entry.diagnostic.severity, entry.range.len()))
|
.min_by_key(|entry| (entry.diagnostic.severity, entry.range.len()))
|
||||||
.map(|entry| entry.diagnostic);
|
.map(|entry| entry.diagnostic);
|
||||||
|
|
|
@ -99,8 +99,8 @@ use itertools::Itertools;
|
||||||
use language::{
|
use language::{
|
||||||
language_settings::{self, all_language_settings, language_settings, InlayHintSettings},
|
language_settings::{self, all_language_settings, language_settings, InlayHintSettings},
|
||||||
markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CharKind, CodeLabel,
|
markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CharKind, CodeLabel,
|
||||||
CursorShape, Diagnostic, Documentation, IndentKind, IndentSize, Language, OffsetRangeExt,
|
CursorShape, Diagnostic, DiagnosticEntry, Documentation, IndentKind, IndentSize, Language,
|
||||||
Point, Selection, SelectionGoal, TransactionId,
|
OffsetRangeExt, Point, Selection, SelectionGoal, TransactionId,
|
||||||
};
|
};
|
||||||
use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange};
|
use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange};
|
||||||
use linked_editing_ranges::refresh_linked_ranges;
|
use linked_editing_ranges::refresh_linked_ranges;
|
||||||
|
@ -3549,13 +3549,12 @@ impl Editor {
|
||||||
Bias::Left,
|
Bias::Left,
|
||||||
);
|
);
|
||||||
let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
|
let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
|
||||||
multi_buffer
|
multi_buffer_snapshot
|
||||||
.range_to_buffer_ranges(multi_buffer_visible_range, cx)
|
.range_to_buffer_ranges(multi_buffer_visible_range)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
|
.filter(|(_, excerpt_visible_range)| !excerpt_visible_range.is_empty())
|
||||||
.filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
|
.filter_map(|(excerpt, excerpt_visible_range)| {
|
||||||
let buffer = buffer_handle.read(cx);
|
let buffer_file = project::File::from_dyn(excerpt.buffer().file())?;
|
||||||
let buffer_file = project::File::from_dyn(buffer.file())?;
|
|
||||||
let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
|
let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
|
||||||
let worktree_entry = buffer_worktree
|
let worktree_entry = buffer_worktree
|
||||||
.read(cx)
|
.read(cx)
|
||||||
|
@ -3564,17 +3563,17 @@ impl Editor {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let language = buffer.language()?;
|
let language = excerpt.buffer().language()?;
|
||||||
if let Some(restrict_to_languages) = restrict_to_languages {
|
if let Some(restrict_to_languages) = restrict_to_languages {
|
||||||
if !restrict_to_languages.contains(language) {
|
if !restrict_to_languages.contains(language) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some((
|
Some((
|
||||||
excerpt_id,
|
excerpt.id(),
|
||||||
(
|
(
|
||||||
buffer_handle,
|
multi_buffer.buffer(excerpt.buffer_id()).unwrap(),
|
||||||
buffer.version().clone(),
|
excerpt.buffer().version().clone(),
|
||||||
excerpt_visible_range,
|
excerpt_visible_range,
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
|
@ -9179,10 +9178,23 @@ impl Editor {
|
||||||
let snapshot = self.snapshot(cx);
|
let snapshot = self.snapshot(cx);
|
||||||
loop {
|
loop {
|
||||||
let diagnostics = if direction == Direction::Prev {
|
let diagnostics = if direction == Direction::Prev {
|
||||||
buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
|
buffer
|
||||||
|
.diagnostics_in_range(0..search_start, true)
|
||||||
|
.map(|DiagnosticEntry { diagnostic, range }| DiagnosticEntry {
|
||||||
|
diagnostic,
|
||||||
|
range: range.to_offset(&buffer),
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
} else {
|
} else {
|
||||||
buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
|
buffer
|
||||||
|
.diagnostics_in_range(search_start..buffer.len(), false)
|
||||||
|
.map(|DiagnosticEntry { diagnostic, range }| DiagnosticEntry {
|
||||||
|
diagnostic,
|
||||||
|
range: range.to_offset(&buffer),
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
|
.into_iter()
|
||||||
.filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start));
|
.filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start));
|
||||||
let group = diagnostics
|
let group = diagnostics
|
||||||
// relies on diagnostics_in_range to return diagnostics with the same starting range to
|
// relies on diagnostics_in_range to return diagnostics with the same starting range to
|
||||||
|
@ -10289,11 +10301,12 @@ impl Editor {
|
||||||
let buffer = self.buffer.read(cx).snapshot(cx);
|
let buffer = self.buffer.read(cx).snapshot(cx);
|
||||||
let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
|
let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
|
||||||
let is_valid = buffer
|
let is_valid = buffer
|
||||||
.diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
|
.diagnostics_in_range(active_diagnostics.primary_range.clone(), false)
|
||||||
.any(|entry| {
|
.any(|entry| {
|
||||||
|
let range = entry.range.to_offset(&buffer);
|
||||||
entry.diagnostic.is_primary
|
entry.diagnostic.is_primary
|
||||||
&& !entry.range.is_empty()
|
&& !range.is_empty()
|
||||||
&& entry.range.start == primary_range_start
|
&& range.start == primary_range_start
|
||||||
&& entry.diagnostic.message == active_diagnostics.primary_message
|
&& entry.diagnostic.message == active_diagnostics.primary_message
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -11493,21 +11506,23 @@ impl Editor {
|
||||||
let (buffer, selection) = if let Some(buffer) = self.buffer().read(cx).as_singleton() {
|
let (buffer, selection) = if let Some(buffer) = self.buffer().read(cx).as_singleton() {
|
||||||
(buffer, selection_range.start.row..selection_range.end.row)
|
(buffer, selection_range.start.row..selection_range.end.row)
|
||||||
} else {
|
} else {
|
||||||
let buffer_ranges = self
|
let multi_buffer = self.buffer().read(cx);
|
||||||
.buffer()
|
let multi_buffer_snapshot = multi_buffer.snapshot(cx);
|
||||||
.read(cx)
|
let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
|
||||||
.range_to_buffer_ranges(selection_range, cx);
|
|
||||||
|
|
||||||
let (buffer, range, _) = if selection.reversed {
|
let (excerpt, range) = if selection.reversed {
|
||||||
buffer_ranges.first()
|
buffer_ranges.first()
|
||||||
} else {
|
} else {
|
||||||
buffer_ranges.last()
|
buffer_ranges.last()
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
let snapshot = buffer.read(cx).snapshot();
|
let snapshot = excerpt.buffer();
|
||||||
let selection = text::ToPoint::to_point(&range.start, &snapshot).row
|
let selection = text::ToPoint::to_point(&range.start, &snapshot).row
|
||||||
..text::ToPoint::to_point(&range.end, &snapshot).row;
|
..text::ToPoint::to_point(&range.end, &snapshot).row;
|
||||||
(buffer.clone(), selection)
|
(
|
||||||
|
multi_buffer.buffer(excerpt.buffer_id()).unwrap().clone(),
|
||||||
|
selection,
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
Some((buffer, selection))
|
Some((buffer, selection))
|
||||||
|
@ -12399,17 +12414,18 @@ impl Editor {
|
||||||
};
|
};
|
||||||
|
|
||||||
let selections = self.selections.all::<usize>(cx);
|
let selections = self.selections.all::<usize>(cx);
|
||||||
let buffer = self.buffer.read(cx);
|
let multi_buffer = self.buffer.read(cx);
|
||||||
|
let multi_buffer_snapshot = multi_buffer.snapshot(cx);
|
||||||
let mut new_selections_by_buffer = HashMap::default();
|
let mut new_selections_by_buffer = HashMap::default();
|
||||||
for selection in selections {
|
for selection in selections {
|
||||||
for (buffer, range, _) in
|
for (excerpt, range) in
|
||||||
buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
|
multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
|
||||||
{
|
{
|
||||||
let mut range = range.to_point(buffer.read(cx));
|
let mut range = range.to_point(excerpt.buffer());
|
||||||
range.start.column = 0;
|
range.start.column = 0;
|
||||||
range.end.column = buffer.read(cx).line_len(range.end.row);
|
range.end.column = excerpt.buffer().line_len(range.end.row);
|
||||||
new_selections_by_buffer
|
new_selections_by_buffer
|
||||||
.entry(buffer)
|
.entry(multi_buffer.buffer(excerpt.buffer_id()).unwrap())
|
||||||
.or_insert(Vec::new())
|
.or_insert(Vec::new())
|
||||||
.push(range)
|
.push(range)
|
||||||
}
|
}
|
||||||
|
@ -12508,13 +12524,15 @@ impl Editor {
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let selections = self.selections.all::<usize>(cx);
|
let selections = self.selections.all::<usize>(cx);
|
||||||
let buffer = self.buffer.read(cx);
|
let multi_buffer = self.buffer.read(cx);
|
||||||
for selection in selections {
|
for selection in selections {
|
||||||
for (mut buffer_handle, mut range, _) in
|
for (excerpt, mut range) in multi_buffer
|
||||||
buffer.range_to_buffer_ranges(selection.range(), cx)
|
.snapshot(cx)
|
||||||
|
.range_to_buffer_ranges(selection.range())
|
||||||
{
|
{
|
||||||
// When editing branch buffers, jump to the corresponding location
|
// When editing branch buffers, jump to the corresponding location
|
||||||
// in their base buffer.
|
// in their base buffer.
|
||||||
|
let mut buffer_handle = multi_buffer.buffer(excerpt.buffer_id()).unwrap();
|
||||||
let buffer = buffer_handle.read(cx);
|
let buffer = buffer_handle.read(cx);
|
||||||
if let Some(base_buffer) = buffer.base_buffer() {
|
if let Some(base_buffer) = buffer.base_buffer() {
|
||||||
range = buffer.range_to_version(range, &base_buffer.read(cx).version());
|
range = buffer.range_to_version(range, &base_buffer.read(cx).version());
|
||||||
|
|
|
@ -45,7 +45,7 @@ use language::{
|
||||||
IndentGuideBackgroundColoring, IndentGuideColoring, IndentGuideSettings,
|
IndentGuideBackgroundColoring, IndentGuideColoring, IndentGuideSettings,
|
||||||
ShowWhitespaceSetting,
|
ShowWhitespaceSetting,
|
||||||
},
|
},
|
||||||
ChunkRendererContext,
|
ChunkRendererContext, DiagnosticEntry,
|
||||||
};
|
};
|
||||||
use lsp::DiagnosticSeverity;
|
use lsp::DiagnosticSeverity;
|
||||||
use multi_buffer::{
|
use multi_buffer::{
|
||||||
|
@ -4741,10 +4741,11 @@ impl EditorElement {
|
||||||
if scrollbar_settings.diagnostics != ScrollbarDiagnostics::None {
|
if scrollbar_settings.diagnostics != ScrollbarDiagnostics::None {
|
||||||
let diagnostics = snapshot
|
let diagnostics = snapshot
|
||||||
.buffer_snapshot
|
.buffer_snapshot
|
||||||
.diagnostics_in_range::<_, Point>(
|
.diagnostics_in_range(Point::zero()..max_point, false)
|
||||||
Point::zero()..max_point,
|
.map(|DiagnosticEntry { diagnostic, range }| DiagnosticEntry {
|
||||||
false,
|
diagnostic,
|
||||||
)
|
range: range.to_point(&snapshot.buffer_snapshot),
|
||||||
|
})
|
||||||
// Don't show diagnostics the user doesn't care about
|
// Don't show diagnostics the user doesn't care about
|
||||||
.filter(|diagnostic| {
|
.filter(|diagnostic| {
|
||||||
match (
|
match (
|
||||||
|
|
|
@ -266,12 +266,11 @@ fn show_hover(
|
||||||
// If there's a diagnostic, assign it on the hover state and notify
|
// If there's a diagnostic, assign it on the hover state and notify
|
||||||
let mut local_diagnostic = snapshot
|
let mut local_diagnostic = snapshot
|
||||||
.buffer_snapshot
|
.buffer_snapshot
|
||||||
.diagnostics_in_range::<_, usize>(anchor..anchor, false)
|
.diagnostics_in_range(anchor..anchor, false)
|
||||||
// Find the entry with the most specific range
|
// Find the entry with the most specific range
|
||||||
.min_by_key(|entry| entry.range.end - entry.range.start)
|
.min_by_key(|entry| {
|
||||||
.map(|entry| DiagnosticEntry {
|
let range = entry.range.to_offset(&snapshot.buffer_snapshot);
|
||||||
diagnostic: entry.diagnostic,
|
range.end - range.start
|
||||||
range: entry.range.to_anchors(&snapshot.buffer_snapshot),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Pull the primary diagnostic out so we can jump to it if the popover is clicked
|
// Pull the primary diagnostic out so we can jump to it if the popover is clicked
|
||||||
|
|
|
@ -456,14 +456,17 @@ impl Editor {
|
||||||
range: Range<Anchor>,
|
range: Range<Anchor>,
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
let (buffer, range, _) = self
|
let multi_buffer = self.buffer.read(cx);
|
||||||
.buffer
|
let multi_buffer_snapshot = multi_buffer.snapshot(cx);
|
||||||
.read(cx)
|
let (excerpt, range) = multi_buffer_snapshot
|
||||||
.range_to_buffer_ranges(range, cx)
|
.range_to_buffer_ranges(range)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.next()?;
|
.next()?;
|
||||||
|
|
||||||
buffer.update(cx, |branch_buffer, cx| {
|
multi_buffer
|
||||||
|
.buffer(excerpt.buffer_id())
|
||||||
|
.unwrap()
|
||||||
|
.update(cx, |branch_buffer, cx| {
|
||||||
branch_buffer.merge_into_base(vec![range], cx);
|
branch_buffer.merge_into_base(vec![range], cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -3943,14 +3943,14 @@ impl BufferSnapshot {
|
||||||
) -> impl 'a + Iterator<Item = DiagnosticEntry<O>>
|
) -> impl 'a + Iterator<Item = DiagnosticEntry<O>>
|
||||||
where
|
where
|
||||||
T: 'a + Clone + ToOffset,
|
T: 'a + Clone + ToOffset,
|
||||||
O: 'a + FromAnchor + Ord,
|
O: 'a + FromAnchor,
|
||||||
{
|
{
|
||||||
let mut iterators: Vec<_> = self
|
let mut iterators: Vec<_> = self
|
||||||
.diagnostics
|
.diagnostics
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(_, collection)| {
|
.map(|(_, collection)| {
|
||||||
collection
|
collection
|
||||||
.range::<T, O>(search_range.clone(), self, true, reversed)
|
.range::<T, text::Anchor>(search_range.clone(), self, true, reversed)
|
||||||
.peekable()
|
.peekable()
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -3964,7 +3964,7 @@ impl BufferSnapshot {
|
||||||
let cmp = a
|
let cmp = a
|
||||||
.range
|
.range
|
||||||
.start
|
.start
|
||||||
.cmp(&b.range.start)
|
.cmp(&b.range.start, self)
|
||||||
// when range is equal, sort by diagnostic severity
|
// when range is equal, sort by diagnostic severity
|
||||||
.then(a.diagnostic.severity.cmp(&b.diagnostic.severity))
|
.then(a.diagnostic.severity.cmp(&b.diagnostic.severity))
|
||||||
// and stabilize order with group_id
|
// and stabilize order with group_id
|
||||||
|
@ -3975,7 +3975,13 @@ impl BufferSnapshot {
|
||||||
cmp
|
cmp
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
iterators[next_ix].next()
|
iterators[next_ix]
|
||||||
|
.next()
|
||||||
|
.map(|DiagnosticEntry { range, diagnostic }| DiagnosticEntry {
|
||||||
|
diagnostic,
|
||||||
|
range: FromAnchor::from_anchor(&range.start, self)
|
||||||
|
..FromAnchor::from_anchor(&range.end, self),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -128,13 +128,18 @@ impl SyntaxTreeView {
|
||||||
fn editor_updated(&mut self, did_reparse: bool, cx: &mut ViewContext<Self>) -> Option<()> {
|
fn editor_updated(&mut self, did_reparse: bool, cx: &mut ViewContext<Self>) -> Option<()> {
|
||||||
// Find which excerpt the cursor is in, and the position within that excerpted buffer.
|
// Find which excerpt the cursor is in, and the position within that excerpted buffer.
|
||||||
let editor_state = self.editor.as_mut()?;
|
let editor_state = self.editor.as_mut()?;
|
||||||
let (buffer, range, excerpt_id) = editor_state.editor.update(cx, |editor, cx| {
|
let snapshot = editor_state
|
||||||
|
.editor
|
||||||
|
.update(cx, |editor, cx| editor.snapshot(cx));
|
||||||
|
let (excerpt, buffer, range) = editor_state.editor.update(cx, |editor, cx| {
|
||||||
let selection_range = editor.selections.last::<usize>(cx).range();
|
let selection_range = editor.selections.last::<usize>(cx).range();
|
||||||
editor
|
let multi_buffer = editor.buffer().read(cx);
|
||||||
.buffer()
|
let (excerpt, range) = snapshot
|
||||||
.read(cx)
|
.buffer_snapshot
|
||||||
.range_to_buffer_ranges(selection_range, cx)
|
.range_to_buffer_ranges(selection_range)
|
||||||
.pop()
|
.pop()?;
|
||||||
|
let buffer = multi_buffer.buffer(excerpt.buffer_id()).unwrap().clone();
|
||||||
|
Some((excerpt, buffer, range))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// If the cursor has moved into a different excerpt, retrieve a new syntax layer
|
// If the cursor has moved into a different excerpt, retrieve a new syntax layer
|
||||||
|
@ -143,16 +148,16 @@ impl SyntaxTreeView {
|
||||||
.active_buffer
|
.active_buffer
|
||||||
.get_or_insert_with(|| BufferState {
|
.get_or_insert_with(|| BufferState {
|
||||||
buffer: buffer.clone(),
|
buffer: buffer.clone(),
|
||||||
excerpt_id,
|
excerpt_id: excerpt.id(),
|
||||||
active_layer: None,
|
active_layer: None,
|
||||||
});
|
});
|
||||||
let mut prev_layer = None;
|
let mut prev_layer = None;
|
||||||
if did_reparse {
|
if did_reparse {
|
||||||
prev_layer = buffer_state.active_layer.take();
|
prev_layer = buffer_state.active_layer.take();
|
||||||
}
|
}
|
||||||
if buffer_state.buffer != buffer || buffer_state.excerpt_id != excerpt_id {
|
if buffer_state.buffer != buffer || buffer_state.excerpt_id != excerpt.id() {
|
||||||
buffer_state.buffer = buffer.clone();
|
buffer_state.buffer = buffer.clone();
|
||||||
buffer_state.excerpt_id = excerpt_id;
|
buffer_state.excerpt_id = excerpt.id();
|
||||||
buffer_state.active_layer = None;
|
buffer_state.active_layer = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1667,42 +1667,6 @@ impl MultiBuffer {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn range_to_buffer_ranges<T: ToOffset>(
|
|
||||||
&self,
|
|
||||||
range: Range<T>,
|
|
||||||
cx: &AppContext,
|
|
||||||
) -> Vec<(Model<Buffer>, Range<usize>, ExcerptId)> {
|
|
||||||
let snapshot = self.read(cx);
|
|
||||||
let start = range.start.to_offset(&snapshot);
|
|
||||||
let end = range.end.to_offset(&snapshot);
|
|
||||||
|
|
||||||
let mut result = Vec::new();
|
|
||||||
let mut cursor = snapshot.excerpts.cursor::<usize>(&());
|
|
||||||
cursor.seek(&start, Bias::Right, &());
|
|
||||||
if cursor.item().is_none() {
|
|
||||||
cursor.prev(&());
|
|
||||||
}
|
|
||||||
|
|
||||||
while let Some(excerpt) = cursor.item() {
|
|
||||||
if *cursor.start() > end {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut end_before_newline = cursor.end(&());
|
|
||||||
if excerpt.has_trailing_newline {
|
|
||||||
end_before_newline -= 1;
|
|
||||||
}
|
|
||||||
let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
|
|
||||||
let start = excerpt_start + (cmp::max(start, *cursor.start()) - *cursor.start());
|
|
||||||
let end = excerpt_start + (cmp::min(end, end_before_newline) - *cursor.start());
|
|
||||||
let buffer = self.buffers.borrow()[&excerpt.buffer_id].buffer.clone();
|
|
||||||
result.push((buffer, start..end, excerpt.id));
|
|
||||||
cursor.next(&());
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_excerpts(
|
pub fn remove_excerpts(
|
||||||
&mut self,
|
&mut self,
|
||||||
excerpt_ids: impl IntoIterator<Item = ExcerptId>,
|
excerpt_ids: impl IntoIterator<Item = ExcerptId>,
|
||||||
|
@ -3914,26 +3878,30 @@ impl MultiBufferSnapshot {
|
||||||
where
|
where
|
||||||
O: text::FromAnchor + 'a,
|
O: text::FromAnchor + 'a,
|
||||||
{
|
{
|
||||||
self.as_singleton()
|
self.all_excerpts()
|
||||||
.into_iter()
|
.flat_map(move |excerpt| excerpt.buffer().diagnostic_group(group_id))
|
||||||
.flat_map(move |(_, _, buffer)| buffer.diagnostic_group(group_id))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn diagnostics_in_range<'a, T, O>(
|
pub fn diagnostics_in_range<'a, T>(
|
||||||
&'a self,
|
&'a self,
|
||||||
range: Range<T>,
|
range: Range<T>,
|
||||||
reversed: bool,
|
reversed: bool,
|
||||||
) -> impl Iterator<Item = DiagnosticEntry<O>> + 'a
|
) -> impl Iterator<Item = DiagnosticEntry<Anchor>> + 'a
|
||||||
where
|
where
|
||||||
T: 'a + ToOffset,
|
T: 'a + ToOffset,
|
||||||
O: 'a + text::FromAnchor + Ord,
|
|
||||||
{
|
{
|
||||||
self.as_singleton()
|
let mut ranges = self.range_to_buffer_ranges(range);
|
||||||
.into_iter()
|
if reversed {
|
||||||
.flat_map(move |(_, _, buffer)| {
|
ranges.reverse();
|
||||||
buffer.diagnostics_in_range(
|
}
|
||||||
range.start.to_offset(self)..range.end.to_offset(self),
|
ranges.into_iter().flat_map(move |(excerpt, range)| {
|
||||||
reversed,
|
let excerpt_id = excerpt.id();
|
||||||
|
excerpt.buffer().diagnostics_in_range(range, reversed).map(
|
||||||
|
move |DiagnosticEntry { diagnostic, range }| DiagnosticEntry {
|
||||||
|
diagnostic,
|
||||||
|
range: self.anchor_in_excerpt(excerpt_id, range.start).unwrap()
|
||||||
|
..self.anchor_in_excerpt(excerpt_id, range.end).unwrap(),
|
||||||
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -4185,6 +4153,42 @@ impl MultiBufferSnapshot {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn range_to_buffer_ranges<T: ToOffset>(
|
||||||
|
&self,
|
||||||
|
range: Range<T>,
|
||||||
|
) -> Vec<(MultiBufferExcerpt<'_>, Range<usize>)> {
|
||||||
|
let start = range.start.to_offset(self);
|
||||||
|
let end = range.end.to_offset(self);
|
||||||
|
|
||||||
|
let mut result = Vec::new();
|
||||||
|
let mut cursor = self.excerpts.cursor::<(usize, Point)>(&());
|
||||||
|
cursor.seek(&start, Bias::Right, &());
|
||||||
|
if cursor.item().is_none() {
|
||||||
|
cursor.prev(&());
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some(excerpt) = cursor.item() {
|
||||||
|
if cursor.start().0 > end {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut end_before_newline = cursor.end(&()).0;
|
||||||
|
if excerpt.has_trailing_newline {
|
||||||
|
end_before_newline -= 1;
|
||||||
|
}
|
||||||
|
let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
|
||||||
|
let start = excerpt_start + (cmp::max(start, cursor.start().0) - cursor.start().0);
|
||||||
|
let end = excerpt_start + (cmp::min(end, end_before_newline) - cursor.start().0);
|
||||||
|
result.push((
|
||||||
|
MultiBufferExcerpt::new(&excerpt, *cursor.start()),
|
||||||
|
start..end,
|
||||||
|
));
|
||||||
|
cursor.next(&());
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns excerpts overlapping the given ranges. If range spans multiple excerpts returns one range for each excerpt
|
/// Returns excerpts overlapping the given ranges. If range spans multiple excerpts returns one range for each excerpt
|
||||||
///
|
///
|
||||||
/// The ranges are specified in the coordinate space of the multibuffer, not the individual excerpted buffers.
|
/// The ranges are specified in the coordinate space of the multibuffer, not the individual excerpted buffers.
|
||||||
|
@ -4664,6 +4668,10 @@ impl<'a> MultiBufferExcerpt<'a> {
|
||||||
self.excerpt.id
|
self.excerpt.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn buffer_id(&self) -> BufferId {
|
||||||
|
self.excerpt.buffer_id
|
||||||
|
}
|
||||||
|
|
||||||
pub fn start_anchor(&self) -> Anchor {
|
pub fn start_anchor(&self) -> Anchor {
|
||||||
Anchor {
|
Anchor {
|
||||||
buffer_id: Some(self.excerpt.buffer_id),
|
buffer_id: Some(self.excerpt.buffer_id),
|
||||||
|
|
|
@ -1234,14 +1234,13 @@ fn test_random_multibuffer(cx: &mut AppContext, mut rng: StdRng) {
|
||||||
start_ix..end_ix
|
start_ix..end_ix
|
||||||
);
|
);
|
||||||
|
|
||||||
let excerpted_buffer_ranges = multibuffer
|
let snapshot = multibuffer.read(cx).snapshot(cx);
|
||||||
.read(cx)
|
let excerpted_buffer_ranges = snapshot.range_to_buffer_ranges(start_ix..end_ix);
|
||||||
.range_to_buffer_ranges(start_ix..end_ix, cx);
|
|
||||||
let excerpted_buffers_text = excerpted_buffer_ranges
|
let excerpted_buffers_text = excerpted_buffer_ranges
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(buffer, buffer_range, _)| {
|
.map(|(excerpt, buffer_range)| {
|
||||||
buffer
|
excerpt
|
||||||
.read(cx)
|
.buffer()
|
||||||
.text_for_range(buffer_range.clone())
|
.text_for_range(buffer_range.clone())
|
||||||
.collect::<String>()
|
.collect::<String>()
|
||||||
})
|
})
|
||||||
|
|
|
@ -3047,6 +3047,12 @@ pub trait FromAnchor {
|
||||||
fn from_anchor(anchor: &Anchor, snapshot: &BufferSnapshot) -> Self;
|
fn from_anchor(anchor: &Anchor, snapshot: &BufferSnapshot) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromAnchor for Anchor {
|
||||||
|
fn from_anchor(anchor: &Anchor, _snapshot: &BufferSnapshot) -> Self {
|
||||||
|
*anchor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FromAnchor for Point {
|
impl FromAnchor for Point {
|
||||||
fn from_anchor(anchor: &Anchor, snapshot: &BufferSnapshot) -> Self {
|
fn from_anchor(anchor: &Anchor, snapshot: &BufferSnapshot) -> Self {
|
||||||
snapshot.summary_for_anchor(anchor)
|
snapshot.summary_for_anchor(anchor)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue