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::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
|
||||||
|
|
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
|
- 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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user