Support highlighting in blocks
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
0f1eb3dd2e
commit
0a6293bcda
10 changed files with 105 additions and 87 deletions
|
@ -12,6 +12,7 @@ use language::{Anchor, Buffer, Point, ToOffset, ToPoint};
|
||||||
use std::{collections::HashSet, ops::Range};
|
use std::{collections::HashSet, ops::Range};
|
||||||
use sum_tree::Bias;
|
use sum_tree::Bias;
|
||||||
use tab_map::TabMap;
|
use tab_map::TabMap;
|
||||||
|
use theme::SyntaxTheme;
|
||||||
use wrap_map::WrapMap;
|
use wrap_map::WrapMap;
|
||||||
|
|
||||||
pub use block_map::{BlockDisposition, BlockProperties, BufferRows, Chunks};
|
pub use block_map::{BlockDisposition, BlockProperties, BufferRows, Chunks};
|
||||||
|
@ -230,12 +231,16 @@ 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, false)
|
.chunks(display_row..self.max_point().row() + 1, None)
|
||||||
.map(|h| h.text)
|
.map(|h| h.text)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn chunks(&mut self, display_rows: Range<u32>) -> block_map::Chunks {
|
pub fn chunks<'a>(
|
||||||
self.blocks_snapshot.chunks(display_rows, true)
|
&'a self,
|
||||||
|
display_rows: Range<u32>,
|
||||||
|
theme: Option<&'a SyntaxTheme>,
|
||||||
|
) -> block_map::Chunks<'a> {
|
||||||
|
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 {
|
||||||
|
@ -736,8 +741,8 @@ mod tests {
|
||||||
.unindent();
|
.unindent();
|
||||||
|
|
||||||
let theme = SyntaxTheme::new(vec![
|
let theme = SyntaxTheme::new(vec![
|
||||||
("mod.body".to_string(), Color::from_u32(0xff0000ff).into()),
|
("mod.body".to_string(), Color::red().into()),
|
||||||
("fn.name".to_string(), Color::from_u32(0x00ff00ff).into()),
|
("fn.name".to_string(), Color::blue().into()),
|
||||||
]);
|
]);
|
||||||
let lang = Arc::new(
|
let lang = Arc::new(
|
||||||
Language::new(
|
Language::new(
|
||||||
|
@ -776,19 +781,19 @@ mod tests {
|
||||||
cx.update(|cx| chunks(0..5, &map, &theme, cx)),
|
cx.update(|cx| chunks(0..5, &map, &theme, cx)),
|
||||||
vec![
|
vec![
|
||||||
("fn ".to_string(), None),
|
("fn ".to_string(), None),
|
||||||
("outer".to_string(), Some("fn.name")),
|
("outer".to_string(), Some(Color::blue())),
|
||||||
("() {}\n\nmod module ".to_string(), None),
|
("() {}\n\nmod module ".to_string(), None),
|
||||||
("{\n fn ".to_string(), Some("mod.body")),
|
("{\n fn ".to_string(), Some(Color::red())),
|
||||||
("inner".to_string(), Some("fn.name")),
|
("inner".to_string(), Some(Color::blue())),
|
||||||
("() {}\n}".to_string(), Some("mod.body")),
|
("() {}\n}".to_string(), Some(Color::red())),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cx.update(|cx| chunks(3..5, &map, &theme, cx)),
|
cx.update(|cx| chunks(3..5, &map, &theme, cx)),
|
||||||
vec![
|
vec![
|
||||||
(" fn ".to_string(), Some("mod.body")),
|
(" fn ".to_string(), Some(Color::red())),
|
||||||
("inner".to_string(), Some("fn.name")),
|
("inner".to_string(), Some(Color::blue())),
|
||||||
("() {}\n}".to_string(), Some("mod.body")),
|
("() {}\n}".to_string(), Some(Color::red())),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -799,11 +804,11 @@ mod tests {
|
||||||
cx.update(|cx| chunks(0..2, &map, &theme, cx)),
|
cx.update(|cx| chunks(0..2, &map, &theme, cx)),
|
||||||
vec![
|
vec![
|
||||||
("fn ".to_string(), None),
|
("fn ".to_string(), None),
|
||||||
("out".to_string(), Some("fn.name")),
|
("out".to_string(), Some(Color::blue())),
|
||||||
("…".to_string(), None),
|
("…".to_string(), None),
|
||||||
(" fn ".to_string(), Some("mod.body")),
|
(" fn ".to_string(), Some(Color::red())),
|
||||||
("inner".to_string(), Some("fn.name")),
|
("inner".to_string(), Some(Color::blue())),
|
||||||
("() {}\n}".to_string(), Some("mod.body")),
|
("() {}\n}".to_string(), Some(Color::red())),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -823,8 +828,8 @@ mod tests {
|
||||||
.unindent();
|
.unindent();
|
||||||
|
|
||||||
let theme = SyntaxTheme::new(vec![
|
let theme = SyntaxTheme::new(vec![
|
||||||
("mod.body".to_string(), Color::from_u32(0xff0000ff).into()),
|
("mod.body".to_string(), Color::red().into()),
|
||||||
("fn.name".to_string(), Color::from_u32(0x00ff00ff).into()),
|
("fn.name".to_string(), Color::blue().into()),
|
||||||
]);
|
]);
|
||||||
let lang = Arc::new(
|
let lang = Arc::new(
|
||||||
Language::new(
|
Language::new(
|
||||||
|
@ -864,7 +869,7 @@ mod tests {
|
||||||
cx.update(|cx| chunks(0..5, &map, &theme, cx)),
|
cx.update(|cx| chunks(0..5, &map, &theme, cx)),
|
||||||
[
|
[
|
||||||
("fn \n".to_string(), None),
|
("fn \n".to_string(), None),
|
||||||
("oute\nr".to_string(), Some("fn.name")),
|
("oute\nr".to_string(), Some(Color::blue())),
|
||||||
("() \n{}\n\n".to_string(), None),
|
("() \n{}\n\n".to_string(), None),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -879,10 +884,10 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cx.update(|cx| chunks(1..4, &map, &theme, cx)),
|
cx.update(|cx| chunks(1..4, &map, &theme, cx)),
|
||||||
[
|
[
|
||||||
("out".to_string(), Some("fn.name")),
|
("out".to_string(), Some(Color::blue())),
|
||||||
("…\n".to_string(), None),
|
("…\n".to_string(), None),
|
||||||
(" \nfn ".to_string(), Some("mod.body")),
|
(" \nfn ".to_string(), Some(Color::red())),
|
||||||
("i\n".to_string(), Some("fn.name"))
|
("i\n".to_string(), Some(Color::blue()))
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1018,19 +1023,19 @@ mod tests {
|
||||||
map: &ModelHandle<DisplayMap>,
|
map: &ModelHandle<DisplayMap>,
|
||||||
theme: &'a SyntaxTheme,
|
theme: &'a SyntaxTheme,
|
||||||
cx: &mut MutableAppContext,
|
cx: &mut MutableAppContext,
|
||||||
) -> Vec<(String, Option<&'a str>)> {
|
) -> Vec<(String, Option<Color>)> {
|
||||||
let mut 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<&str>)> = Vec::new();
|
let mut chunks: Vec<(String, Option<Color>)> = Vec::new();
|
||||||
for chunk in snapshot.chunks(rows) {
|
for chunk in snapshot.chunks(rows, Some(theme)) {
|
||||||
let style_name = chunk.highlight_id.name(theme);
|
let color = chunk.highlight_style.map(|s| s.color);
|
||||||
if let Some((last_chunk, last_style_name)) = chunks.last_mut() {
|
if let Some((last_chunk, last_color)) = chunks.last_mut() {
|
||||||
if style_name == *last_style_name {
|
if color == *last_color {
|
||||||
last_chunk.push_str(chunk.text);
|
last_chunk.push_str(chunk.text);
|
||||||
} else {
|
} else {
|
||||||
chunks.push((chunk.text.to_string(), style_name));
|
chunks.push((chunk.text.to_string(), color));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
chunks.push((chunk.text.to_string(), style_name));
|
chunks.push((chunk.text.to_string(), color));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chunks
|
chunks
|
||||||
|
|
|
@ -15,6 +15,7 @@ use std::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use sum_tree::SumTree;
|
use sum_tree::SumTree;
|
||||||
|
use theme::SyntaxTheme;
|
||||||
|
|
||||||
pub struct BlockMap {
|
pub struct BlockMap {
|
||||||
buffer: ModelHandle<Buffer>,
|
buffer: ModelHandle<Buffer>,
|
||||||
|
@ -459,12 +460,12 @@ impl<'a> BlockMapWriter<'a> {
|
||||||
impl BlockSnapshot {
|
impl BlockSnapshot {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn text(&mut self) -> String {
|
fn text(&mut self) -> String {
|
||||||
self.chunks(0..self.transforms.summary().output_rows, false)
|
self.chunks(0..self.transforms.summary().output_rows, None)
|
||||||
.map(|chunk| chunk.text)
|
.map(|chunk| chunk.text)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn chunks(&self, rows: Range<u32>, highlights: bool) -> Chunks {
|
pub fn chunks<'a>(&'a self, rows: Range<u32>, theme: Option<&'a SyntaxTheme>) -> Chunks<'a> {
|
||||||
let max_output_row = cmp::min(rows.end, self.transforms.summary().output_rows);
|
let max_output_row = cmp::min(rows.end, self.transforms.summary().output_rows);
|
||||||
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
|
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
|
||||||
let input_end = {
|
let input_end = {
|
||||||
|
@ -492,9 +493,7 @@ impl BlockSnapshot {
|
||||||
cursor.start().1 .0 + overshoot
|
cursor.start().1 .0 + overshoot
|
||||||
};
|
};
|
||||||
Chunks {
|
Chunks {
|
||||||
input_chunks: self
|
input_chunks: self.wrap_snapshot.chunks(input_start..input_end, theme),
|
||||||
.wrap_snapshot
|
|
||||||
.chunks(input_start..input_end, highlights),
|
|
||||||
input_chunk: Default::default(),
|
input_chunk: Default::default(),
|
||||||
block_chunks: None,
|
block_chunks: None,
|
||||||
transforms: cursor,
|
transforms: cursor,
|
||||||
|
@ -785,9 +784,9 @@ impl<'a> Iterator for BlockChunks<'a> {
|
||||||
|
|
||||||
let chunk = self.chunk?;
|
let chunk = self.chunk?;
|
||||||
let mut chunk_len = chunk.len();
|
let mut chunk_len = chunk.len();
|
||||||
// let mut highlight_style = None;
|
let mut highlight_style = None;
|
||||||
if let Some((run_len, _)) = self.runs.peek() {
|
if let Some((run_len, style)) = self.runs.peek() {
|
||||||
// highlight_style = Some(style.clone());
|
highlight_style = Some(style.clone());
|
||||||
let run_end_in_chunk = self.run_start + run_len - self.offset;
|
let run_end_in_chunk = self.run_start + run_len - self.offset;
|
||||||
if run_end_in_chunk <= chunk_len {
|
if run_end_in_chunk <= chunk_len {
|
||||||
chunk_len = run_end_in_chunk;
|
chunk_len = run_end_in_chunk;
|
||||||
|
@ -806,7 +805,7 @@ impl<'a> Iterator for BlockChunks<'a> {
|
||||||
|
|
||||||
Some(Chunk {
|
Some(Chunk {
|
||||||
text: chunk,
|
text: chunk,
|
||||||
highlight_id: Default::default(),
|
highlight_style,
|
||||||
diagnostic: None,
|
diagnostic: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1314,7 +1313,7 @@ mod tests {
|
||||||
for start_row in 0..expected_row_count {
|
for start_row in 0..expected_row_count {
|
||||||
let expected_text = expected_lines[start_row..].join("\n");
|
let expected_text = expected_lines[start_row..].join("\n");
|
||||||
let actual_text = blocks_snapshot
|
let actual_text = blocks_snapshot
|
||||||
.chunks(start_row as u32..expected_row_count as u32, false)
|
.chunks(start_row as u32..expected_row_count as u32, None)
|
||||||
.map(|chunk| chunk.text)
|
.map(|chunk| chunk.text)
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
use gpui::{AppContext, ModelHandle};
|
use gpui::{AppContext, ModelHandle};
|
||||||
use language::{
|
use language::{Anchor, AnchorRangeExt, Buffer, Chunk, Point, PointUtf16, TextSummary, ToOffset};
|
||||||
Anchor, AnchorRangeExt, Buffer, Chunk, HighlightId, Point, PointUtf16, TextSummary, ToOffset,
|
|
||||||
};
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::{
|
use std::{
|
||||||
cmp::{self, Ordering},
|
cmp::{self, Ordering},
|
||||||
|
@ -10,6 +8,7 @@ use std::{
|
||||||
sync::atomic::{AtomicUsize, Ordering::SeqCst},
|
sync::atomic::{AtomicUsize, Ordering::SeqCst},
|
||||||
};
|
};
|
||||||
use sum_tree::{Bias, Cursor, FilterCursor, SumTree};
|
use sum_tree::{Bias, Cursor, FilterCursor, SumTree};
|
||||||
|
use theme::SyntaxTheme;
|
||||||
|
|
||||||
pub trait ToFoldPoint {
|
pub trait ToFoldPoint {
|
||||||
fn to_fold_point(&self, snapshot: &Snapshot, bias: Bias) -> FoldPoint;
|
fn to_fold_point(&self, snapshot: &Snapshot, bias: Bias) -> FoldPoint;
|
||||||
|
@ -498,7 +497,7 @@ pub struct Snapshot {
|
||||||
impl Snapshot {
|
impl Snapshot {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn text(&self) -> String {
|
pub fn text(&self) -> String {
|
||||||
self.chunks(FoldOffset(0)..self.len(), false)
|
self.chunks(FoldOffset(0)..self.len(), None)
|
||||||
.map(|c| c.text)
|
.map(|c| c.text)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
@ -630,11 +629,15 @@ impl Snapshot {
|
||||||
|
|
||||||
pub fn chars_at(&self, start: FoldPoint) -> impl '_ + Iterator<Item = char> {
|
pub fn chars_at(&self, start: FoldPoint) -> impl '_ + Iterator<Item = char> {
|
||||||
let start = start.to_offset(self);
|
let start = start.to_offset(self);
|
||||||
self.chunks(start..self.len(), false)
|
self.chunks(start..self.len(), None)
|
||||||
.flat_map(|chunk| chunk.text.chars())
|
.flat_map(|chunk| chunk.text.chars())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn chunks(&self, range: Range<FoldOffset>, enable_highlights: bool) -> Chunks {
|
pub fn chunks<'a>(
|
||||||
|
&'a self,
|
||||||
|
range: Range<FoldOffset>,
|
||||||
|
theme: Option<&'a SyntaxTheme>,
|
||||||
|
) -> Chunks<'a> {
|
||||||
let mut transform_cursor = self.transforms.cursor::<(FoldOffset, usize)>();
|
let mut transform_cursor = self.transforms.cursor::<(FoldOffset, usize)>();
|
||||||
|
|
||||||
transform_cursor.seek(&range.end, Bias::Right, &());
|
transform_cursor.seek(&range.end, Bias::Right, &());
|
||||||
|
@ -647,9 +650,7 @@ impl Snapshot {
|
||||||
|
|
||||||
Chunks {
|
Chunks {
|
||||||
transform_cursor,
|
transform_cursor,
|
||||||
buffer_chunks: self
|
buffer_chunks: self.buffer_snapshot.chunks(buffer_start..buffer_end, theme),
|
||||||
.buffer_snapshot
|
|
||||||
.chunks(buffer_start..buffer_end, enable_highlights),
|
|
||||||
buffer_chunk: None,
|
buffer_chunk: None,
|
||||||
buffer_offset: buffer_start,
|
buffer_offset: buffer_start,
|
||||||
output_offset: range.start.0,
|
output_offset: range.start.0,
|
||||||
|
@ -974,7 +975,7 @@ impl<'a> Iterator for Chunks<'a> {
|
||||||
self.output_offset += output_text.len();
|
self.output_offset += output_text.len();
|
||||||
return Some(Chunk {
|
return Some(Chunk {
|
||||||
text: output_text,
|
text: output_text,
|
||||||
highlight_id: HighlightId::default(),
|
highlight_style: None,
|
||||||
diagnostic: None,
|
diagnostic: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1384,7 +1385,7 @@ mod tests {
|
||||||
log::info!("slicing {:?}..{:?} (text: {:?})", start, end, text);
|
log::info!("slicing {:?}..{:?} (text: {:?})", start, end, text);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
snapshot
|
snapshot
|
||||||
.chunks(start..end, false)
|
.chunks(start..end, None)
|
||||||
.map(|c| c.text)
|
.map(|c| c.text)
|
||||||
.collect::<String>(),
|
.collect::<String>(),
|
||||||
text,
|
text,
|
||||||
|
|
|
@ -4,6 +4,7 @@ use language::{rope, Chunk};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::{cmp, mem, ops::Range};
|
use std::{cmp, mem, ops::Range};
|
||||||
use sum_tree::Bias;
|
use sum_tree::Bias;
|
||||||
|
use theme::SyntaxTheme;
|
||||||
|
|
||||||
pub struct TabMap(Mutex<Snapshot>);
|
pub struct TabMap(Mutex<Snapshot>);
|
||||||
|
|
||||||
|
@ -33,7 +34,7 @@ impl TabMap {
|
||||||
let mut delta = 0;
|
let mut delta = 0;
|
||||||
for chunk in old_snapshot
|
for chunk in old_snapshot
|
||||||
.fold_snapshot
|
.fold_snapshot
|
||||||
.chunks(fold_edit.old_bytes.end..max_offset, false)
|
.chunks(fold_edit.old_bytes.end..max_offset, None)
|
||||||
{
|
{
|
||||||
let patterns: &[_] = &['\t', '\n'];
|
let patterns: &[_] = &['\t', '\n'];
|
||||||
if let Some(ix) = chunk.text.find(patterns) {
|
if let Some(ix) = chunk.text.find(patterns) {
|
||||||
|
@ -116,7 +117,7 @@ impl Snapshot {
|
||||||
self.max_point()
|
self.max_point()
|
||||||
};
|
};
|
||||||
for c in self
|
for c in self
|
||||||
.chunks(range.start..line_end, false)
|
.chunks(range.start..line_end, None)
|
||||||
.flat_map(|chunk| chunk.text.chars())
|
.flat_map(|chunk| chunk.text.chars())
|
||||||
{
|
{
|
||||||
if c == '\n' {
|
if c == '\n' {
|
||||||
|
@ -130,7 +131,7 @@ impl Snapshot {
|
||||||
last_line_chars = first_line_chars;
|
last_line_chars = first_line_chars;
|
||||||
} else {
|
} else {
|
||||||
for _ in self
|
for _ in self
|
||||||
.chunks(TabPoint::new(range.end.row(), 0)..range.end, false)
|
.chunks(TabPoint::new(range.end.row(), 0)..range.end, None)
|
||||||
.flat_map(|chunk| chunk.text.chars())
|
.flat_map(|chunk| chunk.text.chars())
|
||||||
{
|
{
|
||||||
last_line_chars += 1;
|
last_line_chars += 1;
|
||||||
|
@ -150,7 +151,11 @@ impl Snapshot {
|
||||||
self.fold_snapshot.version
|
self.fold_snapshot.version
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn chunks(&self, range: Range<TabPoint>, highlights: bool) -> Chunks {
|
pub fn chunks<'a>(
|
||||||
|
&'a self,
|
||||||
|
range: Range<TabPoint>,
|
||||||
|
theme: Option<&'a SyntaxTheme>,
|
||||||
|
) -> Chunks<'a> {
|
||||||
let (input_start, expanded_char_column, to_next_stop) =
|
let (input_start, expanded_char_column, to_next_stop) =
|
||||||
self.to_fold_point(range.start, Bias::Left);
|
self.to_fold_point(range.start, Bias::Left);
|
||||||
let input_start = input_start.to_offset(&self.fold_snapshot);
|
let input_start = input_start.to_offset(&self.fold_snapshot);
|
||||||
|
@ -165,9 +170,7 @@ impl Snapshot {
|
||||||
};
|
};
|
||||||
|
|
||||||
Chunks {
|
Chunks {
|
||||||
fold_chunks: self
|
fold_chunks: self.fold_snapshot.chunks(input_start..input_end, theme),
|
||||||
.fold_snapshot
|
|
||||||
.chunks(input_start..input_end, highlights),
|
|
||||||
column: expanded_char_column,
|
column: expanded_char_column,
|
||||||
output_position: range.start.0,
|
output_position: range.start.0,
|
||||||
max_output_position: range.end.0,
|
max_output_position: range.end.0,
|
||||||
|
@ -186,7 +189,7 @@ impl Snapshot {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn text(&self) -> String {
|
pub fn text(&self) -> String {
|
||||||
self.chunks(TabPoint::zero()..self.max_point(), false)
|
self.chunks(TabPoint::zero()..self.max_point(), None)
|
||||||
.map(|chunk| chunk.text)
|
.map(|chunk| chunk.text)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
@ -502,7 +505,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expected_text,
|
expected_text,
|
||||||
tabs_snapshot
|
tabs_snapshot
|
||||||
.chunks(start..end, false)
|
.chunks(start..end, None)
|
||||||
.map(|c| c.text)
|
.map(|c| c.text)
|
||||||
.collect::<String>()
|
.collect::<String>()
|
||||||
);
|
);
|
||||||
|
|
|
@ -12,6 +12,7 @@ use lazy_static::lazy_static;
|
||||||
use smol::future::yield_now;
|
use smol::future::yield_now;
|
||||||
use std::{collections::VecDeque, mem, ops::Range, time::Duration};
|
use std::{collections::VecDeque, mem, ops::Range, time::Duration};
|
||||||
use sum_tree::{Bias, Cursor, SumTree};
|
use sum_tree::{Bias, Cursor, SumTree};
|
||||||
|
use theme::SyntaxTheme;
|
||||||
|
|
||||||
pub use super::tab_map::TextSummary;
|
pub use super::tab_map::TextSummary;
|
||||||
pub type Edit = buffer::Edit<u32>;
|
pub type Edit = buffer::Edit<u32>;
|
||||||
|
@ -427,7 +428,7 @@ impl Snapshot {
|
||||||
let mut remaining = None;
|
let mut remaining = None;
|
||||||
let mut chunks = new_tab_snapshot.chunks(
|
let mut chunks = new_tab_snapshot.chunks(
|
||||||
TabPoint::new(edit.new_rows.start, 0)..new_tab_snapshot.max_point(),
|
TabPoint::new(edit.new_rows.start, 0)..new_tab_snapshot.max_point(),
|
||||||
false,
|
None,
|
||||||
);
|
);
|
||||||
let mut edit_transforms = Vec::<Transform>::new();
|
let mut edit_transforms = Vec::<Transform>::new();
|
||||||
for _ in edit.new_rows.start..edit.new_rows.end {
|
for _ in edit.new_rows.start..edit.new_rows.end {
|
||||||
|
@ -553,11 +554,11 @@ impl Snapshot {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn text_chunks(&self, wrap_row: u32) -> impl Iterator<Item = &str> {
|
pub fn text_chunks(&self, wrap_row: u32) -> impl Iterator<Item = &str> {
|
||||||
self.chunks(wrap_row..self.max_point().row() + 1, false)
|
self.chunks(wrap_row..self.max_point().row() + 1, None)
|
||||||
.map(|h| h.text)
|
.map(|h| h.text)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn chunks(&self, rows: Range<u32>, highlights: bool) -> Chunks {
|
pub fn chunks<'a>(&'a self, rows: Range<u32>, theme: Option<&'a SyntaxTheme>) -> Chunks<'a> {
|
||||||
let output_start = WrapPoint::new(rows.start, 0);
|
let output_start = WrapPoint::new(rows.start, 0);
|
||||||
let output_end = WrapPoint::new(rows.end, 0);
|
let output_end = WrapPoint::new(rows.end, 0);
|
||||||
let mut transforms = self.transforms.cursor::<(WrapPoint, TabPoint)>();
|
let mut transforms = self.transforms.cursor::<(WrapPoint, TabPoint)>();
|
||||||
|
@ -570,7 +571,7 @@ impl Snapshot {
|
||||||
.to_tab_point(output_end)
|
.to_tab_point(output_end)
|
||||||
.min(self.tab_snapshot.max_point());
|
.min(self.tab_snapshot.max_point());
|
||||||
Chunks {
|
Chunks {
|
||||||
input_chunks: self.tab_snapshot.chunks(input_start..input_end, highlights),
|
input_chunks: self.tab_snapshot.chunks(input_start..input_end, theme),
|
||||||
input_chunk: Default::default(),
|
input_chunk: Default::default(),
|
||||||
output_position: output_start,
|
output_position: output_start,
|
||||||
max_output_row: rows.end,
|
max_output_row: rows.end,
|
||||||
|
@ -1233,7 +1234,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
let actual_text = self
|
let actual_text = self
|
||||||
.chunks(start_row..end_row, false)
|
.chunks(start_row..end_row, None)
|
||||||
.map(|c| c.text)
|
.map(|c| c.text)
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
|
@ -493,7 +493,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());
|
let chunks = snapshot.chunks(rows.clone(), Some(&style.syntax));
|
||||||
|
|
||||||
let newline_chunk = Chunk {
|
let newline_chunk = Chunk {
|
||||||
text: "\n",
|
text: "\n",
|
||||||
|
@ -517,10 +517,8 @@ impl EditorElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !line_chunk.is_empty() && !line_exceeded_max_len {
|
if !line_chunk.is_empty() && !line_exceeded_max_len {
|
||||||
let highlight_style = chunk
|
let highlight_style =
|
||||||
.highlight_id
|
chunk.highlight_style.unwrap_or(style.text.clone().into());
|
||||||
.style(&style.syntax)
|
|
||||||
.unwrap_or(style.text.clone().into());
|
|
||||||
// Avoid a lookup if the font properties match the previous ones.
|
// Avoid a lookup if the font properties match the previous ones.
|
||||||
let font_id = if highlight_style.font_properties == prev_font_properties {
|
let font_id = if highlight_style.font_properties == prev_font_properties {
|
||||||
prev_font_id
|
prev_font_id
|
||||||
|
|
|
@ -31,7 +31,7 @@ use std::{
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use sum_tree::Bias;
|
use sum_tree::Bias;
|
||||||
use theme::EditorStyle;
|
use theme::{EditorStyle, SyntaxTheme};
|
||||||
use util::post_inc;
|
use util::post_inc;
|
||||||
|
|
||||||
const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
|
const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
|
||||||
|
@ -2709,8 +2709,12 @@ impl Snapshot {
|
||||||
self.display_snapshot.buffer_rows(start_row)
|
self.display_snapshot.buffer_rows(start_row)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn chunks(&mut self, display_rows: Range<u32>) -> display_map::Chunks {
|
pub fn chunks<'a>(
|
||||||
self.display_snapshot.chunks(display_rows)
|
&'a self,
|
||||||
|
display_rows: Range<u32>,
|
||||||
|
theme: Option<&'a SyntaxTheme>,
|
||||||
|
) -> display_map::Chunks<'a> {
|
||||||
|
self.display_snapshot.chunks(display_rows, theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scroll_position(&self) -> Vector2F {
|
pub fn scroll_position(&self) -> Vector2F {
|
||||||
|
|
|
@ -30,7 +30,7 @@ pub struct TextStyle {
|
||||||
pub underline: Option<Color>,
|
pub underline: Option<Color>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
pub struct HighlightStyle {
|
pub struct HighlightStyle {
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
pub font_properties: Properties,
|
pub font_properties: Properties,
|
||||||
|
|
|
@ -12,7 +12,7 @@ use anyhow::{anyhow, Result};
|
||||||
pub use buffer::{Buffer as TextBuffer, Operation as _, *};
|
pub use buffer::{Buffer as TextBuffer, Operation as _, *};
|
||||||
use clock::ReplicaId;
|
use clock::ReplicaId;
|
||||||
use futures::FutureExt as _;
|
use futures::FutureExt as _;
|
||||||
use gpui::{AppContext, Entity, ModelContext, MutableAppContext, Task};
|
use gpui::{fonts::HighlightStyle, AppContext, Entity, ModelContext, MutableAppContext, Task};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use lsp::LanguageServer;
|
use lsp::LanguageServer;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
@ -34,6 +34,7 @@ use std::{
|
||||||
time::{Duration, Instant, SystemTime, UNIX_EPOCH},
|
time::{Duration, Instant, SystemTime, UNIX_EPOCH},
|
||||||
vec,
|
vec,
|
||||||
};
|
};
|
||||||
|
use theme::SyntaxTheme;
|
||||||
use tree_sitter::{InputEdit, Parser, QueryCursor, Tree};
|
use tree_sitter::{InputEdit, Parser, QueryCursor, Tree};
|
||||||
use util::{post_inc, TryFutureExt as _};
|
use util::{post_inc, TryFutureExt as _};
|
||||||
|
|
||||||
|
@ -190,6 +191,7 @@ struct Highlights<'a> {
|
||||||
next_capture: Option<(tree_sitter::QueryMatch<'a, 'a>, usize)>,
|
next_capture: Option<(tree_sitter::QueryMatch<'a, 'a>, usize)>,
|
||||||
stack: Vec<(usize, HighlightId)>,
|
stack: Vec<(usize, HighlightId)>,
|
||||||
highlight_map: HighlightMap,
|
highlight_map: HighlightMap,
|
||||||
|
theme: &'a SyntaxTheme,
|
||||||
_query_cursor: QueryCursorHandle,
|
_query_cursor: QueryCursorHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,7 +209,7 @@ pub struct Chunks<'a> {
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
pub struct Chunk<'a> {
|
pub struct Chunk<'a> {
|
||||||
pub text: &'a str,
|
pub text: &'a str,
|
||||||
pub highlight_id: HighlightId,
|
pub highlight_style: Option<HighlightStyle>,
|
||||||
pub diagnostic: Option<DiagnosticSeverity>,
|
pub diagnostic: Option<DiagnosticSeverity>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1634,12 +1636,16 @@ impl Snapshot {
|
||||||
.all(|chunk| chunk.matches(|c: char| !c.is_whitespace()).next().is_none())
|
.all(|chunk| chunk.matches(|c: char| !c.is_whitespace()).next().is_none())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn chunks<T: ToOffset>(&self, range: Range<T>, highlight: bool) -> Chunks {
|
pub fn chunks<'a, T: ToOffset>(
|
||||||
|
&'a self,
|
||||||
|
range: Range<T>,
|
||||||
|
theme: Option<&'a SyntaxTheme>,
|
||||||
|
) -> Chunks<'a> {
|
||||||
let range = range.start.to_offset(&*self)..range.end.to_offset(&*self);
|
let range = range.start.to_offset(&*self)..range.end.to_offset(&*self);
|
||||||
|
|
||||||
let mut highlights = None;
|
let mut highlights = None;
|
||||||
let mut diagnostic_endpoints = Vec::<DiagnosticEndpoint>::new();
|
let mut diagnostic_endpoints = Vec::<DiagnosticEndpoint>::new();
|
||||||
if highlight {
|
if let Some(theme) = theme {
|
||||||
for (_, range, diagnostic) in
|
for (_, range, diagnostic) in
|
||||||
self.diagnostics
|
self.diagnostics
|
||||||
.intersecting_ranges(range.clone(), self.content(), true)
|
.intersecting_ranges(range.clone(), self.content(), true)
|
||||||
|
@ -1676,6 +1682,7 @@ impl Snapshot {
|
||||||
stack: Default::default(),
|
stack: Default::default(),
|
||||||
highlight_map: language.highlight_map(),
|
highlight_map: language.highlight_map(),
|
||||||
_query_cursor: query_cursor,
|
_query_cursor: query_cursor,
|
||||||
|
theme,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1845,12 +1852,12 @@ impl<'a> Iterator for Chunks<'a> {
|
||||||
let mut chunk_end = (self.chunks.offset() + chunk.len())
|
let mut chunk_end = (self.chunks.offset() + chunk.len())
|
||||||
.min(next_capture_start)
|
.min(next_capture_start)
|
||||||
.min(next_diagnostic_endpoint);
|
.min(next_diagnostic_endpoint);
|
||||||
let mut highlight_id = HighlightId::default();
|
let mut highlight_style = None;
|
||||||
if let Some((parent_capture_end, parent_highlight_id)) =
|
if let Some(highlights) = self.highlights.as_ref() {
|
||||||
self.highlights.as_ref().and_then(|h| h.stack.last())
|
if let Some((parent_capture_end, parent_highlight_id)) = highlights.stack.last() {
|
||||||
{
|
chunk_end = chunk_end.min(*parent_capture_end);
|
||||||
chunk_end = chunk_end.min(*parent_capture_end);
|
highlight_style = parent_highlight_id.style(highlights.theme);
|
||||||
highlight_id = *parent_highlight_id;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let slice =
|
let slice =
|
||||||
|
@ -1862,7 +1869,7 @@ impl<'a> Iterator for Chunks<'a> {
|
||||||
|
|
||||||
Some(Chunk {
|
Some(Chunk {
|
||||||
text: slice,
|
text: slice,
|
||||||
highlight_id,
|
highlight_style,
|
||||||
diagnostic: self.current_diagnostic_severity(),
|
diagnostic: self.current_diagnostic_severity(),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -906,7 +906,7 @@ fn chunks_with_diagnostics<T: ToOffset + ToPoint>(
|
||||||
range: Range<T>,
|
range: Range<T>,
|
||||||
) -> Vec<(String, Option<DiagnosticSeverity>)> {
|
) -> Vec<(String, Option<DiagnosticSeverity>)> {
|
||||||
let mut chunks: Vec<(String, Option<DiagnosticSeverity>)> = Vec::new();
|
let mut chunks: Vec<(String, Option<DiagnosticSeverity>)> = Vec::new();
|
||||||
for chunk in buffer.snapshot().chunks(range, true) {
|
for chunk in buffer.snapshot().chunks(range, Some(&Default::default())) {
|
||||||
if chunks
|
if chunks
|
||||||
.last()
|
.last()
|
||||||
.map_or(false, |prev_chunk| prev_chunk.1 == chunk.diagnostic)
|
.map_or(false, |prev_chunk| prev_chunk.1 == chunk.diagnostic)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue