Use MultiBuffer::insert_excerpt_after to update project diagnostics view
This commit is contained in:
parent
435d405d10
commit
3e59c61a34
1 changed files with 138 additions and 17 deletions
|
@ -1,8 +1,9 @@
|
|||
use anyhow::Result;
|
||||
use collections::{hash_map, HashMap, HashSet};
|
||||
use editor::{
|
||||
context_header_renderer, diagnostic_block_renderer, diagnostic_header_renderer,
|
||||
display_map::{BlockDisposition, BlockProperties},
|
||||
BuildSettings, Editor, ExcerptProperties, MultiBuffer,
|
||||
display_map::{BlockDisposition, BlockId, BlockProperties},
|
||||
BuildSettings, Editor, ExcerptId, ExcerptProperties, MultiBuffer,
|
||||
};
|
||||
use gpui::{
|
||||
action, elements::*, keymap::Binding, AppContext, Entity, ModelHandle, MutableAppContext,
|
||||
|
@ -11,7 +12,7 @@ use gpui::{
|
|||
use language::{Bias, Buffer, Point};
|
||||
use postage::watch;
|
||||
use project::Project;
|
||||
use std::ops::Range;
|
||||
use std::{ops::Range, path::Path, sync::Arc};
|
||||
use util::TryFutureExt;
|
||||
use workspace::Workspace;
|
||||
|
||||
|
@ -33,9 +34,22 @@ struct ProjectDiagnostics {
|
|||
struct ProjectDiagnosticsEditor {
|
||||
editor: ViewHandle<Editor>,
|
||||
excerpts: ModelHandle<MultiBuffer>,
|
||||
path_states: Vec<(Arc<Path>, PathState)>,
|
||||
build_settings: BuildSettings,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct PathState {
|
||||
last_excerpt: ExcerptId,
|
||||
diagnostic_group_states: HashMap<usize, DiagnosticGroupState>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct DiagnosticGroupState {
|
||||
excerpts: Vec<ExcerptId>,
|
||||
blocks: Vec<BlockId>,
|
||||
}
|
||||
|
||||
impl ProjectDiagnostics {
|
||||
fn new(project: ModelHandle<Project>) -> Self {
|
||||
Self { project }
|
||||
|
@ -118,6 +132,7 @@ impl ProjectDiagnosticsEditor {
|
|||
excerpts,
|
||||
editor,
|
||||
build_settings,
|
||||
path_states: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -132,13 +147,66 @@ impl ProjectDiagnosticsEditor {
|
|||
}
|
||||
|
||||
fn populate_excerpts(&mut self, buffer: ModelHandle<Buffer>, cx: &mut ViewContext<Self>) {
|
||||
let mut blocks = Vec::new();
|
||||
let snapshot = buffer.read(cx).snapshot();
|
||||
let snapshot;
|
||||
let path;
|
||||
{
|
||||
let buffer = buffer.read(cx);
|
||||
snapshot = buffer.snapshot();
|
||||
if let Some(file) = buffer.file() {
|
||||
path = file.path().clone();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let path_ix = match self
|
||||
.path_states
|
||||
.binary_search_by_key(&path.as_ref(), |e| e.0.as_ref())
|
||||
{
|
||||
Ok(ix) => ix,
|
||||
Err(ix) => {
|
||||
self.path_states.insert(
|
||||
ix,
|
||||
(
|
||||
path.clone(),
|
||||
PathState {
|
||||
last_excerpt: ExcerptId::max(),
|
||||
diagnostic_group_states: Default::default(),
|
||||
},
|
||||
),
|
||||
);
|
||||
ix
|
||||
}
|
||||
};
|
||||
let mut prev_excerpt_id = if path_ix > 0 {
|
||||
self.path_states[path_ix - 1].1.last_excerpt.clone()
|
||||
} else {
|
||||
ExcerptId::min()
|
||||
};
|
||||
let path_state = &mut self.path_states[path_ix].1;
|
||||
|
||||
let mut blocks_to_add = Vec::new();
|
||||
let mut blocks_to_remove = HashSet::default();
|
||||
let mut excerpts_to_remove = Vec::new();
|
||||
let mut block_counts_by_group = Vec::new();
|
||||
|
||||
let diagnostic_groups = snapshot.diagnostic_groups::<Point>();
|
||||
let excerpts_snapshot = self.excerpts.update(cx, |excerpts, excerpts_cx| {
|
||||
for group in snapshot.diagnostic_groups::<Point>() {
|
||||
for group in &diagnostic_groups {
|
||||
let group_id = group.entries[0].diagnostic.group_id;
|
||||
|
||||
let group_state = match path_state.diagnostic_group_states.entry(group_id) {
|
||||
hash_map::Entry::Occupied(e) => {
|
||||
prev_excerpt_id = e.get().excerpts.last().unwrap().clone();
|
||||
block_counts_by_group.push(0);
|
||||
continue;
|
||||
}
|
||||
hash_map::Entry::Vacant(e) => e.insert(DiagnosticGroupState::default()),
|
||||
};
|
||||
|
||||
let mut block_count = 0;
|
||||
let mut pending_range: Option<(Range<Point>, usize)> = None;
|
||||
let mut is_first_excerpt = true;
|
||||
let mut is_first_excerpt_for_group = true;
|
||||
for (ix, entry) in group.entries.iter().map(Some).chain([None]).enumerate() {
|
||||
if let Some((range, start_ix)) = &mut pending_range {
|
||||
if let Some(entry) = entry {
|
||||
|
@ -154,7 +222,8 @@ impl ProjectDiagnosticsEditor {
|
|||
Point::new(range.end.row + CONTEXT_LINE_COUNT, u32::MAX),
|
||||
Bias::Left,
|
||||
);
|
||||
let excerpt_id = excerpts.push_excerpt(
|
||||
let excerpt_id = excerpts.insert_excerpt_after(
|
||||
&prev_excerpt_id,
|
||||
ExcerptProperties {
|
||||
buffer: &buffer,
|
||||
range: excerpt_start..excerpt_end,
|
||||
|
@ -162,13 +231,18 @@ impl ProjectDiagnosticsEditor {
|
|||
excerpts_cx,
|
||||
);
|
||||
|
||||
prev_excerpt_id = excerpt_id.clone();
|
||||
group_state.excerpts.push(excerpt_id.clone());
|
||||
let header_position = (excerpt_id.clone(), language::Anchor::min());
|
||||
if is_first_excerpt {
|
||||
|
||||
if is_first_excerpt_for_group {
|
||||
is_first_excerpt_for_group = false;
|
||||
let primary = &group.entries[group.primary_ix].diagnostic;
|
||||
let mut header = primary.clone();
|
||||
header.message =
|
||||
primary.message.split('\n').next().unwrap().to_string();
|
||||
blocks.push(BlockProperties {
|
||||
block_count += 1;
|
||||
blocks_to_add.push(BlockProperties {
|
||||
position: header_position,
|
||||
height: 2,
|
||||
render: diagnostic_header_renderer(
|
||||
|
@ -179,7 +253,8 @@ impl ProjectDiagnosticsEditor {
|
|||
disposition: BlockDisposition::Above,
|
||||
});
|
||||
} else {
|
||||
blocks.push(BlockProperties {
|
||||
block_count += 1;
|
||||
blocks_to_add.push(BlockProperties {
|
||||
position: header_position,
|
||||
height: 1,
|
||||
render: context_header_renderer(self.build_settings.clone()),
|
||||
|
@ -187,7 +262,6 @@ impl ProjectDiagnosticsEditor {
|
|||
});
|
||||
}
|
||||
|
||||
is_first_excerpt = false;
|
||||
for entry in &group.entries[*start_ix..ix] {
|
||||
let mut diagnostic = entry.diagnostic.clone();
|
||||
if diagnostic.is_primary {
|
||||
|
@ -198,7 +272,8 @@ impl ProjectDiagnosticsEditor {
|
|||
|
||||
if !diagnostic.message.is_empty() {
|
||||
let buffer_anchor = snapshot.anchor_before(entry.range.start);
|
||||
blocks.push(BlockProperties {
|
||||
block_count += 1;
|
||||
blocks_to_add.push(BlockProperties {
|
||||
position: (excerpt_id.clone(), buffer_anchor),
|
||||
height: diagnostic.message.matches('\n').count() as u8 + 1,
|
||||
render: diagnostic_block_renderer(
|
||||
|
@ -218,14 +293,36 @@ impl ProjectDiagnosticsEditor {
|
|||
pending_range = Some((entry.range.clone(), ix));
|
||||
}
|
||||
}
|
||||
|
||||
block_counts_by_group.push(block_count);
|
||||
}
|
||||
|
||||
path_state
|
||||
.diagnostic_group_states
|
||||
.retain(|group_id, group_state| {
|
||||
if diagnostic_groups
|
||||
.iter()
|
||||
.any(|group| group.entries[0].diagnostic.group_id == *group_id)
|
||||
{
|
||||
true
|
||||
} else {
|
||||
excerpts_to_remove.extend(group_state.excerpts.drain(..));
|
||||
blocks_to_remove.extend(group_state.blocks.drain(..));
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
excerpts_to_remove.sort();
|
||||
excerpts.remove_excerpts(excerpts_to_remove.iter(), excerpts_cx);
|
||||
excerpts.snapshot(excerpts_cx)
|
||||
});
|
||||
|
||||
path_state.last_excerpt = prev_excerpt_id;
|
||||
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
editor.insert_blocks(
|
||||
blocks.into_iter().map(|block| {
|
||||
editor.remove_blocks(blocks_to_remove, cx);
|
||||
let block_ids = editor.insert_blocks(
|
||||
blocks_to_add.into_iter().map(|block| {
|
||||
let (excerpt_id, text_anchor) = block.position;
|
||||
BlockProperties {
|
||||
position: excerpts_snapshot.anchor_in_excerpt(excerpt_id, text_anchor),
|
||||
|
@ -236,6 +333,20 @@ impl ProjectDiagnosticsEditor {
|
|||
}),
|
||||
cx,
|
||||
);
|
||||
|
||||
let mut block_ids = block_ids.into_iter();
|
||||
let mut block_counts_by_group = block_counts_by_group.into_iter();
|
||||
for group in &diagnostic_groups {
|
||||
let group_id = group.entries[0].diagnostic.group_id;
|
||||
let block_count = block_counts_by_group.next().unwrap();
|
||||
let group_state = path_state
|
||||
.diagnostic_group_states
|
||||
.get_mut(&group_id)
|
||||
.unwrap();
|
||||
group_state
|
||||
.blocks
|
||||
.extend(block_ids.by_ref().take(block_count));
|
||||
}
|
||||
});
|
||||
cx.notify();
|
||||
}
|
||||
|
@ -454,7 +565,9 @@ mod tests {
|
|||
assert_eq!(
|
||||
editor.text(),
|
||||
concat!(
|
||||
// Diagnostic group 1 (error for `y`)
|
||||
//
|
||||
// main.rs, diagnostic group 1
|
||||
//
|
||||
"\n", // primary message
|
||||
"\n", // filename
|
||||
" let x = vec![];\n",
|
||||
|
@ -468,7 +581,9 @@ mod tests {
|
|||
" c(y);\n",
|
||||
"\n", // supporting diagnostic
|
||||
" d(x);\n",
|
||||
// Diagnostic group 2 (error for `x`)
|
||||
//
|
||||
// main.rs, diagnostic group 2
|
||||
//
|
||||
"\n", // primary message
|
||||
"\n", // filename
|
||||
"fn main() {\n",
|
||||
|
@ -516,11 +631,15 @@ mod tests {
|
|||
assert_eq!(
|
||||
editor.text(),
|
||||
concat!(
|
||||
//
|
||||
// a.rs
|
||||
//
|
||||
"\n", // primary message
|
||||
"\n", // filename
|
||||
"const a: i32 = 'a';\n",
|
||||
//
|
||||
// main.rs, diagnostic group 1
|
||||
//
|
||||
"\n", // primary message
|
||||
"\n", // filename
|
||||
" let x = vec![];\n",
|
||||
|
@ -534,7 +653,9 @@ mod tests {
|
|||
" c(y);\n",
|
||||
"\n", // supporting diagnostic
|
||||
" d(x);\n",
|
||||
//
|
||||
// main.rs, diagnostic group 2
|
||||
//
|
||||
"\n", // primary message
|
||||
"\n", // filename
|
||||
"fn main() {\n",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue