mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
separated markup and util crates, no more errors
This commit is contained in:
parent
ea62f15ac6
commit
f9e2e3469b
26 changed files with 1609 additions and 14 deletions
20
Cargo.lock
generated
20
Cargo.lock
generated
|
@ -3529,6 +3529,19 @@ dependencies = [
|
|||
"wasmer-wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_code_markup"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"palette",
|
||||
"roc_ast",
|
||||
"roc_module",
|
||||
"roc_utils",
|
||||
"serde",
|
||||
"snafu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_collections"
|
||||
version = "0.1.0"
|
||||
|
@ -3956,6 +3969,13 @@ dependencies = [
|
|||
"roc_types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_utils"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"snafu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ropey"
|
||||
version = "1.3.1"
|
||||
|
|
|
@ -32,7 +32,9 @@ members = [
|
|||
"ast",
|
||||
"cli",
|
||||
"cli/cli_utils",
|
||||
"code_markup",
|
||||
"roc_std",
|
||||
"utils",
|
||||
"docs",
|
||||
"linker",
|
||||
]
|
||||
|
|
|
@ -4,7 +4,7 @@ version = "0.1.0"
|
|||
authors = ["The Roc Contributors"]
|
||||
license = "UPL-1.0"
|
||||
edition = "2018"
|
||||
description = "AST as used by the editor and docs. In contrast to the compiler, these types do not keep track of a location in a file."
|
||||
description = "AST as used by the editor and (soon) docs. In contrast to the compiler, these types do not keep track of a location in a file."
|
||||
|
||||
[dependencies]
|
||||
roc_can = { path = "../compiler/can" }
|
||||
|
|
|
@ -5,7 +5,7 @@ use roc_problem::can::{Problem};
|
|||
use roc_region::all::{Located, Region};
|
||||
use roc_types::{subs::Variable};
|
||||
|
||||
use crate::{lang::{core::{def::def::References, expr::{expr2::{Expr2, ExprId, RecordField, WhenBranch}, expr_to_expr2::to_expr2, output::Output}, pattern::to_pattern2}, env::Env, scope::Scope}, pool::{pool_str::PoolStr, pool_vec::PoolVec, shallow_clone::ShallowClone}};
|
||||
use crate::{lang::{core::{def::def::References, expr::{expr2::{Expr2, ExprId, WhenBranch}, expr_to_expr2::to_expr2, output::Output, record_field::RecordField}, pattern::to_pattern2}, env::Env, scope::Scope}, pool::{pool_str::PoolStr, pool_vec::PoolVec, shallow_clone::ShallowClone}};
|
||||
|
||||
pub (crate) enum CanonicalizeRecordProblem {
|
||||
InvalidOptionalValue {
|
||||
|
|
|
@ -13,7 +13,7 @@ use roc_types::{
|
|||
types::{Category, Reason},
|
||||
};
|
||||
|
||||
use crate::{lang::{core::{expr::expr2::{ClosureExtra, Expr2, ExprId, RecordField, WhenBranch}, pattern::{DestructType, Pattern2, PatternId, PatternState2, RecordDestruct}, types::{Type2, TypeId}, val_def::ValueDef}, env::Env}, pool::{pool::Pool, pool_str::PoolStr, pool_vec::PoolVec, shallow_clone::ShallowClone}};
|
||||
use crate::{lang::{core::{expr::{expr2::{ClosureExtra, Expr2, ExprId, WhenBranch}, record_field::RecordField}, pattern::{DestructType, Pattern2, PatternId, PatternState2, RecordDestruct}, types::{Type2, TypeId}, val_def::ValueDef}, env::Env}, pool::{pool::Pool, pool_str::PoolStr, pool_vec::PoolVec, shallow_clone::ShallowClone}};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Constraint<'a> {
|
||||
|
|
|
@ -7,6 +7,8 @@ use roc_module::symbol::Symbol;
|
|||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::operator::CalledVia;
|
||||
|
||||
use super::record_field::RecordField;
|
||||
|
||||
pub type ArrString = ArrayString<U30>;
|
||||
|
||||
// TODO make the inner types private?
|
||||
|
@ -210,13 +212,6 @@ pub enum FloatVal {
|
|||
F32(f32),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RecordField {
|
||||
InvalidLabelOnly(PoolStr, Variable),
|
||||
LabelOnly(PoolStr, Variable, Symbol),
|
||||
LabeledValue(PoolStr, Variable, ExprId),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WhenBranch {
|
||||
pub patterns: PoolVec<Pattern2>, // 4B
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{lang::core::{expr::expr2::RecordField, val_def::value_def_to_string}, pool::pool::Pool};
|
||||
use crate::{lang::core::{expr::record_field::RecordField, val_def::value_def_to_string}, pool::pool::Pool};
|
||||
|
||||
use roc_types::subs::Variable;
|
||||
use super::expr2::{Expr2, ExprId};
|
||||
|
|
|
@ -3,3 +3,4 @@ pub mod expr2_to_string;
|
|||
pub (crate) mod output;
|
||||
mod introduced_vars;
|
||||
pub (crate) mod expr_to_expr2;
|
||||
pub mod record_field;
|
||||
|
|
52
ast/src/lang/core/expr/record_field.rs
Normal file
52
ast/src/lang/core/expr/record_field.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
|
||||
use roc_types::subs::Variable;
|
||||
|
||||
use crate::{pool::{pool_str::PoolStr}};
|
||||
use roc_module::symbol::Symbol;
|
||||
|
||||
use super::expr2::ExprId;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RecordField {
|
||||
InvalidLabelOnly(PoolStr, Variable),
|
||||
LabelOnly(PoolStr, Variable, Symbol),
|
||||
LabeledValue(PoolStr, Variable, ExprId),
|
||||
}
|
||||
|
||||
use RecordField::*;
|
||||
|
||||
impl RecordField {
|
||||
|
||||
|
||||
pub fn get_record_field_var(&self) -> &Variable {
|
||||
match self {
|
||||
InvalidLabelOnly(_, var) => var,
|
||||
LabelOnly(_, var, _) => var,
|
||||
LabeledValue(_, var, _) => var,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_record_field_pool_str(&self) -> &PoolStr {
|
||||
match self {
|
||||
InvalidLabelOnly(pool_str, _) => pool_str,
|
||||
LabelOnly(pool_str, _, _) => pool_str,
|
||||
LabeledValue(pool_str, _, _) => pool_str,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_record_field_pool_str_mut(&mut self) -> &mut PoolStr {
|
||||
match self {
|
||||
InvalidLabelOnly(pool_str, _) => pool_str,
|
||||
LabelOnly(pool_str, _, _) => pool_str,
|
||||
LabeledValue(pool_str, _, _) => pool_str,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_record_field_val_node_id(&self) -> Option<ExprId> {
|
||||
match self {
|
||||
InvalidLabelOnly(_, _) => None,
|
||||
LabelOnly(_, _, _) => None,
|
||||
LabeledValue(_, _, field_val_id) => Some(*field_val_id),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,4 +2,4 @@ pub mod lang;
|
|||
pub mod pool;
|
||||
mod constrain;
|
||||
mod canonicalize;
|
||||
mod ast_error;
|
||||
pub mod ast_error;
|
|
@ -11,4 +11,4 @@ mod solve;
|
|||
mod types;
|
||||
mod rigids;
|
||||
mod canonicalize;
|
||||
mod ast_error;
|
||||
pub mod ast_error;
|
18
code_markup/Cargo.toml
Normal file
18
code_markup/Cargo.toml
Normal file
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
name = "roc_code_markup"
|
||||
version = "0.1.0"
|
||||
authors = ["The Roc Contributors"]
|
||||
license = "UPL-1.0"
|
||||
edition = "2018"
|
||||
description = "Our own markup language for Roc code. Used by the editor and (soon) the docs."
|
||||
|
||||
[dependencies]
|
||||
roc_ast = { path = "../ast" }
|
||||
roc_module = { path = "../compiler/module" }
|
||||
roc_utils = { path = "../utils" }
|
||||
serde = { version = "1.0.123", features = ["derive"] }
|
||||
palette = "0.5"
|
||||
snafu = { version = "0.6", features = ["backtraces"] }
|
||||
bumpalo = { version = "3.2", features = ["collections"] }
|
||||
|
||||
[dev-dependencies]
|
22
code_markup/src/colors.rs
Normal file
22
code_markup/src/colors.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
use palette::{Hsv, LinSrgb};
|
||||
|
||||
pub type RgbaTup = (f32, f32, f32, f32);
|
||||
pub const WHITE: RgbaTup = (1.0, 1.0, 1.0, 1.0);
|
||||
|
||||
pub fn to_slice((r, g, b, a): RgbaTup) -> [f32; 4] {
|
||||
[r, g, b, a]
|
||||
}
|
||||
|
||||
pub fn from_hsb(hue: usize, saturation: usize, brightness: usize) -> RgbaTup {
|
||||
from_hsba(hue, saturation, brightness, 1.0)
|
||||
}
|
||||
|
||||
pub fn from_hsba(hue: usize, saturation: usize, brightness: usize, alpha: f32) -> RgbaTup {
|
||||
let rgb = LinSrgb::from(Hsv::new(
|
||||
hue as f32,
|
||||
(saturation as f32) / 100.0,
|
||||
(brightness as f32) / 100.0,
|
||||
));
|
||||
|
||||
(rgb.red, rgb.green, rgb.blue, alpha)
|
||||
}
|
5
code_markup/src/lib.rs
Normal file
5
code_markup/src/lib.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
pub mod markup;
|
||||
pub mod markup_error;
|
||||
pub mod slow_pool;
|
||||
pub mod syntax_highlight;
|
||||
pub mod colors;
|
123
code_markup/src/markup/attribute.rs
Normal file
123
code_markup/src/markup/attribute.rs
Normal file
|
@ -0,0 +1,123 @@
|
|||
#![allow(dead_code)]
|
||||
use snafu::ensure;
|
||||
|
||||
use crate::markup_error::{MarkResult, CaretNotFound};
|
||||
|
||||
#[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) -> MarkResult<()> {
|
||||
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(())
|
||||
}
|
||||
}
|
107
code_markup/src/markup/common_nodes.rs
Normal file
107
code_markup/src/markup/common_nodes.rs
Normal file
|
@ -0,0 +1,107 @@
|
|||
|
||||
use roc_ast::lang::core::{ast::ASTNodeId, expr::expr2::ExprId};
|
||||
|
||||
use crate::{slow_pool::MarkNodeId, syntax_highlight::HighlightStyle};
|
||||
|
||||
use super::{attribute::Attributes, nodes, nodes::MarkupNode};
|
||||
|
||||
pub fn new_equals_mn(ast_node_id: ASTNodeId, parent_id_opt: Option<MarkNodeId>) -> MarkupNode {
|
||||
MarkupNode::Text {
|
||||
content: nodes::EQUALS.to_owned(),
|
||||
ast_node_id,
|
||||
syn_high_style: HighlightStyle::Operator,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt,
|
||||
newlines_at_end: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_comma_mn(expr_id: ExprId, parent_id_opt: Option<MarkNodeId>) -> MarkupNode {
|
||||
MarkupNode::Text {
|
||||
content: nodes::COMMA.to_owned(),
|
||||
ast_node_id: ASTNodeId::AExprId(expr_id),
|
||||
syn_high_style: HighlightStyle::Blank,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt,
|
||||
newlines_at_end: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_blank_mn(ast_node_id: ASTNodeId, parent_id_opt: Option<MarkNodeId>) -> MarkupNode {
|
||||
MarkupNode::Blank {
|
||||
ast_node_id,
|
||||
syn_high_style: HighlightStyle::Blank,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt,
|
||||
newlines_at_end: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_blank_mn_w_nls(
|
||||
ast_node_id: ASTNodeId,
|
||||
parent_id_opt: Option<MarkNodeId>,
|
||||
nr_of_newlines: usize,
|
||||
) -> MarkupNode {
|
||||
MarkupNode::Blank {
|
||||
ast_node_id,
|
||||
syn_high_style: HighlightStyle::Blank,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt,
|
||||
newlines_at_end: nr_of_newlines,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_colon_mn(expr_id: ExprId, parent_id_opt: Option<MarkNodeId>) -> MarkupNode {
|
||||
MarkupNode::Text {
|
||||
content: nodes::COLON.to_owned(),
|
||||
ast_node_id: ASTNodeId::AExprId(expr_id),
|
||||
syn_high_style: HighlightStyle::Operator,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt,
|
||||
newlines_at_end: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_left_accolade_mn(expr_id: ExprId, parent_id_opt: Option<MarkNodeId>) -> MarkupNode {
|
||||
MarkupNode::Text {
|
||||
content: nodes::LEFT_ACCOLADE.to_owned(),
|
||||
ast_node_id: ASTNodeId::AExprId(expr_id),
|
||||
syn_high_style: HighlightStyle::Bracket,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt,
|
||||
newlines_at_end: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_right_accolade_mn(expr_id: ExprId, parent_id_opt: Option<MarkNodeId>) -> MarkupNode {
|
||||
MarkupNode::Text {
|
||||
content: nodes::RIGHT_ACCOLADE.to_owned(),
|
||||
ast_node_id: ASTNodeId::AExprId(expr_id),
|
||||
syn_high_style: HighlightStyle::Bracket,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt,
|
||||
newlines_at_end: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_left_square_mn(expr_id: ExprId, parent_id_opt: Option<MarkNodeId>) -> MarkupNode {
|
||||
MarkupNode::Text {
|
||||
content: nodes::LEFT_SQUARE_BR.to_owned(),
|
||||
ast_node_id: ASTNodeId::AExprId(expr_id),
|
||||
syn_high_style: HighlightStyle::Bracket,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt,
|
||||
newlines_at_end: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_right_square_mn(expr_id: ExprId, parent_id_opt: Option<MarkNodeId>) -> MarkupNode {
|
||||
MarkupNode::Text {
|
||||
content: nodes::RIGHT_SQUARE_BR.to_owned(),
|
||||
ast_node_id: ASTNodeId::AExprId(expr_id),
|
||||
syn_high_style: HighlightStyle::Bracket,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt,
|
||||
newlines_at_end: 0,
|
||||
}
|
||||
}
|
4
code_markup/src/markup/mod.rs
Normal file
4
code_markup/src/markup/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
pub mod attribute;
|
||||
pub mod common_nodes;
|
||||
pub mod nodes;
|
||||
pub mod top_level_def;
|
880
code_markup/src/markup/nodes.rs
Normal file
880
code_markup/src/markup/nodes.rs
Normal file
|
@ -0,0 +1,880 @@
|
|||
use crate::{markup::common_nodes::{new_blank_mn, new_colon_mn, new_comma_mn, new_equals_mn, new_left_accolade_mn, new_left_square_mn, new_right_accolade_mn, new_right_square_mn}, markup_error::MarkResult, slow_pool::{MarkNodeId, SlowPool}, syntax_highlight::HighlightStyle};
|
||||
|
||||
use super::{attribute::Attributes, common_nodes::new_blank_mn_w_nls, top_level_def::tld_mark_node};
|
||||
/*use crate::editor::ed_error::EdResult;
|
||||
use crate::editor::ed_error::ExpectedTextNode;
|
||||
use crate::editor::ed_error::{NestedNodeMissingChild, NestedNodeRequired};
|
||||
use crate::editor::markup::common_nodes::new_blank_mn;
|
||||
use crate::editor::markup::common_nodes::new_blank_mn_w_nls;
|
||||
use crate::editor::markup::common_nodes::new_colon_mn;
|
||||
use crate::editor::markup::common_nodes::new_comma_mn;
|
||||
use crate::editor::markup::common_nodes::new_equals_mn;
|
||||
use crate::editor::markup::common_nodes::new_left_accolade_mn;
|
||||
use crate::editor::markup::common_nodes::new_left_square_mn;
|
||||
use crate::editor::markup::common_nodes::new_right_accolade_mn;
|
||||
use crate::editor::markup::common_nodes::new_right_square_mn;
|
||||
use crate::editor::mvc::tld_value_update::tld_mark_node;
|
||||
use crate::editor::slow_pool::MarkNodeId;
|
||||
use crate::editor::slow_pool::SlowPool;
|
||||
use crate::editor::syntax_highlight::HighlightStyle;
|
||||
use crate::editor::util::index_of;
|
||||
use crate::lang::ast::Def2;
|
||||
use crate::lang::ast::DefId;
|
||||
use crate::lang::ast::ExprId;
|
||||
use crate::lang::ast::RecordField;
|
||||
use crate::lang::ast::ValueDef;
|
||||
use crate::lang::parse::ASTNodeId;
|
||||
use crate::lang::parse::{AppHeader, AST};
|
||||
use crate::lang::pattern::get_identifier_string;
|
||||
use crate::lang::{ast::Expr2, expr::Env, pool::PoolStr};
|
||||
use crate::ui::util::slice_get;*/
|
||||
use bumpalo::Bump;
|
||||
use roc_ast::{ast_error::ASTResult, lang::{core::{ast::{AST, ASTNodeId}, def::def2::{Def2, DefId}, expr::{expr2::{Expr2, ExprId}, record_field::RecordField}, header::AppHeader, pattern::get_identifier_string, val_def::ValueDef}, env::Env}, pool::pool_str::PoolStr};
|
||||
use roc_utils::{index_of, slice_get};
|
||||
use roc_module::symbol::Interns;
|
||||
use std::fmt;
|
||||
use crate::markup_error::{NestedNodeMissingChild, NestedNodeRequired, ExpectedTextNode};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MarkupNode {
|
||||
Nested {
|
||||
ast_node_id: ASTNodeId,
|
||||
children_ids: Vec<MarkNodeId>,
|
||||
parent_id_opt: Option<MarkNodeId>,
|
||||
newlines_at_end: usize,
|
||||
},
|
||||
Text {
|
||||
content: String,
|
||||
ast_node_id: ASTNodeId,
|
||||
syn_high_style: HighlightStyle,
|
||||
attributes: Attributes,
|
||||
parent_id_opt: Option<MarkNodeId>,
|
||||
newlines_at_end: usize,
|
||||
},
|
||||
Blank {
|
||||
ast_node_id: ASTNodeId,
|
||||
attributes: Attributes,
|
||||
syn_high_style: HighlightStyle, // TODO remove HighlightStyle, this is always HighlightStyle::Blank
|
||||
parent_id_opt: Option<MarkNodeId>,
|
||||
newlines_at_end: usize,
|
||||
},
|
||||
}
|
||||
|
||||
impl MarkupNode {
|
||||
pub fn get_ast_node_id(&self) -> ASTNodeId {
|
||||
match self {
|
||||
MarkupNode::Nested { ast_node_id, .. } => *ast_node_id,
|
||||
MarkupNode::Text { ast_node_id, .. } => *ast_node_id,
|
||||
MarkupNode::Blank { ast_node_id, .. } => *ast_node_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_parent_id_opt(&self) -> Option<MarkNodeId> {
|
||||
match self {
|
||||
MarkupNode::Nested { parent_id_opt, .. } => *parent_id_opt,
|
||||
MarkupNode::Text { parent_id_opt, .. } => *parent_id_opt,
|
||||
MarkupNode::Blank { parent_id_opt, .. } => *parent_id_opt,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_children_ids(&self) -> Vec<MarkNodeId> {
|
||||
match self {
|
||||
MarkupNode::Nested { children_ids, .. } => children_ids.to_vec(),
|
||||
MarkupNode::Text { .. } => vec![],
|
||||
MarkupNode::Blank { .. } => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_sibling_ids(&self, mark_node_pool: &SlowPool) -> Vec<MarkNodeId> {
|
||||
if let Some(parent_id) = self.get_parent_id_opt() {
|
||||
let parent_node = mark_node_pool.get(parent_id);
|
||||
|
||||
parent_node.get_children_ids()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
// return (index of child in list of children, closest ast index of child corresponding to ast node)
|
||||
pub fn get_child_indices(
|
||||
&self,
|
||||
child_id: MarkNodeId,
|
||||
mark_node_pool: &SlowPool,
|
||||
) -> MarkResult<(usize, usize)> {
|
||||
match self {
|
||||
MarkupNode::Nested { children_ids, .. } => {
|
||||
let mut mark_child_index_opt: Option<usize> = None;
|
||||
let mut child_ids_with_ast: Vec<MarkNodeId> = Vec::new();
|
||||
let self_ast_id = self.get_ast_node_id();
|
||||
|
||||
for (indx, &mark_child_id) in children_ids.iter().enumerate() {
|
||||
if mark_child_id == child_id {
|
||||
mark_child_index_opt = Some(indx);
|
||||
}
|
||||
|
||||
let child_mark_node = mark_node_pool.get(mark_child_id);
|
||||
// a node that points to the same ast_node as the parent is a ',', '[', ']'
|
||||
// those are not "real" ast children
|
||||
if child_mark_node.get_ast_node_id() != self_ast_id {
|
||||
child_ids_with_ast.push(mark_child_id)
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(child_index) = mark_child_index_opt {
|
||||
if child_index == (children_ids.len() - 1) {
|
||||
let ast_child_index = child_ids_with_ast.len();
|
||||
|
||||
Ok((child_index, ast_child_index))
|
||||
} else {
|
||||
// we want to find the index of the closest ast mark node to child_index
|
||||
let mut indices_in_mark = vec![];
|
||||
|
||||
for &c_id in child_ids_with_ast.iter() {
|
||||
indices_in_mark.push(index_of(c_id, children_ids)?);
|
||||
}
|
||||
|
||||
let mut last_diff = usize::MAX;
|
||||
let mut best_index = 0;
|
||||
|
||||
for index in indices_in_mark.iter() {
|
||||
let curr_diff =
|
||||
isize::abs((*index as isize) - (child_index as isize)) as usize;
|
||||
|
||||
if curr_diff >= last_diff {
|
||||
break;
|
||||
} else {
|
||||
last_diff = curr_diff;
|
||||
best_index = *index;
|
||||
}
|
||||
}
|
||||
|
||||
let closest_ast_child = slice_get(best_index, children_ids)?;
|
||||
|
||||
let closest_ast_child_index =
|
||||
index_of(*closest_ast_child, &child_ids_with_ast)?;
|
||||
|
||||
// +1 because we want to insert after ast_child
|
||||
Ok((child_index, closest_ast_child_index + 1))
|
||||
}
|
||||
} else {
|
||||
NestedNodeMissingChild {
|
||||
node_id: child_id,
|
||||
children_ids: children_ids.clone(),
|
||||
}
|
||||
.fail()
|
||||
}
|
||||
}
|
||||
_ => NestedNodeRequired {
|
||||
node_type: self.node_type_as_string(),
|
||||
}
|
||||
.fail(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_content(&self) -> String {
|
||||
match self {
|
||||
MarkupNode::Nested { .. } => "".to_owned(),
|
||||
MarkupNode::Text { content, .. } => content.clone(),
|
||||
MarkupNode::Blank { .. } => BLANK_PLACEHOLDER.to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
// gets content and adds newline from newline_at_end
|
||||
pub fn get_full_content(&self) -> String {
|
||||
let mut full_content = self.get_content();
|
||||
|
||||
for _ in 0..self.get_newlines_at_end() {
|
||||
full_content.push('\n')
|
||||
}
|
||||
|
||||
full_content
|
||||
}
|
||||
|
||||
pub fn get_content_mut(&mut self) -> MarkResult<&mut String> {
|
||||
match self {
|
||||
MarkupNode::Nested { .. } => ExpectedTextNode {
|
||||
function_name: "set_content".to_owned(),
|
||||
node_type: self.node_type_as_string(),
|
||||
}
|
||||
.fail(),
|
||||
MarkupNode::Text { content, .. } => Ok(content),
|
||||
MarkupNode::Blank { .. } => ExpectedTextNode {
|
||||
function_name: "set_content".to_owned(),
|
||||
node_type: self.node_type_as_string(),
|
||||
}
|
||||
.fail(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_all_alphanumeric(&self) -> bool {
|
||||
self.get_content()
|
||||
.chars()
|
||||
.all(|chr| chr.is_ascii_alphanumeric())
|
||||
}
|
||||
|
||||
pub fn add_child_at_index(&mut self, index: usize, child_id: MarkNodeId) -> MarkResult<()> {
|
||||
if let MarkupNode::Nested { children_ids, .. } = self {
|
||||
children_ids.splice(index..index, vec![child_id]);
|
||||
} else {
|
||||
NestedNodeRequired {
|
||||
node_type: self.node_type_as_string(),
|
||||
}
|
||||
.fail()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn node_type_as_string(&self) -> String {
|
||||
let type_str = match self {
|
||||
MarkupNode::Nested { .. } => "Nested",
|
||||
MarkupNode::Text { .. } => "Text",
|
||||
MarkupNode::Blank { .. } => "Blank",
|
||||
};
|
||||
|
||||
type_str.to_owned()
|
||||
}
|
||||
|
||||
pub fn is_blank(&self) -> bool {
|
||||
matches!(self, MarkupNode::Blank { .. })
|
||||
}
|
||||
|
||||
pub fn is_nested(&self) -> bool {
|
||||
matches!(self, MarkupNode::Nested { .. })
|
||||
}
|
||||
|
||||
pub fn get_newlines_at_end(&self) -> usize {
|
||||
match self {
|
||||
MarkupNode::Nested {
|
||||
newlines_at_end, ..
|
||||
} => *newlines_at_end,
|
||||
MarkupNode::Text {
|
||||
newlines_at_end, ..
|
||||
} => *newlines_at_end,
|
||||
MarkupNode::Blank {
|
||||
newlines_at_end, ..
|
||||
} => *newlines_at_end,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_newline_at_end(&mut self) {
|
||||
match self {
|
||||
MarkupNode::Nested {
|
||||
newlines_at_end, ..
|
||||
} => *newlines_at_end += 1,
|
||||
MarkupNode::Text {
|
||||
newlines_at_end, ..
|
||||
} => *newlines_at_end += 1,
|
||||
MarkupNode::Blank {
|
||||
newlines_at_end, ..
|
||||
} => *newlines_at_end += 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_string<'a>(env: &Env<'a>, pool_str: &PoolStr) -> String {
|
||||
pool_str.as_str(env.pool).to_owned()
|
||||
}
|
||||
|
||||
pub const BLANK_PLACEHOLDER: &str = " ";
|
||||
pub const LEFT_ACCOLADE: &str = "{ ";
|
||||
pub const RIGHT_ACCOLADE: &str = " }";
|
||||
pub const LEFT_SQUARE_BR: &str = "[ ";
|
||||
pub const RIGHT_SQUARE_BR: &str = " ]";
|
||||
pub const COLON: &str = ": ";
|
||||
pub const COMMA: &str = ", ";
|
||||
pub const STRING_QUOTES: &str = "\"\"";
|
||||
pub const EQUALS: &str = " = ";
|
||||
|
||||
fn new_markup_node(
|
||||
text: String,
|
||||
node_id: ASTNodeId,
|
||||
highlight_style: HighlightStyle,
|
||||
mark_node_pool: &mut SlowPool,
|
||||
) -> MarkNodeId {
|
||||
let node = MarkupNode::Text {
|
||||
content: text,
|
||||
ast_node_id: node_id,
|
||||
syn_high_style: highlight_style,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt: None,
|
||||
newlines_at_end: 0,
|
||||
};
|
||||
|
||||
mark_node_pool.add(node)
|
||||
}
|
||||
|
||||
pub fn def2_to_markup<'a, 'b>(
|
||||
arena: &'a Bump,
|
||||
env: &mut Env<'b>,
|
||||
def2: &Def2,
|
||||
def2_node_id: DefId,
|
||||
mark_node_pool: &mut SlowPool,
|
||||
interns: &Interns,
|
||||
) -> ASTResult<MarkNodeId> {
|
||||
let ast_node_id = ASTNodeId::ADefId(def2_node_id);
|
||||
|
||||
let mark_node_id = match def2 {
|
||||
Def2::ValueDef {
|
||||
identifier_id,
|
||||
expr_id,
|
||||
} => {
|
||||
let expr_mn_id = expr2_to_markup(
|
||||
arena,
|
||||
env,
|
||||
env.pool.get(*expr_id),
|
||||
*expr_id,
|
||||
mark_node_pool,
|
||||
interns,
|
||||
)?;
|
||||
|
||||
let tld_mn = tld_mark_node(
|
||||
*identifier_id,
|
||||
expr_mn_id,
|
||||
ast_node_id,
|
||||
mark_node_pool,
|
||||
env,
|
||||
interns,
|
||||
)?;
|
||||
|
||||
mark_node_pool.add(tld_mn)
|
||||
}
|
||||
Def2::Blank => mark_node_pool.add(new_blank_mn_w_nls(ast_node_id, None, 2)),
|
||||
};
|
||||
|
||||
Ok(mark_node_id)
|
||||
}
|
||||
|
||||
// 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,
|
||||
expr2_node_id: ExprId,
|
||||
mark_node_pool: &mut SlowPool,
|
||||
interns: &Interns,
|
||||
) -> ASTResult<MarkNodeId> {
|
||||
let ast_node_id = ASTNodeId::AExprId(expr2_node_id);
|
||||
|
||||
let mark_node_id = match expr2 {
|
||||
Expr2::SmallInt { text, .. }
|
||||
| Expr2::I128 { text, .. }
|
||||
| Expr2::U128 { text, .. }
|
||||
| Expr2::Float { text, .. } => {
|
||||
let num_str = get_string(env, text);
|
||||
|
||||
new_markup_node(num_str, ast_node_id, HighlightStyle::Number, mark_node_pool)
|
||||
}
|
||||
Expr2::Str(text) => new_markup_node(
|
||||
"\"".to_owned() + text.as_str(env.pool) + "\"",
|
||||
ast_node_id,
|
||||
HighlightStyle::String,
|
||||
mark_node_pool,
|
||||
),
|
||||
Expr2::GlobalTag { name, .. } => new_markup_node(
|
||||
get_string(env, name),
|
||||
ast_node_id,
|
||||
HighlightStyle::Type,
|
||||
mark_node_pool,
|
||||
),
|
||||
Expr2::Call { expr: expr_id, .. } => {
|
||||
let expr = env.pool.get(*expr_id);
|
||||
expr2_to_markup(arena, env, expr, *expr_id, mark_node_pool, interns)?
|
||||
}
|
||||
Expr2::Var(symbol) => {
|
||||
//TODO make bump_format with arena
|
||||
let text = format!("{:?}", symbol);
|
||||
new_markup_node(text, ast_node_id, HighlightStyle::Variable, mark_node_pool)
|
||||
}
|
||||
Expr2::List { elems, .. } => {
|
||||
let mut children_ids =
|
||||
vec![mark_node_pool.add(new_left_square_mn(expr2_node_id, None))];
|
||||
|
||||
let indexed_node_ids: Vec<(usize, ExprId)> =
|
||||
elems.iter(env.pool).copied().enumerate().collect();
|
||||
|
||||
for (idx, node_id) in indexed_node_ids.iter() {
|
||||
let sub_expr2 = env.pool.get(*node_id);
|
||||
|
||||
children_ids.push(expr2_to_markup(
|
||||
arena,
|
||||
env,
|
||||
sub_expr2,
|
||||
*node_id,
|
||||
mark_node_pool,
|
||||
interns,
|
||||
)?);
|
||||
|
||||
if idx + 1 < elems.len() {
|
||||
children_ids.push(mark_node_pool.add(new_comma_mn(expr2_node_id, None)));
|
||||
}
|
||||
}
|
||||
children_ids.push(mark_node_pool.add(new_right_square_mn(expr2_node_id, None)));
|
||||
|
||||
let list_node = MarkupNode::Nested {
|
||||
ast_node_id,
|
||||
children_ids,
|
||||
parent_id_opt: None,
|
||||
newlines_at_end: 0,
|
||||
};
|
||||
|
||||
mark_node_pool.add(list_node)
|
||||
}
|
||||
Expr2::EmptyRecord => {
|
||||
let children_ids = vec![
|
||||
mark_node_pool.add(new_left_accolade_mn(expr2_node_id, None)),
|
||||
mark_node_pool.add(new_right_accolade_mn(expr2_node_id, None)),
|
||||
];
|
||||
|
||||
let record_node = MarkupNode::Nested {
|
||||
ast_node_id,
|
||||
children_ids,
|
||||
parent_id_opt: None,
|
||||
newlines_at_end: 0,
|
||||
};
|
||||
|
||||
mark_node_pool.add(record_node)
|
||||
}
|
||||
Expr2::Record { fields, .. } => {
|
||||
let mut children_ids =
|
||||
vec![mark_node_pool.add(new_left_accolade_mn(expr2_node_id, None))];
|
||||
|
||||
for (idx, field_node_id) in fields.iter_node_ids().enumerate() {
|
||||
let record_field = env.pool.get(field_node_id);
|
||||
|
||||
let field_name = record_field.get_record_field_pool_str();
|
||||
|
||||
children_ids.push(new_markup_node(
|
||||
field_name.as_str(env.pool).to_owned(),
|
||||
ast_node_id,
|
||||
HighlightStyle::RecordField,
|
||||
mark_node_pool,
|
||||
));
|
||||
|
||||
match record_field {
|
||||
RecordField::InvalidLabelOnly(_, _) => (),
|
||||
RecordField::LabelOnly(_, _, _) => (),
|
||||
RecordField::LabeledValue(_, _, sub_expr2_node_id) => {
|
||||
children_ids.push(mark_node_pool.add(new_colon_mn(expr2_node_id, None)));
|
||||
|
||||
let sub_expr2 = env.pool.get(*sub_expr2_node_id);
|
||||
children_ids.push(expr2_to_markup(
|
||||
arena,
|
||||
env,
|
||||
sub_expr2,
|
||||
*sub_expr2_node_id,
|
||||
mark_node_pool,
|
||||
interns,
|
||||
)?);
|
||||
}
|
||||
}
|
||||
|
||||
if idx + 1 < fields.len() {
|
||||
children_ids.push(mark_node_pool.add(new_comma_mn(expr2_node_id, None)));
|
||||
}
|
||||
}
|
||||
|
||||
children_ids.push(mark_node_pool.add(new_right_accolade_mn(expr2_node_id, None)));
|
||||
|
||||
let record_node = MarkupNode::Nested {
|
||||
ast_node_id,
|
||||
children_ids,
|
||||
parent_id_opt: None,
|
||||
newlines_at_end: 0,
|
||||
};
|
||||
|
||||
mark_node_pool.add(record_node)
|
||||
}
|
||||
Expr2::Blank => mark_node_pool.add(new_blank_mn(ast_node_id, None)),
|
||||
Expr2::LetValue {
|
||||
def_id,
|
||||
body_id: _,
|
||||
body_var: _,
|
||||
} => {
|
||||
let pattern_id = env.pool.get(*def_id).get_pattern_id();
|
||||
|
||||
let pattern2 = env.pool.get(pattern_id);
|
||||
|
||||
let val_name = get_identifier_string(pattern2, interns)?;
|
||||
|
||||
let val_name_mn = MarkupNode::Text {
|
||||
content: val_name,
|
||||
ast_node_id,
|
||||
syn_high_style: HighlightStyle::Variable,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt: None,
|
||||
newlines_at_end: 0,
|
||||
};
|
||||
|
||||
let val_name_mn_id = mark_node_pool.add(val_name_mn);
|
||||
|
||||
let equals_mn_id = mark_node_pool.add(new_equals_mn(ast_node_id, None));
|
||||
|
||||
let value_def = env.pool.get(*def_id);
|
||||
|
||||
match value_def {
|
||||
ValueDef::NoAnnotation {
|
||||
pattern_id: _,
|
||||
expr_id,
|
||||
expr_var: _,
|
||||
} => {
|
||||
let body_mn_id = expr2_to_markup(
|
||||
arena,
|
||||
env,
|
||||
env.pool.get(*expr_id),
|
||||
*expr_id,
|
||||
mark_node_pool,
|
||||
interns,
|
||||
)?;
|
||||
|
||||
let body_mn = mark_node_pool.get_mut(body_mn_id);
|
||||
body_mn.add_newline_at_end();
|
||||
|
||||
let full_let_node = MarkupNode::Nested {
|
||||
ast_node_id,
|
||||
children_ids: vec![val_name_mn_id, equals_mn_id, body_mn_id],
|
||||
parent_id_opt: None,
|
||||
newlines_at_end: 1,
|
||||
};
|
||||
|
||||
mark_node_pool.add(full_let_node)
|
||||
}
|
||||
other => {
|
||||
unimplemented!(
|
||||
"I don't know how to convert {:?} into a MarkupNode yet.",
|
||||
other
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Expr2::RuntimeError() => new_markup_node(
|
||||
"RunTimeError".to_string(),
|
||||
ast_node_id,
|
||||
HighlightStyle::Blank,
|
||||
mark_node_pool,
|
||||
),
|
||||
rest => todo!("implement expr2_to_markup for {:?}", rest),
|
||||
};
|
||||
|
||||
Ok(mark_node_id)
|
||||
}
|
||||
|
||||
pub fn set_parent_for_all(markup_node_id: MarkNodeId, mark_node_pool: &mut SlowPool) {
|
||||
let node = mark_node_pool.get(markup_node_id);
|
||||
|
||||
if let MarkupNode::Nested {
|
||||
ast_node_id: _,
|
||||
children_ids,
|
||||
parent_id_opt: _,
|
||||
newlines_at_end: _,
|
||||
} = 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, mark_node_pool);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_parent_for_all_helper(
|
||||
markup_node_id: MarkNodeId,
|
||||
parent_node_id: MarkNodeId,
|
||||
mark_node_pool: &mut SlowPool,
|
||||
) {
|
||||
let node = mark_node_pool.get_mut(markup_node_id);
|
||||
|
||||
match node {
|
||||
MarkupNode::Nested {
|
||||
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, mark_node_pool);
|
||||
}
|
||||
}
|
||||
MarkupNode::Text { parent_id_opt, .. } => *parent_id_opt = Some(parent_node_id),
|
||||
MarkupNode::Blank { parent_id_opt, .. } => *parent_id_opt = Some(parent_node_id),
|
||||
}
|
||||
}
|
||||
|
||||
fn header_mn(content: String, expr_id: ExprId, mark_node_pool: &mut SlowPool) -> MarkNodeId {
|
||||
let mark_node = MarkupNode::Text {
|
||||
content,
|
||||
ast_node_id: ASTNodeId::AExprId(expr_id),
|
||||
syn_high_style: HighlightStyle::PackageRelated,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt: None,
|
||||
newlines_at_end: 0,
|
||||
};
|
||||
|
||||
mark_node_pool.add(mark_node)
|
||||
}
|
||||
|
||||
fn header_val_mn(
|
||||
content: String,
|
||||
expr_id: ExprId,
|
||||
highlight_style: HighlightStyle,
|
||||
mark_node_pool: &mut SlowPool,
|
||||
) -> MarkNodeId {
|
||||
let mark_node = MarkupNode::Text {
|
||||
content,
|
||||
ast_node_id: ASTNodeId::AExprId(expr_id),
|
||||
syn_high_style: highlight_style,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt: None,
|
||||
newlines_at_end: 0,
|
||||
};
|
||||
|
||||
mark_node_pool.add(mark_node)
|
||||
}
|
||||
|
||||
pub fn header_to_markup(app_header: &AppHeader, mark_node_pool: &mut SlowPool) -> MarkNodeId {
|
||||
let expr_id = app_header.ast_node_id;
|
||||
let ast_node_id = ASTNodeId::AExprId(expr_id);
|
||||
|
||||
let app_node_id = header_mn("app ".to_owned(), expr_id, mark_node_pool);
|
||||
|
||||
let app_name_node_id = header_val_mn(
|
||||
app_header.app_name.clone(),
|
||||
expr_id,
|
||||
HighlightStyle::String,
|
||||
mark_node_pool,
|
||||
);
|
||||
|
||||
let full_app_node = MarkupNode::Nested {
|
||||
ast_node_id,
|
||||
children_ids: vec![app_node_id, app_name_node_id],
|
||||
parent_id_opt: None,
|
||||
newlines_at_end: 1,
|
||||
};
|
||||
|
||||
let packages_node_id = header_mn(" packages ".to_owned(), expr_id, mark_node_pool);
|
||||
|
||||
let pack_left_acc_node_id = mark_node_pool.add(new_left_accolade_mn(expr_id, None));
|
||||
|
||||
let pack_base_node_id = header_val_mn(
|
||||
"base: ".to_owned(),
|
||||
expr_id,
|
||||
HighlightStyle::RecordField,
|
||||
mark_node_pool,
|
||||
);
|
||||
|
||||
let pack_val_node_id = header_val_mn(
|
||||
app_header.packages_base.clone(),
|
||||
expr_id,
|
||||
HighlightStyle::String,
|
||||
mark_node_pool,
|
||||
);
|
||||
|
||||
let pack_right_acc_node_id = mark_node_pool.add(new_right_accolade_mn(expr_id, None));
|
||||
|
||||
let full_packages_node = MarkupNode::Nested {
|
||||
ast_node_id,
|
||||
children_ids: vec![
|
||||
packages_node_id,
|
||||
pack_left_acc_node_id,
|
||||
pack_base_node_id,
|
||||
pack_val_node_id,
|
||||
pack_right_acc_node_id,
|
||||
],
|
||||
parent_id_opt: None,
|
||||
newlines_at_end: 1,
|
||||
};
|
||||
|
||||
let imports_node_id = header_mn(" imports ".to_owned(), expr_id, mark_node_pool);
|
||||
|
||||
let imports_left_square_node_id = mark_node_pool.add(new_left_square_mn(expr_id, None));
|
||||
|
||||
let mut import_child_ids: Vec<MarkNodeId> = add_header_mn_list(
|
||||
&app_header.imports,
|
||||
expr_id,
|
||||
HighlightStyle::Import,
|
||||
mark_node_pool,
|
||||
);
|
||||
|
||||
let imports_right_square_node_id = mark_node_pool.add(new_right_square_mn(expr_id, None));
|
||||
|
||||
let mut full_import_children = vec![imports_node_id, imports_left_square_node_id];
|
||||
|
||||
full_import_children.append(&mut import_child_ids);
|
||||
full_import_children.push(imports_right_square_node_id);
|
||||
|
||||
let full_import_node = MarkupNode::Nested {
|
||||
ast_node_id,
|
||||
children_ids: full_import_children,
|
||||
parent_id_opt: None,
|
||||
newlines_at_end: 1,
|
||||
};
|
||||
|
||||
let provides_node_id = header_mn(" provides ".to_owned(), expr_id, mark_node_pool);
|
||||
|
||||
let provides_left_square_node_id = mark_node_pool.add(new_left_square_mn(expr_id, None));
|
||||
|
||||
let mut provides_val_node_ids: Vec<MarkNodeId> = add_header_mn_list(
|
||||
&app_header.provides,
|
||||
expr_id,
|
||||
HighlightStyle::Provides,
|
||||
mark_node_pool,
|
||||
);
|
||||
|
||||
let provides_right_square_node_id = mark_node_pool.add(new_right_square_mn(expr_id, None));
|
||||
|
||||
let provides_end_node_id = header_mn(" to base".to_owned(), expr_id, mark_node_pool);
|
||||
|
||||
let mut full_provides_children = vec![provides_node_id, provides_left_square_node_id];
|
||||
|
||||
full_provides_children.append(&mut provides_val_node_ids);
|
||||
full_provides_children.push(provides_right_square_node_id);
|
||||
full_provides_children.push(provides_end_node_id);
|
||||
|
||||
let full_provides_node = MarkupNode::Nested {
|
||||
ast_node_id,
|
||||
children_ids: full_provides_children,
|
||||
parent_id_opt: None,
|
||||
newlines_at_end: 1,
|
||||
};
|
||||
|
||||
let full_app_node_id = mark_node_pool.add(full_app_node);
|
||||
let full_packages_node = mark_node_pool.add(full_packages_node);
|
||||
let full_import_node_id = mark_node_pool.add(full_import_node);
|
||||
let full_provides_node_id = mark_node_pool.add(full_provides_node);
|
||||
|
||||
let header_mark_node = MarkupNode::Nested {
|
||||
ast_node_id,
|
||||
children_ids: vec![
|
||||
full_app_node_id,
|
||||
full_packages_node,
|
||||
full_import_node_id,
|
||||
full_provides_node_id,
|
||||
],
|
||||
parent_id_opt: None,
|
||||
newlines_at_end: 1,
|
||||
};
|
||||
|
||||
let header_mn_id = mark_node_pool.add(header_mark_node);
|
||||
|
||||
set_parent_for_all(header_mn_id, mark_node_pool);
|
||||
|
||||
header_mn_id
|
||||
}
|
||||
|
||||
// Used for provides and imports
|
||||
fn add_header_mn_list(
|
||||
str_vec: &[String],
|
||||
expr_id: ExprId,
|
||||
highlight_style: HighlightStyle,
|
||||
mark_node_pool: &mut SlowPool,
|
||||
) -> Vec<MarkNodeId> {
|
||||
let nr_of_elts = str_vec.len();
|
||||
|
||||
str_vec
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(indx, provide_str)| {
|
||||
let provide_str = header_val_mn(
|
||||
provide_str.to_owned(),
|
||||
expr_id,
|
||||
highlight_style,
|
||||
mark_node_pool,
|
||||
);
|
||||
|
||||
if indx != nr_of_elts - 1 {
|
||||
vec![provide_str, mark_node_pool.add(new_comma_mn(expr_id, None))]
|
||||
} else {
|
||||
vec![provide_str]
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn ast_to_mark_nodes<'a, 'b>(
|
||||
arena: &'a Bump,
|
||||
env: &mut Env<'b>,
|
||||
ast: &AST,
|
||||
mark_node_pool: &mut SlowPool,
|
||||
interns: &Interns,
|
||||
) -> ASTResult<Vec<MarkNodeId>> {
|
||||
let mut all_mark_node_ids = vec![header_to_markup(&ast.header, mark_node_pool)];
|
||||
|
||||
for &def_id in ast.def_ids.iter() {
|
||||
let def2 = env.pool.get(def_id);
|
||||
|
||||
let expr2_markup_id = def2_to_markup(arena, env, def2, def_id, mark_node_pool, interns)?;
|
||||
|
||||
set_parent_for_all(expr2_markup_id, mark_node_pool);
|
||||
|
||||
all_mark_node_ids.push(expr2_markup_id);
|
||||
}
|
||||
|
||||
Ok(all_mark_node_ids)
|
||||
}
|
||||
|
||||
impl fmt::Display for MarkupNode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{} ({}, {})",
|
||||
self.node_type_as_string(),
|
||||
self.get_content(),
|
||||
self.get_newlines_at_end()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tree_as_string(root_node_id: MarkNodeId, mark_node_pool: &SlowPool) -> String {
|
||||
let mut full_string = "\n(mark_node_tree)\n".to_owned();
|
||||
|
||||
let node = mark_node_pool.get(root_node_id);
|
||||
|
||||
full_string.push_str(&format!("{} mn_id {}\n", node, root_node_id));
|
||||
|
||||
tree_as_string_helper(node, 1, &mut full_string, mark_node_pool);
|
||||
|
||||
full_string
|
||||
}
|
||||
|
||||
fn tree_as_string_helper(
|
||||
node: &MarkupNode,
|
||||
level: usize,
|
||||
tree_string: &mut String,
|
||||
mark_node_pool: &SlowPool,
|
||||
) {
|
||||
for child_id in node.get_children_ids() {
|
||||
let mut full_str = std::iter::repeat("|--- ")
|
||||
.take(level)
|
||||
.collect::<Vec<&str>>()
|
||||
.join("")
|
||||
.to_owned();
|
||||
|
||||
let child = mark_node_pool.get(child_id);
|
||||
let child_str = format!("{}", mark_node_pool.get(child_id)).replace("\n", "\\n");
|
||||
|
||||
full_str.push_str(&format!("{} mn_id {}\n", child_str, child_id));
|
||||
|
||||
tree_string.push_str(&full_str);
|
||||
|
||||
tree_as_string_helper(child, level + 1, tree_string, mark_node_pool);
|
||||
}
|
||||
}
|
||||
|
||||
// return to the the root parent_id of the current node
|
||||
pub fn get_root_mark_node_id(mark_node_id: MarkNodeId, mark_node_pool: &SlowPool) -> MarkNodeId {
|
||||
let mut curr_mark_node_id = mark_node_id;
|
||||
let mut curr_parent_id_opt = mark_node_pool.get(curr_mark_node_id).get_parent_id_opt();
|
||||
|
||||
while let Some(curr_parent_id) = curr_parent_id_opt {
|
||||
curr_mark_node_id = curr_parent_id;
|
||||
curr_parent_id_opt = mark_node_pool.get(curr_mark_node_id).get_parent_id_opt();
|
||||
}
|
||||
|
||||
curr_mark_node_id
|
||||
}
|
39
code_markup/src/markup/top_level_def.rs
Normal file
39
code_markup/src/markup/top_level_def.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
use roc_ast::{ast_error::ASTResult, lang::{core::{ast::ASTNodeId, pattern::{PatternId, get_identifier_string}}, env::Env}};
|
||||
use roc_module::symbol::Interns;
|
||||
|
||||
use crate::{markup::{attribute::Attributes, common_nodes::new_equals_mn, nodes::MarkupNode}, slow_pool::{MarkNodeId, SlowPool}, syntax_highlight::HighlightStyle};
|
||||
|
||||
|
||||
pub fn tld_mark_node<'a>(
|
||||
identifier_id: PatternId,
|
||||
expr_mark_node_id: MarkNodeId,
|
||||
ast_node_id: ASTNodeId,
|
||||
mark_node_pool: &mut SlowPool,
|
||||
env: &Env<'a>,
|
||||
interns: &Interns,
|
||||
) -> ASTResult<MarkupNode> {
|
||||
let pattern2 = env.pool.get(identifier_id);
|
||||
let val_name = get_identifier_string(pattern2, interns)?;
|
||||
|
||||
let val_name_mn = MarkupNode::Text {
|
||||
content: val_name,
|
||||
ast_node_id,
|
||||
syn_high_style: HighlightStyle::Variable,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt: None,
|
||||
newlines_at_end: 0,
|
||||
};
|
||||
|
||||
let val_name_mn_id = mark_node_pool.add(val_name_mn);
|
||||
|
||||
let equals_mn_id = mark_node_pool.add(new_equals_mn(ast_node_id, None));
|
||||
|
||||
let full_let_node = MarkupNode::Nested {
|
||||
ast_node_id,
|
||||
children_ids: vec![val_name_mn_id, equals_mn_id, expr_mark_node_id],
|
||||
parent_id_opt: None,
|
||||
newlines_at_end: 2,
|
||||
};
|
||||
|
||||
Ok(full_let_node)
|
||||
}
|
56
code_markup/src/markup_error.rs
Normal file
56
code_markup/src/markup_error.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
|
||||
use roc_utils::util_error::UtilError;
|
||||
use snafu::{Backtrace, NoneError, Snafu, ResultExt};
|
||||
|
||||
use crate::slow_pool::MarkNodeId;
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
#[snafu(visibility(pub))]
|
||||
pub enum MarkError {
|
||||
#[snafu(display(
|
||||
"CaretNotFound: No carets were found in the expected node with id {}",
|
||||
node_id
|
||||
))]
|
||||
CaretNotFound {
|
||||
node_id: MarkNodeId,
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
#[snafu(display(
|
||||
"ExpectedTextNode: the function {} expected a Text node, got {} instead.",
|
||||
function_name,
|
||||
node_type
|
||||
))]
|
||||
ExpectedTextNode {
|
||||
function_name: String,
|
||||
node_type: String,
|
||||
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: MarkNodeId,
|
||||
children_ids: Vec<MarkNodeId>,
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
#[snafu(display(
|
||||
"NestedNodeRequired: required a Nested node at this position, node was a {}.",
|
||||
node_type
|
||||
))]
|
||||
NestedNodeRequired {
|
||||
node_type: String,
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
#[snafu(display("UIError: {}", msg))]
|
||||
UtilErrorBacktrace { msg: String, backtrace: Backtrace },
|
||||
}
|
||||
|
||||
pub type MarkResult<T, E = MarkError> = std::result::Result<T, E>;
|
||||
|
||||
impl From<UtilError> for MarkError {
|
||||
fn from(util_err: UtilError) -> Self {
|
||||
let msg = format!("{}", util_err);
|
||||
|
||||
// hack to handle MarkError derive
|
||||
let dummy_res: Result<(), NoneError> = Err(NoneError {});
|
||||
dummy_res.context(UtilErrorBacktrace { msg }).unwrap_err()
|
||||
}
|
||||
}
|
76
code_markup/src/slow_pool.rs
Normal file
76
code_markup/src/slow_pool.rs
Normal file
|
@ -0,0 +1,76 @@
|
|||
|
||||
use std::fmt;
|
||||
|
||||
use crate::markup::nodes::MarkupNode;
|
||||
|
||||
pub type MarkNodeId = 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) -> MarkNodeId {
|
||||
let id = self.nodes.len();
|
||||
|
||||
self.nodes.push(node);
|
||||
|
||||
id
|
||||
}
|
||||
|
||||
pub fn get(&self, node_id: MarkNodeId) -> &MarkupNode {
|
||||
// unwrap because Pool doesn't return Result either
|
||||
self.nodes.get(node_id).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, node_id: MarkNodeId) -> &mut MarkupNode {
|
||||
// unwrap because Pool doesn't return Result either
|
||||
self.nodes.get_mut(node_id).unwrap()
|
||||
}
|
||||
|
||||
pub fn replace_node(&mut self, node_id: MarkNodeId, new_node: MarkupNode) {
|
||||
self.nodes[node_id] = new_node;
|
||||
|
||||
// TODO delete children of old node, this requires SlowPool to be changed to
|
||||
// make sure the indexes still make sense after removal/compaction
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SlowPool {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "\n\n(mark_node_pool)\n")?;
|
||||
|
||||
for (index, node) in self.nodes.iter().enumerate() {
|
||||
let ast_node_id_str = format!("{:?}", node.get_ast_node_id());
|
||||
let ast_node_id: String = ast_node_id_str
|
||||
.chars()
|
||||
.filter(|c| c.is_ascii_digit())
|
||||
.collect();
|
||||
|
||||
let mut child_str = String::new();
|
||||
|
||||
let node_children = node.get_children_ids();
|
||||
|
||||
if !node_children.is_empty() {
|
||||
child_str = format!("children: {:?}", node_children);
|
||||
}
|
||||
|
||||
writeln!(
|
||||
f,
|
||||
"{}: {} ({}) ast_id {:?} {}",
|
||||
index,
|
||||
node.node_type_as_string(),
|
||||
node.get_content(),
|
||||
ast_node_id.parse::<usize>().unwrap(),
|
||||
child_str
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
48
code_markup/src/syntax_highlight.rs
Normal file
48
code_markup/src/syntax_highlight.rs
Normal file
|
@ -0,0 +1,48 @@
|
|||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::colors::{self, RgbaTup, from_hsb};
|
||||
|
||||
#[derive(Hash, Eq, PartialEq, Copy, Clone, Debug, Deserialize, Serialize)]
|
||||
pub enum HighlightStyle {
|
||||
Operator, // =+-<>...
|
||||
String,
|
||||
FunctionName,
|
||||
Type,
|
||||
Bracket,
|
||||
Number,
|
||||
PackageRelated, // app, packages, imports, exposes, provides...
|
||||
Variable,
|
||||
RecordField,
|
||||
Import,
|
||||
Provides,
|
||||
Blank,
|
||||
}
|
||||
|
||||
pub fn default_highlight_map() -> HashMap<HighlightStyle, RgbaTup> {
|
||||
use HighlightStyle::*;
|
||||
|
||||
let mut highlight_map = HashMap::new();
|
||||
[
|
||||
(Operator, colors::WHITE),
|
||||
(String, from_hsb(346, 65, 97)),
|
||||
(FunctionName, colors::WHITE),
|
||||
(Type, colors::WHITE),
|
||||
(Bracket, from_hsb(347, 80, 100)),
|
||||
(Number, from_hsb(185, 50, 75)),
|
||||
(PackageRelated, colors::WHITE),
|
||||
(Variable, colors::WHITE),
|
||||
(RecordField, from_hsb(258, 50, 90)),
|
||||
(Import, from_hsb(185, 50, 75)),
|
||||
(Provides, from_hsb(185, 50, 75)),
|
||||
(Blank, from_hsb(258, 50, 90)),
|
||||
// comment from_hsb(285, 6, 47) or 186, 35, 40
|
||||
]
|
||||
.iter()
|
||||
.for_each(|tup| {
|
||||
highlight_map.insert(tup.0, tup.1);
|
||||
});
|
||||
|
||||
highlight_map
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
use crate::editor::markup::nodes::MarkupNode;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use super::markup::nodes::MarkupNode;
|
||||
|
||||
|
||||
pub type MarkNodeId = usize;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
12
utils/Cargo.toml
Normal file
12
utils/Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "roc_utils"
|
||||
version = "0.1.0"
|
||||
authors = ["The Roc Contributors"]
|
||||
license = "UPL-1.0"
|
||||
edition = "2018"
|
||||
description = "Utility functions used all over the code base."
|
||||
|
||||
[dependencies]
|
||||
snafu = { version = "0.6", features = ["backtraces"] }
|
||||
|
||||
[dev-dependencies]
|
96
utils/src/lib.rs
Normal file
96
utils/src/lib.rs
Normal file
|
@ -0,0 +1,96 @@
|
|||
|
||||
use snafu::OptionExt;
|
||||
use util_error::{UtilResult, KeyNotFound, IndexOfFailed, OutOfBounds};
|
||||
use std::{collections::HashMap, slice::SliceIndex};
|
||||
|
||||
pub mod util_error;
|
||||
|
||||
// replace HashMap method that returns Option with one that returns Result and proper Error
|
||||
pub fn map_get<'a, K: ::std::fmt::Debug + std::hash::Hash + std::cmp::Eq, V>(
|
||||
hash_map: &'a HashMap<K, V>,
|
||||
key: &K,
|
||||
) -> UtilResult<&'a V> {
|
||||
let value = hash_map.get(key).context(KeyNotFound {
|
||||
key_str: format!("{:?}", key),
|
||||
})?;
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
pub fn index_of<T: ::std::fmt::Debug + std::cmp::Eq>(elt: T, slice: &[T]) -> UtilResult<usize> {
|
||||
let index = slice
|
||||
.iter()
|
||||
.position(|slice_elt| *slice_elt == elt)
|
||||
.with_context(|| {
|
||||
let elt_str = format!("{:?}", elt);
|
||||
let collection_str = format!("{:?}", slice);
|
||||
|
||||
IndexOfFailed {
|
||||
elt_str,
|
||||
collection_str,
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(index)
|
||||
}
|
||||
|
||||
// replace slice method that return Option with one that return Result and proper Error
|
||||
pub fn slice_get<T>(index: usize, slice: &[T]) -> UtilResult<&<usize as SliceIndex<[T]>>::Output> {
|
||||
let elt_ref = slice.get(index).context(OutOfBounds {
|
||||
index,
|
||||
collection_name: "Slice",
|
||||
len: slice.len(),
|
||||
})?;
|
||||
|
||||
Ok(elt_ref)
|
||||
}
|
||||
|
||||
pub fn slice_get_mut<T>(
|
||||
index: usize,
|
||||
slice: &mut [T],
|
||||
) -> UtilResult<&mut <usize as SliceIndex<[T]>>::Output> {
|
||||
let slice_len = slice.len();
|
||||
|
||||
let elt_ref = slice.get_mut(index).context(OutOfBounds {
|
||||
index,
|
||||
collection_name: "Slice",
|
||||
len: slice_len,
|
||||
})?;
|
||||
|
||||
Ok(elt_ref)
|
||||
}
|
||||
|
||||
// returns the index of the first occurrence of element and index of the last occurrence
|
||||
pub fn first_last_index_of<T: ::std::fmt::Debug + std::cmp::Eq>(
|
||||
elt: T,
|
||||
slice: &[T],
|
||||
) -> UtilResult<(usize, usize)> {
|
||||
let mut first_index_opt = None;
|
||||
let mut last_index_opt = None;
|
||||
|
||||
for (index, list_elt) in slice.iter().enumerate() {
|
||||
if *list_elt == elt {
|
||||
if first_index_opt.is_none() {
|
||||
first_index_opt = Some(index);
|
||||
last_index_opt = Some(index);
|
||||
} else {
|
||||
last_index_opt = Some(index)
|
||||
}
|
||||
} else if last_index_opt.is_some() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if let (Some(first_index), Some(last_index)) = (first_index_opt, last_index_opt) {
|
||||
Ok((first_index, last_index))
|
||||
} else {
|
||||
let elt_str = format!("{:?}", elt);
|
||||
let collection_str = format!("{:?}", slice);
|
||||
|
||||
IndexOfFailed {
|
||||
elt_str,
|
||||
collection_str,
|
||||
}
|
||||
.fail()
|
||||
}
|
||||
}
|
36
utils/src/util_error.rs
Normal file
36
utils/src/util_error.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
|
||||
use snafu::{Backtrace, Snafu};
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
#[snafu(visibility(pub))]
|
||||
pub enum UtilError {
|
||||
#[snafu(display(
|
||||
"IndexOfFailed: Element {} was not found in collection {}.",
|
||||
elt_str,
|
||||
collection_str
|
||||
))]
|
||||
IndexOfFailed {
|
||||
elt_str: String,
|
||||
collection_str: String,
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
#[snafu(display("KeyNotFound: key {} was not found in HashMap.", key_str,))]
|
||||
KeyNotFound {
|
||||
key_str: String,
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
#[snafu(display(
|
||||
"OutOfBounds: index {} was out of bounds for {} with length {}.",
|
||||
index,
|
||||
collection_name,
|
||||
len
|
||||
))]
|
||||
OutOfBounds {
|
||||
index: usize,
|
||||
collection_name: String,
|
||||
len: usize,
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
}
|
||||
|
||||
pub type UtilResult<T, E = UtilError> = std::result::Result<T, E>;
|
Loading…
Add table
Add a link
Reference in a new issue