Merge pull request #1132 from rtfeldman/insert_string

Insert string literal
This commit is contained in:
Richard Feldman 2021-03-31 19:29:49 -04:00 committed by GitHub
commit d193b9f55f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 317 additions and 163 deletions

View file

@ -19,13 +19,6 @@ impl CodeLines {
} }
} }
pub fn add_to_line(&mut self, line_nr: usize, new_str: &str) -> UIResult<()> {
let line_ref = slice_get_mut(line_nr, &mut self.lines)?;
line_ref.push_str(new_str);
Ok(())
}
pub fn insert_between_line( pub fn insert_between_line(
&mut self, &mut self,
line_nr: usize, line_nr: usize,
@ -36,6 +29,8 @@ impl CodeLines {
line_ref.insert_str(index, new_str); line_ref.insert_str(index, new_str);
self.nr_of_chars += new_str.len();
Ok(()) Ok(())
} }
@ -44,6 +39,8 @@ impl CodeLines {
line_ref.remove(index); line_ref.remove(index);
self.nr_of_chars -= 1;
Ok(()) Ok(())
} }
} }

View file

@ -1,4 +1,3 @@
use crate::editor::code_lines::CodeLines;
use crate::editor::ed_error::EdResult; use crate::editor::ed_error::EdResult;
use crate::editor::slow_pool::MarkNodeId; use crate::editor::slow_pool::MarkNodeId;
use crate::editor::util::index_of; use crate::editor::util::index_of;
@ -73,37 +72,3 @@ impl GridNodeMap {
Ok(caret_pos.column - first_node_index) Ok(caret_pos.column - first_node_index)
} }
} }
// perform updates for both GridNodeMap and CodeLines
pub fn add_to_line_both(
grid_node_map: &mut GridNodeMap,
code_lines: &mut CodeLines,
line_nr: usize,
new_str: &str,
node_id: MarkNodeId,
) -> UIResult<()> {
grid_node_map.add_to_line(line_nr, new_str.len(), node_id)?;
code_lines.add_to_line(line_nr, new_str)
}
pub fn insert_between_line_both(
grid_node_map: &mut GridNodeMap,
code_lines: &mut CodeLines,
line_nr: usize,
index: usize,
new_str: &str,
node_id: MarkNodeId,
) -> UIResult<()> {
grid_node_map.insert_between_line(line_nr, index, new_str.len(), node_id)?;
code_lines.insert_between_line(line_nr, index, new_str)
}
pub fn del_at_line_both(
grid_node_map: &mut GridNodeMap,
code_lines: &mut CodeLines,
line_nr: usize,
index: usize,
) -> UIResult<()> {
grid_node_map.del_at_line(line_nr, index)?;
code_lines.del_at_line(line_nr, index)
}

View file

@ -135,6 +135,7 @@ pub const BLANK_PLACEHOLDER: &str = " ";
pub const LEFT_ACCOLADE: &str = "{ "; pub const LEFT_ACCOLADE: &str = "{ ";
pub const RIGHT_ACCOLADE: &str = " }"; pub const RIGHT_ACCOLADE: &str = " }";
pub const COLON: &str = ": "; pub const COLON: &str = ": ";
pub const STRING_QUOTES: &str = "\"\"";
fn new_markup_node( fn new_markup_node(
text: String, text: String,

View file

@ -1,20 +1,20 @@
use crate::editor::code_lines::CodeLines; use crate::editor::code_lines::CodeLines;
use crate::editor::ed_error::EdResult; use crate::editor::ed_error::EdResult;
use crate::editor::grid_node_map::insert_between_line_both;
use crate::editor::grid_node_map::GridNodeMap; use crate::editor::grid_node_map::GridNodeMap;
use crate::editor::markup::attribute::Attributes;
use crate::editor::markup::nodes; use crate::editor::markup::nodes;
use crate::editor::markup::nodes::MarkupNode; use crate::editor::markup::nodes::MarkupNode;
use crate::editor::mvc::ed_model::EdModel; use crate::editor::mvc::ed_model::EdModel;
use crate::editor::mvc::record_update::start_new_record; use crate::editor::mvc::record_update::start_new_record;
use crate::editor::mvc::record_update::update_new_record;
use crate::editor::mvc::record_update::update_record_colon; use crate::editor::mvc::record_update::update_record_colon;
use crate::editor::mvc::record_update::update_record_field; use crate::editor::mvc::record_update::update_record_field;
use crate::editor::mvc::string_update::start_new_string;
use crate::editor::mvc::string_update::update_small_string;
use crate::editor::mvc::string_update::update_string;
use crate::editor::slow_pool::MarkNodeId; use crate::editor::slow_pool::MarkNodeId;
use crate::editor::slow_pool::SlowPool; use crate::editor::slow_pool::SlowPool;
use crate::editor::syntax_highlight::HighlightStyle;
use crate::editor::util::index_of;
use crate::lang::ast::Expr2; use crate::lang::ast::Expr2;
use crate::lang::pool::{NodeId, PoolStr, PoolVec}; use crate::lang::pool::NodeId;
use crate::ui::text::caret_w_select::CaretWSelect; use crate::ui::text::caret_w_select::CaretWSelect;
use crate::ui::text::lines::MoveCaretFun; use crate::ui::text::lines::MoveCaretFun;
use crate::ui::text::selection::validate_raw_sel; use crate::ui::text::selection::validate_raw_sel;
@ -116,6 +116,25 @@ impl<'a> EdModel<'a> {
Ok(()) Ok(())
} }
// updates grid_node_map and code_lines but nothing else.
pub fn insert_between_line(
&mut self,
line_nr: usize,
index: usize,
new_str: &str,
node_id: MarkNodeId,
) -> UIResult<()> {
self.grid_node_map
.insert_between_line(line_nr, index, new_str.len(), node_id)?;
self.code_lines.insert_between_line(line_nr, index, new_str)
}
// updates grid_node_map and code_lines but nothing else.
pub fn del_at_line(&mut self, line_nr: usize, index: usize) -> UIResult<()> {
self.grid_node_map.del_at_line(line_nr, index)?;
self.code_lines.del_at_line(line_nr, index)
}
} }
impl<'a> SelectableLines for EdModel<'a> { impl<'a> SelectableLines for EdModel<'a> {
@ -308,6 +327,10 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult
update_record_colon(ed_model)?; update_record_colon(ed_model)?;
} }
'"' => {
start_new_string(ed_model)?;
}
'\u{8}' | '\u{7f}' => { '\u{8}' | '\u{7f}' => {
// On Linux, '\u{8}' is backspace, // On Linux, '\u{8}' is backspace,
// On macOS '\u{7f}'. // On macOS '\u{7f}'.
@ -347,78 +370,18 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult
None None
}; };
let prev_mark_node_opt = prev_mark_node_id_opt.map(
|prev_mark_node_id| ed_model.markup_node_pool.get(prev_mark_node_id)
);
let parent_id_opt = curr_mark_node.get_parent_id_opt();
let ast_node_id = curr_mark_node.get_ast_node_id(); let ast_node_id = curr_mark_node.get_ast_node_id();
let ast_node_ref = ed_model.module.env.pool.get(ast_node_id); let ast_node_ref = ed_model.module.env.pool.get(ast_node_id);
if let Some(parent_id) = parent_id_opt {
let sibling_ids = curr_mark_node.get_sibling_ids(&ed_model.markup_node_pool);
let new_child_index = index_of(curr_mark_node_id, &sibling_ids)?;
match ast_node_ref { match ast_node_ref {
Expr2::EmptyRecord => { Expr2::EmptyRecord => {
if let Some(prev_mark_node) = prev_mark_node_opt {
if prev_mark_node.get_content()? == nodes::LEFT_ACCOLADE {
// update Markup
let record_field_str = &ch.to_string();
let record_field_node = MarkupNode::Text { let sibling_ids = curr_mark_node.get_sibling_ids(&ed_model.markup_node_pool);
content: record_field_str.to_owned(),
ast_node_id,
syn_high_style: HighlightStyle::RecordField,
attributes: Attributes::new(),
parent_id_opt: Some(parent_id),
};
let record_field_node_id = ed_model.markup_node_pool.add(record_field_node); update_new_record(&ch.to_string(), prev_mark_node_id_opt, sibling_ids, ed_model)?;
let parent = ed_model.markup_node_pool.get_mut(parent_id);
parent.add_child_at_index(new_child_index, record_field_node_id)?;
// update caret
ed_model.simple_move_carets_right();
// update GridNodeMap and CodeLines
insert_between_line_both(
&mut ed_model.grid_node_map,
&mut ed_model.code_lines,
old_caret_pos.line,
old_caret_pos.column,
record_field_str,
record_field_node_id,
)?;
// update AST
let record_var = ed_model.module.env.var_store.fresh();
let field_name = PoolStr::new(record_field_str, &mut ed_model.module.env.pool);
let field_var = ed_model.module.env.var_store.fresh();
//TODO actually check if field_str belongs to a previously defined variable
let field_val = Expr2::InvalidLookup(
PoolStr::new(record_field_str, ed_model.module.env.pool)
);
let field_val_id = ed_model.module.env.pool.add(field_val);
let first_field = (field_name, field_var, field_val_id);
let fields = PoolVec::new(
vec![first_field].into_iter(),
&mut ed_model.module.env.pool,
);
let new_ast_node =
Expr2::Record {
record_var,
fields,
};
ed_model.module.env.pool.set(ast_node_id, new_ast_node);
}
}
}, },
Expr2::Record { record_var:_, fields } => { Expr2::Record { record_var:_, fields } => {
if let Some(prev_mark_node_id) = prev_mark_node_id_opt { if let Some(prev_mark_node_id) = prev_mark_node_id_opt {
@ -442,13 +405,20 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult
)?; )?;
} }
}, },
Expr2::SmallStr(array_str) => {
update_small_string(ch, array_str, ed_model)?;
},
Expr2::Str(old_pool_str) => {
update_string(&ch.to_string(), old_pool_str, ed_model)?;
},
other => { other => {
unimplemented!("TODO implement updating of Expr2 {:?}.", other) unimplemented!("TODO implement updating of Expr2 {:?}.", other)
}, },
} }
} }
} }
}
Ok(()) Ok(())
} }

View file

@ -4,3 +4,4 @@ pub mod ed_model;
pub mod ed_update; pub mod ed_update;
pub mod ed_view; pub mod ed_view;
mod record_update; mod record_update;
mod string_update;

View file

@ -1,9 +1,6 @@
use crate::editor::ed_error::EdResult; use crate::editor::ed_error::EdResult;
use crate::editor::ed_error::MissingParent; use crate::editor::ed_error::MissingParent;
use crate::editor::ed_error::RecordWithoutFields; use crate::editor::ed_error::RecordWithoutFields;
use crate::editor::grid_node_map::add_to_line_both;
use crate::editor::grid_node_map::del_at_line_both;
use crate::editor::grid_node_map::insert_between_line_both;
use crate::editor::markup::attribute::Attributes; use crate::editor::markup::attribute::Attributes;
use crate::editor::markup::nodes; use crate::editor::markup::nodes;
use crate::editor::markup::nodes::MarkupNode; use crate::editor::markup::nodes::MarkupNode;
@ -44,7 +41,7 @@ pub fn start_new_record(ed_model: &mut EdModel) -> EdResult<()> {
ast_node_id, ast_node_id,
syn_high_style: HighlightStyle::Bracket, syn_high_style: HighlightStyle::Bracket,
attributes: Attributes::new(), attributes: Attributes::new(),
parent_id_opt: Some(curr_mark_node_id), parent_id_opt: Some(curr_mark_node_id), // current node will be replace with nested one
}; };
let left_bracket_node_id = mark_node_pool.add(left_bracket_node); let left_bracket_node_id = mark_node_pool.add(left_bracket_node);
@ -54,7 +51,7 @@ pub fn start_new_record(ed_model: &mut EdModel) -> EdResult<()> {
ast_node_id, ast_node_id,
syn_high_style: HighlightStyle::Bracket, syn_high_style: HighlightStyle::Bracket,
attributes: Attributes::new(), attributes: Attributes::new(),
parent_id_opt: Some(curr_mark_node_id), parent_id_opt: Some(curr_mark_node_id), // current node will be replace with nested one
}; };
let right_bracket_node_id = mark_node_pool.add(right_bracket_node); let right_bracket_node_id = mark_node_pool.add(right_bracket_node);
@ -69,30 +66,23 @@ pub fn start_new_record(ed_model: &mut EdModel) -> EdResult<()> {
mark_node_pool.replace_node(curr_mark_node_id, nested_node); mark_node_pool.replace_node(curr_mark_node_id, nested_node);
// remove data corresponding to Blank node // remove data corresponding to Blank node
del_at_line_both( ed_model.del_at_line(old_caret_pos.line, old_caret_pos.column)?;
&mut ed_model.grid_node_map,
&mut ed_model.code_lines,
old_caret_pos.line,
old_caret_pos.column,
)?;
for _ in 0..nodes::LEFT_ACCOLADE.len() { for _ in 0..nodes::LEFT_ACCOLADE.len() {
ed_model.simple_move_carets_right(); ed_model.simple_move_carets_right();
} }
// update GridNodeMap and CodeLines // update GridNodeMap and CodeLines
add_to_line_both( ed_model.insert_between_line(
&mut ed_model.grid_node_map,
&mut ed_model.code_lines,
old_caret_pos.line, old_caret_pos.line,
old_caret_pos.column,
nodes::LEFT_ACCOLADE, nodes::LEFT_ACCOLADE,
left_bracket_node_id, left_bracket_node_id,
)?; )?;
add_to_line_both( ed_model.insert_between_line(
&mut ed_model.grid_node_map,
&mut ed_model.code_lines,
old_caret_pos.line, old_caret_pos.line,
old_caret_pos.column + nodes::LEFT_ACCOLADE.len(),
nodes::RIGHT_ACCOLADE, nodes::RIGHT_ACCOLADE,
right_bracket_node_id, right_bracket_node_id,
)?; )?;
@ -101,6 +91,81 @@ pub fn start_new_record(ed_model: &mut EdModel) -> EdResult<()> {
Ok(()) Ok(())
} }
pub fn update_new_record(
new_input: &str,
prev_mark_node_id_opt: Option<MarkNodeId>,
sibling_ids: Vec<MarkNodeId>,
ed_model: &mut EdModel,
) -> EdResult<()> {
let prev_mark_node_opt = prev_mark_node_id_opt
.map(|prev_mark_node_id| ed_model.markup_node_pool.get(prev_mark_node_id));
if let Some(prev_mark_node) = prev_mark_node_opt {
let NodeContext {
old_caret_pos,
curr_mark_node_id,
curr_mark_node: _,
parent_id_opt,
ast_node_id,
} = get_node_context(&ed_model)?;
if prev_mark_node.get_content()? == nodes::LEFT_ACCOLADE {
// update Markup
let record_field_node = MarkupNode::Text {
content: new_input.to_owned(),
ast_node_id,
syn_high_style: HighlightStyle::RecordField,
attributes: Attributes::new(),
parent_id_opt,
};
let record_field_node_id = ed_model.markup_node_pool.add(record_field_node);
if let Some(parent_id) = parent_id_opt {
let parent = ed_model.markup_node_pool.get_mut(parent_id);
let new_child_index = index_of(curr_mark_node_id, &sibling_ids)?;
parent.add_child_at_index(new_child_index, record_field_node_id)?;
} else {
MissingParent {
node_id: curr_mark_node_id,
}
.fail()?
}
// update caret
ed_model.simple_move_carets_right();
// update GridNodeMap and CodeLines
ed_model.insert_between_line(
old_caret_pos.line,
old_caret_pos.column,
new_input,
record_field_node_id,
)?;
// update AST
let record_var = ed_model.module.env.var_store.fresh();
let field_name = PoolStr::new(new_input, &mut ed_model.module.env.pool);
let field_var = ed_model.module.env.var_store.fresh();
//TODO actually check if field_str belongs to a previously defined variable
let field_val = Expr2::InvalidLookup(PoolStr::new(new_input, ed_model.module.env.pool));
let field_val_id = ed_model.module.env.pool.add(field_val);
let first_field = (field_name, field_var, field_val_id);
let fields = PoolVec::new(vec![first_field].into_iter(), &mut ed_model.module.env.pool);
let new_ast_node = Expr2::Record { record_var, fields };
ed_model.module.env.pool.set(ast_node_id, new_ast_node);
}
}
Ok(())
}
pub fn update_record_field( pub fn update_record_field(
new_input: &str, new_input: &str,
old_caret_pos: TextPos, old_caret_pos: TextPos,
@ -122,9 +187,7 @@ pub fn update_record_field(
} }
// update GridNodeMap and CodeLines // update GridNodeMap and CodeLines
insert_between_line_both( ed_model.insert_between_line(
&mut ed_model.grid_node_map,
&mut ed_model.code_lines,
old_caret_pos.line, old_caret_pos.line,
old_caret_pos.column, old_caret_pos.column,
new_input, new_input,
@ -212,18 +275,16 @@ pub fn update_record_colon(ed_model: &mut EdModel) -> EdResult<()> {
} }
// update GridNodeMap and CodeLines // update GridNodeMap and CodeLines
add_to_line_both( ed_model.insert_between_line(
&mut ed_model.grid_node_map,
&mut ed_model.code_lines,
old_caret_pos.line, old_caret_pos.line,
old_caret_pos.column,
nodes::COLON, nodes::COLON,
record_colon_node_id, record_colon_node_id,
)?; )?;
add_to_line_both( ed_model.insert_between_line(
&mut ed_model.grid_node_map,
&mut ed_model.code_lines,
old_caret_pos.line, old_caret_pos.line,
old_caret_pos.column + nodes::COLON.len(),
nodes::BLANK_PLACEHOLDER, nodes::BLANK_PLACEHOLDER,
record_blank_node_id, record_blank_node_id,
)?; )?;

View file

@ -0,0 +1,157 @@
use crate::editor::ed_error::EdResult;
use crate::editor::markup::attribute::Attributes;
use crate::editor::markup::nodes;
use crate::editor::markup::nodes::MarkupNode;
use crate::editor::mvc::ed_model::EdModel;
use crate::editor::mvc::ed_update::get_node_context;
use crate::editor::mvc::ed_update::NodeContext;
use crate::editor::syntax_highlight::HighlightStyle;
use crate::lang::ast::ArrString;
use crate::lang::ast::Expr2;
use crate::lang::pool::PoolStr;
pub fn update_small_string(
new_char: &char,
old_array_str: &ArrString,
ed_model: &mut EdModel,
) -> EdResult<()> {
let NodeContext {
old_caret_pos,
curr_mark_node_id,
curr_mark_node: _,
parent_id_opt: _,
ast_node_id,
} = get_node_context(&ed_model)?;
let new_input = &new_char.to_string();
// update markup
let curr_mark_node_mut = ed_model.markup_node_pool.get_mut(curr_mark_node_id);
let content_str_mut = curr_mark_node_mut.get_content_mut()?;
let node_caret_offset = ed_model
.grid_node_map
.get_offset_to_node_id(old_caret_pos, curr_mark_node_id)?;
content_str_mut.insert_str(node_caret_offset, new_input);
// update GridNodeMap and CodeLines
ed_model.insert_between_line(
old_caret_pos.line,
old_caret_pos.column,
new_input,
curr_mark_node_id,
)?;
if old_array_str.len() < ArrString::capacity() {
if let Expr2::SmallStr(ref mut mut_array_str) =
ed_model.module.env.pool.get_mut(ast_node_id)
{
// safe because we checked the length
unsafe {
mut_array_str.push_unchecked(*new_char);
}
} else {
unreachable!()
}
} else {
let mut new_str = old_array_str.as_str().to_owned();
new_str.push(*new_char);
let new_ast_node = Expr2::Str(PoolStr::new(&new_str, ed_model.module.env.pool));
ed_model.module.env.pool.set(ast_node_id, new_ast_node);
}
// update caret
for _ in 0..new_input.len() {
ed_model.simple_move_carets_right();
}
Ok(())
}
pub fn update_string(
new_input: &str,
old_pool_str: &PoolStr,
ed_model: &mut EdModel,
) -> EdResult<()> {
let NodeContext {
old_caret_pos,
curr_mark_node_id,
curr_mark_node: _,
parent_id_opt: _,
ast_node_id,
} = get_node_context(&ed_model)?;
// update markup
let curr_mark_node_mut = ed_model.markup_node_pool.get_mut(curr_mark_node_id);
let content_str_mut = curr_mark_node_mut.get_content_mut()?;
let node_caret_offset = ed_model
.grid_node_map
.get_offset_to_node_id(old_caret_pos, curr_mark_node_id)?;
content_str_mut.insert_str(node_caret_offset, new_input);
// update GridNodeMap and CodeLines
ed_model.insert_between_line(
old_caret_pos.line,
old_caret_pos.column,
new_input,
curr_mark_node_id,
)?;
// update ast
let mut new_string = old_pool_str.as_str(ed_model.module.env.pool).to_owned();
new_string.push_str(new_input);
let new_pool_str = PoolStr::new(&new_string, &mut ed_model.module.env.pool);
let new_ast_node = Expr2::Str(new_pool_str);
ed_model.module.env.pool.set(ast_node_id, new_ast_node);
// update caret
ed_model.simple_move_carets_right();
Ok(())
}
pub fn start_new_string(ed_model: &mut EdModel) -> EdResult<()> {
let NodeContext {
old_caret_pos,
curr_mark_node_id,
curr_mark_node,
parent_id_opt,
ast_node_id,
} = get_node_context(&ed_model)?;
if curr_mark_node.is_blank() {
let new_expr2_node = Expr2::SmallStr(arraystring::ArrayString::new());
ed_model.module.env.pool.set(ast_node_id, new_expr2_node);
let new_string_node = MarkupNode::Text {
content: nodes::STRING_QUOTES.to_owned(),
ast_node_id,
syn_high_style: HighlightStyle::String,
attributes: Attributes::new(),
parent_id_opt,
};
ed_model
.markup_node_pool
.replace_node(curr_mark_node_id, new_string_node);
// remove data corresponding to Blank node
ed_model.del_at_line(old_caret_pos.line, old_caret_pos.column)?;
// update GridNodeMap and CodeLines
ed_model.insert_between_line(
old_caret_pos.line,
old_caret_pos.column,
nodes::STRING_QUOTES,
curr_mark_node_id,
)?;
ed_model.simple_move_carets_right();
}
Ok(())
}

View file

@ -58,6 +58,8 @@ fn size_of_intval() {
assert_eq!(std::mem::size_of::<IntVal>(), 16); assert_eq!(std::mem::size_of::<IntVal>(), 16);
} }
pub type ArrString = ArrayString<U30>;
/// An Expr that fits in 32B. /// An Expr that fits in 32B.
/// It has a 1B discriminant and variants which hold payloads of at most 31B. /// It has a 1B discriminant and variants which hold payloads of at most 31B.
#[derive(Debug)] #[derive(Debug)]
@ -94,7 +96,7 @@ pub enum Expr2 {
text: PoolStr, // 8B text: PoolStr, // 8B
}, },
/// string literals of length up to 30B /// string literals of length up to 30B
SmallStr(ArrayString<U30>), // 31B SmallStr(ArrString), // 31B
/// string literals of length 31B or more /// string literals of length 31B or more
Str(PoolStr), // 8B Str(PoolStr), // 8B
// Lookups // Lookups