Start work on generalizing the BlockMap to allow arbitrary elements
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
e668ff8bcd
commit
0c714210ff
7 changed files with 579 additions and 897 deletions
|
@ -3,13 +3,12 @@ mod fold_map;
|
||||||
mod tab_map;
|
mod tab_map;
|
||||||
mod wrap_map;
|
mod wrap_map;
|
||||||
|
|
||||||
pub use block_map::{BlockDisposition, BlockId, BlockProperties, BufferRows, Chunks};
|
pub use block_map::{
|
||||||
|
AlignedBlock, BlockContext, BlockDisposition, BlockId, BlockProperties, BufferRows, Chunks,
|
||||||
|
};
|
||||||
use block_map::{BlockMap, BlockPoint};
|
use block_map::{BlockMap, BlockPoint};
|
||||||
use fold_map::{FoldMap, ToFoldPoint as _};
|
use fold_map::{FoldMap, ToFoldPoint as _};
|
||||||
use gpui::{
|
use gpui::{fonts::FontId, ElementBox, Entity, ModelContext, ModelHandle};
|
||||||
fonts::{FontId, HighlightStyle},
|
|
||||||
AppContext, Entity, ModelContext, ModelHandle,
|
|
||||||
};
|
|
||||||
use language::{Anchor, Buffer, Point, Subscription as BufferSubscription, ToOffset, ToPoint};
|
use language::{Anchor, Buffer, Point, Subscription as BufferSubscription, ToOffset, ToPoint};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
|
@ -17,8 +16,7 @@ use std::{
|
||||||
};
|
};
|
||||||
use sum_tree::Bias;
|
use sum_tree::Bias;
|
||||||
use tab_map::TabMap;
|
use tab_map::TabMap;
|
||||||
use text::Rope;
|
use theme::SyntaxTheme;
|
||||||
use theme::{BlockStyle, SyntaxTheme};
|
|
||||||
use wrap_map::WrapMap;
|
use wrap_map::WrapMap;
|
||||||
|
|
||||||
pub trait ToDisplayPoint {
|
pub trait ToDisplayPoint {
|
||||||
|
@ -124,14 +122,13 @@ impl DisplayMap {
|
||||||
self.block_map.read(snapshot, edits, cx);
|
self.block_map.read(snapshot, edits, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_blocks<P, T>(
|
pub fn insert_blocks<P>(
|
||||||
&mut self,
|
&mut self,
|
||||||
blocks: impl IntoIterator<Item = BlockProperties<P, T>>,
|
blocks: impl IntoIterator<Item = BlockProperties<P>>,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Vec<BlockId>
|
) -> Vec<BlockId>
|
||||||
where
|
where
|
||||||
P: ToOffset + Clone,
|
P: ToOffset + Clone,
|
||||||
T: Into<Rope> + Clone,
|
|
||||||
{
|
{
|
||||||
let snapshot = self.buffer.read(cx).snapshot();
|
let snapshot = self.buffer.read(cx).snapshot();
|
||||||
let edits = self.buffer_subscription.consume().into_inner();
|
let edits = self.buffer_subscription.consume().into_inner();
|
||||||
|
@ -144,12 +141,11 @@ impl DisplayMap {
|
||||||
block_map.insert(blocks, cx)
|
block_map.insert(blocks, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn restyle_blocks<F1, F2>(&mut self, styles: HashMap<BlockId, (Option<F1>, Option<F2>)>)
|
pub fn replace_blocks<F>(&mut self, styles: HashMap<BlockId, F>)
|
||||||
where
|
where
|
||||||
F1: 'static + Fn(&AppContext) -> Vec<(usize, HighlightStyle)>,
|
F: 'static + Fn(&BlockContext) -> ElementBox,
|
||||||
F2: 'static + Fn(&AppContext) -> BlockStyle,
|
|
||||||
{
|
{
|
||||||
self.block_map.restyle(styles);
|
self.block_map.replace(styles);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_blocks(&mut self, ids: HashSet<BlockId>, cx: &mut ModelContext<Self>) {
|
pub fn remove_blocks(&mut self, ids: HashSet<BlockId>, cx: &mut ModelContext<Self>) {
|
||||||
|
@ -198,8 +194,8 @@ impl DisplayMapSnapshot {
|
||||||
self.buffer_snapshot.len() == 0
|
self.buffer_snapshot.len() == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn buffer_rows<'a>(&'a self, start_row: u32, cx: Option<&'a AppContext>) -> BufferRows<'a> {
|
pub fn buffer_rows<'a>(&'a self, start_row: u32) -> BufferRows<'a> {
|
||||||
self.blocks_snapshot.buffer_rows(start_row, cx)
|
self.blocks_snapshot.buffer_rows(start_row)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn buffer_row_count(&self) -> u32 {
|
pub fn buffer_row_count(&self) -> u32 {
|
||||||
|
@ -256,7 +252,7 @@ impl DisplayMapSnapshot {
|
||||||
|
|
||||||
pub fn text_chunks(&self, display_row: u32) -> impl Iterator<Item = &str> {
|
pub fn text_chunks(&self, display_row: u32) -> impl Iterator<Item = &str> {
|
||||||
self.blocks_snapshot
|
self.blocks_snapshot
|
||||||
.chunks(display_row..self.max_point().row() + 1, None, None)
|
.chunks(display_row..self.max_point().row() + 1, None)
|
||||||
.map(|h| h.text)
|
.map(|h| h.text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,9 +260,8 @@ impl DisplayMapSnapshot {
|
||||||
&'a self,
|
&'a self,
|
||||||
display_rows: Range<u32>,
|
display_rows: Range<u32>,
|
||||||
theme: Option<&'a SyntaxTheme>,
|
theme: Option<&'a SyntaxTheme>,
|
||||||
cx: &'a AppContext,
|
|
||||||
) -> block_map::Chunks<'a> {
|
) -> block_map::Chunks<'a> {
|
||||||
self.blocks_snapshot.chunks(display_rows, theme, Some(cx))
|
self.blocks_snapshot.chunks(display_rows, theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn chars_at<'a>(&'a self, point: DisplayPoint) -> impl Iterator<Item = char> + 'a {
|
pub fn chars_at<'a>(&'a self, point: DisplayPoint) -> impl Iterator<Item = char> + 'a {
|
||||||
|
@ -322,6 +317,13 @@ impl DisplayMapSnapshot {
|
||||||
self.folds_snapshot.folds_in_range(range)
|
self.folds_snapshot.folds_in_range(range)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn blocks_in_range<'a>(
|
||||||
|
&'a self,
|
||||||
|
rows: Range<u32>,
|
||||||
|
) -> impl Iterator<Item = (u32, &'a AlignedBlock)> {
|
||||||
|
self.blocks_snapshot.blocks_in_range(rows)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn intersects_fold<T: ToOffset>(&self, offset: T) -> bool {
|
pub fn intersects_fold<T: ToOffset>(&self, offset: T) -> bool {
|
||||||
self.folds_snapshot.intersects_fold(offset)
|
self.folds_snapshot.intersects_fold(offset)
|
||||||
}
|
}
|
||||||
|
@ -448,13 +450,6 @@ impl ToDisplayPoint for Anchor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
pub enum DisplayRow {
|
|
||||||
Buffer(u32),
|
|
||||||
Block(BlockId, Option<BlockStyle>),
|
|
||||||
Wrap,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -1065,7 +1060,7 @@ mod tests {
|
||||||
) -> Vec<(String, Option<Color>)> {
|
) -> Vec<(String, Option<Color>)> {
|
||||||
let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
|
let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
|
||||||
let mut chunks: Vec<(String, Option<Color>)> = Vec::new();
|
let mut chunks: Vec<(String, Option<Color>)> = Vec::new();
|
||||||
for chunk in snapshot.chunks(rows, Some(theme), cx) {
|
for chunk in snapshot.chunks(rows, Some(theme)) {
|
||||||
let color = chunk.highlight_style.map(|s| s.color);
|
let color = chunk.highlight_style.map(|s| s.color);
|
||||||
if let Some((last_chunk, last_color)) = chunks.last_mut() {
|
if let Some((last_chunk, last_color)) = chunks.last_mut() {
|
||||||
if color == *last_color {
|
if color == *last_color {
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,6 @@
|
||||||
use super::{
|
use super::{
|
||||||
fold_map,
|
fold_map,
|
||||||
tab_map::{self, Edit as TabEdit, Snapshot as TabSnapshot, TabPoint},
|
tab_map::{self, Edit as TabEdit, Snapshot as TabSnapshot, TabPoint},
|
||||||
DisplayRow,
|
|
||||||
};
|
};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
fonts::FontId, text_layout::LineWrapper, Entity, ModelContext, ModelHandle, MutableAppContext,
|
fonts::FontId, text_layout::LineWrapper, Entity, ModelContext, ModelHandle, MutableAppContext,
|
||||||
|
@ -607,13 +606,6 @@ impl Snapshot {
|
||||||
len as u32
|
len as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn line_char_count(&self, row: u32) -> u32 {
|
|
||||||
self.text_chunks(row)
|
|
||||||
.flat_map(|c| c.chars())
|
|
||||||
.take_while(|c| *c != '\n')
|
|
||||||
.count() as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn soft_wrap_indent(&self, row: u32) -> Option<u32> {
|
pub fn soft_wrap_indent(&self, row: u32) -> Option<u32> {
|
||||||
let mut cursor = self.transforms.cursor::<WrapPoint>();
|
let mut cursor = self.transforms.cursor::<WrapPoint>();
|
||||||
cursor.seek(&WrapPoint::new(row + 1, 0), Bias::Right, &());
|
cursor.seek(&WrapPoint::new(row + 1, 0), Bias::Right, &());
|
||||||
|
@ -719,11 +711,7 @@ impl Snapshot {
|
||||||
prev_tab_row = tab_point.row();
|
prev_tab_row = tab_point.row();
|
||||||
soft_wrapped = false;
|
soft_wrapped = false;
|
||||||
}
|
}
|
||||||
expected_buffer_rows.push(if soft_wrapped {
|
expected_buffer_rows.push(if soft_wrapped { None } else { Some(buffer_row) });
|
||||||
DisplayRow::Wrap
|
|
||||||
} else {
|
|
||||||
DisplayRow::Buffer(buffer_row)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for start_display_row in 0..expected_buffer_rows.len() {
|
for start_display_row in 0..expected_buffer_rows.len() {
|
||||||
|
@ -803,7 +791,7 @@ impl<'a> Iterator for Chunks<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for BufferRows<'a> {
|
impl<'a> Iterator for BufferRows<'a> {
|
||||||
type Item = DisplayRow;
|
type Item = Option<u32>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if self.output_row > self.max_output_row {
|
if self.output_row > self.max_output_row {
|
||||||
|
@ -823,11 +811,7 @@ impl<'a> Iterator for BufferRows<'a> {
|
||||||
self.soft_wrapped = true;
|
self.soft_wrapped = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(if soft_wrapped {
|
Some(if soft_wrapped { None } else { Some(buffer_row) })
|
||||||
DisplayRow::Wrap
|
|
||||||
} else {
|
|
||||||
DisplayRow::Buffer(buffer_row)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,12 @@ mod test;
|
||||||
|
|
||||||
use aho_corasick::AhoCorasick;
|
use aho_corasick::AhoCorasick;
|
||||||
use clock::ReplicaId;
|
use clock::ReplicaId;
|
||||||
|
pub use display_map::DisplayPoint;
|
||||||
use display_map::*;
|
use display_map::*;
|
||||||
pub use display_map::{DisplayPoint, DisplayRow};
|
|
||||||
pub use element::*;
|
pub use element::*;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
action,
|
action,
|
||||||
|
elements::Text,
|
||||||
geometry::vector::{vec2f, Vector2F},
|
geometry::vector::{vec2f, Vector2F},
|
||||||
keymap::Binding,
|
keymap::Binding,
|
||||||
text_layout, AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle,
|
text_layout, AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle,
|
||||||
|
@ -28,14 +29,14 @@ use std::{
|
||||||
cmp,
|
cmp,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
iter, mem,
|
iter, mem,
|
||||||
ops::{Range, RangeInclusive},
|
ops::{Deref, Range, RangeInclusive},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use sum_tree::Bias;
|
use sum_tree::Bias;
|
||||||
use text::rope::TextDimension;
|
use text::rope::TextDimension;
|
||||||
use theme::{DiagnosticStyle, EditorStyle, SyntaxTheme};
|
use theme::{DiagnosticStyle, EditorStyle};
|
||||||
use util::post_inc;
|
use util::post_inc;
|
||||||
use workspace::{EntryOpener, Workspace};
|
use workspace::{EntryOpener, Workspace};
|
||||||
|
|
||||||
|
@ -2877,35 +2878,16 @@ impl Editor {
|
||||||
active_diagnostics.is_valid = is_valid;
|
active_diagnostics.is_valid = is_valid;
|
||||||
let mut new_styles = HashMap::new();
|
let mut new_styles = HashMap::new();
|
||||||
for (block_id, diagnostic) in &active_diagnostics.blocks {
|
for (block_id, diagnostic) in &active_diagnostics.blocks {
|
||||||
let severity = diagnostic.severity;
|
|
||||||
let message_len = diagnostic.message.len();
|
|
||||||
new_styles.insert(
|
|
||||||
*block_id,
|
|
||||||
(
|
|
||||||
Some({
|
|
||||||
let build_settings = self.build_settings.clone();
|
let build_settings = self.build_settings.clone();
|
||||||
move |cx: &AppContext| {
|
let diagnostic = diagnostic.clone();
|
||||||
let settings = build_settings.borrow()(cx);
|
new_styles.insert(*block_id, move |cx: &BlockContext| {
|
||||||
vec![(
|
let diagnostic = diagnostic.clone();
|
||||||
message_len,
|
let settings = build_settings.borrow()(cx.cx);
|
||||||
diagnostic_style(severity, is_valid, &settings.style)
|
render_diagnostic(diagnostic, &settings.style)
|
||||||
.text
|
});
|
||||||
.into(),
|
|
||||||
)]
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
Some({
|
|
||||||
let build_settings = self.build_settings.clone();
|
|
||||||
move |cx: &AppContext| {
|
|
||||||
let settings = build_settings.borrow()(cx);
|
|
||||||
diagnostic_style(severity, is_valid, &settings.style).block
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
self.display_map
|
self.display_map
|
||||||
.update(cx, |display_map, _| display_map.restyle_blocks(new_styles));
|
.update(cx, |display_map, _| display_map.replace_blocks(new_styles));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2940,30 +2922,17 @@ impl Editor {
|
||||||
.insert_blocks(
|
.insert_blocks(
|
||||||
diagnostic_group.iter().map(|(range, diagnostic)| {
|
diagnostic_group.iter().map(|(range, diagnostic)| {
|
||||||
let build_settings = self.build_settings.clone();
|
let build_settings = self.build_settings.clone();
|
||||||
let message_len = diagnostic.message.len();
|
let diagnostic = diagnostic.clone();
|
||||||
let severity = diagnostic.severity;
|
let message_height = diagnostic.message.lines().count() as u8;
|
||||||
|
|
||||||
BlockProperties {
|
BlockProperties {
|
||||||
position: range.start,
|
position: range.start,
|
||||||
text: diagnostic.message.as_str(),
|
height: message_height,
|
||||||
build_runs: Some(Arc::new({
|
render: Arc::new(move |cx| {
|
||||||
let build_settings = build_settings.clone();
|
let settings = build_settings.borrow()(cx.cx);
|
||||||
move |cx| {
|
let diagnostic = diagnostic.clone();
|
||||||
let settings = build_settings.borrow()(cx);
|
render_diagnostic(diagnostic, &settings.style)
|
||||||
vec![(
|
}),
|
||||||
message_len,
|
|
||||||
diagnostic_style(severity, true, &settings.style)
|
|
||||||
.text
|
|
||||||
.into(),
|
|
||||||
)]
|
|
||||||
}
|
|
||||||
})),
|
|
||||||
build_style: Some(Arc::new({
|
|
||||||
let build_settings = build_settings.clone();
|
|
||||||
move |cx| {
|
|
||||||
let settings = build_settings.borrow()(cx);
|
|
||||||
diagnostic_style(severity, true, &settings.style).block
|
|
||||||
}
|
|
||||||
})),
|
|
||||||
disposition: BlockDisposition::Below,
|
disposition: BlockDisposition::Below,
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
@ -3482,10 +3451,6 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Snapshot {
|
impl Snapshot {
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.display_snapshot.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_focused(&self) -> bool {
|
pub fn is_focused(&self) -> bool {
|
||||||
self.is_focused
|
self.is_focused
|
||||||
}
|
}
|
||||||
|
@ -3494,23 +3459,6 @@ impl Snapshot {
|
||||||
self.placeholder_text.as_ref()
|
self.placeholder_text.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn buffer_row_count(&self) -> u32 {
|
|
||||||
self.display_snapshot.buffer_row_count()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn buffer_rows<'a>(&'a self, start_row: u32, cx: &'a AppContext) -> BufferRows<'a> {
|
|
||||||
self.display_snapshot.buffer_rows(start_row, Some(cx))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn chunks<'a>(
|
|
||||||
&'a self,
|
|
||||||
display_rows: Range<u32>,
|
|
||||||
theme: Option<&'a SyntaxTheme>,
|
|
||||||
cx: &'a AppContext,
|
|
||||||
) -> display_map::Chunks<'a> {
|
|
||||||
self.display_snapshot.chunks(display_rows, theme, cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn scroll_position(&self) -> Vector2F {
|
pub fn scroll_position(&self) -> Vector2F {
|
||||||
compute_scroll_position(
|
compute_scroll_position(
|
||||||
&self.display_snapshot,
|
&self.display_snapshot,
|
||||||
|
@ -3518,29 +3466,13 @@ impl Snapshot {
|
||||||
&self.scroll_top_anchor,
|
&self.scroll_top_anchor,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn max_point(&self) -> DisplayPoint {
|
impl Deref for Snapshot {
|
||||||
self.display_snapshot.max_point()
|
type Target = DisplayMapSnapshot;
|
||||||
}
|
|
||||||
|
|
||||||
pub fn longest_row(&self) -> u32 {
|
fn deref(&self) -> &Self::Target {
|
||||||
self.display_snapshot.longest_row()
|
&self.display_snapshot
|
||||||
}
|
|
||||||
|
|
||||||
pub fn line_len(&self, display_row: u32) -> u32 {
|
|
||||||
self.display_snapshot.line_len(display_row)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn line(&self, display_row: u32) -> String {
|
|
||||||
self.display_snapshot.line(display_row)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn prev_row_boundary(&self, point: DisplayPoint) -> (DisplayPoint, Point) {
|
|
||||||
self.display_snapshot.prev_row_boundary(point)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next_row_boundary(&self, point: DisplayPoint) -> (DisplayPoint, Point) {
|
|
||||||
self.display_snapshot.next_row_boundary(point)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3709,6 +3641,12 @@ impl SelectionExt for Selection<Point> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_diagnostic(diagnostic: Diagnostic, style: &EditorStyle) -> ElementBox {
|
||||||
|
let mut text_style = style.text.clone();
|
||||||
|
text_style.color = diagnostic_style(diagnostic.severity, true, &style).text;
|
||||||
|
Text::new(diagnostic.message, text_style).boxed()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn diagnostic_style(
|
pub fn diagnostic_style(
|
||||||
severity: DiagnosticSeverity,
|
severity: DiagnosticSeverity,
|
||||||
valid: bool,
|
valid: bool,
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
use crate::display_map::BlockContext;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
DisplayPoint, DisplayRow, Editor, EditorMode, EditorSettings, EditorStyle, Input, Scroll,
|
DisplayPoint, Editor, EditorMode, EditorSettings, EditorStyle, Input, Scroll, Select,
|
||||||
Select, SelectPhase, Snapshot, SoftWrap, MAX_LINE_LEN,
|
SelectPhase, Snapshot, SoftWrap, MAX_LINE_LEN,
|
||||||
};
|
};
|
||||||
use clock::ReplicaId;
|
use clock::ReplicaId;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
@ -13,7 +15,7 @@ use gpui::{
|
||||||
json::{self, ToJson},
|
json::{self, ToJson},
|
||||||
keymap::Keystroke,
|
keymap::Keystroke,
|
||||||
text_layout::{self, RunStyle, TextLayoutCache},
|
text_layout::{self, RunStyle, TextLayoutCache},
|
||||||
AppContext, Axis, Border, Element, Event, EventContext, FontCache, LayoutContext,
|
AppContext, Axis, Border, Element, ElementBox, Event, EventContext, FontCache, LayoutContext,
|
||||||
MutableAppContext, PaintContext, Quad, Scene, SizeConstraint, ViewContext, WeakViewHandle,
|
MutableAppContext, PaintContext, Quad, Scene, SizeConstraint, ViewContext, WeakViewHandle,
|
||||||
};
|
};
|
||||||
use json::json;
|
use json::json;
|
||||||
|
@ -25,7 +27,6 @@ use std::{
|
||||||
fmt::Write,
|
fmt::Write,
|
||||||
ops::Range,
|
ops::Range,
|
||||||
};
|
};
|
||||||
use theme::BlockStyle;
|
|
||||||
|
|
||||||
pub struct EditorElement {
|
pub struct EditorElement {
|
||||||
view: WeakViewHandle<Editor>,
|
view: WeakViewHandle<Editor>,
|
||||||
|
@ -278,51 +279,6 @@ impl EditorElement {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw block backgrounds
|
|
||||||
for (ixs, block_style) in &layout.block_layouts {
|
|
||||||
let row = start_row + ixs.start;
|
|
||||||
let offset = vec2f(0., row as f32 * layout.line_height - scroll_top);
|
|
||||||
let height = ixs.len() as f32 * layout.line_height;
|
|
||||||
cx.scene.push_quad(Quad {
|
|
||||||
bounds: RectF::new(
|
|
||||||
text_bounds.origin() + offset,
|
|
||||||
vec2f(text_bounds.width(), height),
|
|
||||||
),
|
|
||||||
background: block_style.background,
|
|
||||||
border: block_style
|
|
||||||
.border
|
|
||||||
.map_or(Default::default(), |color| Border {
|
|
||||||
width: 1.,
|
|
||||||
color,
|
|
||||||
overlay: true,
|
|
||||||
top: true,
|
|
||||||
right: false,
|
|
||||||
bottom: true,
|
|
||||||
left: false,
|
|
||||||
}),
|
|
||||||
corner_radius: 0.,
|
|
||||||
});
|
|
||||||
cx.scene.push_quad(Quad {
|
|
||||||
bounds: RectF::new(
|
|
||||||
gutter_bounds.origin() + offset,
|
|
||||||
vec2f(gutter_bounds.width(), height),
|
|
||||||
),
|
|
||||||
background: block_style.gutter_background,
|
|
||||||
border: block_style
|
|
||||||
.gutter_border
|
|
||||||
.map_or(Default::default(), |color| Border {
|
|
||||||
width: 1.,
|
|
||||||
color,
|
|
||||||
overlay: true,
|
|
||||||
top: true,
|
|
||||||
right: false,
|
|
||||||
bottom: true,
|
|
||||||
left: false,
|
|
||||||
}),
|
|
||||||
corner_radius: 0.,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint_gutter(
|
fn paint_gutter(
|
||||||
|
@ -461,6 +417,18 @@ impl EditorElement {
|
||||||
cx.scene.pop_layer();
|
cx.scene.pop_layer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn paint_blocks(
|
||||||
|
&mut self,
|
||||||
|
bounds: RectF,
|
||||||
|
visible_bounds: RectF,
|
||||||
|
layout: &LayoutState,
|
||||||
|
cx: &mut PaintContext,
|
||||||
|
) {
|
||||||
|
for (row_range, block) in &layout.blocks {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn max_line_number_width(&self, snapshot: &Snapshot, cx: &LayoutContext) -> f32 {
|
fn max_line_number_width(&self, snapshot: &Snapshot, cx: &LayoutContext) -> f32 {
|
||||||
let digit_count = (snapshot.buffer_row_count() as f32).log10().floor() as usize + 1;
|
let digit_count = (snapshot.buffer_row_count() as f32).log10().floor() as usize + 1;
|
||||||
let style = &self.settings.style;
|
let style = &self.settings.style;
|
||||||
|
@ -487,18 +455,13 @@ impl EditorElement {
|
||||||
active_rows: &BTreeMap<u32, bool>,
|
active_rows: &BTreeMap<u32, bool>,
|
||||||
snapshot: &Snapshot,
|
snapshot: &Snapshot,
|
||||||
cx: &LayoutContext,
|
cx: &LayoutContext,
|
||||||
) -> (
|
) -> Vec<Option<text_layout::Line>> {
|
||||||
Vec<Option<text_layout::Line>>,
|
|
||||||
Vec<(Range<u32>, BlockStyle)>,
|
|
||||||
) {
|
|
||||||
let style = &self.settings.style;
|
let style = &self.settings.style;
|
||||||
let include_line_numbers = snapshot.mode == EditorMode::Full;
|
let include_line_numbers = snapshot.mode == EditorMode::Full;
|
||||||
let mut last_block_id = None;
|
|
||||||
let mut blocks = Vec::<(Range<u32>, BlockStyle)>::new();
|
|
||||||
let mut line_number_layouts = Vec::with_capacity(rows.len());
|
let mut line_number_layouts = Vec::with_capacity(rows.len());
|
||||||
let mut line_number = String::new();
|
let mut line_number = String::new();
|
||||||
for (ix, row) in snapshot
|
for (ix, row) in snapshot
|
||||||
.buffer_rows(rows.start, cx)
|
.buffer_rows(rows.start)
|
||||||
.take((rows.end - rows.start) as usize)
|
.take((rows.end - rows.start) as usize)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
{
|
{
|
||||||
|
@ -508,8 +471,7 @@ impl EditorElement {
|
||||||
} else {
|
} else {
|
||||||
style.line_number
|
style.line_number
|
||||||
};
|
};
|
||||||
match row {
|
if let Some(buffer_row) = row {
|
||||||
DisplayRow::Buffer(buffer_row) => {
|
|
||||||
if include_line_numbers {
|
if include_line_numbers {
|
||||||
line_number.clear();
|
line_number.clear();
|
||||||
write!(&mut line_number, "{}", buffer_row + 1).unwrap();
|
write!(&mut line_number, "{}", buffer_row + 1).unwrap();
|
||||||
|
@ -526,28 +488,12 @@ impl EditorElement {
|
||||||
)],
|
)],
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
last_block_id = None;
|
} else {
|
||||||
}
|
|
||||||
DisplayRow::Block(block_id, style) => {
|
|
||||||
let ix = ix as u32;
|
|
||||||
if last_block_id == Some(block_id) {
|
|
||||||
if let Some((row_range, _)) = blocks.last_mut() {
|
|
||||||
row_range.end += 1;
|
|
||||||
}
|
|
||||||
} else if let Some(style) = style {
|
|
||||||
blocks.push((ix..ix + 1, style));
|
|
||||||
}
|
|
||||||
line_number_layouts.push(None);
|
line_number_layouts.push(None);
|
||||||
last_block_id = Some(block_id);
|
|
||||||
}
|
|
||||||
DisplayRow::Wrap => {
|
|
||||||
line_number_layouts.push(None);
|
|
||||||
last_block_id = None;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(line_number_layouts, blocks)
|
line_number_layouts
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout_lines(
|
fn layout_lines(
|
||||||
|
@ -598,7 +544,7 @@ impl EditorElement {
|
||||||
let mut styles = Vec::new();
|
let mut styles = Vec::new();
|
||||||
let mut row = rows.start;
|
let mut row = rows.start;
|
||||||
let mut line_exceeded_max_len = false;
|
let mut line_exceeded_max_len = false;
|
||||||
let chunks = snapshot.chunks(rows.clone(), Some(&style.syntax), cx);
|
let chunks = snapshot.chunks(rows.clone(), Some(&style.syntax));
|
||||||
|
|
||||||
let newline_chunk = Chunk {
|
let newline_chunk = Chunk {
|
||||||
text: "\n",
|
text: "\n",
|
||||||
|
@ -668,6 +614,27 @@ impl EditorElement {
|
||||||
|
|
||||||
layouts
|
layouts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn layout_blocks(
|
||||||
|
&mut self,
|
||||||
|
rows: Range<u32>,
|
||||||
|
snapshot: &Snapshot,
|
||||||
|
cx: &LayoutContext,
|
||||||
|
) -> Vec<(Range<u32>, ElementBox)> {
|
||||||
|
snapshot
|
||||||
|
.blocks_in_range(rows)
|
||||||
|
.map(|(start_row, block)| {
|
||||||
|
(
|
||||||
|
start_row..start_row + block.height(),
|
||||||
|
block.render(&BlockContext {
|
||||||
|
cx,
|
||||||
|
gutter_width: 0.0,
|
||||||
|
anchor_x: 0.0,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for EditorElement {
|
impl Element for EditorElement {
|
||||||
|
@ -773,8 +740,7 @@ impl Element for EditorElement {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let (line_number_layouts, block_layouts) =
|
let line_number_layouts = self.layout_rows(start_row..end_row, &active_rows, &snapshot, cx);
|
||||||
self.layout_rows(start_row..end_row, &active_rows, &snapshot, cx);
|
|
||||||
|
|
||||||
let mut max_visible_line_width = 0.0;
|
let mut max_visible_line_width = 0.0;
|
||||||
let line_layouts = self.layout_lines(start_row..end_row, &mut snapshot, cx);
|
let line_layouts = self.layout_lines(start_row..end_row, &mut snapshot, cx);
|
||||||
|
@ -784,6 +750,8 @@ impl Element for EditorElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let blocks = self.layout_blocks(start_row..end_row, &snapshot, cx);
|
||||||
|
|
||||||
let mut layout = LayoutState {
|
let mut layout = LayoutState {
|
||||||
size,
|
size,
|
||||||
gutter_size,
|
gutter_size,
|
||||||
|
@ -797,7 +765,7 @@ impl Element for EditorElement {
|
||||||
highlighted_row,
|
highlighted_row,
|
||||||
line_layouts,
|
line_layouts,
|
||||||
line_number_layouts,
|
line_number_layouts,
|
||||||
block_layouts,
|
blocks,
|
||||||
line_height,
|
line_height,
|
||||||
em_width,
|
em_width,
|
||||||
em_advance,
|
em_advance,
|
||||||
|
@ -853,6 +821,7 @@ impl Element for EditorElement {
|
||||||
self.paint_gutter(gutter_bounds, visible_bounds, layout, cx);
|
self.paint_gutter(gutter_bounds, visible_bounds, layout, cx);
|
||||||
}
|
}
|
||||||
self.paint_text(text_bounds, visible_bounds, layout, cx);
|
self.paint_text(text_bounds, visible_bounds, layout, cx);
|
||||||
|
self.paint_blocks(text_bounds, visible_bounds, layout, cx);
|
||||||
|
|
||||||
cx.scene.pop_layer();
|
cx.scene.pop_layer();
|
||||||
|
|
||||||
|
@ -927,7 +896,7 @@ pub struct LayoutState {
|
||||||
highlighted_row: Option<u32>,
|
highlighted_row: Option<u32>,
|
||||||
line_layouts: Vec<text_layout::Line>,
|
line_layouts: Vec<text_layout::Line>,
|
||||||
line_number_layouts: Vec<Option<text_layout::Line>>,
|
line_number_layouts: Vec<Option<text_layout::Line>>,
|
||||||
block_layouts: Vec<(Range<u32>, BlockStyle)>,
|
blocks: Vec<(Range<u32>, ElementBox)>,
|
||||||
line_height: f32,
|
line_height: f32,
|
||||||
em_width: f32,
|
em_width: f32,
|
||||||
em_advance: f32,
|
em_advance: f32,
|
||||||
|
@ -1185,7 +1154,7 @@ mod tests {
|
||||||
});
|
});
|
||||||
let element = EditorElement::new(editor.downgrade(), settings);
|
let element = EditorElement::new(editor.downgrade(), settings);
|
||||||
|
|
||||||
let (layouts, _) = editor.update(cx, |editor, cx| {
|
let layouts = editor.update(cx, |editor, cx| {
|
||||||
let snapshot = editor.snapshot(cx);
|
let snapshot = editor.snapshot(cx);
|
||||||
let mut presenter = cx.build_presenter(window_id, 30.);
|
let mut presenter = cx.build_presenter(window_id, 30.);
|
||||||
let mut layout_cx = presenter.build_layout_context(false, cx);
|
let mut layout_cx = presenter.build_layout_context(false, cx);
|
||||||
|
|
|
@ -301,6 +301,10 @@ impl<T: Element> Default for Lifecycle<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementBox {
|
impl ElementBox {
|
||||||
|
pub fn name(&self) -> Option<&str> {
|
||||||
|
self.0.name.as_deref()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn metadata<T: 'static>(&self) -> Option<&T> {
|
pub fn metadata<T: 'static>(&self) -> Option<&T> {
|
||||||
let element = unsafe { &*self.0.element.as_ptr() };
|
let element = unsafe { &*self.0.element.as_ptr() };
|
||||||
element.metadata().and_then(|m| m.downcast_ref())
|
element.metadata().and_then(|m| m.downcast_ref())
|
||||||
|
|
|
@ -253,8 +253,6 @@ pub struct EditorStyle {
|
||||||
#[derive(Copy, Clone, Deserialize, Default)]
|
#[derive(Copy, Clone, Deserialize, Default)]
|
||||||
pub struct DiagnosticStyle {
|
pub struct DiagnosticStyle {
|
||||||
pub text: Color,
|
pub text: Color,
|
||||||
#[serde(flatten)]
|
|
||||||
pub block: BlockStyle,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default, Deserialize)]
|
#[derive(Clone, Copy, Default, Deserialize)]
|
||||||
|
@ -273,14 +271,6 @@ pub struct InputEditorStyle {
|
||||||
pub selection: SelectionStyle,
|
pub selection: SelectionStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq)]
|
|
||||||
pub struct BlockStyle {
|
|
||||||
pub background: Option<Color>,
|
|
||||||
pub border: Option<Color>,
|
|
||||||
pub gutter_background: Option<Color>,
|
|
||||||
pub gutter_border: Option<Color>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EditorStyle {
|
impl EditorStyle {
|
||||||
pub fn placeholder_text(&self) -> &TextStyle {
|
pub fn placeholder_text(&self) -> &TextStyle {
|
||||||
self.placeholder_text.as_ref().unwrap_or(&self.text)
|
self.placeholder_text.as_ref().unwrap_or(&self.text)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue