We now store character widths in TextBlocks.
This is the first step towards supporting proportional fonts and tabs characters.
This commit is contained in:
parent
9e35085573
commit
e3e2c866b9
|
@ -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
|
||||
|
|
4
todo.md
4
todo.md
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user