Rename foo/mod.rs files too foo.rs
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
co-authored by
Nathan Sobo
parent
9369249287
commit
139bcb8304
@@ -1,722 +0,0 @@
|
||||
mod fold_map;
|
||||
|
||||
use crate::settings::StyleId;
|
||||
|
||||
use super::{buffer, Anchor, Bias, Buffer, Edit, Point, ToOffset, ToPoint};
|
||||
pub use fold_map::BufferRows;
|
||||
use fold_map::{FoldMap, FoldMapSnapshot};
|
||||
use gpui::{AppContext, ModelHandle};
|
||||
use std::{mem, ops::Range};
|
||||
|
||||
pub struct DisplayMap {
|
||||
buffer: ModelHandle<Buffer>,
|
||||
fold_map: FoldMap,
|
||||
tab_size: usize,
|
||||
}
|
||||
|
||||
impl DisplayMap {
|
||||
pub fn new(buffer: ModelHandle<Buffer>, tab_size: usize, ctx: &AppContext) -> Self {
|
||||
DisplayMap {
|
||||
buffer: buffer.clone(),
|
||||
fold_map: FoldMap::new(buffer, ctx),
|
||||
tab_size,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn snapshot(&self, ctx: &AppContext) -> DisplayMapSnapshot {
|
||||
DisplayMapSnapshot {
|
||||
folds_snapshot: self.fold_map.snapshot(ctx),
|
||||
tab_size: self.tab_size,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn folds_in_range<'a, T>(
|
||||
&'a self,
|
||||
range: Range<T>,
|
||||
app: &'a AppContext,
|
||||
) -> impl Iterator<Item = &'a Range<Anchor>>
|
||||
where
|
||||
T: ToOffset,
|
||||
{
|
||||
self.fold_map.folds_in_range(range, app)
|
||||
}
|
||||
|
||||
pub fn fold<T: ToOffset>(
|
||||
&mut self,
|
||||
ranges: impl IntoIterator<Item = Range<T>>,
|
||||
ctx: &AppContext,
|
||||
) {
|
||||
self.fold_map.fold(ranges, ctx)
|
||||
}
|
||||
|
||||
pub fn unfold<T: ToOffset>(
|
||||
&mut self,
|
||||
ranges: impl IntoIterator<Item = Range<T>>,
|
||||
ctx: &AppContext,
|
||||
) {
|
||||
self.fold_map.unfold(ranges, ctx)
|
||||
}
|
||||
|
||||
pub fn intersects_fold<T: ToOffset>(&self, offset: T, ctx: &AppContext) -> bool {
|
||||
self.fold_map.intersects_fold(offset, ctx)
|
||||
}
|
||||
|
||||
pub fn is_line_folded(&self, display_row: u32, ctx: &AppContext) -> bool {
|
||||
self.fold_map.is_line_folded(display_row, ctx)
|
||||
}
|
||||
|
||||
pub fn text(&self, ctx: &AppContext) -> String {
|
||||
self.snapshot(ctx).chunks_at(DisplayPoint::zero()).collect()
|
||||
}
|
||||
|
||||
pub fn line(&self, display_row: u32, ctx: &AppContext) -> String {
|
||||
let mut result = String::new();
|
||||
for chunk in self
|
||||
.snapshot(ctx)
|
||||
.chunks_at(DisplayPoint::new(display_row, 0))
|
||||
{
|
||||
if let Some(ix) = chunk.find('\n') {
|
||||
result.push_str(&chunk[0..ix]);
|
||||
break;
|
||||
} else {
|
||||
result.push_str(chunk);
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn line_indent(&self, display_row: u32, ctx: &AppContext) -> (u32, bool) {
|
||||
let mut indent = 0;
|
||||
let mut is_blank = true;
|
||||
for c in self
|
||||
.snapshot(ctx)
|
||||
.chars_at(DisplayPoint::new(display_row, 0))
|
||||
{
|
||||
if c == ' ' {
|
||||
indent += 1;
|
||||
} else {
|
||||
is_blank = c == '\n';
|
||||
break;
|
||||
}
|
||||
}
|
||||
(indent, is_blank)
|
||||
}
|
||||
|
||||
pub fn line_len(&self, row: u32, ctx: &AppContext) -> u32 {
|
||||
DisplayPoint::new(row, self.fold_map.line_len(row, ctx))
|
||||
.expand_tabs(self, ctx)
|
||||
.column()
|
||||
}
|
||||
|
||||
pub fn max_point(&self, ctx: &AppContext) -> DisplayPoint {
|
||||
self.snapshot(ctx).max_point().expand_tabs(self, ctx)
|
||||
}
|
||||
|
||||
pub fn longest_row(&self, ctx: &AppContext) -> u32 {
|
||||
self.fold_map.longest_row(ctx)
|
||||
}
|
||||
|
||||
pub fn anchor_before(&self, point: DisplayPoint, bias: Bias, app: &AppContext) -> Anchor {
|
||||
self.buffer
|
||||
.read(app)
|
||||
.anchor_before(point.to_buffer_point(self, bias, app))
|
||||
}
|
||||
|
||||
pub fn anchor_after(&self, point: DisplayPoint, bias: Bias, app: &AppContext) -> Anchor {
|
||||
self.buffer
|
||||
.read(app)
|
||||
.anchor_after(point.to_buffer_point(self, bias, app))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DisplayMapSnapshot {
|
||||
folds_snapshot: FoldMapSnapshot,
|
||||
tab_size: usize,
|
||||
}
|
||||
|
||||
impl DisplayMapSnapshot {
|
||||
pub fn buffer_rows(&self, start_row: u32) -> BufferRows {
|
||||
self.folds_snapshot.buffer_rows(start_row)
|
||||
}
|
||||
|
||||
pub fn max_point(&self) -> DisplayPoint {
|
||||
self.expand_tabs(self.folds_snapshot.max_point())
|
||||
}
|
||||
|
||||
pub fn chunks_at(&self, point: DisplayPoint) -> Chunks {
|
||||
let (point, expanded_char_column, to_next_stop) = self.collapse_tabs(point, Bias::Left);
|
||||
let fold_chunks = self
|
||||
.folds_snapshot
|
||||
.chunks_at(self.folds_snapshot.to_display_offset(point));
|
||||
Chunks {
|
||||
fold_chunks,
|
||||
column: expanded_char_column,
|
||||
tab_size: self.tab_size,
|
||||
chunk: &SPACES[0..to_next_stop],
|
||||
skip_leading_tab: to_next_stop > 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn highlighted_chunks_for_rows(&mut self, rows: Range<u32>) -> HighlightedChunks {
|
||||
let start = DisplayPoint::new(rows.start, 0);
|
||||
let start = self.folds_snapshot.to_display_offset(start);
|
||||
let end = DisplayPoint::new(rows.end, 0).min(self.max_point());
|
||||
let end = self.folds_snapshot.to_display_offset(end);
|
||||
HighlightedChunks {
|
||||
fold_chunks: self.folds_snapshot.highlighted_chunks(start..end),
|
||||
column: 0,
|
||||
tab_size: self.tab_size,
|
||||
chunk: "",
|
||||
style_id: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chars_at<'a>(&'a self, point: DisplayPoint) -> impl Iterator<Item = char> + 'a {
|
||||
self.chunks_at(point).flat_map(str::chars)
|
||||
}
|
||||
|
||||
pub fn column_to_chars(&self, display_row: u32, target: u32) -> u32 {
|
||||
let mut count = 0;
|
||||
let mut column = 0;
|
||||
for c in self.chars_at(DisplayPoint::new(display_row, 0)) {
|
||||
if column >= target {
|
||||
break;
|
||||
}
|
||||
count += 1;
|
||||
column += c.len_utf8() as u32;
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
pub fn column_from_chars(&self, display_row: u32, char_count: u32) -> u32 {
|
||||
let mut count = 0;
|
||||
let mut column = 0;
|
||||
for c in self.chars_at(DisplayPoint::new(display_row, 0)) {
|
||||
if c == '\n' || count >= char_count {
|
||||
break;
|
||||
}
|
||||
count += 1;
|
||||
column += c.len_utf8() as u32;
|
||||
}
|
||||
column
|
||||
}
|
||||
|
||||
pub fn clip_point(&self, point: DisplayPoint, bias: Bias) -> DisplayPoint {
|
||||
self.expand_tabs(
|
||||
self.folds_snapshot
|
||||
.clip_point(self.collapse_tabs(point, bias).0, bias),
|
||||
)
|
||||
}
|
||||
|
||||
fn expand_tabs(&self, mut point: DisplayPoint) -> DisplayPoint {
|
||||
let chars = self
|
||||
.folds_snapshot
|
||||
.chars_at(DisplayPoint(Point::new(point.row(), 0)));
|
||||
let expanded = expand_tabs(chars, point.column() as usize, self.tab_size);
|
||||
*point.column_mut() = expanded as u32;
|
||||
point
|
||||
}
|
||||
|
||||
fn collapse_tabs(&self, mut point: DisplayPoint, bias: Bias) -> (DisplayPoint, usize, usize) {
|
||||
let chars = self
|
||||
.folds_snapshot
|
||||
.chars_at(DisplayPoint(Point::new(point.row(), 0)));
|
||||
let expanded = point.column() as usize;
|
||||
let (collapsed, expanded_char_column, to_next_stop) =
|
||||
collapse_tabs(chars, expanded, bias, self.tab_size);
|
||||
*point.column_mut() = collapsed as u32;
|
||||
(point, expanded_char_column, to_next_stop)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
|
||||
pub struct DisplayPoint(Point);
|
||||
|
||||
impl DisplayPoint {
|
||||
pub fn new(row: u32, column: u32) -> Self {
|
||||
Self(Point::new(row, column))
|
||||
}
|
||||
|
||||
pub fn zero() -> Self {
|
||||
Self::new(0, 0)
|
||||
}
|
||||
|
||||
pub fn row(self) -> u32 {
|
||||
self.0.row
|
||||
}
|
||||
|
||||
pub fn column(self) -> u32 {
|
||||
self.0.column
|
||||
}
|
||||
|
||||
pub fn row_mut(&mut self) -> &mut u32 {
|
||||
&mut self.0.row
|
||||
}
|
||||
|
||||
pub fn column_mut(&mut self) -> &mut u32 {
|
||||
&mut self.0.column
|
||||
}
|
||||
|
||||
pub fn to_buffer_point(self, map: &DisplayMap, bias: Bias, ctx: &AppContext) -> Point {
|
||||
map.fold_map
|
||||
.to_buffer_point(self.collapse_tabs(map, bias, ctx), ctx)
|
||||
}
|
||||
|
||||
pub fn to_buffer_offset(self, map: &DisplayMap, bias: Bias, ctx: &AppContext) -> usize {
|
||||
map.fold_map
|
||||
.to_buffer_offset(self.collapse_tabs(&map, bias, ctx), ctx)
|
||||
}
|
||||
|
||||
fn expand_tabs(self, map: &DisplayMap, ctx: &AppContext) -> Self {
|
||||
map.snapshot(ctx).expand_tabs(self)
|
||||
}
|
||||
|
||||
fn collapse_tabs(self, map: &DisplayMap, bias: Bias, ctx: &AppContext) -> Self {
|
||||
map.snapshot(ctx).collapse_tabs(self, bias).0
|
||||
}
|
||||
}
|
||||
|
||||
impl Point {
|
||||
pub fn to_display_point(self, map: &DisplayMap, ctx: &AppContext) -> DisplayPoint {
|
||||
let mut display_point = map.fold_map.to_display_point(self, ctx);
|
||||
let snapshot = map.fold_map.snapshot(ctx);
|
||||
let chars = snapshot.chars_at(DisplayPoint::new(display_point.row(), 0));
|
||||
*display_point.column_mut() =
|
||||
expand_tabs(chars, display_point.column() as usize, map.tab_size) as u32;
|
||||
display_point
|
||||
}
|
||||
}
|
||||
|
||||
impl Anchor {
|
||||
pub fn to_display_point(&self, map: &DisplayMap, app: &AppContext) -> DisplayPoint {
|
||||
self.to_point(map.buffer.read(app))
|
||||
.to_display_point(map, app)
|
||||
}
|
||||
}
|
||||
|
||||
// Handles a tab width <= 16
|
||||
const SPACES: &'static str = " ";
|
||||
|
||||
pub struct Chunks<'a> {
|
||||
fold_chunks: fold_map::Chunks<'a>,
|
||||
chunk: &'a str,
|
||||
column: usize,
|
||||
tab_size: usize,
|
||||
skip_leading_tab: bool,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Chunks<'a> {
|
||||
type Item = &'a str;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.chunk.is_empty() {
|
||||
if let Some(chunk) = self.fold_chunks.next() {
|
||||
self.chunk = chunk;
|
||||
if self.skip_leading_tab {
|
||||
self.chunk = &self.chunk[1..];
|
||||
self.skip_leading_tab = false;
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
for (ix, c) in self.chunk.char_indices() {
|
||||
match c {
|
||||
'\t' => {
|
||||
if ix > 0 {
|
||||
let (prefix, suffix) = self.chunk.split_at(ix);
|
||||
self.chunk = suffix;
|
||||
return Some(prefix);
|
||||
} else {
|
||||
self.chunk = &self.chunk[1..];
|
||||
let len = self.tab_size - self.column % self.tab_size;
|
||||
self.column += len;
|
||||
return Some(&SPACES[0..len]);
|
||||
}
|
||||
}
|
||||
'\n' => self.column = 0,
|
||||
_ => self.column += 1,
|
||||
}
|
||||
}
|
||||
|
||||
let result = Some(self.chunk);
|
||||
self.chunk = "";
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HighlightedChunks<'a> {
|
||||
fold_chunks: fold_map::HighlightedChunks<'a>,
|
||||
chunk: &'a str,
|
||||
style_id: StyleId,
|
||||
column: usize,
|
||||
tab_size: usize,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for HighlightedChunks<'a> {
|
||||
type Item = (&'a str, StyleId);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.chunk.is_empty() {
|
||||
if let Some((chunk, style_id)) = self.fold_chunks.next() {
|
||||
self.chunk = chunk;
|
||||
self.style_id = style_id;
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
for (ix, c) in self.chunk.char_indices() {
|
||||
match c {
|
||||
'\t' => {
|
||||
if ix > 0 {
|
||||
let (prefix, suffix) = self.chunk.split_at(ix);
|
||||
self.chunk = suffix;
|
||||
return Some((prefix, self.style_id));
|
||||
} else {
|
||||
self.chunk = &self.chunk[1..];
|
||||
let len = self.tab_size - self.column % self.tab_size;
|
||||
self.column += len;
|
||||
return Some((&SPACES[0..len], self.style_id));
|
||||
}
|
||||
}
|
||||
'\n' => self.column = 0,
|
||||
_ => self.column += 1,
|
||||
}
|
||||
}
|
||||
|
||||
Some((mem::take(&mut self.chunk), mem::take(&mut self.style_id)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expand_tabs(chars: impl Iterator<Item = char>, column: usize, tab_size: usize) -> usize {
|
||||
let mut expanded_chars = 0;
|
||||
let mut expanded_bytes = 0;
|
||||
let mut collapsed_bytes = 0;
|
||||
for c in chars {
|
||||
if collapsed_bytes == column {
|
||||
break;
|
||||
}
|
||||
if c == '\t' {
|
||||
let tab_len = tab_size - expanded_chars % tab_size;
|
||||
expanded_bytes += tab_len;
|
||||
expanded_chars += tab_len;
|
||||
} else {
|
||||
expanded_bytes += c.len_utf8();
|
||||
expanded_chars += 1;
|
||||
}
|
||||
collapsed_bytes += c.len_utf8();
|
||||
}
|
||||
expanded_bytes
|
||||
}
|
||||
|
||||
pub fn collapse_tabs(
|
||||
mut chars: impl Iterator<Item = char>,
|
||||
column: usize,
|
||||
bias: Bias,
|
||||
tab_size: usize,
|
||||
) -> (usize, usize, usize) {
|
||||
let mut expanded_bytes = 0;
|
||||
let mut expanded_chars = 0;
|
||||
let mut collapsed_bytes = 0;
|
||||
while let Some(c) = chars.next() {
|
||||
if expanded_bytes >= column {
|
||||
break;
|
||||
}
|
||||
|
||||
if c == '\t' {
|
||||
let tab_len = tab_size - (expanded_chars % tab_size);
|
||||
expanded_chars += tab_len;
|
||||
expanded_bytes += tab_len;
|
||||
if expanded_bytes > column {
|
||||
expanded_chars -= expanded_bytes - column;
|
||||
return match bias {
|
||||
Bias::Left => (collapsed_bytes, expanded_chars, expanded_bytes - column),
|
||||
Bias::Right => (collapsed_bytes + 1, expanded_chars, 0),
|
||||
};
|
||||
}
|
||||
} else {
|
||||
expanded_chars += 1;
|
||||
expanded_bytes += c.len_utf8();
|
||||
}
|
||||
|
||||
if expanded_bytes > column && matches!(bias, Bias::Left) {
|
||||
expanded_chars -= 1;
|
||||
break;
|
||||
}
|
||||
|
||||
collapsed_bytes += c.len_utf8();
|
||||
}
|
||||
(collapsed_bytes, expanded_chars, 0)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
language::{Language, LanguageConfig},
|
||||
settings::Theme,
|
||||
test::*,
|
||||
};
|
||||
use buffer::History;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[gpui::test]
|
||||
fn test_chunks_at(app: &mut gpui::MutableAppContext) {
|
||||
let text = sample_text(6, 6);
|
||||
let buffer = app.add_model(|ctx| Buffer::new(0, text, ctx));
|
||||
let map = DisplayMap::new(buffer.clone(), 4, app.as_ref());
|
||||
buffer
|
||||
.update(app, |buffer, ctx| {
|
||||
buffer.edit(
|
||||
vec![
|
||||
Point::new(1, 0)..Point::new(1, 0),
|
||||
Point::new(1, 1)..Point::new(1, 1),
|
||||
Point::new(2, 1)..Point::new(2, 1),
|
||||
],
|
||||
"\t",
|
||||
Some(ctx),
|
||||
)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
&map.snapshot(app.as_ref())
|
||||
.chunks_at(DisplayPoint::new(1, 0))
|
||||
.collect::<String>()[0..10],
|
||||
" b bb"
|
||||
);
|
||||
assert_eq!(
|
||||
&map.snapshot(app.as_ref())
|
||||
.chunks_at(DisplayPoint::new(1, 2))
|
||||
.collect::<String>()[0..10],
|
||||
" b bbbb"
|
||||
);
|
||||
assert_eq!(
|
||||
&map.snapshot(app.as_ref())
|
||||
.chunks_at(DisplayPoint::new(1, 6))
|
||||
.collect::<String>()[0..13],
|
||||
" bbbbb\nc c"
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_highlighted_chunks_at(mut app: gpui::TestAppContext) {
|
||||
use unindent::Unindent as _;
|
||||
|
||||
let grammar = tree_sitter_rust::language();
|
||||
let text = r#"
|
||||
fn outer() {}
|
||||
|
||||
mod module {
|
||||
fn inner() {}
|
||||
}"#
|
||||
.unindent();
|
||||
let highlight_query = tree_sitter::Query::new(
|
||||
grammar,
|
||||
r#"
|
||||
(mod_item name: (identifier) body: _ @mod.body)
|
||||
(function_item name: (identifier) @fn.name)"#,
|
||||
)
|
||||
.unwrap();
|
||||
let theme = Theme::parse(
|
||||
r#"
|
||||
[syntax]
|
||||
"mod.body" = 0xff0000
|
||||
"fn.name" = 0x00ff00"#,
|
||||
)
|
||||
.unwrap();
|
||||
let lang = Arc::new(Language {
|
||||
config: LanguageConfig {
|
||||
name: "Test".to_string(),
|
||||
path_suffixes: vec![".test".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
grammar: grammar.clone(),
|
||||
highlight_query,
|
||||
brackets_query: tree_sitter::Query::new(grammar, "").unwrap(),
|
||||
theme_mapping: Default::default(),
|
||||
});
|
||||
lang.set_theme(&theme);
|
||||
|
||||
let buffer = app.add_model(|ctx| {
|
||||
Buffer::from_history(0, History::new(text.into()), None, Some(lang), ctx)
|
||||
});
|
||||
buffer.condition(&app, |buf, _| !buf.is_parsing()).await;
|
||||
|
||||
let mut map = app.read(|ctx| DisplayMap::new(buffer, 2, ctx));
|
||||
assert_eq!(
|
||||
app.read(|ctx| highlighted_chunks(0..5, &map, &theme, ctx)),
|
||||
vec![
|
||||
("fn ".to_string(), None),
|
||||
("outer".to_string(), Some("fn.name")),
|
||||
("() {}\n\nmod module ".to_string(), None),
|
||||
("{\n fn ".to_string(), Some("mod.body")),
|
||||
("inner".to_string(), Some("fn.name")),
|
||||
("() {}\n}".to_string(), Some("mod.body")),
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
app.read(|ctx| highlighted_chunks(3..5, &map, &theme, ctx)),
|
||||
vec![
|
||||
(" fn ".to_string(), Some("mod.body")),
|
||||
("inner".to_string(), Some("fn.name")),
|
||||
("() {}\n}".to_string(), Some("mod.body")),
|
||||
]
|
||||
);
|
||||
|
||||
app.read(|ctx| map.fold(vec![Point::new(0, 6)..Point::new(3, 2)], ctx));
|
||||
assert_eq!(
|
||||
app.read(|ctx| highlighted_chunks(0..2, &map, &theme, ctx)),
|
||||
vec![
|
||||
("fn ".to_string(), None),
|
||||
("out".to_string(), Some("fn.name")),
|
||||
("…".to_string(), None),
|
||||
(" fn ".to_string(), Some("mod.body")),
|
||||
("inner".to_string(), Some("fn.name")),
|
||||
("() {}\n}".to_string(), Some("mod.body")),
|
||||
]
|
||||
);
|
||||
|
||||
fn highlighted_chunks<'a>(
|
||||
rows: Range<u32>,
|
||||
map: &DisplayMap,
|
||||
theme: &'a Theme,
|
||||
ctx: &AppContext,
|
||||
) -> Vec<(String, Option<&'a str>)> {
|
||||
let mut chunks: Vec<(String, Option<&str>)> = Vec::new();
|
||||
for (chunk, style_id) in map.snapshot(ctx).highlighted_chunks_for_rows(rows) {
|
||||
let style_name = theme.syntax_style_name(style_id);
|
||||
if let Some((last_chunk, last_style_name)) = chunks.last_mut() {
|
||||
if style_name == *last_style_name {
|
||||
last_chunk.push_str(chunk);
|
||||
} else {
|
||||
chunks.push((chunk.to_string(), style_name));
|
||||
}
|
||||
} else {
|
||||
chunks.push((chunk.to_string(), style_name));
|
||||
}
|
||||
}
|
||||
chunks
|
||||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_clip_point(app: &mut gpui::MutableAppContext) {
|
||||
use Bias::{Left, Right};
|
||||
|
||||
let text = "\n'a', 'α',\t'✋',\t'❎', '🍐'\n";
|
||||
let display_text = "\n'a', 'α', '✋', '❎', '🍐'\n";
|
||||
let buffer = app.add_model(|ctx| Buffer::new(0, text, ctx));
|
||||
let ctx = app.as_ref();
|
||||
let map = DisplayMap::new(buffer.clone(), 4, ctx);
|
||||
assert_eq!(map.text(ctx), display_text);
|
||||
|
||||
let map = map.snapshot(ctx);
|
||||
for (input_column, bias, output_column) in vec![
|
||||
("'a', '".len(), Left, "'a', '".len()),
|
||||
("'a', '".len() + 1, Left, "'a', '".len()),
|
||||
("'a', '".len() + 1, Right, "'a', 'α".len()),
|
||||
("'a', 'α', ".len(), Left, "'a', 'α',".len()),
|
||||
("'a', 'α', ".len(), Right, "'a', 'α', ".len()),
|
||||
("'a', 'α', '".len() + 1, Left, "'a', 'α', '".len()),
|
||||
("'a', 'α', '".len() + 1, Right, "'a', 'α', '✋".len()),
|
||||
("'a', 'α', '✋',".len(), Right, "'a', 'α', '✋',".len()),
|
||||
("'a', 'α', '✋', ".len(), Left, "'a', 'α', '✋',".len()),
|
||||
(
|
||||
"'a', 'α', '✋', ".len(),
|
||||
Right,
|
||||
"'a', 'α', '✋', ".len(),
|
||||
),
|
||||
] {
|
||||
assert_eq!(
|
||||
map.clip_point(DisplayPoint::new(1, input_column as u32), bias),
|
||||
DisplayPoint::new(1, output_column as u32),
|
||||
"clip_point(({}, {}))",
|
||||
1,
|
||||
input_column,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expand_tabs() {
|
||||
assert_eq!(expand_tabs("\t".chars(), 0, 4), 0);
|
||||
assert_eq!(expand_tabs("\t".chars(), 1, 4), 4);
|
||||
assert_eq!(expand_tabs("\ta".chars(), 2, 4), 5);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_tabs_with_multibyte_chars(app: &mut gpui::MutableAppContext) {
|
||||
let text = "✅\t\tα\nβ\t\n🏀β\t\tγ";
|
||||
let buffer = app.add_model(|ctx| Buffer::new(0, text, ctx));
|
||||
let ctx = app.as_ref();
|
||||
let map = DisplayMap::new(buffer.clone(), 4, ctx);
|
||||
assert_eq!(map.text(ctx), "✅ α\nβ \n🏀β γ");
|
||||
|
||||
let point = Point::new(0, "✅\t\t".len() as u32);
|
||||
let display_point = DisplayPoint::new(0, "✅ ".len() as u32);
|
||||
assert_eq!(point.to_display_point(&map, ctx), display_point);
|
||||
assert_eq!(display_point.to_buffer_point(&map, Bias::Left, ctx), point,);
|
||||
|
||||
let point = Point::new(1, "β\t".len() as u32);
|
||||
let display_point = DisplayPoint::new(1, "β ".len() as u32);
|
||||
assert_eq!(point.to_display_point(&map, ctx), display_point);
|
||||
assert_eq!(display_point.to_buffer_point(&map, Bias::Left, ctx), point,);
|
||||
|
||||
let point = Point::new(2, "🏀β\t\t".len() as u32);
|
||||
let display_point = DisplayPoint::new(2, "🏀β ".len() as u32);
|
||||
assert_eq!(point.to_display_point(&map, ctx), display_point);
|
||||
assert_eq!(display_point.to_buffer_point(&map, Bias::Left, ctx), point,);
|
||||
|
||||
// Display points inside of expanded tabs
|
||||
assert_eq!(
|
||||
DisplayPoint::new(0, "✅ ".len() as u32).to_buffer_point(&map, Bias::Right, ctx),
|
||||
Point::new(0, "✅\t\t".len() as u32),
|
||||
);
|
||||
assert_eq!(
|
||||
DisplayPoint::new(0, "✅ ".len() as u32).to_buffer_point(&map, Bias::Left, ctx),
|
||||
Point::new(0, "✅\t".len() as u32),
|
||||
);
|
||||
assert_eq!(
|
||||
map.snapshot(ctx)
|
||||
.chunks_at(DisplayPoint::new(0, "✅ ".len() as u32))
|
||||
.collect::<String>(),
|
||||
" α\nβ \n🏀β γ"
|
||||
);
|
||||
assert_eq!(
|
||||
DisplayPoint::new(0, "✅ ".len() as u32).to_buffer_point(&map, Bias::Right, ctx),
|
||||
Point::new(0, "✅\t".len() as u32),
|
||||
);
|
||||
assert_eq!(
|
||||
DisplayPoint::new(0, "✅ ".len() as u32).to_buffer_point(&map, Bias::Left, ctx),
|
||||
Point::new(0, "✅".len() as u32),
|
||||
);
|
||||
assert_eq!(
|
||||
map.snapshot(ctx)
|
||||
.chunks_at(DisplayPoint::new(0, "✅ ".len() as u32))
|
||||
.collect::<String>(),
|
||||
" α\nβ \n🏀β γ"
|
||||
);
|
||||
|
||||
// Clipping display points inside of multi-byte characters
|
||||
assert_eq!(
|
||||
map.snapshot(ctx)
|
||||
.clip_point(DisplayPoint::new(0, "✅".len() as u32 - 1), Bias::Left),
|
||||
DisplayPoint::new(0, 0)
|
||||
);
|
||||
assert_eq!(
|
||||
map.snapshot(ctx)
|
||||
.clip_point(DisplayPoint::new(0, "✅".len() as u32 - 1), Bias::Right),
|
||||
DisplayPoint::new(0, "✅".len() as u32)
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_max_point(app: &mut gpui::MutableAppContext) {
|
||||
let buffer = app.add_model(|ctx| Buffer::new(0, "aaa\n\t\tbbb", ctx));
|
||||
let map = DisplayMap::new(buffer.clone(), 4, app.as_ref());
|
||||
assert_eq!(map.max_point(app.as_ref()), DisplayPoint::new(1, 11))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user