We now store character widths in TextBlocks.

This is the first step towards supporting proportional fonts
and tabs characters.
This commit is contained in:
Nathan Vegdahl 2014-12-28 13:44:38 -08:00
parent 9e35085573
commit e3e2c866b9
2 changed files with 95 additions and 6 deletions

View File

@ -2,31 +2,49 @@
use std::mem; use std::mem;
use std::fmt; use std::fmt;
use string_utils::char_pos_to_byte_pos; use string_utils::{char_pos_to_byte_pos, char_count};
/// A block of text, contiguous in memory /// A block of text, contiguous in memory
pub struct TextBlock { pub struct TextBlock {
// The actual text data, in utf8
pub data: Vec<u8>, pub data: Vec<u8>,
// The visual width of each printable character.
// Characters with variable width (e.g. tab characters)
// have width None.
pub widths: Vec<Option<u8>>,
} }
impl TextBlock { impl TextBlock {
/// Create a new empty text block. /// Create a new empty text block.
pub fn new() -> TextBlock { pub fn new() -> TextBlock {
TextBlock { TextBlock {
data: Vec::<u8>::new() data: Vec::<u8>::new(),
widths: Vec::<Option<u8>>::new(),
} }
} }
/// Create a new text block with the contents of 'text'. /// Create a new text block with the contents of 'text'.
pub fn new_from_str(text: &str) -> TextBlock { pub fn new_from_str(text: &str) -> TextBlock {
let mut tb = TextBlock { let mut tb = TextBlock {
data: Vec::<u8>::with_capacity(text.len()) data: Vec::<u8>::with_capacity(text.len()),
widths: Vec::<Option<u8>>::with_capacity(text.len()),
}; };
for b in text.bytes() { for b in text.bytes() {
tb.data.push(b); tb.data.push(b);
} }
// TODO: handle fonts
for c in text.chars() {
if c == '\t' {
tb.widths.push(None);
}
else {
tb.widths.push(Some(1));
}
}
return tb; return tb;
} }
@ -35,8 +53,35 @@ impl TextBlock {
self.data.len() self.data.len()
} }
/// Returns the total width of text block sans-variable-width characters
pub fn total_non_variable_width(&self) -> uint {
let mut width: uint = 0;
for w in self.widths.iter() {
if let &Some(ww) = w {
width += ww as uint;
}
}
return width;
}
/// Returns the number of variable-width chars in the text block
pub fn variable_width_chars(&self) -> uint {
let mut count: uint = 0;
for w in self.widths.iter() {
if let &None = w {
count += 1;
}
}
return count;
}
/// Insert 'text' at char position 'pos'. /// Insert 'text' at char position 'pos'.
pub fn insert_text(&mut self, text: &str, pos: uint) { pub fn insert_text(&mut self, text: &str, pos: uint) {
//====== TEXT DATA ======
// Find insertion position in bytes // Find insertion position in bytes
let byte_pos = char_pos_to_byte_pos(self.as_str(), pos); let byte_pos = char_pos_to_byte_pos(self.as_str(), pos);
@ -59,6 +104,33 @@ impl TextBlock {
self.data[i] = b; self.data[i] = b;
i += 1 i += 1
} }
//====== WIDTHS ======
// Grow widths size
let cc = char_count(text);
self.widths.grow(cc, None);
// Move old widths forward
from = self.widths.len() - cc;
to = self.widths.len();
while from > pos {
from -= 1;
to -= 1;
self.widths[to] = self.widths[from];
}
// Copy new widths in
i = pos;
for c in text.chars() {
if c == '\t' {
self.widths[i] = None;
}
else {
self.widths[i] = Some(1);
}
i += 1
}
} }
/// Remove the text between char positions 'pos_a' and 'pos_b'. /// Remove the text between char positions 'pos_a' and 'pos_b'.
@ -68,6 +140,7 @@ impl TextBlock {
panic!("TextBlock::remove_text(): pos_a must be less than or equal to pos_b."); panic!("TextBlock::remove_text(): pos_a must be less than or equal to pos_b.");
} }
//====== TEXT DATA ======
// Find removal positions in bytes // Find removal positions in bytes
let byte_pos_a = char_pos_to_byte_pos(self.as_str(), pos_a); let byte_pos_a = char_pos_to_byte_pos(self.as_str(), pos_a);
let byte_pos_b = char_pos_to_byte_pos(self.as_str(), pos_b); let byte_pos_b = char_pos_to_byte_pos(self.as_str(), pos_b);
@ -83,8 +156,22 @@ impl TextBlock {
} }
// Remove data from the end // Remove data from the end
let final_size = self.data.len() + byte_pos_a - byte_pos_b; let final_data_size = self.data.len() + byte_pos_a - byte_pos_b;
self.data.truncate(final_size); self.data.truncate(final_data_size);
//====== WIDTHS ======
from = pos_b;
to = pos_a;
while from < self.widths.len() {
self.widths[to] = self.widths[from];
from += 1;
to += 1;
}
// Remove data from end
let final_widths_size = self.widths.len() + pos_a - pos_b;
self.data.truncate(final_widths_size);
} }
/// Returns an immutable string slice into the text block's memory /// Returns an immutable string slice into the text block's memory

View File

@ -1,4 +1,6 @@
- Proper handling of tab characters - Proper handling of non-uniform-width characters. Specifically, this needs
to address tabs. But it should be done to handle the general case anyway,
since that's unlikely to be more complex and will future-proof things.
- Line number display - Line number display
- Editor info display (filename, current line/column, indentation style, etc.) - Editor info display (filename, current line/column, indentation style, etc.)
- File opening by entering path - File opening by entering path