Support expanding tabs
This commit is contained in:
parent
56812a8348
commit
3c94352f3f
9 changed files with 82 additions and 25 deletions
|
|
@ -210,6 +210,7 @@ pub struct Buffer {
|
||||||
redraw: bool,
|
redraw: bool,
|
||||||
wrap: Wrap,
|
wrap: Wrap,
|
||||||
monospace_width: Option<f32>,
|
monospace_width: Option<f32>,
|
||||||
|
tab_width: u16,
|
||||||
|
|
||||||
/// Scratch buffer for shaping and laying out.
|
/// Scratch buffer for shaping and laying out.
|
||||||
scratch: ShapeBuffer,
|
scratch: ShapeBuffer,
|
||||||
|
|
@ -226,6 +227,7 @@ impl Clone for Buffer {
|
||||||
redraw: self.redraw,
|
redraw: self.redraw,
|
||||||
wrap: self.wrap,
|
wrap: self.wrap,
|
||||||
monospace_width: self.monospace_width,
|
monospace_width: self.monospace_width,
|
||||||
|
tab_width: self.tab_width,
|
||||||
scratch: ShapeBuffer::default(),
|
scratch: ShapeBuffer::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -255,6 +257,7 @@ impl Buffer {
|
||||||
wrap: Wrap::WordOrGlyph,
|
wrap: Wrap::WordOrGlyph,
|
||||||
scratch: ShapeBuffer::default(),
|
scratch: ShapeBuffer::default(),
|
||||||
monospace_width: None,
|
monospace_width: None,
|
||||||
|
tab_width: 8,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -294,6 +297,7 @@ impl Buffer {
|
||||||
self.width,
|
self.width,
|
||||||
self.wrap,
|
self.wrap,
|
||||||
self.monospace_width,
|
self.monospace_width,
|
||||||
|
self.tab_width,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -510,7 +514,7 @@ impl Buffer {
|
||||||
line_i: usize,
|
line_i: usize,
|
||||||
) -> Option<&ShapeLine> {
|
) -> Option<&ShapeLine> {
|
||||||
let line = self.lines.get_mut(line_i)?;
|
let line = self.lines.get_mut(line_i)?;
|
||||||
Some(line.shape_in_buffer(&mut self.scratch, font_system))
|
Some(line.shape_in_buffer(&mut self.scratch, font_system, self.tab_width))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lay out the provided line index and return the result
|
/// Lay out the provided line index and return the result
|
||||||
|
|
@ -527,6 +531,7 @@ impl Buffer {
|
||||||
self.width,
|
self.width,
|
||||||
self.wrap,
|
self.wrap,
|
||||||
self.monospace_width,
|
self.monospace_width,
|
||||||
|
self.tab_width,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -576,6 +581,30 @@ impl Buffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the current `tab_width`
|
||||||
|
pub fn tab_width(&self) -> u16 {
|
||||||
|
self.tab_width
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set tab width (number of spaces between tab stops)
|
||||||
|
pub fn set_tab_width(&mut self, font_system: &mut FontSystem, tab_width: u16) {
|
||||||
|
// A tab width of 0 is not allowed
|
||||||
|
if tab_width == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if tab_width != self.tab_width {
|
||||||
|
self.tab_width = tab_width;
|
||||||
|
// Shaping must be reset when tab width is changed
|
||||||
|
for line in self.lines.iter_mut() {
|
||||||
|
if line.text().contains('\t') {
|
||||||
|
line.reset_shaping();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.redraw = true;
|
||||||
|
self.shape_until_scroll(font_system, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the current buffer dimensions (width, height)
|
/// Get the current buffer dimensions (width, height)
|
||||||
pub fn size(&self) -> (f32, f32) {
|
pub fn size(&self) -> (f32, f32) {
|
||||||
(self.width, self.height)
|
(self.width, self.height)
|
||||||
|
|
|
||||||
|
|
@ -182,8 +182,8 @@ impl BufferLine {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shape line, will cache results
|
/// Shape line, will cache results
|
||||||
pub fn shape(&mut self, font_system: &mut FontSystem) -> &ShapeLine {
|
pub fn shape(&mut self, font_system: &mut FontSystem, tab_width: u16) -> &ShapeLine {
|
||||||
self.shape_in_buffer(&mut ShapeBuffer::default(), font_system)
|
self.shape_in_buffer(&mut ShapeBuffer::default(), font_system, tab_width)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shape a line using a pre-existing shape buffer, will cache results
|
/// Shape a line using a pre-existing shape buffer, will cache results
|
||||||
|
|
@ -191,6 +191,7 @@ impl BufferLine {
|
||||||
&mut self,
|
&mut self,
|
||||||
scratch: &mut ShapeBuffer,
|
scratch: &mut ShapeBuffer,
|
||||||
font_system: &mut FontSystem,
|
font_system: &mut FontSystem,
|
||||||
|
tab_width: u16,
|
||||||
) -> &ShapeLine {
|
) -> &ShapeLine {
|
||||||
if self.shape_opt.is_none() {
|
if self.shape_opt.is_none() {
|
||||||
self.shape_opt = Some(ShapeLine::new_in_buffer(
|
self.shape_opt = Some(ShapeLine::new_in_buffer(
|
||||||
|
|
@ -199,6 +200,7 @@ impl BufferLine {
|
||||||
&self.text,
|
&self.text,
|
||||||
&self.attrs_list,
|
&self.attrs_list,
|
||||||
self.shaping,
|
self.shaping,
|
||||||
|
tab_width,
|
||||||
));
|
));
|
||||||
self.layout_opt = None;
|
self.layout_opt = None;
|
||||||
}
|
}
|
||||||
|
|
@ -218,6 +220,7 @@ impl BufferLine {
|
||||||
width: f32,
|
width: f32,
|
||||||
wrap: Wrap,
|
wrap: Wrap,
|
||||||
match_mono_width: Option<f32>,
|
match_mono_width: Option<f32>,
|
||||||
|
tab_width: u16,
|
||||||
) -> &[LayoutLine] {
|
) -> &[LayoutLine] {
|
||||||
self.layout_in_buffer(
|
self.layout_in_buffer(
|
||||||
&mut ShapeBuffer::default(),
|
&mut ShapeBuffer::default(),
|
||||||
|
|
@ -226,6 +229,7 @@ impl BufferLine {
|
||||||
width,
|
width,
|
||||||
wrap,
|
wrap,
|
||||||
match_mono_width,
|
match_mono_width,
|
||||||
|
tab_width,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -238,10 +242,11 @@ impl BufferLine {
|
||||||
width: f32,
|
width: f32,
|
||||||
wrap: Wrap,
|
wrap: Wrap,
|
||||||
match_mono_width: Option<f32>,
|
match_mono_width: Option<f32>,
|
||||||
|
tab_width: u16,
|
||||||
) -> &[LayoutLine] {
|
) -> &[LayoutLine] {
|
||||||
if self.layout_opt.is_none() {
|
if self.layout_opt.is_none() {
|
||||||
let align = self.align;
|
let align = self.align;
|
||||||
let shape = self.shape_in_buffer(scratch, font_system);
|
let shape = self.shape_in_buffer(scratch, font_system, tab_width);
|
||||||
let mut layout = Vec::with_capacity(1);
|
let mut layout = Vec::with_capacity(1);
|
||||||
shape.layout_to_buffer(
|
shape.layout_to_buffer(
|
||||||
scratch,
|
scratch,
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ pub struct Editor<'buffer> {
|
||||||
selection: Selection,
|
selection: Selection,
|
||||||
cursor_moved: bool,
|
cursor_moved: bool,
|
||||||
auto_indent: bool,
|
auto_indent: bool,
|
||||||
tab_width: u16,
|
|
||||||
change: Option<Change>,
|
change: Option<Change>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -38,7 +37,6 @@ impl<'buffer> Editor<'buffer> {
|
||||||
selection: Selection::None,
|
selection: Selection::None,
|
||||||
cursor_moved: false,
|
cursor_moved: false,
|
||||||
auto_indent: false,
|
auto_indent: false,
|
||||||
tab_width: 4,
|
|
||||||
change: None,
|
change: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -259,18 +257,11 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tab_width(&self) -> u16 {
|
fn tab_width(&self) -> u16 {
|
||||||
self.tab_width
|
self.with_buffer(|buffer| buffer.tab_width())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_tab_width(&mut self, tab_width: u16) {
|
fn set_tab_width(&mut self, font_system: &mut FontSystem, tab_width: u16) {
|
||||||
// A tab width of 0 is not allowed
|
self.with_buffer_mut(|buffer| buffer.set_tab_width(font_system, tab_width));
|
||||||
if tab_width == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if self.tab_width != tab_width {
|
|
||||||
self.tab_width = tab_width;
|
|
||||||
self.with_buffer_mut(|buffer| buffer.set_redraw(true));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shape_as_needed(&mut self, font_system: &mut FontSystem, prune: bool) {
|
fn shape_as_needed(&mut self, font_system: &mut FontSystem, prune: bool) {
|
||||||
|
|
@ -682,7 +673,7 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// For every line in selection
|
// For every line in selection
|
||||||
let tab_width: usize = self.tab_width.into();
|
let tab_width: usize = self.tab_width().into();
|
||||||
for line_i in start.line..=end.line {
|
for line_i in start.line..=end.line {
|
||||||
// Determine indexes of last indent and first character after whitespace
|
// Determine indexes of last indent and first character after whitespace
|
||||||
let mut after_whitespace = 0;
|
let mut after_whitespace = 0;
|
||||||
|
|
@ -745,7 +736,7 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// For every line in selection
|
// For every line in selection
|
||||||
let tab_width: usize = self.tab_width.into();
|
let tab_width: usize = self.tab_width().into();
|
||||||
for line_i in start.line..=end.line {
|
for line_i in start.line..=end.line {
|
||||||
// Determine indexes of last indent and first character after whitespace
|
// Determine indexes of last indent and first character after whitespace
|
||||||
let mut last_indent = 0;
|
let mut last_indent = 0;
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,7 @@ pub trait Edit<'buffer> {
|
||||||
fn tab_width(&self) -> u16;
|
fn tab_width(&self) -> u16;
|
||||||
|
|
||||||
/// Set the current tab width. A `tab_width` of 0 is not allowed, and will be ignored
|
/// Set the current tab width. A `tab_width` of 0 is not allowed, and will be ignored
|
||||||
fn set_tab_width(&mut self, tab_width: u16);
|
fn set_tab_width(&mut self, font_system: &mut FontSystem, tab_width: u16);
|
||||||
|
|
||||||
/// Shape lines until scroll, after adjusting scroll if the cursor moved
|
/// Shape lines until scroll, after adjusting scroll if the cursor moved
|
||||||
fn shape_as_needed(&mut self, font_system: &mut FontSystem, prune: bool);
|
fn shape_as_needed(&mut self, font_system: &mut FontSystem, prune: bool);
|
||||||
|
|
@ -336,6 +336,11 @@ impl<'font_system, 'buffer, E: Edit<'buffer>> BorrowedWithFontSystem<'font_syste
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the current tab width. A `tab_width` of 0 is not allowed, and will be ignored
|
||||||
|
pub fn set_tab_width(&mut self, tab_width: u16) {
|
||||||
|
self.inner.set_tab_width(self.font_system, tab_width);
|
||||||
|
}
|
||||||
|
|
||||||
/// Shape lines until scroll, after adjusting scroll if the cursor moved
|
/// Shape lines until scroll, after adjusting scroll if the cursor moved
|
||||||
pub fn shape_as_needed(&mut self, prune: bool) {
|
pub fn shape_as_needed(&mut self, prune: bool) {
|
||||||
self.inner.shape_as_needed(self.font_system, prune);
|
self.inner.shape_as_needed(self.font_system, prune);
|
||||||
|
|
|
||||||
|
|
@ -250,8 +250,8 @@ impl<'syntax_system, 'buffer> Edit<'buffer> for SyntaxEditor<'syntax_system, 'bu
|
||||||
self.editor.tab_width()
|
self.editor.tab_width()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_tab_width(&mut self, tab_width: u16) {
|
fn set_tab_width(&mut self, font_system: &mut FontSystem, tab_width: u16) {
|
||||||
self.editor.set_tab_width(tab_width);
|
self.editor.set_tab_width(font_system, tab_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shape_as_needed(&mut self, font_system: &mut FontSystem, prune: bool) {
|
fn shape_as_needed(&mut self, font_system: &mut FontSystem, prune: bool) {
|
||||||
|
|
|
||||||
|
|
@ -550,8 +550,8 @@ impl<'syntax_system, 'buffer> Edit<'buffer> for ViEditor<'syntax_system, 'buffer
|
||||||
self.editor.tab_width()
|
self.editor.tab_width()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_tab_width(&mut self, tab_width: u16) {
|
fn set_tab_width(&mut self, font_system: &mut FontSystem, tab_width: u16) {
|
||||||
self.editor.set_tab_width(tab_width);
|
self.editor.set_tab_width(font_system, tab_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shape_as_needed(&mut self, font_system: &mut FontSystem, prune: bool) {
|
fn shape_as_needed(&mut self, font_system: &mut FontSystem, prune: bool) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,11 @@
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
pub use libm::{roundf, truncf};
|
pub use libm::{floorf, roundf, truncf};
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
#[inline]
|
||||||
|
pub fn floorf(x: f32) -> f32 {
|
||||||
|
x.floor()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
||||||
21
src/shape.rs
21
src/shape.rs
|
|
@ -763,6 +763,7 @@ impl ShapeLine {
|
||||||
line: &str,
|
line: &str,
|
||||||
attrs_list: &AttrsList,
|
attrs_list: &AttrsList,
|
||||||
shaping: Shaping,
|
shaping: Shaping,
|
||||||
|
tab_width: u16,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new_in_buffer(
|
Self::new_in_buffer(
|
||||||
&mut ShapeBuffer::default(),
|
&mut ShapeBuffer::default(),
|
||||||
|
|
@ -770,6 +771,7 @@ impl ShapeLine {
|
||||||
line,
|
line,
|
||||||
attrs_list,
|
attrs_list,
|
||||||
shaping,
|
shaping,
|
||||||
|
tab_width,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -785,6 +787,7 @@ impl ShapeLine {
|
||||||
line: &str,
|
line: &str,
|
||||||
attrs_list: &AttrsList,
|
attrs_list: &AttrsList,
|
||||||
shaping: Shaping,
|
shaping: Shaping,
|
||||||
|
tab_width: u16,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut spans = Vec::new();
|
let mut spans = Vec::new();
|
||||||
|
|
||||||
|
|
@ -844,6 +847,24 @@ impl ShapeLine {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adjust for tabs
|
||||||
|
let mut x = 0.0;
|
||||||
|
for span in spans.iter_mut() {
|
||||||
|
for word in span.words.iter_mut() {
|
||||||
|
for glyph in word.glyphs.iter_mut() {
|
||||||
|
if &line[glyph.start..glyph.end] == "\t" {
|
||||||
|
//TODO: better fallback for width
|
||||||
|
let space_x_advance =
|
||||||
|
glyph.font_monospace_em_width.unwrap_or(glyph.x_advance);
|
||||||
|
let tab_x_advance = (tab_width as f32) * space_x_advance;
|
||||||
|
let tab_stop = (math::floorf(x / tab_x_advance) + 1.0) * tab_x_advance;
|
||||||
|
glyph.x_advance = tab_stop - x;
|
||||||
|
}
|
||||||
|
x += glyph.x_advance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
rtl,
|
rtl,
|
||||||
spans,
|
spans,
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ fn stable_wrap() {
|
||||||
font_system.db_mut().load_font_data(font);
|
font_system.db_mut().load_font_data(font);
|
||||||
|
|
||||||
let mut check_wrap = |text: &_, wrap, start_width| {
|
let mut check_wrap = |text: &_, wrap, start_width| {
|
||||||
let line = ShapeLine::new(&mut font_system, text, &attrs, Shaping::Advanced);
|
let line = ShapeLine::new(&mut font_system, text, &attrs, Shaping::Advanced, 8);
|
||||||
|
|
||||||
let layout_unbounded = line.layout(font_size, start_width, wrap, Some(Align::Left), None);
|
let layout_unbounded = line.layout(font_size, start_width, wrap, Some(Align::Left), None);
|
||||||
let max_width = layout_unbounded.iter().map(|l| l.w).fold(0.0, f32::max);
|
let max_width = layout_unbounded.iter().map(|l| l.w).fold(0.0, f32::max);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue