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::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
pub struct TextBlock {
// The actual text data, in utf8
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 {
/// Create a new empty text block.
pub fn new() -> 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'.
pub fn new_from_str(text: &str) -> 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() {
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;
}
@ -35,8 +53,35 @@ impl TextBlock {
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'.
pub fn insert_text(&mut self, text: &str, pos: uint) {
//====== TEXT DATA ======
// Find insertion position in bytes
let byte_pos = char_pos_to_byte_pos(self.as_str(), pos);
@ -59,6 +104,33 @@ impl TextBlock {
self.data[i] = b;
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'.
@ -68,6 +140,7 @@ impl TextBlock {
panic!("TextBlock::remove_text(): pos_a must be less than or equal to pos_b.");
}
//====== TEXT DATA ======
// Find removal positions in bytes
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);
@ -83,8 +156,22 @@ impl TextBlock {
}
// Remove data from the end
let final_size = self.data.len() + byte_pos_a - byte_pos_b;
self.data.truncate(final_size);
let final_data_size = self.data.len() + byte_pos_a - byte_pos_b;
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

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
- Editor info display (filename, current line/column, indentation style, etc.)
- File opening by entering path