mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-21 15:59:20 +03:00
Merge pull request #1087 from rtfeldman/move_caret
Move AST carets right
This commit is contained in:
commit
753909d561
@ -78,6 +78,8 @@ e.g. you have a test `calculate_sum_test` that only uses the function `add`, whe
|
||||
* Searchbar for examples/docs. With permission search strings could be shared with the platform/package authors so they know exactly what their users are struggling with.
|
||||
* Show productivity/feature tips on startup. Show link to page with all tips. Allow not seeing tips next time.
|
||||
* Search friendly editor docs inside the editor. Offer to send search string to Roc maintainers when no results, or if no results were clicked.
|
||||
* File history timeline view. Show timeline with commits that changed this file, the number of lines added and deleted as well as which user made the changes.
|
||||
* Suggested quick fixes should be directly visible and clickable. Not like in vs code where you put the caret on an error until a lightbulb appears in the margin which you have to click for the fixes to apppear, after which you click to apply the fix you want :( .
|
||||
|
||||
#### Autocomplete
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::editor::slow_pool::SlowNodeId;
|
||||
use colored::*;
|
||||
use snafu::{Backtrace, ErrorCompat, NoneError, ResultExt, Snafu};
|
||||
|
||||
@ -9,6 +10,15 @@ use snafu::{Backtrace, ErrorCompat, NoneError, ResultExt, Snafu};
|
||||
#[derive(Debug, Snafu)]
|
||||
#[snafu(visibility(pub))]
|
||||
pub enum EdError {
|
||||
#[snafu(display(
|
||||
"CaretNotFound: No carets were found in the expected node with id {}",
|
||||
node_id
|
||||
))]
|
||||
CaretNotFound {
|
||||
node_id: SlowNodeId,
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
|
||||
#[snafu(display("ClipboardReadFailed: could not get clipboard contents: {}", err_msg))]
|
||||
ClipboardReadFailed { err_msg: String },
|
||||
|
||||
@ -21,6 +31,31 @@ pub enum EdError {
|
||||
))]
|
||||
ClipboardInitFailed { err_msg: String },
|
||||
|
||||
#[snafu(display("GetContentOnNestedNode: tried to get string content from Nested MarkupNode. Can only get content from Text or Blank nodes."))]
|
||||
GetContentOnNestedNode { backtrace: Backtrace },
|
||||
|
||||
#[snafu(display("KeyNotFound: key {} was not found in HashMap.", key_str,))]
|
||||
KeyNotFound {
|
||||
key_str: String,
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
|
||||
#[snafu(display("NestedNodeWithoutChildren: tried to retrieve child from Nested MarkupNode with id {} but it had no children.", node_id))]
|
||||
NestedNodeWithoutChildren {
|
||||
node_id: SlowNodeId,
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
|
||||
#[snafu(display("NestedNodeMissingChild: expected to find child with id {} in Nested MarkupNode, but it was missing. Id's of the children are {:?}.", node_id, children_ids))]
|
||||
NestedNodeMissingChild {
|
||||
node_id: SlowNodeId,
|
||||
children_ids: Vec<SlowNodeId>,
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
|
||||
#[snafu(display("NodeWithoutAttributes: expected to have a node with attributes. This is a Nested MarkupNode, only Text and Blank nodes have attributes."))]
|
||||
NodeWithoutAttributes { backtrace: Backtrace },
|
||||
|
||||
#[snafu(display(
|
||||
"OutOfBounds: index {} was out of bounds for {} with length {}.",
|
||||
index,
|
||||
@ -34,12 +69,6 @@ pub enum EdError {
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
|
||||
#[snafu(display("KeyNotFound: key {} was not found in HashMap.", key_str,))]
|
||||
KeyNotFound {
|
||||
key_str: String,
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
|
||||
#[snafu(display("ParseError: Failed to parse AST: SyntaxError: {}.", syntax_err))]
|
||||
ParseError { syntax_err: String },
|
||||
|
||||
|
@ -3,6 +3,7 @@ use crate::editor::mvc::app_model::AppModel;
|
||||
use crate::editor::mvc::app_update::{
|
||||
handle_copy, handle_cut, handle_paste, pass_keydown_to_focused,
|
||||
};
|
||||
use crate::editor::slow_pool::SlowPool;
|
||||
use winit::event::VirtualKeyCode::*;
|
||||
use winit::event::{ElementState, ModifiersState, VirtualKeyCode};
|
||||
|
||||
@ -11,6 +12,7 @@ pub fn handle_keydown(
|
||||
virtual_keycode: VirtualKeyCode,
|
||||
modifiers: ModifiersState,
|
||||
app_model: &mut AppModel,
|
||||
markup_node_pool: &mut SlowPool,
|
||||
) -> EdResult<()> {
|
||||
if let ElementState::Released = elem_state {
|
||||
return Ok(());
|
||||
@ -18,7 +20,7 @@ pub fn handle_keydown(
|
||||
|
||||
match virtual_keycode {
|
||||
Left | Up | Right | Down => {
|
||||
pass_keydown_to_focused(&modifiers, virtual_keycode, app_model)?
|
||||
pass_keydown_to_focused(&modifiers, virtual_keycode, app_model, markup_node_pool)?
|
||||
}
|
||||
|
||||
Copy => handle_copy(app_model)?,
|
||||
@ -40,7 +42,9 @@ pub fn handle_keydown(
|
||||
}
|
||||
}
|
||||
|
||||
A | Home | End => pass_keydown_to_focused(&modifiers, virtual_keycode, app_model)?,
|
||||
A | Home | End => {
|
||||
pass_keydown_to_focused(&modifiers, virtual_keycode, app_model, markup_node_pool)?
|
||||
}
|
||||
|
||||
_ => (),
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ use super::style::CODE_TXT_XY;
|
||||
use super::util::slice_get;
|
||||
use crate::editor::ed_error::print_ui_err;
|
||||
use crate::editor::resources::strings::NOTHING_OPENED;
|
||||
use crate::editor::slow_pool::SlowPool;
|
||||
use crate::editor::{
|
||||
config::Config,
|
||||
ed_error::print_err,
|
||||
@ -131,6 +132,7 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box<dyn Error>> {
|
||||
let mut env_pool = Pool::with_capacity(1024);
|
||||
let env_arena = Bump::new();
|
||||
let code_arena = Bump::new();
|
||||
let mut markup_node_pool = SlowPool::new();
|
||||
|
||||
let mut var_store = VarStore::default();
|
||||
let dep_idents = IdentIds::exposed_builtins(8);
|
||||
@ -165,7 +167,7 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box<dyn Error>> {
|
||||
}
|
||||
|
||||
let ed_model_opt = {
|
||||
let ed_model_res = ed_model::init_model(&code_str, env, &code_arena);
|
||||
let ed_model_res = ed_model::init_model(&code_str, env, &code_arena, &mut markup_node_pool);
|
||||
|
||||
match ed_model_res {
|
||||
Ok(mut ed_model) => {
|
||||
@ -254,6 +256,7 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box<dyn Error>> {
|
||||
virtual_keycode,
|
||||
keyboard_modifiers,
|
||||
&mut app_model,
|
||||
&mut markup_node_pool,
|
||||
);
|
||||
|
||||
if let Err(e) = keydown_res {
|
||||
@ -290,6 +293,7 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box<dyn Error>> {
|
||||
&size,
|
||||
CODE_TXT_XY.into(),
|
||||
&config,
|
||||
&markup_node_pool,
|
||||
);
|
||||
|
||||
match text_and_rects_res {
|
||||
|
@ -1,197 +0,0 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use super::syntax_highlight::HighlightStyle;
|
||||
use crate::lang::{
|
||||
ast::Expr2,
|
||||
expr::Env,
|
||||
pool::{NodeId, PoolStr},
|
||||
};
|
||||
use bumpalo::Bump;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MarkupNode {
|
||||
// TODO add parent field, necessary for moving caret to next node
|
||||
Nested {
|
||||
ast_node_id: NodeId<Expr2>,
|
||||
children: Vec<MarkupNode>,
|
||||
},
|
||||
Text {
|
||||
content: String,
|
||||
ast_node_id: NodeId<Expr2>,
|
||||
syn_high_style: HighlightStyle,
|
||||
attributes: Vec<Attribute>,
|
||||
},
|
||||
Hole {
|
||||
ast_node_id: NodeId<Expr2>,
|
||||
attributes: Vec<Attribute>,
|
||||
syn_high_style: HighlightStyle,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Attribute {
|
||||
Caret { offset_col: usize },
|
||||
SelectionStart { offset_col: usize },
|
||||
SelectionEnd { offset_col: usize },
|
||||
// Highlight is used for example when searching for a specific string to highlight all search results in the module
|
||||
HighlightStart { offset_col: usize },
|
||||
HighlightEnd { offset_col: usize },
|
||||
// Underline is used for warnings and errors
|
||||
UnderlineStart { offset_col: usize },
|
||||
UnderlineEnd { offset_col: usize },
|
||||
}
|
||||
|
||||
fn get_string<'a>(env: &Env<'a>, pool_str: &PoolStr) -> String {
|
||||
pool_str.as_str(env.pool).to_owned()
|
||||
}
|
||||
|
||||
fn new_markup(text: String, node_id: NodeId<Expr2>, highlight_style: HighlightStyle) -> MarkupNode {
|
||||
MarkupNode::Text {
|
||||
content: text,
|
||||
ast_node_id: node_id,
|
||||
syn_high_style: highlight_style,
|
||||
attributes: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
// make Markup Nodes: generate String representation, assign Highlighting Style
|
||||
pub fn expr2_to_markup<'a, 'b>(arena: &'a Bump, env: &mut Env<'b>, expr2: &Expr2) -> MarkupNode {
|
||||
// TODO find way to add current expr2 to pool
|
||||
let node_id = env.pool.add(Expr2::Hole);
|
||||
|
||||
match expr2 {
|
||||
Expr2::SmallInt { text, .. }
|
||||
| Expr2::I128 { text, .. }
|
||||
| Expr2::U128 { text, .. }
|
||||
| Expr2::Float { text, .. } => {
|
||||
new_markup(get_string(env, &text), node_id, HighlightStyle::Number)
|
||||
}
|
||||
Expr2::Str(text) => new_markup(
|
||||
"\"".to_owned() + text.as_str(env.pool) + "\"",
|
||||
node_id,
|
||||
HighlightStyle::String,
|
||||
),
|
||||
Expr2::GlobalTag { name, .. } => {
|
||||
new_markup(get_string(env, &name), node_id, HighlightStyle::Type)
|
||||
}
|
||||
Expr2::Call { expr: expr_id, .. } => {
|
||||
let expr = env.pool.get(*expr_id);
|
||||
expr2_to_markup(arena, env, expr)
|
||||
}
|
||||
Expr2::Var(symbol) => {
|
||||
//TODO make bump_format with arena
|
||||
let text = format!("{:?}", symbol);
|
||||
new_markup(text, node_id, HighlightStyle::Variable)
|
||||
}
|
||||
Expr2::List { elems, .. } => {
|
||||
let mut children: Vec<MarkupNode> = Vec::new();
|
||||
children.push(new_markup(
|
||||
"[ ".to_string(),
|
||||
node_id,
|
||||
HighlightStyle::Bracket,
|
||||
));
|
||||
|
||||
for (idx, node_id) in elems.iter_node_ids().enumerate() {
|
||||
let sub_expr2 = env.pool.get(node_id);
|
||||
|
||||
children.push(expr2_to_markup(arena, env, sub_expr2));
|
||||
|
||||
if idx + 1 < elems.len() {
|
||||
children.push(new_markup(
|
||||
", ".to_string(),
|
||||
node_id,
|
||||
HighlightStyle::Operator,
|
||||
));
|
||||
}
|
||||
}
|
||||
children.push(new_markup(
|
||||
"] ".to_string(),
|
||||
node_id,
|
||||
HighlightStyle::Bracket,
|
||||
));
|
||||
|
||||
MarkupNode::Nested {
|
||||
ast_node_id: node_id,
|
||||
children,
|
||||
}
|
||||
}
|
||||
Expr2::Record { fields, .. } => {
|
||||
let mut children: Vec<MarkupNode> = Vec::new();
|
||||
children.push(new_markup(
|
||||
"{ ".to_string(),
|
||||
node_id,
|
||||
HighlightStyle::Bracket,
|
||||
));
|
||||
|
||||
for (idx, field_node_id) in fields.iter_node_ids().enumerate() {
|
||||
let (pool_field_name, _, sub_expr2_node_id) = env.pool.get(field_node_id);
|
||||
|
||||
let field_name = pool_field_name.as_str(env.pool);
|
||||
|
||||
let sub_expr2 = env.pool.get(*sub_expr2_node_id);
|
||||
|
||||
children.push(new_markup(
|
||||
field_name.to_string(),
|
||||
node_id,
|
||||
HighlightStyle::RecordField,
|
||||
));
|
||||
children.push(new_markup(
|
||||
": ".to_string(),
|
||||
node_id,
|
||||
HighlightStyle::Operator,
|
||||
));
|
||||
|
||||
children.push(expr2_to_markup(arena, env, sub_expr2));
|
||||
|
||||
if idx + 1 < fields.len() {
|
||||
children.push(new_markup(
|
||||
", ".to_string(),
|
||||
node_id,
|
||||
HighlightStyle::Operator,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
children.push(new_markup(
|
||||
" }".to_string(),
|
||||
node_id,
|
||||
HighlightStyle::Bracket,
|
||||
));
|
||||
|
||||
MarkupNode::Nested {
|
||||
ast_node_id: node_id,
|
||||
children,
|
||||
}
|
||||
}
|
||||
Expr2::Hole => MarkupNode::Hole {
|
||||
ast_node_id: node_id,
|
||||
attributes: Vec::new(),
|
||||
syn_high_style: HighlightStyle::Hole,
|
||||
},
|
||||
rest => todo!("implement expr2_to_markup for {:?}", rest),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_caret_at_start(markup_node: &mut MarkupNode) {
|
||||
match markup_node {
|
||||
MarkupNode::Nested {
|
||||
ast_node_id: _,
|
||||
children,
|
||||
} => {
|
||||
if let Some(child) = children.first_mut() {
|
||||
set_caret_at_start(child)
|
||||
}
|
||||
}
|
||||
MarkupNode::Text {
|
||||
content: _,
|
||||
ast_node_id: _,
|
||||
syn_high_style: _,
|
||||
attributes,
|
||||
} => attributes.push(Attribute::Caret { offset_col: 0 }),
|
||||
MarkupNode::Hole {
|
||||
ast_node_id: _,
|
||||
attributes,
|
||||
syn_high_style: _,
|
||||
} => attributes.push(Attribute::Caret { offset_col: 0 }),
|
||||
};
|
||||
}
|
123
editor/src/editor/markup/attribute.rs
Normal file
123
editor/src/editor/markup/attribute.rs
Normal file
@ -0,0 +1,123 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use crate::editor::ed_error::{CaretNotFound, EdResult};
|
||||
use snafu::ensure;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Caret {
|
||||
pub offset_col: usize,
|
||||
}
|
||||
|
||||
impl Caret {
|
||||
pub fn new_attr(offset_col: usize) -> Attribute {
|
||||
Attribute::Caret {
|
||||
caret: Caret { offset_col },
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub struct SelectionStart {
|
||||
offset_col: usize,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub struct SelectionEnd {
|
||||
offset_col: usize,
|
||||
}
|
||||
|
||||
// Highlight is used for example when searching for a specific string to highlight all search results in the module
|
||||
#[derive(Debug)]
|
||||
pub struct HighlightStart {
|
||||
offset_col: usize,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub struct HighlightEnd {
|
||||
offset_col: usize,
|
||||
}
|
||||
|
||||
// Underline is used for warnings and errors
|
||||
#[derive(Debug)]
|
||||
pub struct UnderlineStart {
|
||||
offset_col: usize,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub struct UnderlineEnd {
|
||||
offset_col: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Attribute {
|
||||
// Rust does not yet support types for enum variants so we have to do it like this
|
||||
Caret { caret: Caret },
|
||||
|
||||
SelectionStart { selection_start: SelectionStart },
|
||||
SelectionEnd { selection_end: SelectionEnd },
|
||||
|
||||
HighlightStart { highlight_start: HighlightStart },
|
||||
HighlightEnd { highlight_end: HighlightEnd },
|
||||
|
||||
UnderlineStart { underline_start: UnderlineStart },
|
||||
UnderlineEnd { underline_end: UnderlineEnd },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Attributes {
|
||||
pub all: Vec<Attribute>,
|
||||
}
|
||||
|
||||
impl Attributes {
|
||||
pub fn new() -> Attributes {
|
||||
Attributes { all: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn add(&mut self, attr: Attribute) {
|
||||
self.all.push(attr);
|
||||
}
|
||||
|
||||
pub fn add_caret(&mut self, offset_col: usize) {
|
||||
self.all.push(Attribute::Caret {
|
||||
caret: Caret { offset_col },
|
||||
});
|
||||
}
|
||||
|
||||
pub fn get_mut_carets(&mut self) -> Vec<&mut Caret> {
|
||||
let mut carets = Vec::new();
|
||||
|
||||
for attr in self.all.iter_mut() {
|
||||
if let Attribute::Caret { caret } = attr {
|
||||
carets.push(caret)
|
||||
}
|
||||
}
|
||||
|
||||
carets
|
||||
}
|
||||
|
||||
pub fn get_carets(&self) -> Vec<Caret> {
|
||||
let mut carets = Vec::new();
|
||||
|
||||
for attr in self.all.iter() {
|
||||
if let Attribute::Caret { caret } = attr {
|
||||
carets.push(*caret)
|
||||
}
|
||||
}
|
||||
|
||||
carets
|
||||
}
|
||||
|
||||
pub fn delete_caret(&mut self, offset_col: usize, node_id: usize) -> EdResult<()> {
|
||||
let old_len = self.all.len();
|
||||
|
||||
self.all.retain(|attr| {
|
||||
if let Attribute::Caret { caret } = attr {
|
||||
caret.offset_col != offset_col
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
|
||||
let new_len = self.all.len();
|
||||
|
||||
ensure!(old_len != new_len, CaretNotFound { node_id });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
130
editor/src/editor/markup/caret.rs
Normal file
130
editor/src/editor/markup/caret.rs
Normal file
@ -0,0 +1,130 @@
|
||||
use crate::editor::mvc::ed_model::LeafIndex;
|
||||
use crate::editor::{
|
||||
ed_error::{CaretNotFound, EdResult, NodeWithoutAttributes},
|
||||
markup::attribute::Caret,
|
||||
markup::nodes::MarkupNode,
|
||||
slow_pool::{SlowNodeId, SlowPool},
|
||||
};
|
||||
use snafu::ensure;
|
||||
|
||||
// Returns id of node that has Caret attribute
|
||||
pub fn set_caret_at_start(
|
||||
markup_node_id: SlowNodeId,
|
||||
markup_node_pool: &mut SlowPool,
|
||||
) -> EdResult<SlowNodeId> {
|
||||
let markup_node = markup_node_pool.get_mut(markup_node_id);
|
||||
|
||||
match markup_node {
|
||||
MarkupNode::Nested {
|
||||
ast_node_id: _,
|
||||
children_ids: _,
|
||||
parent_id_opt: _,
|
||||
} => NodeWithoutAttributes {}.fail(),
|
||||
MarkupNode::Text {
|
||||
content: _,
|
||||
ast_node_id: _,
|
||||
syn_high_style: _,
|
||||
attributes,
|
||||
parent_id_opt: _,
|
||||
} => {
|
||||
attributes.add(Caret::new_attr(0));
|
||||
Ok(markup_node_id)
|
||||
}
|
||||
MarkupNode::Blank {
|
||||
ast_node_id: _,
|
||||
attributes,
|
||||
syn_high_style: _,
|
||||
parent_id_opt: _,
|
||||
} => {
|
||||
attributes.add(Caret::new_attr(0));
|
||||
Ok(markup_node_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns nodes containing the carets after the move, as well as its position in a DFS ordered list of leaves.
|
||||
pub fn move_carets_right_for_node(
|
||||
node_with_caret_id: SlowNodeId,
|
||||
caret_id_leaf_index: LeafIndex,
|
||||
next_leaf_id_opt: Option<SlowNodeId>,
|
||||
markup_node_pool: &mut SlowPool,
|
||||
) -> EdResult<Vec<(SlowNodeId, LeafIndex)>> {
|
||||
let carets = get_carets(node_with_caret_id, markup_node_pool)?;
|
||||
let node_content = markup_node_pool.get(node_with_caret_id).get_content()?;
|
||||
|
||||
ensure!(
|
||||
!carets.is_empty(),
|
||||
CaretNotFound {
|
||||
node_id: node_with_caret_id
|
||||
}
|
||||
);
|
||||
|
||||
let mut new_nodes_w_carets = Vec::new();
|
||||
|
||||
for caret in carets {
|
||||
let (new_node, new_leaf_index) = if caret.offset_col + 1 < node_content.len() {
|
||||
increase_caret_offset(node_with_caret_id, caret.offset_col, markup_node_pool)?;
|
||||
|
||||
(node_with_caret_id, caret_id_leaf_index)
|
||||
} else if let Some(next_child_id) = next_leaf_id_opt {
|
||||
delete_caret(node_with_caret_id, caret.offset_col, markup_node_pool)?;
|
||||
|
||||
let next_child = markup_node_pool.get_mut(next_child_id);
|
||||
let child_attrs = next_child.get_mut_attributes()?;
|
||||
child_attrs.add_caret(0);
|
||||
|
||||
(next_child_id, caret_id_leaf_index + 1)
|
||||
} else if caret.offset_col + 1 == node_content.len() {
|
||||
// For last char in editor.
|
||||
// In other places we jump to start of next node instead of going to end of
|
||||
// this node, otherwise it would be like there is a space between every node.
|
||||
increase_caret_offset(node_with_caret_id, caret.offset_col, markup_node_pool)?;
|
||||
(node_with_caret_id, caret_id_leaf_index)
|
||||
} else {
|
||||
// Caret is at its end, keep it here.
|
||||
(node_with_caret_id, caret_id_leaf_index)
|
||||
};
|
||||
|
||||
new_nodes_w_carets.push((new_node, new_leaf_index));
|
||||
}
|
||||
|
||||
Ok(new_nodes_w_carets)
|
||||
}
|
||||
|
||||
fn get_carets(node_with_caret_id: SlowNodeId, markup_node_pool: &SlowPool) -> EdResult<Vec<Caret>> {
|
||||
let curr_node = markup_node_pool.get(node_with_caret_id);
|
||||
let attributes = curr_node.get_attributes()?;
|
||||
|
||||
Ok(attributes.get_carets())
|
||||
}
|
||||
|
||||
// this method assumes the current caret position is checked and increasing it will not lead to an invalid caret position
|
||||
fn increase_caret_offset(
|
||||
node_id: SlowNodeId,
|
||||
offset_col: usize,
|
||||
markup_node_pool: &mut SlowPool,
|
||||
) -> EdResult<()> {
|
||||
let node = markup_node_pool.get_mut(node_id);
|
||||
let attrs = node.get_mut_attributes()?;
|
||||
let mut carets = attrs.get_mut_carets();
|
||||
|
||||
for caret in carets.iter_mut() {
|
||||
if caret.offset_col == offset_col {
|
||||
caret.offset_col += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn delete_caret(
|
||||
node_id: SlowNodeId,
|
||||
offset_col: usize,
|
||||
markup_node_pool: &mut SlowPool,
|
||||
) -> EdResult<()> {
|
||||
let node = markup_node_pool.get_mut(node_id);
|
||||
let attrs = node.get_mut_attributes()?;
|
||||
attrs.delete_caret(offset_col, node_id)?;
|
||||
|
||||
Ok(())
|
||||
}
|
3
editor/src/editor/markup/mod.rs
Normal file
3
editor/src/editor/markup/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub mod attribute;
|
||||
pub mod caret;
|
||||
pub mod nodes;
|
375
editor/src/editor/markup/nodes.rs
Normal file
375
editor/src/editor/markup/nodes.rs
Normal file
@ -0,0 +1,375 @@
|
||||
use super::attribute::Attributes;
|
||||
use crate::editor::ed_error::GetContentOnNestedNode;
|
||||
use crate::editor::ed_error::NodeWithoutAttributes;
|
||||
use crate::editor::{
|
||||
ed_error::EdResult,
|
||||
slow_pool::{SlowNodeId, SlowPool},
|
||||
syntax_highlight::HighlightStyle,
|
||||
};
|
||||
use crate::lang::{
|
||||
ast::Expr2,
|
||||
expr::Env,
|
||||
pool::{NodeId, PoolStr},
|
||||
};
|
||||
use bumpalo::Bump;
|
||||
use snafu::OptionExt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MarkupNode {
|
||||
Nested {
|
||||
ast_node_id: NodeId<Expr2>,
|
||||
children_ids: Vec<SlowNodeId>,
|
||||
parent_id_opt: Option<SlowNodeId>,
|
||||
},
|
||||
Text {
|
||||
content: String,
|
||||
ast_node_id: NodeId<Expr2>,
|
||||
syn_high_style: HighlightStyle,
|
||||
attributes: Attributes,
|
||||
parent_id_opt: Option<SlowNodeId>,
|
||||
},
|
||||
Blank {
|
||||
ast_node_id: NodeId<Expr2>,
|
||||
attributes: Attributes,
|
||||
syn_high_style: HighlightStyle,
|
||||
parent_id_opt: Option<SlowNodeId>,
|
||||
},
|
||||
}
|
||||
|
||||
pub const BLANK_PLACEHOLDER: &str = " ";
|
||||
|
||||
impl MarkupNode {
|
||||
pub fn get_children_ids(&self) -> Vec<SlowNodeId> {
|
||||
match self {
|
||||
MarkupNode::Nested {
|
||||
ast_node_id: _,
|
||||
children_ids,
|
||||
parent_id_opt: _,
|
||||
} => children_ids.to_vec(),
|
||||
MarkupNode::Text {
|
||||
content: _,
|
||||
ast_node_id: _,
|
||||
syn_high_style: _,
|
||||
attributes: _,
|
||||
parent_id_opt: _,
|
||||
} => Vec::new(),
|
||||
MarkupNode::Blank {
|
||||
ast_node_id: _,
|
||||
attributes: _,
|
||||
syn_high_style: _,
|
||||
parent_id_opt: _,
|
||||
} => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
// can't be &str, this creates borrowing issues
|
||||
pub fn get_content(&self) -> EdResult<String> {
|
||||
match self {
|
||||
MarkupNode::Nested {
|
||||
ast_node_id: _,
|
||||
children_ids: _,
|
||||
parent_id_opt: _,
|
||||
} => GetContentOnNestedNode {}.fail(),
|
||||
MarkupNode::Text {
|
||||
content,
|
||||
ast_node_id: _,
|
||||
syn_high_style: _,
|
||||
attributes: _,
|
||||
parent_id_opt: _,
|
||||
} => Ok(content.clone()),
|
||||
MarkupNode::Blank {
|
||||
ast_node_id: _,
|
||||
attributes: _,
|
||||
syn_high_style: _,
|
||||
parent_id_opt: _,
|
||||
} => Ok(BLANK_PLACEHOLDER.to_owned()),
|
||||
}
|
||||
}
|
||||
|
||||
// Do Depth First Search and return SlowNodeId's in order of encounter
|
||||
// The returning vec is used for caret movement
|
||||
pub fn get_dfs_leaves(
|
||||
&self,
|
||||
node_id: SlowNodeId,
|
||||
markup_node_pool: &SlowPool,
|
||||
ordered_leaves: &mut Vec<SlowNodeId>,
|
||||
) {
|
||||
let children_ids = self.get_children_ids();
|
||||
|
||||
if children_ids.is_empty() {
|
||||
ordered_leaves.push(node_id);
|
||||
} else {
|
||||
for child_id in self.get_children_ids() {
|
||||
let child = markup_node_pool.get(child_id);
|
||||
child.get_dfs_leaves(child_id, markup_node_pool, ordered_leaves);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_mut_attributes(&mut self) -> EdResult<&mut Attributes> {
|
||||
let attrs_ref = match self {
|
||||
MarkupNode::Nested { .. } => None,
|
||||
MarkupNode::Text {
|
||||
content: _,
|
||||
ast_node_id: _,
|
||||
syn_high_style: _,
|
||||
attributes,
|
||||
parent_id_opt: _,
|
||||
} => Some(attributes),
|
||||
MarkupNode::Blank {
|
||||
ast_node_id: _,
|
||||
attributes,
|
||||
syn_high_style: _,
|
||||
parent_id_opt: _,
|
||||
} => Some(attributes),
|
||||
};
|
||||
|
||||
attrs_ref.with_context(|| NodeWithoutAttributes {})
|
||||
}
|
||||
|
||||
pub fn get_attributes(&self) -> EdResult<&Attributes> {
|
||||
let attrs_ref = match self {
|
||||
MarkupNode::Nested { .. } => None,
|
||||
MarkupNode::Text {
|
||||
content: _,
|
||||
ast_node_id: _,
|
||||
syn_high_style: _,
|
||||
attributes,
|
||||
parent_id_opt: _,
|
||||
} => Some(attributes),
|
||||
MarkupNode::Blank {
|
||||
ast_node_id: _,
|
||||
attributes,
|
||||
syn_high_style: _,
|
||||
parent_id_opt: _,
|
||||
} => Some(attributes),
|
||||
};
|
||||
|
||||
attrs_ref.with_context(|| NodeWithoutAttributes {})
|
||||
}
|
||||
}
|
||||
|
||||
fn get_string<'a>(env: &Env<'a>, pool_str: &PoolStr) -> String {
|
||||
pool_str.as_str(env.pool).to_owned()
|
||||
}
|
||||
|
||||
fn new_markup_node(
|
||||
text: String,
|
||||
node_id: NodeId<Expr2>,
|
||||
highlight_style: HighlightStyle,
|
||||
markup_node_pool: &mut SlowPool,
|
||||
) -> SlowNodeId {
|
||||
let node = MarkupNode::Text {
|
||||
content: text,
|
||||
ast_node_id: node_id,
|
||||
syn_high_style: highlight_style,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt: None,
|
||||
};
|
||||
|
||||
markup_node_pool.add(node)
|
||||
}
|
||||
|
||||
// make Markup Nodes: generate String representation, assign Highlighting Style
|
||||
pub fn expr2_to_markup<'a, 'b>(
|
||||
arena: &'a Bump,
|
||||
env: &mut Env<'b>,
|
||||
expr2: &Expr2,
|
||||
markup_node_pool: &mut SlowPool,
|
||||
) -> SlowNodeId {
|
||||
// TODO find way to add current expr2 to pool
|
||||
let node_id = env.pool.add(Expr2::Blank);
|
||||
|
||||
match expr2 {
|
||||
Expr2::SmallInt { text, .. }
|
||||
| Expr2::I128 { text, .. }
|
||||
| Expr2::U128 { text, .. }
|
||||
| Expr2::Float { text, .. } => new_markup_node(
|
||||
get_string(env, &text),
|
||||
node_id,
|
||||
HighlightStyle::Number,
|
||||
markup_node_pool,
|
||||
),
|
||||
Expr2::Str(text) => new_markup_node(
|
||||
"\"".to_owned() + text.as_str(env.pool) + "\"",
|
||||
node_id,
|
||||
HighlightStyle::String,
|
||||
markup_node_pool,
|
||||
),
|
||||
Expr2::GlobalTag { name, .. } => new_markup_node(
|
||||
get_string(env, &name),
|
||||
node_id,
|
||||
HighlightStyle::Type,
|
||||
markup_node_pool,
|
||||
),
|
||||
Expr2::Call { expr: expr_id, .. } => {
|
||||
let expr = env.pool.get(*expr_id);
|
||||
expr2_to_markup(arena, env, expr, markup_node_pool)
|
||||
}
|
||||
Expr2::Var(symbol) => {
|
||||
//TODO make bump_format with arena
|
||||
let text = format!("{:?}", symbol);
|
||||
new_markup_node(text, node_id, HighlightStyle::Variable, markup_node_pool)
|
||||
}
|
||||
Expr2::List { elems, .. } => {
|
||||
let mut children_ids = Vec::new();
|
||||
|
||||
children_ids.push(new_markup_node(
|
||||
"[ ".to_string(),
|
||||
node_id,
|
||||
HighlightStyle::Bracket,
|
||||
markup_node_pool,
|
||||
));
|
||||
|
||||
for (idx, node_id) in elems.iter_node_ids().enumerate() {
|
||||
let sub_expr2 = env.pool.get(node_id);
|
||||
|
||||
children_ids.push(expr2_to_markup(arena, env, sub_expr2, markup_node_pool));
|
||||
|
||||
if idx + 1 < elems.len() {
|
||||
children_ids.push(new_markup_node(
|
||||
", ".to_string(),
|
||||
node_id,
|
||||
HighlightStyle::Operator,
|
||||
markup_node_pool,
|
||||
));
|
||||
}
|
||||
}
|
||||
children_ids.push(new_markup_node(
|
||||
"] ".to_string(),
|
||||
node_id,
|
||||
HighlightStyle::Bracket,
|
||||
markup_node_pool,
|
||||
));
|
||||
|
||||
let list_node = MarkupNode::Nested {
|
||||
ast_node_id: node_id,
|
||||
children_ids,
|
||||
parent_id_opt: None,
|
||||
};
|
||||
|
||||
markup_node_pool.add(list_node)
|
||||
}
|
||||
Expr2::Record { fields, .. } => {
|
||||
let mut children_ids = Vec::new();
|
||||
|
||||
children_ids.push(new_markup_node(
|
||||
"{ ".to_string(),
|
||||
node_id,
|
||||
HighlightStyle::Bracket,
|
||||
markup_node_pool,
|
||||
));
|
||||
|
||||
for (idx, field_node_id) in fields.iter_node_ids().enumerate() {
|
||||
let (pool_field_name, _, sub_expr2_node_id) = env.pool.get(field_node_id);
|
||||
|
||||
let field_name = pool_field_name.as_str(env.pool);
|
||||
|
||||
let sub_expr2 = env.pool.get(*sub_expr2_node_id);
|
||||
|
||||
children_ids.push(new_markup_node(
|
||||
field_name.to_string(),
|
||||
node_id,
|
||||
HighlightStyle::RecordField,
|
||||
markup_node_pool,
|
||||
));
|
||||
|
||||
children_ids.push(new_markup_node(
|
||||
": ".to_string(),
|
||||
node_id,
|
||||
HighlightStyle::Operator,
|
||||
markup_node_pool,
|
||||
));
|
||||
|
||||
children_ids.push(expr2_to_markup(arena, env, sub_expr2, markup_node_pool));
|
||||
|
||||
if idx + 1 < fields.len() {
|
||||
children_ids.push(new_markup_node(
|
||||
", ".to_string(),
|
||||
node_id,
|
||||
HighlightStyle::Operator,
|
||||
markup_node_pool,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
children_ids.push(new_markup_node(
|
||||
" }".to_string(),
|
||||
node_id,
|
||||
HighlightStyle::Bracket,
|
||||
markup_node_pool,
|
||||
));
|
||||
|
||||
let record_node = MarkupNode::Nested {
|
||||
ast_node_id: node_id,
|
||||
children_ids,
|
||||
parent_id_opt: None,
|
||||
};
|
||||
|
||||
markup_node_pool.add(record_node)
|
||||
}
|
||||
Expr2::Blank => markup_node_pool.add(MarkupNode::Blank {
|
||||
ast_node_id: node_id,
|
||||
attributes: Attributes::new(),
|
||||
syn_high_style: HighlightStyle::Blank,
|
||||
parent_id_opt: None,
|
||||
}),
|
||||
rest => todo!("implement expr2_to_markup for {:?}", rest),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_parent_for_all(markup_node_id: SlowNodeId, markup_node_pool: &mut SlowPool) {
|
||||
let node = markup_node_pool.get(markup_node_id);
|
||||
|
||||
if let MarkupNode::Nested {
|
||||
ast_node_id: _,
|
||||
children_ids,
|
||||
parent_id_opt: _,
|
||||
} = node
|
||||
{
|
||||
// need to clone because of borrowing issues
|
||||
let children_ids_clone = children_ids.clone();
|
||||
|
||||
for child_id in children_ids_clone {
|
||||
set_parent_for_all_helper(child_id, markup_node_id, markup_node_pool);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_parent_for_all_helper(
|
||||
markup_node_id: SlowNodeId,
|
||||
parent_node_id: SlowNodeId,
|
||||
markup_node_pool: &mut SlowPool,
|
||||
) {
|
||||
let node = markup_node_pool.get_mut(markup_node_id);
|
||||
|
||||
match node {
|
||||
MarkupNode::Nested {
|
||||
ast_node_id: _,
|
||||
children_ids,
|
||||
parent_id_opt,
|
||||
} => {
|
||||
*parent_id_opt = Some(parent_node_id);
|
||||
|
||||
// need to clone because of borrowing issues
|
||||
let children_ids_clone = children_ids.clone();
|
||||
|
||||
for child_id in children_ids_clone {
|
||||
set_parent_for_all_helper(child_id, markup_node_id, markup_node_pool);
|
||||
}
|
||||
}
|
||||
MarkupNode::Text {
|
||||
content: _,
|
||||
ast_node_id: _,
|
||||
syn_high_style: _,
|
||||
attributes: _,
|
||||
parent_id_opt,
|
||||
} => *parent_id_opt = Some(parent_node_id),
|
||||
MarkupNode::Blank {
|
||||
ast_node_id: _,
|
||||
attributes: _,
|
||||
syn_high_style: _,
|
||||
parent_id_opt,
|
||||
} => *parent_id_opt = Some(parent_node_id),
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ mod markup;
|
||||
mod mvc;
|
||||
mod render_ast;
|
||||
mod resources;
|
||||
mod slow_pool;
|
||||
mod style;
|
||||
mod syntax_highlight;
|
||||
mod theme;
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::app_model::AppModel;
|
||||
use crate::editor::ed_error::EdResult;
|
||||
use crate::ui::ui_error::UIResult;
|
||||
use crate::editor::slow_pool::SlowPool;
|
||||
use crate::window::keyboard_input::from_winit;
|
||||
use winit::event::{ModifiersState, VirtualKeyCode};
|
||||
|
||||
@ -36,14 +36,15 @@ pub fn handle_cut(app_model: &mut AppModel) -> EdResult<()> {
|
||||
|
||||
pub fn pass_keydown_to_focused(
|
||||
modifiers_winit: &ModifiersState,
|
||||
_virtual_keycode: VirtualKeyCode,
|
||||
virtual_keycode: VirtualKeyCode,
|
||||
app_model: &mut AppModel,
|
||||
) -> UIResult<()> {
|
||||
let _modifiers = from_winit(modifiers_winit);
|
||||
markup_node_pool: &mut SlowPool,
|
||||
) -> EdResult<()> {
|
||||
let modifiers = from_winit(modifiers_winit);
|
||||
|
||||
if let Some(ref mut ed_model) = app_model.ed_model_opt {
|
||||
if ed_model.has_focus {
|
||||
unimplemented!("TODO");
|
||||
ed_model.handle_key_down(&modifiers, virtual_keycode, markup_node_pool)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,56 +1,129 @@
|
||||
use crate::editor::ed_error::EdError::ParseError;
|
||||
use crate::editor::ed_error::EdResult;
|
||||
use crate::editor::markup::{expr2_to_markup, set_caret_at_start, Attribute, MarkupNode};
|
||||
use crate::editor::slow_pool::{SlowNodeId, SlowPool};
|
||||
use crate::editor::syntax_highlight::HighlightStyle;
|
||||
use crate::editor::{
|
||||
ed_error::EdError::ParseError,
|
||||
ed_error::EdResult,
|
||||
markup::attribute::{Attributes, Caret},
|
||||
markup::caret::{move_carets_right_for_node, set_caret_at_start},
|
||||
markup::nodes::{expr2_to_markup, set_parent_for_all, MarkupNode},
|
||||
};
|
||||
use crate::graphics::primitives::rect::Rect;
|
||||
use crate::lang::ast::Expr2;
|
||||
use crate::lang::expr::{str_to_expr2, Env};
|
||||
use crate::lang::scope::Scope;
|
||||
use crate::window::keyboard_input::Modifiers;
|
||||
use bumpalo::collections::String as BumpString;
|
||||
use bumpalo::Bump;
|
||||
use roc_region::all::Region;
|
||||
use std::collections::HashSet;
|
||||
use winit::event::VirtualKeyCode;
|
||||
|
||||
pub type LeafIndex = usize;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EdModel<'a> {
|
||||
pub module: EdModule<'a>,
|
||||
pub code_as_str: &'a str,
|
||||
pub markup_root: MarkupNode,
|
||||
pub markup_root_id: SlowNodeId,
|
||||
pub glyph_dim_rect_opt: Option<Rect>,
|
||||
pub has_focus: bool,
|
||||
carets: Vec<&'a MarkupNode>,
|
||||
// This HashSet may have less elements than there are carets. There can be multiple carets for a single node.
|
||||
caret_nodes: HashSet<(SlowNodeId, LeafIndex)>,
|
||||
dfs_ordered_leaves: Vec<SlowNodeId>,
|
||||
}
|
||||
|
||||
pub fn init_model<'a>(
|
||||
code_str: &'a BumpString,
|
||||
env: Env<'a>,
|
||||
code_arena: &'a Bump,
|
||||
markup_node_pool: &mut SlowPool,
|
||||
) -> EdResult<EdModel<'a>> {
|
||||
let mut module = EdModule::new(&code_str, env, code_arena)?;
|
||||
// TODO fix moving issue and insert module.ast_root into pool
|
||||
let ast_root_id = module.env.pool.add(Expr2::Hole);
|
||||
let ast_root_id = module.env.pool.add(Expr2::Blank);
|
||||
|
||||
let markup_root = if code_str.is_empty() {
|
||||
MarkupNode::Hole {
|
||||
let markup_root_id = if code_str.is_empty() {
|
||||
let blank_root = MarkupNode::Blank {
|
||||
ast_node_id: ast_root_id,
|
||||
attributes: vec![Attribute::Caret { offset_col: 0 }],
|
||||
syn_high_style: HighlightStyle::Hole,
|
||||
}
|
||||
attributes: Attributes {
|
||||
all: vec![Caret::new_attr(0)],
|
||||
},
|
||||
syn_high_style: HighlightStyle::Blank,
|
||||
parent_id_opt: None,
|
||||
};
|
||||
|
||||
markup_node_pool.add(blank_root)
|
||||
} else {
|
||||
let mut temp_markup_root = expr2_to_markup(code_arena, &mut module.env, &module.ast_root);
|
||||
set_caret_at_start(&mut temp_markup_root);
|
||||
temp_markup_root
|
||||
let temp_markup_root_id = expr2_to_markup(
|
||||
code_arena,
|
||||
&mut module.env,
|
||||
&module.ast_root,
|
||||
markup_node_pool,
|
||||
);
|
||||
set_parent_for_all(temp_markup_root_id, markup_node_pool);
|
||||
|
||||
temp_markup_root_id
|
||||
};
|
||||
|
||||
let mut dfs_ordered_leaves = Vec::new();
|
||||
markup_node_pool.get(markup_root_id).get_dfs_leaves(
|
||||
markup_root_id,
|
||||
markup_node_pool,
|
||||
&mut dfs_ordered_leaves,
|
||||
);
|
||||
|
||||
// unwrap because it's not possible to only have a single Nested node without children.
|
||||
let first_leaf_id = dfs_ordered_leaves.first().unwrap();
|
||||
let node_w_caret_id = set_caret_at_start(*first_leaf_id, markup_node_pool)?;
|
||||
|
||||
Ok(EdModel {
|
||||
module,
|
||||
code_as_str: code_str,
|
||||
markup_root,
|
||||
markup_root_id,
|
||||
glyph_dim_rect_opt: None,
|
||||
has_focus: true,
|
||||
carets: Vec::new(),
|
||||
caret_nodes: vec![(node_w_caret_id, 0)].into_iter().collect(),
|
||||
dfs_ordered_leaves,
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> EdModel<'a> {
|
||||
pub fn handle_key_down(
|
||||
&mut self,
|
||||
_modifiers: &Modifiers,
|
||||
virtual_keycode: VirtualKeyCode,
|
||||
markup_node_pool: &mut SlowPool,
|
||||
) -> EdResult<()> {
|
||||
match virtual_keycode {
|
||||
VirtualKeyCode::Right => {
|
||||
let mut new_caret_nodes: Vec<(SlowNodeId, LeafIndex)> = Vec::new();
|
||||
|
||||
for (caret_node_id_ref, leaf_index) in self.caret_nodes.iter() {
|
||||
let caret_node_id = *caret_node_id_ref;
|
||||
let next_leaf_id_opt = self.get_next_leaf(*leaf_index);
|
||||
|
||||
new_caret_nodes.extend(move_carets_right_for_node(
|
||||
caret_node_id,
|
||||
*leaf_index,
|
||||
next_leaf_id_opt,
|
||||
markup_node_pool,
|
||||
)?);
|
||||
}
|
||||
|
||||
self.caret_nodes = new_caret_nodes.into_iter().collect();
|
||||
}
|
||||
VirtualKeyCode::Left => unimplemented!("TODO"),
|
||||
_ => (),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_next_leaf(&self, index: usize) -> Option<SlowNodeId> {
|
||||
self.dfs_ordered_leaves.get(index + 1).copied()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EdModule<'a> {
|
||||
pub env: Env<'a>,
|
||||
@ -78,7 +151,7 @@ impl<'a> EdModule<'a> {
|
||||
} else {
|
||||
Ok(EdModule {
|
||||
env,
|
||||
ast_root: Expr2::Hole,
|
||||
ast_root: Expr2::Blank,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ use super::ed_model::EdModel;
|
||||
use crate::editor::config::Config;
|
||||
use crate::editor::ed_error::EdResult;
|
||||
use crate::editor::render_ast::build_code_graphics;
|
||||
use crate::editor::slow_pool::SlowPool;
|
||||
use crate::graphics::primitives::rect::Rect;
|
||||
use crate::ui::ui_error::MissingGlyphDims;
|
||||
use cgmath::Vector2;
|
||||
@ -14,14 +15,16 @@ pub fn model_to_wgpu<'a>(
|
||||
size: &PhysicalSize<u32>,
|
||||
txt_coords: Vector2<f32>,
|
||||
config: &Config,
|
||||
markup_node_pool: &'a SlowPool,
|
||||
) -> EdResult<(wgpu_glyph::Section<'a>, Vec<Rect>)> {
|
||||
let glyph_dim_rect = ed_model.glyph_dim_rect_opt.context(MissingGlyphDims {})?;
|
||||
|
||||
build_code_graphics(
|
||||
&ed_model.markup_root,
|
||||
markup_node_pool.get(ed_model.markup_root_id),
|
||||
size,
|
||||
txt_coords,
|
||||
config,
|
||||
glyph_dim_rect,
|
||||
markup_node_pool,
|
||||
)
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
use super::markup::{Attribute, MarkupNode};
|
||||
use super::markup::attribute::{Attribute, Attributes};
|
||||
use super::markup::nodes::{MarkupNode, BLANK_PLACEHOLDER};
|
||||
use crate::editor::slow_pool::SlowPool;
|
||||
use crate::editor::{ed_error::EdResult, theme::EdTheme, util::map_get};
|
||||
use crate::graphics::primitives::rect::Rect;
|
||||
use crate::graphics::primitives::text as gr_text;
|
||||
@ -14,6 +16,7 @@ pub fn build_code_graphics<'a>(
|
||||
txt_coords: Vector2<f32>,
|
||||
config: &Config,
|
||||
glyph_dim_rect: Rect,
|
||||
markup_node_pool: &'a SlowPool,
|
||||
) -> EdResult<(wgpu_glyph::Section<'a>, Vec<Rect>)> {
|
||||
let area_bounds = (size.width as f32, size.height as f32);
|
||||
let layout = wgpu_glyph::Layout::default().h_align(wgpu_glyph::HorizontalAlign::Left);
|
||||
@ -26,6 +29,7 @@ pub fn build_code_graphics<'a>(
|
||||
txt_coords,
|
||||
glyph_dim_rect,
|
||||
},
|
||||
markup_node_pool,
|
||||
)?;
|
||||
|
||||
let section =
|
||||
@ -44,6 +48,7 @@ struct CodeStyle<'a> {
|
||||
fn markup_to_wgpu<'a>(
|
||||
markup_node: &'a MarkupNode,
|
||||
code_style: &CodeStyle,
|
||||
markup_node_pool: &'a SlowPool,
|
||||
) -> EdResult<(Vec<wgpu_glyph::Text<'a>>, Vec<Rect>)> {
|
||||
let mut wgpu_texts: Vec<wgpu_glyph::Text<'a>> = Vec::new();
|
||||
let mut rects: Vec<Rect> = Vec::new();
|
||||
@ -56,29 +61,33 @@ fn markup_to_wgpu<'a>(
|
||||
&mut rects,
|
||||
code_style,
|
||||
&mut txt_row_col,
|
||||
markup_node_pool,
|
||||
)?;
|
||||
|
||||
Ok((wgpu_texts, rects))
|
||||
}
|
||||
|
||||
fn draw_attributes(
|
||||
attributes: &[Attribute],
|
||||
attributes: &Attributes,
|
||||
txt_row_col: &(usize, usize),
|
||||
code_style: &CodeStyle,
|
||||
) -> Vec<Rect> {
|
||||
let char_width = code_style.glyph_dim_rect.width;
|
||||
|
||||
attributes
|
||||
.all
|
||||
.iter()
|
||||
.map(|attr| match attr {
|
||||
Attribute::Caret { offset_col } => {
|
||||
Attribute::Caret { caret } => {
|
||||
let caret_col = caret.offset_col as f32;
|
||||
|
||||
let top_left_x = code_style.txt_coords.x
|
||||
+ (txt_row_col.1 as f32) * char_width
|
||||
+ (*offset_col as f32) * char_width;
|
||||
+ caret_col * char_width;
|
||||
|
||||
let top_left_y = code_style.txt_coords.y
|
||||
+ (txt_row_col.0 as f32) * char_width
|
||||
+ (*offset_col as f32) * char_width;
|
||||
+ char_width * 0.2;
|
||||
|
||||
make_caret_rect(
|
||||
top_left_x,
|
||||
@ -99,14 +108,24 @@ fn markup_to_wgpu_helper<'a>(
|
||||
rects: &mut Vec<Rect>,
|
||||
code_style: &CodeStyle,
|
||||
txt_row_col: &mut (usize, usize),
|
||||
markup_node_pool: &'a SlowPool,
|
||||
) -> EdResult<()> {
|
||||
match markup_node {
|
||||
MarkupNode::Nested {
|
||||
ast_node_id: _,
|
||||
children,
|
||||
children_ids,
|
||||
parent_id_opt: _,
|
||||
} => {
|
||||
for child in children.iter() {
|
||||
markup_to_wgpu_helper(child, wgpu_texts, rects, code_style, txt_row_col)?;
|
||||
for child_id in children_ids.iter() {
|
||||
let child = markup_node_pool.get(*child_id);
|
||||
markup_to_wgpu_helper(
|
||||
child,
|
||||
wgpu_texts,
|
||||
rects,
|
||||
code_style,
|
||||
txt_row_col,
|
||||
markup_node_pool,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
MarkupNode::Text {
|
||||
@ -114,6 +133,7 @@ fn markup_to_wgpu_helper<'a>(
|
||||
ast_node_id: _,
|
||||
syn_high_style,
|
||||
attributes,
|
||||
parent_id_opt: _,
|
||||
} => {
|
||||
let highlight_color = map_get(&code_style.ed_theme.syntax_high_map, &syn_high_style)?;
|
||||
|
||||
@ -125,13 +145,13 @@ fn markup_to_wgpu_helper<'a>(
|
||||
txt_row_col.1 += content.len();
|
||||
wgpu_texts.push(glyph_text);
|
||||
}
|
||||
MarkupNode::Hole {
|
||||
MarkupNode::Blank {
|
||||
ast_node_id: _,
|
||||
attributes,
|
||||
syn_high_style,
|
||||
parent_id_opt: _,
|
||||
} => {
|
||||
let hole_placeholder = " ";
|
||||
let glyph_text = wgpu_glyph::Text::new(hole_placeholder)
|
||||
let glyph_text = wgpu_glyph::Text::new(BLANK_PLACEHOLDER)
|
||||
.with_color(colors::to_slice(colors::WHITE))
|
||||
.with_scale(code_style.font_size);
|
||||
|
||||
@ -153,7 +173,7 @@ fn markup_to_wgpu_helper<'a>(
|
||||
|
||||
rects.extend(draw_attributes(attributes, txt_row_col, code_style));
|
||||
|
||||
txt_row_col.1 += hole_placeholder.len();
|
||||
txt_row_col.1 += BLANK_PLACEHOLDER.len();
|
||||
wgpu_texts.push(glyph_text);
|
||||
}
|
||||
};
|
||||
|
32
editor/src/editor/slow_pool.rs
Normal file
32
editor/src/editor/slow_pool.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use crate::editor::markup::nodes::MarkupNode;
|
||||
|
||||
pub type SlowNodeId = usize;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SlowPool {
|
||||
nodes: Vec<MarkupNode>,
|
||||
}
|
||||
|
||||
impl SlowPool {
|
||||
pub fn new() -> SlowPool {
|
||||
SlowPool { nodes: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn add(&mut self, node: MarkupNode) -> SlowNodeId {
|
||||
let id = self.nodes.len();
|
||||
|
||||
self.nodes.push(node);
|
||||
|
||||
id
|
||||
}
|
||||
|
||||
pub fn get(&self, node_id: usize) -> &MarkupNode {
|
||||
// unwrap because Pool doesn't return Result either
|
||||
self.nodes.get(node_id).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, node_id: usize) -> &mut MarkupNode {
|
||||
// unwrap because Pool doesn't return Result either
|
||||
self.nodes.get_mut(node_id).unwrap()
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ pub enum HighlightStyle {
|
||||
PackageRelated, // app, packages, imports, exposes, provides...
|
||||
Variable,
|
||||
RecordField,
|
||||
Hole,
|
||||
Blank,
|
||||
}
|
||||
|
||||
pub fn default_highlight_map() -> HashMap<HighlightStyle, RgbaTup> {
|
||||
@ -31,7 +31,7 @@ pub fn default_highlight_map() -> HashMap<HighlightStyle, RgbaTup> {
|
||||
(PackageRelated, gr_colors::WHITE),
|
||||
(Variable, gr_colors::WHITE),
|
||||
(RecordField, from_hsb(258, 50, 90)),
|
||||
(Hole, from_hsb(258, 50, 90)),
|
||||
(Blank, from_hsb(258, 50, 90)),
|
||||
// comment from_hsb(285, 6, 47) or 186, 35, 40
|
||||
]
|
||||
.iter()
|
||||
|
@ -198,7 +198,7 @@ pub enum Expr2 {
|
||||
ext_var: Variable, // 4B
|
||||
arguments: PoolVec<(Variable, NodeId<Expr2>)>, // 8B
|
||||
},
|
||||
Hole, // Rendered as empty box in editor
|
||||
Blank, // Rendered as empty box in editor
|
||||
|
||||
// Compiles, but will crash if reached
|
||||
RuntimeError(/* TODO make a version of RuntimeError that fits in 15B */),
|
||||
|
@ -150,7 +150,7 @@ impl Pool {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_mut<T>(&mut self, node_id: NodeId<T>) -> &mut T {
|
||||
pub fn get_mut<T>(&mut self, node_id: NodeId<T>) -> &mut T {
|
||||
unsafe {
|
||||
let node_ptr = self.nodes.offset(node_id.index as isize) as *mut T;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user