Render path headers in editor element
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
f1e3d5285b
commit
8d95dbe3e6
4 changed files with 126 additions and 154 deletions
|
@ -423,25 +423,16 @@ impl ProjectDiagnosticsEditor {
|
||||||
self.editor.update(cx, |editor, cx| {
|
self.editor.update(cx, |editor, cx| {
|
||||||
blocks_to_remove.extend(path_state.header);
|
blocks_to_remove.extend(path_state.header);
|
||||||
editor.remove_blocks(blocks_to_remove, cx);
|
editor.remove_blocks(blocks_to_remove, cx);
|
||||||
let header_block = first_excerpt_id.map(|excerpt_id| BlockProperties {
|
|
||||||
position: excerpts_snapshot.anchor_in_excerpt(excerpt_id, language::Anchor::min()),
|
|
||||||
height: 2,
|
|
||||||
render: path_header_renderer(buffer, self.build_settings.clone()),
|
|
||||||
disposition: BlockDisposition::Above,
|
|
||||||
});
|
|
||||||
let block_ids = editor.insert_blocks(
|
let block_ids = editor.insert_blocks(
|
||||||
blocks_to_add
|
blocks_to_add.into_iter().map(|block| {
|
||||||
.into_iter()
|
let (excerpt_id, text_anchor) = block.position;
|
||||||
.map(|block| {
|
BlockProperties {
|
||||||
let (excerpt_id, text_anchor) = block.position;
|
position: excerpts_snapshot.anchor_in_excerpt(excerpt_id, text_anchor),
|
||||||
BlockProperties {
|
height: block.height,
|
||||||
position: excerpts_snapshot.anchor_in_excerpt(excerpt_id, text_anchor),
|
render: block.render,
|
||||||
height: block.height,
|
disposition: block.disposition,
|
||||||
render: block.render,
|
}
|
||||||
disposition: block.disposition,
|
}),
|
||||||
}
|
|
||||||
})
|
|
||||||
.chain(header_block.into_iter()),
|
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -660,51 +651,6 @@ impl workspace::ItemView for ProjectDiagnosticsEditor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_header_renderer(buffer: ModelHandle<Buffer>, build_settings: BuildSettings) -> RenderBlock {
|
|
||||||
Arc::new(move |cx| {
|
|
||||||
let settings = build_settings(cx);
|
|
||||||
let style = settings.style.diagnostic_path_header;
|
|
||||||
let font_size = (style.text_scale_factor * settings.style.text.font_size).round();
|
|
||||||
|
|
||||||
let mut filename = None;
|
|
||||||
let mut path = None;
|
|
||||||
if let Some(file) = buffer.read(&**cx).file() {
|
|
||||||
filename = file
|
|
||||||
.path()
|
|
||||||
.file_name()
|
|
||||||
.map(|f| f.to_string_lossy().to_string());
|
|
||||||
path = file
|
|
||||||
.path()
|
|
||||||
.parent()
|
|
||||||
.map(|p| p.to_string_lossy().to_string() + "/");
|
|
||||||
}
|
|
||||||
|
|
||||||
Flex::row()
|
|
||||||
.with_child(
|
|
||||||
Label::new(
|
|
||||||
filename.unwrap_or_else(|| "untitled".to_string()),
|
|
||||||
style.filename.text.clone().with_font_size(font_size),
|
|
||||||
)
|
|
||||||
.contained()
|
|
||||||
.with_style(style.filename.container)
|
|
||||||
.boxed(),
|
|
||||||
)
|
|
||||||
.with_children(path.map(|path| {
|
|
||||||
Label::new(path, style.path.text.clone().with_font_size(font_size))
|
|
||||||
.contained()
|
|
||||||
.with_style(style.path.container)
|
|
||||||
.boxed()
|
|
||||||
}))
|
|
||||||
.aligned()
|
|
||||||
.left()
|
|
||||||
.contained()
|
|
||||||
.with_style(style.container)
|
|
||||||
.with_padding_left(cx.gutter_padding + cx.scroll_x * cx.em_width)
|
|
||||||
.expanded()
|
|
||||||
.named("path header block")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn diagnostic_header_renderer(
|
fn diagnostic_header_renderer(
|
||||||
diagnostic: Diagnostic,
|
diagnostic: Diagnostic,
|
||||||
build_settings: BuildSettings,
|
build_settings: BuildSettings,
|
||||||
|
@ -843,7 +789,10 @@ fn compare_diagnostics<L: language::ToOffset, R: language::ToOffset>(
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use editor::{display_map::BlockContext, DisplayPoint, EditorSnapshot};
|
use editor::{
|
||||||
|
display_map::{BlockContext, TransformBlock},
|
||||||
|
DisplayPoint, EditorSnapshot,
|
||||||
|
};
|
||||||
use gpui::TestAppContext;
|
use gpui::TestAppContext;
|
||||||
use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, PointUtf16};
|
use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, PointUtf16};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
@ -1259,18 +1208,23 @@ mod tests {
|
||||||
editor
|
editor
|
||||||
.blocks_in_range(0..editor.max_point().row())
|
.blocks_in_range(0..editor.max_point().row())
|
||||||
.filter_map(|(row, block)| {
|
.filter_map(|(row, block)| {
|
||||||
block
|
let name = match block {
|
||||||
.render(&BlockContext {
|
TransformBlock::Custom(block) => block
|
||||||
cx,
|
.render(&BlockContext {
|
||||||
anchor_x: 0.,
|
cx,
|
||||||
scroll_x: 0.,
|
anchor_x: 0.,
|
||||||
gutter_padding: 0.,
|
scroll_x: 0.,
|
||||||
gutter_width: 0.,
|
gutter_padding: 0.,
|
||||||
line_height: 0.,
|
gutter_width: 0.,
|
||||||
em_width: 0.,
|
line_height: 0.,
|
||||||
})
|
em_width: 0.,
|
||||||
.name()
|
})
|
||||||
.map(|s| (row, s.to_string()))
|
.name()?
|
||||||
|
.to_string(),
|
||||||
|
TransformBlock::ExcerptHeader { .. } => "path header block".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Some((row, name))
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,10 +90,7 @@ struct Transform {
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum TransformBlock {
|
pub enum TransformBlock {
|
||||||
Custom {
|
Custom(Arc<Block>),
|
||||||
block: Arc<Block>,
|
|
||||||
column: u32,
|
|
||||||
},
|
|
||||||
ExcerptHeader {
|
ExcerptHeader {
|
||||||
buffer: BufferSnapshot,
|
buffer: BufferSnapshot,
|
||||||
range: Range<text::Anchor>,
|
range: Range<text::Anchor>,
|
||||||
|
@ -104,14 +101,14 @@ pub enum TransformBlock {
|
||||||
impl TransformBlock {
|
impl TransformBlock {
|
||||||
fn disposition(&self) -> BlockDisposition {
|
fn disposition(&self) -> BlockDisposition {
|
||||||
match self {
|
match self {
|
||||||
TransformBlock::Custom { block, .. } => block.disposition,
|
TransformBlock::Custom(block) => block.disposition,
|
||||||
TransformBlock::ExcerptHeader { .. } => BlockDisposition::Above,
|
TransformBlock::ExcerptHeader { .. } => BlockDisposition::Above,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn height(&self) -> u8 {
|
pub fn height(&self) -> u8 {
|
||||||
match self {
|
match self {
|
||||||
TransformBlock::Custom { block, .. } => block.height,
|
TransformBlock::Custom(block) => block.height,
|
||||||
TransformBlock::ExcerptHeader { height, .. } => *height,
|
TransformBlock::ExcerptHeader { height, .. } => *height,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,11 +117,7 @@ impl TransformBlock {
|
||||||
impl Debug for TransformBlock {
|
impl Debug for TransformBlock {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Custom { block, column } => f
|
Self::Custom(block) => f.debug_struct("Custom").field("block", block).finish(),
|
||||||
.debug_struct("Custom")
|
|
||||||
.field("block", block)
|
|
||||||
.field("column", column)
|
|
||||||
.finish(),
|
|
||||||
Self::ExcerptHeader { buffer, .. } => f
|
Self::ExcerptHeader { buffer, .. } => f
|
||||||
.debug_struct("ExcerptHeader")
|
.debug_struct("ExcerptHeader")
|
||||||
.field("path", &buffer.path())
|
.field("path", &buffer.path())
|
||||||
|
@ -319,7 +312,6 @@ impl BlockMap {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|block| {
|
.map(|block| {
|
||||||
let mut position = block.position.to_point(&buffer);
|
let mut position = block.position.to_point(&buffer);
|
||||||
let column = wrap_snapshot.from_point(position, Bias::Left).column();
|
|
||||||
match block.disposition {
|
match block.disposition {
|
||||||
BlockDisposition::Above => position.column = 0,
|
BlockDisposition::Above => position.column = 0,
|
||||||
BlockDisposition::Below => {
|
BlockDisposition::Below => {
|
||||||
|
@ -327,13 +319,7 @@ impl BlockMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let position = wrap_snapshot.from_point(position, Bias::Left);
|
let position = wrap_snapshot.from_point(position, Bias::Left);
|
||||||
(
|
(position.row(), TransformBlock::Custom(block.clone()))
|
||||||
position.row(),
|
|
||||||
TransformBlock::Custom {
|
|
||||||
block: block.clone(),
|
|
||||||
column,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
blocks_in_edit.extend(
|
blocks_in_edit.extend(
|
||||||
|
@ -362,10 +348,7 @@ impl BlockMap {
|
||||||
) => Ordering::Equal,
|
) => Ordering::Equal,
|
||||||
(TransformBlock::ExcerptHeader { .. }, _) => Ordering::Less,
|
(TransformBlock::ExcerptHeader { .. }, _) => Ordering::Less,
|
||||||
(_, TransformBlock::ExcerptHeader { .. }) => Ordering::Greater,
|
(_, TransformBlock::ExcerptHeader { .. }) => Ordering::Greater,
|
||||||
(
|
(TransformBlock::Custom(block_a), TransformBlock::Custom(block_b)) => block_a
|
||||||
TransformBlock::Custom { block: block_a, .. },
|
|
||||||
TransformBlock::Custom { block: block_b, .. },
|
|
||||||
) => block_a
|
|
||||||
.disposition
|
.disposition
|
||||||
.cmp(&block_b.disposition)
|
.cmp(&block_b.disposition)
|
||||||
.then_with(|| block_a.id.cmp(&block_b.id)),
|
.then_with(|| block_a.id.cmp(&block_b.id)),
|
||||||
|
@ -908,6 +891,10 @@ impl Block {
|
||||||
pub fn render(&self, cx: &BlockContext) -> ElementBox {
|
pub fn render(&self, cx: &BlockContext) -> ElementBox {
|
||||||
self.render.lock()(cx)
|
self.render.lock()(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn position(&self) -> &Anchor {
|
||||||
|
&self.position
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Block {
|
impl Debug for Block {
|
||||||
|
@ -1008,10 +995,9 @@ mod tests {
|
||||||
let blocks = snapshot
|
let blocks = snapshot
|
||||||
.blocks_in_range(0..8)
|
.blocks_in_range(0..8)
|
||||||
.map(|(start_row, block)| {
|
.map(|(start_row, block)| {
|
||||||
let (block, column) = block.as_custom().unwrap();
|
let block = block.as_custom().unwrap();
|
||||||
(
|
(
|
||||||
start_row..start_row + block.height as u32,
|
start_row..start_row + block.height as u32,
|
||||||
column,
|
|
||||||
block
|
block
|
||||||
.render(&BlockContext {
|
.render(&BlockContext {
|
||||||
cx,
|
cx,
|
||||||
|
@ -1033,9 +1019,9 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
blocks,
|
blocks,
|
||||||
&[
|
&[
|
||||||
(1..3, 2, "block 2".to_string()),
|
(1..2, "block 1".to_string()),
|
||||||
(3..4, 0, "block 1".to_string()),
|
(2..4, "block 2".to_string()),
|
||||||
(7..10, 3, "block 3".to_string()),
|
(7..10, "block 3".to_string()),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1324,7 +1310,6 @@ mod tests {
|
||||||
let mut expected_blocks = Vec::new();
|
let mut expected_blocks = Vec::new();
|
||||||
expected_blocks.extend(custom_blocks.iter().map(|(id, block)| {
|
expected_blocks.extend(custom_blocks.iter().map(|(id, block)| {
|
||||||
let mut position = block.position.to_point(&buffer_snapshot);
|
let mut position = block.position.to_point(&buffer_snapshot);
|
||||||
let column = wraps_snapshot.from_point(position, Bias::Left).column();
|
|
||||||
match block.disposition {
|
match block.disposition {
|
||||||
BlockDisposition::Above => {
|
BlockDisposition::Above => {
|
||||||
position.column = 0;
|
position.column = 0;
|
||||||
|
@ -1426,7 +1411,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
blocks_snapshot
|
blocks_snapshot
|
||||||
.blocks_in_range(0..(expected_row_count as u32))
|
.blocks_in_range(0..(expected_row_count as u32))
|
||||||
.map(|(row, block)| (row, block.as_custom().map(|(b, _)| b.id)))
|
.map(|(row, block)| { (row, block.as_custom().map(|b| b.id)) })
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
expected_block_positions
|
expected_block_positions
|
||||||
);
|
);
|
||||||
|
@ -1508,9 +1493,9 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransformBlock {
|
impl TransformBlock {
|
||||||
fn as_custom(&self) -> Option<(&Block, u32)> {
|
fn as_custom(&self) -> Option<&Block> {
|
||||||
match self {
|
match self {
|
||||||
TransformBlock::Custom { block, column } => Some((block, *column)),
|
TransformBlock::Custom(block) => Some(block),
|
||||||
TransformBlock::ExcerptHeader { .. } => None,
|
TransformBlock::ExcerptHeader { .. } => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -588,10 +588,6 @@ impl WrapSnapshot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn text_summary(&self) -> TextSummary {
|
|
||||||
self.transforms.summary().output
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn max_point(&self) -> WrapPoint {
|
pub fn max_point(&self) -> WrapPoint {
|
||||||
WrapPoint(self.transforms.summary().output.lines)
|
WrapPoint(self.transforms.summary().output.lines)
|
||||||
}
|
}
|
||||||
|
@ -955,10 +951,6 @@ impl WrapPoint {
|
||||||
&mut self.0.row
|
&mut self.0.row
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn column(&self) -> u32 {
|
|
||||||
self.0.column
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn column_mut(&mut self) -> &mut u32 {
|
pub fn column_mut(&mut self) -> &mut u32 {
|
||||||
&mut self.0.column
|
&mut self.0.column
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,12 @@ use super::{
|
||||||
Anchor, DisplayPoint, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle, Input,
|
Anchor, DisplayPoint, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle, Input,
|
||||||
Scroll, Select, SelectPhase, SoftWrap, ToPoint, MAX_LINE_LEN,
|
Scroll, Select, SelectPhase, SoftWrap, ToPoint, MAX_LINE_LEN,
|
||||||
};
|
};
|
||||||
|
use crate::display_map::TransformBlock;
|
||||||
use clock::ReplicaId;
|
use clock::ReplicaId;
|
||||||
use collections::{BTreeMap, HashMap};
|
use collections::{BTreeMap, HashMap};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
color::Color,
|
color::Color,
|
||||||
elements::layout_highlighted_chunks,
|
elements::*,
|
||||||
fonts::{HighlightStyle, Underline},
|
fonts::{HighlightStyle, Underline},
|
||||||
geometry::{
|
geometry::{
|
||||||
rect::RectF,
|
rect::RectF,
|
||||||
|
@ -649,44 +650,84 @@ impl EditorElement {
|
||||||
line_layouts: &[text_layout::Line],
|
line_layouts: &[text_layout::Line],
|
||||||
cx: &mut LayoutContext,
|
cx: &mut LayoutContext,
|
||||||
) -> Vec<(u32, ElementBox)> {
|
) -> Vec<(u32, ElementBox)> {
|
||||||
Default::default()
|
let scroll_x = snapshot.scroll_position.x();
|
||||||
// snapshot
|
snapshot
|
||||||
// .blocks_in_range(rows.clone())
|
.blocks_in_range(rows.clone())
|
||||||
// .map(|(start_row, block)| {
|
.map(|(block_row, block)| {
|
||||||
// let anchor_row = block
|
let mut element = match block {
|
||||||
// .position()
|
TransformBlock::Custom(block) => {
|
||||||
// .to_point(&snapshot.buffer_snapshot)
|
let align_to = block
|
||||||
// .to_display_point(snapshot)
|
.position()
|
||||||
// .row();
|
.to_point(&snapshot.buffer_snapshot)
|
||||||
|
.to_display_point(snapshot);
|
||||||
|
let anchor_x = text_x
|
||||||
|
+ if rows.contains(&align_to.row()) {
|
||||||
|
line_layouts[(align_to.row() - rows.start) as usize]
|
||||||
|
.x_for_index(align_to.column() as usize)
|
||||||
|
} else {
|
||||||
|
layout_line(align_to.row(), snapshot, style, cx.text_layout_cache)
|
||||||
|
.x_for_index(align_to.column() as usize)
|
||||||
|
};
|
||||||
|
|
||||||
// let anchor_x = text_x
|
block.render(&BlockContext {
|
||||||
// + if rows.contains(&anchor_row) {
|
cx,
|
||||||
// line_layouts[(anchor_row - rows.start) as usize]
|
anchor_x,
|
||||||
// .x_for_index(block.column() as usize)
|
gutter_padding,
|
||||||
// } else {
|
line_height,
|
||||||
// layout_line(anchor_row, snapshot, style, cx.text_layout_cache)
|
scroll_x,
|
||||||
// .x_for_index(block.column() as usize)
|
gutter_width,
|
||||||
// };
|
em_width,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
TransformBlock::ExcerptHeader { buffer, .. } => {
|
||||||
|
let style = &self.settings.style.diagnostic_path_header;
|
||||||
|
let font_size =
|
||||||
|
(style.text_scale_factor * self.settings.style.text.font_size).round();
|
||||||
|
|
||||||
// let mut element = block.render(&BlockContext {
|
let mut filename = None;
|
||||||
// cx,
|
let mut parent_path = None;
|
||||||
// anchor_x,
|
if let Some(path) = buffer.path() {
|
||||||
// gutter_padding,
|
filename = path.file_name().map(|f| f.to_string_lossy().to_string());
|
||||||
// line_height,
|
parent_path =
|
||||||
// scroll_x: snapshot.scroll_position.x(),
|
path.parent().map(|p| p.to_string_lossy().to_string() + "/");
|
||||||
// gutter_width,
|
}
|
||||||
// em_width,
|
|
||||||
// });
|
Flex::row()
|
||||||
// element.layout(
|
.with_child(
|
||||||
// SizeConstraint {
|
Label::new(
|
||||||
// min: Vector2F::zero(),
|
filename.unwrap_or_else(|| "untitled".to_string()),
|
||||||
// max: vec2f(width, block.height() as f32 * line_height),
|
style.filename.text.clone().with_font_size(font_size),
|
||||||
// },
|
)
|
||||||
// cx,
|
.contained()
|
||||||
// );
|
.with_style(style.filename.container)
|
||||||
// (start_row, element)
|
.boxed(),
|
||||||
// })
|
)
|
||||||
// .collect()
|
.with_children(parent_path.map(|path| {
|
||||||
|
Label::new(path, style.path.text.clone().with_font_size(font_size))
|
||||||
|
.contained()
|
||||||
|
.with_style(style.path.container)
|
||||||
|
.boxed()
|
||||||
|
}))
|
||||||
|
.aligned()
|
||||||
|
.left()
|
||||||
|
.contained()
|
||||||
|
.with_style(style.container)
|
||||||
|
.with_padding_left(gutter_padding + scroll_x * em_width)
|
||||||
|
.expanded()
|
||||||
|
.named("path header block")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
element.layout(
|
||||||
|
SizeConstraint {
|
||||||
|
min: Vector2F::zero(),
|
||||||
|
max: vec2f(width, block.height() as f32 * line_height),
|
||||||
|
},
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
(block_row, element)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue