Skip to content

Commit 8ac2e3a

Browse files
committed
Support expanding tabs
1 parent 31d3cec commit 8ac2e3a

9 files changed

+85
-24
lines changed

src/buffer.rs

+30-1
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ pub struct Buffer {
221221
redraw: bool,
222222
wrap: Wrap,
223223
monospace_width: Option<f32>,
224+
tab_width: u16,
224225

225226
/// Scratch buffer for shaping and laying out.
226227
scratch: ShapeBuffer,
@@ -237,6 +238,7 @@ impl Clone for Buffer {
237238
redraw: self.redraw,
238239
wrap: self.wrap,
239240
monospace_width: self.monospace_width,
241+
tab_width: self.tab_width,
240242
scratch: ShapeBuffer::default(),
241243
}
242244
}
@@ -266,6 +268,7 @@ impl Buffer {
266268
wrap: Wrap::WordOrGlyph,
267269
scratch: ShapeBuffer::default(),
268270
monospace_width: None,
271+
tab_width: 8,
269272
}
270273
}
271274

@@ -305,6 +308,7 @@ impl Buffer {
305308
self.width,
306309
self.wrap,
307310
self.monospace_width,
311+
self.tab_width,
308312
);
309313
}
310314
}
@@ -496,7 +500,7 @@ impl Buffer {
496500
line_i: usize,
497501
) -> Option<&ShapeLine> {
498502
let line = self.lines.get_mut(line_i)?;
499-
Some(line.shape_in_buffer(&mut self.scratch, font_system))
503+
Some(line.shape_in_buffer(&mut self.scratch, font_system, self.tab_width))
500504
}
501505

502506
/// Lay out the provided line index and return the result
@@ -513,6 +517,7 @@ impl Buffer {
513517
self.width,
514518
self.wrap,
515519
self.monospace_width,
520+
self.tab_width,
516521
))
517522
}
518523

@@ -562,6 +567,30 @@ impl Buffer {
562567
}
563568
}
564569

570+
/// Get the current `tab_width`
571+
pub fn tab_width(&self) -> u16 {
572+
self.tab_width
573+
}
574+
575+
/// Set tab width (number of spaces between tab stops)
576+
pub fn set_tab_width(&mut self, font_system: &mut FontSystem, tab_width: u16) {
577+
// A tab width of 0 is not allowed
578+
if tab_width == 0 {
579+
return;
580+
}
581+
if tab_width != self.tab_width {
582+
self.tab_width = tab_width;
583+
// Shaping must be reset when tab width is changed
584+
for line in self.lines.iter_mut() {
585+
if line.text().contains('\t') {
586+
line.reset_shaping();
587+
}
588+
}
589+
self.redraw = true;
590+
self.shape_until_scroll(font_system, false);
591+
}
592+
}
593+
565594
/// Get the current buffer dimensions (width, height)
566595
pub fn size(&self) -> (f32, f32) {
567596
(self.width, self.height)

src/buffer_line.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -182,15 +182,18 @@ impl BufferLine {
182182
}
183183

184184
/// Shape line, will cache results
185+
//TODO: replace with shape_in_buffer?
186+
//TODO: add tab_width
185187
pub fn shape(&mut self, font_system: &mut FontSystem) -> &ShapeLine {
186-
self.shape_in_buffer(&mut ShapeBuffer::default(), font_system)
188+
self.shape_in_buffer(&mut ShapeBuffer::default(), font_system, 8)
187189
}
188190

189191
/// Shape a line using a pre-existing shape buffer, will cache results
190192
pub fn shape_in_buffer(
191193
&mut self,
192194
scratch: &mut ShapeBuffer,
193195
font_system: &mut FontSystem,
196+
tab_width: u16,
194197
) -> &ShapeLine {
195198
if self.shape_opt.is_none() {
196199
self.shape_opt = Some(ShapeLine::new_in_buffer(
@@ -199,6 +202,7 @@ impl BufferLine {
199202
&self.text,
200203
&self.attrs_list,
201204
self.shaping,
205+
tab_width,
202206
));
203207
self.layout_opt = None;
204208
}
@@ -211,6 +215,8 @@ impl BufferLine {
211215
}
212216

213217
/// Layout line, will cache results
218+
//TODO: replace with layout_in_buffer?
219+
//TODO: add tab_width
214220
pub fn layout(
215221
&mut self,
216222
font_system: &mut FontSystem,
@@ -226,6 +232,7 @@ impl BufferLine {
226232
width,
227233
wrap,
228234
match_mono_width,
235+
8,
229236
)
230237
}
231238

@@ -238,10 +245,11 @@ impl BufferLine {
238245
width: f32,
239246
wrap: Wrap,
240247
match_mono_width: Option<f32>,
248+
tab_width: u16,
241249
) -> &[LayoutLine] {
242250
if self.layout_opt.is_none() {
243251
let align = self.align;
244-
let shape = self.shape_in_buffer(scratch, font_system);
252+
let shape = self.shape_in_buffer(scratch, font_system, tab_width);
245253
let mut layout = Vec::with_capacity(1);
246254
shape.layout_to_buffer(
247255
scratch,

src/edit/editor.rs

+5-14
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ pub struct Editor<'buffer> {
2424
selection: Selection,
2525
cursor_moved: bool,
2626
auto_indent: bool,
27-
tab_width: u16,
2827
change: Option<Change>,
2928
}
3029

@@ -38,7 +37,6 @@ impl<'buffer> Editor<'buffer> {
3837
selection: Selection::None,
3938
cursor_moved: false,
4039
auto_indent: false,
41-
tab_width: 4,
4240
change: None,
4341
}
4442
}
@@ -259,18 +257,11 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> {
259257
}
260258

261259
fn tab_width(&self) -> u16 {
262-
self.tab_width
260+
self.with_buffer(|buffer| buffer.tab_width())
263261
}
264262

265-
fn set_tab_width(&mut self, tab_width: u16) {
266-
// A tab width of 0 is not allowed
267-
if tab_width == 0 {
268-
return;
269-
}
270-
if self.tab_width != tab_width {
271-
self.tab_width = tab_width;
272-
self.with_buffer_mut(|buffer| buffer.set_redraw(true));
273-
}
263+
fn set_tab_width(&mut self, font_system: &mut FontSystem, tab_width: u16) {
264+
self.with_buffer_mut(|buffer| buffer.set_tab_width(font_system, tab_width));
274265
}
275266

276267
fn shape_as_needed(&mut self, font_system: &mut FontSystem, prune: bool) {
@@ -682,7 +673,7 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> {
682673
};
683674

684675
// For every line in selection
685-
let tab_width: usize = self.tab_width.into();
676+
let tab_width: usize = self.tab_width().into();
686677
for line_i in start.line..=end.line {
687678
// Determine indexes of last indent and first character after whitespace
688679
let mut after_whitespace = 0;
@@ -745,7 +736,7 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> {
745736
};
746737

747738
// For every line in selection
748-
let tab_width: usize = self.tab_width.into();
739+
let tab_width: usize = self.tab_width().into();
749740
for line_i in start.line..=end.line {
750741
// Determine indexes of last indent and first character after whitespace
751742
let mut last_indent = 0;

src/edit/mod.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ pub trait Edit<'buffer> {
282282
fn tab_width(&self) -> u16;
283283

284284
/// Set the current tab width. A `tab_width` of 0 is not allowed, and will be ignored
285-
fn set_tab_width(&mut self, tab_width: u16);
285+
fn set_tab_width(&mut self, font_system: &mut FontSystem, tab_width: u16);
286286

287287
/// Shape lines until scroll, after adjusting scroll if the cursor moved
288288
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
336336
})
337337
}
338338

339+
/// Set the current tab width. A `tab_width` of 0 is not allowed, and will be ignored
340+
pub fn set_tab_width(&mut self, tab_width: u16) {
341+
self.inner.set_tab_width(self.font_system, tab_width);
342+
}
343+
339344
/// Shape lines until scroll, after adjusting scroll if the cursor moved
340345
pub fn shape_as_needed(&mut self, prune: bool) {
341346
self.inner.shape_as_needed(self.font_system, prune);

src/edit/syntect.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -250,8 +250,8 @@ impl<'syntax_system, 'buffer> Edit<'buffer> for SyntaxEditor<'syntax_system, 'bu
250250
self.editor.tab_width()
251251
}
252252

253-
fn set_tab_width(&mut self, tab_width: u16) {
254-
self.editor.set_tab_width(tab_width);
253+
fn set_tab_width(&mut self, font_system: &mut FontSystem, tab_width: u16) {
254+
self.editor.set_tab_width(font_system, tab_width);
255255
}
256256

257257
fn shape_as_needed(&mut self, font_system: &mut FontSystem, prune: bool) {

src/edit/vi.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -550,8 +550,8 @@ impl<'syntax_system, 'buffer> Edit<'buffer> for ViEditor<'syntax_system, 'buffer
550550
self.editor.tab_width()
551551
}
552552

553-
fn set_tab_width(&mut self, tab_width: u16) {
554-
self.editor.set_tab_width(tab_width);
553+
fn set_tab_width(&mut self, font_system: &mut FontSystem, tab_width: u16) {
554+
self.editor.set_tab_width(font_system, tab_width);
555555
}
556556

557557
fn shape_as_needed(&mut self, font_system: &mut FontSystem, prune: bool) {

src/math.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
#[cfg(not(feature = "std"))]
2-
pub use libm::{roundf, truncf};
2+
pub use libm::{floorf, roundf, truncf};
3+
4+
#[cfg(feature = "std")]
5+
#[inline]
6+
pub fn floorf(x: f32) -> f32 {
7+
x.floor()
8+
}
39

410
#[cfg(feature = "std")]
511
#[inline]

src/shape.rs

+22
Original file line numberDiff line numberDiff line change
@@ -762,13 +762,15 @@ impl ShapeLine {
762762
line: &str,
763763
attrs_list: &AttrsList,
764764
shaping: Shaping,
765+
tab_width: u16,
765766
) -> Self {
766767
Self::new_in_buffer(
767768
&mut ShapeBuffer::default(),
768769
font_system,
769770
line,
770771
attrs_list,
771772
shaping,
773+
tab_width,
772774
)
773775
}
774776

@@ -784,6 +786,7 @@ impl ShapeLine {
784786
line: &str,
785787
attrs_list: &AttrsList,
786788
shaping: Shaping,
789+
tab_width: u16,
787790
) -> Self {
788791
let mut spans = Vec::new();
789792

@@ -843,6 +846,25 @@ impl ShapeLine {
843846
));
844847
}
845848

849+
// Adjust for tabs
850+
//TODO: is there a cleaner way to do this?
851+
let mut x = 0.0;
852+
for span in spans.iter_mut() {
853+
for word in span.words.iter_mut() {
854+
for glyph in word.glyphs.iter_mut() {
855+
if &line[glyph.start..glyph.end] == "\t" {
856+
//TODO: better fallback for width
857+
let space_x_advance =
858+
glyph.font_monospace_em_width.unwrap_or(glyph.x_advance);
859+
let tab_x_advance = (tab_width as f32) * space_x_advance;
860+
let tab_stop = (math::floorf(x / tab_x_advance) + 1.0) * tab_x_advance;
861+
glyph.x_advance = tab_stop - x;
862+
}
863+
x += glyph.x_advance;
864+
}
865+
}
866+
}
867+
846868
Self { rtl, spans }
847869
}
848870

tests/wrap_stability.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ fn stable_wrap() {
2121
font_system.db_mut().load_font_data(font);
2222

2323
let mut check_wrap = |text: &_, wrap, start_width| {
24-
let line = ShapeLine::new(&mut font_system, text, &attrs, Shaping::Advanced);
24+
let line = ShapeLine::new(&mut font_system, text, &attrs, Shaping::Advanced, 8);
2525

2626
let layout_unbounded = line.layout(font_size, start_width, wrap, Some(Align::Left), None);
2727
let max_width = layout_unbounded.iter().map(|l| l.w).fold(0.0, f32::max);

0 commit comments

Comments
 (0)