Do not derive symbol highlights if they do not fit into multi buffer (#50948)
Release Notes: - N/A --------- Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
1 parent
b06c0e0773
commit
d788673f1e
12 files changed
+103
-206
No files matched your search
@@ -297,7 +297,7 @@ impl DiagnosticBlock {
|
||||
return;
|
||||
};
|
||||
|
||||
for (excerpt_id, range) in multibuffer.excerpts_for_buffer(buffer_id, cx) {
|
||||
for (excerpt_id, _, range) in multibuffer.excerpts_for_buffer(buffer_id, cx) {
|
||||
if range.context.overlaps(&diagnostic.range, &snapshot) {
|
||||
Self::jump_to(
|
||||
editor,
|
||||
|
||||
@@ -583,7 +583,7 @@ impl ProjectDiagnosticsEditor {
|
||||
RetainExcerpts::All | RetainExcerpts::Dirty => multi_buffer
|
||||
.excerpts_for_buffer(buffer_id, cx)
|
||||
.into_iter()
|
||||
.map(|(_, range)| range)
|
||||
.map(|(_, _, range)| range)
|
||||
.sorted_by(|a, b| cmp_excerpts(&buffer_snapshot, a, b))
|
||||
.collect(),
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ use project::{InlayId, lsp_store::LspFoldingRange, lsp_store::TokenType};
|
||||
use serde::Deserialize;
|
||||
use smallvec::SmallVec;
|
||||
use sum_tree::{Bias, TreeMap};
|
||||
use text::{BufferId, LineIndent, Patch, ToOffset as _};
|
||||
use text::{BufferId, LineIndent, Patch};
|
||||
use ui::{SharedString, px};
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
use ztracing::instrument;
|
||||
@@ -1977,57 +1977,11 @@ impl DisplaySnapshot {
|
||||
/// Returned ranges are 0-based relative to `buffer_range.start`.
|
||||
pub(super) fn combined_highlights(
|
||||
&self,
|
||||
buffer_id: BufferId,
|
||||
buffer_range: Range<usize>,
|
||||
multibuffer_range: Range<MultiBufferOffset>,
|
||||
syntax_theme: &theme::SyntaxTheme,
|
||||
) -> Vec<(Range<usize>, HighlightStyle)> {
|
||||
let multibuffer = self.buffer_snapshot();
|
||||
|
||||
let multibuffer_range = multibuffer
|
||||
.excerpts()
|
||||
.find_map(|(excerpt_id, buffer, range)| {
|
||||
if buffer.remote_id() != buffer_id {
|
||||
return None;
|
||||
}
|
||||
let context_start = range.context.start.to_offset(buffer);
|
||||
let context_end = range.context.end.to_offset(buffer);
|
||||
if buffer_range.start < context_start || buffer_range.end > context_end {
|
||||
return None;
|
||||
}
|
||||
let start_anchor = buffer.anchor_before(buffer_range.start);
|
||||
let end_anchor = buffer.anchor_after(buffer_range.end);
|
||||
let mb_range =
|
||||
multibuffer.anchor_range_in_excerpt(excerpt_id, start_anchor..end_anchor)?;
|
||||
Some(mb_range.start.to_offset(multibuffer)..mb_range.end.to_offset(multibuffer))
|
||||
});
|
||||
|
||||
let Some(multibuffer_range) = multibuffer_range else {
|
||||
// Range is outside all excerpts (e.g. symbol name not in a
|
||||
// multi-buffer excerpt). Fall back to buffer-level syntax highlights.
|
||||
let buffer_snapshot = multibuffer.excerpts().find_map(|(_, buffer, _)| {
|
||||
(buffer.remote_id() == buffer_id).then(|| buffer.clone())
|
||||
});
|
||||
let Some(buffer_snapshot) = buffer_snapshot else {
|
||||
return Vec::new();
|
||||
};
|
||||
let mut highlights = Vec::new();
|
||||
let mut offset = 0usize;
|
||||
for chunk in buffer_snapshot.chunks(buffer_range, true) {
|
||||
let chunk_len = chunk.text.len();
|
||||
if chunk_len == 0 {
|
||||
continue;
|
||||
}
|
||||
if let Some(style) = chunk
|
||||
.syntax_highlight_id
|
||||
.and_then(|id| id.style(syntax_theme))
|
||||
{
|
||||
highlights.push((offset..offset + chunk_len, style));
|
||||
}
|
||||
offset += chunk_len;
|
||||
}
|
||||
return highlights;
|
||||
};
|
||||
|
||||
let chunks = custom_highlights::CustomHighlightsChunks::new(
|
||||
multibuffer_range,
|
||||
true,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{cmp, ops::Range};
|
||||
use std::ops::Range;
|
||||
|
||||
use collections::HashMap;
|
||||
use futures::FutureExt;
|
||||
@@ -6,10 +6,15 @@ use futures::future::join_all;
|
||||
use gpui::{App, Context, HighlightStyle, Task};
|
||||
use itertools::Itertools as _;
|
||||
use language::language_settings::language_settings;
|
||||
use language::{Buffer, BufferSnapshot, OutlineItem};
|
||||
use multi_buffer::{Anchor, MultiBufferSnapshot};
|
||||
use text::{Bias, BufferId, OffsetRangeExt as _, ToOffset as _};
|
||||
use language::{Buffer, OutlineItem};
|
||||
use multi_buffer::{
|
||||
Anchor, AnchorRangeExt as _, MultiBufferOffset, MultiBufferRow, MultiBufferSnapshot,
|
||||
ToOffset as _,
|
||||
};
|
||||
use text::BufferId;
|
||||
use theme::{ActiveTheme as _, SyntaxTheme};
|
||||
use unicode_segmentation::UnicodeSegmentation as _;
|
||||
use util::maybe;
|
||||
|
||||
use crate::display_map::DisplaySnapshot;
|
||||
use crate::{Editor, LSP_REQUEST_DEBOUNCE_TIMEOUT};
|
||||
@@ -215,16 +220,13 @@ impl Editor {
|
||||
let display_snapshot =
|
||||
editor.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||
let mut highlighted_results = results;
|
||||
for (buffer_id, items) in &mut highlighted_results {
|
||||
if let Some(buffer) = editor.buffer.read(cx).buffer(*buffer_id) {
|
||||
let snapshot = buffer.read(cx).snapshot();
|
||||
apply_highlights(
|
||||
items,
|
||||
*buffer_id,
|
||||
&snapshot,
|
||||
&display_snapshot,
|
||||
&syntax,
|
||||
);
|
||||
for items in highlighted_results.values_mut() {
|
||||
for item in items {
|
||||
if let Some(highlights) =
|
||||
highlights_from_buffer(&display_snapshot, &item, &syntax)
|
||||
{
|
||||
item.highlight_ranges = highlights;
|
||||
}
|
||||
}
|
||||
}
|
||||
editor.lsp_document_symbols.extend(highlighted_results);
|
||||
@@ -242,34 +244,6 @@ fn lsp_symbols_enabled(buffer: &Buffer, cx: &App) -> bool {
|
||||
.lsp_enabled()
|
||||
}
|
||||
|
||||
/// Applies combined syntax + semantic token highlights to LSP document symbol
|
||||
/// outline items that were built without highlights by the project layer.
|
||||
fn apply_highlights(
|
||||
items: &mut [OutlineItem<text::Anchor>],
|
||||
buffer_id: BufferId,
|
||||
buffer_snapshot: &BufferSnapshot,
|
||||
display_snapshot: &DisplaySnapshot,
|
||||
syntax_theme: &SyntaxTheme,
|
||||
) {
|
||||
for item in items {
|
||||
let symbol_range = item.range.to_offset(buffer_snapshot);
|
||||
let selection_start = item.source_range_for_text.start.to_offset(buffer_snapshot);
|
||||
|
||||
if let Some(highlights) = highlights_from_buffer(
|
||||
&item.text,
|
||||
0,
|
||||
buffer_id,
|
||||
buffer_snapshot,
|
||||
display_snapshot,
|
||||
symbol_range,
|
||||
selection_start,
|
||||
syntax_theme,
|
||||
) {
|
||||
item.highlight_ranges = highlights;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds where the symbol name appears in the buffer and returns combined
|
||||
/// (tree-sitter + semantic token) highlights for those positions.
|
||||
///
|
||||
@@ -278,117 +252,78 @@ fn apply_highlights(
|
||||
/// to word-by-word matching for cases like `impl<T> Trait<T> for Type`
|
||||
/// where the LSP name doesn't appear verbatim in the buffer.
|
||||
fn highlights_from_buffer(
|
||||
name: &str,
|
||||
name_offset_in_text: usize,
|
||||
buffer_id: BufferId,
|
||||
buffer_snapshot: &BufferSnapshot,
|
||||
display_snapshot: &DisplaySnapshot,
|
||||
symbol_range: Range<usize>,
|
||||
selection_start_offset: usize,
|
||||
item: &OutlineItem<text::Anchor>,
|
||||
syntax_theme: &SyntaxTheme,
|
||||
) -> Option<Vec<(Range<usize>, HighlightStyle)>> {
|
||||
if name.is_empty() {
|
||||
let outline_text = &item.text;
|
||||
if outline_text.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let range_start_offset = symbol_range.start;
|
||||
let range_end_offset = symbol_range.end;
|
||||
let multi_buffer_snapshot = display_snapshot.buffer();
|
||||
let multi_buffer_source_range_anchors =
|
||||
multi_buffer_snapshot.text_anchors_to_visible_anchors([
|
||||
item.source_range_for_text.start,
|
||||
item.source_range_for_text.end,
|
||||
]);
|
||||
let Some(anchor_range) = maybe!({
|
||||
Some(
|
||||
(*multi_buffer_source_range_anchors.get(0)?)?
|
||||
..(*multi_buffer_source_range_anchors.get(1)?)?,
|
||||
)
|
||||
}) else {
|
||||
return None;
|
||||
};
|
||||
|
||||
// Try to find the name verbatim in the buffer near the selection range.
|
||||
let search_start = buffer_snapshot.clip_offset(
|
||||
selection_start_offset
|
||||
.saturating_sub(name.len())
|
||||
.max(range_start_offset),
|
||||
Bias::Right,
|
||||
);
|
||||
let search_end = buffer_snapshot.clip_offset(
|
||||
cmp::min(selection_start_offset + name.len() * 2, range_end_offset),
|
||||
Bias::Left,
|
||||
);
|
||||
let selection_point_range = anchor_range.to_point(multi_buffer_snapshot);
|
||||
let mut search_start = selection_point_range.start;
|
||||
search_start.column = 0;
|
||||
let search_start_offset = search_start.to_offset(&multi_buffer_snapshot);
|
||||
let mut search_end = selection_point_range.end;
|
||||
search_end.column = multi_buffer_snapshot.line_len(MultiBufferRow(search_end.row));
|
||||
|
||||
if search_start < search_end {
|
||||
let buffer_text: String = buffer_snapshot
|
||||
.text_for_range(search_start..search_end)
|
||||
.collect();
|
||||
if let Some(found_at) = buffer_text.find(name) {
|
||||
let name_start_offset = search_start + found_at;
|
||||
let name_end_offset = name_start_offset + name.len();
|
||||
let result = highlights_for_buffer_range(
|
||||
name_offset_in_text,
|
||||
name_start_offset..name_end_offset,
|
||||
buffer_id,
|
||||
display_snapshot,
|
||||
syntax_theme,
|
||||
let search_text = multi_buffer_snapshot
|
||||
.text_for_range(search_start..search_end)
|
||||
.collect::<String>();
|
||||
|
||||
let mut outline_text_highlights = Vec::new();
|
||||
match search_text.find(outline_text) {
|
||||
Some(start_index) => {
|
||||
let multibuffer_start = search_start_offset + MultiBufferOffset(start_index);
|
||||
let multibuffer_end = multibuffer_start + MultiBufferOffset(outline_text.len());
|
||||
outline_text_highlights.extend(
|
||||
display_snapshot
|
||||
.combined_highlights(multibuffer_start..multibuffer_end, syntax_theme),
|
||||
);
|
||||
if result.is_some() {
|
||||
return result;
|
||||
}
|
||||
None => {
|
||||
for (outline_text_word_start, outline_word) in outline_text.split_word_bound_indices() {
|
||||
if let Some(start_index) = search_text.find(outline_word) {
|
||||
let multibuffer_start = search_start_offset + MultiBufferOffset(start_index);
|
||||
let multibuffer_end = multibuffer_start + MultiBufferOffset(outline_word.len());
|
||||
outline_text_highlights.extend(
|
||||
display_snapshot
|
||||
.combined_highlights(multibuffer_start..multibuffer_end, syntax_theme)
|
||||
.into_iter()
|
||||
.map(|(range_in_word, style)| {
|
||||
(
|
||||
outline_text_word_start + range_in_word.start
|
||||
..outline_text_word_start + range_in_word.end,
|
||||
style,
|
||||
)
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: match word-by-word. Split the name on whitespace and find
|
||||
// each word sequentially in the buffer's symbol range.
|
||||
let range_start_offset = buffer_snapshot.clip_offset(range_start_offset, Bias::Right);
|
||||
let range_end_offset = buffer_snapshot.clip_offset(range_end_offset, Bias::Left);
|
||||
|
||||
let mut highlights = Vec::new();
|
||||
let mut got_any = false;
|
||||
let buffer_text: String = buffer_snapshot
|
||||
.text_for_range(range_start_offset..range_end_offset)
|
||||
.collect();
|
||||
let mut buf_search_from = 0usize;
|
||||
let mut name_search_from = 0usize;
|
||||
for word in name.split_whitespace() {
|
||||
let name_word_start = name[name_search_from..]
|
||||
.find(word)
|
||||
.map(|pos| name_search_from + pos)
|
||||
.unwrap_or(name_search_from);
|
||||
if let Some(found_in_buf) = buffer_text[buf_search_from..].find(word) {
|
||||
let buf_word_start = range_start_offset + buf_search_from + found_in_buf;
|
||||
let buf_word_end = buf_word_start + word.len();
|
||||
let text_cursor = name_offset_in_text + name_word_start;
|
||||
if let Some(mut word_highlights) = highlights_for_buffer_range(
|
||||
text_cursor,
|
||||
buf_word_start..buf_word_end,
|
||||
buffer_id,
|
||||
display_snapshot,
|
||||
syntax_theme,
|
||||
) {
|
||||
got_any = true;
|
||||
highlights.append(&mut word_highlights);
|
||||
}
|
||||
buf_search_from = buf_search_from + found_in_buf + word.len();
|
||||
}
|
||||
name_search_from = name_word_start + word.len();
|
||||
if outline_text_highlights.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(outline_text_highlights)
|
||||
}
|
||||
|
||||
got_any.then_some(highlights)
|
||||
}
|
||||
|
||||
/// Gets combined (tree-sitter + semantic token) highlights for a buffer byte
|
||||
/// range via the editor's display snapshot, then shifts the returned ranges
|
||||
/// so they start at `text_cursor_start` (the position in the outline item text).
|
||||
fn highlights_for_buffer_range(
|
||||
text_cursor_start: usize,
|
||||
buffer_range: Range<usize>,
|
||||
buffer_id: BufferId,
|
||||
display_snapshot: &DisplaySnapshot,
|
||||
syntax_theme: &SyntaxTheme,
|
||||
) -> Option<Vec<(Range<usize>, HighlightStyle)>> {
|
||||
let raw = display_snapshot.combined_highlights(buffer_id, buffer_range, syntax_theme);
|
||||
if raw.is_empty() {
|
||||
return None;
|
||||
}
|
||||
Some(
|
||||
raw.into_iter()
|
||||
.map(|(range, style)| {
|
||||
(
|
||||
range.start + text_cursor_start..range.end + text_cursor_start,
|
||||
style,
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -7500,7 +7500,8 @@ impl Editor {
|
||||
let mut read_ranges = Vec::new();
|
||||
for highlight in highlights {
|
||||
let buffer_id = cursor_buffer.read(cx).remote_id();
|
||||
for (excerpt_id, excerpt_range) in buffer.excerpts_for_buffer(buffer_id, cx)
|
||||
for (excerpt_id, _, excerpt_range) in
|
||||
buffer.excerpts_for_buffer(buffer_id, cx)
|
||||
{
|
||||
let start = highlight
|
||||
.range
|
||||
@@ -20539,7 +20540,7 @@ impl Editor {
|
||||
let mut all_folded_excerpt_ids = Vec::new();
|
||||
for buffer_id in &ids_to_fold {
|
||||
let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(*buffer_id, cx);
|
||||
all_folded_excerpt_ids.extend(folded_excerpts.into_iter().map(|(id, _)| id));
|
||||
all_folded_excerpt_ids.extend(folded_excerpts.into_iter().map(|(id, _, _)| id));
|
||||
}
|
||||
|
||||
self.display_map.update(cx, |display_map, cx| {
|
||||
@@ -20569,7 +20570,7 @@ impl Editor {
|
||||
display_map.unfold_buffers([buffer_id], cx);
|
||||
});
|
||||
cx.emit(EditorEvent::BufferFoldToggled {
|
||||
ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
|
||||
ids: unfolded_excerpts.iter().map(|&(id, _, _)| id).collect(),
|
||||
folded: false,
|
||||
});
|
||||
cx.notify();
|
||||
@@ -22941,7 +22942,7 @@ impl Editor {
|
||||
.snapshot();
|
||||
|
||||
let mut handled = false;
|
||||
for (id, ExcerptRange { context, .. }) in
|
||||
for (id, _, ExcerptRange { context, .. }) in
|
||||
self.buffer.read(cx).excerpts_for_buffer(buffer_id, cx)
|
||||
{
|
||||
if context.start.cmp(&position, &snapshot).is_ge()
|
||||
|
||||
@@ -1165,8 +1165,8 @@ impl SplittableEditor {
|
||||
let lhs_ranges: Vec<ExcerptRange<Point>> = rhs_multibuffer
|
||||
.excerpts_for_buffer(main_buffer_snapshot.remote_id(), cx)
|
||||
.into_iter()
|
||||
.filter(|(id, _)| rhs_excerpt_ids.contains(id))
|
||||
.map(|(_, excerpt_range)| {
|
||||
.filter(|(id, _, _)| rhs_excerpt_ids.contains(id))
|
||||
.map(|(_, _, excerpt_range)| {
|
||||
let to_base_text = |range: Range<Point>| {
|
||||
let start = diff_snapshot
|
||||
.buffer_point_to_base_text_range(
|
||||
|
||||
@@ -182,7 +182,7 @@ fn conflicts_updated(
|
||||
let excerpts = multibuffer.excerpts_for_buffer(buffer_id, cx);
|
||||
let Some(buffer_snapshot) = excerpts
|
||||
.first()
|
||||
.and_then(|(excerpt_id, _)| snapshot.buffer_for_excerpt(*excerpt_id))
|
||||
.and_then(|(excerpt_id, _, _)| snapshot.buffer_for_excerpt(*excerpt_id))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
@@ -221,7 +221,7 @@ fn conflicts_updated(
|
||||
let mut removed_highlighted_ranges = Vec::new();
|
||||
let mut removed_block_ids = HashSet::default();
|
||||
for (conflict_range, block_id) in old_conflicts {
|
||||
let Some((excerpt_id, _)) = excerpts.iter().find(|(_, range)| {
|
||||
let Some((excerpt_id, _, _)) = excerpts.iter().find(|(_, _, range)| {
|
||||
let precedes_start = range
|
||||
.context
|
||||
.start
|
||||
@@ -263,7 +263,7 @@ fn conflicts_updated(
|
||||
let new_conflicts = &conflict_set.conflicts[event.new_range.clone()];
|
||||
let mut blocks = Vec::new();
|
||||
for conflict in new_conflicts {
|
||||
let Some((excerpt_id, _)) = excerpts.iter().find(|(_, range)| {
|
||||
let Some((excerpt_id, _, _)) = excerpts.iter().find(|(_, _, range)| {
|
||||
let precedes_start = range
|
||||
.context
|
||||
.start
|
||||
|
||||
@@ -94,7 +94,9 @@ impl GoToLine {
|
||||
.read(cx)
|
||||
.excerpts_for_buffer(snapshot.remote_id(), cx)
|
||||
.into_iter()
|
||||
.map(move |(_, range)| text::ToPoint::to_point(&range.context.end, &snapshot).row)
|
||||
.map(move |(_, _, range)| {
|
||||
text::ToPoint::to_point(&range.context.end, &snapshot).row
|
||||
})
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
|
||||
|
||||
@@ -246,7 +246,12 @@ impl StyledText {
|
||||
pub fn with_runs(mut self, runs: Vec<TextRun>) -> Self {
|
||||
let mut text = &**self.text;
|
||||
for run in &runs {
|
||||
text = text.get(run.len..).expect("invalid text run");
|
||||
text = text.get(run.len..).unwrap_or_else(|| {
|
||||
#[cfg(debug_assertions)]
|
||||
panic!("invalid text run. Text: '{text}', run: {run:?}");
|
||||
#[cfg(not(debug_assertions))]
|
||||
panic!("invalid text run");
|
||||
});
|
||||
}
|
||||
assert!(text.is_empty(), "invalid text run");
|
||||
self.runs = Some(runs);
|
||||
|
||||
@@ -1987,7 +1987,7 @@ impl MultiBuffer {
|
||||
&self,
|
||||
buffer_id: BufferId,
|
||||
cx: &App,
|
||||
) -> Vec<(ExcerptId, ExcerptRange<text::Anchor>)> {
|
||||
) -> Vec<(ExcerptId, Arc<BufferSnapshot>, ExcerptRange<text::Anchor>)> {
|
||||
let mut excerpts = Vec::new();
|
||||
let snapshot = self.read(cx);
|
||||
let mut cursor = snapshot.excerpts.cursor::<Option<&Locator>>(());
|
||||
@@ -1997,7 +1997,7 @@ impl MultiBuffer {
|
||||
if let Some(excerpt) = cursor.item()
|
||||
&& excerpt.locator == *locator
|
||||
{
|
||||
excerpts.push((excerpt.id, excerpt.range.clone()));
|
||||
excerpts.push((excerpt.id, excerpt.buffer.clone(), excerpt.range.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2128,7 +2128,7 @@ impl MultiBuffer {
|
||||
) -> Option<Anchor> {
|
||||
let mut found = None;
|
||||
let snapshot = buffer.read(cx).snapshot();
|
||||
for (excerpt_id, range) in self.excerpts_for_buffer(snapshot.remote_id(), cx) {
|
||||
for (excerpt_id, _, range) in self.excerpts_for_buffer(snapshot.remote_id(), cx) {
|
||||
let start = range.context.start.to_point(&snapshot);
|
||||
let end = range.context.end.to_point(&snapshot);
|
||||
if start <= point && point < end {
|
||||
@@ -2157,7 +2157,7 @@ impl MultiBuffer {
|
||||
cx: &App,
|
||||
) -> Option<Anchor> {
|
||||
let snapshot = buffer.read(cx).snapshot();
|
||||
for (excerpt_id, range) in self.excerpts_for_buffer(snapshot.remote_id(), cx) {
|
||||
for (excerpt_id, _, range) in self.excerpts_for_buffer(snapshot.remote_id(), cx) {
|
||||
if range.context.start.cmp(&anchor, &snapshot).is_le()
|
||||
&& range.context.end.cmp(&anchor, &snapshot).is_ge()
|
||||
{
|
||||
|
||||
@@ -1285,7 +1285,7 @@ fn test_resolving_anchors_after_replacing_their_excerpts(cx: &mut App) {
|
||||
let mut ids = multibuffer
|
||||
.excerpts_for_buffer(buffer_2.read(cx).remote_id(), cx)
|
||||
.into_iter()
|
||||
.map(|(id, _)| id);
|
||||
.map(|(id, _, _)| id);
|
||||
(ids.next().unwrap(), ids.next().unwrap())
|
||||
});
|
||||
let snapshot_2 = multibuffer.read(cx).snapshot(cx);
|
||||
|
||||
@@ -1143,7 +1143,7 @@ impl OutlinePanel {
|
||||
.excerpts_for_buffer(buffer.read(cx).remote_id(), cx)
|
||||
})
|
||||
.and_then(|excerpts| {
|
||||
let (excerpt_id, excerpt_range) = excerpts.first()?;
|
||||
let (excerpt_id, _, excerpt_range) = excerpts.first()?;
|
||||
multi_buffer_snapshot
|
||||
.anchor_in_excerpt(*excerpt_id, excerpt_range.context.start)
|
||||
})
|
||||
|
||||
Reference in new issue
Block a user