Draft invisibles' tabs display

This commit is contained in:
Kirill Bulatov 2023-05-04 13:53:33 +03:00 committed by Kirill Bulatov
parent 4f8607039c
commit ad731ea6d2
3 changed files with 75 additions and 11 deletions

View file

@ -1359,7 +1359,11 @@ impl EditorElement {
highlight_style = Some(diagnostic_highlight); highlight_style = Some(diagnostic_highlight);
} }
(chunk.text, highlight_style) HighlightedChunk {
chunk: chunk.text,
style: highlight_style,
is_tab: chunk.is_tab,
}
}); });
layout_highlighted_chunks( layout_highlighted_chunks(
chunks, chunks,

View file

@ -6,7 +6,7 @@ use crate::{
vector::{vec2f, Vector2F}, vector::{vec2f, Vector2F},
}, },
json::{ToJson, Value}, json::{ToJson, Value},
text_layout::{Line, RunStyle, ShapedBoundary}, text_layout::{Invisible, Line, RunStyle, ShapedBoundary},
AppContext, Element, FontCache, LayoutContext, SceneBuilder, SizeConstraint, TextLayoutCache, AppContext, Element, FontCache, LayoutContext, SceneBuilder, SizeConstraint, TextLayoutCache,
View, ViewContext, View, ViewContext,
}; };
@ -114,7 +114,11 @@ impl<V: View> Element<V> for Text {
} else { } else {
result = None; result = None;
} }
result result.map(|(chunk, style)| HighlightedChunk {
chunk,
style,
is_tab: false,
})
}); });
// Perform shaping on these highlighted chunks // Perform shaping on these highlighted chunks
@ -337,9 +341,25 @@ impl<V: View> Element<V> for Text {
} }
} }
pub struct HighlightedChunk<'a> {
pub chunk: &'a str,
pub style: Option<HighlightStyle>,
pub is_tab: bool,
}
impl<'a> HighlightedChunk<'a> {
fn plain_str(str_symbols: &'a str) -> Self {
Self {
chunk: str_symbols,
style: None,
is_tab: str_symbols == "\t",
}
}
}
/// Perform text layout on a series of highlighted chunks of text. /// Perform text layout on a series of highlighted chunks of text.
pub fn layout_highlighted_chunks<'a>( pub fn layout_highlighted_chunks<'a>(
chunks: impl Iterator<Item = (&'a str, Option<HighlightStyle>)>, chunks: impl Iterator<Item = HighlightedChunk<'a>>,
text_style: &TextStyle, text_style: &TextStyle,
text_layout_cache: &TextLayoutCache, text_layout_cache: &TextLayoutCache,
font_cache: &Arc<FontCache>, font_cache: &Arc<FontCache>,
@ -348,13 +368,17 @@ pub fn layout_highlighted_chunks<'a>(
) -> Vec<Line> { ) -> Vec<Line> {
let mut layouts = Vec::with_capacity(max_line_count); let mut layouts = Vec::with_capacity(max_line_count);
let mut line = String::new(); let mut line = String::new();
let mut invisibles = Vec::new();
let mut styles = Vec::new(); let mut styles = Vec::new();
let mut row = 0; let mut row = 0;
let mut line_exceeded_max_len = false; let mut line_exceeded_max_len = false;
for (chunk, highlight_style) in chunks.chain([("\n", Default::default())]) { for highlighted_chunk in chunks.chain(std::iter::once(HighlightedChunk::plain_str("\n"))) {
for (ix, mut line_chunk) in chunk.split('\n').enumerate() { for (ix, mut line_chunk) in highlighted_chunk.chunk.split('\n').enumerate() {
if ix > 0 { if ix > 0 {
layouts.push(text_layout_cache.layout_str(&line, text_style.font_size, &styles)); let mut laid_out_line =
text_layout_cache.layout_str(&line, text_style.font_size, &styles);
laid_out_line.invisibles.extend(invisibles.drain(..));
layouts.push(laid_out_line);
line.clear(); line.clear();
styles.clear(); styles.clear();
row += 1; row += 1;
@ -365,7 +389,7 @@ pub fn layout_highlighted_chunks<'a>(
} }
if !line_chunk.is_empty() && !line_exceeded_max_len { if !line_chunk.is_empty() && !line_exceeded_max_len {
let text_style = if let Some(style) = highlight_style { let text_style = if let Some(style) = highlighted_chunk.style {
text_style text_style
.clone() .clone()
.highlight(style, font_cache) .highlight(style, font_cache)
@ -384,7 +408,6 @@ pub fn layout_highlighted_chunks<'a>(
line_exceeded_max_len = true; line_exceeded_max_len = true;
} }
line.push_str(line_chunk);
styles.push(( styles.push((
line_chunk.len(), line_chunk.len(),
RunStyle { RunStyle {
@ -393,6 +416,12 @@ pub fn layout_highlighted_chunks<'a>(
underline: text_style.underline, underline: text_style.underline,
}, },
)); ));
if highlighted_chunk.is_tab {
invisibles.push(Invisible::Tab {
range: line.len()..line.len() + line_chunk.len(),
});
}
line.push_str(line_chunk);
} }
} }
} }

View file

@ -11,6 +11,7 @@ use crate::{
window::WindowContext, window::WindowContext,
SceneBuilder, SceneBuilder,
}; };
use itertools::Itertools;
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard}; use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
use smallvec::SmallVec; use smallvec::SmallVec;
@ -178,6 +179,7 @@ impl<'a> Hash for CacheKeyRef<'a> {
pub struct Line { pub struct Line {
layout: Arc<LineLayout>, layout: Arc<LineLayout>,
style_runs: SmallVec<[StyleRun; 32]>, style_runs: SmallVec<[StyleRun; 32]>,
pub invisibles: SmallVec<[Invisible; 32]>,
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -211,6 +213,12 @@ pub struct Glyph {
pub is_emoji: bool, pub is_emoji: bool,
} }
#[derive(Debug, Clone)]
pub enum Invisible {
Tab { range: std::ops::Range<usize> },
Whitespace { range: std::ops::Range<usize> },
}
impl Line { impl Line {
fn new(layout: Arc<LineLayout>, runs: &[(usize, RunStyle)]) -> Self { fn new(layout: Arc<LineLayout>, runs: &[(usize, RunStyle)]) -> Self {
let mut style_runs = SmallVec::new(); let mut style_runs = SmallVec::new();
@ -221,7 +229,11 @@ impl Line {
underline: style.underline, underline: style.underline,
}); });
} }
Self { layout, style_runs } Self {
layout,
style_runs,
invisibles: SmallVec::new(),
}
} }
pub fn runs(&self) -> &[Run] { pub fn runs(&self) -> &[Run] {
@ -298,6 +310,16 @@ impl Line {
let mut color = Color::black(); let mut color = Color::black();
let mut underline = None; let mut underline = None;
let tab_ranges = self
.invisibles
.iter()
.filter_map(|invisible| match invisible {
Invisible::Tab { range } => Some(range),
Invisible::Whitespace { .. } => None,
})
.sorted_by(|tab_range_1, tab_range_2| tab_range_1.start.cmp(&tab_range_2.start))
.collect::<Vec<_>>();
for run in &self.layout.runs { for run in &self.layout.runs {
let max_glyph_width = cx let max_glyph_width = cx
.font_cache .font_cache
@ -364,10 +386,19 @@ impl Line {
origin: glyph_origin, origin: glyph_origin,
}); });
} else { } else {
let id = if tab_ranges.iter().any(|tab_range| {
tab_range.start <= glyph.index && glyph.index < tab_range.end
}) {
// TODO kb get a proper (cached) glyph
glyph.id + 100
} else {
glyph.id
};
scene.push_glyph(scene::Glyph { scene.push_glyph(scene::Glyph {
font_id: run.font_id, font_id: run.font_id,
font_size: self.layout.font_size, font_size: self.layout.font_size,
id: glyph.id, id,
origin: glyph_origin, origin: glyph_origin,
color, color,
}); });